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-rect-l.h"
26 #include "color.h"
28 namespace Inkscape {
29 namespace Filters {
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 int FilterDiffuseLighting::render(FilterSlot &slot, FilterUnits const &units) {
47 NRPixBlock *in = slot.get(_input);
48 if (!in) {
49 g_warning("Missing source image for feDiffuseLighting (in=%d)", _input);
50 return 1;
51 }
53 NRPixBlock *out = new NRPixBlock;
55 int w = in->area.x1 - in->area.x0;
56 int h = in->area.y1 - in->area.y0;
57 int x0 = in->area.x0;
58 int y0 = in->area.y0;
59 int i, j;
60 //As long as FilterRes and kernel unit is not supported we hardcode the
61 //default value
62 int dx = 1; //TODO setup
63 int dy = 1; //TODO setup
64 //surface scale
65 Geom::Matrix trans = units.get_matrix_primitiveunits2pb();
66 gdouble ss = surfaceScale * trans[0];
67 gdouble kd = diffuseConstant; //diffuse lighting constant
69 NR::Fvector L, N, LC;
70 gdouble inter;
72 nr_pixblock_setup_fast(out, in->mode,
73 in->area.x0, in->area.y0, in->area.x1, in->area.y1,
74 true);
75 unsigned char *data_i = NR_PIXBLOCK_PX (in);
76 unsigned char *data_o = NR_PIXBLOCK_PX (out);
77 //No light, nothing to do
78 switch (light_type) {
79 case DISTANT_LIGHT:
80 //the light vector is constant
81 {
82 DistantLight *dl = new DistantLight(light.distant, lighting_color);
83 dl->light_vector(L);
84 dl->light_components(LC);
85 //finish the work
86 for (i = 0, j = 0; i < w*h; i++) {
87 NR::compute_surface_normal(N, ss, in, i / w, i % w, dx, dy);
88 inter = kd * NR::scalar_product(N, L);
90 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]); // CLAMP includes rounding!
91 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]);
92 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]);
93 data_o[j++] = 255;
94 }
95 out->empty = FALSE;
96 delete dl;
97 }
98 break;
99 case POINT_LIGHT:
100 {
101 PointLight *pl = new PointLight(light.point, lighting_color, trans);
102 pl->light_components(LC);
103 //TODO we need a reference to the filter to determine primitiveUnits
104 //if objectBoundingBox is used, use a different matrix for light_vector
105 // UPDATE: trans is now correct matrix from primitiveUnits to
106 // pixblock coordinates
107 //finish the work
108 for (i = 0, j = 0; i < w*h; i++) {
109 NR::compute_surface_normal(N, ss, in, i / w, i % w, dx, dy);
110 pl->light_vector(L,
111 i % w + x0,
112 i / w + y0,
113 ss * (double) data_i[4*i+3]/ 255);
114 inter = kd * NR::scalar_product(N, L);
116 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]);
117 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]);
118 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]);
119 data_o[j++] = 255;
120 }
121 out->empty = FALSE;
122 delete pl;
123 }
124 break;
125 case SPOT_LIGHT:
126 {
127 SpotLight *sl = new SpotLight(light.spot, lighting_color, trans);
128 //TODO we need a reference to the filter to determine primitiveUnits
129 //if objectBoundingBox is used, use a different matrix for light_vector
130 // UPDATE: trans is now correct matrix from primitiveUnits to
131 // pixblock coordinates
132 //finish the work
133 for (i = 0, j = 0; i < w*h; i++) {
134 NR::compute_surface_normal(N, ss, in, i / w, i % w, dx, dy);
135 sl->light_vector(L,
136 i % w + x0,
137 i / w + y0,
138 ss * (double) data_i[4*i+3]/ 255);
139 sl->light_components(LC, L);
140 inter = kd * NR::scalar_product(N, L);
142 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]);
143 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]);
144 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]);
145 data_o[j++] = 255;
146 }
147 out->empty = FALSE;
148 delete sl;
149 }
150 break;
151 //else unknown light source, doing nothing
152 case NO_LIGHT:
153 default:
154 {
155 if (light_type != NO_LIGHT)
156 g_warning("unknown light source %d", light_type);
157 for (i = 0; i < w*h; i++) {
158 data_o[4*i+3] = 255;
159 }
160 out->empty = false;
161 }
162 }
164 //finishing
165 slot.set(_output, out);
166 //nr_pixblock_release(in);
167 //delete in;
168 return 0;
169 }
171 void FilterDiffuseLighting::area_enlarge(NRRectL &area, Geom::Matrix const &trans)
172 {
173 // TODO: support kernelUnitLength
174 double scalex = std::fabs(trans[0]) + std::fabs(trans[1]);
175 double scaley = std::fabs(trans[2]) + std::fabs(trans[3]);
177 //FIXME: no +2 should be there!... (noticable only for big scales at big zoom factor)
178 area.x0 -= (int)(scalex) + 2;
179 area.x1 += (int)(scalex) + 2;
180 area.y0 -= (int)(scaley) + 2;
181 area.y1 += (int)(scaley) + 2;
182 }
184 FilterTraits FilterDiffuseLighting::get_input_traits() {
185 return TRAIT_PARALLER;
186 }
188 } /* namespace Filters */
189 } /* namespace Inkscape */
191 /*
192 Local Variables:
193 mode:c++
194 c-file-style:"stroustrup"
195 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
196 indent-tabs-mode:nil
197 fill-column:99
198 End:
199 */
200 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :