This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/intern/cycles/blender/blender_session.cpp
Sergey Sharybin c5d7ea091f Changes to partial update during rendering
Summary:
Mainly addressed to solve old TODO with color managed fallback
to CPU mode when displaying render result during rendering.

That fallback was caused by the fact that partial image update
was always acquiring image buffer for composite output and was
only modifying display buffer directly.

This was a big issue for Cycles rendering which renders layers
one by one and wanted to display progress of each individual
layer. This lead to situations when display buffer was based on
what Cycles passes via RenderResult and didn't take layer/pass
from image editor header into account.

Now made it so image buffer which partial update is operating
with always corresponds to what is set in image editor header.

To make Cycles displaying progress of all the layers one by one
made it so image_rect_update switches image editor user to
newly rendering render layer. It happens only once when render
engine starts rendering next render layer, so should not be
annoying for navigation during rendering.

Additional change to render engines was done to make it so
they're able to merge composite output to final result
without marking tile as done. This is done via do_merge_result
argument to end_result() callback. This argument is optional
so should not break script compatibility.

Additional changes:

- Partial display update for Blender Internal now happens from
  the same thread as tile rendering. This makes it so display
  conversion (which could be pretty heavy actually) is done in
  separate threads. Also gives better UI feedback when rendering
  easy scene with small tiles.

- Avoid freeing/allocating byte buffer for render result
  if it's owned by the image buffer. Only mark it as invalid
  for color management.

  Saves loads of buffer re-allocations in cases when having
  several image editors opened with render result. This change
  in conjunction with the rest of the patch gave around
  50%-100% speedup of render time when displaying non-combined
  pass during rendering on my laptop.

- Partial display buffer update was wrong for buffers with number
  of channels different from 4.

- Remove unused window from RenderJob.

- Made image_buffer_rect_update static since it's only used
  in single file.

Reviewers: brecht

Reviewed By: brecht

CC: dingto

Differential Revision: http://developer.blender.org/D98
2013-12-17 23:42:38 +06:00

839 lines
24 KiB
C++

