00001
00026
00027
00028 #ifndef __ETL__SURFACE_H
00029 #define __ETL__SURFACE_H
00030
00031
00032
00033 #include "_pen.h"
00034 #include "_misc.h"
00035 #include <algorithm>
00036 #include <cstring>
00037
00038
00039
00040
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)
00261 {
00262 if(x>=w_ || y>=h_)
00263 return;
00264
00265
00266 if(x<0)
00267 {
00268 w+=x;
00269 x=0;
00270 }
00271
00272 if(y<0)
00273 {
00274 h+=y;
00275 y=0;
00276 }
00277
00278
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
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
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
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
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
00448
00449 accumulator_type xfa [4];
00450
00451
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
00462 const float txf[] =
00463 {
00464 0.5*xf*(xf*(xf*(-1) + 2) - 1),
00465 0.5*(xf*(xf*(3*xf - 5)) + 2),
00466 0.5*xf*(xf*(-3*xf + 4) + 1),
00467 0.5*xf*xf*(xf-1)
00468 };
00469
00470 const float tyf[] =
00471 {
00472 0.5*yf*(yf*(yf*(-1) + 2) - 1),
00473 0.5*(yf*(yf*(3*yf - 5)) + 2),
00474 0.5*yf*(yf*(-3*yf + 4) + 1),
00475 0.5*yf*yf*(yf-1)
00476 };
00477
00478
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
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
00495
00496 if(x0 > x1) std::swap(x0,x1);
00497 if(y0 > y1) std::swap(y0,y1);
00498
00499
00500
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
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
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
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
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
00561
00562 if(x0 > x1) std::swap(x0,x1);
00563 if(y0 > y1) std::swap(y0,y1);
00564
00565
00566
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
00577 float weight = (y1-y0)*(x1-x0);
00578
00579 assert(weight != 0);
00580
00581
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();
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();
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
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
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
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
00645
00646
00647
00648
00649 #endif