Code

c8a98848363590d27b98957f48fbcdfe72f12a65
[inkscape.git] / src / display / nr-arena-image.cpp
1 #define __NR_ARENA_IMAGE_C__
3 /*
4  * RGBA display list system for inkscape
5  *
6  * Author:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *
9  * Copyright (C) 2001-2002 Lauris Kaplinski
10  * Copyright (C) 2001 Ximian, Inc.
11  *
12  * Released under GNU GPL, read the file 'COPYING' for more information
13  */
15 #include <libnr/nr-compose-transform.h>
16 #include <2geom/transforms.h>
17 #include <libnr/nr-blit.h>
18 #include "../prefs-utils.h"
19 #include "nr-arena-image.h"
20 #include "style.h"
21 #include "display/nr-arena.h"
22 #include "display/nr-filter.h"
23 #include "display/nr-filter-gaussian.h"
24 #include "sp-filter.h"
25 #include "sp-filter-reference.h"
26 #include "sp-gaussian-blur.h"
27 #include "sp-feblend.h"
28 #include "display/nr-filter-blend.h"
30 int nr_arena_image_x_sample = 1;
31 int nr_arena_image_y_sample = 1;
33 /*
34  * NRArenaCanvasImage
35  *
36  */
38 static void nr_arena_image_class_init (NRArenaImageClass *klass);
39 static void nr_arena_image_init (NRArenaImage *image);
40 static void nr_arena_image_finalize (NRObject *object);
42 static unsigned int nr_arena_image_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset);
43 static unsigned int nr_arena_image_render (cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags);
44 static NRArenaItem *nr_arena_image_pick (NRArenaItem *item, Geom::Point p, double delta, unsigned int sticky);
46 static NRArenaItemClass *parent_class;
48 NRType
49 nr_arena_image_get_type (void)
50 {
51     static NRType type = 0;
52     if (!type) {
53         type = nr_object_register_type (NR_TYPE_ARENA_ITEM,
54                                         "NRArenaImage",
55                                         sizeof (NRArenaImageClass),
56                                         sizeof (NRArenaImage),
57                                         (void (*) (NRObjectClass *)) nr_arena_image_class_init,
58                                         (void (*) (NRObject *)) nr_arena_image_init);
59     }
60     return type;
61 }
63 static void
64 nr_arena_image_class_init (NRArenaImageClass *klass)
65 {
66     NRObjectClass *object_class;
67     NRArenaItemClass *item_class;
69     object_class = (NRObjectClass *) klass;
70     item_class = (NRArenaItemClass *) klass;
72     parent_class = (NRArenaItemClass *) ((NRObjectClass *) klass)->parent;
74     object_class->finalize = nr_arena_image_finalize;
75     object_class->cpp_ctor = NRObject::invoke_ctor<NRArenaImage>;
77     item_class->update = nr_arena_image_update;
78     item_class->render = nr_arena_image_render;
79     item_class->pick = nr_arena_image_pick;
80 }
82 static void
83 nr_arena_image_init (NRArenaImage *image)
84 {
85     image->px = NULL;
87     image->pxw = image->pxh = image->pxrs = 0;
88     image->x = image->y = 0.0;
89     image->width = 256.0;
90     image->height = 256.0;
92     image->grid2px.setIdentity();
94     image->style = 0;
95 }
97 static void
98 nr_arena_image_finalize (NRObject *object)
99 {
100     NRArenaImage *image = NR_ARENA_IMAGE (object);
102     image->px = NULL;
104     ((NRObjectClass *) parent_class)->finalize (object);
107 static unsigned int
108 nr_arena_image_update( NRArenaItem *item, NRRectL */*area*/, NRGC *gc, unsigned int /*state*/, unsigned int /*reset*/ )
110     Geom::Matrix grid2px;
112     NRArenaImage *image = NR_ARENA_IMAGE (item);
114     /* Request render old */
115     nr_arena_item_request_render (item);
117     /* Copy affine */
118     grid2px = gc->transform.inverse();
119     double hscale, vscale; // todo: replace with Geom::Scale
120     if (image->px) {
121         hscale = image->pxw / image->width;
122         vscale = image->pxh / image->height;
123     } else {
124         hscale = 1.0;
125         vscale = 1.0;
126     }
128     image->grid2px[0] = grid2px[0] * hscale;
129     image->grid2px[2] = grid2px[2] * hscale;
130     image->grid2px[4] = grid2px[4] * hscale;
131     image->grid2px[1] = grid2px[1] * vscale;
132     image->grid2px[3] = grid2px[3] * vscale;
133     image->grid2px[5] = grid2px[5] * vscale;
135     image->grid2px[4] -= image->x * hscale;
136     image->grid2px[5] -= image->y * vscale;
138     /* Calculate bbox */
139     if (image->px) {
140         NRRect bbox;
142         bbox.x0 = image->x;
143         bbox.y0 = image->y;
144         bbox.x1 = image->x + image->width;
145         bbox.y1 = image->y + image->height;
147         image->c00 = (Geom::Point(bbox.x0, bbox.y0) * gc->transform);
148         image->c01 = (Geom::Point(bbox.x0, bbox.y1) * gc->transform);
149         image->c10 = (Geom::Point(bbox.x1, bbox.y0) * gc->transform);
150         image->c11 = (Geom::Point(bbox.x1, bbox.y1) * gc->transform);
152         nr_rect_d_matrix_transform (&bbox, &bbox, from_2geom(gc->transform));
154         item->bbox.x0 = (int) floor (bbox.x0);
155         item->bbox.y0 = (int) floor (bbox.y0);
156         item->bbox.x1 = (int) ceil (bbox.x1);
157         item->bbox.y1 = (int) ceil (bbox.y1);
158     } else {
159         item->bbox.x0 = (int) gc->transform[4];
160         item->bbox.y0 = (int) gc->transform[5];
161         item->bbox.x1 = item->bbox.x0 - 1;
162         item->bbox.y1 = item->bbox.y0 - 1;
163     }
165     nr_arena_item_request_render (item);
167     return NR_ARENA_ITEM_STATE_ALL;
170 #define FBITS 12
171 #define b2i (image->grid2px)
173 static unsigned int
174 nr_arena_image_render( cairo_t *ct, NRArenaItem *item, NRRectL */*area*/, NRPixBlock *pb, unsigned int /*flags*/ )
176     nr_arena_image_x_sample = prefs_get_int_attribute ("options.bitmapoversample", "value", 1);
177     nr_arena_image_y_sample = nr_arena_image_x_sample;
179     bool outline = (item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE);
181     NRArenaImage *image = NR_ARENA_IMAGE (item);
183     Geom::Matrix d2s;
185     d2s[0] = b2i[0];
186     d2s[1] = b2i[1];
187     d2s[2] = b2i[2];
188     d2s[3] = b2i[3];
189     d2s[4] = b2i[0] * pb->area.x0 + b2i[2] * pb->area.y0 + b2i[4];
190     d2s[5] = b2i[1] * pb->area.x0 + b2i[3] * pb->area.y0 + b2i[5];
192     if (!outline) {
194         if (!image->px) return item->state;
196         guint32 Falpha = item->opacity;
197         if (Falpha < 1) return item->state;
199         unsigned char * dpx = NR_PIXBLOCK_PX (pb);
200         int const drs = pb->rs;
201         int const dw = pb->area.x1 - pb->area.x0;
202         int const dh = pb->area.y1 - pb->area.y0;
204         unsigned char * spx = image->px;
205         int const srs = image->pxrs;
206         int const sw = image->pxw;
207         int const sh = image->pxh;
209         if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8) {
210             /* fixme: This is not implemented yet (Lauris) */
211             /* 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); */
212         } else if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
213             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);
214         } else if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8N) {
216             //FIXME: The _N_N_N_ version gives a gray border around images, see bug 906376
217             // This mode is only used when exporting, screen rendering always has _P_P_P_, so I decided to simply replace it for now
218             // Feel free to propose a better fix
220             //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);
221             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);
222         }
224         pb->empty = FALSE;
226     } else { // outline; draw a rect instead
228         if (!ct)
229             return item->state;
231         guint32 rgba = prefs_get_int_attribute("options.wireframecolors", "images", 0xff0000ff);
232         // FIXME: we use RGBA buffers but cairo writes BGRA (on i386), so we must cheat
233         // by setting color channels in the "wrong" order
234         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));
236         cairo_set_line_width(ct, 0.5);
237         cairo_new_path(ct);
239         Geom::Point shift(pb->area.x0, pb->area.y0);
240         Geom::Point c00 = image->c00 - shift;
241         Geom::Point c01 = image->c01 - shift;
242         Geom::Point c11 = image->c11 - shift;
243         Geom::Point c10 = image->c10 - shift;
245         cairo_move_to (ct, c00[Geom::X], c00[Geom::Y]);
247         // the box
248         cairo_line_to (ct, c10[Geom::X], c10[Geom::Y]);
249         cairo_line_to (ct, c11[Geom::X], c11[Geom::Y]);
250         cairo_line_to (ct, c01[Geom::X], c01[Geom::Y]);
251         cairo_line_to (ct, c00[Geom::X], c00[Geom::Y]);
252         // the diagonals
253         cairo_line_to (ct, c11[Geom::X], c11[Geom::Y]);
254         cairo_move_to (ct, c10[Geom::X], c10[Geom::Y]);
255         cairo_line_to (ct, c01[Geom::X], c01[Geom::Y]);
257         cairo_stroke(ct);
259         pb->empty = FALSE;
260     }
262     return item->state;
265 /** Calculates the closest distance from p to the segment a1-a2*/
266 double
267 distance_to_segment (Geom::Point p, Geom::Point a1, Geom::Point a2)
269     // calculate sides of the triangle and their squares
270     double d1 = Geom::L2(p - a1);
271     double d1_2 = d1 * d1;
272     double d2 = Geom::L2(p - a2);
273     double d2_2 = d2 * d2;
274     double a = Geom::L2(a1 - a2);
275     double a_2 = a * a;
277     // if one of the angles at the base is > 90, return the corresponding side
278     if (d1_2 + a_2 <= d2_2) return d1;
279     if (d2_2 + a_2 <= d1_2) return d2;
281     // otherwise calculate the height to the base
282     double peri = (a + d1 + d2)/2;
283     return (2*sqrt(peri * (peri - a) * (peri - d1) * (peri - d2))/a);
286 static NRArenaItem *
287 nr_arena_image_pick( NRArenaItem *item, Geom::Point p, double delta, unsigned int /*sticky*/ )
289     NRArenaImage *image = NR_ARENA_IMAGE (item);
291     if (!image->px) return NULL;
293     bool outline = (item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE);
295     if (outline) {
297         // frame
298         if (distance_to_segment (p, image->c00, image->c10) < delta) return item;
299         if (distance_to_segment (p, image->c10, image->c11) < delta) return item;
300         if (distance_to_segment (p, image->c11, image->c01) < delta) return item;
301         if (distance_to_segment (p, image->c01, image->c00) < delta) return item;
303         // diagonals
304         if (distance_to_segment (p, image->c00, image->c11) < delta) return item;
305         if (distance_to_segment (p, image->c10, image->c01) < delta) return item;
307         return NULL;
309     } else {
311         unsigned char *const pixels = image->px;
312         int const width = image->pxw;
313         int const height = image->pxh;
314         int const rowstride = image->pxrs;
315         Geom::Point tp = p * image->grid2px;
316         int const ix = (int)(tp[Geom::X]);
317         int const iy = (int)(tp[Geom::Y]);
319         if ((ix < 0) || (iy < 0) || (ix >= width) || (iy >= height))
320             return NULL;
322         unsigned char *pix_ptr = pixels + iy * rowstride + ix * 4;
323         // is the alpha not transparent?
324         return (pix_ptr[3] > 0) ? item : NULL;
325     }
328 /* Utility */
330 void
331 nr_arena_image_set_pixels (NRArenaImage *image, unsigned char const *px, unsigned int pxw, unsigned int pxh, unsigned int pxrs)
333     nr_return_if_fail (image != NULL);
334     nr_return_if_fail (NR_IS_ARENA_IMAGE (image));
336     image->px = (unsigned char *) px;
337     image->pxw = pxw;
338     image->pxh = pxh;
339     image->pxrs = pxrs;
341     nr_arena_item_request_update (NR_ARENA_ITEM (image), NR_ARENA_ITEM_STATE_ALL, FALSE);
344 void
345 nr_arena_image_set_geometry (NRArenaImage *image, double x, double y, double width, double height)
347     nr_return_if_fail (image != NULL);
348     nr_return_if_fail (NR_IS_ARENA_IMAGE (image));
350     image->x = x;
351     image->y = y;
352     image->width = width;
353     image->height = height;
355     nr_arena_item_request_update (NR_ARENA_ITEM (image), NR_ARENA_ITEM_STATE_ALL, FALSE);
358 void nr_arena_image_set_style (NRArenaImage *image, SPStyle *style)
360     g_return_if_fail(image != NULL);
361     g_return_if_fail(NR_IS_ARENA_IMAGE(image));
363     if (style) sp_style_ref(style);
364     if (image->style) sp_style_unref(image->style);
365     image->style = style;
367     //if image has a filter
368     if (style->filter.set && style->getFilter()) {
369         if (!image->filter) {
370             int primitives = sp_filter_primitive_count(SP_FILTER(style->getFilter()));
371             image->filter = new NR::Filter(primitives);
372         }
373         sp_filter_build_renderer(SP_FILTER(style->getFilter()), image->filter);
374     } else {
375         //no filter set for this image
376         delete image->filter;
377         image->filter = NULL;
378     }
380     if (style && style->enable_background.set
381         && style->enable_background.value == SP_CSS_BACKGROUND_NEW) {
382         image->background_new = true;
383     }
387 /*
388   Local Variables:
389   mode:c++
390   c-file-style:"stroustrup"
391   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
392   indent-tabs-mode:nil
393   fill-column:99
394   End:
395 */
396 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :