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