summaryrefslogtreecommitdiffstats
path: root/src/display
diff options
context:
space:
mode:
authorKrzysztof Kosi??ski <tweenk.pl@gmail.com>2010-07-30 00:16:28 +0000
committerKrzysztof KosiƄski <tweenk.pl@gmail.com>2010-07-30 00:16:28 +0000
commitc9a6247b9ac5db8e7c4e8c0f3689f60594324f6f (patch)
tree86b3b20ddd995eae3eb2a0aa6526b30f79ebcc3c /src/display
parentCruft removal (diff)
downloadinkscape-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.cpp85
-rw-r--r--src/display/nr-filter-slot.cpp226
-rw-r--r--src/display/nr-filter-slot.h15
-rw-r--r--src/display/nr-filter-units.h12
-rw-r--r--src/display/nr-filter.cpp9
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.