Răsfoiți Sursa

Updated tinyexr.

Branimir Karadžić 7 ani în urmă
părinte
comite
b2a9b5fdbe
1 a modificat fișierele cu 219 adăugiri și 128 ștergeri
  1. 219 128
      3rdparty/tinyexr/tinyexr.h

+ 219 - 128
3rdparty/tinyexr/tinyexr.h

@@ -116,6 +116,8 @@ extern "C" {
 #define TINYEXR_ERROR_UNSUPPORTED_FORMAT (-7)
 #define TINYEXR_ERROR_INVALID_HEADER (-8)
 #define TINYEXR_ERROR_UNSUPPORTED_FEATURE (-9)
+#define TINYEXR_ERROR_CANT_WRITE_FILE (-10)
+#define TINYEXR_ERROR_SERIALZATION_FAILED (-11)
 
 // @note { OpenEXR file format: http://www.openexr.com/openexrfilelayout.pdf }
 
@@ -279,9 +281,12 @@ extern int LoadEXR(float **out_rgba, int *width, int *height,
 // Save image as fp16(HALF) format when `save_as_fp16` is positive non-zero
 // value.
 // Save image as fp32(FLOAT) format when `save_as_fp16` is 0.
+// Use ZIP compression by default.
+// Returns negative value and may set error string in `err` when there's an
+// error
 extern int SaveEXR(const float *data, const int width, const int height,
                    const int components, const int save_as_fp16,
-                   const char *filename);
+                   const char *filename, const char **err);
 
 // Initialize EXRHeader struct
 extern void InitEXRHeader(EXRHeader *exr_header);
@@ -400,9 +405,9 @@ extern int SaveEXRImageToFile(const EXRImage *image,
 
 // Saves multi-channel, single-frame OpenEXR image to a memory.
 // Image is compressed using EXRImage.compression value.
-// Return the number of bytes if succes.
-// Returns negative value and may set error string in `err` when there's an
-// error
+// Return the number of bytes if success.
+// Return zero and will set error string in `err` when there's an
+// error.
 // When there was an error message, Application must free `err` with
 // FreeEXRErrorMessage()
 extern size_t SaveEXRImageToMemory(const EXRImage *image,
@@ -529,15 +534,23 @@ namespace miniz {
 #if __has_warning("-Wcomma")
 #pragma clang diagnostic ignored "-Wcomma"
 #endif
+
 #if __has_warning("-Wmacro-redefined")
 #pragma clang diagnostic ignored "-Wmacro-redefined"
 #endif
+
 #if __has_warning("-Wcast-qual")
 #pragma clang diagnostic ignored "-Wcast-qual"
 #endif
+
 #if __has_warning("-Wzero-as-null-pointer-constant")
 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
 #endif
+
+#if __has_warning("-Wtautological-constant-compare")
+#pragma clang diagnostic ignored "-Wtautological-constant-compare"
+#endif
+
 #endif
 
 /* miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP
@@ -7253,7 +7266,21 @@ static bool ReadAttribute(std::string *name, std::string *type,
   tinyexr::swap4(reinterpret_cast<unsigned int *>(&data_len));
 
   if (data_len == 0) {
-    return false;
+    if ((*type).compare("string") == 0) {
+      // Accept empty string attribute.
+
+      marker += sizeof(uint32_t);
+      size -= sizeof(uint32_t);
+
+      *marker_size = name_len + 1 + type_len + 1 + sizeof(uint32_t);
+
+      data->resize(1);
+      (*data)[0] = '\0';
+
+      return true;
+    } else {
+      return false;
+    }
   }
 
   marker += sizeof(uint32_t);
@@ -9587,6 +9614,7 @@ bool CompressZfp(std::vector<unsigned char> *outBuf, unsigned int *outSize,
 // -----------------------------------------------------------------
 //
 
+// TODO(syoyo): Refactor function arguments.
 static bool DecodePixelData(/* out */ unsigned char **out_images,
                             const int *requested_pixel_types,
                             const unsigned char *data_ptr, size_t data_len,
@@ -9636,6 +9664,8 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
             FP16 hf;
 
             // hf.u = line_ptr[u];
+            // use `cpy` to avoid unaligned memory access when compiler's
+            // optimization is on.
             tinyexr::cpy2(&(hf.u), line_ptr + u);
 
             tinyexr::swap2(reinterpret_cast<unsigned short *>(&hf.u));
@@ -10070,37 +10100,76 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
 #endif
   } else if (compression_type == TINYEXR_COMPRESSIONTYPE_NONE) {
     for (size_t c = 0; c < num_channels; c++) {
-      if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) {
-        const unsigned short *line_ptr =
-            reinterpret_cast<const unsigned short *>(
-                data_ptr +
-                c * static_cast<size_t>(width) * sizeof(unsigned short));
-
-        if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) {
-          unsigned short *outLine =
-              reinterpret_cast<unsigned short *>(out_images[c]);
-          if (line_order == 0) {
-            outLine += y * x_stride;
-          } else {
-            outLine += (height - 1 - y) * x_stride;
-          }
+      for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
+        if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) {
+          const unsigned short *line_ptr =
+              reinterpret_cast<const unsigned short *>(
+                  data_ptr + v * pixel_data_size * size_t(width) +
+                  channel_offset_list[c] * static_cast<size_t>(width));
+
+          if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) {
+            unsigned short *outLine =
+                reinterpret_cast<unsigned short *>(out_images[c]);
+            if (line_order == 0) {
+              outLine += (size_t(y) + v) * size_t(x_stride);
+            } else {
+              outLine +=
+                  (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride);
+            }
 
-          for (int u = 0; u < width; u++) {
-            tinyexr::FP16 hf;
+            for (int u = 0; u < width; u++) {
+              tinyexr::FP16 hf;
 
-            // hf.u = line_ptr[u];
-            tinyexr::cpy2(&(hf.u), line_ptr + u);
+              // hf.u = line_ptr[u];
+              tinyexr::cpy2(&(hf.u), line_ptr + u);
 
-            tinyexr::swap2(reinterpret_cast<unsigned short *>(&hf.u));
+              tinyexr::swap2(reinterpret_cast<unsigned short *>(&hf.u));
 
-            outLine[u] = hf.u;
+              outLine[u] = hf.u;
+            }
+          } else if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT) {
+            float *outLine = reinterpret_cast<float *>(out_images[c]);
+            if (line_order == 0) {
+              outLine += (size_t(y) + v) * size_t(x_stride);
+            } else {
+              outLine +=
+                  (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride);
+            }
+
+            if (reinterpret_cast<const unsigned char *>(line_ptr + width) >
+                (data_ptr + data_len)) {
+              // Insufficient data size
+              return false;
+            }
+
+            for (int u = 0; u < width; u++) {
+              tinyexr::FP16 hf;
+
+              // address may not be aliged. use byte-wise copy for safety.#76
+              // hf.u = line_ptr[u];
+              tinyexr::cpy2(&(hf.u), line_ptr + u);
+
+              tinyexr::swap2(reinterpret_cast<unsigned short *>(&hf.u));
+
+              tinyexr::FP32 f32 = half_to_float(hf);
+
+              outLine[u] = f32.f;
+            }
+          } else {
+            assert(0);
+            return false;
           }
-        } else if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT) {
+        } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
+          const float *line_ptr = reinterpret_cast<const float *>(
+              data_ptr + v * pixel_data_size * size_t(width) +
+              channel_offset_list[c] * static_cast<size_t>(width));
+
           float *outLine = reinterpret_cast<float *>(out_images[c]);
           if (line_order == 0) {
-            outLine += y * x_stride;
+            outLine += (size_t(y) + v) * size_t(x_stride);
           } else {
-            outLine += (height - 1 - y) * x_stride;
+            outLine +=
+                (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride);
           }
 
           if (reinterpret_cast<const unsigned char *>(line_ptr + width) >
@@ -10110,71 +10179,41 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
           }
 
           for (int u = 0; u < width; u++) {
-            tinyexr::FP16 hf;
-
-            // address may not be aliged. use byte-wise copy for safety.#76
-            // hf.u = line_ptr[u];
-            tinyexr::cpy2(&(hf.u), line_ptr + u);
-
-            tinyexr::swap2(reinterpret_cast<unsigned short *>(&hf.u));
+            float val;
+            tinyexr::cpy4(&val, line_ptr + u);
 
-            tinyexr::FP32 f32 = half_to_float(hf);
+            tinyexr::swap4(reinterpret_cast<unsigned int *>(&val));
 
-            outLine[u] = f32.f;
+            outLine[u] = val;
           }
-        } else {
-          assert(0);
-          return false;
-        }
-      } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
-        const float *line_ptr = reinterpret_cast<const float *>(
-            data_ptr + c * static_cast<size_t>(width) * sizeof(float));
+        } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) {
+          const unsigned int *line_ptr = reinterpret_cast<const unsigned int *>(
+              data_ptr + v * pixel_data_size * size_t(width) +
+              channel_offset_list[c] * static_cast<size_t>(width));
 
-        float *outLine = reinterpret_cast<float *>(out_images[c]);
-        if (line_order == 0) {
-          outLine += y * x_stride;
-        } else {
-          outLine += (height - 1 - y) * x_stride;
-        }
-
-        if (reinterpret_cast<const unsigned char *>(line_ptr + width) >
-            (data_ptr + data_len)) {
-          // Insufficient data size
-          return false;
-        }
-
-        for (int u = 0; u < width; u++) {
-          float val;
-          tinyexr::cpy4(&val, line_ptr + u);
+          unsigned int *outLine =
+              reinterpret_cast<unsigned int *>(out_images[c]);
+          if (line_order == 0) {
+            outLine += (size_t(y) + v) * size_t(x_stride);
+          } else {
+            outLine +=
+                (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride);
+          }
 
-          tinyexr::swap4(reinterpret_cast<unsigned int *>(&val));
+          for (int u = 0; u < width; u++) {
+            if (reinterpret_cast<const unsigned char *>(line_ptr + u) >=
+                (data_ptr + data_len)) {
+              // Corrupsed data?
+              return false;
+            }
 
-          outLine[u] = val;
-        }
-      } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) {
-        const unsigned int *line_ptr = reinterpret_cast<const unsigned int *>(
-            data_ptr + c * static_cast<size_t>(width) * sizeof(unsigned int));
+            unsigned int val;
+            tinyexr::cpy4(&val, line_ptr + u);
 
-        unsigned int *outLine = reinterpret_cast<unsigned int *>(out_images[c]);
-        if (line_order == 0) {
-          outLine += y * x_stride;
-        } else {
-          outLine += (height - 1 - y) * x_stride;
-        }
+            tinyexr::swap4(reinterpret_cast<unsigned int *>(&val));
 
-        for (int u = 0; u < width; u++) {
-          if (reinterpret_cast<const unsigned char *>(line_ptr + u) >=
-              (data_ptr + data_len)) {
-            // Corrupsed data?
-            return false;
+            outLine[u] = val;
           }
-
-          unsigned int val;
-          tinyexr::cpy4(&val, line_ptr + u);
-
-          tinyexr::swap4(reinterpret_cast<unsigned int *>(&val));
-
-          outLine[u] = val;
         }
       }
     }
