1/**
2 * Copyright (c) 2006-2023 LOVE Development Team
3 *
4 * This software is provided 'as-is', without any express or implied
5 * warranty. In no event will the authors be held liable for any damages
6 * arising from the use of this software.
7 *
8 * Permission is granted to anyone to use this software for any purpose,
9 * including commercial applications, and to alter it and redistribute it
10 * freely, subject to the following restrictions:
11 *
12 * 1. The origin of this software must not be misrepresented; you must not
13 * claim that you wrote the original software. If you use this software
14 * in a product, an acknowledgment in the product documentation would be
15 * appreciated but is not required.
16 * 2. Altered source versions must be plainly marked as such, and must not be
17 * misrepresented as being the original software.
18 * 3. This notice may not be removed or altered from any source distribution.
19 **/
20
21// LOVE
22#include "STBHandler.h"
23#include "common/Exception.h"
24#include "common/Color.h"
25
26static void loveSTBIAssert(bool test, const char *teststr)
27{
28 if (!test)
29 throw love::Exception("Could not decode image (stb_image assertion '%s' failed)", teststr);
30}
31
32// Workaround when building for iOS with deployment target=8.0
33#include "common/config.h"
34#if defined(LOVE_IOS)
35#define STBI_NO_THREAD_LOCALS
36#endif
37
38// stb_image
39 #define STBI_ONLY_JPEG
40// #define STBI_ONLY_PNG
41#define STBI_ONLY_BMP
42#define STBI_ONLY_TGA
43#define STBI_ONLY_HDR
44#define STBI_NO_STDIO
45#define STB_IMAGE_IMPLEMENTATION
46#define STBI_ASSERT(A) loveSTBIAssert((A), #A)
47#include "libraries/stb/stb_image.h"
48
49// C
50#include <cstdlib>
51
52namespace love
53{
54namespace image
55{
56namespace magpie
57{
58
59static_assert(sizeof(Color32) == 4, "sizeof(Color32) must equal 4 bytes!");
60
61bool STBHandler::canDecode(Data *data)
62{
63 int w = 0;
64 int h = 0;
65 int comp = 0;
66
67 int status = stbi_info_from_memory((const stbi_uc *) data->getData(),
68 (int) data->getSize(), &w, &h, &comp);
69
70 return status == 1 && w > 0 && h > 0;
71}
72
73bool STBHandler::canEncode(PixelFormat rawFormat, EncodedFormat encodedFormat)
74{
75 return encodedFormat == ENCODED_TGA && rawFormat == PIXELFORMAT_RGBA8;
76}
77
78FormatHandler::DecodedImage STBHandler::decode(Data *data)
79{
80 DecodedImage img;
81
82 const stbi_uc *buffer = (const stbi_uc *) data->getData();
83 int bufferlen = (int) data->getSize();
84 int comp = 0;
85
86 if (stbi_is_hdr_from_memory(buffer, bufferlen))
87 {
88 img.data = (unsigned char *) stbi_loadf_from_memory(buffer, bufferlen, &img.width, &img.height, &comp, 4);
89 img.size = img.width * img.height * 4 * sizeof(float);
90 img.format = PIXELFORMAT_RGBA32F;
91 }
92 else
93 {
94 img.data = stbi_load_from_memory(buffer, bufferlen, &img.width, &img.height, &comp, 4);
95 img.size = img.width * img.height * 4;
96 img.format = PIXELFORMAT_RGBA8;
97 }
98
99 if (img.data == nullptr || img.width <= 0 || img.height <= 0)
100 {
101 const char *err = stbi_failure_reason();
102 if (err == nullptr)
103 err = "unknown error";
104 throw love::Exception("Could not decode image with stb_image (%s).", err);
105 }
106
107 return img;
108}
109
110FormatHandler::EncodedImage STBHandler::encode(const DecodedImage &img, EncodedFormat encodedFormat)
111{
112 if (!canEncode(img.format, encodedFormat))
113 throw love::Exception("Invalid format.");
114
115 // We don't actually use stb_image for encoding, but this code is small
116 // enough that it might as well stay here.
117
118 EncodedImage encimg;
119
120 const size_t headerlen = 18;
121 const size_t bpp = 4;
122
123 encimg.size = (img.width * img.height * bpp) + headerlen;
124
125 // We need to use malloc because we use stb_image_free (which uses free())
126 // as our custom free() function, which is called by the ImageData after
127 // encode() is complete.
128 // stb_image's source code is compiled with this source, so calling malloc()
129 // directly is fine.
130 encimg.data = (unsigned char *) malloc(encimg.size);
131
132 if (encimg.data == nullptr)
133 throw love::Exception("Out of memory.");
134
135 // here's the header for the Targa file format.
136 encimg.data[0] = 0; // ID field size
137 encimg.data[1] = 0; // colormap type
138 encimg.data[2] = 2; // image type
139 encimg.data[3] = encimg.data[4] = 0; // colormap start
140 encimg.data[5] = encimg.data[6] = 0; // colormap length
141 encimg.data[7] = 32; // colormap bits
142 encimg.data[8] = encimg.data[9] = 0; // x origin
143 encimg.data[10] = encimg.data[11] = 0; // y origin
144 // Targa is little endian, so:
145 encimg.data[12] = img.width & 255; // least significant byte of width
146 encimg.data[13] = img.width >> 8; // most significant byte of width
147 encimg.data[14] = img.height & 255; // least significant byte of height
148 encimg.data[15] = img.height >> 8; // most significant byte of height
149 encimg.data[16] = bpp * 8; // bits per pixel
150 encimg.data[17] = 0x20; // descriptor bits (flip bits: 0x10 horizontal, 0x20 vertical)
151
152 // header done. write the pixel data to TGA:
153 memcpy(encimg.data + headerlen, img.data, img.width * img.height * bpp);
154
155 // convert the pixels from RGBA to BGRA.
156 Color32 *encodedpixels = (Color32 *) (encimg.data + headerlen);
157 for (int y = 0; y < img.height; y++)
158 {
159 for (int x = 0; x < img.width; x++)
160 {
161 unsigned char r = encodedpixels[y * img.width + x].r;
162 unsigned char b = encodedpixels[y * img.width + x].b;
163 encodedpixels[y * img.width + x].r = b;
164 encodedpixels[y * img.width + x].b = r;
165 }
166 }
167
168 return encimg;
169}
170
171void STBHandler::freeRawPixels(unsigned char *mem)
172{
173 // The STB decoder gave memory allocated directly by stb_image to the
174 // ImageData, so we use stb_image_free to delete it.
175 stbi_image_free(mem);
176}
177
178} // magpie
179} // image
180} // love
181