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