src/gba/renderers/software-private.h (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#ifndef SOFTWARE_PRIVATE_H
7#define SOFTWARE_PRIVATE_H
8
9#include <mgba/internal/arm/macros.h>
10#include <mgba/internal/gba/renderers/video-software.h>
11
12#ifdef NDEBUG
13#define VIDEO_CHECKS false
14#else
15#define VIDEO_CHECKS true
16#endif
17
18void GBAVideoSoftwareRendererDrawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer,
19 struct GBAVideoSoftwareBackground* background, int y);
20void GBAVideoSoftwareRendererDrawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer,
21 struct GBAVideoSoftwareBackground* background, int y);
22void GBAVideoSoftwareRendererDrawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer,
23 struct GBAVideoSoftwareBackground* background, int y);
24void GBAVideoSoftwareRendererDrawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer,
25 struct GBAVideoSoftwareBackground* background, int y);
26void GBAVideoSoftwareRendererDrawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer,
27 struct GBAVideoSoftwareBackground* background, int y);
28
29int GBAVideoSoftwareRendererPreprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int index, int y);
30void GBAVideoSoftwareRendererPostprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority);
31
32void GBAVideoSoftwareRendererPreprocessBuffer(struct GBAVideoSoftwareRenderer* renderer, int y);
33void GBAVideoSoftwareRendererPostprocessBuffer(struct GBAVideoSoftwareRenderer* renderer);
34int GBAVideoSoftwareRendererPreprocessSpriteLayer(struct GBAVideoSoftwareRenderer* renderer, int y);
35
36static inline unsigned _brighten(unsigned color, int y);
37static inline unsigned _darken(unsigned color, int y);
38static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB);
39
40
41// We stash the priority on the top bits so we can do a one-operator comparison
42// The lower the number, the higher the priority, and sprites take precedence over backgrounds
43// We want to do special processing if the color pixel is target 1, however
44
45static inline void _compositeBlendObjwin(struct GBAVideoSoftwareRenderer* renderer, int x, uint32_t color, uint32_t current) {
46 if (color >= current) {
47 if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
48 color = _mix(renderer->alphaA[x], current, renderer->alphaB[x], color);
49 } else {
50 color = (current & 0x00FFFFFF) | (current & (FLAG_REBLEND | FLAG_OBJWIN));
51 }
52 } else {
53 color = (color & ~FLAG_TARGET_2) | (current & FLAG_OBJWIN);
54 }
55 renderer->row[x] = color;
56}
57
58static inline void _compositeBlendNoObjwin(struct GBAVideoSoftwareRenderer* renderer, int x, uint32_t color, uint32_t current) {
59 if (!IS_WRITABLE(current)) { \
60 return; \
61 } \
62 if (color >= current) {
63 if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
64 color = _mix(renderer->alphaA[x], current, renderer->alphaB[x], color);
65 } else {
66 color = (current & 0x00FFFFFF) | (current & (FLAG_REBLEND | FLAG_OBJWIN));
67 }
68 } else {
69 color = color & ~FLAG_TARGET_2;
70 }
71 renderer->row[x] = color;
72}
73
74static inline void _compositeNoBlendObjwin(struct GBAVideoSoftwareRenderer* renderer, int x, uint32_t color,
75 uint32_t current) {
76 UNUSED(renderer);
77 if (color < current) {
78 color |= (current & FLAG_OBJWIN);
79 } else {
80 color = (current & 0x00FFFFFF) | (current & (FLAG_REBLEND | FLAG_OBJWIN));
81 }
82 renderer->row[x] = color;
83}
84
85static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* renderer, int x, uint32_t color,
86 uint32_t current) {
87 UNUSED(renderer);
88 if (color >= current) {
89 color = (current & 0x00FFFFFF) | (current & (FLAG_REBLEND | FLAG_OBJWIN));
90 }
91 renderer->row[x] = color;
92}
93
94#define COMPOSITE_16_OBJWIN(BLEND, IDX) \
95 if (!IS_WRITABLE(current)) { \
96 continue; \
97 } \
98 if (objwinForceEnable || (!(current & FLAG_OBJWIN)) == objwinOnly) { \
99 unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[paletteData | pixelData] : palette[pixelData]; \
100 unsigned mergedFlags = flags; \
101 if (current & FLAG_OBJWIN) { \
102 mergedFlags = objwinFlags; \
103 } \
104 _composite ## BLEND ## Objwin(renderer, outX + IDX, color | mergedFlags, current); \
105 }
106
107#define COMPOSITE_16_NO_OBJWIN(BLEND, IDX) \
108 _composite ## BLEND ## NoObjwin(renderer, outX + IDX, palette[pixelData] | flags, current);
109
110#define COMPOSITE_256_OBJWIN(BLEND, IDX) \
111 if (!IS_WRITABLE(current)) { \
112 continue; \
113 } \
114 if (objwinForceEnable || (!(current & FLAG_OBJWIN)) == objwinOnly) { \
115 unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[pixelData] : palette[pixelData]; \
116 unsigned mergedFlags = flags; \
117 if (current & FLAG_OBJWIN) { \
118 mergedFlags = objwinFlags; \
119 } \
120 _composite ## BLEND ## Objwin(renderer, outX + IDX, color | mergedFlags, current); \
121 }
122
123#define COMPOSITE_256_NO_OBJWIN COMPOSITE_16_NO_OBJWIN
124
125#define BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, IDX) \
126 pixelData = tileData & 0xF; \
127 current = renderer->row[outX + IDX]; \
128 if (pixelData && IS_WRITABLE(current)) { \
129 COMPOSITE_16_ ## OBJWIN (BLEND, IDX); \
130 } \
131 tileData >>= 4;
132
133#define BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, IDX) \
134 pixelData = tileData & 0xFF; \
135 current = renderer->row[outX + IDX]; \
136 if (pixelData && IS_WRITABLE(current)) { \
137 COMPOSITE_256_ ## OBJWIN (BLEND, IDX); \
138 } \
139 tileData >>= 8;
140
141// TODO: Remove UNUSEDs after implementing OBJWIN for modes 3 - 5
142#define PREPARE_OBJWIN \
143 int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt); \
144 int objwinOnly = 0; \
145 int objwinForceEnable = 0; \
146 UNUSED(objwinForceEnable); \
147 color_t* objwinPalette = renderer->normalPalette; \
148 if (renderer->d.highlightAmount && background->highlight) { \
149 objwinPalette = renderer->highlightPalette; \
150 } \
151 UNUSED(objwinPalette); \
152 if (objwinSlowPath) { \
153 if (background->target1 && GBAWindowControlIsBlendEnable(renderer->objwin.packed) && \
154 (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN)) { \
155 objwinPalette = renderer->variantPalette; \
156 if (renderer->d.highlightAmount && background->highlight) { \
157 palette = renderer->highlightVariantPalette; \
158 } \
159 } \
160 switch (background->index) { \
161 case 0: \
162 objwinForceEnable = GBAWindowControlIsBg0Enable(renderer->objwin.packed) && \
163 GBAWindowControlIsBg0Enable(renderer->currentWindow.packed); \
164 objwinOnly = !GBAWindowControlIsBg0Enable(renderer->objwin.packed); \
165 break; \
166 case 1: \
167 objwinForceEnable = GBAWindowControlIsBg1Enable(renderer->objwin.packed) && \
168 GBAWindowControlIsBg1Enable(renderer->currentWindow.packed); \
169 objwinOnly = !GBAWindowControlIsBg1Enable(renderer->objwin.packed); \
170 break; \
171 case 2: \
172 objwinForceEnable = GBAWindowControlIsBg2Enable(renderer->objwin.packed) && \
173 GBAWindowControlIsBg2Enable(renderer->currentWindow.packed); \
174 objwinOnly = !GBAWindowControlIsBg2Enable(renderer->objwin.packed); \
175 break; \
176 case 3: \
177 objwinForceEnable = GBAWindowControlIsBg3Enable(renderer->objwin.packed) && \
178 GBAWindowControlIsBg3Enable(renderer->currentWindow.packed); \
179 objwinOnly = !GBAWindowControlIsBg3Enable(renderer->objwin.packed); \
180 break; \
181 } \
182 }
183
184#define BACKGROUND_BITMAP_INIT \
185 int32_t x = background->sx + (renderer->start - 1) * background->dx; \
186 int32_t y = background->sy + (renderer->start - 1) * background->dy; \
187 int mosaicH = 0; \
188 int mosaicWait = 0; \
189 if (background->mosaic) { \
190 int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1; \
191 y -= (inY % mosaicV) * background->dmy; \
192 x -= (inY % mosaicV) * background->dmx; \
193 mosaicH = GBAMosaicControlGetBgH(renderer->mosaic); \
194 mosaicWait = renderer->start % (mosaicH + 1); \
195 } \
196 int32_t localX; \
197 int32_t localY; \
198 \
199 uint32_t flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND; \
200 flags |= FLAG_TARGET_2 * background->target2; \
201 int objwinFlags = FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && \
202 GBAWindowControlIsBlendEnable(renderer->objwin.packed)); \
203 objwinFlags |= flags; \
204 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && \
205 GBAWindowControlIsBlendEnable(renderer->currentWindow.packed)); \
206 if (renderer->blendEffect == BLEND_ALPHA && renderer->blda == 0x10 && renderer->bldb == 0) { \
207 flags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2); \
208 objwinFlags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2); \
209 } \
210 int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && \
211 (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); \
212 color_t* palette = renderer->normalPalette; \
213 if (renderer->d.highlightAmount && background->highlight) { \
214 palette = renderer->highlightPalette; \
215 } \
216 if (variant) { \
217 palette = renderer->variantPalette; \
218 if (renderer->d.highlightAmount && background->highlight) { \
219 palette = renderer->highlightVariantPalette; \
220 } \
221 } \
222 UNUSED(palette); \
223 PREPARE_OBJWIN;
224
225#define TEST_LAYER_ENABLED(X) \
226 (softwareRenderer->bg[X].enabled == 3 && \
227 (GBAWindowControlIsBg ## X ## Enable(softwareRenderer->currentWindow.packed) || \
228 (GBARegisterDISPCNTIsObjwinEnable(softwareRenderer->dispcnt) && GBAWindowControlIsBg ## X ## Enable (softwareRenderer->objwin.packed))) && \
229 softwareRenderer->bg[X].priority == priority)
230
231static inline unsigned _brighten(unsigned color, int y) {
232 unsigned c = 0;
233 unsigned a;
234#ifdef COLOR_16_BIT
235 a = color & 0x1F;
236 c |= (a + ((0x1F - a) * y) / 16) & 0x1F;
237
238#ifdef COLOR_5_6_5
239 a = color & 0x7C0;
240 c |= (a + ((0x7C0 - a) * y) / 16) & 0x7C0;
241
242 a = color & 0xF800;
243 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
244#else
245 a = color & 0x3E0;
246 c |= (a + ((0x3E0 - a) * y) / 16) & 0x3E0;
247
248 a = color & 0x7C00;
249 c |= (a + ((0x7C00 - a) * y) / 16) & 0x7C00;
250#endif
251#else
252 a = color & 0xFF;
253 c |= (a + ((0xFF - a) * y) / 16) & 0xFF;
254
255 a = color & 0xFF00;
256 c |= (a + ((0xFF00 - a) * y) / 16) & 0xFF00;
257
258 a = color & 0xFF0000;
259 c |= (a + ((0xFF0000 - a) * y) / 16) & 0xFF0000;
260#endif
261 return c;
262}
263
264static inline unsigned _darken(unsigned color, int y) {
265 unsigned c = 0;
266 unsigned a;
267#ifdef COLOR_16_BIT
268 a = color & 0x1F;
269 c |= (a - (a * y) / 16) & 0x1F;
270
271#ifdef COLOR_5_6_5
272 a = color & 0x7C0;
273 c |= (a - (a * y) / 16) & 0x7C0;
274
275 a = color & 0xF800;
276 c |= (a - (a * y) / 16) & 0xF800;
277#else
278 a = color & 0x3E0;
279 c |= (a - (a * y) / 16) & 0x3E0;
280
281 a = color & 0x7C00;
282 c |= (a - (a * y) / 16) & 0x7C00;
283#endif
284#else
285 a = color & 0xFF;
286 c |= (a - (a * y) / 16) & 0xFF;
287
288 a = color & 0xFF00;
289 c |= (a - (a * y) / 16) & 0xFF00;
290
291 a = color & 0xFF0000;
292 c |= (a - (a * y) / 16) & 0xFF0000;
293#endif
294 return c;
295}
296
297static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB) {
298 unsigned c = 0;
299 unsigned a, b;
300#ifdef COLOR_16_BIT
301#ifdef COLOR_5_6_5
302 a = colorA & 0xF81F;
303 b = colorB & 0xF81F;
304 a |= (colorA & 0x7C0) << 16;
305 b |= (colorB & 0x7C0) << 16;
306 c = ((a * weightA + b * weightB) / 16);
307 if (c & 0x08000000) {
308 c = (c & ~0x0FC00000) | 0x07C00000;
309 }
310 if (c & 0x0020) {
311 c = (c & ~0x003F) | 0x001F;
312 }
313 if (c & 0x10000) {
314 c = (c & ~0x1F800) | 0xF800;
315 }
316 c = (c & 0xF81F) | ((c >> 16) & 0x07C0);
317#else
318 a = colorA & 0x7C1F;
319 b = colorB & 0x7C1F;
320 a |= (colorA & 0x3E0) << 16;
321 b |= (colorB & 0x3E0) << 16;
322 c = ((a * weightA + b * weightB) / 16);
323 if (c & 0x04000000) {
324 c = (c & ~0x07E00000) | 0x03E00000;
325 }
326 if (c & 0x0020) {
327 c = (c & ~0x003F) | 0x001F;
328 }
329 if (c & 0x8000) {
330 c = (c & ~0xF800) | 0x7C00;
331 }
332 c = (c & 0x7C1F) | ((c >> 16) & 0x03E0);
333#endif
334#else
335 a = colorA & 0xFF;
336 b = colorB & 0xFF;
337 c |= ((a * weightA + b * weightB) / 16) & 0x1FF;
338 if (c & 0x00000100) {
339 c = 0x000000FF;
340 }
341
342 a = colorA & 0xFF00;
343 b = colorB & 0xFF00;
344 c |= ((a * weightA + b * weightB) / 16) & 0x1FF00;
345 if (c & 0x00010000) {
346 c = (c & 0x000000FF) | 0x0000FF00;
347 }
348
349 a = colorA & 0xFF0000;
350 b = colorB & 0xFF0000;
351 c |= ((a * weightA + b * weightB) / 16) & 0x1FF0000;
352 if (c & 0x01000000) {
353 c = (c & 0x0000FFFF) | 0x00FF0000;
354 }
355#endif
356 return c;
357}
358
359#endif