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
|
package console
|
||||||
|
|
||||||
import "image/color"
|
import (
|
||||||
|
"gopheros/device/video/console/font"
|
||||||
|
"image/color"
|
||||||
|
)
|
||||||
|
|
||||||
// ScrollDir defines a scroll direction.
|
// ScrollDir defines a scroll direction.
|
||||||
type ScrollDir uint8
|
type ScrollDir uint8
|
||||||
@ -43,3 +46,11 @@ type Device interface {
|
|||||||
// supported colors should be a no-op.
|
// supported colors should be a no-op.
|
||||||
SetPaletteColor(uint8, color.RGBA)
|
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