/* * feComposite filter effect renderer * * Authors: * Niko Kiirala <niko@kiirala.com> * * Copyright (C) 2007 authors * * Released under GNU GPL, read the file 'COPYING' for more information */ #include <cmath> #include "2geom/isnan.h" #include "filters/composite.h" #include "display/nr-filter-composite.h" #include "display/nr-filter-pixops.h" #include "display/nr-filter-slot.h" #include "display/nr-filter-units.h" #include "display/nr-filter-utils.h" #include "libnr/nr-blit.h" #include "libnr/nr-pixblock.h" #include "libnr/nr-pixops.h" inline void composite_over(unsigned char *r, unsigned char const *a, unsigned char const *b) { r[0] = NR_COMPOSEPPP_1111(a[0],a[3],b[0]); r[1] = NR_COMPOSEPPP_1111(a[1],a[3],b[1]); r[2] = NR_COMPOSEPPP_1111(a[2],a[3],b[2]); r[3] = NR_COMPOSEPPP_1111(a[3],a[3],b[3]); } inline void composite_in(unsigned char *r, unsigned char const *a, unsigned char const *b) { r[0] = NR_NORMALIZE_21(a[0] * b[3]); r[1] = NR_NORMALIZE_21(a[1] * b[3]); r[2] = NR_NORMALIZE_21(a[2] * b[3]); r[3] = NR_NORMALIZE_21(a[3] * b[3]); } inline void composite_out(unsigned char *r, unsigned char const *a, unsigned char const *b) { r[0] = NR_NORMALIZE_21(a[0] * (255 - b[3])); r[1] = NR_NORMALIZE_21(a[1] * (255 - b[3])); r[2] = NR_NORMALIZE_21(a[2] * (255 - b[3])); r[3] = NR_NORMALIZE_21(a[3] * (255 - b[3])); } inline void composite_atop(unsigned char *r, unsigned char const *a, unsigned char const *b) { r[0] = NR_NORMALIZE_21(a[0] * b[3] + b[0] * (255 - a[3])); r[1] = NR_NORMALIZE_21(a[1] * b[3] + b[1] * (255 - a[3])); r[2] = NR_NORMALIZE_21(a[2] * b[3] + b[2] * (255 - a[3])); r[3] = b[3]; } inline void composite_xor(unsigned char *r, unsigned char const *a, unsigned char const *b) { r[0] = NR_NORMALIZE_21(a[0] * (255 - b[3]) + b[0] * (255 - a[3])); r[1] = NR_NORMALIZE_21(a[1] * (255 - b[3]) + b[1] * (255 - a[3])); r[2] = NR_NORMALIZE_21(a[2] * (255 - b[3]) + b[2] * (255 - a[3])); r[3] = NR_NORMALIZE_21(a[3] * (255 - b[3]) + b[3] * (255 - a[3])); } // BUGBUG / TODO // This makes arithmetic compositing non re-entrant and non thread safe. static int arith_k1, arith_k2, arith_k3, arith_k4; inline void composite_arithmetic(unsigned char *r, unsigned char const *a, unsigned char const *b) { using Inkscape::Filters::clamp3; r[0] = NR_NORMALIZE_31(clamp3(arith_k1 * a[0] * b[0] + arith_k2 * a[0] + arith_k3 * b[0] + arith_k4)); r[1] = NR_NORMALIZE_31(clamp3(arith_k1 * a[1] * b[1] + arith_k2 * a[1] + arith_k3 * b[1] + arith_k4)); r[2] = NR_NORMALIZE_31(clamp3(arith_k1 * a[2] * b[2] + arith_k2 * a[2] + arith_k3 * b[2] + arith_k4)); r[3] = NR_NORMALIZE_31(clamp3(arith_k1 * a[3] * b[3] + arith_k2 * a[3] + arith_k3 * b[3] + arith_k4)); } namespace Inkscape { namespace Filters { FilterComposite::FilterComposite() : op(COMPOSITE_DEFAULT), k1(0), k2(0), k3(0), k4(0), _input2(Inkscape::Filters::NR_FILTER_SLOT_NOT_SET) {} FilterPrimitive * FilterComposite::create() { return new FilterComposite(); } FilterComposite::~FilterComposite() {} int FilterComposite::render(FilterSlot &slot, FilterUnits const &/*units*/) { NRPixBlock *in1 = slot.get(_input); NRPixBlock *in2 = slot.get(_input2); NRPixBlock *original_in1 = in1; NRPixBlock *original_in2 = in2; NRPixBlock *out; // Bail out if either one of source images is missing if (!in1 || !in2) { g_warning("Missing source image for feComposite (in=%d in2=%d)", _input, _input2); return 1; } out = new NRPixBlock; NRRectL out_area; nr_rect_l_union(&out_area, &in1->area, &in2->area); nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8P, out_area.x0, out_area.y0, out_area.x1, out_area.y1, true); // Blending modes are defined for premultiplied RGBA values, // thus convert them to that format before blending if (in1->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) { in1 = nr_pixblock_new_fast(NR_PIXBLOCK_MODE_R8G8B8A8P, original_in1->area.x0, original_in1->area.y0, original_in1->area.x1, original_in1->area.y1, false); nr_blit_pixblock_pixblock(in1, original_in1); } if (in2->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) { in2 = nr_pixblock_new_fast(NR_PIXBLOCK_MODE_R8G8B8A8P, original_in2->area.x0, original_in2->area.y0, original_in2->area.x1, original_in2->area.y1, false); nr_blit_pixblock_pixblock(in2, original_in2); } /* pixops_mix is defined in display/nr-filter-pixops.h * It mixes the two input images with the function given as template * and places the result in output image. */ switch (op) { case COMPOSITE_IN: pixops_mix<composite_in>(*out, *in1, *in2); break; case COMPOSITE_OUT: pixops_mix<composite_out>(*out, *in1, *in2); break; case COMPOSITE_ATOP: pixops_mix<composite_atop>(*out, *in1, *in2); break; case COMPOSITE_XOR: pixops_mix<composite_xor>(*out, *in1, *in2); break; case COMPOSITE_ARITHMETIC: arith_k1 = (int)round(k1 * 255); arith_k2 = (int)round(k2 * 255 * 255); arith_k3 = (int)round(k3 * 255 * 255); arith_k4 = (int)round(k4 * 255 * 255 * 255); pixops_mix<composite_arithmetic>(*out, *in1, *in2); break; case COMPOSITE_DEFAULT: case COMPOSITE_OVER: default: pixops_mix<composite_over>(*out, *in1, *in2); break; } if (in1 != original_in1) { nr_pixblock_free(in1); } if (in2 != original_in2) { nr_pixblock_free(in2); } out->empty = FALSE; slot.set(_output, out); return 0; } void FilterComposite::set_input(int input) { _input = input; } void FilterComposite::set_input(int input, int slot) { if (input == 0) _input = slot; if (input == 1) _input2 = slot; } void FilterComposite::set_operator(FeCompositeOperator op) { if (op == COMPOSITE_DEFAULT) { this->op = COMPOSITE_OVER; } else if (op == COMPOSITE_OVER || op == COMPOSITE_IN || op == COMPOSITE_OUT || op == COMPOSITE_ATOP || op == COMPOSITE_XOR || op == COMPOSITE_ARITHMETIC) { this->op = op; } } void FilterComposite::set_arithmetic(double k1, double k2, double k3, double k4) { if (!IS_FINITE(k1) || !IS_FINITE(k2) || !IS_FINITE(k3) || !IS_FINITE(k4)) { g_warning("Non-finite parameter for feComposite arithmetic operator"); return; } this->k1 = k1; this->k2 = k2; this->k3 = k3; this->k4 = k4; } } /* namespace Filters */ } /* namespace Inkscape */ /* 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 :