@@ -10680,7 +10719,8 @@ static void ConvertHeader(EXRHeader *exr_header, const HeaderInfo &info) {
 
 static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header,
                        const std::vector<tinyexr::tinyexr_uint64> &offsets,
-                       const unsigned char *head, const size_t size, std::string *err) {
+                       const unsigned char *head, const size_t size,
+                       std::string *err) {
   int num_channels = exr_header->num_channels;
 
   int num_scanline_blocks = 1;
@@ -11045,7 +11085,6 @@ static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header,
         free(exr_image->images);
         exr_image->images = NULL;
       }
-
     }
 
     return ret;
@@ -11240,9 +11279,7 @@ int ParseEXRHeaderFromMemory(EXRHeader *exr_header, const EXRVersion *version,
   }
 
   if (size < tinyexr::kEXRVersionSize) {
-    tinyexr::SetErrorMessage(
-        "Insufficient header/data size.\n",
-        err);
+    tinyexr::SetErrorMessage("Insufficient header/data size.\n", err);
     return TINYEXR_ERROR_INVALID_DATA;
   }
 
@@ -11347,18 +11384,53 @@ int LoadEXRFromMemory(float **out_rgba, int *width, int *height,
       malloc(4 * sizeof(float) * static_cast<size_t>(exr_image.width) *
              static_cast<size_t>(exr_image.height)));
 
