add luminance cutoff for B&W mode

This commit is contained in:
Jorj Bauer 2021-01-19 18:40:13 -05:00
parent efc36d40a8
commit 97059a0a5b
10 changed files with 144 additions and 47 deletions

View File

@ -60,20 +60,22 @@ enum {
ACT_REBOOTANDEJECT = 4,
ACT_MONITOR = 5,
ACT_DISPLAYTYPE = 6,
ACT_DEBUG = 7,
ACT_DISK1 = 8,
ACT_DISK2 = 9,
ACT_HD1 = 10,
ACT_HD2 = 11,
ACT_VOLPLUS = 12,
ACT_VOLMINUS = 13,
ACT_SUSPEND = 14,
ACT_RESTORE = 15,
ACT_PADX_INV = 16,
ACT_PADY_INV = 17,
ACT_PADDLES = 18,
ACT_SPEED = 19,
ACT_ABOUT = 20,
ACT_LUMINANCEUP = 7,
ACT_LUMINANCEDOWN = 8,
ACT_DEBUG = 9,
ACT_DISK1 = 10,
ACT_DISK2 = 11,
ACT_HD1 = 12,
ACT_HD2 = 13,
ACT_VOLPLUS = 14,
ACT_VOLMINUS = 15,
ACT_SUSPEND = 16,
ACT_RESTORE = 17,
ACT_PADX_INV = 18,
ACT_PADY_INV = 19,
ACT_PADDLES = 20,
ACT_SPEED = 21,
ACT_ABOUT = 22,
};
#define NUM_TITLES 4
@ -85,7 +87,8 @@ const uint8_t aiieActions[] = { ACT_ABOUT };
const uint8_t vmActions[] = { ACT_EXIT, ACT_RESET, ACT_REBOOT, ACT_REBOOTANDEJECT,
ACT_MONITOR,
ACT_DEBUG, ACT_SUSPEND, ACT_RESTORE };
const uint8_t hardwareActions[] = { ACT_DISPLAYTYPE, ACT_SPEED,
const uint8_t hardwareActions[] = { ACT_DISPLAYTYPE, ACT_LUMINANCEUP,
ACT_LUMINANCEDOWN, ACT_SPEED,
ACT_PADX_INV, ACT_PADY_INV,
ACT_PADDLES, ACT_VOLPLUS, ACT_VOLMINUS };
const uint8_t diskActions[] = { ACT_DISK1, ACT_DISK2,
@ -387,6 +390,20 @@ uint16_t BIOS::HardwareMenuHandler(bool needsRedraw, bool performAction)
localRedraw = true;
break;
case ACT_LUMINANCEUP:
if (g_luminanceCutoff < 255)
g_luminanceCutoff++;
((AppleDisplay*)g_display)->displayTypeChanged();
localRedraw = true;
break;
case ACT_LUMINANCEDOWN:
if (g_luminanceCutoff > 0)
g_luminanceCutoff--;
((AppleDisplay*)g_display)->displayTypeChanged();
localRedraw = true;
break;
case ACT_SPEED:
currentCPUSpeedIndex++;
currentCPUSpeedIndex %= 4;
@ -797,6 +814,11 @@ bool BIOS::isActionActive(int8_t action)
case ACT_PADDLES:
return true;
case ACT_LUMINANCEUP:
return (g_luminanceCutoff < 255);
case ACT_LUMINANCEDOWN:
return (g_luminanceCutoff > 0);
case ACT_VOLPLUS:
return (g_volume < 15);
case ACT_VOLMINUS:
@ -934,6 +956,14 @@ void BIOS::DrawHardwareMenu()
}
}
break;
case ACT_LUMINANCEUP:
sprintf(buf, "Luminance+: %d", g_luminanceCutoff);
break;
case ACT_LUMINANCEDOWN:
sprintf(buf, "Luminance-: %d", g_luminanceCutoff);
break;
case ACT_SPEED:
{
const char *templateString = "CPU Speed: %s";

View File

@ -10,7 +10,7 @@ PhysicalSpeaker *g_speaker = NULL;
PhysicalPaddles *g_paddles = NULL;
PhysicalPrinter *g_printer = NULL;
VMui *g_ui;
int8_t g_volume = 15;
int8_t g_volume = 7;
uint8_t g_displayType = 3; // FIXME m_perfectcolor
VMRam g_ram;
volatile uint8_t g_debugMode = D_NONE;
@ -19,6 +19,8 @@ uint32_t g_speed = 1023000; // Hz
bool g_invertPaddleX = false;
bool g_invertPaddleY = false;
uint8_t g_luminanceCutoff = 122; // reasonable values are 127 and 128 for 32-bit (SDL); and 122/123 for 16-bit (teensy) depending on whether we're talking about white-on-black or black-on-white
char debugBuf[255];
#ifdef TEENSYDUINO

View File

@ -56,6 +56,8 @@ extern uint32_t g_speed;
extern bool g_invertPaddleX;
extern bool g_invertPaddleY;
extern uint8_t g_luminanceCutoff;
extern char debugBuf[255];
#ifdef TEENSYDUINO

View File

@ -6,7 +6,7 @@
// Fun trivia: the Apple //e was in production from January 1983 to
// November 1993. And the 65C02 in them supported weird BCD math modes.
#define PREFSMAGIC 0x01831093
#define PREFSVERSION 3
#define PREFSVERSION 4
#ifndef MAXPATH
#define MAXPATH 255
@ -21,6 +21,8 @@ typedef struct _prefs {
uint8_t volume;
uint8_t displayType;
uint8_t luminanceCutoff;
uint8_t debug;
uint8_t speed;
@ -33,6 +35,8 @@ typedef struct _prefs {
char disk2[MAXPATH];
char hd1[MAXPATH];
char hd2[MAXPATH];
uint32_t magicFooter;
} prefs_t;
class PrefsStore {

View File

@ -451,6 +451,8 @@ void readPrefs()
if (np.readPrefs(&p)) {
g_volume = p.volume;
g_displayType = p.displayType;
g_luminanceCutoff = p.luminanceCutoff;
g_debugMode = p.debug;
g_speed = (p.speed * (1023000/2)); // steps of half normal speed
if (g_speed < (1023000/2))
@ -484,7 +486,10 @@ void writePrefs()
p.version = PREFSVERSION;
p.volume = g_volume;
p.displayType = g_displayType;
p.luminanceCutoff = g_luminanceCutoff;
p.debug = g_debugMode;
p.speed = g_speed / (1023000/2);
strcpy(p.disk1, ((AppleVM *)g_vm)->DiskName(0));

View File

@ -39,10 +39,14 @@ const uint8_t loresPixelColors[16][3] = { { 0, 0, 0 }, // black
#define color16To32(x) ( (((x) & 0xF800) << 8) | (((x) & 0x07E0) << 5) | (((x) & 0x001F)<<3) )
#define packColor32(x) ( (x[0] << 16) | (x[1] << 8) | (x[2]) )
#define unpackRed(x) ( ((x) & 0xFF0000) >> 16 )
#define unpackGreen(x) ( ((x) & 0xFF00) >> 8 )
#define unpackBlue(x) ( ((x) & 0xFF) )
// FIXME this blend could be optimized - and it's a dumb blend that
// just averages RGB values individually, rather than trying to find a
// sane blend of 2 pixels. Needs thought.
#define blendPackedColor(x,y) ( (((x) & 0xFF0000) + ((y) & 0xFF0000) / 2) + (((x) & 0x00FF00) + ((y) & 0x00FF00)) / 2 + (((x) & 0x0000FF) + ((y) & 0x0000FF)) / 2 )
#define blendPackedColor(x,y) ( (((unpackRed(x) + unpackRed(y))/2) << 16) + (((unpackGreen(x) + unpackGreen(y))/2) << 8) + ((unpackBlue(x) + unpackBlue(y))/2) )
#define luminanceFromRGB(r,g,b) ( ((r)*0.2126) + ((g)*0.7152) + ((b)*0.0722) )
SDLDisplay::SDLDisplay()
{
@ -128,9 +132,9 @@ void SDLDisplay::blit(AiieRect r)
b = (colorIdx & 0x0000FF);
if (g_displayType == m_monochrome) {
float fv = 0.2125 * r + 0.7154 * g + 0.0721 * b;
r = b = 0;
g = fv;
// float fv = 0.2125 * r + 0.7154 * g + 0.0721 * b;
// r = b = 0;
// g = fv;
}
else if (g_displayType == m_blackAndWhite) {
// Used to reduce to B&W in this driver, but now it's up in the apple display
@ -277,9 +281,18 @@ void SDLDisplay::cachePixel(uint16_t x, uint16_t y, uint8_t color)
uint32_t origColor = videoBuffer[y][(x>>1)*SDLDISPLAY_SCALE];
uint32_t newColor = packColor32(loresPixelColors[color]);
if (g_displayType == m_blackAndWhite) {
// FIXME: the two possible sets here of 'origColor && newColor' or 'origColor||newColor'
// work well for black-on-white and white-on-black. But neither is good in the other.
cacheDoubleWidePixel(x>>1,y,(uint32_t)((origColor && newColor) ? 0xFFFFFF : 0x000000));
// There are four reasonable decisions here: if either pixel
// *was* on, then it's on; if both pixels *were* on, then it's
// on; and if the blended value of the two pixels were on, then
// it's on; or if the blended value of the two is above some
// certain overall brightness, then it's on. This is the last of
// those - where the brightness cutoff is defined in the bios as
// g_luminanceCutoff.
uint32_t blendedColor = blendPackedColor(origColor, newColor);
uint32_t luminance = luminanceFromRGB(unpackRed(blendedColor),
unpackGreen(blendedColor),
unpackBlue(blendedColor));
cacheDoubleWidePixel(x>>1,y,(uint32_t)((luminance >= g_luminanceCutoff) ? 0xFFFFFF : 0x000000));
} else {
cacheDoubleWidePixel(x>>1,y,(uint32_t)blendPackedColor(origColor, newColor));
}

View File

@ -2,6 +2,7 @@
#include <DMAChannel.h>
#include "teensy-display.h"
#include "iocompat.h"
#include "appleui.h"
// FIXME should be able to omit this include and rely on the xterns, which
@ -54,8 +55,10 @@ DMAMEM uint16_t dmaBuffer[TEENSYDISPLAY_HEIGHT][TEENSYDISPLAY_WIDTH];
#define RGBto565(r,g,b) ((((r) & 0xF8) << 8) | (((g) & 0xFC) << 3) | ((b) >> 3))
#define _565toR(c) ( ((c) & 0xF800) >> 8 )
#define _565toG(c) ( ((c) & 0x07E0) >> 5 )
#define _565toB(c) ( ((c) & 0x001F) )
#define _565toG(c) ( ((c) & 0x07E0) >> 3 )
#define _565toB(c) ( ((c) & 0x001F) << 3 )
#define luminanceFromRGB(r,g,b) ( ((r)*0.2126) + ((g)*0.7152) + ((b)*0.0722) )
ILI9341_t3n tft = ILI9341_t3n(PIN_CS, PIN_DC, PIN_RST, PIN_MOSI, PIN_SCK, PIN_MISO);
@ -125,7 +128,7 @@ void TeensyDisplay::blit()
if (overlayMessage[0]) {
drawString(M_SELECTDISABLED, 1, TEENSYDISPLAY_HEIGHT - (16 + 12)*TEENSYDISPLAY_SCALE, overlayMessage);
}
nextMessageTime = millis() + 1000;
nextMessageTime = millis() + 10; // DEBUGGING FIXME make 1000 again
}
}
}
@ -262,17 +265,25 @@ void TeensyDisplay::cachePixel(uint16_t x, uint16_t y, uint8_t color)
// This is the case where we need to blend together neighboring
// pixels, because we don't have enough physical screen resoultion.
if (x&1) {
uint8_t origColor = dmaBuffer[y+SCREENINSET_Y][(x>>1)*TEENSYDISPLAY_SCALE+SCREENINSET_X];
cacheDoubleWidePixel(x>>1, y, color);
#if 0
uint8_t newColor = (uint16_t) (origColor + color) / 2;
uint16_t origColor = dmaBuffer[y+SCREENINSET_Y][(x>>1)*TEENSYDISPLAY_SCALE+SCREENINSET_X];
uint16_t newColor = (uint16_t) loresPixelColors[color];
if (g_displayType == m_blackAndWhite) {
cacheDoubleWidePixel(x>>1, y, (origColor && newColor) ? 0xFFFF : 0x0000);
// There are four reasonable decisions here: if either pixel
// *was* on, then it's on; if both pixels *were* on, then it's
// on; and if the blended value of the two pixels were on, then
// it's on; or if the blended value of the two is above some
// certain overall brightness, then it's on. This is the last of
// those - where the brightness cutoff is defined in the bios as
// g_luminanceCutoff.
uint16_t blendedColor = blendColors(origColor, newColor);
uint16_t luminance = luminanceFromRGB(_565toR(blendedColor),
_565toG(blendedColor),
_565toB(blendedColor));
cacheDoubleWidePixel(x>>1,y,(uint16_t)((luminance >= g_luminanceCutoff) ? 0xFFFF : 0x0000));
} else {
cacheDoubleWidePixel(x>>1,y,blendColors(origColor, newColor));
cacheDoubleWidePixel(x>>1,y,color);
// Else if it's black, we leave whatever was in the other pixel.
}
#endif
} else {
// The even pixels always draw.
cacheDoubleWidePixel(x>>1,y,color);
@ -289,6 +300,14 @@ void TeensyDisplay::cachePixel(uint16_t x, uint16_t y, uint8_t color)
}
void TeensyDisplay::cacheDoubleWidePixel(uint16_t x, uint16_t y, uint16_t color16)
{
for (int yoff=0; yoff<TEENSYDISPLAY_SCALE; yoff++) {
for (int xoff=0; xoff<TEENSYDISPLAY_SCALE; xoff++) {
dmaBuffer[(y*TEENSYDISPLAY_SCALE+yoff+SCREENINSET_Y)][x*TEENSYDISPLAY_SCALE+xoff+SCREENINSET_X] = color16;
}
}
}
// "DoubleWide" means "please double the X because I'm in low-res
// width mode".

View File

@ -31,6 +31,7 @@ class TeensyDisplay : public PhysicalDisplay {
virtual void drawImageOfSizeAt(const uint8_t *img, uint16_t sizex, uint8_t sizey, uint16_t wherex, uint8_t wherey);
void cacheDoubleWidePixel(uint16_t x, uint16_t y, uint16_t color16);
virtual void cacheDoubleWidePixel(uint16_t x, uint16_t y, uint8_t color);
virtual void cache2DoubleWidePixels(uint16_t x, uint16_t y, uint8_t colorA, uint8_t colorB);
virtual void cachePixel(uint16_t x, uint16_t y, uint8_t color);

View File

@ -18,7 +18,8 @@ bool TeensyPrefs::readPrefs(prefs_t *readTo)
*pp++ = EEPROM.read(i);
}
if (readTo->magic != PREFSMAGIC) {
if (readTo->magic != PREFSMAGIC ||
readTo->magicFooter != PREFSMAGIC) {
return false;
}
if (readTo->prefsSize != sizeof(prefs_t)) {

View File

@ -16,6 +16,8 @@
#include "teensy-println.h"
#include "smalloc.h"
#include "iocompat.h"
//#define DEBUG_TIMING
#if F_CPU < 240000000
@ -127,7 +129,12 @@ static uint8_t usb_scanmap[256] = {
void onKeypress(uint8_t keycode)
{
if (keycode == 67) {
// F10 is our interrupt button; FIXME this probably needs to be adjustable
g_biosInterrupt = true;
} else {
((AppleVM *)g_vm)->getKeyboard()->keyDepressed(usb_scanmap[keycode]);
}
}
void onKeyrelease(uint8_t keycode)
@ -588,11 +595,22 @@ void readPrefs()
if (p.hd2[0]) {
((AppleVM *)g_vm)->insertHD(1, p.hd2);
}
}
g_luminanceCutoff = p.luminanceCutoff;
g_invertPaddleX = p.invertPaddleX;
g_invertPaddleY = p.invertPaddleY;
} else {
// Set some defaults!
g_volume = 7;
g_displayType = 3; // FIXME constant
g_debugMode = D_NONE;
g_speed = 1023000;
g_luminanceCutoff = 127;
g_invertPaddleX = g_invertPaddleY = false;
}
// Update the paddles with the new inversion state
((TeensyPaddles *)g_paddles)->setRev(g_invertPaddleX, g_invertPaddleY);
}
@ -603,6 +621,7 @@ void writePrefs()
prefs_t p;
p.magic = PREFSMAGIC;
p.magicFooter = PREFSMAGIC;
p.prefsSize = sizeof(prefs_t);
p.version = PREFSVERSION;
@ -611,6 +630,7 @@ void writePrefs()
p.volume = g_volume;
p.displayType = g_displayType;
p.luminanceCutoff = g_luminanceCutoff;
p.debug = g_debugMode;
p.speed = g_speed / (1023000/2);
strcpy(p.disk1, ((AppleVM *)g_vm)->DiskName(0));