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"
28 int nr_arena_image_x_sample = 1;
29 int nr_arena_image_y_sample = 1;
31 /*
32 * NRArenaCanvasImage
33 *
34 */
36 // defined in nr-arena-shape.cpp
37 void nr_pixblock_render_shape_mask_or(NRPixBlock &m, Shape *theS);
39 static void nr_arena_image_class_init (NRArenaImageClass *klass);
40 static void nr_arena_image_init (NRArenaImage *image);
41 static void nr_arena_image_finalize (NRObject *object);
43 static unsigned int nr_arena_image_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset);
44 static unsigned int nr_arena_image_render (cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags);
45 static NRArenaItem *nr_arena_image_pick (NRArenaItem *item, NR::Point p, double delta, unsigned int sticky);
47 static NRArenaItemClass *parent_class;
49 NRType
50 nr_arena_image_get_type (void)
51 {
52 static NRType type = 0;
53 if (!type) {
54 type = nr_object_register_type (NR_TYPE_ARENA_ITEM,
55 "NRArenaImage",
56 sizeof (NRArenaImageClass),
57 sizeof (NRArenaImage),
58 (void (*) (NRObjectClass *)) nr_arena_image_class_init,
59 (void (*) (NRObject *)) nr_arena_image_init);
60 }
61 return type;
62 }
64 static void
65 nr_arena_image_class_init (NRArenaImageClass *klass)
66 {
67 NRObjectClass *object_class;
68 NRArenaItemClass *item_class;
70 object_class = (NRObjectClass *) klass;
71 item_class = (NRArenaItemClass *) klass;
73 parent_class = (NRArenaItemClass *) ((NRObjectClass *) klass)->parent;
75 object_class->finalize = nr_arena_image_finalize;
76 object_class->cpp_ctor = NRObject::invoke_ctor<NRArenaImage>;
78 item_class->update = nr_arena_image_update;
79 item_class->render = nr_arena_image_render;
80 item_class->pick = nr_arena_image_pick;
81 }
83 static void
84 nr_arena_image_init (NRArenaImage *image)
85 {
86 image->px = NULL;
88 image->pxw = image->pxh = image->pxrs = 0;
89 image->x = image->y = 0.0;
90 image->width = 256.0;
91 image->height = 256.0;
93 nr_matrix_set_identity (&image->grid2px);
95 image->style = 0;
96 }
98 static void
99 nr_arena_image_finalize (NRObject *object)
100 {
101 NRArenaImage *image = NR_ARENA_IMAGE (object);
103 image->px = NULL;
105 ((NRObjectClass *) parent_class)->finalize (object);
106 }
108 static unsigned int
109 nr_arena_image_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset)
110 {
111 NRMatrix grid2px;
113 NRArenaImage *image = NR_ARENA_IMAGE (item);
115 /* Request render old */
116 nr_arena_item_request_render (item);
118 /* Copy affine */
119 nr_matrix_invert (&grid2px, &gc->transform);
120 double hscale, vscale; // todo: replace with NR::scale
121 if (image->px) {
122 hscale = image->pxw / image->width;
123 vscale = image->pxh / image->height;
124 } else {
125 hscale = 1.0;
126 vscale = 1.0;
127 }
129 image->grid2px[0] = grid2px.c[0] * hscale;
130 image->grid2px[2] = grid2px.c[2] * hscale;
131 image->grid2px[4] = grid2px.c[4] * hscale;
132 image->grid2px[1] = grid2px.c[1] * vscale;
133 image->grid2px[3] = grid2px.c[3] * vscale;
134 image->grid2px[5] = grid2px.c[5] * vscale;
136 image->grid2px[4] -= image->x * hscale;
137 image->grid2px[5] -= image->y * vscale;
139 /* Calculate bbox */
140 if (image->px) {
141 NRRect bbox;
143 bbox.x0 = image->x;
144 bbox.y0 = image->y;
145 bbox.x1 = image->x + image->width;
146 bbox.y1 = image->y + image->height;
148 image->c00 = (NR::Point(bbox.x0, bbox.y0) * gc->transform);
149 image->c01 = (NR::Point(bbox.x0, bbox.y1) * gc->transform);
150 image->c10 = (NR::Point(bbox.x1, bbox.y0) * gc->transform);
151 image->c11 = (NR::Point(bbox.x1, bbox.y1) * gc->transform);
153 nr_rect_d_matrix_transform (&bbox, &bbox, &gc->transform);
155 item->bbox.x0 = (int) floor (bbox.x0);
156 item->bbox.y0 = (int) floor (bbox.y0);
157 item->bbox.x1 = (int) ceil (bbox.x1);
158 item->bbox.y1 = (int) ceil (bbox.y1);
159 } else {
160 item->bbox.x0 = (int) gc->transform[4];
161 item->bbox.y0 = (int) gc->transform[5];
162 item->bbox.x1 = item->bbox.x0 - 1;
163 item->bbox.y1 = item->bbox.y0 - 1;
164 }
166 nr_arena_item_request_render (item);
168 return NR_ARENA_ITEM_STATE_ALL;
169 }
171 #define FBITS 12
172 #define b2i (image->grid2px)
174 static unsigned int
175 nr_arena_image_render (cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags)
176 {
177 nr_arena_image_x_sample = prefs_get_int_attribute ("options.bitmapoversample", "value", 1);
178 nr_arena_image_y_sample = nr_arena_image_x_sample;
180 bool outline = (item->arena->rendermode == RENDERMODE_OUTLINE);
182 NRArenaImage *image = NR_ARENA_IMAGE (item);
184 NR::Matrix d2s;
186 d2s[0] = b2i[0];
187 d2s[1] = b2i[1];
188 d2s[2] = b2i[2];
189 d2s[3] = b2i[3];
190 d2s[4] = b2i[0] * pb->area.x0 + b2i[2] * pb->area.y0 + b2i[4];
191 d2s[5] = b2i[1] * pb->area.x0 + b2i[3] * pb->area.y0 + b2i[5];
193 if (!outline) {
195 if (!image->px) return item->state;
197 guint32 Falpha = item->opacity;
198 if (Falpha < 1) return item->state;
200 unsigned char * dpx = NR_PIXBLOCK_PX (pb);
201 const int drs = pb->rs;
202 const int dw = pb->area.x1 - pb->area.x0;
203 const int dh = pb->area.y1 - pb->area.y0;
205 unsigned char * spx = image->px;
206 const int srs = image->pxrs;
207 const int sw = image->pxw;
208 const int sh = image->pxh;
210 if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8) {
211 /* fixme: This is not implemented yet (Lauris) */
212 /* 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); */
213 } else if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
214 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);
215 } else if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8N) {
217 //FIXME: The _N_N_N_ version gives a gray border around images, see bug 906376
218 // This mode is only used when exporting, screen rendering always has _P_P_P_, so I decided to simply replace it for now
219 // Feel free to propose a better fix
221 //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);
222 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);
223 }
225 pb->empty = FALSE;
227 } else { // outline; draw a rect instead
229 Path* thePath = new Path;
230 thePath->SetBackData (false);
231 thePath->Reset ();
233 thePath->MoveTo (image->c00);
234 // the box
235 thePath->LineTo (image->c10);
236 thePath->LineTo (image->c11);
237 thePath->LineTo (image->c01);
238 thePath->LineTo (image->c00);
239 // the diagonals
240 thePath->LineTo (image->c11);
241 thePath->MoveTo (image->c10);
242 thePath->LineTo (image->c01);
244 // livarot black magic - exactly this sequnce of conversions to get stable rendering of the shape
245 thePath->Convert(1.0);
246 Shape* theShape = new Shape;
247 thePath->Stroke(theShape, true, 0.25, join_pointy, butt_straight, 5);
248 Shape* theShape1 = new Shape;
249 theShape1->ConvertToShape(theShape, fill_nonZero);
251 NRPixBlock m;
252 nr_pixblock_setup_fast(&m, NR_PIXBLOCK_MODE_A8, area->x0, area->y0, area->x1, area->y1, TRUE);
253 m.visible_area = pb->visible_area;
254 nr_pixblock_render_shape_mask_or(m, theShape1);
255 m.empty = FALSE;
256 guint32 rgba = prefs_get_int_attribute("options.wireframecolors", "images", 0xff0000ff);
257 nr_blit_pixblock_mask_rgba32(pb, &m, rgba);
258 pb->empty = FALSE;
259 nr_pixblock_release(&m);
260 delete theShape;
261 delete theShape1;
262 delete thePath;
263 }
265 return item->state;
266 }
268 static NRArenaItem *
269 nr_arena_image_pick (NRArenaItem *item, NR::Point p, double delta, unsigned int sticky)
270 {
271 NRArenaImage *image = NR_ARENA_IMAGE (item);
273 if (!image->px) return NULL;
275 unsigned char * const pixels = image->px;
276 const int width = image->pxw;
277 const int height = image->pxh;
278 const int rowstride = image->pxrs;
279 NR::Point tp = p * image->grid2px;
280 const int ix = (int)(tp[NR::X]);
281 const int iy = (int)(tp[NR::Y]);
283 if ((ix < 0) || (iy < 0) || (ix >= width) || (iy >= height))
284 return NULL;
286 unsigned char *pix_ptr = pixels + iy * rowstride + ix * 4;
287 // is the alpha not transparent?
288 return (pix_ptr[3] > 0) ? item : NULL;
289 }
291 /* Utility */
293 void
294 nr_arena_image_set_pixels (NRArenaImage *image, const unsigned char *px, unsigned int pxw, unsigned int pxh, unsigned int pxrs)
295 {
296 nr_return_if_fail (image != NULL);
297 nr_return_if_fail (NR_IS_ARENA_IMAGE (image));
299 image->px = (unsigned char *) px;
300 image->pxw = pxw;
301 image->pxh = pxh;
302 image->pxrs = pxrs;
304 nr_arena_item_request_update (NR_ARENA_ITEM (image), NR_ARENA_ITEM_STATE_ALL, FALSE);
305 }
307 void
308 nr_arena_image_set_geometry (NRArenaImage *image, double x, double y, double width, double height)
309 {
310 nr_return_if_fail (image != NULL);
311 nr_return_if_fail (NR_IS_ARENA_IMAGE (image));
313 image->x = x;
314 image->y = y;
315 image->width = width;
316 image->height = height;
318 nr_arena_item_request_update (NR_ARENA_ITEM (image), NR_ARENA_ITEM_STATE_ALL, FALSE);
319 }
321 void nr_arena_image_set_style (NRArenaImage *image, SPStyle *style)
322 {
323 g_return_if_fail(image != NULL);
324 g_return_if_fail(NR_IS_ARENA_IMAGE(image));
326 if (style) sp_style_ref(style);
327 if (image->style) sp_style_unref(image->style);
328 image->style = style;
330 //if there is a filter set for this group
331 if (style && style->filter.set && style->filter.filter) {
333 image->filter = new NR::Filter();
334 image->filter->set_x(style->filter.filter->x);
335 image->filter->set_y(style->filter.filter->y);
336 image->filter->set_width(style->filter.filter->width);
337 image->filter->set_height(style->filter.filter->height);
339 //go through all SP filter primitives
340 for(int i=0; i<style->filter.filter->_primitive_count; i++)
341 {
342 SPFilterPrimitive *primitive = style->filter.filter->_primitives[i];
343 //if primitive is gaussianblur
344 // if(SP_IS_GAUSSIANBLUR(primitive))
345 {
346 NR::FilterGaussian * gaussian = (NR::FilterGaussian *) image->filter->add_primitive(NR::NR_FILTER_GAUSSIANBLUR);
347 SPGaussianBlur * spblur = SP_GAUSSIANBLUR(primitive);
348 float num = spblur->stdDeviation.getNumber();
349 if( num>=0.0 )
350 {
351 float optnum = spblur->stdDeviation.getOptNumber();
352 if( optnum>=0.0 )
353 gaussian->set_deviation((double) num, (double) optnum);
354 else
355 gaussian->set_deviation((double) num);
356 }
357 }
358 }
359 }
360 else
361 {
362 //no filter set for this group
363 image->filter = NULL;
364 }
366 if (style && style->enable_background.set
367 && style->enable_background.value == SP_CSS_BACKGROUND_NEW) {
368 image->background_new = true;
369 }
370 }