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) 2012-2013 Esteban Tovagliari, Jupiter Jazz Limited |
9 | // Copyright (c) 2014-2017 Esteban Tovagliari, 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 | // appleseed.python headers. |
31 | #include "pyseed.h" // has to be first, to avoid redefinition warnings |
32 | |
33 | // appleseed.renderer headers. |
34 | #include "renderer/kernel/aov/imagestack.h" |
35 | #include "renderer/kernel/aov/tilestack.h" |
36 | |
37 | // appleseed.foundation headers. |
38 | #include "foundation/image/canvasproperties.h" |
39 | #include "foundation/image/image.h" |
40 | #include "foundation/image/pixel.h" |
41 | #include "foundation/image/tile.h" |
42 | #include "foundation/platform/types.h" |
43 | |
44 | // Standard headers. |
45 | #include <algorithm> |
46 | #include <cstddef> |
47 | #include <string> |
48 | |
49 | namespace bpy = boost::python; |
50 | using namespace foundation; |
51 | using namespace renderer; |
52 | |
53 | namespace |
54 | { |
55 | Tile* copy_tile(const Tile* source) |
56 | { |
57 | return new Tile(*source); |
58 | } |
59 | |
60 | Tile* deepcopy_tile(const Tile* source, bpy::dict& memo) |
61 | { |
62 | // todo: not sure what to store in memo... |
63 | return new Tile(*source); |
64 | } |
65 | |
66 | void copy_tile_data_to_py_buffer(const Tile& tile, bpy::object& buffer) |
67 | { |
68 | void* array = 0; |
69 | Py_ssize_t len; |
70 | |
71 | const int success = PyObject_AsWriteBuffer(buffer.ptr(), &array, &len); |
72 | |
73 | if (success != 0) |
74 | bpy::throw_error_already_set(); |
75 | |
76 | if (static_cast<size_t>(len) < tile.get_size()) |
77 | { |
78 | PyErr_SetString(PyExc_IndexError, "Buffer size is smaller than data size" ); |
79 | bpy::throw_error_already_set(); |
80 | } |
81 | |
82 | std::copy( |
83 | tile.get_storage(), |
84 | tile.get_storage() + tile.get_size(), |
85 | reinterpret_cast<uint8*>(array)); |
86 | } |
87 | |
88 | bpy::list blender_tile_data(const Tile& tile) |
89 | { |
90 | bpy::list pixels; |
91 | |
92 | for (size_t y = 0, height = tile.get_height(); y < height; ++y) |
93 | { |
94 | for (size_t x = 0, width = tile.get_width(); x < width; ++x) |
95 | { |
96 | // Blender's image coordinate system is y up. |
97 | Color4f linear_rgba; |
98 | tile.get_pixel(x, height - y - 1, linear_rgba); |
99 | |
100 | bpy::list p; |
101 | p.append(linear_rgba[0]); |
102 | p.append(linear_rgba[1]); |
103 | p.append(linear_rgba[2]); |
104 | p.append(linear_rgba[3]); |
105 | pixels.append(p); |
106 | } |
107 | } |
108 | |
109 | return pixels; |
110 | } |
111 | |
112 | Image* copy_image(const Image* source) |
113 | { |
114 | return new Image(*source); |
115 | } |
116 | |
117 | Image* deepcopy_image(const Image* source, bpy::dict& memo) |
118 | { |
119 | // todo: not sure what to store in memo... |
120 | return new Image(*source); |
121 | } |
122 | |
123 | std::string image_stack_get_name(const ImageStack* image_stack, const size_t index) |
124 | { |
125 | return image_stack->get_name(index); |
126 | } |
127 | } |
128 | |
129 | void bind_image() |
130 | { |
131 | bpy::enum_<PixelFormat>("PixelFormat" ) |
132 | .value("UInt8" , PixelFormatUInt8) |
133 | .value("UInt16" , PixelFormatUInt16) |
134 | .value("UInt32" , PixelFormatUInt32) |
135 | .value("Half" , PixelFormatHalf) |
136 | .value("Float" , PixelFormatFloat) |
137 | .value("Double" , PixelFormatDouble) |
138 | ; |
139 | |
140 | bpy::class_<CanvasProperties, boost::noncopyable>("CanvasProperties" , bpy::no_init) |
141 | .def_readonly("canvas_width" , &CanvasProperties::m_canvas_width) |
142 | .def_readonly("canvas_height" , &CanvasProperties::m_canvas_height) |
143 | .def_readonly("tile_width" , &CanvasProperties::m_tile_width) |
144 | .def_readonly("tile_height" , &CanvasProperties::m_tile_height) |
145 | .def_readonly("channel_count" , &CanvasProperties::m_channel_count) |
146 | .def_readonly("pixel_format" , &CanvasProperties::m_pixel_format) |
147 | .def_readonly("rcp_canvas_width" , &CanvasProperties::m_rcp_canvas_width) |
148 | .def_readonly("rcp_canvas_height" , &CanvasProperties::m_rcp_canvas_height) |
149 | .def_readonly("rcp_tile_width" , &CanvasProperties::m_rcp_tile_width) |
150 | .def_readonly("rcp_tile_height" , &CanvasProperties::m_rcp_tile_height) |
151 | .def_readonly("tile_count_x" , &CanvasProperties::m_tile_count_x) |
152 | .def_readonly("tile_count_y" , &CanvasProperties::m_tile_count_y) |
153 | .def_readonly("tile_count" , &CanvasProperties::m_tile_count) |
154 | .def_readonly("pixel_count" , &CanvasProperties::m_pixel_count) |
155 | .def_readonly("pixel_size" , &CanvasProperties::m_pixel_size) |
156 | .def("get_tile_width" , &CanvasProperties::get_tile_width) |
157 | .def("get_tile_height" , &CanvasProperties::get_tile_height) |
158 | ; |
159 | |
160 | bpy::class_<Tile, boost::noncopyable>("Tile" , bpy::init<size_t, size_t, size_t, PixelFormat>()) |
161 | .def("__copy__" , copy_tile, bpy::return_value_policy<bpy::manage_new_object>()) |
162 | .def("__deepcopy__" , deepcopy_tile, bpy::return_value_policy<bpy::manage_new_object>()) |
163 | .def("get_pixel_format" , &Tile::get_pixel_format) |
164 | .def("get_width" , &Tile::get_width) |
165 | .def("get_height" , &Tile::get_height) |
166 | .def("get_channel_count" , &Tile::get_channel_count) |
167 | .def("get_pixel_count" , &Tile::get_pixel_count) |
168 | .def("get_size" , &Tile::get_size) |
169 | .def("copy_data_to" , copy_tile_data_to_py_buffer) // todo: maybe this needs a better name |
170 | |
171 | .def("blender_tile_data" , blender_tile_data) |
172 | ; |
173 | |
174 | const Tile& (Image::*image_get_tile)(const size_t, const size_t) const = &Image::tile; |
175 | |
176 | bpy::class_<Image, boost::noncopyable>("Image" , bpy::no_init) |
177 | .def("__copy__" , copy_image, bpy::return_value_policy<bpy::manage_new_object>()) |
178 | .def("__deepcopy__" , copy_image, bpy::return_value_policy<bpy::manage_new_object>()) |
179 | .def("properties" , &Image::properties, bpy::return_value_policy<bpy::reference_existing_object>()) |
180 | .def("tile" , image_get_tile, bpy::return_value_policy<bpy::reference_existing_object>()) |
181 | ; |
182 | |
183 | const Image& (ImageStack::*image_stack_get_image)(const size_t) const = &ImageStack::get_image; |
184 | |
185 | bpy::class_<ImageStack, boost::noncopyable>("ImageStack" , bpy::no_init) |
186 | .def("empty" , &ImageStack::empty) |
187 | .def("size" , &ImageStack::size) |
188 | .def("get_name" , image_stack_get_name) |
189 | .def("get_image" , image_stack_get_image, bpy::return_value_policy<bpy::reference_existing_object>()) |
190 | ; |
191 | } |
192 | |