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
813static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
814 (void)(unused);
815 int sizeAdjusted = 0x8000 << background->size;
816
817 int32_t x = background->sx - background->dx;
818 int32_t y = background->sy - background->dy;
819 int32_t localX;
820 int32_t localY;
821
822 int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND;
823 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA);
824 flags |= FLAG_TARGET_2 * background->target2;
825
826 uint32_t screenBase = background->screenBase;
827 uint32_t charBase = background->charBase;
828 uint8_t mapData;
829 uint8_t tileData;
830 int variant = background->target1 && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
831
832 int outX;
833 for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
834 x += background->dx;
835 y += background->dy;
836
837 if (background->overflow) {
838 localX = x & (sizeAdjusted - 1);
839 localY = y & (sizeAdjusted - 1);
840 } else if (x < 0 || y < 0 || x >= sizeAdjusted || y >= sizeAdjusted) {
841 continue;
842 } else {
843 localX = x;
844 localY = y;
845 }
846 mapData = ((uint8_t*)renderer->d.vram)[screenBase + (localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)];
847 tileData = ((uint8_t*)renderer->d.vram)[charBase + (mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)];
848
849 if (tileData && !(renderer->row[outX] & FLAG_FINALIZED)) {
850 if (!variant) {
851 _composite(renderer, outX, renderer->normalPalette[tileData] | flags);
852 } else {
853 _composite(renderer, outX, renderer->variantPalette[tileData] | flags);
854 }
855 }
856 }
857}
858
859static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
860 (void)(unused);
861
862 int32_t x = background->sx - background->dx;
863 int32_t y = background->sy - background->dy;
864 int32_t localX;
865 int32_t localY;
866
867 int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND;
868 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA);
869 flags |= FLAG_TARGET_2 * background->target2;
870
871 uint16_t color;
872 uint32_t color32;
873 int variant = background->target1 && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
874
875 int outX;
876 for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
877 x += background->dx;
878 y += background->dy;
879
880 if (x < 0 || y < 0 || (x >> 8) >= VIDEO_HORIZONTAL_PIXELS || (y >> 8) >= VIDEO_VERTICAL_PIXELS) {
881 continue;
882 } else {
883 localX = x;
884 localY = y;
885 }
886
887 color = ((uint16_t*)renderer->d.vram)[(localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
888 color32 = 0;
889 color32 |= (color << 3) & 0xF8;
890 color32 |= (color << 6) & 0xF800;
891 color32 |= (color << 9) & 0xF80000;
892
893 if (!(renderer->row[outX] & FLAG_FINALIZED)) {
894 if (!variant) {
895 _composite(renderer, outX, color32 | flags);
896 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
897 _composite(renderer, outX, _brighten(color32, renderer->bldy) | flags);
898 } else if (renderer->blendEffect == BLEND_DARKEN) {
899 _composite(renderer, outX, _darken(color32, renderer->bldy) | flags);
900 }
901 }
902 }
903}
904
905static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
906 (void)(unused);
907
908 int32_t x = background->sx - background->dx;
909 int32_t y = background->sy - background->dy;
910 int32_t localX;
911 int32_t localY;
912
913 int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND;
914 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA);
915 flags |= FLAG_TARGET_2 * background->target2;
916
917 uint16_t color;
918 int variant = background->target1 && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
919 uint32_t offset = 0;
920 if (renderer->dispcnt.frameSelect) {
921 offset = 0xA000;
922 }
923
924 int outX;
925 for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
926 x += background->dx;
927 y += background->dy;
928
929 if (x < 0 || y < 0 || (x >> 8) >= VIDEO_HORIZONTAL_PIXELS || (y >> 8) >= VIDEO_VERTICAL_PIXELS) {
930 continue;
931 } else {
932 localX = x;
933 localY = y;
934 }
935
936 color = ((uint8_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
937
938 if (color && !(renderer->row[outX] & FLAG_FINALIZED)) {
939 if (!variant) {
940 _composite(renderer, outX, renderer->normalPalette[color] | flags);
941 } else {
942 _composite(renderer, outX, renderer->variantPalette[color] | flags);
943 }
944 }
945 }
946}
947
948static const int _objSizes[32] = {
949 8, 8,
950 16, 16,
951 32, 32,
952 64, 64,
953 16, 8,
954 32, 8,
955 32, 16,
956 64, 32,
957 8, 16,
958 8, 32,
959 16, 32,
960 32, 64,
961 0, 0,
962 0, 0,
963 0, 0,
964 0, 0
965};
966
967#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
968 SPRITE_YBASE_ ## DEPTH(inY); \
969 for (int outX = x >= start ? x : start; outX < x + width && outX < end; ++outX) { \
970 int inX = outX - x; \
971 if (sprite->hflip) { \
972 inX = width - inX - 1; \
973 } \
974 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
975 continue; \
976 } \
977 SPRITE_XBASE_ ## DEPTH(inX); \
978 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
979 }
980
981#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
982 for (int outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) { \
983 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
984 continue; \
985 } \
986 int inX = outX - x; \
987 int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1); \
988 int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1); \
989 \
990 if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
991 continue; \
992 } \
993 \
994 SPRITE_YBASE_ ## DEPTH(localY); \
995 SPRITE_XBASE_ ## DEPTH(localX); \
996 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
997 }
998
999#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
1000#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
1001
1002#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
1003 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1004 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1005 if (tileData && !(renderer->spriteLayer[outX])) { \
1006 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData | (sprite->palette << 4)] | flags; \
1007 }
1008
1009#define SPRITE_DRAW_PIXEL_16_VARIANT(localX) \
1010 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1011 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1012 if (tileData && !(renderer->spriteLayer[outX])) { \
1013 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData | (sprite->palette << 4)] | flags; \
1014 }
1015
1016#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
1017#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width : 0x80) + (localY & 0x7) * 8;
1018
1019#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
1020 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1021 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1022 if (tileData && !(renderer->spriteLayer[outX])) { \
1023 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData] | flags; \
1024 }
1025
1026#define SPRITE_DRAW_PIXEL_256_VARIANT(localX) \
1027 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1028 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1029 if (tileData && !(renderer->spriteLayer[outX])) { \
1030 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData] | flags; \
1031 }
1032
1033static void _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
1034 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1035 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1036 int start = renderer->start;
1037 int end = renderer->end;
1038 if ((y < sprite->y && (sprite->y + height - 256 < 0 || y >= sprite->y + height - 256)) || y >= sprite->y + height) {
1039 return;
1040 }
1041 int flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1042 flags |= FLAG_TARGET_1 * ((renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1043 flags |= FLAG_TARGET_2 *renderer->target2Obj;
1044 int x = sprite->x;
1045 int inY = y - sprite->y;
1046 if (sprite->y + height - 256 >= 0) {
1047 inY += 256;
1048 }
1049 if (sprite->vflip) {
1050 inY = height - inY - 1;
1051 }
1052 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1053 int variant = renderer->target1Obj && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1054 if (!sprite->multipalette) {
1055 if (!variant) {
1056 SPRITE_NORMAL_LOOP(16, NORMAL);
1057 } else {
1058 SPRITE_NORMAL_LOOP(16, VARIANT);
1059 }
1060 } else {
1061 if (!variant) {
1062 SPRITE_NORMAL_LOOP(256, NORMAL);
1063 } else {
1064 SPRITE_NORMAL_LOOP(256, VARIANT);
1065 }
1066 }
1067}
1068
1069static void _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
1070 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1071 int totalWidth = width << sprite->doublesize;
1072 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1073 int totalHeight = height << sprite->doublesize;
1074 int start = renderer->start;
1075 int end = renderer->end;
1076 if ((y < sprite->y && (sprite->y + totalHeight - 256 < 0 || y >= sprite->y + totalHeight - 256)) || y >= sprite->y + totalHeight) {
1077 return;
1078 }
1079 int flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1080 flags |= FLAG_TARGET_1 * ((renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1081 flags |= FLAG_TARGET_2 *renderer->target2Obj;
1082 int x = sprite->x;
1083 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1084 struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
1085 int variant = renderer->target1Obj && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1086 int inY = y - sprite->y;
1087 if (inY < 0) {
1088 inY += 256;
1089 }
1090 if (!sprite->multipalette) {
1091 if (!variant) {
1092 SPRITE_TRANSFORMED_LOOP(16, NORMAL);
1093 } else {
1094 SPRITE_TRANSFORMED_LOOP(16, VARIANT);
1095 }
1096 } else {
1097 if (!variant) {
1098 SPRITE_TRANSFORMED_LOOP(256, NORMAL);
1099 } else {
1100 SPRITE_TRANSFORMED_LOOP(256, VARIANT);
1101 }
1102 }
1103}
1104
1105static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, int priority) {
1106 int x;
1107 for (x = 0; x < 240; ++x) {
1108 uint32_t color = renderer->spriteLayer[x];
1109 if ((color & FLAG_FINALIZED) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority && !(renderer->row[x] & FLAG_FINALIZED)) {
1110 _composite(renderer, x, color & ~FLAG_FINALIZED);
1111 }
1112 }
1113}
1114
1115static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
1116 if (renderer->blendEffect == BLEND_BRIGHTEN) {
1117 for (int i = 0; i < 512; ++i) {
1118 renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
1119 }
1120 } else if (renderer->blendEffect == BLEND_DARKEN) {
1121 for (int i = 0; i < 512; ++i) {
1122 renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
1123 }
1124 } else {
1125 for (int i = 0; i < 512; ++i) {
1126 renderer->variantPalette[i] = renderer->normalPalette[i];
1127 }
1128 }
1129}
1130
1131static inline uint32_t _brighten(uint32_t color, int y) {
1132 uint32_t c = 0;
1133 uint32_t a;
1134 a = color & 0xF8;
1135 c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
1136
1137 a = color & 0xF800;
1138 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1139
1140 a = color & 0xF80000;
1141 c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
1142 return c;
1143}
1144
1145static inline uint32_t _darken(uint32_t color, int y) {
1146 uint32_t c = 0;
1147 uint32_t a;
1148 a = color & 0xF8;
1149 c |= (a - (a * y) / 16) & 0xF8;
1150
1151 a = color & 0xF800;
1152 c |= (a - (a * y) / 16) & 0xF800;
1153
1154 a = color & 0xF80000;
1155 c |= (a - (a * y) / 16) & 0xF80000;
1156 return c;
1157}
1158
1159static uint32_t _mix(int weightA, uint32_t colorA, int weightB, uint32_t colorB) {
1160 uint32_t c = 0;
1161 uint32_t a, b;
1162 a = colorA & 0xF8;
1163 b = colorB & 0xF8;
1164 c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
1165 if (c & 0x00000100) {
1166 c = 0x000000F8;
1167 }
1168
1169 a = colorA & 0xF800;
1170 b = colorB & 0xF800;
1171 c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
1172 if (c & 0x00010000) {
1173 c = (c & 0x000000F8) | 0x0000F800;
1174 }
1175
1176 a = colorA & 0xF80000;
1177 b = colorB & 0xF80000;
1178 c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
1179 if (c & 0x01000000) {
1180 c = (c & 0x0000F8F8) | 0x00F80000;
1181 }
1182 return c;
1183}