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