- Main Page
- Related Pages
- Classes
- Files

# _surface.h

Go to the documentation of this file.

    00001
    00026 /* === S T A R T =========================================================== */
    00027
    00028 #ifndef __ETL__SURFACE_H
    00029 #define __ETL__SURFACE_H
    00030
    00031 /* === H E A D E R S ======================================================= */
    00032
    00033 #include "_pen.h"
    00034 #include "_misc.h"
    00035 #include <algorithm>
    00036 #include <cstring>
    00037
    00038 /* === M A C R O S ========================================================= */
    00039
    00040 /* === C L A S S E S & S T R U C T S ======================================= */
    00041
    00042 _ETL_BEGIN_NAMESPACE
    00043
    00044 template <typename T, typename AT>
    00045 class value_prep
    00046 {
    00047 public:
    00048     typedef T value_type;
    00049     typedef AT accumulator_type;
    00050
    00051     accumulator_type cook(const value_type& x)const { return (accumulator_type)x; }
    00052     value_type uncook(const accumulator_type& x)const { return (value_type)x; }
    00053 };
    00054
    00055 template <typename T, typename AT=T, class VP=value_prep<T,AT> >
    00056 class surface
    00057 {
    00058 public:
    00059     typedef T value_type;
    00060     typedef AT accumulator_type;
    00061     typedef value_type* pointer;
    00062     typedef const value_type* const_pointer;
    00063     typedef value_type& reference;
    00064     typedef generic_pen<value_type,accumulator_type> pen;
    00065     typedef generic_pen<const value_type,accumulator_type> const_pen;
    00066     typedef VP value_prep_type;
    00067
    00068     typedef alpha_pen<const_pen> const_alpha_pen;
    00069     typedef alpha_pen<pen> non_const_alpha_pen;
    00070
    00071     typedef typename pen::difference_type size_type;
    00072     typedef typename pen::difference_type difference_type;
    00073
    00074     typedef typename pen::iterator_x iterator_x;
    00075     typedef typename pen::iterator_y iterator_y;
    00076     typedef typename pen::const_iterator_x const_iterator_x;
    00077     typedef typename pen::const_iterator_y const_iterator_y;
    00078
    00079 private:
    00080     value_type *data_;
    00081     value_type *zero_pos_;
    00082     typename difference_type::value_type pitch_;
    00083     int w_, h_;
    00084     bool deletable_;
    00085
    00086     value_prep_type cooker_;
    00087
    00088     void swap(const surface &x)
    00089     {
    00090         std::swap(data_,x.data_);
    00091         std::swap(zero_pos_,x.zero_pos_);
    00092         std::swap(pitch_,x.pitch_);
    00093         std::swap(w_,x.w_);
    00094         std::swap(h_,x.h_);
    00095         std::swap(deletable_,x.deletable_);
    00096     }
    00097
    00098 public:
    00099     surface():
    00100         data_(0),
    00101         zero_pos_(data_),
    00102         pitch_(0),
    00103         w_(0),h_(0),
    00104         deletable_(false) { }
    00105
    00106     surface(value_type* data, int w, int h, bool deletable=false):
    00107         data_(data),
    00108         zero_pos_(data),
    00109         pitch_(sizeof(value_type)*w),
    00110         w_(w),h_(h),
    00111         deletable_(deletable) { }
    00112
    00113     surface(const typename size_type::value_type &w, const typename size_type::value_type &h):
    00114         data_(new value_type[w*h]),
    00115         zero_pos_(data_),
    00116         pitch_(sizeof(value_type)*w),
    00117         w_(w),h_(h),
    00118         deletable_(true) { }
    00119
    00120     surface(const size_type &s):
    00121         data_(new value_type[s.x*s.y]),
    00122         zero_pos_(data_),
    00123         pitch_(sizeof(value_type)*s.x),
    00124         w_(s.x),h_(s.y),
    00125         deletable_(true) { }
    00126
    00127     template <typename _pen>
    00128     surface(const _pen &_begin, const _pen &_end)
    00129     {
    00130         typename _pen::difference_type size=_end-_begin;
    00131
    00132         data_=new value_type[size.x*size.y];
    00133         w_=size.x;
    00134         h_=size.y;
    00135         zero_pos_=data_;
    00136         pitch_=sizeof(value_type)*w_;
    00137         deletable_=true;
    00138
    00139         int x,y;
    00140
    00141         for(y=0;y<h_;y++)
    00142             for(x=0;x<w_;x++)
    00143                 (*this)[y][x]=_begin.get_value_at(x,y);
    00144     }
    00145
    00146     surface(const surface &s):
    00147         data_(s.data_?new value_type[s.w_*s.h_]:0),
    00148         zero_pos_(data_+(s.zero_pos_-s.data_)),
    00149         pitch_(s.pitch_),
    00150         w_(s.w_),
    00151         h_(s.h_),
    00152         deletable_(s.data_?true:false)
    00153     {
    00154         assert(&s);
    00155         if(s.data_)
    00156         {
    00157             assert(data_);
    00158             memcpy(data_,s.data_,abs(pitch_)*h_);
    00159         }
    00160     }
    00161
    00162 public:
    00163     ~surface()
    00164     {
    00165         if(deletable_)
    00166             delete [] data_;
    00167     }
    00168
    00169     size_type
    00170     size()const
    00171     { return size_type(w_,h_); }
    00172
    00173     typename size_type::value_type get_pitch()const { return pitch_; }
    00174     typename size_type::value_type get_w()const { return w_; }
    00175     typename size_type::value_type get_h()const { return h_; }
    00176
    00177     const surface &mirror(const surface &rhs)
    00178     {
    00179         if(deletable_)delete [] data_;
    00180
    00181         data_=rhs.data_;
    00182         zero_pos_=rhs.zero_pos_;
    00183         pitch_=rhs.pitch_;
    00184         w_=rhs.w_;
    00185         h_=rhs.h_;
    00186         deletable_=false;
    00187
    00188         return *this;
    00189     }
    00190
    00191     const surface &operator=(const surface &rhs)
    00192     {
    00193         set_wh(rhs.w_,rhs.h_);
    00194         zero_pos_=data_+(rhs.zero_pos_-rhs.data_);
    00195         pitch_=rhs.pitch_;
    00196         deletable_=true;
    00197
    00198         memcpy(data_,rhs.data_,pitch_*h_);
    00199
    00200         return *this;
    00201     }
    00202
    00203     void
    00204     set_wh(typename size_type::value_type w, typename size_type::value_type h)
    00205     {
    00206         if(data_)
    00207         {
    00208             if(w==w_ && h==h_ && deletable_)
    00209                 return;
    00210             if(deletable_)
    00211                 delete [] data_;
    00212         }
    00213
    00214         w_=w;
    00215         h_=h;
    00216         pitch_=sizeof(value_type)*w_;
    00217         zero_pos_=data_=new value_type[h_*w_];
    00218         deletable_=true;
    00219     }
    00220
    00221     void
    00222     fill(value_type v, int x, int y, int w, int h)
    00223     {
    00224         assert(data_);
    00225         if(w<=0 || h<=0)return;
    00226         int i;
    00227         pen PEN(get_pen(x,y));
    00228         PEN.set_value(v);
    00229         for(i=0;i<h;i++,PEN.inc_y(),PEN.dec_x(w))
    00230             PEN.put_hline(w);
    00231     }
    00232
    00233     template <class _pen> void
    00234     fill(value_type v, _pen& PEN, int w, int h)
    00235     {
    00236         assert(data_);
    00237         if(w<=0 || h<=0)return;
    00238         int y;
    00239         PEN.set_value(v);
    00240         for(y=0;y<h;y++,PEN.inc_y(),PEN.dec_x(w))
    00241             PEN.put_hline(w);
    00242     }
    00243
    00244     void
    00245     fill(value_type v)
    00246     {
    00247         assert(data_);
    00248         int y;
    00249         pen pen_=begin();
    00250         pen_.set_value(v);
    00251         for(y=0;y<h_;y++,pen_.inc_y(),pen_.dec_x(w_))
    00252             pen_.put_hline(w_);
    00253     }
    00254
    00255     template <class _pen> void blit_to(_pen &pen)
    00256     { return blit_to(pen,0,0, get_w(),get_h()); }
    00257
    00258     template <class _pen> void
    00259     blit_to(_pen &DEST_PEN,
    00260             int x, int y, int w, int h) //src param
    00261     {
    00262         if(x>=w_ || y>=h_)
    00263             return;
    00264
    00265         //clip source origin
    00266         if(x<0)
    00267         {
    00268             w+=x;   //decrease
    00269             x=0;
    00270         }
    00271
    00272         if(y<0)
    00273         {
    00274             h+=y;   //decrease
    00275             y=0;
    00276         }
    00277
    00278         //clip width against dest width
    00279         w = std::min((long)w,(long)(DEST_PEN.end_x()-DEST_PEN.x()));
    00280         h = std::min((long)h,(long)(DEST_PEN.end_y()-DEST_PEN.y()));
    00281
    00282         //clip width against src width
    00283         w = std::min(w,w_-x);
    00284         h = std::min(h,h_-y);
    00285
    00286         if(w<=0 || h<=0)
    00287             return;
    00288
    00289         pen SOURCE_PEN(get_pen(x,y));
    00290
    00291         for(; h>0; h--,DEST_PEN.inc_y(),SOURCE_PEN.inc_y())
    00292         {
    00293             int i;
    00294             for(i=0; i<w; i++,DEST_PEN.inc_x(),SOURCE_PEN.inc_x())
    00295             {
    00296                 DEST_PEN.put_value(SOURCE_PEN.get_value());
    00297             }
    00298             DEST_PEN.dec_x(w);
    00299             SOURCE_PEN.dec_x(w);
    00300         }
    00301     }
    00302
    00303     void
    00304     clear()
    00305     {
    00306         assert(data_);
    00307         if(pitch_==(signed int)sizeof(value_type)*w_)
    00308             memset(data_,0,h_*pitch_);
    00309         else
    00310             fill(value_type());
    00311     }
    00312
    00313     iterator_x
    00314     operator[](const int &y)
    00315     { assert(data_); return (pointer)(((char*)zero_pos_)+y*pitch_); }
    00316
    00317     const_iterator_x
    00318     operator[](const int &y)const
    00319     { assert(data_); return (const_pointer)(((const char*)zero_pos_)+y*pitch_); }
    00320
    00321     void
    00322     flip_v()
    00323     {
    00324         assert(data_);
    00325
    00326         zero_pos_=(pointer)(((char*)zero_pos_)+pitch_*h_);
    00327
    00328         pitch_=-pitch_;
    00329     }
    00330
    00331     bool is_valid()const
    00332     {
    00333         return  data_!=0
    00334             &&  zero_pos_!=0
    00335             &&  w_>0
    00336             &&  h_>0
    00337             &&  pitch_!=0
    00338         ;
    00339     }
    00340
    00341     operator bool()const { return is_valid(); }
    00342
    00343     pen begin() { assert(data_); return pen(data_,w_,h_,pitch_); }
    00344     pen get_pen(int x, int y) { assert(data_); return begin().move(x,y); }
    00345     pen end() { assert(data_); return get_pen(w_,h_); }
    00346
    00347     const_pen begin()const { assert(data_); return const_pen(data_,w_,h_,pitch_); }
    00348     const_pen get_pen(int x, int y)const { assert(data_); return begin().move(x,y); }
    00349     const_pen end()const { assert(data_); return get_pen(w_,h_); }
    00350
    00352     value_type linear_sample(const float x, const float y)const
    00353     {
    00354         int u(floor_to_int(x)), v(floor_to_int(y));
    00355         float a, b;
    00356         static const float epsilon(1.0e-6);
    00357
    00358         if(x<0.0f)u=0,a=0.0f;
    00359         else if(x>w_-1)u=w_-1,a=0.0f;
    00360         else a=x-u;
    00361
    00362         if(y<0.0f)v=0,b=0.0f;
    00363         else if(y>h_-1)v=h_-1,b=0.0f;
    00364         else b=y-v;
    00365
    00366         const float
    00367             c(1.0f-a), d(1.0f-b),
    00368             e(a*d),f(c*b),g(a*b);
    00369
    00370         accumulator_type ret(cooker_.cook((*this)[v][u])*(c*d));
    00371         if(e>=epsilon)ret+=cooker_.cook((*this)[v][u+1])*e;
    00372         if(f>=epsilon)ret+=cooker_.cook((*this)[v+1][u])*f;
    00373         if(g>=epsilon)ret+=cooker_.cook((*this)[v+1][u+1])*g;
    00374         return cooker_.uncook(ret);
    00375     }
    00376
    00378     value_type cosine_sample(const float x, const float y)const
    00379     {
    00380         int u(floor_to_int(x)), v(floor_to_int(y));
    00381         float a, b;
    00382         static const float epsilon(1.0e-6);
    00383
    00384         if(x<0.0f)u=0,a=0.0f;
    00385         else if(x>w_-1)u=w_-1,a=0.0f;
    00386         else a=x-u;
    00387
    00388         if(y<0.0f)v=0,b=0.0f;
    00389         else if(y>h_-1)v=h_-1,b=0.0f;
    00390         else b=y-v;
    00391
    00392         a=(1.0f-cos(a*3.1415927f))*0.5f;
    00393         b=(1.0f-cos(b*3.1415927f))*0.5f;
    00394
    00395         const float
    00396             c(1.0f-a), d(1.0f-b),
    00397             e(a*d),f(c*b),g(a*b);
    00398
    00399         accumulator_type ret(cooker_.cook((*this)[v][u])*(c*d));
    00400         if(e>=epsilon)ret+=cooker_.cook((*this)[v][u+1])*e;
    00401         if(f>=epsilon)ret+=cooker_.cook((*this)[v+1][u])*f;
    00402         if(g>=epsilon)ret+=cooker_.cook((*this)[v+1][u+1])*g;
    00403
    00404         return cooker_.uncook(ret);
    00405     }
    00406
    00408     value_type cubic_sample(float x, float y)const
    00409     {
    00410         #if 0
    00411     #define P(x)    (((x)>=0)?((x)*(x)*(x)):0.0f)
    00412     #define R(x)    ( P(x+2) - 4.0f*P(x+1) + 6.0f*P(x) - 4.0f*P(x-1) )*(1.0f/6.0f)
    00413     #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))))
    00414     #define Z(i,j) ret+=F(i,j)
    00415     #define X(i,j)  // placeholder... To make box more symmetric
    00416
    00417         int u(floor_to_int(x)), v(floor_to_int(y));
    00418         float a, b;
    00419
    00420         // Clamp X
    00421         if(x<0.0f)u=0,a=0.0f;
    00422         else if(u>w_-1)u=w_-1,a=0.0f;
    00423         else a=x-u;
    00424
    00425         // Clamp Y
    00426         if(y<0.0f)v=0,b=0.0f;
    00427         else if(v>h_-1)v=h_-1,b=0.0f;
    00428         else b=y-v;
    00429
    00430         // Interpolate
    00431         accumulator_type ret(F(0,0));
    00432         Z(-1,-1); Z(-1, 0); Z(-1, 1); Z(-1, 2);
    00433         Z( 0,-1); X( 0, 0); Z( 0, 1); Z( 0, 2);
    00434         Z( 1,-1); Z( 1, 0); Z( 1, 1); Z( 1, 2);
    00435         Z( 2,-1); Z( 2, 0); Z( 2, 1); Z( 2, 2);
    00436
    00437         return cooker_.uncook(ret);
    00438
    00439     #undef X
    00440     #undef Z
    00441     #undef F
    00442     #undef P
    00443     #undef R
    00444         #else
    00445
    00446         #define f(j,i)  (cooker_.cook((*this)[j][i]))
    00447         //Using catmull rom interpolation because it doesn't blur at all
    00448         //bezier curve with intermediate ctrl pts: 0.5/3(p(i+1) - p(i-1)) and similar
    00449         accumulator_type xfa [4];
    00450
    00451         //precalculate indices (all clamped) and offset
    00452         const int xi = x > 0 ? (x < w_ ? (int)floor(x) : w_-1) : 0;
    00453         const int xa[] = {std::max(0,xi-1),xi,std::min(w_-1,xi+1),std::min(w_-1,xi+2)};
    00454
    00455         const int yi = y > 0 ? (y < h_ ? (int)floor(y) : h_-1) : 0;
    00456         const int ya[] = {std::max(0,yi-1),yi,std::min(h_-1,yi+1),std::min(h_-1,yi+2)};
    00457
    00458         const float xf = x-xi;
    00459         const float yf = y-yi;
    00460
    00461         //figure polynomials for each point
    00462         const float txf[] =
    00463         {
    00464             0.5*xf*(xf*(xf*(-1) + 2) - 1),  //-t + 2t^2 -t^3
    00465             0.5*(xf*(xf*(3*xf - 5)) + 2),   //2 - 5t^2 + 3t^3
    00466             0.5*xf*(xf*(-3*xf + 4) + 1),    //t + 4t^2 - 3t^3
    00467             0.5*xf*xf*(xf-1)                //-t^2 + t^3
    00468         };
    00469
    00470         const float tyf[] =
    00471         {
    00472             0.5*yf*(yf*(yf*(-1) + 2) - 1),  //-t + 2t^2 -t^3
    00473             0.5*(yf*(yf*(3*yf - 5)) + 2),   //2 - 5t^2 + 3t^3
    00474             0.5*yf*(yf*(-3*yf + 4) + 1),    //t + 4t^2 - 3t^3
    00475             0.5*yf*yf*(yf-1)                //-t^2 + t^3
    00476         };
    00477
    00478         //evaluate polynomial for each row
    00479         for(int i = 0; i < 4; ++i)
    00480         {
    00481             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];
    00482         }
    00483
    00484         //return the cumulative column evaluation
    00485         return cooker_.uncook(xfa[0]*tyf[0] + xfa[1]*tyf[1] + xfa[2]*tyf[2] + xfa[3]*tyf[3]);
    00486 #undef f
    00487 #endif
    00488     }
    00489
    00490     value_type  sample_rect(float x0,float y0,float x1,float y1) const
    00491     {
    00492         const surface &s = *this;
    00493
    00494         //assumes it's clamped to the boundary of the image
    00495         //force min max relationship for x0,x1 and y0,y1
    00496         if(x0 > x1) std::swap(x0,x1);
    00497         if(y0 > y1) std::swap(y0,y1);
    00498
    00499         //local variable madness
    00500         //all things that want to inter-operate should provide a default value constructor for = 0
    00501         accumulator_type acum = 0;
    00502         int xi=0,yi=0;
    00503
    00504         int xib=(int)floor(x0),
    00505             xie=(int)floor(x1);
    00506
    00507         int yib=(int)floor(y0),
    00508             yie=(int)floor(y1);
    00509
    00510         //the weight for the pixel should remain the same...
    00511         float weight = (y1-y0)*(x1-x0);
    00512         assert(weight != 0);
    00513
    00514         float ylast = y0, xlastb = x0;
    00515         const_pen   pen_ = s.get_pen(xib,yib);
    00516
    00517         for(yi = yib; yi < yie; ylast = ++yi, pen_.inc_y())
    00518         {
    00519             const float yweight = yi+1 - ylast;
    00520
    00521             float xlast = xlastb;
    00522             for(xi = xib; xi < xie; xlast = ++xi, pen_.inc_x())
    00523             {
    00524                 const float w = yweight*(xi+1 - xlast);
    00525                 acum += cooker_.cook(pen_.get_value())*w;
    00526             }
    00527
    00528             //post... with next being fractional...
    00529             const float w = yweight*(x1 - xlast);
    00530             acum += cooker_.cook(pen_.get_value())*w;
    00531
    00532             pen_.dec_x(xie-xib);
    00533         }
    00534
    00535         //post in y direction... must have all x...
    00536         {
    00537             const float yweight = y1 - ylast;
    00538
    00539             float xlast = xlastb;
    00540             for(xi = xib; xi < xie; xlast = ++xi)
    00541             {
    00542                 const float w = yweight*(xi+1 - xlast);
    00543
    00544                 acum += cooker_.cook(pen_.get_value())*w;
    00545             }
    00546
    00547             //post... with next being fractional...
    00548             const float w = yweight*(x1 - xlast);
    00549             acum += cooker_.cook(pen_.get_value())*w;
    00550         }
    00551
    00552         acum *= 1/weight;
    00553         return cooker_.uncook(acum);
    00554     }
    00555
    00556     value_type  sample_rect_clip(float x0,float y0,float x1,float y1) const
    00557     {
    00558         const surface &s = *this;
    00559
    00560         //assumes it's clamped to the boundary of the image
    00561         //force min max relationship for x0,x1 and y0,y1
    00562         if(x0 > x1) std::swap(x0,x1);
    00563         if(y0 > y1) std::swap(y0,y1);
    00564
    00565         //local variable madness
    00566         //all things that want to inter-operate should provide a default value constructor for = 0
    00567         accumulator_type acum = 0;
    00568         int xi=0,yi=0;
    00569
    00570         int xib=(int)floor(x0),
    00571             xie=(int)floor(x1);
    00572
    00573         int yib=(int)floor(y0),
    00574             yie=(int)floor(y1);
    00575
    00576         //the weight for the pixel should remain the same...
    00577         float weight = (y1-y0)*(x1-x0);
    00578
    00579         assert(weight != 0);
    00580
    00581         //clip to the input region
    00582         if(x0 >= s.get_w() || x1 <= 0) return acum;
    00583         if(y0 >= s.get_h() || y1 <= 0) return acum;
    00584
    00585         if(x0 < 0) { x0 = 0; xib = 0; }
    00586         if(x1 >= s.get_w())
    00587         {
    00588             x1 = s.get_w(); //want to be just below the last pixel...
    00589             xie = s.get_w()-1;
    00590         }
    00591
    00592         if(y0 < 0) { y0 = 0; yib = 0; }
    00593         if(y1 >= s.get_h())
    00594         {
    00595             y1 = s.get_h(); //want to be just below the last pixel...
    00596             yie = s.get_h()-1;
    00597         }
    00598
    00599         float ylast = y0, xlastb = x0;
    00600         const_pen   pen = s.get_pen(xib,yib);
    00601
    00602         for(yi = yib; yi < yie; ylast = ++yi, pen.inc_y())
    00603         {
    00604             const float yweight = yi+1 - ylast;
    00605
    00606             float xlast = xlastb;
    00607             for(xi = xib; xi < xie; xlast = ++xi, pen.inc_x())
    00608             {
    00609                 const float w = yweight*(xi+1 - xlast);
    00610                 acum += cooker_.cook(pen.get_value())*w;
    00611             }
    00612
    00613             //post... with next being fractional...
    00614             const float w = yweight*(x1 - xlast);
    00615             acum += cooker_.cook(pen.get_value())*w;
    00616
    00617             pen.dec_x(xie-xib);
    00618         }
    00619
    00620         //post in y direction... must have all x...
    00621         {
    00622             const float yweight = y1 - ylast;
    00623
    00624             float xlast = xlastb;
    00625             for(xi = xib; xi < xie; xlast = ++xi)
    00626             {
    00627                 const float w = yweight*(xi+1 - xlast);
    00628
    00629                 acum += cooker_.cook(pen.get_value())*w;
    00630             }
    00631
    00632             //post... with next being fractional...
    00633             const float w = yweight*(x1 - xlast);
    00634             acum += cooker_.cook(pen.get_value())*w;
    00635         }
    00636
    00637         acum *= 1/weight;
    00638         return cooker_.uncook(acum);
    00639     }
    00640 };
    00641
    00642 _ETL_END_NAMESPACE
    00643
    00644 /* === T Y P E D E F S ===================================================== */
    00645
    00646
    00647 /* === E N D =============================================================== */
    00648
    00649 #endif