ref: aa6147bf1c3ab5d88a80abffc0c515f5abfbd814
parent: 71da5810d8ddba2808f34d24452e1bb794904298
author: Jacob Moody <moody@posixcafe.org>
date: Thu Dec 14 19:33:15 EST 2023
implement proper renderer
--- a/mkfile
+++ b/mkfile
@@ -81,8 +81,7 @@
src/Backends/Platform/9front.$O\
src/Backends/Audio/9front.$O\
src/Backends/Controller/9front.$O\
- src/Backends/Rendering/Software.$O\
- src/Backends/Rendering/Window/Software/9front.$O\
+ src/Backends/Rendering/9front.$O\
ASSETS=\
src/Resource/BITMAP/Credit01.bmp.h\
--- a/src/Backends/Platform/9front.cpp
+++ b/src/Backends/Platform/9front.cpp
@@ -13,7 +13,6 @@
static int keyboard_state[BACKEND_KEYBOARD_TOTAL];
static Mousectl *mctl;
-Channel *drawreq; /* Chan(Memimage*) */
int Mark_Rune(int *kbd, Rune r)
{
@@ -130,7 +129,7 @@
void Backend_Proc(void*)
{
- enum { Aresize, Amouse, Adraw, Aend };
+ enum { Aresize, Amouse, Aend };
Mouse m;
Memimage *b;
Memimage *memscreen = nil;
@@ -138,28 +137,16 @@
Alt a[] = {
[Amouse] { nil, &m, CHANRCV },
[Aresize] { nil, nil, CHANRCV },
- [Adraw] { drawreq, &b, CHANRCV },
[Aend] { nil, nil, CHANEND },
};
a[Amouse].c = mctl->c;
a[Aresize].c = mctl->resizec;
- goto Resize;
for(;;){
switch(alt(a)){
case Aresize:
getwindow(display, Refnone);
- Resize:
- free(memscreen);
- memscreen = allocmemimage(screen->r, screen->chan);
- if(memscreen == nil)
- sysfatal("allocmemimage: %r");
break;
- case Adraw:
- memimagedraw(memscreen, memscreen->r, b, ZP, nil, ZP, S);
- loadimage(screen, screen->r, memscreen->data->bdata, Dx(screen->r)*Dy(screen->r)*(screen->depth/8));
- flushimage(display, 1);
- break;
}
}
}
@@ -173,7 +160,6 @@
memimageinit();
if(initdraw(nil, nil, "cstory") < 0)
sysfatal("initdraw: %r");
- drawreq = chancreate(sizeof(Channel*), 1);
mctl = initmouse(nil, screen);
if(mctl == nil)
sysfatal("initmouse: %r");
--- /dev/null
+++ b/src/Backends/Rendering/9front.cpp
@@ -1,0 +1,314 @@
+// Released under the MIT licence.
+// See LICENCE.txt for details.
+
+#include "../Rendering.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <draw.h>
+#include <memdraw.h>
+
+#include "../Misc.h"
+#include "Window/Software.h"
+#include "../../Attributes.h"
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+typedef struct RenderBackend_Surface
+{
+ Image *i;
+ Image *mask;
+ int dirty;
+} RenderBackend_Surface;
+
+typedef struct RenderBackend_GlyphAtlas
+{
+ Memimage *m;
+ Image *color;
+ int dirty;
+ Image *cache;
+} RenderBackend_GlyphAtlas;
+
+static RenderBackend_Surface framebuffer;
+
+static RenderBackend_GlyphAtlas *glyph_atlas;
+static RenderBackend_Surface *glyph_destination_surface;
+
+RenderBackend_Surface* RenderBackend_Init(const char *window_title, size_t width, size_t height, int fullscreen)
+{
+ framebuffer.i = allocimage(display, Rect(0, 0, width, height), screen->chan, 0, DBlue);
+ if(framebuffer.i == nil)
+ sysfatal("could not alloc screen");
+
+ return &framebuffer;
+}
+
+void RenderBackend_Deinit(void)
+{
+ freeimage(framebuffer.i);
+}
+
+void RenderBackend_DrawScreen(void)
+{
+ draw(screen, screen->r, framebuffer.i, nil, ZP);
+ flushimage(display, 1);
+}
+
+RenderBackend_Surface* RenderBackend_CreateSurface(size_t width, size_t height, int render_target)
+{
+ (void)render_target;
+
+ RenderBackend_Surface *surface = mallocz(sizeof(RenderBackend_Surface), 1);
+
+ if (surface == NULL)
+ return NULL;
+
+ surface->i = allocimage(display, Rect(0, 0, width, height), screen->chan, 0, DGreen);
+
+ return surface;
+}
+
+void RenderBackend_FreeSurface(RenderBackend_Surface *surface)
+{
+ freeimage(surface->i);
+ freeimage(surface->mask);
+ free(surface);
+}
+
+int RenderBackend_IsSurfaceLost(RenderBackend_Surface *surface)
+{
+ (void)surface;
+
+ return 0;
+}
+
+void RenderBackend_RestoreSurface(RenderBackend_Surface *surface)
+{
+ (void)surface;
+}
+
+void RenderBackend_UploadSurface(RenderBackend_Surface *surface, const unsigned char *pixels, size_t width, size_t height)
+{
+ Image *i;
+ Rectangle r;
+
+ r = Rect(0, 0, width, height);
+ i = allocimage(display, r, BGR24, 0, DYellow);
+ loadimage(i, r, pixels, width*height*3);
+ draw(surface->i, r, i, nil, ZP);
+ freeimage(i);
+ surface->dirty = 1;
+}
+
+void RenderBackend_CalcMask(RenderBackend_Surface *s)
+{
+ Memimage *m;
+ Rectangle r;
+ int x, y, w, h;
+ ulong *lp;
+ ulong mask;
+
+ switch(s->i->chan){
+ case XRGB32:
+ case ARGB32:
+ case XBGR32:
+ case ABGR32:
+ mask = 0xFFFFFF;
+ break;
+ case RGBA32:
+ mask = 0xFFFFFF00;
+ break;
+ default:
+ /* TODO? */
+ sysfatal("< 32 bit screen color channel");
+ return;
+ }
+ r = s->i->r;
+ w = Dx(r);
+ h = Dy(r);
+ m = allocmemimage(r, s->i->chan);
+ unloadimage(s->i, r, m->data->bdata, h*w*(s->i->depth/8));
+ for(y = 0; y < h; y++)
+ for(x = 0; x < w; x++){
+ lp = m->data->base + y*w + x;
+ if((*lp & mask) == 0)
+ *lp = 0x00;
+ else
+ *lp = 0xFFFFFFFF;
+ }
+ freeimage(s->mask);
+ s->mask = allocimage(display, r, screen->chan, 0, DBlue);
+ loadimage(s->mask, r, m->data->bdata, h*w*(s->i->depth/8));
+ freememimage(m);
+ s->dirty = 0;
+}
+
+void RenderBackend_Blit(RenderBackend_Surface *source_surface, const RenderBackend_Rect *rect, RenderBackend_Surface *destination_surface, long x, long y, int colour_key)
+{
+ Rectangle r, r2;
+ RenderBackend_Rect rect_clamped;
+
+ rect_clamped.left = rect->left;
+ rect_clamped.top = rect->top;
+ rect_clamped.right = rect->right;
+ rect_clamped.bottom = rect->bottom;
+
+ // Clamp the rect and coordinates so we don't write outside the pixel buffer
+ long overflow;
+
+ overflow = 0 - x;
+ if (overflow > 0)
+ {
+ rect_clamped.left += overflow;
+ x += overflow;
+ }
+
+ overflow = 0 - y;
+ if (overflow > 0)
+ {
+ rect_clamped.top += overflow;
+ y += overflow;
+ }
+
+ overflow = (x + (rect_clamped.right - rect_clamped.left)) - destination_surface->i->r.max.x;
+ if (overflow > 0)
+ rect_clamped.right -= overflow;
+
+ overflow = (y + (rect_clamped.bottom - rect_clamped.top)) - destination_surface->i->r.max.y;
+ if (overflow > 0)
+ rect_clamped.bottom -= overflow;
+
+ if (rect_clamped.bottom - rect_clamped.top <= 0)
+ return;
+
+ if (rect_clamped.right - rect_clamped.left <= 0)
+ return;
+
+ r = Rect(rect_clamped.left, rect_clamped.top, rect_clamped.right, rect_clamped.bottom);
+ r2 = Rect(x, y, x+Dx(r), y+Dy(r));
+
+ if(colour_key && source_surface->dirty)
+ RenderBackend_CalcMask(source_surface);
+
+ draw(destination_surface->i, r2, source_surface->i, colour_key ? source_surface->mask : nil, r.min);
+ destination_surface->dirty = 1;
+}
+
+void RenderBackend_ColourFill(RenderBackend_Surface *surface, const RenderBackend_Rect *rect, unsigned char red, unsigned char green, unsigned char blue)
+{
+ Rectangle r;
+ Image *color;
+ RenderBackend_Rect rect_clamped;
+
+ rect_clamped.left = rect->left;
+ rect_clamped.top = rect->top;
+ rect_clamped.right = rect->right;
+ rect_clamped.bottom = rect->bottom;
+
+ // Clamp the rect so it doesn't write outside the pixel buffer
+ long overflow;
+
+ overflow = 0 - rect_clamped.left;
+ if (overflow > 0)
+ rect_clamped.left += overflow;
+
+ overflow = 0 - rect_clamped.top;
+ if (overflow > 0)
+ rect_clamped.top += overflow;
+
+ overflow = rect_clamped.right - surface->i->r.max.x;
+ if (overflow > 0)
+ rect_clamped.right -= overflow;
+
+ overflow = rect_clamped.bottom - surface->i->r.max.y;
+ if (overflow > 0)
+ rect_clamped.bottom -= overflow;
+
+ if (rect_clamped.bottom - rect_clamped.top <= 0)
+ return;
+
+ if (rect_clamped.right - rect_clamped.left <= 0)
+ return;
+
+ r = Rect(rect_clamped.left, rect_clamped.top, rect_clamped.right, rect_clamped.bottom);
+ color = allocimage(display, Rect(0, 0, 1, 1), BGR24, 1, (red<<16)|(green<<8)|(blue<<0) );
+ draw(surface->i, surface->i->r, color, nil, ZP);
+ freeimage(color);
+ surface->dirty = 1;
+}
+
+RenderBackend_GlyphAtlas* RenderBackend_CreateGlyphAtlas(size_t width, size_t height)
+{
+ RenderBackend_GlyphAtlas *atlas = mallocz(sizeof(RenderBackend_GlyphAtlas), 1);
+ atlas->m = allocmemimage(Rect(0, 0, width, height), screen->chan);
+ atlas->dirty = 1;
+ return atlas;
+}
+
+void RenderBackend_DestroyGlyphAtlas(RenderBackend_GlyphAtlas *atlas)
+{
+ freememimage(atlas->m);
+ freeimage(atlas->cache);
+ free(atlas);
+}
+
+void RenderBackend_UploadGlyph(RenderBackend_GlyphAtlas *atlas, size_t x, size_t y, const unsigned char *pixels, size_t width, size_t height, size_t pitch)
+{
+ uchar *s, *d;
+ ulong dw, max;
+ int ix, iy;
+
+ dw = Dx(atlas->m->r);
+ max = height * pitch;
+ for (iy = 0; iy < max; iy += pitch, y += dw){
+ s = pixels + iy;
+ d = atlas->m->data->bdata + (y + x) * 4;
+
+ for (ix = 0; ix < width; ix++){
+ *d++ = *s;
+ *d++ = *s;
+ *d++ = *s;
+ *d++ = 0xFF;
+ s++;
+ }
+ }
+ atlas->dirty = 1;
+}
+
+void RenderBackend_PrepareToDrawGlyphs(RenderBackend_GlyphAtlas *atlas, RenderBackend_Surface *destination_surface, unsigned char red, unsigned char green, unsigned char blue)
+{
+ int x, y, w;
+
+ glyph_atlas = atlas;
+ glyph_destination_surface = destination_surface;
+
+ freeimage(atlas->color);
+ atlas->color = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, (red<<24)|(green<<16)|(blue<<8));
+}
+
+void RenderBackend_DrawGlyph(long x, long y, size_t glyph_x, size_t glyph_y, size_t glyph_width, size_t glyph_height)
+{
+ Rectangle r, cr;
+ Point p;
+
+ r = Rect(x, y, x+glyph_width, y+glyph_height);
+ p = Pt(glyph_x, glyph_y);
+ if(glyph_atlas->dirty){
+ cr = glyph_atlas->m->r;
+ freeimage(glyph_atlas->cache);
+ glyph_atlas->cache = allocimage(display, cr, screen->chan, 0, DYellow);
+ loadimage(glyph_atlas->cache, cr, glyph_atlas->m->data->bdata, Dx(cr)*Dy(cr)*(glyph_atlas->m->depth/8));
+ glyph_atlas->dirty = 0;
+ }
+ draw(glyph_destination_surface->i, r, glyph_atlas->color, glyph_atlas->cache, p);
+}
+
+void RenderBackend_HandleRenderTargetLoss(void)
+{
+}
+
+void RenderBackend_HandleWindowResize(size_t width, size_t height)
+{
+}
--- a/src/Backends/Rendering/Window/Software/9front.cpp
+++ /dev/null
@@ -1,58 +1,0 @@
-// Released under the MIT licence.
-// See LICENCE.txt for details.
-
-#include "../Software.h"
-
-#include <stddef.h>
-#include <stdlib.h>
-#include <thread.h>
-#include <draw.h>
-#include <memdraw.h>
-
-static Memimage *buffer[2];
-static int curbuffer = 0;
-extern Channel *drawreq;
-
-int WindowBackend_Software_CreateWindow(const char *window_title, size_t screen_width, size_t screen_height, int fullscreen)
-{
- (void)window_title;
- (void)fullscreen;
-
- buffer[0] = allocmemimage(Rect(0, 0, screen_width, screen_height), BGR24);
- buffer[1] = allocmemimage(Rect(0, 0, screen_width, screen_height), BGR24);
- if(buffer[0] == nil || buffer[1] == nil)
- sysfatal("allocmemimage: %r");
- return 1;
-}
-
-void WindowBackend_Software_DestroyWindow(void)
-{
-}
-
-unsigned char* WindowBackend_Software_GetFramebuffer(size_t *pitch)
-{
- Memimage *b;
- b = buffer[curbuffer];
- curbuffer = !curbuffer;
- *pitch = Dx(b->r)*(b->depth/8);
-
- return b->data->bdata;
-}
-
-void WindowBackend_Software_Display(void)
-{
- /*
- * If we're not ready for another frame
- * drop it. Prevents performance problems
- * resulting in slowdown of game logic.
- */
- nbsend(drawreq, &buffer[curbuffer]);
-}
-
-void WindowBackend_Software_HandleWindowResize(size_t w, size_t h)
-{
- freememimage(buffer[0]);
- freememimage(buffer[1]);
- buffer[0] = allocmemimage(Rect(0, 0, w, h), BGR24);
- buffer[1] = allocmemimage(Rect(0, 0, w, h), BGR24);
-}