From 6a438bf9ac749132f14198330626ef408d62cbeb Mon Sep 17 00:00:00 2001 From: kiirala Date: Wed, 3 Jan 2007 14:12:44 +0000 Subject: [PATCH] Added bitmap transformer to fix blur with rotation and non-uniform scaling --- src/display/Makefile_insert | 4 +- src/display/nr-filter.cpp | 89 ++++++++++++++++++++++++--- src/display/pixblock-transform.cpp | 98 ++++++++++++++++++++++++++++++ src/display/pixblock-transform.h | 34 +++++++++++ 4 files changed, 216 insertions(+), 9 deletions(-) create mode 100644 src/display/pixblock-transform.cpp create mode 100644 src/display/pixblock-transform.h diff --git a/src/display/Makefile_insert b/src/display/Makefile_insert index a6cecaffb..4d51a7891 100644 --- a/src/display/Makefile_insert +++ b/src/display/Makefile_insert @@ -73,7 +73,9 @@ display_libspdisplay_a_SOURCES = \ display/nr-filter-slot.h \ display/nr-filter-types.h \ display/pixblock-scaler.cpp \ - display/pixblock-scaler.h + display/pixblock-scaler.h \ + display/pixblock-transform.cpp \ + display/pixblock-transform.h display_bezier_utils_test_SOURCES = display/bezier-utils-test.cpp display_bezier_utils_test_LDADD = libnr/libnr.a -lglib-2.0 diff --git a/src/display/nr-filter.cpp b/src/display/nr-filter.cpp index 592623c49..f00a1ef6b 100644 --- a/src/display/nr-filter.cpp +++ b/src/display/nr-filter.cpp @@ -12,6 +12,7 @@ */ #include +#include #include "display/nr-filter.h" #include "display/nr-filter-primitive.h" @@ -19,6 +20,7 @@ #include "display/nr-filter-slot.h" #include "display/nr-filter-types.h" #include "display/pixblock-scaler.h" +#include "display/pixblock-transform.h" #include "display/nr-arena-item.h" #include "libnr/nr-pixblock.h" @@ -30,6 +32,26 @@ //#include "display/nr-arena-shape.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); +} + namespace NR { Filter::Filter() @@ -90,12 +112,61 @@ int Filter::render(NRArenaItem const *item, NRPixBlock *pb) } Matrix trans = *item->ctm; + Matrix paraller_trans = trans; + bool notparaller = false; FilterSlot slot(_slot_count, item); NRPixBlock *in = new NRPixBlock; - // First, if filter resolution is not set to automatic, we should - // scale the input image to correct resolution - if (_x_pixels >= 0) { + // If filter effects region is not paraller to viewport, + // we must first undo the rotation / shear. + // It will be redone after filtering. + // If there is only rotation and uniform scaling (zoom), let's skip this, + // as it will not make a difference with gaussian blur. + if ((fabs(trans[1]) > 1e-6 || fabs(trans[2]) > 1e-6) && + !(fabs(trans[0] - trans[3]) < 1e-6 && fabs(trans[1] + trans[2]) < 1e-6)) { + notparaller = true; + + // TODO: if filter resolution is specified, scaling should be set + // according to that + double scaling_factor = sqrt(trans.expansionX() * trans.expansionX() + + trans.expansionY() * trans.expansionY()); + scale scaling(scaling_factor, scaling_factor); + scale scaling_inv(1.0 / scaling_factor, 1.0 / scaling_factor); + trans *= scaling_inv; + paraller_trans.set_identity(); + paraller_trans *= scaling; + + Matrix itrans = trans.inverse(); + int x0 = pb->area.x0; + int y0 = pb->area.y0; + int x1 = pb->area.x1; + int y1 = pb->area.y1; + int min_x = _min4(itrans[0] * x0 + itrans[2] * y0 + itrans[4], + itrans[0] * x0 + itrans[2] * y1 + itrans[4], + itrans[0] * x1 + itrans[2] * y0 + itrans[4], + itrans[0] * x1 + itrans[2] * y1 + itrans[4]); + int max_x = _max4(itrans[0] * x0 + itrans[2] * y0 + itrans[4], + itrans[0] * x0 + itrans[2] * y1 + itrans[4], + itrans[0] * x1 + itrans[2] * y0 + itrans[4], + itrans[0] * x1 + itrans[2] * y1 + itrans[4]); + int min_y = _min4(itrans[1] * x0 + itrans[3] * y0 + itrans[5], + itrans[1] * x0 + itrans[3] * y1 + itrans[5], + itrans[1] * x1 + itrans[3] * y0 + itrans[5], + itrans[1] * x1 + itrans[3] * y1 + itrans[5]); + int max_y = _max4(itrans[1] * x0 + itrans[3] * y0 + itrans[5], + itrans[1] * x0 + itrans[3] * y1 + itrans[5], + itrans[1] * x1 + itrans[3] * y0 + itrans[5], + itrans[1] * x1 + itrans[3] * y1 + itrans[5]); + + nr_pixblock_setup_fast(in, pb->mode, + min_x, min_y, + max_x, max_y, true); + if (in->data.px == NULL) // memory allocation failed + return 0; + transform_nearest(in, pb, itrans); + } else if (_x_pixels >= 0) { + // If filter resolution is not set to automatic, we should + // scale the input image to correct resolution /* If filter resolution is zero, the object should not be rendered */ if (_x_pixels == 0 || _y_pixels == 0) { int size = (pb->area.x1 - pb->area.x0) @@ -125,7 +196,7 @@ int Filter::render(NRArenaItem const *item, NRPixBlock *pb) scale_bicubic(in, pb); scale res_scaling(x_len / (double)(pb->area.x1 - pb->area.x0), y_len / (double)(pb->area.y1 - pb->area.y0)); - trans *= res_scaling; + paraller_trans *= res_scaling; } else { // If filter resolution is automatic, just make copy of input image nr_pixblock_setup_fast(in, pb->mode, @@ -139,7 +210,7 @@ int Filter::render(NRArenaItem const *item, NRPixBlock *pb) in = NULL; // in is now handled by FilterSlot, we should not touch it // TODO: loop through ALL the primitives and render them one at a time - _primitive[0]->render(slot, trans); + _primitive[0]->render(slot, paraller_trans); NRPixBlock *out = slot.get(_output_slot); // Clear the pixblock, where the output will be put @@ -149,9 +220,11 @@ int Filter::render(NRArenaItem const *item, NRPixBlock *pb) * NR_PIXBLOCK_BPP(pb); memset(NR_PIXBLOCK_PX(pb), 0, size); - // If the filter resolution is automatic, just copy our final image - // to output pixblock, otherwise use bicubic scaling - if (_x_pixels < 0) { + if (notparaller) { + transform_nearest(pb, out, trans); + } else if (_x_pixels < 0) { + // If the filter resolution is automatic, just copy our final image + // to output pixblock, otherwise use bicubic scaling nr_blit_pixblock_pixblock(pb, out); } else { scale_bicubic(pb, out); diff --git a/src/display/pixblock-transform.cpp b/src/display/pixblock-transform.cpp new file mode 100644 index 000000000..1a691b5a8 --- /dev/null +++ b/src/display/pixblock-transform.cpp @@ -0,0 +1,98 @@ +#define __NR_PIXBLOCK_SCALER_CPP__ + +/* + * Functions for blitting pixblocks using matrix transformation + * + * Author: + * Niko Kiirala + * + * Copyright (C) 2006 Niko Kiirala + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#include +using std::floor; + +#include "libnr/nr-pixblock.h" +#include "libnr/nr-matrix.h" + +namespace NR { + +struct RGBA { + int r, g, b, a; +}; + +/** + * Sanity check function for indexing pixblocks. + * Catches reading and writing outside the pixblock area. + * When enabled, decreases filter rendering speed massively. + */ +inline void _check_index(NRPixBlock const * const pb, int const location, int const line) +{ + if(true) { + int max_loc = pb->rs * (pb->area.y1 - pb->area.y0); + if (location < 0 || (location + 4) >= max_loc) + g_warning("Location %d out of bounds (0 ... %d) at line %d", location, max_loc, line); + } +} + +void transform_nearest(NRPixBlock *to, NRPixBlock *from, Matrix &trans) +{ + if (NR_PIXBLOCK_BPP(from) != 4 || NR_PIXBLOCK_BPP(to) != 4) { + g_warning("A non-32-bpp image passed to transform_nearest: scaling aborted."); + return; + } + + // Precalculate sizes of source and destination pixblocks + int from_width = from->area.x1 - from->area.x0; + int from_height = from->area.y1 - from->area.y0; + int to_width = to->area.x1 - to->area.x0; + int to_height = to->area.y1 - to->area.y0; + + Matrix itrans = trans.inverse(); + + // Loop through every pixel of destination image, a line at a time + for (int to_y = 0 ; to_y < to_height ; to_y++) { + for (int to_x = 0 ; to_x < to_width ; to_x++) { + RGBA result = {0,0,0,0}; + + int from_x = (int)round(itrans[0] * (to_x + to->area.x0) + + itrans[2] * (to_y + to->area.y0) + + itrans[4]); + from_x -= from->area.x0; + int from_y = (int)round(itrans[1] * (to_x + to->area.x0) + + itrans[3] * (to_y + to->area.y0) + + itrans[5]); + from_y -= from->area.y0; + + if (from_x >= 0 && from_x < from_width + && from_y >= 0 && from_y < from_height) { + _check_index(from, from_y * from->rs + from_x * 4, __LINE__); + result.r = NR_PIXBLOCK_PX(from)[from_y * from->rs + from_x * 4]; + result.g = NR_PIXBLOCK_PX(from)[from_y * from->rs + from_x * 4 + 1]; + result.b = NR_PIXBLOCK_PX(from)[from_y * from->rs + from_x * 4 + 2]; + result.a = NR_PIXBLOCK_PX(from)[from_y * from->rs + from_x * 4 + 3]; + } + + _check_index(to, to_y * to->rs + to_x * 4, __LINE__); + NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4] = result.r; + NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4 + 1] = result.g; + NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4 + 2] = result.b; + NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4 + 3] = result.a; + } + } +} + +} /* namespace NR */ +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/display/pixblock-transform.h b/src/display/pixblock-transform.h new file mode 100644 index 000000000..6ade028ba --- /dev/null +++ b/src/display/pixblock-transform.h @@ -0,0 +1,34 @@ +#ifndef __NR_PIXBLOCK_TRANSFORM_H__ +#define __NR_PIXBLOCK_TRANSFORM_H__ + +/* + * Functions for blitting pixblocks using matrix transfomation + * + * Author: + * Niko Kiirala + * + * Copyright (C) 2006 Niko Kiirala + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "libnr/nr-pixblock.h" +#include "libnr/nr-matrix.h" + +namespace NR { + +void transform_nearest(NRPixBlock *to, NRPixBlock *from, Matrix &trans); + +} /* namespace NR */ + +#endif // __NR_PIXBLOCK_TRANSFORM_H__ +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : -- 2.39.5