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