mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
Define FontSetter interface, the Font type and helper methods
The helper methods provide support for selecting the best-fit font for the console dimensions and for looking up a font by name.
This commit is contained in:
parent
6195f3fc3b
commit
952d0bf4a5
@ -1,6 +1,9 @@
|
||||
package console
|
||||
|
||||
import "image/color"
|
||||
import (
|
||||
"gopheros/device/video/console/font"
|
||||
"image/color"
|
||||
)
|
||||
|
||||
// ScrollDir defines a scroll direction.
|
||||
type ScrollDir uint8
|
||||
@ -43,3 +46,11 @@ type Device interface {
|
||||
// supported colors should be a no-op.
|
||||
SetPaletteColor(uint8, color.RGBA)
|
||||
}
|
||||
|
||||
// FontSetter is an interface implemented by console devices that
|
||||
// support loadable bitmap fonts.
|
||||
//
|
||||
// SetFont selects a bitmap font to be used by the console.
|
||||
type FontSetter interface {
|
||||
SetFont(*font.Font)
|
||||
}
|
||||
|
97
src/gopheros/device/video/console/font/font.go
Normal file
97
src/gopheros/device/video/console/font/font.go
Normal file
@ -0,0 +1,97 @@
|
||||
package font
|
||||
|
||||
var (
|
||||
// The list of available fonts.
|
||||
availableFonts []*Font
|
||||
)
|
||||
|
||||
// Font describes a bitmap font that can be used by a console device.
|
||||
type Font struct {
|
||||
// The name of the font
|
||||
Name string
|
||||
|
||||
// The width of each glyph in pixels.
|
||||
GlyphWidth uint32
|
||||
|
||||
// The height of each glyph in pixels.
|
||||
GlyphHeight uint32
|
||||
|
||||
// The recommended console resolution for this font.
|
||||
RecommendedWidth uint32
|
||||
RecommendedHeight uint32
|
||||
|
||||
// Font priority (lower is better). When auto-detecting a font to use, the font with
|
||||
// the lowest priority will be preferred
|
||||
Priority uint32
|
||||
|
||||
// The number of bytes describing a row in a glyph.
|
||||
BytesPerRow uint32
|
||||
|
||||
// The font bitmap. Each character consists of BytesPerRow * Height
|
||||
// bytes where each bit indicates whether a pixel should be set to the
|
||||
// foreground or the background color.
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// FindByName looks up a font instance by name. If the font is not found then
|
||||
// the function returns nil.
|
||||
func FindByName(name string) *Font {
|
||||
for _, f := range availableFonts {
|
||||
if f.Name == name {
|
||||
return f
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// BestFit returns the best font from the available font list given the
|
||||
// specified console dimensions. If multiple fonts match the dimension criteria
|
||||
// then their priority attribute is used to select one.
|
||||
//
|
||||
// The algorithm for selecting the best font is the following:
|
||||
// For each font:
|
||||
// - calculate the sum of abs differences between the font recommended dimension
|
||||
// and the console dimensions.
|
||||
// - if the font score is lower than the current best font's score then the
|
||||
// font becomes the new best font.
|
||||
// - if the font score is equal to the current best font's score then the
|
||||
// font with the lowest priority becomes the new best font.
|
||||
func BestFit(consoleWidth, consoleHeight uint32) *Font {
|
||||
var (
|
||||
best *Font
|
||||
bestDelta uint32
|
||||
absDeltaW, absDeltaH, absDelta uint32
|
||||
)
|
||||
|
||||
for _, f := range availableFonts {
|
||||
if f.RecommendedWidth > consoleWidth {
|
||||
absDeltaW = f.RecommendedWidth - consoleWidth
|
||||
} else {
|
||||
absDeltaW = consoleWidth - f.RecommendedWidth
|
||||
}
|
||||
|
||||
if f.RecommendedHeight > consoleHeight {
|
||||
absDeltaH = f.RecommendedHeight - consoleHeight
|
||||
} else {
|
||||
absDeltaH = consoleHeight - f.RecommendedHeight
|
||||
}
|
||||
|
||||
absDelta = absDeltaW + absDeltaH
|
||||
|
||||
if best == nil {
|
||||
best = f
|
||||
bestDelta = absDelta
|
||||
continue
|
||||
}
|
||||
|
||||
if best.Priority < f.Priority || absDelta > bestDelta {
|
||||
continue
|
||||
}
|
||||
|
||||
best = f
|
||||
bestDelta = absDelta
|
||||
}
|
||||
|
||||
return best
|
||||
}
|
59
src/gopheros/device/video/console/font/font_test.go
Normal file
59
src/gopheros/device/video/console/font/font_test.go
Normal file
@ -0,0 +1,59 @@
|
||||
package font
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestFindByName(t *testing.T) {
|
||||
defer func(origList []*Font) {
|
||||
availableFonts = origList
|
||||
}(availableFonts)
|
||||
|
||||
availableFonts = []*Font{
|
||||
&Font{Name: "foo"},
|
||||
&Font{Name: "bar"},
|
||||
}
|
||||
|
||||
exp := availableFonts[1]
|
||||
if got := FindByName("bar"); got != exp {
|
||||
t.Fatalf("expected to get font: %v; got %v", exp, got)
|
||||
}
|
||||
|
||||
if got := FindByName("not-existing-font"); got != nil {
|
||||
t.Fatalf("expected to get nil for a font that does not exist; got %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBestFit(t *testing.T) {
|
||||
defer func(origList []*Font) {
|
||||
availableFonts = origList
|
||||
}(availableFonts)
|
||||
|
||||
availableFonts = []*Font{
|
||||
&Font{Name: "retina1", RecommendedWidth: 2560, RecommendedHeight: 1600, Priority: 2},
|
||||
&Font{Name: "retina2", RecommendedWidth: 2560, RecommendedHeight: 1600, Priority: 1},
|
||||
&Font{Name: "default", RecommendedWidth: 800, RecommendedHeight: 600, Priority: 0},
|
||||
&Font{Name: "standard", RecommendedWidth: 1024, RecommendedHeight: 768, Priority: 0},
|
||||
}
|
||||
|
||||
specs := []struct {
|
||||
consW, consH uint32
|
||||
expName string
|
||||
}{
|
||||
{320, 200, "default"},
|
||||
{800, 600, "default"},
|
||||
{1024, 768, "standard"},
|
||||
{3000, 3000, "retina2"},
|
||||
{2500, 600, "retina2"},
|
||||
}
|
||||
|
||||
for specIndex, spec := range specs {
|
||||
got := BestFit(spec.consW, spec.consH)
|
||||
if got == nil {
|
||||
t.Errorf("[spec %d] unable to find a font", specIndex)
|
||||
continue
|
||||
}
|
||||
|
||||
if got.Name != spec.expName {
|
||||
t.Errorf("[spec %d] expected to get font %q; got %q", specIndex, spec.expName, got.Name)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user