diff --git a/src/Backends/Rendering/Software.cpp b/src/Backends/Rendering/Software.cpp index f59c0d63..572fc06b 100644 --- a/src/Backends/Rendering/Software.cpp +++ b/src/Backends/Rendering/Software.cpp @@ -3,6 +3,8 @@ #include "../Rendering.h" +#include +#include #include #include #include @@ -14,12 +16,16 @@ #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) +int surfaceCount = 0; + typedef struct RenderBackend_Surface { unsigned char *pixels; size_t width; size_t height; size_t pitch; + int id; + std::set* cheapRectangles; } RenderBackend_Surface; typedef struct RenderBackend_GlyphAtlas @@ -86,12 +92,16 @@ RenderBackend_Surface* RenderBackend_CreateSurface(size_t width, size_t height, surface->width = width; surface->height = height; surface->pitch = width * 4; + surface->id = surfaceCount; + surface->cheapRectangles = new std::set; + surfaceCount += 1; return surface; } void RenderBackend_FreeSurface(RenderBackend_Surface *surface) { + delete surface->cheapRectangles; free(surface->pixels); free(surface); } @@ -108,29 +118,106 @@ void RenderBackend_RestoreSurface(RenderBackend_Surface *surface) (void)surface; } +static void dumpSurface(RenderBackend_Surface* surface) { + char filename[64] = {0}; + sprintf(filename, "s%02d.ppm", surface->id); + FILE* fp = fopen(filename, "w"); + fprintf(fp, "P3\n%ld %ld\n255\n", surface->width, surface->height); + + for(int y = 0; y < surface->height; y++) { + int lineidx = y * surface->pitch; + for(int x = 0; x < surface->width; x++) { + int pixidx = lineidx + (x * 4); +#ifdef LITTLE_ENDIAN + fprintf(fp, "%d %d %d\n", + surface->pixels[pixidx + 2], + surface->pixels[pixidx + 1], + surface->pixels[pixidx + 0]); +#else + // Don't bother dumping out surfaces on the Sun. + // Hacky as hell, I know, but this isn't a forever project. +#endif + } + } + + fclose(fp); +} + +// Determines whether a particular tile uses transparency (color keying). +static bool rectUsesColorKey(RenderBackend_Surface* surface, RenderBackend_Rect* rect) { + for(int line = rect->top; line < rect->bottom; line++) { + for(int column = rect->left; column < rect->right; column++) { + unsigned index = (line * surface->pitch) + (column * 4); + if(surface->pixels[index + 0] == 0 && + surface->pixels[index + 1] == 0 && + surface->pixels[index + 2] == 0 && + surface->pixels[index + 3] == 0) { + return true; + } + } + } + + return false; +} + +// Goes through the surface and determines which 16x16 tiles can be drawn +// without using color keying. Such tiles are "cheap". +static void computeCheapRects(RenderBackend_Surface* surface) { + int rcolumns = surface->width / 16; + int rlines = surface->height / 16; + + for(unsigned int ry = 0; ry < rlines; ry++) { + for(unsigned int rx = 0; rx < rcolumns; rx++) { + unsigned int px = rx * 16; + unsigned int py = ry * 16; + RenderBackend_Rect rect = { .left = px, .top = py, .right = px + 16, .bottom = py + 16 }; + if(rectUsesColorKey(surface, &rect) == false) { + unsigned int rectOrigin = (px << 16) | py; + surface->cheapRectangles->insert(rectOrigin); + } + } + } +} + void RenderBackend_UploadSurface(RenderBackend_Surface *surface, const unsigned char *pixels, size_t width, size_t height) { + surface->cheapRectangles->clear(); + for (size_t y = 0; y < height; ++y) { int srcl = y * width * 3; int dstl = y * surface->pitch; for (size_t x = 0; x < width; ++x) { int srcr = x * 3; int dstr = x * 4; + #ifdef LITTLE_ENDIAN surface->pixels[dstl + dstr + 0] = pixels[srcl + srcr + 2]; surface->pixels[dstl + dstr + 1] = pixels[srcl + srcr + 1]; surface->pixels[dstl + dstr + 2] = pixels[srcl + srcr + 0]; + surface->pixels[dstl + dstr + 3] = 0; #else + surface->pixels[dstl + dstr + 0] = 0; surface->pixels[dstl + dstr + 1] = pixels[srcl + srcr + 2]; surface->pixels[dstl + dstr + 2] = pixels[srcl + srcr + 1]; surface->pixels[dstl + dstr + 3] = pixels[srcl + srcr + 0]; #endif } } + + computeCheapRects(surface); + +#ifdef LITTLE_ENDIAN // HACK!! Only dump surfaces on a little endian machine, i.e. my dev machine. + dumpSurface(surface); +#endif } ATTRIBUTE_HOT void RenderBackend_Blit(RenderBackend_Surface *source_surface, const RenderBackend_Rect *rect, RenderBackend_Surface *destination_surface, long x, long y, bool colour_key) { + // If the empty tile is used, don't bother blitting. + if(source_surface->id == 20 && rect->left == 0 && rect->top == 0) { + return; + } + RenderBackend_Rect rect_clamped; rect_clamped.left = rect->left; @@ -169,6 +256,13 @@ ATTRIBUTE_HOT void RenderBackend_Blit(RenderBackend_Surface *source_surface, con if (rect_clamped.right - rect_clamped.left <= 0) return; + // If we're using a 16x16 tile... + if(rect->bottom - rect->top == 16 || rect->right - rect->left == 16) { + // ...enable color key only if this rectangle isn't a known cheap one. + // This way we don't need colorkeying for tiles that aren't transparent + colour_key = (source_surface->cheapRectangles->count((rect->left << 16) | rect->top) == 0); + } + // Do the actual blitting if (colour_key) {