Implementation of an image exporter
This topic introduces:
- Basic concepts about the export of rendered pixel data to disk:
- An example image exporter program, example_psd_exporter.cpp, which exports images in the Photoshop PSD file format PFFS10. The example program demonstrates how to export several canvases with different content (rendered image, normals, z-buffer, etc.) into the same file.
Implementing an image exporter
There are two ways to add support for an image format:
- By using an image plugin that adds support for this file format. The image plugin has the advantage that it extends the generic export facilities with the new image format.
- By using an explicit method that does the job. The explicit method has the advantage that you have more freedom and are not bound to the framework of image plugins.
In this example, the goal is to:
- Export multiple canvases into one file, and
- Export the names of the canvases
An explicit method is used because the framework for image plugins does not support these two features.
The example code is structured as follows:
- Auxilliary methods (from write_int8 to get_pixel_type()) that deal with low-level export of certain data formats and pixel type properties
- The main method export_psd() that accepts an array of canvases to export
- An adaptor of this main method with the same name that accepts an instance of mi::neuraylib::IRender_target to export
- An implementation of the mi::neuraylib::IRender_target interface with three canvases that will be used in this example
- The example code to set up and to render a scene (essentially the same as in example_rendering.cpp).
While the implementation of an image exporter heavily depends on the image format in question, there are often similarities in the file format structure and in the tasks to be accomplished. Typically, there is some sort of file header with meta information, followed by the actual pixel data.
To export a PSD file, the example program iterates over all the canvases to compute the needed metadata (width, height, number of layers, bits per channel) and reject invalid inputs. Next, the file is opened and the file header is written.
The next section in the PSD file format is the layer and mask information section. This section is split into two parts:
- The first part contains all the layer records which are basically a header with metadata for each layer, including the name of the layer.
- The second part contains the actual pixel data for each layer (in the same order as described by the layer records).
Finally, there is a last section named image data section. This section is supposed to contain the pixel data of all layers merged. It is primarily needed for applications that do not deal with the layer and mask information section. In this example, the pixel data of the first canvas is exported again (for simplicity).
When writing your own image exporter, or importer, there are several conversion tasks you have to handle:
- Mapping neuray pixel types (see Types) to pixel types of the image format and/or vice versa.
- Conversion of pixel data to a different pixel type (if needed).
- Flipping the scanline order (if needed). In neuray, scanlines are ordered bottom-up.
- Different tiling of pixel data.
The methods mi::neuraylib::IImage_api::read_raw_pixels() and mi::neuraylib::IImage_api::write_raw_pixels() can be very handy for the last three tasks. If requested, they can convert the pixel type as needed and flip the scanline order. They also convert the data between a possibly tiled canvas and a plain memory buffer.
Using the PSD example exporter
To demonstrate the PSD exporter, the example code is almost identical to that used in Example program section. To demonstrate the export of multiple canvases, a different implementation of the mi::neuraylib::IRender_target interface is used. This implementation holds three canvases:
- One for the rendered image
- One for the normals
- One for the z-buffer
An additional method normalize() modifies the values for the normals and z-buffer such that the data is in the range [0,1] expected by most image formats.
After the image has been rendered, the render target is passed to the method export_psd(), which exports all canvases of the render target into the given file. For comparison, the canvases are additionally exported into individual PNG files.
example_psd_exporter.cpp
001 /****************************************************************************** 002 * © 1986, 2016 NVIDIA Corporation. All rights reserved. 003 *****************************************************************************/ 004 005 // examples/example_psd_exporter.cpp 006 // 007 // Imports a scene file, renders the scene, and writes the image to disk as PSD file. 008 // 009 // The example expects the following command line arguments: 010 // 011 // example_psd_exporter <scene_file> <mdl_path> 012 // 013 // scene_file some scene file, e.g., main.mi 014 // mdl_path path to the MDL modules, e.g., iray-<version>/mdl 015 // 016 // The rendered images are written to files named "example_psd_exporter_*.png". 017 018 #include <cassert> 019 #include <cstdio> 020 #include <iostream> 021 #include <limits> 022 023 #include <mi/neuraylib.h> 024 025 // Include code shared by all examples. 026 #include "example_shared.h" 027 028 // for htons, ntohs 029 #ifndef MI_PLATFORM_WINDOWS 030 #include <arpa/inet.h> 031 #else 032 #ifndef WIN32_LEAN_AND_MEAN 033 #define WIN32_LEAN_AND_MEAN 1 034 #endif 035 #include <winsock2.h> 036 #endif 037 038 // The PSD file for export. Global variable to avoid passing it to every write function below. 039 FILE* g_file; 040 041 // Writes the 8-bit integer \p data to \p g_file. 042 void write_int8( mi::Uint8 data) 043 { 044 fwrite( &data, 1, 1, g_file); 045 } 046 047 // Writes the 16-bit integer \p data to \p g_file (converts data to network byte order). 048 void write_int16( mi::Uint16 data) 049 { 050 data = htons( data); 051 fwrite( &data, 2, 1, g_file); 052 } 053 054 // Writes the 32-bit integer \p data to \p g_file (converts data to network byte order). 055 void write_int32( mi::Uint32 data) 056 { 057 data = htonl( data); 058 fwrite( &data, 4, 1, g_file); 059 } 060 061 // Writes\p length bytes from buffer \p p to \p g_file. 062 void write_buf( const void* p, mi::Size length) 063 { 064 fwrite( p, length, 1, g_file); 065 } 066 067 // Writes the string \p s to \p g_file. 068 void write_str( const char* s) 069 { 070 write_buf( s, strlen( s)); 071 } 072 073 // Writes the string \p s to \p g_file as Pascal string, using a multiple of \p padding bytes. 074 void write_str_pascal( const char* s, mi::Size padding) 075 { 076 mi::Size len = strlen( s); 077 if( len > 255) 078 len = 255; 079 write_int8( static_cast<mi::Uint8>( len)); 080 write_buf( s, len); 081 for( mi::Size i = (len % padding) + 1; i < padding; ++i) 082 write_int8( 0); 083 } 084 085 // Writes pixel data of a channel to \p g_file, starting at \p input (converts data to network byte 086 // order). 087 void write_channel( 088 const mi::Uint8* input, 089 mi::Uint32 width, 090 mi::Uint32 height, 091 mi::Uint32 bytes_per_channel, 092 mi::Uint32 stride) 093 { 094 mi::Uint8* output = new mi::Uint8[width * height * bytes_per_channel]; 095 for( mi::Uint32 i = 0; i < width * height; ++i) 096 for( mi::Uint32 b = 0; b < bytes_per_channel; ++b) 097 output[i*bytes_per_channel + b] = input[i*stride + bytes_per_channel-1-b]; 098 write_buf( output, width * height * bytes_per_channel); 099 delete[] output; 100 } 101 102 // Returns the channel count used for the PSD file depending on the neuray pixel type. 103 // (Pixel types with only 1 or 2 channels are converted to types with 3 channels.) 104 mi::Uint32 get_channel_count( const char* pixel_type) 105 { 106 if( strcmp( pixel_type, "Float32<4>") == 0) return 4; 107 if( strcmp( pixel_type, "Rgba" ) == 0) return 4; 108 if( strcmp( pixel_type, "Rgbea" ) == 0) return 4; 109 if( strcmp( pixel_type, "Rgba_16" ) == 0) return 4; 110 if( strcmp( pixel_type, "Color" ) == 0) return 4; 111 return 3; 112 } 113 114 // Returns the number of bytes per channel for the PSD file depending on the neuray pixel type. 115 mi::Uint32 get_bytes_per_channel( const char* pixel_type) 116 { 117 if( strcmp( pixel_type, "Sint8" ) == 0) return 1; 118 if( strcmp( pixel_type, "Sint32" ) == 0) return 1; 119 if( strcmp( pixel_type, "Rgb" ) == 0) return 1; 120 if( strcmp( pixel_type, "Rgba" ) == 0) return 1; 121 return 2; 122 } 123 124 // Returns the RGB(A) pixel type for given bytes per channel and number of channels. 125 const char* get_pixel_type( mi::Uint32 bytes_per_channel, mi::Uint32 channels) 126 { 127 assert( channels == 3 || channels == 4); 128 129 switch( bytes_per_channel) { 130 case 1: return channels == 3 ? "Rgb" : "Rgba"; 131 case 2: return channels == 3 ? "Rgb_16" : "Rgba_16"; 132 default: assert( false); return "Rgb"; 133 } 134 } 135 136 // Exports an array of canvases as PSD file. 137 // 138 // This method creates PSD files (version 1 only). It supports only 8 and 16 bits per channel 139 // (other pixel types converted accordingly). It does not support compression. 140 // 141 // \param canvases The array of canvases to export. 142 // \param names Optional array of canvas names. 143 // \param filename The name of the file to export to. 144 // \param image_api API component IImage_api (needed for pixel type conversion). 145 // 146 // \see "Adobe Photoshop File Formats Specification", July 2010 for file format details. 147 bool export_psd( 148 const mi::IArray* canvases, 149 const mi::IArray* names, 150 const char* filename, 151 mi::neuraylib::IImage_api* image_api) 152 { 153 if( !canvases || !filename || !image_api) 154 return false; 155 156 // Check consistent array length 157 if( names && names->get_length() != canvases->get_length()) 158 return false; 159 160 // Compute maximum width, height, bytes per channel, and total number of layers 161 mi::Uint32 total_width = 0; 162 mi::Uint32 total_height = 0; 163 mi::Uint32 total_layers = 0; 164 mi::Uint32 bytes_per_channel = 0; 165 for( mi::Uint32 i = 0; i < canvases->get_length(); ++i) { 166 mi::base::Handle<const mi::neuraylib::ICanvas> canvas( 167 canvases->get_element<mi::neuraylib::ICanvas>( i)); 168 if( !canvas.is_valid_interface()) 169 return false; 170 total_width = std::max( total_width, canvas->get_resolution_x()); 171 total_height = std::max( total_height, canvas->get_resolution_y()); 172 total_layers += canvas->get_layers_size(); 173 bytes_per_channel = std::max( bytes_per_channel, 174 get_bytes_per_channel( canvas->get_type())); 175 } 176 177 // Reject canvases too large for PSD files version 1 178 if( total_width > 30000 || total_height > 30000) 179 return false; 180 181 // Reject if too many layers in total 182 if( total_layers == 0 || total_layers > 56) 183 return false; 184 185 // Check names 186 bool has_result = false; 187 for( mi::Uint32 i = 0; names && i < names->get_length(); ++i) { 188 mi::base::Handle<const mi::IString> name( 189 names->get_element<mi::IString>( i)); 190 if( !name.is_valid_interface()) 191 return false; 192 if( strcmp( name->get_c_str(), "result") == 0) 193 has_result = true; 194 } 195 196 g_file = fopen( filename, "wb"); 197 if( !g_file) 198 return false; 199 200 // File header section 201 202 write_str( "8BPS"); // signature 203 write_int16( 1); // version 204 write_buf( "\0\0\0\0\0\0", 6); // reserved 205 write_int16( 3); // number of channels 206 write_int32( total_height); // height 207 write_int32( total_width); // width 208 write_int16( static_cast<mi::Uint16>( 8*bytes_per_channel)); // bits per channel 209 write_int16( 3); // color mode (RGB) 210 211 write_int32( 0); // length of color mode data section 212 write_int32( 0); // length of image resource section 213 214 // Layer and mask information section 215 216 write_int32( 0); // length of layer and mask information section (dummy) 217 long lamis_start = ftell( g_file); 218 219 // Layer and mask information section: Layer info subsection 220 221 write_int32( 0); // length of layer information section (dummy) 222 long lis_start = ftell( g_file); 223 224 write_int16( static_cast<mi::Uint16>( total_layers)); // layer count 225 226 // Layer records 227 228 for( mi::Size i = 0; i < canvases->get_length(); ++i) { 229 230 mi::base::Handle<const mi::neuraylib::ICanvas> canvas( 231 canvases->get_element<mi::neuraylib::ICanvas>( i)); 232 mi::Uint32 width = canvas->get_resolution_x(); 233 234 const char* name = ""; 235 if( names) { 236 mi::base::Handle<const mi::IString> s( names->get_element<mi::IString>( i)); 237 name = s->get_c_str(); 238 } 239 bool is_result = strcmp( name, "result") == 0; 240 241 for( mi::Size j = 0; j < canvas->get_layers_size(); ++j) { 242 243 mi::Uint32 channels = get_channel_count( canvas->get_type()); 244 245 write_int32( 0); // top 246 write_int32( 0); // left 247 write_int32( total_height); // bottom 248 write_int32( width); // right 249 write_int16( static_cast<mi::Uint16>( channels)); // number of channels 250 mi::Uint32 channel_size = width * total_height * bytes_per_channel + 2; 251 write_int16( 0); // channel ID red 252 write_int32( channel_size); // byte count red 253 write_int16( 1); // channel ID green 254 write_int32( channel_size); // byte count green 255 write_int16( 2); // channel ID blue 256 write_int32( channel_size); // byte count blue 257 if( channels == 4) { 258 write_int16( 0xFFFF); // channel ID alpha 259 write_int32( channel_size); // byte count alpha 260 } 261 262 write_str( "8BIM"); // blend mode signature 263 write_str( "norm"); // blend mode key 264 write_int8( 255); // opacity 265 write_int8( 0); // clipping 266 mi::Uint8 flags = static_cast<mi::Uint8>( !has_result || is_result ? 0 : 2); 267 write_int8( flags); // flags (invisible = 2) 268 write_int8( 0); // filler 269 write_int32( 0); // extra data length (dummy) 270 long extra_data_start = ftell( g_file); 271 write_int32( 0); // layer mask data length 272 write_int32( 0); // layer blending ranges length 273 write_str_pascal( name, 4); // layer name 274 275 // extra data length 276 long extra_data_end = ftell( g_file); 277 fseek( g_file, extra_data_start-4, SEEK_SET); 278 write_int32( static_cast<mi::Uint32>( extra_data_end-extra_data_start)); 279 fseek( g_file, extra_data_end, SEEK_SET); 280 } 281 } 282 283 // Channel image data 284 285 for( mi::Size i = 0; i < canvases->get_length(); ++i) { 286 287 mi::base::Handle<const mi::neuraylib::ICanvas> canvas( 288 canvases->get_element<mi::neuraylib::ICanvas>( i)); 289 mi::Uint32 width = canvas->get_resolution_x(); 290 mi::Uint32 height = canvas->get_resolution_y(); 291 292 for( mi::Uint32 j = 0; j < canvas->get_layers_size(); ++j) { 293 294 mi::Uint32 channels = get_channel_count( canvas->get_type()); 295 mi::Uint8* buffer = new mi::Uint8[width * total_height * channels * bytes_per_channel]; 296 memset( buffer, 0, width * total_height * channels * bytes_per_channel); 297 mi::Size offset = width * (total_height-height) * channels * bytes_per_channel; 298 const char* pixel_type = get_pixel_type( bytes_per_channel, channels); 299 image_api->read_raw_pixels( 300 width, height, canvas.get(), 0, 0, j, buffer + offset, true, pixel_type); 301 302 for( mi::Uint32 channel = 0; channel < channels; ++channel) { 303 write_int16( 0); // channel compression method (raw) 304 mi::Uint8* start = buffer + channel * bytes_per_channel; 305 mi::Uint32 stride = channels * bytes_per_channel; 306 write_channel( start, width, total_height, bytes_per_channel, stride); 307 } 308 309 delete[] buffer; 310 } 311 } 312 313 // length of layer information section 314 long lis_end = ftell( g_file); 315 fseek( g_file, lis_start-4, SEEK_SET); 316 write_int32( static_cast<mi::Uint32>( lis_end-lis_start)); 317 fseek( g_file, lis_end, SEEK_SET); 318 319 // length of global layer mask info 320 write_int32( 0); 321 322 // length of layer and mask inform. section 323 long lamis_end = ftell( g_file); 324 fseek( g_file, lamis_start-4, SEEK_SET); 325 write_int32( static_cast<mi::Uint32>( lamis_end-lamis_start)); 326 fseek( g_file, lamis_end, SEEK_SET); 327 328 // Image data section 329 330 mi::base::Handle<const mi::neuraylib::ICanvas> canvas( 331 canvases->get_element<mi::neuraylib::ICanvas>( 0)); 332 mi::Uint32 width = canvas->get_resolution_x(); 333 mi::Uint32 height = canvas->get_resolution_y(); 334 335 for( mi::Uint32 j = 0; j < canvas->get_layers_size(); ++j) { 336 337 mi::Uint32 channels = 3; 338 mi::Uint8* buffer 339 = new mi::Uint8[total_width * total_height * channels * bytes_per_channel]; 340 memset( buffer, 0, total_width * total_height * channels * bytes_per_channel); 341 mi::Size offset = total_width * (total_height-height) * channels * bytes_per_channel; 342 mi::Uint32 padding = (total_width - width) * channels * bytes_per_channel; 343 const char* pixel_type = get_pixel_type( bytes_per_channel, channels); 344 image_api->read_raw_pixels( 345 width, height, canvas.get(), 0, 0, j, buffer + offset, true, pixel_type, padding); 346 347 write_int16( 0); // compression method (raw) 348 for( mi::Uint32 channel = 0; channel < channels; ++channel) { 349 mi::Uint8* start = buffer + channel * bytes_per_channel; 350 mi::Uint32 stride = channels * bytes_per_channel; 351 write_channel( start, width, total_height, bytes_per_channel, stride); 352 } 353 354 delete[] buffer; 355 } 356 357 fclose( g_file); 358 return true; 359 } 360 361 // Exports a render target as PSD file. 362 // 363 // This method creates PSD files (version 1 only). It supports only 8 and 16 bits per channel 364 // (other pixel types converted accordingly). It does not support compression. 365 // 366 // \param render_target The render target to export. 367 // \param filename The name of the file to export to. 368 // \param factory API component IFactory (needed to create new data types). 369 // \param image_api API component IImage_api (needed for pixel type conversion). 370 bool export_psd( 371 mi::neuraylib::IRender_target* render_target, 372 const char* filename, 373 mi::neuraylib::IFactory* factory, 374 mi::neuraylib::IImage_api* image_api) 375 { 376 if( !render_target || !filename || !image_api) 377 return false; 378 379 mi::base::Handle<mi::IDynamic_array> canvases( 380 factory->create<mi::IDynamic_array>( "Interface[]")); 381 check_success( canvases.is_valid_interface()); 382 mi::base::Handle<mi::IDynamic_array> names( 383 factory->create<mi::IDynamic_array>( "String[]")); 384 check_success( names.is_valid_interface()); 385 for( mi::Uint32 i = 0; i < render_target->get_canvas_count(); ++i) { 386 mi::base::Handle<mi::neuraylib::ICanvas> canvas( render_target->get_canvas( i)); 387 canvases->push_back( canvas.get()); 388 mi::base::Handle<mi::IString> name( factory->create<mi::IString>( "String")); 389 name->set_c_str( render_target->get_canvas_name( i)); 390 names->push_back( name.get()); 391 } 392 393 return export_psd( canvases.get(), names.get(), filename, image_api); 394 } 395 396 void configuration( mi::base::Handle<mi::neuraylib::INeuray> neuray, const char* mdl_path) 397 { 398 // Configure the neuray library. Here we set the search path for .mdl files. 399 mi::base::Handle<mi::neuraylib::IRendering_configuration> rendering_configuration( 400 neuray->get_api_component<mi::neuraylib::IRendering_configuration>()); 401 check_success( rendering_configuration.is_valid_interface()); 402 check_success( rendering_configuration->add_mdl_path( mdl_path) == 0); 403 404 // Load the FreeImage, Iray Photoreal, and .mi importer plugins. 405 mi::base::Handle<mi::neuraylib::IPlugin_configuration> plugin_configuration( 406 neuray->get_api_component<mi::neuraylib::IPlugin_configuration>()); 407 #ifndef MI_PLATFORM_WINDOWS 408 check_success( plugin_configuration->load_plugin_library( "freeimage.so") == 0); 409 check_success( plugin_configuration->load_plugin_library( "libiray.so") == 0); 410 check_success( plugin_configuration->load_plugin_library( "mi_importer.so") == 0); 411 #else 412 check_success( plugin_configuration->load_plugin_library( "freeimage.dll") == 0); 413 check_success( plugin_configuration->load_plugin_library( "libiray.dll") == 0); 414 check_success( plugin_configuration->load_plugin_library( "mi_importer.dll") == 0); 415 #endif 416 } 417 418 // A simple implementation of the IRender_target interface with three canvases, one for the 419 // rendering result, for the normals, and one for the z-buffer. 420 class Render_target : public mi::base::Interface_implement<mi::neuraylib::IRender_target> 421 { 422 public: 423 // Constructor. Creates a render target with three canvases. 424 Render_target( mi::Uint32 width, mi::Uint32 height, mi::neuraylib::IImage_api* image_api) 425 { 426 m_canvas[0] = image_api->create_canvas( "Color", width, height, width, height, 1); 427 m_canvas[1] = image_api->create_canvas( "Float32<3>", width, height, width, height, 1); 428 m_canvas[2] = image_api->create_canvas( "Float32", width, height, width, height, 1); 429 } 430 431 // Implement the interface of mi::neuraylib::IRender_target. 432 mi::Uint32 get_canvas_count() const { return 3; } 433 const char* get_canvas_name( mi::Uint32 index) const 434 { 435 if( index == 0) return "result"; 436 if( index == 1) return "normal"; 437 if( index == 2) return "depth"; 438 return 0; 439 } 440 const mi::neuraylib::ICanvas* get_canvas( mi::Uint32 index) const 441 { 442 if( index >= 3) 443 return 0; 444 m_canvas[index]->retain(); 445 return m_canvas[index].get(); 446 } 447 mi::neuraylib::ICanvas* get_canvas( mi::Uint32 index) 448 { 449 if( index >= 3) 450 return 0; 451 m_canvas[index]->retain(); 452 return m_canvas[index].get(); 453 } 454 455 // Normalizes the contents of the second and third canvas. 456 void normalize() { 457 458 // Map values in m_canvas[1] linearly from [-1,1] to [0,1]. 459 mi::base::Handle<mi::neuraylib::ITile> tile( m_canvas[1]->get_tile( 0, 0)); 460 mi::Size n_pixels = tile->get_resolution_x() * tile->get_resolution_y(); 461 mi::Float32_3* data1 = static_cast<mi::Float32_3*>( tile->get_data()); 462 for( mi::Size i = 0; i < n_pixels; ++i) { 463 data1[i].x = mi::math::clamp( 0.5f*data1[i].x + 0.5f, 0.0f, 1.0f); 464 data1[i].y = mi::math::clamp( 0.5f*data1[i].y + 0.5f, 0.0f, 1.0f); 465 data1[i].z = mi::math::clamp( 0.5f*data1[i].z + 0.5f, 0.0f, 1.0f); 466 } 467 468 // Map values in m_canvas[2] (excluding huge values) linearly to [0,1]. 469 tile = m_canvas[2]->get_tile( 0, 0); 470 n_pixels = tile->get_resolution_x() * tile->get_resolution_y(); 471 mi::Float32* data2 = static_cast<mi::Float32*>( tile->get_data()); 472 mi::Float32 min_value = std::numeric_limits<mi::Float32>::max(); 473 mi::Float32 max_value = std::numeric_limits<mi::Float32>::min(); 474 for( mi::Size i = 0; i < n_pixels; ++i) { 475 if (data2[i] < min_value) min_value = data2[i]; 476 if (data2[i] > max_value && data2[i] < 1.0E38f) max_value = data2[i]; 477 } 478 if( min_value == max_value) 479 min_value = max_value-1; 480 for( mi::Size i = 0; i < n_pixels; ++i) 481 if( data2[i] < 1.0E38f) 482 data2[i] = 1.0f - (data2[i] - min_value) / (max_value - min_value); 483 else 484 data2[i] = 0.0f; 485 } 486 487 private: 488 // The three canvases of this render target. 489 mi::base::Handle<mi::neuraylib::ICanvas> m_canvas[3]; 490 }; 491 492 void rendering( mi::base::Handle<mi::neuraylib::INeuray> neuray, 493 const char* scene_file) 494 { 495 // Get the database, the global scope of the database, and create a transaction in the global 496 // scope for importing the scene file and storing the scene. 497 mi::base::Handle<mi::neuraylib::IDatabase> database( 498 neuray->get_api_component<mi::neuraylib::IDatabase>()); 499 check_success( database.is_valid_interface()); 500 mi::base::Handle<mi::neuraylib::IScope> scope( 501 database->get_global_scope()); 502 mi::base::Handle<mi::neuraylib::ITransaction> transaction( 503 scope->create_transaction()); 504 check_success( transaction.is_valid_interface()); 505 506 // Import the scene file 507 mi::base::Handle<mi::neuraylib::IImport_api> import_api( 508 neuray->get_api_component<mi::neuraylib::IImport_api>()); 509 check_success( import_api.is_valid_interface()); 510 mi::base::Handle<const mi::IString> uri( import_api->convert_filename_to_uri( scene_file)); 511 mi::base::Handle<const mi::neuraylib::IImport_result> import_result( 512 import_api->import_elements( transaction.get(), uri->get_c_str())); 513 check_success( import_result->get_error_number() == 0); 514 515 // Create the scene object 516 mi::base::Handle<mi::neuraylib::IScene> scene( 517 transaction->create<mi::neuraylib::IScene>( "Scene")); 518 scene->set_rootgroup( import_result->get_rootgroup()); 519 scene->set_options( import_result->get_options()); 520 scene->set_camera_instance( import_result->get_camera_inst()); 521 transaction->store( scene.get(), "the_scene"); 522 523 // Create the render context using the Iray Photoreal render mode 524 scene = transaction->edit<mi::neuraylib::IScene>( "the_scene"); 525 mi::base::Handle<mi::neuraylib::IRender_context> render_context( 526 scene->create_render_context( transaction.get(), "iray")); 527 check_success( render_context.is_valid_interface()); 528 mi::base::Handle<mi::IString> scheduler_mode( transaction->create<mi::IString>()); 529 scheduler_mode->set_c_str( "batch"); 530 render_context->set_option( "scheduler_mode", scheduler_mode.get()); 531 scene = 0; 532 533 // Create the render target and render the scene 534 mi::base::Handle<mi::neuraylib::IImage_api> image_api( 535 neuray->get_api_component<mi::neuraylib::IImage_api>()); 536 mi::base::Handle<Render_target> render_target( new Render_target( 512, 384, image_api.get())); 537 check_success( 538 render_context->render( transaction.get(), render_target.get(), 0) >= 0); 539 540 // Write the image to disk (entire render target as one PSD file) 541 render_target->normalize(); 542 mi::base::Handle<mi::neuraylib::IFactory> factory( 543 neuray->get_api_component<mi::neuraylib::IFactory>()); 544 check_success( export_psd( 545 render_target.get(), "example_psd_exporter.psd", factory.get(), image_api.get())); 546 547 // Write the image to disk (individual canvases as PNG files) 548 mi::base::Handle<mi::neuraylib::IExport_api> export_api( 549 neuray->get_api_component<mi::neuraylib::IExport_api>()); 550 check_success( export_api.is_valid_interface()); 551 mi::base::Handle<mi::neuraylib::ICanvas> canvas0( render_target->get_canvas( 0)); 552 export_api->export_canvas( "file:example_psd_exporter_0.png", canvas0.get()); 553 mi::base::Handle<mi::neuraylib::ICanvas> canvas1( render_target->get_canvas( 1)); 554 export_api->export_canvas( "file:example_psd_exporter_1.png", canvas1.get()); 555 mi::base::Handle<mi::neuraylib::ICanvas> canvas2( render_target->get_canvas( 2)); 556 export_api->export_canvas( "file:example_psd_exporter_2.png", canvas2.get()); 557 558 transaction->commit(); 559 } 560 561 int main( int argc, char* argv[]) 562 { 563 // Collect command line parameters 564 if( argc != 3) { 565 std::cerr << "Usage: example_psd_exporter <scene_file> <mdl_path>" << std::endl; 566 keep_console_open(); 567 return EXIT_FAILURE; 568 } 569 const char* scene_file = argv[1]; 570 const char* mdl_path = argv[2]; 571 572 // Access the neuray library 573 mi::base::Handle<mi::neuraylib::INeuray> neuray( load_and_get_ineuray()); 574 check_success( neuray.is_valid_interface()); 575 576 // Configure the neuray library 577 configuration( neuray, mdl_path); 578 579 // Start the neuray library 580 mi::Sint32 result = neuray->start(); 581 check_start_success( result); 582 583 // Do the actual rendering 584 rendering( neuray, scene_file); 585 586 // Shut down the neuray library 587 check_success( neuray->shutdown() == 0); 588 neuray = 0; 589 590 // Unload the neuray library 591 check_success( unload()); 592 593 keep_console_open(); 594 return EXIT_SUCCESS; 595 }