1
0
mirror of https://github.com/taigrr/gopher-os synced 2025-01-18 04:43:13 -08:00

Extend vesa driver to support 24 and 32 bpp

This commit is contained in:
Achilleas Anagnostopoulos 2017-07-11 07:29:47 +01:00
parent 13ba4bbbed
commit f02c767257
2 changed files with 646 additions and 34 deletions

View File

@ -16,9 +16,11 @@ import (
)
type VesaFbConsole struct {
bpp uint32
fbPhysAddr uintptr
fb []uint8
bpp uint32
bytesPerPixel uint32
fbPhysAddr uintptr
fb []uint8
colorInfo *multiboot.FramebufferRGBColorInfo
// Console dimensions in pixels
width uint32
@ -43,13 +45,15 @@ type VesaFbConsole struct {
clearChar uint16
}
func NewVesaFbConsole(width, height uint32, bpp uint8, pitch uint32, fbPhysAddr uintptr) *VesaFbConsole {
func NewVesaFbConsole(width, height uint32, bpp uint8, pitch uint32, colorInfo *multiboot.FramebufferRGBColorInfo, fbPhysAddr uintptr) *VesaFbConsole {
return &VesaFbConsole{
bpp: uint32(bpp),
fbPhysAddr: fbPhysAddr,
width: width,
height: height,
pitch: pitch,
bpp: uint32(bpp),
bytesPerPixel: uint32(bpp+1) >> 3,
fbPhysAddr: fbPhysAddr,
colorInfo: colorInfo,
width: width,
height: height,
pitch: pitch,
// light gray text on black background
defaultFg: 7,
defaultBg: 0,
@ -119,6 +123,8 @@ func (cons *VesaFbConsole) Fill(x, y, width, height uint32, _, bg uint8) {
switch cons.bpp {
case 8:
cons.fill8(pX, pY, pW, pH, bg)
case 24, 32:
cons.fill24(pX, pY, pW, pH, bg)
}
}
@ -132,6 +138,19 @@ func (cons *VesaFbConsole) fill8(pX, pY, pW, pH uint32, bg uint8) {
}
}
// fill24 implements a fill operation using a 24/32bpp framebuffer.
func (cons *VesaFbConsole) fill24(pX, pY, pW, pH uint32, bg uint8) {
comp := cons.packColor24(bg)
fbRowOffset := cons.fbOffset(pX, pY)
for ; pH > 0; pH, fbRowOffset = pH-1, fbRowOffset+cons.pitch {
for fbOffset := fbRowOffset; fbOffset < fbRowOffset+pW*cons.bytesPerPixel; fbOffset += cons.bytesPerPixel {
cons.fb[fbOffset] = comp[0]
cons.fb[fbOffset+1] = comp[1]
cons.fb[fbOffset+2] = comp[2]
}
}
}
// Scroll the console contents to the specified direction. The caller
// is responsible for updating (e.g. clear or replace) the contents of
// the region that was scrolled.
@ -171,10 +190,12 @@ func (cons *VesaFbConsole) Write(ch byte, fg, bg uint8, x, y uint32) {
switch cons.bpp {
case 8:
cons.write8(ch, fg, bg, pX, pY)
case 24, 32:
cons.write24(ch, fg, bg, pX, pY)
}
}
// write8 writes a charactero using an 8bpp framebuffer.
// write8 writes a character using an 8bpp framebuffer.
func (cons *VesaFbConsole) write8(glyphIndex, fg, bg uint8, pX, pY uint32) {
var (
fontOffset = uint32(glyphIndex) * cons.font.BytesPerRow * cons.font.GlyphHeight
@ -207,10 +228,104 @@ func (cons *VesaFbConsole) write8(glyphIndex, fg, bg uint8, pX, pY uint32) {
}
}
// write16 writes a character using a 15/162bpp framebuffer.
func (cons *VesaFbConsole) write16(glyphIndex, fg, bg uint8, pX, pY uint32) {
var (
fontOffset = uint32(glyphIndex) * cons.font.BytesPerRow * cons.font.GlyphHeight
fbRowOffset = cons.fbOffset(pX, pY)
fbOffset uint32
x, y uint32
mask uint8
fgComp = cons.packColor16(fg)
bgComp = cons.packColor16(bg)
)
for y = 0; y < cons.font.GlyphHeight; y, fbRowOffset, fontOffset = y+1, fbRowOffset+cons.pitch, fontOffset+1 {
fbOffset = fbRowOffset
fontRowData := cons.font.Data[fontOffset]
mask = 1 << 7
for x = 0; x < cons.font.GlyphWidth; x, fbOffset, mask = x+1, fbOffset+cons.bytesPerPixel, mask>>1 {
// If mask becomes zero while we are still in this loop
// then the font uses > 1 byte per row. We need to
// fetch the next byte and reset the mask.
if mask == 0 {
fontOffset++
fontRowData = cons.font.Data[fontOffset]
mask = 1 << 7
}
if (fontRowData & mask) != 0 {
cons.fb[fbOffset] = fgComp[0]
cons.fb[fbOffset+1] = fgComp[1]
} else {
cons.fb[fbOffset] = bgComp[0]
cons.fb[fbOffset+1] = bgComp[1]
}
}
}
}
// write24 writes a character using a 24/32bpp framebuffer.
func (cons *VesaFbConsole) write24(glyphIndex, fg, bg uint8, pX, pY uint32) {
var (
fontOffset = uint32(glyphIndex) * cons.font.BytesPerRow * cons.font.GlyphHeight
fbRowOffset = cons.fbOffset(pX, pY)
fbOffset uint32
x, y uint32
mask uint8
fgComp = cons.packColor24(fg)
bgComp = cons.packColor24(bg)
)
for y = 0; y < cons.font.GlyphHeight; y, fbRowOffset, fontOffset = y+1, fbRowOffset+cons.pitch, fontOffset+1 {
fbOffset = fbRowOffset
fontRowData := cons.font.Data[fontOffset]
mask = 1 << 7
for x = 0; x < cons.font.GlyphWidth; x, fbOffset, mask = x+1, fbOffset+cons.bytesPerPixel, mask>>1 {
// If mask becomes zero while we are still in this loop
// then the font uses > 1 byte per row. We need to
// fetch the next byte and reset the mask.
if mask == 0 {
fontOffset++
fontRowData = cons.font.Data[fontOffset]
mask = 1 << 7
}
if (fontRowData & mask) != 0 {
cons.fb[fbOffset] = fgComp[0]
cons.fb[fbOffset+1] = fgComp[1]
cons.fb[fbOffset+2] = fgComp[2]
} else {
cons.fb[fbOffset] = bgComp[0]
cons.fb[fbOffset+1] = bgComp[1]
cons.fb[fbOffset+2] = bgComp[2]
}
}
}
}
// fbOffset returns the linear offset into the framebuffer that corresponds to
// the pixel at (x,y).
func (cons *VesaFbConsole) fbOffset(x, y uint32) uint32 {
return ((y + cons.offsetY) * cons.pitch) + (x * cons.bpp >> 3)
return ((y + cons.offsetY) * cons.pitch) + (x * cons.bytesPerPixel)
}
// packColor24 encodes a palette color into the pixel format required by a
// 24/32 bpp framebuffer.
func (cons *VesaFbConsole) packColor24(colorIndex uint8) [3]uint8 {
var (
c = cons.palette[colorIndex].(color.RGBA)
packed uint32 = 0 |
(uint32(c.R>>(8-cons.colorInfo.RedMaskSize)) << cons.colorInfo.RedPosition) |
(uint32(c.G>>(8-cons.colorInfo.GreenMaskSize)) << cons.colorInfo.GreenPosition) |
(uint32(c.B>>(8-cons.colorInfo.BlueMaskSize)) << cons.colorInfo.BluePosition)
)
return [3]uint8{
uint8(packed),
uint8(packed >> 8),
uint8(packed >> 16),
}
}
// Palette returns the active color palette for this console.
@ -222,19 +337,49 @@ func (cons *VesaFbConsole) Palette() color.Palette {
// palette index. Passing a color index greated than the number of
// supported colors should be a no-op.
func (cons *VesaFbConsole) SetPaletteColor(index uint8, rgba color.RGBA) {
cons.palette[index] = rgba
oldColor := cons.palette[index]
// Only program the DAC when we are in indexed (8bpp) mode
if cons.bpp > 8 {
if oldColor != nil && oldColor.(color.RGBA) == rgba {
return
}
// Load palette entry to the DAC. Each DAC entry is a 6-bit value so
// we need to scale the RGB values in the [0-63] range.
portWriteByteFn(0x3c8, index)
portWriteByteFn(0x3c9, rgba.R>>2)
portWriteByteFn(0x3c9, rgba.G>>2)
portWriteByteFn(0x3c9, rgba.B>>2)
cons.palette[index] = rgba
switch cons.bpp {
case 8:
// Load palette entry to the DAC. Each DAC entry is a 6-bit value so
// we need to scale the RGB values in the [0-63] range.
portWriteByteFn(0x3c8, index)
portWriteByteFn(0x3c9, rgba.R>>2)
portWriteByteFn(0x3c9, rgba.G>>2)
portWriteByteFn(0x3c9, rgba.B>>2)
case 24, 32:
if oldColor == nil {
return
}
cons.replace24(oldColor.(color.RGBA), rgba)
}
}
// replace24 replaces all srcColor values with dstColor using a 24/32bpp
// framebuffer.
func (cons *VesaFbConsole) replace24(src, dst color.RGBA) {
tmp := cons.palette[0]
cons.palette[0] = src
srcComp := cons.packColor24(0)
cons.palette[0] = dst
dstComp := cons.packColor24(0)
cons.palette[0] = tmp
for fbOffset := uint32(0); fbOffset < uint32(len(cons.fb)); fbOffset += cons.bytesPerPixel {
if cons.fb[fbOffset] == srcComp[0] &&
cons.fb[fbOffset+1] == srcComp[1] &&
cons.fb[fbOffset+2] == srcComp[2] {
cons.fb[fbOffset] = dstComp[0]
cons.fb[fbOffset+1] = dstComp[1]
cons.fb[fbOffset+2] = dstComp[2]
}
}
}
// loadDefaultPalette is called during driver initialization to setup the
@ -262,7 +407,7 @@ func (cons *VesaFbConsole) loadDefaultPalette() {
color.RGBA{R: 255, G: 255, B: 255}, /* white */
}
// Load default EFA palette for colors 0-16
// Load default EGA palette for colors 0-16
var index int
for ; index < len(egaPalette); index++ {
cons.SetPaletteColor(uint8(index), egaPalette[index])
@ -305,6 +450,7 @@ func (cons *VesaFbConsole) DriverInit(w io.Writer) *kernel.Error {
}))
kfmt.Fprintf(w, "mapped framebuffer to 0x%x\n", fbPage.Address())
kfmt.Fprintf(w, "framebuffer dimensions: %dx%dx%d\n", cons.width, cons.height, cons.bpp)
cons.loadDefaultPalette()
@ -316,8 +462,13 @@ func probeForVesaFbConsole() device.Driver {
var drv device.Driver
fbInfo := getFramebufferInfoFn()
if fbInfo.Type == multiboot.FramebufferTypeIndexed {
drv = NewVesaFbConsole(fbInfo.Width, fbInfo.Height, fbInfo.Bpp, fbInfo.Pitch, uintptr(fbInfo.PhysAddr))
if fbInfo.Type == multiboot.FramebufferTypeIndexed || fbInfo.Type == multiboot.FramebufferTypeRGB {
drv = NewVesaFbConsole(
fbInfo.Width, fbInfo.Height,
fbInfo.Bpp, fbInfo.Pitch,
fbInfo.RGBColorInfo(),
uintptr(fbInfo.PhysAddr),
)
}
return drv

View File

@ -18,7 +18,7 @@ import (
)
func TestVesaFbTextDimensions(t *testing.T) {
var cons Device = NewVesaFbConsole(16, 32, 8, 16, 0)
var cons Device = NewVesaFbConsole(16, 32, 8, 16, nil, 0)
if w, h := cons.Dimensions(Characters); w != 0 || h != 0 {
t.Fatalf("expected console dimensions to be 0x0 before setting a font; got %dx%d", w, h)
@ -54,7 +54,7 @@ func TestVesaFbTextDimensions(t *testing.T) {
}
func TestVesaFbDefaultColors(t *testing.T) {
var cons Device = NewVesaFbConsole(16, 32, 8, 16, 0)
var cons Device = NewVesaFbConsole(16, 32, 8, 16, nil, 0)
if fg, bg := cons.DefaultColors(); fg != 7 || bg != 0 {
t.Fatalf("expected console default colors to be fg:7, bg:0; got fg:%d, bg: %d", fg, bg)
}
@ -124,7 +124,7 @@ func TestVesaFbWrite8bpp(t *testing.T) {
for specIndex, spec := range specs {
fb := make([]uint8, spec.consW*spec.consH)
cons := NewVesaFbConsole(spec.consW, spec.consH, 8, spec.consW, 0)
cons := NewVesaFbConsole(spec.consW, spec.consH, 8, spec.consW, nil, 0)
cons.fb = fb
cons.offsetY = spec.offsetY
cons.SetFont(spec.font)
@ -148,7 +148,107 @@ func TestVesaFbWrite8bpp(t *testing.T) {
}
}
func TestVesaFbScroll8bpp(t *testing.T) {
func TestVesaFbWrite24bpp(t *testing.T) {
specs := []struct {
consW, consH, offsetY uint32
font *font.Font
expFb []byte
}{
{
16, 16, 6,
mockFont8x10,
[]byte("" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000321000000000000" +
"000000000000000000000000000000321321321000000000" +
"000000000000000000000000000321321000321321000000" +
"000000000000000000000000321321000000000321321000" +
"000000000000000000000000321321000000000321321000" +
"000000000000000000000000321321321321321321321000" +
"000000000000000000000000321321000000000321321000" +
"000000000000000000000000321321000000000321321000" +
"000000000000000000000000321321000000000321321000" +
"000000000000000000000000321321000000000321321000",
),
},
{
20, 20, 3,
mockFont10x14,
[]byte("" +
"000000000000000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000321000000000000" +
"000000000000000000000000000000000000000000000321000000000000" +
"000000000000000000000000000000000000000000321321321000000000" +
"000000000000000000000000000000000000000000321321321000000000" +
"000000000000000000000000000000000000000321321000321321000000" +
"000000000000000000000000000000000000000321321000321321000000" +
"000000000000000000000000000000000000000321321000000321321000" +
"000000000000000000000000000000000000321321000000000321321000" +
"000000000000000000000000000000000000321321321321321321321000" +
"000000000000000000000000000000000000321321000000000321321000" +
"000000000000000000000000000000000321321000000000000321321000" +
"000000000000000000000000000000000321321000000000000000321321" +
"000000000000000000000000000000000321321000000000000000321321" +
"000000000000000000000000000000321321321321000000000321321321" +
"000000000000000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000000000000000",
),
},
}
var (
// BGR
colorInfo = &multiboot.FramebufferRGBColorInfo{
RedPosition: 16,
RedMaskSize: 8,
GreenPosition: 8,
GreenMaskSize: 8,
BluePosition: 0,
BlueMaskSize: 8,
}
fg = uint8(1)
fgColor = color.RGBA{R: 1, G: 2, B: 3}
bg = uint8(0)
)
for specIndex, spec := range specs {
fb := make([]uint8, spec.consW*spec.consH*3)
cons := NewVesaFbConsole(spec.consW, spec.consH, 24, spec.consW*3, colorInfo, 0)
cons.fb = fb
cons.offsetY = spec.offsetY
cons.SetFont(spec.font)
cons.loadDefaultPalette()
cons.SetPaletteColor(fg, fgColor)
// ASCII 0 maps to the a blank character in the mock font
// ASCII 1 maps to the letter 'A' in the mock font
cons.Write(0, fg, bg, 0, 0)
cons.Write(1, fg, bg, 2, 1)
// Convert expected contents from ASCII to byte
for i := 0; i < len(spec.expFb); i++ {
spec.expFb[i] -= '0'
}
if !reflect.DeepEqual(spec.expFb, fb) {
t.Errorf("[spec %d] unexpected frame buffer contents:\n%s",
specIndex,
diffFrameBuffer(spec.consW, spec.consH, spec.consW*3, spec.expFb, fb),
)
}
}
}
func TestVesaFbScroll(t *testing.T) {
var (
consW, consH uint32 = 16, 16
offsetY uint32 = 3
@ -325,7 +425,7 @@ func TestVesaFbScroll8bpp(t *testing.T) {
fb := make([]uint8, consW*consH)
copy(fb, origFb)
cons := NewVesaFbConsole(consW, consH, 8, consW, 0)
cons := NewVesaFbConsole(consW, consH, 8, consW, nil, 0)
cons.fb = fb
cons.offsetY = offsetY
@ -355,7 +455,7 @@ func TestVesaFbScroll8bpp(t *testing.T) {
}
}
func TestVesFbFill8(t *testing.T) {
func TestVesaFbFill8(t *testing.T) {
var (
consW, consH uint32 = 16, 26
bg uint8 = 1
@ -538,7 +638,7 @@ func TestVesFbFill8(t *testing.T) {
fb := make([]uint8, consW*consH)
copy(fb, origFb)
cons := NewVesaFbConsole(consW, consH, 8, consW, 0)
cons := NewVesaFbConsole(consW, consH, 8, consW, nil, 0)
cons.fb = fb
cons.offsetY = spec.offsetY
@ -564,6 +664,227 @@ func TestVesFbFill8(t *testing.T) {
}
}
func TestVesaFbFill24(t *testing.T) {
var (
consW, consH uint32 = 16, 26
// BGR
colorInfo = &multiboot.FramebufferRGBColorInfo{
RedPosition: 16,
RedMaskSize: 8,
GreenPosition: 8,
GreenMaskSize: 8,
BluePosition: 0,
BlueMaskSize: 8,
}
bg uint8 = 1
bgColor = color.RGBA{R: 1, G: 2, B: 3}
origFb = []byte("" +
"666666666666666666666666666666666666666666666666" + // }
"777777777777777777777777777777777777777777777777" + // }- reserved rows
"888888888888888888888888888888888888888888888888" + // }
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000",
)
)
specs := []struct {
// Input rect in characters
x, y, w, h uint32
offsetY uint32
expFb []byte
}{
{
0, 0, 1, 1,
0,
[]byte("" +
"321321321321321321321321666666666666666666666666" + // }
"321321321321321321321321777777777777777777777777" + // }- reserved rows
"321321321321321321321321888888888888888888888888" + // }
"321321321321321321321321000000000000000000000000" +
"321321321321321321321321000000000000000000000000" +
"321321321321321321321321000000000000000000000000" +
"321321321321321321321321000000000000000000000000" +
"321321321321321321321321000000000000000000000000" +
"321321321321321321321321000000000000000000000000" +
"321321321321321321321321000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000",
),
},
{
2, 0, 10, 1,
3,
[]byte("" +
"666666666666666666666666666666666666666666666666" + // }
"777777777777777777777777777777777777777777777777" + // }- reserved rows
"888888888888888888888888888888888888888888888888" + // }
"000000000000000000000000321321321321321321321321" +
"000000000000000000000000321321321321321321321321" +
"000000000000000000000000321321321321321321321321" +
"000000000000000000000000321321321321321321321321" +
"000000000000000000000000321321321321321321321321" +
"000000000000000000000000321321321321321321321321" +
"000000000000000000000000321321321321321321321321" +
"000000000000000000000000321321321321321321321321" +
"000000000000000000000000321321321321321321321321" +
"000000000000000000000000321321321321321321321321" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000",
),
},
{
0, 0, 100, 100,
3,
[]byte("" +
"666666666666666666666666666666666666666666666666" + // }
"777777777777777777777777777777777777777777777777" + // }- reserved rows
"888888888888888888888888888888888888888888888888" + // }
"321321321321321321321321321321321321321321321321" +
"321321321321321321321321321321321321321321321321" +
"321321321321321321321321321321321321321321321321" +
"321321321321321321321321321321321321321321321321" +
"321321321321321321321321321321321321321321321321" +
"321321321321321321321321321321321321321321321321" +
"321321321321321321321321321321321321321321321321" +
"321321321321321321321321321321321321321321321321" +
"321321321321321321321321321321321321321321321321" +
"321321321321321321321321321321321321321321321321" +
"321321321321321321321321321321321321321321321321" +
"321321321321321321321321321321321321321321321321" +
"321321321321321321321321321321321321321321321321" +
"321321321321321321321321321321321321321321321321" +
"321321321321321321321321321321321321321321321321" +
"321321321321321321321321321321321321321321321321" +
"321321321321321321321321321321321321321321321321" +
"321321321321321321321321321321321321321321321321" +
"321321321321321321321321321321321321321321321321" +
"321321321321321321321321321321321321321321321321" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000",
),
},
{
100, 100, 1, 1,
6,
[]byte("" +
"666666666666666666666666666666666666666666666666" + // }
"777777777777777777777777777777777777777777777777" + // }- reserved rows
"888888888888888888888888888888888888888888888888" + // }
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000000000" +
"000000000000000000000000321321321321321321321321" +
"000000000000000000000000321321321321321321321321" +
"000000000000000000000000321321321321321321321321" +
"000000000000000000000000321321321321321321321321" +
"000000000000000000000000321321321321321321321321" +
"000000000000000000000000321321321321321321321321" +
"000000000000000000000000321321321321321321321321" +
"000000000000000000000000321321321321321321321321" +
"000000000000000000000000321321321321321321321321" +
"000000000000000000000000321321321321321321321321",
),
},
}
// Convert original fb contents from ASCII to byte
for i := 0; i < len(origFb); i++ {
origFb[i] -= '0'
}
for specIndex, spec := range specs {
// Convert expected contents from ASCII to byte
for i := 0; i < len(spec.expFb); i++ {
spec.expFb[i] -= '0'
}
fb := make([]uint8, consW*consH*3)
copy(fb, origFb)
cons := NewVesaFbConsole(consW, consH, 24, consW*3, colorInfo, 0)
cons.fb = fb
cons.offsetY = spec.offsetY
cons.loadDefaultPalette()
cons.SetPaletteColor(bg, bgColor)
// Calling fill before selecting a font should be a no-op
cons.Fill(spec.x, spec.y, spec.w, spec.h, 0, bg)
if !reflect.DeepEqual(origFb, fb) {
t.Errorf("[spec %d] unexpected frame buffer contents:\n%s",
specIndex,
diffFrameBuffer(consW, consH, consW*3, origFb, fb),
)
}
cons.SetFont(mockFont8x10)
cons.Fill(spec.x, spec.y, spec.w, spec.h, 0, bg)
if !reflect.DeepEqual(spec.expFb, fb) {
t.Errorf("[spec %d] unexpected frame buffer contents:\n%s",
specIndex,
diffFrameBuffer(consW, consH, consW*3, spec.expFb, fb),
)
}
}
}
func TestVesaFbPalette(t *testing.T) {
defer func() {
portWriteByteFn = cpu.PortWriteByte
@ -594,10 +915,12 @@ func TestVesaFbPalette(t *testing.T) {
}
var (
dacIndex uint8
compIndex uint8
dacIndex uint8
compIndex uint8
portWriteCount int
)
portWriteByteFn = func(port uint16, val uint8) {
portWriteCount++
switch port {
case 0x3c8:
dacIndex = val
@ -623,13 +946,20 @@ func TestVesaFbPalette(t *testing.T) {
}
}
cons := NewVesaFbConsole(0, 0, 8, 0, 0)
cons := NewVesaFbConsole(0, 0, 8, 0, nil, 0)
cons.loadDefaultPalette()
customColor := color.RGBA{R: 251, G: 252, B: 253}
expPal[255] = customColor
cons.SetPaletteColor(255, customColor)
// Setting the same RGB value should be a no-op
cons.SetPaletteColor(255, customColor)
if exp := 257 * 4; portWriteCount != exp {
t.Errorf("expected %d calls to cpu.portWriteByte; got %d", exp, portWriteCount)
}
got := cons.Palette()
for index, exp := range expPal {
if got[index] != exp {
@ -638,12 +968,96 @@ func TestVesaFbPalette(t *testing.T) {
}
}
func TestVesaFbReplace24(t *testing.T) {
var (
consW, consH uint32 = 4, 4
// BGR
colorInfo = &multiboot.FramebufferRGBColorInfo{
RedPosition: 16,
RedMaskSize: 8,
GreenPosition: 8,
GreenMaskSize: 8,
BluePosition: 0,
BlueMaskSize: 8,
}
)
specs := []struct {
bpp uint8
inpFb []byte
expFb []byte
}{
{
24,
[]byte("" +
"000100010002" +
"000100010002" +
"000100010002" +
"000100010002",
),
[]byte("" +
"765100010002" +
"765100010002" +
"765100010002" +
"765100010002",
),
},
{
32,
[]byte("" +
"0000100001000020" +
"0000100001000020" +
"0000100001000020" +
"0000100001000020",
),
[]byte("" +
"7650100001000020" +
"7650100001000020" +
"7650100001000020" +
"7650100001000020",
),
},
}
for specIndex, spec := range specs {
// Convert spec fb contents from ASCII to byte
for i := 0; i < len(spec.expFb); i++ {
spec.inpFb[i] -= '0'
spec.expFb[i] -= '0'
}
fb := make([]uint8, consW*consH*uint32(spec.bpp)>>3)
copy(fb, spec.inpFb)
cons := NewVesaFbConsole(consW, consH, spec.bpp, consW*uint32(spec.bpp)>>3, colorInfo, 0)
cons.fb = fb
cons.palette = make(color.Palette, 1)
// First color update should not trigger a replace as the color is not used yet
cons.SetPaletteColor(0, color.RGBA{})
if !reflect.DeepEqual(spec.inpFb, fb) {
t.Errorf("[spec %d] unexpected frame buffer contents:\n%s",
specIndex,
diffFrameBuffer(consW, consH, cons.pitch, spec.expFb, fb),
)
}
// Second color update should replace existing pixels with the new RGB value
cons.SetPaletteColor(0, color.RGBA{R: 5, G: 6, B: 7})
if !reflect.DeepEqual(spec.expFb, fb) {
t.Errorf("[spec %d] unexpected frame buffer contents:\n%s",
specIndex,
diffFrameBuffer(consW, consH, cons.pitch, spec.expFb, fb),
)
}
}
}
func TestVesaFbDriverInterface(t *testing.T) {
defer func() {
mapRegionFn = vmm.MapRegion
portWriteByteFn = cpu.PortWriteByte
}()
var dev device.Driver = NewVesaFbConsole(320, 200, 8, 320, uintptr(0xa0000))
var dev device.Driver = NewVesaFbConsole(320, 200, 8, 320, nil, uintptr(0xa0000))
if dev.DriverName() == "" {
t.Fatal("DriverName() returned an empty string")
@ -698,6 +1112,53 @@ func TestVesaFbProbe(t *testing.T) {
}
}
func TestVesaFbPackColor24(t *testing.T) {
specs := []struct {
colorInfo *multiboot.FramebufferRGBColorInfo
input color.RGBA
exp [3]uint8
}{
{
// RGB
&multiboot.FramebufferRGBColorInfo{
RedPosition: 0,
RedMaskSize: 8,
GreenPosition: 8,
GreenMaskSize: 8,
BluePosition: 16,
BlueMaskSize: 8,
},
color.RGBA{R: 100, G: 200, B: 255},
[3]uint8{100, 200, 255},
},
{
// BGR
&multiboot.FramebufferRGBColorInfo{
RedPosition: 16,
RedMaskSize: 8,
GreenPosition: 8,
GreenMaskSize: 8,
BluePosition: 0,
BlueMaskSize: 8,
},
color.RGBA{R: 250, G: 200, B: 120},
[3]uint8{120, 200, 250},
},
}
cons := NewVesaFbConsole(0, 0, 32, 0, nil, 0)
cons.palette = make(color.Palette, 256)
for specIndex, spec := range specs {
cons.colorInfo = spec.colorInfo
cons.palette[0] = spec.input
if got := cons.packColor24(0); got != spec.exp {
t.Errorf("[spec %d] expected: %v; got %v", specIndex, spec.exp, got)
}
}
}
func dumpFramebuffer(consW, consH, consPitch uint32, fb []byte) string {
var buf bytes.Buffer