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 | #include "ImageData.h" |
22 | #include "Image.h" |
23 | #include "filesystem/Filesystem.h" |
24 | |
25 | #include <algorithm> // min/max |
26 | |
27 | using love::thread::Lock; |
28 | |
29 | namespace love |
30 | { |
31 | namespace image |
32 | { |
33 | |
34 | love::Type ImageData::type("ImageData" , &Data::type); |
35 | |
36 | ImageData::ImageData(Data *data) |
37 | : ImageDataBase(PIXELFORMAT_UNKNOWN, 0, 0) |
38 | { |
39 | decode(data); |
40 | } |
41 | |
42 | ImageData::ImageData(int width, int height, PixelFormat format) |
43 | : ImageDataBase(format, width, height) |
44 | { |
45 | if (!validPixelFormat(format)) |
46 | throw love::Exception("Unsupported pixel format for ImageData" ); |
47 | |
48 | create(width, height, format); |
49 | |
50 | // Set to black/transparency. |
51 | memset(data, 0, getSize()); |
52 | } |
53 | |
54 | ImageData::ImageData(int width, int height, PixelFormat format, void *data, bool own) |
55 | : ImageDataBase(format, width, height) |
56 | { |
57 | if (!validPixelFormat(format)) |
58 | throw love::Exception("Unsupported pixel format for ImageData" ); |
59 | |
60 | if (own) |
61 | this->data = (unsigned char *) data; |
62 | else |
63 | create(width, height, format, data); |
64 | } |
65 | |
66 | ImageData::ImageData(const ImageData &c) |
67 | : ImageDataBase(c.format, c.width, c.height) |
68 | { |
69 | create(width, height, format, c.getData()); |
70 | } |
71 | |
72 | ImageData::~ImageData() |
73 | { |
74 | if (decodeHandler.get()) |
75 | decodeHandler->freeRawPixels(data); |
76 | else |
77 | delete[] data; |
78 | } |
79 | |
80 | love::image::ImageData *ImageData::clone() const |
81 | { |
82 | return new ImageData(*this); |
83 | } |
84 | |
85 | void ImageData::create(int width, int height, PixelFormat format, void *data) |
86 | { |
87 | size_t datasize = width * height * getPixelFormatSize(format); |
88 | |
89 | try |
90 | { |
91 | this->data = new unsigned char[datasize]; |
92 | } |
93 | catch(std::bad_alloc &) |
94 | { |
95 | throw love::Exception("Out of memory" ); |
96 | } |
97 | |
98 | if (data) |
99 | memcpy(this->data, data, datasize); |
100 | |
101 | decodeHandler = nullptr; |
102 | this->format = format; |
103 | |
104 | pixelSetFunction = getPixelSetFunction(format); |
105 | pixelGetFunction = getPixelGetFunction(format); |
106 | } |
107 | |
108 | void ImageData::decode(Data *data) |
109 | { |
110 | FormatHandler *decoder = nullptr; |
111 | FormatHandler::DecodedImage decodedimage; |
112 | |
113 | auto module = Module::getInstance<Image>(Module::M_IMAGE); |
114 | |
115 | if (module == nullptr) |
116 | throw love::Exception("love.image must be loaded in order to decode an ImageData." ); |
117 | |
118 | for (FormatHandler *handler : module->getFormatHandlers()) |
119 | { |
120 | if (handler->canDecode(data)) |
121 | { |
122 | decoder = handler; |
123 | break; |
124 | } |
125 | } |
126 | |
127 | if (decoder) |
128 | decodedimage = decoder->decode(data); |
129 | |
130 | if (decodedimage.data == nullptr) |
131 | { |
132 | auto filedata = dynamic_cast<filesystem::FileData *>(data); |
133 | |
134 | if (filedata != nullptr) |
135 | { |
136 | const std::string &name = filedata->getFilename(); |
137 | throw love::Exception("Could not decode file '%s' to ImageData: unsupported file format" , name.c_str()); |
138 | } |
139 | else |
140 | throw love::Exception("Could not decode data to ImageData: unsupported encoded format" ); |
141 | } |
142 | |
143 | if (decodedimage.size != decodedimage.width * decodedimage.height * getPixelFormatSize(decodedimage.format)) |
144 | { |
145 | decoder->freeRawPixels(decodedimage.data); |
146 | throw love::Exception("Could not convert image!" ); |
147 | } |
148 | |
149 | // Clean up any old data. |
150 | if (decodeHandler) |
151 | decodeHandler->freeRawPixels(this->data); |
152 | else |
153 | delete[] this->data; |
154 | |
155 | this->width = decodedimage.width; |
156 | this->height = decodedimage.height; |
157 | this->data = decodedimage.data; |
158 | this->format = decodedimage.format; |
159 | |
160 | decodeHandler = decoder; |
161 | |
162 | pixelSetFunction = getPixelSetFunction(format); |
163 | pixelGetFunction = getPixelGetFunction(format); |
164 | } |
165 | |
166 | love::filesystem::FileData *ImageData::encode(FormatHandler::EncodedFormat encodedFormat, const char *filename, bool writefile) const |
167 | { |
168 | FormatHandler *encoder = nullptr; |
169 | FormatHandler::EncodedImage encodedimage; |
170 | FormatHandler::DecodedImage rawimage; |
171 | |
172 | rawimage.width = width; |
173 | rawimage.height = height; |
174 | rawimage.size = getSize(); |
175 | rawimage.data = data; |
176 | rawimage.format = format; |
177 | |
178 | auto module = Module::getInstance<Image>(Module::M_IMAGE); |
179 | |
180 | if (module == nullptr) |
181 | throw love::Exception("love.image must be loaded in order to encode an ImageData." ); |
182 | |
183 | for (FormatHandler *handler : module->getFormatHandlers()) |
184 | { |
185 | if (handler->canEncode(format, encodedFormat)) |
186 | { |
187 | encoder = handler; |
188 | break; |
189 | } |
190 | } |
191 | |
192 | if (encoder != nullptr) |
193 | { |
194 | thread::Lock lock(mutex); |
195 | encodedimage = encoder->encode(rawimage, encodedFormat); |
196 | } |
197 | |
198 | if (encoder == nullptr || encodedimage.data == nullptr) |
199 | { |
200 | const char *fname = "unknown" ; |
201 | love::getConstant(format, fname); |
202 | throw love::Exception("No suitable image encoder for %s format." , fname); |
203 | } |
204 | |
205 | love::filesystem::FileData *filedata = nullptr; |
206 | |
207 | try |
208 | { |
209 | filedata = new love::filesystem::FileData(encodedimage.size, filename); |
210 | } |
211 | catch (love::Exception &) |
212 | { |
213 | encoder->freeRawPixels(encodedimage.data); |
214 | throw; |
215 | } |
216 | |
217 | memcpy(filedata->getData(), encodedimage.data, encodedimage.size); |
218 | encoder->freeRawPixels(encodedimage.data); |
219 | |
220 | if (writefile) |
221 | { |
222 | auto fs = Module::getInstance<filesystem::Filesystem>(Module::M_FILESYSTEM); |
223 | |
224 | if (fs == nullptr) |
225 | { |
226 | filedata->release(); |
227 | throw love::Exception("love.filesystem must be loaded in order to write an encoded ImageData to a file." ); |
228 | } |
229 | |
230 | try |
231 | { |
232 | fs->write(filename, filedata->getData(), filedata->getSize()); |
233 | } |
234 | catch (love::Exception &) |
235 | { |
236 | filedata->release(); |
237 | throw; |
238 | } |
239 | } |
240 | |
241 | return filedata; |
242 | } |
243 | |
244 | size_t ImageData::getSize() const |
245 | { |
246 | return size_t(getWidth() * getHeight()) * getPixelSize(); |
247 | } |
248 | |
249 | void *ImageData::getData() const |
250 | { |
251 | return data; |
252 | } |
253 | |
254 | bool ImageData::isSRGB() const |
255 | { |
256 | return false; |
257 | } |
258 | |
259 | bool ImageData::inside(int x, int y) const |
260 | { |
261 | return x >= 0 && x < getWidth() && y >= 0 && y < getHeight(); |
262 | } |
263 | |
264 | static float clamp01(float x) |
265 | { |
266 | return std::min(std::max(x, 0.0f), 1.0f); |
267 | } |
268 | |
269 | static void setPixelR8(const Colorf &c, ImageData::Pixel *p) |
270 | { |
271 | p->rgba8[0] = (uint8) (clamp01(c.r) * 255.0f + 0.5f); |
272 | } |
273 | |
274 | static void setPixelRG8(const Colorf &c, ImageData::Pixel *p) |
275 | { |
276 | p->rgba8[0] = (uint8) (clamp01(c.r) * 255.0f + 0.5f); |
277 | p->rgba8[1] = (uint8) (clamp01(c.g) * 255.0f + 0.5f); |
278 | } |
279 | |
280 | static void setPixelRGBA8(const Colorf &c, ImageData::Pixel *p) |
281 | { |
282 | p->rgba8[0] = (uint8) (clamp01(c.r) * 255.0f + 0.5f); |
283 | p->rgba8[1] = (uint8) (clamp01(c.g) * 255.0f + 0.5f); |
284 | p->rgba8[2] = (uint8) (clamp01(c.b) * 255.0f + 0.5f); |
285 | p->rgba8[3] = (uint8) (clamp01(c.a) * 255.0f + 0.5f); |
286 | } |
287 | |
288 | static void setPixelR16(const Colorf &c, ImageData::Pixel *p) |
289 | { |
290 | p->rgba16[0] = (uint16) (clamp01(c.r) * 65535.0f + 0.5f); |
291 | } |
292 | |
293 | static void setPixelRG16(const Colorf &c, ImageData::Pixel *p) |
294 | { |
295 | p->rgba16[0] = (uint16) (clamp01(c.r) * 65535.0f + 0.5f); |
296 | p->rgba16[1] = (uint16) (clamp01(c.g) * 65535.0f + 0.5f); |
297 | } |
298 | |
299 | static void setPixelRGBA16(const Colorf &c, ImageData::Pixel *p) |
300 | { |
301 | p->rgba16[0] = (uint16) (clamp01(c.r) * 65535.0f + 0.5f); |
302 | p->rgba16[1] = (uint16) (clamp01(c.b) * 65535.0f + 0.5f); |
303 | p->rgba16[2] = (uint16) (clamp01(c.g) * 65535.0f + 0.5f); |
304 | p->rgba16[3] = (uint16) (clamp01(c.a) * 65535.0f + 0.5f); |
305 | } |
306 | |
307 | static void setPixelR16F(const Colorf &c, ImageData::Pixel *p) |
308 | { |
309 | p->rgba16f[0] = float32to16(c.r); |
310 | } |
311 | |
312 | static void setPixelRG16F(const Colorf &c, ImageData::Pixel *p) |
313 | { |
314 | p->rgba16f[0] = float32to16(c.r); |
315 | p->rgba16f[1] = float32to16(c.g); |
316 | } |
317 | |
318 | static void setPixelRGBA16F(const Colorf &c, ImageData::Pixel *p) |
319 | { |
320 | p->rgba16f[0] = float32to16(c.r); |
321 | p->rgba16f[1] = float32to16(c.g); |
322 | p->rgba16f[2] = float32to16(c.b); |
323 | p->rgba16f[3] = float32to16(c.a); |
324 | } |
325 | |
326 | static void setPixelR32F(const Colorf &c, ImageData::Pixel *p) |
327 | { |
328 | p->rgba32f[0] = c.r; |
329 | } |
330 | |
331 | static void setPixelRG32F(const Colorf &c, ImageData::Pixel *p) |
332 | { |
333 | p->rgba32f[0] = c.r; |
334 | p->rgba32f[1] = c.g; |
335 | } |
336 | |
337 | static void setPixelRGBA32F(const Colorf &c, ImageData::Pixel *p) |
338 | { |
339 | p->rgba32f[0] = c.r; |
340 | p->rgba32f[1] = c.g; |
341 | p->rgba32f[2] = c.b; |
342 | p->rgba32f[3] = c.a; |
343 | } |
344 | |
345 | static void setPixelRGBA4(const Colorf &c, ImageData::Pixel *p) |
346 | { |
347 | // LSB->MSB: [a, b, g, r] |
348 | uint16 r = (uint16) (clamp01(c.r) * 0xF + 0.5); |
349 | uint16 g = (uint16) (clamp01(c.g) * 0xF + 0.5); |
350 | uint16 b = (uint16) (clamp01(c.b) * 0xF + 0.5); |
351 | uint16 a = (uint16) (clamp01(c.a) * 0xF + 0.5); |
352 | p->packed16 = (r << 12) | (g << 8) | (b << 4) | (a << 0); |
353 | } |
354 | |
355 | static void setPixelRGB5A1(const Colorf &c, ImageData::Pixel *p) |
356 | { |
357 | // LSB->MSB: [a, b, g, r] |
358 | uint16 r = (uint16) (clamp01(c.r) * 0x1F + 0.5); |
359 | uint16 g = (uint16) (clamp01(c.g) * 0x1F + 0.5); |
360 | uint16 b = (uint16) (clamp01(c.b) * 0x1F + 0.5); |
361 | uint16 a = (uint16) (clamp01(c.a) * 0x1 + 0.5); |
362 | p->packed16 = (r << 11) | (g << 6) | (b << 1) | (a << 0); |
363 | } |
364 | |
365 | static void setPixelRGB565(const Colorf &c, ImageData::Pixel *p) |
366 | { |
367 | // LSB->MSB: [b, g, r] |
368 | uint16 r = (uint16) (clamp01(c.r) * 0x1F + 0.5); |
369 | uint16 g = (uint16) (clamp01(c.g) * 0x3F + 0.5); |
370 | uint16 b = (uint16) (clamp01(c.b) * 0x1F + 0.5); |
371 | p->packed16 = (r << 11) | (g << 5) | (b << 0); |
372 | } |
373 | |
374 | static void setPixelRGB10A2(const Colorf &c, ImageData::Pixel *p) |
375 | { |
376 | // LSB->MSB: [r, g, b, a] |
377 | uint32 r = (uint32) (clamp01(c.r) * 0x3FF + 0.5); |
378 | uint32 g = (uint32) (clamp01(c.g) * 0x3FF + 0.5); |
379 | uint32 b = (uint32) (clamp01(c.b) * 0x3FF + 0.5); |
380 | uint32 a = (uint32) (clamp01(c.a) * 0x3 + 0.5); |
381 | p->packed32 = (r << 0) | (g << 10) | (b << 20) | (a << 30); |
382 | } |
383 | |
384 | static void setPixelRG11B10F(const Colorf &c, ImageData::Pixel *p) |
385 | { |
386 | // LSB->MSB: [r, g, b] |
387 | float11 r = float32to11(c.r); |
388 | float11 g = float32to11(c.g); |
389 | float10 b = float32to10(c.b); |
390 | p->packed32 = (r << 0) | (g << 11) | (b << 22); |
391 | } |
392 | |
393 | static void getPixelR8(const ImageData::Pixel *p, Colorf &c) |
394 | { |
395 | c.r = p->rgba8[0] / 255.0f; |
396 | c.g = 0.0f; |
397 | c.b = 0.0f; |
398 | c.a = 1.0f; |
399 | } |
400 | |
401 | static void getPixelRG8(const ImageData::Pixel *p, Colorf &c) |
402 | { |
403 | c.r = p->rgba8[0] / 255.0f; |
404 | c.g = p->rgba8[1] / 255.0f; |
405 | c.b = 0.0f; |
406 | c.a = 1.0f; |
407 | } |
408 | |
409 | static void getPixelRGBA8(const ImageData::Pixel *p, Colorf &c) |
410 | { |
411 | c.r = p->rgba8[0] / 255.0f; |
412 | c.g = p->rgba8[1] / 255.0f; |
413 | c.b = p->rgba8[2] / 255.0f; |
414 | c.a = p->rgba8[3] / 255.0f; |
415 | } |
416 | |
417 | static void getPixelR16(const ImageData::Pixel *p, Colorf &c) |
418 | { |
419 | c.r = p->rgba16[0] / 65535.0f; |
420 | c.g = 0.0f; |
421 | c.b = 0.0f; |
422 | c.a = 1.0f; |
423 | } |
424 | |
425 | static void getPixelRG16(const ImageData::Pixel *p, Colorf &c) |
426 | { |
427 | c.r = p->rgba16[0] / 65535.0f; |
428 | c.g = p->rgba16[1] / 65535.0f; |
429 | c.b = 0.0f; |
430 | c.a = 1.0f; |
431 | } |
432 | |
433 | static void getPixelRGBA16(const ImageData::Pixel *p, Colorf &c) |
434 | { |
435 | c.r = p->rgba16[0] / 65535.0f; |
436 | c.g = p->rgba16[1] / 65535.0f; |
437 | c.b = p->rgba16[2] / 65535.0f; |
438 | c.a = p->rgba16[3] / 65535.0f; |
439 | } |
440 | |
441 | static void getPixelR16F(const ImageData::Pixel *p, Colorf &c) |
442 | { |
443 | c.r = float16to32(p->rgba16f[0]); |
444 | c.g = 0.0f; |
445 | c.b = 0.0f; |
446 | c.a = 1.0f; |
447 | } |
448 | |
449 | static void getPixelRG16F(const ImageData::Pixel *p, Colorf &c) |
450 | { |
451 | c.r = float16to32(p->rgba16f[0]); |
452 | c.g = float16to32(p->rgba16f[1]); |
453 | c.b = 0.0f; |
454 | c.a = 1.0f; |
455 | } |
456 | |
457 | static void getPixelRGBA16F(const ImageData::Pixel *p, Colorf &c) |
458 | { |
459 | c.r = float16to32(p->rgba16f[0]); |
460 | c.g = float16to32(p->rgba16f[1]); |
461 | c.b = float16to32(p->rgba16f[2]); |
462 | c.a = float16to32(p->rgba16f[3]); |
463 | } |
464 | |
465 | static void getPixelR32F(const ImageData::Pixel *p, Colorf &c) |
466 | { |
467 | c.r = p->rgba32f[0]; |
468 | c.g = 0.0f; |
469 | c.b = 0.0f; |
470 | c.a = 1.0f; |
471 | } |
472 | |
473 | static void getPixelRG32F(const ImageData::Pixel *p, Colorf &c) |
474 | { |
475 | c.r = p->rgba32f[0]; |
476 | c.g = p->rgba32f[1]; |
477 | c.b = 0.0f; |
478 | c.a = 1.0f; |
479 | } |
480 | |
481 | static void getPixelRGBA32F(const ImageData::Pixel *p, Colorf &c) |
482 | { |
483 | c.r = p->rgba32f[0]; |
484 | c.g = p->rgba32f[1]; |
485 | c.b = p->rgba32f[2]; |
486 | c.a = p->rgba32f[3]; |
487 | } |
488 | |
489 | static void getPixelRGBA4(const ImageData::Pixel *p, Colorf &c) |
490 | { |
491 | // LSB->MSB: [a, b, g, r] |
492 | c.r = ((p->packed16 >> 12) & 0xF) / (float)0xF; |
493 | c.g = ((p->packed16 >> 8) & 0xF) / (float)0xF; |
494 | c.b = ((p->packed16 >> 4) & 0xF) / (float)0xF; |
495 | c.a = ((p->packed16 >> 0) & 0xF) / (float)0xF; |
496 | } |
497 | |
498 | static void getPixelRGB5A1(const ImageData::Pixel *p, Colorf &c) |
499 | { |
500 | // LSB->MSB: [a, b, g, r] |
501 | c.r = ((p->packed16 >> 11) & 0x1F) / (float)0x1F; |
502 | c.g = ((p->packed16 >> 6) & 0x1F) / (float)0x1F; |
503 | c.b = ((p->packed16 >> 1) & 0x1F) / (float)0x1F; |
504 | c.a = ((p->packed16 >> 0) & 0x1) / (float)0x1; |
505 | } |
506 | |
507 | static void getPixelRGB565(const ImageData::Pixel *p, Colorf &c) |
508 | { |
509 | // LSB->MSB: [b, g, r] |
510 | c.r = ((p->packed16 >> 11) & 0x1F) / (float)0x1F; |
511 | c.g = ((p->packed16 >> 5) & 0x3F) / (float)0x3F; |
512 | c.b = ((p->packed16 >> 0) & 0x1F) / (float)0x1F; |
513 | c.a = 1.0f; |
514 | } |
515 | |
516 | static void getPixelRGB10A2(const ImageData::Pixel *p, Colorf &c) |
517 | { |
518 | // LSB->MSB: [r, g, b, a] |
519 | c.r = ((p->packed32 >> 0) & 0x3FF) / (float)0x3FF; |
520 | c.g = ((p->packed32 >> 10) & 0x3FF) / (float)0x3FF; |
521 | c.b = ((p->packed32 >> 20) & 0x3FF) / (float)0x3FF; |
522 | c.a = ((p->packed32 >> 30) & 0x3) / (float)0x3; |
523 | } |
524 | |
525 | static void getPixelRG11B10F(const ImageData::Pixel *p, Colorf &c) |
526 | { |
527 | // LSB->MSB: [r, g, b] |
528 | c.r = float11to32((float11) ((p->packed32 >> 0) & 0x7FF)); |
529 | c.g = float11to32((float11) ((p->packed32 >> 11) & 0x7FF)); |
530 | c.b = float10to32((float10) ((p->packed32 >> 22) & 0x3FF)); |
531 | c.a = 1.0f; |
532 | } |
533 | |
534 | void ImageData::setPixel(int x, int y, const Colorf &c) |
535 | { |
536 | if (!inside(x, y)) |
537 | throw love::Exception("Attempt to set out-of-range pixel!" ); |
538 | |
539 | size_t pixelsize = getPixelSize(); |
540 | Pixel *p = (Pixel *) (data + ((y * width + x) * pixelsize)); |
541 | |
542 | if (pixelSetFunction == nullptr) |
543 | throw love::Exception("Unhandled pixel format %d in ImageData::setPixel" , format); |
544 | |
545 | Lock lock(mutex); |
546 | |
547 | pixelSetFunction(c, p); |
548 | } |
549 | |
550 | void ImageData::getPixel(int x, int y, Colorf &c) const |
551 | { |
552 | if (!inside(x, y)) |
553 | throw love::Exception("Attempt to get out-of-range pixel!" ); |
554 | |
555 | size_t pixelsize = getPixelSize(); |
556 | const Pixel *p = (const Pixel *) (data + ((y * width + x) * pixelsize)); |
557 | |
558 | if (pixelGetFunction == nullptr) |
559 | throw love::Exception("Unhandled pixel format %d in ImageData::setPixel" , format); |
560 | |
561 | Lock lock(mutex); |
562 | |
563 | pixelGetFunction(p, c); |
564 | } |
565 | |
566 | Colorf ImageData::getPixel(int x, int y) const |
567 | { |
568 | Colorf c; |
569 | getPixel(x, y, c); |
570 | return c; |
571 | } |
572 | |
573 | union Row |
574 | { |
575 | uint8 *u8; |
576 | uint16 *u16; |
577 | float16 *f16; |
578 | float *f32; |
579 | }; |
580 | |
581 | static void pasteRGBA8toRGBA16(Row src, Row dst, int w) |
582 | { |
583 | for (int i = 0; i < w * 4; i++) |
584 | dst.u16[i] = (uint16) src.u8[i] << 8u; |
585 | } |
586 | |
587 | static void pasteRGBA8toRGBA16F(Row src, Row dst, int w) |
588 | { |
589 | for (int i = 0; i < w * 4; i++) |
590 | dst.f16[i] = float32to16(src.u8[i] / 255.0f); |
591 | } |
592 | |
593 | static void pasteRGBA8toRGBA32F(Row src, Row dst, int w) |
594 | { |
595 | for (int i = 0; i < w * 4; i++) |
596 | dst.f32[i] = src.u8[i] / 255.0f; |
597 | } |
598 | |
599 | static void pasteRGBA16toRGBA8(Row src, Row dst, int w) |
600 | { |
601 | for (int i = 0; i < w * 4; i++) |
602 | dst.u8[i] = src.u16[i] >> 8u; |
603 | } |
604 | |
605 | static void pasteRGBA16toRGBA16F(Row src, Row dst, int w) |
606 | { |
607 | for (int i = 0; i < w * 4; i++) |
608 | dst.f16[i] = float32to16(src.u16[i] / 65535.0f); |
609 | } |
610 | |
611 | static void pasteRGBA16toRGBA32F(Row src, Row dst, int w) |
612 | { |
613 | for (int i = 0; i < w * 4; i++) |
614 | dst.f32[i] = src.u16[i] / 65535.0f; |
615 | } |
616 | |
617 | static void pasteRGBA16FtoRGBA8(Row src, Row dst, int w) |
618 | { |
619 | for (int i = 0; i < w * 4; i++) |
620 | dst.u8[i] = (uint8) (clamp01(float16to32(src.f16[i])) * 255.0f + 0.5f); |
621 | } |
622 | |
623 | static void pasteRGBA16FtoRGBA16(Row src, Row dst, int w) |
624 | { |
625 | for (int i = 0; i < w * 4; i++) |
626 | dst.u16[i] = (uint16) (clamp01(float16to32(src.f16[i])) * 65535.0f + 0.5f); |
627 | } |
628 | |
629 | static void pasteRGBA16FtoRGBA32F(Row src, Row dst, int w) |
630 | { |
631 | for (int i = 0; i < w * 4; i++) |
632 | dst.f32[i] = float16to32(src.f16[i]); |
633 | } |
634 | |
635 | static void pasteRGBA32FtoRGBA8(Row src, Row dst, int w) |
636 | { |
637 | for (int i = 0; i < w * 4; i++) |
638 | dst.u8[i] = (uint8) (clamp01(src.f32[i]) * 255.0f + 0.5f); |
639 | } |
640 | |
641 | static void pasteRGBA32FtoRGBA16(Row src, Row dst, int w) |
642 | { |
643 | for (int i = 0; i < w * 4; i++) |
644 | dst.u16[i] = (uint16) (clamp01(src.f32[i]) * 65535.0f + 0.5f); |
645 | } |
646 | |
647 | static void pasteRGBA32FtoRGBA16F(Row src, Row dst, int w) |
648 | { |
649 | for (int i = 0; i < w * 4; i++) |
650 | dst.f16[i] = float32to16(src.f32[i]); |
651 | } |
652 | |
653 | void ImageData::paste(ImageData *src, int dx, int dy, int sx, int sy, int sw, int sh) |
654 | { |
655 | PixelFormat dstformat = getFormat(); |
656 | PixelFormat srcformat = src->getFormat(); |
657 | |
658 | int srcW = src->getWidth(); |
659 | int srcH = src->getHeight(); |
660 | int dstW = getWidth(); |
661 | int dstH = getHeight(); |
662 | |
663 | size_t srcpixelsize = src->getPixelSize(); |
664 | size_t dstpixelsize = getPixelSize(); |
665 | |
666 | // Check bounds; if the data ends up completely out of bounds, get out early. |
667 | if (sx >= srcW || sx + sw < 0 || sy >= srcH || sy + sh < 0 |
668 | || dx >= dstW || dx + sw < 0 || dy >= dstH || dy + sh < 0) |
669 | return; |
670 | |
671 | // Normalize values to the inside of both images. |
672 | if (dx < 0) |
673 | { |
674 | sw += dx; |
675 | sx -= dx; |
676 | dx = 0; |
677 | } |
678 | if (dy < 0) |
679 | { |
680 | sh += dy; |
681 | sy -= dy; |
682 | dy = 0; |
683 | } |
684 | if (sx < 0) |
685 | { |
686 | sw += sx; |
687 | dx -= sx; |
688 | sx = 0; |
689 | } |
690 | if (sy < 0) |
691 | { |
692 | sh += sy; |
693 | dy -= sy; |
694 | sy = 0; |
695 | } |
696 | |
697 | if (dx + sw > dstW) |
698 | sw = dstW - dx; |
699 | |
700 | if (dy + sh > dstH) |
701 | sh = dstH - dy; |
702 | |
703 | if (sx + sw > srcW) |
704 | sw = srcW - sx; |
705 | |
706 | if (sy + sh > srcH) |
707 | sh = srcH - sy; |
708 | |
709 | Lock lock2(src->mutex); |
710 | Lock lock1(mutex); |
711 | |
712 | uint8 *s = (uint8 *) src->getData(); |
713 | uint8 *d = (uint8 *) getData(); |
714 | |
715 | auto getfunction = src->pixelGetFunction; |
716 | auto setfunction = pixelSetFunction; |
717 | |
718 | // If the dimensions match up, copy the entire memory stream in one go |
719 | if (srcformat == dstformat && (sw == dstW && dstW == srcW && sh == dstH && dstH == srcH)) |
720 | { |
721 | memcpy(d, s, srcpixelsize * sw * sh); |
722 | } |
723 | else if (sw > 0) |
724 | { |
725 | // Otherwise, copy each row individually. |
726 | for (int i = 0; i < sh; i++) |
727 | { |
728 | Row rowsrc = {s + (sx + (i + sy) * srcW) * srcpixelsize}; |
729 | Row rowdst = {d + (dx + (i + dy) * dstW) * dstpixelsize}; |
730 | |
731 | if (srcformat == dstformat) |
732 | memcpy(rowdst.u8, rowsrc.u8, srcpixelsize * sw); |
733 | |
734 | else if (srcformat == PIXELFORMAT_RGBA8 && dstformat == PIXELFORMAT_RGBA16) |
735 | pasteRGBA8toRGBA16(rowsrc, rowdst, sw); |
736 | else if (srcformat == PIXELFORMAT_RGBA8 && dstformat == PIXELFORMAT_RGBA16F) |
737 | pasteRGBA8toRGBA16F(rowsrc, rowdst, sw); |
738 | else if (srcformat == PIXELFORMAT_RGBA8 && dstformat == PIXELFORMAT_RGBA32F) |
739 | pasteRGBA8toRGBA32F(rowsrc, rowdst, sw); |
740 | |
741 | else if (srcformat == PIXELFORMAT_RGBA16 && dstformat == PIXELFORMAT_RGBA8) |
742 | pasteRGBA16toRGBA8(rowsrc, rowdst, sw); |
743 | else if (srcformat == PIXELFORMAT_RGBA16 && dstformat == PIXELFORMAT_RGBA16F) |
744 | pasteRGBA16toRGBA16F(rowsrc, rowdst, sw); |
745 | else if (srcformat == PIXELFORMAT_RGBA16 && dstformat == PIXELFORMAT_RGBA32F) |
746 | pasteRGBA16toRGBA32F(rowsrc, rowdst, sw); |
747 | |
748 | else if (srcformat == PIXELFORMAT_RGBA16F && dstformat == PIXELFORMAT_RGBA8) |
749 | pasteRGBA16FtoRGBA8(rowsrc, rowdst, sw); |
750 | else if (srcformat == PIXELFORMAT_RGBA16F && dstformat == PIXELFORMAT_RGBA16) |
751 | pasteRGBA16FtoRGBA16(rowsrc, rowdst, sw); |
752 | else if (srcformat == PIXELFORMAT_RGBA16F && dstformat == PIXELFORMAT_RGBA32F) |
753 | pasteRGBA16FtoRGBA32F(rowsrc, rowdst, sw); |
754 | |
755 | else if (srcformat == PIXELFORMAT_RGBA32F && dstformat == PIXELFORMAT_RGBA8) |
756 | pasteRGBA32FtoRGBA8(rowsrc, rowdst, sw); |
757 | else if (srcformat == PIXELFORMAT_RGBA32F && dstformat == PIXELFORMAT_RGBA16) |
758 | pasteRGBA32FtoRGBA16(rowsrc, rowdst, sw); |
759 | else if (srcformat == PIXELFORMAT_RGBA32F && dstformat == PIXELFORMAT_RGBA16F) |
760 | pasteRGBA32FtoRGBA16F(rowsrc, rowdst, sw); |
761 | |
762 | else |
763 | { |
764 | // Slow path: convert src -> Colorf -> dst. |
765 | Colorf c; |
766 | for (int x = 0; x < sw; x++) |
767 | { |
768 | auto srcp = (const Pixel *) (rowsrc.u8 + x * srcpixelsize); |
769 | auto dstp = (Pixel *) (rowdst.u8 + x * dstpixelsize); |
770 | getfunction(srcp, c); |
771 | setfunction(c, dstp); |
772 | } |
773 | } |
774 | } |
775 | } |
776 | } |
777 | |
778 | love::thread::Mutex *ImageData::getMutex() const |
779 | { |
780 | return mutex; |
781 | } |
782 | |
783 | size_t ImageData::getPixelSize() const |
784 | { |
785 | return getPixelFormatSize(format); |
786 | } |
787 | |
788 | bool ImageData::validPixelFormat(PixelFormat format) |
789 | { |
790 | switch (format) |
791 | { |
792 | case PIXELFORMAT_R8: |
793 | case PIXELFORMAT_RG8: |
794 | case PIXELFORMAT_RGBA8: |
795 | case PIXELFORMAT_R16: |
796 | case PIXELFORMAT_RG16: |
797 | case PIXELFORMAT_RGBA16: |
798 | case PIXELFORMAT_R16F: |
799 | case PIXELFORMAT_RG16F: |
800 | case PIXELFORMAT_RGBA16F: |
801 | case PIXELFORMAT_R32F: |
802 | case PIXELFORMAT_RG32F: |
803 | case PIXELFORMAT_RGBA32F: |
804 | case PIXELFORMAT_RGBA4: |
805 | case PIXELFORMAT_RGB5A1: |
806 | case PIXELFORMAT_RGB565: |
807 | case PIXELFORMAT_RGB10A2: |
808 | case PIXELFORMAT_RG11B10F: |
809 | return true; |
810 | default: |
811 | return false; |
812 | } |
813 | } |
814 | |
815 | bool ImageData::canPaste(PixelFormat src, PixelFormat dst) |
816 | { |
817 | if (src == dst) |
818 | return true; |
819 | |
820 | if (!(src == PIXELFORMAT_RGBA8 || src == PIXELFORMAT_RGBA16 |
821 | || src == PIXELFORMAT_RGBA16F || src == PIXELFORMAT_RGBA32F)) |
822 | return false; |
823 | |
824 | if (!(dst == PIXELFORMAT_RGBA8 || dst == PIXELFORMAT_RGBA16 |
825 | || dst == PIXELFORMAT_RGBA16F || dst == PIXELFORMAT_RGBA32F)) |
826 | return false; |
827 | |
828 | return true; |
829 | } |
830 | |
831 | ImageData::PixelSetFunction ImageData::getPixelSetFunction(PixelFormat format) |
832 | { |
833 | switch (format) |
834 | { |
835 | case PIXELFORMAT_R8: return setPixelR8; |
836 | case PIXELFORMAT_RG8: return setPixelRG8; |
837 | case PIXELFORMAT_RGBA8: return setPixelRGBA8; |
838 | case PIXELFORMAT_R16: return setPixelR16; |
839 | case PIXELFORMAT_RG16: return setPixelRG16; |
840 | case PIXELFORMAT_RGBA16: return setPixelRGBA16; |
841 | case PIXELFORMAT_R16F: return setPixelR16F; |
842 | case PIXELFORMAT_RG16F: return setPixelRG16F; |
843 | case PIXELFORMAT_RGBA16F: return setPixelRGBA16F; |
844 | case PIXELFORMAT_R32F: return setPixelR32F; |
845 | case PIXELFORMAT_RG32F: return setPixelRG32F; |
846 | case PIXELFORMAT_RGBA32F: return setPixelRGBA32F; |
847 | case PIXELFORMAT_RGBA4: return setPixelRGBA4; |
848 | case PIXELFORMAT_RGB5A1: return setPixelRGB5A1; |
849 | case PIXELFORMAT_RGB565: return setPixelRGB565; |
850 | case PIXELFORMAT_RGB10A2: return setPixelRGB10A2; |
851 | case PIXELFORMAT_RG11B10F: return setPixelRG11B10F; |
852 | default: return nullptr; |
853 | } |
854 | } |
855 | |
856 | ImageData::PixelGetFunction ImageData::getPixelGetFunction(PixelFormat format) |
857 | { |
858 | switch (format) |
859 | { |
860 | case PIXELFORMAT_R8: return getPixelR8; |
861 | case PIXELFORMAT_RG8: return getPixelRG8; |
862 | case PIXELFORMAT_RGBA8: return getPixelRGBA8; |
863 | case PIXELFORMAT_R16: return getPixelR16; |
864 | case PIXELFORMAT_RG16: return getPixelRG16; |
865 | case PIXELFORMAT_RGBA16: return getPixelRGBA16; |
866 | case PIXELFORMAT_R16F: return getPixelR16F; |
867 | case PIXELFORMAT_RG16F: return getPixelRG16F; |
868 | case PIXELFORMAT_RGBA16F: return getPixelRGBA16F; |
869 | case PIXELFORMAT_R32F: return getPixelR32F; |
870 | case PIXELFORMAT_RG32F: return getPixelRG32F; |
871 | case PIXELFORMAT_RGBA32F: return getPixelRGBA32F; |
872 | case PIXELFORMAT_RGBA4: return getPixelRGBA4; |
873 | case PIXELFORMAT_RGB5A1: return getPixelRGB5A1; |
874 | case PIXELFORMAT_RGB565: return getPixelRGB565; |
875 | case PIXELFORMAT_RGB10A2: return getPixelRGB10A2; |
876 | case PIXELFORMAT_RG11B10F: return getPixelRG11B10F; |
877 | default: return nullptr; |
878 | } |
879 | } |
880 | |
881 | bool ImageData::getConstant(const char *in, FormatHandler::EncodedFormat &out) |
882 | { |
883 | return encodedFormats.find(in, out); |
884 | } |
885 | |
886 | bool ImageData::getConstant(FormatHandler::EncodedFormat in, const char *&out) |
887 | { |
888 | return encodedFormats.find(in, out); |
889 | } |
890 | |
891 | std::vector<std::string> ImageData::getConstants(FormatHandler::EncodedFormat) |
892 | { |
893 | return encodedFormats.getNames(); |
894 | } |
895 | |
896 | StringMap<FormatHandler::EncodedFormat, FormatHandler::ENCODED_MAX_ENUM>::Entry ImageData::encodedFormatEntries[] = |
897 | { |
898 | {"tga" , FormatHandler::ENCODED_TGA}, |
899 | {"png" , FormatHandler::ENCODED_PNG}, |
900 | }; |
901 | |
902 | StringMap<FormatHandler::EncodedFormat, FormatHandler::ENCODED_MAX_ENUM> ImageData::encodedFormats(ImageData::encodedFormatEntries, sizeof(ImageData::encodedFormatEntries)); |
903 | |
904 | } // image |
905 | } // love |
906 | |