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-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 // defined in nr-arena-shape.cpp
39 void nr_pixblock_render_shape_mask_or(NRPixBlock &m, Shape *theS);
41 static void nr_arena_image_class_init (NRArenaImageClass *klass);
42 static void nr_arena_image_init (NRArenaImage *image);
43 static void nr_arena_image_finalize (NRObject *object);
45 static unsigned int nr_arena_image_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset);
46 static unsigned int nr_arena_image_render (cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags);
47 static NRArenaItem *nr_arena_image_pick (NRArenaItem *item, NR::Point p, double delta, unsigned int sticky);
49 static NRArenaItemClass *parent_class;
51 NRType
52 nr_arena_image_get_type (void)
53 {
54 static NRType type = 0;
55 if (!type) {
56 type = nr_object_register_type (NR_TYPE_ARENA_ITEM,
57 "NRArenaImage",
58 sizeof (NRArenaImageClass),
59 sizeof (NRArenaImage),
60 (void (*) (NRObjectClass *)) nr_arena_image_class_init,
61 (void (*) (NRObject *)) nr_arena_image_init);
62 }
63 return type;
64 }
66 static void
67 nr_arena_image_class_init (NRArenaImageClass *klass)
68 {
69 NRObjectClass *object_class;
70 NRArenaItemClass *item_class;
72 object_class = (NRObjectClass *) klass;
73 item_class = (NRArenaItemClass *) klass;
75 parent_class = (NRArenaItemClass *) ((NRObjectClass *) klass)->parent;
77 object_class->finalize = nr_arena_image_finalize;
78 object_class->cpp_ctor = NRObject::invoke_ctor<NRArenaImage>;
80 item_class->update = nr_arena_image_update;
81 item_class->render = nr_arena_image_render;
82 item_class->pick = nr_arena_image_pick;
83 }
85 static void
86 nr_arena_image_init (NRArenaImage *image)
87 {
88 image->px = NULL;
90 image->pxw = image->pxh = image->pxrs = 0;
91 image->x = image->y = 0.0;
92 image->width = 256.0;
93 image->height = 256.0;
95 nr_matrix_set_identity (&image->grid2px);
97 image->style = 0;
98 }
100 static void
101 nr_arena_image_finalize (NRObject *object)
102 {
103 NRArenaImage *image = NR_ARENA_IMAGE (object);
105 image->px = NULL;
107 ((NRObjectClass *) parent_class)->finalize (object);
108 }
110 static unsigned int
111 nr_arena_image_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset)
112 {
113 NRMatrix grid2px;
115 NRArenaImage *image = NR_ARENA_IMAGE (item);
117 /* Request render old */
118 nr_arena_item_request_render (item);
120 /* Copy affine */
121 nr_matrix_invert (&grid2px, &gc->transform);
122 double hscale, vscale; // todo: replace with NR::scale
123 if (image->px) {
124 hscale = image->pxw / image->width;
125 vscale = image->pxh / image->height;
126 } else {
127 hscale = 1.0;
128 vscale = 1.0;
129 }
131 image->grid2px[0] = grid2px.c[0] * hscale;
132 image->grid2px[2] = grid2px.c[2] * hscale;
133 image->grid2px[4] = grid2px.c[4] * hscale;
134 image->grid2px[1] = grid2px.c[1] * vscale;
135 image->grid2px[3] = grid2px.c[3] * vscale;
136 image->grid2px[5] = grid2px.c[5] * vscale;
138 image->grid2px[4] -= image->x * hscale;
139 image->grid2px[5] -= image->y * vscale;
141 /* Calculate bbox */
142 if (image->px) {
143 NRRect bbox;
145 bbox.x0 = image->x;
146 bbox.y0 = image->y;
147 bbox.x1 = image->x + image->width;
148 bbox.y1 = image->y + image->height;
150 image->c00 = (NR::Point(bbox.x0, bbox.y0) * gc->transform);
151 image->c01 = (NR::Point(bbox.x0, bbox.y1) * gc->transform);
152 image->c10 = (NR::Point(bbox.x1, bbox.y0) * gc->transform);
153 image->c11 = (NR::Point(bbox.x1, bbox.y1) * gc->transform);
155 nr_rect_d_matrix_transform (&bbox, &bbox, &gc->transform);
157 item->bbox.x0 = (int) floor (bbox.x0);
158 item->bbox.y0 = (int) floor (bbox.y0);
159 item->bbox.x1 = (int) ceil (bbox.x1);
160 item->bbox.y1 = (int) ceil (bbox.y1);
161 } else {
162 item->bbox.x0 = (int) gc->transform[4];
163 item->bbox.y0 = (int) gc->transform[5];
164 item->bbox.x1 = item->bbox.x0 - 1;
165 item->bbox.y1 = item->bbox.y0 - 1;
166 }
168 nr_arena_item_request_render (item);
170 return NR_ARENA_ITEM_STATE_ALL;
171 }
173 #define FBITS 12
174 #define b2i (image->grid2px)
176 static unsigned int
177 nr_arena_image_render (cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags)
178 {
179 nr_arena_image_x_sample = prefs_get_int_attribute ("options.bitmapoversample", "value", 1);
180 nr_arena_image_y_sample = nr_arena_image_x_sample;
182 bool outline = (item->arena->rendermode == RENDERMODE_OUTLINE);
184 NRArenaImage *image = NR_ARENA_IMAGE (item);
186 NR::Matrix d2s;
188 d2s[0] = b2i[0];
189 d2s[1] = b2i[1];
190 d2s[2] = b2i[2];
191 d2s[3] = b2i[3];
192 d2s[4] = b2i[0] * pb->area.x0 + b2i[2] * pb->area.y0 + b2i[4];
193 d2s[5] = b2i[1] * pb->area.x0 + b2i[3] * pb->area.y0 + b2i[5];
195 if (!outline) {
197 if (!image->px) return item->state;
199 guint32 Falpha = item->opacity;
200 if (Falpha < 1) return item->state;
202 unsigned char * dpx = NR_PIXBLOCK_PX (pb);
203 const int drs = pb->rs;
204 const int dw = pb->area.x1 - pb->area.x0;
205 const int dh = pb->area.y1 - pb->area.y0;
207 unsigned char * spx = image->px;
208 const int srs = image->pxrs;
209 const int sw = image->pxw;
210 const int sh = image->pxh;
212 if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8) {
213 /* fixme: This is not implemented yet (Lauris) */
214 /* 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); */
215 } else if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
216 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);
217 } else if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8N) {
219 //FIXME: The _N_N_N_ version gives a gray border around images, see bug 906376
220 // This mode is only used when exporting, screen rendering always has _P_P_P_, so I decided to simply replace it for now
221 // Feel free to propose a better fix
223 //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);
224 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);
225 }
227 pb->empty = FALSE;
229 } else { // outline; draw a rect instead
231 if (!ct)
232 return item->state;
234 guint32 rgba = prefs_get_int_attribute("options.wireframecolors", "images", 0xff0000ff);
235 // FIXME: we use RGBA buffers but cairo writes BGRA (on i386), so we must cheat
236 // by setting color channels in the "wrong" order
237 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));
239 cairo_set_line_width(ct, 0.5);
240 cairo_new_path(ct);
242 NR::Point shift(pb->area.x0, pb->area.y0);
243 NR::Point c00 = image->c00 - shift;
244 NR::Point c01 = image->c01 - shift;
245 NR::Point c11 = image->c11 - shift;
246 NR::Point c10 = image->c10 - shift;
248 cairo_move_to (ct, c00[NR::X], c00[NR::Y]);
250 // the box
251 cairo_line_to (ct, c10[NR::X], c10[NR::Y]);
252 cairo_line_to (ct, c11[NR::X], c11[NR::Y]);
253 cairo_line_to (ct, c01[NR::X], c01[NR::Y]);
254 cairo_line_to (ct, c00[NR::X], c00[NR::Y]);
255 // the diagonals
256 cairo_line_to (ct, c11[NR::X], c11[NR::Y]);
257 cairo_move_to (ct, c10[NR::X], c10[NR::Y]);
258 cairo_line_to (ct, c01[NR::X], c01[NR::Y]);
260 cairo_stroke(ct);
262 pb->empty = FALSE;
263 }
265 return item->state;
266 }
268 /** Calculates the closest distance from p to the segment a1-a2*/
269 double
270 distance_to_segment (NR::Point p, NR::Point a1, NR::Point a2)
271 {
272 // calculate sides of the triangle and their squares
273 double d1 = NR::L2(p - a1);
274 double d1_2 = d1 * d1;
275 double d2 = NR::L2(p - a2);
276 double d2_2 = d2 * d2;
277 double a = NR::L2(a1 - a2);
278 double a_2 = a * a;
280 // if one of the angles at the base is > 90, return the corresponding side
281 if (d1_2 + a_2 <= d2_2) return d1;
282 if (d2_2 + a_2 <= d1_2) return d2;
284 // otherwise calculate the height to the base
285 double peri = (a + d1 + d2)/2;
286 return (2*sqrt(peri * (peri - a) * (peri - d1) * (peri - d2))/a);
287 }
289 static NRArenaItem *
290 nr_arena_image_pick (NRArenaItem *item, NR::Point p, double delta, unsigned int sticky)
291 {
292 NRArenaImage *image = NR_ARENA_IMAGE (item);
294 if (!image->px) return NULL;
296 bool outline = (item->arena->rendermode == RENDERMODE_OUTLINE);
298 if (outline) {
300 // frame
301 if (distance_to_segment (p, image->c00, image->c10) < delta) return item;
302 if (distance_to_segment (p, image->c10, image->c11) < delta) return item;
303 if (distance_to_segment (p, image->c11, image->c01) < delta) return item;
304 if (distance_to_segment (p, image->c01, image->c00) < delta) return item;
306 // diagonals
307 if (distance_to_segment (p, image->c00, image->c11) < delta) return item;
308 if (distance_to_segment (p, image->c10, image->c01) < delta) return item;
310 return NULL;
312 } else {
314 unsigned char * const pixels = image->px;
315 const int width = image->pxw;
316 const int height = image->pxh;
317 const int rowstride = image->pxrs;
318 NR::Point tp = p * image->grid2px;
319 const int ix = (int)(tp[NR::X]);
320 const int iy = (int)(tp[NR::Y]);
322 if ((ix < 0) || (iy < 0) || (ix >= width) || (iy >= height))
323 return NULL;
325 unsigned char *pix_ptr = pixels + iy * rowstride + ix * 4;
326 // is the alpha not transparent?
327 return (pix_ptr[3] > 0) ? item : NULL;
328 }
329 }
331 /* Utility */
333 void
334 nr_arena_image_set_pixels (NRArenaImage *image, const unsigned char *px, unsigned int pxw, unsigned int pxh, unsigned int pxrs)
335 {
336 nr_return_if_fail (image != NULL);
337 nr_return_if_fail (NR_IS_ARENA_IMAGE (image));
339 image->px = (unsigned char *) px;
340 image->pxw = pxw;
341 image->pxh = pxh;
342 image->pxrs = pxrs;
344 nr_arena_item_request_update (NR_ARENA_ITEM (image), NR_ARENA_ITEM_STATE_ALL, FALSE);
345 }
347 void
348 nr_arena_image_set_geometry (NRArenaImage *image, double x, double y, double width, double height)
349 {
350 nr_return_if_fail (image != NULL);
351 nr_return_if_fail (NR_IS_ARENA_IMAGE (image));
353 image->x = x;
354 image->y = y;
355 image->width = width;
356 image->height = height;
358 nr_arena_item_request_update (NR_ARENA_ITEM (image), NR_ARENA_ITEM_STATE_ALL, FALSE);
359 }
361 void nr_arena_image_set_style (NRArenaImage *image, SPStyle *style)
362 {
363 g_return_if_fail(image != NULL);
364 g_return_if_fail(NR_IS_ARENA_IMAGE(image));
366 if (style) sp_style_ref(style);
367 if (image->style) sp_style_unref(image->style);
368 image->style = style;
370 //if there is a filter set for this group
371 if (style && style->filter.set && style->filter.filter) {
373 image->filter = new NR::Filter();
374 image->filter->set_x(style->filter.filter->x);
375 image->filter->set_y(style->filter.filter->y);
376 image->filter->set_width(style->filter.filter->width);
377 image->filter->set_height(style->filter.filter->height);
379 //go through all SP filter primitives
380 for(int i=0; i<style->filter.filter->_primitive_count; i++)
381 {
382 SPFilterPrimitive *primitive = style->filter.filter->_primitives[i];
383 //if primitive is gaussianblur
384 if(SP_IS_GAUSSIANBLUR(primitive))
385 {
386 NR::FilterGaussian * gaussian = (NR::FilterGaussian *) image->filter->add_primitive(NR::NR_FILTER_GAUSSIANBLUR);
387 SPGaussianBlur * spblur = SP_GAUSSIANBLUR(primitive);
388 float num = spblur->stdDeviation.getNumber();
389 if( num>=0.0 )
390 {
391 float optnum = spblur->stdDeviation.getOptNumber();
392 if( optnum>=0.0 )
393 gaussian->set_deviation((double) num, (double) optnum);
394 else
395 gaussian->set_deviation((double) num);
396 }
397 } else if(SP_IS_FEBLEND(primitive)) {
398 // TODO: this is just a test. Besides this whole filter
399 // creation needs to be redone
400 NR::FilterBlend *nrblend = (NR::FilterBlend *) image->filter->add_primitive(NR::NR_FILTER_BLEND);
401 SPFeBlend *spblend = SP_FEBLEND(primitive);
402 nrblend->set_mode(spblend->blend_mode);
403 }
404 }
405 }
406 else
407 {
408 //no filter set for this group
409 image->filter = NULL;
410 }
412 if (style && style->enable_background.set
413 && style->enable_background.value == SP_CSS_BACKGROUND_NEW) {
414 image->background_new = true;
415 }
416 }