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:
parent
13ba4bbbed
commit
f02c767257
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user