载入中...
搜索中...
未找到
Image.cpp
浏览该文件的文档.
1
2
3#include "Image.h"
4#include "UvPaintRegion.h"
5#include "UvPaintSession.h"
6#include "WebPHandler.h"
7#include "common/Exception.h"
8#include "common/Resource.h"
11#include "common/config.h"
12
13#include "medialoader/image/EXRHandler.h"
14#include "medialoader/image/PNGHandler.h"
15#include "medialoader/image/STBHandler.h"
16
17#include "medialoader/image/ASTCHandler.h"
18#include "medialoader/image/KTXHandler.h"
19#include "medialoader/image/PKMHandler.h"
20#include "medialoader/image/PVRHandler.h"
21#include "medialoader/image/ddsHandler.h"
22
23#include <simplesquirrel/simplesquirrel.hpp>
24
25namespace eve {
26namespace image {
27
29
31 using namespace medialoader;
32
33 float16Init(); // Makes sure half-float conversions can be used.
34
35 formatHandlers = {
36 new PNGHandler, new WebPHandler, new STBHandler, new EXRHandler, new DDSHandler,
37 new PVRHandler, new KTXHandler, new PKMHandler, new ASTCHandler,
38 };
39}
40
42 // ImageData objects reference the FormatHandlers in our list, so we should
43 // release them instead of deleting them completely here.
44
45 // TODO: Check if this is necessary
46 // for (FormatHandler *handler : formatHandlers)
47 // handler->release();
48}
49
50
51ImageData* Image::newImageData(Data* data) { return new ImageData(data); }
52
54 if (path.empty()) throw eve::Exception("Image::newImageDataFromFile: empty path");
55
57 if (!resource) throw eve::Exception("Could not load image file: %s", path.c_str());
58 return static_cast<ImageData*>(resource);
59}
60
61ImageData* Image::newImageData(int width, int height, std::string format) {
62 return new ImageData(width, height, format);
63}
64
65ImageData* Image::newImageData(int width, int height, std::string format, void* data, bool own) {
66 return new ImageData(width, height, format, data, own);
67}
68
69
71 for (FormatHandler* handler : formatHandlers) {
72 if (handler->canParseCompressed((const char*)data->getData(), data->getSize())) return true;
73 }
74
75 return false;
76}
77
78const std::list<medialoader::FormatHandler*>& Image::getFormatHandlers() const { return formatHandlers; }
79
80ImageData* Image::newPastedImageData(ImageData* src, int sx, int sy, int w, int h) {
81 ImageData* res = newImageData(w, h, src->getFormat());
82 try {
83 res->paste(src, 0, 0, sx, sy, w, h);
84 } catch (eve::Exception&) {
85 // res->release();
86 throw;
87 }
88 return res;
89}
90
91std::vector<eve::script::Owned<ImageData>> Image::newCubeFaces(ImageData* src) {
92 // The faces array is always ordered +x, -x, +y, -y, +z, -z.
93 std::vector<eve::script::Owned<ImageData>> faces;
94
95 int totalW = src->getWidth();
96 int totalH = src->getHeight();
97
98 if (totalW % 3 == 0 && totalH % 4 == 0 && totalW / 3 == totalH / 4) {
99 // +y
100 // +z +x -z
101 // -y
102 // -x
103
104 int w = totalW / 3;
105 int h = totalH / 4;
106
107 faces.emplace_back(newPastedImageData(src, 1 * w, 1 * h, w, h));
108 faces.emplace_back(newPastedImageData(src, 1 * w, 3 * h, w, h));
109 faces.emplace_back(newPastedImageData(src, 1 * w, 0 * h, w, h));
110 faces.emplace_back(newPastedImageData(src, 1 * w, 2 * h, w, h));
111 faces.emplace_back(newPastedImageData(src, 0 * w, 1 * h, w, h));
112 faces.emplace_back(newPastedImageData(src, 2 * w, 1 * h, w, h));
113 } else if (totalW % 4 == 0 && totalH % 3 == 0 && totalW / 4 == totalH / 3) {
114 // +y
115 // -x +z +x -z
116 // -y
117
118 int w = totalW / 4;
119 int h = totalH / 3;
120
121 faces.emplace_back(newPastedImageData(src, 2 * w, 1 * h, w, h));
122 faces.emplace_back(newPastedImageData(src, 0 * w, 1 * h, w, h));
123 faces.emplace_back(newPastedImageData(src, 1 * w, 0 * h, w, h));
124 faces.emplace_back(newPastedImageData(src, 1 * w, 2 * h, w, h));
125 faces.emplace_back(newPastedImageData(src, 1 * w, 1 * h, w, h));
126 faces.emplace_back(newPastedImageData(src, 3 * w, 1 * h, w, h));
127 } else if (totalH % 6 == 0 && totalW == totalH / 6) {
128 // +x
129 // -x
130 // +y
131 // -y
132 // +z
133 // -z
134
135 int w = totalW;
136 int h = totalH / 6;
137
138 for (int i = 0; i < 6; i++) faces.emplace_back(newPastedImageData(src, 0, i * h, w, h));
139 } else if (totalW % 6 == 0 && totalW / 6 == totalH) {
140 // +x -x +y -y +z -z
141
142 int w = totalW / 6;
143 int h = totalH;
144
145 for (int i = 0; i < 6; i++) faces.emplace_back(newPastedImageData(src, i * w, 0, w, h));
146 } else
147 throw eve::Exception("Unknown cubemap image dimensions!");
148
149 return faces;
150}
151
152std::vector<eve::script::Owned<ImageData>> Image::newVolumeLayers(ImageData* src) {
153 std::vector<eve::script::Owned<ImageData>> layers;
154
155 int totalW = src->getWidth();
156 int totalH = src->getHeight();
157
158 if (totalW % totalH == 0) {
159 for (int i = 0; i < totalW / totalH; i++)
160 layers.emplace_back(newPastedImageData(src, i * totalH, 0, totalH, totalH));
161 } else if (totalH % totalW == 0) {
162 for (int i = 0; i < totalH / totalW; i++)
163 layers.emplace_back(newPastedImageData(src, 0, i * totalW, totalW, totalW));
164 } else
165 throw eve::Exception("Cannot extract volume layers from source ImageData.");
166
167 return layers;
168}
169
170void Image::expose(ssq::Table& table) {
171 auto cls = table.addClass(name, Image::create, false);
172 expose(cls);
173
174 // Single ImageData class for every module that hands image::ImageData*
175 // to scripts (Font glyphs, Model3D embedded textures, this module).
176 auto img = table.addClass<image::ImageData>(
177 "ImageData", std::function<image::ImageData*()>([]() -> image::ImageData* { return nullptr; }), true);
178 img.addFunc("getWidth", &image::ImageData::getWidth);
179 img.addFunc("getHeight", &image::ImageData::getHeight);
180 img.addFunc("getFormat", &image::ImageData::getFormat);
181 img.addFunc("getSize", &image::ImageData::getSize);
182 img.addFunc("getPixelSize", &image::ImageData::getPixelSize);
183 img.addFunc("isSRGB", &image::ImageData::isSRGB);
184 img.addFunc("inside", &image::ImageData::inside);
185 img.addFunc("clone", &image::ImageData::clone);
186 img.addFunc("paste", &image::ImageData::paste);
187 img.addFunc("rotate", &image::ImageData::rotate);
188 img.addFunc("getPixelR", [](image::ImageData* self, int x, int y) -> float {
189 if (!self) return 0.f;
190 return self->getPixel(x, y).r;
191 });
192 img.addFunc("getPixelG", [](image::ImageData* self, int x, int y) -> float {
193 if (!self) return 0.f;
194 return self->getPixel(x, y).g;
195 });
196 img.addFunc("getPixelB", [](image::ImageData* self, int x, int y) -> float {
197 if (!self) return 0.f;
198 return self->getPixel(x, y).b;
199 });
200 img.addFunc("getPixelA", [](image::ImageData* self, int x, int y) -> float {
201 if (!self) return 0.f;
202 return self->getPixel(x, y).a;
203 });
204 img.addFunc("setPixel", [](image::ImageData* self, int x, int y, float r, float g, float b, float a) {
205 if (!self) return;
206 self->setPixel(x, y, image::ImageData::Colorf{r, g, b, a});
207 });
208 img.addFunc("paintCircleUv", [](image::ImageData* self, float u, float v, float radius, float r, float g, float b,
209 float a, bool wrapU, bool wrapV) {
210 if (!self) throw eve::Exception("ImageData.paintCircleUv: null image");
211 auto painted = self->paintCircleUv(u, v, radius, image::ImageData::Colorf{r, g, b, a}, wrapU, wrapV);
212 if (!painted.ok()) throw eve::Exception("%s", painted.status().describe().c_str());
213 return std::move(painted).takeValue().changedPixelCount;
214 });
215
216 auto paint = table.addClass<UvPaintSession>(
217 "UvPaintSession", std::function<UvPaintSession*()>([]() -> UvPaintSession* { return nullptr; }), true);
218 paint.addFunc("initialize", [vm = paint.getHandle()](UvPaintSession* self, ImageData* pixels) {
219 if (!pixels)
220 return eve::script::projectResult(vm, Result<void>::failure(Diagnostic::error(
221 DiagnosticCode::InvalidArgument, "initialize requires ImageData",
222 "image", {}, "image.uvPaintSession.script")));
223 return eve::script::projectResult(vm, self->initializeResult(*pixels));
224 });
225 paint.addFunc("paintCircle", [vm = paint.getHandle()](UvPaintSession* self, float u, float v, float radius, float r,
226 float g, float b, float a, bool wrapU, bool wrapV) {
228 vm, self->paintCircleResult(u, v, radius, r, g, b, a, wrapU, wrapV),
229 [](const UvPaintReceipt& receipt) { return eve::Value(receipt.changedPixelCount); });
230 });
231 paint.addFunc("undo", [vm = paint.getHandle()](UvPaintSession* self) {
232 return eve::script::projectResult(vm, self->undoResult());
233 });
234 paint.addFunc("restore", [vm = paint.getHandle()](UvPaintSession* self) {
235 return eve::script::projectResult(vm, self->restoreResult());
236 });
237 paint.addFunc("bake", [vm = paint.getHandle()](UvPaintSession* self) {
238 return eve::script::projectResult(vm, self->bakeResult());
239 });
240 paint.addFunc("currentImageResult", [vm = paint.getHandle()](UvPaintSession* self) {
241 auto result = self->currentImageResult();
242 if (!result.ok()) return eve::script::projectStatusResult(vm, result.status());
243 auto instance = eve::script::makeOwnedSquirrelInstance<ImageData>(vm, std::move(result).takeValue());
244 if (!instance.ok()) return eve::script::projectStatusResult(vm, instance.status());
245 return eve::script::projectStatusResult(vm, Status::success(), std::move(instance).takeValue());
246 });
247 paint.addFunc("copyCurrentTo", [vm = paint.getHandle()](UvPaintSession* self, ImageData* destination) {
248 if (!destination)
251 "copyCurrentTo requires ImageData", "destination", {},
252 "image.uvPaintSession.script")));
253 return eve::script::projectResult(vm, self->copyCurrentToResult(*destination));
254 });
255 paint.addFunc("getRevision", [](UvPaintSession* self) { return self->revision(); });
256 paint.addFunc("getUndoCount", [](UvPaintSession* self) { return self->undoCount(); });
257 paint.addFunc("isInitialized", [](UvPaintSession* self) { return self->isInitialized(); });
258
259 auto region = table.addClass<UvPaintRegion>(
260 "UvPaintRegion", std::function<UvPaintRegion*()>([]() -> UvPaintRegion* { return nullptr; }), true);
261 region.addFunc("getCenterX", [](UvPaintRegion* self) { return self->centerX; });
262 region.addFunc("getCenterY", [](UvPaintRegion* self) { return self->centerY; });
263 region.addFunc("getX", [](UvPaintRegion* self) { return self->minX; });
264 region.addFunc("getY", [](UvPaintRegion* self) { return self->minY; });
265 region.addFunc("getWidth", [](UvPaintRegion* self) { return self->width(); });
266 region.addFunc("getHeight", [](UvPaintRegion* self) { return self->height(); });
267 region.addFunc("getR", [](UvPaintRegion* self) { return self->r; });
268 region.addFunc("getG", [](UvPaintRegion* self) { return self->g; });
269 region.addFunc("getB", [](UvPaintRegion* self) { return self->b; });
270 region.addFunc("getA", [](UvPaintRegion* self) { return self->a; });
271 region.addFunc("prepare", [vm = region.getHandle()](UvPaintRegion* self, int width, int height, float u, float v,
272 float radiusU, float radiusV, float r, float g, float b,
273 float a, bool flipV) {
274 return eve::script::projectResult(
275 vm, self->prepareResult(width, height, u, v, radiusU, radiusV, r, g, b, a, flipV));
276 });
277}
278
279void Image::expose(ssq::Class& cls) {
280 cls.addFunc("getName", &Image::getName);
281 cls.addFunc("newImageData", static_cast<ImageData* (Image::*)(Data*)>(&Image::newImageData));
282 cls.addFunc("newImageDataFromFile", &Image::newImageDataFromFile);
283 cls.addFunc("newEmptyImageData", [](Image* self, int width, int height, const std::string& format) -> ImageData* {
284 if (!self) return nullptr;
285 return self->newImageData(width, height, format);
286 });
287 cls.addFunc("newUvPaintSession", [](Image*) { return new UvPaintSession(); });
288 cls.addFunc("newUvPaintRegion", [](Image*) { return new UvPaintRegion(); });
289 cls.addFunc("isCompressed", &Image::isCompressed);
290}
291
292} // namespace image
293} // namespace eve
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
HSQUIRRELVM vm
Definition ECS.cpp:20
HSQOBJECT cls
Definition ECS.cpp:21
tensor::Graph g
Definition GpuGraph.cpp:7
vk::UniqueImage image
float u
Definition Grass.cpp:233
double r
float v
int h
std::uint32_t height
std::uint32_t width
HSQOBJECT handler
std::string name
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
#define Module_IMPL(ModuleName, newExpr)
Definition Module.h:26
float radius
std::string path
Definition PlayHost.cpp:110
uint8_t * pixels
std::string resource
The single Squirrel projection for common Result, Status and Value.
Explicit Value/Owned/Borrowed semantics at the Squirrel boundary.
Data public API.
Definition Data.h:8
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
Resource * get(std::string key)
Get the cached resource for key; on a miss, load it through the registered IAssetReloader providers a...
Definition Resource.cpp:141
static ResourceManager & getInstance()
Returns the instance.
Definition Resource.cpp:15
Resource is a game object that is managed by the ResourceManager. It can be loaded from a file or gen...
Definition Resource.h:51
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
Represents raw pixel data.
Definition ImageData.h:38
ImageData * clone() const
Definition ImageData.cpp:73
bool inside(int x, int y) const
Checks whether a position is inside this ImageData. Useful for checking bounds.
void getPixel(int x, int y, Colorf &c) const
Gets the pixel at location (x,y).
medialoader::Colorf Colorf
Definition ImageData.h:41
void paste(ImageData *src, int dx, int dy, int sx, int sy, int sw, int sh)
Paste part of one ImageData onto another. The subregion defined by the top-left corner (sx,...
size_t getSize() const
ImageData * rotate(float radians, std::string filter="nearest", bool expand=true) const
Rotate pixels around the image center and return a new ImageData.
size_t getPixelSize() const
std::string getFormat() const
This module is responsible for decoding files such as PNG, GIF, JPEG into raw pixel data,...
Definition Image.h:22
Image()
Image.
Definition Image.cpp:30
ImageData * newImageDataFromFile(std::string path)
Loads an image from a VFS path through the unified resource cache. Repeated loads of one path share a...
Definition Image.cpp:53
medialoader::FormatHandler FormatHandler
Definition Image.h:24
std::vector< script::Owned< ImageData > > newVolumeLayers(ImageData *src)
Creates a volume layers. @ownership Caller deletes unless documented otherwise.
Definition Image.cpp:152
bool isCompressed(Data *data)
Creates new CompressedImageData from FileData.
Definition Image.cpp:70
std::vector< script::Owned< ImageData > > newCubeFaces(ImageData *src)
Creates a cube faces. @ownership Caller deletes unless documented otherwise.
Definition Image.cpp:91
const std::list< FormatHandler * > & getFormatHandlers() const
Returns the format handlers.
Definition Image.cpp:78
ImageData * newImageData(Data *data)
Creates new ImageData from FileData.
Definition Image.cpp:51
virtual ~Image()
Image.
Definition Image.cpp:41
Internal decoder adapter; each call owns its output and borrows input synchronously.
Definition WebPHandler.h:8
std::unordered_map< std::string, SkillDefinition > & table()
Definition Skill.cpp:65
ssq::Table projectStatusResult(HSQUIRRELVM vm, const Status &status)
Project a checked native status that carries no payload.
ssq::Table projectResult(HSQUIRRELVM vm, Result< void > &&result)
Consume and project a void native Result using the common schema.
Build metadata (engine git commit, build time, third-party version).
Definition Build.cpp:16