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); }
void hue_saturation (HueSaturation *hs, PixelRegion *srcPR, PixelRegion *destPR) { const guchar *src, *s; guchar *dest, *d; const gint hue_thresholds[] = { 21, 64, 106, 149, 192, 234, 255 }; gint alpha; gint w, h; gint r, g, b; gint hue; gint hue_counter; gint secondary_hue = 0; gboolean use_secondary_hue = FALSE; gfloat primary_intensity = 0.0; gfloat secondary_intensity = 0.0; gfloat overlap_hue = (hs->overlap / 100.0) * 21; /* Set the transfer arrays (for speed) */ h = srcPR->h; src = srcPR->data; dest = destPR->data; alpha = pixel_region_has_alpha (srcPR); while (h--) { w = srcPR->w; s = src; d = dest; while (w--) { r = s[RED]; g = s[GREEN]; b = s[BLUE]; gimp_rgb_to_hsl_int (&r, &g, &b); hue = (r + (128 / 6)) / 6; for (hue_counter = 0; hue_counter < 7; hue_counter++) if (r < hue_thresholds[hue_counter] + overlap_hue) { gint hue_threshold = hue_thresholds[hue_counter]; hue = hue_counter; if (overlap_hue > 1.0 && r > hue_threshold - overlap_hue) { secondary_hue = hue_counter + 1; use_secondary_hue = TRUE; secondary_intensity = (r - hue_threshold + overlap_hue) / (2.0 * overlap_hue); primary_intensity = 1.0 - secondary_intensity; } else { use_secondary_hue = FALSE; } break; } if (hue >= 6) { hue = 0; use_secondary_hue = FALSE; } if (secondary_hue >= 6) secondary_hue = 0; if (use_secondary_hue) { /* find nearest hue on the circle * between primary and secondary hue */ gint diff; diff = hs->hue_transfer[hue][r] - hs->hue_transfer[secondary_hue][r]; if (diff < -127 || diff >= 128) r = (gint) (hs->hue_transfer[hue][r] * primary_intensity + (hs->hue_transfer[secondary_hue][r] + 255) * secondary_intensity) % 255; else r = hs->hue_transfer[hue][r] * primary_intensity + hs->hue_transfer[secondary_hue][r] * secondary_intensity; g = hs->saturation_transfer[hue][g] * primary_intensity + hs->saturation_transfer[secondary_hue][g] * secondary_intensity; b = hs->lightness_transfer[hue][b] * primary_intensity + hs->lightness_transfer[secondary_hue][b] * secondary_intensity; } else { r = hs->hue_transfer[hue][r]; g = hs->saturation_transfer[hue][g]; b = hs->lightness_transfer[hue][b]; } gimp_hsl_to_rgb_int (&r, &g, &b); d[RED] = r; d[GREEN] = g; d[BLUE] = b; if (alpha) d[ALPHA] = s[ALPHA]; s += srcPR->bytes; d += destPR->bytes; } src += srcPR->rowstride; dest += destPR->rowstride; } }
/** * \brief Resize texture. * \param[in] in Original texture data. * \param[in] inwidth Original width of texture in pixels. * \param[in] inheight Original height of texture in pixels. * \param[in,out] out Resized texture data. * \param[in] outwidth New width of texture in pixels. * \param[in] outheight New height of texture in pixels. * \param[in] bytes Number of bytes per pixel. * \param[in] interpolation See InterpolationType */ PUBLIC void TM_ResampleTexture( W8 *in, int inwidth, int inheight, W8 *out, int outwidth, int outheight, W16 bytes, InterpolationType interpolation ) { double *src[ 4 ]; W8 *src_tmp; W8 *dest; double *row, *accum; int b; int width, height; int orig_width, orig_height; double y_rat; int i; int old_y = -4; int new_y; int x, y; if( interpolation == INTERPOLATION_NONE ) { scale_region_no_resample( in, inwidth, inheight, out, outwidth, outheight, (char)bytes ); return; } orig_width = inwidth; orig_height = inheight; width = outwidth; height = outheight; #if 0 Com_DPrintf( "scale_region: (%d x %d) -> (%d x %d)\n", orig_width, orig_height, width, height ); #endif /* find the ratios of old y to new y */ y_rat = (double) orig_height / (double) height; /* the data pointers... */ for( i = 0 ; i < 4 ; ++i ) { src[ i ] = (double *) MM_MALLOC( sizeof( double ) * width * bytes ); } dest = (PW8) MM_MALLOC( width * bytes); src_tmp = (PW8) MM_MALLOC( orig_width * bytes ); /* offset the row pointer by 2*bytes so the range of the array is [-2*bytes] to [(orig_width + 2)*bytes] */ row = (double *) MM_MALLOC( sizeof( double ) * (orig_width + 2 * 2) * bytes ); row += bytes * 2; accum = (double *) MM_MALLOC( sizeof( double ) * width * bytes ); /* Scale the selected region */ for( y = 0 ; y < height ; y++ ) { if( height < orig_height ) { int max; double frac; const double inv_ratio = 1.0 / y_rat; if( y == 0 ) /* load the first row if this is the first time through */ { get_scaled_row( &src[0], 0, width, row, src_tmp, in, orig_width, orig_height, bytes ); } new_y = (int)(y * y_rat); frac = 1.0 - (y * y_rat - new_y); for( x = 0 ; x < width * bytes; ++x ) { accum[x] = src[3][x] * frac; } max = (int) ((y + 1) * y_rat) - new_y - 1; get_scaled_row( &src[ 0 ], ++new_y, width, row, src_tmp, in, orig_width, orig_height, bytes ); while( max > 0 ) { for( x = 0 ; x < width * bytes ; ++x ) { accum[x] += src[ 3 ][ x ]; } get_scaled_row( &src[ 0 ], ++new_y, width, row, src_tmp, in, orig_width, orig_height, bytes ); max--; } frac = (y + 1) * y_rat - ((int) ((y + 1) * y_rat)); for( x = 0 ; x < width * bytes ; ++x ) { accum[ x ] += frac * src[ 3 ][ x ]; accum[ x ] *= inv_ratio; } } else if( height > orig_height ) { double p0, p1, p2, p3; double dy; new_y = (int)floor( y * y_rat - 0.5 ); while( old_y <= new_y ) { /* get the necesary lines from the source image, scale them, and put them into src[] */ get_scaled_row( &src[ 0 ], old_y + 2, width, row, src_tmp, in, orig_width, orig_height, bytes ); old_y++; } dy = (y * y_rat - 0.5) - new_y; p0 = cubic( dy, 1, 0, 0, 0 ); p1 = cubic( dy, 0, 1, 0, 0 ); p2 = cubic( dy, 0, 0, 1, 0 ); p3 = cubic( dy, 0, 0, 0, 1 ); for( x = 0 ; x < width * bytes ; ++x ) { accum[ x ] = ( p0 * src[ 0 ][ x ] + p1 * src[ 1 ][ x ] + p2 * src[ 2 ][ x ] + p3 * src[ 3 ][ x ] ); } } else /* height == orig_height */ { get_scaled_row( &src[ 0 ], y, width, row, src_tmp, in, orig_width, orig_height, bytes ); memcpy( accum, src[ 3 ], sizeof( double ) * width * bytes ); } if( pixel_region_has_alpha( bytes ) ) { /* unmultiply the alpha */ double inv_alpha; double *p = accum; int alpha = bytes - 1; int result; W8 *d = dest; for( x = 0 ; x < width ; ++x ) { if( p[ alpha ] > 0.001 ) { inv_alpha = 255.0 / p[ alpha ]; for( b = 0 ; b < alpha ; b++ ) { result = (int)RINT( inv_alpha * p[ b ] ); if( result < 0 ) { d[ b ] = 0; } else if( result > 255 ) { d[ b ] = 255; } else { d[ b ] = result; } } result = (int)RINT( p[ alpha ] ); if( result > 255 ) { d[ alpha ] = 255; } else { d[ alpha ] = result; } } else /* alpha <= 0 */ { for( b = 0 ; b <= alpha ; ++b ) { d[ b ] = 0; } } d += bytes; p += bytes; } } else { int w = width * bytes; for( x = 0 ; x < w ; ++x ) { if( accum[ x ] < 0.0 ) { dest[ x ] = 0; } else if( accum[ x ] > 255.0 ) { dest[ x ] = 255; } else { dest[ x ] = (W8)RINT( accum[ x ] ); } } } pixel_region_set_row( out, bytes, y, width, dest ); } /* free up temporary arrays */ MM_FREE( accum ); for( i = 0 ; i < 4 ; ++i ) { MM_FREE( src[ i ] ); } MM_FREE( src_tmp ); MM_FREE( dest ); row -= 2 * bytes; MM_FREE( row ); }