7#include "medialoader/image/pixelformat.h"
41 this->data = (
unsigned char *)
data;
51 create(width, height, format,
c.getData());
56 decodeHandler->freeRawPixels(data);
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);
75 }
catch (std::bad_alloc &) {
80void ImageData::create(
int width,
int height, std::string format,
void *data) {
81 size_t datasize =
width *
height * getPixelFormatSize(getPixelFormatFromName(format));
84 this->data =
new unsigned char[datasize];
85 }
catch (std::bad_alloc &) {
89 if (data) memcpy(this->data, data, datasize);
91 decodeHandler =
nullptr;
92 this->format = format;
98void ImageData::decode(Data *data) {
100 FormatHandler::DecodedImage decodedimage;
102 auto module = Image::create();
104 if (module ==
nullptr)
throw eve::Exception(
"eve.image must be loaded in order to decode an ImageData.");
107 if (handler->canDecode((
const char*)
data->getData(),
data->getSize())) {
113 if (decoder) decodedimage = decoder->decode((
const char*)
data->getData(),
data->getSize());
115 if (decodedimage.data ==
nullptr) {
116 auto filedata =
dynamic_cast<filesystem::FileData *
>(
data);
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());
122 throw eve::Exception(
"Could not decode data to ImageData: unsupported encoded format");
125 if (decodedimage.size != decodedimage.width * decodedimage.height * getPixelFormatSize(decodedimage.format)) {
126 decoder->freeRawPixels(decodedimage.data);
132 decodeHandler->freeRawPixels(this->data);
136 this->width = decodedimage.width;
137 this->height = decodedimage.height;
138 this->data = decodedimage.data;
140 this->format = [pf = decodedimage.format]() -> std::string {
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";
164 decodeHandler = decoder;
171 bool writefile)
const {
172 EV_PARAM_CHECK(filename !=
nullptr,
"output filename must not be null");
175 FormatHandler::EncodedImage encodedimage;
176 FormatHandler::DecodedImage rawimage;
178 rawimage.width =
width;
181 rawimage.data =
data;
182 rawimage.format = getPixelFormatFromName(format);
184 auto module = Image::create();
186 if (module ==
nullptr)
throw eve::Exception(
"eve.image must be loaded in order to encode an ImageData.");
189 if (handler->canEncode(getPixelFormatFromName(format), encodedFormat)) {
195 if (encoder !=
nullptr) {
196 encodedimage = encoder->encode(rawimage, encodedFormat);
199 if (encoder ==
nullptr || encodedimage.data ==
nullptr) {
200 throw eve::Exception(
"No suitable image encoder for %s format.", format.c_str());
208 encoder->freeRawPixels(encodedimage.data);
212 memcpy(filedata->
getData(), encodedimage.data, encodedimage.size);
213 encoder->freeRawPixels(encodedimage.data);
216 auto fs = eve::filesystem::Filesystem::create();
220 throw eve::Exception(
"eve.filesystem must be loaded in order to write an encoded ImageData to a file.");
234size_t ImageData::getSize()
const {
return size_t(getWidth() * getHeight()) * getPixelSize(); }
236void *ImageData::getData()
const {
return data; }
238bool ImageData::isSRGB()
const {
return false; }
240bool ImageData::inside(
int x,
int y)
const {
return x >= 0 &&
x < getWidth() &&
y >= 0 &&
y < getHeight(); }
242int ImageData::getWidth()
const {
return width; }
244int ImageData::getHeight()
const {
return height; }
246std::string ImageData::getFormat()
const {
return format; }
248static float clamp01(
float x) {
return std::min(std::max(
x, 0.0f), 1.0f); }
250static void setPixelR8(
const Colorf &
c, ImageData::Pixel *
p) {
p->rgba8[0] = (uint8_t)(clamp01(
c.r) * 255.0f + 0.5f); }
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);
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);
264static void setPixelR16(
const Colorf &
c, ImageData::Pixel *
p) {
265 p->rgba16[0] = (uint16_t)(clamp01(
c.r) * 65535.0f + 0.5f);
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);
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);
280static void setPixelR16F(
const Colorf &
c, ImageData::Pixel *
p) {
p->rgba16f[0] = float32to16(
c.r); }
282static void setPixelRG16F(
const Colorf &
c, ImageData::Pixel *
p) {
283 p->rgba16f[0] = float32to16(
c.r);
284 p->rgba16f[1] = float32to16(
c.g);
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);
294static void setPixelR32F(
const Colorf &
c, ImageData::Pixel *
p) {
p->rgba32f[0] =
c.r; }
296static void setPixelRG32F(
const Colorf &
c, ImageData::Pixel *
p) {
301static void setPixelRGBA32F(
const Colorf &
c, ImageData::Pixel *
p) {
308static void setPixelRGBA4(
const Colorf &
c, ImageData::Pixel *
p) {
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);
317static void setPixelRGB5A1(
const Colorf &
c, ImageData::Pixel *
p) {
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);
326static void setPixelRGB565(
const Colorf &
c, ImageData::Pixel *
p) {
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);
334static void setPixelRGB10A2(
const Colorf &
c, ImageData::Pixel *
p) {
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);
343static void setPixelRG11B10F(
const Colorf &
c, ImageData::Pixel *
p) {
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);
351static void getPixelR8(
const ImageData::Pixel *
p, Colorf &
c) {
352 c.r =
p->rgba8[0] / 255.0f;
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;
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;
372static void getPixelR16(
const ImageData::Pixel *
p, Colorf &
c) {
373 c.r =
p->rgba16[0] / 65535.0f;
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;
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;
393static void getPixelR16F(
const ImageData::Pixel *
p, Colorf &
c) {
394 c.r = float16to32(
p->rgba16f[0]);
400static void getPixelRG16F(
const ImageData::Pixel *
p, Colorf &
c) {
401 c.r = float16to32(
p->rgba16f[0]);
402 c.g = float16to32(
p->rgba16f[1]);
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]);
414static void getPixelR32F(
const ImageData::Pixel *
p, Colorf &
c) {
421static void getPixelRG32F(
const ImageData::Pixel *
p, Colorf &
c) {
428static void getPixelRGBA32F(
const ImageData::Pixel *
p, Colorf &
c) {
435static void getPixelRGBA4(
const ImageData::Pixel *
p, Colorf &
c) {
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;
443static void getPixelRGB5A1(
const ImageData::Pixel *
p, Colorf &
c) {
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;
451static void getPixelRGB565(
const ImageData::Pixel *
p, Colorf &
c) {
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;
459static void getPixelRGB10A2(
const ImageData::Pixel *
p, Colorf &
c) {
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;
467static void getPixelRG11B10F(
const ImageData::Pixel *
p, Colorf &
c) {
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));
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!");
478 size_t pixelsize = getPixelSize();
481 if (pixelSetFunction ==
nullptr)
throw eve::Exception(
"Unhandled pixel format %s in ImageData::setPixel", format.c_str());
483 pixelSetFunction(
c,
p);
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!");
489 size_t pixelsize = getPixelSize();
492 if (pixelGetFunction ==
nullptr)
throw eve::Exception(
"Unhandled pixel format %s in ImageData::getPixel", format.c_str());
494 pixelGetFunction(
p,
c);
505enum class RotateFilter {
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" ||
519 return RotateFilter::RotSprite;
521 "Unsupported ImageData rotate filter '%s' (use \"nearest\", \"linear\", or \"rotsprite\")",
525void rotatePoint(
float dx,
float dy,
float c,
float s,
float &ox,
float &oy) {
527 ox = dx *
c - dy *
s;
528 oy = dx *
s + dy *
c;
531void inverseRotatePoint(
float dx,
float dy,
float c,
float s,
float &ox,
float &oy) {
533 ox = dx *
c + dy *
s;
534 oy = -dx *
s + dy *
c;
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;
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);
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;
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);
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;
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);
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);
587 const float w00 = ifx * ify;
588 const float w10 = fx * ify;
589 const float w01 = ifx * fy;
590 const float w11 = fx * fy;
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;
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;
611 const float corners[4][2] = {
613 {srcW - srcCx, -srcCy},
614 {-srcCx, srcH - srcCy},
615 {srcW - srcCx, srcH - srcCy},
617 float minX = 0.f, maxX = 0.f, minY = 0.f, maxY = 0.f;
618 for (
int i = 0; i < 4; ++i) {
620 rotatePoint(corners[i][0], corners[i][1],
c,
s, ox, oy);
625 minX = std::min(minX, ox);
626 maxX = std::max(maxX, ox);
627 minY = std::min(minY, oy);
628 maxY = std::max(maxY, oy);
631 dstW = std::max(1, (
int)std::ceil(maxX - minX));
632 dstH = std::max(1, (
int)std::ceil(maxY - minY));
640 std::vector<Colorf>
px;
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; }
647PixelBuffer bufferFromImage(
const ImageData *img) {
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));
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);
669void scale2xBlock(Colorf center, Colorf up, Colorf left, Colorf down, Colorf right, Colorf &
a,
670 Colorf &
b, Colorf &
c, Colorf &
d) {
672 a = (colorSimilar(left, up) && !colorSimilar(left, down) && !colorSimilar(up, right)) ? up
674 b = (colorSimilar(up, right) && !colorSimilar(up, left) && !colorSimilar(right, down)) ? right
676 c = (colorSimilar(down, left) && !colorSimilar(down, right) && !colorSimilar(left, up)) ? left
678 d = (colorSimilar(right, down) && !colorSimilar(right, up) && !colorSimilar(down, left))
683PixelBuffer scale2x(
const PixelBuffer &src) {
687 dst.px.assign((
size_t)dst.w * (
size_t)dst.h, Colorf{0.f, 0.f, 0.f, 0.f});
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);
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);
715PixelBuffer upscaleRotSprite(
const PixelBuffer &src) {
717 PixelBuffer s2 = scale2x(src);
718 PixelBuffer s4 = scale2x(s2);
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;
728 int outW = 0, outH = 0;
729 float outCx = 0.f, outCy = 0.f;
731 computeRotatedSize(hi.w /
scale, hi.h /
scale,
c,
s,
true, outW, outH, outCx, outCy);
735 double bestScore = std::numeric_limits<double>::infinity();
737 for (
int oy = 0; oy <
scale; ++oy) {
738 for (
int ox = 0; ox <
scale; ++ox) {
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;
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))
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));
757 if (score < bestScore) {
766PixelBuffer rotateExact90(
const PixelBuffer &src,
int turnsClockwise) {
767 turnsClockwise = ((turnsClockwise % 4) + 4) % 4;
768 if (turnsClockwise == 0)
770 PixelBuffer cur = src;
771 for (
int t = 0; t < turnsClockwise; ++t) {
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);
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;
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);
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]))
802 if (colorExact(copy.get(nxs[i], nys[i]),
p))
808 const float dx = ((float)
x + 0.5f) - dstCx;
809 const float dy = ((float)
y + 0.5f) - dstCy;
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))
816 const Colorf srcPix = orig.get(sx, sy);
817 if (!colorExact(srcPix,
p))
818 dst.set(
x,
y, srcPix);
823ImageData *rotateRotSprite(
const ImageData *src,
float radians,
bool expand) {
824 PixelBuffer orig = bufferFromImage(src);
827 const float twoPi = 6.28318530717958647692f;
828 float ang = std::fmod(radians, twoPi);
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;
842 return imageFromBuffer(orig, src->getFormat());
844 if (nearMul(halfPi) || nearMul(pi) || nearMul(halfPi * 3.f)) {
848 else if (nearMul(halfPi * 3.f))
850 PixelBuffer rotated = rotateExact90(orig, turns);
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));
866 return imageFromBuffer(canvas, src->getFormat());
868 return imageFromBuffer(rotated, src->getFormat());
871 constexpr int kScale = 8;
872 PixelBuffer hi = upscaleRotSprite(orig);
874 int bestOx = 0, bestOy = 0;
875 findBestOffset(hi, ang, kScale, bestOx, bestOy);
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);
883 const float hiCx = hi.w * 0.5f;
884 const float hiCy = hi.h * 0.5f;
889 out.px.assign((
size_t)out.w * (
size_t)out.h, Colorf{0.f, 0.f, 0.f, 0.f});
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;
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));
904 restoreSinglePixelDetails(out, orig, ang);
905 return imageFromBuffer(out, src->getFormat());
911 if (pixelGetFunction ==
nullptr || pixelSetFunction ==
nullptr)
912 throw eve::Exception(
"Unhandled pixel format %s in ImageData::rotate", format.c_str());
914 const RotateFilter mode = parseRotateFilter(
filter);
915 if (mode == RotateFilter::RotSprite)
916 return rotateRotSprite(
this, radians, expand);
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;
923 const float c = std::cos(radians);
924 const float s = std::sin(radians);
930 computeRotatedSize(
width,
height,
c,
s, expand, dstW, dstH, dstCx, dstCy);
935 }
catch (std::bad_alloc &) {
940 if (!expand && std::fabs(radians) < 1e-7f) {
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;
950 inverseRotatePoint(dx, dy,
c,
s, relX, relY);
951 const float sx = relX + srcCx - 0.5f;
952 const float sy = relY + srcCy - 0.5f;
955 if (mode == RotateFilter::Nearest)
956 color = sampleNearest(
this, sx, sy);
958 color = sampleBilinear(
this, sx, sy);
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;
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);
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;
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;
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);
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;
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);
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);
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]);
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);
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);
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]);
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");
1026 std::string dstformat = getFormat();
1027 std::string srcformat = src->
getFormat();
1031 int dstW = getWidth();
1032 int dstH = getHeight();
1035 size_t dstpixelsize = getPixelSize();
1038 if (sx >= srcW || sx + sw < 0 || sy >= srcH || sy + sh < 0 || dx >= dstW || dx + sw < 0 || dy >= dstH ||
1064 if (dx + sw > dstW) sw = dstW - dx;
1066 if (dy + sh > dstH) sh = dstH - dy;
1068 if (sx + sw > srcW) sw = srcW - sx;
1070 if (sy + sh > srcH) sh = srcH - sy;
1072 uint8_t *
s = (uint8_t *)src->
getData();
1073 uint8_t *
d = (uint8_t *)getData();
1075 auto getfunction = src->pixelGetFunction;
1076 auto setfunction = pixelSetFunction;
1079 if (srcformat == dstformat && (sw == dstW && dstW == srcW && sh == dstH && dstH == srcH)) {
1080 memcpy(
d,
s, srcpixelsize * sw * sh);
1081 }
else if (sw > 0) {
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};
1087 if (srcformat == dstformat)
1088 memcpy(rowdst.
u8, rowsrc.
u8, srcpixelsize * sw);
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);
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);
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);
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);
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);
1133size_t ImageData::getPixelSize()
const {
return getPixelFormatSize(getPixelFormatFromName(format)); }
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;
1156bool ImageData::canPaste(std::string src, std::string dst) {
1157 if (src == dst)
return true;
1159 if (!(src ==
"RGBA8" || src ==
"RGBA16" || src ==
"RGBA16F" || src ==
"RGBA32F"))
return false;
1161 if (!(dst ==
"RGBA8" || dst ==
"RGBA16" || dst ==
"RGBA16F" || dst ==
"RGBA32F"))
return false;
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;
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;
EVEngine assertion entry point, backed by zeroerr.
#define EV_PARAM_CHECK(cond,...)
Validate a function parameter / public API precondition.
image::ImageData::Colorf color
Resource is a game object that is managed by the ResourceManager. It can be loaded from a file or gen...
Data buffer paired with a filename (used for type identification).
size_t getSize() const
Gets the size of the Data in bytes.
void * getData() const
Gets a pointer to the data. This pointer will obviously not be valid if the Data object is destroyed.
Represents raw pixel data.
ImageData * clone() const
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
PixelSetFunction getPixelSetFunction() const
static bool validPixelFormat(std::string format)
PixelGetFunction getPixelGetFunction() const
size_t getPixelSize() const
void adopt(eve::Resource &replacement) override
Replace this instance's pixels with replacement's (cache reload). The replacement is drained; its des...
medialoader::FormatHandler FormatHandler
std::string getFormat() const