diff options
| author | su_v <suv-sf@users.sourceforge.net> | 2013-03-21 14:21:33 +0000 |
|---|---|---|
| committer | ~suv <suv-sf@users.sourceforge.net> | 2013-03-21 14:21:33 +0000 |
| commit | 6e4dd17bb7c17023c12ffb4a5e8bac62fc9d5e7d (patch) | |
| tree | 0d9b47f006038fcb0f9635af413b2194445cce05 | |
| parent | 988601-changes_2013_03_20a.patch (diff) | |
| parent | Clarified choice of downsampling routine. (diff) | |
| download | inkscape-6e4dd17bb7c17023c12ffb4a5e8bac62fc9d5e7d.tar.gz inkscape-6e4dd17bb7c17023c12ffb4a5e8bac62fc9d5e7d.zip | |
merge from trunk (r1228)
(bzr r11668.1.63)
| -rw-r--r-- | share/keys/default.xml | 19 | ||||
| -rw-r--r-- | share/keys/inkscape.xml | 19 | ||||
| -rw-r--r-- | src/desktop.cpp | 7 | ||||
| -rw-r--r-- | src/display/drawing-image.cpp | 126 | ||||
| -rw-r--r-- | src/display/drawing-image.h | 3 | ||||
| -rw-r--r-- | src/event-log.cpp | 13 |
6 files changed, 158 insertions, 29 deletions
diff --git a/share/keys/default.xml b/share/keys/default.xml index f9a6f0d44..cb1273013 100644 --- a/share/keys/default.xml +++ b/share/keys/default.xml @@ -664,21 +664,24 @@ override) the bindings in the main default.xml. <!-- Effects --> - <bind action="EffectLast" /> - <bind action="EffectLastPref" /> + <bind key="q" modifiers="Alt" action="EffectLast" display="true" /> + <bind key="Q" modifiers="Alt" action="EffectLast" /> + <bind key="q" modifiers="Shift,Alt" action="EffectLastPref" display="true" /> + <bind key="Q" modifiers="Shift,Alt" action="EffectLastPref" /> + <!-- You can also assign keys to individual effects. The action should be set to he effect id from the .inx file, for example "org.ekips.filter.addnodes" for the Add Nodes effect. --> - <bind key="g" modifiers="Ctrl,Alt" action="org.inkscape.typography.newglyphlayer" display="true"/> - <bind key="G" modifiers="Ctrl,Alt" action="org.inkscape.typography.newglyphlayer"/> + <bind key="g" modifiers="Ctrl,Alt" action="org.inkscape.typography.newglyphlayer" display="true"/> + <bind key="G" modifiers="Ctrl,Alt" action="org.inkscape.typography.newglyphlayer"/> <!-- - <bind key="h" modifiers="Ctrl,Alt" action="org.inkscape.typography.previousglyphlayer" display="true"/> - <bind key="H" modifiers="Ctrl,Alt" action="org.inkscape.typography.previousglyphlayer"/> + <bind key="h" modifiers="Ctrl,Alt" action="org.inkscape.typography.previousglyphlayer" display="true"/> + <bind key="H" modifiers="Ctrl,Alt" action="org.inkscape.typography.previousglyphlayer"/> --> - <bind key="j" modifiers="Ctrl,Alt" action="org.inkscape.typography.nextglyphlayer" display="true"/> - <bind key="J" modifiers="Ctrl,Alt" action="org.inkscape.typography.nextglyphlayer"/> + <bind key="j" modifiers="Ctrl,Alt" action="org.inkscape.typography.nextglyphlayer" display="true"/> + <bind key="J" modifiers="Ctrl,Alt" action="org.inkscape.typography.nextglyphlayer"/> </keys> diff --git a/share/keys/inkscape.xml b/share/keys/inkscape.xml index f9a6f0d44..cb1273013 100644 --- a/share/keys/inkscape.xml +++ b/share/keys/inkscape.xml @@ -664,21 +664,24 @@ override) the bindings in the main default.xml. <!-- Effects --> - <bind action="EffectLast" /> - <bind action="EffectLastPref" /> + <bind key="q" modifiers="Alt" action="EffectLast" display="true" /> + <bind key="Q" modifiers="Alt" action="EffectLast" /> + <bind key="q" modifiers="Shift,Alt" action="EffectLastPref" display="true" /> + <bind key="Q" modifiers="Shift,Alt" action="EffectLastPref" /> + <!-- You can also assign keys to individual effects. The action should be set to he effect id from the .inx file, for example "org.ekips.filter.addnodes" for the Add Nodes effect. --> - <bind key="g" modifiers="Ctrl,Alt" action="org.inkscape.typography.newglyphlayer" display="true"/> - <bind key="G" modifiers="Ctrl,Alt" action="org.inkscape.typography.newglyphlayer"/> + <bind key="g" modifiers="Ctrl,Alt" action="org.inkscape.typography.newglyphlayer" display="true"/> + <bind key="G" modifiers="Ctrl,Alt" action="org.inkscape.typography.newglyphlayer"/> <!-- - <bind key="h" modifiers="Ctrl,Alt" action="org.inkscape.typography.previousglyphlayer" display="true"/> - <bind key="H" modifiers="Ctrl,Alt" action="org.inkscape.typography.previousglyphlayer"/> + <bind key="h" modifiers="Ctrl,Alt" action="org.inkscape.typography.previousglyphlayer" display="true"/> + <bind key="H" modifiers="Ctrl,Alt" action="org.inkscape.typography.previousglyphlayer"/> --> - <bind key="j" modifiers="Ctrl,Alt" action="org.inkscape.typography.nextglyphlayer" display="true"/> - <bind key="J" modifiers="Ctrl,Alt" action="org.inkscape.typography.nextglyphlayer"/> + <bind key="j" modifiers="Ctrl,Alt" action="org.inkscape.typography.nextglyphlayer" display="true"/> + <bind key="J" modifiers="Ctrl,Alt" action="org.inkscape.typography.nextglyphlayer"/> </keys> diff --git a/src/desktop.cpp b/src/desktop.cpp index c7a763ae1..d74e8fdad 100644 --- a/src/desktop.cpp +++ b/src/desktop.cpp @@ -1582,6 +1582,13 @@ SPDesktop::setDocument (SPDocument *doc) _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this)); _layer_hierarchy->setTop(doc->getRoot()); + // remove old EventLog if it exists (see also: bug #1071082) + if (event_log) { + doc->removeUndoObserver(*event_log); + delete event_log; + event_log = 0; + } + /* setup EventLog */ event_log = new Inkscape::EventLog(doc); doc->addUndoObserver(*event_log); diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp index 0c8ac9681..2bfd71713 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,126 @@ 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<Geom::Coord>(fabs(_scale[Geom::X]*expansion[Geom::X]),1),std::min<Geom::Coord>(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 (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<int>(floor(orgwidth*scaleExpansionSmall[Geom::X])+1); + int newheight = static_cast<int>(floor(orgheight*scaleExpansionSmall[Geom::Y])+1); + std::vector<int> xBegin(newwidth, -1), yBegin(newheight, -1); + std::vector< std::vector<float> > xCoefs(xBegin.size()), yCoefs(yBegin.size()); + for(int x=0; x<orgwidth; x++) { + double coordBegin = x*static_cast<double>(scaleExpansionSmall[Geom::X]); // x-coord in target coordinates where the current source pixel begins + double coordEnd = (x+1)*static_cast<double>(scaleExpansionSmall[Geom::X]); // x-coord in target coordinates where the current source pixel ends + int begin = static_cast<int>(floor(coordBegin)); // First pixel (x-coord) affected by the current source pixel + int end = static_cast<int>(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<end; nx++) { + // Set xBegin if this is the first source pixel contributing to the target pixel. + if (xBegin[nx]==-1) xBegin[nx] = x; + // This computes the fraction of the current target pixel (at nx) that is covered by the source pixel (at x). + xCoefs[nx].push_back(static_cast<float>(std::min<double>(nx+1,coordEnd) - std::max<double>(nx,coordBegin))); + } + } + for(int y=0; y<orgheight; y++) { + double coordBegin = y*static_cast<double>(scaleExpansionSmall[Geom::Y]); // y-coord in target coordinates where the current source pixel begins + double coordEnd = (y+1)*static_cast<double>(scaleExpansionSmall[Geom::Y]); // y-coord in target coordinates where the current source pixel ends + int begin = static_cast<int>(floor(coordBegin)); // First pixel (y-coord) affected by the current source pixel + int end = static_cast<int>(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<end; ny++) { + // Set yBegin if this is the first source pixel contributing to the target pixel. + if (yBegin[ny]==-1) yBegin[ny] = y; + // This computes the fraction of the current target pixel (at ny) that is covered by the source pixel (at y). + yCoefs[ny].push_back(static_cast<float>(std::min<double>(ny+1,coordEnd) - std::max<double>(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<newheight; y++) { + for(int x=0; x<newwidth; x++) { + float tempSum[4] = {0,0,0,0}; + for(int oy=0; oy<static_cast<int>(yCoefs[y].size()); oy++) { + for(int ox=0; ox<static_cast<int>(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<unsigned char>(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(); diff --git a/src/display/drawing-image.h b/src/display/drawing-image.h index 306096d0e..593185c97 100644 --- a/src/display/drawing-image.h +++ b/src/display/drawing-image.h @@ -45,6 +45,9 @@ protected: cairo_surface_t *_surface; SPStyle *_style; + cairo_surface_t *_new_surface; // Part of hack around Cairo bug + Geom::Point _rescaledSize; // Part of hack around Cairo bug + // TODO: the following three should probably be merged into a new Geom::Viewbox object Geom::Rect _clipbox; ///< for preserveAspectRatio Geom::Point _origin; diff --git a/src/event-log.cpp b/src/event-log.cpp index e2528ce0b..9304f68c5 100644 --- a/src/event-log.cpp +++ b/src/event-log.cpp @@ -38,7 +38,18 @@ EventLog::EventLog(SPDocument* document) : curr_row[_columns.type] = SP_VERB_FILE_NEW; } -EventLog::~EventLog() { } +EventLog::~EventLog() { + // avoid crash by clearing entries here (see bug #1071082) + if (_connected) { + (*_callback_connections)[CALLB_SELECTION_CHANGE].block(); + (*_callback_connections)[CALLB_EXPAND].block(); + + _event_list_store->clear(); + + (*_callback_connections)[CALLB_EXPAND].block(false); + (*_callback_connections)[CALLB_SELECTION_CHANGE].block(false); + } +} void EventLog::notifyUndoEvent(Event* log) |
