Code

Merge from trunk.
[inkscape.git] / src / display / nr-filter-specularlighting.cpp
1 /*
2  * feSpecularLighting 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>
14 #include <cmath>
16 #include "display/nr-3dutils.h"
17 #include "display/nr-arena-item.h"
18 #include "display/nr-filter-specularlighting.h"
19 #include "display/nr-filter-getalpha.h"
20 #include "display/nr-filter-slot.h"
21 #include "display/nr-filter-units.h"
22 #include "display/nr-filter-utils.h"
23 #include "display/nr-light.h"
24 #include "libnr/nr-blit.h"
25 #include "libnr/nr-pixblock.h"
26 #include "libnr/nr-rect-l.h"
27 #include "color.h"
29 namespace Inkscape {
30 namespace Filters {
32 FilterSpecularLighting::FilterSpecularLighting()
33 {
34     light_type = NO_LIGHT;
35     specularConstant = 1;
36     specularExponent = 1;
37     surfaceScale = 1;
38     lighting_color = 0xffffffff;
39 }
41 FilterPrimitive * FilterSpecularLighting::create() {
42     return new FilterSpecularLighting();
43 }
45 FilterSpecularLighting::~FilterSpecularLighting()
46 {}
48 //Investigating Phong Lighting model we should not take N.H but
49 //R.E which equals to 2*N.H^2 - 1
50 //replace the second line by
51 //gdouble scal = scalar_product((N), (H)); scal = 2 * scal * scal - 1;
52 //to get the expected formula
53 #define COMPUTE_INTER(inter, H, N, ks, speculaExponent) \
54 do {\
55     gdouble scal = NR::scalar_product((N), (H)); \
56     if (scal <= 0)\
57         (inter) = 0;\
58     else\
59         (inter) = (ks) * std::pow(scal, (specularExponent));\
60 }while(0)
62 int FilterSpecularLighting::render(FilterSlot &slot, FilterUnits const &units) {
63     NRPixBlock *in = slot.get(_input);
64     if (!in) {
65         g_warning("Missing source image for feSpecularLighting (in=%d)", _input);
66         return 1;
67     }
69     NRPixBlock *out = new NRPixBlock;
71     //Fvector *L = NULL; //vector to the light
73     int w = in->area.x1 - in->area.x0;
74     int h = in->area.y1 - in->area.y0;
75     int x0 = in->area.x0;
76     int y0 = in->area.y0;
77     int i, j;
78     //As long as FilterRes and kernel unit is not supported we hardcode the
79     //default value
80     int dx = 1; //TODO setup
81     int dy = 1; //TODO setup
82     //surface scale
83     Geom::Matrix trans = units.get_matrix_primitiveunits2pb();
84     gdouble ss = surfaceScale * trans[0];
85     gdouble ks = specularConstant; //diffuse lighting constant
86     NR::Fvector L, N, LC, H;
87     gdouble inter;
89     nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8N,
90             in->area.x0, in->area.y0, in->area.x1, in->area.y1,
91             true);
92     unsigned char *data_i = NR_PIXBLOCK_PX (in);
93     unsigned char *data_o = NR_PIXBLOCK_PX (out);
94     //No light, nothing to do
95     switch (light_type) {
96         case DISTANT_LIGHT:
97             //the light vector is constant
98             {
99             DistantLight *dl = new DistantLight(light.distant, lighting_color);
100             dl->light_vector(L);
101             dl->light_components(LC);
102             NR::normalized_sum(H, L, NR::EYE_VECTOR);
103             //finish the work
104             for (i = 0, j = 0; i < w*h; i++) {
105                 NR::compute_surface_normal(N, ss, in, i / w, i % w, dx, dy);
106                 COMPUTE_INTER(inter, N, H, ks, specularExponent);
108                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]); // CLAMP includes rounding!
109                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]);
110                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]);
111                 data_o[j] = MAX(MAX(data_o[j-3], data_o[j-2]), data_o[j-1]);
112                 ++j;
113             }
114             out->empty = FALSE;
115             delete dl;
116             }
117             break;
118         case POINT_LIGHT:
119             {
120             PointLight *pl = new PointLight(light.point, lighting_color, trans);
121             pl->light_components(LC);
122         //TODO we need a reference to the filter to determine primitiveUnits
123         //if objectBoundingBox is used, use a different matrix for light_vector
124         // UPDATE: trans is now correct matrix from primitiveUnits to
125         // pixblock coordinates
126             //finish the work
127             for (i = 0, j = 0; i < w*h; i++) {
128                 NR::compute_surface_normal(N, ss, in, i / w, i % w, dx, dy);
129                 pl->light_vector(L,
130                         i % w + x0,
131                         i / w + y0,
132                         ss * (double) data_i[4*i+3]/ 255);
133                 NR::normalized_sum(H, L, NR::EYE_VECTOR);
134                 COMPUTE_INTER(inter, N, H, ks, specularExponent);
136                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]);
137                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]);
138                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]);
139                 data_o[j] = MAX(MAX(data_o[j-3], data_o[j-2]), data_o[j-1]);
140                 ++j;
141             }
142             out->empty = FALSE;
143             delete pl;
144             }
145             break;
146         case SPOT_LIGHT:
147             {
148             SpotLight *sl = new SpotLight(light.spot, lighting_color, trans);
149         //TODO we need a reference to the filter to determine primitiveUnits
150         //if objectBoundingBox is used, use a different matrix for light_vector
151         // UPDATE: trans is now correct matrix from primitiveUnits to
152         // pixblock coordinates
153             //finish the work
154             for (i = 0, j = 0; i < w*h; i++) {
155                 NR::compute_surface_normal(N, ss, in, i / w, i % w, dx, dy);
156                 sl->light_vector(L,
157                     i % w + x0,
158                     i / w + y0,
159                     ss * (double) data_i[4*i+3]/ 255);
160                 sl->light_components(LC, L);
161                 NR::normalized_sum(H, L, NR::EYE_VECTOR);
162                 COMPUTE_INTER(inter, N, H, ks, specularExponent);
164                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]);
165                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]);
166                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]);
167                 data_o[j] = MAX(MAX(data_o[j-3], data_o[j-2]), data_o[j-1]);
168                 ++j;
169             }
170             out->empty = FALSE;
171             delete sl;
172             }
173             break;
174         //else unknown light source, doing nothing
175         case NO_LIGHT:
176         default:
177             {
178             if (light_type != NO_LIGHT)
179                 g_warning("unknown light source %d", light_type);
180             out->empty = false;
181             }
182     }
184     //finishing
185     slot.set(_output, out);
186     //nr_pixblock_release(in);
187     //delete in;
188     return 0;
191 void FilterSpecularLighting::area_enlarge(NRRectL &area, Geom::Matrix const &trans)
193     // TODO: support kernelUnitLength
194     double scalex = std::fabs(trans[0]) + std::fabs(trans[1]);
195     double scaley = std::fabs(trans[2]) + std::fabs(trans[3]);
197     //FIXME: no +2 should be there!... (noticable only for big scales at big zoom factor)
198     area.x0 -= (int)(scalex) + 2;
199     area.x1 += (int)(scalex) + 2;
200     area.y0 -= (int)(scaley) + 2;
201     area.y1 += (int)(scaley) + 2;
204 FilterTraits FilterSpecularLighting::get_input_traits() {
205     return TRAIT_PARALLER;
208 } /* namespace Filters */
209 } /* namespace Inkscape */
211 /*
212   Local Variables:
213   mode:c++
214   c-file-style:"stroustrup"
215   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
216   indent-tabs-mode:nil
217   fill-column:99
218   End:
219 */
220 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :