diff --git a/CMakeLists.txt b/CMakeLists.txt index ff5a7beb5..4f61ca80d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,10 +47,9 @@ set_target_properties(tfe PROPERTIES OUTPUT_NAME "theforceengine") if(LINUX) find_package(PkgConfig REQUIRED) find_package(Threads REQUIRED) - pkg_check_modules(SDL2 REQUIRED sdl2) + find_package(SDL2 2.24 REQUIRED) + pkg_check_modules(SDL2_IMAGE REQUIRED SDL2_image) pkg_check_modules(GLEW REQUIRED glew) - pkg_check_modules(IL REQUIRED IL) - pkg_check_modules(ILU REQUIRED ILU) set(OpenGL_GL_PREFERENCE GLVND) find_package(OpenGL REQUIRED) target_include_directories(tfe PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) @@ -59,8 +58,7 @@ if(LINUX) ${OPENGL_LIBRARIES} ${GLEW_LIBRARIES} ${SDL2_LIBRARIES} - ${IL_LIBRARIES} - ${ILU_LIBRARIES} + ${SDL2_IMAGE_LIBRARIES} ) if(NOT DISABLE_SYSMIDI) diff --git a/README.md b/README.md index 0b7e13b0f..7984bb267 100644 --- a/README.md +++ b/README.md @@ -52,8 +52,8 @@ Runtime data like Savegames, Configuration, Mods, ... are by default stored at _ This can be overridden by defining the "__TFE_DATA_HOME__" environment variable. ### Required Libraries -* [SDL2](TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp) 2.24 or higher -* [devIL](https://openil.sourceforge.net) +* [SDL2](http://libsdl.org) Version 2.24 +* [SDL2-image](https://github.com/libsdl-org/SDL_image) Version 2.6.3 * [GLEW](http://glew.sourceforge.net/) 2.2.0 * OpenGL 3.3 capable driver (latest [mesa](https://www.mesa3d.org) or nvidia proprietary driver recommended) * [RtMidi](https://www.music.mcgill.ca/~gary/rtmidi/) 5.0.0 or higher for external MIDI Synthesizer support diff --git a/TheForceEngine/TFE_Asset/imageAsset.cpp b/TheForceEngine/TFE_Asset/imageAsset.cpp index 86a61348d..ba7165d13 100644 --- a/TheForceEngine/TFE_Asset/imageAsset.cpp +++ b/TheForceEngine/TFE_Asset/imageAsset.cpp @@ -8,66 +8,78 @@ #include #include #include - -#define IL_USE_PRAGMA_LIBS -#include -#include +#include +#include namespace TFE_Image { - typedef std::map ImageMap; + typedef std::map ImageMap; static ImageMap s_images; static std::vector s_buffer; + static SDL_Surface* convertToRGBA(SDL_Surface* src) + { + SDL_PixelFormat rgba32 = { + .format = SDL_PIXELFORMAT_RGBA32, + .palette = NULL, + .BitsPerPixel = 32, + .BytesPerPixel = 4, + .Rmask = 0x000000FF, + .Gmask = 0x0000FF00, + .Bmask = 0x00FF0000, + .Amask = 0xFF000000, + .Rloss = 0, + .Gloss = 0, + .Bloss = 0, + .Aloss = 0, + .Rshift = 0, + .Gshift = 8, + .Bshift = 16, + .Ashift = 24 + }; + + SDL_Surface* n = SDL_ConvertSurface(src, &rgba32, 0); + SDL_FreeSurface(src); + return n; + } + void init() { + int ret; + int flags = IMG_INIT_PNG | IMG_INIT_JPG; + TFE_System::logWrite(LOG_MSG, "Startup", "TFE_Image::init"); - - // Initialize IL - ilInit(); - iluInit(); - - // We want all images to be loaded in a consistent manner - ilEnable(IL_ORIGIN_SET); - ilOriginFunc(IL_ORIGIN_UPPER_LEFT); + ret = IMG_Init(flags); + if ((ret & flags) != flags) + { + TFE_System::logWrite(LOG_ERROR, "ImageAsset", "SDL_image init failed!"); + } } void shutdown() { freeAll(); - ilShutDown(); + IMG_Quit(); } - Image* loadFromMemory(const u8* buffer, size_t size) + SDL_Surface* loadFromMemory(const u8* buffer, size_t size) { - Image* image = new Image; - - // Now let's switch over to using devIL... - ILuint handle; - - // In the next section, we load one image - ilGenImages(1, &handle); - ilBindImage(handle); - if (ilLoadL(IL_JPG, buffer, (ILuint)size) == IL_FALSE) - { + SDL_RWops* memops = SDL_RWFromConstMem(buffer, size); + if (!memops) return nullptr; - } - - // Let’s spy on it a little bit - image->width = (u32)ilGetInteger(IL_IMAGE_WIDTH); // getting image width - image->height = (u32)ilGetInteger(IL_IMAGE_HEIGHT); // and height - image->data = new u32[image->width * image->height]; - // get the image data - ilCopyPixels(0, 0, 0, image->width, image->height, 1, IL_RGBA, IL_UNSIGNED_BYTE, image->data); - - // Finally, clean the mess! - ilDeleteImages(1, &handle); + SDL_Surface* sdlimg = IMG_Load_RW(memops, 1); + if (!sdlimg) + return nullptr; + if (sdlimg->format->BitsPerPixel != 32) + sdlimg = convertToRGBA(sdlimg); + if (!sdlimg) + return nullptr; - return image; + return sdlimg; } - Image* get(const char* imagePath) + SDL_Surface* get(const char* imagePath) { ImageMap::iterator iImage = s_images.find(imagePath); if (iImage != s_images.end()) @@ -75,47 +87,28 @@ namespace TFE_Image return iImage->second; } - Image* image = new Image; - - // Now let's switch over to using devIL... - ILuint handle; - - // In the next section, we load one image - ilGenImages(1, &handle); - ilBindImage(handle); - if (ilLoadImage(imagePath) == IL_FALSE) - { - // TODO: handle error. - // ILenum error = ilGetError(); + SDL_Surface* sdlimg = IMG_Load(imagePath); + if (!sdlimg) return nullptr; - } - - // Let’s spy on it a little bit - image->width = (u32)ilGetInteger(IL_IMAGE_WIDTH); // getting image width - image->height = (u32)ilGetInteger(IL_IMAGE_HEIGHT); // and height - - image->data = new u32[image->width * image->height]; - // get the image data - ilCopyPixels(0, 0, 0, image->width, image->height, 1, IL_RGBA, IL_UNSIGNED_BYTE, image->data); - - // Finally, clean the mess! - ilDeleteImages(1, &handle); - - s_images[imagePath] = image; - return image; + if (sdlimg->format->BitsPerPixel != 32) + sdlimg = convertToRGBA(sdlimg); + if (!sdlimg) + return nullptr; + + s_images[imagePath] = sdlimg; + return sdlimg; } - void free(Image* image) + void free(SDL_Surface* image) { if (!image) { return; } - delete[] image->data; + SDL_FreeSurface(image); ImageMap::iterator iImage = s_images.begin(); for (; iImage != s_images.end(); ++iImage) { if (iImage->second == image) { - delete iImage->second; s_images.erase(iImage); break; } @@ -127,138 +120,109 @@ namespace TFE_Image ImageMap::iterator iImage = s_images.begin(); for (; iImage != s_images.end(); ++iImage) { - Image* image = iImage->second; + SDL_Surface* image = iImage->second; if (image) { - delete[] image->data; + SDL_FreeSurface(image); } - delete image; } s_images.clear(); } void writeImage(const char* path, u32 width, u32 height, u32* pixelData) { - ILuint handle; - ilGenImages(1, &handle); - ilBindImage(handle); - - ilTexImage(width, height, 1, 4, IL_RGBA, IL_UNSIGNED_BYTE, (void*)pixelData); - ilSetData((void*)pixelData); - ilEnable(IL_FILE_OVERWRITE); - ilSaveImage(path); - - ilBindImage(0); - ilDeleteImage(handle); + SDL_Surface* surf = SDL_CreateRGBSurfaceFrom(pixelData, width, height, + 32, width * sizeof(u32), + 0xFF, 0xFF00, 0xFF0000, 0xFF000000); + if (!surf) + return; + int ret = IMG_SavePNG(surf, path); + if (ret != 0) + { + TFE_System::logWrite(LOG_ERROR, "writeImage", "Saving PNG '%s' failed with '%s'", path, SDL_GetError()); + } } ////////////////////////////////////////////////////// - // Wacky file overrides to get Devil to read and write - // images to/from memory. + // Wacky file override to get SDL-Image to write + // images to memory. SDL_RWmemOps by default do not support writing. ////////////////////////////////////////////////////// - static MemoryStream s_memStream; - - ILHANDLE ILAPIENTRY iOpen(const char *FileName) - { - return (ILHANDLE)1; - } - - void ILAPIENTRY iClose(ILHANDLE Handle) - { - } - - ILboolean ILAPIENTRY iEof(ILHANDLE Handle) - { - return s_memStream.getLoc() >= s_memStream.getSize(); - } - - ILint ILAPIENTRY iGetc(ILHANDLE Handle) + static size_t SDLCALL _sdl_wop_mem(struct SDL_RWops* context, const void *ptr, + size_t size, size_t num) { - u8 c; - s_memStream.read(&c); - return ILint(c); - } - - ILint ILAPIENTRY iPutc(ILubyte Char, ILHANDLE Handle) - { - u8 c = u8(Char); - s_memStream.write(&c); - return 1; - } - - ILint ILAPIENTRY iRead(void *Buffer, ILuint Size, ILuint Number, ILHANDLE Handle) - { - s_memStream.readBuffer(Buffer, Size, Number); - return Number; - } - - ILint ILAPIENTRY iWrite(const void *Buffer, ILuint Size, ILuint Number, ILHANDLE Handle) - { - s_memStream.writeBuffer(Buffer, Size, Number); - return Number; - } - - ILint ILAPIENTRY iSeek(ILHANDLE Handle, ILint Offset, ILint Mode) - { - Stream::Origin c_origin[] = + const size_t bytes = num * size; + const ptrdiff_t space = context->hidden.mem.stop - context->hidden.mem.here; + if (space >= bytes) { - Stream::ORIGIN_START, - Stream::ORIGIN_CURRENT, - Stream::ORIGIN_END, - }; - return s_memStream.seek(Offset, c_origin[Mode]) ? 0 : -1; + memcpy(context->hidden.mem.here, ptr, bytes); + context->hidden.mem.here += bytes; + return bytes; + } + return 0; } - ILint ILAPIENTRY iTell(ILHANDLE Handle) - { - return (ILint)s_memStream.getLoc(); - } - ////////////////////////////////////////////////////// // Code to write and read images from memory. ////////////////////////////////////////////////////// - size_t writeImageToMemory(u8*& output, u32 width, u32 height, const u32* pixelData) + size_t writeImageToMemory(u8* output, u32 srcw, u32 srch, u32 dstw, + u32 dsth, const u32* pixelData) { - s_memStream.open(Stream::MODE_WRITE); - ilSetWrite(iOpen, iClose, iPutc, iSeek, iTell, iWrite); - - writeImage("image.png", width, height, (u32*)pixelData); + size_t written; + int ret; - ilResetWrite(); - s_memStream.close(); + SDL_Surface* surf = SDL_CreateRGBSurfaceFrom((void *)pixelData, srcw, srch, 32, srcw * sizeof(u32), + 0xFF, 0xFF00, 0xFF0000, 0xFF000000); + if (!surf) + return 0; + if ((srcw != dstw) || (srch != dsth)) + { + const SDL_Rect rs = { 0, 0, (int)srcw, (int)srch }; + const SDL_Rect rd = { 0, 0, (int)dstw, (int)dsth }; + SDL_Surface* scaled = SDL_CreateRGBSurface(0, dstw, dsth, 32, + 0xFF, 0xFF00, 0xFF0000, 0xFF000000); + if (!scaled) + { + SDL_FreeSurface(surf); + return 0; + } + ret = SDL_SoftStretchLinear(surf, &rs, scaled, &rd); + if (ret != 0) + { + SDL_FreeSurface(surf); + SDL_FreeSurface(scaled); + return 0; + } + SDL_FreeSurface(surf); + surf = scaled; + srcw = dstw; + srch = dsth; + } - output = (u8*)s_memStream.data(); - return s_memStream.getSize(); + SDL_RWops* memops = SDL_RWFromMem(output, srcw * srch * sizeof(u32)); + if (!memops) + { + SDL_FreeSurface(surf); + return 0; + } + memops->write = _sdl_wop_mem; + ret = IMG_SavePNG_RW(surf, memops, 0); + SDL_FreeSurface(surf); + if (ret == 0) + { + written = memops->hidden.mem.here - memops->hidden.mem.base; + } + SDL_FreeRW(memops); + return written; } - void readImageFromMemory(Image* output, size_t size, const u32* pixelData) + void readImageFromMemory(SDL_Surface** output, size_t size, const u32* pixelData) { - s_memStream.load(size, pixelData); - s_memStream.open(Stream::MODE_READ); - ilSetRead(iOpen, iClose, iEof, iGetc, iRead, iSeek, iTell); - - // Now let's switch over to using devIL... - ILuint handle; - - // In the next section, we load one image - ilGenImages(1, &handle); - ilBindImage(handle); - if (ilLoadImage("image.png") == IL_FALSE) - { - ILenum error = ilGetError(); + SDL_RWops* memops = SDL_RWFromConstMem(pixelData, size); + if (!memops) return; - } - - // Let’s spy on it a little bit - output->width = (u32)ilGetInteger(IL_IMAGE_WIDTH); // getting image width - output->height = (u32)ilGetInteger(IL_IMAGE_HEIGHT); // and height - // get the image data - ilCopyPixels(0, 0, 0, output->width, output->height, 1, IL_RGBA, IL_UNSIGNED_BYTE, output->data); - - // Finally, clean the mess! - ilDeleteImages(1, &handle); - ilResetRead(); - s_memStream.close(); + SDL_Surface* sdlimg = IMG_Load_RW(memops, 1); + if (output) + *output = sdlimg; } } diff --git a/TheForceEngine/TFE_Asset/imageAsset.h b/TheForceEngine/TFE_Asset/imageAsset.h index 8bdc2c0ee..56bf60419 100644 --- a/TheForceEngine/TFE_Asset/imageAsset.h +++ b/TheForceEngine/TFE_Asset/imageAsset.h @@ -1,30 +1,22 @@ #pragma once ////////////////////////////////////////////////////////////////////// // The Force Engine Image Loading -// TODO: Replace DeviL with libPNG? or STB_IMAGE? -// Can use std_image.h and std_image_write.h for reading and writing. ////////////////////////////////////////////////////////////////////// #include - -struct Image -{ - u32 width = 0u; - u32 height = 0u; - u32* data = nullptr; -}; +#include namespace TFE_Image { void init(); void shutdown(); - Image* get(const char* imagePath); - Image* loadFromMemory(const u8* buffer, size_t size); - void free(Image* image); + SDL_Surface* get(const char* imagePath); + SDL_Surface* loadFromMemory(const u8* buffer, size_t size); + void free(SDL_Surface* image); void freeAll(); void writeImage(const char* path, u32 width, u32 height, u32* pixelData); - size_t writeImageToMemory(u8*& output, u32 width, u32 height, const u32* pixelData); - void readImageFromMemory(Image* output, size_t size, const u32* pixelData); + size_t writeImageToMemory(u8* output, u32 srcw, u32 srch, u32 dstw, u32 dsth, const u32* pixelData); + void readImageFromMemory(SDL_Surface** output, size_t size, const u32* pixelData); } diff --git a/TheForceEngine/TFE_Asset/textureAsset.cpp b/TheForceEngine/TFE_Asset/textureAsset.cpp index d7f900e86..3bd48a8bf 100644 --- a/TheForceEngine/TFE_Asset/textureAsset.cpp +++ b/TheForceEngine/TFE_Asset/textureAsset.cpp @@ -572,7 +572,7 @@ namespace TFE_Texture return index; } - Texture* convertImageToTexture_8bit(const char* name, const Image* image, const char* paletteName) + Texture* convertImageToTexture_8bit(const char* name, const SDL_Surface* image, const char* paletteName) { TextureMap::iterator iTex = s_textures.find(name); if (iTex != s_textures.end()) @@ -591,7 +591,7 @@ namespace TFE_Texture strcpy(texture->name, name); // Allocate memory. - texture->memory = new u8[sizeof(TextureFrame) + image->width * image->height]; + texture->memory = new u8[sizeof(TextureFrame) + image->w * image->h]; texture->frames = (TextureFrame*)texture->memory; texture->frameCount = 1; texture->frameRate = 0; // arbitrary. @@ -599,23 +599,24 @@ namespace TFE_Texture texture->frames[0].image = texture->memory + sizeof(TextureFrame); u8* imageOut = texture->frames[0].image; - texture->frames[0].width = image->width; - texture->frames[0].height = image->height; + texture->frames[0].width = image->w; + texture->frames[0].height = image->h; texture->frames[0].logSizeY = 0; texture->frames[0].offsetX = 0; texture->frames[0].offsetY = 0; texture->frames[0].opacity = TEX_OPACITY_TRANS; - texture->frames[0].uvWidth = image->width; - texture->frames[0].uvHeight = image->height; + texture->frames[0].uvWidth = image->w; + texture->frames[0].uvHeight = image->h; // For now look for the closest "manhattan" match. - s32 pixelCount = image->width * image->height; + s32 pixelCount = image->w * image->h; for (s32 i = 0; i < pixelCount; i++) { - u8 srcR = image->data[i] & 0xffu; - u8 srcG = (image->data[i] >> 8u) & 0xff; - u8 srcB = (image->data[i] >> 16u) & 0xff; - u8 srcA = (image->data[i] >> 24u) & 0xff; + u32 pixel = ((u32*)(image->pixels))[i]; + u8 srcR = pixel & 0xffu; + u8 srcG = (pixel >> 8u) & 0xff; + u8 srcB = (pixel >> 16u) & 0xff; + u8 srcA = (pixel >> 24u) & 0xff; if (srcA < 128u) { diff --git a/TheForceEngine/TFE_Editor/levelEditor.cpp b/TheForceEngine/TFE_Editor/levelEditor.cpp index f1763082c..ae08b1e1e 100644 --- a/TheForceEngine/TFE_Editor/levelEditor.cpp +++ b/TheForceEngine/TFE_Editor/levelEditor.cpp @@ -317,10 +317,10 @@ namespace LevelEditor { char imagePath[TFE_MAX_PATH]; TFE_Paths::appendPath(TFE_PathType::PATH_PROGRAM, localPath, imagePath, TFE_MAX_PATH); - Image* image = TFE_Image::get(imagePath); + SDL_Surface* image = TFE_Image::get(imagePath); if (image) { - TextureGpu* editCtrlHandle = TFE_RenderBackend::createTexture(image->width, image->height, image->data); + TextureGpu* editCtrlHandle = TFE_RenderBackend::createTexture(image->w, image->h, (u32*)image->pixels); return TFE_RenderBackend::getGpuPtr(editCtrlHandle); } return nullptr; diff --git a/TheForceEngine/TFE_Editor/levelEditorData.cpp b/TheForceEngine/TFE_Editor/levelEditorData.cpp index 953577768..c20d06f39 100644 --- a/TheForceEngine/TFE_Editor/levelEditorData.cpp +++ b/TheForceEngine/TFE_Editor/levelEditorData.cpp @@ -127,16 +127,16 @@ namespace LevelEditorData if (tex) { return tex; } TFE_Paths::appendPath(TFE_PathType::PATH_PROGRAM, "UI_Images/SpiritObject.png", imagePath, TFE_MAX_PATH); - Image* image = TFE_Image::get(imagePath); + SDL_Surface* image = TFE_Image::get(imagePath); if (image) { s_objIcons.push_back({}); tex = &s_objIcons.back(); tex->scale = { 1.0f, 1.0f }; - tex->texture = TFE_RenderBackend::createTexture(image->width, image->height, image->data); - tex->width = image->width; - tex->height = image->height; + tex->texture = TFE_RenderBackend::createTexture(image->w, image->h, (u32*)image->pixels); + tex->width = image->w; + tex->height = image->h; strcpy(tex->name, "SpiritObject.png"); s_editorLevel.textureMap[tex->name] = tex; @@ -149,16 +149,16 @@ namespace LevelEditorData if (tex) { return tex; } TFE_Paths::appendPath(TFE_PathType::PATH_PROGRAM, "UI_Images/SafeObject.png", imagePath, TFE_MAX_PATH); - Image* image = TFE_Image::get(imagePath); + SDL_Surface* image = TFE_Image::get(imagePath); if (image) { s_objIcons.push_back({}); tex = &s_objIcons.back(); tex->scale = { 1.0f, 1.0f }; - tex->texture = TFE_RenderBackend::createTexture(image->width, image->height, image->data); - tex->width = image->width; - tex->height = image->height; + tex->texture = TFE_RenderBackend::createTexture(image->w, image->h, (u32*)image->pixels); + tex->width = image->w; + tex->height = image->h; strcpy(tex->name, "SafeObject.png"); s_editorLevel.textureMap[tex->name] = tex; @@ -221,16 +221,16 @@ namespace LevelEditorData if (tex) { return tex; } TFE_Paths::appendPath(TFE_PathType::PATH_PROGRAM, "UI_Images/SoundObject.png", imagePath, TFE_MAX_PATH); - Image* image = TFE_Image::get(imagePath); + SDL_Surface* image = TFE_Image::get(imagePath); if (image) { s_objIcons.push_back({}); tex = &s_objIcons.back(); tex->scale = { 1.0f, 1.0f }; - tex->texture = TFE_RenderBackend::createTexture(image->width, image->height, image->data); - tex->width = image->width; - tex->height = image->height; + tex->texture = TFE_RenderBackend::createTexture(image->w, image->h, (u32*)image->pixels); + tex->width = image->w; + tex->height = image->h; strcpy(tex->name, "SoundObject.png"); s_editorLevel.textureMap[tex->name] = tex; diff --git a/TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp b/TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp index 7264a29a8..0c3457795 100644 --- a/TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp +++ b/TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp @@ -275,13 +275,13 @@ namespace TFE_FrontEndUI memset(imagePath, 0, TFE_MAX_PATH); TFE_Paths::appendPath(TFE_PathType::PATH_PROGRAM, localPath, imagePath, TFE_MAX_PATH); } - Image* image = TFE_Image::get(imagePath); + SDL_Surface* image = TFE_Image::get(imagePath); if (image) { - TextureGpu* gpuImage = TFE_RenderBackend::createTexture(image->width, image->height, image->data, MAG_FILTER_LINEAR); + TextureGpu* gpuImage = TFE_RenderBackend::createTexture(image->w, image->h, (u32*)image->pixels, MAG_FILTER_LINEAR); uiImage->image = TFE_RenderBackend::getGpuPtr(gpuImage); - uiImage->width = image->width; - uiImage->height = image->height; + uiImage->width = image->w; + uiImage->height = image->h; return true; } return false; diff --git a/TheForceEngine/TFE_FrontEndUI/modLoader.cpp b/TheForceEngine/TFE_FrontEndUI/modLoader.cpp index f3b6e4bf5..c6d3fc927 100644 --- a/TheForceEngine/TFE_FrontEndUI/modLoader.cpp +++ b/TheForceEngine/TFE_FrontEndUI/modLoader.cpp @@ -1014,16 +1014,15 @@ namespace TFE_FrontEndUI zipArchive.readFile(s_imageBuffer.data(), imageSize); zipArchive.closeFile(); - Image* image = TFE_Image::loadFromMemory((u8*)s_imageBuffer.data(), imageSize); + SDL_Surface* image = TFE_Image::loadFromMemory((u8*)s_imageBuffer.data(), imageSize); if (image) { - TextureGpu* gpuImage = TFE_RenderBackend::createTexture(image->width, image->height, image->data, MAG_FILTER_LINEAR); + TextureGpu* gpuImage = TFE_RenderBackend::createTexture(image->w, image->h, (u32*)image->pixels, MAG_FILTER_LINEAR); poster->texture = gpuImage; - poster->width = image->width; - poster->height = image->height; + poster->width = image->w; + poster->height = image->h; - delete[] image->data; - delete image; + TFE_Image::free(image); } } zipArchive.close(); @@ -1033,13 +1032,13 @@ namespace TFE_FrontEndUI char imagePath[TFE_MAX_PATH]; sprintf(imagePath, "%s%s", baseDir, imageFileName); - Image* image = TFE_Image::get(imagePath); + SDL_Surface* image = TFE_Image::get(imagePath); if (image) { - TextureGpu* gpuImage = TFE_RenderBackend::createTexture(image->width, image->height, image->data, MAG_FILTER_LINEAR); + TextureGpu* gpuImage = TFE_RenderBackend::createTexture(image->w, image->h, (u32*)image->pixels, MAG_FILTER_LINEAR); poster->texture = gpuImage; - poster->width = image->width; - poster->height = image->height; + poster->width = image->w; + poster->height = image->h; } } } diff --git a/TheForceEngine/TFE_Game/reticle.cpp b/TheForceEngine/TFE_Game/reticle.cpp index 61a3a0716..697b2065d 100644 --- a/TheForceEngine/TFE_Game/reticle.cpp +++ b/TheForceEngine/TFE_Game/reticle.cpp @@ -36,14 +36,14 @@ bool reticle_init() TFE_Paths::appendPath(TFE_PathType::PATH_PROGRAM, "UI_Images/ReticleAtlas.png", imagePath, TFE_MAX_PATH); } - Image* image = TFE_Image::get(imagePath); + SDL_Surface* image = TFE_Image::get(imagePath); if (!image) { TFE_System::logWrite(LOG_ERROR, "Reticle", "Cannot load reticle atlas: '%s'.", imagePath); return false; } - s_reticleImage.texture = TFE_RenderBackend::createTexture(image->width, image->height, image->data); + s_reticleImage.texture = TFE_RenderBackend::createTexture(image->w, image->h, (u32*)image->pixels); s_reticleImage.overlayX = 0; s_reticleImage.overlayY = 0; s_reticleImage.overlayWidth = 32; diff --git a/TheForceEngine/TFE_Game/saveSystem.cpp b/TheForceEngine/TFE_Game/saveSystem.cpp index e9a65d943..138d5158d 100644 --- a/TheForceEngine/TFE_Game/saveSystem.cpp +++ b/TheForceEngine/TFE_Game/saveSystem.cpp @@ -48,52 +48,18 @@ namespace TFE_SaveSystem s_imageBufferSize[0] = size; } TFE_RenderBackend::captureScreenToMemory(s_imageBuffer[0]); - - // Scale and crop the image to fit inside 426 x 240 (widescreen). - s64 scale = SAVE_IMAGE_HEIGHT * 65536 / displayInfo.height; - s64 invScale = displayInfo.height * 65536 / SAVE_IMAGE_HEIGHT; - s32 scaledWidth = s32((displayInfo.width * scale) >> 16ll); - s32 newWidth = SAVE_IMAGE_WIDTH, newHeight = SAVE_IMAGE_HEIGHT; - - s32 dstOffset = 0; - s64 srcOffset = 0; - if (scaledWidth < newWidth) - { - dstOffset = (newWidth - scaledWidth) / 2; - } - else if (scaledWidth > newWidth) - { - srcOffset = (scaledWidth - newWidth) / 2; - srcOffset = srcOffset * invScale; - } - - size_t newSize = newWidth * newHeight * 4; - if (newSize > s_imageBufferSize[1]) - { - s_imageBuffer[1] = (u32*)realloc(s_imageBuffer[1], newSize); - s_imageBufferSize[1] = newSize; - } - - const u32 *src; - u32* dst = s_imageBuffer[1]; - memset(dst, 0, newWidth * newHeight * 4); - - s64 u = srcOffset, v = 0; - s64 du = invScale, dv = invScale; - for (s32 y = 0; y < newHeight; y++, v += dv, dst += newWidth) + // Save to memory. + u8* png = (u8*)malloc(SAVE_IMAGE_WIDTH * SAVE_IMAGE_HEIGHT * 4); + if (png) { - u = srcOffset; - src = &s_imageBuffer[0][(v >> 16ll) * displayInfo.width]; - for (s32 x = dstOffset; x < newWidth - dstOffset; x++, u += du) - { - dst[x] = src[u >> 16ll]; - } + u32 pngSize = (u32)TFE_Image::writeImageToMemory(png, displayInfo.width, displayInfo.height, + SAVE_IMAGE_WIDTH, SAVE_IMAGE_HEIGHT, + s_imageBuffer[0]); + } else { + pngSize = 0; + png = s_imageBuffer[0]; } - // Save to memory. - u8* png; - u32 pngSize = (u32)TFE_Image::writeImageToMemory(png, newWidth, newHeight, s_imageBuffer[1]); - // Master version. u32 version = SVER_CUR; stream->write(&version); @@ -129,6 +95,7 @@ namespace TFE_SaveSystem // Image. stream->write(&pngSize); stream->writeBuffer(png, pngSize); + free(png); } void loadHeader(Stream* stream, SaveHeader* header, const char* fileName) @@ -176,10 +143,14 @@ namespace TFE_SaveSystem } stream->readBuffer(s_imageBuffer[0], pngSize); - Image image = { 0 }; - image.data = header->imageData; + SDL_Surface* image; TFE_Image::readImageFromMemory(&image, pngSize, s_imageBuffer[0]); - assert(image.width == SAVE_IMAGE_WIDTH && image.height == SAVE_IMAGE_HEIGHT); + if (image) + { + const u32 sz = SAVE_IMAGE_WIDTH * SAVE_IMAGE_HEIGHT * sizeof(u32); + memcpy(header->imageData, image->pixels, sz); + TFE_Image::free(image); + } } void populateSaveDirectory(std::vector& dir) diff --git a/TheForceEngine/TFE_RenderBackend/Win32OpenGL/screenCapture.cpp b/TheForceEngine/TFE_RenderBackend/Win32OpenGL/screenCapture.cpp index dfffdf7f6..4981cad20 100644 --- a/TheForceEngine/TFE_RenderBackend/Win32OpenGL/screenCapture.cpp +++ b/TheForceEngine/TFE_RenderBackend/Win32OpenGL/screenCapture.cpp @@ -94,10 +94,31 @@ bool ScreenCapture::changeBufferCount(u32 newBufferCount, bool forceRealloc/* = return m_bufferCount; } +static void flipVert32bpp(void* mem, u32 w, u32 h) +{ + const u32 stride = w * 4; + const u32 size = stride * h; + char* upper = (char *)mem; + char* lower = (char *)mem + size - stride; + char* tmpb = (char *)malloc(stride); + while (tmpb && (upper < lower)) { + memcpy(tmpb, upper, stride); + memcpy(upper, lower, stride); + memcpy(lower, tmpb, stride); + upper += stride; + lower -= stride; + } + free(tmpb); +} + void ScreenCapture::captureFrontBufferToMemory(u32* mem) { glReadBuffer(GL_FRONT); glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, mem); + + // Need to flip image upside-down. OpenGL has (0|0) at lower left + // corner, while the rest of the world places it in the upper left. + flipVert32bpp(mem, m_width, m_height); } void ScreenCapture::update(bool flush) diff --git a/TheForceEngine/TFE_Ui/markdown.cpp b/TheForceEngine/TFE_Ui/markdown.cpp index d87265203..9d8527c08 100644 --- a/TheForceEngine/TFE_Ui/markdown.cpp +++ b/TheForceEngine/TFE_Ui/markdown.cpp @@ -95,10 +95,10 @@ namespace TFE_Markdown return iTexture->second; } - const Image* image = TFE_Image::get(path); + const SDL_Surface* image = TFE_Image::get(path); if (!image) { return nullptr; } - TextureGpu* texture = TFE_RenderBackend::createTexture(image->width, image->height, image->data); + TextureGpu* texture = TFE_RenderBackend::createTexture(image->w, image->h, (u32*)image->pixels); if (texture) { s_textures[path] = texture; } return texture; }