1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * feDiffuseLighting renderer
4  *
5  * Authors:
6  *   Niko Kiirala <niko@kiirala.com>
7  *   Jean-Rene Reinhard <jr@komite.net>
8  *   Krzysztof Kosiński <tweenk.pl@gmail.com>
9  *
10  * Copyright (C) 2007-2010 Authors
11  *
12  * Released under GNU GPL v2+, read the file 'COPYING' for more information.
13  */
14 
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"  // only include where actually required!
17 #endif
18 
19 #include <glib.h>
20 
21 #include "display/cairo-templates.h"
22 #include "display/cairo-utils.h"
23 #include "display/nr-3dutils.h"
24 #include "display/nr-filter-diffuselighting.h"
25 #include "display/nr-filter-slot.h"
26 #include "display/nr-filter-units.h"
27 #include "display/nr-filter-utils.h"
28 #include "display/nr-light.h"
29 #include "svg/svg-icc-color.h"
30 #include "svg/svg-color.h"
31 
32 namespace Inkscape {
33 namespace Filters {
34 
FilterDiffuseLighting()35 FilterDiffuseLighting::FilterDiffuseLighting()
36 {
37     light_type = NO_LIGHT;
38     diffuseConstant = 1;
39     surfaceScale = 1;
40     lighting_color = 0xffffffff;
41 }
42 
create()43 FilterPrimitive * FilterDiffuseLighting::create() {
44     return new FilterDiffuseLighting();
45 }
46 
47 FilterDiffuseLighting::~FilterDiffuseLighting()
48 = default;
49 
50 struct DiffuseLight : public SurfaceSynth {
DiffuseLightInkscape::Filters::DiffuseLight51     DiffuseLight(cairo_surface_t *bumpmap, double scale, double kd)
52         : SurfaceSynth(bumpmap)
53         , _scale(scale)
54         , _kd(kd)
55     {}
56 
57 protected:
diffuseLightingInkscape::Filters::DiffuseLight58     guint32 diffuseLighting(int x, int y, NR::Fvector const &light, NR::Fvector const &light_components) {
59         NR::Fvector normal = surfaceNormalAt(x, y, _scale);
60         double k = _kd * NR::scalar_product(normal, light);
61 
62         guint32 r = CLAMP_D_TO_U8(k * light_components[LIGHT_RED]);
63         guint32 g = CLAMP_D_TO_U8(k * light_components[LIGHT_GREEN]);
64         guint32 b = CLAMP_D_TO_U8(k * light_components[LIGHT_BLUE]);
65 
66         ASSEMBLE_ARGB32(pxout, 255,r,g,b)
67         return pxout;
68     }
69     double _scale, _kd;
70 };
71 
72 struct DiffuseDistantLight : public DiffuseLight {
DiffuseDistantLightInkscape::Filters::DiffuseDistantLight73     DiffuseDistantLight(cairo_surface_t *bumpmap, SPFeDistantLight *light, guint32 color,
74             double scale, double diffuse_constant)
75         : DiffuseLight(bumpmap, scale, diffuse_constant)
76     {
77         DistantLight dl(light, color);
78         dl.light_vector(_lightv);
79         dl.light_components(_light_components);
80     }
81 
operator ()Inkscape::Filters::DiffuseDistantLight82     guint32 operator()(int x, int y) {
83         return diffuseLighting(x, y, _lightv, _light_components);
84     }
85 private:
86     NR::Fvector _lightv, _light_components;
87 };
88 
89 struct DiffusePointLight : public DiffuseLight {
DiffusePointLightInkscape::Filters::DiffusePointLight90     DiffusePointLight(cairo_surface_t *bumpmap, SPFePointLight *light, guint32 color,
91                       Geom::Affine const &trans, double scale, double diffuse_constant,
92                       double x0, double y0, int device_scale)
93         : DiffuseLight(bumpmap, scale, diffuse_constant)
94         , _light(light, color, trans, device_scale)
95         , _x0(x0)
96         , _y0(y0)
97     {
98         _light.light_components(_light_components);
99     }
100 
operator ()Inkscape::Filters::DiffusePointLight101     guint32 operator()(int x, int y) {
102         NR::Fvector light;
103         _light.light_vector(light, _x0 + x, _y0 + y, _scale * alphaAt(x, y)/255.0);
104         return diffuseLighting(x, y, light, _light_components);
105     }
106 private:
107     PointLight _light;
108     NR::Fvector _light_components;
109     double _x0, _y0;
110 };
111 
112 struct DiffuseSpotLight : public DiffuseLight {
DiffuseSpotLightInkscape::Filters::DiffuseSpotLight113     DiffuseSpotLight(cairo_surface_t *bumpmap, SPFeSpotLight *light, guint32 color,
114                      Geom::Affine const &trans, double scale, double diffuse_constant,
115                      double x0, double y0, int device_scale)
116         : DiffuseLight(bumpmap, scale, diffuse_constant)
117         , _light(light, color, trans, device_scale)
118         , _x0(x0)
119         , _y0(y0)
120     {}
121 
operator ()Inkscape::Filters::DiffuseSpotLight122     guint32 operator()(int x, int y) {
123         NR::Fvector light, light_components;
124         _light.light_vector(light, _x0 + x, _y0 + y, _scale * alphaAt(x, y)/255.0);
125         _light.light_components(light_components, light);
126         return diffuseLighting(x, y, light, light_components);
127     }
128 private:
129     SpotLight _light;
130     double _x0, _y0;
131 };
132 
render_cairo(FilterSlot & slot)133 void FilterDiffuseLighting::render_cairo(FilterSlot &slot)
134 {
135     cairo_surface_t *input = slot.getcairo(_input);
136     cairo_surface_t *out = ink_cairo_surface_create_same_size(input, CAIRO_CONTENT_COLOR_ALPHA);
137 
138     double r = SP_RGBA32_R_F(lighting_color);
139     double g = SP_RGBA32_G_F(lighting_color);
140     double b = SP_RGBA32_B_F(lighting_color);
141 
142     if (icc) {
143         guchar ru, gu, bu;
144         icc_color_to_sRGB(icc, &ru, &gu, &bu);
145         r = SP_COLOR_U_TO_F(ru);
146         g = SP_COLOR_U_TO_F(gu);
147         b = SP_COLOR_U_TO_F(bu);
148     }
149 
150     // Only alpha channel of input is used, no need to check input color_interpolation_filter value.
151     SPColorInterpolation ci_fp  = SP_CSS_COLOR_INTERPOLATION_AUTO;
152     if( _style ) {
153         ci_fp = (SPColorInterpolation)_style->color_interpolation_filters.computed;
154 
155         // Lighting color is always defined in terms of sRGB, preconvert to linearRGB
156         // if color_interpolation_filters set to linearRGB (for efficiency assuming
157         // next filter primitive has same value of cif).
158         if( ci_fp == SP_CSS_COLOR_INTERPOLATION_LINEARRGB ) {
159             r = srgb_to_linear( r );
160             g = srgb_to_linear( g );
161             b = srgb_to_linear( b );
162         }
163     }
164     set_cairo_surface_ci(out, ci_fp );
165     guint32 color = SP_RGBA32_F_COMPOSE( r, g, b, 1.0 );
166 
167     int device_scale = slot.get_device_scale();
168 
169     Geom::Rect slot_area = slot.get_slot_area();
170     Geom::Point p = slot_area.min();
171 
172     // trans has inverse y... so we can't just scale by device_scale! We must instead explicitly
173     // scale the point and spot light coordinates (as well as "scale").
174 
175     Geom::Affine trans = slot.get_units().get_matrix_primitiveunits2pb();
176 
177     double x0 = p[Geom::X], y0 = p[Geom::Y];
178     double scale = surfaceScale * trans.descrim() * device_scale;
179 
180     switch (light_type) {
181     case DISTANT_LIGHT:
182         ink_cairo_surface_synthesize(out,
183             DiffuseDistantLight(input, light.distant, color, scale, diffuseConstant));
184         break;
185     case POINT_LIGHT:
186         ink_cairo_surface_synthesize(out,
187             DiffusePointLight(input, light.point, color, trans, scale, diffuseConstant, x0, y0, device_scale));
188         break;
189     case SPOT_LIGHT:
190         ink_cairo_surface_synthesize(out,
191             DiffuseSpotLight(input, light.spot, color, trans, scale, diffuseConstant, x0, y0, device_scale));
192         break;
193     default: {
194         cairo_t *ct = cairo_create(out);
195         cairo_set_source_rgba(ct, 0,0,0,1);
196         cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE);
197         cairo_paint(ct);
198         cairo_destroy(ct);
199         } break;
200     }
201 
202     slot.set(_output, out);
203     cairo_surface_destroy(out);
204 }
205 
set_icc(SVGICCColor * icc_color)206 void FilterDiffuseLighting::set_icc(SVGICCColor *icc_color) {
207     icc = icc_color;
208 }
209 
area_enlarge(Geom::IntRect & area,Geom::Affine const &)210 void FilterDiffuseLighting::area_enlarge(Geom::IntRect &area, Geom::Affine const & /*trans*/)
211 {
212     // TODO: support kernelUnitLength
213 
214     // We expand the area by 1 in every direction to avoid artifacts on tile edges.
215     // However, it means that edge pixels will be incorrect.
216     area.expandBy(1);
217 }
218 
complexity(Geom::Affine const &)219 double FilterDiffuseLighting::complexity(Geom::Affine const &)
220 {
221     return 9.0;
222 }
223 
224 } /* namespace Filters */
225 } /* namespace Inkscape */
226 
227 /*
228   Local Variables:
229   mode:c++
230   c-file-style:"stroustrup"
231   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
232   indent-tabs-mode:nil
233   fill-column:99
234   End:
235 */
236 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
237