From 0d60e0e144c0faf5efb9551d1d23b7463ce8a4c0 Mon Sep 17 00:00:00 2001 From: Michael Georgoulopoulos Date: Sun, 15 May 2022 21:26:50 +0300 Subject: [PATCH] Moved RleBitmap to its own module and created minifx screen to experiment --- .clang-format | 3 + GNUmakefile | 4 +- src/minifx.c | 161 +++++++++++++++++++ src/scr/grise.c | 423 +++++++------------------------------------------ src/screen.c | 5 + src/utils/RleBitmap.c | 309 ++++++++++++++++++++++++++++++++++++ src/utils/RleBitmap.h | 66 ++++++++ 7 files changed, 607 insertions(+), 364 deletions(-) create mode 100644 .clang-format create mode 100644 src/minifx.c create mode 100644 src/utils/RleBitmap.c create mode 100644 src/utils/RleBitmap.h diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..c2b7d66 --- /dev/null +++ b/.clang-format @@ -0,0 +1,3 @@ +UseTab: Always +IndentWidth: 8 +ColumnLimit: 100 diff --git a/GNUmakefile b/GNUmakefile index 3608ddf..ea8082c 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -1,4 +1,4 @@ -src = $(wildcard src/*.c) $(wildcard src/3dgfx/*.c) $(wildcard src/scr/*.c) \ +src = $(wildcard src/*.c) $(wildcard src/3dgfx/*.c) $(wildcard src/scr/*.c) $(wildcard src/utils/*.c) \ $(wildcard src/glut/*.c) asmsrc = $(wildcard src/*.asm) obj = $(src:.c=.o) $(asmsrc:.asm=.o) @@ -8,7 +8,7 @@ bin = demo asmsrc += cspr/dbgfont.asm cspr/confont.asm bindata = data/loading.img -inc = -I/usr/local/include -Isrc -Isrc/3dgfx -Isrc/scr -Isrc/glut -Ilibs \ +inc = -I/usr/local/include -Isrc -Isrc/3dgfx -Isrc/scr -Isrc/utils -Isrc/glut -Ilibs \ -Ilibs/imago/src -Ilibs/mikmod/include def = -DMINIGLUT_USE_LIBC -DMIKMOD_STATIC warn = -pedantic -Wall -Wno-unused-variable -Wno-unused-function -Wno-address diff --git a/src/minifx.c b/src/minifx.c new file mode 100644 index 0000000..59a3269 --- /dev/null +++ b/src/minifx.c @@ -0,0 +1,161 @@ +#include "demo.h" +#include "imago2.h" +#include "screen.h" +#include +#include +#include +#include +#include + +#include + +/* APPROX. 170 FPS Minimum */ + +static int init(void); +static void destroy(void); +static void start(long trans_time); +static void stop(long trans_time); +static void draw(void); + +static void updatePropeller(float t, RleBitmap *rle); + +static unsigned short *backBuffer; + +static unsigned char miniFXBuffer[1024]; + +static long lastFrameTime = 0; + +static struct screen scr = {"minifx", init, destroy, start, 0, draw}; + +struct screen *minifx_screen(void) { + return &scr; +} + +static int init(void) { + /* Allocate back buffer */ + backBuffer = (unsigned short *)calloc(FB_WIDTH * FB_HEIGHT, sizeof(unsigned short)); + + return 0; +} + +static void destroy(void) { + free(backBuffer); + backBuffer = 0; +} + +static void start(long trans_time) { lastFrameTime = time_msec; } + +static void draw(void) { + long lastFrameDuration = (time_msec - lastFrameTime) / 1000.0f; + lastFrameTime = time_msec; + + int clearColor = 0x888888; + unsigned short clearColor16 = ((clearColor << 8) & 0xF800) /* R */ + | ((clearColor >> 5) & 0x07E0) /* G */ + | ((clearColor >> 19) & 0x001F); /* B */ + for (int i=0; i= 2; + } + } + + /* Then, encode to rle */ + rleEncode(rle, miniFXBuffer, 32, 32); + + /* Distribute the produced streaks so that they don't produce garbage when interpolated */ + rleDistributeStreaks(rle); +} diff --git a/src/scr/grise.c b/src/scr/grise.c index 1295a07..36e9ff9 100644 --- a/src/scr/grise.c +++ b/src/scr/grise.c @@ -1,35 +1,22 @@ +#include "demo.h" +#include "imago2.h" +#include "screen.h" +#include +#include #include #include #include -#include -#include -#include "imago2.h" -#include "demo.h" -#include "screen.h" -/* APPROX. 170 FPS Minimum */ +#include -typedef struct { - unsigned int w, h; - unsigned char *scans; -} RLEBitmap; - -static RLEBitmap *rleCreate(unsigned int w, unsigned int h); -static void rleDestroy(RLEBitmap *b); -static void rleBlit(unsigned short *dst, int dstW, int dstH, int dstStride, - RLEBitmap *bitmap, int blitX, int blitY); -static void rleBlitScale(unsigned short *dst, int dstW, int dstH, int dstStride, - RLEBitmap *bitmap, int blitX, int blitY, float scaleX, float scaleY); -static void rleBlitScaleInv(unsigned short *dst, int dstW, int dstH, int dstStride, - RLEBitmap *bitmap, int blitX, int blitY, float scaleX, float scaleY); -static RLEBitmap *rleEncode(RLEBitmap *b, unsigned char *pixels, unsigned int w, unsigned int h); +/* APPROX. 170 FPS Minimum */ static void updatePropeller(float t); #define BG_FILENAME "data/grise.png" #define GROBJ_01_FILENAME "data/grobj_01.png" -#define BB_SIZE 512 /* Let's use a power of 2. Maybe we'll zoom/rotate the effect */ +#define BB_SIZE 512 /* Let's use a power of 2. Maybe we'll zoom/rotate the effect */ /* Every backBuffer scanline is guaranteed to have that many dummy pixels before and after */ #define PIXEL_PADDING 32 @@ -59,8 +46,6 @@ static void processNormal(); static void initScrollTables(); static void updateScrollTables(float dt); - - static unsigned short *background = 0; static int backgroundW = 0; static int backgroundH = 0; @@ -80,42 +65,37 @@ static float nearScrollAmount = 0.0f; static unsigned char miniFXBuffer[1024]; -static RLEBitmap *grobj = 0; -static RLEBitmap *rlePropeller = 0; +static RleBitmap *grobj = 0; +static RleBitmap *rlePropeller = 0; -static struct screen scr = { - "galaxyrise", - init, - destroy, - start, - 0, - draw -}; +static struct screen scr = {"galaxyrise", init, destroy, start, 0, draw}; -struct screen *grise_screen(void) -{ +struct screen *grise_screen(void) { return &scr; } -static int init(void) -{ +static int init(void) { unsigned char *tmpBitmap; int tmpBitmapW, tmpBitmapH; /* Allocate back buffer */ - backBuffer = (unsigned short*) calloc(BB_SIZE * BB_SIZE, sizeof(unsigned short)); + backBuffer = (unsigned short *)calloc(BB_SIZE * BB_SIZE, sizeof(unsigned short)); - /* grise.png contains the background (horizon), baked reflection and normalmap for displacement */ - if (!(background = img_load_pixels(BG_FILENAME, &backgroundW, &backgroundH, IMG_FMT_RGBA32))) { + /* grise.png contains the background (horizon), baked reflection and normalmap for + * displacement */ + if (!(background = + img_load_pixels(BG_FILENAME, &backgroundW, &backgroundH, IMG_FMT_RGBA32))) { fprintf(stderr, "failed to load image " BG_FILENAME "\n"); return -1; } /* Convert to 16bpp */ - convert32To16((unsigned int*)background, background, backgroundW * NORMALMAP_SCANLINE); /* Normalmap will keep its 32 bit color */ + convert32To16((unsigned int *)background, background, + backgroundW * NORMALMAP_SCANLINE); /* Normalmap will keep its 32 bit color */ /* Load reflected objects */ - if (!(tmpBitmap = img_load_pixels(GROBJ_01_FILENAME, &tmpBitmapW, &tmpBitmapH, IMG_FMT_GREY8))) { + if (!(tmpBitmap = + img_load_pixels(GROBJ_01_FILENAME, &tmpBitmapW, &tmpBitmapH, IMG_FMT_GREY8))) { fprintf(stderr, "failed to load image " GROBJ_01_FILENAME "\n"); return -1; } @@ -131,8 +111,7 @@ static int init(void) return 0; } -static void destroy(void) -{ +static void destroy(void) { free(backBuffer); backBuffer = 0; @@ -141,14 +120,9 @@ static void destroy(void) rleDestroy(grobj); } -static void start(long trans_time) -{ - lastFrameTime = time_msec; -} - +static void start(long trans_time) { lastFrameTime = time_msec; } -static void draw(void) -{ +static void draw(void) { int scroll = MIN_SCROLL + (MAX_SCROLL - MIN_SCROLL) * mouse_x / FB_WIDTH; unsigned short *dst = backBuffer + PIXEL_PADDING; unsigned short *src = background + scroll; @@ -188,7 +162,9 @@ static void draw(void) } /* Blit reflections first, to be displaced */ - for (i = 0; i < 5; i++) rleBlitScaleInv(backBuffer + PIXEL_PADDING, FB_WIDTH, FB_HEIGHT, BB_SIZE, rlePropeller, 134 + (i-3) * 60, 200, 1.0f, 1.8f); + for (i = 0; i < 5; i++) + rleBlitScaleInv(rlePropeller, backBuffer + PIXEL_PADDING, FB_WIDTH, FB_HEIGHT, + BB_SIZE, 134 + (i - 3) * 60, 200, 1.0f, 1.8f); /* Perform displacement */ dst = backBuffer + HORIZON_HEIGHT * BB_SIZE + PIXEL_PADDING; @@ -202,9 +178,11 @@ static void draw(void) for (i = 0; i < FB_WIDTH; i++) { /* Try to immitate modulo without the division */ - if (i == md) accum += md; + if (i == md) + accum += md; scrolledIndex = i - accum + sc; - if (scrolledIndex >= md) scrolledIndex -= md; + if (scrolledIndex >= md) + scrolledIndex -= md; /* Displace */ d = dispScanline[scrolledIndex]; @@ -216,7 +194,9 @@ static void draw(void) } /* Then after displacement, blit the objects */ - for (i = 0; i < 5; i++) rleBlit(backBuffer + PIXEL_PADDING, FB_WIDTH, FB_HEIGHT, BB_SIZE, rlePropeller, 134 + (i-3) * 60, 100); + for (i = 0; i < 5; i++) + rleBlit(rlePropeller, backBuffer + PIXEL_PADDING, FB_WIDTH, FB_HEIGHT, BB_SIZE, + 134 + (i - 3) * 60, 100); /* Blit effect to framebuffer */ src = backBuffer + PIXEL_PADDING; @@ -235,9 +215,9 @@ static void convert32To16(unsigned int *src32, unsigned short *dst16, unsigned i unsigned int p; while (pixelCount) { p = *src32++; - *dst16++ = ((p << 8) & 0xF800) /* R */ - | ((p >> 5) & 0x07E0) /* G */ - | ((p >> 19) & 0x001F); /* B */ + *dst16++ = ((p << 8) & 0xF800) /* R */ + | ((p >> 5) & 0x07E0) /* G */ + | ((p >> 19) & 0x001F); /* B */ pixelCount--; } } @@ -252,20 +232,22 @@ static void processNormal() { short minDisplacement = 256; unsigned short *dst; short *dst2; - unsigned int *normalmap = (unsigned int*)background; + unsigned int *normalmap = (unsigned int *)background; normalmap += NORMALMAP_SCANLINE * backgroundW; - dst = (unsigned short*)normalmap; - displacementMap = (short*)dst; + dst = (unsigned short *)normalmap; + displacementMap = (short *)dst; dst2 = displacementMap; for (scanline = 0; scanline < REFLECTION_HEIGHT; scanline++) { - scrollModTable[scanline] = (int) (backgroundW / scrollScaleTable[scanline] + 0.5f); + scrollModTable[scanline] = (int)(backgroundW / scrollScaleTable[scanline] + 0.5f); for (i = 0; i < backgroundW; i++) { x = (int)(i * scrollScaleTable[scanline] + 0.5f); if (x < backgroundW) { *dst = (unsigned short)(normalmap[x] >> 8) & 0xFF; - if ((short)*dst > maxDisplacement) maxDisplacement = (short)(*dst); - if ((short)*dst < minDisplacement) minDisplacement = (short)(*dst); + if ((short)*dst > maxDisplacement) + maxDisplacement = (short)(*dst); + if ((short)*dst < minDisplacement) + minDisplacement = (short)(*dst); } else { *dst = 0; } @@ -282,9 +264,13 @@ static void processNormal() { /* Second pass - subtract half maximum displacement to displace in both directions */ for (scanline = 0; scanline < REFLECTION_HEIGHT; scanline++) { for (i = 0; i < backgroundW; i++) { - /* Remember that MIN_SCROLL is the padding around the screen, so ti's the maximum displacement we can get (positive & negative) */ - *dst2 = 2 * MAX_DISPLACEMENT * (*dst2 - minDisplacement) / (maxDisplacement - minDisplacement) - MAX_DISPLACEMENT; - *dst2 = (short)((float)*dst2 / scrollScaleTable[scanline] + 0.5f); /* Displacements must also scale with distance*/ + /* Remember that MIN_SCROLL is the padding around the screen, so ti's the + * maximum displacement we can get (positive & negative) */ + *dst2 = 2 * MAX_DISPLACEMENT * (*dst2 - minDisplacement) / + (maxDisplacement - minDisplacement) - + MAX_DISPLACEMENT; + *dst2 = (short)((float)*dst2 / scrollScaleTable[scanline] + + 0.5f); /* Displacements must also scale with distance*/ dst2++; } } @@ -306,12 +292,11 @@ static void initScrollTables() { } } - static void updateScrollTables(float dt) { int i = 0; nearScrollAmount += dt * NEAR_SCROLL_SPEED; - nearScrollAmount = (float) fmod(nearScrollAmount, 512.0f); + nearScrollAmount = (float)fmod(nearScrollAmount, 512.0f); for (i = 0; i < REFLECTION_HEIGHT; i++) { scrollTable[i] = nearScrollAmount / scrollScaleTable[i]; @@ -320,298 +305,9 @@ static void updateScrollTables(float dt) { } /* ------------------------------------------------------------------------------------------------- - * RLE STUFF + * PROPELLER STUFF * ------------------------------------------------------------------------------------------------- */ -/* Limit streak count per scanline so we can directly jump to specific scanline */ -#define RLE_STREAKS_PER_SCANLINE 4 -/* Every streak is encoded by 2 bytes: offset and count of black pixels in the streak */ -#define RLE_BYTES_PER_SCANLINE RLE_STREAKS_PER_SCANLINE * 2 -#define RLE_FILL_COLOR 0 -#define RLE_FILL_COLOR_32 ((RLE_FILL_COLOR << 16) | RLE_FILL_COLOR) - -#define RLE_FIXED_BITS 16 - -static int rleByteCount(int w, int h) { - return h * RLE_BYTES_PER_SCANLINE + w; -} - -static RLEBitmap *rleCreate(unsigned int w, unsigned int h) { - RLEBitmap *ret = (RLEBitmap*)malloc(sizeof(RLEBitmap)); - ret->w = w; - ret->h = h; - - /* Add some padding at the end of the buffer, with the worst case for a scanline (w/2 streaks) */ - ret->scans = (unsigned char*) calloc(rleByteCount(w, h), 1); - - return ret; -} - -static void rleDestroy(RLEBitmap *b) { - if (!b) return; - free(b->scans); - free(b); -} - -static RLEBitmap *rleEncode(RLEBitmap *b, unsigned char *pixels, unsigned int w, unsigned int h) { - int scanline; - int i; - int penActive = 0; - int counter = 0; - int accum = 0; - unsigned char *output; - - /* https://www.youtube.com/watch?v=RKMR02o1I88&feature=youtu.be&t=55 */ - if (!b) b = rleCreate(w, h); - else memset(b->scans, 0, rleByteCount(b->w, b->h)); /* The following code assumes cleared array */ - - for (scanline = 0; scanline < h; scanline++) { - output = b->scans + scanline * RLE_BYTES_PER_SCANLINE; - accum = 0; - for (i = 0; i < w; i++) { - if (*pixels++) { - if (penActive) { - if (counter >= PIXEL_PADDING) { - *output++ = (unsigned char) counter; - counter = 0; - *output++ = (unsigned char)accum; - } - counter++; - accum++; - } else { - *output++ = (unsigned char)accum; - counter = 1; - accum++; - penActive = 1; - } - } else { - if (penActive) { - *output++ = (unsigned char)counter; - counter = 1; - accum++; - penActive = 0; - } else { - counter++; - accum++; - } - } - } - - if (penActive) { - *output++ = (unsigned char)counter; - } - penActive = 0; - counter = 0; - } - - return b; -} - -static void rleDistributeStreaks(RLEBitmap *bitmap) { - int scanline, halfW = bitmap->w >> 1; - unsigned char *ptr, tmp; - - ptr = bitmap->scans; - for (scanline = 0; scanline < bitmap->h; scanline++) { - if (ptr[0] >= halfW) { - tmp = ptr[0]; - ptr[0] = ptr[6]; - ptr[6] = tmp; - tmp = ptr[1]; - ptr[1] = ptr[7]; - ptr[7] = tmp; - } - - ptr += 8; - } -} - -static void rleBlit(unsigned short *dst, int dstW, int dstH, int dstStride, - RLEBitmap *bitmap, int blitX, int blitY) -{ - int scanline = 0; - int streakPos = 0; - int streakLength = 0; - int streak = 0; - unsigned char *input = bitmap->scans; - unsigned short *output; - unsigned int *output32; - - dst += blitX + blitY * dstStride; - - for (scanline = blitY; scanline < blitY + bitmap->h; scanline++) { - if (scanline < 0 || scanline >= dstH) continue; - for (streak = 0; streak < RLE_STREAKS_PER_SCANLINE; streak++) { - streakPos = *input++; - streakLength = *input++; - - if ((streakPos + blitX) <= 0) continue; - - output = dst + streakPos; - - /* Check if we need to write the first pixel as 16bit */ - if (streakLength % 2) { - *output++ = RLE_FILL_COLOR; - } - - /* Then, write 2 pixels at a time */ - streakLength >>= 1; - output32 = (unsigned int*) output; - while (streakLength--) { - *output32++ = RLE_FILL_COLOR_32; - } - } - - dst += dstStride; - } -} - -static void interpolateScan(unsigned char *output, unsigned char *a, unsigned char *b, float t) { - static int div = 1 << 23; - int ti, i; - - t += 1.0f; - ti = (*((unsigned int*)&t)) & 0x7FFFFF; - - for (i = 0; i < RLE_BYTES_PER_SCANLINE; i++) { - if (*a == 0) { - *output++ = *b++; - a++; - } else { - if (*b == 0) { - *output++ = *a++; - b++; - } else { - *output++ = ((*b++ * ti) + (*a++ * (div - ti))) >> 23; - } - } - } -} - -static void rleBlitScale(unsigned short *dst, int dstW, int dstH, int dstStride, - RLEBitmap *bitmap, int blitX, int blitY, float scaleX, float scaleY) -{ - int scanline = 0; - int streakPos = 0; - int streakLength = 0; - int streak = 0; - unsigned short *output; - unsigned int *output32; - unsigned char *input; - int scanlineCounter = 0; - int scaleXFixed; - static unsigned char scan[512]; - - /*int blitW = (int)(bitmap->w * scaleX + 0.5f);*/ - int blitH = (int)(bitmap->h * scaleY + 0.5f); - - /* From this point on, scaleY will be inverted */ - scaleY = 1.0f / scaleY; - - scaleXFixed = (int)(scaleX * (float)(1 << RLE_FIXED_BITS) + 0.5f); - - dst += blitX + blitY * dstStride; - - for (scanline = blitY; scanline < blitY + blitH; scanline++) { - float normalScan = scanlineCounter * scaleY; /* ScaleY is inverted */ - unsigned char *scan0 = bitmap->scans + RLE_BYTES_PER_SCANLINE * (int)normalScan; - unsigned char *scan1 = scan0 + RLE_BYTES_PER_SCANLINE; - normalScan -= (int)normalScan; - interpolateScan(scan, scan0, scan1, normalScan); - input = scan; - scanlineCounter++; - - if (scanline < 0 || scanline >= dstH) continue; - for (streak = 0; streak < RLE_STREAKS_PER_SCANLINE; streak++) { - streakPos = (*input++ * scaleXFixed) >> RLE_FIXED_BITS; - streakLength = (*input++ * scaleXFixed) >> RLE_FIXED_BITS; - - if ((streakPos + blitX) <= 0) continue; - - output = dst + streakPos; - - /* Check if we need to write the first pixel as 16bit */ - if (streakLength % 2) { - *output++ = RLE_FILL_COLOR; - } - - /* Then, write 2 pixels at a time */ - streakLength >>= 1; - output32 = (unsigned int*)output; - while (streakLength--) { - *output32++ = RLE_FILL_COLOR_32; - } - } - - dst += dstStride; - } -} - - - -static void rleBlitScaleInv(unsigned short *dst, int dstW, int dstH, int dstStride, - RLEBitmap *bitmap, int blitX, int blitY, float scaleX, float scaleY) -{ - int scanline = 0; - int streakPos = 0; - int streakLength = 0; - int streak = 0; - unsigned short *output; - unsigned int *output32; - unsigned char *input; - int scanlineCounter = 0; - int scaleXFixed; - static unsigned char scan[512]; - - /*int blitW = (int)(bitmap->w * scaleX + 0.5f);*/ - int blitH = (int)(bitmap->h * scaleY + 0.5f); - - /* From this point on, scaleY will be inverted */ - scaleY = 1.0f / scaleY; - - scaleXFixed = (int)(scaleX * (float)(1 << RLE_FIXED_BITS) + 0.5f); - - dst += blitX + blitY * dstStride; - - for (scanline = blitY; scanline > blitY - blitH; scanline--) { - float normalScan = scanlineCounter * scaleY; /* ScaleY is inverted */ - unsigned char *scan0 = bitmap->scans + RLE_BYTES_PER_SCANLINE * (int)normalScan; - unsigned char *scan1 = scan0 + RLE_BYTES_PER_SCANLINE; - normalScan -= (int)normalScan; - interpolateScan(scan, scan0, scan1, normalScan); - input = scan; - scanlineCounter++; - - if (scanline < 0 || scanline >= dstH) continue; - for (streak = 0; streak < RLE_STREAKS_PER_SCANLINE; streak++) { - streakPos = (*input++ * scaleXFixed) >> RLE_FIXED_BITS; - streakLength = (*input++ * scaleXFixed) >> RLE_FIXED_BITS; - - if ((streakPos + blitX) <= 0) continue; - - output = dst + streakPos; - - /* Check if we need to write the first pixel as 16bit */ - if (streakLength % 2) { - *output++ = RLE_FILL_COLOR; - } - - /* Then, write 2 pixels at a time */ - streakLength >>= 1; - output32 = (unsigned int*)output; - while (streakLength--) { - *output32++ = RLE_FILL_COLOR_32; - } - } - - dst -= dstStride; - } -} - -/* ------------------------------------------------------------------------------------------------- -* PROPELLER STUFF -* ------------------------------------------------------------------------------------------------- -*/ #define PROPELLER_CIRCLE_RADIUS 18 #define PROPELLER_CIRCLE_RADIUS_SQ (PROPELLER_CIRCLE_RADIUS * PROPELLER_CIRCLE_RADIUS) @@ -667,17 +363,20 @@ static void updatePropeller(float t) { /* First circle */ cx = propellerState.circleX[0] - i; cy = propellerState.circleY[0] - j; - if (cx*cx + cy*cy < PROPELLER_CIRCLE_RADIUS_SQ) count++; + if (cx * cx + cy * cy < PROPELLER_CIRCLE_RADIUS_SQ) + count++; /* 2nd circle */ cx = propellerState.circleX[1] - i; cy = propellerState.circleY[1] - j; - if (cx*cx + cy*cy < PROPELLER_CIRCLE_RADIUS_SQ) count++; + if (cx * cx + cy * cy < PROPELLER_CIRCLE_RADIUS_SQ) + count++; /* 3rd circle */ cx = propellerState.circleX[2] - i; cy = propellerState.circleY[2] - j; - if (cx*cx + cy*cy < PROPELLER_CIRCLE_RADIUS_SQ) count++; + if (cx * cx + cy * cy < PROPELLER_CIRCLE_RADIUS_SQ) + count++; *dst++ = count >= 2; } diff --git a/src/screen.c b/src/screen.c index 972e711..190b19f 100644 --- a/src/screen.c +++ b/src/screen.c @@ -27,6 +27,7 @@ struct screen *infcubes_screen(void); struct screen *hairball_screen(void); struct screen *cybersun_screen(void); struct screen *raytrace_screen(void); +struct screen *minifx_screen(void); void start_loadscr(void); void end_loadscr(void); @@ -87,6 +88,10 @@ int scr_init(void) if(!(scr[idx++] = raytrace_screen())) { return -1; } + if (!(scr[idx++] = minifx_screen())) { + return -1; + } + num_screens = idx; assert(num_screens <= NUM_SCR); diff --git a/src/utils/RleBitmap.c b/src/utils/RleBitmap.c new file mode 100644 index 0000000..8c344f2 --- /dev/null +++ b/src/utils/RleBitmap.c @@ -0,0 +1,309 @@ +#include "RleBitmap.h" +#include +#include + +/* Number of numbers per scanline. Each streak has 2 numbers (start, length) */ +#define RLE_ELEMENTS_PER_SCANLINE RLE_STREAKS_PER_SCANLINE * 2 + +/* Two RLE_TYPE elements per streak (= start,length) */ +#define RLE_BYTES_PER_SCANLINE RLE_ELEMENTS_PER_SCANLINE * sizeof(RLE_TYPE) + +/* RLE_TYPE count required for storing an RLE of w,h */ +static int rleWorstCaseElementCount(int w, int h) { + /* Allocate an extra worst case for one scanline, which is w/2 streaks = w + * (start,length) elements */ + return h * RLE_ELEMENTS_PER_SCANLINE + w; +} + +/* Byte count of the 'scans' buffer */ +static int rleScansByteCount(RleBitmap *rle) { + return rleWorstCaseElementCount(rle->w, rle->h) * sizeof(RLE_TYPE); +} + +RleBitmap *rleCreate(unsigned int w, unsigned int h) { + RleBitmap *ret = (RleBitmap *)malloc(sizeof(RleBitmap)); + ret->w = w; + ret->h = h; + + /* Allocate scans */ + ret->scans = (RLE_TYPE *)calloc(rleWorstCaseElementCount(w, h), sizeof(RLE_TYPE)); + + return ret; +} + +void rleDestroy(RleBitmap *b) { + if (!b) + return; + free(b->scans); + free(b); +} + +void rleClear(RleBitmap *rle) { memset(rle->scans, 0, rleScansByteCount(rle)); } + +RleBitmap *rleEncode(RleBitmap *rle, unsigned char *pixels, unsigned int pixelsW, + unsigned int pixelsH) { + int x = 0; + int y = 0; + int streakActive = 0; + int currentStreakLength = 0; + RLE_TYPE *output = 0; + unsigned char *currentInputPixel = pixels; + + /* https://www.youtube.com/watch?v=RKMR02o1I88&feature=youtu.be&t=55 */ + if (!rle) + rle = rleCreate(pixelsW, pixelsH); + else + rleClear(rle); /* The following code assumes cleared array */ + + for (y = 0; y < pixelsH; y++) { + /* Go to the beginning of the RLE scan */ + output = rle->scans + y * RLE_ELEMENTS_PER_SCANLINE; + + for (x = 0; x < pixelsW; x++) { + if (*currentInputPixel++) { + if (streakActive) { + if (currentStreakLength >= RLE_MAX_STREAK_LENGTH) { + /* Do not allow streaks of more than max length - + * close current streak */ + *output++ = (RLE_TYPE)currentStreakLength; + + /* Begin new streak at current x */ + *output++ = (RLE_TYPE)x; + currentStreakLength = 0; + } + } else { + /* Begin new streak */ + *output++ = (RLE_TYPE)x; + currentStreakLength = 0; + streakActive = 1; + } + currentStreakLength++; + } else { + if (streakActive) { + /* Close current streak */ + *output++ = (RLE_TYPE)currentStreakLength; + currentStreakLength = 0; + streakActive = 0; + } + } /* End if (current pixel on) */ + } /* End for (all x) */ + + /* We reached the end of the scan - close any active streak */ + if (streakActive) { + *output++ = (RLE_TYPE)currentStreakLength; + } + streakActive = 0; + currentStreakLength = 0; + } /* End for (all scans */ + + return rle; +} + +void rleDistributeStreaks(RleBitmap *rle) { + int scanline = 0; + int halfW = rle->w >> 1; + RLE_TYPE *ptr = 0; + RLE_TYPE tmp = 0; + +#define LAST_STREAK RLE_STREAKS_PER_SCANLINE + + ptr = rle->scans; + for (scanline = 0; scanline < rle->h; scanline++) { + if (ptr[0] >= halfW) { + /* Exchange first with last streak */ + tmp = ptr[0]; + ptr[0] = ptr[LAST_STREAK * 2 - 2]; + ptr[LAST_STREAK * 2 - 2] = tmp; + tmp = ptr[1]; + ptr[1] = ptr[LAST_STREAK * 2 - 1]; + ptr[LAST_STREAK * 2 - 1] = tmp; + } + + ptr += 8; + } +} + +void rleBlit(RleBitmap *rle, unsigned short *dst, int dstW, int dstH, int dstStride, int blitX, + int blitY) { + int scanline = 0; + int streakPos = 0; + int streakLength = 0; + int streak = 0; + RLE_TYPE *input = rle->scans; + unsigned short *output; + unsigned int *output32; + + dst += blitX + blitY * dstStride; + + for (scanline = blitY; scanline < blitY + rle->h; scanline++) { + if (scanline < 0 || scanline >= dstH) + continue; + for (streak = 0; streak < RLE_STREAKS_PER_SCANLINE; streak++) { + streakPos = (int)*input++; + streakLength = (int)*input++; + + if ((streakPos + blitX) <= 0) + continue; + + output = dst + streakPos; + + /* Check if we need to write the first pixel as 16bit */ + if (streakLength % 2) { + *output++ = RLE_FILL_COLOR; + } + + /* Then, write 2 pixels at a time */ + streakLength >>= 1; + output32 = (unsigned int *)output; + while (streakLength--) { + *output32++ = RLE_FILL_COLOR_32; + } + } + + dst += dstStride; + } +} + +/* This is madness. We could at least check that we are not interpolating from 0 -> something + * (length). This could remove the need for 'distributeScans' */ +void interpolateScan(RLE_TYPE *output, RLE_TYPE *a, RLE_TYPE *b, float t) { + static int div = 1 << 23; + int ti, i; + + t += 1.0f; + ti = (*((unsigned int *)&t)) & 0x7FFFFF; + + for (i = 0; i < RLE_ELEMENTS_PER_SCANLINE; i++) { + if (*a == 0) { + *output++ = *b++; + a++; + } else { + if (*b == 0) { + *output++ = *a++; + b++; + } else { + *output++ = ((*b++ * ti) + (*a++ * (div - ti))) >> 23; + } + } + } +} + +void rleBlitScale(RleBitmap *rle, unsigned short *dst, int dstW, int dstH, int dstStride, int blitX, + int blitY, float scaleX, float scaleY) { + int scanline = 0; + int streakPos = 0; + int streakLength = 0; + int streak = 0; + unsigned short *output; + unsigned int *output32; + unsigned char *input; + int scanlineCounter = 0; + int scaleXFixed; + static unsigned char scan[512]; + + /*int blitW = (int)(rle->w * scaleX + 0.5f);*/ + int blitH = (int)(rle->h * scaleY + 0.5f); + + /* From this point on, scaleY will be inverted */ + scaleY = 1.0f / scaleY; + + scaleXFixed = (int)(scaleX * (float)(1 << RLE_FIXED_BITS) + 0.5f); + + dst += blitX + blitY * dstStride; + + for (scanline = blitY; scanline < blitY + blitH; scanline++) { + float normalScan = scanlineCounter * scaleY; /* ScaleY is inverted */ + unsigned char *scan0 = rle->scans + RLE_BYTES_PER_SCANLINE * (int)normalScan; + unsigned char *scan1 = scan0 + RLE_BYTES_PER_SCANLINE; + normalScan -= (int)normalScan; + interpolateScan(scan, scan0, scan1, normalScan); + input = scan; + scanlineCounter++; + + if (scanline < 0 || scanline >= dstH) + continue; + for (streak = 0; streak < RLE_STREAKS_PER_SCANLINE; streak++) { + streakPos = (*input++ * scaleXFixed) >> RLE_FIXED_BITS; + streakLength = (*input++ * scaleXFixed) >> RLE_FIXED_BITS; + + if ((streakPos + blitX) <= 0) + continue; + + output = dst + streakPos; + + /* Check if we need to write the first pixel as 16bit */ + if (streakLength % 2) { + *output++ = RLE_FILL_COLOR; + } + + /* Then, write 2 pixels at a time */ + streakLength >>= 1; + output32 = (unsigned int *)output; + while (streakLength--) { + *output32++ = RLE_FILL_COLOR_32; + } + } + + dst += dstStride; + } +} + +void rleBlitScaleInv(RleBitmap *rle, unsigned short *dst, int dstW, int dstH, int dstStride, + int blitX, int blitY, float scaleX, float scaleY) { + int scanline = 0; + int streakPos = 0; + int streakLength = 0; + int streak = 0; + unsigned short *output; + unsigned int *output32; + unsigned char *input; + int scanlineCounter = 0; + int scaleXFixed; + static unsigned char scan[512]; + + /*int blitW = (int)(rle->w * scaleX + 0.5f);*/ + int blitH = (int)(rle->h * scaleY + 0.5f); + + /* From this point on, scaleY will be inverted */ + scaleY = 1.0f / scaleY; + + scaleXFixed = (int)(scaleX * (float)(1 << RLE_FIXED_BITS) + 0.5f); + + dst += blitX + blitY * dstStride; + + for (scanline = blitY; scanline > blitY - blitH; scanline--) { + float normalScan = scanlineCounter * scaleY; /* ScaleY is inverted */ + unsigned char *scan0 = rle->scans + RLE_BYTES_PER_SCANLINE * (int)normalScan; + unsigned char *scan1 = scan0 + RLE_BYTES_PER_SCANLINE; + normalScan -= (int)normalScan; + interpolateScan(scan, scan0, scan1, normalScan); + input = scan; + scanlineCounter++; + + if (scanline < 0 || scanline >= dstH) + continue; + for (streak = 0; streak < RLE_STREAKS_PER_SCANLINE; streak++) { + streakPos = (*input++ * scaleXFixed) >> RLE_FIXED_BITS; + streakLength = (*input++ * scaleXFixed) >> RLE_FIXED_BITS; + + if ((streakPos + blitX) <= 0) + continue; + + output = dst + streakPos; + + /* Check if we need to write the first pixel as 16bit */ + if (streakLength % 2) { + *output++ = RLE_FILL_COLOR; + } + + /* Then, write 2 pixels at a time */ + streakLength >>= 1; + output32 = (unsigned int *)output; + while (streakLength--) { + *output32++ = RLE_FILL_COLOR_32; + } + } + + dst -= dstStride; + } +} diff --git a/src/utils/RleBitmap.h b/src/utils/RleBitmap.h new file mode 100644 index 0000000..3f36b16 --- /dev/null +++ b/src/utils/RleBitmap.h @@ -0,0 +1,66 @@ +#ifndef __RLE_BITMAP_H__ +#define __RLE_BITMAP_H__ + +/* Limit streak count per scanline so we can directly jump to specific scanline + */ +#define RLE_STREAKS_PER_SCANLINE 4 + +/* Streaks will not exceed this many pixels. This allows us to optimize with a + * padded framebuffer. If a streak start position happens to lie within + * framebuffer, we will blit it without checking for out of bounds */ +#define RLE_MAX_STREAK_LENGTH 32 + +/* Using the following type for storing start and for storing length in (start, + * length) pairs. */ +#define RLE_TYPE unsigned char + +/* For now, keep a static fill color. We can change this. Not that much about + * speed, but let's keep function definitions more compact. */ +#define RLE_FILL_COLOR 0 + +/* Two entries of RLE_FILL_COLOR (16 bits) packed one after the other. */ +#define RLE_FILL_COLOR_32 ((RLE_FILL_COLOR << 16) | RLE_FILL_COLOR) + +/* For fixed-point arithmetic. Used for scaling. */ +#define RLE_FIXED_BITS 16 + +/* This is a bitmap (image in 1bpp), encoded as streaks of consecutive pixels. + */ +typedef struct { + unsigned int w, h; + + /* Each scan is RLE_BYTES_PER_SCANLINE long and contains pairs of + * (start, length). */ + RLE_TYPE *scans; +} RleBitmap; + +/* Constructor */ +RleBitmap *rleCreate(unsigned int w, unsigned int h); + +/* Destructor */ +void rleDestroy(RleBitmap *rle); + +/* Clears 'rle' to "all transparent" */ +void rleClear(RleBitmap *rle); + +/* Encode 'pixels' into 'rle' and also return it. Pixels are either 0 or 1. This + * will create an RleBitmap of 'h' scanlines. */ +RleBitmap *rleEncode(RleBitmap *rle, unsigned char *pixels, unsigned int pixelsW, + unsigned int pixelsH); + +/* Rearranges the streaks to make it less frequent that they produce garbege when interpolated */ +void rleDistributeStreaks(RleBitmap *rle); + +/* Blits without scaling */ +void rleBlit(RleBitmap *rle, unsigned short *dst, int dstW, int dstH, int dstStride, int blitX, + int blitY); + +/* Scaled blit */ +void rleBlitScale(RleBitmap *rle, unsigned short *dst, int dstW, int dstH, int dstStride, int blitX, + int blitY, float scaleX, float scaleY); + +/* Inverted blit (upside down) */ +void rleBlitScaleInv(RleBitmap *rle, unsigned short *dst, int dstW, int dstH, int dstStride, + int blitX, int blitY, float scaleX, float scaleY); + +#endif // __RLE_BITMAP_H__ -- 1.7.10.4