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