all repos — mgba @ 11f105eec78e2744ccd20554171c7e0f7794ba14

mGBA Game Boy Advance Emulator

src/gba/renderers/software-bg.c (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#include "gba/renderers/software-private.h"
  7
  8#include <mgba/core/interface.h>
  9#include <mgba/internal/gba/gba.h>
 10
 11#define BACKGROUND_BITMAP_INIT                                                                                        \
 12	int32_t x = background->sx + (renderer->start - 1) * background->dx;                                              \
 13	int32_t y = background->sy + (renderer->start - 1) * background->dy;                                              \
 14	int mosaicH = 0;                                                                                                  \
 15	int mosaicWait = 0;                                                                                               \
 16	if (background->mosaic) {                                                                                         \
 17		int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1;                                                   \
 18		y -= (inY % mosaicV) * background->dmy;                                                                       \
 19		x -= (inY % mosaicV) * background->dmx;                                                                       \
 20		mosaicH = GBAMosaicControlGetBgH(renderer->mosaic);                                                           \
 21		mosaicWait = renderer->start % (mosaicH + 1);                                                                 \
 22	}                                                                                                                 \
 23	int32_t localX;                                                                                                   \
 24	int32_t localY;                                                                                                   \
 25                                                                                                                      \
 26	uint32_t flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND; \
 27	flags |= FLAG_TARGET_2 * background->target2;                                                                     \
 28	int objwinFlags = FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA &&                 \
 29	                                   GBAWindowControlIsBlendEnable(renderer->objwin.packed));                       \
 30	objwinFlags |= flags;                                                                                             \
 31	flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA &&                          \
 32	                          GBAWindowControlIsBlendEnable(renderer->currentWindow.packed));                         \
 33	if (renderer->blendEffect == BLEND_ALPHA && renderer->blda == 0x10 && renderer->bldb == 0) {                      \
 34		flags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2);                                                                    \
 35		objwinFlags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2);                                                              \
 36	}                                                                                                                 \
 37	int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) &&             \
 38	    (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);                           \
 39	color_t* palette = renderer->normalPalette;                                                                       \
 40	if (renderer->d.highlightAmount && background->highlight) {                                                       \
 41		palette = renderer->highlightPalette;                                                                         \
 42	}                                                                                                                 \
 43	if (variant) {                                                                                                    \
 44		palette = renderer->variantPalette;                                                                           \
 45		if (renderer->d.highlightAmount && background->highlight) {                                                   \
 46			palette = renderer->highlightVariantPalette;                                                              \
 47		}                                                                                                             \
 48	}                                                                                                                 \
 49	UNUSED(palette);                                                                                                  \
 50	PREPARE_OBJWIN;
 51
 52#define BACKGROUND_BITMAP_ITERATE(W, H)                     \
 53	x += background->dx;                                    \
 54	y += background->dy;                                    \
 55                                                            \
 56	if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
 57		continue;                                           \
 58	} else {                                                \
 59		localX = x;                                         \
 60		localY = y;                                         \
 61	}
 62
 63#define MODE_2_COORD_OVERFLOW \
 64	localX = x & (sizeAdjusted - 1); \
 65	localY = y & (sizeAdjusted - 1); \
 66
 67#define MODE_2_COORD_NO_OVERFLOW \
 68	if ((x | y) & ~(sizeAdjusted - 1)) { \
 69		continue; \
 70	} \
 71	localX = x; \
 72	localY = y;
 73
 74#define MODE_2_MOSAIC(COORD) \
 75		if (!mosaicWait) { \
 76			COORD \
 77			mapData = screenBase[(localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)]; \
 78			pixelData = charBase[(mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)]; \
 79			\
 80			mosaicWait = mosaicH; \
 81		} else { \
 82			--mosaicWait; \
 83		}
 84
 85#define MODE_2_NO_MOSAIC(COORD) \
 86	COORD \
 87	mapData = screenBase[(localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)]; \
 88	pixelData = charBase[(mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)];
 89
 90#define MODE_2_LOOP(MOSAIC, COORD, BLEND, OBJWIN) \
 91	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) { \
 92		x += background->dx; \
 93		y += background->dy; \
 94		\
 95		uint32_t current = *pixel; \
 96		MOSAIC(COORD) \
 97		if (pixelData && IS_WRITABLE(current)) { \
 98			COMPOSITE_256_ ## OBJWIN (BLEND, 0); \
 99		} \
100	}
101
102#define DRAW_BACKGROUND_MODE_2(BLEND, OBJWIN) \
103	if (background->overflow) { \
104		if (mosaicH > 1) { \
105			MODE_2_LOOP(MODE_2_MOSAIC, MODE_2_COORD_OVERFLOW, BLEND, OBJWIN); \
106		} else { \
107			MODE_2_LOOP(MODE_2_NO_MOSAIC, MODE_2_COORD_OVERFLOW, BLEND, OBJWIN); \
108		} \
109	} else { \
110		if (mosaicH > 1) { \
111			MODE_2_LOOP(MODE_2_MOSAIC, MODE_2_COORD_NO_OVERFLOW, BLEND, OBJWIN); \
112		} else { \
113			MODE_2_LOOP(MODE_2_NO_MOSAIC, MODE_2_COORD_NO_OVERFLOW, BLEND, OBJWIN); \
114		} \
115	}
116
117void GBAVideoSoftwareRendererDrawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
118	int sizeAdjusted = 0x8000 << background->size;
119
120	BACKGROUND_BITMAP_INIT;
121
122	uint8_t* screenBase = &((uint8_t*) renderer->d.vram)[background->screenBase];
123	uint8_t* charBase = &((uint8_t*) renderer->d.vram)[background->charBase];
124	uint8_t mapData;
125	uint8_t pixelData = 0;
126
127	int outX;
128	uint32_t* pixel;
129
130	if (!objwinSlowPath) {
131		if (!(flags & FLAG_TARGET_2)) {
132			DRAW_BACKGROUND_MODE_2(NoBlend, NO_OBJWIN);
133		} else {
134			DRAW_BACKGROUND_MODE_2(Blend, NO_OBJWIN);
135		}
136	} else {
137		if (!(flags & FLAG_TARGET_2)) {
138			DRAW_BACKGROUND_MODE_2(NoBlend, OBJWIN);
139		} else {
140			DRAW_BACKGROUND_MODE_2(Blend, OBJWIN);
141		}
142	}
143}
144
145void GBAVideoSoftwareRendererDrawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
146	BACKGROUND_BITMAP_INIT;
147
148	uint32_t color = renderer->normalPalette[0];
149
150	int outX;
151	uint32_t* pixel;
152	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
153		BACKGROUND_BITMAP_ITERATE(GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
154
155		if (!mosaicWait) {
156			LOAD_16(color, ((localX >> 8) + (localY >> 8) * GBA_VIDEO_HORIZONTAL_PIXELS) << 1, renderer->d.vram);
157			color = mColorFrom555(color);
158			mosaicWait = mosaicH;
159		} else {
160			--mosaicWait;
161		}
162
163		uint32_t current = *pixel;
164		if (!objwinSlowPath || (!(current & FLAG_OBJWIN)) != objwinOnly) {
165			unsigned mergedFlags = flags;
166			if (current & FLAG_OBJWIN) {
167				mergedFlags = objwinFlags;
168			}
169			if (!variant) {
170				_compositeBlendObjwin(renderer, pixel, color | mergedFlags, current);
171			} else if (renderer->blendEffect == BLEND_BRIGHTEN) {
172				_compositeBlendObjwin(renderer, pixel, _brighten(color, renderer->bldy) | mergedFlags, current);
173			} else if (renderer->blendEffect == BLEND_DARKEN) {
174				_compositeBlendObjwin(renderer, pixel, _darken(color, renderer->bldy) | mergedFlags, current);
175			}
176		}
177	}
178}
179
180void GBAVideoSoftwareRendererDrawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
181	BACKGROUND_BITMAP_INIT;
182
183	uint16_t color = 0;
184	uint32_t offset = 0;
185	if (GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt)) {
186		offset = 0xA000;
187	}
188
189	int outX;
190	uint32_t* pixel;
191	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
192		BACKGROUND_BITMAP_ITERATE(GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
193
194		if (!mosaicWait) {
195			color = ((uint8_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * GBA_VIDEO_HORIZONTAL_PIXELS];
196
197			mosaicWait = mosaicH;
198		} else {
199			--mosaicWait;
200		}
201
202		uint32_t current = *pixel;
203		if (color && IS_WRITABLE(current)) {
204			if (!objwinSlowPath) {
205				_compositeBlendNoObjwin(renderer, pixel, palette[color] | flags, current);
206			} else if (objwinForceEnable || (!(current & FLAG_OBJWIN)) == objwinOnly) {
207				color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
208				unsigned mergedFlags = flags;
209				if (current & FLAG_OBJWIN) {
210					mergedFlags = objwinFlags;
211				}
212				_compositeBlendObjwin(renderer, pixel, currentPalette[color] | mergedFlags, current);
213			}
214		}
215	}
216}
217
218void GBAVideoSoftwareRendererDrawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
219	BACKGROUND_BITMAP_INIT;
220
221	uint32_t color = renderer->normalPalette[0];
222	uint32_t offset = 0;
223	if (GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt)) {
224		offset = 0xA000;
225	}
226
227	int outX;
228	uint32_t* pixel;
229	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
230		BACKGROUND_BITMAP_ITERATE(160, 128);
231
232		if (!mosaicWait) {
233			LOAD_16(color, offset + (localX >> 8) * 2 + (localY >> 8) * 320, renderer->d.vram);
234			color = mColorFrom555(color);
235			mosaicWait = mosaicH;
236		} else {
237			--mosaicWait;
238		}
239
240		uint32_t current = *pixel;
241		if (!objwinSlowPath || (!(current & FLAG_OBJWIN)) != objwinOnly) {
242			unsigned mergedFlags = flags;
243			if (current & FLAG_OBJWIN) {
244				mergedFlags = objwinFlags;
245			}
246			if (!variant) {
247				_compositeBlendObjwin(renderer, pixel, color | mergedFlags, current);
248			} else if (renderer->blendEffect == BLEND_BRIGHTEN) {
249				_compositeBlendObjwin(renderer, pixel, _brighten(color, renderer->bldy) | mergedFlags, current);
250			} else if (renderer->blendEffect == BLEND_DARKEN) {
251				_compositeBlendObjwin(renderer, pixel, _darken(color, renderer->bldy) | mergedFlags, current);
252			}
253		}
254	}
255}