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 "sp-filter.h"
24 #include "sp-filter-reference.h"
25 #include "sp-gaussian-blur.h"
26 #include "sp-feblend.h"
27 #include "display/nr-filter-blend.h"
29 int nr_arena_image_x_sample = 1;
30 int nr_arena_image_y_sample = 1;
32 /*
33 * NRArenaCanvasImage
34 *
35 */
37 static void nr_arena_image_class_init (NRArenaImageClass *klass);
38 static void nr_arena_image_init (NRArenaImage *image);
39 static void nr_arena_image_finalize (NRObject *object);
41 static unsigned int nr_arena_image_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset);
42 static unsigned int nr_arena_image_render (cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags);
43 static NRArenaItem *nr_arena_image_pick (NRArenaItem *item, NR::Point p, double delta, unsigned int sticky);
45 static NRArenaItemClass *parent_class;
47 NRType
48 nr_arena_image_get_type (void)
49 {
50 static NRType type = 0;
51 if (!type) {
52 type = nr_object_register_type (NR_TYPE_ARENA_ITEM,
53 "NRArenaImage",
54 sizeof (NRArenaImageClass),
55 sizeof (NRArenaImage),
56 (void (*) (NRObjectClass *)) nr_arena_image_class_init,
57 (void (*) (NRObject *)) nr_arena_image_init);
58 }
59 return type;
60 }
62 static void
63 nr_arena_image_class_init (NRArenaImageClass *klass)
64 {
65 NRObjectClass *object_class;
66 NRArenaItemClass *item_class;
68 object_class = (NRObjectClass *) klass;
69 item_class = (NRArenaItemClass *) klass;
71 parent_class = (NRArenaItemClass *) ((NRObjectClass *) klass)->parent;
73 object_class->finalize = nr_arena_image_finalize;
74 object_class->cpp_ctor = NRObject::invoke_ctor<NRArenaImage>;
76 item_class->update = nr_arena_image_update;
77 item_class->render = nr_arena_image_render;
78 item_class->pick = nr_arena_image_pick;
79 }
81 static void
82 nr_arena_image_init (NRArenaImage *image)
83 {
84 image->px = NULL;
86 image->pxw = image->pxh = image->pxrs = 0;
87 image->x = image->y = 0.0;
88 image->width = 256.0;
89 image->height = 256.0;
91 image->grid2px.set_identity();
93 image->style = 0;
94 }
96 static void
97 nr_arena_image_finalize (NRObject *object)
98 {
99 NRArenaImage *image = NR_ARENA_IMAGE (object);
101 image->px = NULL;
103 ((NRObjectClass *) parent_class)->finalize (object);
104 }
106 static unsigned int
107 nr_arena_image_update( NRArenaItem *item, NRRectL */*area*/, NRGC *gc, unsigned int /*state*/, unsigned int /*reset*/ )
108 {
109 NR::Matrix grid2px;
111 NRArenaImage *image = NR_ARENA_IMAGE (item);
113 /* Request render old */
114 nr_arena_item_request_render (item);
116 /* Copy affine */
117 grid2px = gc->transform.inverse();
118 double hscale, vscale; // todo: replace with NR::scale
119 if (image->px) {
120 hscale = image->pxw / image->width;
121 vscale = image->pxh / image->height;
122 } else {
123 hscale = 1.0;
124 vscale = 1.0;
125 }
127 image->grid2px[0] = grid2px[0] * hscale;
128 image->grid2px[2] = grid2px[2] * hscale;
129 image->grid2px[4] = grid2px[4] * hscale;
130 image->grid2px[1] = grid2px[1] * vscale;
131 image->grid2px[3] = grid2px[3] * vscale;
132 image->grid2px[5] = grid2px[5] * vscale;
134 image->grid2px[4] -= image->x * hscale;
135 image->grid2px[5] -= image->y * vscale;
137 /* Calculate bbox */
138 if (image->px) {
139 NRRect bbox;
141 bbox.x0 = image->x;
142 bbox.y0 = image->y;
143 bbox.x1 = image->x + image->width;
144 bbox.y1 = image->y + image->height;
146 image->c00 = (NR::Point(bbox.x0, bbox.y0) * gc->transform);
147 image->c01 = (NR::Point(bbox.x0, bbox.y1) * gc->transform);
148 image->c10 = (NR::Point(bbox.x1, bbox.y0) * gc->transform);
149 image->c11 = (NR::Point(bbox.x1, bbox.y1) * gc->transform);
151 nr_rect_d_matrix_transform (&bbox, &bbox, &gc->transform);
153 item->bbox.x0 = (int) floor (bbox.x0);
154 item->bbox.y0 = (int) floor (bbox.y0);
155 item->bbox.x1 = (int) ceil (bbox.x1);
156 item->bbox.y1 = (int) ceil (bbox.y1);
157 } else {
158 item->bbox.x0 = (int) gc->transform[4];
159 item->bbox.y0 = (int) gc->transform[5];
160 item->bbox.x1 = item->bbox.x0 - 1;
161 item->bbox.y1 = item->bbox.y0 - 1;
162 }
164 nr_arena_item_request_render (item);
166 return NR_ARENA_ITEM_STATE_ALL;
167 }
169 #define FBITS 12
170 #define b2i (image->grid2px)
172 static unsigned int
173 nr_arena_image_render( cairo_t *ct, NRArenaItem *item, NRRectL */*area*/, NRPixBlock *pb, unsigned int /*flags*/ )
174 {
175 nr_arena_image_x_sample = prefs_get_int_attribute ("options.bitmapoversample", "value", 1);
176 nr_arena_image_y_sample = nr_arena_image_x_sample;
178 bool outline = (item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE);
180 NRArenaImage *image = NR_ARENA_IMAGE (item);
182 NR::Matrix d2s;
184 d2s[0] = b2i[0];
185 d2s[1] = b2i[1];
186 d2s[2] = b2i[2];
187 d2s[3] = b2i[3];
188 d2s[4] = b2i[0] * pb->area.x0 + b2i[2] * pb->area.y0 + b2i[4];
189 d2s[5] = b2i[1] * pb->area.x0 + b2i[3] * pb->area.y0 + b2i[5];
191 if (!outline) {
193 if (!image->px) return item->state;
195 guint32 Falpha = item->opacity;
196 if (Falpha < 1) return item->state;
198 unsigned char * dpx = NR_PIXBLOCK_PX (pb);
199 int const drs = pb->rs;
200 int const dw = pb->area.x1 - pb->area.x0;
201 int const dh = pb->area.y1 - pb->area.y0;
203 unsigned char * spx = image->px;
204 int const srs = image->pxrs;
205 int const sw = image->pxw;
206 int const sh = image->pxh;
208 if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8) {
209 /* fixme: This is not implemented yet (Lauris) */
210 /* 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); */
211 } else if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
212 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);
213 } else if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8N) {
215 //FIXME: The _N_N_N_ version gives a gray border around images, see bug 906376
216 // This mode is only used when exporting, screen rendering always has _P_P_P_, so I decided to simply replace it for now
217 // Feel free to propose a better fix
219 //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);
220 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);
221 }
223 pb->empty = FALSE;
225 } else { // outline; draw a rect instead
227 if (!ct)
228 return item->state;
230 guint32 rgba = prefs_get_int_attribute("options.wireframecolors", "images", 0xff0000ff);
231 // FIXME: we use RGBA buffers but cairo writes BGRA (on i386), so we must cheat
232 // by setting color channels in the "wrong" order
233 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));
235 cairo_set_line_width(ct, 0.5);
236 cairo_new_path(ct);
238 NR::Point shift(pb->area.x0, pb->area.y0);
239 NR::Point c00 = image->c00 - shift;
240 NR::Point c01 = image->c01 - shift;
241 NR::Point c11 = image->c11 - shift;
242 NR::Point c10 = image->c10 - shift;
244 cairo_move_to (ct, c00[NR::X], c00[NR::Y]);
246 // the box
247 cairo_line_to (ct, c10[NR::X], c10[NR::Y]);
248 cairo_line_to (ct, c11[NR::X], c11[NR::Y]);
249 cairo_line_to (ct, c01[NR::X], c01[NR::Y]);
250 cairo_line_to (ct, c00[NR::X], c00[NR::Y]);
251 // the diagonals
252 cairo_line_to (ct, c11[NR::X], c11[NR::Y]);
253 cairo_move_to (ct, c10[NR::X], c10[NR::Y]);
254 cairo_line_to (ct, c01[NR::X], c01[NR::Y]);
256 cairo_stroke(ct);
258 pb->empty = FALSE;
259 }
261 return item->state;
262 }
264 /** Calculates the closest distance from p to the segment a1-a2*/
265 double
266 distance_to_segment (NR::Point p, NR::Point a1, NR::Point a2)
267 {
268 // calculate sides of the triangle and their squares
269 double d1 = NR::L2(p - a1);
270 double d1_2 = d1 * d1;
271 double d2 = NR::L2(p - a2);
272 double d2_2 = d2 * d2;
273 double a = NR::L2(a1 - a2);
274 double a_2 = a * a;
276 // if one of the angles at the base is > 90, return the corresponding side
277 if (d1_2 + a_2 <= d2_2) return d1;
278 if (d2_2 + a_2 <= d1_2) return d2;
280 // otherwise calculate the height to the base
281 double peri = (a + d1 + d2)/2;
282 return (2*sqrt(peri * (peri - a) * (peri - d1) * (peri - d2))/a);
283 }
285 static NRArenaItem *
286 nr_arena_image_pick( NRArenaItem *item, NR::Point p, double delta, unsigned int /*sticky*/ )
287 {
288 NRArenaImage *image = NR_ARENA_IMAGE (item);
290 if (!image->px) return NULL;
292 bool outline = (item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE);
294 if (outline) {
296 // frame
297 if (distance_to_segment (p, image->c00, image->c10) < delta) return item;
298 if (distance_to_segment (p, image->c10, image->c11) < delta) return item;
299 if (distance_to_segment (p, image->c11, image->c01) < delta) return item;
300 if (distance_to_segment (p, image->c01, image->c00) < delta) return item;
302 // diagonals
303 if (distance_to_segment (p, image->c00, image->c11) < delta) return item;
304 if (distance_to_segment (p, image->c10, image->c01) < delta) return item;
306 return NULL;
308 } else {
310 unsigned char *const pixels = image->px;
311 int const width = image->pxw;
312 int const height = image->pxh;
313 int const rowstride = image->pxrs;
314 NR::Point tp = p * image->grid2px;
315 int const ix = (int)(tp[NR::X]);
316 int const iy = (int)(tp[NR::Y]);
318 if ((ix < 0) || (iy < 0) || (ix >= width) || (iy >= height))
319 return NULL;
321 unsigned char *pix_ptr = pixels + iy * rowstride + ix * 4;
322 // is the alpha not transparent?
323 return (pix_ptr[3] > 0) ? item : NULL;
324 }
325 }
327 /* Utility */
329 void
330 nr_arena_image_set_pixels (NRArenaImage *image, unsigned char const *px, unsigned int pxw, unsigned int pxh, unsigned int pxrs)
331 {
332 nr_return_if_fail (image != NULL);
333 nr_return_if_fail (NR_IS_ARENA_IMAGE (image));
335 image->px = (unsigned char *) px;
336 image->pxw = pxw;
337 image->pxh = pxh;
338 image->pxrs = pxrs;
340 nr_arena_item_request_update (NR_ARENA_ITEM (image), NR_ARENA_ITEM_STATE_ALL, FALSE);
341 }
343 void
344 nr_arena_image_set_geometry (NRArenaImage *image, double x, double y, double width, double height)
345 {
346 nr_return_if_fail (image != NULL);
347 nr_return_if_fail (NR_IS_ARENA_IMAGE (image));
349 image->x = x;
350 image->y = y;
351 image->width = width;
352 image->height = height;
354 nr_arena_item_request_update (NR_ARENA_ITEM (image), NR_ARENA_ITEM_STATE_ALL, FALSE);
355 }
357 void nr_arena_image_set_style (NRArenaImage *image, SPStyle *style)
358 {
359 g_return_if_fail(image != NULL);
360 g_return_if_fail(NR_IS_ARENA_IMAGE(image));
362 if (style) sp_style_ref(style);
363 if (image->style) sp_style_unref(image->style);
364 image->style = style;
366 //if image has a filter
367 if (style->filter.set && style->getFilter()) {
368 if (!image->filter) {
369 int primitives = sp_filter_primitive_count(SP_FILTER(style->getFilter()));
370 image->filter = new NR::Filter(primitives);
371 }
372 sp_filter_build_renderer(SP_FILTER(style->getFilter()), image->filter);
373 } else {
374 //no filter set for this image
375 delete image->filter;
376 image->filter = NULL;
377 }
379 if (style && style->enable_background.set
380 && style->enable_background.value == SP_CSS_BACKGROUND_NEW) {
381 image->background_new = true;
382 }
383 }
386 /*
387 Local Variables:
388 mode:c++
389 c-file-style:"stroustrup"
390 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
391 indent-tabs-mode:nil
392 fill-column:99
393 End:
394 */
395 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :