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