all repos — mgba @ 7bb4d9d5ff96205d6ff468de0fd38647126b7f85

mGBA Game Boy Advance Emulator

src/gba/renderers/video-software.c (view raw)

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