nr-arena-image.cpp revision 11b2f376ecd9f48b2d862df617d252aa6c6a654f
10139N/A#define __NR_ARENA_IMAGE_C__
10139N/A
13666N/A/*
10139N/A * RGBA display list system for inkscape
10139N/A *
10139N/A * Author:
10139N/A * Lauris Kaplinski <lauris@kaplinski.com>
10139N/A *
10139N/A * Copyright (C) 2001-2002 Lauris Kaplinski
10139N/A * Copyright (C) 2001 Ximian, Inc.
10139N/A *
10139N/A * Released under GNU GPL, read the file 'COPYING' for more information
10139N/A */
10139N/A
10139N/A#include <libnr/nr-compose-transform.h>
10139N/A#include <2geom/transforms.h>
10139N/A#include <libnr/nr-blit.h>
10139N/A#include "../preferences.h"
10139N/A#include "nr-arena-image.h"
10139N/A#include "style.h"
10139N/A#include "display/inkscape-cairo.h"
10139N/A#include "display/nr-arena.h"
10139N/A#include "display/nr-filter.h"
10139N/A#include "display/nr-filter-gaussian.h"
10139N/A#include "sp-filter.h"
10139N/A#include "sp-filter-reference.h"
10139N/A#include "sp-gaussian-blur.h"
10139N/A#include "filters/blend.h"
10139N/A#include "display/nr-filter-blend.h"
10139N/A
10139N/Aint nr_arena_image_x_sample = 1;
10139N/Aint nr_arena_image_y_sample = 1;
10139N/A
10139N/A/*
10139N/A * NRArenaCanvasImage
10139N/A *
10139N/A */
10139N/A
10139N/Astatic void nr_arena_image_class_init (NRArenaImageClass *klass);
10139N/Astatic void nr_arena_image_init (NRArenaImage *image);
10139N/Astatic void nr_arena_image_finalize (NRObject *object);
10139N/A
10139N/Astatic unsigned int nr_arena_image_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset);
10139N/Astatic unsigned int nr_arena_image_render (cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags);
10139N/Astatic NRArenaItem *nr_arena_image_pick (NRArenaItem *item, Geom::Point p, double delta, unsigned int sticky);
10139N/A
10139N/Astatic NRArenaItemClass *parent_class;
10139N/A
10139N/ANRType
10139N/Anr_arena_image_get_type (void)
10139N/A{
10139N/A static NRType type = 0;
10139N/A if (!type) {
10139N/A type = nr_object_register_type (NR_TYPE_ARENA_ITEM,
10139N/A "NRArenaImage",
10139N/A sizeof (NRArenaImageClass),
10139N/A sizeof (NRArenaImage),
10139N/A (void (*) (NRObjectClass *)) nr_arena_image_class_init,
10139N/A (void (*) (NRObject *)) nr_arena_image_init);
10139N/A }
10139N/A return type;
10139N/A}
10139N/A
10139N/Astatic void
10139N/Anr_arena_image_class_init (NRArenaImageClass *klass)
10139N/A{
10139N/A NRObjectClass *object_class;
10139N/A NRArenaItemClass *item_class;
10139N/A
10139N/A object_class = (NRObjectClass *) klass;
10139N/A item_class = (NRArenaItemClass *) klass;
10139N/A
10139N/A parent_class = (NRArenaItemClass *) ((NRObjectClass *) klass)->parent;
10139N/A
10139N/A object_class->finalize = nr_arena_image_finalize;
10139N/A object_class->cpp_ctor = NRObject::invoke_ctor<NRArenaImage>;
10139N/A
10139N/A item_class->update = nr_arena_image_update;
10139N/A item_class->render = nr_arena_image_render;
10139N/A item_class->pick = nr_arena_image_pick;
10139N/A}
10139N/A
10139N/Astatic void
10139N/Anr_arena_image_init (NRArenaImage *image)
10139N/A{
10139N/A image->pixbuf = NULL;
10139N/A image->x = image->y = 0.0;
10139N/A image->width = 256.0;
10139N/A image->height = 256.0;
10139N/A
10139N/A image->grid2px.setIdentity();
10139N/A image->px2grid.setIdentity();
10139N/A
10139N/A image->style = 0;
10139N/A image->render_opacity = TRUE;
10139N/A}
10139N/A
10139N/Astatic void
10139N/Anr_arena_image_finalize (NRObject *object)
10139N/A{
10139N/A NRArenaImage *image = NR_ARENA_IMAGE (object);
10139N/A
10139N/A image->px = NULL;
10139N/A if (image->pixbuf != NULL)
10139N/A g_object_unref(image->pixbuf);
10139N/A
10139N/A ((NRObjectClass *) parent_class)->finalize (object);
10139N/A}
10139N/A
10139N/Astatic unsigned int
10139N/Anr_arena_image_update( NRArenaItem *item, NRRectL */*area*/, NRGC *gc, unsigned int /*state*/, unsigned int /*reset*/ )
10139N/A{
10139N/A Geom::Matrix grid2px;
10139N/A
10139N/A // clear old bbox
10139N/A nr_arena_item_request_render(item);
10139N/A
10139N/A NRArenaImage *image = NR_ARENA_IMAGE (item);
10139N/A
10139N/A /* Copy affine */
10139N/A grid2px = gc->transform.inverse();
10139N/A double hscale, vscale; // todo: replace with Geom::Scale
10139N/A if (image->pixbuf) {
10139N/A hscale = gdk_pixbuf_get_width(image->pixbuf) / image->width;
10139N/A vscale = gdk_pixbuf_get_height(image->pixbuf) / image->height;
10139N/A } else {
10139N/A hscale = 1.0;
10139N/A vscale = 1.0;
10139N/A }
10139N/A
10139N/A image->grid2px[0] = grid2px[0] * hscale;
10139N/A image->grid2px[2] = grid2px[2] * hscale;
10139N/A image->grid2px[4] = grid2px[4] * hscale;
10139N/A image->grid2px[1] = grid2px[1] * vscale;
10139N/A image->grid2px[3] = grid2px[3] * vscale;
10139N/A image->grid2px[5] = grid2px[5] * vscale;
10139N/A
10139N/A image->grid2px[4] -= image->x * hscale;
10139N/A image->grid2px[5] -= image->y * vscale;
10139N/A
10139N/A /* Calculate bbox */
10139N/A if (image->pixbuf) {
10139N/A NRRect bbox;
10139N/A
10139N/A bbox.x0 = image->x;
10139N/A bbox.y0 = image->y;
10139N/A bbox.x1 = image->x + image->width;
10139N/A bbox.y1 = image->y + image->height;
10139N/A
10139N/A image->c00 = (Geom::Point(bbox.x0, bbox.y0) * gc->transform);
10139N/A image->c01 = (Geom::Point(bbox.x0, bbox.y1) * gc->transform);
10139N/A image->c10 = (Geom::Point(bbox.x1, bbox.y0) * gc->transform);
10139N/A image->c11 = (Geom::Point(bbox.x1, bbox.y1) * gc->transform);
10139N/A
10139N/A nr_rect_d_matrix_transform (&bbox, &bbox, gc->transform);
10139N/A
10139N/A item->bbox.x0 = static_cast<NR::ICoord>(floor(bbox.x0)); // Floor gives the coordinate in which the point resides
10139N/A item->bbox.y0 = static_cast<NR::ICoord>(floor(bbox.y0));
10139N/A item->bbox.x1 = static_cast<NR::ICoord>(ceil (bbox.x1)); // Ceil gives the first coordinate beyond the point
10139N/A item->bbox.y1 = static_cast<NR::ICoord>(ceil (bbox.y1));
10139N/A } else {
10139N/A item->bbox.x0 = (int) gc->transform[4];
10139N/A item->bbox.y0 = (int) gc->transform[5];
10139N/A item->bbox.x1 = item->bbox.x0 - 1;
10139N/A item->bbox.y1 = item->bbox.y0 - 1;
10139N/A }
10139N/A
10139N/A return NR_ARENA_ITEM_STATE_ALL;
10139N/A}
10139N/A
10139N/A#define FBITS 12
10139N/A#define b2i (image->grid2px)
10139N/A
10139N/Astatic unsigned int
10139N/Anr_arena_image_render( cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int /*flags*/ )
10139N/A{
10139N/A if (!ct)
10139N/A return item->state;
10139N/A#if 0
10139N/A Inkscape::Preferences *prefs = Inkscape::Preferences::get();
10139N/A nr_arena_image_x_sample = prefs->getInt("/options/bitmapoversample/value", 1);
10139N/A nr_arena_image_y_sample = nr_arena_image_x_sample;
10139N/A#endif
10139N/A bool outline = (item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE);
10139N/A
10139N/A NRArenaImage *image = NR_ARENA_IMAGE (item);
10139N/A
10139N/A if (!outline) {
10139N/A if (!image->pixbuf) return item->state;
10139N/A
10139N/A // FIXME: at the moment gdk_cairo_set_source_pixbuf creates an ARGB copy
10139N/A // of the pixbuf. Fix this in Cairo and/or GDK.
10139N/A cairo_save(ct);
10139N/A cairo_translate(ct, -area->x0, -area->y0);
10139N/A gdk_cairo_set_source_pixbuf(ct, image->pixbuf, 0, 0);
10139N/A
10139N/A cairo_pattern_t *p = cairo_get_source(ct);
10139N/A ink_cairo_pattern_set_matrix(p, image->grid2px);
10139N/A
10139N/A cairo_paint_with_alpha(ct, ((double) item->opacity) / 255.0);
10139N/A cairo_restore(ct);
10139N/A
10139N/A } else { // outline; draw a rect instead
10139N/A Inkscape::Preferences *prefs = Inkscape::Preferences::get();
10139N/A guint32 rgba = prefs->getInt("/options/wireframecolors/images", 0xff0000ff);
10139N/A // FIXME: we use RGBA buffers but cairo writes BGRA (on i386), so we must cheat
10139N/A // by setting color channels in the "wrong" order
10139N/A cairo_set_source_rgba(ct, SP_RGBA32_B_F(rgba), SP_RGBA32_G_F(rgba), SP_RGBA32_R_F(rgba), SP_RGBA32_A_F(rgba));
10139N/A
10139N/A cairo_set_line_width(ct, 0.5);
10139N/A cairo_new_path(ct);
10139N/A
10139N/A Geom::Point shift(pb->area.x0, pb->area.y0);
10139N/A Geom::Point c00 = image->c00 - shift;
10139N/A Geom::Point c01 = image->c01 - shift;
10139N/A Geom::Point c11 = image->c11 - shift;
10139N/A Geom::Point c10 = image->c10 - shift;
10139N/A
10139N/A cairo_move_to (ct, c00[Geom::X], c00[Geom::Y]);
10139N/A
10139N/A // the box
10139N/A cairo_line_to (ct, c10[Geom::X], c10[Geom::Y]);
10139N/A cairo_line_to (ct, c11[Geom::X], c11[Geom::Y]);
10139N/A cairo_line_to (ct, c01[Geom::X], c01[Geom::Y]);
10139N/A cairo_line_to (ct, c00[Geom::X], c00[Geom::Y]);
10139N/A // the diagonals
10139N/A cairo_line_to (ct, c11[Geom::X], c11[Geom::Y]);
10139N/A cairo_move_to (ct, c10[Geom::X], c10[Geom::Y]);
10139N/A cairo_line_to (ct, c01[Geom::X], c01[Geom::Y]);
10139N/A
10139N/A cairo_stroke(ct);
10139N/A
10139N/A pb->empty = FALSE;
10139N/A }
10139N/A return item->state;
10139N/A}
10139N/A
10139N/A/** Calculates the closest distance from p to the segment a1-a2*/
10139N/Adouble
10139N/Adistance_to_segment (Geom::Point p, Geom::Point a1, Geom::Point a2)
10139N/A{
10139N/A // calculate sides of the triangle and their squares
10139N/A double d1 = Geom::L2(p - a1);
10139N/A double d1_2 = d1 * d1;
10139N/A double d2 = Geom::L2(p - a2);
10139N/A double d2_2 = d2 * d2;
10139N/A double a = Geom::L2(a1 - a2);
10139N/A double a_2 = a * a;
10139N/A
10139N/A // if one of the angles at the base is > 90, return the corresponding side
10139N/A if (d1_2 + a_2 <= d2_2) return d1;
10139N/A if (d2_2 + a_2 <= d1_2) return d2;
10139N/A
10139N/A // otherwise calculate the height to the base
10139N/A double peri = (a + d1 + d2)/2;
10139N/A return (2*sqrt(peri * (peri - a) * (peri - d1) * (peri - d2))/a);
10139N/A}
10139N/A
10139N/Astatic NRArenaItem *
10139N/Anr_arena_image_pick( NRArenaItem *item, Geom::Point p, double delta, unsigned int /*sticky*/ )
10139N/A{
10139N/A NRArenaImage *image = NR_ARENA_IMAGE (item);
10139N/A
10139N/A if (!image->pixbuf) return NULL;
10139N/A
10139N/A bool outline = (item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE);
10139N/A
10139N/A if (outline) {
10139N/A
10139N/A // frame
10139N/A if (distance_to_segment (p, image->c00, image->c10) < delta) return item;
10139N/A if (distance_to_segment (p, image->c10, image->c11) < delta) return item;
10139N/A if (distance_to_segment (p, image->c11, image->c01) < delta) return item;
10139N/A if (distance_to_segment (p, image->c01, image->c00) < delta) return item;
10139N/A
10139N/A // diagonals
10139N/A if (distance_to_segment (p, image->c00, image->c11) < delta) return item;
10139N/A if (distance_to_segment (p, image->c10, image->c01) < delta) return item;
10139N/A
10139N/A return NULL;
10139N/A
10139N/A } else {
10139N/A
10139N/A unsigned char *const pixels = gdk_pixbuf_get_pixels(image->pixbuf);
10139N/A int const width = gdk_pixbuf_get_width(image->pixbuf);
10139N/A int const height = gdk_pixbuf_get_height(image->pixbuf);
10139N/A int const rowstride = gdk_pixbuf_get_rowstride(image->pixbuf);
10139N/A Geom::Point tp = p * image->grid2px;
10139N/A int const ix = (int)(tp[Geom::X]);
10139N/A int const iy = (int)(tp[Geom::Y]);
10139N/A
10139N/A if ((ix < 0) || (iy < 0) || (ix >= width) || (iy >= height))
10139N/A return NULL;
10139N/A
10139N/A unsigned char *pix_ptr = pixels + iy * rowstride + ix * 4;
10139N/A // is the alpha not transparent?
10139N/A return (pix_ptr[3] > 0) ? item : NULL;
10139N/A }
10139N/A}
10139N/A
10139N/A/* Utility */
10139N/A
10139N/Avoid
10139N/Anr_arena_image_set_pixbuf (NRArenaImage *image, GdkPixbuf *pb)
10139N/A{
10139N/A nr_return_if_fail (image != NULL);
10139N/A nr_return_if_fail (NR_IS_ARENA_IMAGE (image));
11904N/A
10139N/A // when done in this order, it won't break if pb == image->pixbuf and the refcount is 1
10139N/A if (pb != NULL) {
10139N/A g_object_ref (pb);
10139N/A }
10139N/A if (image->pixbuf != NULL) {
10139N/A g_object_unref(image->pixbuf);
10139N/A }
10139N/A image->pixbuf = pb;
10139N/A
10139N/A nr_arena_item_request_update (NR_ARENA_ITEM (image), NR_ARENA_ITEM_STATE_ALL, FALSE);
10139N/A}
10139N/A
10139N/Avoid
10139N/Anr_arena_image_set_geometry (NRArenaImage *image, double x, double y, double width, double height)
10139N/A{
10139N/A nr_return_if_fail (image != NULL);
10139N/A nr_return_if_fail (NR_IS_ARENA_IMAGE (image));
10139N/A
10139N/A image->x = x;
10139N/A image->y = y;
10139N/A image->width = width;
10139N/A image->height = height;
10139N/A
10139N/A nr_arena_item_request_update (NR_ARENA_ITEM (image), NR_ARENA_ITEM_STATE_ALL, FALSE);
10139N/A}
10139N/A
10139N/Avoid nr_arena_image_set_style (NRArenaImage *image, SPStyle *style)
10139N/A{
10139N/A g_return_if_fail(image != NULL);
10139N/A g_return_if_fail(NR_IS_ARENA_IMAGE(image));
10139N/A
10139N/A if (style) sp_style_ref(style);
10139N/A if (image->style) sp_style_unref(image->style);
10139N/A image->style = style;
10139N/A
10139N/A //if image has a filter
10139N/A if (style->filter.set && style->getFilter()) {
10139N/A if (!image->filter) {
10139N/A int primitives = sp_filter_primitive_count(SP_FILTER(style->getFilter()));
10139N/A image->filter = new Inkscape::Filters::Filter(primitives);
10139N/A }
10139N/A sp_filter_build_renderer(SP_FILTER(style->getFilter()), image->filter);
10139N/A } else {
10139N/A //no filter set for this image
10139N/A delete image->filter;
10139N/A image->filter = NULL;
10139N/A }
10139N/A
10139N/A if (style && style->enable_background.set
10139N/A && style->enable_background.value == SP_CSS_BACKGROUND_NEW) {
10139N/A image->background_new = true;
10139N/A }
10139N/A
10139N/A nr_arena_item_request_update(image, NR_ARENA_ITEM_STATE_ALL, FALSE);
10139N/A}
10139N/A
10139N/A
10139N/A/*
10139N/A Local Variables:
10139N/A mode:c++
10139N/A c-file-style:"stroustrup"
10139N/A c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
10139N/A indent-tabs-mode:nil
10139N/A fill-column:99
10139N/A End:
10139N/A*/
10139N/A// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
10139N/A