Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/imbuf/intern/filetype.c2
-rw-r--r--source/blender/imbuf/intern/openexr/openexr_api.cpp185
-rw-r--r--source/blender/imbuf/intern/openexr/openexr_api.h7
3 files changed, 186 insertions, 8 deletions
diff --git a/source/blender/imbuf/intern/filetype.c b/source/blender/imbuf/intern/filetype.c
index 74042ef75be..92fa980cd7f 100644
--- a/source/blender/imbuf/intern/filetype.c
+++ b/source/blender/imbuf/intern/filetype.c
@@ -157,7 +157,7 @@ const ImFileType IMB_FILE_TYPES[] = {
.is_a = imb_is_a_openexr,
.load = imb_load_openexr,
.load_filepath = NULL,
- .load_filepath_thumbnail = NULL,
+ .load_filepath_thumbnail = imb_load_filepath_thumbnail_openexr,
.save = imb_save_openexr,
.load_tile = NULL,
.flag = IM_FTYPE_FLOAT,
diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp
index 66ee3cf2c26..104be9008a4 100644
--- a/source/blender/imbuf/intern/openexr/openexr_api.cpp
+++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp
@@ -10,6 +10,7 @@
#include <cstddef>
#include <cstdio>
#include <cstdlib>
+#include <fcntl.h>
#include <fstream>
#include <iostream>
#include <set>
@@ -43,6 +44,8 @@
#include <OpenEXR/ImfInputFile.h>
#include <OpenEXR/ImfOutputFile.h>
#include <OpenEXR/ImfPixelType.h>
+#include <OpenEXR/ImfPreviewImage.h>
+#include <openexr/ImfRgbaFile.h>
#include <OpenEXR/ImfStandardAttributes.h>
#include <OpenEXR/ImfStringAttribute.h>
#include <OpenEXR/ImfVersion.h>
@@ -63,6 +66,9 @@
#if defined(WIN32)
# include "utfconv.h"
+# include <io.h>
+#else
+# include <unistd.h>
#endif
#include "MEM_guardedalloc.h"
@@ -77,7 +83,9 @@ _CRTIMP void __cdecl _invalid_parameter_noinfo(void)
#endif
}
#include "BLI_blenlib.h"
+#include "BLI_fileops.h"
#include "BLI_math_color.h"
+#include "BLI_mmap.h"
#include "BLI_string_utils.h"
#include "BLI_threads.h"
@@ -151,6 +159,66 @@ class IMemStream : public Imf::IStream {
unsigned char *_exrbuf;
};
+/* Memory-Mapped Input Stream */
+
+class IMMapStream : public Imf::IStream {
+ public:
+ IMMapStream(const char *filepath) : IStream(filepath)
+ {
+ int file = BLI_open(filepath, O_BINARY | O_RDONLY, 0);
+ if (file < 0) {
+ throw IEX_NAMESPACE::InputExc("file not found");
+ }
+ _exrpos = 0;
+ _exrsize = BLI_file_descriptor_size(file);
+ imb_mmap_lock();
+ _mmap_file = BLI_mmap_open(file);
+ imb_mmap_unlock();
+ if (_mmap_file == NULL) {
+ throw IEX_NAMESPACE::InputExc("BLI_mmap_open failed");
+ }
+ close(file);
+ _exrbuf = (unsigned char *)BLI_mmap_get_pointer(_mmap_file);
+ }
+
+ ~IMMapStream()
+ {
+ imb_mmap_lock();
+ BLI_mmap_free(_mmap_file);
+ imb_mmap_unlock();
+ }
+
+ /* This is implementing regular `read`, not `readMemoryMapped`, because DWAA and DWAB
+ * decompressors load on unaligned offsets. Therefore we can't avoid the memory copy. */
+
+ bool read(char c[], int n) override
+ {
+ if (_exrpos + n > _exrsize) {
+ throw Iex::InputExc("Unexpected end of file.");
+ }
+ memcpy(c, _exrbuf + _exrpos, n);
+ _exrpos += n;
+
+ return _exrpos < _exrsize;
+ }
+
+ exr_file_offset_t tellg() override
+ {
+ return _exrpos;
+ }
+
+ void seekg(exr_file_offset_t pos) override
+ {
+ _exrpos = pos;
+ }
+
+ private:
+ BLI_mmap_file *_mmap_file;
+ exr_file_offset_t _exrpos;
+ exr_file_offset_t _exrsize;
+ unsigned char *_exrbuf;
+};
+
/* File Input Stream */
class IFileStream : public Imf::IStream {
@@ -2099,19 +2167,122 @@ struct ImBuf *imb_load_openexr(const unsigned char *mem,
}
}
-void imb_initopenexr(void)
+struct ImBuf *imb_load_filepath_thumbnail_openexr(const char *filepath,
+ const int flags,
+ const size_t max_thumb_size,
+ char colorspace[],
+ size_t *r_width,
+ size_t *r_height)
{
- int num_threads = BLI_system_thread_count();
+ IStream *stream = nullptr;
+ Imf::RgbaInputFile *file = nullptr;
+
+ /* OpenExr uses exceptions for error-handling. */
+ try {
+
+ /* The memory-mapped stream is faster, but don't use for huge files as it requires contiguous
+ * address space and we are processing multiple files at once (typically one per processor
+ * core). The 100 MB limit here is arbitrary, but seems reasonable and conservative. */
+ if (BLI_file_size(filepath) < 100 * 1024 * 1024) {
+ stream = new IMMapStream(filepath);
+ }
+ else {
+ stream = new IFileStream(filepath);
+ }
+
+ /* imb_initopenexr() creates a global pool of worker threads. But we thumbnail multiple images
+ * at once, and by default each file will attempt to use the entire pool for itself, stalling
+ * the others. So each thumbnail should use a single thread of the pool. */
+ file = new RgbaInputFile(*stream, 1);
+
+ if (!file->isComplete()) {
+ return nullptr;
+ }
+
+ Imath::Box2i dw = file->dataWindow();
+ int source_w = dw.max.x - dw.min.x + 1;
+ int source_h = dw.max.y - dw.min.y + 1;
+ *r_width = source_w;
+ *r_height = source_h;
+
+ /* If there is an embedded thumbnail, return that instead of making a new one. */
+ if (file->header().hasPreviewImage()) {
+ const Imf::PreviewImage &preview = file->header().previewImage();
+ ImBuf *ibuf = IMB_allocFromBuffer(
+ (unsigned int *)preview.pixels(), NULL, preview.width(), preview.height(), 4);
+ delete file;
+ delete stream;
+ IMB_flipy(ibuf);
+ return ibuf;
+ }
+
+ /* Create a new thumbnail. */
+
+ if (colorspace && colorspace[0]) {
+ colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_FLOAT);
+ }
+
+ float scale_factor = MIN2((float)max_thumb_size / (float)source_w,
+ (float)max_thumb_size / (float)source_h);
+ int dest_w = (int)(source_w * scale_factor);
+ int dest_h = (int)(source_h * scale_factor);
+
+ struct ImBuf *ibuf = IMB_allocImBuf(dest_w, dest_h, 32, IB_rectfloat);
+
+ /* A single row of source pixels. */
+ Imf::Array<Imf::Rgba> pixels(source_w);
+
+ /* Loop through destination thumbnail rows. */
+ for (int h = 0; h < dest_h; h++) {
+
+ /* Load the single source row that corresponds with destination row. */
+ int source_y = (int)((float)h / scale_factor) + dw.min.y;
+ file->setFrameBuffer(&pixels[0] - dw.min.x - source_y * source_w, 1, source_w);
+ file->readPixels(source_y);
+
+ for (int w = 0; w < dest_w; w++) {
+ /* For each destination pixel find single corresponding source pixel. */
+ int source_x = (int)(MIN2((w / scale_factor), dw.max.x - 1));
+ float *dest_px = &ibuf->rect_float[(h * dest_w + w) * 4];
+ dest_px[0] = pixels[source_x].r;
+ dest_px[1] = pixels[source_x].g;
+ dest_px[2] = pixels[source_x].b;
+ dest_px[3] = pixels[source_x].a;
+ }
+ }
- setGlobalThreadCount(num_threads);
+ if (file->lineOrder() == INCREASING_Y) {
+ IMB_flipy(ibuf);
+ }
+
+ delete file;
+ delete stream;
+
+ return ibuf;
+ }
+
+ catch (const std::exception &exc) {
+ std::cerr << exc.what() << std::endl;
+ delete file;
+ delete stream;
+ return nullptr;
+ }
+
+ return nullptr;
+}
+
+void imb_initopenexr(void)
+{
+ /* In a multithreaded program, staticInitialize() must be called once during startup, before the
+ * program accesses any other functions or classes in the IlmImf library. */
+ Imf::staticInitialize();
+ Imf::setGlobalThreadCount(BLI_system_thread_count());
}
void imb_exitopenexr(void)
{
- /* Tells OpenEXR to free thread pool, also ensures there is no running
- * tasks.
- */
- setGlobalThreadCount(0);
+ /* Tells OpenEXR to free thread pool, also ensures there is no running tasks. */
+ Imf::setGlobalThreadCount(0);
}
} /* export "C" */
diff --git a/source/blender/imbuf/intern/openexr/openexr_api.h b/source/blender/imbuf/intern/openexr/openexr_api.h
index c02b03dbe6c..a62c87428b6 100644
--- a/source/blender/imbuf/intern/openexr/openexr_api.h
+++ b/source/blender/imbuf/intern/openexr/openexr_api.h
@@ -26,6 +26,13 @@ bool imb_save_openexr(struct ImBuf *ibuf, const char *name, int flags);
struct ImBuf *imb_load_openexr(const unsigned char *mem, size_t size, int flags, char *colorspace);
+struct ImBuf *imb_load_filepath_thumbnail_openexr(const char *filepath,
+ const int flags,
+ const size_t max_thumb_size,
+ char colorspace[],
+ size_t *r_width,
+ size_t *r_height);
+
#ifdef __cplusplus
}
#endif