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