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 <stdlib.h>
7#include <string.h>
8
9static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer);
10static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer);
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 GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value);
19
20static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y);
21static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
22static void _drawTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y);
23static void _drawSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y);
24
25static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer);
26static inline uint32_t _brighten(uint32_t color, int y);
27static inline uint32_t _darken(uint32_t color, int y);
28static uint32_t _mix(int weightA, uint32_t colorA, int weightB, uint32_t colorB);
29
30static void _sortBackgrounds(struct GBAVideoSoftwareRenderer* renderer);
31static int _backgroundComparator(const void* a, const void* b);
32
33void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) {
34 renderer->d.init = GBAVideoSoftwareRendererInit;
35 renderer->d.deinit = GBAVideoSoftwareRendererDeinit;
36 renderer->d.writeVideoRegister = GBAVideoSoftwareRendererWriteVideoRegister;
37 renderer->d.writePalette = GBAVideoSoftwareRendererWritePalette;
38 renderer->d.drawScanline = GBAVideoSoftwareRendererDrawScanline;
39 renderer->d.finishFrame = GBAVideoSoftwareRendererFinishFrame;
40
41 renderer->d.turbo = 0;
42 renderer->d.framesPending = 0;
43
44 renderer->sortedBg[0] = &renderer->bg[0];
45 renderer->sortedBg[1] = &renderer->bg[1];
46 renderer->sortedBg[2] = &renderer->bg[2];
47 renderer->sortedBg[3] = &renderer->bg[3];
48
49 {
50 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
51 renderer->mutex = mutex;
52 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
53 renderer->upCond = cond;
54 renderer->downCond = cond;
55 }
56}
57
58static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) {
59 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
60 int i;
61
62 softwareRenderer->dispcnt.packed = 0x0080;
63
64 softwareRenderer->target1Obj = 0;
65 softwareRenderer->target1Bd = 0;
66 softwareRenderer->target2Obj = 0;
67 softwareRenderer->target2Bd = 0;
68 softwareRenderer->blendEffect = BLEND_NONE;
69 memset(softwareRenderer->normalPalette, 0, sizeof(softwareRenderer->normalPalette));
70 memset(softwareRenderer->variantPalette, 0, sizeof(softwareRenderer->variantPalette));
71
72 softwareRenderer->blda = 0;
73 softwareRenderer->bldb = 0;
74 softwareRenderer->bldy = 0;
75
76 for (i = 0; i < 4; ++i) {
77 struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i];
78 bg->index = i;
79 bg->enabled = 0;
80 bg->priority = 0;
81 bg->charBase = 0;
82 bg->mosaic = 0;
83 bg->multipalette = 0;
84 bg->screenBase = 0;
85 bg->overflow = 0;
86 bg->size = 0;
87 bg->target1 = 0;
88 bg->target2 = 0;
89 bg->x = 0;
90 bg->y = 0;
91 bg->refx = 0;
92 bg->refy = 0;
93 bg->dx = 256;
94 bg->dmx = 0;
95 bg->dy = 0;
96 bg->dmy = 256;
97 bg->sx = 0;
98 bg->sy = 0;
99 }
100
101 pthread_mutex_init(&softwareRenderer->mutex, 0);
102 pthread_cond_init(&softwareRenderer->upCond, 0);
103 pthread_cond_init(&softwareRenderer->downCond, 0);
104}
105
106static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer) {
107 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
108
109 pthread_mutex_destroy(&softwareRenderer->mutex);
110 pthread_cond_destroy(&softwareRenderer->upCond);
111 pthread_cond_destroy(&softwareRenderer->downCond);
112}
113
114static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
115 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
116 switch (address) {
117 case REG_DISPCNT:
118 value &= 0xFFFB;
119 softwareRenderer->dispcnt.packed = value;
120 GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer);
121 break;
122 case REG_BG0CNT:
123 value &= 0xFFCF;
124 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[0], value);
125 break;
126 case REG_BG1CNT:
127 value &= 0xFFCF;
128 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[1], value);
129 break;
130 case REG_BG2CNT:
131 value &= 0xFFCF;
132 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[2], value);
133 break;
134 case REG_BG3CNT:
135 value &= 0xFFCF;
136 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[3], value);
137 break;
138 case REG_BG0HOFS:
139 value &= 0x01FF;
140 softwareRenderer->bg[0].x = value;
141 break;
142 case REG_BG0VOFS:
143 value &= 0x01FF;
144 softwareRenderer->bg[0].y = value;
145 break;
146 case REG_BG1HOFS:
147 value &= 0x01FF;
148 softwareRenderer->bg[1].x = value;
149 break;
150 case REG_BG1VOFS:
151 value &= 0x01FF;
152 softwareRenderer->bg[1].y = value;
153 break;
154 case REG_BG2HOFS:
155 value &= 0x01FF;
156 softwareRenderer->bg[2].x = value;
157 break;
158 case REG_BG2VOFS:
159 value &= 0x01FF;
160 softwareRenderer->bg[2].y = value;
161 break;
162 case REG_BG3HOFS:
163 value &= 0x01FF;
164 softwareRenderer->bg[3].x = value;
165 break;
166 case REG_BG3VOFS:
167 value &= 0x01FF;
168 softwareRenderer->bg[3].y = value;
169 break;
170 case REG_BLDCNT:
171 GBAVideoSoftwareRendererWriteBLDCNT(softwareRenderer, value);
172 break;
173 case REG_BLDALPHA:
174 softwareRenderer->blda = value & 0x1F;
175 if (softwareRenderer->blda > 0x10) {
176 softwareRenderer->blda = 0x10;
177 }
178 softwareRenderer->bldb = (value >> 8) & 0x1F;
179 if (softwareRenderer->bldb > 0x10) {
180 softwareRenderer->bldb = 0x10;
181 }
182 break;
183 case REG_BLDY:
184 softwareRenderer->bldy = value & 0x1F;
185 if (softwareRenderer->bldy > 0x10) {
186 softwareRenderer->bldy = 0x10;
187 }
188 _updatePalettes(softwareRenderer);
189 break;
190 default:
191 GBALog(GBA_LOG_STUB, "Stub video register write: %03x", address);
192 }
193 return value;
194}
195
196static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
197 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
198 uint32_t color32 = 0;
199 color32 |= (value << 3) & 0xF8;
200 color32 |= (value << 6) & 0xF800;
201 color32 |= (value << 9) & 0xF80000;
202 softwareRenderer->normalPalette[address >> 1] = color32;
203}
204
205static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
206 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
207 uint32_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
208 if (softwareRenderer->dispcnt.forcedBlank) {
209 for (int x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
210 row[x] = GBA_COLOR_WHITE;
211 }
212 return;
213 }
214
215 memset(softwareRenderer->flags, 0, sizeof(softwareRenderer->flags));
216 softwareRenderer->row = row;
217
218 softwareRenderer->start = 0;
219 softwareRenderer->end = VIDEO_HORIZONTAL_PIXELS;
220 _drawScanline(softwareRenderer, y);
221}
222
223static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
224 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
225
226 pthread_mutex_lock(&softwareRenderer->mutex);
227 renderer->framesPending++;
228 pthread_cond_broadcast(&softwareRenderer->upCond);
229 if (!renderer->turbo) {
230 pthread_cond_wait(&softwareRenderer->downCond, &softwareRenderer->mutex);
231 }
232 pthread_mutex_unlock(&softwareRenderer->mutex);
233}
234
235static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) {
236 renderer->bg[0].enabled = renderer->dispcnt.bg0Enable;
237 renderer->bg[1].enabled = renderer->dispcnt.bg1Enable;
238 renderer->bg[2].enabled = renderer->dispcnt.bg2Enable;
239 renderer->bg[3].enabled = renderer->dispcnt.bg3Enable;
240}
241
242static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value) {
243 union GBARegisterBGCNT reg = { .packed = value };
244 bg->priority = reg.priority;
245 bg->charBase = reg.charBase << 14;
246 bg->mosaic = reg.mosaic;
247 bg->multipalette = reg.multipalette;
248 bg->screenBase = reg.screenBase << 11;
249 bg->overflow = reg.overflow;
250 bg->size = reg.size;
251
252 _sortBackgrounds(renderer);
253}
254
255static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value) {
256 union {
257 struct {
258 unsigned target1Bg0 : 1;
259 unsigned target1Bg1 : 1;
260 unsigned target1Bg2 : 1;
261 unsigned target1Bg3 : 1;
262 unsigned target1Obj : 1;
263 unsigned target1Bd : 1;
264 enum BlendEffect effect : 2;
265 unsigned target2Bg0 : 1;
266 unsigned target2Bg1 : 1;
267 unsigned target2Bg2 : 1;
268 unsigned target2Bg3 : 1;
269 unsigned target2Obj : 1;
270 unsigned target2Bd : 1;
271 };
272 uint16_t packed;
273 } bldcnt = { .packed = value };
274
275 enum BlendEffect oldEffect = renderer->blendEffect;
276
277 renderer->bg[0].target1 = bldcnt.target1Bg0;
278 renderer->bg[1].target1 = bldcnt.target1Bg1;
279 renderer->bg[2].target1 = bldcnt.target1Bg2;
280 renderer->bg[3].target1 = bldcnt.target1Bg3;
281 renderer->bg[0].target2 = bldcnt.target2Bg0;
282 renderer->bg[1].target2 = bldcnt.target2Bg1;
283 renderer->bg[2].target2 = bldcnt.target2Bg2;
284 renderer->bg[3].target2 = bldcnt.target2Bg3;
285
286 renderer->blendEffect = bldcnt.effect;
287 renderer->target1Obj = bldcnt.target1Obj;
288 renderer->target1Bd = bldcnt.target1Bd;
289 renderer->target2Obj = bldcnt.target2Obj;
290 renderer->target2Bd = bldcnt.target2Bd;
291
292 if (oldEffect != renderer->blendEffect) {
293 _updatePalettes(renderer);
294 }
295}
296
297static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
298 int i;
299 if (renderer->dispcnt.objEnable) {
300 for (i = 0; i < 128; ++i) {
301 struct GBAObj* sprite = &renderer->d.oam->obj[i];
302 if (sprite->transformed) {
303 _drawTransformedSprite(renderer, &renderer->d.oam->tobj[i], y);
304 } else if (!sprite->disable) {
305 _drawSprite(renderer, sprite, y);
306 }
307 }
308 }
309
310 for (i = 0; i < 4; ++i) {
311 if (renderer->sortedBg[i]->enabled) {
312 _drawBackgroundMode0(renderer, renderer->sortedBg[i], y);
313 }
314 }
315}
316
317#define BACKGROUND_DRAW_PIXEL_16 \
318 if (tileData & 0xF) { \
319 renderer->row[outX] = renderer->normalPalette[(tileData & 0xF) | (mapData.palette << 4)]; \
320 } \
321 tileData >>= 4;
322
323#define BACKGROUND_TEXT_SELECT_CHARACTER \
324 localX = tileX * 8 + inX; \
325 xBase = localX & 0xF8; \
326 if (background->size & 1) { \
327 xBase += (localX & 0x100) << 5; \
328 } \
329 screenBase = (background->screenBase >> 1) + (xBase >> 3) + (yBase << 2); \
330 mapData.packed = renderer->d.vram[screenBase]; \
331 if (!mapData.vflip) { \
332 localY = inY & 0x7; \
333 } else { \
334 localY = 7 - (inY & 0x7); \
335 } \
336
337static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
338 int start = renderer->start;
339 int end = renderer->end;
340 int inX = start + background->x;
341 int inY = y + background->y;
342 union GBATextMapData mapData;
343
344 unsigned yBase = inY & 0xF8;
345 if (background->size & 2) {
346 yBase += inY & 0x100;
347 } else if (background->size == 3) {
348 yBase += (inY & 0x100) << 1;
349 }
350
351 int localX;
352 int localY;
353
354 unsigned xBase;
355
356 uint32_t screenBase;
357 uint32_t charBase;
358
359 int outX = 0;
360 int tileX = 0;
361 if (inX & 0x7) {
362 int end = 0x8 - (inX & 0x7);
363 uint32_t tileData;
364 BACKGROUND_TEXT_SELECT_CHARACTER;
365 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
366 tileData = ((uint32_t*)renderer->d.vram)[charBase];
367 tileData >>= 4 * (inX & 0x7);
368 for (outX = 0; outX < end; ++outX) {
369 BACKGROUND_DRAW_PIXEL_16;
370 }
371
372 tileX = 30;
373 BACKGROUND_TEXT_SELECT_CHARACTER;
374 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
375 tileData = ((uint32_t*)renderer->d.vram)[charBase];
376 for (outX = VIDEO_HORIZONTAL_PIXELS - 8 + end; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
377 BACKGROUND_DRAW_PIXEL_16;
378 }
379
380 tileX = 1;
381 outX = end;
382 }
383
384 for (tileX; tileX < 30; ++tileX) {
385 BACKGROUND_TEXT_SELECT_CHARACTER;
386 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
387 uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase];
388 if (tileData) {
389 BACKGROUND_DRAW_PIXEL_16;
390 ++outX;
391 BACKGROUND_DRAW_PIXEL_16;
392 ++outX;
393 BACKGROUND_DRAW_PIXEL_16;
394 ++outX;
395 BACKGROUND_DRAW_PIXEL_16;
396 ++outX;
397 BACKGROUND_DRAW_PIXEL_16;
398 ++outX;
399 BACKGROUND_DRAW_PIXEL_16;
400 ++outX;
401 BACKGROUND_DRAW_PIXEL_16;
402 ++outX;
403 BACKGROUND_DRAW_PIXEL_16;
404 ++outX;
405 } else {
406 outX += 8;
407 }
408 }
409}
410
411static const int _objSizes[32] = {
412 8, 8,
413 16, 16,
414 32, 32,
415 64, 64,
416 16, 8,
417 32, 8,
418 32, 16,
419 64, 32,
420 8, 16,
421 8, 32,
422 16, 32,
423 32, 64,
424 0, 0,
425 0, 0,
426 0, 0,
427 0, 0
428};
429
430static void _drawSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
431 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
432 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
433 int start = renderer->start;
434 int end = renderer->end;
435 if ((y < sprite->y && (sprite->y + height - 256 < 0 || y >= sprite->y + height - 256)) || y >= sprite->y + height) {
436 return;
437 }
438 struct PixelFlags flags = {
439 .priority = sprite->priority,
440 .isSprite = 1,
441 .target1 = (renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT,
442 .target2 = renderer->target2Obj
443 };
444 int x = sprite->x;
445 int inY = y - sprite->y;
446 if (sprite->y + height - 256 >= 0) {
447 inY += 256;
448 }
449 if (sprite->vflip) {
450 inY = height - inY - 1;
451 }
452 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
453 unsigned yBase = (inY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (inY & 0x7) * 4;
454 for (int outX = x >= start ? x : start; outX < x + width && outX < end; ++outX) {
455 int inX = outX - x;
456 if (sprite->hflip) {
457 inX = width - inX - 1;
458 }
459 if (renderer->flags[outX].isSprite) {
460 continue;
461 }
462 unsigned xBase = (inX & ~0x7) * 4 + ((inX >> 1) & 2);
463 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1];
464 tileData = (tileData >> ((inX & 3) << 2)) & 0xF;
465 if (tileData) {
466 if (!renderer->target1Obj) {
467 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData | (sprite->palette << 4)];
468 } else {
469 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData | (sprite->palette << 4)];
470 }
471 renderer->flags[outX] = flags;
472 }
473 }
474}
475
476static void _drawTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
477 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
478 int totalWidth = width << sprite->doublesize;
479 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
480 int totalHeight = height << sprite->doublesize;
481 int start = renderer->start;
482 int end = renderer->end;
483 if ((y < sprite->y && (sprite->y + totalHeight - 256 < 0 || y >= sprite->y + totalHeight - 256)) || y >= sprite->y + totalHeight) {
484 return;
485 }
486 struct PixelFlags flags = {
487 .priority = sprite->priority,
488 .isSprite = 1,
489 .target1 = (renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT,
490 .target2 = renderer->target2Obj
491 };
492 int x = sprite->x;
493 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
494 struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
495 for (int outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) {
496 if (renderer->flags[outX].isSprite) {
497 continue;
498 }
499 int inY = y - sprite->y;
500 int inX = outX - x;
501 int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1);
502 int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1);
503
504 if (localX < 0 || localX >= width || localY < 0 || localY >= height) {
505 continue;
506 }
507
508 unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
509 unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
510 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1];
511 tileData = (tileData >> ((localX & 3) << 2)) & 0xF;
512 if (tileData) {
513 if (!renderer->target1Obj) {
514 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData | (sprite->palette << 4)];
515 } else {
516 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData | (sprite->palette << 4)];
517 }
518 renderer->flags[outX] = flags;
519 }
520 }
521}
522
523static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
524 if (renderer->blendEffect == BLEND_BRIGHTEN) {
525 for (int i = 0; i < 512; ++i) {
526 renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
527 }
528 } else if (renderer->blendEffect == BLEND_DARKEN) {
529 for (int i = 0; i < 512; ++i) {
530 renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
531 }
532 } else {
533 for (int i = 0; i < 512; ++i) {
534 renderer->variantPalette[i] = renderer->normalPalette[i];
535 }
536 }
537}
538
539static inline uint32_t _brighten(uint32_t color, int y) {
540 uint32_t c = 0;
541 uint32_t a;
542 a = color & 0xF8;
543 c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
544
545 a = color & 0xF800;
546 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
547
548 a = color & 0xF80000;
549 c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
550 return c;
551}
552
553static inline uint32_t _darken(uint32_t color, int y) {
554 uint32_t c = 0;
555 uint32_t a;
556 a = color & 0xF8;
557 c |= (a - (a * y) / 16) & 0xF8;
558
559 a = color & 0xF800;
560 c |= (a - (a * y) / 16) & 0xF800;
561
562 a = color & 0xF80000;
563 c |= (a - (a * y) / 16) & 0xF80000;
564 return c;
565}
566
567static uint32_t _mix(int weightA, uint32_t colorA, int weightB, uint32_t colorB) {
568 uint32_t c = 0;
569 uint32_t a, b;
570 a = colorA & 0xF8;
571 b = colorB & 0xF8;
572 c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
573 if (c & 0x00000100) {
574 c = 0x000000F8;
575 }
576
577 a = colorA & 0xF800;
578 b = colorB & 0xF800;
579 c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
580 if (c & 0x00010000) {
581 c = (c & 0x000000F8) | 0x0000F800;
582 }
583
584 a = colorA & 0xF80000;
585 b = colorB & 0xF80000;
586 c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
587 if (c & 0x01000000) {
588 c = (c & 0x0000F8F8) | 0x00F80000;
589 }
590 return c;
591}
592
593static void _sortBackgrounds(struct GBAVideoSoftwareRenderer* renderer) {
594 qsort(renderer->sortedBg, 4, sizeof(struct GBAVideoSoftwareBackground*), _backgroundComparator);
595}
596
597static int _backgroundComparator(const void* a, const void* b) {
598 const struct GBAVideoSoftwareBackground* bgA = *(const struct GBAVideoSoftwareBackground**) a;
599 const struct GBAVideoSoftwareBackground* bgB = *(const struct GBAVideoSoftwareBackground**) b;
600 if (bgA->priority != bgB->priority) {
601 return bgA->priority - bgB->priority;
602 } else {
603 return bgA->index - bgB->index;
604 }
605}