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 <lcms2.h>
31 
32 #include "file_util.hh"
33 #include "operation.hh"
34 #include "layer.hh"
35 #include "../operations/image_to_map.hh"
36 //#include "../vips/vips_layer.h"
37 
38 static int _phf_operation_serial_id = 0;
39 
40 int
41 vips_layer( int n, VipsImage **out,
42     PF::ProcessorBase* proc,
43     VipsImage* imap, VipsImage* omap,
44     VipsDemandStyle demand_hint,
45     int width, int height, int nbands, int sid, ... );
46 
47 
48 
49 
50 
open()51 void PF::OperationConfigUI::open()
52 {
53   get_layer()->get_processor()->get_par()->save_properties(initial_params);
54 }
55 
56 
57 
58 
OpParBase()59 PF::OpParBase::OpParBase(): output_caching_enabled(false),
60     render_mode(PF_RENDER_PREVIEW),
61     map_flag( false ),
62     editing_flag( false ),
63     modified_flag(false),
64     intensity("intensity",this,1),
65     grey_target_channel("grey_target_channel",this,-1,"Grey","Grey"),
66     rgb_target_channel("rgb_target_channel",this,-1,"RGB","RGB"),
67     lab_target_channel("lab_target_channel",this,-1,"Lab","Lab"),
68     cmyk_target_channel("cmyk_target_channel",this,-1,"CMYK","CMYK"),
69     mask_enabled("mask_enabled",this,true),
70     file_format_version( PF_FILE_VERSION ),
71     enable_padding( "enable_padding", this, false ), test_padding(64)
72 {
73   //blend_mode.set_internal(true);
74   intensity.set_internal(true);
75   //opacity.set_internal(true);
76   grey_target_channel.set_internal(true);
77   rgb_target_channel.set_internal(true);
78   lab_target_channel.set_internal(true);
79   cmyk_target_channel.set_internal(true);
80 
81   processor = NULL;
82   //out = NULL;
83   config_ui = NULL;
84   //blend_mode = PF_BLEND_PASSTHROUGH;
85   //blend_mode = PF_BLEND_NORMAL;
86   //blend_mode.set_enum_value( PF_BLEND_PASSTHROUGH );
87   demand_hint = VIPS_DEMAND_STYLE_ANY;
88   bands = 1;
89   xsize = 100; ysize = 100;
90 
91   rgb_target_channel.add_enum_value(0,"R","R");
92   rgb_target_channel.add_enum_value(1,"G","G");
93   rgb_target_channel.add_enum_value(2,"B","B");
94 
95   lab_target_channel.add_enum_value(0,"L","L");
96   lab_target_channel.add_enum_value(1,"a","a");
97   lab_target_channel.add_enum_value(2,"b","b");
98 
99   cmyk_target_channel.add_enum_value(0,"C","C");
100   cmyk_target_channel.add_enum_value(1,"M","M");
101   cmyk_target_channel.add_enum_value(2,"Y","Y");
102   cmyk_target_channel.add_enum_value(3,"K","K");
103 
104   to_map = NULL;
105 
106   //PF::PropertyBase* prop;
107   //prop = new PF::Property<float>("intensity",&intensity);
108 }
109 
110 
111 
~OpParBase()112 PF::OpParBase::~OpParBase()
113 {
114   //for(unsigned int i = 0; i < outvec.size(); i++ ) {
115   //  PF_UNREF( outvec[i], "~OpParBase(): previous outputs unref" );
116   //}
117   //#ifndef NDEBUG
118   //std::cout<<"~OpParBase(): deleting operation "<<(void*)this<<" ("<<get_type()<<")"<<std::endl;
119   //#endif
120 }
121 
122 
123 
get_property(std::string name)124 PF::PropertyBase* PF::OpParBase::get_property(std::string name)
125 {
126   std::list<PropertyBase*>::iterator pi;
127 
128   // Look into mapped properties first
129   for(pi = mapped_properties.begin(); pi != mapped_properties.end(); pi++) {
130     //std::cout<<"(*pi)->get_name(): "<<(*pi)->get_name()<<"    name: "<<name<<std::endl;
131     if( (*pi)->get_name() == name ) return( *pi );
132   }
133 
134   // If nothing is found, look into our own properties
135   for(pi = properties.begin(); pi != properties.end(); pi++) {
136     //std::cout<<"(*pi)->get_name(): "<<(*pi)->get_name()<<"    name: "<<name<<std::endl;
137     if( (*pi)->get_name() == name ) return( *pi );
138   }
139   return NULL;
140 }
141 
142 
save_properties(std::list<std::string> & plist)143 void PF::OpParBase::save_properties(std::list<std::string>& plist)
144 {
145   std::list<PropertyBase*>::iterator pi;
146   for(pi = mapped_properties.begin(); pi != mapped_properties.end(); pi++) {
147     std::string str = (*pi)->get_str();
148     plist.push_back(str);
149   }
150   for(pi = properties.begin(); pi != properties.end(); pi++) {
151     std::string str = (*pi)->get_str();
152     plist.push_back(str);
153   }
154 }
155 
156 
restore_properties(const std::list<std::string> & plist)157 void PF::OpParBase::restore_properties(const std::list<std::string>& plist)
158 {
159   std::list<PropertyBase*>::iterator pi;
160   std::list<std::string>::const_iterator si;
161   for(pi = mapped_properties.begin(), si = plist.begin();
162       (pi != mapped_properties.end()) && (si != plist.end());
163       pi++, si++) {
164     (*pi)->set_str(*si);
165   }
166   for(pi = properties.begin();
167       (pi != properties.end()) && (si != plist.end());
168       pi++, si++) {
169     (*pi)->set_str(*si);
170   }
171 }
172 
173 
compute_padding(VipsImage * full_res,unsigned int id,unsigned int level)174 void PF::OpParBase::compute_padding( VipsImage* full_res, unsigned int id, unsigned int level )
175 {
176   int p = enable_padding.get() ? test_padding : 0;
177   set_padding( p, id );
178 }
179 
180 
modified()181 void PF::OpParBase::modified()
182 {
183   //std::cout<<"OpParBase::modified() called for "<<type<<std::endl;
184   set_modified();
185   //std::cout<<"OpParBase::modified(): emitting signal_modified."<<std::endl;
186   signal_modified.emit();
187   //std::cout<<"OpParBase::modified(): signal_modified emitted."<<std::endl;
188 }
189 
190 
clear_modified()191 void PF::OpParBase::clear_modified()
192 {
193   modified_flag = false;
194   std::list<PropertyBase*>::iterator pi;
195   for(pi = mapped_properties.begin();
196       pi != mapped_properties.end();
197       pi++) {
198     (*pi)->clear_modified();
199   }
200   for(pi = properties.begin();
201       pi != properties.end();
202       pi++) {
203     (*pi)->clear_modified();
204   }
205 }
206 
set_image_hints(int w,int h,VipsInterpretation interpr)207 void PF::OpParBase::set_image_hints(int w, int h, VipsInterpretation interpr)
208 {
209   xsize = w;
210   ysize = h;
211   coding = VIPS_CODING_NONE;
212   interpretation = interpr;
213 }
214 
215 
216 
set_image_hints(int w,int h,colorspace_t cs)217 void PF::OpParBase::set_image_hints(int w, int h, colorspace_t cs)
218 {
219   xsize = w;
220   ysize = h;
221   coding = VIPS_CODING_NONE;
222   switch(cs) {
223   case PF_COLORSPACE_GRAYSCALE:
224     interpretation = VIPS_INTERPRETATION_B_W; break;
225   case PF_COLORSPACE_RGB:
226     interpretation = VIPS_INTERPRETATION_RGB; break;
227   case PF_COLORSPACE_LAB:
228     interpretation = VIPS_INTERPRETATION_LAB; break;
229   case PF_COLORSPACE_CMYK:
230     interpretation = VIPS_INTERPRETATION_CMYK; break;
231   default:
232     interpretation = VIPS_INTERPRETATION_MULTIBAND; break;
233   }
234 }
235 
236 
import_settings(OpParBase * pin)237 bool PF::OpParBase::import_settings( OpParBase* pin )
238 {
239   if( !pin ) {
240     std::cout<<"OpParBase::import_settings(): pin = NULL"<<std::endl;
241     return false;
242   }
243 
244   std::list<PropertyBase*>& propin = pin->get_properties();
245   std::list<PropertyBase*>::iterator pi=propin.begin(), pj=properties.begin();
246   for( ; pi != propin.end(); pi++, pj++ ) {
247     if( !(*pj)->import( *pi ) ) {
248       std::cout<<"OpParBase::import_settings(): failed to import values for property \""<<(*pj)->get_name()<<"\""<<std::endl;
249       return false;
250     }
251     //std::cout<<"OpParBase::import_settings(): property \""<<(*pj)->get_name()<<"\" se to "<<(*pj)->get_str()<<std::endl;
252   }
253 
254   std::list<PropertyBase*>& mpropin = pin->get_mapped_properties();
255   std::list<PropertyBase*>::iterator mpi=mpropin.begin(), mpj=mapped_properties.begin();
256   for( ; mpi != mpropin.end(); mpi++, mpj++ ) {
257     if( !(*mpj)->import( *mpi ) ) {
258       std::cout<<"OpParBase::import_settings(): failed to import values for property \""<<(*mpj)->get_name()<<"\""<<std::endl;
259       return false;
260     }
261   }
262 
263   // update properties of sub-operations
264   propagate_settings();
265 
266   set_map_flag( pin->is_map() );
267   //std::cout<<"OpParBase["<<get_type()<<"]::import_settings(): set_editing_flag("<<pin->is_editing()<<")"<<std::endl;
268   set_editing_flag( pin->is_editing() );
269   //set_demand_hint( pin->get_demand_hint() );
270   //set_image_hints( pin->get_xsize(), pin->get_ysize(),
271   //	   pin->get_interpretation() );
272   //set_nbands( pin->get_nbands() );
273   //set_coding( pin->get_coding() );
274   //set_format( pin->get_format() );
275   return true;
276 }
277 
278 
build(std::vector<VipsImage * > & in,int first,VipsImage * imap,VipsImage * omap,unsigned int & level)279 VipsImage* PF::OpParBase::build(std::vector<VipsImage*>& in, int first,
280     VipsImage* imap, VipsImage* omap, unsigned int& level)
281 {
282   VipsImage* outnew = NULL;
283   VipsImage* invec[100];
284   unsigned int n = 0;
285   for(unsigned int i = 0; i < in.size(); i++) {
286     if( !in[i] ) continue;
287     invec[n] = in[i];
288     n++;
289   }
290   if(n > 100) n = 100;
291 #ifndef NDEBUG
292   //std::cout<<"OpParBase::build("<<get_type()<<"): size = "<<get_xsize()<<"x"<<get_ysize()<<std::endl;
293   std::cout<<"OpParBase::build("<<get_type()<<"): in.size()="<<in.size()<<"  n="<<n<<std::endl;
294 #endif
295   if( _phf_operation_serial_id == 0xFFFFFFF ) _phf_operation_serial_id = 0;
296   else _phf_operation_serial_id += 1;
297   switch( n ) {
298   case 0:
299     vips_layer( n, &outnew, processor, imap, omap,
300         get_demand_hint(), get_xsize(), get_ysize(), get_nbands(), _phf_operation_serial_id,
301         NULL );
302     break;
303   case 1:
304     vips_layer( n, &outnew, processor, imap, omap,
305         get_demand_hint(), get_xsize(), get_ysize(), get_nbands(), _phf_operation_serial_id,
306         "in0", invec[0], NULL );
307     break;
308   case 2:
309     vips_layer( n, &outnew, processor, imap, omap,
310         get_demand_hint(), get_xsize(), get_ysize(), get_nbands(), _phf_operation_serial_id,
311         "in0", invec[0], "in1", invec[1], NULL );
312     break;
313   case 3:
314     vips_layer( n, &outnew, processor, imap, omap,
315         get_demand_hint(), get_xsize(), get_ysize(), get_nbands(), _phf_operation_serial_id,
316         "in0", invec[0], "in1", invec[1],
317         "in2", invec[2], NULL );
318     break;
319   case 4:
320     vips_layer( n, &outnew, processor, imap, omap,
321         get_demand_hint(), get_xsize(), get_ysize(), get_nbands(), _phf_operation_serial_id,
322         "in0", invec[0], "in1", invec[1],
323         "in2", invec[2], "in3", invec[3], NULL );
324     break;
325   default:
326     break;
327   }
328 
329 #ifndef NDEBUG
330   std::cout<<"OpParBase::build(): type="<<type<<"  format="<<get_format()<<std::endl
331       <<"input images:"<<std::endl;
332   for(int i = 0; i < n; i++) {
333     std::cout<<"  "<<(void*)invec[i]<<"   ref_count="<<G_OBJECT( invec[i] )->ref_count<<std::endl;
334   }
335   std::cout<<"imap: "<<(void*)imap<<std::endl<<"omap: "<<(void*)omap<<std::endl;
336   std::cout<<"out: "<<(void*)outnew<<std::endl<<std::endl;
337 #endif
338 
339   //set_image( outnew );
340 #ifndef NDEBUG
341   std::cout<<"OpParBase::build(): outnew refcount ("<<(void*)outnew<<") = "<<G_OBJECT(outnew)->ref_count<<std::endl;
342 #endif
343   return outnew;
344 }
345 
346 
347 
build_many(std::vector<VipsImage * > & in,int first,VipsImage * imap,VipsImage * omap,unsigned int & level)348 std::vector<VipsImage*> PF::OpParBase::build_many(std::vector<VipsImage*>& in, int first,
349     VipsImage* imap, VipsImage* omap, unsigned int& level)
350 {
351   std::vector<VipsImage*> result;
352   VipsImage* out = build( in, first, imap, omap, level );
353   //std::cout<<"OpParBase::build_many(): padding="<<get_padding()<<std::endl;
354 
355   VipsImage* cached = out;
356   /*
357   if( out && false && needs_caching() ) {
358     int tw = 64, th = 64;
359     // reserve two complete rows of tiles
360     int nt = out->Xsize*2/tw;
361     VipsAccess acc = VIPS_ACCESS_RANDOM;
362     int threaded = 1, persistent = 0;
363 
364     if( vips_tilecache(out, &cached,
365         "tile_width", tw, "tile_height", th, "max_tiles", nt,
366         "access", acc, "threaded", threaded, "persistent", persistent, NULL) ) {
367       std::cout<<"GaussBlurPar::build(): vips_tilecache() failed."<<std::endl;
368       return result;
369     }
370     PF_UNREF( out, "OpParBase::build_many(): out unref" );
371   }
372    */
373   result.push_back( cached );
374   return result;
375 }
376 
377 
build_many_internal(std::vector<VipsImage * > & in,int first,VipsImage * imap,VipsImage * omap,unsigned int & level)378 std::vector<VipsImage*> PF::OpParBase::build_many_internal(std::vector<VipsImage*>& in, int first,
379     VipsImage* imap, VipsImage* omap, unsigned int& level)
380 {
381   std::vector<VipsImage*> in_temp;
382 
383   if( true && convert_inputs_on_map_build() && is_map() && interpretation == VIPS_INTERPRETATION_B_W ) {
384     if( !to_map ) to_map = new_image_to_map();
385     std::vector<VipsImage*> in_temp2;
386     std::vector<VipsImage*>* in_ptr = in_temp.empty() ? &in : &in_temp;
387     for(unsigned int i = 0; i < in_ptr->size(); i++) {
388       VipsImage* img = (*in_ptr)[i];
389       if( img == NULL || img->Type == interpretation ) {
390         in_temp2.push_back(img);
391         std::cout<<"OpParBase::build_many_internal: op_type="<<get_type()<<"  in_temp2.push_back("<<img<<")"<<std::endl;
392         PF_REF( img, "build_many_internal: img ref" );
393       } else {
394         std::vector<VipsImage*> in2; in2.push_back(img);
395         std::cout<<"OpParBase::build_many_internal: to_map="<<to_map<<std::endl;
396         PF::OpParBase* to_map_op = to_map->get_par();
397         to_map_op->set_format(get_format());
398         //to_map_op->set_image_hints(img);
399         //to_map_op->grayscale_image( img->Xsize, img->Ysize );
400         VipsImage* bwimg = to_map_op->build(in2, 0, NULL, NULL, level);
401         std::cout<<"OpParBase::build_many_internal: bwimg built"<<std::endl;
402         in_temp2.push_back(bwimg);
403         //PF_UNREF( img, "build_many_internal: img unref" );
404       }
405     }
406     in_temp = in_temp2;
407   }
408 
409   std::vector<VipsImage*> result;
410   if( in_temp.empty() )
411     result = build_many( in, first, imap, omap, level );
412   else
413     result = build_many( in_temp, first, imap, omap, level );
414 
415 #ifndef NDEBUG
416   std::cout<<"OpParBase::build_many_internal(): filling hierarchy with padding "<<get_padding()<<std::endl;
417 #endif
418   if( in_temp.empty() )
419     fill_image_hierarchy( in, imap, omap, result );
420   else
421     fill_image_hierarchy( in_temp, imap, omap, result );
422 
423   // add output caching if needed
424   if( output_caching_enabled ) {
425 
426     std::vector<VipsImage*> result_cached;
427     for( unsigned int i = 0; i < result.size(); i++ ) {
428       bool is_dup = false;
429       VipsImage* out = result[i];
430       for( unsigned int j = 0; j < in.size(); j++ ) {
431         if( out == in[j] ) {
432           is_dup = true;
433           break;
434         }
435       }
436       if( is_dup ) {
437         result_cached.push_back( out );
438         continue;
439       }
440 
441       int p = get_output_padding( i );
442       if( p > 32 || needs_caching() ) {
443         VipsAccess acc = VIPS_ACCESS_RANDOM;
444         int threaded = 1, persistent = 1;
445         VipsImage* cached;
446         std::cout<<"OpParBase::build_many_internal(): adding tilecache for output image #"
447             <<i<<", padding="<<p<<std::endl;
448         if( !phf_tilecache(out, &cached,
449             "access", acc, "threaded", threaded,
450             "persistent", persistent, NULL) ) {
451           result_cached.push_back( cached );
452           PF_UNREF( out, "OpParBase::build_many_internal(): out unref" );
453           std::cout<<"OpParBase::build_many_internal(): added tilecache for output image #"
454               <<i<<", padding="<<p<<std::endl;
455         } else {
456           std::cout<<"OpParBase::build_many_internal(): phf_tilecache() failed."<<std::endl;
457           result_cached.push_back( out );
458         }
459       } else {
460         result_cached.push_back( out );
461       }
462     }
463     result = result_cached;
464   }
465 
466   if( true && convert_inputs_on_map_build() && is_map() && interpretation == VIPS_INTERPRETATION_B_W ) {
467     std::vector<VipsImage*>* in_ptr = in_temp.empty() ? &in : &in_temp;
468     for(unsigned int i = 0; i < in_ptr->size(); i++) {
469       VipsImage* img = (*in_ptr)[i];
470       PF_UNREF( img, "build_many_internal: img unref" );
471       std::cout<<"OpParBase::build_many_internal: op_type="<<get_type()<<"  "<<img<<" unref"<<std::endl;
472     }
473   }
474 
475   //for(unsigned int i = 0; i < outvec.size(); i++ ) {
476   //  PF_UNREF( outvec[i], "OpParBase::build_many_internal(): previous outputs unref" );
477   //}
478   //outvec = result;
479 
480   return result;
481 }
482 
483 
fill_image_hierarchy(std::vector<VipsImage * > & in,VipsImage * imap,VipsImage * omap,std::vector<VipsImage * > & out)484 void PF::OpParBase::fill_image_hierarchy(std::vector<VipsImage*>& in,
485     VipsImage* imap, VipsImage* omap, std::vector<VipsImage*>& out)
486 {
487   for( unsigned int i = 0; i < out.size(); i++ ) {
488     bool is_dup = false;
489     for( unsigned int j = 0; j < in.size(); j++ ) {
490       if( out[i] == in[j] ) {
491         is_dup = true;
492         break;
493       }
494     }
495     if( is_dup ) continue;
496 
497 #ifndef NDEBUG
498     if( get_padding() > 0 ) {
499       std::cout<<"OpParBase::fill_image_hierarchy(): filling hierarchy for image "<<i<<"("<<out[i]<<") with padding "<<get_padding()<<std::endl;
500     }
501 #endif
502     PF::image_hierarchy_fill( out[i], get_padding(), in );
503     std::vector<VipsImage*> maps;
504     if( imap ) maps.push_back(imap);
505     if( omap ) maps.push_back(omap);
506     if( !maps.empty() ) PF::image_hierarchy_fill( out[i], 0, maps );
507   }
508 }
509 
510 
save(std::ostream & ostr,int level)511 bool PF::OpParBase::save( std::ostream& ostr, int level )
512 {
513   for(int i = 0; i < level; i++) ostr<<"  ";
514   ostr<<"<operation type=\""<<get_type()<<"\">"<<std::endl;
515 
516   for( std::list<PropertyBase*>::iterator pi = properties.begin();
517       pi != properties.end(); pi++ ) {
518     if( (*pi)->is_persistent() == false ) continue;
519     std::string pvalue = (*pi)->get_str();
520     for(int i = 0; i < level+1; i++) ostr<<"  ";
521     ostr<<"<property name=\""<<(*pi)->get_name()<<"\" value=\"";
522     //(*pi)->to_stream( ostr );
523     ostr<<PF::pf_escape_xml(pvalue);
524     ostr<<"\">"<<std::endl;
525     for(int i = 0; i < level+1; i++) ostr<<"  ";
526     ostr<<"</property>"<<std::endl;
527   }
528 
529   for( std::list<PropertyBase*>::iterator pi = mapped_properties.begin();
530       pi != mapped_properties.end(); pi++ ) {
531     if( (*pi)->is_persistent() == false ) continue;
532     std::string pvalue = (*pi)->get_str();
533     for(int i = 0; i < level+1; i++) ostr<<"  ";
534     ostr<<"<property name=\""<<(*pi)->get_name()<<"\" value=\"";
535     //(*pi)->to_stream( ostr );
536     ostr<<PF::pf_escape_xml(pvalue);
537     ostr<<"\">"<<std::endl;
538     for(int i = 0; i < level+1; i++) ostr<<"  ";
539     ostr<<"</property>"<<std::endl;
540   }
541 
542   for(int i = 0; i < level; i++) ostr<<"  ";
543   ostr<<"</operation>"<<std::endl;
544 
545   return true;
546 }
547 
548 
vips_copy_metadata(VipsImage * in,VipsImage * out)549 int PF::vips_copy_metadata( VipsImage* in, VipsImage* out )
550 {
551   if( !out ) return 0;
552   int Xsize = out->Xsize;
553   int Ysize = out->Ysize;
554   int bands = out->Bands;
555   VipsBandFormat fmt = out->BandFmt;
556   VipsCoding coding = out->Coding;
557   VipsInterpretation type = out->Type;
558   gdouble xres = out->Xres;
559   gdouble yres = out->Yres;
560   VipsImage* invec[2] = {in, NULL};
561   vips__image_copy_fields_array( out, invec );
562   vips_image_init_fields( out,
563       Xsize, Ysize, bands, fmt,
564       coding, type, xres, yres
565   );
566   return 0;
567 }
568 
569 
570 
print_embedded_profile(VipsImage * image)571 void PF::print_embedded_profile( VipsImage* image )
572 {
573   PF::ICCProfile* icc_data = PF::get_icc_profile( image );
574   if(icc_data) {
575     cmsHPROFILE in_profile = icc_data->get_profile();
576     if( in_profile ) {
577       char tstr[1024];
578       cmsGetProfileInfoASCII(in_profile, cmsInfoDescription, "en", "US", tstr, 1024);
579       std::cout<<"\""<<tstr<<"\", ";
580       //cmsCloseProfile( in_profile );
581       //return;
582     //} else {
583     //  std::cout<<"Cannot open profile from memory."<<std::endl;
584     }
585   }
586 
587   void *data;
588   size_t data_length;
589   if( !PF_VIPS_IMAGE_GET_BLOB( image, VIPS_META_ICC_NAME, &data, &data_length ) ) {
590     cmsHPROFILE in_profile = cmsOpenProfileFromMem( data, data_length );
591     if( in_profile ) {
592       char tstr[1024];
593       cmsGetProfileInfoASCII(in_profile, cmsInfoDescription, "en", "US", tstr, 1024);
594       std::cout<<"\""<<tstr<<"\""<<std::endl;
595       cmsCloseProfile( in_profile );
596       return;
597     }
598     std::cout<<"Cannot open profile from memory."<<std::endl;
599   }
600   std::cout<<"Embedded ICC profile not found."<<std::endl;
601 }
602 
603 
604 
605 
vivid_light_f(float nbottom,float ntop)606 float PF::vivid_light_f(float nbottom, float ntop)
607 {
608   //nbottom = 50.0f/255.0f;
609   //ntop = 200.0f/255.0f;
610   float nvivid;
611   if( ntop <= 0.5 )
612     nvivid = PF::color_burn( nbottom, ntop*2.0f );
613   else
614     nvivid = PF::color_dodge( nbottom, ntop*2.0f-1.0f );
615 
616   //std::cout<<"vivid_light=("<<nbottom*255<<","<<ntop*255<<")="<<nvivid*255.0f<<std::endl;
617   return nvivid;
618 }
619