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) | ((current << 1) & FLAG_REBLEND);
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) | ((current << 1) & FLAG_REBLEND);
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 color |= (current & FLAG_OBJWIN);
71 } else {
72 color = (current & 0x00FFFFFF) | ((current << 1) & FLAG_REBLEND);
73 }
74 *pixel = color;
75}
76
77static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color,
78 uint32_t current) {
79 UNUSED(renderer);
80 if (color >= current) {
81 color = (current & 0x00FFFFFF) | ((current << 1) & FLAG_REBLEND);
82 }
83 *pixel = color;
84}
85
86#define COMPOSITE_16_OBJWIN(BLEND) \
87 if (objwinForceEnable || (!(current & FLAG_OBJWIN)) == objwinOnly) { \
88 unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[paletteData | pixelData] : palette[pixelData]; \
89 unsigned mergedFlags = flags; \
90 if (current & FLAG_OBJWIN) { \
91 mergedFlags = objwinFlags; \
92 } \
93 _composite ## BLEND ## Objwin(renderer, pixel, color | mergedFlags, current); \
94 }
95
96#define COMPOSITE_16_NO_OBJWIN(BLEND) \
97 _composite ## BLEND ## NoObjwin(renderer, pixel, palette[pixelData] | flags, current);
98
99#define COMPOSITE_256_OBJWIN(BLEND) \
100 if (objwinForceEnable || (!(current & FLAG_OBJWIN)) == objwinOnly) { \
101 unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[pixelData] : palette[pixelData]; \
102 unsigned mergedFlags = flags; \
103 if (current & FLAG_OBJWIN) { \
104 mergedFlags = objwinFlags; \
105 } \
106 _composite ## BLEND ## Objwin(renderer, pixel, color | mergedFlags, current); \
107 }
108
109#define COMPOSITE_256_NO_OBJWIN(BLEND) \
110 COMPOSITE_16_NO_OBJWIN(BLEND)
111
112#define BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN) \
113 pixelData = tileData & 0xF; \
114 current = *pixel; \
115 if (pixelData && IS_WRITABLE(current)) { \
116 COMPOSITE_16_ ## OBJWIN (BLEND); \
117 } \
118 tileData >>= 4;
119
120#define BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN) \
121 pixelData = tileData & 0xFF; \
122 current = *pixel; \
123 if (pixelData && IS_WRITABLE(current)) { \
124 COMPOSITE_256_ ## OBJWIN (BLEND); \
125 } \
126 tileData >>= 8;
127
128// TODO: Remove UNUSEDs after implementing OBJWIN for modes 3 - 5
129#define PREPARE_OBJWIN \
130 int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt); \
131 int objwinOnly = 0; \
132 int objwinForceEnable = 0; \
133 UNUSED(objwinForceEnable); \
134 color_t* objwinPalette = renderer->normalPalette; \
135 UNUSED(objwinPalette); \
136 if (objwinSlowPath) { \
137 if (background->target1 && GBAWindowControlIsBlendEnable(renderer->objwin.packed) && \
138 (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN)) { \
139 objwinPalette = renderer->variantPalette; \
140 } \
141 switch (background->index) { \
142 case 0: \
143 objwinForceEnable = GBAWindowControlIsBg0Enable(renderer->objwin.packed) && \
144 GBAWindowControlIsBg0Enable(renderer->currentWindow.packed); \
145 objwinOnly = !GBAWindowControlIsBg0Enable(renderer->objwin.packed); \
146 break; \
147 case 1: \
148 objwinForceEnable = GBAWindowControlIsBg1Enable(renderer->objwin.packed) && \
149 GBAWindowControlIsBg1Enable(renderer->currentWindow.packed); \
150 objwinOnly = !GBAWindowControlIsBg1Enable(renderer->objwin.packed); \
151 break; \
152 case 2: \
153 objwinForceEnable = GBAWindowControlIsBg2Enable(renderer->objwin.packed) && \
154 GBAWindowControlIsBg2Enable(renderer->currentWindow.packed); \
155 objwinOnly = !GBAWindowControlIsBg2Enable(renderer->objwin.packed); \
156 break; \
157 case 3: \
158 objwinForceEnable = GBAWindowControlIsBg3Enable(renderer->objwin.packed) && \
159 GBAWindowControlIsBg3Enable(renderer->currentWindow.packed); \
160 objwinOnly = !GBAWindowControlIsBg3Enable(renderer->objwin.packed); \
161 break; \
162 } \
163 }
164
165#define BACKGROUND_BITMAP_INIT \
166 int32_t x = background->sx + (renderer->start - 1) * background->dx; \
167 int32_t y = background->sy + (renderer->start - 1) * background->dy; \
168 int mosaicH = 0; \
169 int mosaicWait = 0; \
170 if (background->mosaic) { \
171 int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1; \
172 y -= (inY % mosaicV) * background->dmy; \
173 x -= (inY % mosaicV) * background->dmx; \
174 mosaicH = GBAMosaicControlGetBgH(renderer->mosaic); \
175 mosaicWait = renderer->start % (mosaicH + 1); \
176 } \
177 int32_t localX; \
178 int32_t localY; \
179 \
180 uint32_t flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND; \
181 flags |= FLAG_TARGET_2 * background->target2; \
182 int objwinFlags = FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && \
183 GBAWindowControlIsBlendEnable(renderer->objwin.packed)); \
184 objwinFlags |= flags; \
185 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && \
186 GBAWindowControlIsBlendEnable(renderer->currentWindow.packed)); \
187 if (renderer->blendEffect == BLEND_ALPHA && renderer->blda == 0x10 && renderer->bldb == 0) { \
188 flags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2); \
189 objwinFlags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2); \
190 } \
191 int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && \
192 (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); \
193 color_t* palette = renderer->normalPalette; \
194 if (variant) { \
195 palette = renderer->variantPalette; \
196 } \
197 UNUSED(palette); \
198 PREPARE_OBJWIN;
199
200#define BACKGROUND_BITMAP_ITERATE(W, H) \
201 x += background->dx; \
202 y += background->dy; \
203 \
204 if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
205 continue; \
206 } else { \
207 localX = x; \
208 localY = y; \
209 }
210
211static inline unsigned _brighten(unsigned color, int y) {
212 unsigned c = 0;
213 unsigned a;
214#ifdef COLOR_16_BIT
215 a = color & 0x1F;
216 c |= (a + ((0x1F - a) * y) / 16) & 0x1F;
217
218#ifdef COLOR_5_6_5
219 a = color & 0x7C0;
220 c |= (a + ((0x7C0 - a) * y) / 16) & 0x7C0;
221
222 a = color & 0xF800;
223 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
224#else
225 a = color & 0x3E0;
226 c |= (a + ((0x3E0 - a) * y) / 16) & 0x3E0;
227
228 a = color & 0x7C00;
229 c |= (a + ((0x7C00 - a) * y) / 16) & 0x7C00;
230#endif
231#else
232 a = color & 0xF8;
233 c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
234
235 a = color & 0xF800;
236 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
237
238 a = color & 0xF80000;
239 c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
240#endif
241 return c;
242}
243
244static inline unsigned _darken(unsigned color, int y) {
245 unsigned c = 0;
246 unsigned a;
247#ifdef COLOR_16_BIT
248 a = color & 0x1F;
249 c |= (a - (a * y) / 16) & 0x1F;
250
251#ifdef COLOR_5_6_5
252 a = color & 0x7C0;
253 c |= (a - (a * y) / 16) & 0x7C0;
254
255 a = color & 0xF800;
256 c |= (a - (a * y) / 16) & 0xF800;
257#else
258 a = color & 0x3E0;
259 c |= (a - (a * y) / 16) & 0x3E0;
260
261 a = color & 0x7C00;
262 c |= (a - (a * y) / 16) & 0x7C00;
263#endif
264#else
265 a = color & 0xF8;
266 c |= (a - (a * y) / 16) & 0xF8;
267
268 a = color & 0xF800;
269 c |= (a - (a * y) / 16) & 0xF800;
270
271 a = color & 0xF80000;
272 c |= (a - (a * y) / 16) & 0xF80000;
273#endif
274 return c;
275}
276
277static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB) {
278 unsigned c = 0;
279 unsigned a, b;
280#ifdef COLOR_16_BIT
281#ifdef COLOR_5_6_5
282 a = colorA & 0xF81F;
283 b = colorB & 0xF81F;
284 a |= (colorA & 0x7C0) << 16;
285 b |= (colorB & 0x7C0) << 16;
286 c = ((a * weightA + b * weightB) / 16);
287 if (c & 0x08000000) {
288 c = (c & ~0x0FC00000) | 0x07C00000;
289 }
290 if (c & 0x0020) {
291 c = (c & ~0x003F) | 0x001F;
292 }
293 if (c & 0x10000) {
294 c = (c & ~0x1F800) | 0xF800;
295 }
296 c = (c & 0xF81F) | ((c >> 16) & 0x07C0);
297#else
298 a = colorA & 0x7C1F;
299 b = colorB & 0x7C1F;
300 a |= (colorA & 0x3E0) << 16;
301 b |= (colorB & 0x3E0) << 16;
302 c = ((a * weightA + b * weightB) / 16);
303 if (c & 0x04000000) {
304 c = (c & ~0x07E00000) | 0x03E00000;
305 }
306 if (c & 0x0020) {
307 c = (c & ~0x003F) | 0x001F;
308 }
309 if (c & 0x8000) {
310 c = (c & ~0xF800) | 0x7C00;
311 }
312 c = (c & 0x7C1F) | ((c >> 16) & 0x03E0);
313#endif
314#else
315 a = colorA & 0xF8;
316 b = colorB & 0xF8;
317 c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
318 if (c & 0x00000100) {
319 c = 0x000000F8;
320 }
321
322 a = colorA & 0xF800;
323 b = colorB & 0xF800;
324 c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
325 if (c & 0x00010000) {
326 c = (c & 0x000000F8) | 0x0000F800;
327 }
328
329 a = colorA & 0xF80000;
330 b = colorB & 0xF80000;
331 c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
332 if (c & 0x01000000) {
333 c = (c & 0x0000F8F8) | 0x00F80000;
334 }
335#endif
336 return c;
337}
338
339#endif