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