src/gb/renderers/software.c (view raw)
1/* Copyright (c) 2013-2016 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba/internal/gb/renderers/software.h>
7
8#include <mgba/core/tile-cache.h>
9#include <mgba/internal/gb/io.h>
10#include <mgba-util/memory.h>
11
12static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum GBModel model);
13static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer);
14static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
15static void GBVideoSoftwareRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value);
16static void GBVideoSoftwareRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address);
17static void GBVideoSoftwareRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam);
18static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax);
19static void GBVideoSoftwareRendererFinishScanline(struct GBVideoRenderer* renderer, int y);
20static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer);
21static void GBVideoSoftwareRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels);
22static void GBVideoSoftwareRendererPutPixels(struct GBVideoRenderer* renderer, size_t stride, const void* pixels);
23
24static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, uint8_t* maps, int startX, int endX, int sx, int sy);
25static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int startX, int endX, int y);
26
27static void _clearScreen(struct GBVideoSoftwareRenderer* renderer) {
28 int y;
29 for (y = 0; y < GB_VIDEO_VERTICAL_PIXELS; ++y) {
30 color_t* row = &renderer->outputBuffer[renderer->outputBufferStride * y];
31 int x;
32 for (x = 0; x < GB_VIDEO_HORIZONTAL_PIXELS; x += 4) {
33 row[x + 0] = renderer->palette[0];
34 row[x + 1] = renderer->palette[0];
35 row[x + 2] = renderer->palette[0];
36 row[x + 3] = renderer->palette[0];
37 }
38 }
39}
40
41void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer* renderer) {
42 renderer->d.init = GBVideoSoftwareRendererInit;
43 renderer->d.deinit = GBVideoSoftwareRendererDeinit;
44 renderer->d.writeVideoRegister = GBVideoSoftwareRendererWriteVideoRegister;
45 renderer->d.writePalette = GBVideoSoftwareRendererWritePalette;
46 renderer->d.writeVRAM = GBVideoSoftwareRendererWriteVRAM;
47 renderer->d.writeOAM = GBVideoSoftwareRendererWriteOAM;
48 renderer->d.drawRange = GBVideoSoftwareRendererDrawRange;
49 renderer->d.finishScanline = GBVideoSoftwareRendererFinishScanline;
50 renderer->d.finishFrame = GBVideoSoftwareRendererFinishFrame;
51 renderer->d.getPixels = GBVideoSoftwareRendererGetPixels;
52 renderer->d.putPixels = GBVideoSoftwareRendererPutPixels;
53
54 renderer->d.disableBG = false;
55 renderer->d.disableOBJ = false;
56 renderer->d.disableWIN = false;
57
58 renderer->temporaryBuffer = 0;
59}
60
61static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum GBModel model) {
62 struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
63 softwareRenderer->lcdc = 0;
64 softwareRenderer->scy = 0;
65 softwareRenderer->scx = 0;
66 softwareRenderer->wy = 0;
67 softwareRenderer->currentWy = 0;
68 softwareRenderer->wx = 0;
69 softwareRenderer->model = model;
70
71 _clearScreen(softwareRenderer);
72}
73
74static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer) {
75 struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
76 UNUSED(softwareRenderer);
77}
78
79static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) {
80 struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
81 switch (address) {
82 case REG_LCDC:
83 softwareRenderer->lcdc = value;
84 break;
85 case REG_SCY:
86 softwareRenderer->scy = value;
87 break;
88 case REG_SCX:
89 softwareRenderer->scx = value;
90 break;
91 case REG_WY:
92 softwareRenderer->wy = value;
93 break;
94 case REG_WX:
95 softwareRenderer->wx = value;
96 break;
97 }
98 return value;
99}
100
101static void GBVideoSoftwareRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value) {
102 struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
103#ifdef COLOR_16_BIT
104#ifdef COLOR_5_6_5
105 color_t color = 0;
106 color |= (value & 0x001F) << 11;
107 color |= (value & 0x03E0) << 1;
108 color |= (value & 0x7C00) >> 10;
109#else
110 color_t color = value;
111#endif
112#else
113 color_t color = 0;
114 color |= (value << 3) & 0xF8;
115 color |= (value << 6) & 0xF800;
116 color |= (value << 9) & 0xF80000;
117 color |= (color >> 5) & 0x070707;
118#endif
119 softwareRenderer->palette[index] = color;
120 if (renderer->cache) {
121 mTileCacheWritePalette(renderer->cache, index << 1);
122 }
123}
124
125static void GBVideoSoftwareRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) {
126 if (renderer->cache) {
127 mTileCacheWriteVRAM(renderer->cache, address);
128 }
129}
130
131static void GBVideoSoftwareRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam) {
132 UNUSED(renderer);
133 UNUSED(oam);
134 // Nothing to do
135}
136
137static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax) {
138 struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
139 uint8_t* maps = &softwareRenderer->d.vram[GB_BASE_MAP];
140 if (GBRegisterLCDCIsTileMap(softwareRenderer->lcdc)) {
141 maps += GB_SIZE_MAP;
142 }
143 if (softwareRenderer->d.disableBG) {
144 memset(&softwareRenderer->row[startX], 0, endX - startX);
145 }
146 if (GBRegisterLCDCIsBgEnable(softwareRenderer->lcdc) || softwareRenderer->model >= GB_MODEL_CGB) {
147 if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->wy <= y && endX >= softwareRenderer->wx - 7) {
148 if (softwareRenderer->wx - 7 > 0 && !softwareRenderer->d.disableBG) {
149 GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, softwareRenderer->wx - 7, softwareRenderer->scx, softwareRenderer->scy + y);
150 }
151
152 maps = &softwareRenderer->d.vram[GB_BASE_MAP];
153 if (GBRegisterLCDCIsWindowTileMap(softwareRenderer->lcdc)) {
154 maps += GB_SIZE_MAP;
155 }
156 if (!softwareRenderer->d.disableWIN) {
157 GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, softwareRenderer->wx - 7, endX, 7 - softwareRenderer->wx, softwareRenderer->currentWy);
158 }
159 } else if (!softwareRenderer->d.disableBG) {
160 GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, endX, softwareRenderer->scx, softwareRenderer->scy + y);
161 }
162 } else if (!softwareRenderer->d.disableBG) {
163 memset(&softwareRenderer->row[startX], 0, endX - startX);
164 }
165
166 if (GBRegisterLCDCIsObjEnable(softwareRenderer->lcdc) && !softwareRenderer->d.disableOBJ) {
167 size_t i;
168 for (i = 0; i < oamMax; ++i) {
169 GBVideoSoftwareRendererDrawObj(softwareRenderer, &obj[i], startX, endX, y);
170 }
171 }
172 color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
173 int x;
174 for (x = startX; x + 7 < (endX & ~7); x += 8) {
175 row[x] = softwareRenderer->palette[softwareRenderer->row[x] & 0x7F];
176 row[x + 1] = softwareRenderer->palette[softwareRenderer->row[x + 1] & 0x7F];
177 row[x + 2] = softwareRenderer->palette[softwareRenderer->row[x + 2] & 0x7F];
178 row[x + 3] = softwareRenderer->palette[softwareRenderer->row[x + 3] & 0x7F];
179 row[x + 4] = softwareRenderer->palette[softwareRenderer->row[x + 4] & 0x7F];
180 row[x + 5] = softwareRenderer->palette[softwareRenderer->row[x + 5] & 0x7F];
181 row[x + 6] = softwareRenderer->palette[softwareRenderer->row[x + 6] & 0x7F];
182 row[x + 7] = softwareRenderer->palette[softwareRenderer->row[x + 7] & 0x7F];
183 }
184 for (; x < endX; ++x) {
185 row[x] = softwareRenderer->palette[softwareRenderer->row[x] & 0x7F];
186 }
187}
188
189static void GBVideoSoftwareRendererFinishScanline(struct GBVideoRenderer* renderer, int y) {
190 struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
191 if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->wy <= y && softwareRenderer->wx - 7 < GB_VIDEO_HORIZONTAL_PIXELS) {
192 ++softwareRenderer->currentWy;
193 }
194}
195
196static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer) {
197 struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
198
199 if (softwareRenderer->temporaryBuffer) {
200 mappedMemoryFree(softwareRenderer->temporaryBuffer, GB_VIDEO_HORIZONTAL_PIXELS * GB_VIDEO_VERTICAL_PIXELS * 4);
201 softwareRenderer->temporaryBuffer = 0;
202 }
203 if (!GBRegisterLCDCIsEnable(softwareRenderer->lcdc)) {
204 _clearScreen(softwareRenderer);
205 }
206 softwareRenderer->currentWy = 0;
207}
208
209static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, uint8_t* maps, int startX, int endX, int sx, int sy) {
210 uint8_t* data = renderer->d.vram;
211 uint8_t* attr = &maps[GB_SIZE_VRAM_BANK0];
212 if (!GBRegisterLCDCIsTileData(renderer->lcdc)) {
213 data += 0x1000;
214 }
215 int topY = ((sy >> 3) & 0x1F) * 0x20;
216 int bottomY = sy & 7;
217 if (startX < 0) {
218 startX = 0;
219 }
220 int x;
221 if ((startX + sx) & 7) {
222 int startX2 = startX + 8 - ((startX + sx) & 7);
223 for (x = startX; x < startX2; ++x) {
224 uint8_t* localData = data;
225 int localY = bottomY;
226 int topX = ((x + sx) >> 3) & 0x1F;
227 int bottomX = 7 - ((x + sx) & 7);
228 int bgTile;
229 if (GBRegisterLCDCIsTileData(renderer->lcdc)) {
230 bgTile = maps[topX + topY];
231 } else {
232 bgTile = ((int8_t*) maps)[topX + topY];
233 }
234 int p = 0;
235 if (renderer->model >= GB_MODEL_CGB) {
236 GBObjAttributes attrs = attr[topX + topY];
237 p = GBObjAttributesGetCGBPalette(attrs) * 4;
238 if (GBObjAttributesIsPriority(attrs) && GBRegisterLCDCIsBgEnable(renderer->lcdc)) {
239 p |= 0x80;
240 }
241 if (GBObjAttributesIsBank(attrs)) {
242 localData += GB_SIZE_VRAM_BANK0;
243 }
244 if (GBObjAttributesIsYFlip(attrs)) {
245 localY = 7 - bottomY;
246 }
247 if (GBObjAttributesIsXFlip(attrs)) {
248 bottomX = 7 - bottomX;
249 }
250 }
251 uint8_t tileDataLower = localData[(bgTile * 8 + localY) * 2];
252 uint8_t tileDataUpper = localData[(bgTile * 8 + localY) * 2 + 1];
253 tileDataUpper >>= bottomX;
254 tileDataLower >>= bottomX;
255 renderer->row[x] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
256 }
257 startX = startX2;
258 }
259 for (x = startX; x < endX; x += 8) {
260 uint8_t* localData = data;
261 int localY = bottomY;
262 int topX = ((x + sx) >> 3) & 0x1F;
263 int bgTile;
264 if (GBRegisterLCDCIsTileData(renderer->lcdc)) {
265 bgTile = maps[topX + topY];
266 } else {
267 bgTile = ((int8_t*) maps)[topX + topY];
268 }
269 int p = 0;
270 if (renderer->model >= GB_MODEL_CGB) {
271 GBObjAttributes attrs = attr[topX + topY];
272 p = GBObjAttributesGetCGBPalette(attrs) * 4;
273 if (GBObjAttributesIsPriority(attrs) && GBRegisterLCDCIsBgEnable(renderer->lcdc)) {
274 p |= 0x80;
275 }
276 if (GBObjAttributesIsBank(attrs)) {
277 localData += GB_SIZE_VRAM_BANK0;
278 }
279 if (GBObjAttributesIsYFlip(attrs)) {
280 localY = 7 - bottomY;
281 }
282 if (GBObjAttributesIsXFlip(attrs)) {
283 uint8_t tileDataLower = localData[(bgTile * 8 + localY) * 2];
284 uint8_t tileDataUpper = localData[(bgTile * 8 + localY) * 2 + 1];
285 renderer->row[x + 0] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
286 renderer->row[x + 1] = p | (tileDataUpper & 2) | ((tileDataLower & 2) >> 1);
287 renderer->row[x + 2] = p | ((tileDataUpper & 4) >> 1) | ((tileDataLower & 4) >> 2);
288 renderer->row[x + 3] = p | ((tileDataUpper & 8) >> 2) | ((tileDataLower & 8) >> 3);
289 renderer->row[x + 4] = p | ((tileDataUpper & 16) >> 3) | ((tileDataLower & 16) >> 4);
290 renderer->row[x + 5] = p | ((tileDataUpper & 32) >> 4) | ((tileDataLower & 32) >> 5);
291 renderer->row[x + 6] = p | ((tileDataUpper & 64) >> 5) | ((tileDataLower & 64) >> 6);
292 renderer->row[x + 7] = p | ((tileDataUpper & 128) >> 6) | ((tileDataLower & 128) >> 7);
293 continue;
294 }
295 }
296 uint8_t tileDataLower = localData[(bgTile * 8 + localY) * 2];
297 uint8_t tileDataUpper = localData[(bgTile * 8 + localY) * 2 + 1];
298 renderer->row[x + 7] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
299 renderer->row[x + 6] = p | (tileDataUpper & 2) | ((tileDataLower & 2) >> 1);
300 renderer->row[x + 5] = p | ((tileDataUpper & 4) >> 1) | ((tileDataLower & 4) >> 2);
301 renderer->row[x + 4] = p | ((tileDataUpper & 8) >> 2) | ((tileDataLower & 8) >> 3);
302 renderer->row[x + 3] = p | ((tileDataUpper & 16) >> 3) | ((tileDataLower & 16) >> 4);
303 renderer->row[x + 2] = p | ((tileDataUpper & 32) >> 4) | ((tileDataLower & 32) >> 5);
304 renderer->row[x + 1] = p | ((tileDataUpper & 64) >> 5) | ((tileDataLower & 64) >> 6);
305 renderer->row[x + 0] = p | ((tileDataUpper & 128) >> 6) | ((tileDataLower & 128) >> 7);
306 }
307}
308
309static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int startX, int endX, int y) {
310 int ix = obj->x - 8;
311 if (endX < ix || startX >= ix + 8) {
312 return;
313 }
314 if (obj->x < endX) {
315 endX = obj->x;
316 }
317 if (obj->x - 8 > startX) {
318 startX = obj->x - 8;
319 }
320 if (startX < 0) {
321 startX = 0;
322 }
323 uint8_t* data = renderer->d.vram;
324 int tileOffset = 0;
325 int bottomY;
326 if (GBObjAttributesIsYFlip(obj->attr)) {
327 bottomY = 7 - ((y - obj->y - 16) & 7);
328 if (GBRegisterLCDCIsObjSize(renderer->lcdc) && y - obj->y < -8) {
329 ++tileOffset;
330 }
331 } else {
332 bottomY = (y - obj->y - 16) & 7;
333 if (GBRegisterLCDCIsObjSize(renderer->lcdc) && y - obj->y >= -8) {
334 ++tileOffset;
335 }
336 }
337 if (GBRegisterLCDCIsObjSize(renderer->lcdc) && obj->tile & 1) {
338 --tileOffset;
339 }
340 uint8_t mask = GBObjAttributesIsPriority(obj->attr) ? 0x63 : 0x60;
341 uint8_t mask2 = GBObjAttributesIsPriority(obj->attr) ? 0 : 0x83;
342 int p;
343 if (renderer->model >= GB_MODEL_CGB) {
344 p = (GBObjAttributesGetCGBPalette(obj->attr) + 8) * 4;
345 if (GBObjAttributesIsBank(obj->attr)) {
346 data += GB_SIZE_VRAM_BANK0;
347 }
348 if (!GBRegisterLCDCIsBgEnable(renderer->lcdc)) {
349 mask = 0x60;
350 mask2 = 0x83;
351 }
352 } else {
353 p = (GBObjAttributesGetPalette(obj->attr) + 8) * 4;
354 }
355 int bottomX;
356 int x = startX;
357 if ((x - obj->x) & 7) {
358 for (; x < endX; ++x) {
359 if (GBObjAttributesIsXFlip(obj->attr)) {
360 bottomX = (x - obj->x) & 7;
361 } else {
362 bottomX = 7 - ((x - obj->x) & 7);
363 }
364 int objTile = obj->tile + tileOffset;
365 uint8_t tileDataLower = data[(objTile * 8 + bottomY) * 2];
366 uint8_t tileDataUpper = data[(objTile * 8 + bottomY) * 2 + 1];
367 tileDataUpper >>= bottomX;
368 tileDataLower >>= bottomX;
369 color_t current = renderer->row[x];
370 if (((tileDataUpper | tileDataLower) & 1) && !(current & mask) && (current & mask2) <= 0x80) {
371 renderer->row[x] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
372 }
373 }
374 } else if (GBObjAttributesIsXFlip(obj->attr)) {
375 int objTile = obj->tile + tileOffset;
376 uint8_t tileDataLower = data[(objTile * 8 + bottomY) * 2];
377 uint8_t tileDataUpper = data[(objTile * 8 + bottomY) * 2 + 1];
378 color_t current;
379 current = renderer->row[x];
380 if (((tileDataUpper | tileDataLower) & 1) && !(current & mask) && (current & mask2) <= 0x80) {
381 renderer->row[x] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
382 }
383 current = renderer->row[x + 1];
384 if (((tileDataUpper | tileDataLower) & 2) && !(current & mask) && (current & mask2) <= 0x80) {
385 renderer->row[x + 1] = p | (tileDataUpper & 2) | ((tileDataLower & 2) >> 1);
386 }
387 current = renderer->row[x + 2];
388 if (((tileDataUpper | tileDataLower) & 4) && !(current & mask) && (current & mask2) <= 0x80) {
389 renderer->row[x + 2] = p | ((tileDataUpper & 4) >> 1) | ((tileDataLower & 4) >> 2);
390 }
391 current = renderer->row[x + 3];
392 if (((tileDataUpper | tileDataLower) & 8) && !(current & mask) && (current & mask2) <= 0x80) {
393 renderer->row[x + 3] = p | ((tileDataUpper & 8) >> 2) | ((tileDataLower & 8) >> 3);
394 }
395 current = renderer->row[x + 4];
396 if (((tileDataUpper | tileDataLower) & 16) && !(current & mask) && (current & mask2) <= 0x80) {
397 renderer->row[x + 4] = p | ((tileDataUpper & 16) >> 3) | ((tileDataLower & 16) >> 4);
398 }
399 current = renderer->row[x + 5];
400 if (((tileDataUpper | tileDataLower) & 32) && !(current & mask) && (current & mask2) <= 0x80) {
401 renderer->row[x + 5] = p | ((tileDataUpper & 32) >> 4) | ((tileDataLower & 32) >> 5);
402 }
403 current = renderer->row[x + 6];
404 if (((tileDataUpper | tileDataLower) & 64) && !(current & mask) && (current & mask2) <= 0x80) {
405 renderer->row[x + 6] = p | ((tileDataUpper & 64) >> 5) | ((tileDataLower & 64) >> 6);
406 }
407 current = renderer->row[x + 7];
408 if (((tileDataUpper | tileDataLower) & 128) && !(current & mask) && (current & mask2) <= 0x80) {
409 renderer->row[x + 7] = p | ((tileDataUpper & 128) >> 6) | ((tileDataLower & 128) >> 7);
410 }
411 } else {
412 int objTile = obj->tile + tileOffset;
413 uint8_t tileDataLower = data[(objTile * 8 + bottomY) * 2];
414 uint8_t tileDataUpper = data[(objTile * 8 + bottomY) * 2 + 1];
415 color_t current;
416 current = renderer->row[x + 7];
417 if (((tileDataUpper | tileDataLower) & 1) && !(current & mask) && (current & mask2) <= 0x80) {
418 renderer->row[x + 7] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
419 }
420 current = renderer->row[x + 6];
421 if (((tileDataUpper | tileDataLower) & 2) && !(current & mask) && (current & mask2) <= 0x80) {
422 renderer->row[x + 6] = p | (tileDataUpper & 2) | ((tileDataLower & 2) >> 1);
423 }
424 current = renderer->row[x + 5];
425 if (((tileDataUpper | tileDataLower) & 4) && !(current & mask) && (current & mask2) <= 0x80) {
426 renderer->row[x + 5] = p | ((tileDataUpper & 4) >> 1) | ((tileDataLower & 4) >> 2);
427 }
428 current = renderer->row[x + 4];
429 if (((tileDataUpper | tileDataLower) & 8) && !(current & mask) && (current & mask2) <= 0x80) {
430 renderer->row[x + 4] = p | ((tileDataUpper & 8) >> 2) | ((tileDataLower & 8) >> 3);
431 }
432 current = renderer->row[x + 3];
433 if (((tileDataUpper | tileDataLower) & 16) && !(current & mask) && (current & mask2) <= 0x80) {
434 renderer->row[x + 3] = p | ((tileDataUpper & 16) >> 3) | ((tileDataLower & 16) >> 4);
435 }
436 current = renderer->row[x + 2];
437 if (((tileDataUpper | tileDataLower) & 32) && !(current & mask) && (current & mask2) <= 0x80) {
438 renderer->row[x + 2] = p | ((tileDataUpper & 32) >> 4) | ((tileDataLower & 32) >> 5);
439 }
440 current = renderer->row[x + 1];
441 if (((tileDataUpper | tileDataLower) & 64) && !(current & mask) && (current & mask2) <= 0x80) {
442 renderer->row[x + 1] = p | ((tileDataUpper & 64) >> 5) | ((tileDataLower & 64) >> 6);
443 }
444 current = renderer->row[x];
445 if (((tileDataUpper | tileDataLower) & 128) && !(current & mask) && (current & mask2) <= 0x80) {
446 renderer->row[x] = p | ((tileDataUpper & 128) >> 6) | ((tileDataLower & 128) >> 7);
447 }
448 }
449}
450
451static void GBVideoSoftwareRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels) {
452 struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
453 // TODO: Share with GBAVideoSoftwareRendererGetPixels
454#ifdef COLOR_16_BIT
455 *stride = GB_VIDEO_HORIZONTAL_PIXELS;
456 if (!softwareRenderer->temporaryBuffer) {
457 softwareRenderer->temporaryBuffer = anonymousMemoryMap(GB_VIDEO_HORIZONTAL_PIXELS * GB_VIDEO_VERTICAL_PIXELS * 4);
458 }
459 *pixels = softwareRenderer->temporaryBuffer;
460 unsigned y, x;
461 for (y = 0; y < GB_VIDEO_VERTICAL_PIXELS; ++y) {
462 for (x = 0; x < GB_VIDEO_HORIZONTAL_PIXELS; ++x) {
463 color_t inColor = softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y + x];
464 uint32_t outColor;
465#ifdef COLOR_5_6_5
466 outColor = (inColor & 0x1F) << 19;
467 outColor |= (inColor & 0x7C0) << 5;
468 outColor |= (inColor & 0xF800) >> 8;
469#else
470 outColor = (inColor & 0x1F) << 3;
471 outColor |= (inColor & 0x3E0) << 6;
472 outColor |= (inColor & 0x7C00) << 9;
473#endif
474 softwareRenderer->temporaryBuffer[GB_VIDEO_HORIZONTAL_PIXELS * y + x] = outColor;
475 }
476 }
477#else
478 *stride = softwareRenderer->outputBufferStride;
479 *pixels = softwareRenderer->outputBuffer;
480#endif
481}
482
483
484static void GBVideoSoftwareRendererPutPixels(struct GBVideoRenderer* renderer, size_t stride, const void* pixels) {
485 struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
486 // TODO: Share with GBAVideoSoftwareRendererGetPixels
487
488 const color_t* colorPixels = pixels;
489 unsigned i;
490 for (i = 0; i < GB_VIDEO_VERTICAL_PIXELS; ++i) {
491 memmove(&softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * i], &colorPixels[stride * i], GB_VIDEO_HORIZONTAL_PIXELS * BYTES_PER_PIXEL);
492 }
493}