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 | FLAG_REBLEND | FLAG_TARGET_1)) | (flags & (FLAG_ORDER_MASK | FLAG_REBLEND | FLAG_TARGET_1)); \
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 | FLAG_REBLEND | FLAG_TARGET_1)) | (flags & (FLAG_ORDER_MASK | FLAG_REBLEND | FLAG_TARGET_1)); \
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 | FLAG_REBLEND | FLAG_TARGET_1)) | (flags & (FLAG_ORDER_MASK | FLAG_REBLEND | FLAG_TARGET_1)); \
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 | FLAG_REBLEND | FLAG_TARGET_1)) | (flags & (FLAG_ORDER_MASK | FLAG_REBLEND | FLAG_TARGET_1)); \
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 || GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT) &&
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 renderer->forceTarget1 = true;
267 flags |= FLAG_REBLEND;
268 variant = 0;
269 } else {
270 flags &= ~FLAG_TARGET_1;
271 }
272 }
273
274 color_t* palette = &renderer->normalPalette[0x100];
275 if (renderer->d.highlightAmount && renderer->d.highlightOBJ[index]) {
276 palette = &renderer->highlightPalette[0x100];
277 }
278 color_t* objwinPalette = palette;
279
280 if (GBAObjAttributesAIs256Color(sprite->a) && renderer->objExtPalette) {
281 if (!variant) {
282 palette = renderer->objExtPalette;
283 objwinPalette = palette;
284 } else {
285 palette = renderer->objExtVariantPalette;
286 if (GBAWindowControlIsBlendEnable(renderer->objwin.packed)) {
287 objwinPalette = palette;
288 }
289 }
290 } else if (variant) {
291 palette = &renderer->variantPalette[0x100];
292 if (renderer->d.highlightAmount && renderer->d.highlightOBJ[index]) {
293 palette = &renderer->highlightVariantPalette[0x100];
294 }
295 if (GBAWindowControlIsBlendEnable(renderer->objwin.packed)) {
296 objwinPalette = palette;
297 }
298 }
299
300 int inY = y - ((int) GBAObjAttributesAGetY(sprite->a) + renderer->objOffsetY);
301 int stride = GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? (width >> !GBAObjAttributesAIs256Color(sprite->a)) : 0x80;
302 if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_BITMAP && renderer->bitmapStride) {
303 stride = renderer->bitmapStride << 3;
304 }
305
306 uint32_t current;
307 if (GBAObjAttributesAIsTransformed(sprite->a)) {
308 int totalWidth = width << GBAObjAttributesAGetDoubleSize(sprite->a);
309 int totalHeight = height << GBAObjAttributesAGetDoubleSize(sprite->a);
310 struct GBAOAMMatrix mat;
311 LOAD_16(mat.a, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].a);
312 LOAD_16(mat.b, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].b);
313 LOAD_16(mat.c, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].c);
314 LOAD_16(mat.d, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].d);
315
316 if (inY < 0) {
317 inY += 256;
318 }
319 int outX = x >= start ? x : start;
320 int condition = x + totalWidth;
321 int inX = outX - x;
322 if (end < condition) {
323 condition = end;
324 }
325 int mosaicH = 1;
326 if (GBAObjAttributesAIsMosaic(sprite->a)) {
327 mosaicH = GBAMosaicControlGetObjH(renderer->mosaic) + 1;
328 if (condition % mosaicH) {
329 condition += mosaicH - (condition % mosaicH);
330 }
331 }
332
333 int xAccum = mat.a * (inX - 1 - (totalWidth >> 1)) + mat.b * (inY - (totalHeight >> 1)) + (width << 7);
334 int yAccum = mat.c * (inX - 1 - (totalWidth >> 1)) + mat.d * (inY - (totalHeight >> 1)) + (height << 7);
335
336 // Clip off early pixels
337 // TODO: Transform end coordinates too
338 if (mat.a) {
339 if ((xAccum >> 8) < 0) {
340 int32_t diffX = -xAccum - 1;
341 int32_t x = mat.a ? diffX / mat.a : 0;
342 xAccum += mat.a * x;
343 yAccum += mat.c * x;
344 outX += x;
345 inX += x;
346 } else if ((xAccum >> 8) >= width) {
347 int32_t diffX = (width << 8) - xAccum;
348 int32_t x = mat.a ? diffX / mat.a : 0;
349 xAccum += mat.a * x;
350 yAccum += mat.c * x;
351 outX += x;
352 inX += x;
353 }
354 }
355 if (mat.c) {
356 if ((yAccum >> 8) < 0) {
357 int32_t diffY = - yAccum - 1;
358 int32_t y = mat.c ? diffY / mat.c : 0;
359 xAccum += mat.a * y;
360 yAccum += mat.c * y;
361 outX += y;
362 inX += y;
363 } else if ((yAccum >> 8) >= height) {
364 int32_t diffY = (height << 8) - yAccum;
365 int32_t y = mat.c ? diffY / mat.c : 0;
366 xAccum += mat.a * y;
367 yAccum += mat.c * y;
368 outX += y;
369 inX += y;
370 }
371 }
372
373 if (outX < start || outX >= condition) {
374 return 0;
375 }
376 renderer->spriteCyclesRemaining -= 10;
377
378 if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_BITMAP && renderer->bitmapStride) {
379 int alpha = GBAObjAttributesCGetPalette(sprite->c);
380 if (flags & FLAG_OBJWIN) {
381 SPRITE_TRANSFORMED_LOOP(BITMAP, OBJWIN);
382 } else if (objwinSlowPath) {
383 SPRITE_TRANSFORMED_LOOP(BITMAP, NORMAL_OBJWIN);
384 } else {
385 SPRITE_TRANSFORMED_LOOP(BITMAP, NORMAL);
386 }
387 } else if (!GBAObjAttributesAIs256Color(sprite->a)) {
388 palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 4];
389 if (flags & FLAG_OBJWIN) {
390 SPRITE_TRANSFORMED_LOOP(16, OBJWIN);
391 } else if (mosaicH > 1) {
392 if (objwinSlowPath) {
393 objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 4];
394 SPRITE_TRANSFORMED_MOSAIC_LOOP(16, NORMAL_OBJWIN);
395 } else {
396 SPRITE_TRANSFORMED_MOSAIC_LOOP(16, NORMAL);
397 }
398 } else if (objwinSlowPath) {
399 objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 4];
400 SPRITE_TRANSFORMED_LOOP(16, NORMAL_OBJWIN);
401 } else {
402 SPRITE_TRANSFORMED_LOOP(16, NORMAL);
403 }
404 } else if (!renderer->objExtPalette) {
405 if (flags & FLAG_OBJWIN) {
406 SPRITE_TRANSFORMED_LOOP(256, OBJWIN);
407 } else if (objwinSlowPath) {
408 SPRITE_TRANSFORMED_LOOP(256, NORMAL_OBJWIN);
409 } else {
410 SPRITE_TRANSFORMED_LOOP(256, NORMAL);
411 }
412 } else {
413 palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 8];
414 if (flags & FLAG_OBJWIN) {
415 SPRITE_TRANSFORMED_LOOP(256, OBJWIN);
416 } else if (mosaicH > 1) {
417 if (objwinSlowPath) {
418 SPRITE_TRANSFORMED_MOSAIC_LOOP(256, NORMAL_OBJWIN);
419 } else {
420 SPRITE_TRANSFORMED_MOSAIC_LOOP(256, NORMAL);
421 }
422 } else if (objwinSlowPath) {
423 objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 8];
424 SPRITE_TRANSFORMED_LOOP(256, NORMAL_OBJWIN);
425 } else {
426 SPRITE_TRANSFORMED_LOOP(256, NORMAL);
427 }
428 }
429 if (end == renderer->masterEnd && x + totalWidth > renderer->masterEnd) {
430 renderer->spriteCyclesRemaining -= (x + totalWidth - renderer->masterEnd) * 2;
431 }
432 } else {
433 int outX = x >= start ? x : start;
434 int condition = x + width;
435 int mosaicH = 1;
436 if (GBAObjAttributesAIsMosaic(sprite->a)) {
437 mosaicH = GBAMosaicControlGetObjH(renderer->mosaic) + 1;
438 if (condition % mosaicH) {
439 condition += mosaicH - (condition % mosaicH);
440 }
441 }
442 if ((int) GBAObjAttributesAGetY(sprite->a) + height - 256 >= 0) {
443 inY += 256;
444 }
445 if (GBAObjAttributesBIsVFlip(sprite->b)) {
446 inY = height - inY - 1;
447 }
448 if (end < condition) {
449 condition = end;
450 }
451 int inX = outX - x;
452 int xOffset = 1;
453 if (GBAObjAttributesBIsHFlip(sprite->b)) {
454 inX = width - inX - 1;
455 xOffset = -1;
456 }
457 if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_BITMAP && renderer->bitmapStride) {
458 int alpha = GBAObjAttributesCGetPalette(sprite->c);
459 if (flags & FLAG_OBJWIN) {
460 SPRITE_NORMAL_LOOP(BITMAP, OBJWIN);
461 } else if (mosaicH > 1) {
462 if (objwinSlowPath) {
463 SPRITE_MOSAIC_LOOP(BITMAP, NORMAL_OBJWIN);
464 } else {
465 SPRITE_MOSAIC_LOOP(BITMAP, NORMAL);
466 }
467 } else if (objwinSlowPath) {
468 SPRITE_NORMAL_LOOP(BITMAP, NORMAL_OBJWIN);
469 } else {
470 SPRITE_NORMAL_LOOP(BITMAP, NORMAL);
471 }
472 } else if (!GBAObjAttributesAIs256Color(sprite->a)) {
473 palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 4];
474 if (flags & FLAG_OBJWIN) {
475 SPRITE_NORMAL_LOOP(16, OBJWIN);
476 } else if (mosaicH > 1) {
477 if (objwinSlowPath) {
478 objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 4];
479 SPRITE_MOSAIC_LOOP(16, NORMAL_OBJWIN);
480 } else {
481 SPRITE_MOSAIC_LOOP(16, NORMAL);
482 }
483 } else if (objwinSlowPath) {
484 objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 4];
485 SPRITE_NORMAL_LOOP(16, NORMAL_OBJWIN);
486 } else {
487 SPRITE_NORMAL_LOOP(16, NORMAL);
488 }
489 } else if (!renderer->objExtPalette) {
490 if (flags & FLAG_OBJWIN) {
491 SPRITE_NORMAL_LOOP(256, OBJWIN);
492 } else if (mosaicH > 1) {
493 if (objwinSlowPath) {
494 SPRITE_MOSAIC_LOOP(256, NORMAL_OBJWIN);
495 } else {
496 SPRITE_MOSAIC_LOOP(256, NORMAL);
497 }
498 } else if (objwinSlowPath) {
499 SPRITE_NORMAL_LOOP(256, NORMAL_OBJWIN);
500 } else {
501 SPRITE_NORMAL_LOOP(256, NORMAL);
502 }
503 } else {
504 palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 8];
505 if (flags & FLAG_OBJWIN) {
506 SPRITE_NORMAL_LOOP(256, OBJWIN);
507 } else if (mosaicH > 1) {
508 if (objwinSlowPath) {
509 SPRITE_MOSAIC_LOOP(256, NORMAL_OBJWIN);
510 } else {
511 SPRITE_MOSAIC_LOOP(256, NORMAL);
512 }
513 } else if (objwinSlowPath) {
514 objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 8];
515 SPRITE_NORMAL_LOOP(256, NORMAL_OBJWIN);
516 } else {
517 SPRITE_NORMAL_LOOP(256, NORMAL);
518 }
519
520 }
521 if (end = renderer->masterEnd && x + width > renderer->masterEnd) {
522 renderer->spriteCyclesRemaining -= x + width - renderer->masterEnd;
523 }
524 }
525 return 1;
526}
527
528void GBAVideoSoftwareRendererPostprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority) {
529 int x;
530 uint32_t flags = FLAG_TARGET_2 * renderer->target2Obj;
531
532 int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt);
533 bool objwinDisable = false;
534 bool objwinOnly = false;
535 if (objwinSlowPath) {
536 objwinDisable = !GBAWindowControlIsObjEnable(renderer->objwin.packed);
537 objwinOnly = !objwinDisable && !GBAWindowControlIsObjEnable(renderer->currentWindow.packed);
538 if (objwinDisable && !GBAWindowControlIsObjEnable(renderer->currentWindow.packed)) {
539 return;
540 }
541
542 if (objwinDisable) {
543 for (x = renderer->start; x < renderer->end; ++x) {
544 uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
545 uint32_t current = renderer->row[x];
546 if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && !(current & FLAG_OBJWIN) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
547 _compositeBlendObjwin(renderer, x, color | flags, current);
548 }
549 }
550 return;
551 } else if (objwinOnly) {
552 for (x = renderer->start; x < renderer->end; ++x) {
553 uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
554 uint32_t current = renderer->row[x];
555 if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && (current & FLAG_OBJWIN) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
556 _compositeBlendObjwin(renderer, x, color | flags, current);
557 }
558 }
559 return;
560 } else {
561 for (x = renderer->start; x < renderer->end; ++x) {
562 uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
563 uint32_t current = renderer->row[x];
564 if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
565 _compositeBlendObjwin(renderer, x, color | flags, current);
566 }
567 }
568 return;
569 }
570 } else if (!GBAWindowControlIsObjEnable(renderer->currentWindow.packed)) {
571 return;
572 }
573 for (x = renderer->start; x < renderer->end; ++x) {
574 uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
575 uint32_t current = renderer->row[x];
576 if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
577 _compositeBlendNoObjwin(renderer, x, color | flags, current);
578 }
579 }
580}