/* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * General, non-jittered adaptive supersampling library * Copyright (C) 1997 Federico Mena Quintero * * This program 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 2 of the License, or * (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ /* This code is *largely* based on the sources for POV-Ray 3.0. I am * grateful to the POV-Team for such a great program and for making * their sources available. All comments / bug reports / * etc. regarding this library should be addressed to me, not to the * POV-Ray team. Any bugs are my responsibility, not theirs. */ #include #include #include "appenv.h" #include "asupsample.h" /***** Types *****/ typedef struct { char ready; color_t color; } sample_t; /***** Local functions *****/ static unsigned long render_sub_pixel(int max_depth, int depth, sample_t **block, int x, int y, int x1, int y1, int x3, int y3, double threshold, int sub_pixel_size, render_func_t render_func, color_t *color, void *render_data); static double color_dist(color_t c1, color_t c2); /***** Functions *****/ /*****/ unsigned long adaptive_supersample_area(int x1, int y1, int x2, int y2, int max_depth, double threshold, render_func_t render_func, void *render_data, put_pixel_func_t put_pixel_func, void *put_pixel_data, progress_func_t progress_func, void *progress_data) { int x, y, width; /* Counters, width of region */ int xt, xtt, yt; /* Temporary counters */ int sub_pixel_size; /* Numbe of samples per pixel (1D) */ size_t row_size; /* Memory needed for one row */ color_t color; /* Rendered pixel's color */ sample_t tmp_sample; /* For swapping samples */ sample_t *top_row, *bot_row, *tmp_row; /* Sample rows */ sample_t **block; /* Sample block matrix */ unsigned long num_samples; /* Initialize color */ color.r = 0.0; color.g = 0.0; color.b = 0.0; color.a = 0.0; /* Calculate sub-pixel size */ sub_pixel_size = 1 << max_depth; /* 2**max_depth */ /* Create row arrays */ width = x2 - x1 + 1; row_size = (sub_pixel_size * width + 1) * sizeof(sample_t); top_row = g_malloc(row_size); bot_row = g_malloc(row_size); for (x = 0; x < (sub_pixel_size * width + 1); x++) { top_row[x].ready = 0; top_row[x].color.r = 0.0; top_row[x].color.g = 0.0; top_row[x].color.b = 0.0; top_row[x].color.a = 0.0; bot_row[x].ready = 0; bot_row[x].color.r = 0.0; bot_row[x].color.g = 0.0; bot_row[x].color.b = 0.0; bot_row[x].color.a = 0.0; } /* for */ /* Allocate block matrix */ block = g_malloc((sub_pixel_size + 1) * sizeof(sample_t *)); /* Rows */ for (y = 0; y < (sub_pixel_size + 1); y++) { block[y] = g_malloc((sub_pixel_size + 1) * sizeof(sample_t)); /* Columns */ for (x = 0; x < (sub_pixel_size + 1); x++) { block[y][x].ready = 0; block[y][x].color.r = 0; block[y][x].color.g = 0; block[y][x].color.b = 0; block[y][x].color.a = 0; } } /* Render region */ num_samples = 0; for (y = y1; y <= y2; y++) { /* Clear the bottom row */ for (xt = 0; xt < (sub_pixel_size * width + 1); xt++) bot_row[xt].ready = 0; /* Clear first column */ for (yt = 0; yt < (sub_pixel_size + 1); yt++) block[yt][0].ready = 0; /* Render row */ for (x = x1; x <= x2; x++) { /* Initialize block by clearing all but first row/column */ for (yt = 1; yt < (sub_pixel_size + 1); yt++) for (xt = 1; xt < (sub_pixel_size + 1); xt++) block[yt][xt].ready = 0; /* Copy samples from top row to block */ for (xtt = 0, xt = (x - x1) * sub_pixel_size; xtt < (sub_pixel_size + 1); xtt++, xt++) block[0][xtt] = top_row[xt]; /* Render pixel on (x, y) */ num_samples += render_sub_pixel(max_depth, 1, block, x, y, 0, 0, sub_pixel_size, sub_pixel_size, threshold, sub_pixel_size, render_func, &color, render_data); if (put_pixel_func) (*put_pixel_func)(x, y, color, put_pixel_data); /* Copy block information to rows */ top_row[((x - x1) + 1) * sub_pixel_size] = block[0][sub_pixel_size]; for (xtt = 0, xt = (x - x1) * sub_pixel_size; xtt < (sub_pixel_size + 1); xtt++, xt++) bot_row[xt] = block[sub_pixel_size][xtt]; /* Swap first and last columns */ for (yt = 0; yt < (sub_pixel_size + 1); yt++) { tmp_sample = block[yt][0]; block[yt][0] = block[yt][sub_pixel_size]; block[yt][sub_pixel_size] = tmp_sample; } /* for */ } /* for */ /* Swap rows */ tmp_row = top_row; top_row = bot_row; bot_row = tmp_row; /* Call progress display function */ if (progress_func && !(y & 0xf)) (*progress_func)(y1, y2, y, progress_data); } /* for */ /* Free memory */ for (y = 0; y < (sub_pixel_size + 1); y++) g_free(block[y]); g_free(block); g_free(top_row); g_free(bot_row); return num_samples; } /* adaptive_supersample_area */ /*****/ static unsigned long render_sub_pixel(int max_depth, int depth, sample_t **block, int x, int y, int x1, int y1, int x3, int y3, double threshold, int sub_pixel_size, render_func_t render_func, color_t *color, void *render_data) { int x2, y2; /* Coords of center sample */ double dx1, dy1; /* Delta to upper left sample */ double dx3, dy3; /* Delta to lower right sample */ color_t c1, c2, c3, c4; /* Sample colors */ unsigned long num_samples; /* Get offsets for corners */ dx1 = (double) (x1 - sub_pixel_size / 2) / sub_pixel_size; dx3 = (double) (x3 - sub_pixel_size / 2) / sub_pixel_size; dy1 = (double) (y1 - sub_pixel_size / 2) / sub_pixel_size; dy3 = (double) (y3 - sub_pixel_size / 2) / sub_pixel_size; num_samples = 0; /* Render upper left sample */ if (!block[y1][x1].ready) { num_samples++; (*render_func)(x + dx1, y + dy1, &c1, render_data); block[y1][x1].ready = 1; block[y1][x1].color = c1; } else c1 = block[y1][x1].color; /* Render upper right sample */ if (!block[y1][x3].ready) { num_samples++; (*render_func)(x + dx3, y + dy1, &c2, render_data); block[y1][x3].ready = 1; block[y1][x3].color = c2; } else c2 = block[y1][x3].color; /* Render lower left sample */ if (!block[y3][x1].ready) { num_samples++; (*render_func)(x + dx1, y + dy3, &c3, render_data); block[y3][x1].ready = 1; block[y3][x1].color = c3; } else c3 = block[y3][x1].color; /* Render lower right sample */ if (!block[y3][x3].ready) { num_samples++; (*render_func)(x + dx3, y + dy3, &c4, render_data); block[y3][x3].ready = 1; block[y3][x3].color = c4; } else c4 = block[y3][x3].color; /* Check for supersampling */ if (depth <= max_depth) { /* Check whether we have tu supersample */ if ((color_dist(c1, c2) >= threshold) || (color_dist(c1, c3) >= threshold) || (color_dist(c1, c4) >= threshold) || (color_dist(c2, c3) >= threshold) || (color_dist(c2, c4) >= threshold) || (color_dist(c3, c4) >= threshold)) { /* Calc coordinates of center subsample */ x2 = (x1 + x3) / 2; y2 = (y1 + y3) / 2; /* Render sub-blocks */ num_samples += render_sub_pixel(max_depth, depth + 1, block, x, y, x1, y1, x2, y2, threshold, sub_pixel_size, render_func, &c1, render_data); num_samples += render_sub_pixel(max_depth, depth + 1, block, x, y, x2, y1, x3, y2, threshold, sub_pixel_size, render_func, &c2, render_data); num_samples += render_sub_pixel(max_depth, depth + 1, block, x, y, x1, y2, x2, y3, threshold, sub_pixel_size, render_func, &c3, render_data); num_samples += render_sub_pixel(max_depth, depth + 1, block, x, y, x2, y2, x3, y3, threshold, sub_pixel_size, render_func, &c4, render_data); } /* if */ } /* if */ color->r = 0.25 * (c1.r + c2.r + c3.r + c4.r); color->g = 0.25 * (c1.g + c2.g + c3.g + c4.g); color->b = 0.25 * (c1.b + c2.b + c3.b + c4.b); color->a = 0.25 * (c1.a + c2.a + c3.a + c4.a); return num_samples; } /* render_sub_pixel */ /*****/ static double color_dist(color_t c1, color_t c2) { return fabs(c1.r - c2.r) + fabs(c1.g - c2.g) + fabs(c1.b - c2.b) + fabs(c1.a - c2.a); } /* color_dist */