src/gba/renderers/video-software.c (view raw)
1#include "video-software.h"
2
3#include "gba.h"
4#include "gba-io.h"
5
6#include <string.h>
7
8static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer);
9static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer);
10static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
11static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
12static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
13static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer);
14
15static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer);
16static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value);
17static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value);
18static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value);
19static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value);
20static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value);
21static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
22static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value);
23static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
24static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value);
25static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value);
26
27static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y);
28static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
29static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
30static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
31static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
32static void _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y);
33static void _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y);
34static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, int priority);
35
36static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer);
37static inline uint32_t _brighten(uint32_t color, int y);
38static inline uint32_t _darken(uint32_t color, int y);
39static uint32_t _mix(int weightA, uint32_t colorA, int weightB, uint32_t colorB);
40
41void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) {
42 renderer->d.init = GBAVideoSoftwareRendererInit;
43 renderer->d.deinit = GBAVideoSoftwareRendererDeinit;
44 renderer->d.writeVideoRegister = GBAVideoSoftwareRendererWriteVideoRegister;
45 renderer->d.writePalette = GBAVideoSoftwareRendererWritePalette;
46 renderer->d.drawScanline = GBAVideoSoftwareRendererDrawScanline;
47 renderer->d.finishFrame = GBAVideoSoftwareRendererFinishFrame;
48
49 renderer->d.turbo = 0;
50 renderer->d.framesPending = 0;
51 renderer->d.frameskip = 0;
52
53 {
54 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
55 renderer->mutex = mutex;
56 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
57 renderer->upCond = cond;
58 renderer->downCond = cond;
59 }
60}
61
62static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) {
63 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
64 int i;
65
66 softwareRenderer->dispcnt.packed = 0x0080;
67
68 softwareRenderer->target1Obj = 0;
69 softwareRenderer->target1Bd = 0;
70 softwareRenderer->target2Obj = 0;
71 softwareRenderer->target2Bd = 0;
72 softwareRenderer->blendEffect = BLEND_NONE;
73 memset(softwareRenderer->normalPalette, 0, sizeof(softwareRenderer->normalPalette));
74 memset(softwareRenderer->variantPalette, 0, sizeof(softwareRenderer->variantPalette));
75
76 softwareRenderer->blda = 0;
77 softwareRenderer->bldb = 0;
78 softwareRenderer->bldy = 0;
79
80 for (i = 0; i < 4; ++i) {
81 struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i];
82 bg->index = i;
83 bg->enabled = 0;
84 bg->priority = 0;
85 bg->charBase = 0;
86 bg->mosaic = 0;
87 bg->multipalette = 0;
88 bg->screenBase = 0;
89 bg->overflow = 0;
90 bg->size = 0;
91 bg->target1 = 0;
92 bg->target2 = 0;
93 bg->x = 0;
94 bg->y = 0;
95 bg->refx = 0;
96 bg->refy = 0;
97 bg->dx = 256;
98 bg->dmx = 0;
99 bg->dy = 0;
100 bg->dmy = 256;
101 bg->sx = 0;
102 bg->sy = 0;
103 }
104
105 pthread_mutex_init(&softwareRenderer->mutex, 0);
106 pthread_cond_init(&softwareRenderer->upCond, 0);
107 pthread_cond_init(&softwareRenderer->downCond, 0);
108}
109
110static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer) {
111 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
112
113 pthread_mutex_lock(&softwareRenderer->mutex);
114 pthread_cond_broadcast(&softwareRenderer->upCond);
115 pthread_mutex_unlock(&softwareRenderer->mutex);
116
117 pthread_mutex_destroy(&softwareRenderer->mutex);
118 pthread_cond_destroy(&softwareRenderer->upCond);
119 pthread_cond_destroy(&softwareRenderer->downCond);
120}
121
122static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
123 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
124 switch (address) {
125 case REG_DISPCNT:
126 value &= 0xFFFB;
127 softwareRenderer->dispcnt.packed = value;
128 GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer);
129 break;
130 case REG_BG0CNT:
131 value &= 0xFFCF;
132 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[0], value);
133 break;
134 case REG_BG1CNT:
135 value &= 0xFFCF;
136 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[1], value);
137 break;
138 case REG_BG2CNT:
139 value &= 0xFFCF;
140 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[2], value);
141 break;
142 case REG_BG3CNT:
143 value &= 0xFFCF;
144 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[3], value);
145 break;
146 case REG_BG0HOFS:
147 value &= 0x01FF;
148 softwareRenderer->bg[0].x = value;
149 break;
150 case REG_BG0VOFS:
151 value &= 0x01FF;
152 softwareRenderer->bg[0].y = value;
153 break;
154 case REG_BG1HOFS:
155 value &= 0x01FF;
156 softwareRenderer->bg[1].x = value;
157 break;
158 case REG_BG1VOFS:
159 value &= 0x01FF;
160 softwareRenderer->bg[1].y = value;
161 break;
162 case REG_BG2HOFS:
163 value &= 0x01FF;
164 softwareRenderer->bg[2].x = value;
165 break;
166 case REG_BG2VOFS:
167 value &= 0x01FF;
168 softwareRenderer->bg[2].y = value;
169 break;
170 case REG_BG3HOFS:
171 value &= 0x01FF;
172 softwareRenderer->bg[3].x = value;
173 break;
174 case REG_BG3VOFS:
175 value &= 0x01FF;
176 softwareRenderer->bg[3].y = value;
177 break;
178 case REG_BG2PA:
179 GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[2], value);
180 break;
181 case REG_BG2PB:
182 GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[2], value);
183 break;
184 case REG_BG2PC:
185 GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[2], value);
186 break;
187 case REG_BG2PD:
188 GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[2], value);
189 break;
190 case REG_BG2X_LO:
191 GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[2], value);
192 break;
193 case REG_BG2X_HI:
194 GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[2], value);
195 break;
196 case REG_BG2Y_LO:
197 GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[2], value);
198 break;
199 case REG_BG2Y_HI:
200 GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[2], value);
201 break;
202 case REG_BG3PA:
203 GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[3], value);
204 break;
205 case REG_BG3PB:
206 GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[3], value);
207 break;
208 case REG_BG3PC:
209 GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[3], value);
210 break;
211 case REG_BG3PD:
212 GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[3], value);
213 break;
214 case REG_BG3X_LO:
215 GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[3], value);
216 break;
217 case REG_BG3X_HI:
218 GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[3], value);
219 break;
220 case REG_BG3Y_LO:
221 GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[3], value);
222 break;
223 case REG_BG3Y_HI:
224 GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[3], value);
225 break;
226 case REG_BLDCNT:
227 GBAVideoSoftwareRendererWriteBLDCNT(softwareRenderer, value);
228 break;
229 case REG_BLDALPHA:
230 softwareRenderer->blda = value & 0x1F;
231 if (softwareRenderer->blda > 0x10) {
232 softwareRenderer->blda = 0x10;
233 }
234 softwareRenderer->bldb = (value >> 8) & 0x1F;
235 if (softwareRenderer->bldb > 0x10) {
236 softwareRenderer->bldb = 0x10;
237 }
238 break;
239 case REG_BLDY:
240 softwareRenderer->bldy = value & 0x1F;
241 if (softwareRenderer->bldy > 0x10) {
242 softwareRenderer->bldy = 0x10;
243 }
244 _updatePalettes(softwareRenderer);
245 break;
246 default:
247 GBALog(GBA_LOG_STUB, "Stub video register write: %03x", address);
248 }
249 return value;
250}
251
252static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
253 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
254 uint32_t color32 = 0;
255 color32 |= (value << 3) & 0xF8;
256 color32 |= (value << 6) & 0xF800;
257 color32 |= (value << 9) & 0xF80000;
258 softwareRenderer->normalPalette[address >> 1] = color32;
259 if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
260 softwareRenderer->variantPalette[address >> 1] = _brighten(color32, softwareRenderer->bldy);
261 } else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
262 softwareRenderer->variantPalette[address >> 1] = _darken(color32, softwareRenderer->bldy);
263 }
264}
265
266static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
267 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
268 if (renderer->frameskip > 0) {
269 return;
270 }
271 uint32_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
272 if (softwareRenderer->dispcnt.forcedBlank) {
273 for (int x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
274 row[x] = GBA_COLOR_WHITE;
275 }
276 return;
277 } else {
278 uint32_t backdrop = FLAG_UNWRITTEN | FLAG_PRIORITY | FLAG_IS_BACKGROUND;
279 if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA) {
280 backdrop |= softwareRenderer->normalPalette[0];
281 } else {
282 backdrop |= softwareRenderer->variantPalette[0];
283 }
284 for (int x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
285 row[x] = backdrop;
286 }
287 }
288
289 softwareRenderer->row = row;
290
291 softwareRenderer->start = 0;
292 softwareRenderer->end = VIDEO_HORIZONTAL_PIXELS;
293 _drawScanline(softwareRenderer, y);
294}
295
296static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
297 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
298
299 pthread_mutex_lock(&softwareRenderer->mutex);
300 if (renderer->frameskip > 0) {
301 --renderer->frameskip;
302 } else {
303 renderer->framesPending++;
304 pthread_cond_broadcast(&softwareRenderer->upCond);
305 if (!renderer->turbo) {
306 pthread_cond_wait(&softwareRenderer->downCond, &softwareRenderer->mutex);
307 }
308 }
309 pthread_mutex_unlock(&softwareRenderer->mutex);
310
311 softwareRenderer->bg[2].sx = softwareRenderer->bg[2].refx;
312 softwareRenderer->bg[2].sy = softwareRenderer->bg[2].refy;
313 softwareRenderer->bg[3].sx = softwareRenderer->bg[3].refx;
314 softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy;
315}
316
317static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) {
318 renderer->bg[0].enabled = renderer->dispcnt.bg0Enable;
319 renderer->bg[1].enabled = renderer->dispcnt.bg1Enable;
320 renderer->bg[2].enabled = renderer->dispcnt.bg2Enable;
321 renderer->bg[3].enabled = renderer->dispcnt.bg3Enable;
322}
323
324static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value) {
325 (void)(renderer);
326 union GBARegisterBGCNT reg = { .packed = value };
327 bg->priority = reg.priority;
328 bg->charBase = reg.charBase << 14;
329 bg->mosaic = reg.mosaic;
330 bg->multipalette = reg.multipalette;
331 bg->screenBase = reg.screenBase << 11;
332 bg->overflow = reg.overflow;
333 bg->size = reg.size;
334}
335
336static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
337 bg->dx = value;
338}
339
340static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
341 bg->dmx = value;
342}
343
344static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
345 bg->dy = value;
346}
347
348static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
349 bg->dmy = value;
350}
351
352static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
353 bg->refx = (bg->refx & 0xFFFF0000) | value;
354 bg->sx = bg->refx;
355}
356
357static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
358 bg->refx = (bg->refx & 0x0000FFFF) | (value << 16);
359 bg->refx <<= 4;
360 bg->refx >>= 4;
361 bg->sx = bg->refx;
362}
363
364static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
365 bg->refy = (bg->refy & 0xFFFF0000) | value;
366 bg->sy = bg->refy;
367}
368
369static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
370 bg->refy = (bg->refy & 0x0000FFFF) | (value << 16);
371 bg->refy <<= 4;
372 bg->refy >>= 4;
373 bg->sy = bg->refy;
374}
375
376static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value) {
377 union {
378 struct {
379 unsigned target1Bg0 : 1;
380 unsigned target1Bg1 : 1;
381 unsigned target1Bg2 : 1;
382 unsigned target1Bg3 : 1;
383 unsigned target1Obj : 1;
384 unsigned target1Bd : 1;
385 enum BlendEffect effect : 2;
386 unsigned target2Bg0 : 1;
387 unsigned target2Bg1 : 1;
388 unsigned target2Bg2 : 1;
389 unsigned target2Bg3 : 1;
390 unsigned target2Obj : 1;
391 unsigned target2Bd : 1;
392 };
393 uint16_t packed;
394 } bldcnt = { .packed = value };
395
396 enum BlendEffect oldEffect = renderer->blendEffect;
397
398 renderer->bg[0].target1 = bldcnt.target1Bg0;
399 renderer->bg[1].target1 = bldcnt.target1Bg1;
400 renderer->bg[2].target1 = bldcnt.target1Bg2;
401 renderer->bg[3].target1 = bldcnt.target1Bg3;
402 renderer->bg[0].target2 = bldcnt.target2Bg0;
403 renderer->bg[1].target2 = bldcnt.target2Bg1;
404 renderer->bg[2].target2 = bldcnt.target2Bg2;
405 renderer->bg[3].target2 = bldcnt.target2Bg3;
406
407 renderer->blendEffect = bldcnt.effect;
408 renderer->target1Obj = bldcnt.target1Obj;
409 renderer->target1Bd = bldcnt.target1Bd;
410 renderer->target2Obj = bldcnt.target2Obj;
411 renderer->target2Bd = bldcnt.target2Bd;
412
413 if (oldEffect != renderer->blendEffect) {
414 _updatePalettes(renderer);
415 }
416}
417
418static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
419 int i;
420 memset(renderer->spriteLayer, 0, sizeof(renderer->spriteLayer));
421 if (renderer->dispcnt.objEnable) {
422 for (i = 0; i < 128; ++i) {
423 struct GBAObj* sprite = &renderer->d.oam->obj[i];
424 if (sprite->transformed) {
425 _preprocessTransformedSprite(renderer, &renderer->d.oam->tobj[i], y);
426 } else if (!sprite->disable) {
427 _preprocessSprite(renderer, sprite, y);
428 }
429 }
430 }
431
432 int priority;
433 for (priority = 0; priority < 4; ++priority) {
434 _postprocessSprite(renderer, priority);
435 if (renderer->bg[0].enabled && renderer->bg[0].priority == priority && renderer->dispcnt.mode < 2) {
436 _drawBackgroundMode0(renderer, &renderer->bg[0], y);
437 }
438 if (renderer->bg[1].enabled && renderer->bg[1].priority == priority && renderer->dispcnt.mode < 2) {
439 _drawBackgroundMode0(renderer, &renderer->bg[1], y);
440 }
441 if (renderer->bg[2].enabled && renderer->bg[2].priority == priority) {
442 switch (renderer->dispcnt.mode) {
443 case 0:
444 _drawBackgroundMode0(renderer, &renderer->bg[2], y);
445 break;
446 case 1:
447 case 2:
448 _drawBackgroundMode2(renderer, &renderer->bg[2], y);
449 break;
450 case 3:
451 _drawBackgroundMode3(renderer, &renderer->bg[2], y);
452 break;
453 case 4:
454 _drawBackgroundMode4(renderer, &renderer->bg[2], y);
455 break;
456 }
457 renderer->bg[2].sx += renderer->bg[2].dmx;
458 renderer->bg[2].sy += renderer->bg[2].dmy;
459 }
460 if (renderer->bg[3].enabled && renderer->bg[3].priority == priority) {
461 switch (renderer->dispcnt.mode) {
462 case 0:
463 _drawBackgroundMode0(renderer, &renderer->bg[3], y);
464 break;
465 case 2:
466 _drawBackgroundMode2(renderer, &renderer->bg[3], y);
467 break;
468 }
469 renderer->bg[3].sx += renderer->bg[3].dmx;
470 renderer->bg[3].sy += renderer->bg[3].dmy;
471 }
472 }
473}
474
475static void _composite(struct GBAVideoSoftwareRenderer* renderer, int offset, uint32_t color) {
476 uint32_t current = renderer->row[offset];
477 // We stash the priority on the top bits so we cn do a one-operator comparison
478 // The lower the number, the higher the priority, and sprites take precendence over backgrounds
479 // We want to do special processing if the color pixel is target 1, however
480 if (color < current) {
481 if (current & FLAG_UNWRITTEN) {
482 renderer->row[offset] = color;
483 } else if (!(color & FLAG_TARGET_1) || !(current & FLAG_TARGET_2)) {
484 renderer->row[offset] = color | FLAG_FINALIZED;
485 } else {
486 renderer->row[offset] = _mix(renderer->bldb, current, renderer->blda, color) | FLAG_FINALIZED;
487 }
488 } else {
489 if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
490 renderer->row[offset] = _mix(renderer->blda, current, renderer->bldb, color) | FLAG_FINALIZED;
491 } else {
492 renderer->row[offset] = current | FLAG_FINALIZED;
493 }
494 }
495}
496
497#define BACKGROUND_DRAW_PIXEL_16_NORMAL \
498 if (tileData & 0xF && !(renderer->row[outX] & FLAG_FINALIZED)) { \
499 _composite(renderer, outX, renderer->normalPalette[(tileData & 0xF) | (mapData.palette << 4)] | flags); \
500 } \
501 tileData >>= 4;
502
503#define BACKGROUND_DRAW_PIXEL_16_VARIANT \
504 if (tileData & 0xF && !(renderer->row[outX] & FLAG_FINALIZED)) { \
505 _composite(renderer, outX, renderer->variantPalette[(tileData & 0xF) | (mapData.palette << 4)] | flags); \
506 } \
507 tileData >>= 4;
508
509#define BACKGROUND_DRAW_PIXEL_256_NORMAL \
510 if (tileData & 0xFF && !(renderer->row[outX] & FLAG_FINALIZED)) { \
511 _composite(renderer, outX, renderer->normalPalette[tileData & 0xFF] | flags); \
512 } \
513 tileData >>= 8;
514
515#define BACKGROUND_DRAW_PIXEL_256_VARIANT \
516 if (tileData & 0xFF && !(renderer->row[outX] & FLAG_FINALIZED)) { \
517 _composite(renderer, outX, renderer->variantPalette[tileData & 0xFF] | flags); \
518 } \
519 tileData >>= 8;
520
521#define BACKGROUND_TEXT_SELECT_CHARACTER \
522 localX = tileX * 8 + inX; \
523 xBase = localX & 0xF8; \
524 if (background->size & 1) { \
525 xBase += (localX & 0x100) << 5; \
526 } \
527 screenBase = (background->screenBase >> 1) + (xBase >> 3) + (yBase << 2); \
528 mapData.packed = renderer->d.vram[screenBase]; \
529 if (!mapData.vflip) { \
530 localY = inY & 0x7; \
531 } else { \
532 localY = 7 - (inY & 0x7); \
533 }
534
535#define BACKGROUND_MODE_0_TILE_16_LOOP(TYPE) \
536 for (; tileX < 30; ++tileX) { \
537 BACKGROUND_TEXT_SELECT_CHARACTER; \
538 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY; \
539 uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
540 if (tileData) { \
541 if (!mapData.hflip) { \
542 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
543 ++outX; \
544 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
545 ++outX; \
546 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
547 ++outX; \
548 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
549 ++outX; \
550 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
551 ++outX; \
552 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
553 ++outX; \
554 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
555 ++outX; \
556 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
557 ++outX; \
558 } else { \
559 outX += 7; \
560 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
561 --outX; \
562 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
563 --outX; \
564 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
565 --outX; \
566 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
567 --outX; \
568 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
569 --outX; \
570 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
571 --outX; \
572 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
573 --outX; \
574 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
575 outX += 8; \
576 } \
577 } else { \
578 outX += 8; \
579 } \
580 }
581
582#define BACKGROUND_MODE_0_TILE_256_LOOP(TYPE) \
583 for (; tileX < 30; ++tileX) { \
584 BACKGROUND_TEXT_SELECT_CHARACTER; \
585 charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1); \
586 if (!mapData.hflip) { \
587 uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
588 if (tileData) { \
589 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
590 ++outX; \
591 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
592 ++outX; \
593 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
594 ++outX; \
595 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
596 ++outX; \
597 } else { \
598 outX += 4; \
599 } \
600 tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
601 if (tileData) { \
602 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
603 ++outX; \
604 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
605 ++outX; \
606 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
607 ++outX; \
608 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
609 ++outX; \
610 } else { \
611 outX += 4; \
612 } \
613 } else { \
614 uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
615 if (tileData) { \
616 outX += 3; \
617 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
618 --outX; \
619 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
620 --outX; \
621 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
622 --outX; \
623 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
624 outX += 4; \
625 } else { \
626 outX += 4; \
627 } \
628 tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
629 if (tileData) { \
630 outX += 3; \
631 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
632 --outX; \
633 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
634 --outX; \
635 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
636 --outX; \
637 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
638 outX += 4; \
639 } else { \
640 outX += 4; \
641 } \
642 } \
643 }
644
645static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
646 int inX = background->x;
647 int inY = y + background->y;
648 union GBATextMapData mapData;
649
650 unsigned yBase = inY & 0xF8;
651 if (background->size == 2) {
652 yBase += inY & 0x100;
653 } else if (background->size == 3) {
654 yBase += (inY & 0x100) << 1;
655 }
656
657 int localX;
658 int localY;
659
660 unsigned xBase;
661
662 int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND;
663 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA);
664 flags |= FLAG_TARGET_2 * background->target2;
665
666 uint32_t screenBase;
667 uint32_t charBase;
668 int variant = background->target1 && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
669
670 int outX = 0;
671 int tileX = 0;
672 if (inX & 0x7) {
673 uint32_t tileData;
674 BACKGROUND_TEXT_SELECT_CHARACTER;
675
676 int end = 0x8 - (inX & 0x7);
677 if (!background->multipalette) {
678 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
679 tileData = ((uint32_t*)renderer->d.vram)[charBase];
680 if (!mapData.hflip) {
681 tileData >>= 4 * (inX & 0x7);
682 if (!variant) {
683 for (outX = 0; outX < end; ++outX) {
684 BACKGROUND_DRAW_PIXEL_16_NORMAL;
685 }
686 } else {
687 for (outX = 0; outX < end; ++outX) {
688 BACKGROUND_DRAW_PIXEL_16_VARIANT;
689 }
690 }
691 } else {
692 if (!variant) {
693 for (outX = end; outX--;) {
694 BACKGROUND_DRAW_PIXEL_16_NORMAL;
695 }
696 } else {
697 for (outX = end; outX--;) {
698 BACKGROUND_DRAW_PIXEL_16_VARIANT;
699 }
700 }
701 }
702 } else {
703 // TODO: hflip
704 charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1);
705 outX = 0;
706 int end2 = end - 4;
707 int shift = inX & 0x3;
708 if (end2 > 0) {
709 tileData = ((uint32_t*)renderer->d.vram)[charBase];
710 tileData >>= 8 * shift;
711 shift = 0;
712 if (!variant) {
713 for (; outX < end2; ++outX) {
714 BACKGROUND_DRAW_PIXEL_256_NORMAL;
715 }
716 } else {
717 for (; outX < end2; ++outX) {
718 BACKGROUND_DRAW_PIXEL_256_VARIANT;
719 }
720 }
721 }
722
723 tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
724 tileData >>= 8 * shift;
725 if (!variant) {
726 for (; outX < end; ++outX) {
727 BACKGROUND_DRAW_PIXEL_256_NORMAL;
728 }
729 } else {
730 for (; outX < end; ++outX) {
731 BACKGROUND_DRAW_PIXEL_256_VARIANT;
732 }
733 }
734 }
735
736 tileX = 30;
737 BACKGROUND_TEXT_SELECT_CHARACTER;
738 if (!background->multipalette) {
739 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
740 tileData = ((uint32_t*)renderer->d.vram)[charBase];
741 if (!mapData.hflip) {
742 if (!variant) {
743 for (outX = VIDEO_HORIZONTAL_PIXELS - 8 + end; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
744 BACKGROUND_DRAW_PIXEL_16_NORMAL;
745 }
746 } else {
747 for (outX = VIDEO_HORIZONTAL_PIXELS - 8 + end; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
748 BACKGROUND_DRAW_PIXEL_16_VARIANT;
749 }
750 }
751 } else {
752 tileData >>= 4 * end;
753 if (!variant) {
754 for (outX = VIDEO_HORIZONTAL_PIXELS - 1; outX > VIDEO_HORIZONTAL_PIXELS - 8; --outX) {
755 BACKGROUND_DRAW_PIXEL_16_NORMAL;
756 }
757 } else {
758 for (outX = VIDEO_HORIZONTAL_PIXELS - 1; outX > VIDEO_HORIZONTAL_PIXELS - 8; --outX) {
759 BACKGROUND_DRAW_PIXEL_16_VARIANT;
760 }
761 }
762 }
763 } else {
764 // TODO: hflip
765 charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1);
766 outX = VIDEO_HORIZONTAL_PIXELS - 8 + end;
767 int end2 = 4 - end;
768 if (end2 > 0) {
769 tileData = ((uint32_t*)renderer->d.vram)[charBase];
770 if (!variant) {
771 for (; outX < VIDEO_HORIZONTAL_PIXELS - end2; ++outX) {
772 BACKGROUND_DRAW_PIXEL_256_NORMAL;
773 }
774 } else {
775 for (; outX < VIDEO_HORIZONTAL_PIXELS - end2; ++outX) {
776 BACKGROUND_DRAW_PIXEL_256_VARIANT;
777 }
778 }
779 ++charBase;
780 }
781
782 tileData = ((uint32_t*)renderer->d.vram)[charBase];
783 if (!variant) {
784 for (; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
785 BACKGROUND_DRAW_PIXEL_256_NORMAL;
786 }
787 } else {
788 for (; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
789 BACKGROUND_DRAW_PIXEL_256_VARIANT;
790 }
791 }
792 }
793
794 tileX = 1;
795 outX = end;
796 }
797
798 if (!background->multipalette) {
799 if (!variant) {
800 BACKGROUND_MODE_0_TILE_16_LOOP(NORMAL);
801 } else {
802 BACKGROUND_MODE_0_TILE_16_LOOP(VARIANT);
803 }
804 } else {
805 if (!variant) {
806 BACKGROUND_MODE_0_TILE_256_LOOP(NORMAL);
807 } else {
808 BACKGROUND_MODE_0_TILE_256_LOOP(VARIANT);
809 }
810 }
811}
812
813#define BACKGROUND_BITMAP_INIT \
814 int32_t x = background->sx - background->dx; \
815 int32_t y = background->sy - background->dy; \
816 int32_t localX; \
817 int32_t localY; \
818 \
819 int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND; \
820 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA); \
821 flags |= FLAG_TARGET_2 * background->target2; \
822 int variant = background->target1 && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
823
824#define BACKGROUND_BITMAP_ITERATE \
825 x += background->dx; \
826 y += background->dy; \
827 \
828 if (x < 0 || y < 0 || (x >> 8) >= VIDEO_HORIZONTAL_PIXELS || (y >> 8) >= VIDEO_VERTICAL_PIXELS) { \
829 continue; \
830 } else { \
831 localX = x; \
832 localY = y; \
833 }
834
835static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
836 (void)(unused);
837 int sizeAdjusted = 0x8000 << background->size;
838
839 BACKGROUND_BITMAP_INIT;
840
841 uint32_t screenBase = background->screenBase;
842 uint32_t charBase = background->charBase;
843 uint8_t mapData;
844 uint8_t tileData;
845
846 int outX;
847 for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
848 x += background->dx;
849 y += background->dy;
850
851 if (background->overflow) {
852 localX = x & (sizeAdjusted - 1);
853 localY = y & (sizeAdjusted - 1);
854 } else if (x < 0 || y < 0 || x >= sizeAdjusted || y >= sizeAdjusted) {
855 continue;
856 } else {
857 localX = x;
858 localY = y;
859 }
860 mapData = ((uint8_t*)renderer->d.vram)[screenBase + (localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)];
861 tileData = ((uint8_t*)renderer->d.vram)[charBase + (mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)];
862
863 if (tileData && !(renderer->row[outX] & FLAG_FINALIZED)) {
864 if (!variant) {
865 _composite(renderer, outX, renderer->normalPalette[tileData] | flags);
866 } else {
867 _composite(renderer, outX, renderer->variantPalette[tileData] | flags);
868 }
869 }
870 }
871}
872
873static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
874 (void)(unused);
875
876 BACKGROUND_BITMAP_INIT;
877
878 uint16_t color;
879 uint32_t color32;
880
881 int outX;
882 for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
883 BACKGROUND_BITMAP_ITERATE;
884
885 color = ((uint16_t*)renderer->d.vram)[(localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
886 color32 = 0;
887 color32 |= (color << 3) & 0xF8;
888 color32 |= (color << 6) & 0xF800;
889 color32 |= (color << 9) & 0xF80000;
890
891 if (!(renderer->row[outX] & FLAG_FINALIZED)) {
892 if (!variant) {
893 _composite(renderer, outX, color32 | flags);
894 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
895 _composite(renderer, outX, _brighten(color32, renderer->bldy) | flags);
896 } else if (renderer->blendEffect == BLEND_DARKEN) {
897 _composite(renderer, outX, _darken(color32, renderer->bldy) | flags);
898 }
899 }
900 }
901}
902
903static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
904 (void)(unused);
905
906 BACKGROUND_BITMAP_INIT;
907
908 uint16_t color;
909 uint32_t offset = 0;
910 if (renderer->dispcnt.frameSelect) {
911 offset = 0xA000;
912 }
913
914 int outX;
915 for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
916 BACKGROUND_BITMAP_ITERATE;
917
918 color = ((uint8_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
919
920 if (color && !(renderer->row[outX] & FLAG_FINALIZED)) {
921 if (!variant) {
922 _composite(renderer, outX, renderer->normalPalette[color] | flags);
923 } else {
924 _composite(renderer, outX, renderer->variantPalette[color] | flags);
925 }
926 }
927 }
928}
929
930static const int _objSizes[32] = {
931 8, 8,
932 16, 16,
933 32, 32,
934 64, 64,
935 16, 8,
936 32, 8,
937 32, 16,
938 64, 32,
939 8, 16,
940 8, 32,
941 16, 32,
942 32, 64,
943 0, 0,
944 0, 0,
945 0, 0,
946 0, 0
947};
948
949#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
950 SPRITE_YBASE_ ## DEPTH(inY); \
951 for (int outX = x >= start ? x : start; outX < x + width && outX < end; ++outX) { \
952 int inX = outX - x; \
953 if (sprite->hflip) { \
954 inX = width - inX - 1; \
955 } \
956 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
957 continue; \
958 } \
959 SPRITE_XBASE_ ## DEPTH(inX); \
960 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
961 }
962
963#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
964 for (int outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) { \
965 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
966 continue; \
967 } \
968 int inX = outX - x; \
969 int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1); \
970 int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1); \
971 \
972 if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
973 continue; \
974 } \
975 \
976 SPRITE_YBASE_ ## DEPTH(localY); \
977 SPRITE_XBASE_ ## DEPTH(localX); \
978 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
979 }
980
981#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
982#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
983
984#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
985 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
986 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
987 if (tileData && !(renderer->spriteLayer[outX])) { \
988 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData | (sprite->palette << 4)] | flags; \
989 }
990
991#define SPRITE_DRAW_PIXEL_16_VARIANT(localX) \
992 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
993 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
994 if (tileData && !(renderer->spriteLayer[outX])) { \
995 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData | (sprite->palette << 4)] | flags; \
996 }
997
998#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
999#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width : 0x80) + (localY & 0x7) * 8;
1000
1001#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
1002 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1003 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1004 if (tileData && !(renderer->spriteLayer[outX])) { \
1005 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData] | flags; \
1006 }
1007
1008#define SPRITE_DRAW_PIXEL_256_VARIANT(localX) \
1009 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1010 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1011 if (tileData && !(renderer->spriteLayer[outX])) { \
1012 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData] | flags; \
1013 }
1014
1015static void _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
1016 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1017 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1018 int start = renderer->start;
1019 int end = renderer->end;
1020 if ((y < sprite->y && (sprite->y + height - 256 < 0 || y >= sprite->y + height - 256)) || y >= sprite->y + height) {
1021 return;
1022 }
1023 int flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1024 flags |= FLAG_TARGET_1 * ((renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1025 flags |= FLAG_TARGET_2 *renderer->target2Obj;
1026 int x = sprite->x;
1027 int inY = y - sprite->y;
1028 if (sprite->y + height - 256 >= 0) {
1029 inY += 256;
1030 }
1031 if (sprite->vflip) {
1032 inY = height - inY - 1;
1033 }
1034 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1035 int variant = renderer->target1Obj && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1036 if (!sprite->multipalette) {
1037 if (!variant) {
1038 SPRITE_NORMAL_LOOP(16, NORMAL);
1039 } else {
1040 SPRITE_NORMAL_LOOP(16, VARIANT);
1041 }
1042 } else {
1043 if (!variant) {
1044 SPRITE_NORMAL_LOOP(256, NORMAL);
1045 } else {
1046 SPRITE_NORMAL_LOOP(256, VARIANT);
1047 }
1048 }
1049}
1050
1051static void _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
1052 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1053 int totalWidth = width << sprite->doublesize;
1054 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1055 int totalHeight = height << sprite->doublesize;
1056 int start = renderer->start;
1057 int end = renderer->end;
1058 if ((y < sprite->y && (sprite->y + totalHeight - 256 < 0 || y >= sprite->y + totalHeight - 256)) || y >= sprite->y + totalHeight) {
1059 return;
1060 }
1061 int flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1062 flags |= FLAG_TARGET_1 * ((renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1063 flags |= FLAG_TARGET_2 *renderer->target2Obj;
1064 int x = sprite->x;
1065 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1066 struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
1067 int variant = renderer->target1Obj && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1068 int inY = y - sprite->y;
1069 if (inY < 0) {
1070 inY += 256;
1071 }
1072 if (!sprite->multipalette) {
1073 if (!variant) {
1074 SPRITE_TRANSFORMED_LOOP(16, NORMAL);
1075 } else {
1076 SPRITE_TRANSFORMED_LOOP(16, VARIANT);
1077 }
1078 } else {
1079 if (!variant) {
1080 SPRITE_TRANSFORMED_LOOP(256, NORMAL);
1081 } else {
1082 SPRITE_TRANSFORMED_LOOP(256, VARIANT);
1083 }
1084 }
1085}
1086
1087static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, int priority) {
1088 int x;
1089 for (x = 0; x < 240; ++x) {
1090 uint32_t color = renderer->spriteLayer[x];
1091 if ((color & FLAG_FINALIZED) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority && !(renderer->row[x] & FLAG_FINALIZED)) {
1092 _composite(renderer, x, color & ~FLAG_FINALIZED);
1093 }
1094 }
1095}
1096
1097static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
1098 if (renderer->blendEffect == BLEND_BRIGHTEN) {
1099 for (int i = 0; i < 512; ++i) {
1100 renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
1101 }
1102 } else if (renderer->blendEffect == BLEND_DARKEN) {
1103 for (int i = 0; i < 512; ++i) {
1104 renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
1105 }
1106 } else {
1107 for (int i = 0; i < 512; ++i) {
1108 renderer->variantPalette[i] = renderer->normalPalette[i];
1109 }
1110 }
1111}
1112
1113static inline uint32_t _brighten(uint32_t color, int y) {
1114 uint32_t c = 0;
1115 uint32_t a;
1116 a = color & 0xF8;
1117 c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
1118
1119 a = color & 0xF800;
1120 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1121
1122 a = color & 0xF80000;
1123 c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
1124 return c;
1125}
1126
1127static inline uint32_t _darken(uint32_t color, int y) {
1128 uint32_t c = 0;
1129 uint32_t a;
1130 a = color & 0xF8;
1131 c |= (a - (a * y) / 16) & 0xF8;
1132
1133 a = color & 0xF800;
1134 c |= (a - (a * y) / 16) & 0xF800;
1135
1136 a = color & 0xF80000;
1137 c |= (a - (a * y) / 16) & 0xF80000;
1138 return c;
1139}
1140
1141static uint32_t _mix(int weightA, uint32_t colorA, int weightB, uint32_t colorB) {
1142 uint32_t c = 0;
1143 uint32_t a, b;
1144 a = colorA & 0xF8;
1145 b = colorB & 0xF8;
1146 c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
1147 if (c & 0x00000100) {
1148 c = 0x000000F8;
1149 }
1150
1151 a = colorA & 0xF800;
1152 b = colorB & 0xF800;
1153 c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
1154 if (c & 0x00010000) {
1155 c = (c & 0x000000F8) | 0x0000F800;
1156 }
1157
1158 a = colorA & 0xF80000;
1159 b = colorB & 0xF80000;
1160 c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
1161 if (c & 0x01000000) {
1162 c = (c & 0x0000F8F8) | 0x00F80000;
1163 }
1164 return c;
1165}