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