all repos — mgba @ c207675dfb0ae3e39f9cb0745fe0cca79a697ae2

mGBA Game Boy Advance Emulator

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