Edit comment text.
[synfig.git] / ETL / trunk / ETL / _surface.h
1 /*! ========================================================================
2 ** Extended Template and Library
3 ** Surface Class Implementation
4 ** $Id$
5 **
6 ** Copyright (c) 2002 Robert B. Quattlebaum Jr.
7 ** Copyright (c) 2008 Chris Moore
8 **
9 ** This package is free software; you can redistribute it and/or
10 ** modify it under the terms of the GNU General Public License as
11 ** published by the Free Software Foundation; either version 2 of
12 ** the License, or (at your option) any later version.
13 **
14 ** This package 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 GNU
17 ** General Public License for more details.
18 **
19 ** === N O T E S ===========================================================
20 **
21 ** This is an internal header file, included by other ETL headers.
22 ** You should not attempt to use it directly.
23 **
24 ** ========================================================================= */
25
26 /* === S T A R T =========================================================== */
27
28 #ifndef __ETL__SURFACE_H
29 #define __ETL__SURFACE_H
30
31 /* === H E A D E R S ======================================================= */
32
33 #include "_pen.h"
34 #include "_misc.h"
35 #include <algorithm>
36 #include <cstring>
37
38 /* === M A C R O S ========================================================= */
39
40 /* === C L A S S E S & S T R U C T S ======================================= */
41
42 _ETL_BEGIN_NAMESPACE
43
44 template <typename T, typename AT>
45 class value_prep
46 {
47 public:
48         typedef T value_type;
49         typedef AT accumulator_type;
50
51         accumulator_type cook(const value_type& x)const { return (accumulator_type)x; }
52         value_type uncook(const accumulator_type& x)const { return (value_type)x; }
53 };
54
55 template <typename T, typename AT=T, class VP=value_prep<T,AT> >
56 class surface
57 {
58 public:
59         typedef T value_type;
60         typedef AT accumulator_type;
61         typedef value_type* pointer;
62         typedef const value_type* const_pointer;
63         typedef value_type& reference;
64         typedef generic_pen<value_type,accumulator_type> pen;
65         typedef generic_pen<const value_type,accumulator_type> const_pen;
66         typedef VP value_prep_type;
67
68         typedef alpha_pen<const_pen> const_alpha_pen;
69         typedef alpha_pen<pen> non_const_alpha_pen;
70
71         typedef typename pen::difference_type size_type;
72         typedef typename pen::difference_type difference_type;
73
74         typedef typename pen::iterator_x iterator_x;
75         typedef typename pen::iterator_y iterator_y;
76         typedef typename pen::const_iterator_x const_iterator_x;
77         typedef typename pen::const_iterator_y const_iterator_y;
78
79 private:
80         value_type *data_;
81         value_type *zero_pos_;
82         typename difference_type::value_type pitch_;
83         int w_, h_;
84         bool deletable_;
85
86         value_prep_type cooker_;
87
88         void swap(const surface &x)
89         {
90                 std::swap(data_,x.data_);
91                 std::swap(zero_pos_,x.zero_pos_);
92                 std::swap(pitch_,x.pitch_);
93                 std::swap(w_,x.w_);
94                 std::swap(h_,x.h_);
95                 std::swap(deletable_,x.deletable_);
96         }
97
98 public:
99         surface():
100                 data_(0),
101                 zero_pos_(data_),
102                 pitch_(0),
103                 w_(0),h_(0),
104                 deletable_(false) { }
105
106         surface(value_type* data, int w, int h, bool deletable=false):
107                 data_(data),
108                 zero_pos_(data),
109                 pitch_(sizeof(value_type)*w),
110                 w_(w),h_(h),
111                 deletable_(deletable) { }
112
113         surface(const typename size_type::value_type &w, const typename size_type::value_type &h):
114                 data_(new value_type[w*h]),
115                 zero_pos_(data_),
116                 pitch_(sizeof(value_type)*w),
117                 w_(w),h_(h),
118                 deletable_(true) { }
119
120         surface(const size_type &s):
121                 data_(new value_type[s.x*s.y]),
122                 zero_pos_(data_),
123                 pitch_(sizeof(value_type)*s.x),
124                 w_(s.x),h_(s.y),
125                 deletable_(true) { }
126
127         template <typename _pen>
128         surface(const _pen &_begin, const _pen &_end)
129         {
130                 typename _pen::difference_type size=_end-_begin;
131
132                 data_=new value_type[size.x*size.y];
133                 w_=size.x;
134                 h_=size.y;
135                 zero_pos_=data_;
136                 pitch_=sizeof(value_type)*w_;
137                 deletable_=true;
138
139                 int x,y;
140
141                 for(y=0;y<h_;y++)
142                         for(x=0;x<w_;x++)
143                                 (*this)[y][x]=_begin.get_value_at(x,y);
144         }
145
146         surface(const surface &s):
147                 data_(s.data_?new value_type[s.w_*s.h_]:0),
148                 zero_pos_(data_+(s.zero_pos_-s.data_)),
149                 pitch_(s.pitch_),
150                 w_(s.w_),
151                 h_(s.h_),
152                 deletable_(s.data_?true:false)
153         {
154                 assert(&s);
155                 if(s.data_)
156                 {
157                         assert(data_);
158                         memcpy(data_,s.data_,abs(pitch_)*h_);
159                 }
160         }
161
162 public:
163         ~surface()
164         {
165                 if(deletable_)
166                         delete [] data_;
167         }
168
169         size_type
170         size()const
171         { return size_type(w_,h_); }
172
173         typename size_type::value_type get_pitch()const { return pitch_; }
174         typename size_type::value_type get_w()const { return w_; }
175         typename size_type::value_type get_h()const { return h_; }
176
177         const surface &mirror(const surface &rhs)
178         {
179                 if(deletable_)delete [] data_;
180
181                 data_=rhs.data_;
182                 zero_pos_=rhs.zero_pos_;
183                 pitch_=rhs.pitch_;
184                 w_=rhs.w_;
185                 h_=rhs.h_;
186                 deletable_=false;
187
188                 return *this;
189         }
190
191         const surface &operator=(const surface &rhs)
192         {
193                 set_wh(rhs.w_,rhs.h_);
194                 zero_pos_=data_+(rhs.zero_pos_-rhs.data_);
195                 pitch_=rhs.pitch_;
196                 deletable_=true;
197
198                 memcpy(data_,rhs.data_,pitch_*h_);
199
200                 return *this;
201         }
202
203         void
204         set_wh(typename size_type::value_type w, typename size_type::value_type h)
205         {
206                 if(data_)
207                 {
208                         if(w==w_ && h==h_ && deletable_)
209                                 return;
210                         if(deletable_)
211                                 delete [] data_;
212                 }
213
214                 w_=w;
215                 h_=h;
216                 pitch_=sizeof(value_type)*w_;
217                 zero_pos_=data_=new value_type[h_*w_];
218                 deletable_=true;
219         }
220
221         void
222         fill(value_type v, int x, int y, int w, int h)
223         {
224                 assert(data_);
225                 if(w<=0 || h<=0)return;
226                 int i;
227                 pen PEN(get_pen(x,y));
228                 PEN.set_value(v);
229                 for(i=0;i<h;i++,PEN.inc_y(),PEN.dec_x(w))
230                         PEN.put_hline(w);
231         }
232
233         template <class _pen> void
234         fill(value_type v, _pen& PEN, int w, int h)
235         {
236                 assert(data_);
237                 if(w<=0 || h<=0)return;
238                 int y;
239                 PEN.set_value(v);
240                 for(y=0;y<h;y++,PEN.inc_y(),PEN.dec_x(w))
241                         PEN.put_hline(w);
242         }
243
244         void
245         fill(value_type v)
246         {
247                 assert(data_);
248                 int y;
249                 pen pen_=begin();
250                 pen_.set_value(v);
251                 for(y=0;y<h_;y++,pen_.inc_y(),pen_.dec_x(w_))
252                         pen_.put_hline(w_);
253         }
254
255         template <class _pen> void blit_to(_pen &pen)
256         { return blit_to(pen,0,0, get_w(),get_h()); }
257
258         template <class _pen> void
259         blit_to(_pen &DEST_PEN,
260                         int x, int y, int w, int h) //src param
261         {
262                 if(x>=w_ || y>=h_)
263                         return;
264
265                 //clip source origin
266                 if(x<0)
267                 {
268                         w+=x;   //decrease
269                         x=0;
270                 }
271
272                 if(y<0)
273                 {
274                         h+=y;   //decrease
275                         y=0;
276                 }
277
278                 //clip width against dest width
279                 w = std::min((long)w,(long)(DEST_PEN.end_x()-DEST_PEN.x()));
280                 h = std::min((long)h,(long)(DEST_PEN.end_y()-DEST_PEN.y()));
281
282                 //clip width against src width
283                 w = std::min(w,w_-x);
284                 h = std::min(h,h_-y);
285
286                 if(w<=0 || h<=0)
287                         return;
288
289                 pen SOURCE_PEN(get_pen(x,y));
290
291                 for(; h>0; h--,DEST_PEN.inc_y(),SOURCE_PEN.inc_y())
292                 {
293                         int i;
294                         for(i=0; i<w; i++,DEST_PEN.inc_x(),SOURCE_PEN.inc_x())
295                         {
296                                 DEST_PEN.put_value(SOURCE_PEN.get_value());
297                         }
298                         DEST_PEN.dec_x(w);
299                         SOURCE_PEN.dec_x(w);
300                 }
301         }
302
303         void
304         clear()
305         {
306                 assert(data_);
307                 if(pitch_==(signed int)sizeof(value_type)*w_)
308                         memset(data_,0,h_*pitch_);
309                 else
310                         fill(value_type());
311         }
312
313         iterator_x
314         operator[](const int &y)
315         { assert(data_); return (pointer)(((char*)zero_pos_)+y*pitch_); }
316
317         const_iterator_x
318         operator[](const int &y)const
319         { assert(data_); return (const_pointer)(((const char*)zero_pos_)+y*pitch_); }
320
321         void
322         flip_v()
323         {
324                 assert(data_);
325
326                 zero_pos_=(pointer)(((char*)zero_pos_)+pitch_*h_);
327
328                 pitch_=-pitch_;
329         }
330
331         bool is_valid()const
332         {
333                 return  data_!=0
334                         &&      zero_pos_!=0
335                         &&      w_>0
336                         &&      h_>0
337                         &&      pitch_!=0
338                 ;
339         }
340
341         operator bool()const { return is_valid(); }
342
343         pen begin() { assert(data_); return pen(data_,w_,h_,pitch_); }
344         pen get_pen(int x, int y) { assert(data_); return begin().move(x,y); }
345         pen end() { assert(data_); return get_pen(w_,h_); }
346
347         const_pen begin()const { assert(data_); return const_pen(data_,w_,h_,pitch_); }
348         const_pen get_pen(int x, int y)const { assert(data_); return begin().move(x,y); }
349         const_pen end()const { assert(data_); return get_pen(w_,h_); }
350
351         //! Linear sample
352         value_type linear_sample(const float x, const float y)const
353         {
354                 int u(floor_to_int(x)), v(floor_to_int(y));
355                 float a, b;
356                 static const float epsilon(1.0e-6);
357
358                 if(x<0.0f)u=0,a=0.0f;
359                 else if(x>w_-1)u=w_-1,a=0.0f;
360                 else a=x-u;
361
362                 if(y<0.0f)v=0,b=0.0f;
363                 else if(y>h_-1)v=h_-1,b=0.0f;
364                 else b=y-v;
365
366                 const float
367                         c(1.0f-a), d(1.0f-b),
368                         e(a*d),f(c*b),g(a*b);
369
370                 accumulator_type ret(cooker_.cook((*this)[v][u])*(c*d));
371                 if(e>=epsilon)ret+=cooker_.cook((*this)[v][u+1])*e;
372                 if(f>=epsilon)ret+=cooker_.cook((*this)[v+1][u])*f;
373                 if(g>=epsilon)ret+=cooker_.cook((*this)[v+1][u+1])*g;
374                 return cooker_.uncook(ret);
375         }
376
377         //! Cosine sample
378         value_type cosine_sample(const float x, const float y)const
379         {
380                 int u(floor_to_int(x)), v(floor_to_int(y));
381                 float a, b;
382                 static const float epsilon(1.0e-6);
383
384                 if(x<0.0f)u=0,a=0.0f;
385                 else if(x>w_-1)u=w_-1,a=0.0f;
386                 else a=x-u;
387
388                 if(y<0.0f)v=0,b=0.0f;
389                 else if(y>h_-1)v=h_-1,b=0.0f;
390                 else b=y-v;
391
392                 a=(1.0f-cos(a*3.1415927f))*0.5f;
393                 b=(1.0f-cos(b*3.1415927f))*0.5f;
394
395                 const float
396                         c(1.0f-a), d(1.0f-b),
397                         e(a*d),f(c*b),g(a*b);
398
399                 accumulator_type ret(cooker_.cook((*this)[v][u])*(c*d));
400                 if(e>=epsilon)ret+=cooker_.cook((*this)[v][u+1])*e;
401                 if(f>=epsilon)ret+=cooker_.cook((*this)[v+1][u])*f;
402                 if(g>=epsilon)ret+=cooker_.cook((*this)[v+1][u+1])*g;
403
404                 return cooker_.uncook(ret);
405         }
406
407         //! Cubic sample
408         value_type cubic_sample(float x, float y)const
409         {
410                 #if 0
411         #define P(x)    (((x)>=0)?((x)*(x)*(x)):0.0f)
412         #define R(x)    ( P(x+2) - 4.0f*P(x+1) + 6.0f*P(x) - 4.0f*P(x-1) )*(1.0f/6.0f)
413         #define F(i,j)  (cooker_.cook((*this)[max(min(j+v,h_-1),0)][max(min(i+u,w_-1),0)])*(R((i)-a)*R(b-(j))))
414         #define Z(i,j) ret+=F(i,j)
415         #define X(i,j)  // placeholder... To make box more symmetric
416
417                 int u(floor_to_int(x)), v(floor_to_int(y));
418                 float a, b;
419
420                 // Clamp X
421                 if(x<0.0f)u=0,a=0.0f;
422                 else if(u>w_-1)u=w_-1,a=0.0f;
423                 else a=x-u;
424
425                 // Clamp Y
426                 if(y<0.0f)v=0,b=0.0f;
427                 else if(v>h_-1)v=h_-1,b=0.0f;
428                 else b=y-v;
429
430                 // Interpolate
431                 accumulator_type ret(F(0,0));
432                 Z(-1,-1); Z(-1, 0); Z(-1, 1); Z(-1, 2);
433                 Z( 0,-1); X( 0, 0); Z( 0, 1); Z( 0, 2);
434                 Z( 1,-1); Z( 1, 0); Z( 1, 1); Z( 1, 2);
435                 Z( 2,-1); Z( 2, 0); Z( 2, 1); Z( 2, 2);
436
437                 return cooker_.uncook(ret);
438
439         #undef X
440         #undef Z
441         #undef F
442         #undef P
443         #undef R
444                 #else
445
446                 #define f(j,i)  (cooker_.cook((*this)[j][i]))
447                 //Using catmull rom interpolation because it doesn't blur at all
448                 //bezier curve with intermediate ctrl pts: 0.5/3(p(i+1) - p(i-1)) and similar
449                 accumulator_type xfa [4];
450
451                 //precalculate indices (all clamped) and offset
452                 const int xi = x > 0 ? (x < w_ ? (int)floor(x) : w_-1) : 0;
453                 const int xa[] = {std::max(0,xi-1),xi,std::min(w_-1,xi+1),std::min(w_-1,xi+2)};
454
455                 const int yi = y > 0 ? (y < h_ ? (int)floor(y) : h_-1) : 0;
456                 const int ya[] = {std::max(0,yi-1),yi,std::min(h_-1,yi+1),std::min(h_-1,yi+2)};
457
458                 const float xf = x-xi;
459                 const float yf = y-yi;
460
461                 //figure polynomials for each point
462                 const float txf[] =
463                 {
464                         0.5*xf*(xf*(xf*(-1) + 2) - 1),  //-t + 2t^2 -t^3
465                         0.5*(xf*(xf*(3*xf - 5)) + 2),   //2 - 5t^2 + 3t^3
466                         0.5*xf*(xf*(-3*xf + 4) + 1),    //t + 4t^2 - 3t^3
467                         0.5*xf*xf*(xf-1)                                //-t^2 + t^3
468                 };
469
470                 const float tyf[] =
471                 {
472                         0.5*yf*(yf*(yf*(-1) + 2) - 1),  //-t + 2t^2 -t^3
473                         0.5*(yf*(yf*(3*yf - 5)) + 2),   //2 - 5t^2 + 3t^3
474                         0.5*yf*(yf*(-3*yf + 4) + 1),    //t + 4t^2 - 3t^3
475                         0.5*yf*yf*(yf-1)                                //-t^2 + t^3
476                 };
477
478                 //evaluate polynomial for each row
479                 for(int i = 0; i < 4; ++i)
480                 {
481                         xfa[i] = f(ya[i],xa[0])*txf[0] + f(ya[i],xa[1])*txf[1] + f(ya[i],xa[2])*txf[2] + f(ya[i],xa[3])*txf[3];
482                 }
483
484                 //return the cumulative column evaluation
485                 return cooker_.uncook(xfa[0]*tyf[0] + xfa[1]*tyf[1] + xfa[2]*tyf[2] + xfa[3]*tyf[3]);
486 #undef f
487 #endif
488         }
489
490         value_type      sample_rect(float x0,float y0,float x1,float y1) const
491         {
492                 const surface &s = *this;
493
494                 //assumes it's clamped to the boundary of the image
495                 //force min max relationship for x0,x1 and y0,y1
496                 if(x0 > x1) std::swap(x0,x1);
497                 if(y0 > y1) std::swap(y0,y1);
498
499                 //local variable madness
500                 //all things that want to inter-operate should provide a default value constructor for = 0
501                 accumulator_type acum = 0;
502                 int xi=0,yi=0;
503
504                 int     xib=(int)floor(x0),
505                         xie=(int)floor(x1);
506
507                 int     yib=(int)floor(y0),
508                         yie=(int)floor(y1);
509
510                 //the weight for the pixel should remain the same...
511                 float weight = (y1-y0)*(x1-x0);
512                 assert(weight != 0);
513
514                 float ylast = y0, xlastb = x0;
515                 const_pen       pen_ = s.get_pen(xib,yib);
516
517                 for(yi = yib; yi < yie; ylast = ++yi, pen_.inc_y())
518                 {
519                         const float yweight = yi+1 - ylast;
520
521                         float xlast = xlastb;
522                         for(xi = xib; xi < xie; xlast = ++xi, pen_.inc_x())
523                         {
524                                 const float w = yweight*(xi+1 - xlast);
525                                 acum += cooker_.cook(pen_.get_value())*w;
526                         }
527
528                         //post... with next being fractional...
529                         const float w = yweight*(x1 - xlast);
530                         acum += cooker_.cook(pen_.get_value())*w;
531
532                         pen_.dec_x(xie-xib);
533                 }
534
535                 //post in y direction... must have all x...
536                 {
537                         const float yweight = y1 - ylast;
538
539                         float xlast = xlastb;
540                         for(xi = xib; xi < xie; xlast = ++xi)
541                         {
542                                 const float w = yweight*(xi+1 - xlast);
543
544                                 acum += cooker_.cook(pen_.get_value())*w;
545                         }
546
547                         //post... with next being fractional...
548                         const float w = yweight*(x1 - xlast);
549                         acum += cooker_.cook(pen_.get_value())*w;
550                 }
551
552                 acum *= 1/weight;
553                 return cooker_.uncook(acum);
554         }
555
556         value_type      sample_rect_clip(float x0,float y0,float x1,float y1) const
557         {
558                 const surface &s = *this;
559
560                 //assumes it's clamped to the boundary of the image
561                 //force min max relationship for x0,x1 and y0,y1
562                 if(x0 > x1) std::swap(x0,x1);
563                 if(y0 > y1) std::swap(y0,y1);
564
565                 //local variable madness
566                 //all things that want to inter-operate should provide a default value constructor for = 0
567                 accumulator_type acum = 0;
568                 int xi=0,yi=0;
569
570                 int     xib=(int)floor(x0),
571                         xie=(int)floor(x1);
572
573                 int     yib=(int)floor(y0),
574                         yie=(int)floor(y1);
575
576                 //the weight for the pixel should remain the same...
577                 float weight = (y1-y0)*(x1-x0);
578
579                 assert(weight != 0);
580
581                 //clip to the input region
582                 if(x0 >= s.get_w() || x1 <= 0) return acum;
583                 if(y0 >= s.get_h() || y1 <= 0) return acum;
584
585                 if(x0 < 0) { x0 = 0; xib = 0; }
586                 if(x1 >= s.get_w())
587                 {
588                         x1 = s.get_w(); //want to be just below the last pixel...
589                         xie = s.get_w()-1;
590                 }
591
592                 if(y0 < 0) { y0 = 0; yib = 0; }
593                 if(y1 >= s.get_h())
594                 {
595                         y1 = s.get_h(); //want to be just below the last pixel...
596                         yie = s.get_h()-1;
597                 }
598
599                 float ylast = y0, xlastb = x0;
600                 const_pen       pen = s.get_pen(xib,yib);
601
602                 for(yi = yib; yi < yie; ylast = ++yi, pen.inc_y())
603                 {
604                         const float yweight = yi+1 - ylast;
605
606                         float xlast = xlastb;
607                         for(xi = xib; xi < xie; xlast = ++xi, pen.inc_x())
608                         {
609                                 const float w = yweight*(xi+1 - xlast);
610                                 acum += cooker_.cook(pen.get_value())*w;
611                         }
612
613                         //post... with next being fractional...
614                         const float w = yweight*(x1 - xlast);
615                         acum += cooker_.cook(pen.get_value())*w;
616
617                         pen.dec_x(xie-xib);
618                 }
619
620                 //post in y direction... must have all x...
621                 {
622                         const float yweight = y1 - ylast;
623
624                         float xlast = xlastb;
625                         for(xi = xib; xi < xie; xlast = ++xi)
626                         {
627                                 const float w = yweight*(xi+1 - xlast);
628
629                                 acum += cooker_.cook(pen.get_value())*w;
630                         }
631
632                         //post... with next being fractional...
633                         const float w = yweight*(x1 - xlast);
634                         acum += cooker_.cook(pen.get_value())*w;
635                 }
636
637                 acum *= 1/weight;
638                 return cooker_.uncook(acum);
639         }
640 };
641
642 _ETL_END_NAMESPACE
643
644 /* === T Y P E D E F S ===================================================== */
645
646
647 /* === E N D =============================================================== */
648
649 #endif