src/gba/renderers/video-software.c (view raw)
1#include "video-software.h"
2
3#include "gba.h"
4#include "gba-io.h"
5
6static const int _objSizes[32] = {
7 8, 8,
8 16, 16,
9 32, 32,
10 64, 64,
11 16, 8,
12 32, 8,
13 32, 16,
14 64, 32,
15 8, 16,
16 8, 32,
17 16, 32,
18 32, 64,
19 0, 0,
20 0, 0,
21 0, 0,
22 0, 0
23};
24
25static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer);
26static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer);
27static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
28static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
29static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
30static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
31static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer);
32
33static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer);
34static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value);
35static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value);
36static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value);
37static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value);
38static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value);
39static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
40static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value);
41static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
42static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value);
43static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value);
44
45static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y);
46static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
47static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
48static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
49static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
50static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
51static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer);
52static int _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y);
53static int _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y);
54static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority);
55
56static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer);
57static inline unsigned _brighten(unsigned color, int y);
58static inline unsigned _darken(unsigned color, int y);
59static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB);
60
61void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) {
62 renderer->d.init = GBAVideoSoftwareRendererInit;
63 renderer->d.reset = GBAVideoSoftwareRendererInit;
64 renderer->d.deinit = GBAVideoSoftwareRendererDeinit;
65 renderer->d.writeVideoRegister = GBAVideoSoftwareRendererWriteVideoRegister;
66 renderer->d.writeOAM = GBAVideoSoftwareRendererWriteOAM;
67 renderer->d.writePalette = GBAVideoSoftwareRendererWritePalette;
68 renderer->d.drawScanline = GBAVideoSoftwareRendererDrawScanline;
69 renderer->d.finishFrame = GBAVideoSoftwareRendererFinishFrame;
70}
71
72static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) {
73 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
74 int i;
75
76 softwareRenderer->dispcnt.packed = 0x0080;
77
78 softwareRenderer->target1Obj = 0;
79 softwareRenderer->target1Bd = 0;
80 softwareRenderer->target2Obj = 0;
81 softwareRenderer->target2Bd = 0;
82 softwareRenderer->blendEffect = BLEND_NONE;
83 memset(softwareRenderer->normalPalette, 0, sizeof(softwareRenderer->normalPalette));
84 memset(softwareRenderer->variantPalette, 0, sizeof(softwareRenderer->variantPalette));
85
86 softwareRenderer->blda = 0;
87 softwareRenderer->bldb = 0;
88 softwareRenderer->bldy = 0;
89
90 softwareRenderer->winN[0].h.packed = 0;
91 softwareRenderer->winN[0].v.packed = 0;
92 softwareRenderer->winN[0].control.packed = 0;
93 softwareRenderer->winN[0].control.priority = 0;
94 softwareRenderer->winN[1].h.packed = 0;
95 softwareRenderer->winN[1].v.packed = 0;
96 softwareRenderer->winN[1].control.packed = 0;
97 softwareRenderer->winN[1].control.priority = 1;
98 softwareRenderer->objwin.packed = 0;
99 softwareRenderer->objwin.priority = 2;
100 softwareRenderer->winout.packed = 0;
101 softwareRenderer->winout.priority = 3;
102
103 softwareRenderer->mosaic.packed = 0;
104
105 for (i = 0; i < 4; ++i) {
106 struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i];
107 bg->index = i;
108 bg->enabled = 0;
109 bg->priority = 0;
110 bg->charBase = 0;
111 bg->mosaic = 0;
112 bg->multipalette = 0;
113 bg->screenBase = 0;
114 bg->overflow = 0;
115 bg->size = 0;
116 bg->target1 = 0;
117 bg->target2 = 0;
118 bg->x = 0;
119 bg->y = 0;
120 bg->refx = 0;
121 bg->refy = 0;
122 bg->dx = 256;
123 bg->dmx = 0;
124 bg->dy = 0;
125 bg->dmy = 256;
126 bg->sx = 0;
127 bg->sy = 0;
128 }
129}
130
131static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer) {
132 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
133 UNUSED(softwareRenderer);
134}
135
136static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
137 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
138 switch (address) {
139 case REG_DISPCNT:
140 softwareRenderer->dispcnt.packed = value;
141 GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer);
142 break;
143 case REG_BG0CNT:
144 value &= 0xFFCF;
145 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[0], value);
146 break;
147 case REG_BG1CNT:
148 value &= 0xFFCF;
149 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[1], value);
150 break;
151 case REG_BG2CNT:
152 value &= 0xFFCF;
153 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[2], value);
154 break;
155 case REG_BG3CNT:
156 value &= 0xFFCF;
157 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[3], value);
158 break;
159 case REG_BG0HOFS:
160 value &= 0x01FF;
161 softwareRenderer->bg[0].x = value;
162 break;
163 case REG_BG0VOFS:
164 value &= 0x01FF;
165 softwareRenderer->bg[0].y = value;
166 break;
167 case REG_BG1HOFS:
168 value &= 0x01FF;
169 softwareRenderer->bg[1].x = value;
170 break;
171 case REG_BG1VOFS:
172 value &= 0x01FF;
173 softwareRenderer->bg[1].y = value;
174 break;
175 case REG_BG2HOFS:
176 value &= 0x01FF;
177 softwareRenderer->bg[2].x = value;
178 break;
179 case REG_BG2VOFS:
180 value &= 0x01FF;
181 softwareRenderer->bg[2].y = value;
182 break;
183 case REG_BG3HOFS:
184 value &= 0x01FF;
185 softwareRenderer->bg[3].x = value;
186 break;
187 case REG_BG3VOFS:
188 value &= 0x01FF;
189 softwareRenderer->bg[3].y = value;
190 break;
191 case REG_BG2PA:
192 GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[2], value);
193 break;
194 case REG_BG2PB:
195 GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[2], value);
196 break;
197 case REG_BG2PC:
198 GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[2], value);
199 break;
200 case REG_BG2PD:
201 GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[2], value);
202 break;
203 case REG_BG2X_LO:
204 GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[2], value);
205 break;
206 case REG_BG2X_HI:
207 GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[2], value);
208 break;
209 case REG_BG2Y_LO:
210 GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[2], value);
211 break;
212 case REG_BG2Y_HI:
213 GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[2], value);
214 break;
215 case REG_BG3PA:
216 GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[3], value);
217 break;
218 case REG_BG3PB:
219 GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[3], value);
220 break;
221 case REG_BG3PC:
222 GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[3], value);
223 break;
224 case REG_BG3PD:
225 GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[3], value);
226 break;
227 case REG_BG3X_LO:
228 GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[3], value);
229 break;
230 case REG_BG3X_HI:
231 GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[3], value);
232 break;
233 case REG_BG3Y_LO:
234 GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[3], value);
235 break;
236 case REG_BG3Y_HI:
237 GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[3], value);
238 break;
239 case REG_BLDCNT:
240 GBAVideoSoftwareRendererWriteBLDCNT(softwareRenderer, value);
241 break;
242 case REG_BLDALPHA:
243 softwareRenderer->blda = value & 0x1F;
244 if (softwareRenderer->blda > 0x10) {
245 softwareRenderer->blda = 0x10;
246 }
247 softwareRenderer->bldb = (value >> 8) & 0x1F;
248 if (softwareRenderer->bldb > 0x10) {
249 softwareRenderer->bldb = 0x10;
250 }
251 break;
252 case REG_BLDY:
253 softwareRenderer->bldy = value & 0x1F;
254 if (softwareRenderer->bldy > 0x10) {
255 softwareRenderer->bldy = 0x10;
256 }
257 _updatePalettes(softwareRenderer);
258 break;
259 case REG_WIN0H:
260 softwareRenderer->winN[0].h.packed = value;
261 if (softwareRenderer->winN[0].h.start > VIDEO_HORIZONTAL_PIXELS && softwareRenderer->winN[0].h.start > softwareRenderer->winN[0].h.end) {
262 softwareRenderer->winN[0].h.start = 0;
263 }
264 if (softwareRenderer->winN[0].h.start > softwareRenderer->winN[0].h.end || softwareRenderer->winN[0].h.end > VIDEO_HORIZONTAL_PIXELS) {
265 softwareRenderer->winN[0].h.end = VIDEO_HORIZONTAL_PIXELS;
266 }
267 break;
268 case REG_WIN1H:
269 softwareRenderer->winN[1].h.packed = value;
270 if (softwareRenderer->winN[1].h.start > VIDEO_HORIZONTAL_PIXELS && softwareRenderer->winN[1].h.start > softwareRenderer->winN[1].h.end) {
271 softwareRenderer->winN[1].h.start = 0;
272 }
273 if (softwareRenderer->winN[1].h.start > softwareRenderer->winN[1].h.end || softwareRenderer->winN[1].h.end > VIDEO_HORIZONTAL_PIXELS) {
274 softwareRenderer->winN[1].h.end = VIDEO_HORIZONTAL_PIXELS;
275 }
276 break;
277 case REG_WIN0V:
278 softwareRenderer->winN[0].v.packed = value;
279 if (softwareRenderer->winN[0].v.start > VIDEO_VERTICAL_PIXELS && softwareRenderer->winN[0].v.start > softwareRenderer->winN[0].v.end) {
280 softwareRenderer->winN[0].v.start = 0;
281 }
282 if (softwareRenderer->winN[0].v.start > softwareRenderer->winN[0].v.end || softwareRenderer->winN[0].v.end > VIDEO_HORIZONTAL_PIXELS) {
283 softwareRenderer->winN[0].v.end = VIDEO_VERTICAL_PIXELS;
284 }
285 break;
286 case REG_WIN1V:
287 softwareRenderer->winN[1].v.packed = value;
288 if (softwareRenderer->winN[1].v.start > VIDEO_VERTICAL_PIXELS && softwareRenderer->winN[1].v.start > softwareRenderer->winN[1].v.end) {
289 softwareRenderer->winN[1].v.start = 0;
290 }
291 if (softwareRenderer->winN[1].v.start > softwareRenderer->winN[1].v.end || softwareRenderer->winN[1].v.end > VIDEO_HORIZONTAL_PIXELS) {
292 softwareRenderer->winN[1].v.end = VIDEO_VERTICAL_PIXELS;
293 }
294 break;
295 case REG_WININ:
296 softwareRenderer->winN[0].control.packed = value;
297 softwareRenderer->winN[1].control.packed = value >> 8;
298 break;
299 case REG_WINOUT:
300 softwareRenderer->winout.packed = value;
301 softwareRenderer->objwin.packed = value >> 8;
302 break;
303 case REG_MOSAIC:
304 softwareRenderer->mosaic.packed = value;
305 break;
306 case REG_GREENSWP:
307 GBALog(0, GBA_LOG_STUB, "Stub video register write: 0x%03X", address);
308 break;
309 default:
310 GBALog(0, GBA_LOG_GAME_ERROR, "Invalid video register: 0x%03X", address);
311 }
312 return value;
313}
314
315static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
316 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
317 softwareRenderer->oamDirty = 1;
318 UNUSED(oam);
319}
320
321static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
322 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
323#ifdef COLOR_16_BIT
324#ifdef COLOR_5_6_5
325 unsigned color = 0;
326 color |= (value & 0x001F) << 11;
327 color |= (value & 0x03E0) << 1;
328 color |= (value & 0x7C00) >> 10;
329#else
330 unsigned color = value;
331#endif
332#else
333 unsigned color = 0;
334 color |= (value << 3) & 0xF8;
335 color |= (value << 6) & 0xF800;
336 color |= (value << 9) & 0xF80000;
337#endif
338 softwareRenderer->normalPalette[address >> 1] = color;
339 if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
340 softwareRenderer->variantPalette[address >> 1] = _brighten(color, softwareRenderer->bldy);
341 } else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
342 softwareRenderer->variantPalette[address >> 1] = _darken(color, softwareRenderer->bldy);
343 }
344}
345
346static void _breakWindow(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win) {
347 int activeWindow;
348 int startX = 0;
349 if (win->h.end > 0) {
350 for (activeWindow = 0; activeWindow < softwareRenderer->nWindows; ++activeWindow) {
351 if (win->h.start < softwareRenderer->windows[activeWindow].endX) {
352 // Insert a window before the end of the active window
353 struct Window oldWindow = softwareRenderer->windows[activeWindow];
354 if (win->h.start > startX) {
355 // And after the start of the active window
356 int nextWindow = softwareRenderer->nWindows;
357 ++softwareRenderer->nWindows;
358 for (; nextWindow > activeWindow; --nextWindow) {
359 softwareRenderer->windows[nextWindow] = softwareRenderer->windows[nextWindow - 1];
360 }
361 softwareRenderer->windows[activeWindow].endX = win->h.start;
362 ++activeWindow;
363 }
364 softwareRenderer->windows[activeWindow].control = win->control;
365 softwareRenderer->windows[activeWindow].endX = win->h.end;
366 if (win->h.end >= oldWindow.endX) {
367 // Trim off extra windows we've overwritten
368 for (++activeWindow; win->h.end >= softwareRenderer->windows[activeWindow].endX && softwareRenderer->nWindows > activeWindow; ++activeWindow) {
369 softwareRenderer->windows[activeWindow] = softwareRenderer->windows[activeWindow + 1];
370 --softwareRenderer->nWindows;
371 }
372 } else {
373 ++activeWindow;
374 int nextWindow = softwareRenderer->nWindows;
375 ++softwareRenderer->nWindows;
376 for (; nextWindow > activeWindow; --nextWindow) {
377 softwareRenderer->windows[nextWindow] = softwareRenderer->windows[nextWindow - 1];
378 }
379 softwareRenderer->windows[activeWindow] = oldWindow;
380 }
381 break;
382 }
383 startX = softwareRenderer->windows[activeWindow].endX;
384 }
385 }
386}
387
388static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer) {
389 int i;
390 int oamMax = 0;
391 for (i = 0; i < 128; ++i) {
392 struct GBAObj* obj = &renderer->d.oam->obj[i];
393 if (obj->transformed || !obj->disable) {
394 int height = _objSizes[obj->shape * 8 + obj->size * 2 + 1];
395 if (obj->transformed) {
396 height <<= ((struct GBATransformedObj*) obj)->doublesize;
397 }
398 if (obj->y < VIDEO_VERTICAL_PIXELS || obj->y + height >= VIDEO_VERTICAL_TOTAL_PIXELS) {
399 renderer->sprites[oamMax].y = obj->y;
400 renderer->sprites[oamMax].endY = obj->y + height;
401 renderer->sprites[oamMax].obj = *obj;
402 ++oamMax;
403 }
404 }
405 }
406 renderer->oamMax = oamMax;
407 renderer->oamDirty = 0;
408}
409
410
411static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
412 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
413
414 color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
415 if (softwareRenderer->dispcnt.forcedBlank) {
416 int x;
417 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
418 row[x] = GBA_COLOR_WHITE;
419 }
420 return;
421 }
422
423 int x;
424 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; x += 4) {
425 softwareRenderer->spriteLayer[x] = FLAG_UNWRITTEN;
426 softwareRenderer->spriteLayer[x + 1] = FLAG_UNWRITTEN;
427 softwareRenderer->spriteLayer[x + 2] = FLAG_UNWRITTEN;
428 softwareRenderer->spriteLayer[x + 3] = FLAG_UNWRITTEN;
429 }
430
431 softwareRenderer->windows[0].endX = VIDEO_HORIZONTAL_PIXELS;
432 softwareRenderer->nWindows = 1;
433 if (softwareRenderer->dispcnt.win0Enable || softwareRenderer->dispcnt.win1Enable || softwareRenderer->dispcnt.objwinEnable) {
434 softwareRenderer->windows[0].control = softwareRenderer->winout;
435 if (softwareRenderer->dispcnt.win1Enable && y < softwareRenderer->winN[1].v.end && y >= softwareRenderer->winN[1].v.start) {
436 _breakWindow(softwareRenderer, &softwareRenderer->winN[1]);
437 }
438 if (softwareRenderer->dispcnt.win0Enable && y < softwareRenderer->winN[0].v.end && y >= softwareRenderer->winN[0].v.start) {
439 _breakWindow(softwareRenderer, &softwareRenderer->winN[0]);
440 }
441 } else {
442 softwareRenderer->windows[0].control.packed = 0xFF;
443 }
444
445 int w;
446 x = 0;
447 for (w = 0; w < softwareRenderer->nWindows; ++w) {
448 // TOOD: handle objwin on backdrop
449 uint32_t backdrop = FLAG_UNWRITTEN | FLAG_PRIORITY | FLAG_IS_BACKGROUND;
450 if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !softwareRenderer->windows[w].control.blendEnable) {
451 backdrop |= softwareRenderer->normalPalette[0];
452 } else {
453 backdrop |= softwareRenderer->variantPalette[0];
454 }
455 int end = softwareRenderer->windows[w].endX;
456 for (; x < end; ++x) {
457 softwareRenderer->row[x] = backdrop;
458 }
459 }
460
461 _drawScanline(softwareRenderer, y);
462
463 if (softwareRenderer->target2Bd) {
464 x = 0;
465 for (w = 0; w < softwareRenderer->nWindows; ++w) {
466 uint32_t backdrop = FLAG_UNWRITTEN;
467 if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !softwareRenderer->windows[w].control.blendEnable) {
468 backdrop |= softwareRenderer->normalPalette[0];
469 } else {
470 backdrop |= softwareRenderer->variantPalette[0];
471 }
472 int end = softwareRenderer->windows[w].endX;
473 for (; x < end; ++x) {
474 uint32_t color = softwareRenderer->row[x];
475 if (color & FLAG_TARGET_1) {
476 softwareRenderer->row[x] = _mix(softwareRenderer->bldb, backdrop, softwareRenderer->blda, color);
477 }
478 }
479 }
480 }
481
482#ifdef COLOR_16_BIT
483#ifdef __arm__
484 _to16Bit(row, softwareRenderer->row, VIDEO_HORIZONTAL_PIXELS);
485#else
486 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
487 row[x] = softwareRenderer->row[x];
488 }
489#endif
490#else
491 memcpy(row, softwareRenderer->row, VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
492#endif
493}
494
495static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
496 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
497
498 softwareRenderer->bg[2].sx = softwareRenderer->bg[2].refx;
499 softwareRenderer->bg[2].sy = softwareRenderer->bg[2].refy;
500 softwareRenderer->bg[3].sx = softwareRenderer->bg[3].refx;
501 softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy;
502}
503
504static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) {
505 renderer->bg[0].enabled = renderer->dispcnt.bg0Enable;
506 renderer->bg[1].enabled = renderer->dispcnt.bg1Enable;
507 renderer->bg[2].enabled = renderer->dispcnt.bg2Enable;
508 renderer->bg[3].enabled = renderer->dispcnt.bg3Enable;
509}
510
511static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value) {
512 UNUSED(renderer);
513 union GBARegisterBGCNT reg = { .packed = value };
514 bg->priority = reg.priority;
515 bg->charBase = reg.charBase << 14;
516 bg->mosaic = reg.mosaic;
517 bg->multipalette = reg.multipalette;
518 bg->screenBase = reg.screenBase << 11;
519 bg->overflow = reg.overflow;
520 bg->size = reg.size;
521}
522
523static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
524 bg->dx = value;
525}
526
527static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
528 bg->dmx = value;
529}
530
531static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
532 bg->dy = value;
533}
534
535static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
536 bg->dmy = value;
537}
538
539static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
540 bg->refx = (bg->refx & 0xFFFF0000) | value;
541 bg->sx = bg->refx;
542}
543
544static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
545 bg->refx = (bg->refx & 0x0000FFFF) | (value << 16);
546 bg->refx <<= 4;
547 bg->refx >>= 4;
548 bg->sx = bg->refx;
549}
550
551static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
552 bg->refy = (bg->refy & 0xFFFF0000) | value;
553 bg->sy = bg->refy;
554}
555
556static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
557 bg->refy = (bg->refy & 0x0000FFFF) | (value << 16);
558 bg->refy <<= 4;
559 bg->refy >>= 4;
560 bg->sy = bg->refy;
561}
562
563static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value) {
564 union {
565 struct {
566 unsigned target1Bg0 : 1;
567 unsigned target1Bg1 : 1;
568 unsigned target1Bg2 : 1;
569 unsigned target1Bg3 : 1;
570 unsigned target1Obj : 1;
571 unsigned target1Bd : 1;
572 enum BlendEffect effect : 2;
573 unsigned target2Bg0 : 1;
574 unsigned target2Bg1 : 1;
575 unsigned target2Bg2 : 1;
576 unsigned target2Bg3 : 1;
577 unsigned target2Obj : 1;
578 unsigned target2Bd : 1;
579 };
580 uint16_t packed;
581 } bldcnt = { .packed = value };
582
583 enum BlendEffect oldEffect = renderer->blendEffect;
584
585 renderer->bg[0].target1 = bldcnt.target1Bg0;
586 renderer->bg[1].target1 = bldcnt.target1Bg1;
587 renderer->bg[2].target1 = bldcnt.target1Bg2;
588 renderer->bg[3].target1 = bldcnt.target1Bg3;
589 renderer->bg[0].target2 = bldcnt.target2Bg0;
590 renderer->bg[1].target2 = bldcnt.target2Bg1;
591 renderer->bg[2].target2 = bldcnt.target2Bg2;
592 renderer->bg[3].target2 = bldcnt.target2Bg3;
593
594 renderer->blendEffect = bldcnt.effect;
595 renderer->target1Obj = bldcnt.target1Obj;
596 renderer->target1Bd = bldcnt.target1Bd;
597 renderer->target2Obj = bldcnt.target2Obj;
598 renderer->target2Bd = bldcnt.target2Bd;
599
600 renderer->anyTarget2 = bldcnt.packed & 0x3F00;
601
602 if (oldEffect != renderer->blendEffect) {
603 _updatePalettes(renderer);
604 }
605}
606
607#define TEST_LAYER_ENABLED(X) \
608 (renderer->bg[X].enabled && \
609 (renderer->currentWindow.bg ## X ## Enable || \
610 (renderer->dispcnt.objwinEnable && renderer->objwin.bg ## X ## Enable)) && \
611 renderer->bg[X].priority == priority)
612
613static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
614 int w;
615 renderer->end = 0;
616 int spriteLayers = 0;
617 if (renderer->dispcnt.objEnable) {
618 if (renderer->oamDirty) {
619 _cleanOAM(renderer);
620 }
621 int mosaicV = renderer->mosaic.objV + 1;
622 int mosaicY = y - (y % mosaicV);
623 for (w = 0; w < renderer->nWindows; ++w) {
624 renderer->start = renderer->end;
625 renderer->end = renderer->windows[w].endX;
626 renderer->currentWindow = renderer->windows[w].control;
627 if (!renderer->currentWindow.objEnable) {
628 continue;
629 }
630 int i;
631 int drawn;
632 for (i = 0; i < renderer->oamMax; ++i) {
633 int localY = y;
634 struct GBAVideoSoftwareSprite* sprite = &renderer->sprites[i];
635 if (sprite->obj.mosaic) {
636 localY = mosaicY;
637 }
638 if ((localY < sprite->y && (sprite->endY - 256 < 0 || localY >= sprite->endY - 256)) || localY >= sprite->endY) {
639 continue;
640 }
641 if (sprite->obj.transformed) {
642 drawn = _preprocessTransformedSprite(renderer, &sprite->tobj, localY);
643 } else {
644 drawn = _preprocessSprite(renderer, &sprite->obj, localY);
645 }
646 spriteLayers |= drawn << sprite->obj.priority;
647 }
648 }
649 }
650
651 int priority;
652 for (priority = 0; priority < 4; ++priority) {
653 if (spriteLayers & (1 << priority)) {
654 _postprocessSprite(renderer, priority);
655 }
656 renderer->end = 0;
657 for (w = 0; w < renderer->nWindows; ++w) {
658 renderer->start = renderer->end;
659 renderer->end = renderer->windows[w].endX;
660 renderer->currentWindow = renderer->windows[w].control;
661 if (TEST_LAYER_ENABLED(0) && renderer->dispcnt.mode < 2) {
662 _drawBackgroundMode0(renderer, &renderer->bg[0], y);
663 }
664 if (TEST_LAYER_ENABLED(1) && renderer->dispcnt.mode < 2) {
665 _drawBackgroundMode0(renderer, &renderer->bg[1], y);
666 }
667 if (TEST_LAYER_ENABLED(2)) {
668 switch (renderer->dispcnt.mode) {
669 case 0:
670 _drawBackgroundMode0(renderer, &renderer->bg[2], y);
671 break;
672 case 1:
673 case 2:
674 _drawBackgroundMode2(renderer, &renderer->bg[2], y);
675 break;
676 case 3:
677 _drawBackgroundMode3(renderer, &renderer->bg[2], y);
678 break;
679 case 4:
680 _drawBackgroundMode4(renderer, &renderer->bg[2], y);
681 break;
682 case 5:
683 _drawBackgroundMode5(renderer, &renderer->bg[2], y);
684 break;
685 }
686 }
687 if (TEST_LAYER_ENABLED(3)) {
688 switch (renderer->dispcnt.mode) {
689 case 0:
690 _drawBackgroundMode0(renderer, &renderer->bg[3], y);
691 break;
692 case 2:
693 _drawBackgroundMode2(renderer, &renderer->bg[3], y);
694 break;
695 }
696 }
697 }
698 }
699 renderer->bg[2].sx += renderer->bg[2].dmx;
700 renderer->bg[2].sy += renderer->bg[2].dmy;
701 renderer->bg[3].sx += renderer->bg[3].dmx;
702 renderer->bg[3].sy += renderer->bg[3].dmy;
703}
704
705// We stash the priority on the top bits so we can do a one-operator comparison
706// The lower the number, the higher the priority, and sprites take precendence over backgrounds
707// We want to do special processing if the color pixel is target 1, however
708
709static inline void _compositeBlendObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) {
710 if (color >= current) {
711 if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
712 color = _mix(renderer->blda, current, renderer->bldb, color);
713 } else {
714 color = current & 0x00FFFFFF;
715 }
716 } else {
717 color = (color & ~FLAG_TARGET_2) | (current & FLAG_OBJWIN);
718 }
719 *pixel = color;
720}
721
722static inline void _compositeBlendNoObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) {
723 // We stash the priority on the top bits so we can do a one-operator comparison
724 // The lower the number, the higher the priority, and sprites take precendence over backgrounds
725 // We want to do special processing if the color pixel is target 1, however
726 if (color >= current) {
727 if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
728 color = _mix(renderer->blda, current, renderer->bldb, color);
729 } else {
730 color = current & 0x00FFFFFF;
731 }
732 } else {
733 color = color & ~FLAG_TARGET_2;
734 }
735 *pixel = color;
736}
737
738static inline void _compositeNoBlendObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) {
739 UNUSED(renderer);
740 if (color < current) {
741 *pixel = color | (current & FLAG_OBJWIN);
742 }
743}
744
745static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) {
746 UNUSED(renderer);
747 if (color < current) {
748 *pixel = color;
749 }
750}
751
752#define COMPOSITE_16_OBJWIN(BLEND) \
753 if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) { \
754 unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[paletteData | pixelData] : palette[pixelData]; \
755 _composite ## BLEND ## Objwin(renderer, pixel, color | flags, current); \
756 }
757
758#define COMPOSITE_16_NO_OBJWIN(BLEND) \
759 _composite ## BLEND ## NoObjwin(renderer, pixel, palette[pixelData] | flags, current);
760
761#define COMPOSITE_256_OBJWIN(BLEND) \
762 if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) { \
763 unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[pixelData] : palette[pixelData]; \
764 _composite ## BLEND ## Objwin(renderer, pixel, color | flags, current); \
765 }
766
767#define COMPOSITE_256_NO_OBJWIN(BLEND) \
768 COMPOSITE_16_NO_OBJWIN(BLEND)
769
770#define BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN) \
771 pixelData = tileData & 0xF; \
772 current = *pixel; \
773 if (pixelData && IS_WRITABLE(current)) { \
774 COMPOSITE_16_ ## OBJWIN (BLEND); \
775 } \
776 tileData >>= 4;
777
778#define BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN) \
779 pixelData = tileData & 0xFF; \
780 current = *pixel; \
781 if (pixelData && IS_WRITABLE(current)) { \
782 COMPOSITE_256_ ## OBJWIN (BLEND); \
783 } \
784 tileData >>= 8;
785
786#define BACKGROUND_TEXT_SELECT_CHARACTER \
787 localX = tileX * 8 + inX; \
788 xBase = localX & 0xF8; \
789 if (background->size & 1) { \
790 xBase += (localX & 0x100) << 5; \
791 } \
792 screenBase = yBase + (xBase >> 3); \
793 mapData = renderer->d.vram[screenBase]; \
794 localY = inY & 0x7; \
795 if (GBA_TEXT_MAP_VFLIP(mapData)) { \
796 localY = 7 - localY; \
797 }
798
799#define PREPARE_OBJWIN \
800 int objwinSlowPath = renderer->dispcnt.objwinEnable; \
801 int objwinOnly = 0; \
802 int objwinForceEnable = 0; \
803 color_t* objwinPalette; \
804 if (objwinSlowPath) { \
805 if (background->target1 && renderer->objwin.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN)) { \
806 objwinPalette = renderer->variantPalette; \
807 } else { \
808 objwinPalette = renderer->normalPalette; \
809 } \
810 switch (background->index) { \
811 case 0: \
812 objwinForceEnable = renderer->objwin.bg0Enable && renderer->currentWindow.bg0Enable; \
813 objwinOnly = !renderer->objwin.bg0Enable; \
814 break; \
815 case 1: \
816 objwinForceEnable = renderer->objwin.bg1Enable && renderer->currentWindow.bg1Enable; \
817 objwinOnly = !renderer->objwin.bg1Enable; \
818 break; \
819 case 2: \
820 objwinForceEnable = renderer->objwin.bg2Enable && renderer->currentWindow.bg2Enable; \
821 objwinOnly = !renderer->objwin.bg2Enable; \
822 break; \
823 case 3: \
824 objwinForceEnable = renderer->objwin.bg3Enable && renderer->currentWindow.bg3Enable; \
825 objwinOnly = !renderer->objwin.bg3Enable; \
826 break; \
827 } \
828 }
829
830#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_16(BLEND, OBJWIN) \
831 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
832 palette = &mainPalette[paletteData]; \
833 charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY; \
834 tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
835 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
836 tileData >>= 4 * mod8; \
837 for (; outX < end; ++outX) { \
838 uint32_t* pixel = &renderer->row[outX]; \
839 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
840 } \
841 } else { \
842 for (outX = end - 1; outX >= renderer->start; --outX) { \
843 uint32_t* pixel = &renderer->row[outX]; \
844 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
845 } \
846 }
847
848#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_16(BLEND, OBJWIN) \
849 charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY; \
850 tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
851 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
852 palette = &mainPalette[paletteData]; \
853 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
854 outX = renderer->end - mod8; \
855 if (outX < renderer->start) { \
856 tileData >>= 4 * (renderer->start - outX); \
857 outX = renderer->start; \
858 } \
859 for (; outX < renderer->end; ++outX) { \
860 uint32_t* pixel = &renderer->row[outX]; \
861 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
862 } \
863 } else { \
864 tileData >>= 4 * (0x8 - mod8); \
865 int end2 = renderer->end - 8; \
866 if (end2 < -1) { \
867 end2 = -1; \
868 } \
869 for (outX = renderer->end - 1; outX > end2; --outX) { \
870 uint32_t* pixel = &renderer->row[outX]; \
871 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
872 } \
873 }
874
875#define DRAW_BACKGROUND_MODE_0_MOSAIC_16(BLEND, OBJWIN) \
876 for (; tileX < tileEnd; ++tileX) { \
877 BACKGROUND_TEXT_SELECT_CHARACTER; \
878 charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY; \
879 tileData = carryData; \
880 for (x = 0; x < 8; ++x) { \
881 if (!mosaicWait) { \
882 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
883 palette = &mainPalette[paletteData]; \
884 tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
885 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
886 tileData >>= x * 4; \
887 } else { \
888 tileData >>= (7 - x) * 4; \
889 } \
890 tileData &= 0xF; \
891 tileData |= tileData << 4; \
892 tileData |= tileData << 8; \
893 tileData |= tileData << 12; \
894 tileData |= tileData << 16; \
895 tileData |= tileData << 20; \
896 tileData |= tileData << 24; \
897 tileData |= tileData << 28; \
898 carryData = tileData; \
899 mosaicWait = mosaicH; \
900 } \
901 --mosaicWait; \
902 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
903 ++pixel; \
904 } \
905 }
906
907#define DRAW_BACKGROUND_MODE_0_TILES_16(BLEND, OBJWIN) \
908 for (; tileX < tileEnd; ++tileX) { \
909 BACKGROUND_TEXT_SELECT_CHARACTER; \
910 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
911 palette = &mainPalette[paletteData]; \
912 charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY; \
913 tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
914 if (tileData) { \
915 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
916 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
917 ++pixel; \
918 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
919 ++pixel; \
920 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
921 ++pixel; \
922 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
923 ++pixel; \
924 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
925 ++pixel; \
926 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
927 ++pixel; \
928 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
929 ++pixel; \
930 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
931 ++pixel; \
932 } else { \
933 pixel += 7; \
934 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
935 --pixel; \
936 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
937 --pixel; \
938 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
939 --pixel; \
940 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
941 --pixel; \
942 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
943 --pixel; \
944 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
945 --pixel; \
946 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
947 --pixel; \
948 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
949 pixel += 8; \
950 } \
951 } else { \
952 pixel += 8; \
953 } \
954 }
955
956#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_256(BLEND, OBJWIN) \
957 /* TODO: hflip */ \
958 charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1); \
959 int end2 = end - 4; \
960 int shift = inX & 0x3; \
961 if (end2 > 0) { \
962 tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
963 tileData >>= 8 * shift; \
964 shift = 0; \
965 for (; outX < end2; ++outX) { \
966 uint32_t* pixel = &renderer->row[outX]; \
967 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
968 } \
969 } \
970 \
971 tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
972 tileData >>= 8 * shift; \
973 for (; outX < end; ++outX) { \
974 uint32_t* pixel = &renderer->row[outX]; \
975 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
976 }
977
978#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_256(BLEND, OBJWIN) \
979 /* TODO: hflip */ \
980 charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1); \
981 outX = renderer->end - 8 + end; \
982 int end2 = 4 - end; \
983 if (end2 > 0) { \
984 tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
985 for (; outX < renderer->end - end2; ++outX) { \
986 uint32_t* pixel = &renderer->row[outX]; \
987 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
988 } \
989 ++charBase; \
990 } \
991 \
992 tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
993 for (; outX < renderer->end; ++outX) { \
994 uint32_t* pixel = &renderer->row[outX]; \
995 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
996 }
997
998#define DRAW_BACKGROUND_MODE_0_TILES_256(BLEND, OBJWIN) \
999 for (; tileX < tileEnd; ++tileX) { \
1000 BACKGROUND_TEXT_SELECT_CHARACTER; \
1001 charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1); \
1002 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
1003 tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
1004 if (tileData) { \
1005 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1006 ++pixel; \
1007 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1008 ++pixel; \
1009 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1010 ++pixel; \
1011 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1012 ++pixel; \
1013 } else { \
1014 pixel += 4; \
1015 } \
1016 tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
1017 if (tileData) { \
1018 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1019 ++pixel; \
1020 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1021 ++pixel; \
1022 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1023 ++pixel; \
1024 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1025 ++pixel; \
1026 } else { \
1027 pixel += 4; \
1028 } \
1029 } else { \
1030 uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
1031 if (tileData) { \
1032 pixel += 3; \
1033 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1034 --pixel; \
1035 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1036 --pixel; \
1037 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1038 --pixel; \
1039 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1040 } \
1041 pixel += 4; \
1042 tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
1043 if (tileData) { \
1044 pixel += 3; \
1045 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1046 --pixel; \
1047 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1048 --pixel; \
1049 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1050 --pixel; \
1051 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1052 } \
1053 pixel += 4; \
1054 } \
1055 }
1056
1057#define DRAW_BACKGROUND_MODE_0_MOSAIC_256(BLEND, OBJWIN) \
1058 for (; tileX < tileEnd; ++tileX) { \
1059 BACKGROUND_TEXT_SELECT_CHARACTER; \
1060 charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1); \
1061 tileData = carryData; \
1062 for (x = 0; x < 8; ++x) { \
1063 if (!mosaicWait) { \
1064 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
1065 if (x >= 4) { \
1066 tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
1067 tileData >>= (x - 4) * 8; \
1068 } else { \
1069 tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
1070 tileData >>= x * 8; \
1071 } \
1072 } else { \
1073 if (x >= 4) { \
1074 tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
1075 tileData >>= (7 - x) * 8; \
1076 } else { \
1077 tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
1078 tileData >>= (3 - x) * 8; \
1079 } \
1080 } \
1081 tileData &= 0xFF; \
1082 carryData = tileData; \
1083 mosaicWait = mosaicH; \
1084 } \
1085 tileData |= tileData << 8; \
1086 --mosaicWait; \
1087 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1088 ++pixel; \
1089 } \
1090 }
1091
1092#define DRAW_BACKGROUND_MODE_0(BPP, BLEND, OBJWIN) \
1093 uint32_t* pixel = &renderer->row[outX]; \
1094 if (background->mosaic && renderer->mosaic.bgH) { \
1095 int mosaicH = renderer->mosaic.bgH + 1; \
1096 int x; \
1097 int mosaicWait = outX % mosaicH; \
1098 int carryData = 0; \
1099 paletteData = 0; /* Quiets compiler warning */ \
1100 DRAW_BACKGROUND_MODE_0_MOSAIC_ ## BPP (BLEND, OBJWIN) \
1101 return; \
1102 } \
1103 \
1104 if (inX & 0x7) { \
1105 int mod8 = inX & 0x7; \
1106 BACKGROUND_TEXT_SELECT_CHARACTER; \
1107 \
1108 int end = outX + 0x8 - mod8; \
1109 if (end > renderer->end) { \
1110 /* TODO: ensure tiles are properly aligned from this*/ \
1111 end = renderer->end; \
1112 } \
1113 if (end == outX) { \
1114 return; \
1115 } \
1116 DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_ ## BPP (BLEND, OBJWIN) \
1117 } \
1118 if (inX & 0x7 || (renderer->end - renderer->start) & 0x7) { \
1119 tileX = tileEnd; \
1120 int pixelData; \
1121 int mod8 = (inX + renderer->end - renderer->start) & 0x7; \
1122 BACKGROUND_TEXT_SELECT_CHARACTER; \
1123 \
1124 int end = 0x8 - mod8; \
1125 UNUSED(end); \
1126 DRAW_BACKGROUND_MODE_0_TILE_PREFIX_ ## BPP (BLEND, OBJWIN) \
1127 \
1128 tileX = (inX & 0x7) != 0; \
1129 outX = renderer->start + tileX * 8 - (inX & 0x7); \
1130 } \
1131 \
1132 pixel = &renderer->row[outX]; \
1133 DRAW_BACKGROUND_MODE_0_TILES_ ## BPP (BLEND, OBJWIN)
1134
1135static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
1136 int inX = renderer->start + background->x;
1137 if (background->mosaic) {
1138 int mosaicV = renderer->mosaic.bgV + 1;
1139 y -= y % mosaicV;
1140 }
1141 int inY = y + background->y;
1142 uint16_t mapData;
1143
1144 unsigned yBase = inY & 0xF8;
1145 if (background->size == 2) {
1146 yBase += inY & 0x100;
1147 } else if (background->size == 3) {
1148 yBase += (inY & 0x100) << 1;
1149 }
1150 yBase = (background->screenBase >> 1) + (yBase << 2);
1151
1152 int localX;
1153 int localY;
1154
1155 unsigned xBase;
1156
1157 int flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND;
1158 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA);
1159 flags |= FLAG_TARGET_2 * background->target2;
1160
1161 uint32_t screenBase;
1162 uint32_t charBase;
1163 int variant = background->target1 && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1164 color_t* mainPalette = renderer->normalPalette;
1165 if (variant) {
1166 mainPalette = renderer->variantPalette;
1167 }
1168 color_t* palette = mainPalette;
1169 PREPARE_OBJWIN;
1170
1171 int outX = renderer->start;
1172
1173 uint32_t tileData;
1174 uint32_t current;
1175 int pixelData;
1176 int paletteData;
1177 int tileX = 0;
1178 int tileEnd = (renderer->end - renderer->start + (inX & 0x7)) >> 3;
1179
1180 if (!objwinSlowPath) {
1181 if (!(flags & FLAG_TARGET_2)) {
1182 if (!background->multipalette) {
1183 DRAW_BACKGROUND_MODE_0(16, NoBlend, NO_OBJWIN);
1184 } else {
1185 DRAW_BACKGROUND_MODE_0(256, NoBlend, NO_OBJWIN);
1186 }
1187 } else {
1188 if (!background->multipalette) {
1189 DRAW_BACKGROUND_MODE_0(16, Blend, NO_OBJWIN);
1190 } else {
1191 DRAW_BACKGROUND_MODE_0(256, Blend, NO_OBJWIN);
1192 }
1193 }
1194 } else {
1195 if (!(flags & FLAG_TARGET_2)) {
1196 if (!background->multipalette) {
1197 DRAW_BACKGROUND_MODE_0(16, NoBlend, OBJWIN);
1198 } else {
1199 DRAW_BACKGROUND_MODE_0(256, NoBlend, OBJWIN);
1200 }
1201 } else {
1202 if (!background->multipalette) {
1203 DRAW_BACKGROUND_MODE_0(16, Blend, OBJWIN);
1204 } else {
1205 DRAW_BACKGROUND_MODE_0(256, Blend, OBJWIN);
1206 }
1207 }
1208 }
1209}
1210
1211#define BACKGROUND_BITMAP_INIT \
1212 UNUSED(unused); \
1213 int32_t x = background->sx + (renderer->start - 1) * background->dx; \
1214 int32_t y = background->sy + (renderer->start - 1) * background->dy; \
1215 int32_t localX; \
1216 int32_t localY; \
1217 \
1218 int flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND; \
1219 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA); \
1220 flags |= FLAG_TARGET_2 * background->target2; \
1221 int variant = background->target1 && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); \
1222 color_t* palette = renderer->normalPalette; \
1223 if (variant) { \
1224 palette = renderer->variantPalette; \
1225 } \
1226 PREPARE_OBJWIN;
1227
1228#define BACKGROUND_BITMAP_ITERATE(W, H) \
1229 x += background->dx; \
1230 y += background->dy; \
1231 \
1232 if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
1233 continue; \
1234 } else { \
1235 localX = x; \
1236 localY = y; \
1237 }
1238
1239static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1240 int sizeAdjusted = 0x8000 << background->size;
1241
1242 BACKGROUND_BITMAP_INIT;
1243
1244 uint32_t screenBase = background->screenBase;
1245 uint32_t charBase = background->charBase;
1246 uint8_t mapData;
1247 uint8_t tileData;
1248
1249 int outX;
1250 uint32_t* pixel;
1251 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1252 x += background->dx;
1253 y += background->dy;
1254
1255 if (background->overflow) {
1256 localX = x & (sizeAdjusted - 1);
1257 localY = y & (sizeAdjusted - 1);
1258 } else if ((x | y) & ~(sizeAdjusted - 1)) {
1259 continue;
1260 } else {
1261 localX = x;
1262 localY = y;
1263 }
1264 mapData = ((uint8_t*)renderer->d.vram)[screenBase + (localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)];
1265 tileData = ((uint8_t*)renderer->d.vram)[charBase + (mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)];
1266
1267 uint32_t current = *pixel;
1268 if (tileData && IS_WRITABLE(current)) {
1269 if (!objwinSlowPath) {
1270 _compositeBlendNoObjwin(renderer, pixel, palette[tileData] | flags, current);
1271 } else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) {
1272 color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
1273 _compositeBlendObjwin(renderer, pixel, currentPalette[tileData] | flags, current);
1274 }
1275 }
1276 }
1277}
1278
1279static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1280 BACKGROUND_BITMAP_INIT;
1281
1282 uint32_t color;
1283
1284 int outX;
1285 uint32_t* pixel;
1286 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1287 BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1288
1289 color = ((uint16_t*)renderer->d.vram)[(localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1290#ifndef COLOR_16_BIT
1291 unsigned color32;
1292 color32 = 0;
1293 color32 |= (color << 3) & 0xF8;
1294 color32 |= (color << 6) & 0xF800;
1295 color32 |= (color << 9) & 0xF80000;
1296 color = color32;
1297#endif
1298
1299 uint32_t current = *pixel;
1300 if (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly) {
1301 if (!variant) {
1302 _compositeBlendObjwin(renderer, pixel, color | flags, current);
1303 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1304 _compositeBlendObjwin(renderer, pixel, _brighten(color, renderer->bldy) | flags, current);
1305 } else if (renderer->blendEffect == BLEND_DARKEN) {
1306 _compositeBlendObjwin(renderer, pixel, _darken(color, renderer->bldy) | flags, current);
1307 }
1308 }
1309 }
1310}
1311
1312static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1313 BACKGROUND_BITMAP_INIT;
1314
1315 uint16_t color;
1316 uint32_t offset = 0;
1317 if (renderer->dispcnt.frameSelect) {
1318 offset = 0xA000;
1319 }
1320
1321 int outX;
1322 uint32_t* pixel;
1323 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1324 BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1325
1326 color = ((uint8_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1327
1328 uint32_t current = *pixel;
1329 if (color && IS_WRITABLE(current)) {
1330 if (!objwinSlowPath) {
1331 _compositeBlendNoObjwin(renderer, pixel, palette[color] | flags, current);
1332 } else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) {
1333 color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
1334 _compositeBlendObjwin(renderer, pixel, currentPalette[color] | flags, current);
1335 }
1336 }
1337 }
1338}
1339
1340static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1341 BACKGROUND_BITMAP_INIT;
1342
1343 uint32_t color;
1344 uint32_t offset = 0;
1345 if (renderer->dispcnt.frameSelect) {
1346 offset = 0xA000;
1347 }
1348
1349 int outX;
1350 uint32_t* pixel;
1351 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1352 BACKGROUND_BITMAP_ITERATE(160, 128);
1353
1354 color = ((uint16_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * 160];
1355#ifndef COLOR_16_BIT
1356 unsigned color32 = 0;
1357 color32 |= (color << 9) & 0xF80000;
1358 color32 |= (color << 3) & 0xF8;
1359 color32 |= (color << 6) & 0xF800;
1360 color = color32;
1361#endif
1362
1363 uint32_t current = *pixel;
1364 if (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly) {
1365 if (!variant) {
1366 _compositeBlendObjwin(renderer, pixel, color | flags, current);
1367 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1368 _compositeBlendObjwin(renderer, pixel, _brighten(color, renderer->bldy) | flags, current);
1369 } else if (renderer->blendEffect == BLEND_DARKEN) {
1370 _compositeBlendObjwin(renderer, pixel, _darken(color, renderer->bldy) | flags, current);
1371 }
1372 }
1373 }
1374}
1375
1376#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
1377 SPRITE_YBASE_ ## DEPTH(inY); \
1378 for (; outX < condition; ++outX, inX += xOffset) { \
1379 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1380 continue; \
1381 } \
1382 SPRITE_XBASE_ ## DEPTH(inX); \
1383 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
1384 }
1385
1386#define SPRITE_MOSAIC_LOOP(DEPTH, TYPE) \
1387 SPRITE_YBASE_ ## DEPTH(inY); \
1388 if (outX % mosaicH) { \
1389 inX += (mosaicH - (outX % mosaicH)) * xOffset; \
1390 outX += mosaicH - (outX % mosaicH); \
1391 } \
1392 for (; outX < condition; ++outX, inX += xOffset) { \
1393 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1394 continue; \
1395 } \
1396 int localX = inX - xOffset * (outX % mosaicH); \
1397 SPRITE_XBASE_ ## DEPTH(localX); \
1398 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1399 }
1400
1401#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
1402 int outX; \
1403 for (outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) { \
1404 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1405 continue; \
1406 } \
1407 int inX = outX - x; \
1408 int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1); \
1409 int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1); \
1410 \
1411 if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
1412 continue; \
1413 } \
1414 \
1415 SPRITE_YBASE_ ## DEPTH(localY); \
1416 SPRITE_XBASE_ ## DEPTH(localX); \
1417 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1418 }
1419
1420#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
1421#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
1422
1423#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
1424 unsigned tileData = vramBase[((yBase + charBase + xBase) & 0x7FFF) >> 1]; \
1425 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1426 if (tileData && (renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags) { \
1427 renderer->spriteLayer[outX] = palette[tileData] | flags; \
1428 }
1429
1430#define SPRITE_DRAW_PIXEL_16_OBJWIN(localX) \
1431 unsigned tileData = vramBase[((yBase + charBase + xBase) & 0x7FFF) >> 1]; \
1432 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1433 if (tileData) { \
1434 renderer->row[outX] |= FLAG_OBJWIN; \
1435 }
1436
1437#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
1438#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width : 0x80) + (localY & 0x7) * 8;
1439
1440#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
1441 unsigned tileData = vramBase[((yBase + charBase + xBase) & 0x7FFF) >> 1]; \
1442 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1443 if (tileData && (renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags) { \
1444 renderer->spriteLayer[outX] = palette[tileData] | flags; \
1445 }
1446
1447#define SPRITE_DRAW_PIXEL_256_OBJWIN(localX) \
1448 unsigned tileData = vramBase[((yBase + charBase + xBase) & 0x7FFF) >> 1]; \
1449 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1450 if (tileData) { \
1451 renderer->row[outX] |= FLAG_OBJWIN; \
1452 }
1453
1454static int _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
1455 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1456 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1457 int start = renderer->start;
1458 int end = renderer->end;
1459 uint32_t flags = sprite->priority << OFFSET_PRIORITY;
1460 flags |= FLAG_TARGET_1 * ((renderer->currentWindow.blendEnable && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1461 flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN);
1462 int x = sprite->x;
1463 uint16_t* vramBase = &renderer->d.vram[BASE_TILE >> 1];
1464 unsigned charBase = sprite->tile * 0x20;
1465 int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1466 if (sprite->mode == OBJ_MODE_SEMITRANSPARENT && renderer->target2Bd) {
1467 // Hack: if a sprite is blended, then the variant palette is not used, but we don't know if it's blended in advance
1468 variant = 0;
1469 }
1470 color_t* palette = &renderer->normalPalette[0x100];
1471 if (variant) {
1472 palette = &renderer->variantPalette[0x100];
1473 }
1474
1475 int outX = x >= start ? x : start;
1476 int condition = x + width;
1477 int mosaicH = 1;
1478 if (sprite->mosaic) {
1479 mosaicH = renderer->mosaic.objH + 1;
1480 if (condition % mosaicH) {
1481 condition += mosaicH - (condition % mosaicH);
1482 }
1483 }
1484 int inY = y - sprite->y;
1485 if (sprite->y + height - 256 >= 0) {
1486 inY += 256;
1487 }
1488 if (sprite->vflip) {
1489 inY = height - inY - 1;
1490 }
1491 if (end < condition) {
1492 condition = end;
1493 }
1494 int inX = outX - x;
1495 int xOffset = 1;
1496 if (sprite->hflip) {
1497 inX = width - inX - 1;
1498 xOffset = -1;
1499 }
1500 if (!sprite->multipalette) {
1501 palette = &palette[sprite->palette << 4];
1502 if (flags & FLAG_OBJWIN) {
1503 SPRITE_NORMAL_LOOP(16, OBJWIN);
1504 } else if (sprite->mosaic) {
1505 SPRITE_MOSAIC_LOOP(16, NORMAL);
1506 } else {
1507 SPRITE_NORMAL_LOOP(16, NORMAL);
1508 }
1509 } else {
1510 if (flags & FLAG_OBJWIN) {
1511 SPRITE_NORMAL_LOOP(256, OBJWIN);
1512 } else if (sprite->mosaic) {
1513 SPRITE_MOSAIC_LOOP(256, NORMAL);
1514 } else {
1515 SPRITE_NORMAL_LOOP(256, NORMAL);
1516 }
1517 }
1518 return 1;
1519}
1520
1521static int _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
1522 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1523 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1524 int totalWidth = width << sprite->doublesize;
1525 int totalHeight = height << sprite->doublesize;
1526 int start = renderer->start;
1527 int end = renderer->end;
1528 uint32_t flags = sprite->priority << OFFSET_PRIORITY;
1529 flags |= FLAG_TARGET_1 * ((renderer->currentWindow.blendEnable && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1530 flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN);
1531 int x = sprite->x;
1532 uint16_t* vramBase = &renderer->d.vram[BASE_TILE >> 1];
1533 unsigned charBase = sprite->tile * 0x20;
1534 struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
1535 int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1536 if (sprite->mode == OBJ_MODE_SEMITRANSPARENT && renderer->target2Bd) {
1537 // Hack: if a sprite is blended, then the variant palette is not used, but we don't know if it's blended in advance
1538 variant = 0;
1539 }
1540 color_t* palette = &renderer->normalPalette[0x100];
1541 if (variant) {
1542 palette = &renderer->variantPalette[0x100];
1543 }
1544 int inY = y - sprite->y;
1545 if (inY < 0) {
1546 inY += 256;
1547 }
1548 if (!sprite->multipalette) {
1549 palette = &palette[sprite->palette << 4];
1550 if (flags & FLAG_OBJWIN) {
1551 SPRITE_TRANSFORMED_LOOP(16, OBJWIN);
1552 } else {
1553 SPRITE_TRANSFORMED_LOOP(16, NORMAL);
1554 }
1555 } else {
1556 if (flags & FLAG_OBJWIN) {
1557 SPRITE_TRANSFORMED_LOOP(256, OBJWIN);
1558 } else {
1559 SPRITE_TRANSFORMED_LOOP(256, NORMAL);
1560 }
1561 }
1562 return 1;
1563}
1564
1565static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority) {
1566 int x;
1567 uint32_t* pixel = renderer->row;
1568 uint32_t flags = FLAG_TARGET_2 * renderer->target2Obj;
1569
1570 int objwinSlowPath = renderer->dispcnt.objwinEnable;
1571 int objwinDisable = 0;
1572 if (objwinSlowPath) {
1573 objwinDisable = !renderer->objwin.objEnable;
1574 }
1575 if (objwinSlowPath && objwinDisable) {
1576 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x, ++pixel) {
1577 uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
1578 uint32_t current = *pixel;
1579 if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && !(current & FLAG_OBJWIN) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
1580 _compositeBlendObjwin(renderer, pixel, color | flags, current);
1581 }
1582 }
1583 } else {
1584 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x, ++pixel) {
1585 uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
1586 uint32_t current = *pixel;
1587 if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
1588 _compositeBlendNoObjwin(renderer, pixel, color | flags, current);
1589 }
1590 }
1591 }
1592}
1593
1594static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
1595 int i;
1596 if (renderer->blendEffect == BLEND_BRIGHTEN) {
1597 for (i = 0; i < 512; ++i) {
1598 renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
1599 }
1600 } else if (renderer->blendEffect == BLEND_DARKEN) {
1601 for (i = 0; i < 512; ++i) {
1602 renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
1603 }
1604 } else {
1605 for (i = 0; i < 512; ++i) {
1606 renderer->variantPalette[i] = renderer->normalPalette[i];
1607 }
1608 }
1609}
1610
1611static inline unsigned _brighten(unsigned color, int y) {
1612 unsigned c = 0;
1613 unsigned a;
1614#ifdef COLOR_16_BIT
1615 a = color & 0x1F;
1616 c |= (a + ((0x1F - a) * y) / 16) & 0x1F;
1617
1618#ifdef COLOR_5_6_5
1619 a = color & 0x7C0;
1620 c |= (a + ((0x7C0 - a) * y) / 16) & 0x7C0;
1621
1622 a = color & 0xF800;
1623 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1624#else
1625 a = color & 0x3E0;
1626 c |= (a + ((0x3E0 - a) * y) / 16) & 0x3E0;
1627
1628 a = color & 0x7C00;
1629 c |= (a + ((0x7C00 - a) * y) / 16) & 0x7C00;
1630#endif
1631#else
1632 a = color & 0xF8;
1633 c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
1634
1635 a = color & 0xF800;
1636 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1637
1638 a = color & 0xF80000;
1639 c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
1640#endif
1641 return c;
1642}
1643
1644static inline unsigned _darken(unsigned color, int y) {
1645 unsigned c = 0;
1646 unsigned a;
1647#ifdef COLOR_16_BIT
1648 a = color & 0x1F;
1649 c |= (a - (a * y) / 16) & 0x1F;
1650
1651#ifdef COLOR_5_6_5
1652 a = color & 0x7C0;
1653 c |= (a - (a * y) / 16) & 0x7C0;
1654
1655 a = color & 0xF800;
1656 c |= (a - (a * y) / 16) & 0xF800;
1657#else
1658 a = color & 0x3E0;
1659 c |= (a - (a * y) / 16) & 0x3E0;
1660
1661 a = color & 0x7C00;
1662 c |= (a - (a * y) / 16) & 0x7C00;
1663#endif
1664#else
1665 a = color & 0xF8;
1666 c |= (a - (a * y) / 16) & 0xF8;
1667
1668 a = color & 0xF800;
1669 c |= (a - (a * y) / 16) & 0xF800;
1670
1671 a = color & 0xF80000;
1672 c |= (a - (a * y) / 16) & 0xF80000;
1673#endif
1674 return c;
1675}
1676
1677static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB) {
1678 unsigned c = 0;
1679 unsigned a, b;
1680#ifdef COLOR_16_BIT
1681#ifdef COLOR_5_6_5
1682 a = colorA & 0xF81F;
1683 b = colorB & 0xF81F;
1684 a |= (colorA & 0x7C0) << 16;
1685 b |= (colorB & 0x7C0) << 16;
1686 c = ((a * weightA + b * weightB) / 16);
1687 if (c & 0x08000000) {
1688 c = (c & ~0x0FC00000) | 0x07C00000;
1689 }
1690 if (c & 0x0020) {
1691 c = (c & ~0x003F) | 0x001F;
1692 }
1693 if (c & 0x10000) {
1694 c = (c & ~0x1F800) | 0xF800;
1695 }
1696 c = (c & 0xF81F) | ((c >> 16) & 0x07C0);
1697#else
1698 a = colorA & 0x7C1F;
1699 b = colorB & 0x7C1F;
1700 a |= (colorA & 0x3E0) << 16;
1701 b |= (colorB & 0x3E0) << 16;
1702 c = ((a * weightA + b * weightB) / 16);
1703 if (c & 0x04000000) {
1704 c = (c & ~0x07E00000) | 0x03E00000;
1705 }
1706 if (c & 0x0020) {
1707 c = (c & ~0x003F) | 0x001F;
1708 }
1709 if (c & 0x10000) {
1710 c = (c & ~0x1F800) | 0xF800;
1711 }
1712 c = (c & 0x7C1F) | ((c >> 16) & 0x03E0);
1713#endif
1714#else
1715 a = colorA & 0xF8;
1716 b = colorB & 0xF8;
1717 c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
1718 if (c & 0x00000100) {
1719 c = 0x000000F8;
1720 }
1721
1722 a = colorA & 0xF800;
1723 b = colorB & 0xF800;
1724 c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
1725 if (c & 0x00010000) {
1726 c = (c & 0x000000F8) | 0x0000F800;
1727 }
1728
1729 a = colorA & 0xF80000;
1730 b = colorB & 0xF80000;
1731 c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
1732 if (c & 0x01000000) {
1733 c = (c & 0x0000F8F8) | 0x00F80000;
1734 }
1735#endif
1736 return c;
1737}