X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fdisplay%2Fnr-filter.cpp;h=5ff9d2da5ea5366090d46344c44f4ae72de7912d;hb=a4030d5ca449e7e384bc699cd249ee704faaeab0;hp=8930a74dfb5171b09996d3101078def85e4d5d92;hpb=90a3966dd44e306d23febc15ebd65cde07d7a4dd;p=inkscape.git diff --git a/src/display/nr-filter.cpp b/src/display/nr-filter.cpp index 8930a74df..5ff9d2da5 100644 --- a/src/display/nr-filter.cpp +++ b/src/display/nr-filter.cpp @@ -6,7 +6,7 @@ * Author: * Niko Kiirala * - * Copyright (C) 2006,2007 Niko Kiirala + * Copyright (C) 2006-2008 Niko Kiirala * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -39,19 +39,44 @@ #include "display/nr-filter-tile.h" #include "display/nr-filter-turbulence.h" +#include "display/nr-arena.h" #include "display/nr-arena-item.h" #include "libnr/nr-pixblock.h" #include "libnr/nr-blit.h" -#include "libnr/nr-matrix.h" -#include "libnr/nr-scale.h" +#include <2geom/matrix.h> +#include <2geom/rect.h> #include "svg/svg-length.h" #include "sp-filter-units.h" +#include "preferences.h" + #if defined (SOLARIS) && (SOLARIS == 8) #include "round.h" using Inkscape::round; -#endif +#endif + +namespace Inkscape { +namespace Filters { + +using Geom::X; +using Geom::Y; -namespace NR { +static Geom::OptRect get_item_bbox(NRArenaItem const *item) { + Geom::Rect item_bbox; + if (item->item_bbox) { + item_bbox = *(item->item_bbox); + } else { + // Bounding box might not exist, so create a dummy one. + Geom::Point zero(0, 0); + item_bbox = Geom::Rect(zero, zero); + } + if (item_bbox.min()[X] > item_bbox.max()[X] + || item_bbox.min()[Y] > item_bbox.max()[Y]) + { + // In case of negative-size bbox, return an empty OptRect + return Geom::OptRect(); + } + return Geom::OptRect(item_bbox); +} Filter::Filter() { @@ -67,9 +92,9 @@ Filter::Filter() Filter::Filter(int n) { _primitive_count = 0; - _primitive_table_size = n; - _primitive = new FilterPrimitive*[n]; - for ( int i = 0 ; i < n ; i++ ) { + _primitive_table_size = (n > 0) ? n : 1; // we guarantee there is at least 1(one) filter slot + _primitive = new FilterPrimitive*[_primitive_table_size]; + for ( int i = 0 ; i < _primitive_table_size ; i++ ) { _primitive[i] = NULL; } _common_init(); @@ -108,62 +133,49 @@ int Filter::render(NRArenaItem const *item, NRPixBlock *pb) { if (!_primitive[0]) { // TODO: Should clear the input buffer instead of just returning - return 1; + return 1; } - Matrix trans = item->ctm; + FilterQuality const filterquality = (FilterQuality)item->arena->filterquality; + int const blurquality = item->arena->blurquality; + + Geom::Matrix trans = item->ctm; FilterSlot slot(_slot_count, item); + slot.set_quality(filterquality); + slot.set_blurquality(blurquality); Geom::Rect item_bbox; - if (item->item_bbox) { - item_bbox = *(item->item_bbox); - } else { - // Bounding box might not exist, so create a dummy one. - Geom::Point zero(0, 0); - item_bbox = Geom::Rect(zero, zero); - } - if (item_bbox.min()[X] > item_bbox.max()[X] - || item_bbox.min()[Y] > item_bbox.max()[Y]) { - // Code below assumes non-negative size. - return 1; + Geom::OptRect maybe_bbox = get_item_bbox(item); + if (maybe_bbox.isEmpty()) { + // Code below needs a bounding box + return 1; + } + item_bbox = *maybe_bbox; } Geom::Rect filter_area = filter_effect_area(item_bbox); - if (item_bbox.isEmpty()) { + if (item_bbox.hasZeroArea()) { // It's no use to try and filter an empty object. return 1; } - + FilterUnits units(_filter_units, _primitive_units); units.set_ctm(trans); - units.set_item_bbox(from_2geom(item_bbox)); - units.set_filter_area(from_2geom(filter_area)); + units.set_item_bbox(item_bbox); + units.set_filter_area(filter_area); // TODO: with filterRes of 0x0 should return an empty image + std::pair resolution + = _filter_resolution(filter_area, trans, filterquality); + if(!(resolution.first > 0 && resolution.second > 0)) + return 1; + units.set_resolution(resolution.first, resolution.second); if (_x_pixels > 0) { - double y_len; - if (_y_pixels > 0) { - y_len = _y_pixels; - } else { - y_len = (_x_pixels * (filter_area.max()[Y] - filter_area.min()[Y])) - / (filter_area.max()[X] - filter_area.min()[X]); - } units.set_automatic_resolution(false); - units.set_resolution(_x_pixels, y_len); - } else { - Point origo = filter_area.min(); - origo *= trans; - Point max_i(filter_area.max()[X], filter_area.min()[Y]); - max_i *= trans; - Point max_j(filter_area.min()[X], filter_area.max()[Y]); - max_j *= trans; - double i_len = sqrt((origo[X] - max_i[X]) * (origo[X] - max_i[X]) - + (origo[Y] - max_i[Y]) * (origo[Y] - max_i[Y])); - double j_len = sqrt((origo[X] - max_j[X]) * (origo[X] - max_j[X]) - + (origo[Y] - max_j[Y]) * (origo[Y] - max_j[Y])); + } + else { units.set_automatic_resolution(true); - units.set_resolution(i_len, j_len); } units.set_paraller(false); @@ -180,7 +192,7 @@ int Filter::render(NRArenaItem const *item, NRPixBlock *pb) nr_pixblock_setup_fast(in, pb->mode, pb->area.x0, pb->area.y0, pb->area.x1, pb->area.y1, true); if (in->size != NR_PIXBLOCK_SIZE_TINY && in->data.px == NULL) { - g_warning("NR::Filter::render: failed to reserve temporary buffer"); + g_warning("Inkscape::Filters::Filter::render: failed to reserve temporary buffer"); return 0; } nr_blit_pixblock_pixblock(in, pb); @@ -191,7 +203,7 @@ int Filter::render(NRArenaItem const *item, NRPixBlock *pb) in = slot.get(NR_FILTER_SOURCEGRAPHIC); if (in->area.x1 - in->area.x0 <= 0 || in->area.y1 - in->area.y0 <= 0) { if (in->area.x1 - in->area.x0 < 0 || in->area.y1 - in->area.y0 < 0) { - g_warning("NR::Filter::render: negative area! (%d, %d) (%d, %d)", + g_warning("Inkscape::Filters::Filter::render: negative area! (%d, %d) (%d, %d)", in->area.x0, in->area.y0, in->area.x1, in->area.y1); } return 0; @@ -211,10 +223,49 @@ int Filter::render(NRArenaItem const *item, NRPixBlock *pb) return 0; } -void Filter::area_enlarge(NRRectL &bbox, Matrix const &m) { +void Filter::set_filter_units(SPFilterUnits unit) { + _filter_units = unit; +} + +void Filter::set_primitive_units(SPFilterUnits unit) { + _primitive_units = unit; +} + +void Filter::area_enlarge(NRRectL &bbox, NRArenaItem const *item) const { for (int i = 0 ; i < _primitive_count ; i++) { - if (_primitive[i]) _primitive[i]->area_enlarge(bbox, m); + if (_primitive[i]) _primitive[i]->area_enlarge(bbox, item->ctm); + } +/* + TODO: something. See images at the bottom of filters.svg with medium-low + filtering quality. + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + FilterQuality const filterquality = (FilterQuality)prefs->getInt("/options/filterquality/value"); + + if (_x_pixels <= 0 && (filterquality == FILTER_QUALITY_BEST || + filterquality == FILTER_QUALITY_BETTER)) { + return; + } + + Geom::Rect item_bbox; + Geom::OptRect maybe_bbox = get_item_bbox(item); + if (maybe_bbox.isEmpty()) { + // Code below needs a bounding box + return; } + item_bbox = *maybe_bbox; + + std::pair res_low + = _filter_resolution(item_bbox, item->ctm, filterquality); + //std::pair res_full + // = _filter_resolution(item_bbox, item->ctm, FILTER_QUALITY_BEST); + double pixels_per_block = fmax(item_bbox.width() / res_low.first, + item_bbox.height() / res_low.second); + bbox.x0 -= (int)pixels_per_block; + bbox.x1 += (int)pixels_per_block; + bbox.y0 -= (int)pixels_per_block; + bbox.y1 += (int)pixels_per_block; +*/ } void Filter::bbox_enlarge(NRRectL &bbox) { @@ -230,15 +281,15 @@ void Filter::bbox_enlarge(NRRectL &bbox) { Geom::Rect enlarged = filter_effect_area(tmp_bbox); - bbox.x0 = (ICoord)enlarged.min()[X]; - bbox.y0 = (ICoord)enlarged.min()[Y]; - bbox.x1 = (ICoord)enlarged.max()[X]; - bbox.y1 = (ICoord)enlarged.max()[Y]; + bbox.x0 = (NR::ICoord)enlarged.min()[X]; + bbox.y0 = (NR::ICoord)enlarged.min()[Y]; + bbox.x1 = (NR::ICoord)enlarged.max()[X]; + bbox.y1 = (NR::ICoord)enlarged.max()[Y]; } Geom::Rect Filter::filter_effect_area(Geom::Rect const &bbox) { - Point minp, maxp; + Geom::Point minp, maxp; double len_x = bbox.max()[X] - bbox.min()[X]; double len_y = bbox.max()[Y] - bbox.min()[Y]; /* TODO: fetch somehow the object ex and em lengths */ @@ -275,7 +326,7 @@ Geom::Rect Filter::filter_effect_area(Geom::Rect const &bbox) minp[Y] = _region_y.computed; maxp[Y] = minp[Y] + _region_height.computed; } else { - g_warning("Error in NR::Filter::bbox_enlarge: unrecognized value of _filter_units"); + g_warning("Error in Inkscape::Filters::Filter::bbox_enlarge: unrecognized value of _filter_units"); } Geom::Rect area(minp, maxp); return area; @@ -284,7 +335,7 @@ Geom::Rect Filter::filter_effect_area(Geom::Rect const &bbox) /* Constructor table holds pointers to static methods returning filter * primitives. This table is indexed with FilterPrimitiveType, so that * for example method in _constructor[NR_FILTER_GAUSSIANBLUR] - * returns a filter object of type NR::FilterGaussian. + * returns a filter object of type Inkscape::Filters::FilterGaussian. */ typedef FilterPrimitive*(*FilterConstructor)(); static FilterConstructor _constructor[NR_FILTER_ENDPRIMITIVETYPE]; @@ -329,7 +380,11 @@ void Filter::_enlarge_primitive_table() { for (int i = _primitive_count ; i < _primitive_table_size ; i++) { new_tbl[i] = NULL; } - delete[] _primitive; + if(_primitive != NULL) { + delete[] _primitive; + } else { + g_warning("oh oh"); + } _primitive = new_tbl; } @@ -393,7 +448,7 @@ void Filter::clear_primitives() } void Filter::set_x(SVGLength const &length) -{ +{ if (length._set) _region_x = length; } @@ -408,7 +463,7 @@ void Filter::set_width(SVGLength const &length) _region_width = length; } void Filter::set_height(SVGLength const &length) -{ +{ if (length._set) _region_height = length; } @@ -432,7 +487,72 @@ void Filter::reset_resolution() { _y_pixels = -1; } -} /* namespace NR */ +int Filter::_resolution_limit(FilterQuality const quality) const { + int limit = -1; + switch (quality) { + case FILTER_QUALITY_WORST: + limit = 32; + break; + case FILTER_QUALITY_WORSE: + limit = 64; + break; + case FILTER_QUALITY_NORMAL: + limit = 256; + break; + case FILTER_QUALITY_BETTER: + case FILTER_QUALITY_BEST: + default: + break; + } + return limit; +} + +std::pair Filter::_filter_resolution( + Geom::Rect const &area, Geom::Matrix const &trans, + FilterQuality const filterquality) const +{ + std::pair resolution; + if (_x_pixels > 0) { + double y_len; + if (_y_pixels > 0) { + y_len = _y_pixels; + } else { + y_len = (_x_pixels * (area.max()[Y] - area.min()[Y])) + / (area.max()[X] - area.min()[X]); + } + resolution.first = _x_pixels; + resolution.second = y_len; + } else { + Geom::Point origo = area.min(); + origo *= trans; + Geom::Point max_i(area.max()[X], area.min()[Y]); + max_i *= trans; + Geom::Point max_j(area.min()[X], area.max()[Y]); + max_j *= trans; + double i_len = sqrt((origo[X] - max_i[X]) * (origo[X] - max_i[X]) + + (origo[Y] - max_i[Y]) * (origo[Y] - max_i[Y])); + double j_len = sqrt((origo[X] - max_j[X]) * (origo[X] - max_j[X]) + + (origo[Y] - max_j[Y]) * (origo[Y] - max_j[Y])); + int limit = _resolution_limit(filterquality); + if (limit > 0 && (i_len > limit || j_len > limit)) { + double aspect_ratio = i_len / j_len; + if (i_len > j_len) { + i_len = limit; + j_len = i_len / aspect_ratio; + } + else { + j_len = limit; + i_len = j_len * aspect_ratio; + } + } + resolution.first = i_len; + resolution.second = j_len; + } + return resolution; +} + +} /* namespace Filters */ +} /* namespace Inkscape */ /* Local Variables: @@ -443,4 +563,4 @@ void Filter::reset_resolution() { fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :