diff options
Diffstat (limited to 'src/display/nr-arena-image.cpp')
| -rw-r--r-- | src/display/nr-arena-image.cpp | 270 |
1 files changed, 142 insertions, 128 deletions
diff --git a/src/display/nr-arena-image.cpp b/src/display/nr-arena-image.cpp index 6539ae4d2..cb1bc5849 100644 --- a/src/display/nr-arena-image.cpp +++ b/src/display/nr-arena-image.cpp @@ -12,20 +12,15 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <libnr/nr-compose-transform.h> #include <2geom/transforms.h> -#include <libnr/nr-blit.h> #include "../preferences.h" #include "nr-arena-image.h" #include "style.h" +#include "display/cairo-utils.h" #include "display/nr-arena.h" #include "display/nr-filter.h" -#include "display/nr-filter-gaussian.h" #include "sp-filter.h" #include "sp-filter-reference.h" -#include "sp-gaussian-blur.h" -#include "filters/blend.h" -#include "display/nr-filter-blend.h" int nr_arena_image_x_sample = 1; int nr_arena_image_y_sample = 1; @@ -42,6 +37,7 @@ static void nr_arena_image_finalize (NRObject *object); static unsigned int nr_arena_image_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset); static unsigned int nr_arena_image_render (cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags); static NRArenaItem *nr_arena_image_pick (NRArenaItem *item, Geom::Point p, double delta, unsigned int sticky); +static Geom::Rect nr_arena_image_rect (NRArenaImage *image); static NRArenaItemClass *parent_class; @@ -82,14 +78,11 @@ nr_arena_image_class_init (NRArenaImageClass *klass) static void nr_arena_image_init (NRArenaImage *image) { - image->px = NULL; - - image->pxw = image->pxh = image->pxrs = 0; - image->x = image->y = 0.0; - image->width = 256.0; - image->height = 256.0; - - image->grid2px.setIdentity(); + image->pixbuf = NULL; + image->ctm.setIdentity(); + image->clipbox = Geom::Rect(); + image->ox = image->oy = 0.0; + image->sx = image->sy = 1.0; image->style = 0; image->render_opacity = TRUE; @@ -100,7 +93,12 @@ nr_arena_image_finalize (NRObject *object) { NRArenaImage *image = NR_ARENA_IMAGE (object); - image->px = NULL; + if (image->pixbuf != NULL) { + g_object_unref(image->pixbuf); + cairo_surface_destroy(image->surface); + } + if (image->style) + sp_style_unref(image->style); ((NRObjectClass *) parent_class)->finalize (object); } @@ -108,54 +106,24 @@ nr_arena_image_finalize (NRObject *object) static unsigned int nr_arena_image_update( NRArenaItem *item, NRRectL */*area*/, NRGC *gc, unsigned int /*state*/, unsigned int /*reset*/ ) { - Geom::Affine grid2px; - // clear old bbox nr_arena_item_request_render(item); NRArenaImage *image = NR_ARENA_IMAGE (item); /* Copy affine */ - grid2px = gc->transform.inverse(); - double hscale, vscale; // todo: replace with Geom::Scale - if (image->px) { - hscale = image->pxw / image->width; - vscale = image->pxh / image->height; - } else { - hscale = 1.0; - vscale = 1.0; - } - - image->grid2px[0] = grid2px[0] * hscale; - image->grid2px[2] = grid2px[2] * hscale; - image->grid2px[4] = grid2px[4] * hscale; - image->grid2px[1] = grid2px[1] * vscale; - image->grid2px[3] = grid2px[3] * vscale; - image->grid2px[5] = grid2px[5] * vscale; - - image->grid2px[4] -= image->x * hscale; - image->grid2px[5] -= image->y * vscale; + image->ctm = gc->transform; /* Calculate bbox */ - if (image->px) { + if (image->pixbuf) { NRRect bbox; - bbox.x0 = image->x; - bbox.y0 = image->y; - bbox.x1 = image->x + image->width; - bbox.y1 = image->y + image->height; - - image->c00 = (Geom::Point(bbox.x0, bbox.y0) * gc->transform); - image->c01 = (Geom::Point(bbox.x0, bbox.y1) * gc->transform); - image->c10 = (Geom::Point(bbox.x1, bbox.y0) * gc->transform); - image->c11 = (Geom::Point(bbox.x1, bbox.y1) * gc->transform); + Geom::Rect r = nr_arena_image_rect(image) * gc->transform; - nr_rect_d_matrix_transform (&bbox, &bbox, gc->transform); - - item->bbox.x0 = static_cast<NR::ICoord>(floor(bbox.x0)); // Floor gives the coordinate in which the point resides - item->bbox.y0 = static_cast<NR::ICoord>(floor(bbox.y0)); - item->bbox.x1 = static_cast<NR::ICoord>(ceil (bbox.x1)); // Ceil gives the first coordinate beyond the point - item->bbox.y1 = static_cast<NR::ICoord>(ceil (bbox.y1)); + item->bbox.x0 = floor(r.left()); // Floor gives the coordinate in which the point resides + item->bbox.y0 = floor(r.top()); + item->bbox.x1 = ceil(r.right()); // Ceil gives the first coordinate beyond the point + item->bbox.y1 = ceil(r.bottom()); } else { item->bbox.x0 = (int) gc->transform[4]; item->bbox.y0 = (int) gc->transform[5]; @@ -166,78 +134,63 @@ nr_arena_image_update( NRArenaItem *item, NRRectL */*area*/, NRGC *gc, unsigned return NR_ARENA_ITEM_STATE_ALL; } -#define FBITS 12 -#define b2i (image->grid2px) - static unsigned int -nr_arena_image_render( cairo_t *ct, NRArenaItem *item, NRRectL */*area*/, NRPixBlock *pb, unsigned int /*flags*/ ) +nr_arena_image_render( cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int /*flags*/ ) { - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - nr_arena_image_x_sample = prefs->getInt("/options/bitmapoversample/value", 1); - nr_arena_image_y_sample = nr_arena_image_x_sample; + if (!ct) + return item->state; bool outline = (item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE); NRArenaImage *image = NR_ARENA_IMAGE (item); - Geom::Affine d2s; - - d2s[0] = b2i[0]; - d2s[1] = b2i[1]; - d2s[2] = b2i[2]; - d2s[3] = b2i[3]; - d2s[4] = b2i[0] * pb->area.x0 + b2i[2] * pb->area.y0 + b2i[4]; - d2s[5] = b2i[1] * pb->area.x0 + b2i[3] * pb->area.y0 + b2i[5]; - if (!outline) { + if (!image->pixbuf) return item->state; - if (!image->px) return item->state; + // FIXME: at the moment gdk_cairo_set_source_pixbuf creates an ARGB copy + // of the pixbuf. Fix this in Cairo and/or GDK. + cairo_save(ct); + ink_cairo_transform(ct, image->ctm); - guint32 Falpha = item->opacity; - if (Falpha < 1) return item->state; + cairo_new_path(ct); + cairo_rectangle(ct, image->clipbox.left(), image->clipbox.top(), + image->clipbox.width(), image->clipbox.height()); + cairo_clip(ct); - unsigned char * dpx = NR_PIXBLOCK_PX (pb); - int const drs = pb->rs; - int const dw = pb->area.x1 - pb->area.x0; - int const dh = pb->area.y1 - pb->area.y0; + cairo_translate(ct, image->ox, image->oy); + cairo_scale(ct, image->sx, image->sy); - unsigned char * spx = image->px; - int const srs = image->pxrs; - int const sw = image->pxw; - int const sh = image->pxh; + cairo_set_source_surface(ct, image->surface, 0, 0); - if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8) { - /* fixme: This is not implemented yet (Lauris) */ - /* nr_R8G8B8_R8G8B8_R8G8B8A8_N_TRANSFORM (dpx, dw, dh, drs, spx, sw, sh, srs, d2s, Falpha, nr_arena_image_x_sample, nr_arena_image_y_sample); */ - } else if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) { - nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM (dpx, dw, dh, drs, spx, sw, sh, srs, d2s, Falpha, nr_arena_image_x_sample, nr_arena_image_y_sample); - } else if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8N) { - nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N_TRANSFORM (dpx, dw, dh, drs, spx, sw, sh, srs, d2s, Falpha, nr_arena_image_x_sample, nr_arena_image_y_sample); + cairo_matrix_t tt; + Geom::Affine total; + cairo_get_matrix(ct, &tt); + ink_matrix_to_2geom(total, tt); + + if (total.expansionX() > 1.0 || total.expansionY() > 1.0) { + cairo_pattern_t *p = cairo_get_source(ct); + cairo_pattern_set_filter(p, CAIRO_FILTER_NEAREST); } - pb->empty = FALSE; + cairo_paint_with_alpha(ct, ((double) item->opacity) / 255.0); + cairo_restore(ct); } else { // outline; draw a rect instead - - if (!ct) - return item->state; - + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); guint32 rgba = prefs->getInt("/options/wireframecolors/images", 0xff0000ff); - // FIXME: we use RGBA buffers but cairo writes BGRA (on i386), so we must cheat - // by setting color channels in the "wrong" order - 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)); - cairo_set_line_width(ct, 0.5); + cairo_save(ct); + ink_cairo_transform(ct, image->ctm); + cairo_new_path(ct); - Geom::Point shift(pb->area.x0, pb->area.y0); - Geom::Point c00 = image->c00 - shift; - Geom::Point c01 = image->c01 - shift; - Geom::Point c11 = image->c11 - shift; - Geom::Point c10 = image->c10 - shift; + Geom::Rect r = nr_arena_image_rect (image); + Geom::Point c00 = r.corner(0); + Geom::Point c01 = r.corner(3); + Geom::Point c11 = r.corner(2); + Geom::Point c10 = r.corner(1); cairo_move_to (ct, c00[Geom::X], c00[Geom::Y]); - // the box cairo_line_to (ct, c10[Geom::X], c10[Geom::Y]); cairo_line_to (ct, c11[Geom::X], c11[Geom::Y]); @@ -247,12 +200,12 @@ nr_arena_image_render( cairo_t *ct, NRArenaItem *item, NRRectL */*area*/, NRPixB cairo_line_to (ct, c11[Geom::X], c11[Geom::Y]); cairo_move_to (ct, c10[Geom::X], c10[Geom::Y]); cairo_line_to (ct, c01[Geom::X], c01[Geom::Y]); + cairo_restore(ct); + cairo_set_line_width(ct, 0.5); + ink_cairo_set_source_rgba32(ct, rgba); cairo_stroke(ct); - - pb->empty = FALSE; } - return item->state; } @@ -282,33 +235,47 @@ nr_arena_image_pick( NRArenaItem *item, Geom::Point p, double delta, unsigned in { NRArenaImage *image = NR_ARENA_IMAGE (item); - if (!image->px) return NULL; + if (!image->pixbuf) return NULL; bool outline = (item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE); if (outline) { + Geom::Rect r = nr_arena_image_rect (image); + + Geom::Point c00 = r.corner(0); + Geom::Point c01 = r.corner(3); + Geom::Point c11 = r.corner(2); + Geom::Point c10 = r.corner(1); // frame - if (distance_to_segment (p, image->c00, image->c10) < delta) return item; - if (distance_to_segment (p, image->c10, image->c11) < delta) return item; - if (distance_to_segment (p, image->c11, image->c01) < delta) return item; - if (distance_to_segment (p, image->c01, image->c00) < delta) return item; + if (distance_to_segment (p, c00, c10) < delta) return item; + if (distance_to_segment (p, c10, c11) < delta) return item; + if (distance_to_segment (p, c11, c01) < delta) return item; + if (distance_to_segment (p, c01, c00) < delta) return item; // diagonals - if (distance_to_segment (p, image->c00, image->c11) < delta) return item; - if (distance_to_segment (p, image->c10, image->c01) < delta) return item; + if (distance_to_segment (p, c00, c11) < delta) return item; + if (distance_to_segment (p, c10, c01) < delta) return item; return NULL; } else { - unsigned char *const pixels = image->px; - int const width = image->pxw; - int const height = image->pxh; - int const rowstride = image->pxrs; - Geom::Point tp = p * image->grid2px; - int const ix = (int)(tp[Geom::X]); - int const iy = (int)(tp[Geom::Y]); + unsigned char *const pixels = gdk_pixbuf_get_pixels(image->pixbuf); + int const width = gdk_pixbuf_get_width(image->pixbuf); + int const height = gdk_pixbuf_get_height(image->pixbuf); + int const rowstride = gdk_pixbuf_get_rowstride(image->pixbuf); + + Geom::Point tp = p * image->ctm.inverse(); + Geom::Rect r = nr_arena_image_rect(image); + + if (!r.contains(tp)) + return NULL; + + double vw = width * image->sx; + double vh = height * image->sy; + int ix = floor((tp[Geom::X] - image->ox) / vw * width); + int iy = floor((tp[Geom::Y] - image->oy) / vh * height); if ((ix < 0) || (iy < 0) || (ix >= width) || (iy >= height)) return NULL; @@ -319,32 +286,79 @@ nr_arena_image_pick( NRArenaItem *item, Geom::Point p, double delta, unsigned in } } +Geom::Rect +nr_arena_image_rect (NRArenaImage *image) +{ + Geom::Rect r = image->clipbox; + + if (image->pixbuf) { + double pw = gdk_pixbuf_get_width(image->pixbuf); + double ph = gdk_pixbuf_get_height(image->pixbuf); + double vw = pw * image->sx; + double vh = ph * image->sy; + Geom::Point p(image->ox, image->oy); + Geom::Point wh(vw, vh); + Geom::Rect view(p, p+wh); + Geom::OptRect res = Geom::intersect(r, view); + r = res ? *res : r; + } + + return r; +} + /* Utility */ void -nr_arena_image_set_pixels (NRArenaImage *image, unsigned char const *px, unsigned int pxw, unsigned int pxh, unsigned int pxrs) +nr_arena_image_set_argb32_pixbuf (NRArenaImage *image, GdkPixbuf *pb) +{ + nr_return_if_fail (image != NULL); + nr_return_if_fail (NR_IS_ARENA_IMAGE (image)); + + // when done in this order, it won't break if pb == image->pixbuf and the refcount is 1 + if (pb != NULL) { + g_object_ref (pb); + } + if (image->pixbuf != NULL) { + g_object_unref(image->pixbuf); + cairo_surface_destroy(image->surface); + } + image->pixbuf = pb; + image->surface = pb ? ink_cairo_surface_create_for_argb32_pixbuf(pb) : NULL; + + nr_arena_item_request_update (NR_ARENA_ITEM (image), NR_ARENA_ITEM_STATE_ALL, FALSE); +} + +void +nr_arena_image_set_clipbox (NRArenaImage *image, Geom::Rect const &clip) +{ + nr_return_if_fail (image != NULL); + nr_return_if_fail (NR_IS_ARENA_IMAGE (image)); + + image->clipbox = clip; + + nr_arena_item_request_update (NR_ARENA_ITEM (image), NR_ARENA_ITEM_STATE_ALL, FALSE); +} + +void +nr_arena_image_set_origin (NRArenaImage *image, Geom::Point const &origin) { nr_return_if_fail (image != NULL); nr_return_if_fail (NR_IS_ARENA_IMAGE (image)); - image->px = (unsigned char *) px; - image->pxw = pxw; - image->pxh = pxh; - image->pxrs = pxrs; + image->ox = origin[Geom::X]; + image->oy = origin[Geom::Y]; nr_arena_item_request_update (NR_ARENA_ITEM (image), NR_ARENA_ITEM_STATE_ALL, FALSE); } void -nr_arena_image_set_geometry (NRArenaImage *image, double x, double y, double width, double height) +nr_arena_image_set_scale (NRArenaImage *image, double sx, double sy) { nr_return_if_fail (image != NULL); nr_return_if_fail (NR_IS_ARENA_IMAGE (image)); - image->x = x; - image->y = y; - image->width = width; - image->height = height; + image->sx = sx; + image->sy = sy; nr_arena_item_request_update (NR_ARENA_ITEM (image), NR_ARENA_ITEM_STATE_ALL, FALSE); } |
