From 6febb5a75ac5b7d8b41c4cf6c49799c094b98199 Mon Sep 17 00:00:00 2001 From: Jasper van de Gronde Date: Thu, 21 Mar 2013 12:15:43 +0100 Subject: Handle bitmap downsampling ourselves, see bug #804162 in launchpad. (bzr r12227) --- src/display/drawing-image.cpp | 124 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 112 insertions(+), 12 deletions(-) (limited to 'src/display/drawing-image.cpp') diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp index 0c8ac9681..185b62285 100644 --- a/src/display/drawing-image.cpp +++ b/src/display/drawing-image.cpp @@ -22,6 +22,7 @@ DrawingImage::DrawingImage(Drawing &drawing) : DrawingItem(drawing) , _pixbuf(NULL) , _surface(NULL) + , _new_surface(NULL) , _style(NULL) {} @@ -30,6 +31,7 @@ DrawingImage::~DrawingImage() if (_style) sp_style_unref(_style); if (_pixbuf) { + if (_new_surface) cairo_surface_destroy(_new_surface); cairo_surface_destroy(_surface); g_object_unref(_pixbuf); } @@ -118,26 +120,124 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar if (!outline) { if (!_pixbuf) return RENDER_OK; - + Inkscape::DrawingContext::Save save(ct); ct.transform(_ctm); ct.newPath(); ct.rectangle(_clipbox); ct.clip(); - ct.translate(_origin); - ct.scale(_scale); - ct.setSource(_surface, 0, 0); + ///////////////////////////////////////////////////////////////////////////// + // BEGIN: Hack to avoid Cairo bug + // The total transform (which is RIGHT-multiplied with the item points to get display points) equals: + // scale*translate_origin*_ctm = scale*translate(origin)*expansion*expansionInv*_ctm + // = scale*expansion*translate(origin*expansion)*expansionInv*_ctm + // To avoid a Cairo bug, we handle the scale*expansion part ourselves. + // See https://bugs.launchpad.net/inkscape/+bug/804162 + + Geom::Scale expansion(_ctm.expansion()); + int orgwidth = cairo_image_surface_get_width(_surface); + int orgheight = cairo_image_surface_get_height(_surface); + + if (_scale[Geom::X]*expansion[Geom::X]*orgwidth*255.0<1.0 || _scale[Geom::Y]*expansion[Geom::Y]*orgheight*255.0<1.0) { + // Resized image too small to actually see anything + return RENDER_OK; + } + + // Split scale*expansion in a part that is <= 1.0 and a part that is >= 1.0. We only take care of the part <= 1.0. + Geom::Scale scaleExpansionSmall(std::min(fabs(_scale[Geom::X]*expansion[Geom::X]),1),std::min(fabs(_scale[Geom::Y]*expansion[Geom::Y]),1)); + Geom::Scale scaleExpansionLarge(_scale[Geom::X]*expansion[Geom::X]/scaleExpansionSmall[Geom::X],_scale[Geom::Y]*expansion[Geom::Y]/scaleExpansionSmall[Geom::Y]); + + Geom::Point newSize(Geom::Point(orgwidth,orgheight)*scaleExpansionSmall); + if ((newSize-Geom::Point(orgwidth,orgheight)).length()<0.1) { + // Just use _surface directly. + ct.scale(expansion.inverse()); // This should not include scale (see derivation above) + ct.translate(_origin*expansion); + ct.scale(scaleExpansionLarge); + ct.setSource(_surface, 0, 0); + } else if (!_new_surface || (newSize-_rescaledSize).length()>0.1) { + // Rescaled image is sufficiently different from cached image to recompute + if (_new_surface) cairo_surface_destroy(_new_surface); + _rescaledSize = newSize; + + // This essentially considers an image to be composed of rectangular pixels and computes the least-squares approximation of the original. + // When the scale factor is really large or small this essentially results in using a box filter, while for scale factors approaching 1 it is more like a "tent" kernel. + int newwidth = static_cast(floor(orgwidth*scaleExpansionSmall[Geom::X])+1); + int newheight = static_cast(floor(orgheight*scaleExpansionSmall[Geom::Y])+1); + std::vector xBegin(newwidth, -1), yBegin(newheight, -1); + std::vector< std::vector > xCoefs(xBegin.size()), yCoefs(yBegin.size()); + for(int x=0; x(scaleExpansionSmall[Geom::X]); // x-coord in target coordinates where the current source pixel begins + double coordEnd = (x+1)*static_cast(scaleExpansionSmall[Geom::X]); // x-coord in target coordinates where the current source pixel ends + int begin = static_cast(floor(coordBegin)); // First pixel (x-coord) affected by the current source pixel + int end = static_cast(ceil(coordEnd)); // First pixel (x-coord) NOT affected by the current source pixel (a zero contribution is counted as not affecting the pixel) + for(int nx=begin; nx(std::min(nx+1,coordEnd) - std::max(nx,coordBegin))); + } + } + for(int y=0; y(scaleExpansionSmall[Geom::Y]); // y-coord in target coordinates where the current source pixel begins + double coordEnd = (y+1)*static_cast(scaleExpansionSmall[Geom::Y]); // y-coord in target coordinates where the current source pixel ends + int begin = static_cast(floor(coordBegin)); // First pixel (y-coord) affected by the current source pixel + int end = static_cast(ceil(coordEnd)); // First pixel (y-coord) NOT affected by the current source pixel (a zero contribution is counted as not affecting the pixel) + for(int ny=begin; ny(std::min(ny+1,coordEnd) - std::max(ny,coordBegin))); + } + } + + _new_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, newwidth,newheight); + unsigned char * orgdata = cairo_image_surface_get_data(_surface); + unsigned char * newdata = cairo_image_surface_get_data(_new_surface); + int orgstride = cairo_image_surface_get_stride(_surface); + int newstride = cairo_image_surface_get_stride(_new_surface); + + cairo_surface_flush(_surface); + cairo_surface_flush(_new_surface); + + for(int y=0; y(yCoefs[y].size()); oy++) { + for(int ox=0; ox(xCoefs[x].size()); ox++) { + for(int c=0; c<4; c++) { + tempSum[c] += xCoefs[x][ox]*yCoefs[y][oy]*orgdata[c+4*(xBegin[x]+ox)+orgstride*(yBegin[y]+oy)]; + } + } + } + for(int c=0; c<4; c++) { + newdata[c+4*x+newstride*y] = static_cast(tempSum[c]); + } + } + } + + cairo_surface_mark_dirty(_new_surface); + + ct.scale(expansion.inverse()); // This should not include scale (see derivation above) + ct.translate(_origin*expansion); + ct.scale(scaleExpansionLarge); + ct.setSource(_new_surface, 0, 0); + } else { + // No need to regenerate, but we do draw from _new_surface. + ct.scale(expansion.inverse()); // This should not include scale (see derivation above) + ct.translate(_origin*expansion); + ct.scale(scaleExpansionLarge); + ct.setSource(_new_surface, 0, 0); + } + + // END: Hack to avoid Cairo bug + ///////////////////////////////////////////////////////////////////////////// - cairo_matrix_t tt; - Geom::Affine total; - cairo_get_matrix(ct.raw(), &tt); - ink_matrix_to_2geom(total, tt); + // TODO: If Cairo's problems are gone, uncomment the following: + //ct.translate(_origin); + //ct.scale(_scale); + //ct.setSource(_surface, 0, 0); - if (total.expansionX() > 1.0 || total.expansionY() > 1.0) { - cairo_pattern_t *p = cairo_get_source(ct.raw()); - cairo_pattern_set_filter(p, CAIRO_FILTER_NEAREST); - } //ct.paint(_opacity); ct.paint(); -- cgit v1.2.3 From 787604447932202d97e7e9d4fcd3bec5403417d9 Mon Sep 17 00:00:00 2001 From: Jasper van de Gronde Date: Thu, 21 Mar 2013 12:24:40 +0100 Subject: Clarified choice of downsampling routine. (bzr r12228) --- src/display/drawing-image.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/display/drawing-image.cpp') diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp index 185b62285..2bfd71713 100644 --- a/src/display/drawing-image.cpp +++ b/src/display/drawing-image.cpp @@ -160,8 +160,10 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar if (_new_surface) cairo_surface_destroy(_new_surface); _rescaledSize = newSize; - // This essentially considers an image to be composed of rectangular pixels and computes the least-squares approximation of the original. + // This essentially considers an image to be composed of rectangular pixels (box kernel) and computes the least-squares approximation of the original. // When the scale factor is really large or small this essentially results in using a box filter, while for scale factors approaching 1 it is more like a "tent" kernel. + // Although the quality of the result is not great, it is typically better than an ordinary box filter, and it is guaranteed to preserve the overall brightness of the image. + // The best improvement would probably be to do the same kind of thing based on a tent kernel, but that's quite a bit more complicated, and probably not worth the trouble for a hack like this. int newwidth = static_cast(floor(orgwidth*scaleExpansionSmall[Geom::X])+1); int newheight = static_cast(floor(orgheight*scaleExpansionSmall[Geom::Y])+1); std::vector xBegin(newwidth, -1), yBegin(newheight, -1); -- cgit v1.2.3 From 4dfa1ac03459ef68f137de21c6840382f60c0e77 Mon Sep 17 00:00:00 2001 From: Alex Valavanis Date: Sat, 23 Mar 2013 14:05:04 +0000 Subject: Fix more clang warnings (bzr r12235) --- src/display/drawing-image.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/display/drawing-image.cpp') diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp index 2bfd71713..3f1a86ee7 100644 --- a/src/display/drawing-image.cpp +++ b/src/display/drawing-image.cpp @@ -22,8 +22,8 @@ DrawingImage::DrawingImage(Drawing &drawing) : DrawingItem(drawing) , _pixbuf(NULL) , _surface(NULL) - , _new_surface(NULL) , _style(NULL) + , _new_surface(NULL) {} DrawingImage::~DrawingImage() -- cgit v1.2.3 From 6ecaa3dae4f5ad50d90aba002c5151b326e61c9e Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Mon, 8 Apr 2013 14:16:02 +0200 Subject: Implement read/write of image-rendering property. To be used to control scaling. (bzr r12272) --- src/display/drawing-image.cpp | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/display/drawing-image.cpp') diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp index 3f1a86ee7..753249e60 100644 --- a/src/display/drawing-image.cpp +++ b/src/display/drawing-image.cpp @@ -121,6 +121,13 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar if (!outline) { if (!_pixbuf) return RENDER_OK; + // if (_style) { + // _style->image_rendering.computed + // See: http://www.w3.org/TR/SVG/painting.html#ImageRenderingProperty + // http://www.w3.org/TR/css4-images/#the-image-rendering + // style.h/style.cpp + // } + Inkscape::DrawingContext::Save save(ct); ct.transform(_ctm); ct.newPath(); -- cgit v1.2.3 From ed40d19ee34bde5b7a6b7bf38904838f8d072f87 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Wed, 31 Jul 2013 20:18:48 +0200 Subject: Fix selection of images in outline mode. Fixes LP #1089702 Fixed bugs: - https://launchpad.net/bugs/1089702 (bzr r12440) --- src/display/drawing-image.cpp | 46 ++++++++++++++----------------------------- 1 file changed, 15 insertions(+), 31 deletions(-) (limited to 'src/display/drawing-image.cpp') diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp index 753249e60..bdb7c15b0 100644 --- a/src/display/drawing-image.cpp +++ b/src/display/drawing-image.cpp @@ -9,6 +9,7 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include <2geom/bezier-curve.h> #include "display/cairo-utils.h" #include "display/drawing.h" #include "display/drawing-context.h" @@ -287,21 +288,9 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar static double distance_to_segment (Geom::Point const &p, Geom::Point const &a1, Geom::Point const &a2) { - // calculate sides of the triangle and their squares - double d1 = Geom::L2(p - a1); - double d1_2 = d1 * d1; - double d2 = Geom::L2(p - a2); - double d2_2 = d2 * d2; - double a = Geom::L2(a1 - a2); - double a_2 = a * a; - - // if one of the angles at the base is > 90, return the corresponding side - if (d1_2 + a_2 <= d2_2) return d1; - if (d2_2 + a_2 <= d1_2) return d2; - - // otherwise calculate the height to the base - double peri = (a + d1 + d2)/2; - return (2*sqrt(peri * (peri - a) * (peri - d1) * (peri - d2))/a); + Geom::LineSegment l(a1, a2); + Geom::Point np = l.pointAt(l.nearestPoint(p)); + return Geom::distance(np, p); } DrawingItem * @@ -313,22 +302,17 @@ DrawingImage::_pickItem(Geom::Point const &p, double delta, unsigned /*sticky*/) if (outline) { Geom::Rect r = bounds(); - - Geom::Point c00 = r.corner(0); - Geom::Point c01 = r.corner(3); - Geom::Point c11 = r.corner(2); - Geom::Point c10 = r.corner(1); - - // frame - if (distance_to_segment (p, c00, c10) < delta) return this; - if (distance_to_segment (p, c10, c11) < delta) return this; - if (distance_to_segment (p, c11, c01) < delta) return this; - if (distance_to_segment (p, c01, c00) < delta) return this; - - // diagonals - if (distance_to_segment (p, c00, c11) < delta) return this; - if (distance_to_segment (p, c10, c01) < delta) return this; - + Geom::Point pick = p * _ctm.inverse(); + + // find whether any side or diagonal is within delta + // to do so, iterate over all pairs of corners + for (unsigned i = 0; i < 3; ++i) { // for i=3, there is nothing to do + for (unsigned j = i+1; j < 4; ++j) { + if (distance_to_segment(pick, r.corner(i), r.corner(j)) < delta) { + return this; + } + } + } return NULL; } else { -- 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/drawing-image.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/display/drawing-image.cpp') diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp index bdb7c15b0..b94d48774 100644 --- a/src/display/drawing-image.cpp +++ b/src/display/drawing-image.cpp @@ -22,7 +22,7 @@ namespace Inkscape { DrawingImage::DrawingImage(Drawing &drawing) : DrawingItem(drawing) , _pixbuf(NULL) - , _surface(NULL) + , _surface(NULL) // this is owned by _pixbuf! , _style(NULL) , _new_surface(NULL) {} @@ -33,7 +33,6 @@ DrawingImage::~DrawingImage() sp_style_unref(_style); if (_pixbuf) { if (_new_surface) cairo_surface_destroy(_new_surface); - cairo_surface_destroy(_surface); g_object_unref(_pixbuf); } } @@ -50,7 +49,7 @@ DrawingImage::setARGB32Pixbuf(GdkPixbuf *pb) cairo_surface_destroy(_surface); } _pixbuf = pb; - _surface = pb ? ink_cairo_surface_create_for_argb32_pixbuf(pb) : NULL; + _surface = pb ? ink_cairo_surface_get_for_pixbuf(pb) : NULL; _markForUpdate(STATE_ALL, false); } -- 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/drawing-image.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/display/drawing-image.cpp') diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp index b94d48774..46f066b8e 100644 --- a/src/display/drawing-image.cpp +++ b/src/display/drawing-image.cpp @@ -46,7 +46,7 @@ DrawingImage::setARGB32Pixbuf(GdkPixbuf *pb) } if (_pixbuf != NULL) { g_object_unref(_pixbuf); - cairo_surface_destroy(_surface); + // unrefing the pixbuf also destroys surface } _pixbuf = pb; _surface = pb ? ink_cairo_surface_get_for_pixbuf(pb) : NULL; @@ -206,7 +206,7 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar int orgstride = cairo_image_surface_get_stride(_surface); int newstride = cairo_image_surface_get_stride(_new_surface); - cairo_surface_flush(_surface); + //cairo_surface_flush(_surface); cairo_surface_flush(_new_surface); for(int y=0; y 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/drawing-image.cpp | 64 +++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 32 deletions(-) (limited to 'src/display/drawing-image.cpp') diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp index 46f066b8e..0b661a450 100644 --- a/src/display/drawing-image.cpp +++ b/src/display/drawing-image.cpp @@ -22,34 +22,23 @@ namespace Inkscape { DrawingImage::DrawingImage(Drawing &drawing) : DrawingItem(drawing) , _pixbuf(NULL) - , _surface(NULL) // this is owned by _pixbuf! , _style(NULL) , _new_surface(NULL) {} DrawingImage::~DrawingImage() { - if (_style) + if (_style) { sp_style_unref(_style); - if (_pixbuf) { - if (_new_surface) cairo_surface_destroy(_new_surface); - g_object_unref(_pixbuf); } + + // _pixbuf is owned by SPImage - do not delete it } void -DrawingImage::setARGB32Pixbuf(GdkPixbuf *pb) +DrawingImage::setPixbuf(Inkscape::Pixbuf *pb) { - // when done in this order, it won't break if pb == image->pixbuf and the refcount is 1 - if (pb != NULL) { - g_object_ref (pb); - } - if (_pixbuf != NULL) { - g_object_unref(_pixbuf); - // unrefing the pixbuf also destroys surface - } _pixbuf = pb; - _surface = pb ? ink_cairo_surface_get_for_pixbuf(pb) : NULL; _markForUpdate(STATE_ALL, false); } @@ -86,8 +75,8 @@ DrawingImage::bounds() const { if (!_pixbuf) return _clipbox; - double pw = gdk_pixbuf_get_width(_pixbuf); - double ph = gdk_pixbuf_get_height(_pixbuf); + double pw = _pixbuf->width(); + double ph = _pixbuf->height(); double vw = pw * _scale[Geom::X]; double vh = ph * _scale[Geom::Y]; Geom::Point wh(vw, vh); @@ -143,14 +132,16 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar // See https://bugs.launchpad.net/inkscape/+bug/804162 Geom::Scale expansion(_ctm.expansion()); - int orgwidth = cairo_image_surface_get_width(_surface); - int orgheight = cairo_image_surface_get_height(_surface); + int orgwidth = _pixbuf->width(); + int orgheight = _pixbuf->height(); if (_scale[Geom::X]*expansion[Geom::X]*orgwidth*255.0<1.0 || _scale[Geom::Y]*expansion[Geom::Y]*orgheight*255.0<1.0) { // Resized image too small to actually see anything return RENDER_OK; } - + + _pixbuf->ensurePixelFormat(Inkscape::Pixbuf::PF_CAIRO); + // Split scale*expansion in a part that is <= 1.0 and a part that is >= 1.0. We only take care of the part <= 1.0. Geom::Scale scaleExpansionSmall(std::min(fabs(_scale[Geom::X]*expansion[Geom::X]),1),std::min(fabs(_scale[Geom::Y]*expansion[Geom::Y]),1)); Geom::Scale scaleExpansionLarge(_scale[Geom::X]*expansion[Geom::X]/scaleExpansionSmall[Geom::X],_scale[Geom::Y]*expansion[Geom::Y]/scaleExpansionSmall[Geom::Y]); @@ -161,7 +152,7 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar ct.scale(expansion.inverse()); // This should not include scale (see derivation above) ct.translate(_origin*expansion); ct.scale(scaleExpansionLarge); - ct.setSource(_surface, 0, 0); + ct.setSource(_pixbuf->getSurfaceRaw(), 0, 0); } else if (!_new_surface || (newSize-_rescaledSize).length()>0.1) { // Rescaled image is sufficiently different from cached image to recompute if (_new_surface) cairo_surface_destroy(_new_surface); @@ -200,13 +191,13 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar } } + cairo_surface_t *surface = _pixbuf->getSurfaceRaw(); _new_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, newwidth,newheight); - unsigned char * orgdata = cairo_image_surface_get_data(_surface); + unsigned char * orgdata = cairo_image_surface_get_data(surface); unsigned char * newdata = cairo_image_surface_get_data(_new_surface); - int orgstride = cairo_image_surface_get_stride(_surface); + int orgstride = cairo_image_surface_get_stride(surface); int newstride = cairo_image_surface_get_stride(_new_surface); - - //cairo_surface_flush(_surface); + cairo_surface_flush(_new_surface); for(int y=0; ygetSurfaceRaw(), 0, 0); //ct.paint(_opacity); ct.paint(); @@ -315,10 +306,10 @@ DrawingImage::_pickItem(Geom::Point const &p, double delta, unsigned /*sticky*/) return NULL; } else { - unsigned char *const pixels = gdk_pixbuf_get_pixels(_pixbuf); - int width = gdk_pixbuf_get_width(_pixbuf); - int height = gdk_pixbuf_get_height(_pixbuf); - int rowstride = gdk_pixbuf_get_rowstride(_pixbuf); + unsigned char *const pixels = _pixbuf->pixels(); + int width = _pixbuf->width(); + int height = _pixbuf->height(); + int rowstride = _pixbuf->rowstride(); Geom::Point tp = p * _ctm.inverse(); Geom::Rect r = bounds(); @@ -336,8 +327,17 @@ DrawingImage::_pickItem(Geom::Point const &p, double delta, unsigned /*sticky*/) unsigned char *pix_ptr = pixels + iy * rowstride + ix * 4; // pick if the image is less than 99% transparent - float alpha = (pix_ptr[3] / 255.0f) * _opacity; - return alpha > 0.01 ? this : NULL; + guint32 alpha = 0; + if (_pixbuf->pixelFormat() == Inkscape::Pixbuf::PF_CAIRO) { + guint32 px = *reinterpret_cast(pix_ptr); + alpha = (px & 0xff000000) >> 24; + } else if (_pixbuf->pixelFormat() == Inkscape::Pixbuf::PF_GDK) { + alpha = pix_ptr[3]; + } else { + throw std::runtime_error("Unrecognized pixel format"); + } + float alpha_f = (alpha / 255.0f) * _opacity; + return alpha_f > 0.01 ? this : NULL; } } -- cgit v1.2.3 From 2faa250b2e8a9ce2f1bd29a38024a368d5d379c9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Fri, 27 Sep 2013 00:19:14 +0200 Subject: The downscaling fix is already in Cairo trunk. Therefore, remove the image scaling hack from drawing-image.cpp. (bzr r12599) --- src/display/drawing-image.cpp | 121 ++---------------------------------------- 1 file changed, 4 insertions(+), 117 deletions(-) (limited to 'src/display/drawing-image.cpp') diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp index 0b661a450..a9c0499c2 100644 --- a/src/display/drawing-image.cpp +++ b/src/display/drawing-image.cpp @@ -23,7 +23,6 @@ DrawingImage::DrawingImage(Drawing &drawing) : DrawingItem(drawing) , _pixbuf(NULL) , _style(NULL) - , _new_surface(NULL) {} DrawingImage::~DrawingImage() @@ -123,123 +122,11 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar ct.rectangle(_clipbox); ct.clip(); - ///////////////////////////////////////////////////////////////////////////// - // BEGIN: Hack to avoid Cairo bug - // The total transform (which is RIGHT-multiplied with the item points to get display points) equals: - // scale*translate_origin*_ctm = scale*translate(origin)*expansion*expansionInv*_ctm - // = scale*expansion*translate(origin*expansion)*expansionInv*_ctm - // To avoid a Cairo bug, we handle the scale*expansion part ourselves. - // See https://bugs.launchpad.net/inkscape/+bug/804162 - - Geom::Scale expansion(_ctm.expansion()); - int orgwidth = _pixbuf->width(); - int orgheight = _pixbuf->height(); - - if (_scale[Geom::X]*expansion[Geom::X]*orgwidth*255.0<1.0 || _scale[Geom::Y]*expansion[Geom::Y]*orgheight*255.0<1.0) { - // Resized image too small to actually see anything - return RENDER_OK; - } - - _pixbuf->ensurePixelFormat(Inkscape::Pixbuf::PF_CAIRO); - - // Split scale*expansion in a part that is <= 1.0 and a part that is >= 1.0. We only take care of the part <= 1.0. - Geom::Scale scaleExpansionSmall(std::min(fabs(_scale[Geom::X]*expansion[Geom::X]),1),std::min(fabs(_scale[Geom::Y]*expansion[Geom::Y]),1)); - Geom::Scale scaleExpansionLarge(_scale[Geom::X]*expansion[Geom::X]/scaleExpansionSmall[Geom::X],_scale[Geom::Y]*expansion[Geom::Y]/scaleExpansionSmall[Geom::Y]); - - Geom::Point newSize(Geom::Point(orgwidth,orgheight)*scaleExpansionSmall); - if ((newSize-Geom::Point(orgwidth,orgheight)).length()<0.1) { - // Just use _surface directly. - ct.scale(expansion.inverse()); // This should not include scale (see derivation above) - ct.translate(_origin*expansion); - ct.scale(scaleExpansionLarge); - ct.setSource(_pixbuf->getSurfaceRaw(), 0, 0); - } else if (!_new_surface || (newSize-_rescaledSize).length()>0.1) { - // Rescaled image is sufficiently different from cached image to recompute - if (_new_surface) cairo_surface_destroy(_new_surface); - _rescaledSize = newSize; - - // This essentially considers an image to be composed of rectangular pixels (box kernel) and computes the least-squares approximation of the original. - // When the scale factor is really large or small this essentially results in using a box filter, while for scale factors approaching 1 it is more like a "tent" kernel. - // Although the quality of the result is not great, it is typically better than an ordinary box filter, and it is guaranteed to preserve the overall brightness of the image. - // The best improvement would probably be to do the same kind of thing based on a tent kernel, but that's quite a bit more complicated, and probably not worth the trouble for a hack like this. - int newwidth = static_cast(floor(orgwidth*scaleExpansionSmall[Geom::X])+1); - int newheight = static_cast(floor(orgheight*scaleExpansionSmall[Geom::Y])+1); - std::vector xBegin(newwidth, -1), yBegin(newheight, -1); - std::vector< std::vector > xCoefs(xBegin.size()), yCoefs(yBegin.size()); - for(int x=0; x(scaleExpansionSmall[Geom::X]); // x-coord in target coordinates where the current source pixel begins - double coordEnd = (x+1)*static_cast(scaleExpansionSmall[Geom::X]); // x-coord in target coordinates where the current source pixel ends - int begin = static_cast(floor(coordBegin)); // First pixel (x-coord) affected by the current source pixel - int end = static_cast(ceil(coordEnd)); // First pixel (x-coord) NOT affected by the current source pixel (a zero contribution is counted as not affecting the pixel) - for(int nx=begin; nx(std::min(nx+1,coordEnd) - std::max(nx,coordBegin))); - } - } - for(int y=0; y(scaleExpansionSmall[Geom::Y]); // y-coord in target coordinates where the current source pixel begins - double coordEnd = (y+1)*static_cast(scaleExpansionSmall[Geom::Y]); // y-coord in target coordinates where the current source pixel ends - int begin = static_cast(floor(coordBegin)); // First pixel (y-coord) affected by the current source pixel - int end = static_cast(ceil(coordEnd)); // First pixel (y-coord) NOT affected by the current source pixel (a zero contribution is counted as not affecting the pixel) - for(int ny=begin; ny(std::min(ny+1,coordEnd) - std::max(ny,coordBegin))); - } - } - - cairo_surface_t *surface = _pixbuf->getSurfaceRaw(); - _new_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, newwidth,newheight); - unsigned char * orgdata = cairo_image_surface_get_data(surface); - unsigned char * newdata = cairo_image_surface_get_data(_new_surface); - int orgstride = cairo_image_surface_get_stride(surface); - int newstride = cairo_image_surface_get_stride(_new_surface); - - cairo_surface_flush(_new_surface); - - for(int y=0; y(yCoefs[y].size()); oy++) { - for(int ox=0; ox(xCoefs[x].size()); ox++) { - for(int c=0; c<4; c++) { - tempSum[c] += xCoefs[x][ox]*yCoefs[y][oy]*orgdata[c+4*(xBegin[x]+ox)+orgstride*(yBegin[y]+oy)]; - } - } - } - for(int c=0; c<4; c++) { - newdata[c+4*x+newstride*y] = static_cast(tempSum[c]); - } - } - } - - cairo_surface_mark_dirty(_new_surface); - - ct.scale(expansion.inverse()); // This should not include scale (see derivation above) - ct.translate(_origin*expansion); - ct.scale(scaleExpansionLarge); - ct.setSource(_new_surface, 0, 0); - } else { - // No need to regenerate, but we do draw from _new_surface. - ct.scale(expansion.inverse()); // This should not include scale (see derivation above) - ct.translate(_origin*expansion); - ct.scale(scaleExpansionLarge); - ct.setSource(_new_surface, 0, 0); - } - - // END: Hack to avoid Cairo bug - ///////////////////////////////////////////////////////////////////////////// - - // TODO: If Cairo's problems are gone, uncomment the following: - //ct.translate(_origin); - //ct.scale(_scale); - //ct.setSource(_pixbuf->getSurfaceRaw(), 0, 0); + ct.translate(_origin); + ct.scale(_scale); + ct.setSource(_pixbuf->getSurfaceRaw(), 0, 0); - //ct.paint(_opacity); - ct.paint(); + ct.paint(_opacity); } else { // outline; draw a rect instead Inkscape::Preferences *prefs = Inkscape::Preferences::get(); -- cgit v1.2.3 From 410223aec5614206e8fb485e181791bd3abd80f7 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Tue, 12 Nov 2013 15:28:10 +0100 Subject: Partial fix for blocker bug 1163449: "Imported bitmap appear blurry when zoomed in" Setting style to include "image-rendering:optimizeSpeed" will cause nearest neighbor filter to be used in downscaling. (bzr r12796) --- src/display/drawing-image.cpp | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) (limited to 'src/display/drawing-image.cpp') diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp index a9c0499c2..1b9214c49 100644 --- a/src/display/drawing-image.cpp +++ b/src/display/drawing-image.cpp @@ -108,13 +108,6 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar if (!outline) { if (!_pixbuf) return RENDER_OK; - - // if (_style) { - // _style->image_rendering.computed - // See: http://www.w3.org/TR/SVG/painting.html#ImageRenderingProperty - // http://www.w3.org/TR/css4-images/#the-image-rendering - // style.h/style.cpp - // } Inkscape::DrawingContext::Save save(ct); ct.transform(_ctm); @@ -126,6 +119,24 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar ct.scale(_scale); ct.setSource(_pixbuf->getSurfaceRaw(), 0, 0); + if (_style) { + // See: http://www.w3.org/TR/SVG/painting.html#ImageRenderingProperty + // http://www.w3.org/TR/css4-images/#the-image-rendering + // style.h/style.cpp + switch (_style->image_rendering.computed) { + case SP_CSS_COLOR_RENDERING_AUTO: + // Do nothing + break; + case SP_CSS_COLOR_RENDERING_OPTIMIZEQUALITY: + ct.patternSetFilter( CAIRO_FILTER_BEST ); + break; + case SP_CSS_COLOR_RENDERING_OPTIMIZESPEED: + default: + ct.patternSetFilter( CAIRO_FILTER_NEAREST ); + break; + } + } + ct.paint(_opacity); } else { // outline; draw a rect instead -- cgit v1.2.3 From d4ba8eaa4a621ac60d99a4aad7531d080cece2cd Mon Sep 17 00:00:00 2001 From: David Mathog Date: Sat, 8 Feb 2014 09:44:12 +0100 Subject: DrawingContext: change variable names ct to dc (bug #1272073) Fixed bugs: - https://launchpad.net/bugs/1272073 (bzr r13009) --- src/display/drawing-image.cpp | 52 +++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 26 deletions(-) (limited to 'src/display/drawing-image.cpp') diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp index 1b9214c49..8fd81caac 100644 --- a/src/display/drawing-image.cpp +++ b/src/display/drawing-image.cpp @@ -102,22 +102,22 @@ DrawingImage::_updateItem(Geom::IntRect const &, UpdateContext const &, unsigned return STATE_ALL; } -unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*area*/, unsigned /*flags*/, DrawingItem * /*stop_at*/) +unsigned DrawingImage::_renderItem(DrawingContext &dc, Geom::IntRect const &/*area*/, unsigned /*flags*/, DrawingItem * /*stop_at*/) { bool outline = _drawing.outline(); if (!outline) { if (!_pixbuf) return RENDER_OK; - Inkscape::DrawingContext::Save save(ct); - ct.transform(_ctm); - ct.newPath(); - ct.rectangle(_clipbox); - ct.clip(); + Inkscape::DrawingContext::Save save(dc); + dc.transform(_ctm); + dc.newPath(); + dc.rectangle(_clipbox); + dc.clip(); - ct.translate(_origin); - ct.scale(_scale); - ct.setSource(_pixbuf->getSurfaceRaw(), 0, 0); + dc.translate(_origin); + dc.scale(_scale); + dc.setSource(_pixbuf->getSurfaceRaw(), 0, 0); if (_style) { // See: http://www.w3.org/TR/SVG/painting.html#ImageRenderingProperty @@ -128,24 +128,24 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar // Do nothing break; case SP_CSS_COLOR_RENDERING_OPTIMIZEQUALITY: - ct.patternSetFilter( CAIRO_FILTER_BEST ); + dc.patternSetFilter( CAIRO_FILTER_BEST ); break; case SP_CSS_COLOR_RENDERING_OPTIMIZESPEED: default: - ct.patternSetFilter( CAIRO_FILTER_NEAREST ); + dc.patternSetFilter( CAIRO_FILTER_NEAREST ); break; } } - ct.paint(_opacity); + dc.paint(_opacity); } else { // outline; draw a rect instead Inkscape::Preferences *prefs = Inkscape::Preferences::get(); guint32 rgba = prefs->getInt("/options/wireframecolors/images", 0xff0000ff); - { Inkscape::DrawingContext::Save save(ct); - ct.transform(_ctm); - ct.newPath(); + { Inkscape::DrawingContext::Save save(dc); + dc.transform(_ctm); + dc.newPath(); Geom::Rect r = bounds(); Geom::Point c00 = r.corner(0); @@ -153,21 +153,21 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar Geom::Point c11 = r.corner(2); Geom::Point c10 = r.corner(1); - ct.moveTo(c00); + dc.moveTo(c00); // the box - ct.lineTo(c10); - ct.lineTo(c11); - ct.lineTo(c01); - ct.lineTo(c00); + dc.lineTo(c10); + dc.lineTo(c11); + dc.lineTo(c01); + dc.lineTo(c00); // the diagonals - ct.lineTo(c11); - ct.moveTo(c10); - ct.lineTo(c01); + dc.lineTo(c11); + dc.moveTo(c10); + dc.lineTo(c01); } - ct.setLineWidth(0.5); - ct.setSource(rgba); - ct.stroke(); + dc.setLineWidth(0.5); + dc.setSource(rgba); + dc.stroke(); } return RENDER_OK; } -- cgit v1.2.3 From 72e27901ad42c1d4c34b498e9a797e852179e594 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Fri, 7 Mar 2014 01:51:28 +0100 Subject: Use CAIRO_FILTER_GOOD instead of CAIRO_FILTER_BEST in the interactive renderer, since the latter uses Lanczos3 and is prohibitively slow. (bzr r13124) --- src/display/drawing-image.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/display/drawing-image.cpp') diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp index 8fd81caac..5844c8b08 100644 --- a/src/display/drawing-image.cpp +++ b/src/display/drawing-image.cpp @@ -128,7 +128,8 @@ unsigned DrawingImage::_renderItem(DrawingContext &dc, Geom::IntRect const &/*ar // Do nothing break; case SP_CSS_COLOR_RENDERING_OPTIMIZEQUALITY: - dc.patternSetFilter( CAIRO_FILTER_BEST ); + // In recent Cairo, BEST used Lanczos3, which is prohibitively slow + dc.patternSetFilter( CAIRO_FILTER_GOOD ); break; case SP_CSS_COLOR_RENDERING_OPTIMIZESPEED: default: -- cgit v1.2.3