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" |
44 | BEGIN_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" |
54 | END_EXR_INCLUDES |
55 | |
56 | // Standard headers. |
57 | #include <cassert> |
58 | #include <cstddef> |
59 | |
60 | using namespace Iex; |
61 | using namespace Imath; |
62 | using namespace Imf; |
63 | using namespace std; |
64 | |
65 | namespace foundation |
66 | { |
67 | |
68 | // |
69 | // EXRImageFileWriter class implementation. |
70 | // |
71 | |
72 | namespace |
73 | { |
74 | const char* ChannelName[] = { "R" , "G" , "B" , "A" }; |
75 | } |
76 | |
77 | void 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 ( |
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 | |