all repos — mgba @ 02cb9c56c58e2c69846a90a15a8580e9c03439a9

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