mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'feature/canvas'
This commit is contained in:
commit
96d0193136
|
@ -740,6 +740,11 @@ elseif(BUILD_GLES2)
|
|||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libgles2")
|
||||
endif()
|
||||
|
||||
if(USE_EPOXY OR BUILD_GL OR BUILD_GLES2)
|
||||
# This file should probably go somewhere else
|
||||
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/video-backend.c)
|
||||
endif()
|
||||
|
||||
if(WIN32 AND NOT (LIBMGBA_ONLY OR SKIP_LIBRARY OR USE_EPOXY))
|
||||
message(FATAL_ERROR "Windows requires epoxy module!")
|
||||
endif()
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/* Copyright (c) 2013-2023 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef GEOMETRY_H
|
||||
#define GEOMETRY_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
struct Size {
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
struct Rectangle {
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
void RectangleUnion(struct Rectangle* dst, const struct Rectangle* add);
|
||||
void RectangleCenter(const struct Rectangle* ref, struct Rectangle* rect);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -66,7 +66,11 @@ struct mCore {
|
|||
void (*loadConfig)(struct mCore*, const struct mCoreConfig*);
|
||||
void (*reloadConfigOption)(struct mCore*, const char* option, const struct mCoreConfig*);
|
||||
|
||||
void (*desiredVideoDimensions)(const struct mCore*, unsigned* width, unsigned* height);
|
||||
void (*baseVideoSize)(const struct mCore*, unsigned* width, unsigned* height);
|
||||
void (*currentVideoSize)(const struct mCore*, unsigned* width, unsigned* height);
|
||||
unsigned (*videoScale)(const struct mCore*);
|
||||
size_t (*screenRegions)(const struct mCore*, const struct mCoreScreenRegion**);
|
||||
|
||||
void (*setVideoBuffer)(struct mCore*, color_t* buffer, size_t stride);
|
||||
void (*setVideoGLTex)(struct mCore*, unsigned texid);
|
||||
|
||||
|
|
|
@ -293,6 +293,15 @@ struct mCoreMemoryBlock {
|
|||
uint32_t segmentStart;
|
||||
};
|
||||
|
||||
struct mCoreScreenRegion {
|
||||
size_t id;
|
||||
const char* description;
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
int16_t w;
|
||||
int16_t h;
|
||||
};
|
||||
|
||||
enum mCoreRegisterType {
|
||||
mCORE_REGISTER_GPR = 0,
|
||||
mCORE_REGISTER_FPR,
|
||||
|
|
|
@ -22,6 +22,9 @@ enum {
|
|||
GB_VIDEO_VBLANK_PIXELS = 10,
|
||||
GB_VIDEO_VERTICAL_TOTAL_PIXELS = 154,
|
||||
|
||||
SGB_VIDEO_HORIZONTAL_PIXELS = 256,
|
||||
SGB_VIDEO_VERTICAL_PIXELS = 224,
|
||||
|
||||
// TODO: Figure out exact lengths
|
||||
GB_VIDEO_MODE_2_LENGTH = 80,
|
||||
GB_VIDEO_MODE_3_LENGTH_BASE = 172,
|
||||
|
|
|
@ -361,7 +361,7 @@ bool mCoreTakeScreenshotVF(struct mCore* core, struct VFile* vf) {
|
|||
size_t stride;
|
||||
const void* pixels = 0;
|
||||
unsigned width, height;
|
||||
core->desiredVideoDimensions(core, &width, &height);
|
||||
core->currentVideoSize(core, &width, &height);
|
||||
core->getPixels(core, &pixels, &stride);
|
||||
png_structp png = PNGWriteOpen(vf);
|
||||
png_infop info = PNGWriteHeader(png, width, height);
|
||||
|
|
|
@ -176,7 +176,7 @@ static bool _savePNGState(struct mCore* core, struct VFile* vf, struct mStateExt
|
|||
mappedMemoryFree(state, stateSize);
|
||||
|
||||
unsigned width, height;
|
||||
core->desiredVideoDimensions(core, &width, &height);
|
||||
core->currentVideoSize(core, &width, &height);
|
||||
png_structp png = PNGWriteOpen(vf);
|
||||
png_infop info = PNGWriteHeader(png, width, height);
|
||||
if (!png || !info) {
|
||||
|
@ -529,7 +529,7 @@ bool mCoreLoadStateNamed(struct mCore* core, struct VFile* vf, int flags) {
|
|||
mappedMemoryFree(state, core->stateSize(core));
|
||||
|
||||
unsigned width, height;
|
||||
core->desiredVideoDimensions(core, &width, &height);
|
||||
core->currentVideoSize(core, &width, &height);
|
||||
|
||||
struct mStateExtdataItem item;
|
||||
if (flags & SAVESTATE_SCREENSHOT && mStateExtdataGet(&extdata, EXTDATA_SCREENSHOT, &item)) {
|
||||
|
|
|
@ -60,6 +60,15 @@ static const struct mCoreMemoryBlock _GBCMemoryBlocks[] = {
|
|||
{ GB_BASE_HRAM, "hram", "HRAM", "High RAM", GB_BASE_HRAM, GB_BASE_HRAM + GB_SIZE_HRAM, GB_SIZE_HRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
};
|
||||
|
||||
static const struct mCoreScreenRegion _GBScreenRegions[] = {
|
||||
{ 0, "Screen", 0, 0, GB_VIDEO_HORIZONTAL_PIXELS, GB_VIDEO_VERTICAL_PIXELS }
|
||||
};
|
||||
|
||||
static const struct mCoreScreenRegion _SGBScreenRegions[] = {
|
||||
{ 0, "Screen", (SGB_VIDEO_HORIZONTAL_PIXELS - GB_VIDEO_HORIZONTAL_PIXELS) / 2, (SGB_VIDEO_VERTICAL_PIXELS - GB_VIDEO_VERTICAL_PIXELS) / 2, GB_VIDEO_HORIZONTAL_PIXELS, GB_VIDEO_VERTICAL_PIXELS },
|
||||
{ 1, "Border", 0, 0, SGB_VIDEO_HORIZONTAL_PIXELS, SGB_VIDEO_VERTICAL_PIXELS },
|
||||
};
|
||||
|
||||
static const struct mCoreRegisterInfo _GBRegisters[] = {
|
||||
{ "b", NULL, 1, 0xFF, mCORE_REGISTER_GPR },
|
||||
{ "c", NULL, 1, 0xFF, mCORE_REGISTER_GPR },
|
||||
|
@ -355,14 +364,36 @@ static void _GBCoreReloadConfigOption(struct mCore* core, const char* option, co
|
|||
}
|
||||
}
|
||||
|
||||
static void _GBCoreDesiredVideoDimensions(const struct mCore* core, unsigned* width, unsigned* height) {
|
||||
static void _GBCoreBaseVideoSize(const struct mCore* core, unsigned* width, unsigned* height) {
|
||||
UNUSED(core);
|
||||
*width = SGB_VIDEO_HORIZONTAL_PIXELS;
|
||||
*height = SGB_VIDEO_VERTICAL_PIXELS;
|
||||
}
|
||||
|
||||
static void _GBCoreCurrentVideoSize(const struct mCore* core, unsigned* width, unsigned* height) {
|
||||
const struct GB* gb = core->board;
|
||||
if (gb && (!(gb->model & GB_MODEL_SGB) || !gb->video.sgbBorders)) {
|
||||
*width = GB_VIDEO_HORIZONTAL_PIXELS;
|
||||
*height = GB_VIDEO_VERTICAL_PIXELS;
|
||||
} else {
|
||||
*width = 256;
|
||||
*height = 224;
|
||||
*width = SGB_VIDEO_HORIZONTAL_PIXELS;
|
||||
*height = SGB_VIDEO_VERTICAL_PIXELS;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned _GBCoreVideoScale(const struct mCore* core) {
|
||||
UNUSED(core);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static size_t _GBCoreScreenRegions(const struct mCore* core, const struct mCoreScreenRegion** regions) {
|
||||
const struct GB* gb = core->board;
|
||||
if (gb && (!(gb->model & GB_MODEL_SGB) || !gb->video.sgbBorders)) {
|
||||
*regions = _GBScreenRegions;
|
||||
return 1;
|
||||
} else {
|
||||
*regions = _SGBScreenRegions;
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -424,7 +455,7 @@ static void _GBCoreSetAVStream(struct mCore* core, struct mAVStream* stream) {
|
|||
gb->stream = stream;
|
||||
if (stream && stream->videoDimensionsChanged) {
|
||||
unsigned width, height;
|
||||
core->desiredVideoDimensions(core, &width, &height);
|
||||
core->currentVideoSize(core, &width, &height);
|
||||
stream->videoDimensionsChanged(stream, width, height);
|
||||
}
|
||||
if (stream && stream->audioRateChanged) {
|
||||
|
@ -1232,7 +1263,10 @@ struct mCore* GBCoreCreate(void) {
|
|||
core->setSync = _GBCoreSetSync;
|
||||
core->loadConfig = _GBCoreLoadConfig;
|
||||
core->reloadConfigOption = _GBCoreReloadConfigOption;
|
||||
core->desiredVideoDimensions = _GBCoreDesiredVideoDimensions;
|
||||
core->baseVideoSize = _GBCoreBaseVideoSize;
|
||||
core->currentVideoSize = _GBCoreCurrentVideoSize;
|
||||
core->videoScale = _GBCoreVideoScale;
|
||||
core->screenRegions = _GBCoreScreenRegions;
|
||||
core->setVideoBuffer = _GBCoreSetVideoBuffer;
|
||||
core->setVideoGLTex = _GBCoreSetVideoGLTex;
|
||||
core->getPixels = _GBCoreGetPixels;
|
||||
|
|
|
@ -129,6 +129,10 @@ static const struct mCoreMemoryBlock _GBAMemoryBlocksEEPROM[] = {
|
|||
{ GBA_REGION_SRAM_MIRROR, "eeprom", "EEPROM", "EEPROM (8kiB)", 0, GBA_SIZE_EEPROM, GBA_SIZE_EEPROM, mCORE_MEMORY_RW },
|
||||
};
|
||||
|
||||
static const struct mCoreScreenRegion _GBAScreenRegions[] = {
|
||||
{ 0, "Screen", 0, 0, GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS }
|
||||
};
|
||||
|
||||
static const struct mCoreRegisterInfo _GBARegisters[] = {
|
||||
{ "r0", NULL, 4, 0xFFFFFFFF, mCORE_REGISTER_GPR },
|
||||
{ "r1", NULL, 4, 0xFFFFFFFF, mCORE_REGISTER_GPR },
|
||||
|
@ -422,19 +426,45 @@ static void _GBACoreReloadConfigOption(struct mCore* core, const char* option, c
|
|||
}
|
||||
}
|
||||
|
||||
static void _GBACoreDesiredVideoDimensions(const struct mCore* core, unsigned* width, unsigned* height) {
|
||||
static void _GBACoreBaseVideoSize(const struct mCore* core, unsigned* width, unsigned* height) {
|
||||
UNUSED(core);
|
||||
*width = GBA_VIDEO_HORIZONTAL_PIXELS;
|
||||
*height = GBA_VIDEO_VERTICAL_PIXELS;
|
||||
}
|
||||
|
||||
static void _GBACoreCurrentVideoSize(const struct mCore* core, unsigned* width, unsigned* height) {
|
||||
int scale = 1;
|
||||
#ifdef BUILD_GLES3
|
||||
const struct GBACore* gbacore = (const struct GBACore*) core;
|
||||
int scale = gbacore->glRenderer.scale;
|
||||
if (gbacore->glRenderer.outputTex != (unsigned) -1) {
|
||||
scale = gbacore->glRenderer.scale;
|
||||
}
|
||||
#else
|
||||
UNUSED(core);
|
||||
int scale = 1;
|
||||
#endif
|
||||
|
||||
*width = GBA_VIDEO_HORIZONTAL_PIXELS * scale;
|
||||
*height = GBA_VIDEO_VERTICAL_PIXELS * scale;
|
||||
}
|
||||
|
||||
static unsigned _GBACoreVideoScale(const struct mCore* core) {
|
||||
#ifdef BUILD_GLES3
|
||||
const struct GBACore* gbacore = (const struct GBACore*) core;
|
||||
if (gbacore->glRenderer.outputTex != (unsigned) -1) {
|
||||
return gbacore->glRenderer.scale;
|
||||
}
|
||||
#else
|
||||
UNUSED(core);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
static size_t _GBACoreScreenRegions(const struct mCore* core, const struct mCoreScreenRegion** regions) {
|
||||
UNUSED(core);
|
||||
*regions = _GBAScreenRegions;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _GBACoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t stride) {
|
||||
struct GBACore* gbacore = (struct GBACore*) core;
|
||||
gbacore->renderer.outputBuffer = buffer;
|
||||
|
@ -500,7 +530,7 @@ static void _GBACoreSetAVStream(struct mCore* core, struct mAVStream* stream) {
|
|||
gba->stream = stream;
|
||||
if (stream && stream->videoDimensionsChanged) {
|
||||
unsigned width, height;
|
||||
core->desiredVideoDimensions(core, &width, &height);
|
||||
core->currentVideoSize(core, &width, &height);
|
||||
stream->videoDimensionsChanged(stream, width, height);
|
||||
}
|
||||
if (stream && stream->audioRateChanged) {
|
||||
|
@ -1358,7 +1388,10 @@ struct mCore* GBACoreCreate(void) {
|
|||
core->setSync = _GBACoreSetSync;
|
||||
core->loadConfig = _GBACoreLoadConfig;
|
||||
core->reloadConfigOption = _GBACoreReloadConfigOption;
|
||||
core->desiredVideoDimensions = _GBACoreDesiredVideoDimensions;
|
||||
core->baseVideoSize = _GBACoreBaseVideoSize;
|
||||
core->currentVideoSize = _GBACoreCurrentVideoSize;
|
||||
core->videoScale = _GBACoreVideoScale;
|
||||
core->screenRegions = _GBACoreScreenRegions;
|
||||
core->setVideoBuffer = _GBACoreSetVideoBuffer;
|
||||
core->setVideoGLTex = _GBACoreSetVideoGLTex;
|
||||
core->getPixels = _GBACoreGetPixels;
|
||||
|
|
|
@ -496,7 +496,7 @@ static void _drawTex(struct mCore* core, bool faded, bool both) {
|
|||
int wide = isWide ? 2 : 1;
|
||||
|
||||
unsigned corew, coreh;
|
||||
core->desiredVideoDimensions(core, &corew, &coreh);
|
||||
core->currentVideoSize(core, &corew, &coreh);
|
||||
|
||||
int w = corew;
|
||||
int h = coreh;
|
||||
|
|
|
@ -83,7 +83,7 @@ bool _mExampleRun(const struct mArguments* args, Socket client) {
|
|||
|
||||
// Get the dimensions required for this core and send them to the client.
|
||||
unsigned width, height;
|
||||
core->desiredVideoDimensions(core, &width, &height);
|
||||
core->baseVideoSize(core, &width, &height);
|
||||
ssize_t bufferSize = width * height * BYTES_PER_PIXEL;
|
||||
uint32_t sendNO;
|
||||
sendNO = htonl(width);
|
||||
|
|
|
@ -414,19 +414,13 @@ void retro_get_system_info(struct retro_system_info* info) {
|
|||
|
||||
void retro_get_system_av_info(struct retro_system_av_info* info) {
|
||||
unsigned width, height;
|
||||
core->desiredVideoDimensions(core, &width, &height);
|
||||
core->currentVideoSize(core, &width, &height);
|
||||
info->geometry.base_width = width;
|
||||
info->geometry.base_height = height;
|
||||
#ifdef M_CORE_GB
|
||||
if (core->platform(core) == mPLATFORM_GB) {
|
||||
info->geometry.max_width = 256;
|
||||
info->geometry.max_height = 224;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
info->geometry.max_width = width;
|
||||
info->geometry.max_height = height;
|
||||
}
|
||||
|
||||
core->baseVideoSize(core, &width, &height);
|
||||
info->geometry.max_width = width;
|
||||
info->geometry.max_height = height;
|
||||
|
||||
info->geometry.aspect_ratio = width / (double) height;
|
||||
info->timing.fps = core->frequency(core) / (float) core->frameCycles(core);
|
||||
|
@ -614,7 +608,7 @@ void retro_run(void) {
|
|||
|
||||
core->runFrame(core);
|
||||
unsigned width, height;
|
||||
core->desiredVideoDimensions(core, &width, &height);
|
||||
core->currentVideoSize(core, &width, &height);
|
||||
videoCallback(outputBuffer, width, height, BYTES_PER_PIXEL * 256);
|
||||
|
||||
#ifdef M_CORE_GBA
|
||||
|
|
|
@ -68,7 +68,7 @@
|
|||
outputBuffer = nil;
|
||||
|
||||
unsigned width, height;
|
||||
core->desiredVideoDimensions(core, &width, &height);
|
||||
core->baseVideoSize(core, &width, &height);
|
||||
outputBuffer = malloc(width * height * BYTES_PER_PIXEL);
|
||||
core->setVideoBuffer(core, outputBuffer, width);
|
||||
core->setAudioBufferSize(core, SAMPLES);
|
||||
|
@ -143,14 +143,14 @@
|
|||
- (OEIntRect)screenRect
|
||||
{
|
||||
unsigned width, height;
|
||||
core->desiredVideoDimensions(core, &width, &height);
|
||||
core->currentVideoSize(core, &width, &height);
|
||||
return OEIntRectMake(0, 0, width, height);
|
||||
}
|
||||
|
||||
- (OEIntSize)bufferSize
|
||||
{
|
||||
unsigned width, height;
|
||||
core->desiredVideoDimensions(core, &width, &height);
|
||||
core->baseVideoSize(core, &width, &height);
|
||||
return OEIntSizeMake(width, height);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
|
||||
static const GLint _glVertices[] = {
|
||||
0, 0,
|
||||
256, 0,
|
||||
256, 256,
|
||||
0, 256
|
||||
1, 0,
|
||||
1, 1,
|
||||
0, 1
|
||||
};
|
||||
|
||||
static const GLint _glTexCoords[] = {
|
||||
|
@ -21,76 +21,102 @@ static const GLint _glTexCoords[] = {
|
|||
0, 1
|
||||
};
|
||||
|
||||
static inline void _initTex(void) {
|
||||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||
#ifndef _WIN32
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void mGLContextInit(struct VideoBackend* v, WHandle handle) {
|
||||
UNUSED(handle);
|
||||
struct mGLContext* context = (struct mGLContext*) v;
|
||||
v->width = 1;
|
||||
v->height = 1;
|
||||
memset(context->layerDims, 0, sizeof(context->layerDims));
|
||||
memset(context->imageSizes, -1, sizeof(context->imageSizes));
|
||||
glGenTextures(2, context->tex);
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex[0]);
|
||||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||
#ifndef _WIN32
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
#endif
|
||||
_initTex();
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex[1]);
|
||||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||
#ifndef _WIN32
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
#endif
|
||||
_initTex();
|
||||
context->activeTex = 0;
|
||||
|
||||
glGenTextures(VIDEO_LAYER_MAX, context->tex);
|
||||
int i;
|
||||
for (i = 0; i < VIDEO_LAYER_MAX; ++i) {
|
||||
glBindTexture(GL_TEXTURE_2D, context->layers[i]);
|
||||
_initTex();
|
||||
}
|
||||
}
|
||||
|
||||
static void mGLContextSetDimensions(struct VideoBackend* v, unsigned width, unsigned height) {
|
||||
static inline void _setTexDims(int width, int height) {
|
||||
#ifdef COLOR_16_BIT
|
||||
#ifdef COLOR_5_6_5
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(width), toPow2(height), 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0);
|
||||
#else
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(width), toPow2(height), 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 0);
|
||||
#endif
|
||||
#elif defined(__BIG_ENDIAN__)
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(width), toPow2(height), 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, 0);
|
||||
#else
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(width), toPow2(height), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void mGLContextSetLayerDimensions(struct VideoBackend* v, enum VideoLayer layer, const struct Rectangle* dims) {
|
||||
struct mGLContext* context = (struct mGLContext*) v;
|
||||
if (width == v->width && height == v->height) {
|
||||
if (layer >= VIDEO_LAYER_MAX) {
|
||||
return;
|
||||
}
|
||||
v->width = width;
|
||||
v->height = height;
|
||||
context->layerDims[layer].x = dims->x;
|
||||
context->layerDims[layer].y = dims->y;
|
||||
if (dims->width == context->layerDims[layer].width && dims->height == context->layerDims[layer].height) {
|
||||
return;
|
||||
}
|
||||
context->layerDims[layer].width = dims->width;
|
||||
context->layerDims[layer].height = dims->height;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex[0]);
|
||||
#ifdef COLOR_16_BIT
|
||||
#ifdef COLOR_5_6_5
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(width), toPow2(height), 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0);
|
||||
#else
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(width), toPow2(height), 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 0);
|
||||
#endif
|
||||
#elif defined(__BIG_ENDIAN__)
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(width), toPow2(height), 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, 0);
|
||||
#else
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(width), toPow2(height), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
#endif
|
||||
if (context->imageSizes[layer].width <= 0 || context->imageSizes[layer].height <= 0) {
|
||||
if (layer == VIDEO_LAYER_IMAGE) {
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex[0]);
|
||||
_setTexDims(dims->width, dims->height);
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex[1]);
|
||||
_setTexDims(dims->width, dims->height);
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, context->layers[layer]);
|
||||
_setTexDims(dims->width, dims->height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex[1]);
|
||||
#ifdef COLOR_16_BIT
|
||||
#ifdef COLOR_5_6_5
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(width), toPow2(height), 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0);
|
||||
#else
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(width), toPow2(height), 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 0);
|
||||
#endif
|
||||
#elif defined(__BIG_ENDIAN__)
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(width), toPow2(height), 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, 0);
|
||||
#else
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(width), toPow2(height), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
#endif
|
||||
static void mGLContextLayerDimensions(const struct VideoBackend* v, enum VideoLayer layer, struct Rectangle* dims) {
|
||||
struct mGLContext* context = (struct mGLContext*) v;
|
||||
if (layer >= VIDEO_LAYER_MAX) {
|
||||
return;
|
||||
}
|
||||
memcpy(dims, &context->layerDims[layer], sizeof(*dims));
|
||||
}
|
||||
|
||||
static void mGLContextDeinit(struct VideoBackend* v) {
|
||||
struct mGLContext* context = (struct mGLContext*) v;
|
||||
glDeleteTextures(2, context->tex);
|
||||
glDeleteTextures(VIDEO_LAYER_MAX, context->layers);
|
||||
}
|
||||
|
||||
static void mGLContextResized(struct VideoBackend* v, unsigned w, unsigned h) {
|
||||
unsigned drawW = w;
|
||||
unsigned drawH = h;
|
||||
|
||||
unsigned maxW;
|
||||
unsigned maxH;
|
||||
VideoBackendGetFrameSize(v, &maxW, &maxH);
|
||||
|
||||
if (v->lockAspectRatio) {
|
||||
lockAspectRatioUInt(v->width, v->height, &drawW, &drawH);
|
||||
lockAspectRatioUInt(maxW, maxH, &drawW, &drawH);
|
||||
}
|
||||
if (v->lockIntegerScaling) {
|
||||
lockIntegerRatioUInt(v->width, &drawW);
|
||||
lockIntegerRatioUInt(v->height, &drawH);
|
||||
lockIntegerRatioUInt(maxW, &drawW);
|
||||
lockIntegerRatioUInt(maxH, &drawH);
|
||||
}
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
@ -105,33 +131,7 @@ static void mGLContextClear(struct VideoBackend* v) {
|
|||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void mGLContextDrawFrame(struct VideoBackend* v) {
|
||||
struct mGLContext* context = (struct mGLContext*) v;
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glVertexPointer(2, GL_INT, 0, _glVertices);
|
||||
glTexCoordPointer(2, GL_INT, 0, _glTexCoords);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glOrtho(0, v->width, v->height, 0, 0, 1);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
if (v->interframeBlending) {
|
||||
glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA);
|
||||
glBlendColor(1, 1, 1, 0.5);
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex ^ 1]);
|
||||
if (v->filter) {
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
} else {
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
}
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
glEnable(GL_BLEND);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex]);
|
||||
static void _setFilter(struct VideoBackend* v) {
|
||||
if (v->filter) {
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
@ -139,36 +139,147 @@ void mGLContextDrawFrame(struct VideoBackend* v) {
|
|||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
}
|
||||
}
|
||||
|
||||
static void _setFrame(struct Rectangle* dims, int frameW, int frameH) {
|
||||
GLint viewport[4];
|
||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
glScissor(viewport[0] + dims->x * viewport[2] / frameW,
|
||||
viewport[1] + dims->y * viewport[3] / frameH,
|
||||
dims->width * viewport[2] / frameW,
|
||||
dims->height * viewport[3] / frameH);
|
||||
glTranslatef(dims->x, dims->y, 0);
|
||||
glScalef(toPow2(dims->width), toPow2(dims->height), 1);
|
||||
}
|
||||
|
||||
void mGLContextDrawFrame(struct VideoBackend* v) {
|
||||
struct mGLContext* context = (struct mGLContext*) v;
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glVertexPointer(2, GL_INT, 0, _glVertices);
|
||||
glTexCoordPointer(2, GL_INT, 0, _glTexCoords);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
unsigned frameW, frameH;
|
||||
VideoBackendGetFrameSize(v, &frameW, &frameH);
|
||||
glOrtho(0, frameW, frameH, 0, 0, 1);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
int layer;
|
||||
for (layer = 0; layer < VIDEO_LAYER_IMAGE; ++layer) {
|
||||
if (context->layerDims[layer].width < 1 || context->layerDims[layer].height < 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
glBindTexture(GL_TEXTURE_2D, context->layers[layer]);
|
||||
_setFilter(v);
|
||||
glPushMatrix();
|
||||
_setFrame(&context->layerDims[layer], frameW, frameH);
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
_setFrame(&context->layerDims[VIDEO_LAYER_IMAGE], frameW, frameH);
|
||||
if (v->interframeBlending) {
|
||||
glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA);
|
||||
glBlendColor(1, 1, 1, 0.5);
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex ^ 1]);
|
||||
_setFilter(v);
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
glEnable(GL_BLEND);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex]);
|
||||
_setFilter(v);
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
void mGLContextPostFrame(struct VideoBackend* v, const void* frame) {
|
||||
static void mGLContextSetImageSize(struct VideoBackend* v, enum VideoLayer layer, int width, int height) {
|
||||
struct mGLContext* context = (struct mGLContext*) v;
|
||||
context->activeTex ^= 1;
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex]);
|
||||
if (layer >= VIDEO_LAYER_MAX) {
|
||||
return;
|
||||
}
|
||||
if (width <= 0 || height <= 0) {
|
||||
context->imageSizes[layer].width = -1;
|
||||
context->imageSizes[layer].height = -1;
|
||||
width = context->layerDims[layer].width;
|
||||
height = context->layerDims[layer].height;
|
||||
} else {
|
||||
context->imageSizes[layer].width = width;
|
||||
context->imageSizes[layer].height = height;
|
||||
}
|
||||
if (layer == VIDEO_LAYER_IMAGE) {
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex[0]);
|
||||
_setTexDims(width, height);
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex[1]);
|
||||
_setTexDims(width, height);
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, context->layers[layer]);
|
||||
_setTexDims(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
static void mGLContextImageSize(struct VideoBackend* v, enum VideoLayer layer, int* width, int* height) {
|
||||
struct mGLContext* context = (struct mGLContext*) v;
|
||||
if (layer >= VIDEO_LAYER_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (context->imageSizes[layer].width <= 0 || context->imageSizes[layer].height <= 0) {
|
||||
*width = context->layerDims[layer].width;
|
||||
*height = context->layerDims[layer].height;
|
||||
} else {
|
||||
*width = context->imageSizes[layer].width;
|
||||
*height = context->imageSizes[layer].height;
|
||||
}
|
||||
}
|
||||
|
||||
void mGLContextPostFrame(struct VideoBackend* v, enum VideoLayer layer, const void* frame) {
|
||||
struct mGLContext* context = (struct mGLContext*) v;
|
||||
if (layer >= VIDEO_LAYER_MAX) {
|
||||
return;
|
||||
}
|
||||
if (layer == VIDEO_LAYER_IMAGE) {
|
||||
context->activeTex ^= 1;
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex]);
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, context->layers[layer]);
|
||||
}
|
||||
|
||||
int width = context->imageSizes[layer].width;
|
||||
int height = context->imageSizes[layer].height;
|
||||
|
||||
if (width <= 0 || height <= 0) {
|
||||
width = context->layerDims[layer].width;
|
||||
height = context->layerDims[layer].height;
|
||||
}
|
||||
#ifdef COLOR_16_BIT
|
||||
#ifdef COLOR_5_6_5
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, v->width, v->height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame);
|
||||
#else
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, v->width, v->height, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, frame);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, frame);
|
||||
#endif
|
||||
#elif defined(__BIG_ENDIAN__)
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, v->width, v->height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, frame);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, frame);
|
||||
#else
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, v->width, v->height, GL_RGBA, GL_UNSIGNED_BYTE, frame);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, frame);
|
||||
#endif
|
||||
}
|
||||
|
||||
void mGLContextCreate(struct mGLContext* context) {
|
||||
context->d.init = mGLContextInit;
|
||||
context->d.deinit = mGLContextDeinit;
|
||||
context->d.setDimensions = mGLContextSetDimensions;
|
||||
context->d.resized = mGLContextResized;
|
||||
context->d.swap = 0;
|
||||
context->d.setLayerDimensions = mGLContextSetLayerDimensions;
|
||||
context->d.layerDimensions = mGLContextLayerDimensions;
|
||||
context->d.contextResized = mGLContextResized;
|
||||
context->d.swap = NULL;
|
||||
context->d.clear = mGLContextClear;
|
||||
context->d.postFrame = mGLContextPostFrame;
|
||||
context->d.setImageSize = mGLContextSetImageSize;
|
||||
context->d.imageSize = mGLContextImageSize;
|
||||
context->d.setImage = mGLContextPostFrame;
|
||||
context->d.drawFrame = mGLContextDrawFrame;
|
||||
context->d.setMessage = 0;
|
||||
context->d.clearMessage = 0;
|
||||
}
|
||||
|
|
|
@ -26,8 +26,11 @@ CXX_GUARD_START
|
|||
struct mGLContext {
|
||||
struct VideoBackend d;
|
||||
|
||||
GLuint tex[2];
|
||||
int activeTex;
|
||||
GLuint tex[2];
|
||||
GLuint layers[VIDEO_LAYER_MAX];
|
||||
struct Rectangle layerDims[VIDEO_LAYER_MAX];
|
||||
struct Size imageSizes[VIDEO_LAYER_MAX];
|
||||
};
|
||||
|
||||
void mGLContextCreate(struct mGLContext*);
|
||||
|
|
|
@ -101,12 +101,15 @@ static const GLfloat _vertices[] = {
|
|||
static void mGLES2ContextInit(struct VideoBackend* v, WHandle handle) {
|
||||
UNUSED(handle);
|
||||
struct mGLES2Context* context = (struct mGLES2Context*) v;
|
||||
v->width = 1;
|
||||
v->height = 1;
|
||||
glGenTextures(1, &context->tex);
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
memset(context->layerDims, 0, sizeof(context->layerDims));
|
||||
|
||||
glGenTextures(VIDEO_LAYER_MAX, context->tex);
|
||||
int i;
|
||||
for (i = 0; i < VIDEO_LAYER_MAX; ++i) {
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex[i]);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
glGenBuffers(1, &context->vbo);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
|
||||
|
@ -177,15 +180,7 @@ static void mGLES2ContextInit(struct VideoBackend* v, WHandle handle) {
|
|||
context->finalShader.tex = 0;
|
||||
}
|
||||
|
||||
static void mGLES2ContextSetDimensions(struct VideoBackend* v, unsigned width, unsigned height) {
|
||||
struct mGLES2Context* context = (struct mGLES2Context*) v;
|
||||
if (width == v->width && height == v->height) {
|
||||
return;
|
||||
}
|
||||
v->width = width;
|
||||
v->height = height;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex);
|
||||
static inline void _setTexDims(int width, int height) {
|
||||
#ifdef COLOR_16_BIT
|
||||
#ifdef COLOR_5_6_5
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0);
|
||||
|
@ -197,20 +192,54 @@ static void mGLES2ContextSetDimensions(struct VideoBackend* v, unsigned width, u
|
|||
#else
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t n;
|
||||
for (n = 0; n < context->nShaders; ++n) {
|
||||
if (context->shaders[n].width < 0 || context->shaders[n].height < 0) {
|
||||
context->shaders[n].dirty = true;
|
||||
static void mGLES2ContextSetLayerDimensions(struct VideoBackend* v, enum VideoLayer layer, const struct Rectangle* dims) {
|
||||
struct mGLES2Context* context = (struct mGLES2Context*) v;
|
||||
if (layer >= VIDEO_LAYER_MAX) {
|
||||
return;
|
||||
}
|
||||
if (dims->width != context->layerDims[layer].width && dims->height != context->layerDims[layer].height) {
|
||||
context->layerDims[layer].width = dims->width;
|
||||
context->layerDims[layer].height = dims->height;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex[layer]);
|
||||
if (context->imageSizes[layer].width <= 0 || context->imageSizes[layer].height <= 0) {
|
||||
_setTexDims(dims->width, dims->height);
|
||||
}
|
||||
}
|
||||
context->initialShader.dirty = true;
|
||||
context->interframeShader.dirty = true;
|
||||
|
||||
context->layerDims[layer].x = dims->x;
|
||||
context->layerDims[layer].y = dims->y;
|
||||
|
||||
unsigned newW;
|
||||
unsigned newH;
|
||||
VideoBackendGetFrameSize(v, &newW, &newH);
|
||||
if (newW != context->width || newH != context->height) {
|
||||
size_t n;
|
||||
for (n = 0; n < context->nShaders; ++n) {
|
||||
if (context->shaders[n].width < 0 || context->shaders[n].height < 0) {
|
||||
context->shaders[n].dirty = true;
|
||||
}
|
||||
}
|
||||
context->initialShader.dirty = true;
|
||||
context->interframeShader.dirty = true;
|
||||
context->width = newW;
|
||||
context->height = newH;
|
||||
}
|
||||
}
|
||||
|
||||
static void mGLES2ContextLayerDimensions(const struct VideoBackend* v, enum VideoLayer layer, struct Rectangle* dims) {
|
||||
struct mGLES2Context* context = (struct mGLES2Context*) v;
|
||||
if (layer >= VIDEO_LAYER_MAX) {
|
||||
return;
|
||||
}
|
||||
memcpy(dims, &context->layerDims[layer], sizeof(*dims));
|
||||
}
|
||||
|
||||
static void mGLES2ContextDeinit(struct VideoBackend* v) {
|
||||
struct mGLES2Context* context = (struct mGLES2Context*) v;
|
||||
glDeleteTextures(1, &context->tex);
|
||||
glDeleteTextures(VIDEO_LAYER_MAX, context->tex);
|
||||
glDeleteBuffers(1, &context->vbo);
|
||||
mGLES2ShaderDeinit(&context->initialShader);
|
||||
mGLES2ShaderDeinit(&context->finalShader);
|
||||
|
@ -222,12 +251,16 @@ static void mGLES2ContextResized(struct VideoBackend* v, unsigned w, unsigned h)
|
|||
struct mGLES2Context* context = (struct mGLES2Context*) v;
|
||||
unsigned drawW = w;
|
||||
unsigned drawH = h;
|
||||
|
||||
unsigned maxW = context->width;
|
||||
unsigned maxH = context->height;
|
||||
|
||||
if (v->lockAspectRatio) {
|
||||
lockAspectRatioUInt(v->width, v->height, &drawW, &drawH);
|
||||
lockAspectRatioUInt(maxW, maxH, &drawW, &drawH);
|
||||
}
|
||||
if (v->lockIntegerScaling) {
|
||||
lockIntegerRatioUInt(v->width, &drawW);
|
||||
lockIntegerRatioUInt(v->height, &drawH);
|
||||
lockIntegerRatioUInt(maxW, &drawW);
|
||||
lockIntegerRatioUInt(maxH, &drawH);
|
||||
}
|
||||
size_t n;
|
||||
for (n = 0; n < context->nShaders; ++n) {
|
||||
|
@ -249,7 +282,7 @@ static void mGLES2ContextClear(struct VideoBackend* v) {
|
|||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
|
||||
static void _drawShaderEx(struct mGLES2Context* context, struct mGLES2Shader* shader, int layer) {
|
||||
GLint viewport[4];
|
||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
int drawW = shader->width;
|
||||
|
@ -260,19 +293,19 @@ void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
|
|||
drawW = viewport[2];
|
||||
padW = viewport[0];
|
||||
} else if (shader->width < 0) {
|
||||
drawW = context->d.width * -shader->width;
|
||||
drawW = context->width * -shader->width;
|
||||
}
|
||||
if (!drawH) {
|
||||
drawH = viewport[3];
|
||||
padH = viewport[1];
|
||||
} else if (shader->height < 0) {
|
||||
drawH = context->d.height * -shader->height;
|
||||
drawH = context->height * -shader->height;
|
||||
}
|
||||
if (shader->integerScaling) {
|
||||
padW = 0;
|
||||
padH = 0;
|
||||
drawW -= drawW % context->d.width;
|
||||
drawH -= drawH % context->d.height;
|
||||
drawW -= drawW % context->width;
|
||||
drawH -= drawH % context->height;
|
||||
}
|
||||
|
||||
if (shader->dirty) {
|
||||
|
@ -286,22 +319,29 @@ void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
|
|||
shader->dirty = false;
|
||||
}
|
||||
|
||||
if (layer >= 0 && layer < VIDEO_LAYER_MAX) {
|
||||
glViewport(context->layerDims[layer].x, context->layerDims[layer].y, context->layerDims[layer].width, context->layerDims[layer].height);
|
||||
} else {
|
||||
glViewport(padW, padH, drawW, drawH);
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, shader->fbo);
|
||||
glViewport(padW, padH, drawW, drawH);
|
||||
if (shader->blend) {
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
} else {
|
||||
glDisable(GL_BLEND);
|
||||
glClearColor(0.f, 0.f, 0.f, 1.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
if (layer <= VIDEO_LAYER_BACKGROUND) {
|
||||
glClearColor(0.f, 0.f, 0.f, 1.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST);
|
||||
glUseProgram(shader->program);
|
||||
glUniform1i(shader->texLocation, 0);
|
||||
glUniform2f(shader->texSizeLocation, context->d.width, context->d.height);
|
||||
glUniform2f(shader->texSizeLocation, context->width, context->height);
|
||||
glUniform2f(shader->outputSizeLocation, drawW, drawH);
|
||||
#ifdef BUILD_GLES3
|
||||
if (shader->vao != (GLuint) -1) {
|
||||
|
@ -368,21 +408,36 @@ void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
|
|||
glBindTexture(GL_TEXTURE_2D, shader->tex);
|
||||
}
|
||||
|
||||
static void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
|
||||
_drawShaderEx(context, shader, -1);
|
||||
}
|
||||
|
||||
void mGLES2ContextDrawFrame(struct VideoBackend* v) {
|
||||
struct mGLES2Context* context = (struct mGLES2Context*) v;
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex);
|
||||
|
||||
GLint viewport[4];
|
||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
|
||||
context->finalShader.filter = v->filter;
|
||||
_drawShader(context, &context->initialShader);
|
||||
if (v->interframeBlending) {
|
||||
context->interframeShader.blend = true;
|
||||
glViewport(0, 0, viewport[2], viewport[3]);
|
||||
_drawShader(context, &context->interframeShader);
|
||||
|
||||
int layer;
|
||||
for (layer = 0; layer <= VIDEO_LAYER_IMAGE; ++layer) {
|
||||
if (context->layerDims[layer].width < 1 || context->layerDims[layer].height < 1) {
|
||||
continue;
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex[layer]);
|
||||
_drawShaderEx(context, &context->initialShader, layer);
|
||||
if (layer != VIDEO_LAYER_IMAGE) {
|
||||
continue;
|
||||
}
|
||||
if (v->interframeBlending) {
|
||||
context->interframeShader.blend = true;
|
||||
glViewport(0, 0, viewport[2], viewport[3]);
|
||||
_drawShader(context, &context->interframeShader);
|
||||
}
|
||||
}
|
||||
|
||||
size_t n;
|
||||
for (n = 0; n < context->nShaders; ++n) {
|
||||
glViewport(0, 0, viewport[2], viewport[3]);
|
||||
|
@ -392,8 +447,8 @@ void mGLES2ContextDrawFrame(struct VideoBackend* v) {
|
|||
_drawShader(context, &context->finalShader);
|
||||
if (v->interframeBlending) {
|
||||
context->interframeShader.blend = false;
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex);
|
||||
_drawShader(context, &context->initialShader);
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex[VIDEO_LAYER_IMAGE]);
|
||||
_drawShaderEx(context, &context->initialShader, VIDEO_LAYER_IMAGE);
|
||||
glViewport(0, 0, viewport[2], viewport[3]);
|
||||
_drawShader(context, &context->interframeShader);
|
||||
}
|
||||
|
@ -406,33 +461,79 @@ void mGLES2ContextDrawFrame(struct VideoBackend* v) {
|
|||
#endif
|
||||
}
|
||||
|
||||
void mGLES2ContextPostFrame(struct VideoBackend* v, const void* frame) {
|
||||
static void mGLES2ContextSetImageSize(struct VideoBackend* v, enum VideoLayer layer, int width, int height) {
|
||||
struct mGLES2Context* context = (struct mGLES2Context*) v;
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex);
|
||||
if (layer >= VIDEO_LAYER_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex[layer]);
|
||||
if (width <= 0 || height <= 0) {
|
||||
context->imageSizes[layer].width = -1;
|
||||
context->imageSizes[layer].height = -1;
|
||||
width = context->layerDims[layer].width;
|
||||
height = context->layerDims[layer].height;
|
||||
} else {
|
||||
context->imageSizes[layer].width = width;
|
||||
context->imageSizes[layer].height = height;
|
||||
}
|
||||
_setTexDims(width, height);
|
||||
}
|
||||
|
||||
static void mGLES2ContextImageSize(struct VideoBackend* v, enum VideoLayer layer, int* width, int* height) {
|
||||
struct mGLES2Context* context = (struct mGLES2Context*) v;
|
||||
if (layer >= VIDEO_LAYER_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (context->imageSizes[layer].width <= 0 || context->imageSizes[layer].height <= 0) {
|
||||
*width = context->layerDims[layer].width;
|
||||
*height = context->layerDims[layer].height;
|
||||
} else {
|
||||
*width = context->imageSizes[layer].width;
|
||||
*height = context->imageSizes[layer].height;
|
||||
}
|
||||
}
|
||||
|
||||
void mGLES2ContextPostFrame(struct VideoBackend* v, enum VideoLayer layer, const void* frame) {
|
||||
struct mGLES2Context* context = (struct mGLES2Context*) v;
|
||||
if (layer >= VIDEO_LAYER_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
int width = context->imageSizes[layer].width;
|
||||
int height = context->imageSizes[layer].height;
|
||||
|
||||
if (width <= 0 || height <= 0) {
|
||||
width = context->layerDims[layer].width;
|
||||
height = context->layerDims[layer].height;
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex[layer]);
|
||||
#ifdef COLOR_16_BIT
|
||||
#ifdef COLOR_5_6_5
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, v->width, v->height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame);
|
||||
#else
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, v->width, v->height, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, frame);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, frame);
|
||||
#endif
|
||||
#elif defined(__BIG_ENDIAN__)
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, v->width, v->height, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, frame);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, frame);
|
||||
#else
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, v->width, v->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame);
|
||||
#endif
|
||||
}
|
||||
|
||||
void mGLES2ContextCreate(struct mGLES2Context* context) {
|
||||
context->d.init = mGLES2ContextInit;
|
||||
context->d.deinit = mGLES2ContextDeinit;
|
||||
context->d.setDimensions = mGLES2ContextSetDimensions;
|
||||
context->d.resized = mGLES2ContextResized;
|
||||
context->d.swap = 0;
|
||||
context->d.setLayerDimensions = mGLES2ContextSetLayerDimensions;
|
||||
context->d.layerDimensions = mGLES2ContextLayerDimensions;
|
||||
context->d.contextResized = mGLES2ContextResized;
|
||||
context->d.swap = NULL;
|
||||
context->d.clear = mGLES2ContextClear;
|
||||
context->d.postFrame = mGLES2ContextPostFrame;
|
||||
context->d.setImageSize = mGLES2ContextSetImageSize;
|
||||
context->d.imageSize = mGLES2ContextImageSize;
|
||||
context->d.setImage = mGLES2ContextPostFrame;
|
||||
context->d.drawFrame = mGLES2ContextDrawFrame;
|
||||
context->d.setMessage = 0;
|
||||
context->d.clearMessage = 0;
|
||||
context->shaders = 0;
|
||||
context->nShaders = 0;
|
||||
}
|
||||
|
|
|
@ -79,9 +79,14 @@ struct mGLES2Shader {
|
|||
struct mGLES2Context {
|
||||
struct VideoBackend d;
|
||||
|
||||
GLuint tex;
|
||||
GLuint tex[VIDEO_LAYER_MAX];
|
||||
GLuint vbo;
|
||||
|
||||
struct Rectangle layerDims[VIDEO_LAYER_MAX];
|
||||
struct Size imageSizes[VIDEO_LAYER_MAX];
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
|
||||
struct mGLES2Shader initialShader;
|
||||
struct mGLES2Shader finalShader;
|
||||
struct mGLES2Shader interframeShader;
|
||||
|
|
|
@ -323,7 +323,7 @@ void mPSP2Setup(struct mGUIRunner* runner) {
|
|||
mInputBindAxis(&runner->core->inputMap, PSP2_INPUT, 1, &desc);
|
||||
|
||||
unsigned width, height;
|
||||
runner->core->desiredVideoDimensions(runner->core, &width, &height);
|
||||
runner->core->baseVideoSize(runner->core, &width, &height);
|
||||
tex[0] = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
|
||||
tex[1] = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
|
||||
currentTex = 0;
|
||||
|
@ -614,7 +614,7 @@ void mPSP2Swap(struct mGUIRunner* runner) {
|
|||
|
||||
void mPSP2Draw(struct mGUIRunner* runner, bool faded) {
|
||||
unsigned width, height;
|
||||
runner->core->desiredVideoDimensions(runner->core, &width, &height);
|
||||
runner->core->currentVideoSize(runner->core, &width, &height);
|
||||
if (interframeBlending) {
|
||||
_drawTex(tex[!currentTex], width, height, faded, false);
|
||||
}
|
||||
|
|
|
@ -235,7 +235,7 @@ class Core(object):
|
|||
def desired_video_dimensions(self):
|
||||
width = ffi.new("unsigned*")
|
||||
height = ffi.new("unsigned*")
|
||||
self._core.desiredVideoDimensions(self._core, width, height)
|
||||
self._core.currentVideoSize(self._core, width, height)
|
||||
return width[0], height[0]
|
||||
|
||||
@protected
|
||||
|
|
|
@ -266,11 +266,15 @@ mPlatform CoreController::platform() const {
|
|||
|
||||
QSize CoreController::screenDimensions() const {
|
||||
unsigned width, height;
|
||||
m_threadContext.core->desiredVideoDimensions(m_threadContext.core, &width, &height);
|
||||
m_threadContext.core->currentVideoSize(m_threadContext.core, &width, &height);
|
||||
|
||||
return QSize(width, height);
|
||||
}
|
||||
|
||||
unsigned CoreController::videoScale() const {
|
||||
return m_threadContext.core->videoScale(m_threadContext.core);
|
||||
}
|
||||
|
||||
void CoreController::loadConfig(ConfigController* config) {
|
||||
Interrupter interrupter(this);
|
||||
m_loadStateFlags = config->getOption("loadStateExtdata", m_loadStateFlags).toInt();
|
||||
|
@ -1186,7 +1190,7 @@ int CoreController::updateAutofire() {
|
|||
void CoreController::finishFrame() {
|
||||
if (!m_hwaccel) {
|
||||
unsigned width, height;
|
||||
m_threadContext.core->desiredVideoDimensions(m_threadContext.core, &width, &height);
|
||||
m_threadContext.core->currentVideoSize(m_threadContext.core, &width, &height);
|
||||
|
||||
QMutexLocker locker(&m_bufferMutex);
|
||||
memcpy(m_completeBuffer.data(), m_activeBuffer.constData(), width * height * BYTES_PER_PIXEL);
|
||||
|
|
|
@ -94,6 +94,7 @@ public:
|
|||
|
||||
mPlatform platform() const;
|
||||
QSize screenDimensions() const;
|
||||
unsigned videoScale() const;
|
||||
bool supportsFeature(Feature feature) const { return m_threadContext.core->supportsFeature(m_threadContext.core, static_cast<mCoreFeature>(feature)); }
|
||||
bool hardwareAccelerated() const { return m_hwaccel; }
|
||||
|
||||
|
|
|
@ -54,6 +54,8 @@ public:
|
|||
virtual VideoShader* shaders() = 0;
|
||||
virtual int framebufferHandle() { return -1; }
|
||||
virtual void setVideoScale(int) {}
|
||||
virtual void setBackgroundImage(const QImage&) = 0;
|
||||
virtual QSize contentSize() const = 0;
|
||||
|
||||
virtual void setVideoProxy(std::shared_ptr<VideoProxy> proxy) { m_videoProxy = proxy; }
|
||||
std::shared_ptr<VideoProxy> videoProxy() { return m_videoProxy; }
|
||||
|
|
|
@ -283,6 +283,8 @@ void DisplayGL::startThread(int from) {
|
|||
show();
|
||||
m_gl->reset();
|
||||
}
|
||||
|
||||
QTimer::singleShot(8, this, &DisplayGL::updateContentSize);
|
||||
}
|
||||
|
||||
bool DisplayGL::supportsFormat(const QSurfaceFormat& format) {
|
||||
|
@ -370,12 +372,14 @@ void DisplayGL::unpauseDrawing() {
|
|||
if (!m_gl && shouldDisableUpdates()) {
|
||||
setUpdatesEnabled(false);
|
||||
}
|
||||
QMetaObject::invokeMethod(this, "updateContentSize", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayGL::forceDraw() {
|
||||
if (m_hasStarted) {
|
||||
QMetaObject::invokeMethod(m_painter.get(), "forceDraw");
|
||||
QMetaObject::invokeMethod(this, "updateContentSize", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -438,6 +442,11 @@ void DisplayGL::setVideoScale(int scale) {
|
|||
QMetaObject::invokeMethod(m_painter.get(), "resizeContext");
|
||||
}
|
||||
|
||||
void DisplayGL::setBackgroundImage(const QImage& image) {
|
||||
QMetaObject::invokeMethod(m_painter.get(), "setBackgroundImage", Q_ARG(const QImage&, image));
|
||||
QMetaObject::invokeMethod(this, "updateContentSize", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void DisplayGL::resizeEvent(QResizeEvent* event) {
|
||||
Display::resizeEvent(event);
|
||||
resizePainter();
|
||||
|
@ -491,6 +500,10 @@ void DisplayGL::setupProxyThread() {
|
|||
m_proxyThread.start();
|
||||
}
|
||||
|
||||
void DisplayGL::updateContentSize() {
|
||||
QMetaObject::invokeMethod(m_painter.get(), "contentSize", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QSize, m_cachedContentSize));
|
||||
}
|
||||
|
||||
int DisplayGL::framebufferHandle() {
|
||||
return m_painter->glTex();
|
||||
}
|
||||
|
@ -669,13 +682,44 @@ void PainterGL::resizeContext() {
|
|||
return;
|
||||
}
|
||||
dequeueAll(false);
|
||||
m_backend->setDimensions(m_backend, size.width(), size.height());
|
||||
|
||||
Rectangle dims = {0, 0, size.width(), size.height()};
|
||||
m_backend->setLayerDimensions(m_backend, VIDEO_LAYER_IMAGE, &dims);
|
||||
recenterLayers();
|
||||
}
|
||||
|
||||
void PainterGL::setMessagePainter(MessagePainter* messagePainter) {
|
||||
m_messagePainter = messagePainter;
|
||||
}
|
||||
|
||||
void PainterGL::recenterLayers() {
|
||||
if (!m_context) {
|
||||
return;
|
||||
}
|
||||
const static std::initializer_list<VideoLayer> centeredLayers{VIDEO_LAYER_BACKGROUND, VIDEO_LAYER_IMAGE};
|
||||
Rectangle frame = {0};
|
||||
unsigned scale = std::max(1U, m_context->videoScale());
|
||||
for (VideoLayer l : centeredLayers) {
|
||||
Rectangle dims{};
|
||||
int width, height;
|
||||
m_backend->imageSize(m_backend, l, &width, &height);
|
||||
dims.width = width;
|
||||
dims.height = height;
|
||||
if (l != VIDEO_LAYER_IMAGE) {
|
||||
dims.width *= scale;
|
||||
dims.height *= scale;
|
||||
m_backend->setLayerDimensions(m_backend, l, &dims);
|
||||
}
|
||||
RectangleUnion(&frame, &dims);
|
||||
}
|
||||
for (VideoLayer l : centeredLayers) {
|
||||
Rectangle dims;
|
||||
m_backend->layerDimensions(m_backend, l, &dims);
|
||||
RectangleCenter(&frame, &dims);
|
||||
m_backend->setLayerDimensions(m_backend, l, &dims);
|
||||
}
|
||||
}
|
||||
|
||||
void PainterGL::resize(const QSize& size) {
|
||||
qreal r = m_window->devicePixelRatio();
|
||||
m_size = size;
|
||||
|
@ -844,9 +888,9 @@ void PainterGL::unpause() {
|
|||
|
||||
void PainterGL::performDraw() {
|
||||
float r = m_window->devicePixelRatio();
|
||||
m_backend->resized(m_backend, m_size.width() * r, m_size.height() * r);
|
||||
m_backend->contextResized(m_backend, m_size.width() * r, m_size.height() * r);
|
||||
if (m_buffer) {
|
||||
m_backend->postFrame(m_backend, m_buffer);
|
||||
m_backend->setImage(m_backend, VIDEO_LAYER_IMAGE, m_buffer);
|
||||
}
|
||||
m_backend->drawFrame(m_backend);
|
||||
if (m_showOSD && m_messagePainter && m_paintDev && !glContextHasBug(OpenGLBug::IG4ICD_CRASH)) {
|
||||
|
@ -902,7 +946,7 @@ void PainterGL::dequeue() {
|
|||
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
||||
if (supportsShaders()) {
|
||||
mGLES2Context* gl2Backend = reinterpret_cast<mGLES2Context*>(m_backend);
|
||||
gl2Backend->tex = m_bridgeTexOut;
|
||||
gl2Backend->tex[VIDEO_LAYER_IMAGE] = m_bridgeTexOut;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -996,11 +1040,18 @@ VideoShader* PainterGL::shaders() {
|
|||
return &m_shader;
|
||||
}
|
||||
|
||||
QSize PainterGL::contentSize() const {
|
||||
unsigned width, height;
|
||||
VideoBackendGetFrameSize(m_backend, &width, &height);
|
||||
return {static_cast<int>(width > static_cast<unsigned>(INT_MAX) ? INT_MAX : width),
|
||||
static_cast<int>(height > static_cast<unsigned>(INT_MAX) ? INT_MAX : height)};
|
||||
}
|
||||
|
||||
int PainterGL::glTex() {
|
||||
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
||||
if (supportsShaders()) {
|
||||
mGLES2Context* gl2Backend = reinterpret_cast<mGLES2Context*>(m_backend);
|
||||
return gl2Backend->tex;
|
||||
return gl2Backend->tex[VIDEO_LAYER_IMAGE];
|
||||
}
|
||||
#endif
|
||||
#ifdef BUILD_GL
|
||||
|
@ -1036,6 +1087,27 @@ void PainterGL::updateFramebufferHandle() {
|
|||
m_context->setFramebufferHandle(m_bridgeTexIn);
|
||||
}
|
||||
|
||||
void PainterGL::setBackgroundImage(const QImage& image) {
|
||||
if (!m_started) {
|
||||
makeCurrent();
|
||||
}
|
||||
|
||||
m_backend->setImageSize(m_backend, VIDEO_LAYER_BACKGROUND, image.width(), image.height());
|
||||
recenterLayers();
|
||||
|
||||
if (!image.isNull()) {
|
||||
m_background = image.convertToFormat(QImage::Format_RGB32);
|
||||
m_background = m_background.rgbSwapped();
|
||||
m_backend->setImage(m_backend, VIDEO_LAYER_BACKGROUND, m_background.constBits());
|
||||
} else {
|
||||
m_background = QImage();
|
||||
}
|
||||
|
||||
if (!m_started) {
|
||||
m_gl->doneCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
void PainterGL::swapTex() {
|
||||
if (!m_started) {
|
||||
return;
|
||||
|
|
|
@ -94,6 +94,7 @@ public:
|
|||
VideoShader* shaders() override;
|
||||
void setVideoProxy(std::shared_ptr<VideoProxy>) override;
|
||||
int framebufferHandle() override;
|
||||
QSize contentSize() const override { return m_cachedContentSize; }
|
||||
|
||||
static bool supportsFormat(const QSurfaceFormat&);
|
||||
|
||||
|
@ -113,6 +114,7 @@ public slots:
|
|||
void clearShaders() override;
|
||||
void resizeContext() override;
|
||||
void setVideoScale(int scale) override;
|
||||
void setBackgroundImage(const QImage&) override;
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent*) override { forceDraw(); }
|
||||
|
@ -121,6 +123,7 @@ protected:
|
|||
private slots:
|
||||
void startThread(int);
|
||||
void setupProxyThread();
|
||||
void updateContentSize();
|
||||
|
||||
private:
|
||||
void resizePainter();
|
||||
|
@ -138,6 +141,7 @@ private:
|
|||
mGLWidget* m_gl;
|
||||
QOffscreenSurface m_proxySurface;
|
||||
std::unique_ptr<QOpenGLContext> m_proxyContext;
|
||||
QSize m_cachedContentSize;
|
||||
};
|
||||
|
||||
class PainterGL : public QObject {
|
||||
|
@ -184,10 +188,12 @@ public slots:
|
|||
void filter(bool filter);
|
||||
void resizeContext();
|
||||
void updateFramebufferHandle();
|
||||
void setBackgroundImage(const QImage&);
|
||||
|
||||
void setShaders(struct VDir*);
|
||||
void clearShaders();
|
||||
VideoShader* shaders();
|
||||
QSize contentSize() const;
|
||||
|
||||
signals:
|
||||
void created();
|
||||
|
@ -202,6 +208,7 @@ private:
|
|||
void performDraw();
|
||||
void dequeue();
|
||||
void dequeueAll(bool keep = false);
|
||||
void recenterLayers();
|
||||
|
||||
std::array<std::array<uint32_t, 0x100000>, 3> m_buffers;
|
||||
QList<uint32_t*> m_free;
|
||||
|
@ -220,6 +227,7 @@ private:
|
|||
QWindow* m_window;
|
||||
QSurface* m_surface;
|
||||
QSurfaceFormat m_format;
|
||||
QImage m_background;
|
||||
std::unique_ptr<QOpenGLPaintDevice> m_paintDev;
|
||||
std::unique_ptr<QOpenGLContext> m_gl;
|
||||
int m_finalTexIdx = 0;
|
||||
|
|
|
@ -92,21 +92,103 @@ void DisplayQt::resizeContext() {
|
|||
}
|
||||
}
|
||||
|
||||
void DisplayQt::setBackgroundImage(const QImage& image) {
|
||||
m_background = image;
|
||||
update();
|
||||
}
|
||||
|
||||
void DisplayQt::paintEvent(QPaintEvent*) {
|
||||
QPainter painter(this);
|
||||
painter.fillRect(QRect(QPoint(), size()), Qt::black);
|
||||
if (isFiltered()) {
|
||||
painter.setRenderHint(QPainter::SmoothPixmapTransform);
|
||||
}
|
||||
QRect full(clampSize(QSize(m_width, m_height), size(), isAspectRatioLocked(), isIntegerScalingLocked()));
|
||||
|
||||
QRect bgRect(0, 0, m_background.width(), m_background.height());
|
||||
QRect imRect(0, 0, m_width, m_height);
|
||||
QSize outerFrame = contentSize();
|
||||
|
||||
if (bgRect.width() > imRect.width()) {
|
||||
imRect.moveLeft(bgRect.width() - imRect.width());
|
||||
} else {
|
||||
bgRect.moveLeft(imRect.width() - bgRect.width());
|
||||
}
|
||||
|
||||
if (bgRect.height() > imRect.height()) {
|
||||
imRect.moveTop(bgRect.height() - imRect.height());
|
||||
} else {
|
||||
bgRect.moveTop(imRect.height() - bgRect.height());
|
||||
}
|
||||
|
||||
QRect full(clampSize(outerFrame, size(), isAspectRatioLocked(), isIntegerScalingLocked()));
|
||||
|
||||
if (m_background.isNull()) {
|
||||
imRect = full;
|
||||
} else {
|
||||
if (imRect.x()) {
|
||||
imRect.moveLeft(imRect.x() * full.width() / bgRect.width() / 2);
|
||||
imRect.setWidth(imRect.width() * full.width() / bgRect.width());
|
||||
bgRect.setWidth(full.width());
|
||||
} else {
|
||||
bgRect.moveLeft(bgRect.x() * full.width() / imRect.width() / 2);
|
||||
bgRect.setWidth(bgRect.width() * full.width() / imRect.width());
|
||||
imRect.setWidth(full.width());
|
||||
}
|
||||
if (imRect.y()) {
|
||||
imRect.moveTop(imRect.y() * full.height() / bgRect.height() / 2);
|
||||
imRect.setHeight(imRect.height() * full.height() / bgRect.height());
|
||||
bgRect.setHeight(full.height());
|
||||
} else {
|
||||
bgRect.moveTop(bgRect.y() * full.height() / imRect.height() / 2);
|
||||
bgRect.setHeight(bgRect.height() * full.height() / imRect.height());
|
||||
imRect.setHeight(full.height());
|
||||
}
|
||||
|
||||
if (bgRect.right() > imRect.right()) {
|
||||
if (bgRect.right() < full.right()) {
|
||||
imRect.translate((full.right() - bgRect.right()), 0);
|
||||
bgRect.translate((full.right() - bgRect.right()), 0);
|
||||
}
|
||||
} else {
|
||||
if (imRect.right() < full.right()) {
|
||||
bgRect.translate((full.right() - imRect.right()), 0);
|
||||
imRect.translate((full.right() - imRect.right()), 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (bgRect.bottom() > imRect.bottom()) {
|
||||
if (bgRect.bottom() < full.bottom()) {
|
||||
imRect.translate(0, (full.bottom() - bgRect.bottom()));
|
||||
bgRect.translate(0, (full.bottom() - bgRect.bottom()));
|
||||
}
|
||||
} else {
|
||||
if (imRect.bottom() < full.bottom()) {
|
||||
bgRect.translate(0, (full.bottom() - imRect.bottom()));
|
||||
imRect.translate(0, (full.bottom() - imRect.bottom()));
|
||||
}
|
||||
}
|
||||
painter.drawImage(bgRect, m_background);
|
||||
}
|
||||
|
||||
if (hasInterframeBlending()) {
|
||||
painter.drawImage(full, m_oldBacking, QRect(0, 0, m_width, m_height));
|
||||
painter.drawImage(imRect, m_oldBacking, QRect(0, 0, m_width, m_height));
|
||||
painter.setOpacity(0.5);
|
||||
}
|
||||
painter.drawImage(full, m_backing, QRect(0, 0, m_width, m_height));
|
||||
painter.drawImage(imRect, m_backing, QRect(0, 0, m_width, m_height));
|
||||
painter.setOpacity(1);
|
||||
if (isShowOSD() || isShowFrameCounter()) {
|
||||
messagePainter()->paint(&painter);
|
||||
}
|
||||
}
|
||||
|
||||
QSize DisplayQt::contentSize() const {
|
||||
QSize outerFrame(m_width, m_height);
|
||||
|
||||
if (m_background.width() > outerFrame.width()) {
|
||||
outerFrame.setWidth(m_background.width());
|
||||
}
|
||||
if (m_background.height() > outerFrame.height()) {
|
||||
outerFrame.setHeight(m_background.height());
|
||||
}
|
||||
return outerFrame;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ public:
|
|||
bool isDrawing() const override { return m_isDrawing; }
|
||||
bool supportsShaders() const override { return false; }
|
||||
VideoShader* shaders() override { return nullptr; }
|
||||
QSize contentSize() const override;
|
||||
|
||||
public slots:
|
||||
void stopDrawing() override;
|
||||
|
@ -36,6 +37,7 @@ public slots:
|
|||
void setShaders(struct VDir*) override {}
|
||||
void clearShaders() override {}
|
||||
void resizeContext() override;
|
||||
void setBackgroundImage(const QImage&) override;
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent*) override;
|
||||
|
@ -46,6 +48,7 @@ private:
|
|||
int m_height = -1;
|
||||
QImage m_backing{nullptr};
|
||||
QImage m_oldBacking{nullptr};
|
||||
QImage m_background;
|
||||
std::shared_ptr<CoreController> m_context = nullptr;
|
||||
};
|
||||
|
||||
|
|
|
@ -559,7 +559,7 @@ void FrameView::newVl() {
|
|||
}
|
||||
#endif
|
||||
unsigned width, height;
|
||||
m_vl->desiredVideoDimensions(m_vl, &width, &height);
|
||||
m_vl->baseVideoSize(m_vl, &width, &height);
|
||||
m_framebuffer = QImage(width, height, QImage::Format_RGBX8888);
|
||||
m_vl->setVideoBuffer(m_vl, reinterpret_cast<color_t*>(m_framebuffer.bits()), width);
|
||||
m_vl->reset(m_vl);
|
||||
|
|
|
@ -143,6 +143,9 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
|
|||
connect(m_ui.cheatsBrowse, &QAbstractButton::pressed, [this] () {
|
||||
selectPath(m_ui.cheatsPath, m_ui.cheatsSameDir);
|
||||
});
|
||||
connect(m_ui.bgImageBrowse, &QAbstractButton::pressed, [this] () {
|
||||
selectImage(m_ui.bgImage);
|
||||
});
|
||||
connect(m_ui.clearCache, &QAbstractButton::pressed, this, &SettingsView::libraryCleared);
|
||||
|
||||
// TODO: Move to reloadConfig()
|
||||
|
@ -445,6 +448,13 @@ void SettingsView::selectPath(QLineEdit* field, QCheckBox* sameDir) {
|
|||
}
|
||||
}
|
||||
|
||||
void SettingsView::selectImage(QLineEdit* field) {
|
||||
QString path = GBAApp::app()->getOpenFileName(this, tr("Select image"), tr("Image file (*.png *.jpg *.jpeg)"));
|
||||
if (!path.isNull()) {
|
||||
field->setText(makePortablePath(path));
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsView::updateConfig() {
|
||||
saveSetting("gba.bios", m_ui.gbaBios);
|
||||
saveSetting("gb.bios", m_ui.gbBios);
|
||||
|
@ -501,6 +511,7 @@ void SettingsView::updateConfig() {
|
|||
saveSetting("vbaBugCompat", m_ui.vbaBugCompat);
|
||||
saveSetting("updateAutoCheck", m_ui.updateAutoCheck);
|
||||
saveSetting("showFilenameInLibrary", m_ui.showFilenameInLibrary);
|
||||
saveSetting("backgroundImage", m_ui.bgImage);
|
||||
|
||||
if (m_ui.audioBufferSize->currentText().toInt() > 8192) {
|
||||
m_ui.audioBufferSize->setCurrentText("8192");
|
||||
|
@ -725,6 +736,7 @@ void SettingsView::reloadConfig() {
|
|||
loadSetting("vbaBugCompat", m_ui.vbaBugCompat, true);
|
||||
loadSetting("updateAutoCheck", m_ui.updateAutoCheck);
|
||||
loadSetting("showFilenameInLibrary", m_ui.showFilenameInLibrary);
|
||||
loadSetting("backgroundImage", m_ui.bgImage);
|
||||
|
||||
m_ui.libraryStyle->setCurrentIndex(loadSetting("libraryStyle").toInt());
|
||||
|
||||
|
|
|
@ -71,6 +71,7 @@ public slots:
|
|||
private slots:
|
||||
void selectBios(QLineEdit*);
|
||||
void selectPath(QLineEdit*, QCheckBox*);
|
||||
void selectImage(QLineEdit*);
|
||||
void updateConfig();
|
||||
void reloadConfig();
|
||||
void updateChecked();
|
||||
|
|
|
@ -907,6 +907,41 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="19" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_22">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="bgImage">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="bgImageBrowse">
|
||||
<property name="text">
|
||||
<string>Browse</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="18" column="0" colspan="2">
|
||||
<widget class="Line" name="line_13">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="19" column="0">
|
||||
<widget class="QLabel" name="label_53">
|
||||
<property name="text">
|
||||
<string>Custom border:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="update">
|
||||
|
|
|
@ -1053,6 +1053,8 @@ void Window::reloadDisplayDriver() {
|
|||
#elif defined(M_CORE_GBA)
|
||||
m_display->setMinimumSize(GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
|
||||
#endif
|
||||
|
||||
m_display->setBackgroundImage(QImage{m_config->getOption("backgroundImage")});
|
||||
}
|
||||
|
||||
void Window::reloadAudioDriver() {
|
||||
|
@ -1486,8 +1488,8 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
Action* setSize = m_frameSizes[i];
|
||||
showNormal();
|
||||
QSize size(GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
|
||||
if (m_controller) {
|
||||
size = m_controller->screenDimensions();
|
||||
if (m_display) {
|
||||
size = m_display->contentSize();
|
||||
}
|
||||
size *= i;
|
||||
m_savedScale = i;
|
||||
|
@ -1875,6 +1877,13 @@ void Window::setupOptions() {
|
|||
updateTitle();
|
||||
}, this);
|
||||
|
||||
ConfigOption* backgroundImage = m_config->addOption("backgroundImage");
|
||||
backgroundImage->connect([this](const QVariant& value) {
|
||||
if (m_display) {
|
||||
m_display->setBackgroundImage(QImage{value.toString()});
|
||||
}
|
||||
}, this);
|
||||
m_config->updateOption("backgroundImage");
|
||||
}
|
||||
|
||||
void Window::attachWidget(QWidget* widget) {
|
||||
|
|
|
@ -5,10 +5,17 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "main.h"
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/thread.h>
|
||||
#include <mgba/core/version.h>
|
||||
|
||||
#ifdef USE_PNG
|
||||
#include <mgba-util/png-io.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
#endif
|
||||
|
||||
void mSDLGLDoViewport(int w, int h, struct VideoBackend* v) {
|
||||
v->resized(v, w, h);
|
||||
v->contextResized(v, w, h);
|
||||
v->clear(v);
|
||||
v->swap(v);
|
||||
v->clear(v);
|
||||
|
@ -24,6 +31,60 @@ void mSDLGLCommonSwap(struct VideoBackend* context) {
|
|||
#endif
|
||||
}
|
||||
|
||||
bool mSDLGLCommonLoadBackground(struct VideoBackend* context) {
|
||||
#ifdef USE_PNG
|
||||
struct mSDLRenderer* renderer = context->user;
|
||||
const char* bgImage = mCoreConfigGetValue(&renderer->core->config, "backgroundImage");
|
||||
if (!bgImage) {
|
||||
return false;
|
||||
}
|
||||
struct VFile* vf = VFileOpen(bgImage, O_RDONLY);
|
||||
if (!vf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
png_structp png = PNGReadOpen(vf, 0);
|
||||
png_infop info = png_create_info_struct(png);
|
||||
png_infop end = png_create_info_struct(png);
|
||||
if (!png || !info || !end) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!PNGReadHeader(png, info)) {
|
||||
goto done;
|
||||
}
|
||||
unsigned width = png_get_image_width(png, info);
|
||||
unsigned height = png_get_image_height(png, info);
|
||||
uint32_t* pixels = malloc(width * height * 4);
|
||||
if (!pixels) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!PNGReadPixels(png, info, pixels, width, height, width) || !PNGReadFooter(png, end)) {
|
||||
free(pixels);
|
||||
goto done;
|
||||
}
|
||||
|
||||
struct Rectangle dims = {
|
||||
.width = width,
|
||||
.height = height
|
||||
};
|
||||
context->setLayerDimensions(context, VIDEO_LAYER_BACKGROUND, &dims);
|
||||
context->setImage(context, VIDEO_LAYER_BACKGROUND, pixels);
|
||||
free(pixels);
|
||||
ok = true;
|
||||
|
||||
done:
|
||||
PNGReadClose(png, info, end);
|
||||
vf->close(vf);
|
||||
return ok;
|
||||
#else
|
||||
UNUSED(context);
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool mSDLGLCommonInit(struct mSDLRenderer* renderer) {
|
||||
#ifndef COLOR_16_BIT
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
|
||||
|
@ -66,3 +127,61 @@ bool mSDLGLCommonInit(struct mSDLRenderer* renderer) {
|
|||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void mSDLGLCommonRunloop(struct mSDLRenderer* renderer, void* user) {
|
||||
struct mCoreThread* context = user;
|
||||
SDL_Event event;
|
||||
struct VideoBackend* v = renderer->backend;
|
||||
|
||||
if (mSDLGLCommonLoadBackground(v)) {
|
||||
renderer->player.windowUpdated = true;
|
||||
|
||||
struct Rectangle frame;
|
||||
VideoBackendGetFrame(v, &frame);
|
||||
int i;
|
||||
for (i = 0; i <= VIDEO_LAYER_IMAGE; ++i) {
|
||||
struct Rectangle dims;
|
||||
v->layerDimensions(v, i, &dims);
|
||||
RectangleCenter(&frame, &dims);
|
||||
v->setLayerDimensions(v, i, &dims);
|
||||
}
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
SDL_SetWindowSize(renderer->window, frame.width * renderer->ratio, frame.height * renderer->ratio);
|
||||
#endif
|
||||
}
|
||||
|
||||
while (mCoreThreadIsActive(context)) {
|
||||
while (SDL_PollEvent(&event)) {
|
||||
mSDLHandleEvent(context, &renderer->player, &event);
|
||||
// Event handling can change the size of the screen
|
||||
if (renderer->player.windowUpdated) {
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
SDL_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight);
|
||||
#else
|
||||
renderer->viewportWidth = renderer->player.newWidth;
|
||||
renderer->viewportHeight = renderer->player.newHeight;
|
||||
mSDLGLCommonInit(renderer);
|
||||
#endif
|
||||
mSDLGLDoViewport(renderer->viewportWidth, renderer->viewportHeight, v);
|
||||
renderer->player.windowUpdated = 0;
|
||||
}
|
||||
}
|
||||
renderer->core->currentVideoSize(renderer->core, &renderer->width, &renderer->height);
|
||||
struct Rectangle dims;
|
||||
v->layerDimensions(v, VIDEO_LAYER_IMAGE, &dims);
|
||||
if (renderer->width != dims.width || renderer->height != dims.height) {
|
||||
renderer->core->setVideoBuffer(renderer->core, renderer->outputBuffer, renderer->width);
|
||||
dims.width = renderer->width;
|
||||
dims.height = renderer->height;
|
||||
v->setLayerDimensions(v, VIDEO_LAYER_IMAGE, &dims);
|
||||
}
|
||||
|
||||
if (mCoreSyncWaitFrameStart(&context->impl->sync)) {
|
||||
v->setImage(v, VIDEO_LAYER_IMAGE, renderer->outputBuffer);
|
||||
}
|
||||
mCoreSyncWaitFrameEnd(&context->impl->sync);
|
||||
v->drawFrame(v);
|
||||
v->swap(v);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ struct mSDLRenderer;
|
|||
void mSDLGLDoViewport(int w, int h, struct VideoBackend* v);
|
||||
void mSDLGLCommonSwap(struct VideoBackend* context);
|
||||
bool mSDLGLCommonInit(struct mSDLRenderer* renderer);
|
||||
void mSDLGLCommonRunloop(struct mSDLRenderer* renderer, void* user);
|
||||
bool mSDLGLCommonLoadBackground(struct VideoBackend* context);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
|
|
|
@ -8,19 +8,17 @@
|
|||
#include "gl-common.h"
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/thread.h>
|
||||
#include <mgba-util/math.h>
|
||||
|
||||
#include "platform/opengl/gl.h"
|
||||
|
||||
static bool mSDLGLInit(struct mSDLRenderer* renderer);
|
||||
static void mSDLGLRunloop(struct mSDLRenderer* renderer, void* user);
|
||||
static void mSDLGLDeinit(struct mSDLRenderer* renderer);
|
||||
|
||||
void mSDLGLCreate(struct mSDLRenderer* renderer) {
|
||||
renderer->init = mSDLGLInit;
|
||||
renderer->deinit = mSDLGLDeinit;
|
||||
renderer->runloop = mSDLGLRunloop;
|
||||
renderer->runloop = mSDLGLCommonRunloop;
|
||||
renderer->backend = &renderer->gl.d;
|
||||
}
|
||||
|
||||
bool mSDLGLInit(struct mSDLRenderer* renderer) {
|
||||
|
@ -37,48 +35,18 @@ bool mSDLGLInit(struct mSDLRenderer* renderer) {
|
|||
renderer->gl.d.filter = renderer->filter;
|
||||
renderer->gl.d.swap = mSDLGLCommonSwap;
|
||||
renderer->gl.d.init(&renderer->gl.d, 0);
|
||||
renderer->gl.d.setDimensions(&renderer->gl.d, renderer->width, renderer->height);
|
||||
struct Rectangle dims = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = renderer->width,
|
||||
.height = renderer->height
|
||||
};
|
||||
renderer->gl.d.setLayerDimensions(&renderer->gl.d, VIDEO_LAYER_IMAGE, &dims);
|
||||
|
||||
mSDLGLDoViewport(renderer->viewportWidth, renderer->viewportHeight, &renderer->gl.d);
|
||||
return true;
|
||||
}
|
||||
|
||||
void mSDLGLRunloop(struct mSDLRenderer* renderer, void* user) {
|
||||
struct mCoreThread* context = user;
|
||||
SDL_Event event;
|
||||
struct VideoBackend* v = &renderer->gl.d;
|
||||
|
||||
while (mCoreThreadIsActive(context)) {
|
||||
while (SDL_PollEvent(&event)) {
|
||||
mSDLHandleEvent(context, &renderer->player, &event);
|
||||
// Event handling can change the size of the screen
|
||||
if (renderer->player.windowUpdated) {
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
SDL_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight);
|
||||
#else
|
||||
renderer->viewportWidth = renderer->player.newWidth;
|
||||
renderer->viewportHeight = renderer->player.newHeight;
|
||||
mSDLGLCommonInit(renderer);
|
||||
#endif
|
||||
mSDLGLDoViewport(renderer->viewportWidth, renderer->viewportHeight, v);
|
||||
renderer->player.windowUpdated = 0;
|
||||
}
|
||||
}
|
||||
renderer->core->desiredVideoDimensions(renderer->core, &renderer->width, &renderer->height);
|
||||
if (renderer->width != v->width || renderer->height != v->height) {
|
||||
renderer->core->setVideoBuffer(renderer->core, renderer->outputBuffer, renderer->width);
|
||||
v->setDimensions(v, renderer->width, renderer->height);
|
||||
}
|
||||
|
||||
if (mCoreSyncWaitFrameStart(&context->impl->sync)) {
|
||||
v->postFrame(v, renderer->outputBuffer);
|
||||
}
|
||||
mCoreSyncWaitFrameEnd(&context->impl->sync);
|
||||
v->drawFrame(v);
|
||||
v->swap(v);
|
||||
}
|
||||
}
|
||||
|
||||
void mSDLGLDeinit(struct mSDLRenderer* renderer) {
|
||||
if (renderer->gl.d.deinit) {
|
||||
renderer->gl.d.deinit(&renderer->gl.d);
|
||||
|
|
|
@ -11,20 +11,19 @@
|
|||
#endif
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/thread.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
static bool mSDLGLES2Init(struct mSDLRenderer* renderer);
|
||||
static void mSDLGLES2Runloop(struct mSDLRenderer* renderer, void* user);
|
||||
static void mSDLGLES2Deinit(struct mSDLRenderer* renderer);
|
||||
|
||||
void mSDLGLES2Create(struct mSDLRenderer* renderer) {
|
||||
renderer->init = mSDLGLES2Init;
|
||||
renderer->deinit = mSDLGLES2Deinit;
|
||||
renderer->runloop = mSDLGLES2Runloop;
|
||||
renderer->runloop = mSDLGLCommonRunloop;
|
||||
renderer->backend = &renderer->gl2.d;
|
||||
}
|
||||
|
||||
bool mSDLGLES2Init(struct mSDLRenderer* renderer) {
|
||||
|
@ -50,43 +49,19 @@ bool mSDLGLES2Init(struct mSDLRenderer* renderer) {
|
|||
renderer->gl2.d.swap = mSDLGLCommonSwap;
|
||||
#endif
|
||||
renderer->gl2.d.init(&renderer->gl2.d, 0);
|
||||
renderer->gl2.d.setDimensions(&renderer->gl2.d, renderer->width, renderer->height);
|
||||
|
||||
struct Rectangle dims = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = renderer->width,
|
||||
.height = renderer->height
|
||||
};
|
||||
renderer->gl2.d.setLayerDimensions(&renderer->gl2.d, VIDEO_LAYER_IMAGE, &dims);
|
||||
|
||||
mSDLGLDoViewport(renderer->viewportWidth, renderer->viewportHeight, &renderer->gl2.d);
|
||||
return true;
|
||||
}
|
||||
|
||||
void mSDLGLES2Runloop(struct mSDLRenderer* renderer, void* user) {
|
||||
struct mCoreThread* context = user;
|
||||
SDL_Event event;
|
||||
struct VideoBackend* v = &renderer->gl2.d;
|
||||
|
||||
while (mCoreThreadIsActive(context)) {
|
||||
while (SDL_PollEvent(&event)) {
|
||||
mSDLHandleEvent(context, &renderer->player, &event);
|
||||
// Event handling can change the size of the screen
|
||||
if (renderer->player.windowUpdated) {
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
SDL_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight);
|
||||
#else
|
||||
renderer->viewportWidth = renderer->player.newWidth;
|
||||
renderer->viewportHeight = renderer->player.newHeight;
|
||||
mSDLGLCommonInit(renderer);
|
||||
#endif
|
||||
mSDLGLDoViewport(renderer->viewportWidth, renderer->viewportHeight, v);
|
||||
renderer->player.windowUpdated = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (mCoreSyncWaitFrameStart(&context->impl->sync)) {
|
||||
v->postFrame(v, renderer->outputBuffer);
|
||||
}
|
||||
mCoreSyncWaitFrameEnd(&context->impl->sync);
|
||||
v->drawFrame(v);
|
||||
v->swap(v);
|
||||
}
|
||||
}
|
||||
|
||||
void mSDLGLES2Deinit(struct mSDLRenderer* renderer) {
|
||||
if (renderer->gl2.d.deinit) {
|
||||
renderer->gl2.d.deinit(&renderer->gl2.d);
|
||||
|
|
|
@ -107,7 +107,7 @@ int main(int argc, char** argv) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
renderer.core->desiredVideoDimensions(renderer.core, &renderer.width, &renderer.height);
|
||||
renderer.core->baseVideoSize(renderer.core, &renderer.width, &renderer.height);
|
||||
renderer.ratio = graphicsOpts.multiplier;
|
||||
if (renderer.ratio == 0) {
|
||||
renderer.ratio = 1;
|
||||
|
@ -275,7 +275,7 @@ int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) {
|
|||
|
||||
if (!didFail) {
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
renderer->core->desiredVideoDimensions(renderer->core, &renderer->width, &renderer->height);
|
||||
renderer->core->currentVideoSize(renderer->core, &renderer->width, &renderer->height);
|
||||
unsigned width = renderer->width * renderer->ratio;
|
||||
unsigned height = renderer->height * renderer->ratio;
|
||||
if (width != (unsigned) renderer->viewportWidth && height != (unsigned) renderer->viewportHeight) {
|
||||
|
|
|
@ -76,6 +76,8 @@ struct mSDLRenderer {
|
|||
struct mGLES2Context gl2;
|
||||
#endif
|
||||
|
||||
struct VideoBackend* backend;
|
||||
|
||||
#ifdef USE_PIXMAN
|
||||
pixman_image_t* pix;
|
||||
pixman_image_t* screenpix;
|
||||
|
|
|
@ -28,7 +28,7 @@ bool mSDLSWInit(struct mSDLRenderer* renderer) {
|
|||
SDL_WM_SetCaption(projectName, "");
|
||||
|
||||
unsigned width, height;
|
||||
renderer->core->desiredVideoDimensions(renderer->core, &width, &height);
|
||||
renderer->core->baseVideoSize(renderer->core, &width, &height);
|
||||
SDL_Surface* surface = SDL_GetVideoSurface();
|
||||
SDL_LockSurface(surface);
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ void mSDLSWCreate(struct mSDLRenderer* renderer) {
|
|||
|
||||
bool mSDLSWInit(struct mSDLRenderer* renderer) {
|
||||
unsigned width, height;
|
||||
renderer->core->desiredVideoDimensions(renderer->core, &width, &height);
|
||||
renderer->core->baseVideoSize(renderer->core, &width, &height);
|
||||
renderer->window = SDL_CreateWindow(projectName, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, renderer->viewportWidth, renderer->viewportHeight, SDL_WINDOW_OPENGL | (SDL_WINDOW_FULLSCREEN_DESKTOP * renderer->player.fullscreen));
|
||||
SDL_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight);
|
||||
renderer->player.window = renderer->window;
|
||||
|
|
|
@ -477,7 +477,7 @@ static void _drawFrame(struct mGUIRunner* runner, bool faded) {
|
|||
}
|
||||
|
||||
unsigned width, height;
|
||||
runner->core->desiredVideoDimensions(runner->core, &width, &height);
|
||||
runner->core->currentVideoSize(runner->core, &width, &height);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
if (usePbo) {
|
||||
|
@ -530,7 +530,7 @@ static void _drawScreenshot(struct mGUIRunner* runner, const color_t* pixels, un
|
|||
glBindTexture(GL_TEXTURE_2D, screenshotTex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
|
||||
runner->core->desiredVideoDimensions(runner->core, &width, &height);
|
||||
runner->core->currentVideoSize(runner->core, &width, &height);
|
||||
glDisable(GL_BLEND);
|
||||
bool wasPbo = usePbo;
|
||||
usePbo = false;
|
||||
|
|
|
@ -1032,7 +1032,7 @@ void CInemaTestRun(struct CInemaTest* test) {
|
|||
return;
|
||||
}
|
||||
struct CInemaImage image;
|
||||
core->desiredVideoDimensions(core, &image.width, &image.height);
|
||||
core->baseVideoSize(core, &image.width, &image.height);
|
||||
ssize_t bufferSize = image.width * image.height * BYTES_PER_PIXEL;
|
||||
image.data = malloc(bufferSize);
|
||||
image.stride = image.width;
|
||||
|
@ -1075,7 +1075,7 @@ void CInemaTestRun(struct CInemaTest* test) {
|
|||
for (frame = 0; frame < skip; ++frame) {
|
||||
core->runFrame(core);
|
||||
}
|
||||
core->desiredVideoDimensions(core, &image.width, &image.height);
|
||||
core->currentVideoSize(core, &image.width, &image.height);
|
||||
|
||||
#ifdef USE_FFMPEG
|
||||
struct FFmpegDecoder decoder;
|
||||
|
@ -1139,7 +1139,7 @@ void CInemaTestRun(struct CInemaTest* test) {
|
|||
break;
|
||||
}
|
||||
CIlog(3, "Test frame: %u\n", frameCounter);
|
||||
core->desiredVideoDimensions(core, &image.width, &image.height);
|
||||
core->currentVideoSize(core, &image.width, &image.height);
|
||||
uint8_t* diff = NULL;
|
||||
struct CInemaImage expected = {
|
||||
.data = NULL,
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/* Copyright (c) 2013-2023 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "video-backend.h"
|
||||
|
||||
void VideoBackendGetFrame(const struct VideoBackend* v, struct Rectangle* frame) {
|
||||
memset(frame, 0, sizeof(*frame));
|
||||
int i;
|
||||
for (i = 0; i < VIDEO_LAYER_MAX; ++i) {
|
||||
struct Rectangle dims;
|
||||
v->layerDimensions(v, i, &dims);
|
||||
RectangleUnion(frame, &dims);
|
||||
}
|
||||
}
|
||||
|
||||
void VideoBackendGetFrameSize(const struct VideoBackend* v, unsigned* width, unsigned* height) {
|
||||
struct Rectangle frame;
|
||||
VideoBackendGetFrame(v, &frame);
|
||||
*width = frame.width;
|
||||
*height = frame.height;
|
||||
}
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba-util/geometry.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
typedef HWND WHandle;
|
||||
|
@ -17,26 +19,34 @@ typedef HWND WHandle;
|
|||
typedef void* WHandle;
|
||||
#endif
|
||||
|
||||
enum VideoLayer {
|
||||
VIDEO_LAYER_BACKGROUND = 0,
|
||||
VIDEO_LAYER_BEZEL,
|
||||
VIDEO_LAYER_IMAGE,
|
||||
VIDEO_LAYER_OVERLAY,
|
||||
VIDEO_LAYER_MAX
|
||||
};
|
||||
|
||||
struct VideoBackend {
|
||||
void (*init)(struct VideoBackend*, WHandle handle);
|
||||
void (*deinit)(struct VideoBackend*);
|
||||
void (*setDimensions)(struct VideoBackend*, unsigned width, unsigned height);
|
||||
void (*setLayerDimensions)(struct VideoBackend*, enum VideoLayer, const struct Rectangle*);
|
||||
void (*layerDimensions)(const struct VideoBackend*, enum VideoLayer, struct Rectangle*);
|
||||
void (*swap)(struct VideoBackend*);
|
||||
void (*clear)(struct VideoBackend*);
|
||||
void (*resized)(struct VideoBackend*, unsigned w, unsigned h);
|
||||
void (*postFrame)(struct VideoBackend*, const void* frame);
|
||||
void (*contextResized)(struct VideoBackend*, unsigned w, unsigned h);
|
||||
void (*setImageSize)(struct VideoBackend*, enum VideoLayer, int w, int h);
|
||||
void (*imageSize)(struct VideoBackend*, enum VideoLayer, int* w, int* h);
|
||||
void (*setImage)(struct VideoBackend*, enum VideoLayer, const void* frame);
|
||||
void (*drawFrame)(struct VideoBackend*);
|
||||
void (*setMessage)(struct VideoBackend*, const char* message);
|
||||
void (*clearMessage)(struct VideoBackend*);
|
||||
|
||||
void* user;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
|
||||
bool filter;
|
||||
bool lockAspectRatio;
|
||||
bool lockIntegerScaling;
|
||||
bool interframeBlending;
|
||||
enum VideoLayer cropToLayer;
|
||||
};
|
||||
|
||||
struct VideoShader {
|
||||
|
@ -48,6 +58,9 @@ struct VideoShader {
|
|||
size_t nPasses;
|
||||
};
|
||||
|
||||
void VideoBackendGetFrame(const struct VideoBackend*, struct Rectangle* frame);
|
||||
void VideoBackendGetFrameSize(const struct VideoBackend*, unsigned* width, unsigned* height);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1511,7 +1511,7 @@ void _prepareForFrame(struct mGUIRunner* runner) {
|
|||
}
|
||||
|
||||
void _drawFrame(struct mGUIRunner* runner, bool faded) {
|
||||
runner->core->desiredVideoDimensions(runner->core, &corew, &coreh);
|
||||
runner->core->currentVideoSize(runner->core, &corew, &coreh);
|
||||
uint32_t color = 0xFFFFFF3F;
|
||||
if (!faded) {
|
||||
color |= 0xC0;
|
||||
|
|
|
@ -16,6 +16,7 @@ set(SOURCE_FILES
|
|||
convolve.c
|
||||
elf-read.c
|
||||
export.c
|
||||
geometry.c
|
||||
patch.c
|
||||
patch-fast.c
|
||||
patch-ips.c
|
||||
|
@ -33,6 +34,7 @@ set(GUI_FILES
|
|||
gui/menu.c)
|
||||
|
||||
set(TEST_FILES
|
||||
test/geometry.c
|
||||
test/sfo.c
|
||||
test/string-parser.c
|
||||
test/string-utf8.c
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/* Copyright (c) 2013-2023 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <mgba-util/geometry.h>
|
||||
|
||||
void RectangleUnion(struct Rectangle* dst, const struct Rectangle* add) {
|
||||
int x0 = dst->x;
|
||||
int y0 = dst->y;
|
||||
int x1 = dst->x + dst->width;
|
||||
int y1 = dst->y + dst->height;
|
||||
|
||||
if (add->x < x0) {
|
||||
x0 = add->x;
|
||||
}
|
||||
if (add->y < y0) {
|
||||
y0 = add->y;
|
||||
}
|
||||
if (add->x + add->width > x1) {
|
||||
x1 = add->x + add->width;
|
||||
}
|
||||
if (add->y + add->height > y1) {
|
||||
y1 = add->y + add->height;
|
||||
}
|
||||
|
||||
dst->x = x0;
|
||||
dst->y = y0;
|
||||
dst->width = x1 - x0;
|
||||
dst->height = y1 - y0;
|
||||
}
|
||||
|
||||
void RectangleCenter(const struct Rectangle* ref, struct Rectangle* rect) {
|
||||
rect->x = ref->x + (ref->width - rect->width) / 2;
|
||||
rect->y = ref->y + (ref->height - rect->height) / 2;
|
||||
}
|
|
@ -0,0 +1,213 @@
|
|||
/* Copyright (c) 2013-2023 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "util/test/suite.h"
|
||||
|
||||
#include <mgba-util/geometry.h>
|
||||
|
||||
M_TEST_DEFINE(unionRectOrigin) {
|
||||
struct Rectangle a = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = 1,
|
||||
.height = 1
|
||||
};
|
||||
struct Rectangle b = {
|
||||
.x = 1,
|
||||
.y = 1,
|
||||
.width = 1,
|
||||
.height = 1
|
||||
};
|
||||
RectangleUnion(&a, &b);
|
||||
assert_int_equal(a.x, 0);
|
||||
assert_int_equal(a.y, 0);
|
||||
assert_int_equal(a.width, 2);
|
||||
assert_int_equal(a.height, 2);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(unionRectOriginSwapped) {
|
||||
struct Rectangle a = {
|
||||
.x = 1,
|
||||
.y = 1,
|
||||
.width = 1,
|
||||
.height = 1
|
||||
};
|
||||
struct Rectangle b = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = 1,
|
||||
.height = 1
|
||||
};
|
||||
RectangleUnion(&a, &b);
|
||||
assert_int_equal(a.x, 0);
|
||||
assert_int_equal(a.y, 0);
|
||||
assert_int_equal(a.width, 2);
|
||||
assert_int_equal(a.height, 2);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(unionRectNonOrigin) {
|
||||
struct Rectangle a = {
|
||||
.x = 1,
|
||||
.y = 1,
|
||||
.width = 1,
|
||||
.height = 1
|
||||
};
|
||||
struct Rectangle b = {
|
||||
.x = 2,
|
||||
.y = 2,
|
||||
.width = 1,
|
||||
.height = 1
|
||||
};
|
||||
RectangleUnion(&a, &b);
|
||||
assert_int_equal(a.x, 1);
|
||||
assert_int_equal(a.y, 1);
|
||||
assert_int_equal(a.width, 2);
|
||||
assert_int_equal(a.height, 2);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(unionRectOverlapping) {
|
||||
struct Rectangle a = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = 2,
|
||||
.height = 2
|
||||
};
|
||||
struct Rectangle b = {
|
||||
.x = 1,
|
||||
.y = 1,
|
||||
.width = 2,
|
||||
.height = 2
|
||||
};
|
||||
RectangleUnion(&a, &b);
|
||||
assert_int_equal(a.x, 0);
|
||||
assert_int_equal(a.y, 0);
|
||||
assert_int_equal(a.width, 3);
|
||||
assert_int_equal(a.height, 3);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(unionRectSubRect) {
|
||||
struct Rectangle a = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = 3,
|
||||
.height = 3
|
||||
};
|
||||
struct Rectangle b = {
|
||||
.x = 1,
|
||||
.y = 1,
|
||||
.width = 1,
|
||||
.height = 1
|
||||
};
|
||||
RectangleUnion(&a, &b);
|
||||
assert_int_equal(a.x, 0);
|
||||
assert_int_equal(a.y, 0);
|
||||
assert_int_equal(a.width, 3);
|
||||
assert_int_equal(a.height, 3);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(unionRectNegativeOrigin) {
|
||||
struct Rectangle a = {
|
||||
.x = -1,
|
||||
.y = -1,
|
||||
.width = 1,
|
||||
.height = 1
|
||||
};
|
||||
struct Rectangle b = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = 1,
|
||||
.height = 1
|
||||
};
|
||||
RectangleUnion(&a, &b);
|
||||
assert_int_equal(a.x, -1);
|
||||
assert_int_equal(a.y, -1);
|
||||
assert_int_equal(a.width, 2);
|
||||
assert_int_equal(a.height, 2);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(centerRectBasic) {
|
||||
struct Rectangle ref = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = 4,
|
||||
.height = 4
|
||||
};
|
||||
struct Rectangle a = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = 2,
|
||||
.height = 2
|
||||
};
|
||||
RectangleCenter(&ref, &a);
|
||||
assert_int_equal(a.x, 1);
|
||||
assert_int_equal(a.y, 1);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(centerRectRoundDown) {
|
||||
struct Rectangle ref = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = 4,
|
||||
.height = 4
|
||||
};
|
||||
struct Rectangle a = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = 1,
|
||||
.height = 1
|
||||
};
|
||||
RectangleCenter(&ref, &a);
|
||||
assert_int_equal(a.x, 1);
|
||||
assert_int_equal(a.y, 1);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(centerRectRoundDown2) {
|
||||
struct Rectangle ref = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = 4,
|
||||
.height = 4
|
||||
};
|
||||
struct Rectangle a = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = 3,
|
||||
.height = 2
|
||||
};
|
||||
RectangleCenter(&ref, &a);
|
||||
assert_int_equal(a.x, 0);
|
||||
assert_int_equal(a.y, 1);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(centerRectOffset) {
|
||||
struct Rectangle ref = {
|
||||
.x = 1,
|
||||
.y = 1,
|
||||
.width = 4,
|
||||
.height = 4
|
||||
};
|
||||
struct Rectangle a = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = 2,
|
||||
.height = 2
|
||||
};
|
||||
RectangleCenter(&ref, &a);
|
||||
assert_int_equal(a.x, 2);
|
||||
assert_int_equal(a.y, 2);
|
||||
}
|
||||
|
||||
M_TEST_SUITE_DEFINE(Geometry,
|
||||
cmocka_unit_test(unionRectOrigin),
|
||||
cmocka_unit_test(unionRectOriginSwapped),
|
||||
cmocka_unit_test(unionRectNonOrigin),
|
||||
cmocka_unit_test(unionRectOverlapping),
|
||||
cmocka_unit_test(unionRectSubRect),
|
||||
cmocka_unit_test(unionRectNegativeOrigin),
|
||||
cmocka_unit_test(centerRectBasic),
|
||||
cmocka_unit_test(centerRectRoundDown),
|
||||
cmocka_unit_test(centerRectRoundDown2),
|
||||
cmocka_unit_test(centerRectOffset),
|
||||
)
|
Loading…
Reference in New Issue