mirror of
https://github.com/JorjBauer/aiie.git
synced 2024-12-27 14:30:22 +00:00
working on 80-column text rendering
This commit is contained in:
parent
2bf4297c1c
commit
975b11c6c8
@ -9,8 +9,8 @@
|
||||
|
||||
#include "apple/appleui.h"
|
||||
|
||||
#define SCREENINSET_X (18*2)
|
||||
#define SCREENINSET_Y (13*2)
|
||||
#define SCREENINSET_X (18*SDLDISPLAY_SCALE)
|
||||
#define SCREENINSET_Y (13*SDLDISPLAY_SCALE)
|
||||
|
||||
// RGB map of each of the lowres colors
|
||||
const uint8_t loresPixelColors[16][3] = { { 0, 0, 0 }, // black
|
||||
@ -85,8 +85,8 @@ void SDLDisplay::drawImageOfSizeAt(const uint8_t *img,
|
||||
for (uint16_t x=0; x<sizex; x++) {
|
||||
const uint8_t *p = &img[(y * sizex + x)*3];
|
||||
p = &img[(y * sizex + x)*3];
|
||||
drawPixel((x+wherex)*2, y+wherey, p[0], p[1], p[2]);
|
||||
drawPixel((x+wherex)*2+1, y+wherey, p[0], p[1], p[2]);
|
||||
drawPixel((x+wherex)*SDLDISPLAY_SCALE, y+wherey, p[0], p[1], p[2]);
|
||||
drawPixel((x+wherex)*SDLDISPLAY_SCALE+1, y+wherey, p[0], p[1], p[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -103,8 +103,8 @@ void SDLDisplay::blit(AiieRect r)
|
||||
// not the border. We don't support updating the border *except* by
|
||||
// drawing directly to the screen device...
|
||||
|
||||
for (uint16_t y=r.top*2; y<r.bottom*2; y++) {
|
||||
for (uint16_t x=r.left*2; x<r.right*2; x++) {
|
||||
for (uint16_t y=r.top*SDLDISPLAY_SCALE; y<r.bottom*SDLDISPLAY_SCALE; y++) {
|
||||
for (uint16_t x=r.left*SDLDISPLAY_SCALE; x<r.right*SDLDISPLAY_SCALE; x++) {
|
||||
uint8_t colorIdx = videoBuffer[y*SDLDISPLAY_WIDTH+x];
|
||||
|
||||
uint8_t r, g, b;
|
||||
@ -142,8 +142,7 @@ inline void putpixel(SDL_Renderer *renderer, int x, int y, uint8_t r, uint8_t g,
|
||||
|
||||
void SDLDisplay::drawUIPixel(uint16_t x, uint16_t y, uint16_t color)
|
||||
{
|
||||
drawPixel(x*2, y, color);
|
||||
drawPixel(x*2+1, y, color);
|
||||
drawPixel(x*SDLDISPLAY_SCALE, y, color);
|
||||
}
|
||||
|
||||
void SDLDisplay::drawPixel(uint16_t x, uint16_t y, uint16_t color)
|
||||
@ -154,17 +153,21 @@ void SDLDisplay::drawPixel(uint16_t x, uint16_t y, uint16_t color)
|
||||
b = (color & 0x1F) << 3;
|
||||
|
||||
|
||||
// Pixel-doubling vertically
|
||||
for (int yoff=0; yoff<2; yoff++) {
|
||||
putpixel(renderer, x, yoff+y*2, r, g, b);
|
||||
// Pixel-doubling vertically and horizontally, based on scale
|
||||
for (int yoff=0; yoff<SDLDISPLAY_SCALE; yoff++) {
|
||||
for (int xoff=0; xoff<SDLDISPLAY_SCALE; xoff++) {
|
||||
putpixel(renderer, x+xoff, yoff+y*SDLDISPLAY_SCALE, r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDLDisplay::drawPixel(uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
// Pixel-doubling vertically
|
||||
for (int yoff=0; yoff<2; yoff++) {
|
||||
putpixel(renderer, x, yoff+y*2, r, g, b);
|
||||
// Pixel-doubling horizontally and vertically, based on scale
|
||||
for (int yoff=0; yoff<SDLDISPLAY_SCALE; yoff++) {
|
||||
for (int xoff=0; xoff<SDLDISPLAY_SCALE; xoff++) {
|
||||
putpixel(renderer, x+xoff, yoff+y*SDLDISPLAY_SCALE, r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,35 +233,63 @@ void SDLDisplay::clrScr()
|
||||
SDL_RenderPresent(renderer); // perform the render
|
||||
}
|
||||
|
||||
// This was called with the expectation that it can draw every one of
|
||||
// the 560x192 pixels that could be addressed. The SDLDISPLAY_SCALE is
|
||||
// basically half the X scale - so a 320-pixel-wide screen can show 40
|
||||
// columns fine, which means that we need to be creative for 80 columns,
|
||||
// which need to be alpha-blended...
|
||||
void SDLDisplay::cachePixel(uint16_t x, uint16_t y, uint8_t color)
|
||||
{
|
||||
videoBuffer[y*2*SDLDISPLAY_WIDTH+x] = color;
|
||||
videoBuffer[((y*2)+1)*SDLDISPLAY_WIDTH+x] = color;
|
||||
if (SDLDISPLAY_SCALE == 1) {
|
||||
// we need to alpha blend the X because there aren't enough screen pixels.
|
||||
// This takes advantage of the fact that we always call this linearly
|
||||
// for the 80-column text -- we never (?) do partial screen blits, but
|
||||
// always wind up redrawing the entirety. So we can look at the pixel in
|
||||
// the "shared" cell of RAM, and come up with a color between the two.
|
||||
if (x&1) {
|
||||
uint8_t origColor = videoBuffer[(y*SDLDISPLAY_SCALE)*SDLDISPLAY_WIDTH+(x>>1)*SDLDISPLAY_SCALE];
|
||||
|
||||
uint8_t newColor = (uint16_t) (origColor + color) / 2;
|
||||
|
||||
if (x==1 && y==0) {
|
||||
printf("origcolor: %d color: %d newcolor: %d\n", origColor, color, newColor);
|
||||
}
|
||||
cacheDoubleWidePixel(x>>1,y,newColor);
|
||||
// Else if it's black, we leave whatever was in the other pixel.
|
||||
} else {
|
||||
// The even pixels always draw.
|
||||
cacheDoubleWidePixel(x>>1,y,color);
|
||||
}
|
||||
|
||||
} else {
|
||||
// we have enough resolution to show all the pixels, so just do it
|
||||
x = (x * SDLDISPLAY_SCALE)/2;
|
||||
for (int yoff=0; yoff<SDLDISPLAY_SCALE; yoff++) {
|
||||
for (int xoff=0; xoff<SDLDISPLAY_SCALE; xoff++) {
|
||||
videoBuffer[(y*SDLDISPLAY_SCALE+yoff)*SDLDISPLAY_WIDTH+x+xoff] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "DoubleWide" means "please double the X because I'm in low-res width mode"
|
||||
void SDLDisplay::cacheDoubleWidePixel(uint16_t x, uint16_t y, uint8_t color)
|
||||
{
|
||||
videoBuffer[y*2*SDLDISPLAY_WIDTH+x*2] = color;
|
||||
videoBuffer[y*2*SDLDISPLAY_WIDTH+x*2+1] = color;
|
||||
|
||||
videoBuffer[(y*2+1)*SDLDISPLAY_WIDTH+x*2] = color;
|
||||
videoBuffer[(y*2+1)*SDLDISPLAY_WIDTH+x*2+1] = color;
|
||||
for (int yoff=0; yoff<SDLDISPLAY_SCALE; yoff++) {
|
||||
for (int xoff=0; xoff<SDLDISPLAY_SCALE; xoff++) {
|
||||
videoBuffer[(y*SDLDISPLAY_SCALE+yoff)*SDLDISPLAY_WIDTH+x*SDLDISPLAY_SCALE+xoff] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDLDisplay::cache2DoubleWidePixels(uint16_t x, uint16_t y, uint8_t colorB, uint8_t colorA)
|
||||
{
|
||||
videoBuffer[y*2*SDLDISPLAY_WIDTH+x*2] = colorA;
|
||||
videoBuffer[y*2*SDLDISPLAY_WIDTH+x*2+1] = colorA;
|
||||
|
||||
videoBuffer[y*2*SDLDISPLAY_WIDTH+x*2+2] = colorB;
|
||||
videoBuffer[y*2*SDLDISPLAY_WIDTH+x*2+3] = colorB;
|
||||
|
||||
videoBuffer[(y*2+1)*SDLDISPLAY_WIDTH+x*2] = colorA;
|
||||
videoBuffer[(y*2+1)*SDLDISPLAY_WIDTH+x*2+1] = colorA;
|
||||
|
||||
videoBuffer[(y*2+1)*SDLDISPLAY_WIDTH+x*2+2] = colorB;
|
||||
videoBuffer[(y*2+1)*SDLDISPLAY_WIDTH+x*2+3] = colorB;
|
||||
for (int yoff=0; yoff<SDLDISPLAY_SCALE; yoff++) {
|
||||
for (int xoff=0; xoff<SDLDISPLAY_SCALE; xoff++) {
|
||||
videoBuffer[(y*SDLDISPLAY_SCALE+yoff)*SDLDISPLAY_WIDTH+x*SDLDISPLAY_SCALE+2*xoff] = colorA;
|
||||
videoBuffer[(y*SDLDISPLAY_SCALE+yoff)*SDLDISPLAY_WIDTH+x*SDLDISPLAY_SCALE+1+2*xoff] = colorB;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -8,9 +8,11 @@
|
||||
|
||||
#include "physicaldisplay.h"
|
||||
|
||||
|
||||
#define SDLDISPLAY_WIDTH (320*2)
|
||||
#define SDLDISPLAY_HEIGHT (240*2)
|
||||
// scale can be 1,2,4. '1' is half-width at the highest resolution
|
||||
// (80-col mode). '2' is full width. '4' is double full width.
|
||||
#define SDLDISPLAY_SCALE 1
|
||||
#define SDLDISPLAY_WIDTH (320*SDLDISPLAY_SCALE)
|
||||
#define SDLDISPLAY_HEIGHT (240*SDLDISPLAY_SCALE)
|
||||
|
||||
class SDLDisplay : public PhysicalDisplay {
|
||||
public:
|
||||
|
@ -32,6 +32,9 @@
|
||||
volatile DMAMEM uint16_t dmaBuffer[240][320]; // 240 rows, 320 columns
|
||||
|
||||
#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) )
|
||||
|
||||
ILI9341_t3 tft = ILI9341_t3(PIN_CS, PIN_DC, PIN_RST, PIN_MOSI, PIN_SCK, PIN_MISO);
|
||||
|
||||
@ -302,26 +305,79 @@ void TeensyDisplay::cache2DoubleWidePixels(uint16_t x, uint16_t y,
|
||||
dmaBuffer[y+VOFFSET][x+1+HOFFSET] = loresPixelColors[colorA&0xF];
|
||||
}
|
||||
|
||||
inline double logfn(double x)
|
||||
{
|
||||
// At a value of x=255, log(base 1.022)(x) is 254.636.
|
||||
return log(x)/log(1.022);
|
||||
}
|
||||
|
||||
|
||||
inline uint16_t blendColors(uint16_t a, uint16_t b)
|
||||
{
|
||||
// Straight linear average doesn't work well for inverted text, because the
|
||||
// whites overwhelm the blacks.
|
||||
//return ((uint32_t)a + (uint32_t)b)/2;
|
||||
|
||||
#if 0
|
||||
// Testing a logarithmic color scale. My theory was that, since our
|
||||
// colors here are mostly black or white, it would be reasonable to
|
||||
// use a log scale of the average to bump up the brightness a
|
||||
// little. In practice, it's not really legible.
|
||||
return RGBto565( (uint8_t)(logfn((_565toR(a) + _565toR(b))/2)),
|
||||
(uint8_t)(logfn((_565toG(a) + _565toG(b))/2)),
|
||||
(uint8_t)(logfn((_565toB(a) + _565toB(b))/2)) );
|
||||
#endif
|
||||
|
||||
// Doing an R/G/B average works okay for legibility. It's not great for
|
||||
// inverted text.
|
||||
return RGBto565( (_565toR(a) + _565toR(b))/2,
|
||||
(_565toG(a) + _565toG(b))/2,
|
||||
(_565toB(a) + _565toB(b))/2 );
|
||||
|
||||
}
|
||||
|
||||
// This is the full 560-pixel-wide version -- and we only have 280
|
||||
// pixels in our buffer b/c the display is only 320 pixels wide
|
||||
// itself. So we'll divide x by 2. On odd-numbered X pixels, we also
|
||||
// alpha-blend -- "black" means "clear"
|
||||
// blend the colors of the two virtual pixels that share an onscreen
|
||||
// pixel
|
||||
void TeensyDisplay::cachePixel(uint16_t x, uint16_t y, uint8_t color)
|
||||
{
|
||||
if (/*x&*/1) {
|
||||
// divide x by 2, then this is mostly cacheDoubleWidePixel. Except
|
||||
// we also have to do the alpha blend so we can see both pixels.
|
||||
|
||||
uint16_t *p = &dmaBuffer[y+VOFFSET][(x>>1)+HOFFSET];
|
||||
uint16_t destColor = loresPixelColors[color];
|
||||
|
||||
// if (color == 0)
|
||||
// destColor = *p; // retain the even-numbered pixel's contents ("alpha blend")
|
||||
// Otherwise the odd-numbered pixel's contents "win" as "last drawn"
|
||||
// FIXME: do better blending of these two pixels.
|
||||
|
||||
dmaBuffer[y+VOFFSET][(x>>1)+HOFFSET] = destColor;
|
||||
#if 0
|
||||
static uint8_t previousColor = 0;
|
||||
#endif
|
||||
if (x&1) {
|
||||
// Blend the two pixels. This takes advantage of the fact that we
|
||||
// always call this linearly for 80-column text drawing -- we never
|
||||
// do partial screen blits, but always draw at least a whole character.
|
||||
// So we can look at the pixel in the "shared" cell of RAM, and come up
|
||||
// with a color between the two.
|
||||
|
||||
#if 1
|
||||
// This is straight blending, R/G/B average
|
||||
uint16_t origColor = dmaBuffer[y+VOFFSET][(x>>1)+HOFFSET];
|
||||
uint16_t newColor = loresPixelColors[color];
|
||||
cacheDoubleWidePixel(x>>1, y, blendColors(origColor, newColor));
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// The model we use for the SDL display works better, strangely - it keeps
|
||||
// the lores pixel index color (black, magenda, dark blue, purple, dark
|
||||
// green, etc.) until render time; so when it does the blend here, it's
|
||||
// actually blending in a very nonlinear way - e.g. "black + white / 2"
|
||||
// is actually "black(0) + white(15) / 2 = 15/2 = 7 (light blue)". Weird,
|
||||
// but definitely legible in a mini laptop SDL window with the same scale.
|
||||
// Unfortunately, it doesn't translate well to a ILI9341 panel; the pixels
|
||||
// are kind of muddy and indistinct, so the blue spills over and makes it
|
||||
// very difficult to read.
|
||||
uint8_t origColor = previousColor;
|
||||
uint8_t newColor = (uint16_t)(origColor + color) / 2;
|
||||
cacheDoubleWidePixel(x>>1, y, (uint16_t)color + (uint16_t)previousColor/2);
|
||||
#endif
|
||||
} else {
|
||||
cacheDoubleWidePixel(x, y, color);
|
||||
#if 0
|
||||
previousColor = color; // used for blending
|
||||
#endif
|
||||
cacheDoubleWidePixel(x>>1, y, color);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user