diff options
| author | Krzysztof Kosi??ski <tweenk.pl@gmail.com> | 2010-07-30 00:16:28 +0000 |
|---|---|---|
| committer | Krzysztof KosiĆski <tweenk.pl@gmail.com> | 2010-07-30 00:16:28 +0000 |
| commit | c9a6247b9ac5db8e7c4e8c0f3689f60594324f6f (patch) | |
| tree | 86b3b20ddd995eae3eb2a0aa6526b30f79ebcc3c /src/display | |
| parent | Cruft removal (diff) | |
| download | inkscape-c9a6247b9ac5db8e7c4e8c0f3689f60594324f6f.tar.gz inkscape-c9a6247b9ac5db8e7c4e8c0f3689f60594324f6f.zip | |
Second half of image filter, probably not 100% correct
(bzr r9508.1.40)
Diffstat (limited to 'src/display')
| -rw-r--r-- | src/display/nr-filter-image.cpp | 85 | ||||
| -rw-r--r-- | src/display/nr-filter-slot.cpp | 226 | ||||
| -rw-r--r-- | src/display/nr-filter-slot.h | 15 | ||||
| -rw-r--r-- | src/display/nr-filter-units.h | 12 | ||||
| -rw-r--r-- | src/display/nr-filter.cpp | 9 |
5 files changed, 70 insertions, 277 deletions
diff --git a/src/display/nr-filter-image.cpp b/src/display/nr-filter-image.cpp index 72b0e756f..636f31187 100644 --- a/src/display/nr-filter-image.cpp +++ b/src/display/nr-filter-image.cpp @@ -47,15 +47,33 @@ void FilterImage::render_cairo(FilterSlot &slot) //cairo_surface_t *input = slot.getcairo(_input); + Geom::Matrix m = slot.get_units().get_matrix_user2filterunits().inverse(); + Geom::Point bbox_00 = Geom::Point(0,0) * m; + Geom::Point bbox_w0 = Geom::Point(1,0) * m; + Geom::Point bbox_0h = Geom::Point(0,1) * m; + double bbox_width = Geom::distance(bbox_00, bbox_w0); + double bbox_height = Geom::distance(bbox_00, bbox_0h); + + + // feImage is suppose to use the same parameters as a normal SVG image. + // If a width or height is set to zero, the image is not suppose to be displayed. + // This does not seem to be what Firefox or Opera does, nor does the W3C displacement + // filter test expect this behavior. If the width and/or height are zero, we use + // the width and height of the object bounding box. + if( feImageWidth == 0 ) feImageWidth = bbox_width; + if( feImageHeight == 0 ) feImageHeight = bbox_height; + if (from_element) { if (!SVGElem) return; - return; //not ready yet -/* - // prep the document // TODO: do not recreate the rendering tree every time + // TODO: the entire thing is a hack, we should give filter primitives an "update" method + // like the one for NRArenaItems sp_document_ensure_up_to_date(document); NRArena* arena = NRArena::create(); + Geom::OptRect optarea = SVGElem->getBounds(Geom::identity()); + if (!optarea) return; + unsigned const key = sp_item_display_key_new(1); NRArenaItem* ai = sp_item_invoke_show(SVGElem, arena, key, SP_ITEM_SHOW_DISPLAY); if (!ai) { @@ -64,39 +82,42 @@ void FilterImage::render_cairo(FilterSlot &slot) return; } - Geom::OptRect optarea = SVGElem->getBounds(Geom::identity()); - if (!optarea) return; - Geom::Rect area = *optarea; - Geom::Matrix itrans = slot.get_units().get_matrix_display2pb(); + Geom::Matrix pu2pb = slot.get_units().get_matrix_primitiveunits2pb(); - NRRectL const &slot_area = slot.get_slot_area(); - NRRectL rect; - rect.x0 = floor(area->left()); - rect.x1 = ceil(area->right()); - rect.y0 = floor(area->top()); - rect.y1 = ceil(area->bottom()); + double scaleX = feImageWidth / area.width(); + double scaleY = feImageHeight / area.height(); - cairo_surface_t *out = ink_cairo_surface_create_same_size(in, CAIRO_CONTENT_COLOR_ALPHA); + NRRectL const &sa = slot.get_slot_area(); + cairo_surface_t *out = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + sa.x1 - sa.x0, sa.y1 - sa.y0); cairo_t *ct = cairo_create(out); - cairo_translate(ct, -slot_area.x0, -slot_area.y0); - ink_cairo_transform(ct, itrans); - cairo_translate(ct, rect.x0, rect.y0); + cairo_translate(ct, -sa.x0, -sa.y0); + ink_cairo_transform(ct, pu2pb); // we are now in primitive units + cairo_translate(ct, feImageX, feImageY); + cairo_scale(ct, scaleX, scaleY); + + NRRectL render_rect; + render_rect.x0 = floor(area.left()); + render_rect.y0 = floor(area.top()); + render_rect.x1 = ceil(area.right()); + render_rect.y1 = ceil(area.bottom()); + cairo_translate(ct, render_rect.x0, render_rect.y0); // Update to renderable state NRGC gc(NULL); Geom::Matrix t = Geom::identity(); nr_arena_item_set_transform(ai, &t); gc.transform.setIdentity(); - nr_arena_item_invoke_update( ai, NULL, &gc, - NR_ARENA_ITEM_STATE_ALL, - NR_ARENA_ITEM_STATE_NONE ); - - nr_arena_item_invoke_render(ct, ai, &rect, NULL, NR_ARENA_ITEM_RENDER_NO_CACHE); + nr_arena_item_invoke_update(ai, NULL, &gc, + NR_ARENA_ITEM_STATE_ALL, + NR_ARENA_ITEM_STATE_NONE); + nr_arena_item_invoke_render(ct, ai, &render_rect, NULL, NR_ARENA_ITEM_RENDER_NO_CACHE); + nr_object_unref((NRObject*) arena); slot.set(_output, out); cairo_surface_destroy(out); - return;*/ + return; } if (!image && !broken_ref) { @@ -119,18 +140,19 @@ void FilterImage::render_cairo(FilterSlot &slot) if ( !g_file_test( fullname, G_FILE_TEST_EXISTS ) ) { // Should display Broken Image png. g_warning("FilterImage::render: Can not find: %s", feImageHref ); + return; } image = Gdk::Pixbuf::create_from_file(fullname); if( fullname != feImageHref ) g_free( fullname ); } catch (const Glib::FileError & e) { - g_warning("caught Glib::FileError in FilterImage::render %i", e.code() ); + g_warning("caught Glib::FileError in FilterImage::render: %s", e.what().data() ); return; } catch (const Gdk::PixbufError & e) { - g_warning("Gdk::PixbufError in FilterImage::render: %i", e.code() ); + g_warning("Gdk::PixbufError in FilterImage::render: %s", e.what().data() ); return; } if ( !image ) return; @@ -163,18 +185,6 @@ void FilterImage::render_cairo(FilterSlot &slot) ink_cairo_transform(ct, slot.get_units().get_matrix_primitiveunits2pb()); // now ct is in the coordinates of feImageX etc. - // Get the object bounding box in user coordinates. Image is placed with respect to box. - // Array values: 0: width; 3: height; 4: -x; 5: -y. - Geom::Matrix object_bbox = slot.get_units().get_matrix_user2filterunits(); - - // feImage is suppose to use the same parameters as a normal SVG image. - // If a width or height is set to zero, the image is not suppose to be displayed. - // This does not seem to be what Firefox or Opera does, nor does the W3C displacement - // filter test expect this behavior. If the width and/or height are zero, we use - // the width and height of the object bounding box. - if( feImageWidth == 0 ) feImageWidth = object_bbox[0]; - if( feImageHeight == 0 ) feImageHeight = object_bbox[3]; - double scaleX = feImageWidth / image->get_width(); double scaleY = feImageHeight / image->get_height(); @@ -200,6 +210,7 @@ void FilterImage::set_href(const gchar *href){ cairo_surface_destroy(image_surface); } image.reset(); + broken_ref = false; } void FilterImage::set_document(SPDocument *doc){ diff --git a/src/display/nr-filter-slot.cpp b/src/display/nr-filter-slot.cpp index 935751871..6cca0fc77 100644 --- a/src/display/nr-filter-slot.cpp +++ b/src/display/nr-filter-slot.cpp @@ -21,46 +21,6 @@ #include "display/nr-filter-gaussian.h" #include "display/nr-filter-slot.h" #include "display/nr-filter-units.h" -#include "display/pixblock-scaler.h" -#include "display/pixblock-transform.h" -#include "libnr/nr-pixblock.h" -#include "libnr/nr-blit.h" - -__attribute__ ((const)) -inline static int _max4(const double a, const double b, - const double c, const double d) { - double ret = a; - if (b > ret) ret = b; - if (c > ret) ret = c; - if (d > ret) ret = d; - return (int)round(ret); -} - -__attribute__ ((const)) -inline static int _min4(const double a, const double b, - const double c, const double d) { - double ret = a; - if (b < ret) ret = b; - if (c < ret) ret = c; - if (d < ret) ret = d; - return (int)round(ret); -} - -__attribute__ ((const)) -inline static int _max2(const double a, const double b) { - if (a > b) - return (int)round(a); - else - return (int)round(b); -} - -__attribute__ ((const)) -inline static int _min2(const double a, const double b) { - if (a > b) - return (int)round(b); - else - return (int)round(a); -} namespace Inkscape { namespace Filters { @@ -105,9 +65,6 @@ FilterSlot::~FilterSlot() cairo_surface_t *FilterSlot::getcairo(int slot_nr) { - //int index = _get_index(slot_nr); - //assert(index >= 0); - if (slot_nr == NR_FILTER_SLOT_NOT_SET) slot_nr = _last_out; @@ -240,28 +197,6 @@ cairo_surface_t *FilterSlot::get_result(int res) return r; } -/* -void FilterSlot::get_final(int slot_nr, NRPixBlock *result) { - NRPixBlock *final_usr = get(slot_nr); - Geom::Matrix trans = units.get_matrix_pb2display(); - - int size = (result->area.x1 - result->area.x0) - * (result->area.y1 - result->area.y0) - * NR_PIXBLOCK_BPP(result); - memset(NR_PIXBLOCK_PX(result), 0, size); - - if (fabs(trans[1]) > 1e-6 || fabs(trans[2]) > 1e-6) { - if (filterquality == FILTER_QUALITY_BEST) { - NR::transform_bicubic(result, final_usr, trans); - } else { - NR::transform_nearest(result, final_usr, trans); - } - } else if (fabs(trans[0] - 1) > 1e-6 || fabs(trans[3] - 1) > 1e-6) { - NR::scale_bicubic(result, final_usr, trans); - } else { - nr_blit_pixblock_pixblock(result, final_usr); - } -}*/ void FilterSlot::_set_internal(int slot_nr, cairo_surface_t *surface) { @@ -286,172 +221,11 @@ void FilterSlot::set(int slot_nr, cairo_surface_t *surface) _set_internal(slot_nr, surface); _last_out = slot_nr; - -#if 0 - /* Unnamed slot is for saving filter primitive results, when parameter - * 'result' is not set. Only the filter immediately after this one - * can access unnamed results, so we don't have to worry about overwriting - * previous results in filter chain. On the other hand, we may not - * overwrite any other image with this one, because they might be - * accessed later on. */ - int index = ((slot_nr != NR_FILTER_SLOT_NOT_SET) - ? _get_index(slot_nr) - : _get_index(NR_FILTER_UNNAMED_SLOT)); - assert(index >= 0); - // Unnamed slot is only for Inkscape::Filters::FilterSlot internal use. - assert(slot_nr != NR_FILTER_UNNAMED_SLOT); - assert(slot_nr == NR_FILTER_SLOT_NOT_SET ||_slot_number[index] == slot_nr); - - if (slot_nr == NR_FILTER_SOURCEGRAPHIC || slot_nr == NR_FILTER_BACKGROUNDIMAGE) { - Geom::Matrix trans = units.get_matrix_display2pb(); - if (fabs(trans[1]) > 1e-6 || fabs(trans[2]) > 1e-6) { - NRPixBlock *trans_pb = new NRPixBlock; - int x0 = pb->area.x0; - int y0 = pb->area.y0; - int x1 = pb->area.x1; - int y1 = pb->area.y1; - int min_x = _min4(trans[0] * x0 + trans[2] * y0 + trans[4], - trans[0] * x0 + trans[2] * y1 + trans[4], - trans[0] * x1 + trans[2] * y0 + trans[4], - trans[0] * x1 + trans[2] * y1 + trans[4]); - int max_x = _max4(trans[0] * x0 + trans[2] * y0 + trans[4], - trans[0] * x0 + trans[2] * y1 + trans[4], - trans[0] * x1 + trans[2] * y0 + trans[4], - trans[0] * x1 + trans[2] * y1 + trans[4]); - int min_y = _min4(trans[1] * x0 + trans[3] * y0 + trans[5], - trans[1] * x0 + trans[3] * y1 + trans[5], - trans[1] * x1 + trans[3] * y0 + trans[5], - trans[1] * x1 + trans[3] * y1 + trans[5]); - int max_y = _max4(trans[1] * x0 + trans[3] * y0 + trans[5], - trans[1] * x0 + trans[3] * y1 + trans[5], - trans[1] * x1 + trans[3] * y0 + trans[5], - trans[1] * x1 + trans[3] * y1 + trans[5]); - - nr_pixblock_setup_fast(trans_pb, pb->mode, - min_x, min_y, - max_x, max_y, true); - if (trans_pb->size != NR_PIXBLOCK_SIZE_TINY && trans_pb->data.px == NULL) { - /* TODO: this gets hit occasionally. Worst case scenario: - * images are exported in horizontal stripes. One stripe - * is not too high, but can get thousands of pixels wide. - * Rotate this 45 degrees -> _huge_ image */ - g_warning("Memory allocation failed in Inkscape::Filters::FilterSlot::set (transform)"); - return; - } - if (filterquality == FILTER_QUALITY_BEST) { - NR::transform_bicubic(trans_pb, pb, trans); - } else { - NR::transform_nearest(trans_pb, pb, trans); - } - nr_pixblock_release(pb); - delete pb; - pb = trans_pb; - } else if (fabs(trans[0] - 1) > 1e-6 || fabs(trans[3] - 1) > 1e-6) { - NRPixBlock *trans_pb = new NRPixBlock; - - int x0 = pb->area.x0; - int y0 = pb->area.y0; - int x1 = pb->area.x1; - int y1 = pb->area.y1; - int min_x = _min2(trans[0] * x0 + trans[4], - trans[0] * x1 + trans[4]); - int max_x = _max2(trans[0] * x0 + trans[4], - trans[0] * x1 + trans[4]); - int min_y = _min2(trans[3] * y0 + trans[5], - trans[3] * y1 + trans[5]); - int max_y = _max2(trans[3] * y0 + trans[5], - trans[3] * y1 + trans[5]); - - nr_pixblock_setup_fast(trans_pb, pb->mode, - min_x, min_y, max_x, max_y, true); - if (trans_pb->size != NR_PIXBLOCK_SIZE_TINY && trans_pb->data.px == NULL) { - g_warning("Memory allocation failed in Inkscape::Filters::FilterSlot::set (scaling)"); - return; - } - NR::scale_bicubic(trans_pb, pb, trans); - nr_pixblock_release(pb); - delete pb; - pb = trans_pb; - } - } - - if(_slot[index]) { - nr_pixblock_release(_slot[index]); - delete _slot[index]; - } - _slot[index] = pb; - _last_out = index; -#endif } int FilterSlot::get_slot_count() { return _slots.size(); - /* - int seek = _slot_count; - do { - seek--; - } while (!_slot[seek] && _slot_number[seek] == NR_FILTER_SLOT_NOT_SET); - - return seek + 1;*/ -} - -int FilterSlot::_get_index(int slot_nr) -{ -#if 0 - assert(slot_nr >= 0 || - slot_nr == NR_FILTER_SLOT_NOT_SET || - slot_nr == NR_FILTER_SOURCEGRAPHIC || - slot_nr == NR_FILTER_SOURCEALPHA || - slot_nr == NR_FILTER_BACKGROUNDIMAGE || - slot_nr == NR_FILTER_BACKGROUNDALPHA || - slot_nr == NR_FILTER_FILLPAINT || - slot_nr == NR_FILTER_STROKEPAINT || - slot_nr == NR_FILTER_UNNAMED_SLOT); - - int index = -1; - if (slot_nr == NR_FILTER_SLOT_NOT_SET) { - return _last_out; - } - /* Search, if the slot already exists */ - for (int i = 0 ; i < _slot_count ; i++) { - if (_slot_number[i] == slot_nr) { - index = i; - break; - } - } - - /* If the slot doesn't already exist, create it */ - if (index == -1) { - int seek = _slot_count; - do { - seek--; - } while ((seek >= 0) && (_slot_number[seek] == NR_FILTER_SLOT_NOT_SET)); - /* If there is no space for more slots, create more space */ - if (seek == _slot_count - 1) { - NRPixBlock **new_slot = new NRPixBlock*[_slot_count * 2]; - int *new_number = new int[_slot_count * 2]; - for (int i = 0 ; i < _slot_count ; i++) { - new_slot[i] = _slot[i]; - new_number[i] = _slot_number[i]; - } - for (int i = _slot_count ; i < _slot_count * 2 ; i++) { - new_slot[i] = NULL; - new_number[i] = NR_FILTER_SLOT_NOT_SET; - } - delete[] _slot; - delete[] _slot_number; - _slot = new_slot; - _slot_number = new_number; - _slot_count *= 2; - } - /* Now that there is space, create the slot */ - _slot_number[seek + 1] = slot_nr; - index = seek + 1; - } - return index; -#endif - return 0; } void FilterSlot::set_quality(FilterQuality const q) { diff --git a/src/display/nr-filter-slot.h b/src/display/nr-filter-slot.h index 57f3f1054..7b32f1210 100644 --- a/src/display/nr-filter-slot.h +++ b/src/display/nr-filter-slot.h @@ -27,10 +27,7 @@ namespace Filters { class FilterSlot { public: - /** Creates a new FilterSlot object. - * Parameter specifies the surface which should be used - * for background accesses from filters. - */ + /** Creates a new FilterSlot object. */ FilterSlot(NRArenaItem *item, cairo_t *bgct, NRRectL const *bgarea, cairo_surface_t *graphic, NRRectL const *graphicarea, FilterUnits const &u); /** Destroys the FilterSlot object and all its contents */ @@ -42,8 +39,6 @@ public: * NR_FILTER_SOURCEGRAPHIC, NR_FILTER_SOURCEALPHA, * NR_FILTER_BACKGROUNDIMAGE, NR_FILTER_BACKGROUNDALPHA, * NR_FILTER_FILLPAINT, NR_FILTER_SOURCEPAINT. - * If the defined filter slot is not set before, this function - * returns NULL. Also, that filter slot is created in process. */ cairo_surface_t *getcairo(int slot); NRPixBlock *get(int slot) { return NULL; } @@ -51,10 +46,6 @@ public: /** Sets or re-sets the pixblock associated with given slot. * If there was a pixblock already assigned with this slot, * that pixblock is destroyed. - * Pixblocks passed to this function should be considered - * managed by this FilterSlot object. - * Pixblocks passed to this function should be reserved with - * c++ -style new-operator. */ void set(int slot, cairo_surface_t *s); @@ -104,10 +95,6 @@ private: cairo_surface_t *_get_fill_paint(); cairo_surface_t *_get_stroke_paint(); - /** Returns the table index of given slot. If that slot does not exist, - * it is created. Table index can be used to read the correct - * pixblock from _slot */ - int _get_index(int slot); void _set_internal(int slot, cairo_surface_t *s); }; diff --git a/src/display/nr-filter-units.h b/src/display/nr-filter-units.h index d8489b42e..dcf7e5838 100644 --- a/src/display/nr-filter-units.h +++ b/src/display/nr-filter-units.h @@ -22,6 +22,18 @@ namespace Inkscape { namespace Filters { +/* Notes: + * - "filter units" is a coordinate system where the filter region is contained + * between (0,0) and (1,1). Do not confuse this with the filterUnits property + * - "primitive units" is the coordinate system in which all lengths and distances + * in the filter definition should be interpreted. They are affected by the value + * of the primitiveUnits attribute + * - "pb" is the coordinate system in which filter rendering happens. + * It might be aligned with user or screen coordinates depending on + * the filter primitives used in the filter. + * - "display" are world coordinates of the canvas - pixel grid coordinates + * of the drawing area translated so that (0,0) corresponds to the document origin + */ class FilterUnits { public: FilterUnits(); diff --git a/src/display/nr-filter.cpp b/src/display/nr-filter.cpp index 667a3cc14..8c638415d 100644 --- a/src/display/nr-filter.cpp +++ b/src/display/nr-filter.cpp @@ -269,9 +269,18 @@ void Filter::set_primitive_units(SPFilterUnits unit) { } void Filter::area_enlarge(NRRectL &bbox, NRArenaItem const *item) const { + NRRectL bbox_orig = bbox; for (int i = 0 ; i < _primitive_count ; i++) { if (_primitive[i]) _primitive[i]->area_enlarge(bbox, item->ctm); } + + // HACK: due to some roundoff issue that I can't find at this time, + // some per-pixel filters show seams when rotated. + if (bbox_orig.x0 >= bbox.x0) bbox.x0 = bbox_orig.x0 - 1; + if (bbox_orig.y0 >= bbox.y0) bbox.y0 = bbox_orig.y0 - 1; + if (bbox_orig.x1 <= bbox.x1) bbox.x1 = bbox_orig.x1 + 1; + if (bbox_orig.y1 <= bbox.y1) bbox.y1 = bbox_orig.y1 + 1; + /* TODO: something. See images at the bottom of filters.svg with medium-low filtering quality. |
