IOS7: Add support for 32 bit pixel formats

This enables some game engines only supporting 32 bit color formats,
e.g. 11th hour and Broken Sword 2.5.

Add support for the pixel formats RGBA8888 and ABGR8888. The pixel
formats are defined in big endian while iOS utilizes little endian,
thus requiring converting some formats to get correct color
representation.

Use the Apple Accelerate framework converting the format requiring
to minimize the CPU load since this is done every frame.
This commit is contained in:
Lars Sundström 2023-02-02 15:31:53 +01:00 committed by Thierry Crozat
parent 0d03655605
commit 4dc9c876fd
3 changed files with 63 additions and 10 deletions

View file

@ -103,6 +103,10 @@ void OSystem_iOS7::initVideoContext() {
#ifdef USE_RGB_COLOR
Common::List<Graphics::PixelFormat> OSystem_iOS7::getSupportedFormats() const {
Common::List<Graphics::PixelFormat> list;
// ABGR8888 (big endian)
list.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
// RGBA8888 (big endian)
list.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
// RGB565
list.push_back(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
// CLUT8
@ -140,9 +144,19 @@ void OSystem_iOS7::initSize(uint width, uint height, const Graphics::PixelFormat
// Create the screen texture right here. We need to do this here, since
// when a game requests hi-color mode, we actually set the framebuffer
// to the texture buffer to avoid an additional copy step.
execute_on_main_thread(^ {
[[iOS7AppDelegate iPhoneView] createScreenTexture];
});
// Free any previous screen texture and create new to handle format changes
_videoContext->screenTexture.free();
if (format && *format == Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24)) {
// ABGR8888 (big endian)
_videoContext->screenTexture.create((uint16) getSizeNextPOT(_videoContext->screenWidth), (uint16) getSizeNextPOT(_videoContext->screenHeight), Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
} else if (format && *format == Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)) {
// RGBA8888 (big endian)
_videoContext->screenTexture.create((uint16) getSizeNextPOT(_videoContext->screenWidth), (uint16) getSizeNextPOT(_videoContext->screenHeight), Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
} else {
// Assume RGB565
_videoContext->screenTexture.create((uint16) getSizeNextPOT(_videoContext->screenWidth), (uint16) getSizeNextPOT(_videoContext->screenHeight), Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
}
// In case the client code tries to set up a non supported mode, we will
// fall back to CLUT8 and set the transaction error accordingly.
@ -151,6 +165,10 @@ void OSystem_iOS7::initSize(uint width, uint height, const Graphics::PixelFormat
_gfxTransactionError = kTransactionFormatNotSupported;
}
execute_on_main_thread(^{
[[iOS7AppDelegate iPhoneView] setGameScreenCoords];
});
if (!format || format->bytesPerPixel == 1) {
_framebuffer.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
} else {

View file

@ -25,6 +25,7 @@
#include <UIKit/UIKit.h>
#include <Foundation/Foundation.h>
#include <QuartzCore/QuartzCore.h>
#include <Accelerate/Accelerate.h>
#include <OpenGLES/EAGL.h>
#include <OpenGLES/ES2/gl.h>
@ -41,6 +42,8 @@ typedef struct {
GLfloat u,v;
} GLVertex;
uint getSizeNextPOT(uint size);
@interface iPhoneView : UIView {
VideoContext _videoContext;
@ -97,7 +100,7 @@ typedef struct {
- (VideoContext *)getVideoContext;
- (void)createScreenTexture;
- (void)setGameScreenCoords;
- (void)initSurface;
- (void)setViewTransformation;

View file

@ -120,7 +120,7 @@ uint getSizeNextPOT(uint size) {
eaglLayer.opaque = YES;
eaglLayer.drawableProperties = @{
kEAGLDrawablePropertyRetainedBacking: @NO,
kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGB565
kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8,
};
_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
@ -603,6 +603,30 @@ uint getSizeNextPOT(uint size) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _videoContext.mouseTexture.w, _videoContext.mouseTexture.h, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, _videoContext.mouseTexture.getPixels()); printOpenGLError();
}
- (void *)getTextureInRGBA8888BE_AsRGBA8888LE {
// Allocate a pixel buffer with 32 bits per pixel
void *pixelBuffer = malloc(_videoContext.screenTexture.h * _videoContext.screenTexture.w * sizeof(uint32_t));
// Copy the texture pixels as we don't want to operate on the
memcpy(pixelBuffer, _videoContext.screenTexture.getPixels(), _videoContext.screenTexture.h * _videoContext.screenTexture.w * sizeof(uint32_t));
// Utilize the Accelerator Framwork to do some byte swapping
vImage_Buffer src;
src.height = _videoContext.screenTexture.h;
src.width = _videoContext.screenTexture.w;
src.rowBytes = _videoContext.screenTexture.pitch;
src.data = _videoContext.screenTexture.getPixels();
// Initialise dst with src, change data pointer to pixelBuffer
vImage_Buffer dst = src;
dst.data = pixelBuffer;
// Swap pixel channels from RGBA BE to RGBA LE (ABGR)
const uint8_t map[4] = { 3, 2, 1, 0 };
vImagePermuteChannels_ARGB8888(&src, &dst, map, kvImageNoFlags);
return pixelBuffer;
}
- (void)updateMainSurface {
glBufferData(GL_ARRAY_BUFFER, sizeof(GLVertex) * 4, _gameScreenCoords, GL_STATIC_DRAW);
glVertexAttribPointer(_positionSlot, 2, GL_FLOAT, GL_FALSE, sizeof(GLVertex), 0);
@ -613,8 +637,18 @@ uint getSizeNextPOT(uint size) {
// Unfortunately we have to update the whole texture every frame, since glTexSubImage2D is actually slower in all cases
// due to the iPhone internals having to convert the whole texture back from its internal format when used.
// In the future we could use several tiled textures instead.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _videoContext.screenTexture.w, _videoContext.screenTexture.h, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, _videoContext.screenTexture.getPixels()); printOpenGLError();
if (_videoContext.screenTexture.format == Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24)) {
// ABGR8888 in big endian which in little endian is RBGA8888 -> no convertion needed
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _videoContext.screenTexture.w, _videoContext.screenTexture.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, _videoContext.screenTexture.getPixels()); printOpenGLError();
} else if (_videoContext.screenTexture.format == Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)) {
// RGBA8888 (big endian) = ABGR8888 (little endian) -> needs convertion
void* pixelBuffer = [self getTextureInRGBA8888BE_AsRGBA8888LE];
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _videoContext.screenTexture.w, _videoContext.screenTexture.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixelBuffer); printOpenGLError();
free(pixelBuffer);
} else {
// Assuming RGB565
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _videoContext.screenTexture.w, _videoContext.screenTexture.h, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, _videoContext.screenTexture.getPixels()); printOpenGLError();
}
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); printOpenGLError();
}
@ -639,14 +673,12 @@ uint getSizeNextPOT(uint size) {
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); printOpenGLError();
}
- (void)createScreenTexture {
- (void)setGameScreenCoords{
const uint screenTexWidth = getSizeNextPOT(_videoContext.screenWidth);
const uint screenTexHeight = getSizeNextPOT(_videoContext.screenHeight);
_gameScreenCoords[1].u = _gameScreenCoords[3].u = _videoContext.screenWidth / (GLfloat)screenTexWidth;
_gameScreenCoords[2].v = _gameScreenCoords[3].v = _videoContext.screenHeight / (GLfloat)screenTexHeight;
_videoContext.screenTexture.create((uint16) screenTexWidth, (uint16) screenTexHeight, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
}
- (void)initSurface {