Some optimizations.

This commit is contained in:
Scott Duensing 2026-03-10 00:37:35 -05:00
parent 7a1777ebfc
commit 73f7e37ba6
5 changed files with 152 additions and 108 deletions

View file

@ -155,7 +155,7 @@ static void compositeAndFlush(AppContextT *ctx) {
drawCursorAt(ctx, ctx->mouseX, ctx->mouseY);
// 5. Flush this dirty rect to LFB
flushRect(d, ops, dr);
flushRect(d, dr);
}
resetClipRect(d);

View file

@ -4,6 +4,8 @@
#include <string.h>
#define DIRTY_MERGE_GAP 4
// ============================================================
// Prototypes
// ============================================================
@ -82,7 +84,7 @@ void dirtyListMerge(DirtyListT *dl) {
for (int32_t i = 0; i < dl->count; i++) {
for (int32_t j = i + 1; j < dl->count; j++) {
if (rectsOverlapOrAdjacent(&dl->rects[i], &dl->rects[j], 4)) {
if (rectsOverlapOrAdjacent(&dl->rects[i], &dl->rects[j], DIRTY_MERGE_GAP)) {
rectUnion(&dl->rects[i], &dl->rects[j], &dl->rects[i]);
dl->rects[j] = dl->rects[dl->count - 1];
dl->count--;
@ -99,9 +101,7 @@ void dirtyListMerge(DirtyListT *dl) {
// flushRect
// ============================================================
void flushRect(DisplayT *d, const BlitOpsT *ops, const RectT *r) {
(void)ops;
void flushRect(DisplayT *d, const RectT *r) {
int32_t bpp = d->format.bytesPerPixel;
int32_t x = r->x;

View file

@ -17,7 +17,7 @@ void dirtyListMerge(DirtyListT *dl);
void dirtyListClear(DirtyListT *dl);
// Flush a single rectangle from backbuffer to LFB
void flushRect(DisplayT *d, const BlitOpsT *ops, const RectT *r);
void flushRect(DisplayT *d, const RectT *r);
// Intersect two rectangles, returns true if intersection is non-empty
bool rectIntersect(const RectT *a, const RectT *b, RectT *result);

View file

@ -368,6 +368,7 @@ int32_t videoInit(DisplayT *d, int32_t requestedW, int32_t requestedH, int32_t p
if (!d->backBuf) {
fprintf(stderr, "VBE: Failed to allocate %lu byte backbuffer\n", (unsigned long)fbSize);
__djgpp_nearptr_disable();
return -1;
}
@ -381,6 +382,7 @@ int32_t videoInit(DisplayT *d, int32_t requestedW, int32_t requestedH, int32_t p
fprintf(stderr, "VBE: Failed to allocate palette\n");
free(d->backBuf);
d->backBuf = NULL;
__djgpp_nearptr_disable();
return -1;
}

View file

@ -10,11 +10,37 @@
#include <string.h>
#include <stdio.h>
// ============================================================
// Constants
// ============================================================
#define GADGET_PAD 2
#define GADGET_INSET 2 // inset from title bar edges
// ============================================================
// Title bar gadget geometry
// ============================================================
typedef struct {
int32_t titleX;
int32_t titleY;
int32_t titleW;
int32_t titleH;
int32_t gadgetS; // gadget square size
int32_t gadgetY; // gadget Y position
int32_t closeX; // close gadget X
int32_t minX; // minimize gadget X
int32_t maxX; // maximize gadget X (-1 if not resizable)
int32_t textLeftEdge; // left edge of title text area
int32_t textRightEdge; // right edge of title text area
} TitleGeomT;
// ============================================================
// Prototypes
// ============================================================
static void computeMenuBarPositions(WindowT *win, const BitmapFontT *font);
static void computeTitleGeom(const WindowT *win, TitleGeomT *g);
static void drawBorderFrame(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, int32_t x, int32_t y, int32_t w, int32_t h);
static void drawMenuBar(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors, WindowT *win);
static void drawResizeBreaks(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, WindowT *win);
@ -50,6 +76,42 @@ static void computeMenuBarPositions(WindowT *win, const BitmapFontT *font) {
}
// ============================================================
// computeTitleGeom
// ============================================================
//
// Compute title bar gadget positions. Used by both drawTitleBar()
// and wmHitTest() to keep geometry in sync.
static void computeTitleGeom(const WindowT *win, TitleGeomT *g) {
g->titleX = win->x + CHROME_BORDER_WIDTH;
g->titleY = win->y + CHROME_BORDER_WIDTH;
g->titleW = win->w - CHROME_BORDER_WIDTH * 2;
g->titleH = CHROME_TITLE_HEIGHT;
g->gadgetS = g->titleH - GADGET_INSET * 2;
g->gadgetY = g->titleY + GADGET_INSET;
// Close gadget on the left
g->closeX = g->titleX + GADGET_PAD;
// Rightmost gadget position (decremented as gadgets are placed)
int32_t rightX = g->titleX + g->titleW - GADGET_PAD - g->gadgetS;
if (win->resizable) {
g->maxX = rightX;
rightX -= g->gadgetS + GADGET_PAD;
} else {
g->maxX = -1;
}
g->minX = rightX;
// Text area between close gadget and minimize gadget
g->textLeftEdge = g->closeX + g->gadgetS + GADGET_PAD;
g->textRightEdge = g->minX - GADGET_PAD;
}
// ============================================================
// drawBorderFrame
// ============================================================
@ -201,34 +263,46 @@ static void drawResizeBreaks(DisplayT *d, const BlitOpsT *ops, const ColorScheme
// ============================================================
static void drawScaledRect(DisplayT *d, int32_t dstX, int32_t dstY, int32_t dstW, int32_t dstH, const uint8_t *src, int32_t srcW, int32_t srcH, int32_t srcPitch, int32_t bpp) {
for (int32_t dy = 0; dy < dstH; dy++) {
int32_t sy = (dy * srcH) / dstH;
int32_t screenY = dstY + dy;
// Pre-compute visible row/col range (clip against display clip rect)
int32_t rowStart = 0;
int32_t rowEnd = dstH;
int32_t colStart = 0;
int32_t colEnd = dstW;
if (screenY < d->clipY || screenY >= d->clipY + d->clipH) {
continue;
}
if (dstY < d->clipY) { rowStart = d->clipY - dstY; }
if (dstY + dstH > d->clipY + d->clipH) { rowEnd = d->clipY + d->clipH - dstY; }
if (dstX < d->clipX) { colStart = d->clipX - dstX; }
if (dstX + dstW > d->clipX + d->clipW) { colEnd = d->clipX + d->clipW - dstX; }
uint8_t *dstRow = d->backBuf + screenY * d->pitch;
if (rowStart >= rowEnd || colStart >= colEnd) {
return;
}
// Pre-compute source X lookup table (one division per column instead of per pixel)
int32_t srcXTab[ICON_SIZE];
int32_t visibleCols = colEnd - colStart;
for (int32_t dx = 0; dx < visibleCols; dx++) {
srcXTab[dx] = ((colStart + dx) * srcW) / dstW * bpp;
}
// Blit with pre-computed lookups — no per-pixel divisions or clip checks
for (int32_t dy = rowStart; dy < rowEnd; dy++) {
int32_t sy = (dy * srcH) / dstH;
uint8_t *dstRow = d->backBuf + (dstY + dy) * d->pitch + (dstX + colStart) * bpp;
const uint8_t *srcRow = src + sy * srcPitch;
for (int32_t dx = 0; dx < dstW; dx++) {
int32_t sx = (dx * srcW) / dstW;
int32_t screenX = dstX + dx;
if (screenX < d->clipX || screenX >= d->clipX + d->clipW) {
continue;
if (bpp == 1) {
for (int32_t dx = 0; dx < visibleCols; dx++) {
dstRow[dx] = srcRow[srcXTab[dx]];
}
const uint8_t *srcPx = srcRow + sx * bpp;
uint8_t *dstPx = dstRow + screenX * bpp;
if (bpp == 1) {
*dstPx = *srcPx;
} else if (bpp == 2) {
*(uint16_t *)dstPx = *(const uint16_t *)srcPx;
} else {
*(uint32_t *)dstPx = *(const uint32_t *)srcPx;
} else if (bpp == 2) {
for (int32_t dx = 0; dx < visibleCols; dx++) {
*(uint16_t *)(dstRow + dx * 2) = *(const uint16_t *)(srcRow + srcXTab[dx]);
}
} else {
for (int32_t dx = 0; dx < visibleCols; dx++) {
*(uint32_t *)(dstRow + dx * 4) = *(const uint32_t *)(srcRow + srcXTab[dx]);
}
}
}
@ -345,45 +419,30 @@ static void drawScrollbarArrow(DisplayT *d, const BlitOpsT *ops, const ColorSche
// ============================================================
static void drawTitleBar(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors, WindowT *win) {
int32_t titleX = win->x + CHROME_BORDER_WIDTH;
int32_t titleY = win->y + CHROME_BORDER_WIDTH;
int32_t titleW = win->w - CHROME_BORDER_WIDTH * 2;
int32_t titleH = CHROME_TITLE_HEIGHT;
TitleGeomT g;
computeTitleGeom(win, &g);
uint32_t bg = win->focused ? colors->activeTitleBg : colors->inactiveTitleBg;
uint32_t fg = win->focused ? colors->activeTitleFg : colors->inactiveTitleFg;
// Fill title bar background
rectFill(d, ops, titleX, titleY, titleW, titleH, bg);
// GEOS Motif gadget sizing
int32_t gadgetS = titleH - 4; // gadget size, fits within title bar
int32_t gadgetY = titleY + 2;
int32_t gadgetPad = 2;
rectFill(d, ops, g.titleX, g.titleY, g.titleW, g.titleH, bg);
// Close gadget on the LEFT (system menu / close)
int32_t closeX = titleX + gadgetPad;
drawTitleGadget(d, ops, colors, closeX, gadgetY, gadgetS);
drawTitleGadget(d, ops, colors, g.closeX, g.gadgetY, g.gadgetS);
// Horizontal bar icon inside close gadget
drawHLine(d, ops, closeX + 3, gadgetY + gadgetS / 2,
gadgetS - 6, colors->contentFg);
drawHLine(d, ops, g.closeX + 3, g.gadgetY + g.gadgetS / 2,
g.gadgetS - 6, colors->contentFg);
// Right edge of title text area
int32_t textRightEdge = titleX + titleW - gadgetPad;
// Rightmost gadget position
int32_t rightGadgetX = titleX + titleW - gadgetPad - gadgetS;
if (win->resizable) {
if (g.maxX >= 0) {
// Maximize/restore gadget on the far RIGHT
int32_t maxX = rightGadgetX;
drawTitleGadget(d, ops, colors, maxX, gadgetY, gadgetS);
drawTitleGadget(d, ops, colors, g.maxX, g.gadgetY, g.gadgetS);
if (win->maximized) {
// Restore icon: larger box outline filling more of the gadget
int32_t bx = maxX + 2;
int32_t by = gadgetY + 2;
int32_t bs = gadgetS - 4;
int32_t bx = g.maxX + 2;
int32_t by = g.gadgetY + 2;
int32_t bs = g.gadgetS - 4;
rectFill(d, ops, bx, by, bs, bs, colors->buttonFace);
drawHLine(d, ops, bx, by, bs, colors->contentFg);
drawHLine(d, ops, bx, by + 1, bs, colors->contentFg);
@ -392,30 +451,24 @@ static void drawTitleBar(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *fo
drawVLine(d, ops, bx + bs - 1, by, bs, colors->contentFg);
} else {
// Maximize icon: small box outline
rectFill(d, ops, maxX + 3, gadgetY + 3,
gadgetS - 6, gadgetS - 6, colors->buttonFace);
drawHLine(d, ops, maxX + 3, gadgetY + 3, gadgetS - 6, colors->contentFg);
drawHLine(d, ops, maxX + 3, gadgetY + gadgetS - 4, gadgetS - 6, colors->contentFg);
drawVLine(d, ops, maxX + 3, gadgetY + 3, gadgetS - 6, colors->contentFg);
drawVLine(d, ops, maxX + gadgetS - 4, gadgetY + 3, gadgetS - 6, colors->contentFg);
rectFill(d, ops, g.maxX + 3, g.gadgetY + 3,
g.gadgetS - 6, g.gadgetS - 6, colors->buttonFace);
drawHLine(d, ops, g.maxX + 3, g.gadgetY + 3, g.gadgetS - 6, colors->contentFg);
drawHLine(d, ops, g.maxX + 3, g.gadgetY + g.gadgetS - 4, g.gadgetS - 6, colors->contentFg);
drawVLine(d, ops, g.maxX + 3, g.gadgetY + 3, g.gadgetS - 6, colors->contentFg);
drawVLine(d, ops, g.maxX + g.gadgetS - 4, g.gadgetY + 3, g.gadgetS - 6, colors->contentFg);
}
rightGadgetX -= gadgetS + gadgetPad;
}
// Minimize gadget (always present, to the left of maximize or on the far right)
int32_t minX = rightGadgetX;
drawTitleGadget(d, ops, colors, minX, gadgetY, gadgetS);
// Minimize gadget (always present)
drawTitleGadget(d, ops, colors, g.minX, g.gadgetY, g.gadgetS);
// Small square centered in minimize gadget
int32_t miniSize = 4;
rectFill(d, ops, minX + (gadgetS - miniSize) / 2, gadgetY + (gadgetS - miniSize) / 2,
rectFill(d, ops, g.minX + (g.gadgetS - miniSize) / 2, g.gadgetY + (g.gadgetS - miniSize) / 2,
miniSize, miniSize, colors->contentFg);
textRightEdge = minX - gadgetPad;
// Title text — centered between close gadget and right edge, truncated to fit
int32_t textLeftEdge = closeX + gadgetS + gadgetPad;
int32_t availW = textRightEdge - textLeftEdge;
// Title text — centered between close gadget and minimize, truncated to fit
int32_t availW = g.textRightEdge - g.textLeftEdge;
if (availW > 0) {
int32_t maxChars = availW / font->charWidth;
@ -426,15 +479,16 @@ static void drawTitleBar(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *fo
}
if (len > 0) {
int32_t textW = len * font->charWidth;
int32_t textX = textLeftEdge + (availW - textW) / 2;
int32_t textY = titleY + (titleH - font->charHeight) / 2;
// Build truncated title for drawText
char truncTitle[MAX_TITLE_LEN];
memcpy(truncTitle, win->title, len);
truncTitle[len] = '\0';
// Draw truncated title character by character
for (int32_t i = 0; i < len; i++) {
drawChar(d, ops, font, textX + i * font->charWidth, textY,
win->title[i], fg, bg, true);
}
int32_t textW = len * font->charWidth;
int32_t textX = g.textLeftEdge + (availW - textW) / 2;
int32_t textY = g.titleY + (g.titleH - font->charHeight) / 2;
drawText(d, ops, font, textX, textY, truncTitle, fg, bg, true);
}
}
}
@ -464,13 +518,13 @@ static int32_t scrollbarThumbInfo(const ScrollbarT *sb, int32_t *thumbPos, int32
return trackLen;
}
*thumbSize = (sb->pageSize * trackLen) / (range + sb->pageSize);
*thumbSize = (int32_t)(((int64_t)sb->pageSize * trackLen) / (range + sb->pageSize));
if (*thumbSize < SCROLLBAR_WIDTH) {
*thumbSize = SCROLLBAR_WIDTH;
}
*thumbPos = ((sb->value - sb->min) * (trackLen - *thumbSize)) / range;
*thumbPos = (int32_t)(((int64_t)(sb->value - sb->min) * (trackLen - *thumbSize)) / range);
return trackLen;
}
@ -944,46 +998,34 @@ int32_t wmHitTest(const WindowStackT *stack, int32_t mx, int32_t my, int32_t *hi
continue;
}
// Title bar gadget geometry (must match drawTitleBar)
int32_t titleX = win->x + CHROME_BORDER_WIDTH;
int32_t titleY = win->y + CHROME_BORDER_WIDTH;
int32_t titleW = win->w - CHROME_BORDER_WIDTH * 2;
int32_t gadgetS = CHROME_TITLE_HEIGHT - 4;
int32_t gadgetY = titleY + 2;
int32_t gadgetP = 2;
TitleGeomT g;
computeTitleGeom(win, &g);
// Close gadget (top-left)
int32_t closeX = titleX + gadgetP;
if (mx >= closeX && mx < closeX + gadgetS &&
my >= gadgetY && my < gadgetY + gadgetS) {
if (mx >= g.closeX && mx < g.closeX + g.gadgetS &&
my >= g.gadgetY && my < g.gadgetY + g.gadgetS) {
*hitPart = 2;
return i;
}
// Rightmost gadget position
int32_t rightGadgetX = titleX + titleW - gadgetP - gadgetS;
// Maximize gadget only on resizable windows
if (win->resizable) {
if (mx >= rightGadgetX && mx < rightGadgetX + gadgetS &&
my >= gadgetY && my < gadgetY + gadgetS) {
*hitPart = 8;
return i;
}
rightGadgetX -= gadgetS + gadgetP;
// Maximize gadget (resizable windows only)
if (g.maxX >= 0 &&
mx >= g.maxX && mx < g.maxX + g.gadgetS &&
my >= g.gadgetY && my < g.gadgetY + g.gadgetS) {
*hitPart = 8;
return i;
}
// Minimize gadget (always present)
if (mx >= rightGadgetX && mx < rightGadgetX + gadgetS &&
my >= gadgetY && my < gadgetY + gadgetS) {
if (mx >= g.minX && mx < g.minX + g.gadgetS &&
my >= g.gadgetY && my < g.gadgetY + g.gadgetS) {
*hitPart = 7;
return i;
}
// Title bar (drag area — between gadgets)
if (my >= titleY && my < titleY + CHROME_TITLE_HEIGHT &&
mx >= titleX && mx < titleX + titleW) {
if (my >= g.titleY && my < g.titleY + CHROME_TITLE_HEIGHT &&
mx >= g.titleX && mx < g.titleX + g.titleW) {
*hitPart = 1;
return i;
}