1 /*
2  */
3 
4 /*
5 
6     Copyright (C) 2014 Ferrero Andrea
7 
8     This program is free software: you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation, either version 3 of the License, or
11     (at your option) any later version.
12 
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16     GNU General Public License for more details.
17 
18     You should have received a copy of the GNU General Public License
19     along with this program. If not, see <http://www.gnu.org/licenses/>.
20 
21 
22  */
23 
24 /*
25 
26     These files are distributed with PhotoFlow - http://aferrero2707.github.io/PhotoFlow/
27 
28  */
29 
30 #ifndef PF_DRAW_H
31 #define PF_DRAW_H
32 
33 #include <string.h>
34 #include <iostream>
35 
36 #include "../base/array2d.hh"
37 #include "../base/format_info.hh"
38 #include "../base/operation.hh"
39 #include "../base/processor.hh"
40 #include "../base/rawbuffer.hh"
41 
42 //#include "diskbuffer.hh"
43 //#include "blender.hh"
44 
45 namespace PF
46 {
47 
48   struct RGBColor
49   {
50     float r, g, b;
51 
RGBColorPF::RGBColor52     RGBColor(): r(0), g(0), b(0) {}
RGBColorPF::RGBColor53     RGBColor( float _r, float _g, float _b ): r(_r), g(_g), b(_b) {}
RGBColorPF::RGBColor54     RGBColor( const RGBColor& color ):
55       r( color.r ), g( color.g ), b( color.b )
56     {
57     }
operator =PF::RGBColor58     RGBColor & operator=(const RGBColor &c)
59     {
60       r = c.r;
61       g = c.g;
62       b = c.b;
63       return(*this);
64     }
65   };
66 
operator ==(const RGBColor & lhs,const RGBColor & rhs)67   inline bool operator==(const RGBColor& lhs, const RGBColor& rhs)
68   {
69     return( (lhs.r==rhs.r) && (lhs.g==rhs.g) && (lhs.b==rhs.b) );
70   }
71 
operator !=(const RGBColor & lhs,const RGBColor & rhs)72   inline bool operator!=(const RGBColor& lhs, const RGBColor& rhs)
73   {
74     return( !(lhs == rhs) );
75   }
76 
operator >>(std::istream & str,RGBColor & color)77   inline std::istream& operator >>( std::istream& str, RGBColor& color )
78   {
79     str>>color.r>>color.g>>color.b;
80     return str;
81   }
82 
operator <<(std::ostream & str,const RGBColor & color)83   inline std::ostream& operator <<( std::ostream& str, const RGBColor& color )
84   {
85     str<<color.r<<" "<<color.g<<" "<<color.b<<" ";
86     return str;
87   }
88 
89   template<> inline
set_gobject_property(gpointer object,const std::string name,const RGBColor & value)90   void set_gobject_property<RGBColor>(gpointer object, const std::string name, const RGBColor& value)
91   {
92     //g_object_set( object, name.c_str(), value, NULL );
93   }
94 
95 
96 
97   class DrawPar: public OpParBase
98   {
99     Property<float> pen_grey, pen_R, pen_G, pen_B, pen_L, pen_a, pen_b, pen_C, pen_M, pen_Y, pen_K;
100     Property<float> bgd_grey, bgd_R, bgd_G, bgd_B, bgd_L, bgd_a, bgd_b, bgd_C, bgd_M, bgd_Y, bgd_K;
101     Property<RGBColor> pen_color;
102     Property<RGBColor> bgd_color;
103     Property<bool> bgd_transparent;
104     Property<int> pen_size;
105     Property<float> pen_opacity;
106     Property<float> pen_smoothness;
107     Property< std::list< Stroke<Pencil> > > strokes;
108 
109 		ProcessorBase* diskbuf;
110     RawBuffer* rawbuf;
111 
112     unsigned int scale_factor;
113 
114     Pencil pen;
115 
116   public:
117     DrawPar();
118     ~DrawPar();
119 
get_pen_grey()120     Property<float>& get_pen_grey() { return pen_grey; }
get_pen_R()121     Property<float>& get_pen_R() { return pen_R; }
get_pen_G()122     Property<float>& get_pen_G() { return pen_G; }
get_pen_B()123     Property<float>& get_pen_B() { return pen_B; }
get_pen_L()124     Property<float>& get_pen_L() { return pen_L; }
get_pen_a()125     Property<float>& get_pen_a() { return pen_a; }
get_pen_b()126     Property<float>& get_pen_b() { return pen_a; }
get_pen_C()127     Property<float>& get_pen_C() { return pen_C; }
get_pen_M()128     Property<float>& get_pen_M() { return pen_M; }
get_pen_Y()129     Property<float>& get_pen_Y() { return pen_Y; }
get_pen_K()130     Property<float>& get_pen_K() { return pen_K; }
131 
get_bgd_grey()132     Property<float>& get_bgd_grey() { return bgd_grey; }
get_bgd_R()133     Property<float>& get_bgd_R() { return bgd_R; }
get_bgd_G()134     Property<float>& get_bgd_G() { return bgd_G; }
get_bgd_B()135     Property<float>& get_bgd_B() { return bgd_B; }
get_bgd_L()136     Property<float>& get_bgd_L() { return bgd_L; }
get_bgd_a()137     Property<float>& get_bgd_a() { return bgd_a; }
get_bgd_b()138     Property<float>& get_bgd_b() { return bgd_a; }
get_bgd_C()139     Property<float>& get_bgd_C() { return bgd_C; }
get_bgd_M()140     Property<float>& get_bgd_M() { return bgd_M; }
get_bgd_Y()141     Property<float>& get_bgd_Y() { return bgd_Y; }
get_bgd_K()142     Property<float>& get_bgd_K() { return bgd_K; }
143 
get_pen_color()144     Property<RGBColor>& get_pen_color() { return pen_color; }
get_bgd_color()145     Property<RGBColor>& get_bgd_color() { return bgd_color; }
get_bgd_transparent()146     Property<bool>& get_bgd_transparent() { return bgd_transparent; }
147 
has_intensity()148     bool has_intensity() { return false; }
needs_input()149     bool needs_input() { return false; }
150 
get_pen()151     Pencil& get_pen() { return pen; }
152 
get_rawbuf()153     RawBuffer* get_rawbuf() { return rawbuf; }
154 
155     void init_buffer( unsigned int level );
156 
get_scale_factor()157     unsigned int get_scale_factor() { return scale_factor; }
158 
159     VipsImage* build(std::vector<VipsImage*>& in, int first,
160 		     VipsImage* imap, VipsImage* omap,
161 		     unsigned int& level);
162 
start_stroke()163     void start_stroke()
164     {
165       start_stroke( pen_size.get(), pen_opacity.get(), pen_smoothness.get() );
166     }
167     void start_stroke( unsigned int pen_size, float opacity, float smoothness );
168     void end_stroke();
169 
get_strokes()170     Property< std::list< Stroke<Pencil> > >& get_strokes() { return strokes; }
171 
172     void draw_point( int x, int y, VipsRect& update );
173   };
174 
175 
176 
177   template < OP_TEMPLATE_DEF >
178   class DrawProc
179   {
180   public:
render(VipsRegion ** ireg,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * oreg,DrawPar * par)181     void render(VipsRegion** ireg, int n, int in_first,
182                 VipsRegion* imap, VipsRegion* omap,
183                 VipsRegion* oreg, DrawPar* par)
184     {
185       //std::cout<<"DrawProc::render() called"<<std::endl;
186       DrawPar* opar = par;//dynamic_cast<DrawPar*>(par);
187       if( !opar ) return;
188       std::list< Stroke<Pencil> >& strokes = opar->get_strokes().get();
189       VipsRect *r = &oreg->valid;
190       //std::cout<<"nbands: "<<oreg->im->Bands<<std::endl;
191       //std::cout<<"r->left="<<r->left<<"  r->top="<<r->top
192       //         <<"  r->width="<<r->width<<"  r->height="<<r->height<<std::endl;
193       int line_size = r->width * oreg->im->Bands; //layer->in_all[0]->Bands;
194       //int width = r->width;
195       //int height = r->height;
196 
197       //T* p;
198       T* pin;
199       T* pout;
200       T* ptemp;
201       int x, x0, y, y0, ch, row1, row2;
202       int point_clip_right, point_clip_bottom;
203 
204       T** temp = new T*[r->height];
205       for( y = 0; y < r->height; y++) temp[y] = new T[line_size];
206 
207       pout = (T*)VIPS_REGION_ADDR( oreg, r->left, r->top );
208       //std::cout<<"pout="<<(void*)pout<<std::endl;
209 
210       T val[16];
211       //std::cout<<"DrawProc: bgd color: "<<opar->get_bgd_color().get().r<<" "
212       //    <<opar->get_bgd_color().get().g<<" "<<opar->get_bgd_color().get().b<<std::endl;
213       val[0] = (T)(opar->get_bgd_color().get().r*FormatInfo<T>::RANGE + FormatInfo<T>::MIN);
214       val[1] = (T)(opar->get_bgd_color().get().g*FormatInfo<T>::RANGE + FormatInfo<T>::MIN);
215       val[2] = (T)(opar->get_bgd_color().get().b*FormatInfo<T>::RANGE + FormatInfo<T>::MIN);
216       //val[0] = (T)(1*FormatInfo<T>::RANGE + FormatInfo<T>::MIN);
217       //val[1] = (T)(0*FormatInfo<T>::RANGE + FormatInfo<T>::MIN);
218       //val[2] = (T)(0*FormatInfo<T>::RANGE + FormatInfo<T>::MIN);
219 
220       if( opar->get_bgd_transparent().get() ) {
221         for( y = 0; y < r->height; y++ ) {
222           //p = (T*)VIPS_REGION_ADDR( ireg[in_first], point_clip.left, point_clip.top + y );
223           pin = (T*)VIPS_REGION_ADDR( ireg[in_first], r->left, r->top + y );
224           pout = (T*)VIPS_REGION_ADDR( oreg, r->left, r->top + y );
225           for( x = 0; x < line_size; x += oreg->im->Bands ) {
226             for( ch = 0; ch < oreg->im->Bands; ch++ ) {
227               pout[x+ch] = pin[x+ch];
228             }
229           }
230         }
231       } else {
232         for( y = 0; y < r->height; y++ ) {
233           pout = (T*)VIPS_REGION_ADDR( oreg, r->left, r->top + y );
234           for( x = 0; x < line_size; x += oreg->im->Bands ) {
235             for( ch = 0; ch < oreg->im->Bands; ch++ ) {
236               pout[x+ch] = val[ch];
237             }
238           }
239         }
240       }
241 
242       PF::Array2D<float> opacity_max;
243       opacity_max.Init( r->width, r->height, r->top, r->left );
244 
245       std::list< Stroke<Pencil> >::iterator si;
246       std::list< std::pair<int, int> >::iterator pi;
247       VipsRect point_area;
248       VipsRect point_clip;
249       VipsRect stroke_area;
250       VipsRect stroke_clip;
251       //std::cout<<"DrawProc::render(): strokes.size()="<<strokes.size()<<std::endl;
252       int sn = 0;
253       for( si = strokes.begin(); si != strokes.end(); ++si, sn++ ) {
254         //std::cout<<"stroke area: "<<si->get_area().width<<","<<si->get_area().height
255         //    <<"+"<<si->get_area().left<<"+"<<si->get_area().top<<std::endl;
256         stroke_area.left = si->get_area().left/static_cast<int>(opar->get_scale_factor());
257         stroke_area.top = si->get_area().top/static_cast<int>(opar->get_scale_factor());
258         stroke_area.width = si->get_area().width/static_cast<int>(opar->get_scale_factor());
259         stroke_area.height = si->get_area().height/static_cast<int>(opar->get_scale_factor());
260         //std::cout<<"stroke area: "<<stroke_area.width<<","<<stroke_area.height
261         //    <<"+"<<stroke_area.left<<"+"<<stroke_area.top<<std::endl;
262         vips_rect_intersectrect( r, &stroke_area, &stroke_clip );
263         //std::cout<<"stroke clip: "<<stroke_clip.width<<","<<stroke_clip.height
264         //    <<"+"<<stroke_clip.left<<"+"<<stroke_clip.top<<std::endl;
265         if( (stroke_clip.width<1) || (stroke_clip.height<1) ) continue;
266         // copy current region to temp buffer
267         if( temp ) {
268           for( y = 0; y < r->height; y++ ) {
269             //p = (T*)VIPS_REGION_ADDR( ireg[in_first], point_clip.left, point_clip.top + y );
270             pout = (T*)VIPS_REGION_ADDR( oreg, r->left, r->top + y );
271             memcpy( temp[y], pout, line_size*sizeof(T) );
272           }
273         }
274 
275         Pencil& pen = si->get_pen();
276         //std::cout<<"  pen color: "<<pen.get_channel(0)<<std::endl;
277         int pen_size = (int)(pen.get_size()/opar->get_scale_factor());
278         int pen_size2 = pen_size*pen_size;
279         for( ch = 0; ch < oreg->im->Bands; ch++ ) {
280           val[ch] = (T)(pen.get_channel(ch)*FormatInfo<T>::RANGE + FormatInfo<T>::MIN);
281         }
282 
283         for( int ic = 0; ic < r->width; ic++ ) {
284           for( int ir = 0; ir < r->height; ir++ ) {
285             opacity_max.GetLocal(ir, ic) = 0;
286           }
287         }
288 
289         point_area.width = point_area.height = pen_size*2 + 1;
290         PF::PencilMask* resized_mask = NULL;
291         PF::PencilMask* mask = &(pen.get_mask());
292         if( pen_size != pen.get_size() ) {
293           resized_mask = new PF::PencilMask;
294           resized_mask->init( point_area.width, pen.get_opacity(), pen.get_smoothness() );
295           mask = resized_mask;
296         }
297 
298         std::list< std::pair<int, int> >& points = si->get_points();
299         //std::cout<<"DrawProc::render(): points.size()="<<points.size()<<std::endl;
300         int pn = 0;
301         for( pi = points.begin(); pi != points.end(); ++pi, pn++ ) {
302           //std::cout<<"Drawing point "<<pi->first<<","<<pi->second<<std::endl;
303           point_area.left = pi->first/static_cast<int>(opar->get_scale_factor()) - pen_size;
304           point_area.top = pi->second/static_cast<int>(opar->get_scale_factor()) - pen_size;
305           //std::cout<<"Point area: "<<point_area.width<<","<<point_area.height
306           //    <<"+"<<point_area.left<<"+"<<point_area.top<<std::endl;
307           vips_rect_intersectrect( r, &point_area, &point_clip );
308           //std::cout<<"Point clip: "<<point_clip.width<<","<<point_clip.height
309           //    <<"+"<<point_clip.left<<"+"<<point_clip.top<<std::endl;
310           if( (point_clip.width<1) || (point_clip.height<1) ) continue;
311           point_clip_right = point_clip.left + point_clip.width - 1;
312           point_clip_bottom = point_clip.top + point_clip.height - 1;
313 
314           x0 = pi->first/opar->get_scale_factor();
315           y0 = pi->second/opar->get_scale_factor();
316           for( y = 0; y < point_area.height; y++ ) {
317             int row = y + point_area.top;
318             //std::cout<<"  row="<<row<<std::endl;
319             if( row < point_clip.top ) continue;
320             if( row > point_clip_bottom ) break;
321             pout = (T*)VIPS_REGION_ADDR( oreg, point_area.left, row );
322             ptemp = &(temp[point_area.top-r->top+y][(point_area.left-r->left)*oreg->im->Bands]);
323             for( x = 0; x < point_area.width; x++, pout += oreg->im->Bands, ptemp += oreg->im->Bands ) {
324               int col = x+point_area.left;
325               float mval = mask->get( x, y );
326               //std::cout<<"    col="<<col<<std::endl;
327               //mval = mval/2+0.5;
328               //std::cout<<"    opacity_max.Get("<<row<<", "<<col<<")="<<opacity_max.Get(row, col)<<"    mval="<<mval<<std::endl;
329               if( (col < point_clip.left) ) continue;
330               if( (col > point_clip_right) ) break;
331               if( mval < 0.00001 ) continue;
332               if( mval <= opacity_max.Get(row, col) ) continue;
333               opacity_max.Get(row, col) = mval;
334               for( ch = 0; ch < oreg->im->Bands; ch++ ) {
335                 float out = mval*val[ch] + (1.0f-mval)*pout[ch];
336                 //std::cout<<"    pout["<<ch<<"]= "<<pout[ch]<<"  out["<<ch<<"]="<<out<<std::endl;
337                 //if( pn == 0 )
338                   ptemp[ch] = static_cast<T>(out);
339               }
340             }
341             //if( pn > 0 && y > 1 ) break;
342           }
343         }
344 
345         // copy temp buffer back to current region
346         if( temp ) {
347           for( y = 0; y < r->height; y++ ) {
348             //p = (T*)VIPS_REGION_ADDR( ireg[in_first], point_clip.left, point_clip.top + y );
349             pout = (T*)VIPS_REGION_ADDR( oreg, r->left, r->top + y );
350             memcpy( pout, temp[y], line_size*sizeof(T) );
351           }
352         }
353       }
354 
355       if( temp ) {
356         for( y = 0; y < r->height; y++) delete[] temp[y];
357         delete[] temp;
358       }
359     }
360   };
361 
362 
363   ProcessorBase* new_draw();
364 }
365 
366 #endif
367 
368 
369 
370