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
27using love::thread::Lock;
28
29namespace love
30{
31namespace image
32{
33
34love::Type ImageData::type("ImageData", &Data::type);
35
36ImageData::ImageData(Data *data)
37 : ImageDataBase(PIXELFORMAT_UNKNOWN, 0, 0)
38{
39 decode(data);
40}
41
42ImageData::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
54ImageData::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
66ImageData::ImageData(const ImageData &c)
67 : ImageDataBase(c.format, c.width, c.height)
68{
69 create(width, height, format, c.getData());
70}
71
72ImageData::~ImageData()
73{
74 if (decodeHandler.get())
75 decodeHandler->freeRawPixels(data);
76 else
77 delete[] data;
78}
79
80love::image::ImageData *ImageData::clone() const
81{
82 return new ImageData(*this);
83}
84
85void 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
108void 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
166love::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
244size_t ImageData::getSize() const
245{
246 return size_t(getWidth() * getHeight()) * getPixelSize();
247}
248
249void *ImageData::getData() const
250{
251 return data;
252}
253
254bool ImageData::isSRGB() const
255{
256 return false;
257}
258
259bool ImageData::inside(int x, int y) const
260{
261 return x >= 0 && x < getWidth() && y >= 0 && y < getHeight();
262}
263
264static float clamp01(float x)
265{
266 return std::min(std::max(x, 0.0f), 1.0f);
267}
268
269static void setPixelR8(const Colorf &c, ImageData::Pixel *p)
270{
271 p->rgba8[0] = (uint8) (clamp01(c.r) * 255.0f + 0.5f);
272}
273
274static 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
280static 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
288static void setPixelR16(const Colorf &c, ImageData::Pixel *p)
289{
290 p->rgba16[0] = (uint16) (clamp01(c.r) * 65535.0f + 0.5f);
291}
292
293static 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
299static 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
307static void setPixelR16F(const Colorf &c, ImageData::Pixel *p)
308{
309 p->rgba16f[0] = float32to16(c.r);
310}
311
312static 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
318static 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
326static void setPixelR32F(const Colorf &c, ImageData::Pixel *p)
327{
328 p->rgba32f[0] = c.r;
329}
330
331static void setPixelRG32F(const Colorf &c, ImageData::Pixel *p)
332{
333 p->rgba32f[0] = c.r;
334 p->rgba32f[1] = c.g;
335}
336
337static 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
345static 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
355static 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
365static 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
374static 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
384static 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
393static 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
401static 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
409static 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
417static 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
425static 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
433static 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
441static 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
449static 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
457static 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
465static 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
473static 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
481static 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
489static 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
498static 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
507static 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
516static 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
525static 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
534void 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
550void 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
566Colorf ImageData::getPixel(int x, int y) const
567{
568 Colorf c;
569 getPixel(x, y, c);
570 return c;
571}
572
573union Row
574{
575 uint8 *u8;
576 uint16 *u16;
577 float16 *f16;
578 float *f32;
579};
580
581static 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
587static 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
593static 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
599static 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
605static 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
611static 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
617static 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
623static 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
629static 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
635static 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
641static 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
647static 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
653void 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
778love::thread::Mutex *ImageData::getMutex() const
779{
780 return mutex;
781}
782
783size_t ImageData::getPixelSize() const
784{
785 return getPixelFormatSize(format);
786}
787
788bool 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
815bool 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
831ImageData::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
856ImageData::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
881bool ImageData::getConstant(const char *in, FormatHandler::EncodedFormat &out)
882{
883 return encodedFormats.find(in, out);
884}
885
886bool ImageData::getConstant(FormatHandler::EncodedFormat in, const char *&out)
887{
888 return encodedFormats.find(in, out);
889}
890
891std::vector<std::string> ImageData::getConstants(FormatHandler::EncodedFormat)
892{
893 return encodedFormats.getNames();
894}
895
896StringMap<FormatHandler::EncodedFormat, FormatHandler::ENCODED_MAX_ENUM>::Entry ImageData::encodedFormatEntries[] =
897{
898 {"tga", FormatHandler::ENCODED_TGA},
899 {"png", FormatHandler::ENCODED_PNG},
900};
901
902StringMap<FormatHandler::EncodedFormat, FormatHandler::ENCODED_MAX_ENUM> ImageData::encodedFormats(ImageData::encodedFormatEntries, sizeof(ImageData::encodedFormatEntries));
903
904} // image
905} // love
906