载入中...
搜索中...
未找到
ImageData.cpp
浏览该文件的文档.
1
2
3#include "ImageData.h"
4#include "Image.h"
7#include "medialoader/image/pixelformat.h"
8
9#include "common/Assert.h"
10#include "common/Exception.h"
11
12#include <algorithm> // min/max
13#include <cmath>
14#include <cstring> // memcpy
15#include <limits>
16#include <utility>
17#include <vector>
18
19using namespace medialoader;
20
21namespace eve {
22namespace image {
23
24ImageData::ImageData(Data *data) : Resource(""), decodeHandler(nullptr) { decode(data); }
25
26ImageData::ImageData(int width, int height, std::string format) : Resource(""), width(width), height(height), format(format), decodeHandler(nullptr) {
27 if (!validPixelFormat(format)) throw eve::Exception("Unsupported pixel format for ImageData");
28
29 create(width, height, format);
30
31 // Set to black/transparency.
32 memset(data, 0, getSize());
33}
34
35ImageData::ImageData(int width, int height, std::string format, void *data, bool own)
36 : Resource(""), width(width), height(height), format(format), decodeHandler(nullptr)
37{
38 if (!validPixelFormat(format)) throw eve::Exception("Unsupported pixel format for ImageData");
39
40 if (own)
41 this->data = (unsigned char *)data;
42 else
43 create(width, height, format, data);
44}
45
46ImageData::ImageData(const ImageData &c) : Resource(c.getUri()), decodeHandler(nullptr) {
47 width = c.width;
48 height = c.height;
49 format = c.format;
50
51 create(width, height, format, c.getData());
52}
53
55 if (decodeHandler)
56 decodeHandler->freeRawPixels(data);
57 else
58 delete[] data;
59}
60
61void ImageData::adopt(eve::Resource &replacement) {
62 auto &other = static_cast<ImageData &>(replacement);
63 std::swap(data, other.data);
64 std::swap(width, other.width);
65 std::swap(height, other.height);
66 std::swap(format, other.format);
67 std::swap(decodeHandler, other.decodeHandler);
68 std::swap(pixelSetFunction, other.pixelSetFunction);
69 std::swap(pixelGetFunction, other.pixelGetFunction);
70}
71
73 try {
74 return new ImageData(*this);
75 } catch (std::bad_alloc &) {
76 throw eve::Exception("Out of memory");
77 }
78}
79
80void ImageData::create(int width, int height, std::string format, void *data) {
81 size_t datasize = width * height * getPixelFormatSize(getPixelFormatFromName(format));
82
83 try {
84 this->data = new unsigned char[datasize];
85 } catch (std::bad_alloc &) {
86 throw eve::Exception("Out of memory");
87 }
88
89 if (data) memcpy(this->data, data, datasize);
90
91 decodeHandler = nullptr;
92 this->format = format;
93
94 pixelSetFunction = getPixelSetFunction(format);
95 pixelGetFunction = getPixelGetFunction(format);
96}
97
98void ImageData::decode(Data *data) {
99 FormatHandler *decoder = nullptr;
100 FormatHandler::DecodedImage decodedimage;
101
102 auto module = Image::create();
103
104 if (module == nullptr) throw eve::Exception("eve.image must be loaded in order to decode an ImageData.");
105
106 for (FormatHandler *handler : module->getFormatHandlers()) {
107 if (handler->canDecode((const char*)data->getData(), data->getSize())) {
108 decoder = handler;
109 break;
110 }
111 }
112
113 if (decoder) decodedimage = decoder->decode((const char*)data->getData(), data->getSize());
114
115 if (decodedimage.data == nullptr) {
116 auto filedata = dynamic_cast<filesystem::FileData *>(data);
117
118 if (filedata != nullptr) {
119 const std::string &name = filedata->getFilename();
120 throw eve::Exception("Could not decode file '%s' to ImageData: unsupported file format", name.c_str());
121 } else
122 throw eve::Exception("Could not decode data to ImageData: unsupported encoded format");
123 }
124
125 if (decodedimage.size != decodedimage.width * decodedimage.height * getPixelFormatSize(decodedimage.format)) {
126 decoder->freeRawPixels(decodedimage.data);
127 throw eve::Exception("Could not convert image!");
128 }
129
130 // Clean up any old data.
131 if (decodeHandler)
132 decodeHandler->freeRawPixels(this->data);
133 else
134 delete[] this->data;
135
136 this->width = decodedimage.width;
137 this->height = decodedimage.height;
138 this->data = decodedimage.data;
139 // PixelFormat enum must not be assigned into std::string (that becomes a single char).
140 this->format = [pf = decodedimage.format]() -> std::string {
141 switch (pf) {
142 case PIXELFORMAT_R8: return "R8";
143 case PIXELFORMAT_RG8: return "RG8";
144 case PIXELFORMAT_RGBA8:
145 case PIXELFORMAT_sRGBA8: return "RGBA8";
146 case PIXELFORMAT_R16: return "R16";
147 case PIXELFORMAT_RG16: return "RG16";
148 case PIXELFORMAT_RGBA16: return "RGBA16";
149 case PIXELFORMAT_R16F: return "R16F";
150 case PIXELFORMAT_RG16F: return "RG16F";
151 case PIXELFORMAT_RGBA16F: return "RGBA16F";
152 case PIXELFORMAT_R32F: return "R32F";
153 case PIXELFORMAT_RG32F: return "RG32F";
154 case PIXELFORMAT_RGBA32F: return "RGBA32F";
155 case PIXELFORMAT_RGBA4: return "RGBA4";
156 case PIXELFORMAT_RGB5A1: return "RGB5A1";
157 case PIXELFORMAT_RGB565: return "RGB565";
158 case PIXELFORMAT_RGB10A2: return "RGB10A2";
159 case PIXELFORMAT_RG11B10F: return "RG11B10F";
160 default: return "RGBA8";
161 }
162 }();
163
164 decodeHandler = decoder;
165
166 pixelSetFunction = getPixelSetFunction(format);
167 pixelGetFunction = getPixelGetFunction(format);
168}
169
170eve::filesystem::FileData *ImageData::encode(FormatHandler::EncodedFormat encodedFormat, const char *filename,
171 bool writefile) const {
172 EV_PARAM_CHECK(filename != nullptr, "output filename must not be null");
173
174 FormatHandler *encoder = nullptr;
175 FormatHandler::EncodedImage encodedimage;
176 FormatHandler::DecodedImage rawimage;
177
178 rawimage.width = width;
179 rawimage.height = height;
180 rawimage.size = getSize();
181 rawimage.data = data;
182 rawimage.format = getPixelFormatFromName(format);
183
184 auto module = Image::create();
185
186 if (module == nullptr) throw eve::Exception("eve.image must be loaded in order to encode an ImageData.");
187
188 for (FormatHandler *handler : module->getFormatHandlers()) {
189 if (handler->canEncode(getPixelFormatFromName(format), encodedFormat)) {
190 encoder = handler;
191 break;
192 }
193 }
194
195 if (encoder != nullptr) {
196 encodedimage = encoder->encode(rawimage, encodedFormat);
197 }
198
199 if (encoder == nullptr || encodedimage.data == nullptr) {
200 throw eve::Exception("No suitable image encoder for %s format.", format.c_str());
201 }
202
203 eve::filesystem::FileData *filedata = nullptr;
204
205 try {
206 filedata = new eve::filesystem::FileData(filename, encodedimage.size);
207 } catch (eve::Exception &) {
208 encoder->freeRawPixels(encodedimage.data);
209 throw;
210 }
211
212 memcpy(filedata->getData(), encodedimage.data, encodedimage.size);
213 encoder->freeRawPixels(encodedimage.data);
214
215 if (writefile) {
216 auto fs = eve::filesystem::Filesystem::create();
217
218 if (fs == nullptr) {
219 // filedata->release();
220 throw eve::Exception("eve.filesystem must be loaded in order to write an encoded ImageData to a file.");
221 }
222
223 try {
224 fs->write(filename, filedata->getData(), filedata->getSize());
225 } catch (eve::Exception &) {
226 // filedata->release();
227 throw;
228 }
229 }
230
231 return filedata;
232}
233
234size_t ImageData::getSize() const { return size_t(getWidth() * getHeight()) * getPixelSize(); }
235
236void *ImageData::getData() const { return data; }
237
238bool ImageData::isSRGB() const { return false; }
239
240bool ImageData::inside(int x, int y) const { return x >= 0 && x < getWidth() && y >= 0 && y < getHeight(); }
241
242int ImageData::getWidth() const { return width; }
243
244int ImageData::getHeight() const { return height; }
245
246std::string ImageData::getFormat() const { return format; }
247
248static float clamp01(float x) { return std::min(std::max(x, 0.0f), 1.0f); }
249
250static void setPixelR8(const Colorf &c, ImageData::Pixel *p) { p->rgba8[0] = (uint8_t)(clamp01(c.r) * 255.0f + 0.5f); }
251
252static void setPixelRG8(const Colorf &c, ImageData::Pixel *p) {
253 p->rgba8[0] = (uint8_t)(clamp01(c.r) * 255.0f + 0.5f);
254 p->rgba8[1] = (uint8_t)(clamp01(c.g) * 255.0f + 0.5f);
255}
256
257static void setPixelRGBA8(const Colorf &c, ImageData::Pixel *p) {
258 p->rgba8[0] = (uint8_t)(clamp01(c.r) * 255.0f + 0.5f);
259 p->rgba8[1] = (uint8_t)(clamp01(c.g) * 255.0f + 0.5f);
260 p->rgba8[2] = (uint8_t)(clamp01(c.b) * 255.0f + 0.5f);
261 p->rgba8[3] = (uint8_t)(clamp01(c.a) * 255.0f + 0.5f);
262}
263
264static void setPixelR16(const Colorf &c, ImageData::Pixel *p) {
265 p->rgba16[0] = (uint16_t)(clamp01(c.r) * 65535.0f + 0.5f);
266}
267
268static void setPixelRG16(const Colorf &c, ImageData::Pixel *p) {
269 p->rgba16[0] = (uint16_t)(clamp01(c.r) * 65535.0f + 0.5f);
270 p->rgba16[1] = (uint16_t)(clamp01(c.g) * 65535.0f + 0.5f);
271}
272
273static void setPixelRGBA16(const Colorf &c, ImageData::Pixel *p) {
274 p->rgba16[0] = (uint16_t)(clamp01(c.r) * 65535.0f + 0.5f);
275 p->rgba16[1] = (uint16_t)(clamp01(c.b) * 65535.0f + 0.5f);
276 p->rgba16[2] = (uint16_t)(clamp01(c.g) * 65535.0f + 0.5f);
277 p->rgba16[3] = (uint16_t)(clamp01(c.a) * 65535.0f + 0.5f);
278}
279
280static void setPixelR16F(const Colorf &c, ImageData::Pixel *p) { p->rgba16f[0] = float32to16(c.r); }
281
282static void setPixelRG16F(const Colorf &c, ImageData::Pixel *p) {
283 p->rgba16f[0] = float32to16(c.r);
284 p->rgba16f[1] = float32to16(c.g);
285}
286
287static void setPixelRGBA16F(const Colorf &c, ImageData::Pixel *p) {
288 p->rgba16f[0] = float32to16(c.r);
289 p->rgba16f[1] = float32to16(c.g);
290 p->rgba16f[2] = float32to16(c.b);
291 p->rgba16f[3] = float32to16(c.a);
292}
293
294static void setPixelR32F(const Colorf &c, ImageData::Pixel *p) { p->rgba32f[0] = c.r; }
295
296static void setPixelRG32F(const Colorf &c, ImageData::Pixel *p) {
297 p->rgba32f[0] = c.r;
298 p->rgba32f[1] = c.g;
299}
300
301static void setPixelRGBA32F(const Colorf &c, ImageData::Pixel *p) {
302 p->rgba32f[0] = c.r;
303 p->rgba32f[1] = c.g;
304 p->rgba32f[2] = c.b;
305 p->rgba32f[3] = c.a;
306}
307
308static void setPixelRGBA4(const Colorf &c, ImageData::Pixel *p) {
309 // LSB->MSB: [a, b, g, r]
310 uint16_t r = (uint16_t)(clamp01(c.r) * 0xF + 0.5);
311 uint16_t g = (uint16_t)(clamp01(c.g) * 0xF + 0.5);
312 uint16_t b = (uint16_t)(clamp01(c.b) * 0xF + 0.5);
313 uint16_t a = (uint16_t)(clamp01(c.a) * 0xF + 0.5);
314 p->packed16 = (r << 12) | (g << 8) | (b << 4) | (a << 0);
315}
316
317static void setPixelRGB5A1(const Colorf &c, ImageData::Pixel *p) {
318 // LSB->MSB: [a, b, g, r]
319 uint16_t r = (uint16_t)(clamp01(c.r) * 0x1F + 0.5);
320 uint16_t g = (uint16_t)(clamp01(c.g) * 0x1F + 0.5);
321 uint16_t b = (uint16_t)(clamp01(c.b) * 0x1F + 0.5);
322 uint16_t a = (uint16_t)(clamp01(c.a) * 0x1 + 0.5);
323 p->packed16 = (r << 11) | (g << 6) | (b << 1) | (a << 0);
324}
325
326static void setPixelRGB565(const Colorf &c, ImageData::Pixel *p) {
327 // LSB->MSB: [b, g, r]
328 uint16_t r = (uint16_t)(clamp01(c.r) * 0x1F + 0.5);
329 uint16_t g = (uint16_t)(clamp01(c.g) * 0x3F + 0.5);
330 uint16_t b = (uint16_t)(clamp01(c.b) * 0x1F + 0.5);
331 p->packed16 = (r << 11) | (g << 5) | (b << 0);
332}
333
334static void setPixelRGB10A2(const Colorf &c, ImageData::Pixel *p) {
335 // LSB->MSB: [r, g, b, a]
336 uint32_t r = (uint32_t)(clamp01(c.r) * 0x3FF + 0.5);
337 uint32_t g = (uint32_t)(clamp01(c.g) * 0x3FF + 0.5);
338 uint32_t b = (uint32_t)(clamp01(c.b) * 0x3FF + 0.5);
339 uint32_t a = (uint32_t)(clamp01(c.a) * 0x3 + 0.5);
340 p->packed32 = (r << 0) | (g << 10) | (b << 20) | (a << 30);
341}
342
343static void setPixelRG11B10F(const Colorf &c, ImageData::Pixel *p) {
344 // LSB->MSB: [r, g, b]
345 float11 r = float32to11(c.r);
346 float11 g = float32to11(c.g);
347 float10 b = float32to10(c.b);
348 p->packed32 = (r << 0) | (g << 11) | (b << 22);
349}
350
351static void getPixelR8(const ImageData::Pixel *p, Colorf &c) {
352 c.r = p->rgba8[0] / 255.0f;
353 c.g = 0.0f;
354 c.b = 0.0f;
355 c.a = 1.0f;
356}
357
358static void getPixelRG8(const ImageData::Pixel *p, Colorf &c) {
359 c.r = p->rgba8[0] / 255.0f;
360 c.g = p->rgba8[1] / 255.0f;
361 c.b = 0.0f;
362 c.a = 1.0f;
363}
364
365static void getPixelRGBA8(const ImageData::Pixel *p, Colorf &c) {
366 c.r = p->rgba8[0] / 255.0f;
367 c.g = p->rgba8[1] / 255.0f;
368 c.b = p->rgba8[2] / 255.0f;
369 c.a = p->rgba8[3] / 255.0f;
370}
371
372static void getPixelR16(const ImageData::Pixel *p, Colorf &c) {
373 c.r = p->rgba16[0] / 65535.0f;
374 c.g = 0.0f;
375 c.b = 0.0f;
376 c.a = 1.0f;
377}
378
379static void getPixelRG16(const ImageData::Pixel *p, Colorf &c) {
380 c.r = p->rgba16[0] / 65535.0f;
381 c.g = p->rgba16[1] / 65535.0f;
382 c.b = 0.0f;
383 c.a = 1.0f;
384}
385
386static void getPixelRGBA16(const ImageData::Pixel *p, Colorf &c) {
387 c.r = p->rgba16[0] / 65535.0f;
388 c.g = p->rgba16[1] / 65535.0f;
389 c.b = p->rgba16[2] / 65535.0f;
390 c.a = p->rgba16[3] / 65535.0f;
391}
392
393static void getPixelR16F(const ImageData::Pixel *p, Colorf &c) {
394 c.r = float16to32(p->rgba16f[0]);
395 c.g = 0.0f;
396 c.b = 0.0f;
397 c.a = 1.0f;
398}
399
400static void getPixelRG16F(const ImageData::Pixel *p, Colorf &c) {
401 c.r = float16to32(p->rgba16f[0]);
402 c.g = float16to32(p->rgba16f[1]);
403 c.b = 0.0f;
404 c.a = 1.0f;
405}
406
407static void getPixelRGBA16F(const ImageData::Pixel *p, Colorf &c) {
408 c.r = float16to32(p->rgba16f[0]);
409 c.g = float16to32(p->rgba16f[1]);
410 c.b = float16to32(p->rgba16f[2]);
411 c.a = float16to32(p->rgba16f[3]);
412}
413
414static void getPixelR32F(const ImageData::Pixel *p, Colorf &c) {
415 c.r = p->rgba32f[0];
416 c.g = 0.0f;
417 c.b = 0.0f;
418 c.a = 1.0f;
419}
420
421static void getPixelRG32F(const ImageData::Pixel *p, Colorf &c) {
422 c.r = p->rgba32f[0];
423 c.g = p->rgba32f[1];
424 c.b = 0.0f;
425 c.a = 1.0f;
426}
427
428static void getPixelRGBA32F(const ImageData::Pixel *p, Colorf &c) {
429 c.r = p->rgba32f[0];
430 c.g = p->rgba32f[1];
431 c.b = p->rgba32f[2];
432 c.a = p->rgba32f[3];
433}
434
435static void getPixelRGBA4(const ImageData::Pixel *p, Colorf &c) {
436 // LSB->MSB: [a, b, g, r]
437 c.r = ((p->packed16 >> 12) & 0xF) / (float)0xF;
438 c.g = ((p->packed16 >> 8) & 0xF) / (float)0xF;
439 c.b = ((p->packed16 >> 4) & 0xF) / (float)0xF;
440 c.a = ((p->packed16 >> 0) & 0xF) / (float)0xF;
441}
442
443static void getPixelRGB5A1(const ImageData::Pixel *p, Colorf &c) {
444 // LSB->MSB: [a, b, g, r]
445 c.r = ((p->packed16 >> 11) & 0x1F) / (float)0x1F;
446 c.g = ((p->packed16 >> 6) & 0x1F) / (float)0x1F;
447 c.b = ((p->packed16 >> 1) & 0x1F) / (float)0x1F;
448 c.a = ((p->packed16 >> 0) & 0x1) / (float)0x1;
449}
450
451static void getPixelRGB565(const ImageData::Pixel *p, Colorf &c) {
452 // LSB->MSB: [b, g, r]
453 c.r = ((p->packed16 >> 11) & 0x1F) / (float)0x1F;
454 c.g = ((p->packed16 >> 5) & 0x3F) / (float)0x3F;
455 c.b = ((p->packed16 >> 0) & 0x1F) / (float)0x1F;
456 c.a = 1.0f;
457}
458
459static void getPixelRGB10A2(const ImageData::Pixel *p, Colorf &c) {
460 // LSB->MSB: [r, g, b, a]
461 c.r = ((p->packed32 >> 0) & 0x3FF) / (float)0x3FF;
462 c.g = ((p->packed32 >> 10) & 0x3FF) / (float)0x3FF;
463 c.b = ((p->packed32 >> 20) & 0x3FF) / (float)0x3FF;
464 c.a = ((p->packed32 >> 30) & 0x3) / (float)0x3;
465}
466
467static void getPixelRG11B10F(const ImageData::Pixel *p, Colorf &c) {
468 // LSB->MSB: [r, g, b]
469 c.r = float11to32((float11)((p->packed32 >> 0) & 0x7FF));
470 c.g = float11to32((float11)((p->packed32 >> 11) & 0x7FF));
471 c.b = float10to32((float10)((p->packed32 >> 22) & 0x3FF));
472 c.a = 1.0f;
473}
474
475void ImageData::setPixel(int x, int y, const Colorf &c) {
476 if (!inside(x, y)) throw eve::Exception("Attempt to set out-of-range pixel!");
477
478 size_t pixelsize = getPixelSize();
479 Pixel *p = (Pixel *)(data + ((y * width + x) * pixelsize));
480
481 if (pixelSetFunction == nullptr) throw eve::Exception("Unhandled pixel format %s in ImageData::setPixel", format.c_str());
482
483 pixelSetFunction(c, p);
484}
485
486void ImageData::getPixel(int x, int y, Colorf &c) const {
487 if (!inside(x, y)) throw eve::Exception("Attempt to get out-of-range pixel!");
488
489 size_t pixelsize = getPixelSize();
490 const Pixel *p = (const Pixel *)(data + ((y * width + x) * pixelsize));
491
492 if (pixelGetFunction == nullptr) throw eve::Exception("Unhandled pixel format %s in ImageData::getPixel", format.c_str());
493
494 pixelGetFunction(p, c);
495}
496
497Colorf ImageData::getPixel(int x, int y) const {
498 Colorf c;
499 getPixel(x, y, c);
500 return c;
501}
502
503namespace {
504
505enum class RotateFilter {
506 Nearest,
507 Linear,
508 RotSprite,
509};
510
511RotateFilter parseRotateFilter(const std::string &name) {
512 if (name == "nearest" || name == "Nearest" || name == "NEAREST")
513 return RotateFilter::Nearest;
514 if (name == "linear" || name == "Linear" || name == "LINEAR" ||
515 name == "bilinear" || name == "Bilinear" || name == "BILINEAR")
516 return RotateFilter::Linear;
517 if (name == "rotsprite" || name == "RotSprite" || name == "ROTSPRITE" ||
518 name == "rotSprite")
519 return RotateFilter::RotSprite;
520 throw eve::Exception(
521 "Unsupported ImageData rotate filter '%s' (use \"nearest\", \"linear\", or \"rotsprite\")",
522 name.c_str());
523}
524
525void rotatePoint(float dx, float dy, float c, float s, float &ox, float &oy) {
526 // Same convention as Math::rotate2X/Y.
527 ox = dx * c - dy * s;
528 oy = dx * s + dy * c;
529}
530
531void inverseRotatePoint(float dx, float dy, float c, float s, float &ox, float &oy) {
532 // Inverse of Math::rotate2*: apply -angle (cos'=c, sin'=-s).
533 ox = dx * c + dy * s;
534 oy = -dx * s + dy * c;
535}
536
537bool colorExact(const Colorf &a, const Colorf &b) {
538 return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
539}
540
541// RotSprite's modified Scale2x treats "similar" pixels as matches (not only identical).
542bool colorSimilar(const Colorf &a, const Colorf &b) {
543 const float dr = std::fabs(a.r - b.r);
544 const float dg = std::fabs(a.g - b.g);
545 const float db = std::fabs(a.b - b.b);
546 const float da = std::fabs(a.a - b.a);
547 return (dr + dg + db + da) <= (40.f / 255.f);
548}
549
550float colorDistSq(const Colorf &a, const Colorf &b) {
551 const float dr = a.r - b.r;
552 const float dg = a.g - b.g;
553 const float db = a.b - b.b;
554 const float da = a.a - b.a;
555 return dr * dr + dg * dg + db * db + da * da;
556}
557
558Colorf sampleNearest(const ImageData *src, float sx, float sy) {
559 const int rx = (int)std::floor(sx + 0.5f);
560 const int ry = (int)std::floor(sy + 0.5f);
561 if (!src->inside(rx, ry))
562 return Colorf{0.f, 0.f, 0.f, 0.f};
563 return src->getPixel(rx, ry);
564}
565
566Colorf sampleBilinear(const ImageData *src, float sx, float sy) {
567 const int x0 = (int)std::floor(sx);
568 const int y0 = (int)std::floor(sy);
569 const int x1 = x0 + 1;
570 const int y1 = y0 + 1;
571 const float fx = sx - (float)x0;
572 const float fy = sy - (float)y0;
573 const float ifx = 1.f - fx;
574 const float ify = 1.f - fy;
575
576 auto sampleOrZero = [&](int x, int y) -> Colorf {
577 if (!src->inside(x, y))
578 return Colorf{0.f, 0.f, 0.f, 0.f};
579 return src->getPixel(x, y);
580 };
581
582 const Colorf c00 = sampleOrZero(x0, y0);
583 const Colorf c10 = sampleOrZero(x1, y0);
584 const Colorf c01 = sampleOrZero(x0, y1);
585 const Colorf c11 = sampleOrZero(x1, y1);
586
587 const float w00 = ifx * ify;
588 const float w10 = fx * ify;
589 const float w01 = ifx * fy;
590 const float w11 = fx * fy;
591
592 Colorf out;
593 out.r = w00 * c00.r + w10 * c10.r + w01 * c01.r + w11 * c11.r;
594 out.g = w00 * c00.g + w10 * c10.g + w01 * c01.g + w11 * c11.g;
595 out.b = w00 * c00.b + w10 * c10.b + w01 * c01.b + w11 * c11.b;
596 out.a = w00 * c00.a + w10 * c10.a + w01 * c01.a + w11 * c11.a;
597 return out;
598}
599
600void computeRotatedSize(int srcW, int srcH, float c, float s, bool expand, int &dstW, int &dstH,
601 float &dstCx, float &dstCy) {
602 const float srcCx = srcW * 0.5f;
603 const float srcCy = srcH * 0.5f;
604 if (!expand) {
605 dstW = srcW;
606 dstH = srcH;
607 dstCx = srcCx;
608 dstCy = srcCy;
609 return;
610 }
611 const float corners[4][2] = {
612 {-srcCx, -srcCy},
613 {srcW - srcCx, -srcCy},
614 {-srcCx, srcH - srcCy},
615 {srcW - srcCx, srcH - srcCy},
616 };
617 float minX = 0.f, maxX = 0.f, minY = 0.f, maxY = 0.f;
618 for (int i = 0; i < 4; ++i) {
619 float ox, oy;
620 rotatePoint(corners[i][0], corners[i][1], c, s, ox, oy);
621 if (i == 0) {
622 minX = maxX = ox;
623 minY = maxY = oy;
624 } else {
625 minX = std::min(minX, ox);
626 maxX = std::max(maxX, ox);
627 minY = std::min(minY, oy);
628 maxY = std::max(maxY, oy);
629 }
630 }
631 dstW = std::max(1, (int)std::ceil(maxX - minX));
632 dstH = std::max(1, (int)std::ceil(maxY - minY));
633 dstCx = dstW * 0.5f;
634 dstCy = dstH * 0.5f;
635}
636
637struct PixelBuffer {
638 int w = 0;
639 int h = 0;
640 std::vector<Colorf> px;
641
642 Colorf get(int x, int y) const { return px[(size_t)y * (size_t)w + (size_t)x]; }
643 void set(int x, int y, const Colorf &c) { px[(size_t)y * (size_t)w + (size_t)x] = c; }
644 bool inside(int x, int y) const { return x >= 0 && y >= 0 && x < w && y < h; }
645};
646
647PixelBuffer bufferFromImage(const ImageData *img) {
648 PixelBuffer b;
649 b.w = img->getWidth();
650 b.h = img->getHeight();
651 b.px.resize((size_t)b.w * (size_t)b.h);
652 for (int y = 0; y < b.h; ++y)
653 for (int x = 0; x < b.w; ++x)
654 b.set(x, y, img->getPixel(x, y));
655 return b;
656}
657
658ImageData *imageFromBuffer(const PixelBuffer &b, const std::string &format) {
659 ImageData *dst = new ImageData(b.w, b.h, format);
660 for (int y = 0; y < b.h; ++y)
661 for (int x = 0; x < b.w; ++x) {
662 const Colorf &c = b.get(x, y);
663 if (c.a != 0.f || c.r != 0.f || c.g != 0.f || c.b != 0.f)
664 dst->setPixel(x, y, c);
665 }
666 return dst;
667}
668
669void scale2xBlock(Colorf center, Colorf up, Colorf left, Colorf down, Colorf right, Colorf &a,
670 Colorf &b, Colorf &c, Colorf &d) {
671 // Classic Scale2x / EPX rules, with RotSprite-style similarity.
672 a = (colorSimilar(left, up) && !colorSimilar(left, down) && !colorSimilar(up, right)) ? up
673 : center;
674 b = (colorSimilar(up, right) && !colorSimilar(up, left) && !colorSimilar(right, down)) ? right
675 : center;
676 c = (colorSimilar(down, left) && !colorSimilar(down, right) && !colorSimilar(left, up)) ? left
677 : center;
678 d = (colorSimilar(right, down) && !colorSimilar(right, up) && !colorSimilar(down, left))
679 ? down
680 : center;
681}
682
683PixelBuffer scale2x(const PixelBuffer &src) {
684 PixelBuffer dst;
685 dst.w = src.w * 2;
686 dst.h = src.h * 2;
687 dst.px.assign((size_t)dst.w * (size_t)dst.h, Colorf{0.f, 0.f, 0.f, 0.f});
688
689 auto at = [&](int x, int y) -> Colorf {
690 x = std::clamp(x, 0, src.w - 1);
691 y = std::clamp(y, 0, src.h - 1);
692 return src.get(x, y);
693 };
694
695 for (int y = 0; y < src.h; ++y) {
696 for (int x = 0; x < src.w; ++x) {
697 const Colorf E = at(x, y);
698 const Colorf B = at(x, y - 1);
699 const Colorf D = at(x - 1, y);
700 const Colorf H = at(x, y + 1);
701 const Colorf F = at(x + 1, y);
702 Colorf p00, p10, p01, p11;
703 scale2xBlock(E, B, D, H, F, p00, p10, p01, p11);
704 const int dx = x * 2;
705 const int dy = y * 2;
706 dst.set(dx, dy, p00);
707 dst.set(dx + 1, dy, p10);
708 dst.set(dx, dy + 1, p01);
709 dst.set(dx + 1, dy + 1, p11);
710 }
711 }
712 return dst;
713}
714
715PixelBuffer upscaleRotSprite(const PixelBuffer &src) {
716 // 2× → 4× → 8×
717 PixelBuffer s2 = scale2x(src);
718 PixelBuffer s4 = scale2x(s2);
719 return scale2x(s4);
720}
721
722void findBestOffset(const PixelBuffer &hi, float radians, int scale, int &bestOx, int &bestOy) {
723 const float c = std::cos(radians);
724 const float s = std::sin(radians);
725 const float srcCx = hi.w * 0.5f;
726 const float srcCy = hi.h * 0.5f;
727
728 int outW = 0, outH = 0;
729 float outCx = 0.f, outCy = 0.f;
730 // Score on the expanded low-res AABB mapped into hi-res by *scale.
731 computeRotatedSize(hi.w / scale, hi.h / scale, c, s, true, outW, outH, outCx, outCy);
732
733 bestOx = 0;
734 bestOy = 0;
735 double bestScore = std::numeric_limits<double>::infinity();
736
737 for (int oy = 0; oy < scale; ++oy) {
738 for (int ox = 0; ox < scale; ++ox) {
739 double score = 0.0;
740 for (int yt = 0; yt < outH; ++yt) {
741 const float dy = ((float)yt + 0.5f) - outCy;
742 for (int xt = 0; xt < outW; ++xt) {
743 const float dx = ((float)xt + 0.5f) - outCx;
744 float relX, relY;
745 inverseRotatePoint(dx * (float)scale, dy * (float)scale, c, s, relX, relY);
746 const int sx = (int)std::floor(relX + srcCx + (float)ox);
747 const int sy = (int)std::floor(relY + srcCy + (float)oy);
748 if (!hi.inside(sx, sy))
749 continue;
750 const Colorf p = hi.get(sx, sy);
751 if (hi.inside(sx + 1, sy))
752 score += (double)colorDistSq(p, hi.get(sx + 1, sy));
753 if (hi.inside(sx, sy + 1))
754 score += (double)colorDistSq(p, hi.get(sx, sy + 1));
755 }
756 }
757 if (score < bestScore) {
758 bestScore = score;
759 bestOx = ox;
760 bestOy = oy;
761 }
762 }
763 }
764}
765
766PixelBuffer rotateExact90(const PixelBuffer &src, int turnsClockwise) {
767 turnsClockwise = ((turnsClockwise % 4) + 4) % 4;
768 if (turnsClockwise == 0)
769 return src;
770 PixelBuffer cur = src;
771 for (int t = 0; t < turnsClockwise; ++t) {
772 PixelBuffer next;
773 next.w = cur.h;
774 next.h = cur.w;
775 next.px.resize((size_t)next.w * (size_t)next.h);
776 for (int y = 0; y < cur.h; ++y)
777 for (int x = 0; x < cur.w; ++x)
778 next.set(cur.h - 1 - y, x, cur.get(x, y));
779 cur = std::move(next);
780 }
781 return cur;
782}
783
784void restoreSinglePixelDetails(PixelBuffer &dst, const PixelBuffer &orig, float radians) {
785 const float c = std::cos(radians);
786 const float s = std::sin(radians);
787 const float srcCx = orig.w * 0.5f;
788 const float srcCy = orig.h * 0.5f;
789 const float dstCx = dst.w * 0.5f;
790 const float dstCy = dst.h * 0.5f;
791
792 PixelBuffer copy = dst;
793 for (int y = 0; y < dst.h; ++y) {
794 for (int x = 0; x < dst.w; ++x) {
795 const Colorf p = copy.get(x, y);
796 int same = 0;
797 const int nxs[4] = {x - 1, x + 1, x, x};
798 const int nys[4] = {y, y, y - 1, y + 1};
799 for (int i = 0; i < 4; ++i) {
800 if (!copy.inside(nxs[i], nys[i]))
801 continue;
802 if (colorExact(copy.get(nxs[i], nys[i]), p))
803 ++same;
804 }
805 if (same < 3)
806 continue;
807
808 const float dx = ((float)x + 0.5f) - dstCx;
809 const float dy = ((float)y + 0.5f) - dstCy;
810 float relX, relY;
811 inverseRotatePoint(dx, dy, c, s, relX, relY);
812 const int sx = (int)std::floor(relX + srcCx);
813 const int sy = (int)std::floor(relY + srcCy);
814 if (!orig.inside(sx, sy))
815 continue;
816 const Colorf srcPix = orig.get(sx, sy);
817 if (!colorExact(srcPix, p))
818 dst.set(x, y, srcPix);
819 }
820 }
821}
822
823ImageData *rotateRotSprite(const ImageData *src, float radians, bool expand) {
824 PixelBuffer orig = bufferFromImage(src);
825
826 // Normalize angle to [0, 2π).
827 const float twoPi = 6.28318530717958647692f;
828 float ang = std::fmod(radians, twoPi);
829 if (ang < 0.f)
830 ang += twoPi;
831
832 // Exact quarter turns: no upscale needed.
833 const float halfPi = 1.57079632679489661923f;
834 const float pi = 3.14159265358979323846f;
835 const float eps = 1e-4f;
836 auto nearMul = [&](float target) {
837 return std::fabs(ang - target) < eps || std::fabs(ang - target - twoPi) < eps;
838 };
839 if (nearMul(0.f)) {
840 if (!expand)
841 return src->clone();
842 return imageFromBuffer(orig, src->getFormat());
843 }
844 if (nearMul(halfPi) || nearMul(pi) || nearMul(halfPi * 3.f)) {
845 int turns = 1;
846 if (nearMul(pi))
847 turns = 2;
848 else if (nearMul(halfPi * 3.f))
849 turns = 3;
850 PixelBuffer rotated = rotateExact90(orig, turns);
851 if (!expand) {
852 // Center the exact rotation into the original canvas.
853 PixelBuffer canvas;
854 canvas.w = orig.w;
855 canvas.h = orig.h;
856 canvas.px.assign((size_t)canvas.w * (size_t)canvas.h, Colorf{0.f, 0.f, 0.f, 0.f});
857 const int ox = (canvas.w - rotated.w) / 2;
858 const int oy = (canvas.h - rotated.h) / 2;
859 for (int y = 0; y < rotated.h; ++y)
860 for (int x = 0; x < rotated.w; ++x) {
861 const int dx = x + ox;
862 const int dy = y + oy;
863 if (canvas.inside(dx, dy))
864 canvas.set(dx, dy, rotated.get(x, y));
865 }
866 return imageFromBuffer(canvas, src->getFormat());
867 }
868 return imageFromBuffer(rotated, src->getFormat());
869 }
870
871 constexpr int kScale = 8;
872 PixelBuffer hi = upscaleRotSprite(orig);
873
874 int bestOx = 0, bestOy = 0;
875 findBestOffset(hi, ang, kScale, bestOx, bestOy);
876
877 const float c = std::cos(ang);
878 const float s = std::sin(ang);
879 int dstW = 0, dstH = 0;
880 float dstCx = 0.f, dstCy = 0.f;
881 computeRotatedSize(orig.w, orig.h, c, s, expand, dstW, dstH, dstCx, dstCy);
882
883 const float hiCx = hi.w * 0.5f;
884 const float hiCy = hi.h * 0.5f;
885
886 PixelBuffer out;
887 out.w = dstW;
888 out.h = dstH;
889 out.px.assign((size_t)out.w * (size_t)out.h, Colorf{0.f, 0.f, 0.f, 0.f});
890
891 for (int yt = 0; yt < dstH; ++yt) {
892 const float dy = ((float)yt + 0.5f) - dstCy;
893 for (int xt = 0; xt < dstW; ++xt) {
894 const float dx = ((float)xt + 0.5f) - dstCx;
895 float relX, relY;
896 inverseRotatePoint(dx * (float)kScale, dy * (float)kScale, c, s, relX, relY);
897 const int sx = (int)std::floor(relX + hiCx + (float)bestOx);
898 const int sy = (int)std::floor(relY + hiCy + (float)bestOy);
899 if (hi.inside(sx, sy))
900 out.set(xt, yt, hi.get(sx, sy));
901 }
902 }
903
904 restoreSinglePixelDetails(out, orig, ang);
905 return imageFromBuffer(out, src->getFormat());
906}
907
908} // namespace
909
910ImageData *ImageData::rotate(float radians, std::string filter, bool expand) const {
911 if (pixelGetFunction == nullptr || pixelSetFunction == nullptr)
912 throw eve::Exception("Unhandled pixel format %s in ImageData::rotate", format.c_str());
913
914 const RotateFilter mode = parseRotateFilter(filter);
915 if (mode == RotateFilter::RotSprite)
916 return rotateRotSprite(this, radians, expand);
917
918 const float srcW = (float)width;
919 const float srcH = (float)height;
920 const float srcCx = srcW * 0.5f;
921 const float srcCy = srcH * 0.5f;
922
923 const float c = std::cos(radians);
924 const float s = std::sin(radians);
925
926 int dstW = width;
927 int dstH = height;
928 float dstCx = srcCx;
929 float dstCy = srcCy;
930 computeRotatedSize(width, height, c, s, expand, dstW, dstH, dstCx, dstCy);
931
932 ImageData *dst = nullptr;
933 try {
934 dst = new ImageData(dstW, dstH, format);
935 } catch (std::bad_alloc &) {
936 throw eve::Exception("Out of memory");
937 }
938
939 // Near-identity: copy when angle is ~0 and canvas unchanged.
940 if (!expand && std::fabs(radians) < 1e-7f) {
941 std::memcpy(dst->getData(), data, getSize());
942 return dst;
943 }
944
945 for (int yt = 0; yt < dstH; ++yt) {
946 const float dy = ((float)yt + 0.5f) - dstCy;
947 for (int xt = 0; xt < dstW; ++xt) {
948 const float dx = ((float)xt + 0.5f) - dstCx;
949 float relX, relY;
950 inverseRotatePoint(dx, dy, c, s, relX, relY);
951 const float sx = relX + srcCx - 0.5f;
952 const float sy = relY + srcCy - 0.5f;
953
955 if (mode == RotateFilter::Nearest)
956 color = sampleNearest(this, sx, sy);
957 else
958 color = sampleBilinear(this, sx, sy);
959
960 if (color.a != 0.f || color.r != 0.f || color.g != 0.f || color.b != 0.f)
961 dst->setPixel(xt, yt, color);
962 }
963 }
964
965 return dst;
966}
967
968union Row {
969 uint8_t *u8;
970 uint16_t *u16;
971 float16 *f16;
972 float *f32;
973};
974
975static void pasteRGBA8toRGBA16(Row src, Row dst, int w) {
976 for (int i = 0; i < w * 4; i++) dst.u16[i] = (uint16_t)src.u8[i] << 8u;
977}
978
979static void pasteRGBA8toRGBA16F(Row src, Row dst, int w) {
980 for (int i = 0; i < w * 4; i++) dst.f16[i] = float32to16(src.u8[i] / 255.0f);
981}
982
983static void pasteRGBA8toRGBA32F(Row src, Row dst, int w) {
984 for (int i = 0; i < w * 4; i++) dst.f32[i] = src.u8[i] / 255.0f;
985}
986
987static void pasteRGBA16toRGBA8(Row src, Row dst, int w) {
988 for (int i = 0; i < w * 4; i++) dst.u8[i] = src.u16[i] >> 8u;
989}
990
991static void pasteRGBA16toRGBA16F(Row src, Row dst, int w) {
992 for (int i = 0; i < w * 4; i++) dst.f16[i] = float32to16(src.u16[i] / 65535.0f);
993}
994
995static void pasteRGBA16toRGBA32F(Row src, Row dst, int w) {
996 for (int i = 0; i < w * 4; i++) dst.f32[i] = src.u16[i] / 65535.0f;
997}
998
999static void pasteRGBA16FtoRGBA8(Row src, Row dst, int w) {
1000 for (int i = 0; i < w * 4; i++) dst.u8[i] = (uint8_t)(clamp01(float16to32(src.f16[i])) * 255.0f + 0.5f);
1001}
1002
1003static void pasteRGBA16FtoRGBA16(Row src, Row dst, int w) {
1004 for (int i = 0; i < w * 4; i++) dst.u16[i] = (uint16_t)(clamp01(float16to32(src.f16[i])) * 65535.0f + 0.5f);
1005}
1006
1007static void pasteRGBA16FtoRGBA32F(Row src, Row dst, int w) {
1008 for (int i = 0; i < w * 4; i++) dst.f32[i] = float16to32(src.f16[i]);
1009}
1010
1011static void pasteRGBA32FtoRGBA8(Row src, Row dst, int w) {
1012 for (int i = 0; i < w * 4; i++) dst.u8[i] = (uint8_t)(clamp01(src.f32[i]) * 255.0f + 0.5f);
1013}
1014
1015static void pasteRGBA32FtoRGBA16(Row src, Row dst, int w) {
1016 for (int i = 0; i < w * 4; i++) dst.u16[i] = (uint16_t)(clamp01(src.f32[i]) * 65535.0f + 0.5f);
1017}
1018
1019static void pasteRGBA32FtoRGBA16F(Row src, Row dst, int w) {
1020 for (int i = 0; i < w * 4; i++) dst.f16[i] = float32to16(src.f32[i]);
1021}
1022
1023void ImageData::paste(ImageData *src, int dx, int dy, int sx, int sy, int sw, int sh) {
1024 EV_PARAM_CHECK(src != nullptr, "source ImageData must not be null");
1025
1026 std::string dstformat = getFormat();
1027 std::string srcformat = src->getFormat();
1028
1029 int srcW = src->getWidth();
1030 int srcH = src->getHeight();
1031 int dstW = getWidth();
1032 int dstH = getHeight();
1033
1034 size_t srcpixelsize = src->getPixelSize();
1035 size_t dstpixelsize = getPixelSize();
1036
1037 // Check bounds; if the data ends up completely out of bounds, get out early.
1038 if (sx >= srcW || sx + sw < 0 || sy >= srcH || sy + sh < 0 || dx >= dstW || dx + sw < 0 || dy >= dstH ||
1039 dy + sh < 0)
1040 return;
1041
1042 // Normalize values to the inside of both images.
1043 if (dx < 0) {
1044 sw += dx;
1045 sx -= dx;
1046 dx = 0;
1047 }
1048 if (dy < 0) {
1049 sh += dy;
1050 sy -= dy;
1051 dy = 0;
1052 }
1053 if (sx < 0) {
1054 sw += sx;
1055 dx -= sx;
1056 sx = 0;
1057 }
1058 if (sy < 0) {
1059 sh += sy;
1060 dy -= sy;
1061 sy = 0;
1062 }
1063
1064 if (dx + sw > dstW) sw = dstW - dx;
1065
1066 if (dy + sh > dstH) sh = dstH - dy;
1067
1068 if (sx + sw > srcW) sw = srcW - sx;
1069
1070 if (sy + sh > srcH) sh = srcH - sy;
1071
1072 uint8_t *s = (uint8_t *)src->getData();
1073 uint8_t *d = (uint8_t *)getData();
1074
1075 auto getfunction = src->pixelGetFunction;
1076 auto setfunction = pixelSetFunction;
1077
1078 // If the dimensions match up, copy the entire memory stream in one go
1079 if (srcformat == dstformat && (sw == dstW && dstW == srcW && sh == dstH && dstH == srcH)) {
1080 memcpy(d, s, srcpixelsize * sw * sh);
1081 } else if (sw > 0) {
1082 // Otherwise, copy each row individually.
1083 for (int i = 0; i < sh; i++) {
1084 Row rowsrc = {s + (sx + (i + sy) * srcW) * srcpixelsize};
1085 Row rowdst = {d + (dx + (i + dy) * dstW) * dstpixelsize};
1086
1087 if (srcformat == dstformat)
1088 memcpy(rowdst.u8, rowsrc.u8, srcpixelsize * sw);
1089
1090 else if (srcformat == "RGBA8" && dstformat == "RGBA16")
1091 pasteRGBA8toRGBA16(rowsrc, rowdst, sw);
1092 else if (srcformat == "RGBA8" && dstformat == "RGBA16F")
1093 pasteRGBA8toRGBA16F(rowsrc, rowdst, sw);
1094 else if (srcformat == "RGBA8" && dstformat == "RGBA32F")
1095 pasteRGBA8toRGBA32F(rowsrc, rowdst, sw);
1096
1097 else if (srcformat == "RGBA16" && dstformat == "RGBA8")
1098 pasteRGBA16toRGBA8(rowsrc, rowdst, sw);
1099 else if (srcformat == "RGBA16" && dstformat == "RGBA16F")
1100 pasteRGBA16toRGBA16F(rowsrc, rowdst, sw);
1101 else if (srcformat == "RGBA16" && dstformat == "RGBA32F")
1102 pasteRGBA16toRGBA32F(rowsrc, rowdst, sw);
1103
1104 else if (srcformat == "RGBA16F" && dstformat == "RGBA8")
1105 pasteRGBA16FtoRGBA8(rowsrc, rowdst, sw);
1106 else if (srcformat == "RGBA16F" && dstformat == "RGBA16")
1107 pasteRGBA16FtoRGBA16(rowsrc, rowdst, sw);
1108 else if (srcformat == "RGBA16F" && dstformat == "RGBA32F")
1109 pasteRGBA16FtoRGBA32F(rowsrc, rowdst, sw);
1110
1111 else if (srcformat == "RGBA32F" && dstformat == "RGBA8")
1112 pasteRGBA32FtoRGBA8(rowsrc, rowdst, sw);
1113 else if (srcformat == "RGBA32F" && dstformat == "RGBA16")
1114 pasteRGBA32FtoRGBA16(rowsrc, rowdst, sw);
1115 else if (srcformat == "RGBA32F" && dstformat == "RGBA16F")
1116 pasteRGBA32FtoRGBA16F(rowsrc, rowdst, sw);
1117
1118 else {
1119 // Slow path: convert src -> Colorf -> dst.
1120 Colorf c;
1121 for (int x = 0; x < sw; x++) {
1122 auto srcp = (const Pixel *)(rowsrc.u8 + x * srcpixelsize);
1123 auto dstp = (Pixel *)(rowdst.u8 + x * dstpixelsize);
1124 getfunction(srcp, c);
1125 setfunction(c, dstp);
1126 }
1127 }
1128 }
1129 }
1130}
1131
1132
1133size_t ImageData::getPixelSize() const { return getPixelFormatSize(getPixelFormatFromName(format)); }
1134
1135bool ImageData::validPixelFormat(std::string format) {
1136 if (format == "R8") return true;
1137 if (format == "RG8") return true;
1138 if (format == "RGBA8") return true;
1139 if (format == "R16") return true;
1140 if (format == "RG16") return true;
1141 if (format == "RGBA16") return true;
1142 if (format == "R16F") return true;
1143 if (format == "RG16F") return true;
1144 if (format == "RGBA16F") return true;
1145 if (format == "R32F") return true;
1146 if (format == "RG32F") return true;
1147 if (format == "RGBA32F") return true;
1148 if (format == "RGBA4") return true;
1149 if (format == "RGB5A1") return true;
1150 if (format == "RGB565") return true;
1151 if (format == "RGB10A2") return true;
1152 if (format == "RG11B10F") return true;
1153 return false;
1154}
1155
1156bool ImageData::canPaste(std::string src, std::string dst) {
1157 if (src == dst) return true;
1158
1159 if (!(src == "RGBA8" || src == "RGBA16" || src == "RGBA16F" || src == "RGBA32F")) return false;
1160
1161 if (!(dst == "RGBA8" || dst == "RGBA16" || dst == "RGBA16F" || dst == "RGBA32F")) return false;
1162
1163 return true;
1164}
1165
1166ImageData::PixelSetFunction ImageData::getPixelSetFunction(std::string format) {
1167 if (format == "R8") return setPixelR8;
1168 if (format == "RG8") return setPixelRG8;
1169 if (format == "RGBA8") return setPixelRGBA8;
1170 if (format == "R16") return setPixelR16;
1171 if (format == "RG16") return setPixelRG16;
1172 if (format == "RGBA16") return setPixelRGBA16;
1173 if (format == "R16F") return setPixelR16F;
1174 if (format == "RG16F") return setPixelRG16F;
1175 if (format == "RGBA16F") return setPixelRGBA16F;
1176 if (format == "R32F") return setPixelR32F;
1177 if (format == "RG32F") return setPixelRG32F;
1178 if (format == "RGBA32F") return setPixelRGBA32F;
1179 if (format == "RGBA4") return setPixelRGBA4;
1180 if (format == "RGB5A1") return setPixelRGB5A1;
1181 if (format == "RGB565") return setPixelRGB565;
1182 if (format == "RGB10A2") return setPixelRGB10A2;
1183 if (format == "RG11B10F") return setPixelRG11B10F;
1184 return nullptr;
1185}
1186
1187ImageData::PixelGetFunction ImageData::getPixelGetFunction(std::string format) {
1188 if (format == "R8") return getPixelR8;
1189 if (format == "RG8") return getPixelRG8;
1190 if (format == "RGBA8") return getPixelRGBA8;
1191 if (format == "R16") return getPixelR16;
1192 if (format == "RG16") return getPixelRG16;
1193 if (format == "RGBA16") return getPixelRGBA16;
1194 if (format == "R16F") return getPixelR16F;
1195 if (format == "RG16F") return getPixelRG16F;
1196 if (format == "RGBA16F") return getPixelRGBA16F;
1197 if (format == "R32F") return getPixelR32F;
1198 if (format == "RG32F") return getPixelRG32F;
1199 if (format == "RGBA32F") return getPixelRGBA32F;
1200 if (format == "RGBA4") return getPixelRGBA4;
1201 if (format == "RGB5A1") return getPixelRGB5A1;
1202 if (format == "RGB565") return getPixelRGB565;
1203 if (format == "RGB10A2") return getPixelRGB10A2;
1204 if (format == "RG11B10F") return getPixelRG11B10F;
1205 return nullptr;
1206}
1207
1208} // namespace image
1209} // namespace eve
EVEngine assertion entry point, backed by zeroerr.
#define EV_PARAM_CHECK(cond,...)
Validate a function parameter / public API precondition.
Definition Assert.h:30
int y
Definition Grass.cpp:135
float height
Definition Grass.cpp:235
int x
Definition Grass.cpp:135
int h
int w
std::vector< Colorf > px
uint32_t a
uint32_t b
uint32_t c
int width
glm::vec4 p[6]
Light2D::Data * data
const char * name
Definition RockMesh.cpp:21
std::string filter
int d
image::ImageData::Colorf color
std::string image
float scale
Definition TreeMesh.cpp:122
uint32_t s
Definition Weather.cpp:28
Resource is a game object that is managed by the ResourceManager. It can be loaded from a file or gen...
Definition Resource.h:35
Data buffer paired with a filename (used for type identification).
Definition FileData.h:12
size_t getSize() const
Gets the size of the Data in bytes.
Definition FileData.h:24
void * getData() const
Gets a pointer to the data. This pointer will obviously not be valid if the Data object is destroyed.
Definition FileData.h:23
Represents raw pixel data.
Definition ImageData.h:26
ImageData * clone() const
Definition ImageData.cpp:72
filesystem::FileData * encode(FormatHandler::EncodedFormat format, const char *filename, bool writefile) const
Encodes raw pixel data into a given format.
void setPixel(int x, int y, const Colorf &p)
Sets the pixel at location (x,y).
medialoader::Colorf Colorf
Definition ImageData.h:29
size_t getSize() const
PixelSetFunction getPixelSetFunction() const
Definition ImageData.h:132
static bool validPixelFormat(std::string format)
void * getData() const
PixelGetFunction getPixelGetFunction() const
Definition ImageData.h:133
size_t getPixelSize() const
ImageData(Data *data)
Definition ImageData.cpp:24
void adopt(eve::Resource &replacement) override
Replace this instance's pixels with replacement's (cache reload). The replacement is drained; its des...
Definition ImageData.cpp:61
medialoader::FormatHandler FormatHandler
Definition ImageData.h:30
std::string getFormat() const
Definition Build.cpp:11
uint16_t * u16