/*
* Copyright 2011-2013 Blender Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
#include "background.h"
#include "buffers.h"
#include "camera.h"
#include "device.h"
#include "integrator.h"
#include "film.h"
#include "light.h"
#include "scene.h"
#include "session.h"
#include "shader.h"
#include "util_color.h"
#include "util_foreach.h"
#include "util_function.h"
#include "util_progress.h"
#include "util_time.h"
#include "blender_sync.h"
#include "blender_session.h"
#include "blender_util.h"
CCL_NAMESPACE_BEGIN
BlenderSession::BlenderSession(BL::RenderEngine b_engine_, BL::UserPreferences b_userpref_,
BL::BlendData b_data_, BL::Scene b_scene_)
: b_engine(b_engine_), b_userpref(b_userpref_), b_data(b_data_), b_render(b_engine_.render()), b_scene(b_scene_),
b_v3d(PointerRNA_NULL), b_rv3d(PointerRNA_NULL)
{
/* offline render */
width = render_resolution_x(b_render);
height = render_resolution_y(b_render);
background = true;
last_redraw_time = 0.0;
start_resize_time = 0.0;
create_session();
}
BlenderSession::BlenderSession(BL::RenderEngine b_engine_, BL::UserPreferences b_userpref_,
BL::BlendData b_data_, BL::Scene b_scene_,
BL::SpaceView3D b_v3d_, BL::RegionView3D b_rv3d_, int width_, int height_)
: b_engine(b_engine_), b_userpref(b_userpref_), b_data(b_data_), b_render(b_scene_.render()), b_scene(b_scene_),
b_v3d(b_v3d_), b_rv3d(b_rv3d_)
{
/* 3d view render */
width = width_;
height = height_;
background = false;
last_redraw_time = 0.0;
start_resize_time = 0.0;
create_session();
session->start();
}
BlenderSession::~BlenderSession()
{
free_session();
}
void BlenderSession::create_session()
{
SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background);
SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
/* reset status/progress */
last_status = "";
last_progress = -1.0f;
start_resize_time = 0.0;
/* create scene */
scene = new Scene(scene_params, session_params.device);
/* create session */
session = new Session(session_params);
session->scene = scene;
session->progress.set_update_callback(function_bind(&BlenderSession::tag_redraw, this));
session->progress.set_cancel_callback(function_bind(&BlenderSession::test_cancel, this));
session->set_pause(BlenderSync::get_session_pause(b_scene, background));
/* create sync */
sync = new BlenderSync(b_engine, b_data, b_scene, scene, !background, session->progress, session_params.device.type == DEVICE_CPU);
if(b_v3d) {
/* full data sync */
sync->sync_data(b_v3d, b_engine.camera_override());
sync->sync_view(b_v3d, b_rv3d, width, height);
}
else {
/* for final render we will do full data sync per render layer, only
* do some basic syncing here, no objects or materials for speed */
sync->sync_render_layers(b_v3d, NULL);
sync->sync_integrator();
sync->sync_camera(b_render, b_engine.camera_override(), width, height);
}
/* set buffer parameters */
BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, b_scene, b_v3d, b_rv3d, scene->camera, width, height);
session->reset(buffer_params, session_params.samples);
b_engine.use_highlight_tiles(session_params.progressive_refine == false);
/* setup callbacks for builtin image support */
scene->image_manager->builtin_image_info_cb = function_bind(&BlenderSession::builtin_image_info, this, _1, _2, _3, _4, _5, _6);
scene->image_manager->builtin_image_pixels_cb = function_bind(&BlenderSession::builtin_image_pixels, this, _1, _2, _3);
scene->image_manager->builtin_image_float_pixels_cb = function_bind(&BlenderSession::builtin_image_float_pixels, this, _1, _2, _3);
}
void BlenderSession::reset_session(BL::BlendData b_data_, BL::Scene b_scene_)
{
b_data = b_data_;
b_render = b_engine.render();
b_scene = b_scene_;
SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background);
SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
width = render_resolution_x(b_render);
height = render_resolution_y(b_render);
if(scene->params.modified(scene_params) ||
session->params.modified(session_params) ||
!scene_params.persistent_data)
{
/* if scene or session parameters changed, it's easier to simply re-create
* them rather than trying to distinguish which settings need to be updated
*/
delete session;
create_session();
return;
}
session->progress.reset();
scene->reset();
session->tile_manager.set_tile_order(session_params.tile_order);
/* peak memory usage should show current render peak, not peak for all renders
* made by this render session
*/
session->stats.mem_peak = session->stats.mem_used;
/* sync object should be re-created */
sync = new BlenderSync(b_engine, b_data, b_scene, scene, !background, session->progress, session_params.device.type == DEVICE_CPU);
/* for final render we will do full data sync per render layer, only
* do some basic syncing here, no objects or materials for speed */
sync->sync_render_layers(b_v3d, NULL);
sync->sync_integrator();
sync->sync_camera(b_render, b_engine.camera_override(), width, height);
BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, b_scene, PointerRNA_NULL, PointerRNA_NULL, scene->camera, width, height);
session->reset(buffer_params, session_params.samples);
b_engine.use_highlight_tiles(session_params.progressive_refine == false);
/* reset time */
start_resize_time = 0.0;
}
void BlenderSession::free_session()
{
if(sync)
delete sync;
delete session;
}
static PassType get_pass_type(BL::RenderPass b_pass)
{
switch(b_pass.type()) {
case BL::RenderPass::type_COMBINED:
return PASS_COMBINED;
case BL::RenderPass::type_Z:
return PASS_DEPTH;
case BL::RenderPass::type_MIST:
return PASS_MIST;
case BL::RenderPass::type_NORMAL:
return PASS_NORMAL;
case BL::RenderPass::type_OBJECT_INDEX:
return PASS_OBJECT_ID;
case BL::RenderPass::type_UV:
return PASS_UV;
case BL::RenderPass::type_VECTOR:
return PASS_MOTION;
case BL::RenderPass::type_MATERIAL_INDEX:
return PASS_MATERIAL_ID;
case BL::RenderPass::type_DIFFUSE_DIRECT:
return PASS_DIFFUSE_DIRECT;
case BL::RenderPass::type_GLOSSY_DIRECT:
return PASS_GLOSSY_DIRECT;
case BL::RenderPass::type_TRANSMISSION_DIRECT:
return PASS_TRANSMISSION_DIRECT;
case BL::RenderPass::type_SUBSURFACE_DIRECT:
return PASS_SUBSURFACE_DIRECT;
case BL::RenderPass::type_DIFFUSE_INDIRECT:
return PASS_DIFFUSE_INDIRECT;
case BL::RenderPass::type_GLOSSY_INDIRECT:
return PASS_GLOSSY_INDIRECT;
case BL::RenderPass::type_TRANSMISSION_INDIRECT:
return PASS_TRANSMISSION_INDIRECT;
case BL::RenderPass::type_SUBSURFACE_INDIRECT:
return PASS_SUBSURFACE_INDIRECT;
case BL::RenderPass::type_DIFFUSE_COLOR:
return PASS_DIFFUSE_COLOR;
case BL::RenderPass::type_GLOSSY_COLOR:
return PASS_GLOSSY_COLOR;
case BL::RenderPass::type_TRANSMISSION_COLOR:
return PASS_TRANSMISSION_COLOR;
case BL::RenderPass::type_SUBSURFACE_COLOR:
return PASS_SUBSURFACE_COLOR;
case BL::RenderPass::type_EMIT:
return PASS_EMISSION;
case BL::RenderPass::type_ENVIRONMENT:
return PASS_BACKGROUND;
case BL::RenderPass::type_AO:
return PASS_AO;
case BL::RenderPass::type_SHADOW:
return PASS_SHADOW;
case BL::RenderPass::type_DIFFUSE:
case BL::RenderPass::type_COLOR:
case BL::RenderPass::type_REFRACTION:
case BL::RenderPass::type_SPECULAR:
case BL::RenderPass::type_REFLECTION:
return PASS_NONE;
}
return PASS_NONE;
}
static BL::RenderResult begin_render_result(BL::RenderEngine b_engine, int x, int y, int w, int h, const char *layername)
{
return b_engine.begin_result(x, y, w, h, layername);
}
static void end_render_result(BL::RenderEngine b_engine, BL::RenderResult b_rr, bool cancel, bool do_merge_results)
{
b_engine.end_result(b_rr, (int)cancel, (int)do_merge_results);
}
void BlenderSession::do_write_update_render_tile(RenderTile& rtile, bool do_update_only)
{
BufferParams& params = rtile.buffers->params;
int x = params.full_x - session->tile_manager.params.full_x;
int y = params.full_y - session->tile_manager.params.full_y;
int w = params.width;
int h = params.height;
/* get render result */
BL::RenderResult b_rr = begin_render_result(b_engine, x, y, w, h, b_rlay_name.c_str());
/* can happen if the intersected rectangle gives 0 width or height */
if (b_rr.ptr.data == NULL) {
return;
}
BL::RenderResult::layers_iterator b_single_rlay;
b_rr.layers.begin(b_single_rlay);
/* layer will be missing if it was disabled in the UI */
if(b_single_rlay == b_rr.layers.end())
return;
BL::RenderLayer b_rlay = *b_single_rlay;
if (do_update_only) {
/* update only needed */
if (rtile.sample != 0) {
/* sample would be zero at initial tile update, which is only needed
* to tag tile form blender side as IN PROGRESS for proper highlight
* no buffers should be sent to blender yet
*/
update_render_result(b_rr, b_rlay, rtile);
}
end_render_result(b_engine, b_rr, true, true);
}
else {
/* write result */
write_render_result(b_rr, b_rlay, rtile);
end_render_result(b_engine, b_rr, false, true);
}
}
void BlenderSession::write_render_tile(RenderTile& rtile)
{
do_write_update_render_tile(rtile, false);
}
void BlenderSession::update_render_tile(RenderTile& rtile)
{
/* use final write for preview renders, otherwise render result wouldn't be
* be updated in blender side
* would need to be investigated a bit further, but for now shall be fine
*/
if (!b_engine.is_preview())
do_write_update_render_tile(rtile, true);
else
do_write_update_render_tile(rtile, false);
}
void BlenderSession::render()
{
/* set callback to write out render results */
session->write_render_tile_cb = function_bind(&BlenderSession::write_render_tile, this, _1);
session->update_render_tile_cb = function_bind(&BlenderSession::update_render_tile, this, _1);
/* get buffer parameters */
SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, b_scene, b_v3d, b_rv3d, scene->camera, width, height);
/* render each layer */
BL::RenderSettings r = b_scene.render();
BL::RenderSettings::layers_iterator b_iter;
for(r.layers.begin(b_iter); b_iter != r.layers.end(); ++b_iter) {
b_rlay_name = b_iter->name();
/* temporary render result to find needed passes */
BL::RenderResult b_rr = begin_render_result(b_engine, 0, 0, 1, 1, b_rlay_name.c_str());
BL::RenderResult::layers_iterator b_single_rlay;
b_rr.layers.begin(b_single_rlay);
/* layer will be missing if it was disabled in the UI */
if(b_single_rlay == b_rr.layers.end()) {
end_render_result(b_engine, b_rr, true, false);
continue;
}
BL::RenderLayer b_rlay = *b_single_rlay;
/* add passes */
vector<Pass> passes;
Pass::add(PASS_COMBINED, passes);
if(session_params.device.advanced_shading) {
/* loop over passes */
BL::RenderLayer::passes_iterator b_pass_iter;
for(b_rlay.passes.begin(b_pass_iter); b_pass_iter != b_rlay.passes.end(); ++b_pass_iter) {
BL::RenderPass b_pass(*b_pass_iter);
PassType pass_type = get_pass_type(b_pass);
if(pass_type == PASS_MOTION && scene->integrator->motion_blur)
continue;
if(pass_type != PASS_NONE)
Pass::add(pass_type, passes);
}
}
/* free result without merging */
end_render_result(b_engine, b_rr, true, false);
buffer_params.passes = passes;
scene->film->tag_passes_update(scene, passes);
scene->film->tag_update(scene);
scene->integrator->tag_update(scene);
/* update scene */
sync->sync_camera(b_render, b_engine.camera_override(), width, height);
sync->sync_data(b_v3d, b_engine.camera_override(), b_rlay_name.c_str());
/* update number of samples per layer */
int samples = sync->get_layer_samples();
bool bound_samples = sync->get_layer_bound_samples();
if(samples != 0 && (!bound_samples || (samples < session_params.samples)))
session->reset(buffer_params, samples);
else
session->reset(buffer_params, session_params.samples);
/* render */
session->start();
session->wait();
if(session->progress.get_cancel())
break;
}
/* clear callback */
session->write_render_tile_cb = NULL;
session->update_render_tile_cb = NULL;
/* free all memory used (host and device), so we wouldn't leave render
* engine with extra memory allocated
*/
session->device_free();
delete sync;
sync = NULL;
}
void BlenderSession::do_write_update_render_result(BL::RenderResult b_rr, BL::RenderLayer b_rlay, RenderTile& rtile, bool do_update_only)
{
RenderBuffers *buffers = rtile.buffers;
/* copy data from device */
if(!buffers->copy_from_device())
return;
BufferParams& params = buffers->params;
float exposure = scene->film->exposure;
vector<float> pixels(params.width*params.height*4);
if (!do_update_only) {
/* copy each pass */
BL::RenderLayer::passes_iterator b_iter;
for(b_rlay.passes.begin(b_iter); b_iter != b_rlay.passes.end(); ++b_iter) {
BL::RenderPass b_pass(*b_iter);
/* find matching pass type */
PassType pass_type = get_pass_type(b_pass);
int components = b_pass.channels();
/* copy pixels */
if(!buffers->get_pass_rect(pass_type, exposure, rtile.sample, components, &pixels[0]))
memset(&pixels[0], 0, pixels.size()*sizeof(float));
b_pass.rect(&pixels[0]);
}
}
/* copy combined pass */
if(buffers->get_pass_rect(PASS_COMBINED, exposure, rtile.sample, 4, &pixels[0]))
b_rlay.rect(&pixels[0]);
/* tag result as updated */
b_engine.update_result(b_rr);
}
void BlenderSession::write_render_result(BL::RenderResult b_rr, BL::RenderLayer b_rlay, RenderTile& rtile)
{
do_write_update_render_result(b_rr, b_rlay, rtile, false);
}
void BlenderSession::update_render_result(BL::RenderResult b_rr, BL::RenderLayer b_rlay, RenderTile& rtile)
{
do_write_update_render_result(b_rr, b_rlay, rtile, true);
}
void BlenderSession::synchronize()
{
/* only used for viewport render */
if(!b_v3d)
return;
/* on session/scene parameter changes, we recreate session entirely */
SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background);
SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
if(session->params.modified(session_params) ||
scene->params.modified(scene_params))
{
free_session();
create_session();
session->start();
return;
}
/* increase samples, but never decrease */
session->set_samples(session_params.samples);
session->set_pause(BlenderSync::get_session_pause(b_scene, background));
/* copy recalc flags, outside of mutex so we can decide to do the real
* synchronization at a later time to not block on running updates */
sync->sync_recalc();
/* try to acquire mutex. if we don't want to or can't, come back later */
if(!session->ready_to_reset() || !session->scene->mutex.try_lock()) {
tag_update();
return;
}
/* data and camera synchronize */
sync->sync_data(b_v3d, b_engine.camera_override());
if(b_rv3d)
sync->sync_view(b_v3d, b_rv3d, width, height);
else
sync->sync_camera(b_render, b_engine.camera_override(), width, height);
/* unlock */
session->scene->mutex.unlock();
/* reset if needed */
if(scene->need_reset()) {
BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, b_scene, b_v3d, b_rv3d, scene->camera, width, height);
session->reset(buffer_params, session_params.samples);
/* reset time */
start_resize_time = 0.0;
}
}
bool BlenderSession::draw(int w, int h)
{
/* pause in redraw in case update is not being called due to final render */
session->set_pause(BlenderSync::get_session_pause(b_scene, background));
/* before drawing, we verify camera and viewport size changes, because
* we do not get update callbacks for those, we must detect them here */
if(session->ready_to_reset()) {
bool reset = false;
/* if dimensions changed, reset */
if(width != w || height != h) {
if(start_resize_time == 0.0) {
/* don't react immediately to resizes to avoid flickery resizing
* of the viewport, and some window managers changing the window
* size temporarily on unminimize */
start_resize_time = time_dt();
tag_redraw();
}
else if(time_dt() - start_resize_time < 0.2) {
tag_redraw();
}
else {
width = w;
height = h;
reset = true;
}
}
/* try to acquire mutex. if we can't, come back later */
if(!session->scene->mutex.try_lock()) {
tag_update();
}
else {
/* update camera from 3d view */
sync->sync_view(b_v3d, b_rv3d, width, height);
if(scene->camera->need_update)
reset = true;
session->scene->mutex.unlock();
}
/* reset if requested */
if(reset) {
SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, b_scene, b_v3d, b_rv3d, scene->camera, width, height);
session->reset(buffer_params, session_params.samples);
start_resize_time = 0.0;
}
}
else {
tag_update();
}
/* update status and progress for 3d view draw */
update_status_progress();
/* draw */
BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, b_scene, b_v3d, b_rv3d, scene->camera, width, height);
if(session->params.display_buffer_linear)
b_engine.bind_display_space_shader(b_scene);
bool draw_ok = !session->draw(buffer_params);
if(session->params.display_buffer_linear)
b_engine.unbind_display_space_shader();
return draw_ok;
}
void BlenderSession::get_status(string& status, string& substatus)
{
session->progress.get_status(status, substatus);
}
void BlenderSession::get_progress(float& progress, double& total_time)
{
double tile_time;
int tile, sample, samples_per_tile;
int tile_total = session->tile_manager.state.num_tiles;
session->progress.get_tile(tile, total_time, tile_time);
sample = session->progress.get_sample();
samples_per_tile = session->tile_manager.num_samples;
if(samples_per_tile && tile_total)
progress = ((float)sample / (float)(tile_total * samples_per_tile));
else
progress = 0.0;
}
void BlenderSession::update_status_progress()
{
string timestatus, status, substatus;
string scene = "";
float progress;
double total_time, remaining_time = 0;
char time_str[128];
float mem_used = (float)session->stats.mem_used / 1024.0f / 1024.0f;
float mem_peak = (float)session->stats.mem_peak / 1024.0f / 1024.0f;
int samples = session->tile_manager.state.sample + 1;
int total_samples = session->tile_manager.num_samples;
get_status(status, substatus);
get_progress(progress, total_time);
if(background) {
if(progress>0)
remaining_time = (1-progress) * (total_time / progress);
scene += " | " + b_scene.name();
if(b_rlay_name != "")
scene += ", " + b_rlay_name;
}
else {
BLI_timestr(total_time, time_str, sizeof(time_str));
timestatus = "Time:" + string(time_str) + " | ";
if(samples > 0 && total_samples != USHRT_MAX)
remaining_time = (total_samples - samples) * (total_time / samples);
}
if(remaining_time>0) {
BLI_timestr(remaining_time, time_str, sizeof(time_str));
timestatus += "Remaining:" + string(time_str) + " | ";
}
timestatus += string_printf("Mem:%.2fM, Peak:%.2fM", mem_used, mem_peak);
if(status.size() > 0)
status = " | " + status;
if(substatus.size() > 0)
status += " | " + substatus;
if(status != last_status) {
b_engine.update_memory_stats(mem_used, mem_peak);
last_status = status;
}
if(progress != last_progress) {
b_engine.update_progress(progress);
b_engine.update_stats("", (timestatus + scene + status).c_str());
last_progress = progress;
}
}
void BlenderSession::tag_update()
{
/* tell blender that we want to get another update callback */
b_engine.tag_update();
}
void BlenderSession::tag_redraw()
{
if(background) {
/* update stats and progress, only for background here because
* in 3d view we do it in draw for thread safety reasons */
update_status_progress();
/* offline render, redraw if timeout passed */
if(time_dt() - last_redraw_time > 1.0) {
b_engine.tag_redraw();
last_redraw_time = time_dt();
}
}
else {
/* tell blender that we want to redraw */
b_engine.tag_redraw();
}
}
void BlenderSession::test_cancel()
{
/* test if we need to cancel rendering */
if(background)
if(b_engine.test_break())
session->progress.set_cancel("Cancelled");
}
/* builtin image file name is actually an image datablock name with
* absolute sequence frame number concatenated via '@' character
*
* this function splits frame from builtin name
*/
int BlenderSession::builtin_image_frame(const string &builtin_name)
{
int last = builtin_name.find_last_of('@');
return atoi(builtin_name.substr(last + 1, builtin_name.size() - last - 1).c_str());
}
void BlenderSession::builtin_image_info(const string &builtin_name, void *builtin_data, bool &is_float, int &width, int &height, int &channels)
{
PointerRNA ptr;
RNA_id_pointer_create((ID*)builtin_data, &ptr);
BL::Image b_image(ptr);
if(b_image) {
is_float = b_image.is_float();
width = b_image.size()[0];
height = b_image.size()[1];
channels = b_image.channels();
}
else {
is_float = false;
width = 0;
height = 0;
channels = 0;
}
}
bool BlenderSession::builtin_image_pixels(const string &builtin_name, void *builtin_data, unsigned char *pixels)
{
int frame = builtin_image_frame(builtin_name);
PointerRNA ptr;
RNA_id_pointer_create((ID*)builtin_data, &ptr);
BL::Image b_image(ptr);
if(b_image) {
int width = b_image.size()[0];
int height = b_image.size()[1];
int channels = b_image.channels();
unsigned char *image_pixels;
image_pixels = image_get_pixels_for_frame(b_image, frame);
if(image_pixels) {
memcpy(pixels, image_pixels, width * height * channels * sizeof(unsigned char));
MEM_freeN(image_pixels);
}
else {
if(channels == 1) {
memset(pixels, 0, width * height * sizeof(unsigned char));
}
else {
unsigned char *cp = pixels;
for(int i = 0; i < width * height; i++, cp += channels) {
cp[0] = 255;
cp[1] = 0;
cp[2] = 255;
if(channels == 4)
cp[3] = 255;
}
}
}
/* premultiply, byte images are always straight for blender */
unsigned char *cp = pixels;
for(int i = 0; i < width * height; i++, cp += channels) {
cp[0] = (cp[0] * cp[3]) >> 8;
cp[1] = (cp[1] * cp[3]) >> 8;
cp[2] = (cp[2] * cp[3]) >> 8;
}
return true;
}
return false;
}
bool BlenderSession::builtin_image_float_pixels(const string &builtin_name, void *builtin_data, float *pixels)
{
int frame = builtin_image_frame(builtin_name);
PointerRNA ptr;
RNA_id_pointer_create((ID*)builtin_data, &ptr);
BL::Image b_image(ptr);
if(b_image) {
int width = b_image.size()[0];
int height = b_image.size()[1];
int channels = b_image.channels();
float *image_pixels;
image_pixels = image_get_float_pixels_for_frame(b_image, frame);
if(image_pixels) {
memcpy(pixels, image_pixels, width * height * channels * sizeof(float));
MEM_freeN(image_pixels);
}
else {
if(channels == 1) {
memset(pixels, 0, width * height * sizeof(float));
}
else {
float *fp = pixels;
for(int i = 0; i < width * height; i++, fp += channels) {
fp[0] = 1.0f;
fp[1] = 0.0f;
fp[2] = 1.0f;
if(channels == 4)
fp[3] = 1.0f;
}
}
}
return true;
}
return false;
}
CCL_NAMESPACE_END