nr-filter.cpp revision 3a3365c39fe3932d005ae1aa4324ef48606f5e18
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free * SVG filters rendering
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free * Niko Kiirala <niko@kiirala.com>
94bd7adc978850d91de1a92a8b5c05d01162a74cJohan B. C. Engelen * Copyright (C) 2006-2008 Niko Kiirala
94bd7adc978850d91de1a92a8b5c05d01162a74cJohan B. C. Engelen * Released under GNU GPL, read the file 'COPYING' for more information
94bd7adc978850d91de1a92a8b5c05d01162a74cJohan B. C. Engelen#include "display/nr-filter-convolve-matrix.h"
94bd7adc978850d91de1a92a8b5c05d01162a74cJohan B. C. Engelen#include "display/nr-filter-colormatrix.h"
94bd7adc978850d91de1a92a8b5c05d01162a74cJohan B. C. Engelen#include "display/nr-filter-component-transfer.h"
94bd7adc978850d91de1a92a8b5c05d01162a74cJohan B. C. Engelen#include "display/nr-filter-diffuselighting.h"
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free#include "display/nr-filter-displacement-map.h"
94bd7adc978850d91de1a92a8b5c05d01162a74cJohan B. C. Engelen#include "display/nr-filter-morphology.h"
94bd7adc978850d91de1a92a8b5c05d01162a74cJohan B. C. Engelen#include "display/nr-filter-specularlighting.h"
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free // Having "not set" here as value means the output of last filter
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free // primitive will be used as output of this filter
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free // These are the default values for filter region,
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free // as specified in SVG standard
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free // NB: SVGLength.set takes prescaled percent values: -.10 means -10%
94bd7adc978850d91de1a92a8b5c05d01162a74cJohan B. C. Engelen _region_width.set(SVGLength::PERCENT, 1.20, 0);
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free _region_height.set(SVGLength::PERCENT, 1.20, 0);
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free // Filter resolution, negative value here stands for "automatic"
94bd7adc978850d91de1a92a8b5c05d01162a74cJohan B. C. Engelen _filter_units = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free _primitive_units = SP_FILTER_UNITS_USERSPACEONUSE;
94bd7adc978850d91de1a92a8b5c05d01162a74cJohan B. C. Engelenint Filter::render(Inkscape::DrawingItem const *item, DrawingContext &bgct, DrawingContext &graphic)
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free // when no primitives are defined, clear source graphic
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free FilterQuality const filterquality = (FilterQuality)item->drawing().filterQuality();
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free int const blurquality = item->drawing().blurQuality();
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free Geom::OptRect maybe_bbox = item->itemBounds();
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free // Code below needs a bounding box
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free // It's no use to try and filter an empty object.
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free Geom::Rect filter_area = filter_effect_area(item_bbox);
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free FilterUnits units(_filter_units, _primitive_units);
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free = _filter_resolution(filter_area, trans, filterquality);
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free if (!(resolution.first > 0 && resolution.second > 0)) {
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free // zero resolution - clear source graphic and return
94bd7adc978850d91de1a92a8b5c05d01162a74cJohan B. C. Engelen graphic.setOperator(CAIRO_OPERATOR_SOURCE);
94bd7adc978850d91de1a92a8b5c05d01162a74cJohan B. C. Engelen graphic.setOperator(CAIRO_OPERATOR_OVER);
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free units.set_resolution(resolution.first, resolution.second);
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free Geom::Affine pbtrans = units.get_matrix_display2pb();
94bd7adc978850d91de1a92a8b5c05d01162a74cJohan B. C. Engelen for (unsigned i = 0 ; i < _primitive.size() ; i++) {
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free if (!_primitive[i]->can_handle_affine(pbtrans)) {
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free FilterSlot slot(const_cast<Inkscape::DrawingItem*>(item), bgct, graphic, units);
81efba162b1fc851b6bf0a5ca33928c37effa779apenner for (unsigned i = 0 ; i < _primitive.size() ; i++) {
81efba162b1fc851b6bf0a5ca33928c37effa779apenner Geom::Point origin = graphic.targetLogicalBounds().min();
81efba162b1fc851b6bf0a5ca33928c37effa779apenner cairo_surface_t *result = slot.get_result(_output_slot);
81efba162b1fc851b6bf0a5ca33928c37effa779apenner graphic.setSource(result, origin[Geom::X], origin[Geom::Y]);
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-freevoid Filter::set_primitive_units(SPFilterUnits unit) {
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-freevoid Filter::area_enlarge(Geom::IntRect &bbox, Inkscape::DrawingItem const *item) const {
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free for (unsigned i = 0 ; i < _primitive.size() ; i++) {
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free if (_primitive[i]) _primitive[i]->area_enlarge(b, item->ctm());
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free TODO: something. See images at the bottom of filters.svg with medium-low
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free filtering quality.
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free Inkscape::Preferences *prefs = Inkscape::Preferences::get();
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free FilterQuality const filterquality = (FilterQuality)prefs->getInt("/options/filterquality/value");
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free if (_x_pixels <= 0 && (filterquality == FILTER_QUALITY_BEST ||
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free filterquality == FILTER_QUALITY_BETTER)) {
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free Geom::Rect item_bbox;
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free Geom::OptRect maybe_bbox = item->itemBounds();
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free if (maybe_bbox.isEmpty()) {
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free // Code below needs a bounding box
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free item_bbox = *maybe_bbox;
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free std::pair<double,double> res_low
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free = _filter_resolution(item_bbox, item->ctm(), filterquality);
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free //std::pair<double,double> res_full
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free // = _filter_resolution(item_bbox, item->ctm(), FILTER_QUALITY_BEST);
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free double pixels_per_block = fmax(item_bbox.width() / res_low.first,
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free bbox.x0 -= (int)pixels_per_block;
81efba162b1fc851b6bf0a5ca33928c37effa779apenner bbox.x1 += (int)pixels_per_block;
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free bbox.y0 -= (int)pixels_per_block;
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free bbox.y1 += (int)pixels_per_block;
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-freeGeom::IntRect Filter::compute_drawbox(Inkscape::DrawingItem const *item, Geom::Rect const &item_bbox) {
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free Geom::Rect enlarged = filter_effect_area(item_bbox);
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-freeGeom::Rect Filter::filter_effect_area(Geom::Rect const &bbox)
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free /* TODO: fetch somehow the object ex and em lengths */
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free if (_filter_units == SP_FILTER_UNITS_OBJECTBOUNDINGBOX) {
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free minp[X] = bbox.min()[X] + _region_x.computed * len_x;
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free if (_region_width.unit == SVGLength::PERCENT) {
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free maxp[X] = minp[X] + _region_width.computed * len_x;
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free minp[Y] = bbox.min()[Y] + _region_y.computed * len_y;
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free if (_region_height.unit == SVGLength::PERCENT) {
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free maxp[Y] = minp[Y] + _region_height.computed * len_y;
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free } else if (_filter_units == SP_FILTER_UNITS_USERSPACEONUSE) {
895229950881e4c5be06fc7999ed0ef9aea62e92tavmjong-free /* TODO: make sure bbox and fe region are in same coordinate system */
g_warning("Error in Inkscape::Filters::Filter::filter_effect_area: unrecognized value of _filter_units");
return area;
static bool created = false;
if(created) return;
created = true;
return handle;
return target;
delete _primitive[i];
if (pixels > 0) {
switch (quality) {
case FILTER_QUALITY_WORST:
case FILTER_QUALITY_WORSE:
case FILTER_QUALITY_NORMAL:
case FILTER_QUALITY_BETTER:
case FILTER_QUALITY_BEST:
return limit;
if (_x_pixels > 0) {
double y_len;
if (_y_pixels > 0) {
return resolution;