2022-01-18 19:29:53 +00:00
|
|
|
// Copyright (c) 2022 Sam Blenny
|
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
|
|
|
//
|
|
|
|
|
package lib
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"image"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Holds packed XOR mask values of a blit pattern for character's glyph.
|
|
|
|
|
//
|
|
|
|
|
// Pixel order: row-major order traversal of px matrix; top-left pixel goes in
|
|
|
|
|
// least significant bit of of .Words[1]
|
|
|
|
|
// Mask values: bit=1 means foreground, bit=0 means background
|
|
|
|
|
//
|
|
|
|
|
// Patterns that need padding because their size is not a multiple of 32 bits
|
|
|
|
|
// (width*height % 32 != 0) get padded with zeros in the least significant bits
|
|
|
|
|
// of the last word.
|
|
|
|
|
type BlitPattern struct {
|
|
|
|
|
Words []uint32
|
|
|
|
|
Width uint8
|
|
|
|
|
CS CharSpec
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Extract matrix of pixels from an image containing grid of glyphs
|
|
|
|
|
// - img: image.Image from png file containing glyph grid
|
|
|
|
|
// - font: Glyph sheet specs (glyph size, border/gutter, etc)
|
|
|
|
|
// - cs: Character specs (source row and column in glyph grid)
|
|
|
|
|
func NewBlitPattern(img image.Image, font FontSpec, cs CharSpec, dbg bool) BlitPattern {
|
|
|
|
|
row := cs.Row
|
|
|
|
|
col := cs.Col
|
|
|
|
|
imgRect := img.Bounds()
|
|
|
|
|
rows := (imgRect.Max.Y - font.Border) / (font.Size + font.Gutter)
|
|
|
|
|
if row < 0 || row >= rows || col < 0 || col >= font.Cols {
|
|
|
|
|
panic("row or column out of range")
|
|
|
|
|
}
|
|
|
|
|
// Get pixels for grid cell, converting from RGBA to 1-bit
|
|
|
|
|
gridSize := font.Size + font.Gutter
|
|
|
|
|
border := font.Border
|
|
|
|
|
pxMatrix := Matrix{}
|
|
|
|
|
for y := border + (row * gridSize); y < (row+1)*gridSize; y++ {
|
|
|
|
|
var row MatrixRow
|
|
|
|
|
for x := border + (col * gridSize); x < (col+1)*gridSize; x++ {
|
|
|
|
|
r, _, _, _ := img.At(x, y).RGBA()
|
|
|
|
|
//fmt.Println(r, g, b, a)
|
|
|
|
|
if r == 0 {
|
|
|
|
|
row = append(row, 1)
|
|
|
|
|
} else {
|
|
|
|
|
row = append(row, 0)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pxMatrix = append(pxMatrix, row)
|
|
|
|
|
}
|
|
|
|
|
pxMatrix = pxMatrix.Trim(font, row, col)
|
|
|
|
|
width := uint8(len(pxMatrix[0]))
|
add Tall font face and support for 32x32 bitmaps
Thanks to @samblenny for getting the ball rolling on this.
This commit adds a "tall" font face which is somewhere in betwee
the Regular size (12 pts) and 2x Small size (18 pts). This is
a compromise between text density and readability that has been
missing for some time from the UI.
The 32x32 glyph support is "hacked in", in that an extra flag
has been added to support this special case, rather than either
(a) making the library more generic (e.g. supporting 8x, 16x, 32x, 64x
glyphs) or (b) adding more metadata to the font libraries so that
the bitmap pattern is automatically associated with the font (right
now the parameters are set by hand in blitstr2/fonts.rs).
This is a violation of the "zero, one, many" principle in coding,
but I'm waiving that because (a) 64x glyphs don't make sense on
our system...you're not going to be creating posters and headlines
on this device (ha ha I say this today, tomorrow I will get a PR
for a poster editor app that absolutely needs 64x glyphs) and (b)
the font subsystem is, at least for now, a fairly mature and
well-tested code base so a minor futzing seems more appropriate
than ripping everything out.
The point at which things should be greatly refactored is when
we consider supporting right-to-left scripts or scripts that
require complex ligatures. But I think for now the system as
writ covers 80% of humanity's language needs acceptably
(but not perfectly).
2023-07-05 04:43:44 +00:00
|
|
|
if font.Size <= 16 {
|
|
|
|
|
pxMatrix = pxMatrix.padTo16x16()
|
|
|
|
|
} else {
|
|
|
|
|
pxMatrix = pxMatrix.padTo32x32()
|
|
|
|
|
}
|
2022-01-18 19:29:53 +00:00
|
|
|
pxMatrix.Debug(cs, dbg)
|
add Tall font face and support for 32x32 bitmaps
Thanks to @samblenny for getting the ball rolling on this.
This commit adds a "tall" font face which is somewhere in betwee
the Regular size (12 pts) and 2x Small size (18 pts). This is
a compromise between text density and readability that has been
missing for some time from the UI.
The 32x32 glyph support is "hacked in", in that an extra flag
has been added to support this special case, rather than either
(a) making the library more generic (e.g. supporting 8x, 16x, 32x, 64x
glyphs) or (b) adding more metadata to the font libraries so that
the bitmap pattern is automatically associated with the font (right
now the parameters are set by hand in blitstr2/fonts.rs).
This is a violation of the "zero, one, many" principle in coding,
but I'm waiving that because (a) 64x glyphs don't make sense on
our system...you're not going to be creating posters and headlines
on this device (ha ha I say this today, tomorrow I will get a PR
for a poster editor app that absolutely needs 64x glyphs) and (b)
the font subsystem is, at least for now, a fairly mature and
well-tested code base so a minor futzing seems more appropriate
than ripping everything out.
The point at which things should be greatly refactored is when
we consider supporting right-to-left scripts or scripts that
require complex ligatures. But I think for now the system as
writ covers 80% of humanity's language needs acceptably
(but not perfectly).
2023-07-05 04:43:44 +00:00
|
|
|
if font.Size <= 16 {
|
|
|
|
|
patternBytes := pxMatrix.convertToPattern()
|
|
|
|
|
return BlitPattern{patternBytes, width, cs}
|
|
|
|
|
} else {
|
|
|
|
|
patternBytes := pxMatrix.convertToPattern32()
|
|
|
|
|
return BlitPattern{patternBytes, width, cs}
|
|
|
|
|
}
|
2022-01-18 19:29:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert blit pattern to rust source code for part of an array of bytes
|
|
|
|
|
func ConvertPatternToRust(pattern BlitPattern) string {
|
2023-12-27 06:10:48 +00:00
|
|
|
patternStr := " "
|
2022-01-18 19:29:53 +00:00
|
|
|
wordsPerRow := uint32(8)
|
|
|
|
|
ceilRow := uint32(len(pattern.Words)) / wordsPerRow
|
|
|
|
|
if uint32(len(pattern.Words))%wordsPerRow > 0 {
|
|
|
|
|
ceilRow += 1
|
|
|
|
|
}
|
|
|
|
|
for i := uint32(0); i < ceilRow; i++ {
|
2023-12-27 06:10:48 +00:00
|
|
|
if i != 0 {
|
|
|
|
|
patternStr += " "
|
|
|
|
|
}
|
2022-01-18 19:29:53 +00:00
|
|
|
start := i * wordsPerRow
|
|
|
|
|
end := min(uint32(len(pattern.Words)), (i+1)*wordsPerRow)
|
|
|
|
|
line := pattern.Words[start:end]
|
|
|
|
|
var s []string
|
|
|
|
|
for _, word := range line {
|
|
|
|
|
s = append(s, fmt.Sprintf("0x%08x", word))
|
|
|
|
|
}
|
2023-12-27 06:10:48 +00:00
|
|
|
patternStr += strings.Join(s, ", ") + ",\n"
|
2022-01-18 19:29:53 +00:00
|
|
|
}
|
|
|
|
|
return patternStr
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return lowest value among two integers
|
|
|
|
|
func min(a uint32, b uint32) uint32 {
|
|
|
|
|
if b > a {
|
|
|
|
|
return a
|
|
|
|
|
}
|
|
|
|
|
return b
|
|
|
|
|
}
|