やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

pyxelで日本語を表示する1(ビットマップ・フォント)

 フォントやコードを拝借して。

成果物

情報源

 圧倒的感謝っ!

概要

  1. ttfファイルを入手する
  2. ttfファイルからPNG画像ファイルを出力する

 2の画像ファイルがビットマップフォントである。

手順

1. ttfファイルを入手する

 フォントめっちゃカッコイイ! 感謝メッセージ送りたいが、Twitterアカウント作れない……。と思ったらマシュマロとかいうサービスで匿名メッセ送れた! こんなのあるのか。参考記事にも感謝メッセしたいのだが、マシュマロってない。

wget http://www17.plala.or.jp/xxxxxxx/00ff/x12y16pxLineLinker.ttf
wget http://www17.plala.or.jp/xxxxxxx/00ff/x16y32pxGridGazer.ttf
wget http://www17.plala.or.jp/xxxxxxx/00ff/x12y20pxScanLine.ttf
wget http://www17.plala.or.jp/xxxxxxx/00ff/x14y20pxScoreDozer.ttf
wget http://www17.plala.or.jp/xxxxxxx/00ff/x14y24pxHeadUpDaisy.ttf
wget http://www17.plala.or.jp/xxxxxxx/00ff/x8y12pxTheStrongGamer.ttf

2. ttfファイルからPNG画像ファイルを出力する

2-1. 環境構築

 上記にあるとおり、pillownumpyが必要。こいつでttfからPNGに変換する。

$ pyenv global system
$ python3 -V
Python 3.7.3
$ pip3 install pillow
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Requirement already satisfied: pillow in /usr/lib/python3/dist-packages (5.4.1)
$ pip3 install numpy 
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Requirement already satisfied: numpy in /usr/lib/python3/dist-packages (1.16.2)

 systemのPythonpyxelをインストールした。

git clone https://github.com/kitao/pyxel.git
cd pyxel
make -C pyxel/core clean all
sudo pip3 install .

Python 3.8.2 だと Pillow がインストールできなかった……。エラーログ。

$ pyenv global 3.8.2
$ python -V
Python 3.8.2
$ pip install Pillow
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting Pillow
  Using cached Pillow-7.0.0.tar.gz (38.2 MB)
