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 = ®ion->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