Skip to content

Commit

Permalink
Crop glyphs
Browse files Browse the repository at this point in the history
  • Loading branch information
mikee47 committed Dec 23, 2024
1 parent 78614c0 commit 2f99e8f
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 30 deletions.
22 changes: 15 additions & 7 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ Resource script entries look like this:
"<name>": {
"codepoints": "<filter>", // Which character glyphs to include. See below.
"chars": "<text string>", // List of required character codepoints
"alpha", // Optional: maximum number of bits per pixel (1, 2, 4 or 8). Default is 8.
"mono", // Optional: Set to *true* to use 1 bit per pixel. Default is *false*.
"size", // Optional parameter for freetype fonts
"normal": "<filename>",
"italic": "<filename>",
"bold": "<filename>",
Expand All @@ -188,7 +191,7 @@ Resource script entries look like this:
Styles are optional but a font must have at least one typeface.

By default, all ASCII characters from 0x20 (space) to 0x7e (~).
By default, all ASCII characters from 0x20 (space) to 0x7e (~) are generated.
The ``codepoints`` parameter is a comma-separated list of ranges:

a-z,A-Z,0-9,0x4000-0x4050
Expand Down Expand Up @@ -228,12 +231,17 @@ The following font classes are currently supported:

The ``freetype`` library supports other types so if required these are easily added.

These fonts have some additional parameters:
"mono": <True/False>
Whether to produce monochrome (1-bit) or grayscale (8-bit alpha) glyphs.
If not specified, defaults to grayscale.
"size": <Point size of font>
e.g. 16, 14.5
An optional **size** parameter indicates the point size for the font to generate.
If omitted, the first typeface defined for that font is used.

The **alpha** parameter sets a limit on the number of bits per pixel used for the generate glyphs.
The freetype and VLW fonts are generated using 8 bits per pixel for smoother appearance.
This can be reduced to 4, 2 or 1 bit (monochrome) to reduce storage space.

The **mono** parameter is equivalent to **alpha: 1**, and is ignored if **alpha** is specified.

Generated glyphs are cropped to remove any blank space around the image, then packed to minimise memory usage.
This means that the bitmap data is contiguous, without breaks, from top left to bottom right.


Images
Expand Down
15 changes: 13 additions & 2 deletions Tools/rc/rclib/font.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import sys
import struct
from .base import Resource, findFile, StructSize, fstrSize
from PIL import Image
from PIL import Image, ImageChops

class FontStyle(enum.Enum):
"""Style is a set of these values, using strings here but bitfields in library"""
Expand Down Expand Up @@ -67,10 +67,21 @@ def set_bitmap(self, img: Image):
""" Convert bitmap to internal (GFX) format and set width, height.
We remove space round the glyph, update glyph offsets and pack the resulting bits.
"""

# Find smallest bounding box and crop
w, h = img.width, img.height
bg = Image.new('L', (w, h), 0)
diff = ImageChops.difference(img, bg)
diff = ImageChops.add(diff, diff, 2.0, -100)
bbox = diff.getbbox()
if bbox:
img = img.crop(bbox)
w, h = img.width, img.height
self.xOffset += bbox[0]
self.yOffset += bbox[1]

self.width = w
self.height = h
stride = w # All modes 1 byte per pixel
alpha = 1 if img.mode == '1' else self.typeface.font.alpha
self.alpha = alpha
pixels_per_byte = 8 // alpha
Expand Down
22 changes: 1 addition & 21 deletions Tools/rc/rclib/freetype.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,33 +26,13 @@
import array
import freetype
from .font import Glyph, Typeface
from PIL import Image, ImageChops
from io import BytesIO
from PIL import Image


def pointsToPixels(points26):
return round(points26 / 64)


def print_bitmap_diff(bitmap: freetype.Bitmap):
if bitmap.width + bitmap.rows == 0:
return
mode = '1' if bitmap.pixel_mode == freetype.FT_PIXEL_MODE_MONO else 'L'
img = Image.frombuffer(mode, (bitmap.width, bitmap.rows), bytearray(bitmap.buffer))
bg = Image.new(mode, (bitmap.width, bitmap.rows), 0)
diff = ImageChops.difference(img, bg)
diff = ImageChops.add(diff, diff, 2.0, -100)
bbox = diff.getbbox()
if bbox is None:
return
w, h = bbox[2] - bbox[0], bbox[3] - bbox[1]
size_orig = bitmap.width * bitmap.rows
size_new = w * h
pix_diff = size_orig - size_new
if pix_diff:
print(f'glyph = ({bitmap.width}, {bitmap.rows}), bbox = ({w}, {h}), pix. diff {pix_diff} ({size_orig} -> {size_new})')


def parse_typeface(typeface: Typeface):
face = freetype.Face(typeface.source)

Expand Down

0 comments on commit 2f99e8f

Please sign in to comment.