-  for (int i = 0; i < exr_image.width * exr_image.height; i++) {
-    (*out_rgba)[4 * i + 0] =
-        reinterpret_cast<float **>(exr_image.images)[idxR][i];
-    (*out_rgba)[4 * i + 1] =
-        reinterpret_cast<float **>(exr_image.images)[idxG][i];
-    (*out_rgba)[4 * i + 2] =
-        reinterpret_cast<float **>(exr_image.images)[idxB][i];
-    if (idxA != -1) {
-      (*out_rgba)[4 * i + 3] =
-          reinterpret_cast<float **>(exr_image.images)[idxA][i];
-    } else {
-      (*out_rgba)[4 * i + 3] = 1.0;
+  if (exr_header.tiled) {
+    for (int it = 0; it < exr_image.num_tiles; it++) {
+      for (int j = 0; j < exr_header.tile_size_y; j++)
+        for (int i = 0; i < exr_header.tile_size_x; i++) {
+          const int ii =
+              exr_image.tiles[it].offset_x * exr_header.tile_size_x + i;
+          const int jj =
+              exr_image.tiles[it].offset_y * exr_header.tile_size_y + j;
+          const int idx = ii + jj * exr_image.width;
+
+          // out of region check.
+          if (ii >= exr_image.width) {
+            continue;
+          }
+          if (jj >= exr_image.height) {
+            continue;
+          }
+          const int srcIdx = i + j * exr_header.tile_size_x;
+          unsigned char **src = exr_image.tiles[it].images;
+          (*out_rgba)[4 * idx + 0] =
+              reinterpret_cast<float **>(src)[idxR][srcIdx];
+          (*out_rgba)[4 * idx + 1] =
+              reinterpret_cast<float **>(src)[idxG][srcIdx];
+          (*out_rgba)[4 * idx + 2] =
+              reinterpret_cast<float **>(src)[idxB][srcIdx];
+          if (idxA != -1) {
+            (*out_rgba)[4 * idx + 3] =
+                reinterpret_cast<float **>(src)[idxA][srcIdx];
+          } else {
+            (*out_rgba)[4 * idx + 3] = 1.0;
+          }
+        }
+    }
+  } else {
+    for (int i = 0; i < exr_image.width * exr_image.height; i++) {
+      (*out_rgba)[4 * i + 0] =
+          reinterpret_cast<float **>(exr_image.images)[idxR][i];
+      (*out_rgba)[4 * i + 1] =
+          reinterpret_cast<float **>(exr_image.images)[idxG][i];
+      (*out_rgba)[4 * i + 2] =
+          reinterpret_cast<float **>(exr_image.images)[idxB][i];
+      if (idxA != -1) {
+        (*out_rgba)[4 * i + 3] =
+            reinterpret_cast<float **>(exr_image.images)[idxA][i];
+      } else {
+        (*out_rgba)[4 * i + 3] = 1.0;
+      }
     }
   }
 
@@ -11396,7 +11468,8 @@ int LoadEXRImageFromFile(EXRImage *exr_image, const EXRHeader *exr_header,
   fseek(fp, 0, SEEK_SET);
 
   if (filesize < 16) {
-    tinyexr::SetErrorMessage("File size too short " + std::string(filename), err);
+    tinyexr::SetErrorMessage("File size too short " + std::string(filename),
+                             err);
     return TINYEXR_ERROR_INVALID_FILE;
   }
 
@@ -11442,7 +11515,7 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image,
   if (exr_image == NULL || memory_out == NULL ||
       exr_header->compression_type < 0) {
     tinyexr::SetErrorMessage("Invalid argument for SaveEXRImageToMemory", err);
-    return 0;  // @fixme
+    return 0;
   }
 
 #if !TINYEXR_USE_PIZ
@@ -11613,8 +11686,6 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image,
           sizeof(
               tinyexr::tinyexr_int64);  // sizeof(header) + sizeof(offsetTable)
 
-  std::vector<unsigned char> data;
-
   std::vector<std::vector<unsigned char> > data_list(
       static_cast<size_t>(num_blocks));
   std::vector<size_t> channel_offset_list(
@@ -11853,9 +11924,9 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image,
     } else if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) {
 #if TINYEXR_USE_PIZ
       unsigned int bufLen =
-          1024 + static_cast<unsigned int>(
-                     1.2 * static_cast<unsigned int>(
-                               buf.size()));  // @fixme { compute good bound. }
+          8192 + static_cast<unsigned int>(
+                     2 * static_cast<unsigned int>(
+                             buf.size()));  // @fixme { compute good bound. }
       std::vector<unsigned char> block(bufLen);
       unsigned int outSize = static_cast<unsigned int>(block.size());
 
@@ -11914,13 +11985,12 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image,
   }  // omp parallel
 
   for (size_t i = 0; i < static_cast<size_t>(num_blocks); i++) {
-    data.insert(data.end(), data_list[i].begin(), data_list[i].end());
-
     offsets[i] = offset;
     tinyexr::swap8(reinterpret_cast<tinyexr::tinyexr_uint64 *>(&offsets[i]));
     offset += data_list[i].size();
   }
 
+  size_t totalSize = static_cast<size_t>(offset);
   {
     memory.insert(
         memory.end(), reinterpret_cast<unsigned char *>(&offsets.at(0)),
@@ -11928,14 +11998,21 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image,
             sizeof(tinyexr::tinyexr_uint64) * static_cast<size_t>(num_blocks));
   }
 
-  { memory.insert(memory.end(), data.begin(), data.end()); }
-
-  assert(memory.size() > 0);
+  if ( memory.size() == 0 ) {
+    tinyexr::SetErrorMessage("Output memory size is zero", err);
+    return 0;
+  }
 
-  (*memory_out) = static_cast<unsigned char *>(malloc(memory.size()));
+  (*memory_out) = static_cast<unsigned char *>(malloc(totalSize));
   memcpy((*memory_out), &memory.at(0), memory.size());
+  unsigned char *memory_ptr = *memory_out + memory.size();
+
+  for (size_t i = 0; i < static_cast<size_t>(num_blocks); i++) {
+    memcpy(memory_ptr, &data_list[i].at(0), data_list[i].size());
+    memory_ptr += data_list[i].size();
+  }
 
-  return memory.size();  // OK
+  return totalSize;  // OK
 }
 
 int SaveEXRImageToFile(const EXRImage *exr_image, const EXRHeader *exr_header,
@@ -11950,7 +12027,7 @@ int SaveEXRImageToFile(const EXRImage *exr_image, const EXRHeader *exr_header,
   if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) {
     tinyexr::SetErrorMessage("PIZ compression is not supported in this build",
                              err);
-    return 0;
+    return TINYEXR_ERROR_UNSUPPORTED_FEATURE;
   }
 #endif
 
@@ -11958,7 +12035,7 @@ int SaveEXRImageToFile(const EXRImage *exr_image, const EXRHeader *exr_header,
   if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) {
     tinyexr::SetErrorMessage("ZFP compression is not supported in this build",
                              err);
-    return 0;
+    return TINYEXR_ERROR_UNSUPPORTED_FEATURE;
   }
 #endif
 
@@ -11970,19 +12047,28 @@ int SaveEXRImageToFile(const EXRImage *exr_image, const EXRHeader *exr_header,
 #endif
   if (!fp) {
     tinyexr::SetErrorMessage("Cannot write a file", err);
-    return TINYEXR_ERROR_CANT_OPEN_FILE;
+    return TINYEXR_ERROR_CANT_WRITE_FILE;
   }
 
   unsigned char *mem = NULL;
   size_t mem_size = SaveEXRImageToMemory(exr_image, exr_header, &mem, err);
+  if (mem_size == 0) {
+    return TINYEXR_ERROR_SERIALZATION_FAILED;
+  }
 
+  size_t written_size = 0;
   if ((mem_size > 0) && mem) {
-    fwrite(mem, 1, mem_size, fp);
+    written_size = fwrite(mem, 1, mem_size, fp);
   }
   free(mem);
 
   fclose(fp);
 
+  if (written_size != mem_size) {
+    tinyexr::SetErrorMessage("Cannot write a file", err);
+    return TINYEXR_ERROR_CANT_WRITE_FILE;
+  }
+
   return TINYEXR_SUCCESS;
 }
 
@@ -12515,8 +12601,7 @@ int ParseEXRMultipartHeaderFromMemory(EXRHeader ***exr_headers,
   }
 
   if (size < tinyexr::kEXRVersionSize) {
-    tinyexr::SetErrorMessage(
-        "Data size too short", err);
+    tinyexr::SetErrorMessage("Data size too short", err);
     return TINYEXR_ERROR_INVALID_DATA;
   }
 
@@ -12852,20 +12937,27 @@ int LoadEXRMultipartImageFromFile(EXRImage *exr_images,
 }
 
 int SaveEXR(const float *data, int width, int height, int components,
-            const int save_as_fp16, const char *outfilename) {
+            const int save_as_fp16, const char *outfilename, const char **err) {
   if ((components == 1) || components == 3 || components == 4) {
     // OK
   } else {
+    std::stringstream ss;
+    ss << "Unsupported component value : " << components << std::endl;
+
+    tinyexr::SetErrorMessage(ss.str(), err);
     return TINYEXR_ERROR_INVALID_ARGUMENT;
   }
 
-  // Assume at least 16x16 pixels.
-  if (width < 16) return TINYEXR_ERROR_INVALID_ARGUMENT;
-  if (height < 16) return TINYEXR_ERROR_INVALID_ARGUMENT;
-
   EXRHeader header;
   InitEXRHeader(&header);
 
+  if ((width < 16) && (height < 16)) {
+    // No compression for small image.
+    header.compression_type = TINYEXR_COMPRESSIONTYPE_NONE;
+  } else {
+    header.compression_type = TINYEXR_COMPRESSIONTYPE_ZIP;
+  }
+
   EXRImage image;
   InitEXRImage(&image);
 
@@ -12971,8 +13063,7 @@ int SaveEXR(const float *data, int width, int height, int components,
     }
   }
 
-  const char *err;
-  int ret = SaveEXRImageToFile(&image, &header, outfilename, &err);
+  int ret = SaveEXRImageToFile(&image, &header, outfilename, err);
   if (ret != TINYEXR_SUCCESS) {
     return ret;
   }