1
2//
3// This source file is part of appleseed.
4// Visit http://appleseedhq.net/ for additional information and resources.
5//
6// This software is released under the MIT license.
7//
8// Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
9// Copyright (c) 2014-2017 Francois Beaune, The appleseedhq Organization
10//
11// Permission is hereby granted, free of charge, to any person obtaining a copy
12// of this software and associated documentation files (the "Software"), to deal
13// in the Software without restriction, including without limitation the rights
14// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15// copies of the Software, and to permit persons to whom the Software is
16// furnished to do so, subject to the following conditions:
17//
18// The above copyright notice and this permission notice shall be included in
19// all copies or substantial portions of the Software.
20//
21// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27// THE SOFTWARE.
28//
29
30// Interface header.
31#include "exrimagefilewriter.h"
32
33// appleseed.foundation headers.
34#include "foundation/core/exceptions/exceptionioerror.h"
35#include "foundation/image/canvasproperties.h"
36#include "foundation/image/exceptionunsupportedimageformat.h"
37#include "foundation/image/exrutils.h"
38#include "foundation/image/icanvas.h"
39#include "foundation/image/pixel.h"
40#include "foundation/image/tile.h"
41
42// OpenEXR headers.
43#include "foundation/platform/exrheaderguards.h"
44BEGIN_EXR_INCLUDES
45#include "OpenEXR/IexBaseExc.h"
46#include "OpenEXR/ImathBox.h"
47#include "OpenEXR/ImathVec.h"
48#include "OpenEXR/ImfChannelList.h"
49#include "OpenEXR/ImfFrameBuffer.h"
50#include "OpenEXR/ImfHeader.h"
51#include "OpenEXR/ImfPixelType.h"
52#include "OpenEXR/ImfTileDescription.h"
53#include "OpenEXR/ImfTiledOutputFile.h"
54END_EXR_INCLUDES
55
56// Standard headers.
57#include <cassert>
58#include <cstddef>
59
60using namespace Iex;
61using namespace Imath;
62using namespace Imf;
63using namespace std;
64
65namespace foundation
66{
67
68//
69// EXRImageFileWriter class implementation.
70//
71
72namespace
73{
74 const char* ChannelName[] = { "R", "G", "B", "A" };
75}
76
77void EXRImageFileWriter::write(
78 const char* filename,
79 const ICanvas& image,
80 const ImageAttributes& image_attributes)
81{
82 initialize_openexr();
83
84 try
85 {
86 // Retrieve canvas properties.
87 const CanvasProperties& props = image.properties();
88
89 // todo: lift this limitation.
90 assert(props.m_channel_count <= 4);
91
92 // Figure out the pixel type, based on the pixel format of the image.
93 PixelType pixel_type = FLOAT;
94 switch (props.m_pixel_format)
95 {
96 case PixelFormatUInt32: pixel_type = UINT; break;
97 case PixelFormatHalf: pixel_type = HALF; break;
98 case PixelFormatFloat: pixel_type = FLOAT; break;
99 default: throw ExceptionUnsupportedImageFormat();
100 }
101
102 // Construct TileDescription object.
103 const TileDescription tile_desc(
104 static_cast<unsigned int>(props.m_tile_width),
105 static_cast<unsigned int>(props.m_tile_height),
106 ONE_LEVEL);
107
108 // Construct ChannelList object.
109 ChannelList channels;
110 for (size_t c = 0; c < props.m_channel_count; ++c)
111 channels.insert(ChannelName[c], Channel(pixel_type));
112
113 // Construct Header object.
114 Header header(
115 static_cast<int>(props.m_canvas_width),
116 static_cast<int>(props.m_canvas_height));
117 header.setTileDescription(tile_desc);
118 header.channels() = channels;
119
120 // Add image attributes to the Header object.
121 add_attributes(image_attributes, header);
122
123 // Create the output file.
124 TiledOutputFile file(filename, header);
125
126 // Write tiles.
127 for (size_t y = 0; y < props.m_tile_count_y; ++y)
128 {
129 for (size_t x = 0; x < props.m_tile_count_x; ++x)
130 {
131 const int ix = static_cast<int>(x);
132 const int iy = static_cast<int>(y);
133 const Box2i range = file.dataWindowForTile(ix, iy);
134 const Tile& tile = image.tile(x, y);
135 const size_t channel_size = Pixel::size(tile.get_pixel_format());
136 const size_t stride_x = channel_size * props.m_channel_count;
137 const size_t stride_y = stride_x * tile.get_width();
138 const size_t tile_origin = range.min.x * stride_x + range.min.y * stride_y;
139 const char* tile_base = reinterpret_cast<const char*>(tile.pixel(0, 0)) - tile_origin;
140
141 // Construct FrameBuffer object.
142 FrameBuffer framebuffer;
143 for (size_t c = 0; c < props.m_channel_count; ++c)
144 {
145 const char* base = tile_base + c * channel_size;
146 framebuffer.insert(
147 ChannelName[c],
148 Slice(
149 pixel_type,
150 const_cast<char*>(base),
151 stride_x,
152 stride_y));
153 }
154
155 // Write tile.
156 file.setFrameBuffer(framebuffer);
157 file.writeTile(ix, iy);
158 }
159 }
160 }
161 catch (const BaseExc& e)
162 {
163 // I/O error.
164 throw ExceptionIOError(e.what());
165 }
166}
167
168} // namespace foundation
169