_surface.h

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

Generated on Thu Jan 12 22:17:45 2006 for ETL by  doxygen 1.4.6