src/gba/renderers/software-obj.c (view raw)
1/* Copyright (c) 2013-2015 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "gba/renderers/software-private.h"
7
8#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
9 SPRITE_YBASE_ ## DEPTH(inY); \
10 unsigned tileData; \
11 for (; outX < condition; ++outX, inX += xOffset) { \
12 renderer->spriteCyclesRemaining -= 1; \
13 SPRITE_XBASE_ ## DEPTH(inX); \
14 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
15 }
16
17#define SPRITE_MOSAIC_LOOP(DEPTH, TYPE) \
18 SPRITE_YBASE_ ## DEPTH(inY); \
19 unsigned tileData; \
20 for (; outX < condition; ++outX, inX += xOffset) { \
21 int localX = inX - xOffset * (outX % mosaicH); \
22 if (localX < 0) { \
23 localX = 0; \
24 } else if (localX > width - 1) {\
25 localX = width - 1; \
26 } \
27 SPRITE_XBASE_ ## DEPTH(localX); \
28 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
29 }
30
31#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
32 unsigned tileData; \
33 unsigned widthMask = ~(width - 1); \
34 unsigned heightMask = ~(height - 1); \
35 for (; outX < condition; ++outX, ++inX) { \
36 renderer->spriteCyclesRemaining -= 2; \
37 xAccum += mat.a; \
38 yAccum += mat.c; \
39 int localX = xAccum >> 8; \
40 int localY = yAccum >> 8; \
41 \
42 if (localX & widthMask || localY & heightMask) { \
43 break; \
44 } \
45 \
46 SPRITE_YBASE_ ## DEPTH(localY); \
47 SPRITE_XBASE_ ## DEPTH(localX); \
48 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
49 }
50
51#define SPRITE_TRANSFORMED_MOSAIC_LOOP(DEPTH, TYPE) \
52 unsigned tileData; \
53 unsigned widthMask = ~(width - 1); \
54 unsigned heightMask = ~(height - 1); \
55 int localX = xAccum >> 8; \
56 int localY = yAccum >> 8; \
57 for (; outX < condition; ++outX, ++inX) { \
58 renderer->spriteCyclesRemaining -= 2; \
59 xAccum += mat.a; \
60 yAccum += mat.c; \
61 \
62 if (outX % mosaicH == 0) { \
63 localX = xAccum >> 8; \
64 localY = yAccum >> 8; \
65 } \
66 \
67 if (localX & widthMask || localY & heightMask) { \
68 continue; \
69 } \
70 \
71 SPRITE_YBASE_ ## DEPTH(localY); \
72 SPRITE_XBASE_ ## DEPTH(localX); \
73 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
74 }
75
76#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
77#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * stride + (localY & 0x7) * 4;
78
79#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
80 uint32_t spriteBase = ((yBase + charBase + xBase) & 0x3FFFE); \
81 uint16_t* vramBase = renderer->d.vramOBJ[spriteBase >> VRAM_BLOCK_OFFSET]; \
82 if (UNLIKELY(!vramBase)) { \
83 return 0; \
84 } \
85 LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \
86 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
87 current = renderer->spriteLayer[outX]; \
88 if ((current & FLAG_ORDER_MASK) > flags) { \
89 if (tileData) { \
90 renderer->spriteLayer[outX] = palette[tileData] | flags; \
91 } else if (current != FLAG_UNWRITTEN) { \
92 renderer->spriteLayer[outX] = (current & ~FLAG_ORDER_MASK) | GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY; \
93 } \
94 }
95
96#define SPRITE_DRAW_PIXEL_16_NORMAL_OBJWIN(localX) \
97 uint32_t spriteBase = ((yBase + charBase + xBase) & 0x3FFFE); \
98 uint16_t* vramBase = renderer->d.vramOBJ[spriteBase >> VRAM_BLOCK_OFFSET]; \
99 if (UNLIKELY(!vramBase)) { \
100 return 0; \
101 } \
102 LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \
103 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
104 current = renderer->spriteLayer[outX]; \
105 if ((current & FLAG_ORDER_MASK) > flags) { \
106 if (tileData) { \
107 unsigned color = (renderer->row[outX] & FLAG_OBJWIN) ? objwinPalette[tileData] : palette[tileData]; \
108 renderer->spriteLayer[outX] = color | flags; \
109 } else if (current != FLAG_UNWRITTEN) { \
110 renderer->spriteLayer[outX] = (current & ~FLAG_ORDER_MASK) | GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY; \
111 } \
112 }
113
114#define SPRITE_DRAW_PIXEL_16_OBJWIN(localX) \
115 uint32_t spriteBase = ((yBase + charBase + xBase) & 0x3FFFE); \
116 uint16_t* vramBase = renderer->d.vramOBJ[spriteBase >> VRAM_BLOCK_OFFSET]; \
117 if (UNLIKELY(!vramBase)) { \
118 return 0; \
119 } \
120 LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \
121 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
122 if (tileData) { \
123 renderer->row[outX] |= FLAG_OBJWIN; \
124 }
125
126#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
127#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * stride + (localY & 0x7) * 8;
128
129#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
130 uint32_t spriteBase = ((yBase + charBase + xBase) & 0x3FFFE); \
131 uint16_t* vramBase = renderer->d.vramOBJ[spriteBase >> VRAM_BLOCK_OFFSET]; \
132 if (UNLIKELY(!vramBase)) { \
133 return 0; \
134 } \
135 LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \
136 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
137 current = renderer->spriteLayer[outX]; \
138 if ((current & FLAG_ORDER_MASK) > flags) { \
139 if (tileData) { \
140 renderer->spriteLayer[outX] = palette[tileData] | flags; \
141 } else if (current != FLAG_UNWRITTEN) { \
142 renderer->spriteLayer[outX] = (current & ~FLAG_ORDER_MASK) | GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY; \
143 } \
144 }
145
146#define SPRITE_DRAW_PIXEL_256_NORMAL_OBJWIN(localX) \
147 uint32_t spriteBase = ((yBase + charBase + xBase) & 0x3FFFE); \
148 uint16_t* vramBase = renderer->d.vramOBJ[spriteBase >> VRAM_BLOCK_OFFSET]; \
149 if (UNLIKELY(!vramBase)) { \
150 return 0; \
151 } \
152 LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \
153 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
154 current = renderer->spriteLayer[outX]; \
155 if ((current & FLAG_ORDER_MASK) > flags) { \
156 if (tileData) { \
157 unsigned color = (renderer->row[outX] & FLAG_OBJWIN) ? objwinPalette[tileData] : palette[tileData]; \
158 renderer->spriteLayer[outX] = color | flags; \
159 } else if (current != FLAG_UNWRITTEN) { \
160 renderer->spriteLayer[outX] = (current & ~FLAG_ORDER_MASK) | GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY; \
161 } \
162 }
163
164#define SPRITE_DRAW_PIXEL_256_OBJWIN(localX) \
165 uint32_t spriteBase = ((yBase + charBase + xBase) & 0x3FFFE); \
166 uint16_t* vramBase = renderer->d.vramOBJ[spriteBase >> VRAM_BLOCK_OFFSET]; \
167 if (UNLIKELY(!vramBase)) { \
168 return 0; \
169 } \
170 LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \
171 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
172 if (tileData) { \
173 renderer->row[outX] |= FLAG_OBJWIN; \
174 }
175
176#ifndef COLOR_16_BIT
177#define TILE_TO_COLOR(tileData) \
178 unsigned color32; \
179 color32 = 0; \
180 color32 |= (tileData << 3) & 0xF8; \
181 color32 |= (tileData << 6) & 0xF800; \
182 color32 |= (tileData << 9) & 0xF80000; \
183 color32 |= (color32 >> 5) & 0x070707; \
184 color = color32;
185#elif COLOR_5_6_5
186#define TILE_TO_COLOR(tileData) \
187 uint16_t color16 = 0; \
188 color16 |= (tileData & 0x001F) << 11; \
189 color16 |= (tileData & 0x03E0) << 1; \
190 color16 |= (tileData & 0x7C00) >> 10; \
191 color = color16;
192#else
193#define TILE_TO_COLOR(tileData) \
194 color = tileData;
195#endif
196
197#define SPRITE_XBASE_BITMAP(localX) unsigned xBase = (localX & (stride - 1)) << 1;
198#define SPRITE_YBASE_BITMAP(localY) unsigned yBase = localY * (stride << 1);
199
200#define SPRITE_DRAW_PIXEL_BITMAP_NORMAL(localX) \
201 uint32_t spriteBase = ((yBase + charBase + xBase) & 0x3FFFE); \
202 uint16_t* vramBase = renderer->d.vramOBJ[spriteBase >> VRAM_BLOCK_OFFSET]; \
203 if (UNLIKELY(!vramBase)) { \
204 return 0; \
205 } \
206 LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \
207 current = renderer->spriteLayer[outX]; \
208 if ((current & FLAG_ORDER_MASK) > flags) { \
209 if (tileData & 0x8000) { \
210 uint32_t color; \
211 TILE_TO_COLOR(tileData); \
212 renderer->spriteLayer[outX] = color | flags; \
213 } else if (current != FLAG_UNWRITTEN) { \
214 renderer->spriteLayer[outX] = (current & ~FLAG_ORDER_MASK) | GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY; \
215 } \
216 }
217
218#define SPRITE_DRAW_PIXEL_BITMAP_NORMAL_OBJWIN(localX) SPRITE_DRAW_PIXEL_BITMAP_NORMAL(localX)
219
220#define SPRITE_DRAW_PIXEL_BITMAP_OBJWIN(localX) \
221 uint32_t spriteBase = ((yBase + charBase + xBase) & 0x3FFFE); \
222 uint16_t* vramBase = renderer->d.vramOBJ[spriteBase >> VRAM_BLOCK_OFFSET]; \
223 if (UNLIKELY(!vramBase)) { \
224 return 0; \
225 } \
226 LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \
227 if (tileData & 0x8000) { \
228 renderer->row[outX] |= FLAG_OBJWIN; \
229 }
230
231int GBAVideoSoftwareRendererPreprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int index, int y) {
232 int width = GBAVideoObjSizes[GBAObjAttributesAGetShape(sprite->a) * 4 + GBAObjAttributesBGetSize(sprite->b)][0];
233 int height = GBAVideoObjSizes[GBAObjAttributesAGetShape(sprite->a) * 4 + GBAObjAttributesBGetSize(sprite->b)][1];
234 int start = renderer->start;
235 int end = renderer->end;
236 uint32_t flags = GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY;
237 flags |= FLAG_TARGET_1 * ((GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT);
238 flags |= FLAG_OBJWIN * (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_OBJWIN);
239 if ((flags & FLAG_OBJWIN) && renderer->currentWindow.priority < renderer->objwin.priority) {
240 return 0;
241 }
242 int32_t x = (uint32_t) GBAObjAttributesBGetX(sprite->b) << 23;
243 x >>= 23;
244 x += renderer->objOffsetX;
245 unsigned charBase = GBAObjAttributesCGetTile(sprite->c);
246 if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_BITMAP && renderer->bitmapStride) {
247 charBase = (charBase & (renderer->bitmapStride - 1)) * 0x10 + (charBase & ~(renderer->bitmapStride - 1)) * 0x80;
248 } else {
249 charBase *= renderer->tileStride;
250 }
251 if (!renderer->d.vramOBJ[charBase >> VRAM_BLOCK_OFFSET]) {
252 return 0;
253 }
254
255 int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt) && GBAWindowControlGetBlendEnable(renderer->objwin.packed) != GBAWindowControlIsBlendEnable(renderer->currentWindow.packed);
256 int variant = renderer->target1Obj &&
257 GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) &&
258 (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
259 if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT || objwinSlowPath) {
260 int target2 = renderer->target2Bd;
261 target2 |= renderer->bg[0].target2;
262 target2 |= renderer->bg[1].target2;
263 target2 |= renderer->bg[2].target2;
264 target2 |= renderer->bg[3].target2;
265 if (target2) {
266 flags |= FLAG_REBLEND;
267 variant = 0;
268 } else {
269 flags &= ~FLAG_TARGET_1;
270 }
271 }
272
273 color_t* palette = &renderer->normalPalette[0x100];
274 if (renderer->d.highlightAmount && renderer->d.highlightOBJ[index]) {
275 palette = &renderer->highlightPalette[0x100];
276 }
277 color_t* objwinPalette = palette;
278
279 if (GBAObjAttributesAIs256Color(sprite->a) && renderer->objExtPalette) {
280 if (!variant) {
281 palette = renderer->objExtPalette;
282 objwinPalette = palette;
283 } else {
284 palette = renderer->objExtVariantPalette;
285 if (GBAWindowControlIsBlendEnable(renderer->objwin.packed)) {
286 objwinPalette = palette;
287 }
288 }
289 } else if (variant) {
290 palette = &renderer->variantPalette[0x100];
291 if (renderer->d.highlightAmount && renderer->d.highlightOBJ[index]) {
292 palette = &renderer->highlightVariantPalette[0x100];
293 }
294 if (GBAWindowControlIsBlendEnable(renderer->objwin.packed)) {
295 objwinPalette = palette;
296 }
297 }
298
299 int inY = y - ((int) GBAObjAttributesAGetY(sprite->a) + renderer->objOffsetY);
300 int stride = GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? (width >> !GBAObjAttributesAIs256Color(sprite->a)) : 0x80;
301 if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_BITMAP && renderer->bitmapStride) {
302 stride = renderer->bitmapStride << 3;
303 }
304
305 uint32_t current;
306 if (GBAObjAttributesAIsTransformed(sprite->a)) {
307 int totalWidth = width << GBAObjAttributesAGetDoubleSize(sprite->a);
308 int totalHeight = height << GBAObjAttributesAGetDoubleSize(sprite->a);
309 struct GBAOAMMatrix mat;
310 LOAD_16(mat.a, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].a);
311 LOAD_16(mat.b, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].b);
312 LOAD_16(mat.c, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].c);
313 LOAD_16(mat.d, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].d);
314
315 if (inY < 0) {
316 inY += 256;
317 }
318 int outX = x >= start ? x : start;
319 int condition = x + totalWidth;
320 int inX = outX - x;
321 if (end < condition) {
322 condition = end;
323 }
324 int mosaicH = 1;
325 if (GBAObjAttributesAIsMosaic(sprite->a)) {
326 mosaicH = GBAMosaicControlGetObjH(renderer->mosaic) + 1;
327 if (condition % mosaicH) {
328 condition += mosaicH - (condition % mosaicH);
329 }
330 }
331
332 int xAccum = mat.a * (inX - 1 - (totalWidth >> 1)) + mat.b * (inY - (totalHeight >> 1)) + (width << 7);
333 int yAccum = mat.c * (inX - 1 - (totalWidth >> 1)) + mat.d * (inY - (totalHeight >> 1)) + (height << 7);
334
335 // Clip off early pixels
336 // TODO: Transform end coordinates too
337 if (mat.a) {
338 if ((xAccum >> 8) < 0) {
339 int32_t diffX = -xAccum - 1;
340 int32_t x = mat.a ? diffX / mat.a : 0;
341 xAccum += mat.a * x;
342 yAccum += mat.c * x;
343 outX += x;
344 inX += x;
345 } else if ((xAccum >> 8) >= width) {
346 int32_t diffX = (width << 8) - xAccum;
347 int32_t x = mat.a ? diffX / mat.a : 0;
348 xAccum += mat.a * x;
349 yAccum += mat.c * x;
350 outX += x;
351 inX += x;
352 }
353 }
354 if (mat.c) {
355 if ((yAccum >> 8) < 0) {
356 int32_t diffY = - yAccum - 1;
357 int32_t y = mat.c ? diffY / mat.c : 0;
358 xAccum += mat.a * y;
359 yAccum += mat.c * y;
360 outX += y;
361 inX += y;
362 } else if ((yAccum >> 8) >= height) {
363 int32_t diffY = (height << 8) - yAccum;
364 int32_t y = mat.c ? diffY / mat.c : 0;
365 xAccum += mat.a * y;
366 yAccum += mat.c * y;
367 outX += y;
368 inX += y;
369 }
370 }
371
372 if (outX < start || outX >= condition) {
373 return 0;
374 }
375 renderer->spriteCyclesRemaining -= 10;
376
377 if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_BITMAP && renderer->bitmapStride) {
378 int alpha = GBAObjAttributesCGetPalette(sprite->c);
379 if (flags & FLAG_OBJWIN) {
380 SPRITE_TRANSFORMED_LOOP(BITMAP, OBJWIN);
381 } else if (objwinSlowPath) {
382 SPRITE_TRANSFORMED_LOOP(BITMAP, NORMAL_OBJWIN);
383 } else {
384 SPRITE_TRANSFORMED_LOOP(BITMAP, NORMAL);
385 }
386 } else if (!GBAObjAttributesAIs256Color(sprite->a)) {
387 palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 4];
388 if (flags & FLAG_OBJWIN) {
389 SPRITE_TRANSFORMED_LOOP(16, OBJWIN);
390 } else if (mosaicH > 1) {
391 if (objwinSlowPath) {
392 objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 4];
393 SPRITE_TRANSFORMED_MOSAIC_LOOP(16, NORMAL_OBJWIN);
394 } else {
395 SPRITE_TRANSFORMED_MOSAIC_LOOP(16, NORMAL);
396 }
397 } else if (objwinSlowPath) {
398 objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 4];
399 SPRITE_TRANSFORMED_LOOP(16, NORMAL_OBJWIN);
400 } else {
401 SPRITE_TRANSFORMED_LOOP(16, NORMAL);
402 }
403 } else if (!renderer->objExtPalette) {
404 if (flags & FLAG_OBJWIN) {
405 SPRITE_TRANSFORMED_LOOP(256, OBJWIN);
406 } else if (objwinSlowPath) {
407 SPRITE_TRANSFORMED_LOOP(256, NORMAL_OBJWIN);
408 } else {
409 SPRITE_TRANSFORMED_LOOP(256, NORMAL);
410 }
411 } else {
412 palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 8];
413 if (flags & FLAG_OBJWIN) {
414 SPRITE_TRANSFORMED_LOOP(256, OBJWIN);
415 } else if (mosaicH > 1) {
416 if (objwinSlowPath) {
417 SPRITE_TRANSFORMED_MOSAIC_LOOP(256, NORMAL_OBJWIN);
418 } else {
419 SPRITE_TRANSFORMED_MOSAIC_LOOP(256, NORMAL);
420 }
421 } else if (objwinSlowPath) {
422 objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 8];
423 SPRITE_TRANSFORMED_LOOP(256, NORMAL_OBJWIN);
424 } else {
425 SPRITE_TRANSFORMED_LOOP(256, NORMAL);
426 }
427 }
428 if (end == renderer->masterEnd && x + totalWidth > renderer->masterEnd) {
429 renderer->spriteCyclesRemaining -= (x + totalWidth - renderer->masterEnd) * 2;
430 }
431 } else {
432 int outX = x >= start ? x : start;
433 int condition = x + width;
434 int mosaicH = 1;
435 if (GBAObjAttributesAIsMosaic(sprite->a)) {
436 mosaicH = GBAMosaicControlGetObjH(renderer->mosaic) + 1;
437 if (condition % mosaicH) {
438 condition += mosaicH - (condition % mosaicH);
439 }
440 }
441 if ((int) GBAObjAttributesAGetY(sprite->a) + height - 256 >= 0) {
442 inY += 256;
443 }
444 if (GBAObjAttributesBIsVFlip(sprite->b)) {
445 inY = height - inY - 1;
446 }
447 if (end < condition) {
448 condition = end;
449 }
450 int inX = outX - x;
451 int xOffset = 1;
452 if (GBAObjAttributesBIsHFlip(sprite->b)) {
453 inX = width - inX - 1;
454 xOffset = -1;
455 }
456 if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_BITMAP && renderer->bitmapStride) {
457 int alpha = GBAObjAttributesCGetPalette(sprite->c);
458 if (flags & FLAG_OBJWIN) {
459 SPRITE_NORMAL_LOOP(BITMAP, OBJWIN);
460 } else if (mosaicH > 1) {
461 if (objwinSlowPath) {
462 SPRITE_MOSAIC_LOOP(BITMAP, NORMAL_OBJWIN);
463 } else {
464 SPRITE_MOSAIC_LOOP(BITMAP, NORMAL);
465 }
466 } else if (objwinSlowPath) {
467 SPRITE_NORMAL_LOOP(BITMAP, NORMAL_OBJWIN);
468 } else {
469 SPRITE_NORMAL_LOOP(BITMAP, NORMAL);
470 }
471 } else if (!GBAObjAttributesAIs256Color(sprite->a)) {
472 palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 4];
473 if (flags & FLAG_OBJWIN) {
474 SPRITE_NORMAL_LOOP(16, OBJWIN);
475 } else if (mosaicH > 1) {
476 if (objwinSlowPath) {
477 objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 4];
478 SPRITE_MOSAIC_LOOP(16, NORMAL_OBJWIN);
479 } else {
480 SPRITE_MOSAIC_LOOP(16, NORMAL);
481 }
482 } else if (objwinSlowPath) {
483 objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 4];
484 SPRITE_NORMAL_LOOP(16, NORMAL_OBJWIN);
485 } else {
486 SPRITE_NORMAL_LOOP(16, NORMAL);
487 }
488 } else if (!renderer->objExtPalette) {
489 if (flags & FLAG_OBJWIN) {
490 SPRITE_NORMAL_LOOP(256, OBJWIN);
491 } else if (mosaicH > 1) {
492 if (objwinSlowPath) {
493 SPRITE_MOSAIC_LOOP(256, NORMAL_OBJWIN);
494 } else {
495 SPRITE_MOSAIC_LOOP(256, NORMAL);
496 }
497 } else if (objwinSlowPath) {
498 SPRITE_NORMAL_LOOP(256, NORMAL_OBJWIN);
499 } else {
500 SPRITE_NORMAL_LOOP(256, NORMAL);
501 }
502 } else {
503 palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 8];
504 if (flags & FLAG_OBJWIN) {
505 SPRITE_NORMAL_LOOP(256, OBJWIN);
506 } else if (mosaicH > 1) {
507 if (objwinSlowPath) {
508 SPRITE_MOSAIC_LOOP(256, NORMAL_OBJWIN);
509 } else {
510 SPRITE_MOSAIC_LOOP(256, NORMAL);
511 }
512 } else if (objwinSlowPath) {
513 objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 8];
514 SPRITE_NORMAL_LOOP(256, NORMAL_OBJWIN);
515 } else {
516 SPRITE_NORMAL_LOOP(256, NORMAL);
517 }
518
519 }
520 if (end = renderer->masterEnd && x + width > renderer->masterEnd) {
521 renderer->spriteCyclesRemaining -= x + width - renderer->masterEnd;
522 }
523 }
524 return 1;
525}
526
527void GBAVideoSoftwareRendererPostprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority) {
528 int x;
529 uint32_t flags = FLAG_TARGET_2 * renderer->target2Obj;
530
531 int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt);
532 bool objwinDisable = false;
533 bool objwinOnly = false;
534 if (objwinSlowPath) {
535 objwinDisable = !GBAWindowControlIsObjEnable(renderer->objwin.packed);
536 objwinOnly = !objwinDisable && !GBAWindowControlIsObjEnable(renderer->currentWindow.packed);
537 if (objwinDisable && !GBAWindowControlIsObjEnable(renderer->currentWindow.packed)) {
538 return;
539 }
540
541 if (objwinDisable) {
542 for (x = renderer->start; x < renderer->end; ++x) {
543 uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
544 uint32_t current = renderer->row[x];
545 if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && !(current & FLAG_OBJWIN) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
546 _compositeBlendObjwin(renderer, x, color | flags, current);
547 }
548 }
549 return;
550 } else if (objwinOnly) {
551 for (x = renderer->start; x < renderer->end; ++x) {
552 uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
553 uint32_t current = renderer->row[x];
554 if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && (current & FLAG_OBJWIN) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
555 _compositeBlendObjwin(renderer, x, color | flags, current);
556 }
557 }
558 return;
559 } else {
560 for (x = renderer->start; x < renderer->end; ++x) {
561 uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
562 uint32_t current = renderer->row[x];
563 if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
564 _compositeBlendObjwin(renderer, x, color | flags, current);
565 }
566 }
567 return;
568 }
569 } else if (!GBAWindowControlIsObjEnable(renderer->currentWindow.packed)) {
570 return;
571 }
572 for (x = renderer->start; x < renderer->end; ++x) {
573 uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
574 uint32_t current = renderer->row[x];
575 if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
576 _compositeBlendNoObjwin(renderer, x, color | flags, current);
577 }
578 }
579}