NRPixBlock *FilterSlot::get(int slot_nr)
{
    int index = _get_index(slot_nr);
    assert(index >= 0);

    /* If we didn't have the specified image, but we could create it
     * from the other information we have, let's do that */
    if (_slot[index] == NULL
        && (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))
    {
        /* If needed, fetch background */
        if (slot_nr == NR_FILTER_BACKGROUNDIMAGE) {
            NRPixBlock *pb;
            pb = nr_arena_item_get_background(_arena_item);
            if (pb) {
                pb->empty = false;
                this->set(NR_FILTER_BACKGROUNDIMAGE, pb);
            } else {
                NRPixBlock *source = this->get(NR_FILTER_SOURCEGRAPHIC);
                pb = new NRPixBlock();
                if (!pb) return NULL; // Allocation failed
                nr_pixblock_setup_fast(pb, source->mode,
                                       source->area.x0, source->area.y0,
                                       source->area.x1, source->area.y1, true);
                if (pb->size != NR_PIXBLOCK_SIZE_TINY && pb->data.px == NULL) {
                    // allocation failed
                    delete pb;
                    return NULL;
                }
                pb->empty = FALSE;
                this->set(NR_FILTER_BACKGROUNDIMAGE, pb);
            }
        } else if (slot_nr == NR_FILTER_SOURCEALPHA) {
            /* If only a alpha channel is needed, strip it from full image */
            NRPixBlock *src = get(NR_FILTER_SOURCEGRAPHIC);
            NRPixBlock *sa = filter_get_alpha(src);
            set(NR_FILTER_SOURCEALPHA, sa);
        } else if (slot_nr == NR_FILTER_BACKGROUNDALPHA) {
            NRPixBlock *src = get(NR_FILTER_BACKGROUNDIMAGE);
            NRPixBlock *ba = filter_get_alpha(src);
            set(NR_FILTER_BACKGROUNDALPHA, ba);
        } else if (slot_nr == NR_FILTER_FILLPAINT) {
            /* When a paint is needed, fetch it from arena item */
            // TODO
        } else if (slot_nr == NR_FILTER_STROKEPAINT) {
            // TODO
        }
    }

    if (_slot[index]) {
        _slot[index]->empty = false;
    }

    assert(slot_nr == NR_FILTER_SLOT_NOT_SET ||_slot_number[index] == slot_nr);
    return _slot[index];
}
void
nr_gdk_draw_rgba32_solid (GdkDrawable *drawable, GdkGC *gc, gint x, gint y, gint w, gint h, guint32 rgba)
{
	NRPixBlock pb;

	nr_pixblock_setup_fast (&pb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, w, h, FALSE);

	nr_render_rgba32_rgb (NR_PIXBLOCK_PX (&pb), w, h, pb.rs, x, y, rgba);
	gdk_draw_rgb_image (drawable, gc, x, y, w, h, GDK_RGB_DITHER_MAX, NR_PIXBLOCK_PX (&pb), pb.rs);

	nr_pixblock_release (&pb);
}
void
nr_gdk_draw_gray_garbage (GdkDrawable *drawable, GdkGC *gc, gint x, gint y, gint w, gint h)
{
	gint xx, yy;

	for (yy = y; yy < y + h; yy += 64) {
		for (xx = x; xx < x + w; xx += 64) {
			NRPixBlock pb;
			gint ex, ey;
			ex = MIN (xx + 64, x + w);
			ey = MIN (yy + 64, y + h);
			nr_pixblock_setup_fast (&pb, NR_PIXBLOCK_MODE_R8G8B8, xx, yy, ex, ey, FALSE);
			nr_pixblock_render_gray_noise (&pb, NULL);
			gdk_draw_rgb_image (drawable, gc, xx, yy, ex - xx, ey - yy, GDK_RGB_DITHER_NONE, NR_PIXBLOCK_PX (&pb), pb.rs);
			nr_pixblock_release (&pb);
		}
	}
}
NRPixBlock *filter_get_alpha(NRPixBlock *src)
{
    NRPixBlock *dst = new NRPixBlock;
    nr_pixblock_setup_fast(dst, NR_PIXBLOCK_MODE_R8G8B8A8P,
                           src->area.x0, src->area.y0,
                           src->area.x1, src->area.y1, false);
    if (!dst || (dst->size != NR_PIXBLOCK_SIZE_TINY && dst->data.px == NULL)) {
        g_warning("Memory allocation failed in filter_get_alpha");
        delete dst;
        return NULL;
    }
    nr_blit_pixblock_pixblock(dst, src);

    unsigned char *data = NR_PIXBLOCK_PX(dst);
    int end = dst->rs * (dst->area.y1 - dst->area.y0);
    for (int i = 0 ; i < end ; i += 4) {
        data[i + 0] = 0;
        data[i + 1] = 0;
        data[i + 2] = 0;
    }
    dst->empty = false;

    return dst;
}
void
nr_pixblock_draw_line_rgba32 (NRPixBlock *d, long x0, long y0, long x1, long y1, short /*first*/, unsigned long rgba)
{
	long deltax, deltay, xinc1, xinc2, yinc1, yinc2;
	long den, num, numadd, numpixels;
	long x, y, curpixel;
	/* Pixblock */
	int dbpp;
	NRPixBlock spb;
	unsigned char *spx;

	if (x1 >= x0) {
		deltax = x1 - x0;
		xinc1 = 1;
		xinc2 = 1;
	} else {
		deltax = x0 - x1;
		xinc1 = -1;
		xinc2 = -1;
	}

	if (y1 >= y0) {
		deltay = y1 - y0;
		yinc1 = 1;
		yinc2 = 1;
	} else {
		deltay = y0 - y1;
		yinc1 = -1;
		yinc2 = -1;
	}

	if (deltax >= deltay) {
		xinc1 = 0;
		yinc2 = 0;
		den = deltax;
		num = deltax / 2;
		numadd = deltay;
		numpixels = deltax;
	} else {
		xinc2 = 0;
		yinc1 = 0;
		den = deltay;
		num = deltay / 2;
		numadd = deltax;
		numpixels = deltay;
	}

	/* We can be quite sure 1x1 pixblock is TINY */
	nr_pixblock_setup_fast (&spb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, 1, 1, 0);
	spb.empty = 0;
	spx = NR_PIXBLOCK_PX (&spb);
	spx[0] = NR_RGBA32_R (rgba);
	spx[1] = NR_RGBA32_G (rgba);
	spx[2] = NR_RGBA32_B (rgba);
	spx[3] = NR_RGBA32_A (rgba);

	dbpp = NR_PIXBLOCK_BPP (d);

	x = x0;
	y = y0;

	for (curpixel = 0; curpixel <= numpixels; curpixel++) {
		if ((x >= d->area.x0) && (y >= d->area.y0) && (x < d->area.x1) && (y < d->area.y1)) {
			nr_compose_pixblock_pixblock_pixel (d, NR_PIXBLOCK_PX (d) + (y - d->area.y0) * d->rs + (x - d->area.x0) * dbpp, &spb, spx);
		}
		num += numadd;
		if (num >= den) {
			num -= den;
			x += xinc1;
			y += yinc1;
		}
		x += xinc2;
		y += yinc2;
	}

	nr_pixblock_release (&spb);
}
int FilterConvolveMatrix::render(FilterSlot &slot, FilterUnits const &/*units*/) {
    NRPixBlock *in = slot.get(_input);
    if (!in) {
        g_warning("Missing source image for feConvolveMatrix (in=%d)", _input);
        return 1;
    }
    if (orderX<=0 || orderY<=0) {
        g_warning("Empty kernel!");
        return 1;
    }
    if (targetX<0 || targetX>=orderX || targetY<0 || targetY>=orderY) {
        g_warning("Invalid target!");
        return 1;
    }
    if (kernelMatrix.size()!=(unsigned int)(orderX*orderY)) {
        g_warning("kernelMatrix does not have orderX*orderY elements!");
        return 1;
    }

    if (bias!=0) {
        g_warning("It is unknown whether Inkscape's implementation of bias in feConvolveMatrix is correct!");
        // The SVG specification implies that feConvolveMatrix is defined for premultiplied colors (which makes sense).
        // It also says that bias should simply be added to the result for each color (without taking the alpha into account)
        // However, it also says that one purpose of bias is "to have .5 gray value be the zero response of the filter".
        // It seems sensible to indeed support the latter behaviour instead of the former, but this does appear to go against the standard.
        // Note that Batik simply does not support bias!=0
    }
    if (edgeMode!=CONVOLVEMATRIX_EDGEMODE_NONE) {
        g_warning("Inkscape only supports edgeMode=\"none\" (and a filter uses a different one)!");
        // Note that to properly support edgeMode the interaction with area_enlarge should be well understood (and probably something needs to change)
        // area_enlarge should NOT let Inkscape enlarge the area beyond the filter area, it should only enlarge the rendered area if a part of the object is rendered to make it overlapping (enough) with adjacent parts.
    }

    NRPixBlock *out = new NRPixBlock;

    nr_pixblock_setup_fast(out, in->mode,
                           in->area.x0, in->area.y0, in->area.x1, in->area.y1,
                           true);

    unsigned char *in_data = NR_PIXBLOCK_PX(in);
    unsigned char *out_data = NR_PIXBLOCK_PX(out);

    unsigned int const width = in->area.x1 - in->area.x0;
    unsigned int const height = in->area.y1 - in->area.y0;

    // Set up predivided kernel matrix
    std::vector<double> kernel(kernelMatrix);
    for(size_t i=0; i<kernel.size(); i++) {
        kernel[i] /= divisor; // The code that creates this object makes sure that divisor != 0
    }

    if (in->mode==NR_PIXBLOCK_MODE_R8G8B8A8P) {
        if (preserveAlpha) {
            convolve2D<true,true>(out_data, in_data, width, height, &kernel.front(), orderX, orderY, targetX, targetY, bias);
        } else {
            convolve2D<true,false>(out_data, in_data, width, height, &kernel.front(), orderX, orderY, targetX, targetY, bias);
        }
    } else {
        if (preserveAlpha) {
            convolve2D<false,true>(out_data, in_data, width, height, &kernel.front(), orderX, orderY, targetX, targetY, bias);
        } else {
            convolve2D<false,false>(out_data, in_data, width, height, &kernel.front(), orderX, orderY, targetX, targetY, bias);
        }
    }

    out->empty = FALSE;
    slot.set(_output, out);
    return 0;
}
Exemple #7
0
static void
sp_canvas_arena_render (SPCanvasItem *item, SPCanvasBuf *buf)
{
	SPCanvasArena *arena;
	gint bw, bh, sw, sh;
	gint x, y;

	arena = SP_CANVAS_ARENA (item);

	nr_arena_item_invoke_update (arena->root, NULL, &arena->gc,
				     NR_ARENA_ITEM_STATE_BBOX | NR_ARENA_ITEM_STATE_RENDER,
				     NR_ARENA_ITEM_STATE_NONE);

	if (buf->is_bg) {
		sp_canvas_clear_buffer (buf);
		buf->is_bg = FALSE;
		buf->is_buf = TRUE;
	}

	bw = buf->rect.x1 - buf->rect.x0;
	bh = buf->rect.y1 - buf->rect.y0;
	if ((bw < 1) || (bh < 1)) return;

	/* 65536 is max cached buffer and we need 4 channels */
	if (bw * bh < 16384) {
		/* We can go with single buffer */
		sw = bw;
		sh = bh;
	} else if (bw <= 2048) {
		/* Go with row buffer */
		sw = bw;
		sh = 16384 / bw;
	} else if (bh <= 2048) {
		/* Go with column buffer */
		sw = 16384 / bh;
		sh = bh;
	} else {
		sw = 128;
		sh = 128;
	}

/* fixme: RGB transformed bitmap blit is not implemented (Lauris) */
/* And even if it would be, unless it uses MMX there is little reason to go RGB */
#define STRICT_RGBA

	for (y = buf->rect.y0; y < buf->rect.y1; y += sh) {
		for (x = buf->rect.x0; x < buf->rect.x1; x += sw) {
			NRRectL area;
#ifdef STRICT_RGBA
			NRPixBlock pb;
#endif
			NRPixBlock cb;

			area.x0 = x;
			area.y0 = y;
			area.x1 = MIN (x + sw, buf->rect.x1);
			area.y1 = MIN (y + sh, buf->rect.y1);

#ifdef STRICT_RGBA
			nr_pixblock_setup_fast (&pb, NR_PIXBLOCK_MODE_R8G8B8A8P, area.x0, area.y0, area.x1, area.y1, TRUE);
			/* fixme: */
			pb.empty = FALSE;
#endif

			nr_pixblock_setup_extern (&cb, NR_PIXBLOCK_MODE_R8G8B8, area.x0, area.y0, area.x1, area.y1,
						  buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + 3 * (x - buf->rect.x0),
						  buf->buf_rowstride,
						  FALSE, FALSE);

#ifdef STRICT_RGBA
			nr_arena_item_invoke_render (arena->root, &area, &pb, 0);
			nr_blit_pixblock_pixblock (&cb, &pb);
			nr_pixblock_release (&pb);
#else
			nr_arena_item_invoke_render (arena->root, &area, &cb, 0);
#endif

			nr_pixblock_release (&cb);
		}
	}
}
void FilterSlot::set(int slot_nr, NRPixBlock *pb)
{
    /* 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);
            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;
}
static gint sp_dropper_context_root_handler(SPEventContext *event_context, GdkEvent *event)
{
    SPDropperContext *dc = (SPDropperContext *) event_context;
    int ret = FALSE;
    SPDesktop *desktop = event_context->desktop;
    Inkscape::Preferences *prefs = Inkscape::Preferences::get();

    int pick = prefs->getInt("/tools/dropper/pick", SP_DROPPER_PICK_VISIBLE);
    bool setalpha = prefs->getBool("/tools/dropper/setalpha", true);

    switch (event->type) {
	case GDK_BUTTON_PRESS:
            if (event->button.button == 1 && !event_context->space_panning) {
                dc->centre = Geom::Point(event->button.x, event->button.y);
                dc->dragging = TRUE;
                ret = TRUE;
            }
            break;
	case GDK_MOTION_NOTIFY:
            if (event->motion.state & GDK_BUTTON2_MASK) {
                // pass on middle-drag
                ret = FALSE;
                break;
            } else if (!event_context->space_panning) {
                // otherwise, constantly calculate color no matter is any button pressed or not

                double rw = 0.0;
                double W(0), R(0), G(0), B(0), A(0);

                if (dc->dragging) {
                    // calculate average

                    // radius
                    rw = std::min(Geom::L2(Geom::Point(event->button.x, event->button.y) - dc->centre), 400.0);

                    if (rw == 0) { // happens sometimes, little idea why...
                        break;
                    }

                    Geom::Point const cd = desktop->w2d(dc->centre);
                    Geom::Matrix const w2dt = desktop->w2d();
                    const double scale = rw * w2dt.descrim();
                    Geom::Matrix const sm( Geom::Scale(scale, scale) * Geom::Translate(cd) );
                    sp_canvas_item_affine_absolute(dc->area, sm);
                    sp_canvas_item_show(dc->area);

                    /* Get buffer */
                    const int x0 = (int) floor(dc->centre[Geom::X] - rw);
                    const int y0 = (int) floor(dc->centre[Geom::Y] - rw);
                    const int x1 = (int) ceil(dc->centre[Geom::X] + rw);
                    const int y1 = (int) ceil(dc->centre[Geom::Y] + rw);

                    if ((x1 > x0) && (y1 > y0)) {
                        NRPixBlock pb;
                        nr_pixblock_setup_fast(&pb, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1, TRUE);
                        /* fixme: (Lauris) */
                        sp_canvas_arena_render_pixblock(SP_CANVAS_ARENA(sp_desktop_drawing(desktop)), &pb);
                        for (int y = y0; y < y1; y++) {
                            const unsigned char *s = NR_PIXBLOCK_PX(&pb) + (y - y0) * pb.rs;
                            for (int x = x0; x < x1; x++) {
                                const double dx = x - dc->centre[Geom::X];
                                const double dy = y - dc->centre[Geom::Y];
                                const double w = exp(-((dx * dx) + (dy * dy)) / (rw * rw));
                                W += w;
                                R += w * s[0];
                                G += w * s[1];
                                B += w * s[2];
                                A += w * s[3];
                                s += 4;
                            }
                        }
                        nr_pixblock_release(&pb);

                        R = (R + 0.001) / (255.0 * W);
                        G = (G + 0.001) / (255.0 * W);
                        B = (B + 0.001) / (255.0 * W);
                        A = (A + 0.001) / (255.0 * W);

                        R = CLAMP(R, 0.0, 1.0);
                        G = CLAMP(G, 0.0, 1.0);
                        B = CLAMP(B, 0.0, 1.0);
                        A = CLAMP(A, 0.0, 1.0);
                    }

                } else {
                    // pick single pixel
                    NRPixBlock pb;
                    int x = (int) floor(event->button.x);
                    int y = (int) floor(event->button.y);
                    nr_pixblock_setup_fast(&pb, NR_PIXBLOCK_MODE_R8G8B8A8P, x, y, x+1, y+1, TRUE);
                    sp_canvas_arena_render_pixblock(SP_CANVAS_ARENA(sp_desktop_drawing(desktop)), &pb);
                    const unsigned char *s = NR_PIXBLOCK_PX(&pb);

                    R = s[0] / 255.0;
                    G = s[1] / 255.0;
                    B = s[2] / 255.0;
                    A = s[3] / 255.0;
                }

                if (pick == SP_DROPPER_PICK_VISIBLE) {
                    // compose with page color
                    guint32 bg = sp_desktop_namedview(desktop)->pagecolor;
                    R = R + (SP_RGBA32_R_F(bg)) * (1 - A);
                    G = G + (SP_RGBA32_G_F(bg)) * (1 - A);
                    B = B + (SP_RGBA32_B_F(bg)) * (1 - A);
                    A = 1.0;
                } else {
                    // un-premultiply color channels
                    if (A > 0) {
                        R /= A;
                        G /= A;
                        B /= A;
                    }
                }

                if (fabs(A) < 1e-4) {
                    A = 0; // suppress exponentials, CSS does not allow that
                }

                // remember color
                dc->R = R;
                dc->G = G;
                dc->B = B;
                dc->alpha = A;

                // status message
                double alpha_to_set = setalpha? dc->alpha : 1.0;
                guint32 c32 = SP_RGBA32_F_COMPOSE(R, G, B, alpha_to_set);

                gchar c[64];
                sp_svg_write_color(c, sizeof(c), c32);

                // alpha of color under cursor, to show in the statusbar
                // locale-sensitive printf is OK, since this goes to the UI, not into SVG
                gchar *alpha = g_strdup_printf(_(" alpha %.3g"), alpha_to_set);
                // where the color is picked, to show in the statusbar
                gchar *where = dc->dragging ? g_strdup_printf(_(", averaged with radius %d"), (int) rw) : g_strdup_printf(_(" under cursor"));
                // message, to show in the statusbar
                const gchar *message = dc->dragging ? _("<b>Release mouse</b> to set color.") : _("<b>Click</b> to set fill, <b>Shift+click</b> to set stroke; <b>drag</b> to average color in area; with <b>Alt</b> to pick inverse color; <b>Ctrl+C</b> to copy the color under mouse to clipboard");
                event_context->defaultMessageContext()->setF(
                    Inkscape::NORMAL_MESSAGE,
                    "<b>%s%s</b>%s. %s", c,
                    (pick == SP_DROPPER_PICK_VISIBLE)? "" : alpha,
                    where, message
                    );

                g_free(where);
                g_free(alpha);

                ret = TRUE;
            }
            break;
	case GDK_BUTTON_RELEASE:
            if (event->button.button == 1 && !event_context->space_panning)
            {
                sp_canvas_item_hide(dc->area);
                dc->dragging = FALSE;

                double alpha_to_set = setalpha? dc->alpha : 1.0;

                // do the actual color setting
                sp_desktop_set_color(desktop,
                                     (event->button.state & GDK_MOD1_MASK)?
                                     ColorRGBA(1 - dc->R, 1 - dc->G, 1 - dc->B, alpha_to_set) : ColorRGBA(dc->R, dc->G, dc->B, alpha_to_set),
                                     false,  !(event->button.state & GDK_SHIFT_MASK));

                // REJON: set aux. toolbar input to hex color!


                if (!(sp_desktop_selection(desktop)->isEmpty())) {
                    sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_DROPPER, 
                                     _("Set picked color"));
                }

                ret = TRUE;
            }
            break;
	case GDK_KEY_PRESS:
            switch (get_group0_keyval(&event->key)) {
		case GDK_Up:
		case GDK_Down:
		case GDK_KP_Up:
		case GDK_KP_Down:
                    // prevent the zoom field from activation
                    if (!MOD__CTRL_ONLY) {
                        ret = TRUE;
                    }
                    break;
		case GDK_Escape:
                    sp_desktop_selection(desktop)->clear();
		default:
                    break;
            }
            break;
	default:
            break;
    }

    if (!ret) {
        if (((SPEventContextClass *) parent_class)->root_handler) {
            ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event);
        }
    }

    return ret;
}
int FilterSpecularLighting::render(FilterSlot &slot, FilterUnits const &units) {
    NRPixBlock *in = slot.get(_input);
    if (!in) {
        g_warning("Missing source image for feSpecularLighting (in=%d)", _input);
        return 1;
    }

    NRPixBlock *out = new NRPixBlock;

    //Fvector *L = NULL; //vector to the light

    int w = in->area.x1 - in->area.x0;
    int h = in->area.y1 - in->area.y0;
    int x0 = in->area.x0;
    int y0 = in->area.y0;
    int i, j;
    //As long as FilterRes and kernel unit is not supported we hardcode the
    //default value
    int dx = 1; //TODO setup
    int dy = 1; //TODO setup
    //surface scale
    Geom::Matrix trans = units.get_matrix_primitiveunits2pb();
    gdouble ss = surfaceScale * trans[0];
    gdouble ks = specularConstant; //diffuse lighting constant
    NR::Fvector L, N, LC, H;
    gdouble inter;

    nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8N,
            in->area.x0, in->area.y0, in->area.x1, in->area.y1,
            true);
    unsigned char *data_i = NR_PIXBLOCK_PX (in);
    unsigned char *data_o = NR_PIXBLOCK_PX (out);
    //No light, nothing to do
    switch (light_type) {
        case DISTANT_LIGHT:
            //the light vector is constant
            {
            DistantLight *dl = new DistantLight(light.distant, lighting_color);
            dl->light_vector(L);
            dl->light_components(LC);
            NR::normalized_sum(H, L, NR::EYE_VECTOR);
            //finish the work
            for (i = 0, j = 0; i < w*h; i++) {
                NR::compute_surface_normal(N, ss, in, i / w, i % w, dx, dy);
                COMPUTE_INTER(inter, N, H, ks, specularExponent);

                data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]); // CLAMP includes rounding!
                data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]);
                data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]);
                data_o[j] = MAX(MAX(data_o[j-3], data_o[j-2]), data_o[j-1]);
                ++j;
            }
            out->empty = FALSE;
            delete dl;
            }
            break;
        case POINT_LIGHT:
            {
            PointLight *pl = new PointLight(light.point, lighting_color, trans);
            pl->light_components(LC);
        //TODO we need a reference to the filter to determine primitiveUnits
        //if objectBoundingBox is used, use a different matrix for light_vector
        // UPDATE: trans is now correct matrix from primitiveUnits to
        // pixblock coordinates
            //finish the work
            for (i = 0, j = 0; i < w*h; i++) {
                NR::compute_surface_normal(N, ss, in, i / w, i % w, dx, dy);
                pl->light_vector(L,
                        i % w + x0,
                        i / w + y0,
                        ss * (double) data_i[4*i+3]/ 255);
                NR::normalized_sum(H, L, NR::EYE_VECTOR);
                COMPUTE_INTER(inter, N, H, ks, specularExponent);

                data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]);
                data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]);
                data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]);
                data_o[j] = MAX(MAX(data_o[j-3], data_o[j-2]), data_o[j-1]);
                ++j;
            }
            out->empty = FALSE;
            delete pl;
            }
            break;
        case SPOT_LIGHT:
            {
            SpotLight *sl = new SpotLight(light.spot, lighting_color, trans);
        //TODO we need a reference to the filter to determine primitiveUnits
        //if objectBoundingBox is used, use a different matrix for light_vector
        // UPDATE: trans is now correct matrix from primitiveUnits to
        // pixblock coordinates
            //finish the work
            for (i = 0, j = 0; i < w*h; i++) {
                NR::compute_surface_normal(N, ss, in, i / w, i % w, dx, dy);
                sl->light_vector(L,
                    i % w + x0,
                    i / w + y0,
                    ss * (double) data_i[4*i+3]/ 255);
                sl->light_components(LC, L);
                NR::normalized_sum(H, L, NR::EYE_VECTOR);
                COMPUTE_INTER(inter, N, H, ks, specularExponent);

                data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]);
                data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]);
                data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]);
                data_o[j] = MAX(MAX(data_o[j-3], data_o[j-2]), data_o[j-1]);
                ++j;
            }
            out->empty = FALSE;
            delete sl;
            }
            break;
        //else unknown light source, doing nothing
        case NO_LIGHT:
        default:
            {
            if (light_type != NO_LIGHT)
                g_warning("unknown light source %d", light_type);
            out->empty = false;
            }
    }

    //finishing
    slot.set(_output, out);
    //nr_pixblock_release(in);
    //delete in;
    return 0;
}