1 /*
2 
3  */
4 
5 /*
6 
7     Copyright (C) 2014 Ferrero Andrea
8 
9     This program is free software: you can redistribute it and/or modify
10     it under the terms of the GNU General Public License as published by
11     the Free Software Foundation, either version 3 of the License, or
12     (at your option) any later version.
13 
14     This program is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17     GNU General Public License for more details.
18 
19     You should have received a copy of the GNU General Public License
20     along with this program. If not, see <http://www.gnu.org/licenses/>.
21 
22 
23  */
24 
25 /*
26 
27     These files are distributed with PhotoFlow - http://aferrero2707.github.io/PhotoFlow/
28 
29  */
30 
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #include <fstream>
34 
35 #include "fileutils.hh"
36 //#include "pf_mkstemp.hh"
37 #include "exif_data.hh"
38 #include "image.hh"
39 #include "imageprocessor.hh"
40 #include "pf_file_loader.hh"
41 #include "../operations/operations.hh"
42 //#include "../operations/convert2srgb.hh"
43 //#include "../operations/convertformat.hh"
44 #include "../operations/icc_transform.hh"
45 #include "../operations/scale.hh"
46 #include "../operations/sharpen.hh"
47 #include "../operations/trcconv.hh"
48 //#include "../operations/gmic/gmic_untiled_op.hh"
49 
50 #define BENCHMARK
51 #include "../rt/rtengine/StopWatch.h"
52 
53 extern int vips_exrsave(VipsImage *in, const char *filename, int halfFloat, void *exif, int exif_len);
54 
55 
56 
image_rebuild_callback(gpointer data)57 gint PF::image_rebuild_callback( gpointer data )
58 {
59   PF::Image* image = (PF::Image*)data;
60 #ifndef NDEBUG
61   //std::cout<<"PF::image_rebuild_callback(): called, image->is_modified()="<<image->is_modified()<<std::endl;
62 #endif
63   if( image->is_modified() ) {
64 
65     bool result = image->get_layer_manager().rebuild_prepare();
66     /*
67 #ifndef NDEBUG
68     std::cout<<"PF::image_rebuild_callback(): rebuild prepare "<<(result?"OK":"failed")<<std::endl;
69 #endif
70     if( !result ) {
71       // something wrong here, we release all the locks and stop the update
72       // in fact, this should never happen...
73       for( unsigned int i = 0; i < image->get_npipelines(); i++ ) {
74 	PF::Pipeline* pipeline = image->get_pipeline( i );
75 	if( !pipeline ) continue;
76 	pipeline->unlock_processing();
77       }
78       return false;
79     }
80      */
81 
82     image->clear_modified();
83 
84     // Loop on pipelines, re-build and update
85     for( unsigned int i = 0; i < image->get_npipelines(); i++ ) {
86       PF::Pipeline* pipeline = image->get_pipeline( i );
87       if( !pipeline ) continue;
88 
89 #ifndef NDEBUG
90       std::cout<<"PF::image_rebuild_callback(): updating pipeline #"<<i<<std::endl;
91 #endif
92       //vips_cache_drop_all();
93       image->get_layer_manager().rebuild( pipeline, PF::PF_COLORSPACE_RGB, 100, 100, NULL );
94       //pipeline->update();
95     }
96 
97     image->get_layer_manager().rebuild_finalize();
98   }
99   return false;
100 }
101 
102 
Image()103 PF::Image::Image():
104           layer_manager( this ),
105           async( false ),
106           modified_flag( false ),
107           rebuilding( false ),
108           loaded( false ),
109           disable_update( false ),
110           force_synced_update( false )
111 {
112   image_mutex = vips_g_mutex_new();
113 
114   //rebuild_mutex = vips_g_mutex_new();
115   //g_mutex_lock( rebuild_mutex );
116   //rebuild_done = vips_g_cond_new();
117 
118   //export_mutex = vips_g_mutex_new();
119   //g_mutex_lock( export_mutex );
120   //export_done = vips_g_cond_new();
121 
122   //sample_mutex = vips_g_mutex_new();
123   //g_mutex_lock( sample_mutex );
124   //sample_done = vips_g_cond_new();
125 
126   //remove_layer_mutex = vips_g_mutex_new();
127   //g_mutex_lock( remove_layer_mutex );
128   //remove_layer_done = vips_g_cond_new();
129 
130   layer_manager.signal_modified.connect(sigc::mem_fun(this, &Image::update_all) );
131   layer_manager.signal_modified.connect(sigc::mem_fun(this, &Image::modified) );
132   //convert2srgb = new PF::Processor<PF::Convert2sRGBPar,PF::Convert2sRGBProc>();
133   convert_format = new_convert_format();
134   convert2outprof = new_icc_transform();
135   resize = new_scale();
136   sharpen = new_sharpen();
137   convert2linear = new_trcconv();
138 
139   //add_pipeline( VIPS_FORMAT_UCHAR, 0 );
140   //add_pipeline( VIPS_FORMAT_UCHAR, 0 );
141   //add_pipeline( VIPS_FORMAT_USHORT, 0 );
142   //add_pipeline( VIPS_FORMAT_USHORT, 0 );
143 
144   //pipelines[0]->set_render_mode( PF_RENDER_PREVIEW );
145   //pipelines[1]->set_render_mode( PF_RENDER_PREVIEW );
146 }
147 
~Image()148 PF::Image::~Image()
149 {
150   /*
151   for( unsigned int vi = 0; vi < pipelines.size(); vi++ ) {
152     if( pipelines[vi] != NULL )
153       delete pipelines[vi];
154   }
155    */
156   std::cout<<"[Image::~Image()] freeing image_mutex"<<std::endl;
157   vips_g_mutex_free(image_mutex);
158   //std::cout<<"[Image::~Image()] freeing rebuild_mutex"<<std::endl;
159   //vips_g_mutex_free(rebuild_mutex);
160   //vips_g_cond_free(rebuild_done);
161   //std::cout<<"[Image::~Image()] freeing export_mutex"<<std::endl;
162   //vips_g_mutex_free(export_mutex);
163   //vips_g_cond_free(export_done);
164   //std::cout<<"[Image::~Image()] freeing sample_mutex"<<std::endl;
165   //vips_g_mutex_free(sample_mutex);
166   //vips_g_cond_free(sample_done);
167   //std::cout<<"[Image::~Image()] freeing remove_layer_mutex"<<std::endl;
168   //vips_g_mutex_free(remove_layer_mutex);
169   //vips_g_cond_free(remove_layer_done);
170 }
171 
172 
lock()173 void PF::Image::lock()
174 {
175   //std::cout<<"+++++++++++++++++++++"<<std::endl;
176   //std::cout<<"  LOCKING REBUILD MUTEX"<<std::endl;
177   //std::cout<<"+++++++++++++++++++++"<<std::endl;
178   g_mutex_lock( image_mutex);
179 }
180 
unlock()181 void PF::Image::unlock()
182 {
183   //std::cout<<"---------------------"<<std::endl;
184   //std::cout<<"  UNLOCKING REBUILD MUTEX"<<std::endl;
185   //std::cout<<"---------------------"<<std::endl;
186   g_mutex_unlock( image_mutex);
187   //std::cout<<"---------------------"<<std::endl;
188   //std::cout<<"  REBUILD MUTEX UNLOCKED"<<std::endl;
189   //std::cout<<"---------------------"<<std::endl;
190 }
191 
192 
193 /*void PF::Image::export_lock()
194 {
195   //std::cout<<"+++++++++++++++++++++"<<std::endl;
196   //std::cout<<"  LOCKING REBUILD MUTEX"<<std::endl;
197   //std::cout<<"+++++++++++++++++++++"<<std::endl;
198   //g_mutex_lock( export_mutex);
199   export_done.lock();
200 }
201 
202 void PF::Image::export_unlock()
203 {
204   //std::cout<<"---------------------"<<std::endl;
205   //std::cout<<"  UNLOCKING REBUILD MUTEX"<<std::endl;
206   //std::cout<<"---------------------"<<std::endl;
207   //g_mutex_unlock( export_mutex);
208   export_done.unlock();
209   //std::cout<<"---------------------"<<std::endl;
210   //std::cout<<"  REBUILD MUTEX UNLOCKED"<<std::endl;
211   //std::cout<<"---------------------"<<std::endl;
212 }
213 
214 void PF::Image::sample_lock()
215 {
216   //std::cout<<"+++++++++++++++++++++"<<std::endl;
217   //std::cout<<"  LOCKING SAMPLE MUTEX"<<std::endl;
218   //std::cout<<"+++++++++++++++++++++"<<std::endl;
219   //g_mutex_lock( sample_mutex);
220   sample_cond.lock();
221 }
222 
223 void PF::Image::sample_unlock()
224 {
225   //std::cout<<"---------------------"<<std::endl;
226   //std::cout<<"  UNLOCKING SAMPLE MUTEX"<<std::endl;
227   //std::cout<<"---------------------"<<std::endl;
228   //g_mutex_unlock( sample_mutex);
229   sample_cond.unlock();
230   //std::cout<<"---------------------"<<std::endl;
231   //std::cout<<"  SAMPLE MUTEX UNLOCKED"<<std::endl;
232   //std::cout<<"---------------------"<<std::endl;
233 }
234 
235 
236 void PF::Image::destroy_lock()
237 {
238   //std::cout<<"+++++++++++++++++++++"<<std::endl;
239   //std::cout<<"  LOCKING SAMPLE MUTEX"<<std::endl;
240   //std::cout<<"+++++++++++++++++++++"<<std::endl;
241   //g_mutex_lock( sample_mutex);
242   destroy_cond.lock();
243 }
244 
245 void PF::Image::destroy_unlock()
246 {
247   //std::cout<<"---------------------"<<std::endl;
248   //std::cout<<"  UNLOCKING SAMPLE MUTEX"<<std::endl;
249   //std::cout<<"---------------------"<<std::endl;
250   //g_mutex_unlock( sample_mutex);
251   destroy_cond.unlock();
252   //std::cout<<"---------------------"<<std::endl;
253   //std::cout<<"  SAMPLE MUTEX UNLOCKED"<<std::endl;
254   //std::cout<<"---------------------"<<std::endl;
255 }*/
256 
257 
258 
259 
260 // The area parameter represents the region of the image that was actually
261 // modified and that needs to be re-computed. This allows certain sinks
262 // to reduce the amount of computations in case only part of the image
263 // needs to be updated. If area is NULL, it means that the whole image
264 // was changed.
set_pipeline_level(PF::Pipeline * target_pipeline,int level)265 void PF::Image::set_pipeline_level( PF::Pipeline* target_pipeline, int level )
266 {
267 #ifndef NDEBUG
268   std::cout<<"Image::set_pipeline_level( "<<target_pipeline<<", "<<level<<" ) called."<<std::endl;
269 #endif
270 
271   if( !target_pipeline ) return;
272 
273   if( PF::PhotoFlow::Instance().is_batch() ) {
274     target_pipeline->set_level( level );
275   } else {
276     ProcessRequestInfo request;
277     request.image = this;
278     request.pipeline = target_pipeline;
279     request.level = level;
280     request.request = PF::IMAGE_PIPELINE_SET_LEVEL;
281     PF::ImageProcessor::Instance().submit_request( request );
282   }
283 }
284 
285 
get_compatible_node(PF::Layer * layer,PF::Pipeline * pipeline,unsigned int level)286 PF::PipelineNode* PF::Image::get_compatible_node(PF::Layer* layer, PF::Pipeline* pipeline, unsigned int level)
287 {
288   PF::PipelineNode* node = NULL;
289   return node;
290   for(unsigned int pi = 0; pi < pipelines.size(); pi++) {
291     PF::Pipeline* p = pipelines[pi];
292     if( p == pipeline ) break;
293 
294     PF::PipelineNode* n = p->get_node( layer->get_id() );
295     if( n->level_real == level ) {
296       node = n;
297       break;
298     }
299   }
300 
301   return node;
302 }
303 
304 
update(PF::Pipeline * target_pipeline,bool sync)305 void PF::Image::update( PF::Pipeline* target_pipeline, bool sync )
306 {
307 #ifndef NDEBUG
308   std::cout<<"Image::update( "<<target_pipeline<<", "<<sync<<" ) called."<<std::endl;
309 #endif
310   if( disable_update ) return;
311 
312   if( force_synced_update )
313     sync = true;
314 
315   if( PF::PhotoFlow::Instance().is_batch() ) {
316     do_update( target_pipeline );
317   } else {
318     ProcessRequestInfo request;
319     request.image = this;
320     request.pipeline = target_pipeline;
321     request.request = PF::IMAGE_REBUILD;
322     /*
323 		if(area) {
324 			request.area.left = area->left;
325 			request.area.top = area->top;
326 			request.area.width = area->width;
327 			request.area.height = area->height;
328 		} else {
329      */
330     request.area.width = request.area.height = 0;
331     //}
332 
333     if( sync ) rebuild_done_reset(); //rebuild_cond.lock(); //g_mutex_lock( rebuild_mutex );
334 #ifndef NDEBUG
335     std::cout<<"PF::Image::update(): submitting rebuild request..."<<std::endl;
336 #endif
337     PF::ImageProcessor::Instance().submit_request( request );
338 #ifndef NDEBUG
339     std::cout<<"PF::Image::update(): request submitted."<<std::endl;
340 #endif
341 
342     if( sync ) {
343 #ifndef NDEBUG
344       std::cout<<"PF::Image::update(): waiting for rebuild_done...."<<std::endl;
345 #endif
346       //unlock(); //g_mutex_unlock( rebuild_mutex );
347       //g_cond_wait( rebuild_done, rebuild_mutex );
348       //rebuild_cond.wait();
349       //rebuild_cond.unlock();
350       rebuild_done_wait( true );
351 #ifndef NDEBUG
352       std::cout<<"PF::Image::update(): ... rebuild_done received."<<std::endl;
353 #endif
354     }
355 
356     // In sync mode, the image is left in a locked state to allow further
357     // actions to be taken before any subsequent rebuild and reprocessing
358     // takes place
359     //if( sync && target_pipeline ) {
360     //  std::cout<<"PF::Image::update(): unlocking rebuild mutex after condition...."<<std::endl;
361     //}
362   }
363 
364   /*
365   if( is_async() )
366     update_async();
367   else
368     update_sync();
369    */
370 }
371 
372 
do_update(PF::Pipeline * target_pipeline,bool update_gui)373 void PF::Image::do_update( PF::Pipeline* target_pipeline, bool update_gui )
374 {
375   //std::cout<<"PF::Image::do_update(): is_modified()="<<is_modified()<<std::endl;
376   //if( !is_modified() ) return;
377 
378   // Set the rebuild condition to FALSE
379   //rebuild_done_reset();
380 
381 #ifndef NDEBUG
382   std::cout<<std::endl<<"============================================"<<std::endl;
383 #endif
384   //std::cout<<"PF::Image::do_update(): is_modified()="<<is_modified()<<std::endl;
385   bool result = get_layer_manager().rebuild_prepare();
386   /*
387     #ifndef NDEBUG
388     std::cout<<"PF::image_rebuild_callback(): rebuild prepare "<<(result?"OK":"failed")<<std::endl;
389     #endif
390     if( !result ) {
391     // something wrong here, we stop the update
392     // in fact, this should never happen...
393     return false;
394     }
395    */
396 
397   //clear_modified();
398 
399   //vips_cache_drop_all();
400 
401   // Loop on pipelines, re-build and update
402   for( unsigned int i = 0; i < get_npipelines(); i++ ) {
403     //if( i > 1 ) break;
404     PF::Pipeline* pipeline = get_pipeline( i );
405     if( !pipeline ) continue;
406 //#ifndef NDEBUG
407     std::cout<<"PF::Image::do_update(): preparing pipeline #"<<i<<std::endl;
408 //#endif
409 
410     if( !target_pipeline) {
411       // We do not target a specific pipeline
412       // For the sake of performance, we only rebuild pipelines that have
413       // sinks attached to them, as otherwise the pipeline can be considered inactive
414       //if( !(pipeline->has_sinks()) ) continue;
415     } else {
416       // We only rebuild the target pipeline
417       if( pipeline != target_pipeline ) continue;
418     }
419 
420     if( pipeline->get_auto_zoom() ) {
421       // This pipeline requires to automatically set the zoom level so that
422       // the width and height do not exceed a given size, so we have to
423       // look into the previously processed pipelines to get the most
424       // accurate estimate of the full-res image
425       unsigned int level_min = 1000;
426       PF::Pipeline* hires_pipeline = NULL;
427       for( unsigned int j = 0; j < i; j++ ) {
428         PF::Pipeline* pipeline2 = get_pipeline( j );
429         if( !pipeline2 ) continue;
430         //std::cout<<"pipeline("<<j<<")->get_level()="<<pipeline2->get_level()<<std::endl;
431         if( pipeline2->get_level() < level_min ) {
432           hires_pipeline = pipeline2;
433           level_min = pipeline2->get_level();
434         }
435       }
436       int level = -1;
437       if( hires_pipeline ) {
438         level = (int)hires_pipeline->get_level();
439         //std::cout<<"hires_pipeline->get_level()="<<level<<std::endl;
440         VipsImage* hires_image = NULL;
441         if( pipeline->get_output_layer_id() >= 0 ) {
442           PF::PipelineNode* node = hires_pipeline->get_node( pipeline->get_output_layer_id() );
443           if( node ) hires_image = node->blended;
444         } else {
445           hires_image = hires_pipeline->get_output();
446         }
447         if( hires_image ) {
448           int w = hires_image->Xsize;
449           int h = hires_image->Ysize;
450           //std::cout<<"hires_image dimensions: "<<w<<","<<h<<std::endl;
451           while( (w > pipeline->get_auto_zoom_width()) ||
452               (h > pipeline->get_auto_zoom_height()) ) {
453             w /= 2; h /= 2; level += 1;
454           }
455           //std::cout<<"auto_zoom dimensions: "<<w<<","<<h<<"  level: "<<level<<std::endl;
456         }
457       }
458       if( level >= 0 ) {
459         pipeline->set_level( level );
460       }
461     }
462 
463 //#ifndef NDEBUG
464     std::cout<<"PF::Image::do_update(): updating pipeline #"<<i<<std::endl;
465 //#endif
466     //get_layer_manager().rebuild( pipeline, PF::PF_COLORSPACE_RGB, 100, 100, area );
467     get_layer_manager().rebuild( pipeline, PF::PF_COLORSPACE_RGB, 100, 100, NULL );
468 //#ifndef NDEBUG
469     std::cout<<"PF::Image::do_update(): pipeline #"<<i<<" updated."<<std::endl;
470 //#endif
471     //pipeline->update();
472   }
473 
474   //std::cout<<"PF::Image::update(): waiting for rebuild_done...."<<std::endl;
475   // Set the rebuild condition to TRUE and emit the signal
476   rebuild_done_signal();
477   //rebuild_cond.unlock();
478   //std::cout<<"PF::Image::do_update(): signaling done condition."<<std::endl;
479   signal_updated.emit();
480 
481 #ifndef NDEBUG
482   std::cout<<"PF::Image::do_update(): finalizing..."<<std::endl;
483 #endif
484   bool _update_gui;
485   if( target_pipeline ) _update_gui = false;
486   else _update_gui = update_gui;
487   get_layer_manager().rebuild_finalize( _update_gui );
488 #ifndef NDEBUG
489   std::cout<<"PF::Image::do_update(): finalizing done."<<std::endl;
490 #endif
491 
492 #ifndef NDEBUG
493   for( unsigned int i = 0; i < get_npipelines(); i++ ) {
494     PF::Pipeline* pipeline = get_pipeline( i );
495     if( !pipeline ) continue;
496     std::cout<<"PF::Image::do_update(): ref_counts of pipeline #"<<i<<std::endl;
497     for(int ni = 0; ni < pipeline->get_nodes().size(); ni++ ) {
498       PF::PipelineNode* node = pipeline->get_nodes()[ni];
499       if( !node ) continue;
500       if( !(node->image) ) {
501         std::cout<<"  node #"<<ni<<" ("<<(void*)node->image<<")"<<std::endl;
502         continue;
503       }
504       std::cout<<"  node #"<<ni<<" ("<<(void*)node->image<<") = "<<G_OBJECT(node->image)->ref_count<<std::endl;
505     }
506   }
507   std::cout<<std::endl<<"============================================"<<std::endl<<std::endl<<std::endl;
508 #endif
509 
510   save_backup();
511 }
512 
513 
514 
sample(int layer_id,int x,int y,int size,VipsImage ** image,std::vector<float> & values)515 void PF::Image::sample( int layer_id, int x, int y, int size,
516     VipsImage** image, std::vector<float>& values ) {
517   int left = (int)x-size/2;
518   int top = (int)y-size/2;
519   int width = size;
520   int height = size;
521   VipsRect area = {left, top, width, height};
522   std::vector<VipsRect> areas; areas.push_back(area);
523   sample(layer_id, areas, false, image, values);
524 }
525 
526 
527 
sample(int layer_id,std::vector<VipsRect> & areas,bool weighted,VipsImage ** image,std::vector<float> & values)528 void PF::Image::sample( int layer_id, std::vector<VipsRect>& areas, bool weighted,
529     VipsImage** image, std::vector<float>& values )
530 {
531 
532   if( true || PF::PhotoFlow::Instance().is_batch() ) {
533     do_sample( layer_id, areas, weighted );
534   } else {
535     ProcessRequestInfo request;
536     request.image = this;
537     request.layer_id = layer_id;
538     request.request = PF::IMAGE_SAMPLE;
539     request.areas = areas;
540     request.weighted_average = weighted;
541 
542     //sample_lock(); //g_mutex_lock( sample_mutex );
543 #ifndef NDEBUG
544     std::cout<<"PF::Image::sample(): submitting sample request..."<<std::endl;
545 #endif
546     PF::ImageProcessor::Instance().submit_request( request );
547 #ifndef NDEBUG
548     std::cout<<"PF::Image::sample(): request submitted."<<std::endl;
549 #endif
550 
551     //g_cond_wait( sample_done, sample_mutex );
552     //std::cout<<"Image::sample(): unlocking mutex."<<std::endl;
553     //sample_unlock(); //g_mutex_unlock( sample_mutex );
554 #ifndef NDEBUG
555     std::cout<<"Image::sample(): waiting for done."<<std::endl;
556 #endif
557     sample_cond.wait();
558     //sample_unlock();
559 #ifndef NDEBUG
560     std::cout<<"Image::sample(): done received."<<std::endl;
561 #endif
562   }
563 
564   if(image) *image = sampler_image;
565   values.clear();
566   values = sampler_values;
567 
568   /*
569   if( is_async() )
570     update_async();
571   else
572     update_sync();
573    */
574 }
575 
576 
do_sample(int layer_id,std::vector<VipsRect> & areas,bool weighted)577 void PF::Image::do_sample( int layer_id, std::vector<VipsRect>& areas, bool weighted )
578 {
579   //std::cout<<"Image::do_sample(): waiting for rebuild_done..."<<std::endl;
580   //rebuild_lock();
581   //rebuild_done_wait( true );
582   //std::cout<<"Image::do_sample(): rebuild_done received"<<std::endl;
583 
584 #ifndef NDEBUG
585   std::cout<<"Image::do_sample(): locking image..."<<std::endl;
586 #endif
587   lock();
588 #ifndef NDEBUG
589   std::cout<<"Image::do_sample(): image locked"<<std::endl;
590 #endif
591 
592   // Get the default pipeline of the image
593   // (it is supposed to be at 1:1 zoom level
594   // and floating point accuracy)
595   PF::Pipeline* pipeline = get_pipeline( 0 );
596   if( !pipeline ) {
597     std::cout<<"Image::do_sample(): NULL pipeline"<<std::endl;
598     std::cout<<"Image::do_sample(): unlocking image."<<std::endl;
599     unlock();
600     return;
601   }
602 
603   // Get the node associated to the layer
604   PF::PipelineNode* node = pipeline->get_node( layer_id );
605   if( !node ) {
606     std::cout<<"Image::do_sample(): NULL pipeline node"<<std::endl;
607     std::cout<<"Image::do_sample(): unlocking image."<<std::endl;
608     unlock();
609     return;
610   }
611 
612   // Finally, get the underlying VIPS image associated to the layer
613   VipsImage* image = node->image;
614   if( !image ) {
615     std::cout<<"Image::do_sample(): NULL image"<<std::endl;
616     std::cout<<"Image::do_sample(): unlocking image."<<std::endl;
617     unlock();
618     return;
619   }
620 
621   // Now we have to process a small portion of the image
622   // to get the corresponding Lab values
623   VipsImage* spot = image;
624   VipsRect all = {0 ,0, image->Xsize, image->Ysize};
625 
626   /*
627 	if( vips_crop( image, &spot,
628 								 clipped.left, clipped.top,
629 								 clipped.width+1, clipped.height+1,
630 								 NULL ) ) {
631     std::cout<<"Image::do_sample(): vips_crop() failed"<<std::endl;
632 		return;
633   }
634 	VipsRect rspot = {0 ,0, spot->Xsize, spot->Ysize};
635    */
636 
637   //VipsImage* outimg = im_open( "spot_wb_img", "p" );
638   //if (vips_sink_screen (spot, outimg, NULL,
639   //											64, 64, 1,
640   //												0, NULL, this))
641   //	return;
642 
643   PF::ProcessorBase* convert_format = new_convert_format();
644   std::vector<VipsImage*> in;
645   in.push_back( spot );
646   convert_format->get_par()->set_image_hints( spot );
647   convert_format->get_par()->set_format( VIPS_FORMAT_FLOAT );
648   unsigned int level = 0;
649   VipsImage* outimg = convert_format->get_par()->build( in, 0, NULL, NULL, level );
650   if( outimg == NULL ) {
651     std::cout<<"Image::do_sample(): NULL image after convert_format"<<std::endl;
652     std::cout<<"Image::do_sample(): unlocking image."<<std::endl;
653     unlock();
654     return;
655   }
656   //PF_UNREF( spot, "Image::do_sample() spot unref" )
657   //if( vips_sink_memory( spot ) )
658   //  return;
659 
660   float wtot = 0;
661   std::vector< std::vector<float> > sumv;
662   std::vector<float> weights;
663   for(unsigned int ai = 0; ai < areas.size(); ai++) {
664     VipsRect& area = areas[ai];
665     VipsRect clipped;
666     vips_rect_intersectrect( &area, &all, &clipped );
667     VipsRect rspot = {clipped.left, clipped.top, clipped.width+1, clipped.height+1};
668     //std::cout<<"Image::do_sample(): rspot="<<rspot.width<<","<<rspot.height<<"+"<<rspot.left<<"+"<<rspot.top<<std::endl;
669 
670     //PF_PRINT_REF( outimg, "Image::do_sample(): outimg refcount before vips_region_new()" )
671     VipsRegion* region = vips_region_new( outimg );
672     if (vips_region_prepare (region, &rspot)) {
673       std::cout<<"Image::do_sample(): vips_region_prepare() failed"<<std::endl;
674       std::cout<<"Image::do_sample(): unlocking image."<<std::endl;
675       unlock();
676       return;
677     }
678     //PF_PRINT_REF( outimg, "Image::do_sample(): outimg refcount after vips_region_new()" )
679 
680     int row, col, b;
681     int line_size = clipped.width*image->Bands;
682     float* p;
683     std::vector<float> sum;
684     for( int i = 0; i < 16; i++ ) sum.push_back(0);
685     for( row = 0; row < clipped.height; row++ ) {
686       p = (float*)VIPS_REGION_ADDR( region, rspot.left, rspot.top+row );
687       //std::cout<<"do_sample(): rspot.left="<<rspot.left<<"  rspot.top+row="<<rspot.top+row<<std::endl;
688       for( col = 0; col < line_size; col += image->Bands ) {
689         for( b = 0; b < image->Bands; b++ ) {
690           sum[b] += p[col+b];
691           //std::cout<<"do_sample(): p["<<row<<"]["<<col+b<<"]="<<p[col+b]<<std::endl;
692         }
693       }
694     }
695     sumv.push_back(sum);
696     weights.push_back(clipped.width*clipped.height);
697     wtot += clipped.width*clipped.height;
698 
699     PF_UNREF( region, "Image::do_sample(): region unref" );
700   }
701 
702   sampler_values.clear();
703   if( weighted ) {
704     for( int b = 0; b < image->Bands; b++ ) {
705       float bsum = 0;
706       for(int ai = 0; ai < sumv.size(); ai++) {
707         bsum += sumv[ai][b];
708       }
709 #ifndef NDEBUG
710       std::cout<<"sampler_values.push_back("<<sum/(sumv.size())<<")"<<std::endl;
711 #endif
712       sampler_values.push_back( bsum/wtot );
713     }
714     sampler_image = image;
715   } else {
716     for( int b = 0; b < image->Bands; b++ ) {
717       float bsum = 0;
718       for(int ai = 0; ai < sumv.size(); ai++) {
719         bsum += sumv[ai][b] / weights[ai];
720       }
721 #ifndef NDEBUG
722       std::cout<<"sampler_values.push_back("<<sum/(sumv.size())<<")"<<std::endl;
723 #endif
724       sampler_values.push_back( bsum/(sumv.size()) );
725     }
726     sampler_image = image;
727   }
728 #ifndef NDEBUG
729   std::cout<<"Image::do_sample() finished."<<std::endl;
730 #endif
731 
732   //g_object_unref( spot );
733   //PF_PRINT_REF( outimg, "Image::do_sample(): outimg refcount before region unref" );
734   //PF_PRINT_REF( outimg, "Image::do_sample(): outimg refcount after region unref" );
735   PF_UNREF( outimg, "Image::do_sample(): outimg unref" );
736 
737 #ifndef NDEBUG
738   std::cout<<"Image::do_sample(): unlocking image."<<std::endl;
739 #endif
740   unlock();
741 }
742 
743 
destroy()744 void PF::Image::destroy()
745 {
746   if( true || PF::PhotoFlow::Instance().is_batch() ) {
747     PF::ImageProcessor::Instance().remove_image_from_queue(this);
748     do_destroy();
749   } else {
750     ProcessRequestInfo request;
751     request.image = this;
752     request.request = PF::IMAGE_DESTROY;
753 
754     // Set the rebuild condition to FALSE
755     rebuild_done_reset();
756     //destroy_lock(); //g_mutex_lock( sample_mutex );
757 //#ifndef NDEBUG
758     std::cout<<"PF::Image::destroy(): submitting destroy request..."<<std::endl;
759 //#endif
760     PF::ImageProcessor::Instance().submit_request( request );
761 //#ifndef NDEBUG
762     std::cout<<"PF::Image::destroy(): request submitted."<<std::endl;
763 //#endif
764 
765 //#ifndef NDEBUG
766     std::cout<<"Image::destroy(): waiting for done."<<std::endl;
767 //#endif
768     //destroy_cond.wait();
769     //destroy_unlock();
770     rebuild_done_wait( true );
771 //#ifndef NDEBUG
772     std::cout<<"Image::destroy(): done received."<<std::endl;
773 //#endif
774   }
775 }
776 
777 
do_destroy()778 void PF::Image::do_destroy()
779 {
780 //#ifndef NDEBUG
781   std::cout<<"Image::do_destroy() called."<<std::endl;
782 //#endif
783   // Set the rebuild condition to FALSE
784   //rebuild_done_reset();
785 
786   for( unsigned int vi = 0; vi < pipelines.size(); vi++ ) {
787     if( pipelines[vi] != NULL ) {
788 //#ifndef NDEBUG
789       std::cout<<"Image::do_destroy(): deleting pipeline #"<<vi<<std::endl;
790 //#endif
791       delete pipelines[vi];
792 //#ifndef NDEBUG
793       std::cout<<"Image::do_destroy(): pipeline #"<<vi<<" delete"<<std::endl;
794 //#endif
795     }
796   }
797 #ifndef NDEBUG
798   //std::cout<<"Image::do_destroy(): deleting convert2srgb"<<std::endl;
799 #endif
800   //delete convert2srgb;
801 #ifndef NDEBUG
802   std::cout<<"Image::do_destroy(): convert2srgb deleted"<<std::endl;
803   std::cout<<"Image::do_destroy(): deleting convert_format"<<std::endl;
804 #endif
805   delete convert_format;
806 #ifndef NDEBUG
807   std::cout<<"Image::do_destroy(): convert_format deleted"<<std::endl;
808   std::cout<<"Image::do_destroy(): deleting convert2outprof"<<std::endl;
809 #endif
810   delete convert2outprof;
811 #ifndef NDEBUG
812   std::cout<<"Image::do_destroy(): convert2outprof deleted"<<std::endl;
813 #endif
814   delete resize;
815   delete sharpen;
816 
817   // Set the rebuild condition to TRUE and emit the signal
818 //#ifndef NDEBUG
819   std::cout<<"Image::do_destroy() finished."<<std::endl;
820 //#endif
821   rebuild_done_signal();
822 }
823 
824 
remove_layer(PF::Layer * layer)825 void PF::Image::remove_layer( PF::Layer* layer )
826 {
827   bool sync = true;
828   if( force_synced_update )
829     sync = true;
830 
831   if( PF::PhotoFlow::Instance().is_batch() ) {
832     do_remove_layer( layer );
833   } else {
834     ProcessRequestInfo request;
835     request.image = this;
836     request.layer = layer;
837     request.request = PF::IMAGE_REMOVE_LAYER;
838 
839     if( sync ) remove_layer_reset(); //rebuild_cond.lock(); //g_mutex_lock( rebuild_mutex );
840 #ifndef NDEBUG
841     std::cout<<"PF::Image::remove_layer(): submitting rebuild request..."<<std::endl;
842 #endif
843     PF::ImageProcessor::Instance().submit_request( request );
844 #ifndef NDEBUG
845     std::cout<<"PF::Image::remove_layer(): request submitted."<<std::endl;
846 #endif
847 
848     if( sync ) {
849 #ifndef NDEBUG
850       std::cout<<"PF::Image::remove_layer(): waiting for rebuild_done...."<<std::endl;
851 #endif
852       remove_layer_wait( true );
853 #ifndef NDEBUG
854       std::cout<<"PF::Image::remove_layer(): ... rebuild_done received."<<std::endl;
855 #endif
856     }
857   }
858 }
859 
860 
do_remove_layer(PF::Layer * layer)861 void PF::Image::do_remove_layer( PF::Layer* layer )
862 {
863   // Set the rebuild condition to FALSE
864   //rebuild_done_reset();
865 
866   std::list<Layer*> children;
867   layer_manager.get_child_layers( layer, children );
868   for( std::list<Layer*>::iterator i = children.begin(); i != children.end(); i++ ) {
869     if( !(*i) ) continue;
870     (*i)->set_dirty( true );
871     PF::ProcessorBase* proc = (*i)->get_processor();
872     if(proc && proc->get_par()) proc->get_par()->set_modified();
873     proc = (*i)->get_blender();
874     if(proc && proc->get_par()) proc->get_par()->set_modified();
875   }
876 
877   remove_from_inputs( layer );
878   std::list<Layer*>* list = layer_manager.get_list( layer );
879   if( list )
880     remove_layer( layer, *list );
881 }
882 
883 
remove_from_inputs(PF::Layer * layer)884 void PF::Image::remove_from_inputs( PF::Layer* layer )
885 {
886   remove_from_inputs( layer, layer_manager.get_layers() );
887 }
888 
889 
remove_from_inputs(PF::Layer * layer,std::list<Layer * > & list)890 void PF::Image::remove_from_inputs( PF::Layer* layer, std::list<Layer*>& list )
891 {
892   std::list<PF::Layer*>::iterator li;
893   for(li = list.begin(); li != list.end(); ++li) {
894     PF::Layer* l = *li;
895     l->remove_input( layer->get_id() );
896     remove_from_inputs( layer, l->get_sublayers() );
897     remove_from_inputs( layer, l->get_imap_layers() );
898     remove_from_inputs( layer, l->get_omap_layers() );
899   }
900 }
901 
902 
remove_layer(PF::Layer * layer,std::list<Layer * > & list)903 void PF::Image::remove_layer( PF::Layer* layer, std::list<Layer*>& list )
904 {
905 #ifndef NDEBUG
906   if( layer ) std::cout<<"Image::remove_layer(\""<<layer->get_name()<<"\") called."<<std::endl;
907 #endif
908   std::vector<Pipeline*>::iterator vi;
909   for( vi = pipelines.begin(); vi != pipelines.end(); vi++ ) {
910     (*vi)->remove_node( layer->get_id() );
911   }
912 
913   std::list<Layer*>::iterator it;
914   std::list<Layer*> sublayers = layer->get_sublayers();
915   for( it = sublayers.begin(); it != sublayers.end(); it++ ) {
916     remove_layer( *it, layer->get_sublayers() );
917   }
918 
919   sublayers = layer->get_imap_layers();
920   for( it = sublayers.begin(); it != sublayers.end(); it++ ) {
921     remove_layer( *it, layer->get_imap_layers() );
922   }
923 
924   sublayers = layer->get_omap_layers();
925   for( it = sublayers.begin(); it != sublayers.end(); it++ ) {
926     remove_layer( *it, layer->get_omap_layers() );
927   }
928 
929   std::list<PF::Layer*>::iterator li;
930   for(li = list.begin(); li != list.end(); ++li) {
931     PF::Layer* l = *li;
932     if( l->get_id() == layer->get_id() ) {
933       list.erase( li );
934       break;
935     }
936   }
937 
938   layer_manager.delete_layer( layer );
939 }
940 
941 
open(std::string filename,std::string bckname)942 bool PF::Image::open( std::string filename, std::string bckname )
943 {
944   std::string ext;
945   if( !getFileExtensionLowcase( "/", filename, ext ) ) return false;
946   disable_update = true;
947 
948 #ifndef NDEBUG
949   std::cout<<"ext: "<<ext<<std::endl;
950 #endif
951 
952   if( !bckname.empty() ) {
953 
954     std::cout<<"Opening image backup "<<bckname<<std::endl;
955 
956     PF::load_pf_image( bckname, this );
957     file_name = filename;
958     backup_file_name = bckname;
959 
960   } else if( ext == "pfi" ) {
961 
962     std::cout<<"Opening PFI image "<<filename<<std::endl;
963 
964     loaded = false;
965     if( PF::load_pf_image( filename, this ) ) {
966       //PF::PhotoFlow::Instance().set_image( pf_image );
967       //layersWidget.set_image( pf_image );
968       //add_pipeline( VIPS_FORMAT_UCHAR, 0 );
969       file_name = filename;
970     } else {
971       return false;
972     }
973 
974   } else if( ext=="tiff" || ext=="tif" || ext=="jpg" || ext=="jpeg" || ext=="png" || ext=="exr" || ext=="fits" || ext=="fts" || ext=="fit" ) {
975 
976     //PF::PhotoFlow::Instance().set_image( pf_image );
977     //layersWidget.set_image( pf_image );
978 
979     std::cout<<"Opening raster image "<<filename<<std::endl;
980 
981     PF::Layer* limg = layer_manager.new_layer();
982     PF::ProcessorBase* proc = PF::PhotoFlow::Instance().new_operation( "imageread", limg );
983     if( proc->get_par() && proc->get_par()->get_property( "file_name" ) )
984       proc->get_par()->get_property( "file_name" )->set_str( filename );
985     limg->set_processor( proc );
986     limg->set_name( _("background") );
987     limg->set_sticky(true);
988     layer_manager.get_layers().push_back( limg );
989     file_name = filename;
990 
991     /*
992     PF::Processor<PF::ImageReaderPar,PF::ImageReader>* imgread =
993       new PF::Processor<PF::ImageReaderPar,PF::ImageReader>();
994     imgread->get_par()->set_file_name( filename );
995 
996     PF::Layer* limg = layer_manager.new_layer();
997     limg->set_processor( imgread );
998     limg->set_name( "background" );
999 
1000     PF::ImageReadConfigDialog* img_config =
1001       new PF::ImageReadConfigDialog( limg );
1002     imgread->get_par()->set_config_ui( img_config );
1003     //img_config->set_layer( limg );
1004     //img_config->set_image( pf_image );
1005 
1006     //layer_manager.get_layers().push_back( limg );
1007     layersWidget.add_layer( limg );
1008      */
1009   } else {
1010 
1011     std::cout<<"Opening RAW image "<<filename<<std::endl;
1012 
1013     PF::Layer* limg = layer_manager.new_layer();
1014     PF::ProcessorBase* proc = PF::PhotoFlow::Instance().new_operation( "raw_loader", limg );
1015     if( proc->get_par() && proc->get_par()->get_property( "file_name" ) )
1016       proc->get_par()->get_property( "file_name" )->set_str( filename );
1017     limg->set_processor( proc );
1018     limg->set_name( "RAW loader" );
1019     limg->set_sticky(true);
1020     layer_manager.get_layers().push_back( limg );
1021 
1022     if( !PF::PhotoFlow::Instance().is_batch() ) {
1023       Glib::ustring profile;
1024 #ifdef __WIN32__
1025       profile = PF::PhotoFlow::Instance().get_presets_dir() + "\\default.pfp";
1026 #else
1027       profile = PF::PhotoFlow::Instance().get_presets_dir() + "/default.pfp";
1028 #endif
1029       struct stat buffer;
1030       int stat_result = stat(profile.c_str(), &buffer);
1031       if( stat_result != 0 ) profile = "";
1032 
1033       if( !profile.empty() &&
1034           PF::PhotoFlow::Instance().get_options().get_apply_default_preset() ) {
1035         PF::insert_pf_preset( profile.c_str(), this, NULL, &(layer_manager.get_layers()), false );
1036       } else {
1037         PF::Layer* limg2 = layer_manager.new_layer();
1038         PF::ProcessorBase* proc2 = PF::PhotoFlow::Instance().new_operation( "raw_developer_v2", limg2 );
1039         limg2->set_processor( proc2 );
1040         limg2->set_name( "RAW developer" );
1041         limg2->set_sticky(true);
1042         layer_manager.get_layers().push_back( limg2 );
1043       }
1044     }
1045 
1046     file_name = filename;
1047 
1048     /*
1049     limg = layer_manager.new_layer();
1050     proc = PF::PhotoFlow::Instance().new_operation( "raw_developer", limg );
1051     limg->set_processor( proc );
1052     limg->set_name( "RAW developer" );
1053     layer_manager.get_layers().push_back( limg );
1054      */
1055 
1056     /*
1057     limg = layer_manager.new_layer();
1058     proc = PF::PhotoFlow::Instance().new_operation( "raw_output", limg );
1059     limg->set_processor( proc );
1060     limg->set_name( "RAW output" );
1061     layer_manager.get_layers().push_back( limg );
1062      */
1063   }
1064   disable_update = false;
1065 
1066   //imageArea.set_pipeline( pf_image->get_pipeline(0) );
1067   //pf_image->signal_modified.connect( sigc::mem_fun(&imageArea, &ImageArea::update_image) );
1068   //sleep(5);
1069   //update();
1070 
1071   return true;
1072 }
1073 
1074 
save(std::string filename,bool do_clear,bool update_filename)1075 bool PF::Image::save( std::string filename, bool do_clear, bool update_filename )
1076 {
1077   std::string ext;
1078   if( getFileExtension( "/", filename, ext ) &&
1079       ext == "pfi" ) {
1080 
1081     std::ofstream of;
1082     of.open( filename.c_str() );
1083     if( !of ) return false;
1084     of<<"<image version=\""<<PF_FILE_VERSION<<"\">"<<std::endl;
1085     layer_manager.save( of );
1086     of<<"</image>"<<std::endl;
1087     if(update_filename) file_name = filename;
1088     if(do_clear) clear_modified();
1089     return true;
1090   } else {
1091     return false;
1092   }
1093 }
1094 
1095 
1096 
save_backup()1097 bool PF::Image::save_backup()
1098 {
1099   if( backup_file_name.empty() ) return false;
1100 
1101   std::ofstream of;
1102   of.open( backup_file_name.c_str() );
1103   if( !of ) return false;
1104   of<<"<image version=\""<<PF_FILE_VERSION<<"\">"<<std::endl;
1105   layer_manager.save( of );
1106   of<<"</image>"<<std::endl;
1107   return true;
1108 }
1109 
1110 
1111 
export_merged(std::string filename,image_export_opt_t * export_opt)1112 bool PF::Image::export_merged( std::string filename, image_export_opt_t* export_opt )
1113 {
1114   export_ok = false;
1115   if( PF::PhotoFlow::Instance().is_batch() ) {
1116     do_export_merged( filename, export_opt );
1117   } else {
1118     ProcessRequestInfo request;
1119     request.image = this;
1120     request.request = PF::IMAGE_EXPORT;
1121     request.area.width = request.area.height = 0;
1122     request.filename = filename;
1123     request.data = NULL;
1124     if( export_opt ) {
1125       request.data = malloc(sizeof(image_export_opt_t));
1126       memcpy(request.data, export_opt, sizeof(image_export_opt_t));
1127     }
1128     std::cout<<"PF::Image::export_merged(): request.data="<<request.data<<std::endl;
1129 
1130 #ifndef NDEBUG
1131     std::cout<<"PF::Image::export_merged(): locking mutex..."<<std::endl;
1132 #endif
1133     //g_mutex_lock( export_mutex );
1134     //export_done.lock();
1135 #ifndef NDEBUG
1136     std::cout<<"PF::Image::export_merged(): submitting export request..."<<std::endl;
1137 #endif
1138     PF::ImageProcessor::Instance().submit_request( request );
1139 #ifndef NDEBUG
1140     std::cout<<"PF::Image::export_merged(): request submitted."<<std::endl;
1141 #endif
1142 
1143     std::cout<<"PF::Image::export_merged(): waiting for export_done...."<<std::endl;
1144     //g_cond_wait( export_done, export_mutex );
1145     export_cond.wait();
1146     std::cout<<"PF::Image::export_merged(): ... export_done received."<<std::endl;
1147 
1148     //g_mutex_unlock( export_mutex );
1149   }
1150   return export_ok;
1151 }
1152 
1153 
do_export_merged(std::string filename,image_export_opt_t * export_opt)1154 void PF::Image::do_export_merged( std::string filename, image_export_opt_t* export_opt )
1155 {
1156   std::string ext;
1157   if( getFileExtension( "/", filename, ext ) &&
1158       ext != "pfi" ) {
1159     std::cout<<"Saving image to file "<<filename<<"..."<<std::endl;
1160     //Glib::Threads::Mutex::Lock lock( rebuild_mutex );
1161     unsigned int level = 0;
1162     PF::Pipeline* pipeline = add_pipeline( VIPS_FORMAT_FLOAT, 0, PF_RENDER_NORMAL );
1163     if( pipeline ) pipeline->set_op_caching_enabled( true );
1164     //PF::Pipeline* pipeline = add_pipeline( VIPS_FORMAT_USHORT, 0, PF_RENDER_NORMAL );
1165     do_update();
1166     /*
1167     while( true ) {
1168       PF::CacheBuffer* buf = layer_manager.get_cache_buffer( PF_RENDER_NORMAL );
1169       //std::cout<<"ImageProcessor::run(): buf="<<buf<<std::endl;
1170       if( !buf ) break;
1171       buf->write();
1172 #warning "TODO: check if one can update only the export pipeline"
1173       do_update();
1174     }
1175      */
1176     std::cout<<"Image::do_export_merged(): ext="<<ext<<std::endl;
1177 
1178     std::string msg;
1179 
1180     VipsImage* image = pipeline->get_output();
1181     VipsImage* outimg = NULL;
1182 
1183     /**/
1184     int th = 128;
1185     int tw = 128; //image->Xsize;
1186     int nt = (image->Xsize/tw + 1);
1187     VipsAccess acc = VIPS_ACCESS_RANDOM;
1188     int threaded = 1, persistent = 1;
1189     VipsImage* cached;
1190     if( !phf_tilecache(image, &cached,
1191         "access", acc, "threaded", threaded, "persistent", persistent, NULL) ) {
1192       //PF_UNREF( image, "Image::do_export_merged(): image unref" );
1193       image = cached;
1194     } else {
1195       std::cout<<"Image::do_export_merged(): vips_tilecache() failed."<<std::endl;
1196       PF_REF( image, "Image::do_export_merged(): image ref" );
1197     }
1198     /**/
1199     //PF_REF( image, "Image::do_export_merged(): image ref" );
1200 
1201     bool saved = false;
1202     bool add_exif = false;
1203 
1204     std::vector<VipsImage*> in;
1205 
1206     if( export_opt ) std::cout<<"  output size: "<<export_opt->size<<std::endl;
1207     if( export_opt && export_opt->size != PF::SIZE_ORIGINAL ) {
1208       PF::ScalePar* op_par =
1209           dynamic_cast<PF::ScalePar*>( resize->get_par() );
1210       in.clear();
1211       in.push_back( image );
1212       op_par->set_image_hints( image );
1213       op_par->set_format( VIPS_FORMAT_FLOAT );
1214       op_par->set_scale_mode(PF::SCALE_MODE_FIT);
1215       op_par->set_scale_unit(PF::SCALE_UNIT_PX);
1216       op_par->set_scale_interp(export_opt->interpolator);
1217       int width = 0, height = 0;
1218       switch(export_opt->size) {
1219       case PF::SIZE_400_300: width=400; height=300; break;
1220       case PF::SIZE_800_600: width=800; height=600; break;
1221       case PF::SIZE_1280_720: width=1280; height=720; break;
1222       case PF::SIZE_1280_800: width=1280; height=800; break;
1223       case PF::SIZE_1280_1024: width=1280; height=1024; break;
1224       case PF::SIZE_1440_900: width=1440; height=900; break;
1225       case PF::SIZE_1600_1200: width=1600; height=1200; break;
1226       case PF::SIZE_1920_1080: width=1920; height=1080; break;
1227       case PF::SIZE_1920_1200: width=1920; height=1200; break;
1228       case PF::SIZE_2048_1400: width=2048; height=1400; break;
1229       case PF::SIZE_2048_2048: width=2048; height=2048; break;
1230       case PF::SIZE_2K: width=2048; height=1080; break;
1231       case PF::SIZE_4K: width=3840; height=2160; break;
1232       case PF::SIZE_5K: width=5120; height=2880; break;
1233       case PF::SIZE_8K: width=7680; height=4320; break;
1234       case PF::SIZE_A4_300DPI: width=3508; height=2480; break;
1235       case PF::SIZE_A4P_300DPI: width=2480; height=3508; break;
1236       case PF::SIZE_CUSTOM: width=export_opt->width; height=export_opt->height; break;
1237       default: break;
1238       }
1239       std::cout<<"  width="<<width<<"  height="<<height<<std::endl;
1240       if( width>0 || height>0 ) {
1241         op_par->set_scale_width_pixels(width);
1242         op_par->set_scale_height_pixels(height);
1243         VipsImage* out = op_par->build( in, 0, NULL, NULL, level );
1244         PF_UNREF( image, "Image::do_export_merged(): image unref" );
1245         image = out;
1246       }
1247     }
1248 
1249     if( export_opt && export_opt->sharpen_enabled ) {
1250       VipsImage* base = image;
1251       PF::SharpenPar* op_par =
1252           dynamic_cast<PF::SharpenPar*>( sharpen->get_par() );
1253       in.clear();
1254       in.push_back( image );
1255       op_par->set_image_hints( image );
1256       op_par->set_format( VIPS_FORMAT_FLOAT );
1257       op_par->set_usm_radius(export_opt->sharpen_radius);
1258       op_par->propagate_settings();
1259       op_par->compute_padding(image, 0, 0);
1260       VipsImage* out = op_par->build( in, 0, NULL, NULL, level );
1261       PF_UNREF( image, "Image::do_export_merged(): image unref" );
1262       image = out;
1263     }
1264 
1265     PF::ICCProfile* iccprof = NULL;
1266     if( export_opt ) {
1267       std::cout<<"Image::do_export_merged(): profile_type="<<export_opt->profile_type<<"  trc="<<export_opt->trc_type<<std::endl;
1268       if( export_opt->profile_type == PF::PROF_TYPE_FROM_DISK ) {
1269         iccprof = PF::ICCStore::Instance().get_profile( export_opt->custom_profile_name );
1270         std::cout<<"Image::do_export_merged(): iccprof="<<iccprof<<"    custom_profile_name="<<export_opt->custom_profile_name<<std::endl;
1271       } else if( export_opt->profile_type == PF::PROF_TYPE_EMBEDDED ) {
1272         // No ICC conversion in this case
1273         iccprof = NULL;
1274       } else {//if( pmode == PF::PROF_MODE_CUSTOM ) {
1275         iccprof = PF::ICCStore::Instance().get_profile( export_opt->profile_type, export_opt->trc_type );
1276       }
1277     }
1278 
1279 
1280     if( iccprof ) {
1281       PF::ICCTransformPar* tr_par =
1282           dynamic_cast<PF::ICCTransformPar*>( convert2outprof->get_par() );
1283       in.clear();
1284       in.push_back( image );
1285       tr_par->set_image_hints( image );
1286       tr_par->set_format( VIPS_FORMAT_FLOAT );
1287       tr_par->set_out_profile( iccprof );
1288       tr_par->set_intent( export_opt->intent );
1289       tr_par->set_bpc( export_opt->bpc );
1290       tr_par->set_adaptation_state( 0 );
1291       tr_par->set_clip_negative(false);
1292       tr_par->set_clip_overflow(false);
1293       VipsImage* out = tr_par->build( in, 0, NULL, NULL, level );
1294       PF_UNREF( image, "Image::do_export_merged(): image unref" );
1295       image = out;
1296       print_embedded_profile( image );
1297     }
1298 
1299     int out_width = image->Xsize;
1300     int out_height = image->Ysize;
1301 
1302     int sRGBout = 1;
1303     iccprof = PF::get_icc_profile(image);
1304     if(iccprof && ((iccprof->get_profile_type() != PF::PROF_TYPE_sRGB) || (iccprof->get_trc_type() != PF::PF_TRC_STANDARD))) sRGBout = 0;
1305 
1306 
1307     if( ext == "jpg" || ext == "jpeg" ) {
1308       /*
1309       in.clear();
1310       in.push_back( image );
1311       convert_format->get_par()->set_image_hints( image );
1312       convert_format->get_par()->set_format( VIPS_FORMAT_UCHAR );
1313       outimg = convert_format->get_par()->build( in, 0, NULL, NULL, level );
1314       PF_UNREF( image, "Image::do_export_merged(): image unref" );
1315       */
1316       //outimg = image;
1317       if( vips_linear1(image, &outimg, 255, 0, NULL) ) {
1318         std::cout<<"WARNING!!! Image::do_export_merged(): vips_linear1() failed"<<std::endl;
1319         outimg = image;
1320         PF_REF( image, "Image::do_export_merged(): image ref after vips_linear1() failed" );
1321       }
1322       PF_UNREF( image, "Image::do_export_merged(): image unref" );
1323       if( outimg ) {
1324         BENCHFUN
1325         Glib::Timer timer;
1326         timer.start();
1327         gint Q = 75;
1328         gboolean no_subsample = true;
1329         gboolean overshoot_deringing = true;
1330         gboolean trellis_quant = true;
1331         gboolean optimize_scans = true;
1332         gint quant_table = 0;
1333         if( export_opt ) {
1334           Q = export_opt->jpeg_quality;
1335           no_subsample = (export_opt->jpeg_chroma_subsampling == false);
1336           quant_table = export_opt->jpeg_quant_table;
1337         }
1338         std::cout<<"Image::do_export_merged(): Q="<<Q<<"  no_subsample="<<no_subsample<<std::endl;
1339         if( vips_jpegsave( outimg, filename.c_str(), "Q", Q, "no_subsample", no_subsample,
1340             "quant_table", quant_table, "overshoot_deringing", overshoot_deringing,
1341             "trellis_quant", trellis_quant, "optimize_scans", optimize_scans, NULL ) == 0 ) {
1342           if( PF::PhotoFlow::Instance().get_options().get_save_sidecar_files() != 0 &&
1343               !(PF::PhotoFlow::Instance().is_plugin()) ) {
1344             save(filename+".pfi", false, false);
1345           }
1346           saved = true;
1347           add_exif = true;
1348         }
1349         timer.stop();
1350         std::cout<<"Jpeg image saved in "<<timer.elapsed()<<" s"<<std::endl;
1351       }
1352     }
1353 
1354     if( ext == "tif" || ext == "tiff" ) {
1355       /*
1356       in.clear();
1357       in.push_back( image );
1358       convert_format->get_par()->set_image_hints( image );
1359       //convert_format->get_par()->set_format( VIPS_FORMAT_USHORT );
1360       convert_format->get_par()->set_format( VIPS_FORMAT_FLOAT );
1361       outimg = convert_format->get_par()->build( in, 0, NULL, NULL, level );
1362       */
1363       outimg = image;
1364       std::cout<<"Image::do_export_merged(): export_opt="<<export_opt<<std::endl;
1365       if( export_opt )
1366         std::cout<<"Image::do_export_merged(): tiff_format="<<export_opt->tiff_format<<std::endl;
1367       if( export_opt && export_opt->tiff_format != EXPORT_FORMAT_TIFF_32f ) {
1368         int max = 255;
1369         if( export_opt->tiff_format == EXPORT_FORMAT_TIFF_16 ) max = 65535;
1370         std::cout<<"Image::do_export_merged(): max="<<max<<std::endl;
1371         if( !vips_linear1(image, &outimg, max, 0, NULL) ) {
1372           PF_UNREF( image, "Image::do_export_merged(): image unref" );
1373           image = outimg;
1374         } else {
1375           std::cout<<"WARNING!!! Image::do_export_merged(): vips_linear1() failed"<<std::endl;
1376         }
1377 
1378         VipsBandFormat format = VIPS_FORMAT_UCHAR;
1379         if( export_opt->tiff_format == EXPORT_FORMAT_TIFF_16 ) format = VIPS_FORMAT_USHORT;
1380         if( !vips_cast(image, &outimg, format, NULL) ) {
1381           PF_UNREF( image, "Image::do_export_merged(): image unref" );
1382           image = outimg;
1383         } else {
1384           std::cout<<"WARNING!!! Image::do_export_merged(): vips_cast() failed"<<std::endl;
1385         }
1386       }
1387       std::cout<<"Image::do_export_merged(): saving TIFF file "<<filename<<"   outimg="<<outimg<<std::endl;
1388       if( outimg ) {
1389         BENCHFUN
1390         int predictor = 2;
1391 #ifndef NDEBUG
1392         std::cout<<"Image::do_export_merged(): calling vips_tiffsave()..."<<std::endl;
1393 #endif
1394 
1395         int compression = (export_opt->tiff_compress==1) ? VIPS_FOREIGN_TIFF_COMPRESSION_DEFLATE : VIPS_FOREIGN_TIFF_COMPRESSION_NONE;
1396         if( vips_tiffsave( outimg, filename.c_str(), "compression", compression,
1397             //VIPS_FOREIGN_TIFF_COMPRESSION_DEFLATE,
1398             //VIPS_FOREIGN_TIFF_COMPRESSION_NONE,
1399             //    "predictor", VIPS_FOREIGN_TIFF_PREDICTOR_NONE, NULL );
1400             "predictor", VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL, NULL ) == 0 ) {
1401 #ifndef NDEBUG
1402           std::cout<<"Image::do_export_merged(): vips_tiffsave() finished..."<<std::endl;
1403 #endif
1404           //vips_image_write_to_file( outimg, filename.c_str(), NULL );
1405           if( PF::PhotoFlow::Instance().get_options().get_save_sidecar_files() != 0 &&
1406               !(PF::PhotoFlow::Instance().is_plugin()) ) {
1407             save(filename+".pfi", false, false);
1408           }
1409           saved = true;
1410           add_exif = true;
1411         }
1412       }
1413     }
1414 
1415     if( ext == "exr" ) {
1416       int halfFloat = 0;
1417       outimg = image;
1418       std::cout<<"Image::do_export_merged(): export_opt="<<export_opt<<std::endl;
1419       if( export_opt )
1420         std::cout<<"Image::do_export_merged(): exr_format="<<export_opt->exr_format<<std::endl;
1421       std::cout<<"Image::do_export_merged(): saving EXR file "<<filename<<"   outimg="<<outimg<<std::endl;
1422       if( export_opt && export_opt->tiff_format != EXPORT_FORMAT_EXR_16f ) {
1423         halfFloat = 1;
1424       }
1425       if( outimg ) {
1426         PF::TRCConvPar* lin_par =
1427             dynamic_cast<PF::TRCConvPar*>( convert2linear->get_par() );
1428         in.clear();
1429         in.push_back( outimg );
1430         lin_par->set_image_hints( image );
1431         lin_par->set_format( VIPS_FORMAT_FLOAT );
1432         lin_par->set_to_perceptual( false );
1433         VipsImage* tempimg = lin_par->build( in, 0, NULL, NULL, level );
1434         PF_UNREF( outimg, "Image::do_export_merged(): outimg unref" );
1435         outimg = tempimg;
1436 
1437         BENCHFUN
1438 #ifndef NDEBUG
1439         std::cout<<"Image::do_export_merged(): calling vips_exrsave()..."<<std::endl;
1440 #endif
1441         PF::exiv2_data_t* exiv2_buf;
1442         size_t exiv2_buf_length;
1443         if( PF_VIPS_IMAGE_GET_BLOB( outimg, PF_META_EXIV2_NAME, (&exiv2_buf), &exiv2_buf_length ) )
1444           exiv2_buf = NULL;
1445         uint8_t* blob = NULL;
1446         int length = 0;
1447         if(exiv2_buf) {
1448           blob = exiv2_buf->blob;
1449           length = exiv2_buf->length;
1450         }
1451         if(vips_exrsave(outimg, filename.c_str(), halfFloat, blob, length) == 0) {
1452 #ifndef NDEBUG
1453           std::cout<<"Image::do_export_merged(): vips_exrsave() finished..."<<std::endl;
1454 #endif
1455           if( PF::PhotoFlow::Instance().get_options().get_save_sidecar_files() != 0 &&
1456               !(PF::PhotoFlow::Instance().is_plugin()) ) {
1457             save(filename+".pfi", false, false);
1458           }
1459           saved = true;
1460           export_ok = true;
1461         }
1462       }
1463     }
1464     /**/
1465 
1466     if( add_exif ) {
1467 
1468       try {
1469         PF::exiv2_data_t* exiv2_buf;
1470         size_t exiv2_buf_length;
1471         if( PF_VIPS_IMAGE_GET_BLOB( outimg, PF_META_EXIV2_NAME, (&exiv2_buf), &exiv2_buf_length ) )
1472           exiv2_buf = NULL;
1473         //if( exiv2_buf && (exiv2_buf_length==sizeof(PF::exiv2_data_t)) && exiv2_buf->image.get() != NULL ) {
1474         if( exiv2_buf && (exiv2_buf_length==sizeof(PF::exiv2_data_t)) && exiv2_buf->blob != NULL ) {
1475 
1476           dt_exif_write_blob(exiv2_buf->blob, exiv2_buf->length, filename.c_str(), sRGBout, out_width, out_height);
1477 
1478           Exiv2::BasicIo::AutoPtr file (new Exiv2::FileIo (filename));
1479           Exiv2::Image::AutoPtr exiv2_image = Exiv2::ImageFactory::open(file);
1480           if(exiv2_image.get() != 0) {
1481             exiv2_image->readMetadata();
1482 
1483             void *iccdata;
1484             size_t iccdata_length;
1485 
1486             if( !PF_VIPS_IMAGE_GET_BLOB( outimg, VIPS_META_ICC_NAME, &iccdata, &iccdata_length ) ) {
1487               Exiv2::byte *iccdata2 = (Exiv2::byte *)iccdata;
1488               Exiv2::DataBuf iccbuf(iccdata2, iccdata_length);
1489               exiv2_image->setIccProfile( iccbuf, true );
1490             }
1491             exiv2_image->writeMetadata();
1492           }
1493         }
1494         export_ok = true;
1495       } catch(Exiv2::AnyError &e) {
1496         std::string s(e.what());
1497         std::cerr << "[exiv2] " << filename << ": " << s << std::endl;
1498       }
1499     }
1500 
1501     if( outimg ) {
1502       msg = std::string("PF::Image::export_merged(") + filename + "): outimg unref";
1503       PF_UNREF( outimg, msg.c_str() );
1504     }
1505     remove_pipeline( pipeline );
1506     delete pipeline;
1507     //layer_manager.reset_cache_buffers( PF_RENDER_NORMAL, true );
1508     std::cout<<"Image saved to file "<<filename<<std::endl;
1509 
1510     if( export_opt ) free(export_opt);
1511   }
1512 }
1513 
1514 
1515 
1516 
1517 
memsave_start(struct _VipsImage * out,void * a,void * b)1518 static void* memsave_start( struct _VipsImage *out, void *a, void *b )
1519 {
1520   PF::ImageBuffer* imgbuf = (PF::ImageBuffer*)a;
1521   return imgbuf;
1522 }
1523 
1524 
memsave_stop(void * seq,void * a,void * b)1525 static int memsave_stop( void* seq, void *a, void *b )
1526 {
1527   return(0);
1528 }
1529 
1530 
1531 
1532 /* Loop over region, accumulating a sum in *tmp.
1533  */
memsave_scan(VipsRegion * region,void * seq,void * a,void * b,gboolean * stop)1534 static int memsave_scan( VipsRegion *region,
1535     void *seq, void *a, void *b, gboolean *stop )
1536 {
1537   VipsRect *r = &region->valid;
1538   int lsk = VIPS_REGION_LSKIP( region );
1539   int bands = vips_image_get_bands( region->im );
1540   int lsz = bands * r->width;
1541 
1542   int x, y;
1543   VipsPel* p;
1544   void* pout;
1545 
1546   PF::ImageBuffer* imgbuf = (PF::ImageBuffer*)seq;
1547 
1548   for( y = 0; y < r->height; y++ ) {
1549     p = VIPS_REGION_ADDR( region, r->left, r->top+y );
1550     pout = &( imgbuf->buf[ (r->top+y)*imgbuf->width*3 + r->left*3 ] );
1551     memcpy( pout, p, sizeof(float)*lsz );
1552   }
1553 
1554   return( 0 );
1555 }
1556 
1557 
1558 
export_merged_to_mem(PF::ImageBuffer * imgbuf,void * out_iccdata,size_t out_iccsize)1559 void PF::Image::export_merged_to_mem( PF::ImageBuffer* imgbuf, void* out_iccdata, size_t out_iccsize )
1560 {
1561   imgbuf->iccdata = NULL;
1562   imgbuf->iccsize = 0;
1563   imgbuf->buf = NULL;
1564 
1565 //#ifndef NDEBUG
1566   std::cout<<"Image::export_merged_to_mem(): waiting for caching completion..."<<std::endl;
1567 //#endif
1568   PF::ImageProcessor::Instance().wait_for_caching();
1569 //#ifndef NDEBUG
1570   std::cout<<"Image::export_merged_to_mem(): ... caching completed"<<std::endl;
1571 //#endif
1572 
1573   unsigned int level = 0;
1574   PF::Pipeline* pipeline = add_pipeline( VIPS_FORMAT_FLOAT, 0, PF_RENDER_NORMAL );
1575   if( pipeline ) pipeline->set_op_caching_enabled( true );
1576   update( pipeline, true );
1577 #ifndef NDEBUG
1578   std::cout<<"Image::export_merged_to_mem(): image updated."<<std::endl;
1579 #endif
1580 
1581   std::string msg;
1582   VipsImage* image = pipeline->get_output();
1583   VipsImage* outimg = NULL;
1584 
1585   std::vector<VipsImage*> in;
1586   in.clear();
1587   in.push_back( image );
1588   convert_format->get_par()->set_image_hints( image );
1589   convert_format->get_par()->set_format( VIPS_FORMAT_FLOAT );
1590   VipsImage* floatimg = convert_format->get_par()->build( in, 0, NULL, NULL, level );
1591 
1592   PF::ICCProfile* out_iccprofile = NULL;
1593   outimg = floatimg;
1594 #ifndef NDEBUG
1595   std::cout<<"Image::export_merged_to_mem(): out_iccdata="<<(void*)out_iccdata<<std::endl;
1596 #endif
1597   if( floatimg && out_iccdata ) {
1598     out_iccprofile = PF::ICCStore::Instance().get_profile( out_iccdata, out_iccsize );
1599 #ifndef NDEBUG
1600     std::cout<<"Image::export_merged_to_mem(): out_iccprofile="<<(void*)out_iccprofile<<std::endl;
1601 #endif
1602     if( out_iccprofile ) {
1603       PF::ICCTransformPar* conv_par =
1604           dynamic_cast<PF::ICCTransformPar*>( convert2outprof->get_par() );
1605 #ifndef NDEBUG
1606       std::cout<<"Image::export_merged_to_mem(): conv_par="<<(void*)conv_par<<std::endl;
1607 #endif
1608       if( conv_par ) {
1609         in.clear();
1610         in.push_back( floatimg );
1611         conv_par->set_image_hints( floatimg );
1612         conv_par->set_format( VIPS_FORMAT_FLOAT );
1613         conv_par->set_out_profile( out_iccprofile );
1614         outimg = convert2outprof->get_par()->build( in, 0, NULL, NULL, level );
1615         PF_UNREF( floatimg, "" );
1616       }
1617     }
1618   }
1619 
1620 #ifndef NDEBUG
1621   std::cout<<"Image::export_merged_to_mem(): outimg="<<outimg<<std::endl;
1622 #endif
1623   if( outimg ) {
1624     imgbuf->buf = (float*)malloc( sizeof(float)*3*outimg->Xsize*outimg->Ysize );
1625     imgbuf->width = outimg->Xsize;
1626     imgbuf->height = outimg->Ysize;
1627 
1628     vips_sink( outimg, memsave_start, memsave_scan, memsave_stop, imgbuf, NULL );
1629 #ifndef NDEBUG
1630     std::cout<<"Image::export_merged_to_mem(): vips_sink() finished."<<std::endl;
1631 #endif
1632 
1633     //if( out_iccprofile ) cmsCloseProfile( out_iccprofile );
1634     //std::cout<<"Image::export_merged_to_mem(): output profile closed."<<std::endl;
1635 
1636     void *iccdata;
1637     size_t iccsize;
1638     if( !PF_VIPS_IMAGE_GET_BLOB( outimg, VIPS_META_ICC_NAME, &iccdata, &iccsize ) ) {
1639       imgbuf->iccdata = malloc(iccsize);
1640       if( imgbuf->iccdata ) {
1641         imgbuf->iccsize = iccsize;
1642         memcpy( imgbuf->iccdata, iccdata, iccsize );
1643       }
1644     } else {
1645       imgbuf->iccdata = NULL;
1646       imgbuf->iccsize = 0;
1647     }
1648 
1649     imgbuf->trc_type = PF_TRC_STANDARD;
1650     ICCProfile* iccinfo  = get_icc_profile( outimg );
1651     if( iccinfo ) imgbuf->trc_type = iccinfo->get_trc_type();
1652 
1653     /*
1654     void* gexiv2_buf;
1655     size_t gexiv2_buf_length;
1656     if( !vips_image_get_blob( outimg, "gexiv2-data",
1657                              &gexiv2_buf, &gexiv2_buf_length ) &&
1658         gexiv2_buf && (gexiv2_buf_length==sizeof(GExiv2Metadata)) ) {
1659       imgbuf->exif_buf = (GExiv2Metadata*)gexiv2_buf;
1660       //imgbuf->exif_buf = (GExiv2Metadata*)malloc( sizeof(GExiv2Metadata) );
1661       //if( imgbuf->exif_buf ) {
1662         //memcpy( imgbuf->exif_buf, gexiv2_buf, sizeof(GExiv2Metadata) );
1663       //}
1664     } else {
1665       imgbuf->exif_buf = NULL;
1666     }
1667      */
1668 
1669     msg = std::string("PF::Image::export_merged_to_mem(): outimg unref");
1670     PF_UNREF( outimg, msg.c_str() );
1671   }
1672 
1673   remove_pipeline( pipeline );
1674   delete pipeline;
1675   //layer_manager.reset_cache_buffers( PF_RENDER_NORMAL, true );
1676   std::cout<<"Image saved to memory "<<std::endl;
1677 }
1678 
1679 
1680 
export_merged_to_tiff(const std::string filename)1681 void PF::Image::export_merged_to_tiff( const std::string filename )
1682 {
1683   std::cout<<"Image::export_merged_to_tiff(): waiting for caching completion..."<<std::endl;
1684   PF::ImageProcessor::Instance().wait_for_caching();
1685   std::cout<<"Image::export_merged_to_tiff(): ... caching completed"<<std::endl;
1686 
1687   unsigned int level = 0;
1688   PF::Pipeline* pipeline = add_pipeline( VIPS_FORMAT_FLOAT, 0, PF_RENDER_NORMAL );
1689   if( pipeline ) pipeline->set_op_caching_enabled( true );
1690   update( pipeline, true );
1691 //#ifndef NDEBUG
1692   std::cout<<"Image::export_merged_to_tiff(): image updated."<<std::endl;
1693 //#endif
1694 
1695   void* out_iccdata;
1696   size_t out_iccsize;
1697 
1698   PF::Layer* layer = get_layer_manager().get_layers().front();
1699   if( !layer ) return;
1700 
1701   PipelineNode* node = pipeline->get_node( layer->get_id() );
1702   if( !node ) return;
1703 
1704   VipsImage* bgdimg = node->image;
1705   cmsHPROFILE in_profile = NULL;
1706   PF::ICCProfile* iccprof_in = PF::get_icc_profile( bgdimg );
1707   if( iccprof_in )  {
1708     in_profile = iccprof_in->get_profile();
1709   }
1710 
1711 
1712   std::vector<VipsImage*> in;
1713   std::string msg;
1714   VipsImage* image = pipeline->get_output();
1715   VipsImage* outimg = NULL;
1716   /*
1717   in.clear();
1718   in.push_back( image );
1719   convert_format->get_par()->set_image_hints( image );
1720   convert_format->get_par()->set_format( VIPS_FORMAT_FLOAT );
1721   VipsImage* floatimg = convert_format->get_par()->build( in, 0, NULL, NULL, level );
1722   */
1723   VipsImage* floatimg = image; PF_REF( floatimg, "" );
1724   outimg = floatimg;
1725   PF::ICCTransformPar* conv_par =
1726       dynamic_cast<PF::ICCTransformPar*>( convert2outprof->get_par() );
1727 //#ifndef NDEBUG
1728   std::cout<<"Image::export_merged_to_tiff(): conv_par="<<(void*)conv_par<<std::endl;
1729   std::cout<<"Image::export_merged_to_tiff(): iccprof_in="<<(void*)iccprof_in<<std::endl;
1730 //#endif
1731   if( conv_par ) {
1732     in.clear();
1733     in.push_back( floatimg );
1734     conv_par->set_image_hints( floatimg );
1735     conv_par->set_format( VIPS_FORMAT_FLOAT );
1736     conv_par->set_out_profile( iccprof_in );
1737     outimg = convert2outprof->get_par()->build( in, 0, NULL, NULL, level );
1738     PF_UNREF( floatimg, "" );
1739   }
1740 
1741   std::cout<<"Image::export_merged_to_tiff(): outimg="<<outimg<<std::endl;
1742   if( outimg ) {
1743     std::cout<<"Image::do_export_merged(): calling vips_tiffsave()..."<<std::endl;
1744     vips_tiffsave( outimg, filename.c_str(), "compression", VIPS_FOREIGN_TIFF_COMPRESSION_NONE,
1745         "predictor", VIPS_FOREIGN_TIFF_PREDICTOR_NONE, NULL );
1746     //    "predictor", VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL, NULL );
1747     std::cout<<"Image::do_export_merged(): vips_tiffsave() finished: "<<filename<<std::endl;
1748     char tstr[500];
1749     snprintf(tstr, 499, "ls -lh %s", filename.c_str());
1750     std::cout<<tstr<<":"<<std::endl;
1751     int ret = system(tstr);
1752 
1753     msg = std::string("PF::Image::export_merged_to_tiff(): outimg unref");
1754     PF_UNREF( outimg, msg.c_str() );
1755   }
1756 
1757   remove_pipeline( pipeline );
1758   delete pipeline;
1759   //layer_manager.reset_cache_buffers( PF_RENDER_NORMAL, true );
1760   std::cout<<"Image saved to TIFF: \""<<filename<<"\""<<std::endl;
1761 }
1762