/* GIMP - The GNU Image Manipulation Program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * 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. */ #include "config.h" #include #include #include "libgimpmath/gimpmath.h" #include "paint-funcs-types.h" #include "base/pixel-region.h" #include "paint-funcs.h" #include "reduce-region.h" static void reduce_pixel_region (PixelRegion *srcPR, guchar *dst, gint bytes); static void reduce_buffer (const guchar *src, guchar *dst, gint bytes, gint width, gint height, gint alpha); static void reduce_row (const guchar *src, guchar *dst, gint width, gint bytes, gint alpha); static void reduce_bilinear_pr (PixelRegion *dstPR, PixelRegion *srcPR); static void reduce_bilinear (PixelRegion *dstPR, const guchar *src, gint source_w, gint source_h, gint bytes); void reduce_region (PixelRegion *srcPR, PixelRegion *dstPR, GimpProgressFunc progress_callback, gpointer progress_data) { const gint bytes = dstPR->bytes; gint alpha = 0; gint level = 0; gdouble scale = (gdouble) dstPR->h / (gdouble) srcPR->h; if (pixel_region_has_alpha (srcPR)) alpha = bytes - 1; while (scale <= 0.5) { scale *= 2; level++; } if (level > 0) { gint width = srcPR->w / 2; gint height = srcPR->h / 2; guchar *dst = g_new (guchar, width * height * bytes); reduce_pixel_region (srcPR, dst, bytes); level--; if (level > 0) { while (level) { guchar *src = dst; width = width / 2; height = height / 2; dst = g_new (guchar, width * height * bytes); reduce_buffer (src, dst, bytes, width * 2, height * 2, alpha); g_free(src); level--; } } reduce_bilinear (dstPR, dst, width, height, bytes); g_free (dst); } else { reduce_bilinear_pr (dstPR, srcPR); } } static void reduce_pixel_region (PixelRegion *srcPR, guchar *dst, gint bytes) { guchar *src_buf, *row0, *row1, *row2; gint y; gint src_width = srcPR->w; gint dst_width = srcPR->w/2; guchar *dst_ptr; gint alpha = 0; if (pixel_region_has_alpha (srcPR)) alpha = bytes - 1; src_buf = g_new (guchar, src_width * bytes * 3); row0 = src_buf; row1 = row0 + src_width * bytes; row2 = row1 + src_width * bytes; dst_ptr = dst; for (y = 0 ; y < srcPR->h ; y+=2) { pixel_region_get_row (srcPR, 0, ABS(y-1), src_width, row0, 1); pixel_region_get_row (srcPR, 0, y , src_width, row1, 1); pixel_region_get_row (srcPR, 0, y+1 , src_width, row2, 1); reduce_row(src_buf, dst_ptr, src_width, bytes, alpha); dst_ptr += dst_width * bytes; } g_free (src_buf); } static void reduce_buffer (const guchar *src, guchar *dst, gint bytes, gint width, gint height, gint alpha) { guchar *src_buf; guchar *row0, *row1, *row2; gint y, pos; gint dst_width = width/2; guchar *dstptr; gint rowstride = width * bytes; src_buf = g_new (guchar, rowstride * 3); row0 = src_buf; row1 = row0 + rowstride; row2 = row1 + rowstride; dstptr = dst; for (y = 0 ; y < height ; y+=2) { pos = ABS (y-1) * rowstride; memcpy (row0, src+pos, rowstride); pos = y * rowstride; memcpy (row1, src+pos, rowstride); pos= (y+1) * rowstride; memcpy (row2, src+pos, rowstride); reduce_row (src_buf, dstptr, width, bytes, alpha); dstptr += dst_width * bytes; } g_free (src_buf); } static void reduce_row (const guchar *src, guchar *dst, gint width, gint bytes, gint alpha) { gint x, b, start; gdouble alpha_sum; gdouble sum; const guchar *src0; const guchar *src1; const guchar *src2; guchar *dstptr; /* Pre calculated gausian matrix */ /* Standard deviation = 0.71 12 32 12 32 86 32 12 32 12 Matrix sum is = 262 Normalize by dividing with 273 */ dstptr = dst; src0 = src; src1 = src + width * bytes; src2 = src1 + width * bytes; start = bytes -1; sum = 0.0; alpha_sum = 0.0; for (x=0; x < width ; x+=2) { for (b = start ; b >= 0 ; b--) { sum += src0[ABS(x-1) * bytes + b] * 12; sum += src0[ABS(x) * bytes + b] * 32; sum += src0[ABS(x+1) * bytes + b] * 12; sum += src1[ABS(x-1) * bytes + b] * 32; sum += src1[ABS(x) * bytes + b] * 86; sum += src1[ABS(x+1) * bytes + b] * 32; sum += src2[ABS(x-1) * bytes + b] * 12; sum += src2[ABS(x) * bytes + b] * 32; sum += src2[ABS(x+1) * bytes + b] * 12; sum /= 273.0; if ( alpha && (b == alpha)) { alpha_sum = sum; dstptr[b] = (guchar)alpha_sum; } else { if (alpha) { if (alpha_sum != 0.0) sum = sum * ( 262.0 / alpha_sum ); else sum = 0.0; } dstptr[b] = (guchar) sum; } } dstptr += bytes; } } static void reduce_bilinear (PixelRegion *dstPR, const guchar *src, gint source_w, gint source_h, gint bytes) { const gdouble scale = (gdouble) dstPR->h / (gdouble) source_h; gint x, y, b; gint x0, x1, y0, y1; gdouble xfrac, yfrac; gdouble s00, s01, s10, s11; guchar value; guchar *dst; dst = g_new (guchar, dstPR->w * dstPR->h * bytes); for (y = 0; y < dstPR->h; y++) { yfrac = ( y / scale ); y0 = (gint) floor (yfrac); y1 = (gint) ceil (yfrac); yfrac = yfrac - y0; for (x = 0; x < dstPR->w; x++) { xfrac = (x / scale); x0 = (gint) floor (xfrac); x1 = (gint) ceil (xfrac); xfrac = xfrac - x0; for (b = 0 ; b < bytes ; b++) { s00 = src[(x0 + y0 * source_w) * bytes + b]; s10 = src[(x1 + y0 * source_w) * bytes + b]; s01 = src[(x0 + y1 * source_w) * bytes + b]; s11 = src[(x1 + y1 * source_w) * bytes + b]; value = (gint)((1 - yfrac) * ((1 - xfrac) * s00 + xfrac * s01) + yfrac * ((1 - xfrac) * s10 + xfrac * s11)); dst[x * bytes + b] = value; } } pixel_region_set_row (dstPR, 0, y, dstPR->w, dst); } } static void reduce_bilinear_pr (PixelRegion *dstPR, PixelRegion *srcPR) { const gint bytes = dstPR->bytes; gint x, y, b; gint x0, x1, y0, y1; gdouble scale; gdouble xfrac, yfrac; gdouble s00, s01, s10, s11; guchar value; guchar *dst; guchar *src0; guchar *src1; /* Copy and return if scale = 1.0 */ if (srcPR->h == dstPR->h) { copy_region (srcPR, dstPR); return; } scale = (gdouble) dstPR->h / (gdouble) srcPR->h; /* Interpolate */ src0 = g_new0 (guchar, srcPR->w * bytes); src1 = g_new0 (guchar, srcPR->w * bytes); dst = g_new0 (guchar, dstPR->w * bytes); for (y = 0; y < dstPR->h; y++) { yfrac = y / scale; y0 = (gint) floor (yfrac); y1 = (gint) ceil (yfrac); yfrac = yfrac - y0; pixel_region_get_row (srcPR, 0, y0, srcPR->w, src0, 1); pixel_region_get_row (srcPR, 0, y1, srcPR->w, src1, 1); for (x = 0; x < dstPR->w; x++) { xfrac = (x / scale); x0 = (gint)floor(xfrac); x1 = (gint)ceil(xfrac); xfrac = xfrac - x0; for (b = 0 ; b < bytes ; b++) { s00 = src0[x0 * bytes + b]; s01 = src0[x1 * bytes + b]; s10 = src1[x0 * bytes + b]; s11 = src1[x1 * bytes + b]; value = (1 - yfrac) * ( (1 - xfrac) * s00 + xfrac * s01 ) + yfrac * ( (1 - xfrac) * s10 + xfrac * s11 ); dst[x * bytes + b] = (guchar) value; } } pixel_region_set_row (dstPR, 0, y, dstPR->w, dst); } g_free(src0); g_free(src1); g_free(dst); }