Installing collected packages: Pillow
    Running setup.py install for Pillow ... error
    ERROR: Command errored out with exit status 1:
     command: /home/pi/.pyenv/versions/3.8.2/bin/python3.8 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-dbfv3n9v/Pillow/setup.py'"'"'; __file__='"'"'/tmp/pip-install-dbfv3n9v/Pillow/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /tmp/pip-record-3ukhynwd/install-record.txt --single-version-externally-managed --compile --install-headers /home/pi/.pyenv/versions/3.8.2/include/python3.8/Pillow
         cwd: /tmp/pip-install-dbfv3n9v/Pillow/
    Complete output (174 lines):
    running install
    running build
    running build_py
    creating build
    creating build/lib.linux-armv7l-3.8
    creating build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/FtexImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/PpmImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImageFont.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/JpegImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/PsdImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/SgiImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/XVThumbImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/__main__.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/Image.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/_binary.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/PyAccess.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImageSequence.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/DdsImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/XpmImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/McIdasImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/IcnsImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImageTransform.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/BmpImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImagePalette.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/IptcImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/TiffImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/GdImageFile.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/_util.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/CurImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/TiffTags.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/FliImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/WebPImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImageGrab.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/WmfImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImageEnhance.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImageShow.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/PdfImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImageOps.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/GbrImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImageTk.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/BlpImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/PcfFontFile.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/WalImageFile.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/Jpeg2KImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImageMorph.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/TgaImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/EpsImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/_tkinter_finder.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/IcoImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImageWin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/GimpGradientFile.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImageDraw.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/features.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/Hdf5StubImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/JpegPresets.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/__init__.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/DcxImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImagePath.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImageDraw2.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImageFilter.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/PcdImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/PSDraw.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/BufrStubImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImageChops.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/FpxImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImageCms.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ExifTags.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/PdfParser.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/FontFile.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/PaletteFile.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImageMath.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImageQt.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/PalmImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/GifImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImtImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/_version.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/MicImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/MspImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ContainerIO.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImageColor.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/GimpPaletteFile.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/PixarImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImageStat.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/GribStubImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/BdfFontFile.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/MpegImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/FitsStubImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/TarIO.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/SpiderImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImageFile.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/SunImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/PcxImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/XbmImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/PngImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/ImageMode.py -> build/lib.linux-armv7l-3.8/PIL
    copying src/PIL/MpoImagePlugin.py -> build/lib.linux-armv7l-3.8/PIL
    running egg_info
    writing src/Pillow.egg-info/PKG-INFO
    writing dependency_links to src/Pillow.egg-info/dependency_links.txt
    writing top-level names to src/Pillow.egg-info/top_level.txt
    reading manifest file 'src/Pillow.egg-info/SOURCES.txt'
    reading manifest template 'MANIFEST.in'
    warning: no files found matching '*.c'
    warning: no files found matching '*.h'
    warning: no files found matching '*.sh'
    warning: no previously-included files found matching '.appveyor.yml'
    warning: no previously-included files found matching '.coveragerc'
    warning: no previously-included files found matching '.codecov.yml'
    warning: no previously-included files found matching '.editorconfig'
    warning: no previously-included files found matching '.readthedocs.yml'
    warning: no previously-included files found matching 'azure-pipelines.yml'
    warning: no previously-included files matching '.git*' found anywhere in distribution
    warning: no previously-included files matching '*.pyc' found anywhere in distribution
    warning: no previously-included files matching '*.so' found anywhere in distribution
    no previously-included directories found matching '.azure-pipelines'
    no previously-included directories found matching '.travis'
    writing manifest file 'src/Pillow.egg-info/SOURCES.txt'
    running build_ext
    
    
    The headers or library files could not be found for jpeg,
    a required dependency when compiling Pillow from source.
    
    Please see the install instructions at:
       https://pillow.readthedocs.io/en/latest/installation.html
    
    Traceback (most recent call last):
      File "/tmp/pip-install-dbfv3n9v/Pillow/setup.py", line 852, in <module>
        setup(
      File "/home/pi/.pyenv/versions/3.8.2/lib/python3.8/site-packages/setuptools/__init__.py", line 145, in setup
        return distutils.core.setup(**attrs)
      File "/home/pi/.pyenv/versions/3.8.2/lib/python3.8/distutils/core.py", line 148, in setup
        dist.run_commands()
      File "/home/pi/.pyenv/versions/3.8.2/lib/python3.8/distutils/dist.py", line 966, in run_commands
        self.run_command(cmd)
      File "/home/pi/.pyenv/versions/3.8.2/lib/python3.8/distutils/dist.py", line 985, in run_command
        cmd_obj.run()
      File "/home/pi/.pyenv/versions/3.8.2/lib/python3.8/site-packages/setuptools/command/install.py", line 61, in run
        return orig.install.run(self)
      File "/home/pi/.pyenv/versions/3.8.2/lib/python3.8/distutils/command/install.py", line 545, in run
        self.run_command('build')
      File "/home/pi/.pyenv/versions/3.8.2/lib/python3.8/distutils/cmd.py", line 313, in run_command
        self.distribution.run_command(command)
      File "/home/pi/.pyenv/versions/3.8.2/lib/python3.8/distutils/dist.py", line 985, in run_command
        cmd_obj.run()
      File "/home/pi/.pyenv/versions/3.8.2/lib/python3.8/distutils/command/build.py", line 135, in run
        self.run_command(cmd_name)
      File "/home/pi/.pyenv/versions/3.8.2/lib/python3.8/distutils/cmd.py", line 313, in run_command
        self.distribution.run_command(command)
      File "/home/pi/.pyenv/versions/3.8.2/lib/python3.8/distutils/dist.py", line 985, in run_command
        cmd_obj.run()
      File "/home/pi/.pyenv/versions/3.8.2/lib/python3.8/distutils/command/build_ext.py", line 340, in run
        self.build_extensions()
      File "/tmp/pip-install-dbfv3n9v/Pillow/setup.py", line 687, in build_extensions
        raise RequiredDependencyException(f)
    __main__.RequiredDependencyException: jpeg
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-install-dbfv3n9v/Pillow/setup.py", line 907, in <module>
        raise RequiredDependencyException(msg)
    __main__.RequiredDependencyException:
    
    The headers or library files could not be found for jpeg,
    a required dependency when compiling Pillow from source.
    
    Please see the install instructions at:
       https://pillow.readthedocs.io/en/latest/installation.html
    
    
    ----------------------------------------
ERROR: Command errored out with exit status 1: /home/pi/.pyenv/versions/3.8.2/bin/python3.8 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-dbfv3n9v/Pillow/setup.py'"'"'; __file__='"'"'/tmp/pip-install-dbfv3n9v/Pillow/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /tmp/pip-record-3ukhynwd/install-record.txt --single-version-externally-managed --compile --install-headers /home/pi/.pyenv/versions/3.8.2/include/python3.8/Pillow Check the logs for full command output.

2-2. コード作成

 こちらのを少し手直しして動くようにした。

example.py

#!/usr/bin/env python3
# coding: utf8
from PIL import Image, ImageFont, ImageDraw
import pyxel
import string

class Font:
    def __init__(self, file, size, alphabet):
        import numpy as np
        self.file = file
        self.size = size
        self.alphabet = alphabet

        px_w, px_h = size
        # pt_w, pt_h = (int(n * 0.75) for n in meta['size']) # 96 dpi
        ## load custom font
        font = ImageFont.truetype(file, size=px_h) # it must be pt_h, but px_h brings better result
        img = Image.new('1', size=(256, 256))
        draw = ImageDraw.Draw(img)
        coords = {}
        x, y = 0, 0
        for c in alphabet:
            if x + px_w > 256:
                x = 0
                y += px_h
            draw.text((x, y), c, font=font, fill=1)
            coords[c] = (x, y)
            x += px_w
        self.coords = coords
        self.img = img
        self.data = np.array(img.getdata()).reshape(256, 256)


def draw_font(img, font, col=7):
    img_bank = pyxel.image(img)
    for y in range(256):
        for x in range(256):
            img_bank.set(x, y, col if font.data[y][x] else 0) 

def text(font, x, y, s):
    w, h = font.size
    left = x

    for ch in s:
        if ch == '\n':
            x = left
            y += h
            continue

        if ch == ' ':
            x += w
            continue
        
        if ch in font.coords.keys():
            u, v = font.coords[ch]
            pyxel.blt(x, y, 0, u, v, w, h, 0)
#             for y_ in range(h):
#                 for x_ in range(w):
#                     if font.data[v+y_, u+x_]:
#                         pyxel.pix(x+x_, y+y_, col)
        x += w


def update():
    pass

def draw():
    pyxel.cls(0)
    text(font, 0, 0, """
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
みにくいよりうつくしいほうがいい。
Explicit is better than implicit.
あんじするよりめいじするほうがいい。
Simple is better than complex.
ふくざつであるよりはへいいであるほうがいい。
Complex is better than complicated.
それでも、こみいっているよりはふくざつであるほうがまし。
Flat is better than nested.
ネストはあさいほうがいい。
Sparse is better than dense.
みっしゅうしているよりはすきまがあるほうがいい。
Readability counts.
よみやすいことはぜんである。
Special cases aren't special enough to break the rules.
とくしゅであることはルールをやぶるりゆうにならない。
Although practicality beats purity.
しかし、じつようせいをもとめるとじゅんすいさがうしなわれることが
ある。""".strip())

pyxel.init(255, 255, caption="よくある手法で外部フォント")

fontfile = 'x8y12pxTheStrongGamer.ttf'
letter_size = (8, 12)
ascii_chars = string.punctuation + string.digits + string.ascii_letters
ひらがな = "".join(chr(c) for c in range(ord('ぁ'), ord('ゔ')+1))+"ー"
カタカナ = "".join(chr(c) for c in range(ord('ァ'), ord('ヶ')+1))+"ー"
# 半角カタカナ = "".join(chr(c) for c in range(ord('ァ')-6, ord('゙')+2))
alphabet = ascii_chars + ひらがな + カタカナ + "、。「」"
font = Font(fontfile, letter_size, alphabet)
draw_font(0, font)
font.img.save('x8y12pxTheStrongGamer.png')

pyxel.run(update, draw)

 上記のうち、以下のコードでビットマップフォント画像ファイルを出力する。

font.img.save('x8y12pxTheStrongGamer.png')

f:id:ytyaru:20200331205938p:plain

2-3. 実行

python3 example.py
  • x8y12pxTheStrongGamer.pngファイルが出力された
  • 画面は以下のようになった

f:id:ytyaru:20200331205500p:plain

 素敵すぎる! レトロ感パない!

課題

漢字が使えない

 同じ手法を使えば漢字もできる。だが、pyxelの仕様に収まらない。漢字は6355字ある。pyxelの.pyxresは256*256ピクセルを3枚しか持てないため2016文字までしか収まらない。

  • 漢字: 6355字
    • JIS第1水準漢字 : 2965字
    • JIS第2水準漢字 : 3390字

 画像1枚あたり672文字入る。今回の文字サイズは幅8, 高さ12ピクセル。これは256*256ピクセルの画像に何文字入るか算出すると以下。

256/8 = 32
256/12 = 21.333
32 * 21 = 672

 pyxelにおいて1ゲームあたり最大2016文字のビットマップフォントが使える。

672 * 3 = 2016

 JIS第1水準漢字すら満たせずに上限。

 そもそも、メモリの無駄遣いに思える。もうttfで描画できるシステム使いたいレベル。

文言を厳選せねばならない

 平仮名と片仮名だけだとポケモンみたいに「分かち書き」っぽく書かねば読みづらいのでは?

かがくの ちからって すげー !

 レトロだわ。読みづらいけど、それゆえに言葉を厳選することになるからクオリティの高いものになる傾向がある。だからレトロゲーは強い。逆にいえば、レトロゲーの作者には崇高なセンスが求められる。

使いやすくしたい。

 今のコードはフォントファイルがないとpyxelに読み込めない。PNG画像だけでも読み込めるようにしたい。

所感

 これで平仮名と片仮名だけだが日本語が使えるようになった!

 あとはコードを使いやすくしたい。次回、やってみる。

前回まで

対象環境

$ uname -a
Linux raspberrypi 4.19.97-v7l+ #1294 SMP Thu Jan 30 13:21:14 GMT 2020 armv7l GNU/Linux