1 /* base class for all PhotoFlow layer operations
2  */
3 
4 /*
5 
6     Copyright (C) 2014 Ferrero Andrea
7 
8     This program is free software: you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation, either version 3 of the License, or
11     (at your option) any later version.
12 
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16     GNU General Public License for more details.
17 
18     You should have received a copy of the GNU General Public License
19     along with this program. If not, see <http://www.gnu.org/licenses/>.
20 
21 
22  */
23 
24 /*
25 
26     These files are distributed with PhotoFlow - http://aferrero2707.github.io/PhotoFlow/
27 
28  */
29 
30 
31 #ifndef OPERATION_H
32 #define OPERATION_H
33 
34 #include <math.h>
35 #include <float.h>
36 #include <unistd.h>
37 
38 #include <string>
39 #include <list>
40 #include <vector>
41 #include <iostream>
42 #include <fstream>
43 
44 #include <vips/vips.h>
45 //#include <vips/vips>
46 
47 #include "pftypes.hh"
48 #include "format_info.hh"
49 #include "property.hh"
50 #include "color.hh"
51 #include "image_hierarchy.hh"
52 #include "photoflow.hh"
53 
54 
55 
56 #define OP_TEMPLATE_DEF \
57     typename T, class BLENDER, PF::colorspace_t CS,	\
58     int CHMIN, int CHMAX,								\
59     bool has_imap, bool has_omap, bool PREVIEW
60 
61 #define OP_TEMPLATE_IMP \
62     T, BLENDER, CS, CHMIN, CHMAX, has_imap, has_omap, PREVIEW
63 
64 #define OP_TEMPLATE_DEF_BLENDER_SPEC \
65     typename T, PF::colorspace_t CS,	int CHMIN, int CHMAX,		\
66     bool has_imap, bool has_omap, bool PREVIEW
67 
68 #define OP_TEMPLATE_IMP_BLENDER_SPEC(BLENDER_SPEC) \
69     T, BLENDER_SPEC< T, CS, CHMIN, CHMAX, has_omap >, CS, CHMIN, CHMAX, has_imap, has_omap, PREVIEW
70 
71 
72 #define OP_TEMPLATE_DEF_TYPE_SPEC \
73     class BLENDER, PF::colorspace_t CS, int CHMIN, int CHMAX,		\
74     bool has_imap, bool has_omap, bool PREVIEW
75 
76 #define OP_TEMPLATE_IMP_TYPE_SPEC(TYPE_SPEC) \
77     TYPE_SPEC, BLENDER, CS, CHMIN, CHMAX, has_imap, has_omap, PREVIEW
78 
79 
80 #define OP_TEMPLATE_DEF_CS_SPEC \
81     typename T, class BLENDER, int CHMIN, int CHMAX,	\
82     bool has_imap, bool has_omap, bool PREVIEW
83 
84 #define OP_TEMPLATE_IMP_CS_SPEC(CS_SPEC) \
85     T, BLENDER, CS_SPEC, CHMIN, CHMAX, has_imap, has_omap, PREVIEW
86 
87 
88 
89 #define OP_TEMPLATE_DEF_PREVIEW_SPEC \
90     typename T, class BLENDER, PF::colorspace_t CS,	int CHMIN, int CHMAX,	\
91     bool has_imap, bool has_omap
92 
93 #define OP_TEMPLATE_IMP_PREVIEW_SPEC(PREVIEW_SPEC) \
94     T, BLENDER, CS, CHMIN, CHMAX, has_imap, has_omap, PREVIEW_SPEC
95 
96 
97 #define PF_OUPUT_CACHE_TS 128
98 
99 
100 
101 namespace PF
102 {
103 
104 class ProcessorBase;
105 class Layer;
106 
107 template<typename T>
108 class PixelMatrix
109 {
110   T* data;
111   T** rows;
112   T** ptr;
113   int w, h, co, ro;
114 public:
PixelMatrix()115   PixelMatrix(): data(NULL), w(0), h(0), ptr(NULL), rows(NULL) {}
PixelMatrix(T * buf,int width,int height,int rowstride,int roffs,int coffs)116   PixelMatrix(T* buf, int width, int height, int rowstride, int roffs, int coffs):
117     data(NULL), w(width), h(height), ro(roffs), co(coffs)
118   {
119     ptr = new T*[h];
120     rows = ptr - roffs;
121     for(int i = 0; i < height; i++) {
122       ptr[i] = buf - coffs;
123       buf += rowstride;
124     }
125     //std::cout<<"Initialized pixel matrix from buf="<<buf<<"  "<<w<<"x"<<h<<"+"<<coffs<<","<<roffs<<std::endl;
126   }
PixelMatrix(int width,int height,int roffs=0,int coffs=0)127   PixelMatrix(int width, int height, int roffs=0, int coffs=0):
128     data(NULL), w(width), h(height), ro(roffs), co(coffs), ptr(NULL), rows(NULL)
129   {
130     data = new T[w*h];
131     int rowstride = w;
132     T* buf = data;
133     ptr = new T*[h];
134     rows = ptr - roffs;
135     for(int i = 0; i < height; i++) {
136       ptr[i] = buf - coffs;
137       buf += rowstride;
138     }
139     //std::cout<<"Initialized pixel matrix "<<w<<"x"<<h<<"+"<<coffs<<","<<roffs<<std::endl;
140   }
PixelMatrix(const PixelMatrix & m)141   PixelMatrix(const PixelMatrix& m):
142     data(NULL), w(0), h(0)
143   {
144     w = m.width();
145     h = m.height();
146     ro = m.roffs();
147     co = m.coffs();
148     data = new T[w*h];
149     int rowstride = w;
150     T* buf = data;
151     ptr = new T*[h];
152     rows = ptr - ro;
153     for(int i = 0; i < h; i++) {
154       ptr[i] = buf - co;
155       memcpy(buf, m[i] + co, sizeof(T)*w);
156       buf += rowstride;
157     }
158     //std::cout<<"Initialized pixel matrix "<<w<<"x"<<h<<"+"<<coffs<<","<<roffs<<std::endl;
159   }
~PixelMatrix()160   ~PixelMatrix()
161   {
162     if(data) delete[] data;
163     if(ptr) delete[] ptr;
164   }
165 
operator =(const PixelMatrix & m)166   PixelMatrix& operator =(const PixelMatrix& m)
167   {
168     if(data) delete[] data;
169     if(ptr) delete[] ptr;
170 
171     w = m.width();
172     h = m.height();
173     ro = m.roffs();
174     co = m.coffs();
175     data = new T[w*h];
176     int rowstride = w;
177     T* buf = data;
178     ptr = new T*[h];
179     rows = ptr - ro;
180     for(int i = 0; i < h; i++) {
181       ptr[i] = buf - co;
182       memcpy(buf, m[i] + co, sizeof(T)*w);
183       buf += rowstride;
184     }
185 
186     return(*this);
187     //std::cout<<"Initialized pixel matrix "<<w<<"x"<<h<<"+"<<coffs<<","<<roffs<<std::endl;
188   }
189 
resize(int width,int height,int roffs=0,int coffs=0)190   void resize(int width, int height, int roffs=0, int coffs=0)
191   {
192     if(data) delete[] data;
193     if(ptr) delete[] ptr;
194 
195     w = width;
196     h = height;
197     ro = roffs;
198     co = coffs;
199     data = new T[w*h];
200     int rowstride = w;
201     T* buf = data;
202     ptr = new T*[h];
203     rows = ptr - roffs;
204     for(int i = 0; i < height; i++) {
205       ptr[i] = buf - coffs;
206       buf += rowstride;
207     }
208     //std::cout<<"Initialized pixel matrix "<<w<<"x"<<h<<"+"<<coffs<<","<<roffs<<std::endl;
209   }
210 
211   // use as pointer to T**
operator T**()212   operator T**() { return rows; }
get_rows()213   T** get_rows() { return rows; }
operator [](int id) const214   T* operator[](int id) const { return rows[id]; }
width() const215   int width() const { return w; }
height() const216   int height() const { return h; }
roffs() const217   int roffs() const { return ro; }
coffs() const218   int coffs() const { return co; }
219 };
220 
221 
222 
223 
224 class OperationConfigUI
225 {
226   std::list<std::string> initial_params;
227 
228   Layer* layer;
229 
230 public:
231 
OperationConfigUI(Layer * l)232   OperationConfigUI( Layer* l ): layer( l ) {}
~OperationConfigUI()233   virtual ~OperationConfigUI() {}
234 
get_layer()235   Layer* get_layer() { return layer; }
236   //void set_layer( Layer* l ) { layer = l; }
237 
238   virtual void open() = 0;
239   virtual void init() = 0;
240   virtual void update() = 0;
do_update()241   virtual void do_update() { update(); }
242   virtual void update_properties() = 0;
243 };
244 
245 /* Base class for all operation parameter implementations
246  */
247 class OpParBase: public sigc::trackable
248 {
249   VipsDemandStyle demand_hint;
250 
251   std::string type;
252 
253   ProcessorBase* processor;
254   ProcessorBase* to_map;
255 
256   OperationConfigUI* config_ui;
257 
258   // Requested image fields
259   int xsize;
260   int ysize;
261   int bands;
262   VipsBandFormat format;
263   VipsCoding coding;
264   VipsInterpretation interpretation;
265 
266   // Padding required by the operation when processing input images
267   std::vector<int> input_paddings;
268   // Total padding requested to the output of the operation by all the
269   // child operations in the pipeline
270   std::vector<int> output_paddings;
271   bool output_caching_enabled;
272 
273   rendermode_t render_mode;
274 
275   bool map_flag;
276 
277   bool editing_flag;
278 
279   bool modified_flag;
280 
281   std::string default_name;
282 
283   std::list<PropertyBase*> mapped_properties;
284   std::list<PropertyBase*> properties;
285 
286   Property<float> intensity;
287 
288   PropertyBase grey_target_channel;
289   PropertyBase rgb_target_channel;
290   PropertyBase lab_target_channel;
291   PropertyBase cmyk_target_channel;
292 
293   Property<bool> mask_enabled;
294 
295   int file_format_version;
296 
297   Property<bool> enable_padding;
298   int test_padding;
299 
300 public:
301   sigc::signal<void> signal_modified;
302 
303   OpParBase();
304   virtual ~OpParBase();
305 
get_type()306   std::string get_type() { return type; }
set_type(std::string str)307   void set_type( std::string str ) { type = str; }
308 
get_properties()309   std::list<PropertyBase*>& get_properties() { return properties; }
get_mapped_properties()310   std::list<PropertyBase*>& get_mapped_properties() { return mapped_properties; }
add_property(PropertyBase * p)311   void add_property( PropertyBase* p )
312   {
313     properties.push_back(p);
314     p->signal_modified.connect(sigc::mem_fun(this, &OpParBase::modified) );
315   }
map_property(PropertyBase * p)316   void map_property( PropertyBase* p ) {
317     if( p->is_internal() )
318       return;
319     mapped_properties.push_back(p);
320     p->signal_modified.connect(sigc::mem_fun(this, &OpParBase::modified) );
321   }
map_properties(std::list<PropertyBase * > pl)322   void map_properties( std::list<PropertyBase*> pl )
323   {
324     mapped_properties.insert( mapped_properties.end(),
325         pl.begin(), pl.end() );
326     for( std::list<PropertyBase*>::iterator i = pl.begin();
327         i != pl.end(); i++ )
328       (*i)->signal_modified.connect(sigc::mem_fun(this, &OpParBase::modified) );
329   }
330   void save_properties(std::list<std::string>& plist);
331   void restore_properties(const std::list<std::string>& plist);
332 
set_property(std::string pname,const T & newval)333   template<typename T> bool set_property(std::string pname, const T& newval)
334   {
335     PF::PropertyBase* prop = get_property(pname);
336     if( !prop ) return false;
337     prop->update( newval );
338     return true;
339   }
340 
341 
342   virtual bool import_settings( OpParBase* pin );
343   // update properties of sub-operations
propagate_settings()344   virtual void propagate_settings() {}
345 
set_processor(ProcessorBase * p)346   void set_processor(ProcessorBase* p) { processor = p; }
get_processor()347   ProcessorBase* get_processor() { return processor; }
348 
set_demand_hint(VipsDemandStyle val)349   void set_demand_hint(VipsDemandStyle val) { demand_hint = val; }
get_demand_hint()350   VipsDemandStyle get_demand_hint() { return demand_hint; }
351 
set_intensity(float val)352   void set_intensity(float val) { intensity.set(val); }
get_intensity()353   float get_intensity() { return intensity.get(); }
354 
is_map()355   bool is_map() { return map_flag; }
set_map_flag(bool flag)356   void set_map_flag( bool flag ) { map_flag = flag; }
357 
is_editing_locked()358   virtual bool is_editing_locked() { return false; }
is_editing()359   bool is_editing() { return( editing_flag || is_editing_locked() ); }
set_editing_flag(bool flag)360   void set_editing_flag( bool flag ) { editing_flag = flag; }
361 
get_mask_enabled()362   bool get_mask_enabled() { return mask_enabled.get(); }
set_mask_enabled(bool val)363   void set_mask_enabled( bool val ) { mask_enabled.update(val); }
364 
convert_inputs_on_map_build()365   virtual bool convert_inputs_on_map_build() { return true; }
366 
is_modified()367   bool is_modified() { return modified_flag; }
set_modified()368   void set_modified() { modified_flag = true; }
369   void clear_modified();
370   virtual void modified();
371 
372 
get_default_name()373   std::string get_default_name() { return default_name; }
set_default_name(std::string n)374   void set_default_name( std::string n ) { default_name = n; }
375 
set_file_format_version(int v)376   void set_file_format_version(int v) { file_format_version = v; }
get_file_format_version()377   int get_file_format_version() { return file_format_version; }
378 
379 
get_rgb_target_channel()380   int get_rgb_target_channel()
381   {
382     if( !(rgb_target_channel.get_enum_value().second.first.empty()) )
383       return( rgb_target_channel.get_enum_value().first );
384     else
385       return -1;
386   }
387 
get_lab_target_channel()388   int get_lab_target_channel()
389   {
390     if( !(lab_target_channel.get_enum_value().second.first.empty()) )
391       return( lab_target_channel.get_enum_value().first );
392     else
393       return -1;
394   }
395 
get_cmyk_target_channel()396   int get_cmyk_target_channel()
397   {
398     if( !(cmyk_target_channel.get_enum_value().second.first.empty()) )
399       return( cmyk_target_channel.get_enum_value().first );
400     else
401       return -1;
402   }
403 
404   /* Function to derive the output area from the input area
405    */
transform(const VipsRect * rin,VipsRect * rout,int)406   virtual void transform(const VipsRect* rin, VipsRect* rout, int /*id*/)
407   {
408     int p = enable_padding.get() ? test_padding : 0;
409     rout->left = rin->left+p;
410     rout->top = rin->top+p;
411     rout->width = rin->width-p*2;
412     rout->height = rin->height-p*2;
413   }
414 
415   /* Function to derive the area to be read from input images,
416        based on the requested output area
417    */
transform_inv(const VipsRect * rout,VipsRect * rin,int)418   virtual void transform_inv(const VipsRect* rout, VipsRect* rin, int /*id*/)
419   {
420     int p = enable_padding.get() ? test_padding : 0;
421     rin->left = rout->left-p;
422     rin->top = rout->top-p;
423     rin->width = rout->width+p*2;
424     rin->height = rout->height+p*2;
425   }
426 
427 
has_intensity()428   virtual bool has_intensity() { return true; }
has_opacity()429   virtual bool has_opacity() { return true; }
has_target_channel()430   virtual bool has_target_channel() { return false; }
needs_input()431   virtual bool needs_input() { return true; }
needs_caching()432   virtual bool needs_caching() { return false; }
init_hidden()433   virtual bool init_hidden() { return false; }
434 
435   // whether the operation returns an input image at a given zoom level
436   // id is the output image index
is_noop(VipsImage * full_res,unsigned int id,unsigned int level)437   virtual bool is_noop( VipsImage* full_res, unsigned int id, unsigned int level )
438   {
439     return false;
440   }
441   virtual void compute_padding( VipsImage* full_res, unsigned int id, unsigned int level );
set_padding(int p,unsigned int id)442   void set_padding( int p, unsigned int id )
443   {
444     for( unsigned int i = input_paddings.size(); i <= id; i++ ) input_paddings.push_back(0);
445     input_paddings[id] = p;
446   }
get_padding(unsigned int id=0)447   int get_padding( unsigned int id = 0 )
448   {
449     return( (input_paddings.size()>id) ? input_paddings[id] : 0 );
450   }
get_output_padding(unsigned int id=0)451   int get_output_padding( unsigned int id = 0 )
452   {
453     return( (output_paddings.size()>id) ? output_paddings[id] : 0 );
454   }
set_output_padding(int p,unsigned int id=0)455   bool set_output_padding( int p, unsigned int id = 0 )
456   {
457     for( unsigned int i = output_paddings.size(); i <= id; i++ ) output_paddings.push_back(0);
458     if( output_paddings[id] < p ) {
459       output_paddings[id] = p;
460       return true;
461     }
462     return false;
463   }
reset_output_padding()464   void reset_output_padding() { output_paddings.clear(); }
set_output_caching(bool flag)465   void set_output_caching(bool flag) { output_caching_enabled = flag; }
get_output_caching()466   bool get_output_caching() { return output_caching_enabled; }
get_test_padding()467   virtual int get_test_padding() { return 0; }
468 
469   // get the real zoom level for a given requested level
get_real_level(unsigned int level)470   virtual unsigned int get_real_level(unsigned int level) { return level; }
471   // when the image is built at a zoom level different from the requested one,
472   // wether the image should be shrunk during the blending phasse or not
do_shirnk_on_blend()473   virtual bool do_shirnk_on_blend() { return false; }
474 
475   // return the number of output images. Equal to 1 in most cases
get_output_num()476   virtual int get_output_num() { return 1; }
477 
get_render_mode()478   rendermode_t get_render_mode() { return render_mode; }
set_render_mode(rendermode_t m)479   void set_render_mode(rendermode_t m) { render_mode = m; }
480 
481   // called after all properties have been loaded from .pfi file
finalize()482   virtual void finalize() {}
483 
pre_build(rendermode_t)484   virtual void pre_build( rendermode_t /*mode*/ ) {}
485 
486   virtual VipsImage* build(std::vector<VipsImage*>& in, int first,
487       VipsImage* imap, VipsImage* omap, unsigned int& level);
488   virtual std::vector<VipsImage*> build_many(std::vector<VipsImage*>& in, int first,
489       VipsImage* imap, VipsImage* omap, unsigned int& level);
490   std::vector<VipsImage*> build_many_internal(std::vector<VipsImage*>& in, int first,
491       VipsImage* imap, VipsImage* omap, unsigned int& level);
492   virtual void fill_image_hierarchy(std::vector<VipsImage*>& in,
493       VipsImage* imap, VipsImage* omap, std::vector<VipsImage*>& out);
494 
495 
496   PropertyBase* get_property(std::string name);
497 
set_property_value(std::string name,const T & newval)498   template<typename T> bool set_property_value(std::string name, const T& newval)
499   {
500     PropertyBase* prop = get_property(name);
501     if( !prop ) return false;
502     prop->update( newval );
503     return true;
504   }
505 
506 
507 
get_config_ui()508   OperationConfigUI* get_config_ui() { return config_ui; }
set_config_ui(OperationConfigUI * ui)509   void set_config_ui( OperationConfigUI* ui ) { config_ui = ui; }
510 
set_image_dimensions(int w,int h)511   void set_image_dimensions(int w, int h)
512   {
513     xsize = w; ysize = h;
514   }
get_xsize()515   int get_xsize() { return xsize; }
get_ysize()516   int get_ysize() { return ysize; }
get_nbands()517   int get_nbands() { return bands; }
set_nbands(int n)518   void set_nbands( int n ) { bands = n; }
get_interpretation()519   VipsInterpretation get_interpretation() { return interpretation; }
get_colorspace()520   colorspace_t get_colorspace() { return( PF::convert_colorspace( get_interpretation() ) ); }
accepts_colorspace(colorspace_t)521   virtual bool accepts_colorspace(colorspace_t) { return true; }
get_format()522   VipsBandFormat get_format() { return format; }
set_format(VipsBandFormat fmt)523   virtual void set_format( VipsBandFormat fmt ) { format = fmt; }
get_coding()524   VipsCoding get_coding() { return coding; }
set_coding(VipsCoding c)525   void set_coding( VipsCoding c ) { coding = c; }
526 
set_image_hints(OpParBase * op)527   virtual void set_image_hints( OpParBase* op )
528   {
529     if( !op ) return;
530     set_image_hints( op->get_xsize(), op->get_ysize(),
531         op->get_interpretation() );
532     bands = op->get_nbands();
533     rgb_target_channel.set_enum_value(op->get_rgb_target_channel());
534     lab_target_channel.set_enum_value(op->get_lab_target_channel());
535     cmyk_target_channel.set_enum_value(op->get_cmyk_target_channel());
536     set_demand_hint( op->get_demand_hint() );
537     set_map_flag( op->is_map() );
538     set_coding( op->get_coding() );
539     set_format( op->get_format() );
540     set_render_mode( op->get_render_mode() );
541   }
542 
set_image_hints(VipsImage * img)543   virtual void set_image_hints( VipsImage* img )
544   {
545     if( !img ) return;
546     set_image_hints( img->Xsize, img->Ysize,
547         img->Type );
548     bands = img->Bands;
549   }
550 
551   void set_image_hints(int w, int h, VipsInterpretation interpr);
552   void set_image_hints(int w, int h, colorspace_t cs);
553 
grayscale_image(int w,int h)554   void grayscale_image(int w, int h)
555   {
556     xsize = w; ysize = h;
557     bands = 1; interpretation = VIPS_INTERPRETATION_B_W;
558     coding = VIPS_CODING_NONE;
559   }
560 
rgb_image(int w,int h)561   void rgb_image(int w, int h)
562   {
563     xsize = w; ysize = h;
564     bands = 3; interpretation = VIPS_INTERPRETATION_RGB;
565     coding = VIPS_CODING_NONE;
566   }
567 
lab_image(int w,int h)568   void lab_image(int w, int h)
569   {
570     xsize = w; ysize = h;
571     bands = 3; interpretation = VIPS_INTERPRETATION_LAB;
572     coding = VIPS_CODING_NONE;
573   }
574 
cmyk_image(int w,int h)575   void cmyk_image(int w, int h)
576   {
577     xsize = w; ysize = h;
578     bands = 4; interpretation = VIPS_INTERPRETATION_CMYK;
579     coding = VIPS_CODING_NONE;
580   }
581 
multiband_image(int w,int h,int b)582   void multiband_image(int w, int h, int b)
583   {
584     xsize = w; ysize = h;
585     bands = b; interpretation = VIPS_INTERPRETATION_MULTIBAND;
586     coding = VIPS_CODING_NONE;
587   }
588 
print()589   virtual void print() {}
590   bool save( std::ostream& ostr, int level );
591 };
592 
593 
594 /* Base parameters for all transform operations
595  *
596  * A transform operation can only replace the input image,
597  * (ex. icc transform or image rescaling), so intensity or opacity maps are useless
598  */
599 class OpParTransform: public OpParBase
600 {
601 public:
has_intensity()602   virtual bool has_intensity() { return false; }
has_opacity()603   virtual bool has_opacity() { return false; }
604 };
605 
606 
607 
608 template<class T, bool has_imap>
609 class IntensityProc
610 {
611 public:
get_intensity(float & intensity,T * & p,int & x)612   float get_intensity(float& intensity, T*& p, int& x)
613   {
614     //std::cout<<"IntensityProc<T,true>::get_intensity(): "<<(intensity*p[x]/(FormatInfo<T>::MAX-FormatInfo<T>::MIN))<<std::endl;
615     return(intensity*(p[x++]+FormatInfo<T>::MIN)/(FormatInfo<T>::RANGE));
616   }
617 };
618 
619 
620 
621 template<class T>
622 class IntensityProc<T,false>
623 {
624 public:
get_intensity(float & intensity,T * &,int &)625   float get_intensity(float& intensity, T*& /*p*/, int& /*x*/)
626   {
627     //std::cout<<"IntensityProc<T,false>::get_intensity()"<<std::endl;
628     return(intensity);
629   }
630 };
631 
632 
633 
634 template<typename T, colorspace_t colorspace, int CHMIN, int CHMAX, bool has_omap>
635 class BlendBase
636 {
637 public:
638   T* pmap;
init_line(VipsRegion * omap,int left,int top)639   void init_line(VipsRegion* omap, int left, int top) { pmap = (T*)VIPS_REGION_ADDR( omap, left, top ); }
640 };
641 
642 
643 template<typename T, colorspace_t colorspace, int CHMIN, int CHMAX >
644 class BlendBase<T, colorspace, CHMIN, CHMAX, false>
645 {
646 public:
647   T* pmap;
init_line(VipsRegion *,int,int)648   void init_line(VipsRegion* /*omap*/, int /*left*/, int /*top*/) { }
649 };
650 
651 
652 
653 
654 #include "blend_passthrough.hh"
655 #include "blend_normal.hh"
656 #include "blend_add.hh"
657 #include "blend_subtract.hh"
658 #include "blend_grain_extract.hh"
659 #include "blend_grain_merge.hh"
660 #include "blend_multiply.hh"
661 #include "blend_divide.hh"
662 #include "blend_screen.hh"
663 #include "blend_lighten.hh"
664 #include "blend_darken.hh"
665 #include "blend_overlay.hh"
666 #include "blend_soft_light.hh"
667 #include "blend_hard_light.hh"
668 #include "blend_vivid_light.hh"
669 #include "blend_luminosity.hh"
670 #include "blend_luminance.hh"
671 #include "blend_color.hh"
672 #include "blend_exclusion.hh"
673 #include "blend_lch.hh"
674 
675 int vips_copy_metadata( VipsImage* in, VipsImage* out );
676 
677 void print_embedded_profile( VipsImage* img );
678 };
679 
680 
681 //int vips_pflayer( VipsImage **in, VipsImage* imap, VipsImage* omap, VipsImage **out, int n,
682 //	      PF::OperationBase* op, ... );
683 
684 
685 #endif
686