PRIVATE void get_premultiplied_double_row( W8 *in, int PRbytes, int x, int y, int w, double *row, W8 *tmp_src, int n ) { int b; int bytes = PRbytes; pixel_region_get_row( in, y, w, tmp_src, bytes ); if( pixel_region_has_alpha( bytes ) ) { /* premultiply the alpha into the double array */ double *irow = row; int alpha = bytes - 1; double mod_alpha; for( x = 0; x < w; ++x ) { mod_alpha = tmp_src[ alpha ] / 255.0; for( b = 0; b < alpha; ++b ) { irow[ b ] = mod_alpha * tmp_src[ b ]; } irow[ b ] = tmp_src[ alpha ]; irow += bytes; tmp_src += bytes; } } else /* no alpha */ { for( x = 0; x < w * bytes; ++x ) { row[ x ] = tmp_src[ x ]; } } /* set the off edge pixels to their nearest neighbor */ for( b = 0; b < 2 * bytes; b++ ) { row[ b - 2 * bytes ] = row[ b % bytes ]; } for( b = 0; b < bytes * 2; b++ ) { row[ b + w * bytes ] = row[ (w - 1) * bytes + b % bytes ]; } }
void subsample_region (PixelRegion *srcPR, PixelRegion *destPR, gint subsample) { const gint width = destPR->w; const gint height = destPR->h; const gint orig_width = srcPR->w / subsample; const gint orig_height = srcPR->h / subsample; const gdouble x_ratio = (gdouble) orig_width / (gdouble) width; const gdouble y_ratio = (gdouble) orig_height / (gdouble) height; const gint bytes = destPR->bytes; const gint destwidth = destPR->rowstride; const gboolean has_alpha = pixel_region_has_alpha (srcPR); guchar *src, *s; guchar *dest, *d; gdouble *row, *r; gint src_row, src_col; gdouble x_sum, y_sum; gdouble x_last, y_last; gdouble *x_frac, y_frac; gdouble tot_frac; gint i, j; gint b; gint frac; gboolean advance_dest; #if 0 g_printerr ("subsample_region: (%d x %d) -> (%d x %d)\n", orig_width, orig_height, width, height); #endif /* the data pointers... */ src = g_new (guchar, orig_width * bytes); dest = destPR->data; /* allocate an array to help with the calculations */ row = g_new (gdouble, width * bytes); x_frac = g_new (gdouble, width + orig_width); /* initialize the pre-calculated pixel fraction array */ src_col = 0; x_sum = (gdouble) src_col; x_last = x_sum; for (i = 0; i < width + orig_width; i++) { if (x_sum + x_ratio <= (src_col + 1 + EPSILON)) { x_sum += x_ratio; x_frac[i] = x_sum - x_last; } else { src_col ++; x_frac[i] = src_col - x_last; } x_last += x_frac[i]; } /* clear the "row" array */ memset (row, 0, sizeof (gdouble) * width * bytes); /* counters... */ src_row = 0; y_sum = (gdouble) src_row; y_last = y_sum; pixel_region_get_row (srcPR, srcPR->x, srcPR->y + src_row * subsample, orig_width * subsample, src, subsample); /* Scale the selected region */ for (i = 0; i < height; ) { src_col = 0; x_sum = (gdouble) src_col; /* determine the fraction of the src pixel we are using for y */ if (y_sum + y_ratio <= (src_row + 1 + EPSILON)) { y_sum += y_ratio; y_frac = y_sum - y_last; advance_dest = TRUE; } else { src_row ++; y_frac = src_row - y_last; advance_dest = FALSE; } y_last += y_frac; s = src; r = row; frac = 0; j = width; while (j) { tot_frac = x_frac[frac++] * y_frac; if (has_alpha) { /* premultiply */ gdouble local_frac = tot_frac * (gdouble) s[bytes - 1] / 255.0; for (b = 0; b < (bytes - 1); b++) r[b] += s[b] * local_frac; r[bytes - 1] += local_frac; } else { for (b = 0; b < bytes; b++) r[b] += s[b] * tot_frac; } /* increment the destination */ if (x_sum + x_ratio <= (src_col + 1 + EPSILON)) { r += bytes; x_sum += x_ratio; j--; } /* increment the source */ else { s += bytes; src_col++; } } if (advance_dest) { tot_frac = 1.0 / (x_ratio * y_ratio); /* copy "row" to "dest" */ d = dest; r = row; j = width; while (j--) { if (has_alpha) { /* unpremultiply */ gdouble alpha = r[bytes - 1]; if (alpha > EPSILON) { for (b = 0; b < (bytes - 1); b++) d[b] = (guchar) ((r[b] / alpha) + 0.5); d[bytes - 1] = (guchar) ((alpha * tot_frac * 255.0) + 0.5); } else { for (b = 0; b < bytes; b++) d[b] = 0; } } else { for (b = 0; b < bytes; b++) d[b] = (guchar) ((r[b] * tot_frac) + 0.5); } r += bytes; d += bytes; } dest += destwidth; /* clear the "row" array */ memset (row, 0, sizeof (gdouble) * destwidth); i++; } else { pixel_region_get_row (srcPR, srcPR->x, srcPR->y + src_row * subsample, orig_width * subsample, src, subsample); } } /* free up temporary arrays */ g_free (row); g_free (x_frac); g_free (src); }
void subsample_indexed_region (PixelRegion *srcPR, PixelRegion *destPR, const guchar *cmap, gint subsample) { const gint width = destPR->w; const gint height = destPR->h; const gint orig_width = srcPR->w / subsample; const gint orig_height = srcPR->h / subsample; const gdouble x_ratio = (gdouble) orig_width / (gdouble) width; const gdouble y_ratio = (gdouble) orig_height / (gdouble) height; const gint bytes = destPR->bytes; const gint destwidth = destPR->rowstride; const gboolean has_alpha = pixel_region_has_alpha (srcPR); guchar *src, *s; guchar *dest, *d; gdouble *row, *r; gint src_row, src_col; gdouble x_sum, y_sum; gdouble x_last, y_last; gdouble *x_frac, y_frac; gdouble tot_frac; gint i, j; gint b; gint frac; gboolean advance_dest; g_return_if_fail (cmap != NULL); /* the data pointers... */ src = g_new (guchar, orig_width * bytes); dest = destPR->data; /* allocate an array to help with the calculations */ row = g_new0 (gdouble, width * bytes); x_frac = g_new (gdouble, width + orig_width); /* initialize the pre-calculated pixel fraction array */ src_col = 0; x_sum = (gdouble) src_col; x_last = x_sum; for (i = 0; i < width + orig_width; i++) { if (x_sum + x_ratio <= (src_col + 1 + EPSILON)) { x_sum += x_ratio; x_frac[i] = x_sum - x_last; } else { src_col++; x_frac[i] = src_col - x_last; } x_last += x_frac[i]; } /* counters... */ src_row = 0; y_sum = (gdouble) src_row; y_last = y_sum; pixel_region_get_row (srcPR, srcPR->x, srcPR->y + src_row * subsample, orig_width * subsample, src, subsample); /* Scale the selected region */ for (i = 0; i < height; ) { src_col = 0; x_sum = (gdouble) src_col; /* determine the fraction of the src pixel we are using for y */ if (y_sum + y_ratio <= (src_row + 1 + EPSILON)) { y_sum += y_ratio; y_frac = y_sum - y_last; advance_dest = TRUE; } else { src_row++; y_frac = src_row - y_last; advance_dest = FALSE; } y_last += y_frac; s = src; r = row; frac = 0; j = width; while (j) { gint index = *s * 3; tot_frac = x_frac[frac++] * y_frac; /* transform the color to RGB */ if (has_alpha) { if (s[ALPHA_I] & 0x80) { r[RED] += cmap[index++] * tot_frac; r[GREEN] += cmap[index++] * tot_frac; r[BLUE] += cmap[index++] * tot_frac; r[ALPHA] += tot_frac; } /* else the pixel contributes nothing and needs not to be added */ } else { r[RED] += cmap[index++] * tot_frac; r[GREEN] += cmap[index++] * tot_frac; r[BLUE] += cmap[index++] * tot_frac; } /* increment the destination */ if (x_sum + x_ratio <= (src_col + 1 + EPSILON)) { r += bytes; x_sum += x_ratio; j--; } /* increment the source */ else { s += srcPR->bytes; src_col++; } } if (advance_dest) { tot_frac = 1.0 / (x_ratio * y_ratio); /* copy "row" to "dest" */ d = dest; r = row; j = width; while (j--) { if (has_alpha) { /* unpremultiply */ gdouble alpha = r[bytes - 1]; if (alpha > EPSILON) { for (b = 0; b < (bytes - 1); b++) d[b] = (guchar) ((r[b] / alpha) + 0.5); d[bytes - 1] = (guchar) ((alpha * tot_frac * 255.0) + 0.5); } else { for (b = 0; b < bytes; b++) d[b] = 0; } } else { for (b = 0; b < bytes; b++) d[b] = (guchar) ((r[b] * tot_frac) + 0.5); } r += bytes; d += bytes; } dest += destwidth; /* clear the "row" array */ memset (row, 0, sizeof (gdouble) * destwidth); i++; } else { pixel_region_get_row (srcPR, srcPR->x, srcPR->y + src_row * subsample, orig_width * subsample, src, subsample); } } /* free up temporary arrays */ g_free (row); g_free (x_frac); g_free (src); }
/* non-interpolating scale_region. */ PRIVATE void scale_region_no_resample( W8 *in, int inwidth, int inheight, W8 *out, int outwidth, int outheight, char bytes ) { int *x_src_offsets; int *y_src_offsets; W8 *src; W8 *dest; int width, height, orig_width, orig_height; int last_src_y; int row_bytes; int x, y, b; orig_width = inwidth; orig_height = inheight; width = outwidth; height = outheight; /* the data pointers... */ x_src_offsets = (int *) MM_MALLOC( sizeof( int ) * width * bytes ); y_src_offsets = (int *) MM_MALLOC( sizeof( int ) * height ); src = (unsigned char *) MM_MALLOC( orig_width * bytes); dest = (unsigned char *) MM_MALLOC( width * bytes); /* pre-calc the scale tables */ for( b = 0; b < bytes; b++ ) { for( x = 0; x < width; x++ ) { x_src_offsets[ b + x * bytes ] = b + bytes * ((x * orig_width + orig_width / 2) / width); } } for( y = 0; y < height; y++ ) { y_src_offsets[ y ] = (y * orig_height + orig_height / 2) / height; } /* do the scaling */ row_bytes = width * bytes; last_src_y = -1; for( y = 0; y < height; y++ ) { /* if the source of this line was the same as the source * of the last line, there's no point in re-rescaling. */ if( y_src_offsets[ y ] != last_src_y ) { pixel_region_get_row( in, y_src_offsets[ y ], orig_width, src, bytes ); //pixel_region_get_row( srcPR, 0, y_src_offsets[y], orig_width, src, 1 ); for( x = 0 ; x < row_bytes ; x++ ) { dest[ x ] = src[ x_src_offsets[ x ] ]; } last_src_y = y_src_offsets[ y ]; } pixel_region_set_row( out, bytes, y, width, dest ); } MM_FREE( x_src_offsets ); MM_FREE( y_src_offsets ); MM_FREE( src ); MM_FREE( dest ); }
gboolean gimp_image_crop_auto_shrink (GimpImage *image, gint x1, gint y1, gint x2, gint y2, gboolean active_drawable_only, gint *shrunk_x1, gint *shrunk_y1, gint *shrunk_x2, gint *shrunk_y2) { GimpDrawable *active_drawable = NULL; GimpPickable *pickable; ColorsEqualFunc colors_equal_func; guchar bgcolor[MAX_CHANNELS] = { 0, 0, 0, 0 }; gboolean has_alpha; PixelRegion PR; guchar *buffer = NULL; gint width, height; GimpImageType type; gint bytes; gint x, y, abort; gboolean retval = FALSE; g_return_val_if_fail (image != NULL, FALSE); g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); g_return_val_if_fail (shrunk_x1 != NULL, FALSE); g_return_val_if_fail (shrunk_y1 != NULL, FALSE); g_return_val_if_fail (shrunk_x2 != NULL, FALSE); g_return_val_if_fail (shrunk_y2 != NULL, FALSE); gimp_set_busy (image->gimp); /* You should always keep in mind that crop->tx2 and crop->ty2 are the NOT the coordinates of the bottomright corner of the area to be cropped. They point at the pixel located one to the right and one to the bottom. */ if (active_drawable_only) { active_drawable = gimp_image_get_active_drawable (image); if (! active_drawable) goto FINISH; pickable = GIMP_PICKABLE (active_drawable); } else { pickable = GIMP_PICKABLE (image->projection); } gimp_pickable_flush (pickable); type = gimp_pickable_get_image_type (pickable); bytes = GIMP_IMAGE_TYPE_BYTES (type); has_alpha = GIMP_IMAGE_TYPE_HAS_ALPHA (type); switch (gimp_image_crop_guess_bgcolor (pickable, bytes, has_alpha, bgcolor, x1, x2-1, y1, y2-1)) { case AUTO_CROP_ALPHA: colors_equal_func = (ColorsEqualFunc) gimp_image_crop_colors_alpha; break; case AUTO_CROP_COLOR: colors_equal_func = (ColorsEqualFunc) gimp_image_crop_colors_equal; break; default: goto FINISH; break; } width = x2 - x1; height = y2 - y1; pixel_region_init (&PR, gimp_pickable_get_tiles (pickable), x1, y1, width, height, FALSE); /* The following could be optimized further by processing * the smaller side first instead of defaulting to width --Sven */ buffer = g_malloc ((width > height ? width : height) * bytes); /* Check how many of the top lines are uniform/transparent. */ abort = FALSE; for (y = y1; y < y2 && !abort; y++) { pixel_region_get_row (&PR, x1, y, width, buffer, 1); for (x = 0; x < width && !abort; x++) abort = !(colors_equal_func) (bgcolor, buffer + x * bytes, bytes); } if (y == y2 && !abort) goto FINISH; y1 = y - 1; /* Check how many of the bottom lines are uniform/transparent. */ abort = FALSE; for (y = y2; y > y1 && !abort; y--) { pixel_region_get_row (&PR, x1, y-1 , width, buffer, 1); for (x = 0; x < width && !abort; x++) abort = !(colors_equal_func) (bgcolor, buffer + x * bytes, bytes); } y2 = y + 1; /* compute a new height for the next operations */ height = y2 - y1; /* Check how many of the left lines are uniform/transparent. */ abort = FALSE; for (x = x1; x < x2 && !abort; x++) { pixel_region_get_col (&PR, x, y1, height, buffer, 1); for (y = 0; y < height && !abort; y++) abort = !(colors_equal_func) (bgcolor, buffer + y * bytes, bytes); } x1 = x - 1; /* Check how many of the right lines are uniform/transparent. */ abort = FALSE; for (x = x2; x > x1 && !abort; x--) { pixel_region_get_col (&PR, x-1, y1, height, buffer, 1); for (y = 0; y < height && !abort; y++) abort = !(colors_equal_func) (bgcolor, buffer + y * bytes, bytes); } x2 = x + 1; *shrunk_x1 = x1; *shrunk_y1 = y1; *shrunk_x2 = x2; *shrunk_y2 = y2; retval = TRUE; FINISH: g_free (buffer); gimp_unset_busy (image->gimp); return retval; }