all repos — mgba @ f6755a6e1b7b0cf2b944cd8ca842746f11d6bf82

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