Files
izapple2/screen/ntscFilter.go
Iván Izaguirre 4e0cf9167c Four screen modes, phosphor and scan lines apart
The screen modes mixed two independent things. Green, plain and NTSC read
as three points on one scale, but plain and NTSC are the same colour
rendering and differ in whether the gaps between the scan lines are drawn,
while green is a phosphor that happened to always get them.

They are now the four combinations of the two: ScreenModeGreen,
ScreenModeColor, ScreenModeGreenScanlines and ScreenModeColorScanlines,
built from a bit for each property. isColor and hasScanlines read them, so
each place says what it means, and NextScreenMode rotates through the four.

The old modes map onto the new ones exactly, green to green with scan lines,
plain to color, NTSC to color with scan lines, and the frontends are moved
over so nothing they show changes. The scenarios of the tests now cover all
four, and their images are renamed after the modes. The three that already
existed render byte for byte as they did.
2026-08-07 08:53:22 +02:00

126 lines
3.1 KiB
Go

package screen
import (
"fmt"
"image"
"image/color"
)
/*
See:
https://mrob.com/pub/xapple2/colors.html
https://archive.org/details/IIgs_2523063_Master_Color_Values
*/
var ntscColorMap = [16]color.Color{
color.RGBA{0, 0, 0, 255}, // Black
color.RGBA{227, 30, 96, 255}, // Magenta
color.RGBA{96, 78, 189, 255}, // Dark Blue
color.RGBA{255, 68, 253, 255}, // Purple
color.RGBA{0, 163, 96, 255}, // Dark Green
color.RGBA{156, 156, 156, 255}, // Grey 1
color.RGBA{20, 207, 253, 255}, // Medium Blue
color.RGBA{208, 195, 255, 255}, // Light Blue
color.RGBA{96, 114, 3, 255}, // Brown
color.RGBA{255, 106, 60, 255}, // Orange
color.RGBA{156, 156, 156, 255}, // Grey 2
color.RGBA{255, 160, 208, 255}, // Pink
color.RGBA{20, 245, 60, 255}, // Green
color.RGBA{208, 221, 141, 255}, // Yellow
color.RGBA{114, 255, 208, 255}, // Aquamarine
color.RGBA{255, 255, 255, 255}, // White
}
var attenuatedColorMap = buildAttenuatedColorMap(ntscColorMap)
func buildAttenuatedColorMap(colorMap [16]color.Color) [16]color.Color {
colors := [16]color.Color{}
for i := range len(colorMap) {
r, g, b, _ := colorMap[i].RGBA()
colors[i] = color.RGBA64{
uint16(4 * r / 5), uint16(4 * g / 5), uint16(4 * b / 5),
65535,
}
}
return colors
}
/*
var rgbColorMap = [16]color.Color{
color.RGBA{0, 0, 0, 255}, // Black
color.RGBA{221, 0, 51, 255}, // Magenta
color.RGBA{0, 0, 153, 255}, // Dark Blue
color.RGBA{221, 34, 221, 255}, // Purple
color.RGBA{0, 119, 34, 255}, // Dark Green
color.RGBA{85, 85, 85, 255}, // Grey 1
color.RGBA{34, 34, 255, 255}, // Medium Blue
color.RGBA{102, 170, 255, 255}, // Light Blue
color.RGBA{136, 85, 0, 255}, // Brown
color.RGBA{255, 102, 0, 255}, // Orange
color.RGBA{170, 170, 170, 255}, // Grey 2
color.RGBA{255, 153, 136, 255}, // Pink
color.RGBA{17, 221, 0, 255}, // Green
color.RGBA{255, 255, 0, 255}, // Yellow
color.RGBA{68, 255, 153, 255}, // Aquamarine
color.RGBA{255, 255, 255, 255}, // White
}
*/
func filterNTSCColor(in *image.RGBA, mask *image.Alpha, screenMode int) *image.RGBA {
colorMap := ntscColorMap // or rgbColorMap
colorMapLow := ntscColorMap
if hasScanlines(screenMode) {
colorMapLow = attenuatedColorMap
}
b := in.Bounds()
width := b.Dx()
height := b.Dy()
size := image.Rect(0, 0, width+4, height)
out := image.NewRGBA(size)
if width < 2*hiResWidth {
panic(fmt.Sprintf("The image has width %v. We can't apply the NTSC filter.", width))
}
for y := range height {
// We store the last four bits. We start with 0000
v := 0
for x := range width {
cIn := in.At(x, y)
r, _, _, _ := cIn.RGBA()
pos := 1 << uint(x%4)
if r != 0 {
v |= pos
} else {
v &^= pos
}
var cOut color.Color
if r != 0 {
cOut = colorMap[v]
} else {
cOut = colorMapLow[v]
}
if mask != nil {
// RGB mode7
_, _, _, a := mask.At(x, y).RGBA()
if a > 0 {
cOut = cIn
}
}
out.Set(x, y, cOut)
}
// We fade for the last three positions
for x := width; x < width+4; x++ {
v >>= 1
cOut := colorMap[v]
out.Set(x, y, cOut)
}
}
return out
}