1 /*
2  * softsurface.cpp
3  *  An 8-bit rendering surface that can quickly upscale and blit 8-bit paletted buffers to an external 32-bit buffer.
4  *
5  * Copyright � 2018, Alex Dawson. All rights reserved.
6  */
7 
8 #include "softsurface.h"
9 
10 #include "pragmas.h"
11 #include "build.h"
12 
13 static int bufferSize;
14 static uint8_t* buffer;
15 static vec2_t bufferRes;
16 
17 static vec2_t destBufferRes;
18 
19 static uint32_t xScale16;
20 static uint32_t yScale16;
21 static uint32_t recXScale16;
22 
23 static uint32_t pPal[256];
24 
25 // lookup table to find the source position within a scanline
26 static uint16_t* scanPosLookupTable;
27 
28 template <uint32_t multiple>
roundUp(uint32_t num)29 static uint32_t roundUp(uint32_t num)
30 {
31     return (num+multiple-1)/multiple * multiple;
32 }
33 
countTrailingZeros(uint32_t u)34 static uint32_t countTrailingZeros(uint32_t u)
35 {
36 #if (defined __GNUC__  && __GNUC__>=3) || defined __clang__
37     return __builtin_ctz(u);
38 #elif defined _MSC_VER
39     DWORD result;
40     _BitScanForward(&result, u);
41     return result;
42 #else
43     uint32_t last = u;
44     for (; u != 0; last = u, u >>= 1);
45     return last;
46 #endif
47 }
48 
softsurface_initialize(vec2_t bufferResolution,vec2_t destBufferResolution)49 bool softsurface_initialize(vec2_t bufferResolution,
50                             vec2_t destBufferResolution)
51 {
52     if (buffer)
53         softsurface_destroy();
54 
55     bufferRes = bufferResolution;
56     destBufferRes = destBufferResolution;
57 
58     xScale16 = divscale16(destBufferRes.x, bufferRes.x);
59     yScale16 = divscale16(destBufferRes.y, bufferRes.y);
60     recXScale16 = divscale16(bufferRes.x, destBufferRes.x);
61 
62     // allocate one continuous block of memory large enough to hold the buffer, the palette,
63     // and the scanPosLookupTable while maintaining alignment for each
64     uint32_t newBufferSize = roundUp<16>(bufferRes.x * bufferRes.y);
65     zpl_virtual_memory vm = Xvm_alloc(0, newBufferSize + sizeof(uint16_t) * destBufferRes.x);
66 
67     bufferSize = vm.size;
68     buffer     = (uint8_t *)vm.data;
69 
70     scanPosLookupTable = (uint16_t *)(buffer + newBufferSize);
71 
72     // calculate the scanPosLookupTable for horizontal scaling
73     uint32_t incr = recXScale16;
74     for (int32_t i = 0; i < destBufferRes.x; ++i)
75     {
76         scanPosLookupTable[i] = incr >> 16;
77         incr += recXScale16;
78     }
79 
80     return true;
81 }
82 
softsurface_destroy()83 void softsurface_destroy()
84 {
85     if (!buffer)
86         return;
87 
88     Xvm_free(zpl_vm(buffer, bufferSize));
89     buffer = nullptr;
90 
91     scanPosLookupTable = 0;
92 
93     xScale16 = 0;
94     yScale16 = 0;
95     recXScale16 = 0;
96 
97     bufferRes = {};
98     destBufferRes = {};
99 }
100 
softsurface_setPalette(void * pPalette,uint32_t destRedMask,uint32_t destGreenMask,uint32_t destBlueMask)101 void softsurface_setPalette(void* pPalette,
102                             uint32_t destRedMask,
103                             uint32_t destGreenMask,
104                             uint32_t destBlueMask)
105 {
106     if (!buffer)
107         return;
108     if (!pPalette)
109         return;
110 
111     uint32_t destRedShift = countTrailingZeros(destRedMask);
112     uint32_t destRedLoss = 8 - countTrailingZeros((destRedMask>>destRedShift)+1);
113     uint32_t destGreenShift = countTrailingZeros(destGreenMask);
114     uint32_t destGreenLoss = 8 - countTrailingZeros((destGreenMask>>destGreenShift)+1);
115     uint32_t destBlueShift = countTrailingZeros(destBlueMask);
116     uint32_t destBlueLoss = 8 - countTrailingZeros((destBlueMask>>destBlueShift)+1);
117 
118     uint8_t* pUI8Palette = (uint8_t*) pPalette;
119     for (int i = 0; i < 256; ++i)
120     {
121         pPal[i] = ((pUI8Palette[sizeof(uint32_t)*i] >> destRedLoss << destRedShift) & destRedMask) |
122                   ((pUI8Palette[sizeof(uint32_t)*i+1] >> destGreenLoss << destGreenShift) & destGreenMask) |
123                   ((pUI8Palette[sizeof(uint32_t)*i+2] >> destBlueLoss << destBlueShift) & destBlueMask);
124     }
125 }
126 
softsurface_getBuffer()127 uint8_t* softsurface_getBuffer()
128 {
129     return buffer;
130 }
131 
softsurface_getBufferResolution()132 vec2_t softsurface_getBufferResolution()
133 {
134     return bufferRes;
135 }
136 
softsurface_getDestinationBufferResolution()137 vec2_t softsurface_getDestinationBufferResolution()
138 {
139     return destBufferRes;
140 }
141 
142 #define BLIT(x) pDst[x] = *((UINTTYPE*)(pPal+pSrc[pScanPos[x]]))
143 #define BLIT2(x) BLIT(x); BLIT(x+1)
144 #define BLIT4(x) BLIT2(x); BLIT2(x+2)
145 #define BLIT8(x) BLIT4(x); BLIT4(x+4)
146 #define BLIT16(x) BLIT8(x); BLIT8(x+8)
147 #define BLIT32(x) BLIT16(x); BLIT16(x+16)
148 #define BLIT64(x) BLIT32(x); BLIT32(x+32)
149 template <typename UINTTYPE>
softsurface_blitBufferInternal(UINTTYPE * destBuffer)150 void softsurface_blitBufferInternal(UINTTYPE* destBuffer)
151 {
152     const uint8_t* __restrict pSrc = buffer;
153     UINTTYPE* __restrict pDst = destBuffer;
154     const UINTTYPE* const pEnd = destBuffer+destBufferRes.x*mulscale16(yScale16, bufferRes.y);
155     uint32_t remainder = 0;
156     while (pDst < pEnd)
157     {
158         uint16_t* __restrict pScanPos = scanPosLookupTable;
159         UINTTYPE* const pScanEnd = pDst+destBufferRes.x;
160         while (pDst < pScanEnd-64)
161         {
162             BLIT64(0);
163             pDst += 64;
164             pScanPos += 64;
165         }
166         while (pDst < pScanEnd)
167         {
168             BLIT(0);
169             ++pDst;
170             ++pScanPos;
171         }
172         pSrc += bufferRes.x;
173 
174         static const uint32_t MASK16 = (1<<16)-1;
175         uint32_t linesCopied = 1;
176         uint32_t linesToCopy = yScale16+remainder;
177         remainder = linesToCopy & MASK16;
178         linesToCopy = (linesToCopy >> 16)-1;
179         const UINTTYPE* const __restrict pScanLineSrc = pDst-destBufferRes.x;
180         while (linesToCopy)
181         {
182             uint32_t lines = min(linesCopied, linesToCopy);
183             memcpy(pDst, pScanLineSrc, sizeof(UINTTYPE)*lines*destBufferRes.x);
184             pDst += lines*destBufferRes.x;
185             linesToCopy -= lines;
186         }
187     }
188 }
189 
softsurface_blitBuffer(uint32_t * destBuffer,uint32_t destBpp)190 void softsurface_blitBuffer(uint32_t* destBuffer,
191                             uint32_t destBpp)
192 {
193     if (!buffer)
194         return;
195     if (!destBuffer)
196         return;
197 
198     switch (destBpp)
199     {
200     case 15:
201         softsurface_blitBufferInternal<uint16_t>((uint16_t*) destBuffer);
202         break;
203     case 16:
204         softsurface_blitBufferInternal<uint16_t>((uint16_t*) destBuffer);
205         break;
206     case 24:
207         softsurface_blitBufferInternal<uint32_t>(destBuffer);
208         break;
209     case 32:
210         softsurface_blitBufferInternal<uint32_t>(destBuffer);
211         break;
212     default:
213         return;
214     }
215 }
216