static gpointer negative_exec (GthAsyncTask *task, gpointer user_data) { NegativeData *negative_data = user_data; cairo_format_t format; int width; int height; int source_stride; int destination_stride; unsigned char *p_source_line; unsigned char *p_destination_line; unsigned char *p_source; unsigned char *p_destination; gboolean cancelled; double progress; gboolean terminated; int x, y; unsigned char red, green, blue, alpha; format = cairo_image_surface_get_format (negative_data->source); width = cairo_image_surface_get_width (negative_data->source); height = cairo_image_surface_get_height (negative_data->source); source_stride = cairo_image_surface_get_stride (negative_data->source); negative_data->destination = cairo_image_surface_create (format, width, height); destination_stride = cairo_image_surface_get_stride (negative_data->destination); p_source_line = _cairo_image_surface_flush_and_get_data (negative_data->source); p_destination_line = _cairo_image_surface_flush_and_get_data (negative_data->destination); for (y = 0; y < height; y++) { gth_async_task_get_data (task, NULL, &cancelled, NULL); if (cancelled) return NULL; progress = (double) y / height; gth_async_task_set_data (task, NULL, NULL, &progress); p_source = p_source_line; p_destination = p_destination_line; for (x = 0; x < width; x++) { CAIRO_GET_RGBA (p_source, red, green, blue, alpha); CAIRO_SET_RGBA (p_destination, 255 - red, 255 - green, 255 - blue, alpha); p_source += 4; p_destination += 4; } p_source_line += source_stride; p_destination_line += destination_stride; } cairo_surface_mark_dirty (negative_data->destination); terminated = TRUE; gth_async_task_set_data (task, &terminated, NULL, NULL); return NULL; }
cairo_surface_t * _cairo_image_surface_transform (cairo_surface_t *source, GthTransform transform) { cairo_surface_t *destination = NULL; cairo_format_t format; int width; int height; int source_stride; int destination_width; int destination_height; int line_start; int line_step; int pixel_step; unsigned char *p_source_line; unsigned char *p_destination_line; unsigned char *p_source; unsigned char *p_destination; int x; if (source == NULL) return NULL; format = cairo_image_surface_get_format (source); width = cairo_image_surface_get_width (source); height = cairo_image_surface_get_height (source); source_stride = cairo_image_surface_get_stride (source); _cairo_image_surface_transform_get_steps (format, width, height, transform, &destination_width, &destination_height, &line_start, &line_step, &pixel_step); destination = cairo_image_surface_create (format, destination_width, destination_height); p_source_line = _cairo_image_surface_flush_and_get_data (source); p_destination_line = _cairo_image_surface_flush_and_get_data (destination) + line_start; while (height-- > 0) { p_source = p_source_line; p_destination = p_destination_line; for (x = 0; x < width; x++) { memcpy (p_destination, p_source, 4); p_source += 4; p_destination += pixel_step; } p_source_line += source_stride; p_destination_line += line_step; } cairo_surface_mark_dirty (destination); return destination; }
cairo_surface_t * _cairo_image_surface_color_shift (cairo_surface_t *image, int shift) { cairo_surface_t *shifted; int i, j; int width, height, src_stride, dest_stride; guchar *src_pixels, *src_row, *src_pixel; guchar *dest_pixels, *dest_row, *dest_pixel; int val, temp; guchar r, g, b, a; shifted = _cairo_image_surface_create_compatible (image); width = cairo_image_surface_get_width (image); height = cairo_image_surface_get_height (image); src_stride = cairo_image_surface_get_stride (image); src_pixels = _cairo_image_surface_flush_and_get_data (image); dest_stride = cairo_image_surface_get_stride (shifted); dest_pixels = _cairo_image_surface_flush_and_get_data (shifted); src_row = src_pixels; dest_row = dest_pixels; for (i = 0; i < height; i++) { src_pixel = src_row; dest_pixel = dest_row; for (j = 0; j < width; j++) { CAIRO_GET_RGBA (src_pixel, r, g, b, a); val = r + shift; r = CLAMP (val, 0, 255); val = g + shift; g = CLAMP (val, 0, 255); val = b + shift; b = CLAMP (val, 0, 255); CAIRO_SET_RGBA (dest_pixel, r, g, b, a); src_pixel += 4; dest_pixel += 4; } src_row += src_stride; dest_row += dest_stride; } cairo_surface_mark_dirty (shifted); return shifted; }
void _cairo_paint_full_gradient (cairo_surface_t *surface, GdkRGBA *h_color1, GdkRGBA *h_color2, GdkRGBA *v_color1, GdkRGBA *v_color2) { cairo_color_255_t hcolor1; cairo_color_255_t hcolor2; cairo_color_255_t vcolor1; cairo_color_255_t vcolor2; int width; int height; int s_stride; unsigned char *s_pixels; int h, w; double x, y; double x_y, x_1_y, y_1_x, _1_x_1_y; guchar red, green, blue; if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) return; _gdk_rgba_to_cairo_color_255 (h_color1, &hcolor1); _gdk_rgba_to_cairo_color_255 (h_color2, &hcolor2); _gdk_rgba_to_cairo_color_255 (v_color1, &vcolor1); _gdk_rgba_to_cairo_color_255 (v_color2, &vcolor2); width = cairo_image_surface_get_width (surface); height = cairo_image_surface_get_height (surface); s_stride = cairo_image_surface_get_stride (surface); s_pixels = _cairo_image_surface_flush_and_get_data (surface); for (h = 0; h < height; h++) { guchar *s_iter = s_pixels; x = (double) (height - h) / height; for (w = 0; w < width; w++) { y = (double) (width - w) / width; x_y = x * y; x_1_y = x * (1.0 - y); y_1_x = y * (1.0 - x); _1_x_1_y = (1.0 - x) * (1.0 - y); red = hcolor1.r * x_y + hcolor2.r * x_1_y + vcolor1.r * y_1_x + vcolor2.r * _1_x_1_y; green = hcolor1.g * x_y + hcolor2.g * x_1_y + vcolor1.g * y_1_x + vcolor2.g * _1_x_1_y; blue = hcolor1.b * x_y + hcolor2.b * x_1_y + vcolor1.b * y_1_x + vcolor2.b * _1_x_1_y; CAIRO_SET_RGB (s_iter, red, green, blue); s_iter += 4; } s_pixels += s_stride; } cairo_surface_mark_dirty (surface); }
cairo_surface_t * _cairo_image_surface_copy_subsurface (cairo_surface_t *source, int src_x, int src_y, int width, int height) { cairo_surface_t *destination; cairo_status_t status; int source_stride; int destination_stride; unsigned char *p_source; unsigned char *p_destination; int row_size; g_return_val_if_fail (source != NULL, NULL); g_return_val_if_fail (src_x + width <= cairo_image_surface_get_width (source), NULL); g_return_val_if_fail (src_y + height <= cairo_image_surface_get_height (source), NULL); destination = cairo_image_surface_create (cairo_image_surface_get_format (source), width, height); status = cairo_surface_status (destination); if (status != CAIRO_STATUS_SUCCESS) { g_warning ("_cairo_image_surface_copy_subsurface: could not create the surface: %s", cairo_status_to_string (status)); cairo_surface_destroy (destination); return NULL; } source_stride = cairo_image_surface_get_stride (source); destination_stride = cairo_image_surface_get_stride (destination); p_source = _cairo_image_surface_flush_and_get_data (source) + (src_y * source_stride) + (src_x * 4); p_destination = _cairo_image_surface_flush_and_get_data (destination); row_size = width * 4; while (height-- > 0) { memcpy (p_destination, p_source, row_size); p_source += source_stride; p_destination += destination_stride; } cairo_surface_mark_dirty (destination); return destination; }
static cairo_surface_t * _cairo_surface_reduce_by_half (cairo_surface_t *src) { int src_width, src_height; cairo_surface_t *dest; int src_rowstride, dest_rowstride; guchar *row0, *row1, *row2; guchar *src_data, *dest_data; int y; src_width = cairo_image_surface_get_width (src); src_height = cairo_image_surface_get_height (src); dest = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, src_width / 2, src_height / 2); dest_rowstride = cairo_image_surface_get_stride (dest); dest_data = _cairo_image_surface_flush_and_get_data (dest); src_rowstride = cairo_image_surface_get_stride (src); src_data = _cairo_image_surface_flush_and_get_data (src); for (y = 0; y < src_height - (src_height % 2); y += 2) { row0 = src_data + (MAX (y - 1, 0) * src_rowstride); row1 = src_data + (y * src_rowstride); row2 = src_data + (MIN (y + 1, src_height - 1) * src_rowstride); _cairo_surface_reduce_row (dest_data, row0, row1, row2, src_width); dest_data += dest_rowstride; } cairo_surface_mark_dirty (dest); return dest; }
/* Started from from http://www.gtkforums.com/about5204.html * Author: tadeboro */ GdkPixbuf * _gdk_pixbuf_new_from_cairo_surface (cairo_surface_t *surface) { int width; int height; int s_stride; unsigned char *s_pixels; GdkPixbuf *pixbuf; int p_stride; guchar *p_pixels; int p_n_channels; if (surface == NULL) return NULL; if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) return NULL; width = cairo_image_surface_get_width (surface); height = cairo_image_surface_get_height (surface); s_stride = cairo_image_surface_get_stride (surface); s_pixels = _cairo_image_surface_flush_and_get_data (surface); pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, _cairo_image_surface_get_has_alpha (surface), 8, width, height); p_stride = gdk_pixbuf_get_rowstride (pixbuf); p_pixels = gdk_pixbuf_get_pixels (pixbuf); p_n_channels = gdk_pixbuf_get_n_channels (pixbuf); while (height--) { guchar *s_iter = s_pixels; guchar *p_iter = p_pixels; int i; for (i = 0; i < width; i++) { gdouble alpha_factor = (gdouble) 0xff / s_iter[CAIRO_ALPHA]; p_iter[0] = (guchar) (alpha_factor * s_iter[CAIRO_RED] + .5); p_iter[1] = (guchar) (alpha_factor * s_iter[CAIRO_GREEN] + .5); p_iter[2] = (guchar) (alpha_factor * s_iter[CAIRO_BLUE] + .5); if (p_n_channels == 4) p_iter[3] = s_iter[CAIRO_ALPHA]; s_iter += 4; p_iter += p_n_channels; } s_pixels += s_stride; p_pixels += p_stride; } return pixbuf; }
static cairo_surface_t * _cairo_surface_create_from_ppm (int width, int height, int colors, int bits, guchar *buffer, gsize buffer_size) { cairo_surface_t *surface; int stride; guchar *buffer_p; int r, c; guchar *row; guchar *column; guint32 pixel; if (bits != 8) return NULL; surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); stride = cairo_image_surface_get_stride (surface); buffer_p = buffer; row = _cairo_image_surface_flush_and_get_data (surface); for (r = 0; r < height; r++) { column = row; for (c = 0; c < width; c++) { switch (colors) { case 4: pixel = CAIRO_RGBA_TO_UINT32 (buffer_p[0], buffer_p[1], buffer_p[2], buffer_p[3]); break; case 3: pixel = CAIRO_RGBA_TO_UINT32 (buffer_p[0], buffer_p[1], buffer_p[2], 0xff); break; case 1: pixel = CAIRO_RGBA_TO_UINT32 (buffer_p[0], buffer_p[0], buffer_p[0], 0xff); break; } memcpy (column, &pixel, sizeof (guint32)); column += 4; buffer_p += colors; } row += stride; } cairo_surface_mark_dirty (surface); return surface; }
cairo_surface_t * _cairo_image_surface_copy (cairo_surface_t *source) { cairo_surface_t *result; unsigned char *p_source; unsigned char *p_destination; if (source == NULL) return NULL; result = _cairo_image_surface_create (cairo_image_surface_get_format (source), cairo_image_surface_get_width (source), cairo_image_surface_get_height (source)); if (result == NULL) return NULL; p_source = _cairo_image_surface_flush_and_get_data (source); p_destination = _cairo_image_surface_flush_and_get_data (result); memcpy (p_destination, p_source, cairo_image_surface_get_stride (source) * cairo_image_surface_get_height (source)); cairo_surface_mark_dirty (result); return result; }
gboolean _cairo_image_surface_get_has_alpha (cairo_surface_t *surface) { cairo_surface_metadata_t *metadata; gboolean has_alpha; int width; int height; int row_stride; guchar *row; int h, w; if (surface == NULL) return FALSE; metadata = _cairo_image_surface_get_metadata (surface); if ((metadata != NULL) && (metadata->valid_data & _CAIRO_METADATA_FLAG_HAS_ALPHA)) return metadata->has_alpha; has_alpha = FALSE; if (cairo_image_surface_get_format (surface) == CAIRO_FORMAT_ARGB32) { /* search an alpha value lower than 255 */ width = cairo_image_surface_get_width (surface); height = cairo_image_surface_get_height (surface); row_stride = cairo_image_surface_get_stride (surface); row = _cairo_image_surface_flush_and_get_data (surface); for (h = 0; ! has_alpha && (h < height); h++) { guchar *pixel = row; for (w = 0; w < width; w++) { if (pixel[CAIRO_ALPHA] < 255) { has_alpha = TRUE; break; } pixel += 4; } row += row_stride; } } _cairo_metadata_set_has_alpha (metadata, has_alpha); return has_alpha; }
gboolean _cairo_image_surface_get_has_alpha (cairo_surface_t *surface) { cairo_surface_metadata_t *metadata; int width; int height; int row_stride; guchar *row; int h, w; if (surface == NULL) return FALSE; metadata = cairo_surface_get_user_data (surface, &surface_metadata_key); if ((metadata != NULL) && (metadata->valid_data & _CAIRO_METADATA_FLAG_HAS_ALPHA)) return metadata->has_alpha; if (cairo_image_surface_get_format (surface) != CAIRO_FORMAT_ARGB32) return FALSE; /* search an alpha value lower than 255 */ width = cairo_image_surface_get_width (surface); height = cairo_image_surface_get_height (surface); row_stride = cairo_image_surface_get_stride (surface); row = _cairo_image_surface_flush_and_get_data (surface); for (h = 0; h < height; h++) { guchar *pixel = row; for (w = 0; w < width; w++) { if (pixel[CAIRO_ALPHA] < 255) return TRUE; pixel += 4; } row += row_stride; } return FALSE; }
static int _WebPPictureImportCairoSurface (WebPPicture *const picture, cairo_surface_t *image) { int stride; guchar *src_row; uint32_t *dest_row; int y, x, temp; guchar r, g, b, a; if (_cairo_image_surface_get_has_alpha (image)) picture->colorspace |= WEBP_CSP_ALPHA_BIT; else picture->colorspace &= ~WEBP_CSP_ALPHA_BIT; if (! WebPPictureAlloc (picture)) return 0; stride = cairo_image_surface_get_stride (image); src_row = _cairo_image_surface_flush_and_get_data (image); dest_row = picture->argb; for (y= 0; y < cairo_image_surface_get_height (image); y++) { guchar *pixel = src_row; for (x = 0; x < cairo_image_surface_get_width (image); x++) { CAIRO_GET_RGBA (pixel, r, g, b, a); dest_row[x] = ((a << 24) | (r << 16) | (g << 8) | b); pixel += 4; } src_row += stride; dest_row += picture->argb_stride; } return 1; }
cairo_surface_t * _cairo_image_surface_create_from_pixbuf (GdkPixbuf *pixbuf) { cairo_surface_t *surface; cairo_surface_metadata_t *metadata; int width; int height; int p_stride; int p_n_channels; guchar *p_pixels; int s_stride; unsigned char *s_pixels; int h, w; guint32 pixel; guchar r, g, b, a; if (pixbuf == NULL) return NULL; g_object_get (G_OBJECT (pixbuf), "width", &width, "height", &height, "rowstride", &p_stride, "n-channels", &p_n_channels, "pixels", &p_pixels, NULL ); surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); s_stride = cairo_image_surface_get_stride (surface); s_pixels = _cairo_image_surface_flush_and_get_data (surface); metadata = _cairo_image_surface_get_metadata (surface); _cairo_metadata_set_has_alpha (metadata, (p_n_channels == 4)); if (p_n_channels == 4) { guchar *s_iter; guchar *p_iter; for (h = 0; h < height; h++) { s_iter = s_pixels; p_iter = p_pixels; for (w = 0; w < width; w++) { a = p_iter[3]; if (a == 0xff) { pixel = CAIRO_RGBA_TO_UINT32 (p_iter[0], p_iter[1], p_iter[2], 0xff); } else if (a == 0) { pixel = 0; } else { r = _cairo_multiply_alpha (p_iter[0], a); g = _cairo_multiply_alpha (p_iter[1], a); b = _cairo_multiply_alpha (p_iter[2], a); pixel = CAIRO_RGBA_TO_UINT32 (r, g, b, a); } memcpy (s_iter, &pixel, sizeof (guint32)); s_iter += 4; p_iter += p_n_channels; } s_pixels += s_stride; p_pixels += p_stride; } } else { guchar *s_iter; guchar *p_iter; for (h = 0; h < height; h++) { s_iter = s_pixels; p_iter = p_pixels; for (w = 0; w < width; w++) { pixel = CAIRO_RGBA_TO_UINT32 (p_iter[0], p_iter[1], p_iter[2], 0xff); memcpy (s_iter, &pixel, sizeof (guint32)); s_iter += 4; p_iter += p_n_channels; } s_pixels += s_stride; p_pixels += p_stride; } } cairo_surface_mark_dirty (surface); return surface; }
GthImage * _cairo_image_surface_create_from_tiff (GInputStream *istream, GthFileData *file_data, int requested_size, int *original_width_p, int *original_height_p, gboolean *loaded_original_p, gpointer user_data, GCancellable *cancellable, GError **error) { GthImage *image; Handle handle; TIFF *tif; gboolean first_directory; int best_directory; int max_width, max_height, min_diff; uint32 image_width; uint32 image_height; uint32 spp; uint16 extrasamples; uint16 *sampleinfo; uint16 orientation; char emsg[1024]; cairo_surface_t *surface; cairo_surface_metadata_t*metadata; uint32 *raster; image = gth_image_new (); handle.cancellable = cancellable; handle.size = 0; if ((file_data != NULL) && (file_data->info != NULL)) { handle.istream = g_buffered_input_stream_new (istream); handle.size = g_file_info_get_size (file_data->info); } else { void *data; gsize size; /* read the whole stream to get the file size */ if (! _g_input_stream_read_all (istream, &data, &size, cancellable, error)) return image; handle.istream = g_memory_input_stream_new_from_data (data, size, g_free); handle.size = size; } TIFFSetErrorHandler (tiff_error_handler); TIFFSetWarningHandler (tiff_error_handler); tif = TIFFClientOpen ("gth-tiff-reader", "r", &handle, tiff_read, tiff_write, tiff_seek, tiff_close, tiff_size, NULL, NULL); if (tif == NULL) { g_object_unref (handle.istream); g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, "Couldn't allocate memory for writing TIFF file"); return image; } /* find the best image to load */ first_directory = TRUE; best_directory = -1; max_width = -1; max_height = -1; min_diff = 0; do { int width; int height; if (TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &width) != 1) continue; if (TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &height) != 1) continue; if (! TIFFRGBAImageOK (tif, emsg)) continue; if (width > max_width) { max_width = width; max_height = height; if (requested_size <= 0) best_directory = TIFFCurrentDirectory (tif); } if (requested_size > 0) { int diff = abs (requested_size - width); if (first_directory) { min_diff = diff; best_directory = TIFFCurrentDirectory (tif); } else if (diff < min_diff) { min_diff = diff; best_directory = TIFFCurrentDirectory (tif); } } first_directory = FALSE; } while (TIFFReadDirectory (tif)); if (best_directory == -1) { TIFFClose (tif); g_object_unref (handle.istream); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Invalid TIFF format"); return image; } /* read the image */ TIFFSetDirectory (tif, best_directory); TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &image_width); TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &image_height); TIFFGetField (tif, TIFFTAG_SAMPLESPERPIXEL, &spp); TIFFGetFieldDefaulted (tif, TIFFTAG_EXTRASAMPLES, &extrasamples, &sampleinfo); if (TIFFGetFieldDefaulted (tif, TIFFTAG_ORIENTATION, &orientation) != 1) orientation = ORIENTATION_TOPLEFT; if (original_width_p) *original_width_p = max_width; if (original_height_p) *original_height_p = max_height; if (loaded_original_p) *loaded_original_p = (max_width == image_width); surface = _cairo_image_surface_create (CAIRO_FORMAT_ARGB32, image_width, image_height); if (surface == NULL) { TIFFClose (tif); g_object_unref (handle.istream); g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, "Couldn't allocate memory for writing TIFF file"); return image; } metadata = _cairo_image_surface_get_metadata (surface); _cairo_metadata_set_has_alpha (metadata, (extrasamples == 1) || (spp == 4)); _cairo_metadata_set_original_size (metadata, max_width, max_height); raster = (uint32*) _TIFFmalloc (image_width * image_height * sizeof (uint32)); if (raster == NULL) { cairo_surface_destroy (surface); TIFFClose (tif); g_object_unref (handle.istream); g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, "Couldn't allocate memory for writing TIFF file"); return image; } if (TIFFReadRGBAImageOriented (tif, image_width, image_height, raster, orientation, 0)) { guchar *surface_row; int line_step; int x, y, temp; guchar r, g, b, a; uint32 *src_pixel; surface_row = _cairo_image_surface_flush_and_get_data (surface); line_step = cairo_image_surface_get_stride (surface); src_pixel = raster; for (y = 0; y < image_height; y++) { guchar *dest_pixel = surface_row; if (g_cancellable_is_cancelled (cancellable)) goto stop_loading; for (x = 0; x < image_width; x++) { r = TIFFGetR (*src_pixel); g = TIFFGetG (*src_pixel); b = TIFFGetB (*src_pixel); a = TIFFGetA (*src_pixel); CAIRO_SET_RGBA (dest_pixel, r, g, b, a); dest_pixel += 4; src_pixel += 1; } surface_row += line_step; } } stop_loading: cairo_surface_mark_dirty (surface); if (! g_cancellable_is_cancelled (cancellable)) gth_image_set_cairo_surface (image, surface); _TIFFfree (raster); cairo_surface_destroy (surface); TIFFClose (tif); g_object_unref (handle.istream); return image; }
static gpointer grayscale_exec (GthAsyncTask *task, gpointer user_data) { GrayscaleData *grayscale_data = user_data; cairo_surface_t *source; cairo_format_t format; int width; int height; int source_stride; cairo_surface_t *destination; int destination_stride; unsigned char *p_source_line; unsigned char *p_destination_line; unsigned char *p_source; unsigned char *p_destination; gboolean cancelled; double progress; int x, y; unsigned char red, green, blue, alpha; unsigned char min, max, value; source = gth_image_task_get_source_surface (GTH_IMAGE_TASK (task)); format = cairo_image_surface_get_format (source); width = cairo_image_surface_get_width (source); height = cairo_image_surface_get_height (source); source_stride = cairo_image_surface_get_stride (source); destination = cairo_image_surface_create (format, width, height); destination_stride = cairo_image_surface_get_stride (destination); p_source_line = _cairo_image_surface_flush_and_get_data (source); p_destination_line = _cairo_image_surface_flush_and_get_data (destination); for (y = 0; y < height; y++) { gth_async_task_get_data (task, NULL, &cancelled, NULL); if (cancelled) return NULL; progress = (double) y / height; gth_async_task_set_data (task, NULL, NULL, &progress); p_source = p_source_line; p_destination = p_destination_line; for (x = 0; x < width; x++) { CAIRO_GET_RGBA (p_source, red, green, blue, alpha); switch (grayscale_data->method) { case METHOD_BRIGHTNESS: value = (0.2125 * red + 0.7154 * green + 0.072 * blue); break; case METHOD_SATURATION: max = MAX (MAX (red, green), blue); min = MIN (MIN (red, green), blue); value = (max + min) / 2; break; case METHOD_AVARAGE: value = (0.3333 * red + 0.3333 * green + 0.3333 * blue); break; default: g_assert_not_reached (); } CAIRO_SET_RGBA (p_destination, value, value, value, alpha); p_source += 4; p_destination += 4; } p_source_line += source_stride; p_destination_line += destination_stride; } cairo_surface_mark_dirty (destination); gth_image_task_set_destination_surface (GTH_IMAGE_TASK (task), destination); cairo_surface_destroy (destination); cairo_surface_destroy (source); return NULL; }
static gpointer adjust_contrast_exec (GthAsyncTask *task, gpointer user_data) { AdjustContrastData *adjust_data = user_data; cairo_format_t format; int width; int height; int source_stride; cairo_surface_t *destination; int destination_stride; unsigned char *p_source_line; unsigned char *p_destination_line; unsigned char *p_source; unsigned char *p_destination; gboolean cancelled; double progress; int x, y; unsigned char red, green, blue, alpha; GthImage *destination_image; /* initialize some extra data */ adjust_contrast_setup (adjust_data); /* convert the image */ format = cairo_image_surface_get_format (adjust_data->source); width = cairo_image_surface_get_width (adjust_data->source); height = cairo_image_surface_get_height (adjust_data->source); source_stride = cairo_image_surface_get_stride (adjust_data->source); destination = cairo_image_surface_create (format, width, height); destination_stride = cairo_image_surface_get_stride (destination); p_source_line = _cairo_image_surface_flush_and_get_data (adjust_data->source); p_destination_line = _cairo_image_surface_flush_and_get_data (destination); for (y = 0; y < height; y++) { gth_async_task_get_data (task, NULL, &cancelled, NULL); if (cancelled) return NULL; progress = (double) y / height; gth_async_task_set_data (task, NULL, NULL, &progress); p_source = p_source_line; p_destination = p_destination_line; for (x = 0; x < width; x++) { CAIRO_GET_RGBA (p_source, red, green, blue, alpha); red = adjust_contrast_func (adjust_data, GTH_HISTOGRAM_CHANNEL_RED, red); green = adjust_contrast_func (adjust_data, GTH_HISTOGRAM_CHANNEL_GREEN, green); blue = adjust_contrast_func (adjust_data, GTH_HISTOGRAM_CHANNEL_BLUE, blue); CAIRO_SET_RGBA (p_destination, red, green, blue, alpha); p_source += 4; p_destination += 4; } p_source_line += source_stride; p_destination_line += destination_stride; } cairo_surface_mark_dirty (destination); destination_image = gth_image_new_for_surface (destination); gth_image_task_set_destination (GTH_IMAGE_TASK (task), destination_image); _g_object_unref (destination_image); cairo_surface_destroy (destination); return NULL; }
GthImage * _cairo_image_surface_create_from_webp (GInputStream *istream, GthFileData *file_data, int requested_size, int *original_width, int *original_height, gboolean *loaded_original, gpointer user_data, GCancellable *cancellable, GError **error) { GthImage *image; WebPDecoderConfig config; guchar *buffer; gssize bytes_read; int width, height; cairo_surface_t *surface; cairo_surface_metadata_t *metadata; WebPIDecoder *idec; image = gth_image_new (); if (! WebPInitDecoderConfig (&config)) return image; buffer = g_new (guchar, BUFFER_SIZE); bytes_read = g_input_stream_read (istream, buffer, BUFFER_SIZE, cancellable, error); if (WebPGetFeatures (buffer, bytes_read, &config.input) != VP8_STATUS_OK) { g_free (buffer); return image; } width = config.input.width; height = config.input.height; if (original_width != NULL) *original_width = width; if (original_height != NULL) *original_height = height; #if SCALING_WORKS if (requested_size > 0) scale_keeping_ratio (&width, &height, requested_size, requested_size, FALSE); #endif surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); metadata = _cairo_image_surface_get_metadata (surface); _cairo_metadata_set_has_alpha (metadata, config.input.has_alpha); config.options.no_fancy_upsampling = 1; #if SCALING_WORKS if (requested_size > 0) { config.options.use_scaling = 1; config.options.scaled_width = width; config.options.scaled_height = height; } #endif #if G_BYTE_ORDER == G_LITTLE_ENDIAN config.output.colorspace = MODE_BGRA; #elif G_BYTE_ORDER == G_BIG_ENDIAN config.output.colorspace = MODE_ARGB; #endif config.output.u.RGBA.rgba = (uint8_t *) _cairo_image_surface_flush_and_get_data (surface); config.output.u.RGBA.stride = cairo_image_surface_get_stride (surface); config.output.u.RGBA.size = cairo_image_surface_get_stride (surface) * height; config.output.is_external_memory = 1; idec = WebPINewDecoder (&config.output); if (idec == NULL) { g_free (buffer); return image; } do { VP8StatusCode status = WebPIAppend (idec, buffer, bytes_read); if ((status != VP8_STATUS_OK) && (status != VP8_STATUS_SUSPENDED)) break; } while ((bytes_read = g_input_stream_read (istream, buffer, BUFFER_SIZE, cancellable, error)) > 0); cairo_surface_mark_dirty (surface); if (cairo_surface_status (surface) == CAIRO_STATUS_SUCCESS) gth_image_set_cairo_surface (image, surface); WebPIDelete (idec); WebPFreeDecBuffer (&config.output); g_free (buffer); return image; }
cairo_surface_t * _cairo_image_surface_scale_bilinear_2x2 (cairo_surface_t *image, int new_width, int new_height) { cairo_surface_t *scaled; int src_width; int src_height; guchar *p_src; guchar *p_dest; int src_rowstride; int dest_rowstride; ScaleReal step_x, step_y; guchar *p_dest_row; guchar *p_src_row; guchar *p_src_col; guchar *p_dest_col; ScaleReal x_src, y_src; int x, y; guchar r00, r01, r10, r11; guchar g00, g01, g10, g11; guchar b00, b01, b10, b11; guchar a00, a01, a10, a11; guchar r, g, b, a; guint32 pixel; ScaleReal tmp; ScaleReal x_fract, y_fract; int col, row; scaled = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, new_width, new_height); src_width = cairo_image_surface_get_width (image); src_height = cairo_image_surface_get_height (image); p_src = _cairo_image_surface_flush_and_get_data (image); p_dest = _cairo_image_surface_flush_and_get_data (scaled); src_rowstride = cairo_image_surface_get_stride (image); dest_rowstride = cairo_image_surface_get_stride (scaled); cairo_surface_flush (scaled); step_x = (ScaleReal) src_width / new_width; step_y = (ScaleReal) src_height / new_height; p_dest_row = p_dest; p_src_row = p_src; y_src = 0; for (y = 0; y < new_height; y++) { row = floor (y_src); y_fract = y_src - row; p_src_row = p_src + (row * src_rowstride); p_dest_col = p_dest_row; p_src_col = p_src_row; x_src = 0; for (x = 0; x < new_width; x++) { col = floor (x_src); x_fract = x_src - col; p_src_col = p_src_row + (col * 4); /* x00 */ CAIRO_COPY_RGBA (p_src_col, r00, g00, b00, a00); /* x01 */ if (col + 1 < src_width - 1) { p_src_col += 4; CAIRO_COPY_RGBA (p_src_col, r01, g01, b01, a01); } else { r01 = r00; g01 = g00; b01 = b00; a01 = a00; } /* x10 */ if (row + 1 < src_height - 1) { p_src_col = p_src_row + src_rowstride + (col * 4); CAIRO_COPY_RGBA (p_src_col, r10, g10, b10, a10); } else { r10 = r00; g10 = g00; b10 = b00; a10 = a00; } /* x11 */ if ((row + 1 < src_height - 1) && (col + 1 < src_width - 1)) { p_src_col += 4; CAIRO_COPY_RGBA (p_src_col, r11, g11, b11, a11); } else { r11 = r00; g11 = g00; b11 = b00; a11 = a00; } BILINEAR_INTERPOLATE (r, r00, r01, r10, r11, x_fract, y_fract); BILINEAR_INTERPOLATE (g, g00, g01, g10, g11, x_fract, y_fract); BILINEAR_INTERPOLATE (b, b00, b01, b10, b11, x_fract, y_fract); BILINEAR_INTERPOLATE (a, a00, a01, a10, a11, x_fract, y_fract); pixel = CAIRO_RGBA_TO_UINT32 (r, g, b, a); memcpy (p_dest_col, &pixel, 4); p_dest_col += 4; x_src += step_x; } p_dest_row += dest_rowstride; y_src += step_y; } cairo_surface_mark_dirty (scaled); return scaled; }
static gboolean _cairo_surface_write_as_tga (cairo_surface_t *image, char **buffer, gsize *buffer_size, char **keys, char **values, GError **error) { GthBufferData *buffer_data; int out_bpp = 0; int row; guchar header[18]; guchar footer[26]; gboolean rle_compression; gboolean alpha; guchar *pixels, *ptr, *buf; int width, height; int rowstride; rle_compression = TRUE; if (keys && *keys) { char **kiter = keys; char **viter = values; while (*kiter) { if (strcmp (*kiter, "compression") == 0) { if (*viter == NULL) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, "Must specify a compression type"); return FALSE; } if (strcmp (*viter, "none") == 0) rle_compression = FALSE; else if (strcmp (*viter, "rle") == 0) rle_compression = TRUE; else { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, "Unsupported compression type passed to the TGA saver"); return FALSE; } } else { g_warning ("Bad option name '%s' passed to the TGA saver", *kiter); return FALSE; } ++kiter; ++viter; } } width = cairo_image_surface_get_width (image); height = cairo_image_surface_get_height (image); alpha = _cairo_image_surface_get_has_alpha (image); pixels = _cairo_image_surface_flush_and_get_data (image); rowstride = cairo_image_surface_get_stride (image); buffer_data = gth_buffer_data_new (); /* write the header */ header[0] = 0; /* No image identifier / description */ header[1] = 0; header[2] = rle_compression ? 10 : 2; header[3] = header[4] = header[5] = header[6] = header[7] = 0; header[8] = header[9] = 0; /* xorigin */ header[10] = header[11] = 0; /* yorigin */ header[12] = width % 256; header[13] = width / 256; header[14] = height % 256; header[15] = height / 256; if (alpha) { out_bpp = 4; header[16] = 32; /* bpp */ header[17] = 0x28; /* alpha + orientation */ } else { out_bpp = 3; header[16] = 24; /* bpp */ header[17] = 0x20; /* alpha + orientation */ } gth_buffer_data_write (buffer_data, header, sizeof (header), error); /* allocate a small buffer to convert image data */ buf = g_try_malloc (width * out_bpp * sizeof (guchar)); if (! buf) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Insufficient memory")); return FALSE; } ptr = pixels; for (row = 0; row < height; ++row) { _cairo_copy_line_as_rgba_little_endian (buf, ptr, width, alpha); if (rle_compression) rle_write (buffer_data, buf, width, out_bpp, error); else gth_buffer_data_write (buffer_data, buf, width * out_bpp, error); ptr += rowstride; } g_free (buf); /* write the footer */ memset (footer, 0, 8); /* No extensions, no developer directory */ memcpy (footer + 8, magic, sizeof (magic)); /* magic signature */ gth_buffer_data_write (buffer_data, footer, sizeof (footer), error); gth_buffer_data_get (buffer_data, buffer, buffer_size); gth_buffer_data_free (buffer_data, FALSE); return TRUE; }
static gboolean _cairo_surface_write_as_jpeg (cairo_surface_t *image, char **buffer, gsize *buffer_size, char **keys, char **values, GError **error) { struct jpeg_compress_struct cinfo; struct error_handler_data jerr; guchar *buf = NULL; guchar *pixels = NULL; volatile int quality = 85; /* default; must be between 0 and 100 */ volatile int smoothing = 0; volatile gboolean optimize = FALSE; #ifdef HAVE_PROGRESSIVE_JPEG volatile gboolean progressive = FALSE; #endif int w, h = 0; int rowstride = 0; if (keys && *keys) { char **kiter = keys; char **viter = values; while (*kiter) { if (strcmp (*kiter, "quality") == 0) { char *endptr = NULL; quality = strtol (*viter, &endptr, 10); if (endptr == *viter) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, "JPEG quality must be a value between 0 and 100; value '%s' could not be parsed.", *viter); return FALSE; } if (quality < 0 || quality > 100) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, "JPEG quality must be a value between 0 and 100; value '%d' is not allowed.", quality); return FALSE; } } else if (strcmp (*kiter, "smooth") == 0) { char *endptr = NULL; smoothing = strtol (*viter, &endptr, 10); if (endptr == *viter) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, "JPEG smoothing must be a value between 0 and 100; value '%s' could not be parsed.", *viter); return FALSE; } if (smoothing < 0 || smoothing > 100) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, "JPEG smoothing must be a value between 0 and 100; value '%d' is not allowed.", smoothing); return FALSE; } } else if (strcmp (*kiter, "optimize") == 0) { if (strcmp (*viter, "yes") == 0) optimize = TRUE; else if (strcmp (*viter, "no") == 0) optimize = FALSE; else { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, "JPEG optimize option must be 'yes' or 'no', value is: %s", *viter); return FALSE; } } #ifdef HAVE_PROGRESSIVE_JPEG else if (strcmp (*kiter, "progressive") == 0) { if (strcmp (*viter, "yes") == 0) progressive = TRUE; else if (strcmp (*viter, "no") == 0) progressive = FALSE; else { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, "JPEG progressive option must be 'yes' or 'no', value is: %s", *viter); return FALSE; } } #endif else { g_warning ("Bad option name '%s' passed to JPEG saver", *kiter); return FALSE; } ++kiter; ++viter; } } rowstride = cairo_image_surface_get_stride (image); w = cairo_image_surface_get_width (image); h = cairo_image_surface_get_height (image); pixels = _cairo_image_surface_flush_and_get_data (image); g_return_val_if_fail (pixels != NULL, FALSE); /* allocate a small buffer to convert image data */ buf = g_try_malloc (w * 3 * sizeof (guchar)); if (! buf) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, "Couldn't allocate memory for loading JPEG file"); return FALSE; } /* set up error handling */ cinfo.err = jpeg_std_error (&(jerr.pub)); jerr.pub.error_exit = fatal_error_handler; jerr.pub.output_message = output_message_handler; jerr.error = error; if (sigsetjmp (jerr.setjmp_buffer, 1)) { jpeg_destroy_compress (&cinfo); g_free (buf); return FALSE; } /* setup compress params */ jpeg_create_compress (&cinfo); _jpeg_memory_dest (&cinfo, (void **)buffer, buffer_size); cinfo.image_width = w; cinfo.image_height = h; cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; /* set up jepg compression parameters */ jpeg_set_defaults (&cinfo); jpeg_set_quality (&cinfo, quality, TRUE); cinfo.smoothing_factor = smoothing; cinfo.optimize_coding = optimize; #ifdef HAVE_PROGRESSIVE_JPEG if (progressive) jpeg_simple_progression (&cinfo); #endif /* HAVE_PROGRESSIVE_JPEG */ jpeg_start_compress (&cinfo, TRUE); /* go one scanline at a time... and save */ while (cinfo.next_scanline < cinfo.image_height) { JSAMPROW *jbuf; /* convert scanline from RGBA to RGB packed */ _cairo_copy_line_as_rgba_big_endian (buf, pixels, w, FALSE); /* write scanline */ jbuf = (JSAMPROW *)(&buf); jpeg_write_scanlines (&cinfo, jbuf, 1); pixels += rowstride; } /* finish off */ jpeg_finish_compress (&cinfo); jpeg_destroy_compress (&cinfo); g_free (buf); return TRUE; }
static void horizontal_scale_transpose (cairo_surface_t *image, cairo_surface_t *scaled, ScaleReal scale_factor, resize_filter_t *resize_filter) { ScaleReal scale; ScaleReal support; int y; int image_width; int scaled_width; int scaled_height; guchar *p_src; guchar *p_dest; int src_rowstride; int dest_rowstride; ScaleReal *weights; if (resize_filter->cancelled) return; scale = MAX ((ScaleReal) 1.0 / scale_factor + EPSILON, 1.0); support = scale * resize_filter_get_support (resize_filter); if (support < 0.5) { support = 0.5; scale = 1.0; } image_width = cairo_image_surface_get_width (image); scaled_width = cairo_image_surface_get_width (scaled); scaled_height = cairo_image_surface_get_height (scaled); p_src = _cairo_image_surface_flush_and_get_data (image); p_dest = _cairo_image_surface_flush_and_get_data (scaled); src_rowstride = cairo_image_surface_get_stride (image); dest_rowstride = cairo_image_surface_get_stride (scaled); weights = g_new (ScaleReal, 2.0 * support + 3.0); scale = reciprocal (scale); for (y = 0; y < scaled_height; y++) { guchar *p_src_row; guchar *p_dest_pixel; ScaleReal bisect; int start; int stop; ScaleReal density; int n; int x; int i; #ifdef HAVE_VECTOR_OPERATIONS r4vector v_pixel, v_rgba; #endif /* HAVE_VECTOR_OPERATIONS */ if (resize_filter->task != NULL) { double progress; gth_async_task_get_data (resize_filter->task, NULL, &resize_filter->cancelled, NULL); if (resize_filter->cancelled) goto out; progress = (double) resize_filter->processed_lines++ / resize_filter->total_lines; gth_async_task_set_data (resize_filter->task, NULL, NULL, &progress); } bisect = ((ScaleReal) y + 0.5) / scale_factor + EPSILON; start = MAX (bisect - support + 0.5, 0); stop = MIN (bisect + support + 0.5, (ScaleReal) image_width); density = 0.0; for (n = 0; n < stop - start; n++) { weights[n] = resize_filter_get_weight (resize_filter, scale * ((ScaleReal) (start + n) - bisect + 0.5)); density += weights[n]; } /* g_assert (n == stop - start); g_assert (stop - start <= (2.0 * support) + 3); */ if ((density != 0.0) && (density != 1.0)) { density = reciprocal (density); for (i = 0; i < n; i++) weights[i] *= density; } p_src_row = p_src + (start * 4); p_dest_pixel = p_dest; for (x = 0; x < scaled_width; x++) { guchar *p_src_pixel; p_src_pixel = p_src_row; #ifdef HAVE_VECTOR_OPERATIONS v_rgba.v = (v4r) { 0.0, 0.0, 0.0, 0.0 }; for (i = 0; i < n; i++) { v_pixel.v = (v4r) { p_src_pixel[0], p_src_pixel[1], p_src_pixel[2], p_src_pixel[3] }; v_rgba.v = v_rgba.v + (v_pixel.v * weights[i]); p_src_pixel += 4; } v_rgba.v = v_rgba.v + 0.5; p_dest_pixel[0] = CLAMP_PIXEL (v_rgba.r[0]); p_dest_pixel[1] = CLAMP_PIXEL (v_rgba.r[1]); p_dest_pixel[2] = CLAMP_PIXEL (v_rgba.r[2]); p_dest_pixel[3] = CLAMP_PIXEL (v_rgba.r[3]); #else /* ! HAVE_VECTOR_OPERATIONS */ ScaleReal r, g, b, a, w; r = g = b = a = 0.0; for (i = 0; i < n; i++) { w = weights[i]; r += w * p_src_pixel[CAIRO_RED]; g += w * p_src_pixel[CAIRO_GREEN]; b += w * p_src_pixel[CAIRO_BLUE]; a += w * p_src_pixel[CAIRO_ALPHA]; p_src_pixel += 4; } p_dest_pixel[CAIRO_RED] = CLAMP_PIXEL (r + 0.5); p_dest_pixel[CAIRO_GREEN] = CLAMP_PIXEL (g + 0.5); p_dest_pixel[CAIRO_BLUE] = CLAMP_PIXEL (b + 0.5); p_dest_pixel[CAIRO_ALPHA] = CLAMP_PIXEL (a + 0.5); #endif /* HAVE_VECTOR_OPERATIONS */ p_dest_pixel += 4; p_src_row += src_rowstride; } p_dest += dest_rowstride; } out: cairo_surface_mark_dirty (scaled); g_free (weights); }
cairo_surface_t * _cairo_image_surface_scale_nearest (cairo_surface_t *image, int new_width, int new_height) { #if 0 cairo_surface_t *scaled; int src_width; int src_height; guchar *p_src; guchar *p_dest; int src_rowstride; int dest_rowstride; gfixed step_x, step_y; guchar *p_src_row; guchar *p_src_col; guchar *p_dest_row; guchar *p_dest_col; gfixed max_row, max_col; gfixed x_src, y_src; int x, y; g_return_val_if_fail (cairo_image_surface_get_format (image) == CAIRO_FORMAT_ARGB32, NULL); scaled = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, new_width, new_height); src_width = cairo_image_surface_get_width (image); src_height = cairo_image_surface_get_height (image); p_src = _cairo_image_surface_flush_and_get_data (image); p_dest = _cairo_image_surface_flush_and_get_data (scaled); src_rowstride = cairo_image_surface_get_stride (image); dest_rowstride = cairo_image_surface_get_stride (scaled); cairo_surface_flush (scaled); step_x = GDOUBLE_TO_FIXED ((double) src_width / new_width); step_y = GDOUBLE_TO_FIXED ((double) src_height / new_height); p_dest_row = p_dest; p_src_row = p_src; max_row = GINT_TO_FIXED (src_height - 1); max_col = GINT_TO_FIXED (src_width - 1); /* pick the pixel in the middle to avoid the shift effect. */ y_src = gfixed_div (step_y, GFIXED_2); for (y = 0; y < new_height; y++) { p_dest_col = p_dest_row; p_src_col = p_src_row; x_src = gfixed_div (step_x, GFIXED_2); for (x = 0; x < new_width; x++) { p_src_col = p_src_row + (GFIXED_TO_INT (MIN (x_src, max_col)) << 2); /* p_src_col = p_src_row + x_src * 4 */ memcpy (p_dest_col, p_src_col, 4); p_dest_col += 4; x_src += step_x; } p_dest_row += dest_rowstride; y_src += step_y; p_src_row = p_src + (GFIXED_TO_INT (MIN (y_src, max_row)) * src_rowstride); } cairo_surface_mark_dirty (scaled); return scaled; #else cairo_surface_t *output; cairo_t *cr; output = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, new_width, new_height); cr = cairo_create (output); cairo_scale (cr, (double) new_width / cairo_image_surface_get_width (image), (double) new_height / cairo_image_surface_get_height (image)); cairo_set_source_surface (cr, image, 0.0, 0.0); cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST); cairo_rectangle (cr, 0.0, 0.0, cairo_image_surface_get_width (image), cairo_image_surface_get_height (image)); cairo_fill (cr); cairo_surface_flush (output); cairo_destroy (cr); return output; #endif }
static gboolean _cairo_surface_write_as_png (cairo_surface_t *image, char **buffer, gsize *buffer_size, char **keys, char **values, GError **error) { int compression_level; int width, height; gboolean alpha; guchar *pixels, *ptr, *buf; int rowstride; CairoPngData *cairo_png_data; png_color_8 sig_bit; int bpp; int row; compression_level = 6; if (keys && *keys) { char **kiter = keys; char **viter = values; while (*kiter) { if (strcmp (*kiter, "compression") == 0) { if (*viter == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Must specify a compression level"); return FALSE; } compression_level = atoi (*viter); if (compression_level < 0 || compression_level > 9) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Unsupported compression level passed to the PNG saver"); return FALSE; } } else { g_warning ("Bad option name '%s' passed to the PNG saver", *kiter); return FALSE; } ++kiter; ++viter; } } width = cairo_image_surface_get_width (image); height = cairo_image_surface_get_height (image); alpha = _cairo_image_surface_get_has_alpha (image); pixels = _cairo_image_surface_flush_and_get_data (image); rowstride = cairo_image_surface_get_stride (image); cairo_png_data = g_new0 (CairoPngData, 1); cairo_png_data->error = error; cairo_png_data->buffer_data = gth_buffer_data_new (); cairo_png_data->png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, &cairo_png_data->error, gerror_error_func, gerror_warning_func); if (cairo_png_data->png_ptr == NULL) { _cairo_png_data_destroy (cairo_png_data); return FALSE; } cairo_png_data->png_info_ptr = png_create_info_struct (cairo_png_data->png_ptr); if (cairo_png_data->png_info_ptr == NULL) { _cairo_png_data_destroy (cairo_png_data); return FALSE; } if (PNG_SETJMP (cairo_png_data->png_ptr)) { _cairo_png_data_destroy (cairo_png_data); return FALSE; } png_set_write_fn (cairo_png_data->png_ptr, cairo_png_data, cairo_png_write_data_func, cairo_png_flush_data_func); /* Set the image information here */ png_set_IHDR (cairo_png_data->png_ptr, cairo_png_data->png_info_ptr, width, height, 8, (alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB), PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); /* Options */ sig_bit.red = 8; sig_bit.green = 8; sig_bit.blue = 8; if (alpha) sig_bit.alpha = 8; png_set_sBIT (cairo_png_data->png_ptr, cairo_png_data->png_info_ptr, &sig_bit); png_set_compression_level (cairo_png_data->png_ptr, compression_level); /* Write the file header information. */ png_write_info (cairo_png_data->png_ptr, cairo_png_data->png_info_ptr); /* Write the image */ bpp = alpha ? 4 : 3; buf = g_new (guchar, width * bpp); ptr = pixels; for (row = 0; row < height; ++row) { _cairo_copy_line_as_rgba_big_endian (buf, ptr, width, alpha); png_write_rows (cairo_png_data->png_ptr, &buf, 1); ptr += rowstride; } g_free (buf); png_write_end (cairo_png_data->png_ptr, cairo_png_data->png_info_ptr); gth_buffer_data_get (cairo_png_data->buffer_data, buffer, buffer_size); _cairo_png_data_destroy (cairo_png_data); return TRUE; }
static cairo_surface_t* rotate (cairo_surface_t *image, double angle, gboolean high_quality, guchar r0, guchar g0, guchar b0, guchar a0, GthAsyncTask *task) { cairo_surface_t *image_with_background; cairo_surface_t *rotated; double angle_rad; double cos_angle, sin_angle; double src_width, src_height; int new_width, new_height; int src_rowstride, new_rowstride; int xi, yi; double x, y; double x2, y2; int x2min, y2min; int x2max, y2max; double fx, fy; guchar *p_src, *p_new; guchar *p_src2, *p_new2; guchar r00, r01, r10, r11; guchar g00, g01, g10, g11; guchar b00, b01, b10, b11; guchar a00, a01, a10, a11; double half_new_width; double half_new_height; double half_src_width; double half_src_height; int tmp; guchar r, g, b, a; guint32 pixel; angle = CLAMP (angle, -90.0, 90.0); angle_rad = angle / 180.0 * G_PI; cos_angle = cos (angle_rad); sin_angle = sin (angle_rad); src_width = cairo_image_surface_get_width (image); src_height = cairo_image_surface_get_height (image); new_width = GDOUBLE_ROUND_TO_INT ( cos_angle * src_width + fabs(sin_angle) * src_height); new_height = GDOUBLE_ROUND_TO_INT (fabs (sin_angle) * src_width + cos_angle * src_height); if (a0 == 0xff) { /* pre-multiply the background color */ image_with_background = _cairo_image_surface_copy (image); p_src = _cairo_image_surface_flush_and_get_data (image); p_new = _cairo_image_surface_flush_and_get_data (image_with_background); src_rowstride = cairo_image_surface_get_stride (image); new_rowstride = cairo_image_surface_get_stride (image_with_background); cairo_surface_flush (image_with_background); for (yi = 0; yi < src_height; yi++) { p_src2 = p_src; p_new2 = p_new; for (xi = 0; xi < src_width; xi++) { a = p_src2[CAIRO_ALPHA]; r = p_src2[CAIRO_RED] + _cairo_multiply_alpha (r0, 0xff - a); g = p_src2[CAIRO_GREEN] + _cairo_multiply_alpha (g0, 0xff - a); b = p_src2[CAIRO_BLUE] + _cairo_multiply_alpha (b0, 0xff - a); pixel = CAIRO_RGBA_TO_UINT32 (r, g, b, 0xff); memcpy (p_new2, &pixel, sizeof (guint32)); p_new2 += 4; p_src2 += 4; } p_src += src_rowstride; p_new += new_rowstride; } cairo_surface_mark_dirty (image_with_background); } else image_with_background = cairo_surface_reference (image); /* create the rotated image */ rotated = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, new_width, new_height); p_src = _cairo_image_surface_flush_and_get_data (image_with_background); p_new = _cairo_image_surface_flush_and_get_data (rotated); src_rowstride = cairo_image_surface_get_stride (image_with_background); new_rowstride = cairo_image_surface_get_stride (rotated); /* * bilinear interpolation * fx * v00------------v01 * | | | * fy |--------v | * | | * | | * | | * v10------------v11 */ #define INTERPOLATE(v, v00, v01, v10, v11, fx, fy) \ tmp = (1.0 - (fy)) * \ ((1.0 - (fx)) * (v00) + (fx) * (v01)) \ + \ (fy) * \ ((1.0 - (fx)) * (v10) + (fx) * (v11)); \ v = CLAMP (tmp, 0, 255); #define GET_VALUES(r, g, b, a, x, y) \ if (x >= 0 && x < src_width && y >= 0 && y < src_height) { \ p_src2 = p_src + src_rowstride * y + 4 * x; \ r = p_src2[CAIRO_RED]; \ g = p_src2[CAIRO_GREEN]; \ b = p_src2[CAIRO_BLUE]; \ a = p_src2[CAIRO_ALPHA]; \ } \ else { \ r = r0; \ g = g0; \ b = b0; \ a = a0; \ } half_new_width = new_width / 2.0; half_new_height = new_height / 2.0; half_src_width = src_width / 2.0; half_src_height = src_height / 2.0; cairo_surface_flush (rotated); y = - half_new_height; for (yi = 0; yi < new_height; yi++) { if (task != NULL) { gboolean cancelled; double progress; gth_async_task_get_data (task, NULL, &cancelled, NULL); if (cancelled) goto out; progress = (double) yi / new_height; gth_async_task_set_data (task, NULL, NULL, &progress); } p_new2 = p_new; x = - half_new_width; for (xi = 0; xi < new_width; xi++) { x2 = cos_angle * x - sin_angle * y + half_src_width; y2 = sin_angle * x + cos_angle * y + half_src_height; if (high_quality) { /* Bilinear interpolation. */ x2min = (int) x2; y2min = (int) y2; x2max = x2min + 1; y2max = y2min + 1; GET_VALUES (r00, g00, b00, a00, x2min, y2min); GET_VALUES (r01, g01, b01, a01, x2max, y2min); GET_VALUES (r10, g10, b10, a10, x2min, y2max); GET_VALUES (r11, g11, b11, a11, x2max, y2max); fx = x2 - x2min; fy = y2 - y2min; INTERPOLATE (r, r00, r01, r10, r11, fx, fy); INTERPOLATE (g, g00, g01, g10, g11, fx, fy); INTERPOLATE (b, b00, b01, b10, b11, fx, fy); INTERPOLATE (a, a00, a01, a10, a11, fx, fy); pixel = CAIRO_RGBA_TO_UINT32 (r, g, b, a); memcpy (p_new2, &pixel, sizeof (guint32)); } else { /* Nearest neighbor */ x2min = GDOUBLE_ROUND_TO_INT (x2); y2min = GDOUBLE_ROUND_TO_INT (y2); GET_VALUES (p_new2[CAIRO_RED], p_new2[CAIRO_GREEN], p_new2[CAIRO_BLUE], p_new2[CAIRO_ALPHA], x2min, y2min); } p_new2 += 4; x += 1.0; } p_new += new_rowstride; y += 1.0; } out: cairo_surface_mark_dirty (rotated); cairo_surface_destroy (image_with_background); #undef INTERPOLATE #undef GET_VALUES return rotated; }