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

Extend vesa driver support to 15 and 16bpp framebuffers

This commit is contained in:
Achilleas Anagnostopoulos 2017-07-11 18:41:09 +01:00
parent f02c767257
commit f4f3745073
2 changed files with 493 additions and 0 deletions

View File

@ -123,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 15, 16:
cons.fill16(pX, pY, pW, pH, bg)
case 24, 32:
cons.fill24(pX, pY, pW, pH, bg)
}
@ -138,6 +140,18 @@ func (cons *VesaFbConsole) fill8(pX, pY, pW, pH uint32, bg uint8) {
}
}
// fill16 implements a fill operation using a 15/16bpp framebuffer.
func (cons *VesaFbConsole) fill16(pX, pY, pW, pH uint32, bg uint8) {
comp := cons.packColor16(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]
}
}
}
// 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)
@ -190,6 +204,8 @@ 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 15, 16:
cons.write16(ch, fg, bg, pX, pY)
case 24, 32:
cons.write24(ch, fg, bg, pX, pY)
}
@ -328,6 +344,23 @@ func (cons *VesaFbConsole) packColor24(colorIndex uint8) [3]uint8 {
}
}
// packColor16 encodes a palette color into the pixel format required by a
// 15/16 bpp framebuffer.
func (cons *VesaFbConsole) packColor16(colorIndex uint8) [2]uint8 {
var (
c = cons.palette[colorIndex].(color.RGBA)
packed uint16 = 0 |
(uint16(c.R>>(8-cons.colorInfo.RedMaskSize)) << cons.colorInfo.RedPosition) |
(uint16(c.G>>(8-cons.colorInfo.GreenMaskSize)) << cons.colorInfo.GreenPosition) |
(uint16(c.B>>(8-cons.colorInfo.BlueMaskSize)) << cons.colorInfo.BluePosition)
)
return [2]uint8{
uint8(packed),
uint8(packed >> 8),
}
}
// Palette returns the active color palette for this console.
func (cons *VesaFbConsole) Palette() color.Palette {
return cons.palette
@ -353,6 +386,12 @@ func (cons *VesaFbConsole) SetPaletteColor(index uint8, rgba color.RGBA) {
portWriteByteFn(0x3c9, rgba.R>>2)
portWriteByteFn(0x3c9, rgba.G>>2)
portWriteByteFn(0x3c9, rgba.B>>2)
case 15, 16:
if oldColor == nil {
return
}
cons.replace16(oldColor.(color.RGBA), rgba)
case 24, 32:
if oldColor == nil {
return
@ -362,6 +401,24 @@ func (cons *VesaFbConsole) SetPaletteColor(index uint8, rgba color.RGBA) {
}
}
// replace16 replaces all srcColor values with dstColor using a 15/16bpp
// framebuffer.
func (cons *VesaFbConsole) replace16(src, dst color.RGBA) {
tmp := cons.palette[0]
cons.palette[0] = src
srcComp := cons.packColor16(0)
cons.palette[0] = dst
dstComp := cons.packColor16(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] = dstComp[0]
cons.fb[fbOffset+1] = dstComp[1]
}
}
}
// replace24 replaces all srcColor values with dstColor using a 24/32bpp
// framebuffer.
func (cons *VesaFbConsole) replace24(src, dst color.RGBA) {

View File

@ -148,6 +148,106 @@ func TestVesaFbWrite8bpp(t *testing.T) {
}
}
func TestVesaFbWrite16bpp(t *testing.T) {
specs := []struct {
consW, consH, offsetY uint32
font *font.Font
expFb []byte
}{
{
16, 16, 6,
mockFont8x10,
[]byte("" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000001400000000" +
"00000000000000000000141414000000" +
"00000000000000000014140014140000" +
"00000000000000001414000000141400" +
"00000000000000001414000000141400" +
"00000000000000001414141414141400" +
"00000000000000001414000000141400" +
"00000000000000001414000000141400" +
"00000000000000001414000000141400" +
"00000000000000001414000000141400",
),
},
{
20, 20, 3,
mockFont10x14,
[]byte("" +
"0000000000000000000000000000000000000000" +
"0000000000000000000000000000000000000000" +
"0000000000000000000000000000000000000000" +
"0000000000000000000000000000001400000000" +
"0000000000000000000000000000001400000000" +
"0000000000000000000000000000141414000000" +
"0000000000000000000000000000141414000000" +
"0000000000000000000000000014140014140000" +
"0000000000000000000000000014140014140000" +
"0000000000000000000000000014140000141400" +
"0000000000000000000000001414000000141400" +
"0000000000000000000000001414141414141400" +
"0000000000000000000000001414000000141400" +
"0000000000000000000000141400000000141400" +
"0000000000000000000000141400000000001414" +
"0000000000000000000000141400000000001414" +
"0000000000000000000014141414000000141414" +
"0000000000000000000000000000000000000000" +
"0000000000000000000000000000000000000000" +
"0000000000000000000000000000000000000000",
),
},
}
var (
// RGB555
colorInfo = &multiboot.FramebufferRGBColorInfo{
RedPosition: 10,
RedMaskSize: 5,
GreenPosition: 5,
GreenMaskSize: 5,
BluePosition: 0,
BlueMaskSize: 5,
}
fg = uint8(1)
fgColor = color.RGBA{R: 10, G: 0, B: 12}
bg = uint8(0)
)
for specIndex, spec := range specs {
fb := make([]uint8, spec.consW*spec.consH*2)
cons := NewVesaFbConsole(spec.consW, spec.consH, 16, spec.consW*2, 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*2, spec.expFb, fb),
)
}
}
}
func TestVesaFbWrite24bpp(t *testing.T) {
specs := []struct {
consW, consH, offsetY uint32
@ -664,6 +764,227 @@ func TestVesaFbFill8(t *testing.T) {
}
}
func TestVesaFbFill16(t *testing.T) {
var (
consW, consH uint32 = 16, 26
// RGB555
colorInfo = &multiboot.FramebufferRGBColorInfo{
RedPosition: 10,
RedMaskSize: 5,
GreenPosition: 5,
GreenMaskSize: 5,
BluePosition: 0,
BlueMaskSize: 5,
}
bg uint8 = 1
bgColor = color.RGBA{R: 10, G: 0, B: 12}
origFb = []byte("" +
"66666666666666666666666666666666" + // }
"77777777777777777777777777777777" + // }- reserved rows
"88888888888888888888888888888888" + // }
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000",
)
)
specs := []struct {
// Input rect in characters
x, y, w, h uint32
offsetY uint32
expFb []byte
}{
{
0, 0, 1, 1,
0,
[]byte("" +
"14141414141414146666666666666666" + // }
"14141414141414147777777777777777" + // }- reserved rows
"14141414141414148888888888888888" + // }
"14141414141414140000000000000000" +
"14141414141414140000000000000000" +
"14141414141414140000000000000000" +
"14141414141414140000000000000000" +
"14141414141414140000000000000000" +
"14141414141414140000000000000000" +
"14141414141414140000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000",
),
},
{
2, 0, 10, 1,
3,
[]byte("" +
"66666666666666666666666666666666" + // }
"77777777777777777777777777777777" + // }- reserved rows
"88888888888888888888888888888888" + // }
"00000000000000001414141414141414" +
"00000000000000001414141414141414" +
"00000000000000001414141414141414" +
"00000000000000001414141414141414" +
"00000000000000001414141414141414" +
"00000000000000001414141414141414" +
"00000000000000001414141414141414" +
"00000000000000001414141414141414" +
"00000000000000001414141414141414" +
"00000000000000001414141414141414" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000",
),
},
{
0, 0, 100, 100,
3,
[]byte("" +
"66666666666666666666666666666666" + // }
"77777777777777777777777777777777" + // }- reserved rows
"88888888888888888888888888888888" + // }
"14141414141414141414141414141414" +
"14141414141414141414141414141414" +
"14141414141414141414141414141414" +
"14141414141414141414141414141414" +
"14141414141414141414141414141414" +
"14141414141414141414141414141414" +
"14141414141414141414141414141414" +
"14141414141414141414141414141414" +
"14141414141414141414141414141414" +
"14141414141414141414141414141414" +
"14141414141414141414141414141414" +
"14141414141414141414141414141414" +
"14141414141414141414141414141414" +
"14141414141414141414141414141414" +
"14141414141414141414141414141414" +
"14141414141414141414141414141414" +
"14141414141414141414141414141414" +
"14141414141414141414141414141414" +
"14141414141414141414141414141414" +
"14141414141414141414141414141414" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000",
),
},
{
100, 100, 1, 1,
6,
[]byte("" +
"66666666666666666666666666666666" + // }
"77777777777777777777777777777777" + // }- reserved rows
"88888888888888888888888888888888" + // }
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000000000000000000000" +
"00000000000000001414141414141414" +
"00000000000000001414141414141414" +
"00000000000000001414141414141414" +
"00000000000000001414141414141414" +
"00000000000000001414141414141414" +
"00000000000000001414141414141414" +
"00000000000000001414141414141414" +
"00000000000000001414141414141414" +
"00000000000000001414141414141414" +
"00000000000000001414141414141414",
),
},
}
// 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*2)
copy(fb, origFb)
cons := NewVesaFbConsole(consW, consH, 16, consW*2, 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*2, spec.expFb, fb),
)
}
}
}
func TestVesaFbFill24(t *testing.T) {
var (
consW, consH uint32 = 16, 26
@ -968,6 +1289,69 @@ func TestVesaFbPalette(t *testing.T) {
}
}
func TestVesaFbReplace16(t *testing.T) {
var (
consW, consH uint32 = 4, 4
// BGR
colorInfo = &multiboot.FramebufferRGBColorInfo{
RedPosition: 10,
RedMaskSize: 5,
GreenPosition: 5,
GreenMaskSize: 5,
BluePosition: 0,
BlueMaskSize: 5,
}
)
specs := []struct {
bpp uint8
inpFb []byte
expFb []byte
}{
{
24,
[]byte{
0x00, 0x00, 0x12, 0x00, 0x00, 0x34, 0x34, 0x12,
0x00, 0x00, 0x12, 0x00, 0x00, 0x34, 0x34, 0x12,
0x00, 0x00, 0x12, 0x00, 0x00, 0x34, 0x34, 0x12,
0x00, 0x00, 0x12, 0x00, 0x00, 0x34, 0x34, 0x12,
},
[]byte{
0x34, 0x12, 0x12, 0x00, 0x00, 0x34, 0x34, 0x12,
0x34, 0x12, 0x12, 0x00, 0x00, 0x34, 0x34, 0x12,
0x34, 0x12, 0x12, 0x00, 0x00, 0x34, 0x34, 0x12,
0x34, 0x12, 0x12, 0x00, 0x00, 0x34, 0x34, 0x12,
},
},
}
for specIndex, spec := range specs {
fb := make([]uint8, consW*consH*2)
copy(fb, spec.inpFb)
cons := NewVesaFbConsole(consW, consH, 16, consW*2, 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: 32, G: 136, B: 160})
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 TestVesaFbReplace24(t *testing.T) {
var (
consW, consH uint32 = 4, 4
@ -1112,6 +1496,58 @@ func TestVesaFbProbe(t *testing.T) {
}
}
func TestVesaFbPackColor16(t *testing.T) {
specs := []struct {
colorInfo *multiboot.FramebufferRGBColorInfo
input color.RGBA
exp uint16
}{
{
// RGB555
&multiboot.FramebufferRGBColorInfo{
RedPosition: 10,
RedMaskSize: 5,
GreenPosition: 5,
GreenMaskSize: 5,
BluePosition: 0,
BlueMaskSize: 5,
},
color.RGBA{R: 32, G: 136, B: 160},
0x1234,
},
{
// RGB565
&multiboot.FramebufferRGBColorInfo{
RedPosition: 11,
RedMaskSize: 5,
GreenPosition: 5,
GreenMaskSize: 6,
BluePosition: 0,
BlueMaskSize: 5,
},
color.RGBA{R: 250, G: 200, B: 120},
(250>>3)<<11 | (200>>2)<<5 | (120 >> 3),
},
}
cons := NewVesaFbConsole(0, 0, 16, 0, nil, 0)
cons.palette = make(color.Palette, 256)
for specIndex, spec := range specs {
cons.colorInfo = spec.colorInfo
cons.palette[0] = spec.input
exp := [2]uint8{
uint8(spec.exp),
uint8(spec.exp >> 8),
}
if got := cons.packColor16(0); got != exp {
t.Errorf("[spec %d] expected: %v; got %v", specIndex, exp, got)
}
}
}
func TestVesaFbPackColor24(t *testing.T) {
specs := []struct {
colorInfo *multiboot.FramebufferRGBColorInfo