src/gba/renderers/software-bg.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#include <mgba/core/interface.h>
9#include <mgba/internal/gba/gba.h>
10
11#define BACKGROUND_BITMAP_INIT \
12 int32_t x = background->sx + (renderer->start - 1) * background->dx; \
13 int32_t y = background->sy + (renderer->start - 1) * background->dy; \
14 int mosaicH = 0; \
15 int mosaicWait = 0; \
16 if (background->mosaic) { \
17 int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1; \
18 y -= (inY % mosaicV) * background->dmy; \
19 x -= (inY % mosaicV) * background->dmx; \
20 mosaicH = GBAMosaicControlGetBgH(renderer->mosaic); \
21 mosaicWait = renderer->start % (mosaicH + 1); \
22 } \
23 int32_t localX; \
24 int32_t localY; \
25 \
26 uint32_t flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND; \
27 flags |= FLAG_TARGET_2 * background->target2; \
28 int objwinFlags = FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && \
29 GBAWindowControlIsBlendEnable(renderer->objwin.packed)); \
30 objwinFlags |= flags; \
31 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && \
32 GBAWindowControlIsBlendEnable(renderer->currentWindow.packed)); \
33 if (renderer->blendEffect == BLEND_ALPHA && renderer->blda == 0x10 && renderer->bldb == 0) { \
34 flags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2); \
35 objwinFlags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2); \
36 } \
37 int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && \
38 (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); \
39 color_t* palette = renderer->normalPalette; \
40 if (renderer->d.highlightAmount && background->highlight) { \
41 palette = renderer->highlightPalette; \
42 } \
43 if (variant) { \
44 palette = renderer->variantPalette; \
45 if (renderer->d.highlightAmount && background->highlight) { \
46 palette = renderer->highlightVariantPalette; \
47 } \
48 } \
49 UNUSED(palette); \
50 PREPARE_OBJWIN;
51
52#define BACKGROUND_BITMAP_ITERATE(W, H) \
53 x += background->dx; \
54 y += background->dy; \
55 \
56 if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
57 continue; \
58 } else { \
59 localX = x; \
60 localY = y; \
61 }
62
63#define MODE_2_COORD_OVERFLOW \
64 localX = x & (sizeAdjusted - 1); \
65 localY = y & (sizeAdjusted - 1); \
66
67#define MODE_2_COORD_NO_OVERFLOW \
68 if ((x | y) & ~(sizeAdjusted - 1)) { \
69 continue; \
70 } \
71 localX = x; \
72 localY = y;
73
74#define MODE_2_MOSAIC(COORD) \
75 if (!mosaicWait) { \
76 COORD \
77 mapData = screenBase[(localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)]; \
78 pixelData = charBase[(mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)]; \
79 \
80 mosaicWait = mosaicH; \
81 } else { \
82 --mosaicWait; \
83 }
84
85#define MODE_2_NO_MOSAIC(COORD) \
86 COORD \
87 mapData = screenBase[(localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)]; \
88 pixelData = charBase[(mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)];
89
90#define MODE_2_LOOP(MOSAIC, COORD, BLEND, OBJWIN) \
91 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) { \
92 x += background->dx; \
93 y += background->dy; \
94 \
95 uint32_t current = *pixel; \
96 MOSAIC(COORD) \
97 if (pixelData && IS_WRITABLE(current)) { \
98 COMPOSITE_256_ ## OBJWIN (BLEND, 0); \
99 } \
100 }
101
102#define DRAW_BACKGROUND_MODE_2(BLEND, OBJWIN) \
103 if (background->overflow) { \
104 if (mosaicH > 1) { \
105 MODE_2_LOOP(MODE_2_MOSAIC, MODE_2_COORD_OVERFLOW, BLEND, OBJWIN); \
106 } else { \
107 MODE_2_LOOP(MODE_2_NO_MOSAIC, MODE_2_COORD_OVERFLOW, BLEND, OBJWIN); \
108 } \
109 } else { \
110 if (mosaicH > 1) { \
111 MODE_2_LOOP(MODE_2_MOSAIC, MODE_2_COORD_NO_OVERFLOW, BLEND, OBJWIN); \
112 } else { \
113 MODE_2_LOOP(MODE_2_NO_MOSAIC, MODE_2_COORD_NO_OVERFLOW, BLEND, OBJWIN); \
114 } \
115 }
116
117void GBAVideoSoftwareRendererDrawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
118 int sizeAdjusted = 0x8000 << background->size;
119
120 BACKGROUND_BITMAP_INIT;
121
122 uint8_t* screenBase = &((uint8_t*) renderer->d.vram)[background->screenBase];
123 uint8_t* charBase = &((uint8_t*) renderer->d.vram)[background->charBase];
124 uint8_t mapData;
125 uint8_t pixelData = 0;
126
127 int outX;
128 uint32_t* pixel;
129
130 if (!objwinSlowPath) {
131 if (!(flags & FLAG_TARGET_2)) {
132 DRAW_BACKGROUND_MODE_2(NoBlend, NO_OBJWIN);
133 } else {
134 DRAW_BACKGROUND_MODE_2(Blend, NO_OBJWIN);
135 }
136 } else {
137 if (!(flags & FLAG_TARGET_2)) {
138 DRAW_BACKGROUND_MODE_2(NoBlend, OBJWIN);
139 } else {
140 DRAW_BACKGROUND_MODE_2(Blend, OBJWIN);
141 }
142 }
143}
144
145void GBAVideoSoftwareRendererDrawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
146 BACKGROUND_BITMAP_INIT;
147
148 uint32_t color = renderer->normalPalette[0];
149
150 int outX;
151 uint32_t* pixel;
152 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
153 BACKGROUND_BITMAP_ITERATE(GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
154
155 if (!mosaicWait) {
156 LOAD_16(color, ((localX >> 8) + (localY >> 8) * GBA_VIDEO_HORIZONTAL_PIXELS) << 1, renderer->d.vram);
157 color = mColorFrom555(color);
158 mosaicWait = mosaicH;
159 } else {
160 --mosaicWait;
161 }
162
163 uint32_t current = *pixel;
164 if (!objwinSlowPath || (!(current & FLAG_OBJWIN)) != objwinOnly) {
165 unsigned mergedFlags = flags;
166 if (current & FLAG_OBJWIN) {
167 mergedFlags = objwinFlags;
168 }
169 if (!variant) {
170 _compositeBlendObjwin(renderer, pixel, color | mergedFlags, current);
171 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
172 _compositeBlendObjwin(renderer, pixel, _brighten(color, renderer->bldy) | mergedFlags, current);
173 } else if (renderer->blendEffect == BLEND_DARKEN) {
174 _compositeBlendObjwin(renderer, pixel, _darken(color, renderer->bldy) | mergedFlags, current);
175 }
176 }
177 }
178}
179
180void GBAVideoSoftwareRendererDrawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
181 BACKGROUND_BITMAP_INIT;
182
183 uint16_t color = 0;
184 uint32_t offset = 0;
185 if (GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt)) {
186 offset = 0xA000;
187 }
188
189 int outX;
190 uint32_t* pixel;
191 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
192 BACKGROUND_BITMAP_ITERATE(GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
193
194 if (!mosaicWait) {
195 color = ((uint8_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * GBA_VIDEO_HORIZONTAL_PIXELS];
196
197 mosaicWait = mosaicH;
198 } else {
199 --mosaicWait;
200 }
201
202 uint32_t current = *pixel;
203 if (color && IS_WRITABLE(current)) {
204 if (!objwinSlowPath) {
205 _compositeBlendNoObjwin(renderer, pixel, palette[color] | flags, current);
206 } else if (objwinForceEnable || (!(current & FLAG_OBJWIN)) == objwinOnly) {
207 color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
208 unsigned mergedFlags = flags;
209 if (current & FLAG_OBJWIN) {
210 mergedFlags = objwinFlags;
211 }
212 _compositeBlendObjwin(renderer, pixel, currentPalette[color] | mergedFlags, current);
213 }
214 }
215 }
216}
217
218void GBAVideoSoftwareRendererDrawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
219 BACKGROUND_BITMAP_INIT;
220
221 uint32_t color = renderer->normalPalette[0];
222 uint32_t offset = 0;
223 if (GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt)) {
224 offset = 0xA000;
225 }
226
227 int outX;
228 uint32_t* pixel;
229 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
230 BACKGROUND_BITMAP_ITERATE(160, 128);
231
232 if (!mosaicWait) {
233 LOAD_16(color, offset + (localX >> 8) * 2 + (localY >> 8) * 320, renderer->d.vram);
234 color = mColorFrom555(color);
235 mosaicWait = mosaicH;
236 } else {
237 --mosaicWait;
238 }
239
240 uint32_t current = *pixel;
241 if (!objwinSlowPath || (!(current & FLAG_OBJWIN)) != objwinOnly) {
242 unsigned mergedFlags = flags;
243 if (current & FLAG_OBJWIN) {
244 mergedFlags = objwinFlags;
245 }
246 if (!variant) {
247 _compositeBlendObjwin(renderer, pixel, color | mergedFlags, current);
248 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
249 _compositeBlendObjwin(renderer, pixel, _brighten(color, renderer->bldy) | mergedFlags, current);
250 } else if (renderer->blendEffect == BLEND_DARKEN) {
251 _compositeBlendObjwin(renderer, pixel, _darken(color, renderer->bldy) | mergedFlags, current);
252 }
253 }
254 }
255}