// Copyright 2002, 2007 David Hilvert , // /* 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