platform-game/Forráskód/modules/auxiliary/gfxPrimitives.c
Andras Schmelczer c9b542eccf
All checks were successful
Build & deploy web version / Build WebAssembly & deploy (push) Successful in 1m8s
Migrate to SDL2 and deploy as WASM
2026-08-09 20:00:56 +01:00

300 lines
9.4 KiB
C

#include "gfxPrimitives.h"
#include <math.h>
#include <stdbool.h>
#include <stdlib.h>
typedef struct target {
SDL_Surface* surface;
Uint32* pixels;
int pitch, w, h;
int rShift, gShift, bShift;
Uint32 aMask;
bool locked;
} target;
static bool renderingEnabled = true; /**< Kikapcsolva minden rajzoló primitív azonnal visszatér. */
/** \brief Ki-be kapcsolja a rajzolást.
* A behozott képkockákat a main.c "utolérő" ciklusa többször is végigszimulálja, de csak
* az utolsót mutatja meg. A közbülső lépések alatt kikapcsolva a rajzolás nem dolgozik feleslegesen.
*/
void setRenderingEnabled(bool enabled) {
renderingEnabled = enabled;
}
bool isRenderingEnabled() {
return renderingEnabled;
}
static bool beginTarget(SDL_Surface* dst, target* t) {
if(!renderingEnabled || dst == NULL || dst -> format -> BytesPerPixel != 4) return false;
t -> surface = dst;
t -> locked = SDL_MUSTLOCK(dst);
if(t -> locked && SDL_LockSurface(dst) != 0) return false;
t -> pixels = (Uint32*) dst -> pixels;
t -> pitch = dst -> pitch / 4;
t -> w = dst -> w;
t -> h = dst -> h;
t -> rShift = dst -> format -> Rshift;
t -> gShift = dst -> format -> Gshift;
t -> bShift = dst -> format -> Bshift;
t -> aMask = dst -> format -> Amask;
return true;
}
static void endTarget(target* t) {
if(t -> locked) SDL_UnlockSurface(t -> surface);
}
static inline Uint32 packColor(const target* t, Uint8 r, Uint8 g, Uint8 b) {
return t -> aMask | ((Uint32) r << t -> rShift) | ((Uint32) g << t -> gShift) | ((Uint32) b << t -> bShift);
}
static inline void blendPixel(const target* t, int x, int y, Uint8 r, Uint8 g, Uint8 b, int a) {
if(a <= 0 || x < 0 || y < 0 || x >= t -> w || y >= t -> h) return;
Uint32* p = t -> pixels + y * t -> pitch + x;
if(a >= 255) {
*p = packColor(t, r, g, b);
return;
}
Uint32 c = *p;
int ia = 255 - a;
Uint32 dr = (c >> t -> rShift) & 0xFF;
Uint32 dg = (c >> t -> gShift) & 0xFF;
Uint32 db = (c >> t -> bShift) & 0xFF;
*p = packColor(t,
(Uint8) ((r * a + dr * ia + 127) / 255),
(Uint8) ((g * a + dg * ia + 127) / 255),
(Uint8) ((b * a + db * ia + 127) / 255));
}
static void blendHLine(const target* t, int xa, int xb, int y, Uint8 r, Uint8 g, Uint8 b, int a) {
if(y < 0 || y >= t -> h || a <= 0) return;
if(xa > xb) { int tmp = xa; xa = xb; xb = tmp; }
if(xa < 0) xa = 0;
if(xb > t -> w - 1) xb = t -> w - 1;
if(xa > xb) return;
Uint32* p = t -> pixels + y * t -> pitch + xa;
int i;
if(a >= 255) {
Uint32 c = packColor(t, r, g, b);
for(i = xa; i <= xb; i++) *p++ = c;
}
else {
int ia = 255 - a;
int ra = r * a, ga = g * a, ba = b * a;
for(i = xa; i <= xb; i++, p++) {
Uint32 c = *p;
*p = packColor(t,
(Uint8) ((ra + ((c >> t -> rShift) & 0xFF) * ia + 127) / 255),
(Uint8) ((ga + ((c >> t -> gShift) & 0xFF) * ia + 127) / 255),
(Uint8) ((ba + ((c >> t -> bShift) & 0xFF) * ia + 127) / 255));
}
}
}
static inline void unpackColor(Uint32 color, Uint8* r, Uint8* g, Uint8* b, Uint8* a) {
*r = (color >> 24) & 0xFF;
*g = (color >> 16) & 0xFF;
*b = (color >> 8) & 0xFF;
*a = color & 0xFF;
}
int boxRGBA(SDL_Surface* dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
target t;
if(!beginTarget(dst, &t)) return -1;
if(x1 > x2) { Sint16 tmp = x1; x1 = x2; x2 = tmp; }
if(y1 > y2) { Sint16 tmp = y1; y1 = y2; y2 = tmp; }
int yStart = y1 < 0 ? 0 : y1;
int yEnd = y2 > t.h - 1 ? t.h - 1 : y2;
int y;
for(y = yStart; y <= yEnd; y++) blendHLine(&t, x1, x2, y, r, g, b, a);
endTarget(&t);
return 0;
}
int boxColor(SDL_Surface* dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color) {
Uint8 r, g, b, a;
unpackColor(color, &r, &g, &b, &a);
return boxRGBA(dst, x1, y1, x2, y2, r, g, b, a);
}
static int lineRGBA(SDL_Surface* dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
target t;
if(!beginTarget(dst, &t)) return -1;
int dx = abs(x2 - x1), dy = -abs(y2 - y1);
int sx = x1 < x2 ? 1 : -1, sy = y1 < y2 ? 1 : -1;
int err = dx + dy;
int x = x1, y = y1;
while(true) {
blendPixel(&t, x, y, r, g, b, a);
if(x == x2 && y == y2) break;
int e2 = 2 * err;
if(e2 >= dy) { err += dy; x += sx; }
if(e2 <= dx) { err += dx; y += sy; }
}
endTarget(&t);
return 0;
}
int lineColor(SDL_Surface* dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color) {
Uint8 r, g, b, a;
unpackColor(color, &r, &g, &b, &a);
return lineRGBA(dst, x1, y1, x2, y2, r, g, b, a);
}
int filledCircleColor(SDL_Surface* dst, Sint16 x, Sint16 y, Sint16 rad, Uint32 color) {
Uint8 r, g, b, a;
unpackColor(color, &r, &g, &b, &a);
target t;
if(!beginTarget(dst, &t)) return -1;
int dy;
for(dy = -rad; dy <= rad; dy++) {
int span = (int) floor(sqrt((double) rad * rad - (double) dy * dy));
blendHLine(&t, x - span, x + span, y + dy, r, g, b, a);
}
endTarget(&t);
return 0;
}
int aacircleColor(SDL_Surface* dst, Sint16 x, Sint16 y, Sint16 rad, Uint32 color) {
Uint8 r, g, b, a;
unpackColor(color, &r, &g, &b, &a);
target t;
if(!beginTarget(dst, &t)) return -1;
int dx, dy;
for(dy = -rad - 1; dy <= rad + 1; dy++)
for(dx = -rad - 1; dx <= rad + 1; dx++) {
double d = sqrt((double) dx * dx + (double) dy * dy);
double coverage = 1.0 - fabs(d - rad);
if(coverage > 0.0)
blendPixel(&t, x + dx, y + dy, r, g, b, (int) (a * (coverage > 1.0 ? 1.0 : coverage)));
}
endTarget(&t);
return 0;
}
int filledPolygonRGBA(SDL_Surface* dst, const Sint16* vx, const Sint16* vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
if(n < 3) return -1;
target t;
if(!beginTarget(dst, &t)) return -1;
//A játék végig négyszögeket rajzol, azoknak bőven elég a veremben álló tömb;
//képkockánként több tízezer malloc/free párt spórolunk vele.
double inlineCrossings[16];
double* crossings = inlineCrossings;
if(n > (int)(sizeof(inlineCrossings) / sizeof(*inlineCrossings))) {
crossings = (double*) malloc(n * sizeof(double));
if(crossings == NULL) { endTarget(&t); return -1; }
}
int minY = vy[0], maxY = vy[0];
int i;
for(i = 1; i < n; i++) {
if(vy[i] < minY) minY = vy[i];
if(vy[i] > maxY) maxY = vy[i];
}
int yStart = minY < 0 ? 0 : minY;
int yEnd = maxY > t.h - 1 ? t.h - 1 : maxY;
int y;
for(y = yStart; y <= yEnd; y++) {
int count = 0;
for(i = 0; i < n; i++) {
int j = (i + 1) % n;
int y1 = vy[i], y2 = vy[j], x1 = vx[i], x2 = vx[j];
if(y1 > y2) { int tmp = y1; y1 = y2; y2 = tmp; tmp = x1; x1 = x2; x2 = tmp; }
if((y >= y1 && y < y2) || (y == maxY && y > y1 && y <= y2))
crossings[count++] = x1 + ((double) (y - y1) * (x2 - x1)) / (y2 - y1);
}
if(count < 2) continue;
//Néhány elemre a beszúrásos rendezés gyorsabb, mint a qsort függvénypointeres hívogatása
int k;
for(i = 1; i < count; i++) {
double key = crossings[i];
for(k = i - 1; k >= 0 && crossings[k] > key; k--) crossings[k + 1] = crossings[k];
crossings[k + 1] = key;
}
for(i = 0; i + 1 < count; i += 2)
blendHLine(&t, (int) floor(crossings[i] + 0.5), (int) floor(crossings[i + 1] + 0.5), y, r, g, b, a);
}
if(crossings != inlineCrossings) free(crossings);
endTarget(&t);
return 0;
}
static void aaLine(const target* t, double x0, double y0, double x1, double y1, Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
bool steep = fabs(y1 - y0) > fabs(x1 - x0);
double tmp;
if(steep) { tmp = x0; x0 = y0; y0 = tmp; tmp = x1; x1 = y1; y1 = tmp; }
if(x0 > x1) { tmp = x0; x0 = x1; x1 = tmp; tmp = y0; y0 = y1; y1 = tmp; }
double dx = x1 - x0;
double gradient = dx == 0.0 ? 0.0 : (y1 - y0) / dx;
int xStart = (int) floor(x0 + 0.5), xEnd = (int) floor(x1 + 0.5);
double intersection = y0 + gradient * (xStart - x0);
int x;
for(x = xStart; x <= xEnd; x++) {
int base = (int) floor(intersection);
double frac = intersection - base;
if(steep) {
blendPixel(t, base, x, r, g, b, (int) (a * (1.0 - frac)));
blendPixel(t, base + 1, x, r, g, b, (int) (a * frac));
}
else {
blendPixel(t, x, base, r, g, b, (int) (a * (1.0 - frac)));
blendPixel(t, x, base + 1, r, g, b, (int) (a * frac));
}
intersection += gradient;
}
}
int aapolygonRGBA(SDL_Surface* dst, const Sint16* vx, const Sint16* vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
if(n < 2) return -1;
target t;
if(!beginTarget(dst, &t)) return -1;
int i;
for(i = 0; i < n; i++) {
int j = (i + 1) % n;
aaLine(&t, vx[i], vy[i], vx[j], vy[j], r, g, b, a);
}
endTarget(&t);
return 0;
}