// // render.c // Extension // // Created by Dave Hayden on 10/20/15. // Copyright © 2015 Panic, Inc. All rights reserved. // #include #include #include "render.h" #define LCD_ROWS 240 #define LCD_COLUMNS 400 #if !defined(MIN) #define MIN(a, b) (((a)<(b))?(a):(b)) #endif #if !defined(MAX) #define MAX(a, b) (((a)>(b))?(a):(b)) #endif #if ENABLE_Z_BUFFER static uint16_t zbuf[LCD_COLUMNS*LCD_ROWS]; static float zscale; #define Z_BIAS 0 void resetZBuffer(float zmin) { memset(zbuf, 0, sizeof(zbuf)); zscale = 0xffff * (zmin + Z_BIAS); } #endif static inline uint32_t swap(uint32_t n) { #if TARGET_PLAYDATE //return __REV(n); uint32_t result; __asm volatile ("rev %0, %1" : "=l" (result) : "l" (n)); return(result); #else return ((n & 0xff000000) >> 24) | ((n & 0xff0000) >> 8) | ((n & 0xff00) << 8) | (n << 24); #endif } /* #if ENABLE_Z_BUFFER void getZMask(uint8_t* buffer, int rowstride) { for ( int y = 0; y < LCD_ROWS; ++y ) { uint32_t* row = (uint32_t*)&buffer[y*rowstride]; uint16_t* zrow = &zbuf[y*LCD_COLUMNS]; uint32_t mask = 0; int x = 0; while ( x < rowstride ) { if ( zrow[x] < 0xffff ) mask |= 0x80000000 >> (x%32); ++x; if ( x%32 == 0 ) { *row++ = swap(mask); mask = 0; } } if ( x%32 != 0 ) *row++ = swap(mask); } } #endif */ static inline void _drawMaskPattern(uint32_t* p, uint32_t mask, uint32_t color) { if ( mask == 0xffffffff ) *p = color; else *p = (*p & ~mask) | (color & mask); } static void drawFragment(uint32_t* row, int x1, int x2, uint32_t color) { if ( x2 < 0 || x1 >= LCD_COLUMNS ) return; if ( x1 < 0 ) x1 = 0; if ( x2 > LCD_COLUMNS ) x2 = LCD_COLUMNS; if ( x1 > x2 ) return; // Operate on 32 bits at a time int startbit = x1 % 32; uint32_t startmask = swap((1 << (32 - startbit)) - 1); int endbit = x2 % 32; uint32_t endmask = swap(((1 << endbit) - 1) << (32 - endbit)); int col = x1 / 32; uint32_t* p = row + col; if ( col == x2 / 32 ) { uint32_t mask = 0; if ( startbit > 0 && endbit > 0 ) mask = startmask & endmask; else if ( startbit > 0 ) mask = startmask; else if ( endbit > 0 ) mask = endmask; _drawMaskPattern(p, mask, color); } else { int x = x1; if ( startbit > 0 ) { _drawMaskPattern(p++, startmask, color); x += (32 - startbit); } while ( x + 32 <= x2 ) { _drawMaskPattern(p++, 0xffffffff, color); x += 32; } if ( endbit > 0 ) _drawMaskPattern(p, endmask, color); } } #if ENABLE_Z_BUFFER static void drawFragment_zbuf(uint32_t* row, uint16_t* zbrow, int x, int endx, uint32_t z, int32_t dzdx, uint32_t color) { if ( endx < 0 || x >= LCD_COLUMNS ) return; if ( x < 0 ) { z += -x * dzdx; x = 0; } if ( endx > LCD_COLUMNS ) endx = LCD_COLUMNS; uint32_t mask = 0; uint32_t* p = row + x/32; while ( x < endx ) { uint16_t zi = z >> 16; if ( zi > zbrow[x] ) { mask |= 0x80000000u >> (x%32); zbrow[x] = zi; } ++x; z += dzdx; if ( x % 32 == 0 ) { _drawMaskPattern(p++, swap(mask), color); mask = 0; } } if ( x % 32 != 0 ) _drawMaskPattern(p, swap(mask), color); } #endif static inline int32_t slope(float x1, float y1, float x2, float y2) { float dx = x2-x1; float dy = y2-y1; if ( dy < 1 ) return dx * (1<<16); else return dx / dy * (1<<16); } #if ENABLE_Z_BUFFER LCDRowRange drawLine_zbuf(uint8_t* bitmap, int rowstride, Point3D* p1, Point3D* p2, int thick, uint8_t pattern[8]) { if ( p1->y > p2->y ) { Point3D* tmp = p1; p1 = p2; p2 = tmp; } int y = p1->y; int endy = p2->y; if ( y >= LCD_ROWS || endy < 0 || MIN(p1->x, p2->x) >= LCD_COLUMNS || MAX(p1->x, p2->x) < 0 ) return (LCDRowRange){ 0, 0 }; int32_t x = p1->x * (1<<16); int32_t dx = slope(p1->x, p1->y, p2->x, p2->y + 1); float py = p1->y; // move lines a bit forward so they don't get buried in solid geometry float z1 = zscale / (p1->z + Z_BIAS) + 256; float z2 = zscale / (p2->z + Z_BIAS) + 256; if ( z1 > 65535 ) z1 = 65535; if ( z2 > 65535 ) z2 = 65535; int32_t dzdy = slope(z1, p1->y, z2, p2->y + 1); int32_t dzdx = slope(z1, p1->x, z2, p2->x); uint32_t z = z1 * (1<<16); if ( y < 0 ) { x += -y * dx; z += -y * dzdy; y = 0; py = 0; } int32_t x1 = x + dx * (y+1-py); while ( y <= endy ) { uint8_t p = pattern[y%8]; uint32_t color = (p<<24) | (p<<16) | (p<<8) | p; if ( y == endy ) x1 = p2->x * (1<<16); if ( dx < 0 ) { z += dzdy; drawFragment_zbuf((uint32_t*)&bitmap[y*rowstride], &zbuf[y*LCD_COLUMNS], x1>>16, (x>>16) + thick, z, dzdx, color); } else { drawFragment_zbuf((uint32_t*)&bitmap[y*rowstride], &zbuf[y*LCD_COLUMNS], x>>16, (x1>>16) + thick, z, dzdx, color); z += dzdy; } if ( ++y == LCD_ROWS ) break; x = x1; x1 += dx; } return (LCDRowRange){ MAX(0, p1->y), MIN(LCD_ROWS, p2->y) }; } #endif LCDRowRange drawLine(uint8_t* bitmap, int rowstride, Point3D* p1, Point3D* p2, int thick, uint8_t pattern[8]) { if ( p1->y > p2->y ) { Point3D* tmp = p1; p1 = p2; p2 = tmp; } int y = p1->y; int endy = p2->y; if ( y >= LCD_ROWS || endy < 0 || MIN(p1->x, p2->x) >= LCD_COLUMNS || MAX(p1->x, p2->x) < 0 ) return (LCDRowRange){ 0, 0 }; int32_t x = p1->x * (1<<16); int32_t dx = slope(p1->x, p1->y, p2->x, p2->y); float py = p1->y; if ( y < 0 ) { x += -p1->y * dx; y = 0; py = 0; } int32_t x1 = x + dx * (y+1-py); while ( y <= endy ) { uint8_t p = pattern[y%8]; uint32_t color = (p<<24) | (p<<16) | (p<<8) | p; if ( y == endy ) x1 = p2->x * (1<<16); if ( dx < 0 ) drawFragment((uint32_t*)&bitmap[y*rowstride], x1>>16, (x>>16) + thick, color); else drawFragment((uint32_t*)&bitmap[y*rowstride], x>>16, (x1>>16) + thick, color); if ( ++y == LCD_ROWS ) break; x = x1; x1 += dx; } return (LCDRowRange){ MAX(0, p1->y), MIN(LCD_ROWS, p2->y) }; } static void fillRange(uint8_t* bitmap, int rowstride, int y, int endy, int32_t* x1p, int32_t dx1, int32_t* x2p, int32_t dx2, uint8_t pattern[8]) { int32_t x1 = *x1p, x2 = *x2p; if ( endy < 0 ) { int dy = endy - y; *x1p = x1 + dy * dx1; *x2p = x2 + dy * dx2; return; } if ( y < 0 ) { x1 += -y * dx1; x2 += -y * dx2; y = 0; } while ( y < endy ) { uint8_t p = pattern[y%8]; uint32_t color = (p<<24) | (p<<16) | (p<<8) | p; drawFragment((uint32_t*)&bitmap[y*rowstride], (x1>>16), (x2>>16)+1, color); x1 += dx1; x2 += dx2; ++y; } *x1p = x1; *x2p = x2; } #if ENABLE_Z_BUFFER static void fillRange_zbuf(uint8_t* bitmap, int rowstride, int y, int endy, int32_t* x1p, int32_t dx1, int32_t* x2p, int32_t dx2, uint32_t* zp, int32_t dzdy, int32_t dzdx, uint8_t pattern[8]) { int32_t x1 = *x1p, x2 = *x2p; uint32_t z = *zp; if ( endy < 0 ) { int dy = endy - y; *x1p = x1 + dy * dx1; *x2p = x2 + dy * dx2; *zp = z + dy * dzdy; return; } if ( y < 0 ) { x1 += -y * dx1; x2 += -y * dx2; z += -y * dzdy; y = 0; } while ( y < endy ) { uint8_t p = pattern[y%8]; uint32_t color = (p<<24) | (p<<16) | (p<<8) | p; drawFragment_zbuf((uint32_t*)&bitmap[y*rowstride], &zbuf[y*LCD_COLUMNS], (x1>>16), (x2>>16)+1, z, dzdx, color); x1 += dx1; x2 += dx2; z += dzdy; ++y; } *x1p = x1; *x2p = x2; *zp = z; } #endif static inline void sortTri(Point3D** p1, Point3D** p2, Point3D** p3) { float y1 = (*p1)->y, y2 = (*p2)->y, y3 = (*p3)->y; if ( y1 <= y2 && y1 < y3 ) { if ( y3 < y2 ) // 1,3,2 { Point3D* tmp = *p2; *p2 = *p3; *p3 = tmp; } } else if ( y2 < y1 && y2 < y3 ) { Point3D* tmp = *p1; *p1 = *p2; if ( y3 < y1 ) // 2,3,1 { *p2 = *p3; *p3 = tmp; } else // 2,1,3 *p2 = tmp; } else { Point3D* tmp = *p1; *p1 = *p3; if ( y1 < y2 ) // 3,1,2 { *p3 = *p2; *p2 = tmp; } else // 3,2,1 *p3 = tmp; } } LCDRowRange fillTriangle(uint8_t* bitmap, int rowstride, Point3D* p1, Point3D* p2, Point3D* p3, uint8_t pattern[8]) { // sort by y coord sortTri(&p1, &p2, &p3); int endy = MIN(LCD_ROWS, p3->y); if ( p1->y > LCD_ROWS || endy < 0 ) return (LCDRowRange){ 0, 0 }; int32_t x1 = p1->x * (1<<16); int32_t x2 = x1; int32_t sb = slope(p1->x, p1->y, p2->x, p2->y); int32_t sc = slope(p1->x, p1->y, p3->x, p3->y); int32_t dx1 = MIN(sb, sc); int32_t dx2 = MAX(sb, sc); fillRange(bitmap, rowstride, p1->y, MIN(LCD_ROWS, p2->y), &x1, dx1, &x2, dx2, pattern); int dx = slope(p2->x, p2->y, p3->x, p3->y); if ( sb < sc ) { x1 = p2->x * (1<<16); fillRange(bitmap, rowstride, p2->y, endy, &x1, dx, &x2, dx2, pattern); } else { x2 = p2->x * (1<<16); fillRange(bitmap, rowstride, p2->y, endy, &x1, dx1, &x2, dx, pattern); } return (LCDRowRange){ MAX(0, p1->y), endy }; } #if ENABLE_Z_BUFFER LCDRowRange fillTriangle_zbuf(uint8_t* bitmap, int rowstride, Point3D* p1, Point3D* p2, Point3D* p3, uint8_t pattern[8]) { sortTri(&p1, &p2, &p3); int endy = MIN(LCD_ROWS, p3->y); int det = (p3->x - p1->x) * (p2->y - p1->y) - (p2->x - p1->x) * (p3->y - p1->y); if ( p1->y > LCD_ROWS || endy < 0 || det == 0 ) return (LCDRowRange){ 0, 0 }; int32_t x1 = p1->x * (1<<16); int32_t x2 = x1; int32_t sb = slope(p1->x, p1->y, p2->x, p2->y); int32_t sc = slope(p1->x, p1->y, p3->x, p3->y); int32_t dx1 = MIN(sb,sc); int32_t dx2 = MAX(sb,sc); float z1 = zscale / (p1->z + Z_BIAS); float z2 = zscale / (p2->z + Z_BIAS); float z3 = zscale / (p3->z + Z_BIAS); float mx = p1->x + (p2->y-p1->y) * (p3->x-p1->x) / (p3->y-p1->y); float mz = z1 + (p2->y-p1->y) * (z3-z1) / (p3->y-p1->y); int32_t dzdx, dzdy; if ( sc < sb ) { dzdx = slope(mz, mx, z2, p2->x); dzdy = slope(z1, p1->y, z3, p3->y); } else { dzdx = slope(z2, p2->x, mz, mx); dzdy = slope(z1, p1->y, z2, p2->y); } uint32_t z = z1 * (1<<16); fillRange_zbuf(bitmap, rowstride, p1->y, MIN(LCD_ROWS, p2->y), &x1, dx1, &x2, dx2, &z, dzdy, dzdx, pattern); int dx = slope(p2->x, p2->y, p3->x, p3->y); if ( sb < sc ) { dzdy = slope(z2, p2->y, z3, p3->y); x1 = p2->x * (1<<16); z = z2 * (1<<16); fillRange_zbuf(bitmap, rowstride, p2->y, endy, &x1, dx, &x2, dx2, &z, dzdy, dzdx, pattern); } else { x2 = p2->x * (1<<16); fillRange_zbuf(bitmap, rowstride, p2->y, endy, &x1, dx1, &x2, dx, &z, dzdy, dzdx, pattern); } return (LCDRowRange){ MAX(0, p1->y), endy }; } #endif LCDRowRange fillQuad(uint8_t* bitmap, int rowstride, Point3D* p1, Point3D* p2, Point3D* p3, Point3D* p4, uint8_t pattern[8]) { // XXX - implement with 3 fillRange_zbuf() calls fillTriangle(bitmap, rowstride, p1, p2, p3, pattern); return fillTriangle(bitmap, rowstride, p1, p3, p4, pattern); } #if ENABLE_Z_BUFFER LCDRowRange fillQuad_zbuf(uint8_t* bitmap, int rowstride, Point3D* p1, Point3D* p2, Point3D* p3, Point3D* p4, uint8_t pattern[8]) { // XXX - implement with 3 fillRange_zbuf() calls fillTriangle_zbuf(bitmap, rowstride, p1, p2, p3, pattern); return fillTriangle_zbuf(bitmap, rowstride, p1, p3, p4, pattern); } #endif