diff --git a/src/gopheros/device/video/console/vesa_fb.go b/src/gopheros/device/video/console/vesa_fb.go index 1552fe5..ec355cd 100644 --- a/src/gopheros/device/video/console/vesa_fb.go +++ b/src/gopheros/device/video/console/vesa_fb.go @@ -3,6 +3,7 @@ package console import ( "gopheros/device" "gopheros/device/video/console/font" + "gopheros/device/video/console/logo" "gopheros/kernel" "gopheros/kernel/hal/multiboot" "gopheros/kernel/kfmt" @@ -75,8 +76,63 @@ func (cons *VesaFbConsole) SetFont(f *font.Font) { } cons.font = f - cons.widthInChars = cons.width / uint32(f.GlyphWidth) - cons.heightInChars = (cons.height - cons.offsetY) / uint32(f.GlyphHeight) + cons.widthInChars = cons.width / f.GlyphWidth + cons.heightInChars = (cons.height - cons.offsetY) / f.GlyphHeight +} + +// SetLogo selects the logo to be displayed by the console. The logo colors will +// be remapped to the end of the console's palette and space equal to the logo +// height will be reserved at the top of the framebuffer for diplaying the logo. +// +// As setting a logo changes the available space for rendering text, SetLogo +// must be invoked before SetFont. +func (cons *VesaFbConsole) SetLogo(l *logo.Image) { + if l == nil { + return + } + + // Map the logo colors to the console palette replacing the transparent + // color index with the console default bg color + offset := uint8(len(cons.palette) - len(l.Palette)) + for i, rgba := range l.Palette { + if uint8(i) == l.TransparentIndex { + rgba = cons.palette[cons.defaultBg].(color.RGBA) + } + cons.setPaletteColor(uint8(i)+offset, rgba, false) + } + + // Draw the logo + var fbRowOffset uint32 + switch l.Align { + case logo.AlignLeft: + fbRowOffset = cons.fbOffset(0, 0) + case logo.AlignCenter: + fbRowOffset = cons.fbOffset((cons.width-l.Width)>>1, 0) + case logo.AlignRight: + fbRowOffset = cons.fbOffset(cons.width-l.Width, 0) + } + + for y, logoOffset := uint32(0), 0; y < l.Height; y, fbRowOffset = y+1, fbRowOffset+cons.pitch { + for x, fbOffset := uint32(0), fbRowOffset; x < l.Width; x, fbOffset, logoOffset = x+1, fbOffset+cons.bytesPerPixel, logoOffset+1 { + c := l.Data[logoOffset] + offset + + switch cons.bpp { + case 8: + cons.fb[fbOffset] = c + case 15, 16: + colorComp := cons.packColor16(c) + cons.fb[fbOffset] = colorComp[0] + cons.fb[fbOffset+1] = colorComp[1] + case 24, 32: + colorComp := cons.packColor24(c) + cons.fb[fbOffset] = colorComp[0] + cons.fb[fbOffset+1] = colorComp[1] + cons.fb[fbOffset+2] = colorComp[2] + } + } + } + + cons.offsetY = l.Height } // Dimensions returns the console width and height in the specified dimension. @@ -383,6 +439,14 @@ func (cons *VesaFbConsole) SetPaletteColor(index uint8, rgba color.RGBA) { return } + cons.setPaletteColor(index, rgba, true) +} + +// setPaletteColor updates the color definition for the specified +// palette index. If replace is true, then all occurrences of the old color +// in the framebuffer will be replaced by the new color value (if bpp > 8). +func (cons *VesaFbConsole) setPaletteColor(index uint8, rgba color.RGBA, replace bool) { + oldColor := cons.palette[index] cons.palette[index] = rgba switch cons.bpp { @@ -394,13 +458,13 @@ func (cons *VesaFbConsole) SetPaletteColor(index uint8, rgba color.RGBA) { portWriteByteFn(0x3c9, rgba.G>>2) portWriteByteFn(0x3c9, rgba.B>>2) case 15, 16: - if oldColor == nil { + if oldColor == nil || !replace { return } cons.replace16(oldColor.(color.RGBA), rgba) case 24, 32: - if oldColor == nil { + if oldColor == nil || !replace { return } @@ -417,7 +481,7 @@ func (cons *VesaFbConsole) replace16(src, dst color.RGBA) { cons.palette[0] = dst dstComp := cons.packColor16(0) cons.palette[0] = tmp - for fbOffset := uint32(0); fbOffset < uint32(len(cons.fb)); fbOffset += cons.bytesPerPixel { + for fbOffset := cons.fbOffset(0, 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] @@ -435,7 +499,7 @@ func (cons *VesaFbConsole) replace24(src, dst color.RGBA) { cons.palette[0] = dst dstComp := cons.packColor24(0) cons.palette[0] = tmp - for fbOffset := uint32(0); fbOffset < uint32(len(cons.fb)); fbOffset += cons.bytesPerPixel { + for fbOffset := cons.fbOffset(0, 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] { diff --git a/src/gopheros/device/video/console/vesa_fb_test.go b/src/gopheros/device/video/console/vesa_fb_test.go index d32af9c..1fc1c2a 100644 --- a/src/gopheros/device/video/console/vesa_fb_test.go +++ b/src/gopheros/device/video/console/vesa_fb_test.go @@ -5,6 +5,7 @@ import ( "fmt" "gopheros/device" "gopheros/device/video/console/font" + "gopheros/device/video/console/logo" "gopheros/kernel" "gopheros/kernel/cpu" "gopheros/kernel/hal/multiboot" @@ -1595,6 +1596,154 @@ func TestVesaFbPackColor24(t *testing.T) { } } +func TestVesaFbSetLogo(t *testing.T) { + defer func() { + portWriteByteFn = cpu.PortWriteByte + }() + + var ( + consW uint32 = 4 + consH uint32 = 2 + inpFb8 = []byte{ + 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, + } + inpFb16 = []byte{ + 0xaa, 0xab, 0xaa, 0xab, 0xaa, 0xab, 0xaa, 0xab, + 0xaa, 0xab, 0xaa, 0xab, 0xaa, 0xab, 0xaa, 0xab, + } + inpFb24 = []byte{ + 0xaa, 0xab, 0xac, 0xaa, 0xab, 0xac, 0xaa, 0xab, 0xac, 0xaa, 0xab, 0xac, + 0xaa, 0xab, 0xac, 0xaa, 0xab, 0xac, 0xaa, 0xab, 0xac, 0xaa, 0xab, 0xac, + } + mockLogo = &logo.Image{ + Width: 2, + Height: 2, + Align: logo.AlignLeft, + TransparentIndex: 0, + Palette: []color.RGBA{ + color.RGBA{R: 255, G: 0, B: 255}, + color.RGBA{R: 255, G: 0, B: 128}, + }, + Data: []byte{ + 0x0, 0x1, + 0x1, 0x0, + }, + } + ) + + specs := []struct { + inpFb []byte + bpp uint8 + colorInfo *multiboot.FramebufferRGBColorInfo + align logo.Alignment + logo *logo.Image + expFb []byte + }{ + { + inpFb8, + 8, + nil, + logo.AlignLeft, + mockLogo, + []byte{ + 0xfe, 0xff, 0xaa, 0xaa, + 0xff, 0xfe, 0xaa, 0xaa, + }, + }, + { + inpFb8, + 8, + nil, + logo.AlignCenter, + mockLogo, + []byte{ + 0xaa, 0xfe, 0xff, 0xaa, + 0xaa, 0xff, 0xfe, 0xaa, + }, + }, + { + inpFb8, + 8, + nil, + logo.AlignRight, + mockLogo, + []byte{ + 0xaa, 0xaa, 0xfe, 0xff, + 0xaa, 0xaa, 0xff, 0xfe, + }, + }, + { + inpFb16, + 16, + // RGB555 + &multiboot.FramebufferRGBColorInfo{ + RedPosition: 10, + RedMaskSize: 5, + GreenPosition: 5, + GreenMaskSize: 5, + BluePosition: 0, + BlueMaskSize: 5, + }, + logo.AlignLeft, + mockLogo, + []byte{ + 0x00, 0x00, 0x10, 0x7c, 0xaa, 0xab, 0xaa, 0xab, + 0x10, 0x7c, 0x00, 0x00, 0xaa, 0xab, 0xaa, 0xab, + }, + }, + { + inpFb24, + 24, + // RGB + &multiboot.FramebufferRGBColorInfo{ + RedPosition: 0, + RedMaskSize: 8, + GreenPosition: 8, + GreenMaskSize: 8, + BluePosition: 16, + BlueMaskSize: 8, + }, + logo.AlignRight, + mockLogo, + []byte{ + 0xaa, 0xab, 0xac, 0xaa, 0xab, 0xac, 0x00, 0x00, 0x00, 0xff, 0x00, 0x80, + 0xaa, 0xab, 0xac, 0xaa, 0xab, 0xac, 0xff, 0x00, 0x80, 0x00, 0x00, 0x00, + }, + }, + } + + var () + for specIndex, spec := range specs { + portWriteByteFn = func(port uint16, val uint8) {} + + cons := NewVesaFbConsole(consW, consH, spec.bpp, consW*uint32(spec.bpp>>3), spec.colorInfo, 0) + cons.fb = make([]byte, len(spec.inpFb)) + copy(cons.fb, spec.inpFb) + cons.palette = make(color.Palette, 256) + cons.loadDefaultPalette() + + // Setting a nil logo should be a no-op + cons.SetLogo(nil) + if !reflect.DeepEqual(spec.inpFb, cons.fb) { + t.Errorf("[spec %d] unexpected frame buffer contents:\n%s", + specIndex, + diffFrameBuffer(consW, consH, cons.pitch, spec.expFb, cons.fb), + ) + } + + mockLogo.Align = spec.align + cons.SetLogo(mockLogo) + + if !reflect.DeepEqual(spec.expFb, cons.fb) { + t.Errorf("[spec %d] unexpected frame buffer contents:\n%s", + specIndex, + diffFrameBuffer(consW, consH, cons.pitch, spec.expFb, cons.fb), + ) + } + } +} + func dumpFramebuffer(consW, consH, consPitch uint32, fb []byte) string { var buf bytes.Buffer @@ -1602,7 +1751,7 @@ func dumpFramebuffer(consW, consH, consPitch uint32, fb []byte) string { fmt.Fprintf(&buf, "%04d |", y) index := (y * consPitch) for x := uint32(0); x < consPitch; x++ { - fmt.Fprintf(&buf, "%d", fb[index+x]) + fmt.Fprintf(&buf, "%d ", fb[index+x]) } fmt.Fprintln(&buf, "|") }