all repos — mgba @ 7ec435b94c9b982dd875a6b21798ce3c4a41be6a

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