1 /*
2 * feColorMatrix filter primitive renderer
3 *
4 * Authors:
5 * Felipe CorrĂȘa da Silva Sanches <juca@members.fsf.org>
6 * Jasper van de Gronde <th.v.d.gronde@hccnet.nl>
7 *
8 * Copyright (C) 2007 authors
9 *
10 * Released under GNU GPL, read the file 'COPYING' for more information
11 */
13 #include "display/nr-filter-colormatrix.h"
14 #include "display/nr-filter-units.h"
15 #include "display/nr-filter-utils.h"
16 #include "libnr/nr-blit.h"
17 #include <math.h>
19 namespace Inkscape {
20 namespace Filters {
22 FilterColorMatrix::FilterColorMatrix()
23 {
24 }
26 FilterPrimitive * FilterColorMatrix::create() {
27 return new FilterColorMatrix();
28 }
30 FilterColorMatrix::~FilterColorMatrix()
31 {}
33 int FilterColorMatrix::render(FilterSlot &slot, FilterUnits const &/*units*/) {
34 NRPixBlock *in = slot.get(_input);
35 if (!in) {
36 g_warning("Missing source image for feColorMatrix (in=%d)", _input);
37 return 1;
38 }
40 NRPixBlock *out = new NRPixBlock;
41 if ((type==COLORMATRIX_SATURATE || type==COLORMATRIX_HUEROTATE) && in->mode != NR_PIXBLOCK_MODE_R8G8B8A8N) {
42 // saturate and hueRotate do not touch the alpha channel and are linear (per-pixel) operations, so no premultiplied -> non-premultiplied operation is necessary
43 nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8P,
44 in->area.x0, in->area.y0, in->area.x1, in->area.y1,
45 true);
46 } else {
47 nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8N,
48 in->area.x0, in->area.y0, in->area.x1, in->area.y1,
49 true);
50 }
52 // this primitive is defined for non-premultiplied RGBA values,
53 // thus convert them to that format
54 // However, since not all operations care, the input is only transformed if necessary.
55 bool free_in_on_exit = false;
56 if (in->mode != out->mode) {
57 NRPixBlock *original_in = in;
58 in = new NRPixBlock;
59 nr_pixblock_setup_fast(in, out->mode,
60 original_in->area.x0, original_in->area.y0,
61 original_in->area.x1, original_in->area.y1,
62 true);
63 nr_blit_pixblock_pixblock(in, original_in);
64 free_in_on_exit = true;
65 }
67 unsigned char *in_data = NR_PIXBLOCK_PX(in);
68 unsigned char *out_data = NR_PIXBLOCK_PX(out);
69 unsigned char r,g,b,a;
70 int x,y,x0,y0,x1,y1,i;
71 x0=in->area.x0;
72 y0=in->area.y0;
73 x1=in->area.x1;
74 y1=in->area.y1;
76 switch(type){
77 case COLORMATRIX_MATRIX:
78 {
79 if (values.size()!=20) {
80 g_warning("ColorMatrix: values parameter error. Wrong size: %i.", static_cast<int>(values.size()));
81 return -1;
82 }
83 double a04 = 255*values[4];
84 double a14 = 255*values[9];
85 double a24 = 255*values[14];
86 double a34 = 255*values[19];
87 for (x=x0;x<x1;x++){
88 for (y=y0;y<y1;y++){
89 i = ((x-x0) + (x1-x0)*(y-y0))*4;
90 r = in_data[i];
91 g = in_data[i+1];
92 b = in_data[i+2];
93 a = in_data[i+3];
94 out_data[i] = CLAMP_D_TO_U8( r*values[0] + g*values[1] + b*values[2] + a*values[3] + a04 ); // CLAMP includes rounding!
95 out_data[i+1] = CLAMP_D_TO_U8( r*values[5] + g*values[6] + b*values[7] + a*values[8] + a14 );
96 out_data[i+2] = CLAMP_D_TO_U8( r*values[10] + g*values[11] + b*values[12] + a*values[13] + a24 );
97 out_data[i+3] = CLAMP_D_TO_U8( r*values[15] + g*values[16] + b*values[17] + a*values[18] + a34 );
98 }
99 }
100 }
101 break;
102 case COLORMATRIX_SATURATE:
103 {
104 double v = std::max(0.0,std::min(1.0,value)); // The standard says it should be between 0 and 1, and clamping it here makes it unnecessary to clamp the color values.
105 double a00 = 0.213+0.787*v, a01 = 0.715-0.715*v, a02 = 0.072-0.072*v;
106 double a10 = 0.213-0.213*v, a11 = 0.715+0.285*v, a12 = 0.072-0.072*v;
107 double a20 = 0.213-0.213*v, a21 = 0.715-0.715*v, a22 = 0.072+0.928*v;
108 for (x=x0;x<x1;x++){
109 for (y=y0;y<y1;y++){
110 i = ((x-x0) + (x1-x0)*(y-y0))*4;
111 r = in_data[i];
112 g = in_data[i+1];
113 b = in_data[i+2];
114 a = in_data[i+3];
115 out_data[i] = static_cast<unsigned char>( r*a00 + g*a01 + b*a02 + .5 );
116 out_data[i+1] = static_cast<unsigned char>( r*a10 + g*a11 + b*a12 + .5 );
117 out_data[i+2] = static_cast<unsigned char>( r*a20 + g*a21 + b*a22 + .5 );
118 out_data[i+3] = a;
119 }
120 }
121 }
122 break;
123 case COLORMATRIX_HUEROTATE:
124 {
125 double coshue = cos(value * M_PI/180.0);
126 double sinhue = sin(value * M_PI/180.0);
127 double a00 = 0.213 + coshue*( 0.787) + sinhue*(-0.213);
128 double a01 = 0.715 + coshue*(-0.715) + sinhue*(-0.715);
129 double a02 = 0.072 + coshue*(-0.072) + sinhue*( 0.928);
130 double a10 = 0.213 + coshue*(-0.213) + sinhue*( 0.143);
131 double a11 = 0.715 + coshue*( 0.285) + sinhue*( 0.140);
132 double a12 = 0.072 + coshue*(-0.072) + sinhue*(-0.283);
133 double a20 = 0.213 + coshue*(-0.213) + sinhue*(-0.787);
134 double a21 = 0.715 + coshue*(-0.715) + sinhue*( 0.715);
135 double a22 = 0.072 + coshue*( 0.928) + sinhue*( 0.072);
136 if (in->mode==NR_PIXBLOCK_MODE_R8G8B8A8P) {
137 // Although it does not change the alpha channel, it can give "out-of-bound" results, and in this case the bound is determined by the alpha channel
138 for (x=x0;x<x1;x++){
139 for (y=y0;y<y1;y++){
140 i = ((x-x0) + (x1-x0)*(y-y0))*4;
141 r = in_data[i];
142 g = in_data[i+1];
143 b = in_data[i+2];
144 a = in_data[i+3];
146 out_data[i] = static_cast<unsigned char>(std::max(0.0,std::min((double)a, r*a00 + g*a01 + b*a02 + .5 )));
147 out_data[i+1] = static_cast<unsigned char>(std::max(0.0,std::min((double)a, r*a10 + g*a11 + b*a12 + .5 )));
148 out_data[i+2] = static_cast<unsigned char>(std::max(0.0,std::min((double)a, r*a20 + g*a21 + b*a22 + .5 )));
149 out_data[i+3] = a;
150 }
151 }
152 } else {
153 for (x=x0;x<x1;x++){
154 for (y=y0;y<y1;y++){
155 i = ((x-x0) + (x1-x0)*(y-y0))*4;
156 r = in_data[i];
157 g = in_data[i+1];
158 b = in_data[i+2];
159 a = in_data[i+3];
161 out_data[i] = CLAMP_D_TO_U8( r*a00 + g*a01 + b*a02);
162 out_data[i+1] = CLAMP_D_TO_U8( r*a10 + g*a11 + b*a12);
163 out_data[i+2] = CLAMP_D_TO_U8( r*a20 + g*a21 + b*a22);
164 out_data[i+3] = a;
165 }
166 }
167 }
168 }
169 break;
170 case COLORMATRIX_LUMINANCETOALPHA:
171 for (x=x0;x<x1;x++){
172 for (y=y0;y<y1;y++){
173 i = ((x-x0) + (x1-x0)*(y-y0))*4;
174 r = in_data[i];
175 g = in_data[i+1];
176 b = in_data[i+2];
177 out_data[i] = 0;
178 out_data[i+1] = 0;
179 out_data[i+2] = 0;
180 out_data[i+3] = static_cast<unsigned char>( r*0.2125 + g*0.7154 + b*0.0721 + .5 );
181 }
182 }
183 break;
184 case COLORMATRIX_ENDTYPE:
185 break;
186 }
188 if (free_in_on_exit) {
189 nr_pixblock_release(in);
190 delete in;
191 }
193 out->empty = FALSE;
194 slot.set(_output, out);
195 return 0;
196 }
198 void FilterColorMatrix::area_enlarge(NRRectL &/*area*/, Geom::Matrix const &/*trans*/)
199 {
200 }
202 void FilterColorMatrix::set_type(FilterColorMatrixType t){
203 type = t;
204 }
206 void FilterColorMatrix::set_value(gdouble v){
207 value = v;
208 }
210 void FilterColorMatrix::set_values(std::vector<gdouble> &v){
211 values = v;
212 }
214 } /* namespace Filters */
215 } /* namespace Inkscape */
217 /*
218 Local Variables:
219 mode:c++
220 c-file-style:"stroustrup"
221 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
222 indent-tabs-mode:nil
223 fill-column:99
224 End:
225 */
226 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :