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
49namespace bpy = boost::python;
50using namespace foundation;
51using namespace renderer;
52
53namespace
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
129void 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