520 lines
13 KiB
C++
520 lines
13 KiB
C++
// Copyright 2002, 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
|
|
*/
|
|
|
|
/*
|
|
* combine.h: A renderer that combines two renderings.
|
|
*/
|
|
|
|
#ifndef __combine_h__
|
|
#define __combine_h__
|
|
|
|
#include "../transformation.h"
|
|
#include "../image.h"
|
|
#include "../point.h"
|
|
#include "incremental.h"
|
|
#include "../filter/filter.h"
|
|
|
|
/*
|
|
* Combine two renderings.
|
|
*
|
|
* Available data is taken from the PARTIAL rendering. When no data from
|
|
* the PARTIAL rendering is available, data from the DEFAULT rendering
|
|
* is substituted.
|
|
*/
|
|
|
|
class combine : public render {
|
|
private:
|
|
render *_default;
|
|
render *partial;
|
|
mutable image *output_image;
|
|
mutable image *defined_image;
|
|
int synced;
|
|
|
|
class refilter : public thread::decompose_domain {
|
|
combine *c;
|
|
const render *fine;
|
|
const render *coarse;
|
|
const filter::filter *f;
|
|
const image *fine_weight;
|
|
const image *fine_image;
|
|
const image *coarse_image;
|
|
const image *coarse_defined;
|
|
image *output_image;
|
|
|
|
/*
|
|
* Attempt to determine a distance by finding two nearby defined
|
|
* pixels, such that each pixel is in a 90-degree axis-aligned
|
|
* cone opposite the other.
|
|
*/
|
|
|
|
ale_pos find_nonzero_weight_distance(int i, int j, int k) {
|
|
|
|
assert (i >= 0);
|
|
assert (j >= 0);
|
|
assert (i < (int) coarse_defined->height());
|
|
assert (j < (int) coarse_defined->width());
|
|
|
|
assert (coarse_defined->get_chan(i, j, k) > 0);
|
|
|
|
ale_pos zero = +0.0;
|
|
ale_pos one = +1.0;
|
|
|
|
ale_pos nearest = one / zero;
|
|
|
|
assert (isinf(nearest) && nearest > 0);
|
|
|
|
int radius = 0;
|
|
int in_bounds = 1;
|
|
|
|
int coords[2];
|
|
|
|
while (radius < nearest && in_bounds) {
|
|
in_bounds = 0;
|
|
|
|
for (int ii = i - radius; ii <= i + radius; ii++)
|
|
for (int jj = j - radius; jj <= j + radius;
|
|
jj += ((abs(i - ii) == radius)
|
|
? 1
|
|
: radius * 2)) {
|
|
if (ii < 0
|
|
|| jj < 0
|
|
|| ii >= (int) coarse_defined->height()
|
|
|| jj >= (int) coarse_defined->width()
|
|
|| !(coarse_defined->get_chan(ii, jj, k) > 0))
|
|
continue;
|
|
|
|
in_bounds = 1;
|
|
|
|
if (!(fine_weight->get_chan(ii, jj, k) > 0))
|
|
continue;
|
|
|
|
ale_pos distance = sqrt( (ale_pos) ((i - ii) * (i - ii)
|
|
+ (j - jj) * (j - jj)));
|
|
|
|
if (distance < nearest) {
|
|
nearest = distance;
|
|
coords[0] = ii;
|
|
coords[1] = jj;
|
|
}
|
|
|
|
}
|
|
|
|
radius++;
|
|
}
|
|
|
|
if (isinf(nearest))
|
|
return nearest;
|
|
|
|
int cone_axis = 0;
|
|
int cone_dir = 1;
|
|
|
|
if (abs(coords[0] - i) < abs(coords[1] - j))
|
|
cone_axis = 1;
|
|
|
|
int orig_coords[2] = {i, j};
|
|
|
|
if (coords[cone_axis] - orig_coords[cone_axis] > 0)
|
|
cone_dir = -1;
|
|
|
|
nearest = one / zero;
|
|
|
|
assert (isinf(nearest) && nearest > 0);
|
|
|
|
radius = 1;
|
|
in_bounds = 1;
|
|
|
|
int coords2[2];
|
|
|
|
i = coords[0];
|
|
j = coords[1];
|
|
|
|
while (radius < nearest && in_bounds) {
|
|
in_bounds = 0;
|
|
|
|
coords2[cone_axis] = orig_coords[cone_axis] + radius * cone_dir;
|
|
|
|
for (coords2[1 - cone_axis] = orig_coords[1 - cone_axis] - radius;
|
|
coords2[1 - cone_axis] < orig_coords[1 - cone_axis] + radius;
|
|
coords2[1 - cone_axis]++) {
|
|
|
|
int ii = coords2[0];
|
|
int jj = coords2[1];
|
|
|
|
if (ii < 0
|
|
|| jj < 0
|
|
|| ii >= (int) coarse_defined->height()
|
|
|| jj >= (int) coarse_defined->width()
|
|
|| !(coarse_defined->get_chan(ii, jj, k) > 0))
|
|
continue;
|
|
|
|
in_bounds = 1;
|
|
|
|
if (!(fine_weight->get_chan(ii, jj, k) > 0))
|
|
continue;
|
|
|
|
ale_pos distance = sqrt( (ale_pos) ((i - ii) * (i - ii)
|
|
+ (j - jj) * (j - jj)));
|
|
|
|
if (distance < nearest)
|
|
nearest = distance;
|
|
}
|
|
|
|
radius++;
|
|
}
|
|
|
|
return nearest;
|
|
}
|
|
|
|
protected:
|
|
void subdomain_algorithm(unsigned int thread,
|
|
int i_min, int i_max, int j_min, int j_max) {
|
|
|
|
for (int i = i_min; i < i_max; i++)
|
|
for (int j = j_min; j < j_max; j++)
|
|
for (unsigned int k = 0; k < 3; k++){
|
|
|
|
if (!(coarse_defined->get_chan(i, j, k) > 0))
|
|
continue;
|
|
|
|
ale_pos filter_scale = 1;
|
|
ale_real filtered_weight;
|
|
ale_real filtered_value;
|
|
|
|
/*
|
|
* Attempt to set an initial filter scale based
|
|
* on the proximity of two nearby k-defined
|
|
* pixels.
|
|
*/
|
|
|
|
ale_pos n1 = find_nonzero_weight_distance(i, j, k);
|
|
|
|
if (!finite(n1)) {
|
|
output_image->set_chan(i, j, k, coarse_image->get_pixel(i, j)[k]);
|
|
continue;
|
|
}
|
|
|
|
filter_scale = n1;
|
|
|
|
do {
|
|
|
|
filtered_weight = 0;
|
|
filtered_value = 0;
|
|
|
|
/*
|
|
* lrintf() may be faster than ceil/floor() on some architectures.
|
|
* See render/psf/raster.h for more details.
|
|
*/
|
|
|
|
int support_extreme = (int) lrintf(f->support() * filter_scale);
|
|
assert (support_extreme >= 0);
|
|
|
|
for (int ii = -support_extreme;
|
|
ii < support_extreme; ii++)
|
|
for (int jj = -support_extreme;
|
|
jj < support_extreme; jj++) {
|
|
|
|
if (ii + i < 0
|
|
|| jj + j < 0
|
|
|| ii + i >= (int) fine_weight->height()
|
|
|| jj + j >= (int) fine_weight->width())
|
|
continue;
|
|
|
|
ale_real pw = fine_weight->get_chan(i + ii, j + jj, k);
|
|
|
|
if (!(pw > 0))
|
|
continue;
|
|
|
|
/*
|
|
* XXX: Set the weight to one
|
|
* for now, to prevent
|
|
* interference from certainty
|
|
* values calculated under
|
|
* different assumptions.
|
|
*/
|
|
|
|
pw = 1;
|
|
|
|
ale_real w = pw * f->response(point(ii / filter_scale,
|
|
jj / filter_scale));
|
|
|
|
ale_real v = fine_image->get_pixel(i + ii, j + jj)[k];
|
|
|
|
if (!finite(w) || !finite(v))
|
|
continue;
|
|
|
|
filtered_weight += w;
|
|
filtered_value += w * v;
|
|
}
|
|
|
|
if (filtered_weight < render::get_wt())
|
|
/* filter_scale += 1; */
|
|
filter_scale *= 2;
|
|
|
|
} while (filtered_weight < render::get_wt()
|
|
&& filter_scale < coarse_defined->width()
|
|
+ coarse_defined->height());
|
|
|
|
output_image->set_chan(i, j, k, filtered_value / filtered_weight);
|
|
}
|
|
}
|
|
public:
|
|
refilter(combine *_c,
|
|
const render *_fine,
|
|
const render *_coarse,
|
|
const filter::filter *_f,
|
|
const image *_fine_weight,
|
|
const image *_fine_image,
|
|
const image *_coarse_image,
|
|
const image *_coarse_defined,
|
|
image *_output_image) : decompose_domain(0, _coarse_defined->height(),
|
|
0, _coarse_defined->width()) {
|
|
|
|
c = _c;
|
|
fine = _fine;
|
|
coarse = _coarse;
|
|
f = _f;
|
|
fine_weight = _fine_weight;
|
|
fine_image = _fine_image;
|
|
coarse_image = _coarse_image;
|
|
coarse_defined = _coarse_defined;
|
|
output_image = _output_image;
|
|
}
|
|
};
|
|
|
|
const image *get_image_dynamic() const {
|
|
assert(typeid(*partial) == typeid(incremental));
|
|
|
|
if (typeid(*_default) != typeid(combine) || !synced) {
|
|
/*
|
|
* Degenerate case.
|
|
*/
|
|
output_image = _default->get_image()->clone("degenerate dynamic filter");
|
|
return output_image;
|
|
}
|
|
|
|
combine *c = (combine *)_default;
|
|
const render *fine = c->get_partial();
|
|
const render *coarse = c->get_default();
|
|
const filter::filter *f = ((incremental *)partial)->get_invariant()->ssfe()->
|
|
get_scaled_filter()->get_filter();
|
|
const image *fine_weight = fine->get_defined();
|
|
const image *fine_image = fine->get_image();
|
|
const image *coarse_image = coarse->get_image();
|
|
const image *coarse_defined = coarse->get_defined();
|
|
|
|
output_image = new_image_ale_real(coarse_defined->height(),
|
|
coarse_defined->width(), 3, NULL);
|
|
|
|
output_image->set_offset(coarse_defined->offset());
|
|
|
|
assert (coarse_defined->width() == fine_image->width());
|
|
assert (coarse_defined->height() == fine_image->height());
|
|
assert (coarse_defined->width() == fine_weight->width());
|
|
assert (coarse_defined->height() == fine_weight->height());
|
|
|
|
ui::get()->refilter_start();
|
|
|
|
refilter r(c, fine, coarse, f, fine_weight, fine_image, coarse_image,
|
|
coarse_defined, output_image);
|
|
r.run();
|
|
|
|
ui::get()->refilter_done();
|
|
|
|
return output_image;
|
|
}
|
|
public:
|
|
|
|
/*
|
|
* Constructor
|
|
*/
|
|
combine(render *_default, render *partial) {
|
|
this->_default = _default;
|
|
this->partial = partial;
|
|
this->output_image = NULL;
|
|
this->defined_image = NULL;
|
|
this->synced = 0;
|
|
}
|
|
|
|
virtual ~combine() {
|
|
if (output_image)
|
|
delete output_image;
|
|
if (defined_image)
|
|
delete defined_image;
|
|
}
|
|
|
|
/*
|
|
* Result of rendering.
|
|
*/
|
|
|
|
virtual const image *get_image() const {
|
|
|
|
if (output_image)
|
|
return output_image;
|
|
|
|
assert(typeid(*partial) != typeid(combine));
|
|
|
|
/*
|
|
* Dynamic filtering is handled separately.
|
|
*/
|
|
if (typeid(*partial) == typeid(incremental)
|
|
&& (((incremental *)partial)->get_invariant()->
|
|
ssfe()->get_scaled_filter()->is_dynamic()))
|
|
return get_image_dynamic();
|
|
|
|
const image *default_image = _default->get_image();
|
|
|
|
output_image = new_image_ale_real(default_image->height(),
|
|
default_image->width(), 3, NULL);
|
|
|
|
output_image->set_offset(default_image->offset());
|
|
|
|
const image *partial_image = partial->get_image();
|
|
const image *partial_weight = partial->get_defined();
|
|
|
|
assert (default_image->width() == partial_image->width());
|
|
assert (default_image->height() == partial_image->height());
|
|
|
|
for (unsigned int i = 0; i < default_image->height(); i++)
|
|
for (unsigned int j = 0; j < default_image->width(); j++)
|
|
output_image->set_pixel(i, j,
|
|
((ale_real) ((pixel) partial_weight->get_pixel(i, j)).min_norm()
|
|
>= render::get_wt())
|
|
? partial_image->get_pixel(i, j)
|
|
: default_image->get_pixel(i, j));
|
|
|
|
return output_image;
|
|
}
|
|
|
|
/*
|
|
* Definition map. Unit-depth image whose pixels are nonzero where
|
|
* the image is defined.
|
|
*/
|
|
|
|
virtual const image *get_defined() const {
|
|
unsigned int i, j, k;
|
|
|
|
if (defined_image)
|
|
return defined_image;
|
|
|
|
const image *partial_weight = partial->get_defined();
|
|
const image *default_weight = _default->get_defined();
|
|
|
|
assert (default_weight->width() == partial_weight->width());
|
|
assert (default_weight->height() == partial_weight->height());
|
|
|
|
defined_image = new_image_ale_real(default_weight->height(),
|
|
default_weight->width(), 3, NULL);
|
|
|
|
defined_image->set_offset(default_weight->offset());
|
|
|
|
for (i = 0; i < default_weight->height(); i++)
|
|
for (j = 0; j < default_weight->width(); j++)
|
|
for (k = 0; k < default_weight->depth(); k++)
|
|
defined_image->set_pixel(i, j,
|
|
((ale_real) ((pixel) partial_weight->get_pixel(i, j)).min_norm()
|
|
>= render::get_wt())
|
|
? partial_weight->get_pixel(i, j)
|
|
: default_weight->get_pixel(i, j));
|
|
|
|
return defined_image;
|
|
}
|
|
|
|
/*
|
|
* Perform rendering steps requiring no frames beyond frame N.
|
|
*/
|
|
|
|
virtual void sync(int n) {
|
|
render::sync(n);
|
|
if (output_image) {
|
|
delete output_image;
|
|
output_image = NULL;
|
|
}
|
|
if (defined_image) {
|
|
delete defined_image;
|
|
defined_image = NULL;
|
|
}
|
|
_default->sync(n);
|
|
partial->sync(n);
|
|
}
|
|
|
|
virtual void step() {
|
|
}
|
|
|
|
virtual int sync() {
|
|
if (output_image) {
|
|
delete output_image;
|
|
output_image = NULL;
|
|
}
|
|
if (defined_image) {
|
|
delete defined_image;
|
|
defined_image = NULL;
|
|
}
|
|
_default->sync();
|
|
partial->sync();
|
|
synced = 1;
|
|
|
|
return 1;
|
|
}
|
|
|
|
virtual void init_point_renderer(unsigned int h, unsigned int w, unsigned int d) {
|
|
_default->init_point_renderer(h, w, d);
|
|
partial->init_point_renderer(h, w, d);
|
|
output_image = new image_zero(h, w, d);
|
|
defined_image = new image_zero(h, w, d);
|
|
}
|
|
|
|
virtual void point_render(unsigned int i, unsigned int j, unsigned int f, transformation t) {
|
|
_default->point_render(i, j, f, t);
|
|
partial->point_render(i, j, f, t);
|
|
}
|
|
|
|
virtual void finish_point_rendering() {
|
|
_default->finish_point_rendering();
|
|
partial->finish_point_rendering();
|
|
delete defined_image;
|
|
delete output_image;
|
|
|
|
/*
|
|
* These will be generated upon a call to get_image() or
|
|
* get_defined().
|
|
*/
|
|
|
|
defined_image = NULL;
|
|
output_image = NULL;
|
|
}
|
|
|
|
const render *get_default() const {
|
|
return _default;
|
|
}
|
|
|
|
const render *get_partial() const {
|
|
return partial;
|
|
}
|
|
|
|
void free_memory() {
|
|
delete output_image;
|
|
delete defined_image;
|
|
output_image = NULL;
|
|
defined_image = NULL;
|
|
}
|
|
};
|
|
|
|
#endif
|