ale/d2/render/incremental.h
2022-07-30 14:46:04 -03:00

428 lines
10 KiB
C++

// Copyright 2002, 2004, 2007 David Hilvert <dhilvert@auricle.dyndns.org>,
// <dhilvert@ugcs.caltech.edu>
/* This file is part of the Anti-Lamenessing Engine.
The Anti-Lamenessing Engine is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
The Anti-Lamenessing Engine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the Anti-Lamenessing Engine; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __incremental_h__
#define __incremental_h__
#include "invariant.h"
#include "../render.h"
#include "../transformation.h"
#include "../image.h"
#include "../point.h"
/*
* Class for incremental renderers.
*/
class incremental : public render {
protected:
image_weighted_avg *accum_image;
invariant *inv;
/*
* Set extents of image and weight according to a new image to be
* merged. This function should remove only superfluous undefined
* areas.
*/
void set_extents_by_map(unsigned int frame_num, transformation t) {
assert (accum_image != NULL);
ale_pos extend_offset_i = accum_image->offset()[0];
ale_pos extend_offset_j = accum_image->offset()[1];
int extend_top = 0;
int extend_bottom = 0;
int extend_left = 0;
int extend_right = 0;
ale_pos zero = 0;
ale_pos infinity = 1 / zero;
assert (!finite(infinity));
assert (!isnan(infinity));
assert (infinity > 0);
point min, max;
min[0] = min[1] = infinity;
max[0] = max[1] = -infinity;
for (unsigned int i = 0; i < t.unscaled_height(); i++)
for (unsigned int j = 0; j < t.unscaled_width(); j++) {
if (is_excluded_f(i, j, frame_num))
continue;
point p = t.transform_unscaled(point(i, j));
if (is_excluded_r(accum_image->offset(), p, frame_num))
continue;
if (p[0] < min[0]) {
min[0] = p[0];
}
if (p[0] > max[0]) {
max[0] = p[0];
}
if (p[1] < min[1]) {
min[1] = p[1];
}
if (p[1] > max[1]) {
max[1] = p[1];
}
}
if (!finite(max[0])
|| !finite(max[1])
|| !finite(min[0])
|| !finite(min[1]))
return;
extend_top = (int) ceil(extend_offset_i - floor(min[0]));
extend_left = (int) ceil(extend_offset_j - floor(min[1]));
extend_bottom = (int) ceil(ceil(max[0]) - (ale_pos) (accum_image->height() - 1 + extend_offset_i));
extend_right = (int) ceil(ceil(max[1]) - (ale_pos) (accum_image->width() - 1 + extend_offset_j));
accum_image->_extend(extend_top, extend_bottom,
extend_left, extend_right);
}
void increase_extents_by_map(unsigned int frame_num, transformation t) {
assert (accum_image != NULL);
ale_pos extend_offset_i = accum_image->offset()[0];
ale_pos extend_offset_j = accum_image->offset()[1];
int extend_top = 0;
int extend_bottom = 0;
int extend_left = 0;
int extend_right = 0;
double zero = 0;
double infinity = 1 / zero;
assert (!finite(infinity));
assert (!isnan(infinity));
assert (infinity > 0);
point min, max;
min[0] = min[1] = infinity;
max[0] = max[1] = -infinity;
for (unsigned int i = 0; i < t.unscaled_height(); i++)
for (unsigned int j = 0; j < t.unscaled_width(); j++) {
if (is_excluded_f(i, j, frame_num))
continue;
point p = t.transform_unscaled(point(i, j));
if (is_excluded_r(point(0, 0), p, frame_num))
continue;
if (p[0] < min[0]) {
min[0] = p[0];
}
if (p[0] > max[0]) {
max[0] = p[0];
}
if (p[1] < min[1]) {
min[1] = p[1];
}
if (p[1] > max[1]) {
max[1] = p[1];
}
}
if (!finite(max[0])
|| !finite(max[1])
|| !finite(min[0])
|| !finite(min[1]))
return;
if (ceil(min[0]) < extend_offset_i)
extend_top = (int) ceil(extend_offset_i - floor(min[0]));
if (ceil(min[1]) < extend_offset_j)
extend_left = (int) ceil(extend_offset_j - floor(min[1]));
if (floor(max[0]) > accum_image->height() - 1 + extend_offset_i)
extend_bottom = (int) ceil(ceil(max[0]) - (ale_pos) (accum_image->height() - 1 + extend_offset_i));
if (floor(max[1]) > accum_image->width() - 1 + extend_offset_j)
extend_right = (int) ceil(ceil(max[1]) - (ale_pos) (accum_image->width() - 1 + extend_offset_j));
accum_image->_extend(extend_top, extend_bottom,
extend_left, extend_right);
}
/*
* Merge operation for a single pixel in the accumulated image.
*/
void _merge_pixel(int frame, const image *delta, transformation t, int i, int j, const filter::ssfe *_ssfe) {
if (_ssfe->ex_is_honored() && is_excluded_r(i, j, frame))
return;
if (accum_image->accumulate_norender(i, j))
return;
/*
* Pixel value to be merged, and the associated
* confidence
*/
pixel value, confidence;
if (exposure::get_confidence() != 0) {
_ssfe->filtered(i, j, frame, &value, &confidence, ((pixel) accum_image->get_pixel(i, j)), accum_image->get_weights()->get_pixel(i, j));
} else {
_ssfe->filtered(i, j, frame, &value, &confidence);
}
accum_image->accumulate(i, j, frame, value, confidence);
}
/*
* Merge part of a delta frame with part of the accumulated image using
* the specified transformation.
*/
class merge : public thread::decompose_domain {
incremental *instance;
int frame;
const image *delta;
transformation t;
invariant *inv;
image_weighted_avg *accum_image;
protected:
void prepare_subdomains(unsigned int N) {
ale_pos_disable_casting();
ale_real_disable_casting();
ale_accum_disable_casting();
}
void subdomain_algorithm(unsigned int thread,
int i_min, int i_max, int j_min, int j_max) {
point offset = accum_image->offset();
assert (accum_image != NULL);
assert (delta != NULL);
const filter::ssfe *_ssfe = inv->ssfe();
for (int i = i_min; i < i_max; i++)
for (int j = j_min; j < j_max; j++) {
#if 0
/*
* This is untested, but it should work, and is less
* verbose than what follows.
*/
instance->_merge_pixel(frame, delta, t, i, j, _ssfe);
#else
if (_ssfe->ex_is_honored() && instance->is_excluded_r(i, j, frame))
continue;
if (accum_image->accumulate_norender(i, j))
continue;
/*
* Pixel value to be merged, and the associated
* confidence
*/
pixel value, confidence;
if (exposure::get_confidence() != 0) {
_ssfe->filtered(i, j, frame, &value, &confidence,
((pixel) accum_image->get_pixel(i, j)),
accum_image->get_weights()->get_pixel(i, j));
} else {
_ssfe->filtered(i, j, frame, &value, &confidence);
}
accum_image->accumulate(i, j, frame, value, confidence);
#endif
}
}
void finish_subdomains(unsigned int N) {
ale_pos_enable_casting();
ale_real_enable_casting();
ale_accum_enable_casting();
}
public:
merge(incremental *_instance,
int _frame,
const image *_delta,
transformation _t) : decompose_domain(0, _instance->accum_image->height(),
0, _instance->accum_image->width()),
t(_t) {
instance = _instance;
frame = _frame;
delta = _delta;
t = _t;
inv = instance->inv;
accum_image = instance->accum_image;
}
};
void
_merge(int frame, const image *delta, transformation t) {
ui::get()->d2_incremental_start();
point offset = accum_image->offset();
assert (accum_image != NULL);
assert (delta != NULL);
const filter::ssfe *_ssfe = inv->ssfe();
_ssfe->set_parameters(t, delta, offset);
merge m(this, frame, delta, t);
m.run();
ui::get()->d2_incremental_stop();
}
public:
/*
* Constructor
*/
incremental(invariant *inv) {
this->inv = inv;
accum_image = NULL;
}
/*
* Invariant
*/
const invariant *get_invariant() const {
return inv;
}
/*
* Result of rendering.
*/
virtual const image *get_image() const {
assert (accum_image != NULL);
return accum_image;
}
/*
* Definition map. Unit-depth image whose pixels are nonzero where
* the image is defined.
*/
virtual const image *get_defined() const {
assert (accum_image != NULL);
return accum_image->get_weights();
}
/*
* Perform the current rendering step.
*/
virtual void step() {
/*
* Dynamic invariants are not incrementally updated.
*/
if (inv->ssfe()->get_scaled_filter()->is_dynamic()) {
/*
* Create a trivial image for the case where there is
* no chain suffix.
*/
if (accum_image == NULL)
accum_image = new image_weighted_simple(1, 1, 3, inv);
return;
}
assert (get_step() >= -1);
if (get_step() == 0) {
transformation t = align::of(0);
const image *im = image_rw::open(0);
ui::get()->rendering();
if (inv->is_median())
accum_image = new image_weighted_median(1, 1, 3);
else
accum_image = new image_weighted_simple(1, 1, 3, inv);
set_extents_by_map(0, t);
_merge(0, im, t);
image_rw::close(0);
} else if (align::match(get_step())) {
transformation t = align::of(get_step());
ui::get()->rendering();
if (is_extend())
increase_extents_by_map(get_step(), t);
const image *im = image_rw::open(get_step());
_merge(get_step(), im, t);
image_rw::close(get_step());
}
}
virtual void init_point_renderer(unsigned int h, unsigned int w, unsigned int d) {
assert(accum_image == NULL);
if (inv->is_median())
accum_image = new image_weighted_median(h, w, d);
else
accum_image = new image_weighted_simple(h, w, d, inv);
assert(accum_image);
}
virtual void point_render(unsigned int i, unsigned int j, unsigned int f, transformation t) {
const image *im = d2::image_rw::get_open(f);
const filter::ssfe *_ssfe = inv->ssfe();
_ssfe->set_parameters(t, im, accum_image->offset());
_merge_pixel(f, im, t, i, j, _ssfe);
}
virtual void finish_point_rendering() {
return;
}
void free_memory() {
delete accum_image;
accum_image = NULL;
}
};
#endif