Code

Some accuracy improvements for nr-filter-colormatrix (including some changes to let...
[inkscape.git] / src / display / nr-filter-colormatrix.cpp
1 /*
2  * feColorMatrix filter primitive renderer
3  *
4  * Authors:
5  *   Felipe CorrĂȘa da Silva Sanches <felipe.sanches@gmail.com>
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 NR {
21 FilterColorMatrix::FilterColorMatrix()
22 {
23 }
25 FilterPrimitive * FilterColorMatrix::create() {
26     return new FilterColorMatrix();
27 }
29 FilterColorMatrix::~FilterColorMatrix()
30 {}
32 int FilterColorMatrix::render(FilterSlot &slot, FilterUnits const &/*units*/) {
33     NRPixBlock *in = slot.get(_input);
34     if (!in) {
35         g_warning("Missing source image for feColorMatrix (in=%d)", _input);
36         return 1;
37     }
39     NRPixBlock *out = new NRPixBlock;
41     bool premultiplied;
42     if ((type==COLORMATRIX_SATURATE || type==COLORMATRIX_HUEROTATE) && in->mode != NR_PIXBLOCK_MODE_R8G8B8A8N) {
43         // saturate and hueRotate do not touch the alpha channel and are linear (per-pixel) operations, so no premultiplied -> non-premultiplied operation is necessary
44         nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8P,
45                                in->area.x0, in->area.y0, in->area.x1, in->area.y1,
46                                true);
47         premultiplied = true;
48     } else {
49         nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8N,
50                                in->area.x0, in->area.y0, in->area.x1, in->area.y1,
51                                true);
52         premultiplied = false;
53     }
55     // this primitive is defined for non-premultiplied RGBA values,
56     // thus convert them to that format
57     //   However, since not all operations care, the input is only transformed if necessary.
58     bool free_in_on_exit = false;
59     if (in->mode != out->mode) {
60         NRPixBlock *original_in = in;
61         in = new NRPixBlock;
62         nr_pixblock_setup_fast(in, out->mode,
63                                original_in->area.x0, original_in->area.y0,
64                                original_in->area.x1, original_in->area.y1,
65                                true);
66         nr_blit_pixblock_pixblock(in, original_in);
67         free_in_on_exit = true;
68     }
70     unsigned char *in_data = NR_PIXBLOCK_PX(in);
71     unsigned char *out_data = NR_PIXBLOCK_PX(out);
72     unsigned char r,g,b,a;
73     int x,y,x0,y0,x1,y1,i;
74     x0=in->area.x0;
75     y0=in->area.y0;
76     x1=in->area.x1;
77     y1=in->area.y1;
79     switch(type){
80         case COLORMATRIX_MATRIX:
81             {
82                 if (values.size()!=20) {
83                     g_warning("ColorMatrix: values parameter error. Wrong size: %i.", static_cast<int>(values.size()));
84                     return -1;
85                 }
86                 double a04 = 255*values[4] + .5;
87                 double a14 = 255*values[9] + .5;
88                 double a24 = 255*values[14] + .5;
89                 double a34 = 255*values[19] + .5;
90                 for (x=x0;x<x1;x++){
91                     for (y=y0;y<y1;y++){
92                         i = ((x-x0) + (x1-x0)*(y-y0))*4;
93                         r = in_data[i];
94                         g = in_data[i+1];
95                         b = in_data[i+2];
96                         a = in_data[i+3];
97                         out_data[i] = CLAMP_D_TO_U8( r*values[0] + g*values[1] + b*values[2] + a*values[3] + a04 );
98                         out_data[i+1] = CLAMP_D_TO_U8( r*values[5] + g*values[6] + b*values[7] + a*values[8] + a14 );
99                         out_data[i+2] = CLAMP_D_TO_U8( r*values[10] + g*values[11] + b*values[12] + a*values[13] + a24 );
100                         out_data[i+3] = CLAMP_D_TO_U8( r*values[15] + g*values[16] + b*values[17] + a*values[18] + a34 );
101                     }
102                 }
103             }
104             break;
105         case COLORMATRIX_SATURATE:
106             {
107                 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.
108                 double a00 = 0.213+0.787*v, a01 = 0.715-0.715*v, a02 = 0.072-0.072*v;
109                 double a10 = 0.213-0.213*v, a11 = 0.715+0.285*v, a12 = 0.072-0.072*v;
110                 double a20 = 0.213-0.213*v, a21 = 0.715-0.715*v, a22 = 0.072+0.928*v;
111                 for (x=x0;x<x1;x++){
112                     for (y=y0;y<y1;y++){
113                         i = ((x-x0) + (x1-x0)*(y-y0))*4;
114                         r = in_data[i];
115                         g = in_data[i+1];
116                         b = in_data[i+2];
117                         a = in_data[i+3];
118                         out_data[i]   = static_cast<unsigned char>( r*a00 + g*a01 + b*a02 + .5 );
119                         out_data[i+1] = static_cast<unsigned char>( r*a10 + g*a11 + b*a12 + .5 );
120                         out_data[i+2] = static_cast<unsigned char>( r*a20 + g*a21 + b*a22 + .5 );
121                         out_data[i+3] = a;
122                     }
123                 }
124             }
125             break;
126         case COLORMATRIX_HUEROTATE:
127             {
128                 double coshue = cos(value * M_PI/180.0);
129                 double sinhue = sin(value * M_PI/180.0);
130                 double a00 = 0.213 + coshue*( 0.787) + sinhue*(-0.213);
131                 double a01 = 0.715 + coshue*(-0.715) + sinhue*(-0.715);
132                 double a02 = 0.072 + coshue*(-0.072) + sinhue*( 0.928);
133                 double a10 = 0.213 + coshue*(-0.213) + sinhue*( 0.143);
134                 double a11 = 0.715 + coshue*( 0.285) + sinhue*( 0.140);
135                 double a12 = 0.072 + coshue*(-0.072) + sinhue*(-0.283);
136                 double a20 = 0.213 + coshue*(-0.213) + sinhue*(-0.787);
137                 double a21 = 0.715 + coshue*(-0.715) + sinhue*( 0.715);
138                 double a22 = 0.072 + coshue*( 0.928) + sinhue*( 0.072);
139                 if (premultiplied) {
140                     // 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
141                     for (x=x0;x<x1;x++){
142                         for (y=y0;y<y1;y++){
143                             i = ((x-x0) + (x1-x0)*(y-y0))*4;
144                             r = in_data[i];
145                             g = in_data[i+1];
146                             b = in_data[i+2];
147                             a = in_data[i+3];
149                             out_data[i]   = static_cast<unsigned char>(std::max(0.0,std::min((double)a, r*a00 + g*a01 + b*a02 + .5 )));
150                             out_data[i+1] = static_cast<unsigned char>(std::max(0.0,std::min((double)a, r*a10 + g*a11 + b*a12 + .5 )));
151                             out_data[i+2] = static_cast<unsigned char>(std::max(0.0,std::min((double)a, r*a20 + g*a21 + b*a22 + .5 )));
152                             out_data[i+3] = a;
153                         }
154                     }
155                 } else {
156                     for (x=x0;x<x1;x++){
157                         for (y=y0;y<y1;y++){
158                             i = ((x-x0) + (x1-x0)*(y-y0))*4;
159                             r = in_data[i];
160                             g = in_data[i+1];
161                             b = in_data[i+2];
162                             a = in_data[i+3];
164                             out_data[i]   = CLAMP_D_TO_U8( r*a00 + g*a01 + b*a02 + .5 );
165                             out_data[i+1] = CLAMP_D_TO_U8( r*a10 + g*a11 + b*a12 + .5 );
166                             out_data[i+2] = CLAMP_D_TO_U8( r*a20 + g*a21 + b*a22 + .5 );
167                             out_data[i+3] = a;
168                         }
169                     }
170                 }
171             }
172             break;
173         case COLORMATRIX_LUMINANCETOALPHA:
174             for (x=x0;x<x1;x++){
175                 for (y=y0;y<y1;y++){
176                     i = ((x-x0) + (x1-x0)*(y-y0))*4;
177                     r = in_data[i];
178                     g = in_data[i+1];
179                     b = in_data[i+2];
180                     out_data[i] = 0;
181                     out_data[i+1] = 0;
182                     out_data[i+2] = 0;
183                     out_data[i+3] = static_cast<unsigned char>( r*0.2125 + g*0.7154 + b*0.0721 + .5 );
184                 }
185             }
186             break;
187         case COLORMATRIX_ENDTYPE:
188             break;
189     }
191     if (free_in_on_exit) {
192         nr_pixblock_release(in);
193         delete in;
194     }
196     out->empty = FALSE;
197     slot.set(_output, out);
198     return 0;
201 void FilterColorMatrix::area_enlarge(NRRectL &/*area*/, Geom::Matrix const &/*trans*/)
205 void FilterColorMatrix::set_type(FilterColorMatrixType t){
206         type = t;
209 void FilterColorMatrix::set_value(gdouble v){
210         value = v;
213 void FilterColorMatrix::set_values(std::vector<gdouble> &v){
214         values = v;
217 } /* namespace NR */
219 /*
220   Local Variables:
221   mode:c++
222   c-file-style:"stroustrup"
223   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
224   indent-tabs-mode:nil
225   fill-column:99
226   End:
227 */
228 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :