shun-iwasawa 7bad49
#ifndef TINYEXR_OTMOD_H_
shun-iwasawa 7bad49
#define TINYEXR_OTMOD_H_
shun-iwasawa 7bad49
shun-iwasawa 7bad49
#define TINYEXR_IMPLEMENTATION
shun-iwasawa 7bad49
#include "tinyexr.h"
shun-iwasawa 7bad49
shun-iwasawa 7bad49
/*
shun-iwasawa 7bad49
 * This source is based on TinyEXR code, enabling to use file handle
shun-iwasawa 7bad49
 * as an argument instead of file path in order to fit usage in OpenToonz.
shun-iwasawa 7bad49
 * TinyEXR code is licensed under the following:
shun-iwasawa 7bad49
 */
shun-iwasawa 7bad49
shun-iwasawa 7bad49
// Start of TinyEXR license -------------------------------------------------
shun-iwasawa 7bad49
/*
shun-iwasawa 7bad49
Copyright (c) 2014 - 2021, Syoyo Fujita and many contributors.
shun-iwasawa 7bad49
All rights reserved.
shun-iwasawa 7bad49
shun-iwasawa 7bad49
Redistribution and use in source and binary forms, with or without
shun-iwasawa 7bad49
modification, are permitted provided that the following conditions are met:
shun-iwasawa 7bad49
    * Redistributions of source code must retain the above copyright
shun-iwasawa 7bad49
      notice, this list of conditions and the following disclaimer.
shun-iwasawa 7bad49
    * Redistributions in binary form must reproduce the above copyright
shun-iwasawa 7bad49
      notice, this list of conditions and the following disclaimer in the
shun-iwasawa 7bad49
      documentation and/or other materials provided with the distribution.
shun-iwasawa 7bad49
    * Neither the name of the Syoyo Fujita nor the
shun-iwasawa 7bad49
      names of its contributors may be used to endorse or promote products
shun-iwasawa 7bad49
      derived from this software without specific prior written permission.
shun-iwasawa 7bad49
shun-iwasawa 7bad49
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
shun-iwasawa 7bad49
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
shun-iwasawa 7bad49
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
shun-iwasawa 7bad49
DISCLAIMED. IN NO EVENT SHALL <copyright holder=""> BE LIABLE FOR ANY</copyright>
shun-iwasawa 7bad49
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
shun-iwasawa 7bad49
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
shun-iwasawa 7bad49
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
shun-iwasawa 7bad49
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
shun-iwasawa 7bad49
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
shun-iwasawa 7bad49
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
shun-iwasawa 7bad49
*/
shun-iwasawa 7bad49
// End of TinyEXR license -------------------------------------------------
shun-iwasawa 7bad49
// TinyEXR contains some OpenEXR code, which is licensed under ------------
shun-iwasawa 7bad49
shun-iwasawa 7bad49
///////////////////////////////////////////////////////////////////////////
shun-iwasawa 7bad49
//
shun-iwasawa 7bad49
// Copyright (c) 2002, Industrial Light & Magic, a division of Lucas
shun-iwasawa 7bad49
// Digital Ltd. LLC
shun-iwasawa 7bad49
//
shun-iwasawa 7bad49
// All rights reserved.
shun-iwasawa 7bad49
//
shun-iwasawa 7bad49
// Redistribution and use in source and binary forms, with or without
shun-iwasawa 7bad49
// modification, are permitted provided that the following conditions are
shun-iwasawa 7bad49
// met:
shun-iwasawa 7bad49
// *       Redistributions of source code must retain the above copyright
shun-iwasawa 7bad49
// notice, this list of conditions and the following disclaimer.
shun-iwasawa 7bad49
// *       Redistributions in binary form must reproduce the above
shun-iwasawa 7bad49
// copyright notice, this list of conditions and the following disclaimer
shun-iwasawa 7bad49
// in the documentation and/or other materials provided with the
shun-iwasawa 7bad49
// distribution.
shun-iwasawa 7bad49
// *       Neither the name of Industrial Light & Magic nor the names of
shun-iwasawa 7bad49
// its contributors may be used to endorse or promote products derived
shun-iwasawa 7bad49
// from this software without specific prior written permission.
shun-iwasawa 7bad49
//
shun-iwasawa 7bad49
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
shun-iwasawa 7bad49
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
shun-iwasawa 7bad49
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
shun-iwasawa 7bad49
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
shun-iwasawa 7bad49
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
shun-iwasawa 7bad49
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
shun-iwasawa 7bad49
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
shun-iwasawa 7bad49
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
shun-iwasawa 7bad49
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
shun-iwasawa 7bad49
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
shun-iwasawa 7bad49
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
shun-iwasawa 7bad49
//
shun-iwasawa 7bad49
///////////////////////////////////////////////////////////////////////////
shun-iwasawa 7bad49
shun-iwasawa 7bad49
// End of OpenEXR license -------------------------------------------------
shun-iwasawa 7bad49
shun-iwasawa 7bad49
#ifdef __cplusplus
shun-iwasawa 7bad49
extern "C" {
shun-iwasawa 7bad49
#endif
shun-iwasawa 7bad49
shun-iwasawa 7bad49
extern int ParseEXRVersionFromFileHandle(EXRVersion *version, FILE *fp);
shun-iwasawa 7bad49
shun-iwasawa 7bad49
extern int ParseEXRHeaderFromFileHandle(EXRHeader *exr_header,
shun-iwasawa 7bad49
                                        const EXRVersion *exr_version, FILE *fp,
shun-iwasawa 7bad49
                                        const char **err);
shun-iwasawa 7bad49
shun-iwasawa 7bad49
extern int LoadEXRImageFromFileHandle(EXRImage *exr_image,
shun-iwasawa 7bad49
                                      const EXRHeader *exr_header, FILE *fp,
shun-iwasawa 7bad49
                                      const char **err);
shun-iwasawa 7bad49
shun-iwasawa 7bad49
extern int LoadEXRHeaderFromFileHandle(EXRHeader &exr_header, FILE *file,
shun-iwasawa 7bad49
                                       const char **err);
shun-iwasawa 7bad49
shun-iwasawa 7bad49
extern int LoadEXRImageBufFromFileHandle(float **out_rgba,
shun-iwasawa 7bad49
                                         EXRHeader &exr_header, FILE *file,
shun-iwasawa 7bad49
                                         const char **err);
shun-iwasawa 7bad49
shun-iwasawa 7bad49
extern int SaveEXRImageToFileHandle(const EXRImage *exr_image,
shun-iwasawa 7bad49
                                    const EXRHeader *exr_header, FILE *fp,
shun-iwasawa 7bad49
                                    const char **err);
shun-iwasawa 7bad49
shun-iwasawa 7bad49
#ifdef __cplusplus
shun-iwasawa 7bad49
}
shun-iwasawa 7bad49
#endif
shun-iwasawa 7bad49
shun-iwasawa 7bad49
#endif  // TINYEXR_OTMOD_H_
shun-iwasawa 7bad49
shun-iwasawa 7bad49
#ifdef TINYEXR_OTMOD_IMPLEMENTATION
shun-iwasawa 7bad49
#ifndef TINYEXR_OTMOD_IMPLEMENTATION_DEFINED
shun-iwasawa 7bad49
#define TINYEXR_OTMOD_IMPLEMENTATION_DEFINED
shun-iwasawa 7bad49
shun-iwasawa 7bad49
int ParseEXRVersionFromFileHandle(EXRVersion *version, FILE *fp) {
shun-iwasawa 7bad49
  if (!fp) {
shun-iwasawa 7bad49
    return TINYEXR_ERROR_CANT_OPEN_FILE;
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  size_t file_size;
shun-iwasawa 7bad49
  // Compute size
shun-iwasawa 7bad49
  fseek(fp, 0, SEEK_END);
shun-iwasawa 7bad49
  file_size = static_cast<size_t>(ftell(fp));</size_t>
shun-iwasawa 7bad49
  fseek(fp, 0, SEEK_SET);
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  if (file_size < tinyexr::kEXRVersionSize) {
shun-iwasawa 7bad49
    return TINYEXR_ERROR_INVALID_FILE;
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  unsigned char buf[tinyexr::kEXRVersionSize];
shun-iwasawa 7bad49
  size_t ret = fread(&buf[0], 1, tinyexr::kEXRVersionSize, fp);
shun-iwasawa 7bad49
  // fclose(fp);
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  if (ret != tinyexr::kEXRVersionSize) {
shun-iwasawa 7bad49
    return TINYEXR_ERROR_INVALID_FILE;
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  return ParseEXRVersionFromMemory(version, buf, tinyexr::kEXRVersionSize);
shun-iwasawa 7bad49
}
shun-iwasawa 7bad49
shun-iwasawa 7bad49
int ParseEXRHeaderFromFileHandle(EXRHeader *exr_header,
shun-iwasawa 7bad49
                                 const EXRVersion *exr_version, FILE *fp,
shun-iwasawa 7bad49
                                 const char **err) {
shun-iwasawa 7bad49
  if (exr_header == NULL || exr_version == NULL) {
shun-iwasawa 7bad49
    tinyexr::SetErrorMessage("Invalid argument for ParseEXRHeaderFromFile",
shun-iwasawa 7bad49
                             err);
shun-iwasawa 7bad49
    return TINYEXR_ERROR_INVALID_ARGUMENT;
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
  if (!fp) {
shun-iwasawa 7bad49
    tinyexr::SetErrorMessage("Cannot read file ", err);
shun-iwasawa 7bad49
    return TINYEXR_ERROR_CANT_OPEN_FILE;
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  size_t filesize;
shun-iwasawa 7bad49
  // Compute size
shun-iwasawa 7bad49
  fseek(fp, 0, SEEK_END);
shun-iwasawa 7bad49
  filesize = static_cast<size_t>(ftell(fp));</size_t>
shun-iwasawa 7bad49
  fseek(fp, 0, SEEK_SET);
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  std::vector<unsigned char=""> buf(filesize);  // @todo { use mmap }</unsigned>
shun-iwasawa 7bad49
  {
shun-iwasawa 7bad49
    size_t ret;
shun-iwasawa 7bad49
    ret = fread(&buf[0], 1, filesize, fp);
shun-iwasawa 7bad49
    assert(ret == filesize);
shun-iwasawa 7bad49
    // fclose(fp);
shun-iwasawa 7bad49
shun-iwasawa 7bad49
    if (ret != filesize) {
shun-iwasawa 7bad49
      tinyexr::SetErrorMessage("fread() error", err);
shun-iwasawa 7bad49
      return TINYEXR_ERROR_INVALID_FILE;
shun-iwasawa 7bad49
    }
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  return ParseEXRHeaderFromMemory(exr_header, exr_version, &buf.at(0), filesize,
shun-iwasawa 7bad49
                                  err);
shun-iwasawa 7bad49
}
shun-iwasawa 7bad49
shun-iwasawa 7bad49
int LoadEXRImageFromFileHandle(EXRImage *exr_image, const EXRHeader *exr_header,
shun-iwasawa 7bad49
                               FILE *fp, const char **err) {
shun-iwasawa 7bad49
  if (exr_image == NULL) {
shun-iwasawa 7bad49
    tinyexr::SetErrorMessage("Invalid argument for LoadEXRImageFromFile", err);
shun-iwasawa 7bad49
    return TINYEXR_ERROR_INVALID_ARGUMENT;
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  if (!fp) {
shun-iwasawa 7bad49
    tinyexr::SetErrorMessage("Cannot read file", err);
shun-iwasawa 7bad49
    return TINYEXR_ERROR_CANT_OPEN_FILE;
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  size_t filesize;
shun-iwasawa 7bad49
  // Compute size
shun-iwasawa 7bad49
  fseek(fp, 0, SEEK_END);
shun-iwasawa 7bad49
  filesize = static_cast<size_t>(ftell(fp));</size_t>
shun-iwasawa 7bad49
  fseek(fp, 0, SEEK_SET);
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  if (filesize < 16) {
shun-iwasawa 7bad49
    tinyexr::SetErrorMessage("File size too short", err);
shun-iwasawa 7bad49
    return TINYEXR_ERROR_INVALID_FILE;
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  std::vector<unsigned char=""> buf(filesize);  // @todo { use mmap }</unsigned>
shun-iwasawa 7bad49
  {
shun-iwasawa 7bad49
    size_t ret;
shun-iwasawa 7bad49
    ret = fread(&buf[0], 1, filesize, fp);
shun-iwasawa 7bad49
    assert(ret == filesize);
shun-iwasawa 7bad49
    // fclose(fp);
shun-iwasawa 7bad49
    (void)ret;
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  return LoadEXRImageFromMemory(exr_image, exr_header, &buf.at(0), filesize,
shun-iwasawa 7bad49
                                err);
shun-iwasawa 7bad49
}
shun-iwasawa 7bad49
shun-iwasawa 7bad49
int LoadEXRHeaderFromFileHandle(EXRHeader &exr_header, FILE *file,
shun-iwasawa 7bad49
                                const char **err) {
shun-iwasawa 7bad49
  EXRVersion exr_version;
shun-iwasawa 7bad49
  InitEXRHeader(&exr_header);
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  {
shun-iwasawa 7bad49
    FILE *_fp = file;
shun-iwasawa 7bad49
    int ret   = ParseEXRVersionFromFileHandle(&exr_version, _fp);
shun-iwasawa 7bad49
    if (ret != TINYEXR_SUCCESS) {
shun-iwasawa 7bad49
      std::stringstream ss;
shun-iwasawa 7bad49
      ss << "Failed to open EXR file or read version info from EXR file. code("
shun-iwasawa 7bad49
         << ret << ")";
shun-iwasawa 7bad49
      tinyexr::SetErrorMessage(ss.str(), err);
shun-iwasawa 7bad49
      return ret;
shun-iwasawa 7bad49
    }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
    if (exr_version.multipart || exr_version.non_image) {
shun-iwasawa 7bad49
      tinyexr::SetErrorMessage(
shun-iwasawa 7bad49
          "Loading multipart or DeepImage is not supported  in LoadEXR() API",
shun-iwasawa 7bad49
          err);
shun-iwasawa 7bad49
      return TINYEXR_ERROR_INVALID_DATA;  // @fixme.
shun-iwasawa 7bad49
    }
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  {
shun-iwasawa 7bad49
    FILE *_fp = file;
shun-iwasawa 7bad49
    int ret = ParseEXRHeaderFromFileHandle(&exr_header, &exr_version, _fp, err);
shun-iwasawa 7bad49
    if (ret != TINYEXR_SUCCESS) {
shun-iwasawa 7bad49
      FreeEXRHeader(&exr_header);
shun-iwasawa 7bad49
      return ret;
shun-iwasawa 7bad49
    }
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
  return TINYEXR_SUCCESS;
shun-iwasawa 7bad49
}
shun-iwasawa 7bad49
shun-iwasawa 7bad49
int LoadEXRImageBufFromFileHandle(float **out_rgba, EXRHeader &exr_header,
shun-iwasawa 7bad49
                                  FILE *file, const char **err) {
shun-iwasawa 7bad49
  if (out_rgba == NULL) {
shun-iwasawa 7bad49
    tinyexr::SetErrorMessage("Invalid argument for LoadEXR()", err);
shun-iwasawa 7bad49
    return TINYEXR_ERROR_INVALID_ARGUMENT;
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  EXRImage exr_image;
shun-iwasawa 7bad49
  InitEXRImage(&exr_image);
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  // Read HALF channel as FLOAT.
shun-iwasawa 7bad49
  for (int i = 0; i < exr_header.num_channels; i++) {
shun-iwasawa 7bad49
    if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) {
shun-iwasawa 7bad49
      exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
shun-iwasawa 7bad49
    }
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  // TODO: Probably limit loading to layers (channels) selected by layer index
shun-iwasawa 7bad49
  {
shun-iwasawa 7bad49
    FILE *_fp = file;
shun-iwasawa 7bad49
    int ret   = LoadEXRImageFromFileHandle(&exr_image, &exr_header, _fp, err);
shun-iwasawa 7bad49
    if (ret != TINYEXR_SUCCESS) {
shun-iwasawa 7bad49
      FreeEXRHeader(&exr_header);
shun-iwasawa 7bad49
      return ret;
shun-iwasawa 7bad49
    }
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  // RGBA
shun-iwasawa 7bad49
  int idxR = -1;
shun-iwasawa 7bad49
  int idxG = -1;
shun-iwasawa 7bad49
  int idxB = -1;
shun-iwasawa 7bad49
  int idxA = -1;
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  std::vector<std::string> layer_names;</std::string>
shun-iwasawa 7bad49
  tinyexr::GetLayers(exr_header, layer_names);
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  std::vector<tinyexr::layerchannel> channels;</tinyexr::layerchannel>
shun-iwasawa 7bad49
  tinyexr::ChannelsInLayer(exr_header, "", channels);
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  if (channels.size() < 1) {
shun-iwasawa 7bad49
    tinyexr::SetErrorMessage("Layer Not Found", err);
shun-iwasawa 7bad49
    FreeEXRHeader(&exr_header);
shun-iwasawa 7bad49
    FreeEXRImage(&exr_image);
shun-iwasawa 7bad49
    return TINYEXR_ERROR_LAYER_NOT_FOUND;
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  size_t ch_count = channels.size() < 4 ? channels.size() : 4;
shun-iwasawa 7bad49
  for (size_t c = 0; c < ch_count; c++) {
shun-iwasawa 7bad49
    const tinyexr::LayerChannel &ch = channels[c];
shun-iwasawa 7bad49
shun-iwasawa 7bad49
    if (ch.name == "R") {
shun-iwasawa 7bad49
      idxR = int(ch.index);
shun-iwasawa 7bad49
    } else if (ch.name == "G") {
shun-iwasawa 7bad49
      idxG = int(ch.index);
shun-iwasawa 7bad49
    } else if (ch.name == "B") {
shun-iwasawa 7bad49
      idxB = int(ch.index);
shun-iwasawa 7bad49
    } else if (ch.name == "A") {
shun-iwasawa 7bad49
      idxA = int(ch.index);
shun-iwasawa 7bad49
    }
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  if (channels.size() == 1) {
shun-iwasawa 7bad49
    int chIdx = int(channels.front().index);
shun-iwasawa 7bad49
    // Grayscale channel only.
shun-iwasawa 7bad49
shun-iwasawa 7bad49
    (*out_rgba) = reinterpret_cast<float *="">(</float>
shun-iwasawa 7bad49
        malloc(4 * sizeof(float) * static_cast<size_t>(exr_image.width) *</size_t>
shun-iwasawa 7bad49
               static_cast<size_t>(exr_image.height)));</size_t>
shun-iwasawa 7bad49
shun-iwasawa 7bad49
    if (exr_header.tiled) {
shun-iwasawa 7bad49
      for (int it = 0; it < exr_image.num_tiles; it++) {
shun-iwasawa 7bad49
        for (int j = 0; j < exr_header.tile_size_y; j++) {
shun-iwasawa 7bad49
          for (int i = 0; i < exr_header.tile_size_x; i++) {
shun-iwasawa 7bad49
            const int ii = exr_image.tiles[it].offset_x *
shun-iwasawa 7bad49
                               static_cast<int>(exr_header.tile_size_x) +</int>
shun-iwasawa 7bad49
                           i;
shun-iwasawa 7bad49
            const int jj = exr_image.tiles[it].offset_y *
shun-iwasawa 7bad49
                               static_cast<int>(exr_header.tile_size_y) +</int>
shun-iwasawa 7bad49
                           j;
shun-iwasawa 7bad49
            const int idx = ii + jj * static_cast<int>(exr_image.width);</int>
shun-iwasawa 7bad49
shun-iwasawa 7bad49
            // out of region check.
shun-iwasawa 7bad49
            if (ii >= exr_image.width) {
shun-iwasawa 7bad49
              continue;
shun-iwasawa 7bad49
            }
shun-iwasawa 7bad49
            if (jj >= exr_image.height) {
shun-iwasawa 7bad49
              continue;
shun-iwasawa 7bad49
            }
shun-iwasawa 7bad49
            const int srcIdx    = i + j * exr_header.tile_size_x;
shun-iwasawa 7bad49
            unsigned char **src = exr_image.tiles[it].images;
shun-iwasawa 7bad49
            (*out_rgba)[4 * idx + 0] =
shun-iwasawa 7bad49
                reinterpret_cast<float **="">(src)[chIdx][srcIdx];</float>
shun-iwasawa 7bad49
            (*out_rgba)[4 * idx + 1] =
shun-iwasawa 7bad49
                reinterpret_cast<float **="">(src)[chIdx][srcIdx];</float>
shun-iwasawa 7bad49
            (*out_rgba)[4 * idx + 2] =
shun-iwasawa 7bad49
                reinterpret_cast<float **="">(src)[chIdx][srcIdx];</float>
shun-iwasawa 7bad49
            (*out_rgba)[4 * idx + 3] =
shun-iwasawa 7bad49
                reinterpret_cast<float **="">(src)[chIdx][srcIdx];</float>
shun-iwasawa 7bad49
          }
shun-iwasawa 7bad49
        }
shun-iwasawa 7bad49
      }
shun-iwasawa 7bad49
    } else {
shun-iwasawa 7bad49
      for (int i = 0; i < exr_image.width * exr_image.height; i++) {
shun-iwasawa 7bad49
        const float val =
shun-iwasawa 7bad49
            reinterpret_cast<float **="">(exr_image.images)[chIdx][i];</float>
shun-iwasawa 7bad49
        (*out_rgba)[4 * i + 0] = val;
shun-iwasawa 7bad49
        (*out_rgba)[4 * i + 1] = val;
shun-iwasawa 7bad49
        (*out_rgba)[4 * i + 2] = val;
shun-iwasawa 7bad49
        (*out_rgba)[4 * i + 3] = val;
shun-iwasawa 7bad49
      }
shun-iwasawa 7bad49
    }
shun-iwasawa 7bad49
  } else {
shun-iwasawa 7bad49
    // Assume RGB(A)
shun-iwasawa 7bad49
shun-iwasawa 7bad49
    if (idxR == -1) {
shun-iwasawa 7bad49
      tinyexr::SetErrorMessage("R channel not found", err);
shun-iwasawa 7bad49
shun-iwasawa 7bad49
      FreeEXRHeader(&exr_header);
shun-iwasawa 7bad49
      FreeEXRImage(&exr_image);
shun-iwasawa 7bad49
      return TINYEXR_ERROR_INVALID_DATA;
shun-iwasawa 7bad49
    }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
    if (idxG == -1) {
shun-iwasawa 7bad49
      tinyexr::SetErrorMessage("G channel not found", err);
shun-iwasawa 7bad49
      FreeEXRHeader(&exr_header);
shun-iwasawa 7bad49
      FreeEXRImage(&exr_image);
shun-iwasawa 7bad49
      return TINYEXR_ERROR_INVALID_DATA;
shun-iwasawa 7bad49
    }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
    if (idxB == -1) {
shun-iwasawa 7bad49
      tinyexr::SetErrorMessage("B channel not found", err);
shun-iwasawa 7bad49
      FreeEXRHeader(&exr_header);
shun-iwasawa 7bad49
      FreeEXRImage(&exr_image);
shun-iwasawa 7bad49
      return TINYEXR_ERROR_INVALID_DATA;
shun-iwasawa 7bad49
    }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
    (*out_rgba) = reinterpret_cast<float *="">(</float>
shun-iwasawa 7bad49
        malloc(4 * sizeof(float) * static_cast<size_t>(exr_image.width) *</size_t>
shun-iwasawa 7bad49
               static_cast<size_t>(exr_image.height)));</size_t>
shun-iwasawa 7bad49
    if (exr_header.tiled) {
shun-iwasawa 7bad49
      for (int it = 0; it < exr_image.num_tiles; it++) {
shun-iwasawa 7bad49
        for (int j = 0; j < exr_header.tile_size_y; j++) {
shun-iwasawa 7bad49
          for (int i = 0; i < exr_header.tile_size_x; i++) {
shun-iwasawa 7bad49
            const int ii =
shun-iwasawa 7bad49
                exr_image.tiles[it].offset_x * exr_header.tile_size_x + i;
shun-iwasawa 7bad49
            const int jj =
shun-iwasawa 7bad49
                exr_image.tiles[it].offset_y * exr_header.tile_size_y + j;
shun-iwasawa 7bad49
            const int idx = ii + jj * exr_image.width;
shun-iwasawa 7bad49
shun-iwasawa 7bad49
            // out of region check.
shun-iwasawa 7bad49
            if (ii >= exr_image.width) {
shun-iwasawa 7bad49
              continue;
shun-iwasawa 7bad49
            }
shun-iwasawa 7bad49
            if (jj >= exr_image.height) {
shun-iwasawa 7bad49
              continue;
shun-iwasawa 7bad49
            }
shun-iwasawa 7bad49
            const int srcIdx    = i + j * exr_header.tile_size_x;
shun-iwasawa 7bad49
            unsigned char **src = exr_image.tiles[it].images;
shun-iwasawa 7bad49
            (*out_rgba)[4 * idx + 0] =
shun-iwasawa 7bad49
                reinterpret_cast<float **="">(src)[idxR][srcIdx];</float>
shun-iwasawa 7bad49
            (*out_rgba)[4 * idx + 1] =
shun-iwasawa 7bad49
                reinterpret_cast<float **="">(src)[idxG][srcIdx];</float>
shun-iwasawa 7bad49
            (*out_rgba)[4 * idx + 2] =
shun-iwasawa 7bad49
                reinterpret_cast<float **="">(src)[idxB][srcIdx];</float>
shun-iwasawa 7bad49
            if (idxA != -1) {
shun-iwasawa 7bad49
              (*out_rgba)[4 * idx + 3] =
shun-iwasawa 7bad49
                  reinterpret_cast<float **="">(src)[idxA][srcIdx];</float>
shun-iwasawa 7bad49
            } else {
shun-iwasawa 7bad49
              (*out_rgba)[4 * idx + 3] = 1.0;
shun-iwasawa 7bad49
            }
shun-iwasawa 7bad49
          }
shun-iwasawa 7bad49
        }
shun-iwasawa 7bad49
      }
shun-iwasawa 7bad49
    } else {
shun-iwasawa 7bad49
      for (int i = 0; i < exr_image.width * exr_image.height; i++) {
shun-iwasawa 7bad49
        (*out_rgba)[4 * i + 0] =
shun-iwasawa 7bad49
            reinterpret_cast<float **="">(exr_image.images)[idxR][i];</float>
shun-iwasawa 7bad49
        (*out_rgba)[4 * i + 1] =
shun-iwasawa 7bad49
            reinterpret_cast<float **="">(exr_image.images)[idxG][i];</float>
shun-iwasawa 7bad49
        (*out_rgba)[4 * i + 2] =
shun-iwasawa 7bad49
            reinterpret_cast<float **="">(exr_image.images)[idxB][i];</float>
shun-iwasawa 7bad49
        if (idxA != -1) {
shun-iwasawa 7bad49
          (*out_rgba)[4 * i + 3] =
shun-iwasawa 7bad49
              reinterpret_cast<float **="">(exr_image.images)[idxA][i];</float>
shun-iwasawa 7bad49
        } else {
shun-iwasawa 7bad49
          (*out_rgba)[4 * i + 3] = 1.0;
shun-iwasawa 7bad49
        }
shun-iwasawa 7bad49
      }
shun-iwasawa 7bad49
    }
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  FreeEXRHeader(&exr_header);
shun-iwasawa 7bad49
  FreeEXRImage(&exr_image);
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  return TINYEXR_SUCCESS;
shun-iwasawa 7bad49
}
shun-iwasawa 7bad49
shun-iwasawa 7bad49
int SaveEXRImageToFileHandle(const EXRImage *exr_image,
shun-iwasawa 7bad49
                             const EXRHeader *exr_header, FILE *fp,
shun-iwasawa 7bad49
                             const char **err) {
shun-iwasawa 7bad49
  if (exr_image == NULL || exr_header->compression_type < 0) {
shun-iwasawa 7bad49
    tinyexr::SetErrorMessage("Invalid argument for SaveEXRImageToFile", err);
shun-iwasawa 7bad49
    return TINYEXR_ERROR_INVALID_ARGUMENT;
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
#if !TINYEXR_USE_PIZ
shun-iwasawa 7bad49
  if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) {
shun-iwasawa 7bad49
    tinyexr::SetErrorMessage("PIZ compression is not supported in this build",
shun-iwasawa 7bad49
                             err);
shun-iwasawa 7bad49
    return TINYEXR_ERROR_UNSUPPORTED_FEATURE;
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
#endif
shun-iwasawa 7bad49
shun-iwasawa 7bad49
#if !TINYEXR_USE_ZFP
shun-iwasawa 7bad49
  if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) {
shun-iwasawa 7bad49
    tinyexr::SetErrorMessage("ZFP compression is not supported in this build",
shun-iwasawa 7bad49
                             err);
shun-iwasawa 7bad49
    return TINYEXR_ERROR_UNSUPPORTED_FEATURE;
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
#endif
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  if (!fp) {
shun-iwasawa 7bad49
    tinyexr::SetErrorMessage("Cannot write a file", err);
shun-iwasawa 7bad49
    return TINYEXR_ERROR_CANT_WRITE_FILE;
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  unsigned char *mem = NULL;
shun-iwasawa 7bad49
  size_t mem_size    = SaveEXRImageToMemory(exr_image, exr_header, &mem, err);
shun-iwasawa 7bad49
  if (mem_size == 0) {
shun-iwasawa 7bad49
    return TINYEXR_ERROR_SERIALZATION_FAILED;
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  size_t written_size = 0;
shun-iwasawa 7bad49
  if ((mem_size > 0) && mem) {
shun-iwasawa 7bad49
    written_size = fwrite(mem, 1, mem_size, fp);
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
  free(mem);
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  // fclose(fp);
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  if (written_size != mem_size) {
shun-iwasawa 7bad49
    tinyexr::SetErrorMessage("Cannot write a file", err);
shun-iwasawa 7bad49
    return TINYEXR_ERROR_CANT_WRITE_FILE;
shun-iwasawa 7bad49
  }
shun-iwasawa 7bad49
shun-iwasawa 7bad49
  return TINYEXR_SUCCESS;
shun-iwasawa 7bad49
}
shun-iwasawa 7bad49
shun-iwasawa 7bad49
#endif  // TINYEXR_OTMOD_IMPLEMENTATION_DEFINED
shun-iwasawa 7bad49
#endif  // TINYEXR_OTMOD_IMPLEMENTATION