static void transfer_pixels (gdouble *src1, gdouble *src2, guchar *dest, gint jump, gint bytes, gint width) { gint i, b; gdouble sum[4]; for(i = 0; i < width; i++) { for (b = 0; b < bytes; b++) { sum[b] = src1[b] + src2[b]; if (sum[b] > 255) sum[b] = 255; else if(sum[b] < 0) sum[b] = 0; } /* Convert to lightness if RGB */ if (bytes > 2) *dest = (guchar) gimp_rgb_to_l_int (sum[0], sum[1], sum[2]); else *dest = (guchar) sum[0]; src1 += bytes; src2 += bytes; dest += jump; } }
void color_balance (ColorBalance *cb, PixelRegion *srcPR, PixelRegion *destPR) { const guchar *src, *s; guchar *dest, *d; gboolean alpha; gint r, g, b; gint r_n, g_n, b_n; gint w, h; h = srcPR->h; src = srcPR->data; dest = destPR->data; alpha = (srcPR->bytes == 4) ? TRUE : FALSE; while (h--) { w = srcPR->w; s = src; d = dest; while (w--) { r = s[RED_PIX]; g = s[GREEN_PIX]; b = s[BLUE_PIX]; r_n = cb->r_lookup[r]; g_n = cb->g_lookup[g]; b_n = cb->b_lookup[b]; if (cb->preserve_luminosity) { gimp_rgb_to_hsl_int (&r_n, &g_n, &b_n); b_n = gimp_rgb_to_l_int (r, g, b); gimp_hsl_to_rgb_int (&r_n, &g_n, &b_n); } d[RED_PIX] = r_n; d[GREEN_PIX] = g_n; d[BLUE_PIX] = b_n; if (alpha) d[ALPHA_PIX] = s[ALPHA_PIX]; s += srcPR->bytes; d += destPR->bytes; } src += srcPR->rowstride; dest += destPR->rowstride; } }
static VALUE rb_gimp_rgb_to_l_int (VALUE self, VALUE red, VALUE green, VALUE blue) { gint lightness = gimp_rgb_to_l_int(NUM2INT(red), NUM2INT(green), NUM2INT(blue)); return INT2NUM(lightness); }
/* * Photocopy algorithm * ----------------- * Mask radius = radius of pixel neighborhood for intensity comparison * Threshold = relative intensity difference which will result in darkening * Ramp = amount of relative intensity difference before total black * Blur radius = mask radius / 3.0 * * Algorithm: * For each pixel, calculate pixel intensity value to be: avg (blur radius) * relative diff = pixel intensity / avg (mask radius) * If relative diff < Threshold * intensity mult = (Ramp - MIN (Ramp, (Threshold - relative diff))) / Ramp * pixel intensity *= intensity mult * Else * pixel intensity = white */ static void photocopy (GimpDrawable *drawable, GimpPreview *preview) { GimpPixelRgn src_rgn, dest_rgn; GimpPixelRgn *pr; gint width, height; gint bytes; gboolean has_alpha; guchar *dest1; guchar *dest2; guchar *src1, *sp_p1, *sp_m1; guchar *src2, *sp_p2, *sp_m2; gdouble n_p1[5], n_m1[5]; gdouble n_p2[5], n_m2[5]; gdouble d_p1[5], d_m1[5]; gdouble d_p2[5], d_m2[5]; gdouble bd_p1[5], bd_m1[5]; gdouble bd_p2[5], bd_m2[5]; gdouble *val_p1, *val_m1, *vp1, *vm1; gdouble *val_p2, *val_m2, *vp2, *vm2; gint x1, y1; gint i, j; gint row, col; gint terms; gint progress, max_progress; gint initial_p1[4]; gint initial_p2[4]; gint initial_m1[4]; gint initial_m2[4]; gdouble radius; gdouble val; gdouble std_dev1; gdouble std_dev2; gdouble ramp_down; gdouble ramp_up; if (preview) { gimp_preview_get_position (preview, &x1, &y1); gimp_preview_get_size (preview, &width, &height); } else { gint x2, y2; gimp_drawable_mask_bounds (drawable->drawable_id, &x1, &y1, &x2, &y2); width = x2 - x1; height = y2 - y1; } bytes = drawable->bpp; has_alpha = gimp_drawable_has_alpha (drawable->drawable_id); val_p1 = g_new (gdouble, MAX (width, height)); val_p2 = g_new (gdouble, MAX (width, height)); val_m1 = g_new (gdouble, MAX (width, height)); val_m2 = g_new (gdouble, MAX (width, height)); dest1 = g_new0 (guchar, width * height); dest2 = g_new0 (guchar, width * height); progress = 0; max_progress = width * height * 3; gimp_pixel_rgn_init (&src_rgn, drawable, x1, y1, width, height, FALSE, FALSE); for (pr = gimp_pixel_rgns_register (1, &src_rgn); pr != NULL; pr = gimp_pixel_rgns_process (pr)) { guchar *src_ptr = src_rgn.data; guchar *dest_ptr = dest1 + (src_rgn.y - y1) * width + (src_rgn.x - x1); for (row = 0; row < src_rgn.h; row++) { for (col = 0; col < src_rgn.w; col++) { /* desaturate */ if (bytes > 2) dest_ptr[col] = (guchar) gimp_rgb_to_l_int (src_ptr[col * bytes + 0], src_ptr[col * bytes + 1], src_ptr[col * bytes + 2]); else dest_ptr[col] = (guchar) src_ptr[col * bytes]; /* compute transfer */ val = pow (dest_ptr[col], (1.0 / GAMMA)); dest_ptr[col] = (guchar) CLAMP (val, 0, 255); } src_ptr += src_rgn.rowstride; dest_ptr += width; } if (!preview) { progress += src_rgn.w * src_rgn.h; gimp_progress_update ((gdouble) progress / (gdouble) max_progress); } } /* Calculate the standard deviations */ radius = MAX (1.0, 10 * (1.0 - pvals.sharpness)); radius = fabs (radius) + 1.0; std_dev1 = sqrt (-(radius * radius) / (2 * log (1.0 / 255.0))); radius = fabs (pvals.mask_radius) + 1.0; std_dev2 = sqrt (-(radius * radius) / (2 * log (1.0 / 255.0))); /* derive the constants for calculating the gaussian from the std dev */ find_constants (n_p1, n_m1, d_p1, d_m1, bd_p1, bd_m1, std_dev1); find_constants (n_p2, n_m2, d_p2, d_m2, bd_p2, bd_m2, std_dev2); /* First the vertical pass */ for (col = 0; col < width; col++) { memset (val_p1, 0, height * sizeof (gdouble)); memset (val_p2, 0, height * sizeof (gdouble)); memset (val_m1, 0, height * sizeof (gdouble)); memset (val_m2, 0, height * sizeof (gdouble)); src1 = dest1 + col; sp_p1 = src1; sp_m1 = src1 + (height - 1) * width; vp1 = val_p1; vp2 = val_p2; vm1 = val_m1 + (height - 1); vm2 = val_m2 + (height - 1); /* Set up the first vals */ initial_p1[0] = sp_p1[0]; initial_m1[0] = sp_m1[0]; for (row = 0; row < height; row++) { gdouble *vpptr1, *vmptr1; gdouble *vpptr2, *vmptr2; terms = (row < 4) ? row : 4; vpptr1 = vp1; vmptr1 = vm1; vpptr2 = vp2; vmptr2 = vm2; for (i = 0; i <= terms; i++) { *vpptr1 += n_p1[i] * sp_p1[-i * width] - d_p1[i] * vp1[-i]; *vmptr1 += n_m1[i] * sp_m1[i * width] - d_m1[i] * vm1[i]; *vpptr2 += n_p2[i] * sp_p1[-i * width] - d_p2[i] * vp2[-i]; *vmptr2 += n_m2[i] * sp_m1[i * width] - d_m2[i] * vm2[i]; } for (j = i; j <= 4; j++) { *vpptr1 += (n_p1[j] - bd_p1[j]) * initial_p1[0]; *vmptr1 += (n_m1[j] - bd_m1[j]) * initial_m1[0]; *vpptr2 += (n_p2[j] - bd_p2[j]) * initial_p1[0]; *vmptr2 += (n_m2[j] - bd_m2[j]) * initial_m1[0]; } sp_p1 += width; sp_m1 -= width; vp1 += 1; vp2 += 1; vm1 -= 1; vm2 -= 1; } transfer_pixels (val_p1, val_m1, dest1 + col, width, height); transfer_pixels (val_p2, val_m2, dest2 + col, width, height); if (!preview) { progress += height; if ((col % 5) == 0) gimp_progress_update ((gdouble) progress / (gdouble) max_progress); } } for (row = 0; row < height; row++) { memset (val_p1, 0, width * sizeof (gdouble)); memset (val_p2, 0, width * sizeof (gdouble)); memset (val_m1, 0, width * sizeof (gdouble)); memset (val_m2, 0, width * sizeof (gdouble)); src1 = dest1 + row * width; src2 = dest2 + row * width; sp_p1 = src1; sp_p2 = src2; sp_m1 = src1 + width - 1; sp_m2 = src2 + width - 1; vp1 = val_p1; vp2 = val_p2; vm1 = val_m1 + width - 1; vm2 = val_m2 + width - 1; /* Set up the first vals */ initial_p1[0] = sp_p1[0]; initial_p2[0] = sp_p2[0]; initial_m1[0] = sp_m1[0]; initial_m2[0] = sp_m2[0]; for (col = 0; col < width; col++) { gdouble *vpptr1, *vmptr1; gdouble *vpptr2, *vmptr2; terms = (col < 4) ? col : 4; vpptr1 = vp1; vmptr1 = vm1; vpptr2 = vp2; vmptr2 = vm2; for (i = 0; i <= terms; i++) { *vpptr1 += n_p1[i] * sp_p1[-i] - d_p1[i] * vp1[-i]; *vmptr1 += n_m1[i] * sp_m1[i] - d_m1[i] * vm1[i]; *vpptr2 += n_p2[i] * sp_p2[-i] - d_p2[i] * vp2[-i]; *vmptr2 += n_m2[i] * sp_m2[i] - d_m2[i] * vm2[i]; } for (j = i; j <= 4; j++) { *vpptr1 += (n_p1[j] - bd_p1[j]) * initial_p1[0]; *vmptr1 += (n_m1[j] - bd_m1[j]) * initial_m1[0]; *vpptr2 += (n_p2[j] - bd_p2[j]) * initial_p2[0]; *vmptr2 += (n_m2[j] - bd_m2[j]) * initial_m2[0]; } sp_p1 ++; sp_p2 ++; sp_m1 --; sp_m2 --; vp1 ++; vp2 ++; vm1 --; vm2 --; } transfer_pixels (val_p1, val_m1, dest1 + row * width, 1, width); transfer_pixels (val_p2, val_m2, dest2 + row * width, 1, width); if (!preview) { progress += width; if ((row % 5) == 0) gimp_progress_update ((gdouble) progress / (gdouble) max_progress); } } /* Compute the ramp value which sets 'pct_black' % of the darkened pixels black */ ramp_down = compute_ramp (dest1, dest2, width * height, pvals.pct_black, 1); ramp_up = compute_ramp (dest1, dest2, width * height, 1.0 - pvals.pct_white, 0); /* Initialize the pixel regions. */ gimp_pixel_rgn_init (&src_rgn, drawable, x1, y1, width, height, FALSE, FALSE); gimp_pixel_rgn_init (&dest_rgn, drawable, x1, y1, width, height, (preview == NULL), TRUE); pr = gimp_pixel_rgns_register (2, &src_rgn, &dest_rgn); while (pr) { guchar *src_ptr = src_rgn.data; guchar *dest_ptr = dest_rgn.data; guchar *blur_ptr = dest1 + (src_rgn.y - y1) * width + (src_rgn.x - x1); guchar *avg_ptr = dest2 + (src_rgn.y - y1) * width + (src_rgn.x - x1); gdouble diff, mult; gdouble lightness = 0.0; for (row = 0; row < src_rgn.h; row++) { for (col = 0; col < src_rgn.w; col++) { if (avg_ptr[col] > EPSILON) { diff = (gdouble) blur_ptr[col] / (gdouble) avg_ptr[col]; if (diff < pvals.threshold) { if (ramp_down == 0.0) mult = 0.0; else mult = (ramp_down - MIN (ramp_down, (pvals.threshold - diff))) / ramp_down; lightness = CLAMP (blur_ptr[col] * mult, 0, 255); } else { if (ramp_up == 0.0) mult = 1.0; else mult = MIN (ramp_up, (diff - pvals.threshold)) / ramp_up; lightness = 255 - (1.0 - mult) * (255 - blur_ptr[col]); lightness = CLAMP (lightness, 0, 255); } } else { lightness = 0; } if (bytes < 3) { dest_ptr[col * bytes] = (guchar) lightness; if (has_alpha) dest_ptr[col * bytes + 1] = src_ptr[col * src_rgn.bpp + 1]; } else { dest_ptr[col * bytes + 0] = lightness; dest_ptr[col * bytes + 1] = lightness; dest_ptr[col * bytes + 2] = lightness; if (has_alpha) dest_ptr[col * bytes + 3] = src_ptr[col * src_rgn.bpp + 3]; } } src_ptr += src_rgn.rowstride; dest_ptr += dest_rgn.rowstride; blur_ptr += width; avg_ptr += width; } if (preview) { gimp_drawable_preview_draw_region (GIMP_DRAWABLE_PREVIEW (preview), &dest_rgn); } else { progress += src_rgn.w * src_rgn.h; gimp_progress_update ((gdouble) progress / (gdouble) max_progress); } pr = gimp_pixel_rgns_process (pr); } if (! preview) { /* merge the shadow, update the drawable */ gimp_drawable_flush (drawable); gimp_drawable_merge_shadow (drawable->drawable_id, TRUE); gimp_drawable_update (drawable->drawable_id, x1, y1, width, height); } /* free up buffers */ g_free (val_p1); g_free (val_p2); g_free (val_m1); g_free (val_m2); g_free (dest1); g_free (dest2); }
static void softglow (GimpDrawable *drawable, GimpPreview *preview) { GimpPixelRgn src_rgn, dest_rgn; GimpPixelRgn *pr; gint width, height; gint bytes; gboolean has_alpha; guchar *dest; guchar *src, *sp_p, *sp_m; gdouble n_p[5], n_m[5]; gdouble d_p[5], d_m[5]; gdouble bd_p[5], bd_m[5]; gdouble *val_p, *val_m, *vp, *vm; gint x1, y1, x2, y2; gint i, j; gint row, col, b; gint terms; gint progress, max_progress; gint initial_p[4]; gint initial_m[4]; gint tmp; gdouble radius; gdouble std_dev; gdouble val; if (preview) { gimp_preview_get_position (preview, &x1, &y1); gimp_preview_get_size (preview, &width, &height); x2 = x1 + width; y2 = y1 + height; } else { gimp_drawable_mask_bounds (drawable->drawable_id, &x1, &y1, &x2, &y2); width = (x2 - x1); height = (y2 - y1); } bytes = drawable->bpp; has_alpha = gimp_drawable_has_alpha (drawable->drawable_id); val_p = g_new (gdouble, MAX (width, height)); val_m = g_new (gdouble, MAX (width, height)); dest = g_new0 (guchar, width * height); progress = 0; max_progress = width * height * 3; /* Initialize the pixel regions. */ gimp_pixel_rgn_init (&src_rgn, drawable, x1, y1, width, height, FALSE, FALSE); for (pr = gimp_pixel_rgns_register (1, &src_rgn); pr != NULL; pr = gimp_pixel_rgns_process (pr)) { guchar *src_ptr = src_rgn.data; guchar *dest_ptr = dest + (src_rgn.y - y1) * width + (src_rgn.x - x1); for (row = 0; row < src_rgn.h; row++) { for (col = 0; col < src_rgn.w; col++) { /* desaturate */ if (bytes > 2) dest_ptr[col] = (guchar) gimp_rgb_to_l_int (src_ptr[col * bytes + 0], src_ptr[col * bytes + 1], src_ptr[col * bytes + 2]); else dest_ptr[col] = (guchar) src_ptr[col * bytes]; /* compute sigmoidal transfer */ val = dest_ptr[col] / 255.0; val = 255.0 / (1 + exp (-(SIGMOIDAL_BASE + (svals.sharpness * SIGMOIDAL_RANGE)) * (val - 0.5))); val = val * svals.brightness; dest_ptr[col] = (guchar) CLAMP (val, 0, 255); } src_ptr += src_rgn.rowstride; dest_ptr += width; } if (!preview) { progress += src_rgn.w * src_rgn.h; gimp_progress_update ((gdouble) progress / (gdouble) max_progress); } } /* Calculate the standard deviations */ radius = fabs (svals.glow_radius) + 1.0; std_dev = sqrt (-(radius * radius) / (2 * log (1.0 / 255.0))); /* derive the constants for calculating the gaussian from the std dev */ find_constants (n_p, n_m, d_p, d_m, bd_p, bd_m, std_dev); /* First the vertical pass */ for (col = 0; col < width; col++) { memset (val_p, 0, height * sizeof (gdouble)); memset (val_m, 0, height * sizeof (gdouble)); src = dest + col; sp_p = src; sp_m = src + width * (height - 1); vp = val_p; vm = val_m + (height - 1); /* Set up the first vals */ initial_p[0] = sp_p[0]; initial_m[0] = sp_m[0]; for (row = 0; row < height; row++) { gdouble *vpptr, *vmptr; terms = (row < 4) ? row : 4; vpptr = vp; vmptr = vm; for (i = 0; i <= terms; i++) { *vpptr += n_p[i] * sp_p[-i * width] - d_p[i] * vp[-i]; *vmptr += n_m[i] * sp_m[i * width] - d_m[i] * vm[i]; } for (j = i; j <= 4; j++) { *vpptr += (n_p[j] - bd_p[j]) * initial_p[0]; *vmptr += (n_m[j] - bd_m[j]) * initial_m[0]; } sp_p += width; sp_m -= width; vp ++; vm --; } transfer_pixels (val_p, val_m, dest + col, width, height); if (!preview) { progress += height; if ((col % 5) == 0) gimp_progress_update ((gdouble) progress / (gdouble) max_progress); } } for (row = 0; row < height; row++) { memset (val_p, 0, width * sizeof (gdouble)); memset (val_m, 0, width * sizeof (gdouble)); src = dest + row * width; sp_p = src; sp_m = src + width - 1; vp = val_p; vm = val_m + width - 1; /* Set up the first vals */ initial_p[0] = sp_p[0]; initial_m[0] = sp_m[0]; for (col = 0; col < width; col++) { gdouble *vpptr, *vmptr; terms = (col < 4) ? col : 4; vpptr = vp; vmptr = vm; for (i = 0; i <= terms; i++) { *vpptr += n_p[i] * sp_p[-i] - d_p[i] * vp[-i]; *vmptr += n_m[i] * sp_m[i] - d_m[i] * vm[i]; } for (j = i; j <= 4; j++) { *vpptr += (n_p[j] - bd_p[j]) * initial_p[0]; *vmptr += (n_m[j] - bd_m[j]) * initial_m[0]; } sp_p ++; sp_m --; vp ++; vm --; } transfer_pixels (val_p, val_m, dest + row * width, 1, width); if (!preview) { progress += width; if ((row % 5) == 0) gimp_progress_update ((gdouble) progress / (gdouble) max_progress); } } /* Initialize the pixel regions. */ gimp_pixel_rgn_init (&src_rgn, drawable, x1, y1, width, height, FALSE, FALSE); gimp_pixel_rgn_init (&dest_rgn, drawable, x1, y1, width, height, (preview == NULL), TRUE); for (pr = gimp_pixel_rgns_register (2, &src_rgn, &dest_rgn); pr != NULL; pr = gimp_pixel_rgns_process (pr)) { guchar *src_ptr = src_rgn.data; guchar *dest_ptr = dest_rgn.data; guchar *blur_ptr = dest + (src_rgn.y - y1) * width + (src_rgn.x - x1); for (row = 0; row < src_rgn.h; row++) { for (col = 0; col < src_rgn.w; col++) { /* screen op */ for (b = 0; b < (has_alpha ? (bytes - 1) : bytes); b++) dest_ptr[col * bytes + b] = 255 - INT_MULT((255 - src_ptr[col * bytes + b]), (255 - blur_ptr[col]), tmp); if (has_alpha) dest_ptr[col * bytes + b] = src_ptr[col * bytes + b]; } src_ptr += src_rgn.rowstride; dest_ptr += dest_rgn.rowstride; blur_ptr += width; } if (preview) { gimp_drawable_preview_draw_region (GIMP_DRAWABLE_PREVIEW (preview), &dest_rgn); } else { progress += src_rgn.w * src_rgn.h; gimp_progress_update ((gdouble) progress / (gdouble) max_progress); } } if (! preview) { /* merge the shadow, update the drawable */ gimp_drawable_flush (drawable); gimp_drawable_merge_shadow (drawable->drawable_id, TRUE); gimp_drawable_update (drawable->drawable_id, x1, y1, (x2 - x1), (y2 - y1)); } /* free up buffers */ g_free (val_p); g_free (val_m); g_free (dest); }