all repos — mgba @ 2f0501d3c476e234859d2c6063636bb03a92bea3

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 (!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