Code

Fixed crash with lighting effects and missing source image
[inkscape.git] / src / display / nr-filter-diffuselighting.cpp
1 /*
2  * feDiffuseLighting renderer
3  *
4  * Authors:
5  *   Niko Kiirala <niko@kiirala.com>
6  *   Jean-Rene Reinhard <jr@komite.net>
7  *
8  * Copyright (C) 2007 authors
9  *
10  * Released under GNU GPL, read the file 'COPYING' for more information
11  */
13 #include <glib/gmessages.h>
15 #include "display/nr-3dutils.h"
16 #include "display/nr-arena-item.h"
17 #include "display/nr-filter-diffuselighting.h"
18 #include "display/nr-filter-getalpha.h"
19 #include "display/nr-filter-slot.h"
20 #include "display/nr-filter-units.h"
21 #include "display/nr-filter-utils.h"
22 #include "display/nr-light.h"
23 #include "libnr/nr-blit.h"
24 #include "libnr/nr-pixblock.h"
25 #include "libnr/nr-matrix.h"
26 #include "libnr/nr-rect-l.h"
27 #include "color.h"
29 namespace NR {
31 FilterDiffuseLighting::FilterDiffuseLighting() 
32 {
33     light_type = NO_LIGHT;
34     diffuseConstant = 1;
35     surfaceScale = 1;
36     lighting_color = 0xffffffff;
37 }
39 FilterPrimitive * FilterDiffuseLighting::create() {
40     return new FilterDiffuseLighting();
41 }
43 FilterDiffuseLighting::~FilterDiffuseLighting()
44 {}
46 #define COMPUTE_INTER(inter, N, L, kd) \
47 do {\
48     (inter) = (kd) * scalar_product((N), (L)); \
49     if ((inter) < 0) (inter) = 0; \
50 }while(0)
53 int FilterDiffuseLighting::render(FilterSlot &slot, FilterUnits const &units) {
54     NRPixBlock *in = slot.get(_input);
55     if (!in) {
56         g_warning("Missing source image for feDiffuseLighting (in=%d)", _input);
57         return 1;
58     }
60     NRPixBlock *out = new NRPixBlock;
62     int w = in->area.x1 - in->area.x0;
63     int h = in->area.y1 - in->area.y0;
64     int x0 = in->area.x0;
65     int y0 = in->area.y0;
66     int i, j;
67     //As long as FilterRes and kernel unit is not supported we hardcode the
68     //default value
69     int dx = 1; //TODO setup
70     int dy = 1; //TODO setup
71     //surface scale
72     Matrix trans = units.get_matrix_primitiveunits2pb();
73     gdouble ss = surfaceScale * trans[0];
74     gdouble kd = diffuseConstant; //diffuse lighting constant
76     Fvector L, N, LC;
77     gdouble inter;
79     nr_pixblock_setup_fast(out, in->mode,
80             in->area.x0, in->area.y0, in->area.x1, in->area.y1,
81             true);
82     unsigned char *data_i = NR_PIXBLOCK_PX (in);
83     unsigned char *data_o = NR_PIXBLOCK_PX (out);
84     //No light, nothing to do
85     switch (light_type) {
86         case DISTANT_LIGHT:  
87             //the light vector is constant
88             {
89             DistantLight *dl = new DistantLight(light.distant, lighting_color);
90             dl->light_vector(L);
91             dl->light_components(LC);
92             //finish the work
93             for (i = 0, j = 0; i < w*h; i++) {
94                 compute_surface_normal(N, ss, in, i / w, i % w, dx, dy);
95                 COMPUTE_INTER(inter, N, L, kd);
97                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]);
98                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]);
99                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]);
100                 data_o[j++] = 255;
101             }
102             out->empty = FALSE;
103             delete dl;
104             }
105             break;
106         case POINT_LIGHT:
107             {
108             PointLight *pl = new PointLight(light.point, lighting_color, trans);
109             pl->light_components(LC);
110         //TODO we need a reference to the filter to determine primitiveUnits
111         //if objectBoundingBox is used, use a different matrix for light_vector
112         // UPDATE: trans is now correct matrix from primitiveUnits to
113         // pixblock coordinates
114             //finish the work
115             for (i = 0, j = 0; i < w*h; i++) {
116                 compute_surface_normal(N, ss, in, i / w, i % w, dx, dy);
117                 pl->light_vector(L,
118                         i % w + x0,
119                         i / w + y0,
120                         ss * (double) data_i[4*i+3]/ 255);
121                 COMPUTE_INTER(inter, N, L, kd);
123                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]);
124                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]);
125                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]);
126                 data_o[j++] = 255;
127             }
128             out->empty = FALSE;
129             delete pl;
130             }
131             break;
132         case SPOT_LIGHT:
133             {
134             SpotLight *sl = new SpotLight(light.spot, lighting_color, trans);
135         //TODO we need a reference to the filter to determine primitiveUnits
136         //if objectBoundingBox is used, use a different matrix for light_vector
137         // UPDATE: trans is now correct matrix from primitiveUnits to
138         // pixblock coordinates
139             //finish the work
140             for (i = 0, j = 0; i < w*h; i++) {
141                 compute_surface_normal(N, ss, in, i / w, i % w, dx, dy);
142                 sl->light_vector(L,
143                     i % w + x0,
144                     i / w + y0,
145                     ss * (double) data_i[4*i+3]/ 255);
146                 sl->light_components(LC, L);
147                 COMPUTE_INTER(inter, N, L, kd);
148                 
149                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]);
150                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]);
151                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]);
152                 data_o[j++] = 255;
153             }
154             out->empty = FALSE;
155             delete sl;
156             }
157             break;
158         //else unknown light source, doing nothing
159         case NO_LIGHT:
160         default:
161             {
162             if (light_type != NO_LIGHT)
163                 g_warning("unknown light source %d", light_type);
164             for (i = 0; i < w*h; i++) {
165                 data_o[4*i+3] = 255;
166             }
167             out->empty = false;
168             }
169     }
170         
171     //finishing
172     slot.set(_output, out);
173     //nr_pixblock_release(in);
174     //delete in;
175     return 0;
178 FilterTraits FilterDiffuseLighting::get_input_traits() {
179     return TRAIT_PARALLER;
182 } /* namespace NR */
184 /*
185   Local Variables:
186   mode:c++
187   c-file-style:"stroustrup"
188   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
189   indent-tabs-mode:nil
190   fill-column:99
191   End:
192 */
193 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :