#include "Image.h" #include "common/Exception.h" #include "common/Resource.h" #include "common/config.h" #include "medialoader/image/PNGHandler.h" #include "medialoader/image/STBHandler.h" #include "medialoader/image/EXRHandler.h" #include "medialoader/image/ddsHandler.h" #include "medialoader/image/PVRHandler.h" #include "medialoader/image/KTXHandler.h" #include "medialoader/image/PKMHandler.h" #include "medialoader/image/ASTCHandler.h" #include namespace eve { namespace image { Module_IMPL(Image, new Image()); Image::Image() { using namespace medialoader; float16Init(); // Makes sure half-float conversions can be used. formatHandlers = { new PNGHandler, new STBHandler, new EXRHandler, new DDSHandler, new PVRHandler, new KTXHandler, new PKMHandler, new ASTCHandler, }; } Image::~Image() { // ImageData objects reference the FormatHandlers in our list, so we should // release them instead of deleting them completely here. // TODO: Check if this is necessary // for (FormatHandler *handler : formatHandlers) // handler->release(); } ImageData *Image::newImageData(Data *data) { return new ImageData(data); } ImageData *Image::newImageDataFromFile(std::string path) { if (path.empty()) throw eve::Exception("Image::newImageDataFromFile: empty path"); eve::Resource *resource = eve::ResourceManager::getInstance().get(path); if (!resource) throw eve::Exception("Could not load image file: %s", path.c_str()); return static_cast(resource); } ImageData *Image::newImageData(int width, int height, std::string format) { return new ImageData(width, height, format); } ImageData *Image::newImageData(int width, int height, std::string format, void *data, bool own) { return new ImageData(width, height, format, data, own); } bool Image::isCompressed(Data *data) { for (FormatHandler *handler : formatHandlers) { if (handler->canParseCompressed((const char*)data->getData(), data->getSize())) return true; } return false; } const std::list &Image::getFormatHandlers() const { return formatHandlers; } ImageData *Image::newPastedImageData(ImageData *src, int sx, int sy, int w, int h) { ImageData *res = newImageData(w, h, src->getFormat()); try { res->paste(src, 0, 0, sx, sy, w, h); } catch (eve::Exception &) { // res->release(); throw; } return res; } std::vector> Image::newCubeFaces(ImageData *src) { // The faces array is always ordered +x, -x, +y, -y, +z, -z. std::vector> faces; int totalW = src->getWidth(); int totalH = src->getHeight(); if (totalW % 3 == 0 && totalH % 4 == 0 && totalW / 3 == totalH / 4) { // +y // +z +x -z // -y // -x int w = totalW / 3; int h = totalH / 4; faces.emplace_back(newPastedImageData(src, 1*w, 1*h, w, h)); faces.emplace_back(newPastedImageData(src, 1*w, 3*h, w, h)); faces.emplace_back(newPastedImageData(src, 1*w, 0*h, w, h)); faces.emplace_back(newPastedImageData(src, 1*w, 2*h, w, h)); faces.emplace_back(newPastedImageData(src, 0*w, 1*h, w, h)); faces.emplace_back(newPastedImageData(src, 2*w, 1*h, w, h)); } else if (totalW % 4 == 0 && totalH % 3 == 0 && totalW / 4 == totalH / 3) { // +y // -x +z +x -z // -y int w = totalW / 4; int h = totalH / 3; faces.emplace_back(newPastedImageData(src, 2*w, 1*h, w, h)); faces.emplace_back(newPastedImageData(src, 0*w, 1*h, w, h)); faces.emplace_back(newPastedImageData(src, 1*w, 0*h, w, h)); faces.emplace_back(newPastedImageData(src, 1*w, 2*h, w, h)); faces.emplace_back(newPastedImageData(src, 1*w, 1*h, w, h)); faces.emplace_back(newPastedImageData(src, 3*w, 1*h, w, h)); } else if (totalH % 6 == 0 && totalW == totalH / 6) { // +x // -x // +y // -y // +z // -z int w = totalW; int h = totalH / 6; for (int i = 0; i < 6; i++) faces.emplace_back(newPastedImageData(src, 0, i * h, w, h)); } else if (totalW % 6 == 0 && totalW / 6 == totalH) { // +x -x +y -y +z -z int w = totalW / 6; int h = totalH; for (int i = 0; i < 6; i++) faces.emplace_back(newPastedImageData(src, i * w, 0, w, h)); } else throw eve::Exception("Unknown cubemap image dimensions!"); return faces; } std::vector> Image::newVolumeLayers(ImageData *src) { std::vector> layers; int totalW = src->getWidth(); int totalH = src->getHeight(); if (totalW % totalH == 0) { for (int i = 0; i < totalW / totalH; i++) layers.emplace_back(newPastedImageData(src, i * totalH, 0, totalH, totalH)); } else if (totalH % totalW == 0) { for (int i = 0; i < totalH / totalW; i++) layers.emplace_back(newPastedImageData(src, 0, i * totalW, totalW, totalW)); } else throw eve::Exception("Cannot extract volume layers from source ImageData."); return layers; } void Image::expose(ssq::Table &table) { auto cls = table.addClass(name, Image::create, false); expose(cls); // Single ImageData class for every module that hands image::ImageData* // to scripts (Font glyphs, Model3D embedded textures, this module). auto img = table.addClass( "ImageData", std::function([]() -> image::ImageData * { return nullptr; }), true); img.addFunc("getWidth", &image::ImageData::getWidth); img.addFunc("getHeight", &image::ImageData::getHeight); img.addFunc("getFormat", &image::ImageData::getFormat); img.addFunc("getSize", &image::ImageData::getSize); img.addFunc("getPixelSize", &image::ImageData::getPixelSize); img.addFunc("isSRGB", &image::ImageData::isSRGB); img.addFunc("inside", &image::ImageData::inside); img.addFunc("clone", &image::ImageData::clone); img.addFunc("paste", &image::ImageData::paste); img.addFunc("rotate", &image::ImageData::rotate); img.addFunc("getPixelR", [](image::ImageData *self, int x, int y) -> float { if (!self) return 0.f; return self->getPixel(x, y).r; }); img.addFunc("getPixelG", [](image::ImageData *self, int x, int y) -> float { if (!self) return 0.f; return self->getPixel(x, y).g; }); img.addFunc("getPixelB", [](image::ImageData *self, int x, int y) -> float { if (!self) return 0.f; return self->getPixel(x, y).b; }); img.addFunc("getPixelA", [](image::ImageData *self, int x, int y) -> float { if (!self) return 0.f; return self->getPixel(x, y).a; }); img.addFunc("setPixel", [](image::ImageData *self, int x, int y, float r, float g, float b, float a) { if (!self) return; self->setPixel(x, y, image::ImageData::Colorf{r, g, b, a}); }); img.addFunc("paintCircleUv", [](image::ImageData *self, float u, float v, float radius, float r, float g, float b, float a, bool wrapU, bool wrapV) { if (!self) throw eve::Exception("ImageData.paintCircleUv: null image"); auto painted = self->paintCircleUv(u, v, radius, image::ImageData::Colorf{r, g, b, a}, wrapU, wrapV); if (!painted.ok()) throw eve::Exception("%s", painted.status().describe().c_str()); return std::move(painted).takeValue().changedPixelCount; }); } void Image::expose(ssq::Class &cls) { cls.addFunc("getName", &Image::getName); cls.addFunc("newImageData", static_cast(&Image::newImageData)); cls.addFunc("newImageDataFromFile", &Image::newImageDataFromFile); cls.addFunc("newEmptyImageData", [](Image *self, int width, int height, const std::string &format) -> ImageData * { if (!self) return nullptr; return self->newImageData(width, height, format); }); cls.addFunc("isCompressed", &Image::isCompressed); } } // image } // eve