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 if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
198 softwareRenderer->variantPalette[address >> 1] = _brighten(color32, softwareRenderer->bldy);
199 } else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
200 softwareRenderer->variantPalette[address >> 1] = _darken(color32, softwareRenderer->bldy);
201 }
202}
203
204static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
205 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
206 uint32_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
207 if (softwareRenderer->dispcnt.forcedBlank) {
208 for (int x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
209 row[x] = GBA_COLOR_WHITE;
210 }
211 return;
212 } else {
213 uint32_t backdrop;
214 if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA) {
215 backdrop = softwareRenderer->normalPalette[0];
216 } else {
217 backdrop = softwareRenderer->variantPalette[0];
218 }
219 for (int x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
220 row[x] = backdrop;
221 }
222 }
223
224 memset(softwareRenderer->flags, 0, sizeof(softwareRenderer->flags));
225 memset(softwareRenderer->spriteLayer, 0, sizeof(softwareRenderer->spriteLayer));
226 softwareRenderer->row = row;
227
228 softwareRenderer->start = 0;
229 softwareRenderer->end = VIDEO_HORIZONTAL_PIXELS;
230 _drawScanline(softwareRenderer, y);
231}
232
233static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
234 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
235
236 pthread_mutex_lock(&softwareRenderer->mutex);
237 renderer->framesPending++;
238 pthread_cond_broadcast(&softwareRenderer->upCond);
239 if (!renderer->turbo) {
240 pthread_cond_wait(&softwareRenderer->downCond, &softwareRenderer->mutex);
241 }
242 pthread_mutex_unlock(&softwareRenderer->mutex);
243}
244
245static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) {
246 renderer->bg[0].enabled = renderer->dispcnt.bg0Enable;
247 renderer->bg[1].enabled = renderer->dispcnt.bg1Enable;
248 renderer->bg[2].enabled = renderer->dispcnt.bg2Enable;
249 renderer->bg[3].enabled = renderer->dispcnt.bg3Enable;
250}
251
252static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value) {
253 (void)(renderer);
254 union GBARegisterBGCNT reg = { .packed = value };
255 bg->priority = reg.priority;
256 bg->charBase = reg.charBase << 14;
257 bg->mosaic = reg.mosaic;
258 bg->multipalette = reg.multipalette;
259 bg->screenBase = reg.screenBase << 11;
260 bg->overflow = reg.overflow;
261 bg->size = reg.size;
262}
263
264static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value) {
265 union {
266 struct {
267 unsigned target1Bg0 : 1;
268 unsigned target1Bg1 : 1;
269 unsigned target1Bg2 : 1;
270 unsigned target1Bg3 : 1;
271 unsigned target1Obj : 1;
272 unsigned target1Bd : 1;
273 enum BlendEffect effect : 2;
274 unsigned target2Bg0 : 1;
275 unsigned target2Bg1 : 1;
276 unsigned target2Bg2 : 1;
277 unsigned target2Bg3 : 1;
278 unsigned target2Obj : 1;
279 unsigned target2Bd : 1;
280 };
281 uint16_t packed;
282 } bldcnt = { .packed = value };
283
284 enum BlendEffect oldEffect = renderer->blendEffect;
285
286 renderer->bg[0].target1 = bldcnt.target1Bg0;
287 renderer->bg[1].target1 = bldcnt.target1Bg1;
288 renderer->bg[2].target1 = bldcnt.target1Bg2;
289 renderer->bg[3].target1 = bldcnt.target1Bg3;
290 renderer->bg[0].target2 = bldcnt.target2Bg0;
291 renderer->bg[1].target2 = bldcnt.target2Bg1;
292 renderer->bg[2].target2 = bldcnt.target2Bg2;
293 renderer->bg[3].target2 = bldcnt.target2Bg3;
294
295 renderer->blendEffect = bldcnt.effect;
296 renderer->target1Obj = bldcnt.target1Obj;
297 renderer->target1Bd = bldcnt.target1Bd;
298 renderer->target2Obj = bldcnt.target2Obj;
299 renderer->target2Bd = bldcnt.target2Bd;
300
301 if (oldEffect != renderer->blendEffect) {
302 _updatePalettes(renderer);
303 }
304}
305
306static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
307 uint32_t* row = renderer->row;
308
309 int i;
310 if (renderer->dispcnt.objEnable) {
311 for (i = 0; i < 128; ++i) {
312 struct GBAObj* sprite = &renderer->d.oam->obj[i];
313 if (sprite->transformed) {
314 _drawTransformedSprite(renderer, &renderer->d.oam->tobj[i], y);
315 } else if (!sprite->disable) {
316 _drawSprite(renderer, sprite, y);
317 }
318 }
319 }
320
321 int priority;
322 for (priority = 0; priority < 4; ++priority) {
323 for (i = 0; i < 4; ++i) {
324 if (renderer->bg[i].enabled && renderer->bg[i].priority == priority) {
325 _drawBackgroundMode0(renderer, &renderer->bg[i], y);
326 }
327 }
328 }
329}
330
331static void _composite(struct GBAVideoSoftwareRenderer* renderer, int offset, uint32_t color, struct PixelFlags flags) {
332 struct PixelFlags currentFlags = renderer->flags[offset];
333 if (currentFlags.isSprite && flags.priority >= currentFlags.priority) {
334 if (currentFlags.target1) {
335 if (currentFlags.written && currentFlags.target2) {
336 renderer->row[offset] = _mix(renderer->blda, renderer->row[offset], renderer->bldb, renderer->spriteLayer[offset]);
337 } else if (flags.target2) {
338 renderer->row[offset] = _mix(renderer->bldb, color, renderer->blda, renderer->spriteLayer[offset]);
339 }
340 } else if (!currentFlags.written) {
341 renderer->row[offset] = renderer->spriteLayer[offset];
342 }
343 renderer->flags[offset].finalized = 1;
344 return;
345 }
346 if (renderer->blendEffect != BLEND_ALPHA) {
347 renderer->row[offset] = color;
348 renderer->flags[offset].finalized = 1;
349 } else if (renderer->blendEffect == BLEND_ALPHA) {
350 if (currentFlags.written) {
351 if (currentFlags.target1 && flags.target2) {
352 renderer->row[offset] = _mix(renderer->bldb, color, renderer->blda, renderer->row[offset]);
353 }
354 renderer->flags[offset].finalized = 1;
355 } else {
356 renderer->row[offset] = color;
357 renderer->flags[offset].target1 = flags.target1;
358 }
359 }
360 renderer->flags[offset].written = 1;
361}
362
363#define BACKGROUND_DRAW_PIXEL_16 \
364 if (tileData & 0xF && !renderer->flags[outX].finalized) { \
365 _composite(renderer, outX, renderer->normalPalette[tileData & 0xF | (mapData.palette << 4)], flags); \
366 } \
367 tileData >>= 4;
368
369#define BACKGROUND_DRAW_PIXEL_256 \
370 if (tileData & 0xFF && !renderer->flags[outX].finalized) { \
371 _composite(renderer, outX, renderer->normalPalette[tileData & 0xFF], flags); \
372 } \
373 tileData >>= 8;
374
375#define BACKGROUND_TEXT_SELECT_CHARACTER \
376 localX = tileX * 8 + inX; \
377 xBase = localX & 0xF8; \
378 if (background->size & 1) { \
379 xBase += (localX & 0x100) << 5; \
380 } \
381 screenBase = (background->screenBase >> 1) + (xBase >> 3) + (yBase << 2); \
382 mapData.packed = renderer->d.vram[screenBase]; \
383 if (!mapData.vflip) { \
384 localY = inY & 0x7; \
385 } else { \
386 localY = 7 - (inY & 0x7); \
387 } \
388
389static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
390 int start = renderer->start;
391 int end = renderer->end;
392 int inX = start + background->x;
393 int inY = y + background->y;
394 union GBATextMapData mapData;
395
396 unsigned yBase = inY & 0xF8;
397 if (background->size & 2) {
398 yBase += inY & 0x100;
399 } else if (background->size == 3) {
400 yBase += (inY & 0x100) << 1;
401 }
402
403 int localX;
404 int localY;
405
406 unsigned xBase;
407
408 struct PixelFlags flags = {
409 .target1 = background->target1 && renderer->blendEffect == BLEND_ALPHA,
410 .target2 = background->target2,
411 .priority = background->priority
412 };
413
414 uint32_t screenBase;
415 uint32_t charBase;
416
417 int outX = 0;
418 int tileX = 0;
419 if (inX & 0x7) {
420 int end = 0x8 - (inX & 0x7);
421 uint32_t tileData;
422 BACKGROUND_TEXT_SELECT_CHARACTER;
423 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
424 tileData = ((uint32_t*)renderer->d.vram)[charBase];
425 tileData >>= 4 * (inX & 0x7);
426 for (outX = 0; outX < end; ++outX) {
427 BACKGROUND_DRAW_PIXEL_16;
428 }
429
430 tileX = 30;
431 BACKGROUND_TEXT_SELECT_CHARACTER;
432 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
433 tileData = ((uint32_t*)renderer->d.vram)[charBase];
434 for (outX = VIDEO_HORIZONTAL_PIXELS - 8 + end; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
435 BACKGROUND_DRAW_PIXEL_16;
436 }
437
438 tileX = 1;
439 outX = end;
440 }
441
442 if (!background->multipalette) {
443 for (tileX; tileX < 30; ++tileX) {
444 BACKGROUND_TEXT_SELECT_CHARACTER;
445 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
446 uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase];
447 if (tileData) {
448 if (!mapData.hflip) {
449 BACKGROUND_DRAW_PIXEL_16;
450 ++outX;
451 BACKGROUND_DRAW_PIXEL_16;
452 ++outX;
453 BACKGROUND_DRAW_PIXEL_16;
454 ++outX;
455 BACKGROUND_DRAW_PIXEL_16;
456 ++outX;
457 BACKGROUND_DRAW_PIXEL_16;
458 ++outX;
459 BACKGROUND_DRAW_PIXEL_16;
460 ++outX;
461 BACKGROUND_DRAW_PIXEL_16;
462 ++outX;
463 BACKGROUND_DRAW_PIXEL_16;
464 ++outX;
465 } else {
466 outX += 7;
467 BACKGROUND_DRAW_PIXEL_16;
468 --outX;
469 BACKGROUND_DRAW_PIXEL_16;
470 --outX;
471 BACKGROUND_DRAW_PIXEL_16;
472 --outX;
473 BACKGROUND_DRAW_PIXEL_16;
474 --outX;
475 BACKGROUND_DRAW_PIXEL_16;
476 --outX;
477 BACKGROUND_DRAW_PIXEL_16;
478 --outX;
479 BACKGROUND_DRAW_PIXEL_16;
480 --outX;
481 BACKGROUND_DRAW_PIXEL_16;
482 outX += 8;
483 }
484 } else {
485 outX += 8;
486 }
487 }
488 } else {
489 for (tileX; tileX < 30; ++tileX) {
490 BACKGROUND_TEXT_SELECT_CHARACTER;
491 charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1);
492 if (!mapData.hflip) {
493 uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase];
494 if (tileData) {
495 BACKGROUND_DRAW_PIXEL_256;
496 ++outX;
497 BACKGROUND_DRAW_PIXEL_256;
498 ++outX;
499 BACKGROUND_DRAW_PIXEL_256;
500 ++outX;
501 BACKGROUND_DRAW_PIXEL_256;
502 ++outX;
503 } else {
504 outX += 4;
505 }
506 tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
507 if (tileData) {
508 BACKGROUND_DRAW_PIXEL_256;
509 ++outX;
510 BACKGROUND_DRAW_PIXEL_256;
511 ++outX;
512 BACKGROUND_DRAW_PIXEL_256;
513 ++outX;
514 BACKGROUND_DRAW_PIXEL_256;
515 ++outX;
516 } else {
517 outX += 4;
518 }
519 } else {
520 uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
521 if (tileData) {
522 outX += 3;
523 BACKGROUND_DRAW_PIXEL_256;
524 --outX;
525 BACKGROUND_DRAW_PIXEL_256;
526 --outX;
527 BACKGROUND_DRAW_PIXEL_256;
528 --outX;
529 BACKGROUND_DRAW_PIXEL_256;
530 outX += 4;
531 } else {
532 outX += 4;
533 }
534 tileData = ((uint32_t*)renderer->d.vram)[charBase];
535 if (tileData) {
536 outX += 3;
537 BACKGROUND_DRAW_PIXEL_256;
538 --outX;
539 BACKGROUND_DRAW_PIXEL_256;
540 --outX;
541 BACKGROUND_DRAW_PIXEL_256;
542 --outX;
543 BACKGROUND_DRAW_PIXEL_256;
544 outX += 4;
545 } else {
546 outX += 4;
547 }
548 }
549 }
550 }
551}
552
553static const int _objSizes[32] = {
554 8, 8,
555 16, 16,
556 32, 32,
557 64, 64,
558 16, 8,
559 32, 8,
560 32, 16,
561 64, 32,
562 8, 16,
563 8, 32,
564 16, 32,
565 32, 64,
566 0, 0,
567 0, 0,
568 0, 0,
569 0, 0
570};
571
572static void _drawSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
573 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
574 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
575 int start = renderer->start;
576 int end = renderer->end;
577 if ((y < sprite->y && (sprite->y + height - 256 < 0 || y >= sprite->y + height - 256)) || y >= sprite->y + height) {
578 return;
579 }
580 struct PixelFlags flags = {
581 .priority = sprite->priority,
582 .isSprite = 1,
583 .target1 = (renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT,
584 .target2 = renderer->target2Obj
585 };
586 int x = sprite->x;
587 int inY = y - sprite->y;
588 if (sprite->y + height - 256 >= 0) {
589 inY += 256;
590 }
591 if (sprite->vflip) {
592 inY = height - inY - 1;
593 }
594 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
595 unsigned yBase = (inY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (inY & 0x7) * 4;
596 for (int outX = x >= start ? x : start; outX < x + width && outX < end; ++outX) {
597 int inX = outX - x;
598 if (sprite->hflip) {
599 inX = width - inX - 1;
600 }
601 if (renderer->flags[outX].isSprite) {
602 continue;
603 }
604 unsigned xBase = (inX & ~0x7) * 4 + ((inX >> 1) & 2);
605 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1];
606 tileData = (tileData >> ((inX & 3) << 2)) & 0xF;
607 if (tileData) {
608 if (renderer->blendEffect == BLEND_NONE || renderer->blendEffect == BLEND_ALPHA || !renderer->target1Obj) {
609 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData | (sprite->palette << 4)];
610 } else {
611 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData | (sprite->palette << 4)];
612 }
613 renderer->flags[outX] = flags;
614 }
615 }
616}
617
618static void _drawTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
619 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
620 int totalWidth = width << sprite->doublesize;
621 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
622 int totalHeight = height << sprite->doublesize;
623 int start = renderer->start;
624 int end = renderer->end;
625 if ((y < sprite->y && (sprite->y + totalHeight - 256 < 0 || y >= sprite->y + totalHeight - 256)) || y >= sprite->y + totalHeight) {
626 return;
627 }
628 struct PixelFlags flags = {
629 .priority = sprite->priority,
630 .isSprite = 1,
631 .target1 = (renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT,
632 .target2 = renderer->target2Obj
633 };
634 int x = sprite->x;
635 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
636 struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
637 for (int outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) {
638 if (renderer->flags[outX].isSprite) {
639 continue;
640 }
641 int inY = y - sprite->y;
642 int inX = outX - x;
643 int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1);
644 int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1);
645
646 if (localX < 0 || localX >= width || localY < 0 || localY >= height) {
647 continue;
648 }
649
650 unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
651 unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
652 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1];
653 tileData = (tileData >> ((localX & 3) << 2)) & 0xF;
654 if (tileData) {
655 if (renderer->blendEffect == BLEND_NONE || renderer->blendEffect == BLEND_ALPHA || !renderer->target1Obj) {
656 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData | (sprite->palette << 4)];
657 } else {
658 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData | (sprite->palette << 4)];
659 }
660 renderer->flags[outX] = flags;
661 }
662 }
663}
664
665static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
666 if (renderer->blendEffect == BLEND_BRIGHTEN) {
667 for (int i = 0; i < 512; ++i) {
668 renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
669 }
670 } else if (renderer->blendEffect == BLEND_DARKEN) {
671 for (int i = 0; i < 512; ++i) {
672 renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
673 }
674 } else {
675 for (int i = 0; i < 512; ++i) {
676 renderer->variantPalette[i] = renderer->normalPalette[i];
677 }
678 }
679}
680
681static inline uint32_t _brighten(uint32_t color, int y) {
682 uint32_t c = 0;
683 uint32_t a;
684 a = color & 0xF8;
685 c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
686
687 a = color & 0xF800;
688 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
689
690 a = color & 0xF80000;
691 c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
692 return c;
693}
694
695static inline uint32_t _darken(uint32_t color, int y) {
696 uint32_t c = 0;
697 uint32_t a;
698 a = color & 0xF8;
699 c |= (a - (a * y) / 16) & 0xF8;
700
701 a = color & 0xF800;
702 c |= (a - (a * y) / 16) & 0xF800;
703
704 a = color & 0xF80000;
705 c |= (a - (a * y) / 16) & 0xF80000;
706 return c;
707}
708
709static uint32_t _mix(int weightA, uint32_t colorA, int weightB, uint32_t colorB) {
710 uint32_t c = 0;
711 uint32_t a, b;
712 a = colorA & 0xF8;
713 b = colorB & 0xF8;
714 c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
715 if (c & 0x00000100) {
716 c = 0x000000F8;
717 }
718
719 a = colorA & 0xF800;
720 b = colorB & 0xF800;
721 c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
722 if (c & 0x00010000) {
723 c = (c & 0x000000F8) | 0x0000F800;
724 }
725
726 a = colorA & 0xF80000;
727 b = colorB & 0xF80000;
728 c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
729 if (c & 0x01000000) {
730 c = (c & 0x0000F8F8) | 0x00F80000;
731 }
732 return c;
733}