Working on proper content rendering.

This commit is contained in:
Scott Duensing 2022-06-17 19:58:36 -05:00
parent 3c51c33430
commit 003a0c0ba8
5 changed files with 228 additions and 193 deletions

View file

@ -19,38 +19,39 @@ static void surfacePixelSet8(uint16_t x, uint16_t y, ColorT color);
static void surfacePixelSet16(uint16_t x, uint16_t y, ColorT color);
static void surfacePixelSet32(uint16_t x, uint16_t y, ColorT color);
#include "gui.h"
void surfaceBlit(int16_t targetX, int16_t targetY, SurfaceT *source) {
void surfaceBlit(int16_t targetX1, int16_t targetY1, int16_t width, int16_t height, SurfaceT *source) {
int16_t y;
int16_t x1 = 0;
int16_t y1 = 0;
int16_t x2 = source->width;
int16_t y2 = source->height;
size_t bytes;
size_t offsetTarget;
size_t offsetSource;
// Did they provide a valid width/height?
if (width <= 0 || width > source->width) width = source->width;
if (height <= 0 || height > source->height) height = source->height;
// Clip on top and left. x1 & y1 are pixel locations inside the source bitmap.
if (targetX < 0) x1 = -targetX;
if (targetY < 0) y1 = -targetY;
if (targetX1 < 0) x1 = -targetX1;
if (targetY1 < 0) y1 = -targetY1;
// Clip on right and bottom.
if (targetX + x2 > __surfaceActive->width) x2 -= targetX + x2 - __surfaceActive->width;
if (targetY + y2 > __surfaceActive->height) y2 -= targetY + y2 - __surfaceActive->height;
if (targetX1 + width > __surfaceActive->width) width -= targetX1 + width - __surfaceActive->width;
if (targetY1 + height > __surfaceActive->height) height -= targetY1 + height - __surfaceActive->height;
// Are we still on the screen?
if (x1 < 0 || y1 < 0 || x2 < x1 || y2 < y1) return;
if (x1 < 0 || y1 < 0 || width < x1 || height < y1) return;
if (targetX == 0 && targetY == 0 && __surfaceActive->width == source->width && __surfaceActive->height == source->height) {
if (targetX1 == 0 && targetY1 == 0 && __surfaceActive->width == source->width && __surfaceActive->height == source->height) {
// Direct blit of entire surface.
memcpy(__surfaceActive->buffer.bits8, source->buffer.bits8, source->bytes);
} else {
// Blit into larger surface.
offsetTarget = (targetY + y1) * __surfaceActive->scanline + (targetX + x1) * __surfaceBytesPerPixel;
offsetTarget = (targetY1 + y1) * __surfaceActive->scanline + (targetX1 + x1) * __surfaceBytesPerPixel;
offsetSource = y1 * source->scanline + x1 * __surfaceBytesPerPixel;
bytes = (x2 - x1) * __surfaceBytesPerPixel;
for (y=y1; y<y2; y++) {
bytes = (width - x1) * __surfaceBytesPerPixel;
for (y=y1; y<height; y++) {
memcpy(&__surfaceActive->buffer.bits8[offsetTarget], &source->buffer.bits8[offsetSource], bytes);
offsetTarget += __surfaceActive->scanline;
offsetSource += source->scanline;

View file

@ -45,7 +45,7 @@ extern ColorT (*surfacePixelGet)(SurfaceT *surface, int16_t x, int16_t y);
extern void (*surfacePixelSet)(uint16_t x, uint16_t y, ColorT color);
void surfaceBlit(int16_t targetX, int16_t targetY, SurfaceT *source);
void surfaceBlit(int16_t targetX1, int16_t targetY1, int16_t width, int16_t height, SurfaceT *source);
void surfaceBlitWithTransparency(int16_t targetX, int16_t targetY, SurfaceT *source, ColorT transparent);
void surfaceClear(ColorT color);
ColorT surfaceColorMake(uint8_t r, uint8_t g, uint8_t b);

View file

@ -5,25 +5,211 @@
uint8_t __MAGIC_WINDOW = 0;
static WindowT **_windowList = NULL;
static WindowT *_windowTop = NULL;
WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title, uint8_t flags, ...) {
WindowT *win = NULL;
static void windowCache(WindowT *w);
static void windowCache(WindowT *w) {
int16_t i;
int16_t x1;
int16_t y1;
int16_t x2;
int16_t y2;
int16_t tx1;
int16_t tx2;
int16_t ty2;
int16_t originalX;
int16_t originalY;
ColorT titleBackgroundColor;
ColorT widgetColor;
SurfaceT *target = surfaceGet();
// Move the window to 0,0 to cache to it's own surface.
originalX = w->base.r.x;
originalY = w->base.r.y;
w->base.r.x = 0;
w->base.r.y = 0;
// Do we have a cached surface already?
if (w->cached) {
// Did the size change?
surfaceSet(w->cached);
if ((surfaceWidthGet() != w->base.r.w) || (surfaceHeightGet() != w->base.r.h)) {
// Yeah. We will recreate it.
surfaceDestroy(&w->cached);
}
}
// Do we need to create a surface?
if (!w->cached) {
w->cached = surfaceCreate(w->base.r.w, w->base.r.h);
}
// Draw into cache.
surfaceSet(w->cached);
// Determine some colors.
titleBackgroundColor = (w == _windowTop) ? GUI_DARKGRAY : GUI_LIGHTGRAY;
widgetColor = (w == _windowTop) ? GUI_LIGHTGRAY : GUI_DARKGRAY;
// Get ready!
x1 = w->base.r.x;
y1 = w->base.r.y;
x2 = w->base.r.x + w->base.r.w - 1;
y2 = w->base.r.y + w->base.r.h - 1;
// Draw border.
surfaceBoxHighlight(x1, y1, x2, y2, GUI_WHITE, GUI_BLACK);
x1++; y1++; x2--; y2--;
for (i=0; i<3; i++) {
surfaceBox(x1, y1, x2, y2, GUI_LIGHTGRAY);
x1++; y1++; x2--; y2--;
}
surfaceBoxHighlight(x1, y1, x2, y2, GUI_BLACK, GUI_WHITE);
x1++; y1++; x2--; y2--;
// Do we need a titlebar?
if (w->title || w->flags & WIN_CLOSE || w->flags & WIN_MAXIMIZE || w->flags & WIN_MINIMIZE) {
tx1 = x1;
tx2 = x2;
ty2 = y1 + 20;
// Close box?
if (w->flags & WIN_CLOSE) {
tx1 += 20;
w->close.x = x1;
w->close.y = y1;
w->close.x2 = tx1 - 1;
w->close.y2 = ty2 - 1;
surfaceBoxFilled(w->close.x + 1, w->close.y + 1, w->close.x2 - 1, w->close.y2 - 1, titleBackgroundColor);
surfaceBoxHighlight(w->close.x + 3, w->close.y + 8, w->close.x2 - 3, w->close.y2 - 8, GUI_WHITE, widgetColor);
surfaceBoxHighlight(w->close.x, w->close.y, w->close.x2, w->close.y2, GUI_WHITE, GUI_BLACK);
} else {
w->close.x = w->close.y = w->close.x2 = w->close.y2 = 0;
}
// Maximize box?
if (w->flags & WIN_MAXIMIZE) {
tx2 -= 20;
w->maximize.x = tx2 + 1;
w->maximize.y = y1;
w->maximize.x2 = tx2 + 20;
w->maximize.y2 = ty2 - 1;
surfaceBoxFilled(w->maximize.x + 1, w->maximize.y + 1, w->maximize.x2 - 1, w->maximize.y2 - 1, titleBackgroundColor);
surfaceLine(w->maximize.x + 4, w->maximize.y + 10, w->maximize.x + 10, w->maximize.y + 4, GUI_WHITE);
surfaceLine(w->maximize.x + 4, w->maximize.y + 10, w->maximize.x + 10, w->maximize.y2 - 3, GUI_WHITE);
surfaceLine(w->maximize.x2 - 3, w->maximize.y + 10, w->maximize.x2 - 9, w->maximize.y + 4, widgetColor);
surfaceLine(w->maximize.x2 - 3, w->maximize.y + 10, w->maximize.x2 - 9, w->maximize.y2 - 3, widgetColor);
surfaceBoxHighlight(w->maximize.x, w->maximize.y, w->maximize.x2, w->maximize.y2, GUI_WHITE, GUI_BLACK);
} else {
w->maximize.x = w->maximize.y = w->maximize.x2 = w->maximize.y2 = 0;
}
// Minimize box?
if (w->flags & WIN_MINIMIZE) {
tx2 -= 20;
w->minimize.x = tx2 + 1;
w->minimize.y = y1;
w->minimize.x2 = tx2 + 20;
w->minimize.y2 = ty2 - 1;
surfaceBoxFilled(w->minimize.x + 1, w->minimize.y + 1, w->minimize.x2 - 1, w->minimize.y2 - 1, titleBackgroundColor);
surfaceBoxHighlight(w->minimize.x + 7, w->minimize.y + 7, w->minimize.x2 - 7, w->minimize.y2 - 7, GUI_WHITE, widgetColor);
surfaceBoxHighlight(w->minimize.x, w->minimize.y, w->minimize.x2, w->minimize.y2, GUI_WHITE, GUI_BLACK);
} else {
w->minimize.x = w->minimize.y = w->minimize.x2 = w->maximize.y2 = 0;
}
// Draw titlebar background.
w->titlebar.x = tx1;
w->titlebar.y = y1;
w->titlebar.x2 = tx2;
w->titlebar.y2 = ty2 - 1;
surfaceBoxFilled(w->titlebar.x + 1, w->titlebar.y + 1, w->titlebar.x2 - 1, w->titlebar.y2 - 1, titleBackgroundColor);
if (w->title) {
fontSet(__guiFontVGA8x16);
fontColorSet(GUI_WHITE, titleBackgroundColor);
fontRender(w->title, w->titlebar.x + 12, w->titlebar.y + 2);
}
surfaceBoxHighlight(w->titlebar.x, w->titlebar.y, w->titlebar.x2, w->titlebar.y2, GUI_WHITE, GUI_BLACK);
} else {
w->close.x = w->close.y = w->close.x2 = w->close.y2 = 0;
w->maximize.x = w->maximize.y = w->maximize.x2 = w->maximize.y2 = 0;
w->minimize.x = w->minimize.y = w->minimize.x2 = w->maximize.y2 = 0;
w->titlebar.x = w->titlebar.y = w->titlebar.x2 = w->titlebar.y2 = 0;
}
// Find content area.
y1 += 20;
w->bounds.x = x1;
w->bounds.y = y1;
w->bounds.x2 = x2;
w->bounds.y2 = y2;
// Resize handle.
if (w->flags & WIN_RESIZE) {
x1 = w->base.r.x + w->base.r.w - 2;
x2 = x1 - 3;
y1 = w->base.r.y + w->base.r.h - 21;
surfaceLineH(x1, x2, y1, GUI_BLACK);
surfaceLineH(x1, x2, y1 + 1, GUI_WHITE);
x1 = w->base.r.x + w->base.r.w - 21;
y1 = w->base.r.y + w->base.r.h - 2;
y2 = y1 - 3;
surfaceLineV(x1, y1, y2, GUI_BLACK);
surfaceLineV(x1 + 1, y1, y2, GUI_WHITE);
}
// Blit contents.
if (w->content) surfaceBlit(w->bounds.x, w->bounds.y, w->bounds.x2 - w->bounds.x, w->bounds.y2 - w->bounds.y, w->content);
// Fixup all the widget coordinates.
windowMoveTo(w, originalX, originalY);
surfaceSet(target);
}
// Passing "flags" as a default int provides proper alignment for the following va_args list.
WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title, int flags, ...) {
int16_t width;
int16_t height;
va_list args;
WindowT *win = NULL;
SurfaceT *t = surfaceGet();
NEW(WindowT, win);
guiWidgetBaseSet((WidgetT *)win, __MAGIC_WINDOW, x, y, w, h);
win->title = strdup(title);
win->flags = flags;
win->title = strdup(title);
win->flags = (uint8_t)flags;
win->close.x = win->close.y = win->close.x2 = win->close.y2 = 0;
win->titlebar.x = win->titlebar.y = win->titlebar.x2 = win->titlebar.y2 = 0;
win->minimize.x = win->minimize.y = win->minimize.x2 = win->minimize.y2 = 0;
win->maximize.x = win->maximize.y = win->maximize.x2 = win->maximize.y2 = 0;
win->bounds.x = win->bounds.y = win->bounds.x2 = win->bounds.y2 = 0;
win->cached = NULL;
win->content = NULL;
win->cached = NULL;
// If the window is resizable, we need to get two more arguments for the content size.
if (win->flags & WIN_RESIZE) {
va_start(args, flags);
width = va_arg(args, int);
height = va_arg(args, int);
va_end(args);
} else {
// Use whatever the default content area is. This causes an extra draw on create. Oh well.
windowCache(win);
width = win->bounds.x2 - win->bounds.x;
height = win->bounds.y2 - win->bounds.y;
guiWidgetDirtySet(W(win), 1);
}
win->content = surfaceCreate(width, height);
surfaceSet(win->content);
surfaceClear(GUI_LIGHTGRAY);
surfaceSet(t);
arrput(_windowList, win);
@ -49,6 +235,8 @@ void windowDestroy(struct WidgetS *widget, ...) {
if (_windowList[i]->title) DEL(_windowList[i]->title);
// Free cached surface.
if (_windowList[i]->cached) surfaceDestroy(&_windowList[i]->cached);
// Free content surface.
if (_windowList[i]->content) surfaceDestroy(&_windowList[i]->content);
// Delete the window.
DEL(_windowList[i]);
// Remove it from window list.
@ -151,172 +339,16 @@ void windowMoveTo(WindowT *w, uint16_t x, uint16_t y) {
void windowPaint(struct WidgetS *widget, ...) {
int16_t i;
int16_t x1;
int16_t y1;
int16_t x2;
int16_t y2;
int16_t tx1;
int16_t tx2;
int16_t ty2;
int16_t originalX;
int16_t originalY;
WindowT *w;
SurfaceT *target;
ColorT titleBackgroundColor;
ColorT widgetColor;
target = surfaceGet();
w = (WindowT *)widget;
WindowT *w = (WindowT *)widget;
// Do we need redrawn?
if (guiWidgetDirtyGet(widget)) {
guiWidgetDirtySet(widget, 0);
// Move the window to 0,0 to cache to it's own surface.
originalX = w->base.r.x;
originalY = w->base.r.y;
w->base.r.x = 0;
w->base.r.y = 0;
// Do we have a cached surface already?
if (w->cached) {
// Did the size change?
surfaceSet(w->cached);
if ((surfaceWidthGet() != w->base.r.w) || (surfaceHeightGet() != w->base.r.h)) {
// Yeah. We will recreate it.
surfaceDestroy(&w->cached);
}
}
// Do we need to create a surface?
if (!w->cached) {
w->cached = surfaceCreate(w->base.r.w, w->base.r.h);
}
// Draw into cache.
surfaceSet(w->cached);
// Determine some colors.
titleBackgroundColor = (w == _windowTop) ? GUI_DARKGRAY : GUI_LIGHTGRAY;
widgetColor = (w == _windowTop) ? GUI_LIGHTGRAY : GUI_DARKGRAY;
// Get ready!
x1 = w->base.r.x;
y1 = w->base.r.y;
x2 = w->base.r.x + w->base.r.w - 1;
y2 = w->base.r.y + w->base.r.h - 1;
// Draw border.
surfaceBoxHighlight(x1, y1, x2, y2, GUI_WHITE, GUI_BLACK);
x1++; y1++; x2--; y2--;
for (i=0; i<3; i++) {
surfaceBox(x1, y1, x2, y2, GUI_LIGHTGRAY);
x1++; y1++; x2--; y2--;
}
surfaceBoxHighlight(x1, y1, x2, y2, GUI_BLACK, GUI_WHITE);
x1++; y1++; x2--; y2--;
// Do we need a titlebar?
if (w->title || w->flags & WIN_CLOSE || w->flags & WIN_MAXIMIZE || w->flags & WIN_MINIMIZE) {
tx1 = x1;
tx2 = x2;
ty2 = y1 + 20;
// Close box?
if (w->flags & WIN_CLOSE) {
tx1 += 20;
w->close.x = x1;
w->close.y = y1;
w->close.x2 = tx1 - 1;
w->close.y2 = ty2 - 1;
surfaceBoxFilled(w->close.x + 1, w->close.y + 1, w->close.x2 - 1, w->close.y2 - 1, titleBackgroundColor);
surfaceBoxHighlight(w->close.x + 3, w->close.y + 8, w->close.x2 - 3, w->close.y2 - 8, GUI_WHITE, widgetColor);
surfaceBoxHighlight(w->close.x, w->close.y, w->close.x2, w->close.y2, GUI_WHITE, GUI_BLACK);
} else {
w->close.x = w->close.y = w->close.x2 = w->close.y2 = 0;
}
// Maximize box?
if (w->flags & WIN_MAXIMIZE) {
tx2 -= 20;
w->maximize.x = tx2 + 1;
w->maximize.y = y1;
w->maximize.x2 = tx2 + 20;
w->maximize.y2 = ty2 - 1;
surfaceBoxFilled(w->maximize.x + 1, w->maximize.y + 1, w->maximize.x2 - 1, w->maximize.y2 - 1, titleBackgroundColor);
surfaceLine(w->maximize.x + 4, w->maximize.y + 10, w->maximize.x + 10, w->maximize.y + 4, GUI_WHITE);
surfaceLine(w->maximize.x + 4, w->maximize.y + 10, w->maximize.x + 10, w->maximize.y2 - 3, GUI_WHITE);
surfaceLine(w->maximize.x2 - 3, w->maximize.y + 10, w->maximize.x2 - 9, w->maximize.y + 4, widgetColor);
surfaceLine(w->maximize.x2 - 3, w->maximize.y + 10, w->maximize.x2 - 9, w->maximize.y2 - 3, widgetColor);
surfaceBoxHighlight(w->maximize.x, w->maximize.y, w->maximize.x2, w->maximize.y2, GUI_WHITE, GUI_BLACK);
} else {
w->maximize.x = w->maximize.y = w->maximize.x2 = w->maximize.y2 = 0;
}
// Minimize box?
if (w->flags & WIN_MINIMIZE) {
tx2 -= 20;
w->minimize.x = tx2 + 1;
w->minimize.y = y1;
w->minimize.x2 = tx2 + 20;
w->minimize.y2 = ty2 - 1;
surfaceBoxFilled(w->minimize.x + 1, w->minimize.y + 1, w->minimize.x2 - 1, w->minimize.y2 - 1, titleBackgroundColor);
surfaceBoxHighlight(w->minimize.x + 7, w->minimize.y + 7, w->minimize.x2 - 7, w->minimize.y2 - 7, GUI_WHITE, widgetColor);
surfaceBoxHighlight(w->minimize.x, w->minimize.y, w->minimize.x2, w->minimize.y2, GUI_WHITE, GUI_BLACK);
} else {
w->minimize.x = w->minimize.y = w->minimize.x2 = w->maximize.y2 = 0;
}
// Draw titlebar background.
w->titlebar.x = tx1;
w->titlebar.y = y1;
w->titlebar.x2 = tx2;
w->titlebar.y2 = ty2 - 1;
surfaceBoxFilled(w->titlebar.x + 1, w->titlebar.y + 1, w->titlebar.x2 - 1, w->titlebar.y2 - 1, titleBackgroundColor);
if (w->title) {
fontSet(__guiFontVGA8x16);
fontColorSet(GUI_WHITE, titleBackgroundColor);
fontRender(w->title, w->titlebar.x + 12, w->titlebar.y + 2);
}
surfaceBoxHighlight(w->titlebar.x, w->titlebar.y, w->titlebar.x2, w->titlebar.y2, GUI_WHITE, GUI_BLACK);
} else {
w->close.x = w->close.y = w->close.x2 = w->close.y2 = 0;
w->maximize.x = w->maximize.y = w->maximize.x2 = w->maximize.y2 = 0;
w->minimize.x = w->minimize.y = w->minimize.x2 = w->maximize.y2 = 0;
w->titlebar.x = w->titlebar.y = w->titlebar.x2 = w->titlebar.y2 = 0;
}
// Find content area.
y1 += 20;
w->bounds.x = x1;
w->bounds.y = y1;
w->bounds.x2 = x2;
w->bounds.y2 = y2;
// Resize handle.
if (w->flags & WIN_RESIZE) {
x1 = w->base.r.x + w->base.r.w - 2;
x2 = x1 - 3;
y1 = w->base.r.y + w->base.r.h - 21;
surfaceLineH(x1, x2, y1, GUI_BLACK);
surfaceLineH(x1, x2, y1 + 1, GUI_WHITE);
x1 = w->base.r.x + w->base.r.w - 21;
y1 = w->base.r.y + w->base.r.h - 2;
y2 = y1 - 3;
surfaceLineV(x1, y1, y2, GUI_BLACK);
surfaceLineV(x1 + 1, y1, y2, GUI_WHITE);
}
// Fake Window contents.
surfaceBoxFilled(w->bounds.x, w->bounds.y, w->bounds.x2, w->bounds.y2, GUI_BLACK);
// Fixup all the widget coordinates.
windowMoveTo(w, originalX, originalY);
windowCache(w);
}
// By now we have a valid cached window. Blit it.
surfaceSet(target);
surfaceBlit(w->base.r.x, w->base.r.y, w->cached);
surfaceBlit(w->base.r.x, w->base.r.y, 0, 0, w->cached);
}
@ -335,6 +367,19 @@ RegisterT *windowRegister(uint8_t magic) {
}
void wmShutdown(void) {
while (arrlen(_windowList) > 0) {
windowDestroy((WidgetT *)_windowList[0]);
}
arrfree(_windowList);
}
void wmStartup(void) {
// Nada
}
void wmUpdate(EventT *event) {
int16_t i;
int16_t x2;
@ -455,15 +500,3 @@ void wmUpdate(EventT *event) {
} // Do we have windows?
}
void wmShutdown(void) {
while (arrlen(_windowList) > 0) {
windowDestroy((WidgetT *)_windowList[0]);
}
arrfree(_windowList);
}
void wmStartup(void) {
}

View file

@ -21,6 +21,7 @@ typedef struct WindowS {
RectT minimize; // Coordinates of minimize box, if any.
RectT maximize; // Coordinates of maximize box, if any.
RectT bounds; // Inside edge of window frame.
SurfaceT *content; // Actual window contents - widgets and such.
SurfaceT *cached; // Once rendered, keep a cached copy for faster redrawing.
} WindowT;
@ -28,7 +29,7 @@ typedef struct WindowS {
extern uint8_t __MAGIC_WINDOW; // Magic ID assigned to us from the GUI.
WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title, uint8_t flags, ...);
WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title, int flags, ...);
void windowDestroy(struct WidgetS *widget, ...);
void windowFocusSet(WindowT *win);
void windowMoveTo(WindowT *win, uint16_t x, uint16_t y);
@ -36,9 +37,9 @@ void windowPaint(struct WidgetS *widget, ...);
RegisterT *windowRegister(uint8_t magic);
void wmUpdate(EventT *event);
void wmShutdown(void);
void wmStartup(void);
void wmUpdate(EventT *event);
#endif // WMWINDOW_H

View file

@ -14,7 +14,7 @@ int main(int argc, char *argv[]) {
if (guiStartup(800, 600, 32) == SUCCESS) {
for (i=1; i<4; i++) {
sprintf(title, "Testing %d", i);
windowCreate(i * 50, i * 50, 300, 200, title, WIN_CLOSE | WIN_MAXIMIZE | WIN_MINIMIZE | WIN_RESIZE);
windowCreate(i * 50, i * 50, 300, 200, title, WIN_CLOSE | WIN_MAXIMIZE | WIN_MINIMIZE);
}
guiRun();
guiShutdown();