mirror of https://github.com/GNOME/gimp.git
391 lines
9.8 KiB
C
391 lines
9.8 KiB
C
/* 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 <string.h>
|
|
|
|
#include <glib-object.h>
|
|
|
|
#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);
|
|
}
|