all repos — mgba @ f03389bfca8e6ec52815ae2d9941ec7f0a9e691c

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
 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, int x, uint32_t color, uint32_t current) {
 46	if (color >= current) {
 47		if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
 48			color = _mix(renderer->alphaA[x], current, renderer->alphaB[x], color);
 49		} else {
 50			color = (current & 0x00FFFFFF) | (current & (FLAG_REBLEND | FLAG_OBJWIN));
 51		}
 52	} else {
 53		color = (color & ~FLAG_TARGET_2) | (current & FLAG_OBJWIN);
 54	}
 55	renderer->row[x] = color;
 56}
 57
 58static inline void _compositeBlendNoObjwin(struct GBAVideoSoftwareRenderer* renderer, int x, 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->alphaA[x], current, renderer->alphaB[x], color);
 65		} else {
 66			color = (current & 0x00FFFFFF) | (current & (FLAG_REBLEND | FLAG_OBJWIN));
 67		}
 68	} else {
 69		color = color & ~FLAG_TARGET_2;
 70	}
 71	renderer->row[x] = color;
 72}
 73
 74static inline void _compositeNoBlendObjwin(struct GBAVideoSoftwareRenderer* renderer, int x, 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 & (FLAG_REBLEND | FLAG_OBJWIN));
 81	}
 82	renderer->row[x] = color;
 83}
 84
 85static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* renderer, int x, uint32_t color,
 86                                             uint32_t current) {
 87	UNUSED(renderer);
 88	if (color >= current) {
 89		color = (current & 0x00FFFFFF) | (current & (FLAG_REBLEND | FLAG_OBJWIN));
 90	}
 91	renderer->row[x] = 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, outX + IDX, color | mergedFlags, current); \
105	}
106
107#define COMPOSITE_16_NO_OBJWIN(BLEND, IDX) \
108	_composite ## BLEND ## NoObjwin(renderer, outX + 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, outX + 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 = renderer->row[outX + 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 = renderer->row[outX + 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	if (renderer->d.highlightAmount && background->highlight) {                                   \
149		objwinPalette = renderer->highlightPalette;                                               \
150	}                                                                                             \
151	UNUSED(objwinPalette);                                                                        \
152	if (objwinSlowPath) {                                                                         \
153		if (background->target1 && GBAWindowControlIsBlendEnable(renderer->objwin.packed) &&      \
154		    (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN)) { \
155			objwinPalette = renderer->variantPalette;                                             \
156			if (renderer->d.highlightAmount && background->highlight) {                           \
157				palette = renderer->highlightVariantPalette;                                      \
158			}                                                                                     \
159		}                                                                                         \
160		switch (background->index) {                                                              \
161		case 0:                                                                                   \
162			objwinForceEnable = GBAWindowControlIsBg0Enable(renderer->objwin.packed) &&           \
163			    GBAWindowControlIsBg0Enable(renderer->currentWindow.packed);                      \
164			objwinOnly = !GBAWindowControlIsBg0Enable(renderer->objwin.packed);                   \
165			break;                                                                                \
166		case 1:                                                                                   \
167			objwinForceEnable = GBAWindowControlIsBg1Enable(renderer->objwin.packed) &&           \
168			    GBAWindowControlIsBg1Enable(renderer->currentWindow.packed);                      \
169			objwinOnly = !GBAWindowControlIsBg1Enable(renderer->objwin.packed);                   \
170			break;                                                                                \
171		case 2:                                                                                   \
172			objwinForceEnable = GBAWindowControlIsBg2Enable(renderer->objwin.packed) &&           \
173			    GBAWindowControlIsBg2Enable(renderer->currentWindow.packed);                      \
174			objwinOnly = !GBAWindowControlIsBg2Enable(renderer->objwin.packed);                   \
175			break;                                                                                \
176		case 3:                                                                                   \
177			objwinForceEnable = GBAWindowControlIsBg3Enable(renderer->objwin.packed) &&           \
178			    GBAWindowControlIsBg3Enable(renderer->currentWindow.packed);                      \
179			objwinOnly = !GBAWindowControlIsBg3Enable(renderer->objwin.packed);                   \
180			break;                                                                                \
181		}                                                                                         \
182	}
183
184#define BACKGROUND_BITMAP_INIT                                                                                        \
185	int32_t x = background->sx + (renderer->start - 1) * background->dx;                                              \
186	int32_t y = background->sy + (renderer->start - 1) * background->dy;                                              \
187	int mosaicH = 0;                                                                                                  \
188	int mosaicWait = 0;                                                                                               \
189	if (background->mosaic) {                                                                                         \
190		int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1;                                                   \
191		y -= (inY % mosaicV) * background->dmy;                                                                       \
192		x -= (inY % mosaicV) * background->dmx;                                                                       \
193		mosaicH = GBAMosaicControlGetBgH(renderer->mosaic);                                                           \
194		mosaicWait = renderer->start % (mosaicH + 1);                                                                 \
195	}                                                                                                                 \
196	int32_t localX;                                                                                                   \
197	int32_t localY;                                                                                                   \
198                                                                                                                      \
199	uint32_t flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND; \
200	flags |= FLAG_TARGET_2 * background->target2;                                                                     \
201	int objwinFlags = FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA &&                 \
202	                                   GBAWindowControlIsBlendEnable(renderer->objwin.packed));                       \
203	objwinFlags |= flags;                                                                                             \
204	flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA &&                          \
205	                          GBAWindowControlIsBlendEnable(renderer->currentWindow.packed));                         \
206	if (renderer->blendEffect == BLEND_ALPHA && renderer->blda == 0x10 && renderer->bldb == 0) {                      \
207		flags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2);                                                                    \
208		objwinFlags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2);                                                              \
209	}                                                                                                                 \
210	int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) &&             \
211	    (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);                           \
212	color_t* palette = renderer->normalPalette;                                                                       \
213	if (renderer->d.highlightAmount && background->highlight) {                                                       \
214		palette = renderer->highlightPalette;                                                                         \
215	}                                                                                                                 \
216	if (variant) {                                                                                                    \
217		palette = renderer->variantPalette;                                                                           \
218		if (renderer->d.highlightAmount && background->highlight) {                                                   \
219			palette = renderer->highlightVariantPalette;                                                              \
220		}                                                                                                             \
221	}                                                                                                                 \
222	UNUSED(palette);                                                                                                  \
223	PREPARE_OBJWIN;
224
225#define BACKGROUND_BITMAP_ITERATE(W, H)                     \
226	x += background->dx;                                    \
227	y += background->dy;                                    \
228                                                            \
229	if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
230		continue;                                           \
231	} else {                                                \
232		localX = x;                                         \
233		localY = y;                                         \
234	}
235
236#define TEST_LAYER_ENABLED(X) \
237	(softwareRenderer->bg[X].enabled == 4 && \
238	(GBAWindowControlIsBg ## X ## Enable(softwareRenderer->currentWindow.packed) || \
239	(GBARegisterDISPCNTIsObjwinEnable(softwareRenderer->dispcnt) && GBAWindowControlIsBg ## X ## Enable (softwareRenderer->objwin.packed))) && \
240	softwareRenderer->bg[X].priority == priority)
241
242static inline unsigned _brighten(unsigned color, int y) {
243	unsigned c = 0;
244	unsigned a;
245#ifdef COLOR_16_BIT
246	a = color & 0x1F;
247	c |= (a + ((0x1F - a) * y) / 16) & 0x1F;
248
249#ifdef COLOR_5_6_5
250	a = color & 0x7C0;
251	c |= (a + ((0x7C0 - a) * y) / 16) & 0x7C0;
252
253	a = color & 0xF800;
254	c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
255#else
256	a = color & 0x3E0;
257	c |= (a + ((0x3E0 - a) * y) / 16) & 0x3E0;
258
259	a = color & 0x7C00;
260	c |= (a + ((0x7C00 - a) * y) / 16) & 0x7C00;
261#endif
262#else
263	a = color & 0xFF;
264	c |= (a + ((0xFF - a) * y) / 16) & 0xFF;
265
266	a = color & 0xFF00;
267	c |= (a + ((0xFF00 - a) * y) / 16) & 0xFF00;
268
269	a = color & 0xFF0000;
270	c |= (a + ((0xFF0000 - a) * y) / 16) & 0xFF0000;
271#endif
272	return c;
273}
274
275static inline unsigned _darken(unsigned color, int y) {
276	unsigned c = 0;
277	unsigned a;
278#ifdef COLOR_16_BIT
279	a = color & 0x1F;
280	c |= (a - (a * y) / 16) & 0x1F;
281
282#ifdef COLOR_5_6_5
283	a = color & 0x7C0;
284	c |= (a - (a * y) / 16) & 0x7C0;
285
286	a = color & 0xF800;
287	c |= (a - (a * y) / 16) & 0xF800;
288#else
289	a = color & 0x3E0;
290	c |= (a - (a * y) / 16) & 0x3E0;
291
292	a = color & 0x7C00;
293	c |= (a - (a * y) / 16) & 0x7C00;
294#endif
295#else
296	a = color & 0xFF;
297	c |= (a - (a * y) / 16) & 0xFF;
298
299	a = color & 0xFF00;
300	c |= (a - (a * y) / 16) & 0xFF00;
301
302	a = color & 0xFF0000;
303	c |= (a - (a * y) / 16) & 0xFF0000;
304#endif
305	return c;
306}
307
308static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB) {
309	unsigned c = 0;
310	unsigned a, b;
311#ifdef COLOR_16_BIT
312#ifdef COLOR_5_6_5
313	a = colorA & 0xF81F;
314	b = colorB & 0xF81F;
315	a |= (colorA & 0x7C0) << 16;
316	b |= (colorB & 0x7C0) << 16;
317	c = ((a * weightA + b * weightB) / 16);
318	if (c & 0x08000000) {
319		c = (c & ~0x0FC00000) | 0x07C00000;
320	}
321	if (c & 0x0020) {
322		c = (c & ~0x003F) | 0x001F;
323	}
324	if (c & 0x10000) {
325		c = (c & ~0x1F800) | 0xF800;
326	}
327	c = (c & 0xF81F) | ((c >> 16) & 0x07C0);
328#else
329	a = colorA & 0x7C1F;
330	b = colorB & 0x7C1F;
331	a |= (colorA & 0x3E0) << 16;
332	b |= (colorB & 0x3E0) << 16;
333	c = ((a * weightA + b * weightB) / 16);
334	if (c & 0x04000000) {
335		c = (c & ~0x07E00000) | 0x03E00000;
336	}
337	if (c & 0x0020) {
338		c = (c & ~0x003F) | 0x001F;
339	}
340	if (c & 0x8000) {
341		c = (c & ~0xF800) | 0x7C00;
342	}
343	c = (c & 0x7C1F) | ((c >> 16) & 0x03E0);
344#endif
345#else
346	a = colorA & 0xFF;
347	b = colorB & 0xFF;
348	c |= ((a * weightA + b * weightB) / 16) & 0x1FF;
349	if (c & 0x00000100) {
350		c = 0x000000FF;
351	}
352
353	a = colorA & 0xFF00;
354	b = colorB & 0xFF00;
355	c |= ((a * weightA + b * weightB) / 16) & 0x1FF00;
356	if (c & 0x00010000) {
357		c = (c & 0x000000FF) | 0x0000FF00;
358	}
359
360	a = colorA & 0xFF0000;
361	b = colorB & 0xFF0000;
362	c |= ((a * weightA + b * weightB) / 16) & 0x1FF0000;
363	if (c & 0x01000000) {
364		c = (c & 0x0000FFFF) | 0x00FF0000;
365	}
366#endif
367	return c;
368}
369
370#endif