src/ds/renderers/software.c (view raw)
1/* Copyright (c) 2013-2017 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 <mgba/internal/ds/renderers/software.h>
7#include "gba/renderers/software-private.h"
8
9#include <mgba/internal/arm/macros.h>
10#include <mgba/internal/ds/gx.h>
11#include <mgba/internal/ds/io.h>
12
13static void DSVideoSoftwareRendererInit(struct DSVideoRenderer* renderer);
14static void DSVideoSoftwareRendererDeinit(struct DSVideoRenderer* renderer);
15static void DSVideoSoftwareRendererReset(struct DSVideoRenderer* renderer);
16static uint16_t DSVideoSoftwareRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
17static void DSVideoSoftwareRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
18static void DSVideoSoftwareRendererWriteOAM(struct DSVideoRenderer* renderer, uint32_t oam);
19static void DSVideoSoftwareRendererInvalidateExtPal(struct DSVideoRenderer* renderer, bool obj, bool engB, int slot);
20static void DSVideoSoftwareRendererDrawScanline(struct DSVideoRenderer* renderer, int y);
21static void DSVideoSoftwareRendererDrawScanlineDirectly(struct DSVideoRenderer* renderer, int y, color_t* scanline);
22static void DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer);
23static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels);
24static void DSVideoSoftwareRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels);
25
26static void DSVideoSoftwareRendererDrawBackgroundExt0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY);
27static void DSVideoSoftwareRendererDrawBackgroundExt1(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY);
28static void DSVideoSoftwareRendererDrawBackgroundExt2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY);
29
30static bool _regenerateExtPalette(struct DSVideoSoftwareRenderer* renderer, bool obj, bool engB, int slot) {
31 color_t* palette;
32 color_t* variantPalette;
33 struct GBAVideoSoftwareRenderer* softwareRenderer;
34 uint16_t* vram;
35 if (!obj) {
36 if (!engB) {
37 palette = &renderer->extPaletteA[slot * 4096];
38 variantPalette = &renderer->variantPaletteA[slot * 4096];
39 softwareRenderer = &renderer->engA;
40 vram = renderer->d.vramABGExtPal[slot];
41 } else {
42 palette = &renderer->extPaletteB[slot * 4096];
43 variantPalette = &renderer->variantPaletteB[slot * 4096];
44 softwareRenderer = &renderer->engB;
45 vram = renderer->d.vramBBGExtPal[slot];
46 }
47 } else {
48 if (!engB) {
49 palette = renderer->objExtPaletteA;
50 variantPalette = renderer->variantPaletteA;
51 softwareRenderer = &renderer->engA;
52 vram = renderer->d.vramAOBJExtPal;
53 } else {
54 palette = renderer->objExtPaletteB;
55 variantPalette = renderer->variantPaletteB;
56 softwareRenderer = &renderer->engB;
57 vram = renderer->d.vramBOBJExtPal;
58 }
59 }
60 if (!vram) {
61 return false;
62 }
63 int i;
64 for (i = 0; i < 4096; ++i) {
65 uint16_t value = vram[i];
66#ifdef COLOR_16_BIT
67#ifdef COLOR_5_6_5
68 unsigned color = 0;
69 color |= (value & 0x001F) << 11;
70 color |= (value & 0x03E0) << 1;
71 color |= (value & 0x7C00) >> 10;
72#else
73 unsigned color = value;
74#endif
75#else
76 unsigned color = 0;
77 color |= (value << 3) & 0xF8;
78 color |= (value << 6) & 0xF800;
79 color |= (value << 9) & 0xF80000;
80 color |= (color >> 5) & 0x070707;
81#endif
82 palette[i] = color;
83 if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
84 variantPalette[i] = _brighten(color, softwareRenderer->bldy);
85 } else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
86 variantPalette[i] = _darken(color, softwareRenderer->bldy);
87 }
88 }
89 if (obj) {
90 softwareRenderer->objExtPalette = palette;
91 softwareRenderer->objExtVariantPalette = variantPalette;
92 } else {
93 if (slot >= 2) {
94 if (GBARegisterBGCNTIsExtPaletteSlot(softwareRenderer->bg[slot - 2].control)) {
95 softwareRenderer->bg[slot - 2].extPalette = palette;
96 softwareRenderer->bg[slot - 2].variantPalette = variantPalette;
97 }
98 } else if (slot < 2 && !GBARegisterBGCNTIsExtPaletteSlot(softwareRenderer->bg[slot].control) ) {
99 softwareRenderer->bg[slot].extPalette = palette;
100 softwareRenderer->bg[slot].variantPalette = variantPalette;
101 }
102 softwareRenderer->bg[slot].extPalette = palette;
103 softwareRenderer->bg[slot].variantPalette = variantPalette;
104 }
105 return true;
106}
107
108static void _updateCharBase(struct DSVideoSoftwareRenderer* softwareRenderer, bool engB) {
109 struct GBAVideoSoftwareRenderer* eng;
110 if (!engB) {
111 eng = &softwareRenderer->engA;
112 } else {
113 eng = &softwareRenderer->engB;
114 }
115 int i;
116 uint32_t charBase = DSRegisterDISPCNTGetCharBase(softwareRenderer->dispcntA) << 16;
117 uint32_t screenBase = DSRegisterDISPCNTGetScreenBase(softwareRenderer->dispcntA) << 16;
118 for (i = 0; i < 4; ++i) {
119 if (!engB) {
120 uint32_t control = eng->bg[i].control;
121 eng->d.writeVideoRegister(&eng->d, DS9_REG_A_BG0CNT + i * 2, control);
122 eng->bg[i].control = control;
123 }
124
125 eng->bg[i].charBase = GBARegisterBGCNTGetCharBase(eng->bg[i].control) << 14;
126
127 if (!engB) {
128 softwareRenderer->engA.bg[i].charBase += charBase;
129 softwareRenderer->engA.bg[i].screenBase &= ~0x70000;
130 softwareRenderer->engA.bg[i].screenBase |= screenBase;
131 }
132 }
133}
134
135void DSVideoSoftwareRendererCreate(struct DSVideoSoftwareRenderer* renderer) {
136 renderer->d.init = DSVideoSoftwareRendererInit;
137 renderer->d.reset = DSVideoSoftwareRendererReset;
138 renderer->d.deinit = DSVideoSoftwareRendererDeinit;
139 renderer->d.writeVideoRegister = DSVideoSoftwareRendererWriteVideoRegister;
140 renderer->d.writePalette = DSVideoSoftwareRendererWritePalette;
141 renderer->d.writeOAM = DSVideoSoftwareRendererWriteOAM;
142 renderer->d.invalidateExtPal = DSVideoSoftwareRendererInvalidateExtPal;
143 renderer->d.drawScanline = DSVideoSoftwareRendererDrawScanline;
144 renderer->d.drawScanlineDirectly = DSVideoSoftwareRendererDrawScanlineDirectly;
145 renderer->d.finishFrame = DSVideoSoftwareRendererFinishFrame;
146 renderer->d.getPixels = DSVideoSoftwareRendererGetPixels;
147 renderer->d.putPixels = DSVideoSoftwareRendererPutPixels;
148
149 renderer->d.disableABG[0] = false;
150 renderer->d.disableABG[1] = false;
151 renderer->d.disableABG[2] = false;
152 renderer->d.disableABG[3] = false;
153 renderer->d.disableAOBJ = false;
154
155 renderer->d.disableBBG[0] = false;
156 renderer->d.disableBBG[1] = false;
157 renderer->d.disableBBG[2] = false;
158 renderer->d.disableBBG[3] = false;
159 renderer->d.disableBOBJ = false;
160
161 renderer->engA.d.cache = NULL;
162 GBAVideoSoftwareRendererCreate(&renderer->engA);
163 renderer->engA.combinedObjSort = true;
164
165 renderer->engB.d.cache = NULL;
166 GBAVideoSoftwareRendererCreate(&renderer->engB);
167 renderer->engB.combinedObjSort = true;
168}
169
170static void DSVideoSoftwareRendererInit(struct DSVideoRenderer* renderer) {
171 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
172 softwareRenderer->engA.d.palette = &renderer->palette[0];
173 softwareRenderer->engA.d.oam = &renderer->oam->oam[0];
174 softwareRenderer->engA.masterEnd = DS_VIDEO_HORIZONTAL_PIXELS;
175 softwareRenderer->engA.masterHeight = DS_VIDEO_VERTICAL_PIXELS;
176 softwareRenderer->engA.masterScanlines = DS_VIDEO_VERTICAL_TOTAL_PIXELS;
177 softwareRenderer->engA.outputBufferStride = softwareRenderer->outputBufferStride;
178 softwareRenderer->engB.d.palette = &renderer->palette[512];
179 softwareRenderer->engB.d.oam = &renderer->oam->oam[1];
180 softwareRenderer->engB.masterEnd = DS_VIDEO_HORIZONTAL_PIXELS;
181 softwareRenderer->engB.masterHeight = DS_VIDEO_VERTICAL_PIXELS;
182 softwareRenderer->engB.masterScanlines = DS_VIDEO_VERTICAL_TOTAL_PIXELS;
183 softwareRenderer->engB.outputBufferStride = softwareRenderer->outputBufferStride;
184
185 DSVideoSoftwareRendererReset(renderer);
186}
187
188static void DSVideoSoftwareRendererReset(struct DSVideoRenderer* renderer) {
189 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
190 softwareRenderer->engA.d.reset(&softwareRenderer->engA.d);
191 softwareRenderer->engB.d.reset(&softwareRenderer->engB.d);
192 softwareRenderer->powcnt = 0;
193 softwareRenderer->dispcntA = 0;
194 softwareRenderer->dispcntB = 0;
195}
196
197static void DSVideoSoftwareRendererDeinit(struct DSVideoRenderer* renderer) {
198 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
199 softwareRenderer->engA.d.deinit(&softwareRenderer->engA.d);
200 softwareRenderer->engB.d.deinit(&softwareRenderer->engB.d);
201}
202
203static void DSVideoSoftwareRendererUpdateDISPCNT(struct DSVideoSoftwareRenderer* softwareRenderer, bool engB) {
204 uint32_t dispcnt;
205 struct GBAVideoSoftwareRenderer* eng;
206 if (!engB) {
207 dispcnt = softwareRenderer->dispcntA;
208 eng = &softwareRenderer->engA;
209 } else {
210 dispcnt = softwareRenderer->dispcntB;
211 eng = &softwareRenderer->engB;
212 }
213 uint16_t fakeDispcnt = dispcnt & 0xFF87;
214 if (!DSRegisterDISPCNTIsTileObjMapping(dispcnt)) {
215 eng->tileStride = 0x20;
216 } else {
217 eng->tileStride = 0x20 << DSRegisterDISPCNTGetTileBoundary(dispcnt);
218 fakeDispcnt = GBARegisterDISPCNTFillObjCharacterMapping(fakeDispcnt);
219 }
220 eng->bitmapStride = 0x10 << DSRegisterDISPCNTGetBitmapObj2D(dispcnt);
221 eng->d.writeVideoRegister(&eng->d, DS9_REG_A_DISPCNT_LO, fakeDispcnt);
222 eng->dispcnt |= dispcnt & 0xFFFF0000;
223 if (DSRegisterDISPCNTIsBgExtPalette(dispcnt)) {
224 color_t* extPalette;
225 if (!engB) {
226 extPalette = softwareRenderer->extPaletteA;
227 } else {
228 extPalette = softwareRenderer->extPaletteB;
229 }
230 int i;
231 for (i = 0; i < 4; ++i) {
232 int slot = i;
233 if (i < 2 && GBARegisterBGCNTIsExtPaletteSlot(eng->bg[i].control)) {
234 slot += 2;
235 }
236 if (eng->bg[i].extPalette != &extPalette[slot * 4096]) {
237 _regenerateExtPalette(softwareRenderer, false, engB, slot);
238 }
239 }
240 } else {
241 eng->bg[0].extPalette = NULL;
242 eng->bg[1].extPalette = NULL;
243 eng->bg[2].extPalette = NULL;
244 eng->bg[3].extPalette = NULL;
245 }
246 if (DSRegisterDISPCNTIsObjExtPalette(dispcnt)) {
247 if (!engB) {
248 if (softwareRenderer->engA.objExtPalette != softwareRenderer->objExtPaletteA) {
249 _regenerateExtPalette(softwareRenderer, true, engB, 0);
250 }
251 } else {
252 if (softwareRenderer->engB.objExtPalette != softwareRenderer->objExtPaletteB) {
253 _regenerateExtPalette(softwareRenderer, true, engB, 0);
254 }
255 }
256 } else {
257 if (!engB) {
258 softwareRenderer->engA.objExtPalette = NULL;
259 } else {
260 softwareRenderer->engB.objExtPalette = NULL;
261 }
262 }
263 if (!engB) {
264 eng->dispcnt = DSRegisterDISPCNTClear3D(eng->dispcnt);
265 eng->dispcnt |= DSRegisterDISPCNTIs3D(dispcnt);
266 _updateCharBase(softwareRenderer, engB);
267 }
268}
269
270static uint16_t DSVideoSoftwareRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
271 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
272 if (address >= DS9_REG_A_BG0CNT && address <= DS9_REG_A_BLDY) {
273 softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, address, value);
274 } else if (address >= DS9_REG_B_BG0CNT && address <= DS9_REG_B_BLDY) {
275 softwareRenderer->engB.d.writeVideoRegister(&softwareRenderer->engB.d, address & 0xFF, value);
276 } else {
277 mLOG(DS_VIDEO, STUB, "Stub video register write: %04X:%04X", address, value);
278 }
279 switch (address) {
280 case DS9_REG_A_BG0CNT:
281 case DS9_REG_A_BG1CNT:
282 softwareRenderer->engA.bg[(address - DS9_REG_A_BG0CNT) >> 1].control = value;
283 // Fall through
284 case DS9_REG_A_BG2CNT:
285 case DS9_REG_A_BG3CNT:
286 _updateCharBase(softwareRenderer, false);
287 break;
288 case DS9_REG_B_BG0CNT:
289 case DS9_REG_B_BG1CNT:
290 softwareRenderer->engB.bg[(address - DS9_REG_B_BG0CNT) >> 1].control = value;
291 // Fall through
292 case DS9_REG_B_BG2CNT:
293 case DS9_REG_B_BG3CNT:
294 _updateCharBase(softwareRenderer, true);
295 break;
296 case DS9_REG_A_MASTER_BRIGHT:
297 softwareRenderer->engA.masterBright = DSRegisterMASTER_BRIGHTGetMode(value);
298 softwareRenderer->engA.masterBrightY = DSRegisterMASTER_BRIGHTGetY(value);
299 if (softwareRenderer->engA.masterBrightY > 0x10) {
300 softwareRenderer->engA.masterBrightY = 0x10;
301 }
302 break;
303 case DS9_REG_B_MASTER_BRIGHT:
304 softwareRenderer->engB.masterBright = DSRegisterMASTER_BRIGHTGetMode(value);
305 softwareRenderer->engB.masterBrightY = DSRegisterMASTER_BRIGHTGetY(value);
306 if (softwareRenderer->engB.masterBrightY > 0x10) {
307 softwareRenderer->engB.masterBrightY = 0x10;
308 }
309 break;
310 case DS9_REG_A_BLDCNT:
311 case DS9_REG_A_BLDY:
312 // TODO: Optimize
313 _regenerateExtPalette(softwareRenderer, false, false, 0);
314 _regenerateExtPalette(softwareRenderer, false, false, 1);
315 _regenerateExtPalette(softwareRenderer, false, false, 2);
316 _regenerateExtPalette(softwareRenderer, false, false, 3);
317 _regenerateExtPalette(softwareRenderer, true, false, 0);
318 break;
319 case DS9_REG_B_BLDCNT:
320 case DS9_REG_B_BLDY:
321 // TODO: Optimize
322 _regenerateExtPalette(softwareRenderer, false, true, 0);
323 _regenerateExtPalette(softwareRenderer, false, true, 1);
324 _regenerateExtPalette(softwareRenderer, false, true, 2);
325 _regenerateExtPalette(softwareRenderer, false, true, 3);
326 _regenerateExtPalette(softwareRenderer, true, true, 0);
327 break;
328 case DS9_REG_A_DISPCNT_LO:
329 softwareRenderer->dispcntA &= 0xFFFF0000;
330 softwareRenderer->dispcntA |= value;
331 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, false);
332 break;
333 case DS9_REG_A_DISPCNT_HI:
334 softwareRenderer->dispcntA &= 0x0000FFFF;
335 softwareRenderer->dispcntA |= value << 16;
336 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, false);
337 break;
338 case DS9_REG_B_DISPCNT_LO:
339 softwareRenderer->dispcntB &= 0xFFFF0000;
340 softwareRenderer->dispcntB |= value;
341 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, true);
342 break;
343 case DS9_REG_B_DISPCNT_HI:
344 softwareRenderer->dispcntB &= 0x0000FFFF;
345 softwareRenderer->dispcntB |= value << 16;
346 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, true);
347 break;
348 case DS9_REG_POWCNT1:
349 value &= 0x810F;
350 softwareRenderer->powcnt = value;
351 }
352 return value;
353}
354
355static void DSVideoSoftwareRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
356 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
357 if (address < 0x400) {
358 softwareRenderer->engA.d.writePalette(&softwareRenderer->engA.d, address & 0x3FF, value);
359 } else {
360 softwareRenderer->engB.d.writePalette(&softwareRenderer->engB.d, address & 0x3FF, value);
361 }
362}
363
364static void DSVideoSoftwareRendererWriteOAM(struct DSVideoRenderer* renderer, uint32_t oam) {
365 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
366 if (oam < 0x200) {
367 softwareRenderer->engA.d.writeOAM(&softwareRenderer->engA.d, oam & 0x1FF);
368 } else {
369 softwareRenderer->engB.d.writeOAM(&softwareRenderer->engB.d, oam & 0x1FF);
370 }
371}
372
373static void DSVideoSoftwareRendererInvalidateExtPal(struct DSVideoRenderer* renderer, bool obj, bool engB, int slot) {
374 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
375 _regenerateExtPalette(softwareRenderer, obj, engB, slot);
376}
377
378static void DSVideoSoftwareRendererDrawGBAScanline(struct GBAVideoRenderer* renderer, struct DSGX* gx, int y) {
379 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
380
381 if (y == DS_VIDEO_VERTICAL_PIXELS - 1) {
382 softwareRenderer->nextY = 0;
383 } else {
384 softwareRenderer->nextY = y + 1;
385 }
386
387 bool dirty = softwareRenderer->scanlineDirty[y >> 5] & (1 << (y & 0x1F));
388 if (memcmp(softwareRenderer->nextIo, softwareRenderer->cache[y].io, sizeof(softwareRenderer->nextIo))) {
389 memcpy(softwareRenderer->cache[y].io, softwareRenderer->nextIo, sizeof(softwareRenderer->nextIo));
390 dirty = true;
391 }
392
393 int x;
394 color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
395 if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) {
396 for (x = 0; x < softwareRenderer->masterEnd; ++x) {
397 row[x] = GBA_COLOR_WHITE;
398 }
399 return;
400 }
401
402 GBAVideoSoftwareRendererPreprocessBuffer(softwareRenderer, y);
403 softwareRenderer->spriteCyclesRemaining = 1530; // TODO: Figure out the actual value; this value is just a fudge factor for now
404 int spriteLayers = GBAVideoSoftwareRendererPreprocessSpriteLayer(softwareRenderer, y);
405 memset(softwareRenderer->alphaA, softwareRenderer->blda, sizeof(softwareRenderer->alphaA));
406 memset(softwareRenderer->alphaB, softwareRenderer->bldb, sizeof(softwareRenderer->alphaB));
407
408 int w;
409 unsigned priority;
410 for (priority = 0; priority < 4; ++priority) {
411 softwareRenderer->end = 0;
412 for (w = 0; w < softwareRenderer->nWindows; ++w) {
413 softwareRenderer->start = softwareRenderer->end;
414 softwareRenderer->end = softwareRenderer->windows[w].endX;
415 softwareRenderer->currentWindow = softwareRenderer->windows[w].control;
416 if (spriteLayers & (1 << priority)) {
417 GBAVideoSoftwareRendererPostprocessSprite(softwareRenderer, priority);
418 }
419 if (TEST_LAYER_ENABLED(0)) {
420 if (DSRegisterDISPCNTIs3D(softwareRenderer->dispcnt) && gx) {
421 const color_t* scanline;
422 gx->renderer->getScanline(gx->renderer, y, &scanline);
423 uint32_t flags = (softwareRenderer->bg[0].priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND | FLAG_TARGET_1 | FLAG_TARGET_2;
424 int x;
425 for (x = softwareRenderer->start; x < softwareRenderer->end; ++x) {
426 color_t color = scanline[x];
427 if (color & 0xFC000000) {
428 if (softwareRenderer->bg[0].target1 && GBAWindowControlIsBlendEnable(softwareRenderer->currentWindow.packed)) {
429 if (softwareRenderer->blendEffect == BLEND_DARKEN) {
430 color = _darken(color, softwareRenderer->bldy) | (color & 0xFF000000);
431 } else if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
432 color = _brighten(color, softwareRenderer->bldy) | (color & 0xFF000000);
433 }
434 }
435 if ((scanline[x] >> 28) != 0xF) {
436 // TODO: More precise values
437 softwareRenderer->alphaA[x] = (color >> 28) + 1;
438 softwareRenderer->alphaB[x] = 0xF - (color >> 28);
439 _compositeBlendNoObjwin(softwareRenderer, x, (color & 0x00FFFFFF) | flags, softwareRenderer->row[x]);
440 } else {
441 if (!(softwareRenderer->row[x] & FLAG_TARGET_1)) {
442 _compositeNoBlendNoObjwin(softwareRenderer, x, (color & 0x00FFFFFF) | flags, softwareRenderer->row[x]);
443 } else {
444 _compositeBlendNoObjwin(softwareRenderer, x, (color & 0x00FFFFFF) | flags, softwareRenderer->row[x]);
445 }
446 softwareRenderer->alphaA[x] = 0x10;
447 softwareRenderer->alphaB[x] = 0;
448 }
449 }
450 }
451 } else {
452 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[0], y);
453 }
454 }
455 if (TEST_LAYER_ENABLED(1)) {
456 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[1], y);
457 }
458 if (TEST_LAYER_ENABLED(2)) {
459 switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
460 case 0:
461 case 1:
462 case 3:
463 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[2], y);
464 break;
465 case 2:
466 case 4:
467 GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[2], y);
468 break;
469 case 5:
470 if (!GBARegisterBGCNTIsExtendedMode1(softwareRenderer->bg[2].control)) {
471 DSVideoSoftwareRendererDrawBackgroundExt0(softwareRenderer, &softwareRenderer->bg[2], y);
472 } else if (!GBARegisterBGCNTIsExtendedMode0(softwareRenderer->bg[2].control)) {
473 DSVideoSoftwareRendererDrawBackgroundExt1(softwareRenderer, &softwareRenderer->bg[2], y);
474 } else {
475 DSVideoSoftwareRendererDrawBackgroundExt2(softwareRenderer, &softwareRenderer->bg[2], y);
476 }
477 break;
478 }
479 }
480 if (TEST_LAYER_ENABLED(3)) {
481 switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
482 case 0:
483 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[3], y);
484 break;
485 case 1:
486 case 2:
487 GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[3], y);
488 break;
489 case 3:
490 case 4:
491 case 5:
492 if (!GBARegisterBGCNTIsExtendedMode1(softwareRenderer->bg[3].control)) {
493 DSVideoSoftwareRendererDrawBackgroundExt0(softwareRenderer, &softwareRenderer->bg[3], y);
494 } else if (!GBARegisterBGCNTIsExtendedMode0(softwareRenderer->bg[3].control)) {
495 DSVideoSoftwareRendererDrawBackgroundExt1(softwareRenderer, &softwareRenderer->bg[3], y);
496 } else {
497 DSVideoSoftwareRendererDrawBackgroundExt2(softwareRenderer, &softwareRenderer->bg[3], y);
498 }
499 break;
500 }
501 }
502 }
503 }
504
505 if (softwareRenderer->bg[0].enabled > 0 && softwareRenderer->bg[0].enabled < 3) {
506 ++softwareRenderer->bg[0].enabled;
507 }
508 if (softwareRenderer->bg[1].enabled > 0 && softwareRenderer->bg[1].enabled < 3) {
509 ++softwareRenderer->bg[1].enabled;
510 }
511 if (softwareRenderer->bg[2].enabled > 0 && softwareRenderer->bg[2].enabled < 3) {
512 ++softwareRenderer->bg[2].enabled;
513 }
514 if (softwareRenderer->bg[3].enabled > 0 && softwareRenderer->bg[3].enabled < 3) {
515 ++softwareRenderer->bg[3].enabled;
516 }
517
518 GBAVideoSoftwareRendererPostprocessBuffer(softwareRenderer);
519}
520
521static void _advanceAffine(struct GBAVideoSoftwareRenderer* softwareRenderer) {
522 switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
523 case 2:
524 case 4:
525 case 5:
526 softwareRenderer->bg[2].sx += softwareRenderer->bg[2].dmx;
527 softwareRenderer->bg[2].sy += softwareRenderer->bg[2].dmy;
528 // Fall through
529 case 1:
530 case 3:
531 softwareRenderer->bg[3].sx += softwareRenderer->bg[3].dmx;
532 softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy;
533 break;
534 }
535}
536
537static void _deadvanceAffine(struct GBAVideoSoftwareRenderer* softwareRenderer) {
538 switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
539 case 2:
540 case 4:
541 case 5:
542 softwareRenderer->bg[2].sx -= softwareRenderer->bg[2].dmx;
543 softwareRenderer->bg[2].sy -= softwareRenderer->bg[2].dmy;
544 // Fall through
545 case 1:
546 case 3:
547 softwareRenderer->bg[3].sx -= softwareRenderer->bg[3].dmx;
548 softwareRenderer->bg[3].sy -= softwareRenderer->bg[3].dmy;
549 break;
550 }
551}
552
553static void _drawScanlineA(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
554 memcpy(softwareRenderer->engA.d.vramBG, softwareRenderer->d.vramABG, sizeof(softwareRenderer->engA.d.vramBG));
555 memcpy(softwareRenderer->engA.d.vramOBJ, softwareRenderer->d.vramAOBJ, sizeof(softwareRenderer->engA.d.vramOBJ));
556 color_t* row = &softwareRenderer->engA.outputBuffer[softwareRenderer->outputBufferStride * y];
557
558 int x;
559 switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntA)) {
560 case 0:
561 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
562 row[x] = GBA_COLOR_WHITE;
563 }
564 return;
565 case 1:
566 DSVideoSoftwareRendererDrawGBAScanline(&softwareRenderer->engA.d, softwareRenderer->d.gx, y);
567 break;
568 case 2: {
569 uint16_t* vram = &softwareRenderer->d.vram[0x10000 * DSRegisterDISPCNTGetVRAMBlock(softwareRenderer->dispcntA)];
570 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
571 color_t color;
572 LOAD_16(color, (x + y * DS_VIDEO_HORIZONTAL_PIXELS) * 2, vram);
573#ifndef COLOR_16_BIT
574 unsigned color32 = 0;
575 color32 |= (color << 9) & 0xF80000;
576 color32 |= (color << 3) & 0xF8;
577 color32 |= (color << 6) & 0xF800;
578 color32 |= (color32 >> 5) & 0x070707;
579 color = color32;
580#elif COLOR_5_6_5
581 uint16_t color16 = 0;
582 color16 |= (color & 0x001F) << 11;
583 color16 |= (color & 0x03E0) << 1;
584 color16 |= (color & 0x7C00) >> 10;
585 color = color16;
586#endif
587 softwareRenderer->engA.row[x] = color;
588 }
589 break;
590 }
591 case 3:
592 break;
593 }
594
595 _advanceAffine(&softwareRenderer->engA);
596
597#ifdef COLOR_16_BIT
598#if defined(__ARM_NEON) && !defined(__APPLE__)
599 _to16Bit(row, softwareRenderer->engA.row, DS_VIDEO_HORIZONTAL_PIXELS);
600#else
601 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
602 row[x] = softwareRenderer->engA.row[x];
603 }
604#endif
605#else
606 switch (softwareRenderer->engA.masterBright) {
607 case 0:
608 default:
609 memcpy(row, softwareRenderer->engA.row, softwareRenderer->engA.masterEnd * sizeof(*row));
610 break;
611 case 1:
612 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
613 row[x] = _brighten(softwareRenderer->engA.row[x], softwareRenderer->engA.masterBrightY);
614 }
615 break;
616 case 2:
617 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
618 row[x] = _darken(softwareRenderer->engA.row[x], softwareRenderer->engA.masterBrightY);
619 }
620 break;
621 }
622#endif
623}
624
625static void _drawScanlineB(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
626 memcpy(softwareRenderer->engB.d.vramBG, softwareRenderer->d.vramBBG, sizeof(softwareRenderer->engB.d.vramBG));
627 memcpy(softwareRenderer->engB.d.vramOBJ, softwareRenderer->d.vramBOBJ, sizeof(softwareRenderer->engB.d.vramOBJ));
628 color_t* row = &softwareRenderer->engB.outputBuffer[softwareRenderer->outputBufferStride * y];
629
630 int x;
631 switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntB)) {
632 case 0:
633 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
634 row[x] = GBA_COLOR_WHITE;
635 }
636 return;
637 case 1:
638 DSVideoSoftwareRendererDrawGBAScanline(&softwareRenderer->engB.d, NULL, y);
639 break;
640 }
641
642 _advanceAffine(&softwareRenderer->engB);
643
644#ifdef COLOR_16_BIT
645#if defined(__ARM_NEON) && !defined(__APPLE__)
646 _to16Bit(row, softwareRenderer->engB.row, DS_VIDEO_HORIZONTAL_PIXELS);
647#else
648 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
649 row[x] = softwareRenderer->engB.row[x];
650 }
651#endif
652#else
653 switch (softwareRenderer->engB.masterBright) {
654 case 0:
655 default:
656 memcpy(row, softwareRenderer->engB.row, softwareRenderer->engB.masterEnd * sizeof(*row));
657 break;
658 case 1:
659 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
660 row[x] = _brighten(softwareRenderer->engB.row[x], softwareRenderer->engB.masterBrightY);
661 }
662 break;
663 case 2:
664 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
665 row[x] = _darken(softwareRenderer->engB.row[x], softwareRenderer->engB.masterBrightY);
666 }
667 break;
668 }
669#endif
670}
671
672static void DSVideoSoftwareRendererDrawScanline(struct DSVideoRenderer* renderer, int y) {
673 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
674 if (!DSRegisterPOWCNT1IsSwap(softwareRenderer->powcnt)) {
675 softwareRenderer->engA.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS];
676 softwareRenderer->engB.outputBuffer = softwareRenderer->outputBuffer;
677 } else {
678 softwareRenderer->engA.outputBuffer = softwareRenderer->outputBuffer;
679 softwareRenderer->engB.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS];
680 }
681
682 softwareRenderer->engA.d.disableBG[0] = softwareRenderer->d.disableABG[0];
683 softwareRenderer->engA.d.disableBG[1] = softwareRenderer->d.disableABG[1];
684 softwareRenderer->engA.d.disableBG[2] = softwareRenderer->d.disableABG[2];
685 softwareRenderer->engA.d.disableBG[3] = softwareRenderer->d.disableABG[3];
686 softwareRenderer->engA.d.disableOBJ = softwareRenderer->d.disableAOBJ;
687
688 softwareRenderer->engB.d.disableBG[0] = softwareRenderer->d.disableBBG[0];
689 softwareRenderer->engB.d.disableBG[1] = softwareRenderer->d.disableBBG[1];
690 softwareRenderer->engB.d.disableBG[2] = softwareRenderer->d.disableBBG[2];
691 softwareRenderer->engB.d.disableBG[3] = softwareRenderer->d.disableBBG[3];
692 softwareRenderer->engB.d.disableOBJ = softwareRenderer->d.disableBOBJ;
693
694 _drawScanlineA(softwareRenderer, y);
695 _drawScanlineB(softwareRenderer, y);
696}
697
698static void DSVideoSoftwareRendererDrawScanlineDirectly(struct DSVideoRenderer* renderer, int y, color_t* scanline) {
699 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
700 _deadvanceAffine(&softwareRenderer->engA);
701 DSVideoSoftwareRendererDrawGBAScanline(&softwareRenderer->engA.d, softwareRenderer->d.gx, y);
702 _advanceAffine(&softwareRenderer->engA);
703 memcpy(scanline, softwareRenderer->engA.row, softwareRenderer->engA.masterEnd * sizeof(*scanline));
704}
705
706static void DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer) {
707 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
708 softwareRenderer->engA.d.finishFrame(&softwareRenderer->engA.d);
709 softwareRenderer->engB.d.finishFrame(&softwareRenderer->engB.d);
710}
711
712static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels) {
713 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
714#ifdef COLOR_16_BIT
715#error Not yet supported
716#else
717 *stride = softwareRenderer->outputBufferStride;
718 *pixels = softwareRenderer->outputBuffer;
719#endif
720}
721
722static void DSVideoSoftwareRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels) {
723}
724
725#define EXT_0_COORD_OVERFLOW \
726 localX = x & (sizeAdjusted - 1); \
727 localY = y & (sizeAdjusted - 1); \
728
729#define EXT_0_COORD_NO_OVERFLOW \
730 if ((x | y) & ~(sizeAdjusted - 1)) { \
731 continue; \
732 } \
733 localX = x; \
734 localY = y;
735
736#define EXT_0_EXT_PAL \
737 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 8; \
738 palette = &mainPalette[paletteData];
739
740#define EXT_0_PAL palette = mainPalette;
741
742#define EXT_0_NO_MOSAIC(COORD, PAL) \
743 COORD \
744 uint32_t screenBase = background->screenBase + (localX >> 10) + (((localY >> 6) & 0xFE0) << background->size); \
745 uint16_t* screenBlock = renderer->d.vramBG[screenBase >> VRAM_BLOCK_OFFSET]; \
746 if (UNLIKELY(!screenBlock)) { \
747 continue; \
748 } \
749 LOAD_16(mapData, screenBase & (VRAM_BLOCK_MASK - 1), screenBlock); \
750 PAL; \
751 if (GBA_TEXT_MAP_VFLIP(mapData)) { \
752 localY = 0x7FF - localY; \
753 } \
754 if (GBA_TEXT_MAP_HFLIP(mapData)) { \
755 localX = 0x7FF - localX; \
756 } \
757 uint32_t charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8); \
758 uint16_t* vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
759 pixelData = ((uint8_t*) vram)[charBase & VRAM_BLOCK_MASK];
760
761#define EXT_0_MOSAIC(COORD, PAL) \
762 if (!mosaicWait) { \
763 EXT_0_NO_MOSAIC(COORD, PAL) \
764 mosaicWait = mosaicH; \
765 } else { \
766 --mosaicWait; \
767 }
768
769#define EXT_0_LOOP(MOSAIC, COORD, PAL, BLEND, OBJWIN) \
770 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) { \
771 x += background->dx; \
772 y += background->dy; \
773 \
774 uint32_t current = *pixel; \
775 MOSAIC(COORD, PAL) \
776 if (pixelData) { \
777 COMPOSITE_256_ ## OBJWIN (BLEND, 0); \
778 } \
779 }
780
781#define DRAW_BACKGROUND_EXT_0(BLEND, OBJWIN) \
782 if (background->extPalette) { \
783 if (background->overflow) { \
784 if (mosaicH > 1) { \
785 EXT_0_LOOP(EXT_0_MOSAIC, EXT_0_COORD_OVERFLOW, EXT_0_EXT_PAL, BLEND, OBJWIN); \
786 } else { \
787 EXT_0_LOOP(EXT_0_NO_MOSAIC, EXT_0_COORD_OVERFLOW, EXT_0_EXT_PAL, BLEND, OBJWIN); \
788 } \
789 } else { \
790 if (mosaicH > 1) { \
791 EXT_0_LOOP(EXT_0_MOSAIC, EXT_0_COORD_NO_OVERFLOW, EXT_0_EXT_PAL, BLEND, OBJWIN); \
792 } else { \
793 EXT_0_LOOP(EXT_0_NO_MOSAIC, EXT_0_COORD_NO_OVERFLOW, EXT_0_EXT_PAL, BLEND, OBJWIN); \
794 } \
795 } \
796 } else { \
797 if (background->overflow) { \
798 if (mosaicH > 1) { \
799 EXT_0_LOOP(EXT_0_MOSAIC, EXT_0_COORD_OVERFLOW, EXT_0_PAL, BLEND, OBJWIN); \
800 } else { \
801 EXT_0_LOOP(EXT_0_NO_MOSAIC, EXT_0_COORD_OVERFLOW, EXT_0_PAL, BLEND, OBJWIN); \
802 } \
803 } else { \
804 if (mosaicH > 1) { \
805 EXT_0_LOOP(EXT_0_MOSAIC, EXT_0_COORD_NO_OVERFLOW, EXT_0_PAL, BLEND, OBJWIN); \
806 } else { \
807 EXT_0_LOOP(EXT_0_NO_MOSAIC, EXT_0_COORD_NO_OVERFLOW, EXT_0_PAL, BLEND, OBJWIN); \
808 } \
809 } \
810 }
811
812void DSVideoSoftwareRendererDrawBackgroundExt0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
813 int sizeAdjusted = 0x8000 << background->size;
814
815 BACKGROUND_BITMAP_INIT;
816
817 color_t* mainPalette = background->extPalette;
818 if (variant) {
819 mainPalette = background->variantPalette;
820 }
821 if (!mainPalette) {
822 mainPalette = renderer->normalPalette;
823 if (variant) {
824 mainPalette = renderer->variantPalette;
825 }
826 }
827 int paletteData;
828
829 uint16_t mapData;
830 uint8_t pixelData = 0;
831
832 int outX;
833 uint32_t* pixel;
834
835 if (!objwinSlowPath) {
836 if (!(flags & FLAG_TARGET_2)) {
837 DRAW_BACKGROUND_EXT_0(NoBlend, NO_OBJWIN);
838 } else {
839 DRAW_BACKGROUND_EXT_0(Blend, NO_OBJWIN);
840 }
841 } else {
842 if (!(flags & FLAG_TARGET_2)) {
843 DRAW_BACKGROUND_EXT_0(NoBlend, OBJWIN);
844 } else {
845 DRAW_BACKGROUND_EXT_0(Blend, OBJWIN);
846 }
847 }
848}
849
850#define DS_BACKGROUND_BITMAP_ITERATE(W, H) \
851 x += background->dx; \
852 y += background->dy; \
853 \
854 if (background->overflow) { \
855 localX = x & ((W << 8) - 1); \
856 localY = y & ((H << 8) - 1); \
857 } else if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
858 continue; \
859 } else { \
860 localX = x; \
861 localY = y; \
862 }
863
864void DSVideoSoftwareRendererDrawBackgroundExt1(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
865 BACKGROUND_BITMAP_INIT;
866
867 uint32_t screenBase = (background->screenBase & 0xFF00) * 8;
868 uint8_t color;
869 int width, height;
870 switch (background->size) {
871 case 0:
872 width = 128;
873 height = 128;
874 break;
875 case 1:
876 width = 256;
877 height = 256;
878 break;
879 case 2:
880 width = 512;
881 height = 256;
882 break;
883 case 3:
884 width = 512;
885 height = 512;
886 break;
887 }
888
889 int outX;
890 for (outX = renderer->start; outX < renderer->end; ++outX) {
891 DS_BACKGROUND_BITMAP_ITERATE(width, height);
892
893 if (!mosaicWait) {
894 uint32_t address = (localX >> 8) + (localY >> 8) * width + screenBase;
895 uint8_t* vram = (uint8_t*) renderer->d.vramBG[address >> VRAM_BLOCK_OFFSET];
896 color = vram[address & VRAM_BLOCK_MASK];
897 mosaicWait = mosaicH;
898 } else {
899 --mosaicWait;
900 }
901
902 uint32_t current = renderer->row[outX];
903 if (color && IS_WRITABLE(current)) {
904 if (!objwinSlowPath) {
905 _compositeBlendNoObjwin(renderer, outX, palette[color] | flags, current);
906 } else if (objwinForceEnable || (!(current & FLAG_OBJWIN)) == objwinOnly) {
907 color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
908 unsigned mergedFlags = flags;
909 if (current & FLAG_OBJWIN) {
910 mergedFlags = objwinFlags;
911 }
912 _compositeBlendObjwin(renderer, outX, currentPalette[color] | mergedFlags, current);
913 }
914 }
915 }
916}
917
918void DSVideoSoftwareRendererDrawBackgroundExt2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
919 BACKGROUND_BITMAP_INIT;
920
921 uint32_t screenBase = (background->screenBase & 0xFF00) * 4;
922 uint32_t color;
923 int width, height;
924 switch (background->size) {
925 case 0:
926 width = 128;
927 height = 128;
928 break;
929 case 1:
930 width = 256;
931 height = 256;
932 break;
933 case 2:
934 width = 512;
935 height = 256;
936 break;
937 case 3:
938 width = 512;
939 height = 512;
940 break;
941 }
942
943 int outX;
944 for (outX = renderer->start; outX < renderer->end; ++outX) {
945 DS_BACKGROUND_BITMAP_ITERATE(width, height);
946
947 if (!mosaicWait) {
948 uint32_t address = ((localX >> 8) + (localY >> 8) * width + screenBase) << 1;
949 uint16_t* vram = renderer->d.vramBG[address >> VRAM_BLOCK_OFFSET];
950 LOAD_16(color, address & VRAM_BLOCK_MASK, vram);
951#ifndef COLOR_16_BIT
952 unsigned color32;
953 color32 = 0;
954 color32 |= (color << 3) & 0xF8;
955 color32 |= (color << 6) & 0xF800;
956 color32 |= (color << 9) & 0xF80000;
957 color32 |= (color32 >> 5) & 0x070707;
958 color = color32;
959#elif COLOR_5_6_5
960 uint16_t color16 = 0;
961 color16 |= (color & 0x001F) << 11;
962 color16 |= (color & 0x03E0) << 1;
963 color16 |= (color & 0x7C00) >> 10;
964 color = color16;
965#endif
966 mosaicWait = mosaicH;
967 } else {
968 --mosaicWait;
969 }
970
971 uint32_t current = renderer->row[outX];
972 if (!objwinSlowPath || (!(current & FLAG_OBJWIN)) != objwinOnly) {
973 unsigned mergedFlags = flags;
974 if (current & FLAG_OBJWIN) {
975 mergedFlags = objwinFlags;
976 }
977 if (!variant) {
978 _compositeBlendObjwin(renderer, outX, color | mergedFlags, current);
979 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
980 _compositeBlendObjwin(renderer, outX, _brighten(color, renderer->bldy) | mergedFlags, current);
981 } else if (renderer->blendEffect == BLEND_DARKEN) {
982 _compositeBlendObjwin(renderer, outX, _darken(color, renderer->bldy) | mergedFlags, current);
983 }
984 }
985 }
986}