all repos — mgba @ 3ba13e9286534dc653743168b67a1113666a841d

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