1 /*
2 */
3
4 /*
5
6 Copyright (C) 2014 Ferrero Andrea
7
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20
21
22 */
23
24 /*
25
26 These files are distributed with PhotoFlow - http://aferrero2707.github.io/PhotoFlow/
27
28 */
29
30 #include "icc_transform.hh"
31 #include "../base/processor_imp.hh"
32
33
34
ICCTransformPar()35 PF::ICCTransformPar::ICCTransformPar():
36 OpParBase(),
37 out_profile( NULL ),
38 intent( INTENT_RELATIVE_COLORIMETRIC ),
39 bpc( true ),
40 adaptation_state(-1),
41 input_cs_type( cmsSigRgbData ),
42 output_cs_type( cmsSigRgbData ),
43 clip_negative(false),
44 clip_overflow(false),
45 gamut_mapping(false),
46 saturation_intent(0)
47 {
48 do_Lab = true; do_LCh = do_LSh = false;
49 set_type("icc_transform" );
50
51 set_default_name( _("ICC transform") );
52 }
53
54
build(std::vector<VipsImage * > & in,int first,VipsImage * imap,VipsImage * omap,unsigned int & level)55 VipsImage* PF::ICCTransformPar::build(std::vector<VipsImage*>& in, int first,
56 VipsImage* imap, VipsImage* omap,
57 unsigned int& level)
58 {
59 if( (int)in.size() < first+1 ) {
60 return NULL;
61 }
62
63 VipsImage* image = in[first];
64 if( !image ) {
65 return NULL;
66 }
67
68 void *data;
69 size_t data_length;
70
71 in_profile = PF::get_icc_profile( in[first] );
72
73 if( !in_profile || !out_profile ) {
74 PF_REF( in[first], "ICCTransformPar::build(): input image ref for missing input or output profiles" );
75 std::cout<<"ICCTransformPar::build(): missing input or output profiles, no transform needed"<<std::endl;
76 return in[first];
77 }
78
79
80 bool matching = false;
81 if( in_profile && out_profile && in_profile->equals_to(out_profile) ) {
82 matching = true;
83 }
84
85 if( matching ) {
86 PF_REF( in[first], "ICCTransformPar::build(): input image ref for equal input and output profiles" );
87 //std::cout<<"ICCTransformPar::build(): matching input and output profiles, no transform needed"<<std::endl;
88 return in[first];
89 }
90
91 if( gamut_mapping )
92 out_profile->init_gamut_mapping();
93
94
95 //std::cout<<"ICCTransformPar::build(): image="<<in[0]<<" data="<<data<<" data_length="<<data_length<<std::endl;
96
97 bool in_changed = false;
98 if( in_profile && in_profile->get_profile() ) {
99 char tstr[1024];
100 cmsGetProfileInfoASCII(in_profile->get_profile(), cmsInfoDescription, "en", "US", tstr, 1024);
101 #ifndef NDEBUG
102 std::cout<<"icc_transform: embedded profile: "<<in_profile<<std::endl;
103 std::cout<<"icc_transform: embedded profile name: "<<tstr<<std::endl;
104 #endif
105
106 if( in_profile_name != tstr ) {
107 in_changed = true;
108 }
109
110 input_cs_type = cmsGetColorSpace( in_profile->get_profile() );
111 }
112
113 #ifndef NDEBUG
114 if( out_profile )
115 std::cout<<"icc_transform: out_profile="<<out_profile<<" ("<<out_profile->get_profile()<<")"<<std::endl;
116 #endif
117
118 if( in_profile && out_profile && out_profile->get_profile() ) {
119 transform.init( in_profile, out_profile, in[0]->BandFmt, intent, get_bpc(), adaptation_state );
120 }
121
122 if( out_profile && out_profile->get_profile() ) {
123 #ifndef NDEBUG
124 std::cout<<"icc_transform: output profile: "<<out_profile<<std::endl;
125 char tstr[1024];
126 cmsGetProfileInfoASCII(out_profile->get_profile(), cmsInfoDescription, "en", "US", tstr, 1024);
127 std::cout<<"icc_transform: output profile: "<<tstr<<std::endl;
128 #endif
129 output_cs_type = cmsGetColorSpace( out_profile->get_profile() );
130 switch( output_cs_type ) {
131 case cmsSigGrayData:
132 grayscale_image( get_xsize(), get_ysize() );
133 break;
134 case cmsSigRgbData:
135 rgb_image( get_xsize(), get_ysize() );
136 break;
137 case cmsSigLabData:
138 lab_image( get_xsize(), get_ysize() );
139 break;
140 case cmsSigCmykData:
141 cmyk_image( get_xsize(), get_ysize() );
142 break;
143 default:
144 break;
145 }
146 }
147
148 //if( in_profile ) cmsCloseProfile( in_profile );
149
150 VipsImage* out = OpParBase::build( in, first, NULL, NULL, level );
151
152 if( out && out_profile ) {
153 PF::set_icc_profile( out, out_profile );
154 }
155
156 return out;
157 }
158
159
160 using namespace PF;
161
162
163 template < OP_TEMPLATE_DEF >
164 class ICCTransformProc
165 {
166 public:
render(VipsRegion ** ireg,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * oreg,OpParBase * par)167 void render(VipsRegion** ireg, int n, int in_first,
168 VipsRegion* imap, VipsRegion* omap,
169 VipsRegion* oreg, OpParBase* par)
170 {
171 ICCTransformPar* opar = dynamic_cast<ICCTransformPar*>(par);
172 if( !opar ) return;
173 VipsRect *r = &oreg->valid;
174 int line_size = r->width * oreg->im->Bands; //layer->in_all[0]->Bands;
175 int width = r->width;
176 int height = r->height;
177
178 T* p;
179 T* pin;
180 T* pout;
181 int x, y;
182
183 for( y = 0; y < height; y++ ) {
184 p = (T*)VIPS_REGION_ADDR( ireg[in_first], r->left, r->top + y );
185 pout = (T*)VIPS_REGION_ADDR( oreg, r->left, r->top + y );
186
187 pin = p;
188 if(opar->get_transform().valid())
189 //cmsDoTransform( opar->get_transform(), pin, pout, width );
190 opar->get_transform().apply(pin,pout,width);
191 else
192 memcpy( pout, pin, sizeof(T)*line_size );
193 }
194 }
195 };
196
197
198
199
200 template < OP_TEMPLATE_DEF_TYPE_SPEC >
201 class ICCTransformProc< OP_TEMPLATE_IMP_TYPE_SPEC(float) >
202 {
203 public:
render(VipsRegion ** ireg,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * oreg,OpParBase * par)204 void render(VipsRegion** ireg, int n, int in_first,
205 VipsRegion* imap, VipsRegion* omap,
206 VipsRegion* oreg, OpParBase* par)
207 {
208 ICCTransformPar* opar = dynamic_cast<ICCTransformPar*>(par);
209 if( !opar ) return;
210 VipsRect *r = &oreg->valid;
211 int line_size_in = ireg[in_first]->valid.width * ireg[in_first]->im->Bands; //layer->in_all[0]->Bands;
212 int line_size_out = r->width * oreg->im->Bands; //layer->in_all[0]->Bands;
213 int line_size_max = (line_size_in > line_size_out) ? line_size_in : line_size_out;
214 int width = r->width;
215 int height = r->height;
216
217 float* p;
218 //float* pin;
219 float* pout;
220 int x, y;
221
222 float* line = NULL;
223 if( opar->get_input_cs_type() == cmsSigLabData ||
224 opar->get_input_cs_type() == cmsSigCmykData ||
225 opar->get_gamut_mapping() ) {
226 line = new float[line_size_max];
227 }
228
229 for( y = 0; y < height; y++ ) {
230 //std::cout<<"icc_transform: ti="<<ti<<" y="<<y<<" corner="<<r->left<<","<<r->top<<std::endl;
231 p = (float*)VIPS_REGION_ADDR( ireg[in_first], r->left, r->top + y );
232 pout = (float*)VIPS_REGION_ADDR( oreg, r->left, r->top + y );
233
234 if(opar->get_transform().valid()) {
235 if( opar->get_input_cs_type() == cmsSigLabData ) {
236 for( x = 0; x < line_size_in; x+= 3 ) {
237 line[x] = (cmsFloat32Number) (p[x] * 100.0);
238 line[x+1] = (cmsFloat32Number) (p[x+1]*256.0f - 128.0f);
239 line[x+2] = (cmsFloat32Number) (p[x+2]*256.0f - 128.0f);
240 if( false && r->left==0 && r->top==0 && x==0 && y==0 ) {
241 std::cout<<"ICCTransform::render(): line="<<line[x]<<" "<<line[x+1]<<" "<<line[x+2]<<std::endl;
242 }
243 }
244 //cmsDoTransform( opar->get_transform(), line, pout, width );
245 opar->get_transform().apply(line,pout,width);
246 if( false && r->left==0 && r->top==0 && y==0 ) {
247 std::cout<<"ICCTransform::render(Lab): pout="<<pout[0]<<" "<<pout[1]<<" "<<pout[2]<<std::endl;
248 }
249 } else if( opar->get_input_cs_type() == cmsSigCmykData ) {
250 for( x = 0; x < line_size_in; x+= 4 ) {
251 line[x] = (cmsFloat32Number) (p[x] * 100.0);
252 line[x+1] = (cmsFloat32Number) (p[x+1] * 100.0);
253 line[x+2] = (cmsFloat32Number) (p[x+2] * 100.0);
254 line[x+3] = (cmsFloat32Number) (p[x+3] * 100.0);
255 if( false && r->left==0 && r->top==0 && x==0 && y==0 ) {
256 std::cout<<"ICCTransform::render(CMYK in): line="<<line[x]<<" "<<line[x+1]<<" "<<line[x+2]<<" "<<line[x+3]<<std::endl;
257 }
258 }
259 //cmsDoTransform( opar->get_transform(), line, pout, width );
260 opar->get_transform().apply(line,pout,width);
261 if( false && r->left==0 && r->top==0 && y==0 ) {
262 std::cout<<"ICCTransform::render(CMYK in): pout="<<pout[0]<<" "<<pout[1]<<" "<<pout[2]<<std::endl;
263 }
264 } else {
265 if( opar->get_in_profile() && opar->get_in_profile()->is_rgb() &&
266 opar->get_out_profile() && opar->get_out_profile()->is_rgb() &&
267 opar->get_gamut_mapping() ) {
268 ICCProfile* inprof = opar->get_in_profile();
269 ICCProfile* outprof = opar->get_out_profile();
270 float** gbound = outprof->get_gamut_boundary();
271 float* gLid = outprof->get_gamut_Lid_Cmax();
272 for( x = 0; x < line_size_out; x+= 3 ) {
273 line[x] = p[x]; line[x+1] = p[x+1]; line[x+2] = p[x+2];
274 inprof->gamut_mapping(line[x], line[x+1], line[x+2], gbound, gLid, opar->get_saturation_intent());
275 }
276 opar->get_transform().apply(line,pout,width);
277 } else {
278 //cmsDoTransform( opar->get_transform(), p, pout, width );
279 opar->get_transform().apply(p,pout,width);
280 }
281 if( false && r->left==0 && r->top==0 && y==0 ) {
282 std::cout<<"ICCTransform::render(): pout="<<pout[0]<<" "<<pout[1]<<" "<<pout[2]<<std::endl;
283 }
284 }
285 if( opar->get_output_cs_type() == cmsSigLabData ) {
286 for( x = 0; x < line_size_out; x+= 3 ) {
287
288 if( opar->get_LCh_format() || opar->get_LSh_format() ) {
289 PF::Lab2LCH( &(pout[x]), &(pout[x]), 1 );
290 if( opar->get_LSh_format() ) {
291 float den = std::sqrt(pout[x]*pout[x] + pout[x+1]*pout[x+1]);
292 if( den > 1.0e-10 ) pout[x+1] /= den;
293 else pout[x+1] = 0;
294 } else {
295 pout[x+1] /= 256.0f;
296 }
297 pout[x] = (cmsFloat32Number) (pout[x] / 100.0);
298 //std::cout<<"H: "<<pout[x+2]<<std::endl;
299 pout[x+2] /= (M_PI*2);
300 } else {
301 pout[x] = (cmsFloat32Number) (pout[x] / 100.0);
302 pout[x+1] = (cmsFloat32Number) ((pout[x+1] + 128.0f) / 256.0f);
303 pout[x+2] = (cmsFloat32Number) ((pout[x+2] + 128.0f) / 256.0f);
304 }
305
306 //if( r->left==0 && r->top==0 && x==0 && y==0 ) {
307 // std::cout<<"Convert2LabProc::render(): pout="<<pout[x]<<" "<<pout[x+1]<<" "<<pout[x+2]<<std::endl;
308 //}
309 }
310 } else if( opar->get_output_cs_type() == cmsSigCmykData ) {
311 for( x = 0; x < line_size_out; x+= 4 ) {
312 if( false && r->left==0 && r->top==0 && x==0 && y==0 ) {
313 std::cout<<"ICCTransform::render(CMYK out): pout="<<pout[x]<<" "<<pout[x+1]<<" "<<pout[x+2]<<" "<<pout[x+3]<<std::endl;
314 }
315 pout[x] = (cmsFloat32Number) (pout[x] / 100.0);
316 pout[x+1] = (cmsFloat32Number) (pout[x+1] / 100.0);
317 pout[x+2] = (cmsFloat32Number) (pout[x+2] / 100.0);
318 pout[x+3] = (cmsFloat32Number) (pout[x+3] / 100.0);
319 if( false && r->left==0 && r->top==0 && x==0 && y==0 ) {
320 std::cout<<"ICCTransform::render(CMYK out): pout="<<pout[x]<<" "<<pout[x+1]<<" "<<pout[x+2]<<" "<<pout[x+3]<<std::endl;
321 }
322 }
323 }
324 } else {
325 memcpy( pout, p, sizeof(float)*line_size_in );
326 }
327 //std::cout<<"opar->get_out_profile(): "<<opar->get_out_profile()
328 // <<" opar->get_out_profile()->is_rgb(): "<<opar->get_out_profile()->is_rgb()
329 // <<" opar->get_gamut_mapping(): "<<opar->get_gamut_mapping()<<std::endl;
330 if( opar->get_clip_negative() || opar->get_clip_overflow() ) {
331 for( x = 0; x < line_size_out; x+= 1 ) {
332 if( opar->get_clip_negative() ) pout[x] = MAX( pout[x], 0.f );
333 if( opar->get_clip_overflow() ) pout[x] = MIN( pout[x], 1.f );
334 }
335 }
336 }
337
338 if( line ) {
339 delete( line );
340 }
341 }
342 };
343
344
345
new_icc_transform()346 PF::ProcessorBase* PF::new_icc_transform()
347 {
348 return new PF::Processor<PF::ICCTransformPar,ICCTransformProc>();
349 }
350