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