From 7215dbf9d7ba8c147c66f16f23c0d6481491c76b Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Sat, 22 Dec 2012 17:40:01 +0100 Subject: Add utilities to tag a cairo surface with color interpolation value. (bzr r11971) --- src/display/cairo-utils.cpp | 110 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) (limited to 'src/display/cairo-utils.cpp') diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index 2e2eb42dd..92303fed8 100644 --- a/src/display/cairo-utils.cpp +++ b/src/display/cairo-utils.cpp @@ -24,6 +24,7 @@ #include <2geom/transforms.h> #include <2geom/sbasis-to-bezier.h> #include "color.h" +#include "style.h" #include "helper/geom-curves.h" namespace Inkscape { @@ -276,6 +277,26 @@ feed_pathvector_to_cairo (cairo_t *ct, Geom::PathVector const &pathv) } } +SPColorInterpolation +get_cairo_surface_ci(cairo_surface_t *surface) { + void* data = cairo_surface_get_user_data( surface, &ci_key ); + if( data != NULL ) { + return (SPColorInterpolation)GPOINTER_TO_INT( data ); + } else { + return SP_CSS_COLOR_INTERPOLATION_AUTO; + } +} + +void +set_cairo_surface_ci(cairo_surface_t *surface, SPColorInterpolation ci) { + cairo_surface_set_user_data(surface, &ci_key, GINT_TO_POINTER (ci), NULL); +} + +void +copy_cairo_surface_ci(cairo_surface_t *in, cairo_surface_t *out) { + cairo_surface_set_user_data(out, &ci_key, cairo_surface_get_user_data(in, &ci_key), NULL); +} + void ink_cairo_set_source_rgba32(cairo_t *ct, guint32 rgba) { @@ -395,6 +416,7 @@ cairo_surface_t * ink_cairo_surface_create_identical(cairo_surface_t *s) { cairo_surface_t *ns = ink_cairo_surface_create_same_size(s, cairo_surface_get_content(s)); + cairo_surface_set_user_data(ns, &ci_key, cairo_surface_get_user_data(s, &ci_key), NULL); return ns; } @@ -547,6 +569,94 @@ void ink_cairo_surface_average_color_premul(cairo_surface_t *surface, double &r, a = CLAMP(a, 0.0, 1.0); } +void srgb_to_linear( guint32* c, guint32 a ) { + + *c = unpremul_alpha( *c, a ); + + double cc = *c/255.0; + + if( cc < 0.04045 ) { + cc /= 12.92; + } else { + cc = pow( (cc+0.055)/1.055, 2.4 ); + } + cc *= 255.0; + + *c = (int)cc; + + *c = premul_alpha( *c, a ); +} + +void linear_to_srgb( guint32* c, guint32 a ) { + + *c = unpremul_alpha( *c, a ); + + double cc = *c/255.0; + + if( cc < 0.0031308 ) { + cc *= 12.92; + } else { + cc = pow( cc, 1.0/2.4 )*1.055-0.055; + } + cc *= 255.0; + + *c = (int)cc; + + *c = premul_alpha( *c, a ); +} + +int ink_cairo_surface_srgb_to_linear(cairo_surface_t *surface) +{ + cairo_surface_flush(surface); + int width = cairo_image_surface_get_width(surface); + int height = cairo_image_surface_get_height(surface); + int stride = cairo_image_surface_get_stride(surface); + unsigned char *data = cairo_image_surface_get_data(surface); + + /* TODO convert this to OpenMP somehow */ + for (int y = 0; y < height; ++y, data += stride) { + for (int x = 0; x < width; ++x) { + guint32 px = *reinterpret_cast(data + 4*x); + EXTRACT_ARGB32(px, a,r,g,b) ; // Unneeded semi-colon for indenting + if( a != 0 ) { + srgb_to_linear( &r, a ); + srgb_to_linear( &g, a ); + srgb_to_linear( &b, a ); + } + ASSEMBLE_ARGB32(px2, a,r,g,b); + *reinterpret_cast(data + 4*x) = px2; + } + } + set_cairo_surface_ci( surface, SP_CSS_COLOR_INTERPOLATION_LINEARRGB ); + return width * height; +} + +int ink_cairo_surface_linear_to_srgb(cairo_surface_t *surface) +{ + cairo_surface_flush(surface); + int width = cairo_image_surface_get_width(surface); + int height = cairo_image_surface_get_height(surface); + int stride = cairo_image_surface_get_stride(surface); + unsigned char *data = cairo_image_surface_get_data(surface); + + /* TODO convert this to OpenMP somehow */ + for (int y = 0; y < height; ++y, data += stride) { + for (int x = 0; x < width; ++x) { + guint32 px = *reinterpret_cast(data + 4*x); + EXTRACT_ARGB32(px, a,r,g,b) ; // Unneeded semi-colon for indenting + if( a != 0 ) { + linear_to_srgb( &r, a ); + linear_to_srgb( &g, a ); + linear_to_srgb( &b, a ); + } + ASSEMBLE_ARGB32(px2, a,r,g,b); + *reinterpret_cast(data + 4*x) = px2; + } + } + set_cairo_surface_ci( surface, SP_CSS_COLOR_INTERPOLATION_SRGB ); + return width * height; +} + cairo_pattern_t * ink_cairo_pattern_create_checkerboard() { -- cgit v1.2.3 From d1e043d27a35b00c9e49a2deef1c2ee5bc8b2cc5 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Sun, 23 Dec 2012 10:55:50 +0100 Subject: Move some code from filter primitives to cairo-utils.cpp. Fix bug in lighting primitives. (bzr r11976) --- src/display/cairo-utils.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'src/display/cairo-utils.cpp') diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index 92303fed8..692e31837 100644 --- a/src/display/cairo-utils.cpp +++ b/src/display/cairo-utils.cpp @@ -287,9 +287,26 @@ get_cairo_surface_ci(cairo_surface_t *surface) { } } +/** Set the color_interpolation_value for a Cairo surface. + * Transform the surface between sRGB and linearRGB if necessary. */ void set_cairo_surface_ci(cairo_surface_t *surface, SPColorInterpolation ci) { - cairo_surface_set_user_data(surface, &ci_key, GINT_TO_POINTER (ci), NULL); + + if( cairo_surface_get_content( surface ) != CAIRO_CONTENT_ALPHA ) { + + SPColorInterpolation ci_in = get_cairo_surface_ci( surface ); + + if( ci_in == SP_CSS_COLOR_INTERPOLATION_SRGB && + ci == SP_CSS_COLOR_INTERPOLATION_LINEARRGB ) { + ink_cairo_surface_srgb_to_linear( surface ); + } + if( ci_in == SP_CSS_COLOR_INTERPOLATION_LINEARRGB && + ci == SP_CSS_COLOR_INTERPOLATION_SRGB ) { + ink_cairo_surface_linear_to_srgb( surface ); + } + + cairo_surface_set_user_data(surface, &ci_key, GINT_TO_POINTER (ci), NULL); + } } void @@ -627,7 +644,6 @@ int ink_cairo_surface_srgb_to_linear(cairo_surface_t *surface) *reinterpret_cast(data + 4*x) = px2; } } - set_cairo_surface_ci( surface, SP_CSS_COLOR_INTERPOLATION_LINEARRGB ); return width * height; } @@ -653,7 +669,6 @@ int ink_cairo_surface_linear_to_srgb(cairo_surface_t *surface) *reinterpret_cast(data + 4*x) = px2; } } - set_cairo_surface_ci( surface, SP_CSS_COLOR_INTERPOLATION_SRGB ); return width * height; } -- cgit v1.2.3 From a9016bc7deeacce3ff8d44d2d09ec512dc662445 Mon Sep 17 00:00:00 2001 From: "Jon A. Cruz" Date: Sat, 19 Jan 2013 22:54:44 -0800 Subject: Fix multiple instances of ci_key. (bzr r12045) --- src/display/cairo-utils.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src/display/cairo-utils.cpp') diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index 692e31837..9655fc194 100644 --- a/src/display/cairo-utils.cpp +++ b/src/display/cairo-utils.cpp @@ -27,6 +27,17 @@ #include "style.h" #include "helper/geom-curves.h" +namespace { + +/** + * Key for cairo_surface_t to keep track of current color interpolation value + * Only the address of the structure is used, it is never initialized. See: + * http://www.cairographics.org/manual/cairo-Types.html#cairo-user-data-key-t + */ +cairo_user_data_key_t ci_key; + +} // namespace + namespace Inkscape { CairoGroup::CairoGroup(cairo_t *_ct) : ct(_ct), pushed(false) {} -- cgit v1.2.3 From 7900b2459ca0a6f6e11a367443046582c4852752 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Tue, 29 Jan 2013 15:23:22 +0100 Subject: Filter color values always defined as sRGB (or ICC). (bzr r12070) --- src/display/cairo-utils.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/display/cairo-utils.cpp') diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index 9655fc194..831b37899 100644 --- a/src/display/cairo-utils.cpp +++ b/src/display/cairo-utils.cpp @@ -597,6 +597,15 @@ void ink_cairo_surface_average_color_premul(cairo_surface_t *surface, double &r, a = CLAMP(a, 0.0, 1.0); } +void srgb_to_linear( double* c ) { + + if( *c < 0.04045 ) { + *c /= 12.92; + } else { + *c = pow( (*c+0.055)/1.055, 2.4 ); + } +} + void srgb_to_linear( guint32* c, guint32 a ) { *c = unpremul_alpha( *c, a ); -- cgit v1.2.3 From a7c6061c0b32affc13bfb818161ca7fc002ef210 Mon Sep 17 00:00:00 2001 From: "Johan B. C. Engelen" Date: Wed, 30 Jan 2013 21:47:35 +0100 Subject: code cleanup. don't use pointers when you don't have to. use const whereever you can. (bzr r12077) --- src/display/cairo-utils.cpp | 41 ++++++++++++++++------------------------- 1 file changed, 16 insertions(+), 25 deletions(-) (limited to 'src/display/cairo-utils.cpp') diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index 831b37899..8eeee0277 100644 --- a/src/display/cairo-utils.cpp +++ b/src/display/cairo-utils.cpp @@ -597,20 +597,11 @@ void ink_cairo_surface_average_color_premul(cairo_surface_t *surface, double &r, a = CLAMP(a, 0.0, 1.0); } -void srgb_to_linear( double* c ) { +static guint32 srgb_to_linear( const guint32 c, const guint32 a ) { - if( *c < 0.04045 ) { - *c /= 12.92; - } else { - *c = pow( (*c+0.055)/1.055, 2.4 ); - } -} - -void srgb_to_linear( guint32* c, guint32 a ) { - - *c = unpremul_alpha( *c, a ); + const guint32 c1 = unpremul_alpha( c, a ); - double cc = *c/255.0; + double cc = c1/255.0; if( cc < 0.04045 ) { cc /= 12.92; @@ -619,16 +610,16 @@ void srgb_to_linear( guint32* c, guint32 a ) { } cc *= 255.0; - *c = (int)cc; + const guint32 c2 = (int)cc; - *c = premul_alpha( *c, a ); + return premul_alpha( c2, a ); } -void linear_to_srgb( guint32* c, guint32 a ) { +static guint32 linear_to_srgb( const guint32 c, const guint32 a ) { - *c = unpremul_alpha( *c, a ); + const guint32 c1 = unpremul_alpha( c, a ); - double cc = *c/255.0; + double cc = c1/255.0; if( cc < 0.0031308 ) { cc *= 12.92; @@ -637,9 +628,9 @@ void linear_to_srgb( guint32* c, guint32 a ) { } cc *= 255.0; - *c = (int)cc; + const guint32 c2 = (int)cc; - *c = premul_alpha( *c, a ); + return premul_alpha( c2, a ); } int ink_cairo_surface_srgb_to_linear(cairo_surface_t *surface) @@ -656,9 +647,9 @@ int ink_cairo_surface_srgb_to_linear(cairo_surface_t *surface) guint32 px = *reinterpret_cast(data + 4*x); EXTRACT_ARGB32(px, a,r,g,b) ; // Unneeded semi-colon for indenting if( a != 0 ) { - srgb_to_linear( &r, a ); - srgb_to_linear( &g, a ); - srgb_to_linear( &b, a ); + r = srgb_to_linear( r, a ); + g = srgb_to_linear( g, a ); + b = srgb_to_linear( b, a ); } ASSEMBLE_ARGB32(px2, a,r,g,b); *reinterpret_cast(data + 4*x) = px2; @@ -681,9 +672,9 @@ int ink_cairo_surface_linear_to_srgb(cairo_surface_t *surface) guint32 px = *reinterpret_cast(data + 4*x); EXTRACT_ARGB32(px, a,r,g,b) ; // Unneeded semi-colon for indenting if( a != 0 ) { - linear_to_srgb( &r, a ); - linear_to_srgb( &g, a ); - linear_to_srgb( &b, a ); + r = linear_to_srgb( r, a ); + g = linear_to_srgb( g, a ); + b = linear_to_srgb( b, a ); } ASSEMBLE_ARGB32(px2, a,r,g,b); *reinterpret_cast(data + 4*x) = px2; -- cgit v1.2.3 From 2b80c70fe2db359933a311ae2f1e331c7e54af5f Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Sun, 3 Mar 2013 15:32:49 +0100 Subject: Use ink_cairo_surface_filter when converting surfaces between linearRGB and sRGB. Huge speed advantage when using multiple threads. (bzr r12170) --- src/display/cairo-utils.cpp | 91 +++++++++++++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 27 deletions(-) (limited to 'src/display/cairo-utils.cpp') diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index 8eeee0277..a55b05fe3 100644 --- a/src/display/cairo-utils.cpp +++ b/src/display/cairo-utils.cpp @@ -26,6 +26,7 @@ #include "color.h" #include "style.h" #include "helper/geom-curves.h" +#include "display/cairo-templates.h" namespace { @@ -633,6 +634,22 @@ static guint32 linear_to_srgb( const guint32 c, const guint32 a ) { return premul_alpha( c2, a ); } +struct SurfaceSrgbToLinear { + + guint32 operator()(guint32 in) { + EXTRACT_ARGB32(in, a,r,g,b) ; // Unneeded semi-colon for indenting + if( a != 0 ) { + r = srgb_to_linear( r, a ); + g = srgb_to_linear( g, a ); + b = srgb_to_linear( b, a ); + } + ASSEMBLE_ARGB32(out, a,r,g,b); + return out; + } +private: + /* None */ +}; + int ink_cairo_surface_srgb_to_linear(cairo_surface_t *surface) { cairo_surface_flush(surface); @@ -641,23 +658,41 @@ int ink_cairo_surface_srgb_to_linear(cairo_surface_t *surface) int stride = cairo_image_surface_get_stride(surface); unsigned char *data = cairo_image_surface_get_data(surface); + ink_cairo_surface_filter( surface, surface, SurfaceSrgbToLinear() ); + /* TODO convert this to OpenMP somehow */ - for (int y = 0; y < height; ++y, data += stride) { - for (int x = 0; x < width; ++x) { - guint32 px = *reinterpret_cast(data + 4*x); - EXTRACT_ARGB32(px, a,r,g,b) ; // Unneeded semi-colon for indenting - if( a != 0 ) { - r = srgb_to_linear( r, a ); - g = srgb_to_linear( g, a ); - b = srgb_to_linear( b, a ); - } - ASSEMBLE_ARGB32(px2, a,r,g,b); - *reinterpret_cast(data + 4*x) = px2; - } - } + // for (int y = 0; y < height; ++y, data += stride) { + // for (int x = 0; x < width; ++x) { + // guint32 px = *reinterpret_cast(data + 4*x); + // EXTRACT_ARGB32(px, a,r,g,b) ; // Unneeded semi-colon for indenting + // if( a != 0 ) { + // r = srgb_to_linear( r, a ); + // g = srgb_to_linear( g, a ); + // b = srgb_to_linear( b, a ); + // } + // ASSEMBLE_ARGB32(px2, a,r,g,b); + // *reinterpret_cast(data + 4*x) = px2; + // } + // } return width * height; } +struct SurfaceLinearToSrgb { + + guint32 operator()(guint32 in) { + EXTRACT_ARGB32(in, a,r,g,b) ; // Unneeded semi-colon for indenting + if( a != 0 ) { + r = linear_to_srgb( r, a ); + g = linear_to_srgb( g, a ); + b = linear_to_srgb( b, a ); + } + ASSEMBLE_ARGB32(out, a,r,g,b); + return out; + } +private: + /* None */ +}; + int ink_cairo_surface_linear_to_srgb(cairo_surface_t *surface) { cairo_surface_flush(surface); @@ -666,20 +701,22 @@ int ink_cairo_surface_linear_to_srgb(cairo_surface_t *surface) int stride = cairo_image_surface_get_stride(surface); unsigned char *data = cairo_image_surface_get_data(surface); - /* TODO convert this to OpenMP somehow */ - for (int y = 0; y < height; ++y, data += stride) { - for (int x = 0; x < width; ++x) { - guint32 px = *reinterpret_cast(data + 4*x); - EXTRACT_ARGB32(px, a,r,g,b) ; // Unneeded semi-colon for indenting - if( a != 0 ) { - r = linear_to_srgb( r, a ); - g = linear_to_srgb( g, a ); - b = linear_to_srgb( b, a ); - } - ASSEMBLE_ARGB32(px2, a,r,g,b); - *reinterpret_cast(data + 4*x) = px2; - } - } + ink_cairo_surface_filter( surface, surface, SurfaceLinearToSrgb() ); + + // /* TODO convert this to OpenMP somehow */ + // for (int y = 0; y < height; ++y, data += stride) { + // for (int x = 0; x < width; ++x) { + // guint32 px = *reinterpret_cast(data + 4*x); + // EXTRACT_ARGB32(px, a,r,g,b) ; // Unneeded semi-colon for indenting + // if( a != 0 ) { + // r = linear_to_srgb( r, a ); + // g = linear_to_srgb( g, a ); + // b = linear_to_srgb( b, a ); + // } + // ASSEMBLE_ARGB32(px2, a,r,g,b); + // *reinterpret_cast(data + 4*x) = px2; + // } + // } return width * height; } -- cgit v1.2.3 From efa9bdd372b89848165dc575fafff45851514e69 Mon Sep 17 00:00:00 2001 From: Alex Valavanis Date: Sat, 16 Mar 2013 12:20:26 +0000 Subject: Warning cleanup (bzr r12211) --- src/display/cairo-utils.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/display/cairo-utils.cpp') diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index a55b05fe3..fc56c7b26 100644 --- a/src/display/cairo-utils.cpp +++ b/src/display/cairo-utils.cpp @@ -655,8 +655,8 @@ int ink_cairo_surface_srgb_to_linear(cairo_surface_t *surface) cairo_surface_flush(surface); int width = cairo_image_surface_get_width(surface); int height = cairo_image_surface_get_height(surface); - int stride = cairo_image_surface_get_stride(surface); - unsigned char *data = cairo_image_surface_get_data(surface); + // int stride = cairo_image_surface_get_stride(surface); + // unsigned char *data = cairo_image_surface_get_data(surface); ink_cairo_surface_filter( surface, surface, SurfaceSrgbToLinear() ); @@ -698,8 +698,8 @@ int ink_cairo_surface_linear_to_srgb(cairo_surface_t *surface) cairo_surface_flush(surface); int width = cairo_image_surface_get_width(surface); int height = cairo_image_surface_get_height(surface); - int stride = cairo_image_surface_get_stride(surface); - unsigned char *data = cairo_image_surface_get_data(surface); + // int stride = cairo_image_surface_get_stride(surface); + // unsigned char *data = cairo_image_surface_get_data(surface); ink_cairo_surface_filter( surface, surface, SurfaceLinearToSrgb() ); @@ -888,4 +888,4 @@ guint32 argb32_from_rgba(guint32 in) fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : -- cgit v1.2.3 From 0697088a2e3fbb3f7777db721e8c5e4661311dbe Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Fri, 13 Sep 2013 23:14:45 +0200 Subject: Improve the functions which create GdkPixbuf from Cairo surface and vice versa. Simplifies some code. Also introduce proper refcounting into svg_preview_cache.cpp and fix its users. (bzr r12512) --- src/display/cairo-utils.cpp | 139 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 118 insertions(+), 21 deletions(-) (limited to 'src/display/cairo-utils.cpp') diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index fc56c7b26..2726c9d5a 100644 --- a/src/display/cairo-utils.cpp +++ b/src/display/cairo-utils.cpp @@ -28,16 +28,13 @@ #include "helper/geom-curves.h" #include "display/cairo-templates.h" -namespace { - /** * Key for cairo_surface_t to keep track of current color interpolation value * Only the address of the structure is used, it is never initialized. See: * http://www.cairographics.org/manual/cairo-Types.html#cairo-user-data-key-t */ -cairo_user_data_key_t ci_key; - -} // namespace +cairo_user_data_key_t ink_color_interpolation_key; +cairo_user_data_key_t ink_pixbuf_key; namespace Inkscape { @@ -291,7 +288,7 @@ feed_pathvector_to_cairo (cairo_t *ct, Geom::PathVector const &pathv) SPColorInterpolation get_cairo_surface_ci(cairo_surface_t *surface) { - void* data = cairo_surface_get_user_data( surface, &ci_key ); + void* data = cairo_surface_get_user_data( surface, &ink_color_interpolation_key ); if( data != NULL ) { return (SPColorInterpolation)GPOINTER_TO_INT( data ); } else { @@ -317,13 +314,13 @@ set_cairo_surface_ci(cairo_surface_t *surface, SPColorInterpolation ci) { ink_cairo_surface_linear_to_srgb( surface ); } - cairo_surface_set_user_data(surface, &ci_key, GINT_TO_POINTER (ci), NULL); + cairo_surface_set_user_data(surface, &ink_color_interpolation_key, GINT_TO_POINTER (ci), NULL); } } void copy_cairo_surface_ci(cairo_surface_t *in, cairo_surface_t *out) { - cairo_surface_set_user_data(out, &ci_key, cairo_surface_get_user_data(in, &ci_key), NULL); + cairo_surface_set_user_data(out, &ink_color_interpolation_key, cairo_surface_get_user_data(in, &ink_color_interpolation_key), NULL); } void @@ -375,26 +372,112 @@ ink_cairo_pattern_set_matrix(cairo_pattern_t *cp, Geom::Affine const &m) } void -ink_cairo_set_source_argb32_pixbuf(cairo_t *ct, GdkPixbuf *pb, double x, double y) +ink_cairo_set_source_pixbuf(cairo_t *ct, GdkPixbuf *pb, double x, double y) { - cairo_surface_t *pbs = ink_cairo_surface_create_for_argb32_pixbuf(pb); + cairo_surface_t *pbs = ink_cairo_surface_get_for_pixbuf(pb); cairo_set_source_surface(ct, pbs, x, y); - cairo_surface_destroy(pbs); } +/* The functions below implement the following hack: + * + * The pixels formats of Cairo and GdkPixbuf are different. + * GdkPixbuf accesses pixels as bytes, alpha is not premultiplied, + * and successive bytes of a single pixel contain R, G, B and A components. + * Cairo accesses pixels as 32-bit ints, alpha is premultiplied, + * and each int contains as 0xAARRGGBB, accessed with bitwise operations. + * + * In other words, on a little endian system, a GdkPixbuf will contain: + * char *data = "rgbargbargba...." + * int *data = { 0xAABBGGRR, 0xAABBGGRR, 0xAABBGGRR, ... } + * while a Cairo image surface will contain: + * char *data = "bgrabgrabgra...." + * int *data = { 0xAARRGGBB, 0xAARRGGBB, 0xAARRGGBB, ... } + * + * It is possible to convert between these two formats (almost) losslessly. + * Some color information from partially transparent regions of the image + * is lost, but the result when displaying this image will remain the same. + * + * The functions below allow interoperation between GdkPixbuf + * and Cairo surfaces, allowing pixbufs to be used as Cairo sources, + * and saving Cairo surfaces using GdkPixbuf APIs. + * This is implemented by creating a GdkPixbuf and a Cairo image surface + * which share their data. Depending on what is needed at a given time, + * the pixels are converted in place to the Cairo or the GdkPixbuf format. + * In this way, only one copy of the image data is needed. + * + * Given either a GdkPixbuf or a Cairo surface, these functions create + * the other object and convert to its format. The returned object should be + * freed using cairo_surface_destroy or g_object_unref when it's no longer + * needed. + * + * Memory ownership semantics: + * Regardless of whether the pixels are stored in memory originally belonging + * to Cairo surface or to GdkPixbuf, the GdkPixbuf is the master object. + * To free the memory, unref the GdkPixbuf ONLY. + */ + +/** + * Converts the pixbuf to Cairo pixel format and returns an image surface + * which can be used as a source. + * + * The returned surface should be unreferenced + * with cairo_surface_destroy() once it's no longer needed. + * Calling this function causes the pixbuf to be unsuitable for use + * with GTK drawing functions. + */ cairo_surface_t * -ink_cairo_surface_create_for_argb32_pixbuf(GdkPixbuf *pb) +ink_cairo_surface_get_for_pixbuf(GdkPixbuf *pb) { - guchar *data = gdk_pixbuf_get_pixels(pb); - int w = gdk_pixbuf_get_width(pb); - int h = gdk_pixbuf_get_height(pb); - int stride = gdk_pixbuf_get_rowstride(pb); + cairo_surface_t *pbs = + reinterpret_cast(g_object_get_data(G_OBJECT(pb), "cairo_surface")); + + ink_pixbuf_ensure_argb32(pb); + + if (pbs == NULL) { + guchar *data = gdk_pixbuf_get_pixels(pb); + int w = gdk_pixbuf_get_width(pb); + int h = gdk_pixbuf_get_height(pb); + int stride = gdk_pixbuf_get_rowstride(pb); + + // create a surface that stores the data + cairo_surface_t *pbs = cairo_image_surface_create_for_data( + data, CAIRO_FORMAT_ARGB32, w, h, stride); + + g_object_set_data_full(G_OBJECT(pb), "cairo_surface", pbs, (GDestroyNotify) cairo_surface_destroy); + cairo_surface_set_user_data(pbs, &ink_pixbuf_key, pb, NULL); + } - cairo_surface_t *pbs = cairo_image_surface_create_for_data( - data, CAIRO_FORMAT_ARGB32, w, h, stride); return pbs; } +/** + * Converts the Cairo surface to GdkPixbuf pixel format. + * GdkPixbuf takes ownership of the passed surface reference, + * so it should NOT be freed after calling this function. + */ +GdkPixbuf *ink_pixbuf_create_from_cairo_surface(cairo_surface_t *s) +{ + GdkPixbuf *pb = reinterpret_cast(cairo_surface_get_user_data(s, &ink_pixbuf_key)); + if (pb == NULL) { + pb = gdk_pixbuf_new_from_data( + cairo_image_surface_get_data(s), GDK_COLORSPACE_RGB, TRUE, 8, + cairo_image_surface_get_width(s), cairo_image_surface_get_height(s), + cairo_image_surface_get_stride(s), NULL, NULL); + + g_object_set_data_full(G_OBJECT(pb), "pixel_format", g_strdup("argb32"), g_free); + g_object_set_data_full(G_OBJECT(pb), "cairo_surface", s, (GDestroyNotify) cairo_surface_destroy); + + cairo_surface_set_user_data(s, &ink_pixbuf_key, pb, NULL); + } else { + g_warning("Received surface which is already owned by GdkPixbuf"); + g_object_ref(pb); + } + + ink_pixbuf_ensure_normal(pb); + + return pb; +} + /** * Cleanup function for GdkPixbuf. * This function should be passed as the GdkPixbufDestroyNotify parameter @@ -445,7 +528,7 @@ cairo_surface_t * ink_cairo_surface_create_identical(cairo_surface_t *s) { cairo_surface_t *ns = ink_cairo_surface_create_same_size(s, cairo_surface_get_content(s)); - cairo_surface_set_user_data(ns, &ci_key, cairo_surface_get_user_data(s, &ci_key), NULL); + cairo_surface_set_user_data(ns, &ink_color_interpolation_key, cairo_surface_get_user_data(s, &ink_color_interpolation_key), NULL); return ns; } @@ -845,13 +928,20 @@ convert_pixels_argb32_to_pixbuf(guchar *data, int w, int h, int stride) * using it with GTK will result in corrupted drawings. */ void -convert_pixbuf_normal_to_argb32(GdkPixbuf *pb) +ink_pixbuf_ensure_argb32(GdkPixbuf *pb) { + gchar *pixel_format = reinterpret_cast(g_object_get_data(G_OBJECT(pb), "pixel_format")); + if (pixel_format != NULL && strcmp(pixel_format, "argb32") == 0) { + // nothing to do + return; + } + convert_pixels_pixbuf_to_argb32( gdk_pixbuf_get_pixels(pb), gdk_pixbuf_get_width(pb), gdk_pixbuf_get_height(pb), gdk_pixbuf_get_rowstride(pb)); + g_object_set_data_full(G_OBJECT(pb), "pixel_format", g_strdup("argb32"), g_free); } /** @@ -859,13 +949,20 @@ convert_pixbuf_normal_to_argb32(GdkPixbuf *pb) * Once this is done, the pixbuf can be used with GTK again. */ void -convert_pixbuf_argb32_to_normal(GdkPixbuf *pb) +ink_pixbuf_ensure_normal(GdkPixbuf *pb) { + gchar *pixel_format = reinterpret_cast(g_object_get_data(G_OBJECT(pb), "pixel_format")); + if (pixel_format == NULL || strcmp(pixel_format, "pixbuf") == 0) { + // nothing to do + return; + } + convert_pixels_argb32_to_pixbuf( gdk_pixbuf_get_pixels(pb), gdk_pixbuf_get_width(pb), gdk_pixbuf_get_height(pb), gdk_pixbuf_get_rowstride(pb)); + g_object_set_data_full(G_OBJECT(pb), "pixel_format", g_strdup("pixbuf"), g_free); } guint32 argb32_from_rgba(guint32 in) -- cgit v1.2.3 From 28840aad6554556231882cfd8c4fd85ede197a24 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Sat, 14 Sep 2013 02:53:34 +0200 Subject: Fix serious bug in recent GdkPixbuf / Cairo interop rework (bzr r12515) --- src/display/cairo-utils.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src/display/cairo-utils.cpp') diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index 2726c9d5a..2d94d024f 100644 --- a/src/display/cairo-utils.cpp +++ b/src/display/cairo-utils.cpp @@ -420,10 +420,9 @@ ink_cairo_set_source_pixbuf(cairo_t *ct, GdkPixbuf *pb, double x, double y) * Converts the pixbuf to Cairo pixel format and returns an image surface * which can be used as a source. * - * The returned surface should be unreferenced - * with cairo_surface_destroy() once it's no longer needed. + * The returned surface is owned by the GdkPixbuf and should not be freed. * Calling this function causes the pixbuf to be unsuitable for use - * with GTK drawing functions. + * with GTK drawing functions until ink_pixbuf_ensure_normal() is called. */ cairo_surface_t * ink_cairo_surface_get_for_pixbuf(GdkPixbuf *pb) @@ -440,7 +439,7 @@ ink_cairo_surface_get_for_pixbuf(GdkPixbuf *pb) int stride = gdk_pixbuf_get_rowstride(pb); // create a surface that stores the data - cairo_surface_t *pbs = cairo_image_surface_create_for_data( + pbs = cairo_image_surface_create_for_data( data, CAIRO_FORMAT_ARGB32, w, h, stride); g_object_set_data_full(G_OBJECT(pb), "cairo_surface", pbs, (GDestroyNotify) cairo_surface_destroy); -- cgit v1.2.3 From 35dc2e5a640375d51119f2051a240454b5f5b8c8 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Sat, 14 Sep 2013 03:59:43 +0200 Subject: Do not recompress images when embedding and generating PDFs. Fixes blocker bug #871563. Fixed bugs: - https://launchpad.net/bugs/871563 (bzr r12516) --- src/display/cairo-utils.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/display/cairo-utils.cpp') diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index 2d94d024f..755553033 100644 --- a/src/display/cairo-utils.cpp +++ b/src/display/cairo-utils.cpp @@ -423,6 +423,11 @@ ink_cairo_set_source_pixbuf(cairo_t *ct, GdkPixbuf *pb, double x, double y) * The returned surface is owned by the GdkPixbuf and should not be freed. * Calling this function causes the pixbuf to be unsuitable for use * with GTK drawing functions until ink_pixbuf_ensure_normal() is called. + * + * @bug You have to call g_object_set_data(G_OBJECT(pb), "cairo_surface", NULL) + * when unrefing the last reference to the pixbuf. Otherwise there will be + * crashes, because cairo_surface_destroy is called after the pixbuf data + * is already freed. */ cairo_surface_t * ink_cairo_surface_get_for_pixbuf(GdkPixbuf *pb) -- cgit v1.2.3 From 083367b313247a4cf0c082fff25e993892dc38d1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Thu, 19 Sep 2013 02:57:10 +0200 Subject: Encapsulate the shared memory hack for Cairo and GdkPixbuf in a class called Inkscape::Pixbuf. Replace usage in the code as appropriate. (bzr r12531) --- src/display/cairo-utils.cpp | 539 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 416 insertions(+), 123 deletions(-) (limited to 'src/display/cairo-utils.cpp') diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index 755553033..2c7b543c1 100644 --- a/src/display/cairo-utils.cpp +++ b/src/display/cairo-utils.cpp @@ -15,6 +15,8 @@ #include "display/cairo-utils.h" #include +#include +#include #include <2geom/pathvector.h> #include <2geom/bezier-curve.h> #include <2geom/hvlinesegment.h> @@ -28,6 +30,8 @@ #include "helper/geom-curves.h" #include "display/cairo-templates.h" +static void ink_cairo_pixbuf_cleanup(guchar *, void *); + /** * Key for cairo_surface_t to keep track of current color interpolation value * Only the address of the structure is used, it is never initialized. See: @@ -116,6 +120,380 @@ Cairo::RefPtr CairoContext::create(Cairo::RefPtr c return ret; } + +/* The class below implement the following hack: + * + * The pixels formats of Cairo and GdkPixbuf are different. + * GdkPixbuf accesses pixels as bytes, alpha is not premultiplied, + * and successive bytes of a single pixel contain R, G, B and A components. + * Cairo accesses pixels as 32-bit ints, alpha is premultiplied, + * and each int contains as 0xAARRGGBB, accessed with bitwise operations. + * + * In other words, on a little endian system, a GdkPixbuf will contain: + * char *data = "rgbargbargba...." + * int *data = { 0xAABBGGRR, 0xAABBGGRR, 0xAABBGGRR, ... } + * while a Cairo image surface will contain: + * char *data = "bgrabgrabgra...." + * int *data = { 0xAARRGGBB, 0xAARRGGBB, 0xAARRGGBB, ... } + * + * It is possible to convert between these two formats (almost) losslessly. + * Some color information from partially transparent regions of the image + * is lost, but the result when displaying this image will remain the same. + * + * The class allows interoperation between GdkPixbuf + * and Cairo surfaces without creating a copy of the image. + * This is implemented by creating a GdkPixbuf and a Cairo image surface + * which share their data. Depending on what is needed at a given time, + * the pixels are converted in place to the Cairo or the GdkPixbuf format. + */ + +/** Create a pixbuf from a Cairo surface. + * The constructor takes ownership of the passed surface, + * so it should not be destroyed. */ +Pixbuf::Pixbuf(cairo_surface_t *s) + : _pixbuf(gdk_pixbuf_new_from_data( + cairo_image_surface_get_data(s), GDK_COLORSPACE_RGB, TRUE, 8, + cairo_image_surface_get_width(s), cairo_image_surface_get_height(s), + cairo_image_surface_get_stride(s), NULL, NULL)) + , _surface(s) + , _mod_time(0) + , _pixel_format(PF_CAIRO) + , _cairo_store(true) +{} + +/** Create a pixbuf from a GdkPixbuf. + * The constructor takes ownership of the passed GdkPixbuf reference, + * so it should not be unrefed. */ +Pixbuf::Pixbuf(GdkPixbuf *pb) + : _pixbuf(pb) + , _surface(0) + , _mod_time(0) + , _pixel_format(PF_GDK) + , _cairo_store(false) +{ + _forceAlpha(); + _surface = cairo_image_surface_create_for_data( + gdk_pixbuf_get_pixels(_pixbuf), CAIRO_FORMAT_ARGB32, + gdk_pixbuf_get_width(_pixbuf), gdk_pixbuf_get_height(_pixbuf), gdk_pixbuf_get_rowstride(_pixbuf)); +} + +Pixbuf::Pixbuf(Inkscape::Pixbuf const &other) + : _pixbuf(gdk_pixbuf_copy(other._pixbuf)) + , _surface(cairo_image_surface_create_for_data( + gdk_pixbuf_get_pixels(_pixbuf), CAIRO_FORMAT_ARGB32, + gdk_pixbuf_get_width(_pixbuf), gdk_pixbuf_get_height(_pixbuf), gdk_pixbuf_get_rowstride(_pixbuf))) + , _mod_time(other._mod_time) + , _path(other._path) + , _pixel_format(other._pixel_format) + , _cairo_store(false) +{} + +Pixbuf::~Pixbuf() +{ + if (_cairo_store) { + g_object_unref(_pixbuf); + cairo_surface_destroy(_surface); + } else { + cairo_surface_destroy(_surface); + g_object_unref(_pixbuf); + } +} + +Pixbuf *Pixbuf::create_from_data_uri(gchar const *uri_data) +{ + Pixbuf *pixbuf = NULL; + + bool data_is_image = false; + bool data_is_base64 = false; + + gchar const *data = uri_data; + + while (*data) { + if (strncmp(data,"base64",6) == 0) { + /* base64-encoding */ + data_is_base64 = true; + data_is_image = true; // Illustrator produces embedded images without MIME type, so we assume it's image no matter what + data += 6; + } + else if (strncmp(data,"image/png",9) == 0) { + /* PNG image */ + data_is_image = true; + data += 9; + } + else if (strncmp(data,"image/jpg",9) == 0) { + /* JPEG image */ + data_is_image = true; + data += 9; + } + else if (strncmp(data,"image/jpeg",10) == 0) { + /* JPEG image */ + data_is_image = true; + data += 10; + } + else if (strncmp(data,"image/jp2",9) == 0) { + /* JPEG2000 image */ + data_is_image = true; + data += 9; + } + else { /* unrecognized option; skip it */ + while (*data) { + if (((*data) == ';') || ((*data) == ',')) { + break; + } + data++; + } + } + if ((*data) == ';') { + data++; + continue; + } + if ((*data) == ',') { + data++; + break; + } + } + + if ((*data) && data_is_image && data_is_base64) { + GdkPixbuf *buf = NULL; + GdkPixbufLoader *loader = gdk_pixbuf_loader_new(); + + if (!loader) return NULL; + + gsize decoded_len = 0; + guchar *decoded = g_base64_decode(data, &decoded_len); + + if (gdk_pixbuf_loader_write(loader, decoded, decoded_len, NULL)) { + gdk_pixbuf_loader_close(loader, NULL); + buf = gdk_pixbuf_loader_get_pixbuf(loader); + if (buf) { + g_object_ref(buf); + pixbuf = new Pixbuf(buf); + + GdkPixbufFormat *fmt = gdk_pixbuf_loader_get_format(loader); + gchar *fmt_name = gdk_pixbuf_format_get_name(fmt); + pixbuf->_setMimeData(decoded, decoded_len, fmt_name); + g_free(fmt_name); + } else { + g_free(decoded); + } + } else { + g_free(decoded); + } + g_object_unref(loader); + } + + return pixbuf; +} + +Pixbuf *Pixbuf::create_from_file(std::string const &fn) +{ + Pixbuf *pb = NULL; + // test correctness of filename + if (!g_file_test(fn.c_str(), G_FILE_TEST_EXISTS)) { + return NULL; + } + struct stat stdir; + int val = g_stat(fn.c_str(), &stdir); + if (val == 0 && stdir.st_mode & S_IFDIR){ + return NULL; + } + + // we need to load the entire file into memory, + // since we'll store it as MIME data + gchar *data = NULL; + gsize len = 0; + GError *error; + + if (g_file_get_contents(fn.c_str(), &data, &len, &error)) { + + GdkPixbufLoader *loader = gdk_pixbuf_loader_new(); + gdk_pixbuf_loader_write(loader, (guchar *) data, len, NULL); + gdk_pixbuf_loader_close(loader, NULL); + + GdkPixbuf *buf = gdk_pixbuf_loader_get_pixbuf(loader); + if (buf) { + g_object_ref(buf); + pb = new Pixbuf(buf); + pb->_mod_time = stdir.st_mtime; + pb->_path = fn; + + GdkPixbufFormat *fmt = gdk_pixbuf_loader_get_format(loader); + gchar *fmt_name = gdk_pixbuf_format_get_name(fmt); + pb->_setMimeData((guchar *) data, len, fmt_name); + g_free(fmt_name); + } else { + g_free(data); + } + g_object_unref(loader); + + // TODO: we could also read DPI, ICC profile, gamma correction, and other information + // from the file. This can be done by using format-specific libraries e.g. libpng. + } else { + return NULL; + } + + return pb; +} + +/** + * Converts the pixbuf to GdkPixbuf pixel format. + * The returned pixbuf can be used e.g. in calls to gdk_pixbuf_save(). + */ +GdkPixbuf *Pixbuf::getPixbufRaw(bool convert_format) +{ + if (convert_format) { + ensurePixelFormat(PF_GDK); + } + return _pixbuf; +} + +/** + * Converts the pixbuf to Cairo pixel format and returns an image surface + * which can be used as a source. + * + * The returned surface is owned by the GdkPixbuf and should not be freed. + * Calling this function causes the pixbuf to be unsuitable for use + * with GTK drawing functions until ensurePixelFormat(Pixbuf::PIXEL_FORMAT_PIXBUF) is called. + */ +cairo_surface_t *Pixbuf::getSurfaceRaw(bool convert_format) +{ + if (convert_format) { + ensurePixelFormat(PF_CAIRO); + } + return _surface; +} + +/* Declaring this function in the header requires including , + * which stupidly includes which in turn pulls in . + * However, since glib 2.32, has to be included before + * when compiling with G_DISABLE_DEPRECATED, as we do in non-release builds. + * This necessitates spamming a lot of files with #include + * at the top. + * + * Since we don't really use gdkmm, do not define this function for now. */ + +/* +Glib::RefPtr Pixbuf::getPixbuf(bool convert_format = true) +{ + g_object_ref(_pixbuf); + Glib::RefPtr p(getPixbuf(convert_format)); + return p; +} +*/ + +Cairo::RefPtr Pixbuf::getSurface(bool convert_format) +{ + Cairo::RefPtr p(new Cairo::Surface(getSurfaceRaw(convert_format), false)); + return p; +} + +/** Retrieves the original compressed data for the surface, if any. + * The returned data belongs to the object and should not be freed. */ +guchar const *Pixbuf::getMimeData(gsize &len, std::string &mimetype) const +{ + static gchar const *mimetypes[] = { + CAIRO_MIME_TYPE_JPEG, CAIRO_MIME_TYPE_JP2, CAIRO_MIME_TYPE_PNG, NULL }; + static guint mimetypes_len = g_strv_length(const_cast(mimetypes)); + + guchar const *data = NULL; + + for (guint i = 0; i < mimetypes_len; ++i) { + unsigned long len_long = 0; + cairo_surface_get_mime_data(const_cast(_surface), mimetypes[i], &data, &len); + len = len_long; // this assumes that the added range of long is not needed. the code below assumes gsize range of values is sufficient. + if (data != NULL) { + mimetype = mimetypes[i]; + break; + } + } + + return data; +} + +int Pixbuf::width() const { + return gdk_pixbuf_get_width(const_cast(_pixbuf)); +} +int Pixbuf::height() const { + return gdk_pixbuf_get_height(const_cast(_pixbuf)); +} +int Pixbuf::rowstride() const { + return gdk_pixbuf_get_rowstride(const_cast(_pixbuf)); +} +guchar const *Pixbuf::pixels() const { + return gdk_pixbuf_get_pixels(const_cast(_pixbuf)); +} +guchar *Pixbuf::pixels() { + return gdk_pixbuf_get_pixels(_pixbuf); +} +void Pixbuf::markDirty() { + cairo_surface_mark_dirty(_surface); +} + +void Pixbuf::_forceAlpha() +{ + if (gdk_pixbuf_get_has_alpha(_pixbuf)) return; + + GdkPixbuf *old = _pixbuf; + _pixbuf = gdk_pixbuf_add_alpha(old, FALSE, 0, 0, 0); + g_object_unref(old); +} + +void Pixbuf::_setMimeData(guchar *data, gsize len, Glib::ustring const &format) +{ + gchar const *mimetype = NULL; + + if (format == "jpeg") { + mimetype = CAIRO_MIME_TYPE_JPEG; + } else if (format == "jpeg2000") { + mimetype = CAIRO_MIME_TYPE_JP2; + } else if (format == "png") { + mimetype = CAIRO_MIME_TYPE_PNG; + } + + if (mimetype != NULL) { + cairo_surface_set_mime_data(_surface, mimetype, data, len, g_free, data); + //g_message("Setting Cairo MIME data: %s", mimetype); + } else { + g_free(data); + //g_message("Not setting Cairo MIME data: unknown format %s", name.c_str()); + } +} + +void Pixbuf::ensurePixelFormat(PixelFormat fmt) +{ + if (_pixel_format == PF_GDK) { + if (fmt == PF_GDK) { + return; + } + if (fmt == PF_CAIRO) { + convert_pixels_pixbuf_to_argb32( + gdk_pixbuf_get_pixels(_pixbuf), + gdk_pixbuf_get_width(_pixbuf), + gdk_pixbuf_get_height(_pixbuf), + gdk_pixbuf_get_rowstride(_pixbuf)); + _pixel_format = fmt; + return; + } + g_assert_not_reached(); + } + if (_pixel_format == PF_CAIRO) { + if (fmt == PF_GDK) { + convert_pixels_argb32_to_pixbuf( + gdk_pixbuf_get_pixels(_pixbuf), + gdk_pixbuf_get_width(_pixbuf), + gdk_pixbuf_get_height(_pixbuf), + gdk_pixbuf_get_rowstride(_pixbuf)); + _pixel_format = fmt; + return; + } + if (fmt == PF_CAIRO) { + return; + } + g_assert_not_reached(); + } + g_assert_not_reached(); +} + } // namespace Inkscape /* @@ -371,129 +749,6 @@ ink_cairo_pattern_set_matrix(cairo_pattern_t *cp, Geom::Affine const &m) cairo_pattern_set_matrix(cp, &cm); } -void -ink_cairo_set_source_pixbuf(cairo_t *ct, GdkPixbuf *pb, double x, double y) -{ - cairo_surface_t *pbs = ink_cairo_surface_get_for_pixbuf(pb); - cairo_set_source_surface(ct, pbs, x, y); -} - -/* The functions below implement the following hack: - * - * The pixels formats of Cairo and GdkPixbuf are different. - * GdkPixbuf accesses pixels as bytes, alpha is not premultiplied, - * and successive bytes of a single pixel contain R, G, B and A components. - * Cairo accesses pixels as 32-bit ints, alpha is premultiplied, - * and each int contains as 0xAARRGGBB, accessed with bitwise operations. - * - * In other words, on a little endian system, a GdkPixbuf will contain: - * char *data = "rgbargbargba...." - * int *data = { 0xAABBGGRR, 0xAABBGGRR, 0xAABBGGRR, ... } - * while a Cairo image surface will contain: - * char *data = "bgrabgrabgra...." - * int *data = { 0xAARRGGBB, 0xAARRGGBB, 0xAARRGGBB, ... } - * - * It is possible to convert between these two formats (almost) losslessly. - * Some color information from partially transparent regions of the image - * is lost, but the result when displaying this image will remain the same. - * - * The functions below allow interoperation between GdkPixbuf - * and Cairo surfaces, allowing pixbufs to be used as Cairo sources, - * and saving Cairo surfaces using GdkPixbuf APIs. - * This is implemented by creating a GdkPixbuf and a Cairo image surface - * which share their data. Depending on what is needed at a given time, - * the pixels are converted in place to the Cairo or the GdkPixbuf format. - * In this way, only one copy of the image data is needed. - * - * Given either a GdkPixbuf or a Cairo surface, these functions create - * the other object and convert to its format. The returned object should be - * freed using cairo_surface_destroy or g_object_unref when it's no longer - * needed. - * - * Memory ownership semantics: - * Regardless of whether the pixels are stored in memory originally belonging - * to Cairo surface or to GdkPixbuf, the GdkPixbuf is the master object. - * To free the memory, unref the GdkPixbuf ONLY. - */ - -/** - * Converts the pixbuf to Cairo pixel format and returns an image surface - * which can be used as a source. - * - * The returned surface is owned by the GdkPixbuf and should not be freed. - * Calling this function causes the pixbuf to be unsuitable for use - * with GTK drawing functions until ink_pixbuf_ensure_normal() is called. - * - * @bug You have to call g_object_set_data(G_OBJECT(pb), "cairo_surface", NULL) - * when unrefing the last reference to the pixbuf. Otherwise there will be - * crashes, because cairo_surface_destroy is called after the pixbuf data - * is already freed. - */ -cairo_surface_t * -ink_cairo_surface_get_for_pixbuf(GdkPixbuf *pb) -{ - cairo_surface_t *pbs = - reinterpret_cast(g_object_get_data(G_OBJECT(pb), "cairo_surface")); - - ink_pixbuf_ensure_argb32(pb); - - if (pbs == NULL) { - guchar *data = gdk_pixbuf_get_pixels(pb); - int w = gdk_pixbuf_get_width(pb); - int h = gdk_pixbuf_get_height(pb); - int stride = gdk_pixbuf_get_rowstride(pb); - - // create a surface that stores the data - pbs = cairo_image_surface_create_for_data( - data, CAIRO_FORMAT_ARGB32, w, h, stride); - - g_object_set_data_full(G_OBJECT(pb), "cairo_surface", pbs, (GDestroyNotify) cairo_surface_destroy); - cairo_surface_set_user_data(pbs, &ink_pixbuf_key, pb, NULL); - } - - return pbs; -} - -/** - * Converts the Cairo surface to GdkPixbuf pixel format. - * GdkPixbuf takes ownership of the passed surface reference, - * so it should NOT be freed after calling this function. - */ -GdkPixbuf *ink_pixbuf_create_from_cairo_surface(cairo_surface_t *s) -{ - GdkPixbuf *pb = reinterpret_cast(cairo_surface_get_user_data(s, &ink_pixbuf_key)); - if (pb == NULL) { - pb = gdk_pixbuf_new_from_data( - cairo_image_surface_get_data(s), GDK_COLORSPACE_RGB, TRUE, 8, - cairo_image_surface_get_width(s), cairo_image_surface_get_height(s), - cairo_image_surface_get_stride(s), NULL, NULL); - - g_object_set_data_full(G_OBJECT(pb), "pixel_format", g_strdup("argb32"), g_free); - g_object_set_data_full(G_OBJECT(pb), "cairo_surface", s, (GDestroyNotify) cairo_surface_destroy); - - cairo_surface_set_user_data(s, &ink_pixbuf_key, pb, NULL); - } else { - g_warning("Received surface which is already owned by GdkPixbuf"); - g_object_ref(pb); - } - - ink_pixbuf_ensure_normal(pb); - - return pb; -} - -/** - * Cleanup function for GdkPixbuf. - * This function should be passed as the GdkPixbufDestroyNotify parameter - * to gdk_pixbuf_new_from_data when creating a GdkPixbuf backed by - * a Cairo surface. - */ -void ink_cairo_pixbuf_cleanup(guchar * /*pixels*/, void *data) -{ - cairo_surface_t *surface = reinterpret_cast(data); - cairo_surface_destroy(surface); -} - /** * Create an exact copy of a surface. * Creates a surface that has the same type, content type, dimensions and contents @@ -833,6 +1088,44 @@ ink_cairo_pattern_create_checkerboard() return p; } +/** + * Converts the Cairo surface to a GdkPixbuf pixel format, + * without allocating extra memory. + * + * This function is intended mainly for creating previews displayed by GTK. + * For loading images for display on the canvas, use the Inkscape::Pixbuf object. + * + * The returned GdkPixbuf takes ownership of the passed surface reference, + * so it should NOT be freed after calling this function. + */ +GdkPixbuf *ink_pixbuf_create_from_cairo_surface(cairo_surface_t *s) +{ + guchar *pixels = cairo_image_surface_get_data(s); + int w = cairo_image_surface_get_width(s); + int h = cairo_image_surface_get_height(s); + int rs = cairo_image_surface_get_stride(s); + + convert_pixels_argb32_to_pixbuf(pixels, w, h, rs); + + GdkPixbuf *pb = gdk_pixbuf_new_from_data( + pixels, GDK_COLORSPACE_RGB, TRUE, 8, + w, h, rs, ink_cairo_pixbuf_cleanup, s); + + return pb; +} + +/** + * Cleanup function for GdkPixbuf. + * This function should be passed as the GdkPixbufDestroyNotify parameter + * to gdk_pixbuf_new_from_data when creating a GdkPixbuf backed by + * a Cairo surface. + */ +static void ink_cairo_pixbuf_cleanup(guchar * /*pixels*/, void *data) +{ + cairo_surface_t *surface = static_cast(data); + cairo_surface_destroy(surface); +} + /* The following two functions use "from" instead of "to", because when you write: val1 = argb32_from_pixbuf(val1); the name of the format is closer to the value in that format. */ -- cgit v1.2.3 From 0191af5c169ac3e3086873f6edbe33dd17800773 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Thu, 19 Sep 2013 15:23:02 +0200 Subject: Fix type mismatch for platforms where gsize is not unsigned long (bzr r12537) --- src/display/cairo-utils.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/display/cairo-utils.cpp') diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index 2c7b543c1..451f0b509 100644 --- a/src/display/cairo-utils.cpp +++ b/src/display/cairo-utils.cpp @@ -399,9 +399,9 @@ guchar const *Pixbuf::getMimeData(gsize &len, std::string &mimetype) const for (guint i = 0; i < mimetypes_len; ++i) { unsigned long len_long = 0; - cairo_surface_get_mime_data(const_cast(_surface), mimetypes[i], &data, &len); - len = len_long; // this assumes that the added range of long is not needed. the code below assumes gsize range of values is sufficient. + cairo_surface_get_mime_data(const_cast(_surface), mimetypes[i], &data, &len_long); if (data != NULL) { + len = len_long; mimetype = mimetypes[i]; break; } -- cgit v1.2.3 From d3f86d34d0d92505a798f8c9b83ca12dfcbf50ab Mon Sep 17 00:00:00 2001 From: Kris De Gussem Date: Sun, 13 Oct 2013 17:24:57 +0200 Subject: cppcheck (bzr r12680) --- src/display/cairo-utils.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/display/cairo-utils.cpp') diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index 451f0b509..5b358ade7 100644 --- a/src/display/cairo-utils.cpp +++ b/src/display/cairo-utils.cpp @@ -254,7 +254,6 @@ Pixbuf *Pixbuf::create_from_data_uri(gchar const *uri_data) } if ((*data) && data_is_image && data_is_base64) { - GdkPixbuf *buf = NULL; GdkPixbufLoader *loader = gdk_pixbuf_loader_new(); if (!loader) return NULL; @@ -264,7 +263,7 @@ Pixbuf *Pixbuf::create_from_data_uri(gchar const *uri_data) if (gdk_pixbuf_loader_write(loader, decoded, decoded_len, NULL)) { gdk_pixbuf_loader_close(loader, NULL); - buf = gdk_pixbuf_loader_get_pixbuf(loader); + GdkPixbuf *buf = gdk_pixbuf_loader_get_pixbuf(loader); if (buf) { g_object_ref(buf); pixbuf = new Pixbuf(buf); -- cgit v1.2.3