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 | 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 (color >= current) {
56 if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
57 color = _mix(renderer->blda, current, renderer->bldb, color);
58 } else {
59 color = current & (0x00FFFFFF | FLAG_REBLEND | FLAG_OBJWIN);
60 }
61 } else {
62 color = color & ~FLAG_TARGET_2;
63 }
64 *pixel = color;
65}
66
67static inline void _compositeNoBlendObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color,
68 uint32_t current) {
69 UNUSED(renderer);
70 if (color < current) {
71 color |= (current & FLAG_OBJWIN);
72 } else {
73 color = current & (0x00FFFFFF | FLAG_REBLEND | FLAG_OBJWIN);
74 }
75 *pixel = color;
76}
77
78static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color,
79 uint32_t current) {
80 UNUSED(renderer);
81 if (color >= current) {
82 color = current & (0x00FFFFFF | FLAG_REBLEND | FLAG_OBJWIN);
83 }
84 *pixel = color;
85}
86
87#define COMPOSITE_16_OBJWIN(BLEND, IDX) \
88 if (objwinForceEnable || (!(current & FLAG_OBJWIN)) == objwinOnly) { \
89 unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[paletteData | pixelData] : palette[pixelData]; \
90 unsigned mergedFlags = flags; \
91 if (current & FLAG_OBJWIN) { \
92 mergedFlags = objwinFlags; \
93 } \
94 _composite ## BLEND ## Objwin(renderer, &pixel[IDX], color | mergedFlags, current); \
95 }
96
97#define COMPOSITE_16_NO_OBJWIN(BLEND, IDX) \
98 _composite ## BLEND ## NoObjwin(renderer, &pixel[IDX], palette[pixelData] | flags, current);
99
100#define COMPOSITE_256_OBJWIN(BLEND, IDX) \
101 if (objwinForceEnable || (!(current & FLAG_OBJWIN)) == objwinOnly) { \
102 unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[pixelData] : palette[pixelData]; \
103 unsigned mergedFlags = flags; \
104 if (current & FLAG_OBJWIN) { \
105 mergedFlags = objwinFlags; \
106 } \
107 _composite ## BLEND ## Objwin(renderer, &pixel[IDX], color | mergedFlags, current); \
108 }
109
110#define COMPOSITE_256_NO_OBJWIN COMPOSITE_16_NO_OBJWIN
111
112#define BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, IDX) \
113 pixelData = tileData & 0xF; \
114 current = pixel[IDX]; \
115 if (pixelData && IS_WRITABLE(current)) { \
116 COMPOSITE_16_ ## OBJWIN (BLEND, IDX); \
117 } \
118 tileData >>= 4;
119
120#define BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, IDX) \
121 pixelData = tileData & 0xFF; \
122 current = pixel[IDX]; \
123 if (pixelData && IS_WRITABLE(current)) { \
124 COMPOSITE_256_ ## OBJWIN (BLEND, IDX); \
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 if (renderer->d.highlightAmount && background->highlight) { \
136 objwinPalette = renderer->highlightPalette; \
137 } \
138 UNUSED(objwinPalette); \
139 if (objwinSlowPath) { \
140 if (background->target1 && GBAWindowControlIsBlendEnable(renderer->objwin.packed) && \
141 (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN)) { \
142 objwinPalette = renderer->variantPalette; \
143 if (renderer->d.highlightAmount && background->highlight) { \
144 palette = renderer->highlightVariantPalette; \
145 } \
146 } \
147 switch (background->index) { \
148 case 0: \
149 objwinForceEnable = GBAWindowControlIsBg0Enable(renderer->objwin.packed) && \
150 GBAWindowControlIsBg0Enable(renderer->currentWindow.packed); \
151 objwinOnly = !GBAWindowControlIsBg0Enable(renderer->objwin.packed); \
152 break; \
153 case 1: \
154 objwinForceEnable = GBAWindowControlIsBg1Enable(renderer->objwin.packed) && \
155 GBAWindowControlIsBg1Enable(renderer->currentWindow.packed); \
156 objwinOnly = !GBAWindowControlIsBg1Enable(renderer->objwin.packed); \
157 break; \
158 case 2: \
159 objwinForceEnable = GBAWindowControlIsBg2Enable(renderer->objwin.packed) && \
160 GBAWindowControlIsBg2Enable(renderer->currentWindow.packed); \
161 objwinOnly = !GBAWindowControlIsBg2Enable(renderer->objwin.packed); \
162 break; \
163 case 3: \
164 objwinForceEnable = GBAWindowControlIsBg3Enable(renderer->objwin.packed) && \
165 GBAWindowControlIsBg3Enable(renderer->currentWindow.packed); \
166 objwinOnly = !GBAWindowControlIsBg3Enable(renderer->objwin.packed); \
167 break; \
168 } \
169 }
170
171static inline unsigned _brighten(unsigned color, int y) {
172 unsigned c = 0;
173 unsigned a;
174#ifdef COLOR_16_BIT
175 a = color & 0x1F;
176 c |= (a + ((0x1F - a) * y) / 16) & 0x1F;
177
178#ifdef COLOR_5_6_5
179 a = color & 0x7C0;
180 c |= (a + ((0x7C0 - a) * y) / 16) & 0x7C0;
181
182 a = color & 0xF800;
183 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
184#else
185 a = color & 0x3E0;
186 c |= (a + ((0x3E0 - a) * y) / 16) & 0x3E0;
187
188 a = color & 0x7C00;
189 c |= (a + ((0x7C00 - a) * y) / 16) & 0x7C00;
190#endif
191#else
192 a = color & 0xFF;
193 c |= (a + ((0xFF - a) * y) / 16) & 0xFF;
194
195 a = color & 0xFF00;
196 c |= (a + ((0xFF00 - a) * y) / 16) & 0xFF00;
197
198 a = color & 0xFF0000;
199 c |= (a + ((0xFF0000 - a) * y) / 16) & 0xFF0000;
200#endif
201 return c;
202}
203
204static inline unsigned _darken(unsigned color, int y) {
205 unsigned c = 0;
206 unsigned a;
207#ifdef COLOR_16_BIT
208 a = color & 0x1F;
209 c |= (a - (a * y) / 16) & 0x1F;
210
211#ifdef COLOR_5_6_5
212 a = color & 0x7C0;
213 c |= (a - (a * y) / 16) & 0x7C0;
214
215 a = color & 0xF800;
216 c |= (a - (a * y) / 16) & 0xF800;
217#else
218 a = color & 0x3E0;
219 c |= (a - (a * y) / 16) & 0x3E0;
220
221 a = color & 0x7C00;
222 c |= (a - (a * y) / 16) & 0x7C00;
223#endif
224#else
225 a = color & 0xFF;
226 c |= (a - (a * y) / 16) & 0xFF;
227
228 a = color & 0xFF00;
229 c |= (a - (a * y) / 16) & 0xFF00;
230
231 a = color & 0xFF0000;
232 c |= (a - (a * y) / 16) & 0xFF0000;
233#endif
234 return c;
235}
236
237static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB) {
238 unsigned c = 0;
239 unsigned a, b;
240#ifdef COLOR_16_BIT
241#ifdef COLOR_5_6_5
242 a = colorA & 0xF81F;
243 b = colorB & 0xF81F;
244 a |= (colorA & 0x7C0) << 16;
245 b |= (colorB & 0x7C0) << 16;
246 c = ((a * weightA + b * weightB) / 16);
247 if (c & 0x08000000) {
248 c = (c & ~0x0FC00000) | 0x07C00000;
249 }
250 if (c & 0x0020) {
251 c = (c & ~0x003F) | 0x001F;
252 }
253 if (c & 0x10000) {
254 c = (c & ~0x1F800) | 0xF800;
255 }
256 c = (c & 0xF81F) | ((c >> 16) & 0x07C0);
257#else
258 a = colorA & 0x7C1F;
259 b = colorB & 0x7C1F;
260 a |= (colorA & 0x3E0) << 16;
261 b |= (colorB & 0x3E0) << 16;
262 c = ((a * weightA + b * weightB) / 16);
263 if (c & 0x04000000) {
264 c = (c & ~0x07E00000) | 0x03E00000;
265 }
266 if (c & 0x0020) {
267 c = (c & ~0x003F) | 0x001F;
268 }
269 if (c & 0x8000) {
270 c = (c & ~0xF800) | 0x7C00;
271 }
272 c = (c & 0x7C1F) | ((c >> 16) & 0x03E0);
273#endif
274#else
275 a = colorA & 0xFF;
276 b = colorB & 0xFF;
277 c |= ((a * weightA + b * weightB) / 16) & 0x1FF;
278 if (c & 0x00000100) {
279 c = 0x000000FF;
280 }
281
282 a = colorA & 0xFF00;
283 b = colorB & 0xFF00;
284 c |= ((a * weightA + b * weightB) / 16) & 0x1FF00;
285 if (c & 0x00010000) {
286 c = (c & 0x000000FF) | 0x0000FF00;
287 }
288
289 a = colorA & 0xFF0000;
290 b = colorB & 0xFF0000;
291 c |= ((a * weightA + b * weightB) / 16) & 0x1FF0000;
292 if (c & 0x01000000) {
293 c = (c & 0x0000FFFF) | 0x00FF0000;
294 }
295#endif
296 return c;
297}
298
299#endif