This example demonstrates the export of rendered pixel data to disk. Even though the Iray SDK already supports a wide range of image formats you might want to add support for your own image format. As an example we show the export of images in the Photoshop PSD file format [PFFS10]. In particular, we want to export several canvases with different content (rendered image, normals, z-buffer, etc.) into the same file.
There are two ways to add support for an image format: via an image plugin that adds support for this this file format, and via an explicit method that does the job. The image plugin has the advantage that it extends the generic export facilities with the new image format. The explicit method has the advantage that you have more freedom and are not bound to the framework of image plugins. In this example we go with the latter approach since we want export multiple canvases into one file, and in addition also their names; two features that are currently not supported by the framework for image plugins.
#include <cassert>
#include <cstdio>
#include <iostream>
#include <limits>
#include "example_shared.h"
#ifndef MI_PLATFORM_WINDOWS
#include <arpa/inet.h>
#else
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#include <winsock2.h>
#endif
{
public:
const char* get_canvas_name(
mi::Uint32 index)
const;
void normalize();
private:
};
FILE* g_file;
{
fwrite( &data, 1, 1, g_file);
}
{
data = htons( data);
fwrite( &data, 2, 1, g_file);
}
{
data = htonl( data);
fwrite( &data, 4, 1, g_file);
}
{
fwrite( p,
length, 1, g_file);
}
void write_str( const char* s)
{
write_buf( s, strlen( s));
}
void write_str_pascal(
const char* s,
mi::Size padding)
{
if( len > 255)
len = 255;
write_buf( s, len);
for(
mi::Size i = (len % padding) + 1; i < padding; ++i)
write_int8( 0);
}
void write_channel(
{
for(
mi::Uint32 b = 0; b < bytes_per_channel; ++b)
output[i*bytes_per_channel + b] = input[i*stride + bytes_per_channel-1-b];
write_buf( output, width * height * bytes_per_channel);
delete[] output;
}
mi::Uint32 get_channel_count(
const char* pixel_type)
{
if( strcmp( pixel_type, "Float32<4>") == 0) return 4;
if( strcmp( pixel_type, "Rgba" ) == 0) return 4;
if( strcmp( pixel_type, "Rgbea" ) == 0) return 4;
if( strcmp( pixel_type, "Rgba_16" ) == 0) return 4;
if( strcmp( pixel_type, "Color" ) == 0) return 4;
return 3;
}
mi::Uint32 get_bytes_per_channel(
const char* pixel_type)
{
if( strcmp( pixel_type, "Sint8" ) == 0) return 1;
if( strcmp( pixel_type, "Sint32" ) == 0) return 1;
if( strcmp( pixel_type, "Rgb" ) == 0) return 1;
if( strcmp( pixel_type, "Rgba" ) == 0) return 1;
return 2;
}
{
assert( channels == 3 || channels == 4);
switch( bytes_per_channel) {
case 1: return channels == 3 ? "Rgb" : "Rgba";
case 2: return channels == 3 ? "Rgb_16" : "Rgba_16";
default: assert( false); return "Rgb";
}
}
bool export_psd(
const char* filename,
{
if( !canvases || !filename || !image_api)
return false;
return false;
if( !canvas.is_valid_interface())
return false;
total_width = std::max( total_width, canvas->get_resolution_x());
total_height = std::max( total_height, canvas->get_resolution_y());
total_layers += canvas->get_layers_size();
bytes_per_channel = std::max( bytes_per_channel,
get_bytes_per_channel( canvas->get_type()));
}
if( total_width > 30000 || total_height > 30000)
return false;
if( total_layers == 0 || total_layers > 56)
return false;
bool has_result = false;
if( !name.is_valid_interface())
return false;
if( strcmp( name->get_c_str(), "result") == 0)
has_result = true;
}
g_file = fopen( filename, "wb");
if( !g_file)
return false;
write_str( "8BPS");
write_int16( 1);
write_buf( "\0\0\0\0\0\0", 6);
write_int16( 3);
write_int32( total_height);
write_int32( total_width);
write_int16(
static_cast<mi::Uint16>( 8*bytes_per_channel));
write_int16( 3);
write_int32( 0);
write_int32( 0);
write_int32( 0);
long lamis_start = ftell( g_file);
write_int32( 0);
long lis_start = ftell( g_file);
write_int16(
static_cast<mi::Uint16>( total_layers));
const char* name = "";
if( names) {
name = s->get_c_str();
}
bool is_result = strcmp( name, "result") == 0;
for(
mi::Size j = 0; j < canvas->get_layers_size(); ++j) {
mi::Uint32 channels = get_channel_count( canvas->get_type());
write_int32( 0);
write_int32( 0);
write_int32( total_height);
write_int32( width);
mi::Uint32 channel_size = width * total_height * bytes_per_channel + 2;
write_int16( 0);
write_int32( channel_size);
write_int16( 1);
write_int32( channel_size);
write_int16( 2);
write_int32( channel_size);
if( channels == 4) {
write_int16( 0xFFFF);
write_int32( channel_size);
}
write_str( "8BIM");
write_str( "norm");
write_int8( 255);
write_int8( 0);
write_int8( flags);
write_int8( 0);
write_int32( 0);
long extra_data_start = ftell( g_file);
write_int32( 0);
write_int32( 0);
write_str_pascal( name, 4);
long extra_data_end = ftell( g_file);
fseek( g_file, extra_data_start-4, SEEK_SET);
write_int32(
static_cast<mi::Uint32>( extra_data_end-extra_data_start));
fseek( g_file, extra_data_end, SEEK_SET);
}
}
for(
mi::Uint32 j = 0; j < canvas->get_layers_size(); ++j) {
mi::Uint32 channels = get_channel_count( canvas->get_type());
memset( buffer, 0, width * total_height * channels * bytes_per_channel);
mi::Size offset = width * (total_height-height) * channels * bytes_per_channel;
const char* pixel_type = get_pixel_type( bytes_per_channel, channels);
width, height, canvas.get(), 0, 0, j, buffer + offset, true, pixel_type);
for(
mi::Uint32 channel = 0; channel < channels; ++channel) {
write_int16( 0);
mi::Uint8* start = buffer + channel * bytes_per_channel;
write_channel( start, width, total_height, bytes_per_channel, stride);
}
delete[] buffer;
}
}
long lis_end = ftell( g_file);
fseek( g_file, lis_start-4, SEEK_SET);
write_int32(
static_cast<mi::Uint32>( lis_end-lis_start));
fseek( g_file, lis_end, SEEK_SET);
write_int32( 0);
long lamis_end = ftell( g_file);
fseek( g_file, lamis_start-4, SEEK_SET);
write_int32(
static_cast<mi::Uint32>( lamis_end-lamis_start));
fseek( g_file, lamis_end, SEEK_SET);
for(
mi::Uint32 j = 0; j < canvas->get_layers_size(); ++j) {
=
new mi::Uint8[total_width * total_height * channels * bytes_per_channel];
memset( buffer, 0, total_width * total_height * channels * bytes_per_channel);
mi::Size offset = total_width * (total_height-height) * channels * bytes_per_channel;
mi::Uint32 padding = (total_width - width) * channels * bytes_per_channel;
const char* pixel_type = get_pixel_type( bytes_per_channel, channels);
width, height, canvas.get(), 0, 0, j, buffer + offset, true, pixel_type, padding);
write_int16( 0);
for(
mi::Uint32 channel = 0; channel < channels; ++channel) {
mi::Uint8* start = buffer + channel * bytes_per_channel;
write_channel( start, width, total_height, bytes_per_channel, stride);
}
delete[] buffer;
}
fclose( g_file);
return true;
}
bool export_psd(
Render_target* render_target,
const char* filename,
{
if( !render_target || !filename || !image_api)
return false;
check_success( canvases.is_valid_interface());
check_success( names.is_valid_interface());
for(
mi::Uint32 i = 0; i < render_target->get_canvas_count(); ++i) {
canvases->push_back( canvas.get());
name->set_c_str( render_target->get_canvas_name( i));
names->push_back( name.get());
}
return export_psd( canvases.get(), names.get(), filename, image_api);
}
{
check_success( rc->add_mdl_path( mdl_path) == 0);
check_success( rc->add_mdl_path( ".") == 0);
}
{
m_canvas[0] = image_api->
create_canvas(
"Color", width, height, 1);
m_canvas[1] = image_api->
create_canvas(
"Float32<3>", width, height, 1);
m_canvas[2] = image_api->
create_canvas(
"Float32", width, height, 1);
}
mi::Uint32 Render_target::get_canvas_count()
const {
return 3; }
{
switch (index) {
}
}
const char* Render_target::get_canvas_name(
mi::Uint32 index)
const
{
switch (index) {
case 0: return "result";
case 1: return "normal";
case 2: return "depth";
default: return 0;
}
}
{ return 0; }
{
if( index >= 3)
return 0;
return m_canvas[index].get();
}
{
if( index >= 3)
return 0;
return m_canvas[index].get();
}
void Render_target::normalize()
{
mi::Size n_pixels = tile->get_resolution_x() * tile->get_resolution_y();
for(
mi::Size i = 0; i < n_pixels; ++i) {
}
tile = m_canvas[2]->get_tile();
n_pixels = tile->get_resolution_x() * tile->get_resolution_y();
mi::Float32 min_value = std::numeric_limits<mi::Float32>::max();
mi::Float32 max_value = std::numeric_limits<mi::Float32>::min();
for(
mi::Size i = 0; i < n_pixels; ++i) {
if (data2[i] < min_value) min_value = data2[i];
if (data2[i] > max_value && data2[i] < 1.0E38f) max_value = data2[i];
}
if( min_value == max_value)
min_value = max_value-1;
if( data2[i] < 1.0E38f)
data2[i] = 1.0f - (data2[i] - min_value) / (max_value - min_value);
else
data2[i] = 0.0f;
}
void rendering(
{
check_success( database.is_valid_interface());
database->get_global_scope());
scope->create_transaction());
check_success( transaction.is_valid_interface());
check_success( import_api.is_valid_interface());
import_api->import_elements( transaction.get(), uri->get_c_str()));
check_success( import_result->get_error_number() == 0);
scene->set_rootgroup( import_result->get_rootgroup());
scene->set_options( import_result->get_options());
scene->set_camera_instance( import_result->get_camera_inst());
transaction->store( scene.get(), "the_scene");
scene->create_render_context( transaction.get(), "iray"));
check_success( render_context.is_valid_interface());
scheduler_mode->set_c_str( "batch");
render_context->set_option( "scheduler_mode", scheduler_mode.get());
scene = 0;
check_success(
render_context->render( transaction.get(), render_target.get(), 0) >= 0);
render_target->normalize();
check_success( export_psd(
render_target.get(), "example_psd_exporter.psd", factory.get(), image_api.get()));
check_success( export_api.is_valid_interface());
export_api->export_canvas( "file:example_psd_exporter_0.png", canvas0.get());
export_api->export_canvas( "file:example_psd_exporter_1.png", canvas1.get());
export_api->export_canvas( "file:example_psd_exporter_2.png", canvas2.get());
transaction->commit();
}
int main( int argc, char* argv[])
{
if( argc != 3) {
std::cerr << "Usage: example_psd_exporter <scene_file> <mdl_path>" << std::endl;
keep_console_open();
return EXIT_FAILURE;
}
const char* scene_file = argv[1];
const char* mdl_path = argv[2];
check_success( neuray.is_valid_interface());
configuration( neuray.get(), mdl_path);
check_start_success( result);
rendering( neuray.get(), scene_file);
check_success( neuray->
shutdown() == 0);
neuray = 0;
check_success( unload());
keep_console_open();
return EXIT_SUCCESS;
}
This interface represents static arrays, i.e., arrays with a fixed number of elements.
Definition: iarray.h:37
virtual const base::IInterface * get_element(Size index) const =0
Returns the index -th element of the array.
virtual Size get_length() const =0
Returns the size of the array.
This interface represents dynamic arrays, i.e., arrays with a variable number of elements.
Definition: idynamic_array.h:36
A simple string class.
Definition: istring.h:22
Handle class template for interfaces, automatizing the lifetime control via reference counting.
Definition: handle.h:113
Mixin class template for deriving interface implementations.
Definition: interface_implement.h:41
Fixed-size math vector class template with generic operations.
Definition: vector.h:286
Abstract interface for render target canvas parameters.
Definition: irender_target.h:106
Abstract interface for a canvas represented by a rectangular array of tiles.
Definition: icanvas.h:85
This interface is used to interact with the distributed database.
Definition: idatabase.h:293
This interface is used to export files.
Definition: iexport_api.h:38
This API component allows the creation, assignment, and cloning of instances of types.
Definition: ifactory.h:35
virtual base::IInterface * create(const char *type_name, Uint32 argc=0, const base::IInterface *argv[]=0)=0
Creates an object of the type type_name.
This interface provides various utilities related to canvases and buffers.
Definition: iimage_api.h:49
virtual ICanvas * create_canvas(const char *pixel_type, Uint32 width, Uint32 height, Uint32 layers=1, bool is_cubemap=false, Float32 gamma=0.0f) const =0
Creates a canvas with given pixel type, resolution, and layers.
virtual Sint32 read_raw_pixels(Uint32 width, Uint32 height, const ICanvas *canvas, Uint32 canvas_x, Uint32 canvas_y, Uint32 canvas_layer, void *buffer, bool buffer_topdown, const char *buffer_pixel_type, Uint32 buffer_padding=0) const =0
Reads raw pixel data from a canvas.
This interface is used to import files.
Definition: iimport_api.h:100
This is an object representing the Iray library.
Definition: ineuray.h:44
virtual Sint32 shutdown(bool blocking=true)=0
Shuts down the library.
virtual base::IInterface * get_api_component(const base::Uuid &uuid) const =0
Returns an API component from the Iray SDK API.
virtual Sint32 start(bool blocking=true)=0
Starts the operation of the Iray library.
This interface is used to load plugins and to query information about loaded plugins.
Definition: iplugin_configuration.h:24
This interface is used to query and change the rendering configuration.
Definition: irendering_configuration.h:109
The scene is the top-level element describing a subset of DB elements to be rendered.
Definition: iscene.h:44
#define MI_BASE_DLL_FILE_EXT
The operating system specific default filename extension for shared libraries (DLLs)
Definition: config.h:340
virtual Uint32 retain() const =0
Increments the reference count.
unsigned int Uint32
32-bit unsigned integer.
Definition: types.h:49
unsigned char Uint8
8-bit unsigned integer.
Definition: types.h:47
Uint64 Size
Unsigned integral type that is large enough to hold the size of all types.
Definition: types.h:112
float Float32
32-bit float.
Definition: types.h:51
unsigned short Uint16
16-bit unsigned integer.
Definition: types.h:48
signed int Sint32
32-bit signed integer.
Definition: types.h:46
Float32 length(Float32 a)
Returns the Euclidean norm of the scalar a (its absolute value).
Definition: function.h:1114
Color clamp(const Color &c, const Color &low, const Color &high)
Returns the color c elementwise clamped to the range [low, high].
Definition: color.h:525
Canvas_type
The type of a canvas in an mi::neuraylib::IRender_target_base.
Definition: irender_target.h:32
@ TYPE_DEPTH
"Float32" orthogonal distance to sensor
Definition: irender_target.h:42
@ TYPE_UNDEFINED
not a valid canvas type
Definition: irender_target.h:62
@ TYPE_RESULT
"Color" (includes alpha) or "Rgb_fp" result
Definition: irender_target.h:33
@ TYPE_NORMAL
"Float32_3" shading normal
Definition: irender_target.h:46