/* * Roo/E, the Kangaroo Punch Portable GUI Toolkit * Copyright (C) 2026 Scott Duensing * * http://kangaroopunch.com * * * This file is part of Roo/E. * * Roo/E is free software: you can redistribute it and/or modify it under the * terms of the GNU Affero General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) * any later version. * * Roo/E is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU Affero General Public License * along with Roo/E. If not, see . * */ #include "kpssurf.h" SurfaceT *__surfaceActive = NULL; uint8_t __surfaceBitsPerPixel = 0; uint8_t __surfaceBytesPerPixel = 0; SurfaceFormatT __surfaceFormat = { 0 }; void (*surfaceLineH)(int16_t x1, int16_t x2, int16_t y, ColorT c); void (*surfaceLineV)(int16_t x, int16_t y1, int16_t y2, ColorT c); ColorT (*surfacePixelGet)(SurfaceT *surface, int16_t x, int16_t y); void (*surfacePixelSet)(uint16_t x, uint16_t y, ColorT color); static void surfaceLineH8(int16_t x1, int16_t x2, int16_t y, ColorT c); static void surfaceLineH16(int16_t x1, int16_t x2, int16_t y, ColorT c); static void surfaceLineH32(int16_t x1, int16_t x2, int16_t y, ColorT c); static void surfaceLineV8(int16_t x, int16_t y1, int16_t y2, ColorT c); static void surfaceLineV16(int16_t x, int16_t y1, int16_t y2, ColorT c); static void surfaceLineV32(int16_t x, int16_t y1, int16_t y2, ColorT c); static ColorT surfacePixelGet8(SurfaceT *surface, int16_t x, int16_t y); static ColorT surfacePixelGet16(SurfaceT *surface, int16_t x, int16_t y); static ColorT surfacePixelGet32(SurfaceT *surface, int16_t x, int16_t y); 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); void surfaceBlit(int16_t targetX, int16_t targetY, int16_t offsetX, int16_t offsetY, int16_t width, int16_t height, SurfaceT *source) { int16_t i; int16_t x = 0; int16_t y = 0; 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) x = -targetX; if (targetY < 0) y = -targetY; // Clip on right and bottom. if (targetX + width - offsetX > __surfaceActive->width) width -= targetX + width - offsetX - __surfaceActive->width; if (targetY + height - offsetY > __surfaceActive->height) height -= targetY + height - offsetY - __surfaceActive->height; // Are we still on the screen? if (x < 0 || y < 0 || width < x || height < y) return; if (targetX == 0 && targetY == 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 + y) * __surfaceActive->scanline + (targetX + x) * __surfaceBytesPerPixel; offsetSource = (y + offsetY) * source->scanline + (x + offsetX) * __surfaceBytesPerPixel; bytes = (width - x) * __surfaceBytesPerPixel; for (i=y; ibuffer.bits8[offsetTarget], &source->buffer.bits8[offsetSource], bytes); offsetTarget += __surfaceActive->scanline; offsetSource += source->scanline; } } } void surfaceBlitWithTransparency(int16_t targetX, int16_t targetY, SurfaceT *source, ColorT transparent) { int16_t x; int16_t y; int16_t x1 = 0; int16_t y1 = 0; int16_t x2 = source->width; int16_t y2 = source->height; ColorT pixel; // Clip on top and left. x1 & y1 are pixel locations inside the source bitmap. ox & oy offset those into screen coordinates. if (targetX < 0) x1 = -targetX; if (targetY < 0) y1 = -targetY; // 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; // Are we still on the screen? if (x1 < 0 || y1 < 0 || x2 < x1 || y2 < y1) return; // Blit. for (y=y1; ywidth - 1, 0, color); // Copy it to the other lines. offsetTarget = __surfaceActive->scanline; for (x=1; x<__surfaceActive->height; x++) { memcpy(&__surfaceActive->buffer.bits8[offsetTarget], &__surfaceActive->buffer.bits8[0], __surfaceActive->scanline); offsetTarget += __surfaceActive->scanline; } } ColorT surfaceColorMake(uint8_t r, uint8_t g, uint8_t b) { return (r >> __surfaceFormat.rLoss) << __surfaceFormat.rShift | (g >> __surfaceFormat.gLoss) << __surfaceFormat.gShift | (b >> __surfaceFormat.bLoss) << __surfaceFormat.bShift | ((255 >> __surfaceFormat.aLoss) << __surfaceFormat.aShift & __surfaceFormat.aMask); } SurfaceT *surfaceCreate(int16_t width, int16_t height) { SurfaceT *surface = NULL; NEW(SurfaceT, surface); if (!surface) return NULL; surface->width = width; surface->height = height; surface->scanline = width * __surfaceBytesPerPixel; surface->bytes = surface->scanline * height; surface->buffer.bits8 = malloc(surface->bytes); if (!surface->buffer.bits8) { free(surface); return NULL; } return surface; } void surfaceBox(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT c) { surfaceLineH(x1, x2, y1, c); surfaceLineH(x1, x2, y2, c); surfaceLineV(x1, y1, y2, c); surfaceLineV(x2, y1, y2, c); } void surfaceBoxFilled(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT c) { int16_t i; size_t offsetTarget; size_t offsetSource; uint16_t width; if (x1 > x2) { i = x1; x1 = x2; x2 = i; } if (y1 > y2) { i = y1; y1 = y2; y2 = i; } width = (x2 - x1 + 1) * __surfaceBytesPerPixel; // Draw the top line. surfaceLineH(x1, x2, y1, c); // Copy it to the other lines. offsetTarget = __surfaceActive->scanline * (y1 + 1) + (x1 * __surfaceBytesPerPixel); offsetSource = __surfaceActive->scanline * y1 + (x1 * __surfaceBytesPerPixel); for (i=y1 + 1; i<=y2; i++) { memcpy(&__surfaceActive->buffer.bits8[offsetTarget], &__surfaceActive->buffer.bits8[offsetSource], width); offsetTarget += __surfaceActive->scanline; } } void surfaceBoxHighlight(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT highlight, ColorT shadow) { surfaceLineH(x1, x2, y1, highlight); surfaceLineV(x1, y1, y2, highlight); surfaceLineH(x1, x2, y2, shadow); surfaceLineV(x2, y1, y2, shadow); } void surfaceDestroy(SurfaceT **surface) { SurfaceT *s = *surface; DEL(s->buffer.bits8); DEL(s); *surface = NULL; } SurfaceT *surfaceGet(void) { return __surfaceActive; } int16_t surfaceHeightGet(SurfaceT *surface) { return surface->height; } void surfaceLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT color) { int16_t x; int16_t y; int16_t dx; int16_t dy; int16_t incX; int16_t incY; int16_t balance; if (x2 >= x1) { dx = x2 - x1; incX = 1; } else { dx = x1 - x2; incX = -1; } if (y2 >= y1) { dy = y2 - y1; incY = 1; } else { dy = y1 - y2; incY = -1; } x = x1; y = y1; if (dx >= dy) { dy <<= 1; balance = dy - dx; dx <<= 1; while (x != x2) { surfacePixelSet(x, y, color); if (balance >= 0) { y += incY; balance -= dx; } balance += dy; x += incX; } surfacePixelSet(x, y, color); } else { dx <<= 1; balance = dx - dy; dy <<= 1; while (y != y2) { surfacePixelSet(x, y, color); if (balance >= 0) { x += incX; balance -= dy; } balance += dx; y += incY; } surfacePixelSet(x, y, color); } } static void surfaceLineH8(int16_t x1, int16_t x2, int16_t y, ColorT c) { int16_t i; size_t offset; if (x1 > x2) { i = x2; x2 = x1; x1 = i; } offset = y * __surfaceActive->width + x1; for (i=x1; i<=x2; i++) __surfaceActive->buffer.bits8[offset++] = (uint8_t)c; } static void surfaceLineH16(int16_t x1, int16_t x2, int16_t y, ColorT c) { int16_t i; size_t offset; if (x1 > x2) { i = x2; x2 = x1; x1 = i; } offset = y * __surfaceActive->width + x1; for (i=x1; i<=x2; i++) __surfaceActive->buffer.bits16[offset++] = (uint16_t)c; } static void surfaceLineH32(int16_t x1, int16_t x2, int16_t y, ColorT c) { int16_t i; size_t offset; if (x1 > x2) { i = x2; x2 = x1; x1 = i; } offset = y * __surfaceActive->width + x1; for (i=x1; i<=x2; i++) __surfaceActive->buffer.bits32[offset++] = (uint32_t)c; } static void surfaceLineV8(int16_t x, int16_t y1, int16_t y2, ColorT c) { int16_t i; size_t offset; if (y1 > y2) { i = y2; y2 = y1; y1 = i; } offset = y1 * __surfaceActive->width + x; for (i=y1; i<=y2; i++) { __surfaceActive->buffer.bits8[offset] = (uint8_t)c; offset += __surfaceActive->width; } } static void surfaceLineV16(int16_t x, int16_t y1, int16_t y2, ColorT c) { int16_t i; size_t offset; if (y1 > y2) { i = y2; y2 = y1; y1 = i; } offset = y1 * __surfaceActive->width + x; for (i=y1; i<=y2; i++) { __surfaceActive->buffer.bits16[offset] = (uint16_t)c; offset += __surfaceActive->width; } } static void surfaceLineV32(int16_t x, int16_t y1, int16_t y2, ColorT c) { int16_t i; size_t offset; if (y1 > y2) { i = y2; y2 = y1; y1 = i; } offset = y1 * __surfaceActive->width + x; for (i=y1; i<=y2; i++) { __surfaceActive->buffer.bits32[offset] = (uint32_t)c; offset += __surfaceActive->width; } } SurfaceT *surfaceLoad(char *filename) { char ext[5] = { 0 }; char *name = NULL; FILE *in = NULL; SurfaceT *i = NULL; sprintf(ext, "S%d", __surfaceBitsPerPixel); name = utilFileExtensionChange(filename, ext); if (!utilFileExists(name)) { in = fopen(name, "rb"); if (in) { NEW(SurfaceT, i); if (!i) return NULL; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-result" fread(&i->width, sizeof(uint16_t), 1, in); fread(&i->height, sizeof(uint16_t), 1, in); fread(&i->scanline, sizeof(size_t), 1, in); fread(&i->bytes, sizeof(size_t), 1, in); i->buffer.bits8 = (uint8_t *)malloc(i->bytes); fread(i->buffer.bits8, i->bytes, 1, in); #pragma GCC diagnostic pop fclose(in); } } DEL(name); return i; } static ColorT surfacePixelGet8(SurfaceT *surface, int16_t x, int16_t y) { return surface->buffer.bits8[y * surface->width + x]; } static ColorT surfacePixelGet16(SurfaceT *surface, int16_t x, int16_t y) { return surface->buffer.bits16[y * surface->width + x]; } static ColorT surfacePixelGet32(SurfaceT *surface, int16_t x, int16_t y) { return surface->buffer.bits32[y * surface->width + x]; } static void surfacePixelSet8(uint16_t x, uint16_t y, ColorT color) { __surfaceActive->buffer.bits8[y * __surfaceActive->width + x] = (uint8_t)color; } static void surfacePixelSet16(uint16_t x, uint16_t y, ColorT color) { __surfaceActive->buffer.bits16[y * __surfaceActive->width + x] = (uint16_t)color; } static void surfacePixelSet32(uint16_t x, uint16_t y, ColorT color) { __surfaceActive->buffer.bits32[y * __surfaceActive->width + x] = color; } void surfaceSave(SurfaceT *surface, char *filename) { char ext[5] = { 0 }; char *name = NULL; FILE *out = NULL; sprintf(ext, "S%d", __surfaceBitsPerPixel); name = utilFileExtensionChange(filename, ext); out = fopen(name, "wb"); if (out) { fwrite(&surface->width, sizeof(uint16_t), 1, out); fwrite(&surface->height, sizeof(uint16_t), 1, out); fwrite(&surface->scanline, sizeof(size_t), 1, out); fwrite(&surface->bytes, sizeof(size_t), 1, out); fwrite(surface->buffer.bits8, surface->bytes, 1, out); fclose(out); } DEL(name); } void surfaceSet(SurfaceT *surface) { __surfaceActive = surface; } void surfaceShutdown(void) { // Nada } void surfaceStartup(uint8_t bits) { uint8_t redMaskSize; uint8_t greenMaskSize; uint8_t blueMaskSize; uint8_t alphaMaskSize; __surfaceBitsPerPixel = bits; __surfaceBytesPerPixel = bits >> 3; switch (bits) { case 8: // xxx 3:3:2 alphaMaskSize = 0; redMaskSize = 3; greenMaskSize = 3; blueMaskSize = 2; surfaceLineH = surfaceLineH8; surfaceLineV = surfaceLineV8; surfacePixelSet = surfacePixelSet8; surfacePixelGet = surfacePixelGet8; break; case 16: // xx 5:6:5 alphaMaskSize = 0; redMaskSize = 5; greenMaskSize = 6; blueMaskSize = 5; surfaceLineH = surfaceLineH16; surfaceLineV = surfaceLineV16; surfacePixelSet = surfacePixelSet16; surfacePixelGet = surfacePixelGet16; break; default: // x 8:8:8 alphaMaskSize = 8; redMaskSize = 8; greenMaskSize = 8; blueMaskSize = 8; surfaceLineH = surfaceLineH32; surfaceLineV = surfaceLineV32; surfacePixelSet = surfacePixelSet32; surfacePixelGet = surfacePixelGet32; break; } __surfaceFormat.bShift = 0; __surfaceFormat.gShift = __surfaceFormat.bShift + blueMaskSize; __surfaceFormat.rShift = __surfaceFormat.gShift + greenMaskSize; __surfaceFormat.aShift = __surfaceFormat.rShift + redMaskSize; __surfaceFormat.rMask = ((1UL << redMaskSize) - 1) << __surfaceFormat.rShift; __surfaceFormat.gMask = ((1UL << greenMaskSize) - 1) << __surfaceFormat.gShift; __surfaceFormat.bMask = ((1UL << blueMaskSize) - 1) << __surfaceFormat.bShift; __surfaceFormat.aMask = ((1UL << alphaMaskSize) - 1) << __surfaceFormat.aShift; __surfaceFormat.rLoss = 8 - redMaskSize; __surfaceFormat.gLoss = 8 - greenMaskSize; __surfaceFormat.bLoss = 8 - blueMaskSize; __surfaceFormat.aLoss = 8 - alphaMaskSize; /* logWrite("Surface Red Mask %u Shift %u Loss %u\n", __surfaceFormat.rMask, __surfaceFormat.rShift, __surfaceFormat.rLoss); logWrite("Surface Green Mask %u Shift %u Loss %u\n", __surfaceFormat.gMask, __surfaceFormat.gShift, __surfaceFormat.gLoss); logWrite("Surface Blue Mask %u Shift %u Loss %u\n", __surfaceFormat.bMask, __surfaceFormat.bShift, __surfaceFormat.bLoss); logWrite("Surface Alpha Mask %u Shift %u Loss %u\n\n", __surfaceFormat.aMask, __surfaceFormat.aShift, __surfaceFormat.aLoss); */ } int16_t surfaceWidthGet(SurfaceT *surface) { return surface->width; }