/** * Copyright (c) 2006-2021 LOVE Development Team * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. **/ // LOVE #include "STBHandler.h" #include "common/Exception.h" #include "common/Color.h" static void loveSTBIAssert(bool test, const char *teststr) { if (!test) throw love::Exception("Could not decode image (stb_image assertion '%s' failed)", teststr); } // Workaround when building for iOS with deployment target=8.0 #include "common/config.h" #if defined(LOVE_IOS) #define STBI_NO_THREAD_LOCALS #endif // stb_image #define STBI_ONLY_JPEG // #define STBI_ONLY_PNG #define STBI_ONLY_BMP #define STBI_ONLY_TGA #define STBI_ONLY_HDR #define STBI_NO_STDIO #define STB_IMAGE_STATIC #define STB_IMAGE_IMPLEMENTATION #define STBI_ASSERT(A) loveSTBIAssert((A), #A) #include "libraries/stb/stb_image.h" // C #include namespace love { namespace image { namespace magpie { static_assert(sizeof(Color32) == 4, "sizeof(Color32) must equal 4 bytes!"); bool STBHandler::canDecode(Data *data) { int w = 0; int h = 0; int comp = 0; int status = stbi_info_from_memory((const stbi_uc *) data->getData(), (int) data->getSize(), &w, &h, &comp); return status == 1 && w > 0 && h > 0; } bool STBHandler::canEncode(PixelFormat rawFormat, EncodedFormat encodedFormat) { return encodedFormat == ENCODED_TGA && rawFormat == PIXELFORMAT_RGBA8_UNORM; } FormatHandler::DecodedImage STBHandler::decode(Data *data) { DecodedImage img; const stbi_uc *buffer = (const stbi_uc *) data->getData(); int bufferlen = (int) data->getSize(); int comp = 0; if (stbi_is_hdr_from_memory(buffer, bufferlen)) { img.data = (unsigned char *) stbi_loadf_from_memory(buffer, bufferlen, &img.width, &img.height, &comp, 4); img.size = img.width * img.height * 4 * sizeof(float); img.format = PIXELFORMAT_RGBA32_FLOAT; } else { img.data = stbi_load_from_memory(buffer, bufferlen, &img.width, &img.height, &comp, 4); img.size = img.width * img.height * 4; img.format = PIXELFORMAT_RGBA8_UNORM; } if (img.data == nullptr || img.width <= 0 || img.height <= 0) { const char *err = stbi_failure_reason(); if (err == nullptr) err = "unknown error"; throw love::Exception("Could not decode image with stb_image (%s).", err); } return img; } FormatHandler::EncodedImage STBHandler::encode(const DecodedImage &img, EncodedFormat encodedFormat) { if (!canEncode(img.format, encodedFormat)) throw love::Exception("Invalid format."); // We don't actually use stb_image for encoding, but this code is small // enough that it might as well stay here. EncodedImage encimg; const size_t headerlen = 18; const size_t bpp = 4; encimg.size = (img.width * img.height * bpp) + headerlen; // We need to use malloc because we use stb_image_free (which uses free()) // as our custom free() function, which is called by the ImageData after // encode() is complete. // stb_image's source code is compiled with this source, so calling malloc() // directly is fine. encimg.data = (unsigned char *) malloc(encimg.size); if (encimg.data == nullptr) throw love::Exception("Out of memory."); // here's the header for the Targa file format. encimg.data[0] = 0; // ID field size encimg.data[1] = 0; // colormap type encimg.data[2] = 2; // image type encimg.data[3] = encimg.data[4] = 0; // colormap start encimg.data[5] = encimg.data[6] = 0; // colormap length encimg.data[7] = 32; // colormap bits encimg.data[8] = encimg.data[9] = 0; // x origin encimg.data[10] = encimg.data[11] = 0; // y origin // Targa is little endian, so: encimg.data[12] = img.width & 255; // least significant byte of width encimg.data[13] = img.width >> 8; // most significant byte of width encimg.data[14] = img.height & 255; // least significant byte of height encimg.data[15] = img.height >> 8; // most significant byte of height encimg.data[16] = bpp * 8; // bits per pixel encimg.data[17] = 0x20; // descriptor bits (flip bits: 0x10 horizontal, 0x20 vertical) // header done. write the pixel data to TGA: memcpy(encimg.data + headerlen, img.data, img.width * img.height * bpp); // convert the pixels from RGBA to BGRA. Color32 *encodedpixels = (Color32 *) (encimg.data + headerlen); for (int y = 0; y < img.height; y++) { for (int x = 0; x < img.width; x++) { unsigned char r = encodedpixels[y * img.width + x].r; unsigned char b = encodedpixels[y * img.width + x].b; encodedpixels[y * img.width + x].r = b; encodedpixels[y * img.width + x].b = r; } } return encimg; } void STBHandler::freeRawPixels(unsigned char *mem) { // The STB decoder gave memory allocated directly by stb_image to the // ImageData, so we use stb_image_free to delete it. stbi_image_free(mem); } } // magpie } // image } // love