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