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