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