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 "../base/exif_data.hh"
31 #include "icc_transform.hh"
32 #include "image_reader.hh"
33
34
ImageReaderPar()35 PF::ImageReaderPar::ImageReaderPar():
36 OpParBase(),
37 file_name("file_name", this),
38 //out_profile_mode("profile_mode",this,PF::PROF_TYPE_REC2020,"REC2020","Rec.2020"),
39 in_profile_mode("in_profile_mode",this,PF::PROF_MODE_EMBEDDED_sRGB,"EMBEDDED",_("embedded")),
40 in_profile_type("in_profile_type",this,PF::PROF_TYPE_REC2020,"REC2020",_("Rec.2020")),
41 in_trc_type("in_trc_type",this,PF::PF_TRC_LINEAR,"TRC_LINEAR","linear"),
42 in_profile_name("in_profile_name",this),
43 //out_profile_mode("out_profile_mode",this,PF::PROF_TYPE_EMBEDDED,"EMBEDDED",_("same")),
44 out_profile_mode("out_profile_mode",this,PF::PROF_MODE_DEFAULT,"DEFAULT",_("default")),
45 out_profile_type("out_profile_type",this,PF::PROF_TYPE_REC2020,"REC2020",_("Rec.2020")),
46 //out_profile_type("out_profile_type",this,PF::PROF_TYPE_EMBEDDED,"EMBEDDED",_("use input")),
47 out_trc_type("out_trc_type",this,PF::PF_TRC_LINEAR,"TRC_LINEAR","linear"),
48 out_profile_name("out_profile_name",this),
49 image(NULL),
50 current_format(VIPS_FORMAT_NOTSET),
51 raster_image( NULL )
52 {
53 //in_profile_mode.add_enum_value(PF::PROF_MODE_EMBEDDED,"EMBEDDED",_("embedded"));
54 //in_profile_mode.add_enum_value(PF::PROF_MODE_EMBEDDED,"EMBEDDED_sRGB",_("embedded (sRGB)"));
55 in_profile_mode.add_enum_value(PF::PROF_MODE_NONE,"NONE",_("none"));
56 in_profile_mode.add_enum_value(PF::PROF_MODE_CUSTOM,"CUSTOM",_("custom"));
57 in_profile_mode.add_enum_value(PF::PROF_MODE_ICC,"ICC",_("ICC from disk"));
58
59 //in_profile_type.add_enum_value(PF::PROF_TYPE_NONE,"NONE","NONE");
60 in_profile_type.add_enum_value(PF::PROF_TYPE_sRGB,"sRGB","sRGB");
61 in_profile_type.add_enum_value(PF::PROF_TYPE_REC2020,"REC2020","Rec.2020");
62 in_profile_type.add_enum_value(PF::PROF_TYPE_ACES,"ACES","ACES");
63 in_profile_type.add_enum_value(PF::PROF_TYPE_ACEScg,"ACEScg","ACEScg");
64 in_profile_type.add_enum_value(PF::PROF_TYPE_ADOBE,"ADOBE","Adobe RGB 1998");
65 in_profile_type.add_enum_value(PF::PROF_TYPE_PROPHOTO,"PROPHOTO","ProPhoto RGB");
66 in_profile_type.add_enum_value(PF::PROF_TYPE_LAB,"LAB","Lab");
67 in_profile_type.add_enum_value(PF::PROF_TYPE_XYZ,"XYZ","XYZ D50");
68 //in_profile_type.add_enum_value(PF::PROF_TYPE_CUSTOM,"CUSTOM","Custom");
69
70 //out_profile_mode.add_enum_value(PF::PROF_TYPE_NONE,"NONE","NONE");
71 out_profile_mode.add_enum_value(PF::PROF_MODE_EMBEDDED,"EMBEDDED",_("use input"));
72 out_profile_mode.add_enum_value(PF::PROF_MODE_CUSTOM,"CUSTOM",_("custom"));
73 out_profile_mode.add_enum_value(PF::PROF_MODE_ICC,"ICC",_("ICC from disk"));
74
75 out_profile_type.add_enum_value(PF::PROF_TYPE_EMBEDDED,"EMBEDDED",_("use input"));
76 out_profile_type.add_enum_value(PF::PROF_TYPE_sRGB,"sRGB","sRGB");
77 out_profile_type.add_enum_value(PF::PROF_TYPE_REC2020,"REC2020","Rec.2020");
78 out_profile_type.add_enum_value(PF::PROF_TYPE_ADOBE,"ADOBE","Adobe RGB 1998");
79 out_profile_type.add_enum_value(PF::PROF_TYPE_PROPHOTO,"PROPHOTO","ProPhoto RGB");
80 out_profile_type.add_enum_value(PF::PROF_TYPE_ACEScg,"ACEScg","ACEScg");
81 out_profile_type.add_enum_value(PF::PROF_TYPE_ACES,"ACES","ACES");
82 //out_profile_type.add_enum_value(PF::PROF_TYPE_CUSTOM,"CUSTOM","Custom");
83 out_profile_type.add_enum_value(PF::PROF_TYPE_LAB,"LAB","Lab");
84 out_profile_type.add_enum_value(PF::PROF_TYPE_XYZ,"XYZ","XYZ D50");
85 out_profile_type.add_enum_value(PF::PROF_TYPE_FROM_SETTINGS,"FROM_SETTINGS","from settings");
86 out_profile_type.add_enum_value(PF::PROF_TYPE_FROM_DISK,"FROM_DISK","ICC from disk");
87
88
89 //in_trc_type.add_enum_value(PF::PF_TRC_LINEAR,"TRC_LINEAR","linear");
90 in_trc_type.add_enum_value(PF::PF_TRC_PERCEPTUAL,"TRC_PERCEPTUAL","perceptual");
91 in_trc_type.add_enum_value(PF::PF_TRC_STANDARD,"TRC_STANDARD","standard");
92
93 //out_trc_type.add_enum_value(PF::PF_TRC_LINEAR,"TRC_LINEAR","linear");
94 out_trc_type.add_enum_value(PF::PF_TRC_PERCEPTUAL,"TRC_PERCEPTUAL","perceptual");
95 out_trc_type.add_enum_value(PF::PF_TRC_STANDARD,"TRC_STANDARD","standard");
96
97 convert_format = new_convert_format();
98 blender = new_blender();
99 cs_transform = new_icc_transform();
100
101 set_type("imageread" );
102
103 set_default_name( _("image layer") );
104 }
105
106
~ImageReaderPar()107 PF::ImageReaderPar::~ImageReaderPar()
108 {
109 std::cout<<"ImageReaderPar::~ImageReaderPar(): raster_image="<<(void*)raster_image<<std::endl;
110 if( raster_image ) {
111 raster_image->unref();
112 std::cout<<"ImageReaderPar::~ImageReaderPar(): raster_image->get_nref()="<<raster_image->get_nref()<<std::endl;
113 if( raster_image->get_nref() == 0 ) {
114 std::map<Glib::ustring, RasterImage*>::iterator i =
115 raster_images.find( raster_image->get_file_name() );
116 if( i != raster_images.end() )
117 raster_images.erase( i );
118 delete raster_image;
119 std::cout<<"ImageReaderPar::~ImageReaderPar(): raster_image deleted"<<std::endl;
120 raster_image = 0;
121 }
122 }
123
124 delete convert_format;
125 delete blender;
126 delete cs_transform;
127 }
128
129
build(std::vector<VipsImage * > & in,int first,VipsImage * imap,VipsImage * omap,unsigned int & level)130 VipsImage* PF::ImageReaderPar::build(std::vector<VipsImage*>& in, int first,
131 VipsImage* imap, VipsImage* omap,
132 unsigned int& level)
133 {
134 bool modified = false;
135
136 if( file_name.get().empty() )
137 return NULL;
138
139
140 std::map<Glib::ustring, RasterImage*>::iterator i =
141 raster_images.find( file_name.get() );
142
143 RasterImage* new_raster_image = NULL;
144
145 if( i == raster_images.end() ) {
146 std::cout<<"ImageReaderPar::build(): creating new RasterImage for file "<<file_name.get()<<std::endl;
147 new_raster_image = new RasterImage( file_name.get() );
148 std::cout<<"ImageReaderPar::build(): RasterImage for file "<<file_name.get()<<" created"<<std::endl;
149 if( new_raster_image )
150 raster_images.insert( make_pair(file_name.get(), new_raster_image) );
151 } else {
152 //std::cout<<"ImageReaderPar::build(): raster_image found ("<<file_name.get()<<")"<<std::endl;
153 new_raster_image = i->second;
154 new_raster_image->ref();
155 }
156
157 //std::cout<<"ImageReaderPar::build(): raster_image="<<(void*)raster_image<<std::endl;
158 //if( raster_image ) std::cout<<"raster_image->get_nref(): "<<raster_image->get_nref()<<std::endl;
159 //if( new_raster_image ) std::cout<<"new_raster_image->get_nref(): "<<new_raster_image->get_nref()<<std::endl;
160
161 if( raster_image ) {
162 raster_image->unref();
163 //std::cout<<"ImageReaderPar::build(): raster_image->get_nref()="<<raster_image->get_nref()<<std::endl;
164 if( raster_image->get_nref() == 0 ) {
165 std::map<Glib::ustring, RasterImage*>::iterator i =
166 raster_images.find( file_name.get() );
167 if( i != raster_images.end() )
168 raster_images.erase( i );
169 delete raster_image;
170 std::cout<<"ImageReaderPar::build(): raster_image deleted"<<std::endl;
171 }
172 }
173
174 raster_image = new_raster_image;
175
176
177 if( !raster_image )
178 return NULL;
179
180 VipsImage* image = raster_image->get_image( level );
181
182 if( !image ) return NULL;
183
184 VipsImage* tmp;
185 vips_copy( image, &tmp, NULL );
186 PF_UNREF( image, "ImageReaderPar::build(): image unref after vips_copy" );
187 image = tmp;
188
189 if( image->Bands == 1 ) {
190
191 VipsImage* out;
192 VipsImage* bandv[3];
193 bandv[0] = image;
194 bandv[1] = image;
195 bandv[2] = image;
196 if( vips_bandjoin( bandv, &out, 3, NULL ) ) {
197 PF_UNREF( image, "ImageReaderPar::build(): image unref after bandjoin failure" );
198 return NULL;
199 }
200 PF_UNREF( image, "ImageReaderPar::build(): image unref" );
201 rgb_image( out->Xsize, out->Ysize );
202
203 vips_image_init_fields( out,
204 image->Xsize, image->Ysize,
205 3, image->BandFmt,
206 VIPS_CODING_NONE,
207 VIPS_INTERPRETATION_RGB,
208 1.0, 1.0);
209
210 image = out;
211 std::cout<<"ImageReaderPar::build(): converted from 1 to 3 bands"<<std::endl;
212 std::cout<<"ImageReaderPar::build(): image->Type"<<image->Type<<std::endl;
213
214 }
215
216
217
218 /*
219 {
220 size_t exifsz;
221 PF::exif_data_t* exif_data;
222 if( !vips_image_get_blob( image, PF_META_EXIF_NAME,
223 (void**)&exif_data,&exifsz ) ) {
224 //std::cout<<"ImageReaderPar::build(): exif_custom_data found in image("<<image<<")"<<std::endl;
225 } else {
226 //std::cout<<"ImageReaderPar::build(): exif_custom_data not found in image("<<image<<")"<<std::endl;
227 }
228 }
229 */
230 #ifndef NDEBUG
231 std::cout<<"ImageReaderPar::build(): "<<std::endl
232 <<"input images:"<<std::endl;
233 for(int i = 0; i < in.size(); i++) {
234 std::cout<<" "<<(void*)in[i]<<std::endl;
235 }
236 std::cout<<"image: "<<image<<" image->Interpretation: "<<image->Type<<std::endl;
237 std::cout<<"imap: "<<(void*)imap<<std::endl<<"omap: "<<(void*)omap<<std::endl;
238 #endif
239
240 if( is_map() && image->Bands > 1 ) {
241 VipsImage* out;
242 int nbands = 1;
243 if( vips_extract_band( image, &out, 0, "n", nbands, NULL ) ) {
244 std::cout<<"ImageReaderPar::build(): vips_extract_band() failed"<<std::endl;
245 return NULL;
246 }
247
248 vips_image_init_fields( out,
249 image->Xsize, image->Ysize,
250 nbands, image->BandFmt,
251 image->Coding,
252 image->Type,
253 1.0, 1.0);
254 PF_UNREF( image, "ImageReaderPar::build(): image unref after extract_band" );
255 image = out;
256 }
257
258
259
260 #ifndef NDEBUG
261 std::cout<<std::endl<<"================="<<std::endl;
262 std::cout<<"ImageReaderPar::build(): get_format()="<<get_format()<<" image->BandFmt="<<image->BandFmt<<std::endl;
263 #endif
264 VipsImage* out = image;
265 std::vector<VipsImage*> in2;
266 in2.push_back( image );
267 convert_format->get_par()->set_image_hints( image );
268 convert_format->get_par()->set_format( get_format() );
269 out = convert_format->get_par()->build( in2, 0, NULL, NULL, level );
270
271 if( !out ) return NULL;
272 PF_UNREF( image, "ImageReaderPar::build(): image unref after convert_format" );
273
274 #ifndef NDEBUG
275 {
276 size_t exifsz;
277 PF::exif_data_t* exif_data;
278 if( !PF_VIPS_IMAGE_GET_BLOB( image, PF_META_EXIF_NAME, &exif_data, &exifsz ) ) {
279 std::cout<<"ImageReaderPar::build(): exif_custom_data found in converted image("<<image<<")"<<std::endl;
280 } else {
281 std::cout<<"ImageReaderPar::build(): exif_custom_data not found in converted image("<<image<<")"<<std::endl;
282 }
283 }
284 {
285 size_t exifsz;
286 PF::exif_data_t* exif_data;
287 if( !PF_VIPS_IMAGE_GET_BLOB( out, PF_META_EXIF_NAME, &exif_data, &exifsz ) ) {
288 std::cout<<"ImageReaderPar::build(): exif_custom_data found in out("<<out<<")"<<std::endl;
289 } else {
290 std::cout<<"ImageReaderPar::build(): exif_custom_data not found in out("<<out<<")"<<std::endl;
291 }
292 }
293 #endif
294
295 set_image_hints( out );
296 #ifndef NDEBUG
297 std::cout<<"ImageReaderPar::build(): image refcount ("<<(void*)image<<") = "<<G_OBJECT(image)->ref_count<<std::endl;
298 std::cout<<" out refcount ("<<(void*)out<<") = "<<G_OBJECT(out)->ref_count<<std::endl;
299 #endif
300
301
302 bool changed = in_profile_mode.is_modified() || in_profile_type.is_modified() || in_trc_type.is_modified() ||
303 out_profile_mode.is_modified() || out_profile_type.is_modified() || out_trc_type.is_modified();
304
305 //if( in_profile ) cmsCloseProfile( in_profile );
306 //if( out_profile ) cmsCloseProfile( out_profile );
307 profile_type_t ptype;
308 //profile_mode_t pmode;
309 TRC_type trc_type;
310 PF::ICCProfile* in_iccprof = NULL;
311
312 if( (profile_mode_t)in_profile_mode.get_enum_value().first == PF::PROF_MODE_NONE ) {
313 // do nothing
314 } else if( (profile_mode_t)in_profile_mode.get_enum_value().first == PF::PROF_MODE_EMBEDDED ||
315 (profile_mode_t)in_profile_mode.get_enum_value().first == PF::PROF_MODE_EMBEDDED_sRGB ) {
316 void *data;
317 size_t data_length;
318 if( !PF_VIPS_IMAGE_GET_BLOB( image, VIPS_META_ICC_NAME, &data, &data_length ) ) {
319 in_iccprof = PF::ICCStore::Instance().get_profile( data, data_length );
320 if( in_iccprof ) {
321 cmsHPROFILE in_profile = in_iccprof->get_profile();
322 if( false && in_profile ) {
323 char tstr[1024];
324 cmsGetProfileInfoASCII(in_profile, cmsInfoDescription, "en", "US", tstr, 1024);
325 std::cout<<"ImageReader: Embedded profile found: "<<tstr<<std::endl;
326 //cmsCloseProfile( profile_in );
327 }
328 }
329 } else {
330 in_iccprof = PF::ICCStore::Instance().get_profile( PROF_TYPE_sRGB, PF_TRC_STANDARD );
331 if( in_iccprof ) {
332 cmsHPROFILE in_profile = in_iccprof->get_profile();
333 if( false && in_profile ) {
334 char tstr[1024];
335 cmsGetProfileInfoASCII(in_profile, cmsInfoDescription, "en", "US", tstr, 1024);
336 std::cout<<"ImageReader: using default profile: "<<tstr<<std::endl;
337 //cmsCloseProfile( profile_in );
338 }
339 }
340 }
341 } else if( (profile_mode_t)in_profile_mode.get_enum_value().first == PF::PROF_MODE_ICC ) {
342 in_iccprof = PF::ICCStore::Instance().get_profile( in_profile_name.get() );
343 if( in_iccprof ) {
344 cmsHPROFILE in_profile = in_iccprof->get_profile();
345 if( false && in_profile ) {
346 char tstr[1024];
347 cmsGetProfileInfoASCII(in_profile, cmsInfoDescription, "en", "US", tstr, 1024);
348 std::cout<<"ImageReader: custom input profile from disk: "<<tstr<<std::endl;
349 }
350 }
351 } else {
352 ptype = (profile_type_t)in_profile_type.get_enum_value().first;
353 trc_type = (TRC_type)in_trc_type.get_enum_value().first;
354 //std::cout<<"Getting input profile..."<<std::endl;
355 in_iccprof = PF::ICCStore::Instance().get_profile( ptype, trc_type );
356 if( in_iccprof ) {
357 //std::cout<<"... OK"<<std::endl;
358 } else {
359 std::cout<<"ImageReader: cannot load input profile"<<std::endl;
360 }
361 }
362
363 PF::ICCProfile* out_iccprof = NULL;
364 if( (profile_mode_t)in_profile_mode.get_enum_value().first != PF::PROF_MODE_NONE ) {
365 PF::set_icc_profile( out, in_iccprof );
366 // only retrieve the working profile if the image is color managed
367 ptype = (profile_type_t)out_profile_type.get_enum_value().first;
368 if( ptype == PF::PROF_TYPE_EMBEDDED ) {
369 // do nothing
370 //std::cout<<"Using embedded profile"<<std::endl;
371 out_iccprof = in_iccprof;
372 } else if( ptype == PF::PROF_TYPE_FROM_SETTINGS && in_iccprof ) {
373 ptype = PF::PhotoFlow::Instance().get_options().get_working_profile_type();
374 trc_type = PF::PhotoFlow::Instance().get_options().get_working_trc_type();
375 //std::cout<<"Getting output profile..."<<std::endl;
376 out_iccprof = PF::ICCStore::Instance().get_profile( ptype, trc_type );
377 } else if( ptype == PF::PROF_TYPE_FROM_DISK && in_iccprof ) {
378 out_iccprof = PF::ICCStore::Instance().get_profile( out_profile_name.get() );
379 } else {//if( pmode == PF::PROF_MODE_CUSTOM && in_iccprof ) {
380 ptype = (profile_type_t)out_profile_type.get_enum_value().first;
381 trc_type = (TRC_type)out_trc_type.get_enum_value().first;
382 //std::cout<<"Getting output profile..."<<std::endl;
383 out_iccprof = PF::ICCStore::Instance().get_profile( ptype, trc_type );
384 }
385 if( out_iccprof ) {
386 //std::cout<<"... OK"<<std::endl;
387 } else {
388 std::cout<<"ImageReader: cannot load output profile"<<std::endl;
389 }
390
391 if( in_iccprof && out_iccprof && in_iccprof->get_profile() != out_iccprof->get_profile() ) {
392 PF::ICCTransformPar* tr_par =
393 dynamic_cast<PF::ICCTransformPar*>( cs_transform->get_par() );
394 std::vector<VipsImage*> in2;
395 in2.push_back( out );
396 tr_par->set_image_hints( out );
397 tr_par->set_format( get_format() );
398 tr_par->set_out_profile( out_iccprof );
399 tr_par->set_bpc( false );
400 tr_par->set_adaptation_state( 0.f );
401 tr_par->set_clip_negative(false);
402 tr_par->set_clip_overflow(false);
403 VipsImage* out2 = tr_par->build( in2, 0, NULL, NULL, level );
404 if( out2 ) {
405 PF_UNREF( out, "RawOutputPar::build(): out unref" );
406 }
407 out = out2;
408 }
409 }
410
411 //PF::print_embedded_profile( out );
412 return out;
413 }
414