Code

noop: CodingStyle: const placement
[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 <libnr/nr-blit.h>
17 #include "../prefs-utils.h"
18 #include "nr-arena-image.h"
19 #include "style.h"
20 #include "display/nr-arena.h"
21 #include "display/nr-filter.h"
22 #include "display/nr-filter-gaussian.h"
23 #include <livarot/Path.h>
24 #include <livarot/Shape.h>
25 #include "sp-filter.h"
26 #include "sp-filter-reference.h"
27 #include "sp-gaussian-blur.h"
28 #include "sp-feblend.h"
29 #include "display/nr-filter-blend.h"
31 int nr_arena_image_x_sample = 1;
32 int nr_arena_image_y_sample = 1;
34 /*
35  * NRArenaCanvasImage
36  *
37  */
39 // defined in nr-arena-shape.cpp
40 void nr_pixblock_render_shape_mask_or(NRPixBlock &m, Shape *theS);
42 static void nr_arena_image_class_init (NRArenaImageClass *klass);
43 static void nr_arena_image_init (NRArenaImage *image);
44 static void nr_arena_image_finalize (NRObject *object);
46 static unsigned int nr_arena_image_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset);
47 static unsigned int nr_arena_image_render (cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags);
48 static NRArenaItem *nr_arena_image_pick (NRArenaItem *item, NR::Point p, double delta, unsigned int sticky);
50 static NRArenaItemClass *parent_class;
52 NRType
53 nr_arena_image_get_type (void)
54 {
55         static NRType type = 0;
56         if (!type) {
57                 type = nr_object_register_type (NR_TYPE_ARENA_ITEM,
58                                                 "NRArenaImage",
59                                                 sizeof (NRArenaImageClass),
60                                                 sizeof (NRArenaImage),
61                                                 (void (*) (NRObjectClass *)) nr_arena_image_class_init,
62                                                 (void (*) (NRObject *)) nr_arena_image_init);
63         }
64         return type;
65 }
67 static void
68 nr_arena_image_class_init (NRArenaImageClass *klass)
69 {
70         NRObjectClass *object_class;
71         NRArenaItemClass *item_class;
73         object_class = (NRObjectClass *) klass;
74         item_class = (NRArenaItemClass *) klass;
76         parent_class = (NRArenaItemClass *) ((NRObjectClass *) klass)->parent;
78         object_class->finalize = nr_arena_image_finalize;
79         object_class->cpp_ctor = NRObject::invoke_ctor<NRArenaImage>;
81         item_class->update = nr_arena_image_update;
82         item_class->render = nr_arena_image_render;
83         item_class->pick = nr_arena_image_pick;
84 }
86 static void
87 nr_arena_image_init (NRArenaImage *image)
88 {
89         image->px = NULL;
91         image->pxw = image->pxh = image->pxrs = 0;
92         image->x = image->y = 0.0;
93         image->width = 256.0;
94         image->height = 256.0;
96         nr_matrix_set_identity (&image->grid2px);
98         image->style = 0;
99 }
101 static void
102 nr_arena_image_finalize (NRObject *object)
104         NRArenaImage *image = NR_ARENA_IMAGE (object);
106         image->px = NULL;
108         ((NRObjectClass *) parent_class)->finalize (object);
111 static unsigned int
112 nr_arena_image_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset)
114         NRMatrix grid2px;
116         NRArenaImage *image = NR_ARENA_IMAGE (item);
118         /* Request render old */
119         nr_arena_item_request_render (item);
121         /* Copy affine */
122         nr_matrix_invert (&grid2px, &gc->transform);
123         double hscale, vscale; // todo: replace with NR::scale
124         if (image->px) {
125                 hscale = image->pxw / image->width;
126                 vscale = image->pxh / image->height;
127         } else {
128                 hscale = 1.0;
129                 vscale = 1.0;
130         }
132         image->grid2px[0] = grid2px.c[0] * hscale;
133         image->grid2px[2] = grid2px.c[2] * hscale;
134         image->grid2px[4] = grid2px.c[4] * hscale;
135         image->grid2px[1] = grid2px.c[1] * vscale;
136         image->grid2px[3] = grid2px.c[3] * vscale;
137         image->grid2px[5] = grid2px.c[5] * vscale;
139         image->grid2px[4] -= image->x * hscale;
140         image->grid2px[5] -= image->y * vscale;
142         /* Calculate bbox */
143         if (image->px) {
144                 NRRect bbox;
146                 bbox.x0 = image->x;
147                 bbox.y0 = image->y;
148                 bbox.x1 = image->x + image->width;
149                 bbox.y1 = image->y + image->height;
151                 image->c00 = (NR::Point(bbox.x0, bbox.y0) * gc->transform);
152                 image->c01 = (NR::Point(bbox.x0, bbox.y1) * gc->transform);
153                 image->c10 = (NR::Point(bbox.x1, bbox.y0) * gc->transform);
154                 image->c11 = (NR::Point(bbox.x1, bbox.y1) * gc->transform);
156                 nr_rect_d_matrix_transform (&bbox, &bbox, &gc->transform);
158                 item->bbox.x0 = (int) floor (bbox.x0);
159                 item->bbox.y0 = (int) floor (bbox.y0);
160                 item->bbox.x1 = (int) ceil (bbox.x1);
161                 item->bbox.y1 = (int) ceil (bbox.y1);
162         } else {
163                 item->bbox.x0 = (int) gc->transform[4];
164                 item->bbox.y0 = (int) gc->transform[5];
165                 item->bbox.x1 = item->bbox.x0 - 1;
166                 item->bbox.y1 = item->bbox.y0 - 1;
167         }
169         nr_arena_item_request_render (item);
171         return NR_ARENA_ITEM_STATE_ALL;
174 #define FBITS 12
175 #define b2i (image->grid2px)
177 static unsigned int
178 nr_arena_image_render (cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags)
180         nr_arena_image_x_sample = prefs_get_int_attribute ("options.bitmapoversample", "value", 1);
181         nr_arena_image_y_sample = nr_arena_image_x_sample;
183         bool outline = (item->arena->rendermode == RENDERMODE_OUTLINE);
185         NRArenaImage *image = NR_ARENA_IMAGE (item);
187         NR::Matrix d2s;
189         d2s[0] = b2i[0];
190         d2s[1] = b2i[1];
191         d2s[2] = b2i[2];
192         d2s[3] = b2i[3];
193         d2s[4] = b2i[0] * pb->area.x0 + b2i[2] * pb->area.y0 + b2i[4];
194         d2s[5] = b2i[1] * pb->area.x0 + b2i[3] * pb->area.y0 + b2i[5];
196         if (!outline) {
198                 if (!image->px) return item->state;
200                 guint32 Falpha = item->opacity;
201                 if (Falpha < 1) return item->state;
203                 unsigned char * dpx = NR_PIXBLOCK_PX (pb);
204                 int const drs = pb->rs;
205                 int const dw = pb->area.x1 - pb->area.x0;
206                 int const dh = pb->area.y1 - pb->area.y0;
208                 unsigned char * spx = image->px;
209                 int const srs = image->pxrs;
210                 int const sw = image->pxw;
211                 int const sh = image->pxh;
213                 if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8) {
214                         /* fixme: This is not implemented yet (Lauris) */
215                         /* 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); */
216                 } else if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
217                         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);
218                 } else if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8N) {
220                         //FIXME: The _N_N_N_ version gives a gray border around images, see bug 906376
221                         // This mode is only used when exporting, screen rendering always has _P_P_P_, so I decided to simply replace it for now
222                         // Feel free to propose a better fix
224                         //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);
225                         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);
226                 }
228                 pb->empty = FALSE;
230         } else { // outline; draw a rect instead
232                 if (!ct) 
233                         return item->state;
235                 guint32 rgba = prefs_get_int_attribute("options.wireframecolors", "images", 0xff0000ff);
236       // FIXME: we use RGBA buffers but cairo writes BGRA (on i386), so we must cheat 
237       // by setting color channels in the "wrong" order
238       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));
240       cairo_set_line_width(ct, 0.5);
241       cairo_new_path(ct);
243                 NR::Point shift(pb->area.x0, pb->area.y0);
244                 NR::Point c00 = image->c00 - shift;
245                 NR::Point c01 = image->c01 - shift;
246                 NR::Point c11 = image->c11 - shift;
247                 NR::Point c10 = image->c10 - shift;
249                 cairo_move_to (ct, c00[NR::X], c00[NR::Y]);
251                 // the box
252                 cairo_line_to (ct, c10[NR::X], c10[NR::Y]);
253                 cairo_line_to (ct, c11[NR::X], c11[NR::Y]);
254                 cairo_line_to (ct, c01[NR::X], c01[NR::Y]);
255                 cairo_line_to (ct, c00[NR::X], c00[NR::Y]);
256                 // the diagonals
257                 cairo_line_to (ct, c11[NR::X], c11[NR::Y]);
258                 cairo_move_to (ct, c10[NR::X], c10[NR::Y]);
259                 cairo_line_to (ct, c01[NR::X], c01[NR::Y]);
261       cairo_stroke(ct);
263                 pb->empty = FALSE;
264         }
266         return item->state;
269 /** Calculates the closest distance from p to the segment a1-a2*/
270 double 
271 distance_to_segment (NR::Point p, NR::Point a1, NR::Point a2)
273         // calculate sides of the triangle and their squares
274         double d1 = NR::L2(p - a1); 
275         double d1_2 = d1 * d1;
276         double d2 = NR::L2(p - a2);
277         double d2_2 = d2 * d2;
278         double a = NR::L2(a1 - a2); 
279         double a_2 = a * a;
281         // if one of the angles at the base is > 90, return the corresponding side
282         if (d1_2 + a_2 <= d2_2) return d1;
283         if (d2_2 + a_2 <= d1_2) return d2;
285         // otherwise calculate the height to the base
286         double peri = (a + d1 + d2)/2;
287         return (2*sqrt(peri * (peri - a) * (peri - d1) * (peri - d2))/a);
290 static NRArenaItem *
291 nr_arena_image_pick (NRArenaItem *item, NR::Point p, double delta, unsigned int sticky)
293         NRArenaImage *image = NR_ARENA_IMAGE (item);
295         if (!image->px) return NULL;
297         bool outline = (item->arena->rendermode == RENDERMODE_OUTLINE);
299         if (outline) {
301                 // frame
302                 if (distance_to_segment (p, image->c00, image->c10) < delta) return item;
303                 if (distance_to_segment (p, image->c10, image->c11) < delta) return item;
304                 if (distance_to_segment (p, image->c11, image->c01) < delta) return item;
305                 if (distance_to_segment (p, image->c01, image->c00) < delta) return item;
307                 // diagonals
308                 if (distance_to_segment (p, image->c00, image->c11) < delta) return item;
309                 if (distance_to_segment (p, image->c10, image->c01) < delta) return item;
311                 return NULL;
313         } else {
315                 unsigned char *const pixels = image->px;
316                 int const width = image->pxw;
317                 int const height = image->pxh;
318                 int const rowstride = image->pxrs;
319                 NR::Point tp = p * image->grid2px;
320                 int const ix = (int)(tp[NR::X]);
321                 int const iy = (int)(tp[NR::Y]);
323                 if ((ix < 0) || (iy < 0) || (ix >= width) || (iy >= height))
324                         return NULL;
326                 unsigned char *pix_ptr = pixels + iy * rowstride + ix * 4;
327                 // is the alpha not transparent?
328                 return (pix_ptr[3] > 0) ? item : NULL;
329         }
332 /* Utility */
334 void
335 nr_arena_image_set_pixels (NRArenaImage *image, unsigned char const *px, unsigned int pxw, unsigned int pxh, unsigned int pxrs)
337         nr_return_if_fail (image != NULL);
338         nr_return_if_fail (NR_IS_ARENA_IMAGE (image));
340         image->px = (unsigned char *) px;
341         image->pxw = pxw;
342         image->pxh = pxh;
343         image->pxrs = pxrs;
345         nr_arena_item_request_update (NR_ARENA_ITEM (image), NR_ARENA_ITEM_STATE_ALL, FALSE);
348 void
349 nr_arena_image_set_geometry (NRArenaImage *image, double x, double y, double width, double height)
351         nr_return_if_fail (image != NULL);
352         nr_return_if_fail (NR_IS_ARENA_IMAGE (image));
354         image->x = x;
355         image->y = y;
356         image->width = width;
357         image->height = height;
359         nr_arena_item_request_update (NR_ARENA_ITEM (image), NR_ARENA_ITEM_STATE_ALL, FALSE);
362 void nr_arena_image_set_style (NRArenaImage *image, SPStyle *style)
364     g_return_if_fail(image != NULL);
365     g_return_if_fail(NR_IS_ARENA_IMAGE(image));
367     if (style) sp_style_ref(style);
368     if (image->style) sp_style_unref(image->style);
369     image->style = style;
371     //if image has a filter
372     if (style->filter.set && style->getFilter()) {
373         if (!image->filter) {
374             int primitives = sp_filter_primitive_count(SP_FILTER(style->getFilter()));
375             image->filter = new NR::Filter(primitives);
376         }
377         sp_filter_build_renderer(SP_FILTER(style->getFilter()), image->filter);
378     } else {
379         //no filter set for this image
380         delete image->filter;
381         image->filter = NULL;
382     }
384     if (style && style->enable_background.set
385         && style->enable_background.value == SP_CSS_BACKGROUND_NEW) {
386         image->background_new = true;
387     }
390 /*
391   Local Variables:
392   mode:c++
393   c-file-style:"stroustrup"
394   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
395   indent-tabs-mode:nil
396   fill-column:99
397   End:
398 */
399 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :