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