/*************************************/ /* PIKA image manipulation routines. */ /*************************************/ #include "config.h" #include #include #include "lighting-main.h" #include "lighting-image.h" #include "lighting-preview.h" #include "lighting-ui.h" PikaDrawable *input_drawable; PikaDrawable *output_drawable; GeglBuffer *source_buffer; GeglBuffer *dest_buffer; PikaDrawable *bump_drawable; GeglBuffer *bump_buffer; const Babl *bump_format; PikaDrawable *env_drawable; GeglBuffer *env_buffer; guchar *preview_rgb_data = NULL; gint preview_rgb_stride; cairo_surface_t *preview_surface = NULL; glong maxcounter; gint width, height; gint env_width, env_height; PikaRGB background; gint border_x1, border_y1, border_x2, border_y2; guchar sinemap[256], spheremap[256], logmap[256]; /******************/ /* Implementation */ /******************/ guchar peek_map (GeglBuffer *buffer, const Babl *format, gint x, gint y) { guchar data[4]; guchar ret_val; gegl_buffer_sample (buffer, x, y, NULL, data, format, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); if (babl_format_get_bytes_per_pixel (format)) { ret_val = data[0]; } else { ret_val = (guchar)((float)((data[0] + data[1] + data[2])/3.0)); } return ret_val; } PikaRGB peek (gint x, gint y) { PikaRGB color; gegl_buffer_sample (source_buffer, x, y, NULL, &color, babl_format ("R'G'B'A double"), GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); if (! babl_format_has_alpha (gegl_buffer_get_format (source_buffer))) color.a = 1.0; return color; } PikaRGB peek_env_map (gint x, gint y) { PikaRGB color; if (x < 0) x = 0; else if (x >= env_width) x = env_width - 1; if (y < 0) y = 0; else if (y >= env_height) y = env_height - 1; gegl_buffer_sample (env_buffer, x, y, NULL, &color, babl_format ("R'G'B'A double"), GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); color.a = 1.0; return color; } void poke (gint x, gint y, PikaRGB *color) { if (x < 0) x = 0; else if (x >= width) x = width - 1; if (y < 0) y = 0; else if (y >= height) y = height - 1; gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (x, y, 1, 1), 0, babl_format ("R'G'B'A double"), color, GEGL_AUTO_ROWSTRIDE); } gint check_bounds (gint x, gint y) { if (x < border_x1 || y < border_y1 || x >= border_x2 || y >= border_y2) return FALSE; else return TRUE; } PikaVector3 int_to_pos (gint x, gint y) { PikaVector3 pos; if (width >= height) { pos.x = (gdouble) x / (gdouble) width; pos.y = (gdouble) y / (gdouble) width; pos.y += 0.5 * (1.0 - (gdouble) height / (gdouble) width); } else { pos.x = (gdouble) x / (gdouble) height; pos.y = (gdouble) y / (gdouble) height; pos.x += 0.5 * (1.0 - (gdouble) width / (gdouble) height); } pos.z = 0.0; return pos; } PikaVector3 int_to_posf (gdouble x, gdouble y) { PikaVector3 pos; if (width >= height) { pos.x = x / (gdouble) width; pos.y = y / (gdouble) width; pos.y += 0.5 * (1.0 - (gdouble) height / (gdouble) width); } else { pos.x = x / (gdouble) height; pos.y = y / (gdouble) height; pos.x += 0.5 * (1.0 - (gdouble) width / (gdouble) height); } pos.z = 0.0; return pos; } void pos_to_int (gdouble x, gdouble y, gint *scr_x, gint *scr_y) { if (width >= height) { y -= 0.5 * (1.0 - (gdouble) height / (gdouble) width); *scr_x = RINT ((x * (gdouble) width)); *scr_y = RINT ((y * (gdouble) width)); } else { x -= 0.5 * (1.0 - (gdouble) width / (gdouble) height); *scr_x = RINT ((x * (gdouble) height)); *scr_y = RINT ((y *(gdouble) height)); } } void pos_to_float (gdouble x, gdouble y, gdouble *xf, gdouble *yf) { if (width >= height) { y -= 0.5 * (1.0 - (gdouble) height / (gdouble) width); *xf = x * (gdouble) (width-1); *yf = y * (gdouble) (width-1); } else { x -= 0.5 * (1.0 - (gdouble) width / (gdouble) height); *xf = x * (gdouble) (height-1); *yf = y * (gdouble) (height-1); } } /**********************************************/ /* Compute the image color at pos (u,v) using */ /* Quartics bilinear interpolation stuff. */ /**********************************************/ PikaRGB get_image_color (gdouble u, gdouble v, gint *inside) { gint x1, y1, x2, y2; PikaRGB p[4]; x1 = RINT (u); y1 = RINT (v); if (check_bounds (x1, y1) == FALSE) { *inside = FALSE; return background; } x2 = (x1 + 1); y2 = (y1 + 1); if (check_bounds (x2, y2) == FALSE) { *inside = TRUE; return peek (x1, y1); } *inside = TRUE; p[0] = peek (x1, y1); p[1] = peek (x2, y1); p[2] = peek (x1, y2); p[3] = peek (x2, y2); return pika_bilinear_rgba (u, v, p); } gdouble get_map_value (GeglBuffer *buffer, const Babl *format, gdouble u, gdouble v, gint *inside) { gint x1, y1, x2, y2; gdouble p[4]; x1 = RINT (u); y1 = RINT (v); x2 = (x1 + 1); y2 = (y1 + 1); if (check_bounds (x2, y2) == FALSE) { *inside = TRUE; return (gdouble) peek_map (buffer, format, x1, y1); } *inside = TRUE; p[0] = (gdouble) peek_map (buffer, format, x1, y1); p[1] = (gdouble) peek_map (buffer, format, x2, y1); p[2] = (gdouble) peek_map (buffer, format, x1, y2); p[3] = (gdouble) peek_map (buffer, format, x2, y2); return pika_bilinear (u, v, p); } static void compute_maps (void) { gint x; gdouble val, c, d; /* Compute Sine, Log and Spherical transfer function maps */ /* ====================================================== */ c = 1.0 / 255.0; d = 1.15 * 255.0; for (x = 0; x < 256; x++) { sinemap[x] = (guchar) (255.0 * (0.5 * (sin ((G_PI * c * (gdouble) x) - 0.5 * G_PI) + 1.0))); spheremap[x] = (guchar) (255.0 * (sqrt (sin (G_PI * (gdouble) x / 512.0)))); val = (d * exp (-1.0 / (8.0 * c * ((gdouble) x + 5.0)))); if (val > 255.0) val = 255.0; logmap[x] = (guchar) val; } } /****************************************/ /* Allocate memory for temporary images */ /****************************************/ gint image_setup (PikaDrawable *drawable, gint interactive) { gint w, h; gboolean ret; compute_maps (); /* Get some useful info on the input drawable */ /* ========================================== */ input_drawable = drawable; output_drawable = drawable; ret = pika_drawable_mask_intersect (drawable, &border_x1, &border_y1, &w, &h); border_x2 = border_x1 + w; border_y2 = border_y1 + h; if (! ret) return FALSE; width = pika_drawable_get_width (input_drawable); height = pika_drawable_get_height (input_drawable); source_buffer = pika_drawable_get_buffer (input_drawable); maxcounter = (glong) width * (glong) height; if (interactive) { preview_rgb_stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, PREVIEW_WIDTH); preview_rgb_data = g_new0 (guchar, preview_rgb_stride * PREVIEW_HEIGHT); preview_surface = cairo_image_surface_create_for_data (preview_rgb_data, CAIRO_FORMAT_RGB24, PREVIEW_WIDTH, PREVIEW_HEIGHT, preview_rgb_stride); } return TRUE; } void bumpmap_setup (PikaDrawable *bumpmap) { if (bumpmap) { if (! bump_buffer) { bump_buffer = pika_drawable_get_buffer (bumpmap); } if (pika_drawable_is_rgb (bumpmap)) bump_format = babl_format ("R'G'B' u8"); else bump_format = babl_format ("Y' u8"); /* FIXME */ } } void envmap_setup (PikaDrawable *envmap) { if (envmap && ! env_buffer) { env_width = pika_drawable_get_width (envmap); env_height = pika_drawable_get_height (envmap); env_buffer = pika_drawable_get_buffer (envmap); } }