00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #pragma once
00027
00028 #include "cinder/Cinder.h"
00029 #include "cinder/Area.h"
00030 #include "cinder/Channel.h"
00031 #include "cinder/ChanTraits.h"
00032 #include "cinder/Color.h"
00033
00034 #include <boost/logic/tribool.hpp>
00035
00036 namespace cinder {
00037
00039 class SurfaceChannelOrder {
00040 public:
00041 SurfaceChannelOrder() : mCode( UNSPECIFIED ), mRed( INVALID ), mGreen( INVALID ), mBlue( INVALID ), mAlpha( INVALID ), mPixelInc( INVALID ) {}
00042 SurfaceChannelOrder( int aCode );
00043 SurfaceChannelOrder( const SurfaceChannelOrder &aOrder );
00044
00045
00046
00047 uint8_t getRedOffset() const { return mRed; }
00048 uint8_t getGreenOffset() const { return mGreen; }
00049 uint8_t getBlueOffset() const { return mBlue; }
00050 uint8_t getAlphaOffset() const { return mAlpha; }
00051 bool hasAlpha() const { return ( mAlpha != INVALID ) ? true : false; }
00052 uint8_t getPixelInc() const { return mPixelInc; }
00053 int getCode() const { return mCode; }
00054
00055 bool operator==( const SurfaceChannelOrder& sco ) const
00056 {
00057 return mCode == sco.mCode;
00058 }
00059
00060 enum { CHAN_RED, CHAN_GREEN, CHAN_BLUE, CHAN_ALPHA, INVALID = 255 };
00061 enum { RGBA, BGRA, ARGB, ABGR, RGBX, BGRX, XRGB, XBGR, RGB, BGR, UNSPECIFIED };
00062
00063 int getImageIoChannelOrder() const;
00064
00065 private:
00066 void set( uint8_t aRed, uint8_t aGreen, uint8_t aBlue, uint8_t aAlpha, uint8_t aPixelInc );
00067 int mCode;
00068 uint8_t mRed, mGreen, mBlue, mAlpha, mPixelInc;
00069
00070 };
00071
00073 class SurfaceConstraints {
00074 public:
00075 virtual ~SurfaceConstraints() {}
00076
00077 virtual SurfaceChannelOrder getChannelOrder( bool alpha ) const { return ( alpha ) ? SurfaceChannelOrder::RGBA : SurfaceChannelOrder::RGB; }
00078 virtual int32_t getRowBytes( int requestedWidth, const SurfaceChannelOrder &sco, int elementSize ) const { return requestedWidth * elementSize * sco.getPixelInc(); }
00079 };
00080
00081 class SurfaceConstraintsDefault : public SurfaceConstraints {
00082 };
00083
00084 typedef std::shared_ptr<class ImageSource> ImageSourceRef;
00085 typedef std::shared_ptr<class ImageTarget> ImageTargetRef;
00086
00087 template<typename T>
00089 class SurfaceT {
00090 private:
00092 struct Obj {
00093 Obj( int32_t aWidth, int32_t aHeight, SurfaceChannelOrder aChannelOrder, T *aData, bool aOwnsData, int32_t aRowBytes );
00094 ~Obj();
00095
00096 void initChannels();
00097 void setChannelOrder( const SurfaceChannelOrder &aChannelOrder );
00098 void setDeallocator( void(*aDeallocatorFunc)( void * ), void *aDeallocatorRefcon );
00099
00100 int32_t mWidth, mHeight, mRowBytes;
00101 bool mIsPremultiplied;
00102 T *mData;
00103 bool mOwnsData;
00104 SurfaceChannelOrder mChannelOrder;
00105 ChannelT<T> mChannels[4];
00106
00107 void (*mDeallocatorFunc)(void *refcon);
00108 void *mDeallocatorRefcon;
00109 };
00111
00112 public:
00114 SurfaceT() {}
00120 SurfaceT( int32_t width, int32_t height, bool alpha, SurfaceChannelOrder channelOrder = SurfaceChannelOrder::UNSPECIFIED );
00121 SurfaceT( int32_t width, int32_t height, bool alpha, const SurfaceConstraints &constraints );
00123 SurfaceT( T *data, int32_t width, int32_t height, int32_t rowBytes, SurfaceChannelOrder channelOrder );
00129 SurfaceT( ImageSourceRef imageSource, const SurfaceConstraints &constraints = SurfaceConstraintsDefault(), boost::tribool alpha = boost::logic::indeterminate );
00130
00131 operator ImageSourceRef() const;
00132 operator ImageTargetRef();
00133
00135 int32_t getWidth() const { return mObj->mWidth; }
00137 int32_t getHeight() const { return mObj->mHeight; }
00139 Vec2i getSize() const { return Vec2i( mObj->mWidth, mObj->mHeight ); }
00141 float getAspectRatio() const { return mObj->mWidth / (float)mObj->mHeight; }
00143 Area getBounds() const { return Area( 0, 0, mObj->mWidth, mObj->mHeight ); }
00145 bool hasAlpha() const { return mObj->mChannelOrder.hasAlpha(); }
00147 bool isPremultiplied() const { return mObj->mIsPremultiplied; }
00149 bool setPremultiplied( bool premult = true ) const { return mObj->mIsPremultiplied = premult; }
00151 int32_t getRowBytes() const { return mObj->mRowBytes; }
00153 uint8_t getPixelInc() const { return mObj->mChannelOrder.getPixelInc(); }
00154
00156 SurfaceT clone( bool copyPixels = true ) const;
00158 SurfaceT clone( const Area &area, bool copyPixels = true ) const;
00159
00161 T* getData() { return mObj->mData; }
00162 const T* getData() const { return mObj->mData; }
00163 T* getData( const Vec2i &offset ) { return reinterpret_cast<T*>( reinterpret_cast<unsigned char*>( mObj->mData + offset.x * getPixelInc() ) + offset.y * mObj->mRowBytes ); }
00164 const T* getData( const Vec2i &offset ) const { return reinterpret_cast<T*>( reinterpret_cast<unsigned char*>( mObj->mData + offset.x * getPixelInc() ) + offset.y * mObj->mRowBytes ); }
00166 T* getDataRed( const Vec2i &offset ) { return getData( offset ) + getRedOffset(); }
00167 const T* getDataRed( const Vec2i &offset ) const { return getData( offset ) + getRedOffset(); }
00169 T* getDataGreen( const Vec2i &offset ) { return getData( offset ) + getGreenOffset(); }
00170 const T* getDataGreen( const Vec2i &offset ) const { return getData( offset ) + getGreenOffset(); }
00172 T* getDataBlue( const Vec2i &offset ) { return getData( offset ) + getBlueOffset(); }
00173 const T* getDataBlue( const Vec2i &offset ) const { return getData( offset ) + getBlueOffset(); }
00175 T* getDataAlpha( const Vec2i &offset ) { return getData( offset ) + getAlphaOffset(); }
00176 const T* getDataAlpha( const Vec2i &offset ) const { return getData( offset ) + getAlphaOffset(); }
00177
00179 void setDeallocator( void(*aDeallocatorFunc)( void * ), void *aDeallocatorRefcon );
00180
00182 const SurfaceChannelOrder& getChannelOrder() const { return mObj->mChannelOrder; }
00184 uint8_t getRedOffset() const { return mObj->mChannelOrder.getRedOffset(); }
00186 uint8_t getGreenOffset() const { return mObj->mChannelOrder.getGreenOffset(); }
00188 uint8_t getBlueOffset() const { return mObj->mChannelOrder.getBlueOffset(); }
00190 uint8_t getAlphaOffset() const { return mObj->mChannelOrder.getAlphaOffset(); }
00192 void setChannelOrder( const SurfaceChannelOrder &aChannelOrder );
00193
00195 ChannelT<T>& getChannel( uint8_t channelIndex ) { return mObj->mChannels[channelIndex]; }
00197 const ChannelT<T>& getChannel( uint8_t channelIndex ) const { return mObj->mChannels[channelIndex]; }
00198
00200 ChannelT<T>& getChannelRed() { return mObj->mChannels[SurfaceChannelOrder::CHAN_RED]; }
00202 ChannelT<T>& getChannelGreen() { return mObj->mChannels[SurfaceChannelOrder::CHAN_GREEN]; }
00204 ChannelT<T>& getChannelBlue() { return mObj->mChannels[SurfaceChannelOrder::CHAN_BLUE]; }
00206 ChannelT<T>& getChannelAlpha() { return mObj->mChannels[SurfaceChannelOrder::CHAN_ALPHA]; }
00207
00209 const ChannelT<T>& getChannelRed() const { return mObj->mChannels[SurfaceChannelOrder::CHAN_RED]; }
00211 const ChannelT<T>& getChannelGreen() const { return mObj->mChannels[SurfaceChannelOrder::CHAN_GREEN]; }
00213 const ChannelT<T>& getChannelBlue() const { return mObj->mChannels[SurfaceChannelOrder::CHAN_BLUE]; }
00215 const ChannelT<T>& getChannelAlpha() const { return mObj->mChannels[SurfaceChannelOrder::CHAN_ALPHA]; }
00216
00218 ColorAT<T> getPixel( Vec2i pos ) const { pos.x = constrain<int32_t>( pos.x, 0, mObj->mWidth - 1); pos.y = constrain<int32_t>( pos.y, 0, mObj->mHeight - 1 ); const T *p = getData( pos ); return ColorAT<T>( p[getRedOffset()], p[getGreenOffset()], p[getBlueOffset()], ( hasAlpha() ) ? p[getAlphaOffset()] : CHANTRAIT<T>::max() ); }
00220 void setPixel( Vec2i pos, const ColorT<T> &c ) { pos.x = constrain<int32_t>( pos.x, 0, mObj->mWidth - 1); pos.y = constrain<int32_t>( pos.y, 0, mObj->mHeight - 1 ); T *p = getData( pos ); p[getRedOffset()] = c.r; p[getGreenOffset()] = c.g; p[getBlueOffset()] = c.b; }
00222 void setPixel( Vec2i pos, const ColorAT<T> &c ) { pos.x = constrain<int32_t>( pos.x, 0, mObj->mWidth - 1); pos.y = constrain<int32_t>( pos.y, 0, mObj->mHeight - 1 ); T *p = getData( pos ); p[getRedOffset()] = c.r; p[getGreenOffset()] = c.g; p[getBlueOffset()] = c.b; if( hasAlpha() ) p[getAlphaOffset()] = c.a; }
00223
00225 void copyFrom( const SurfaceT<T> &srcSurface, const Area &srcArea, const Vec2i &relativeOffset = Vec2i::zero() );
00226
00228 ColorT<T> areaAverage( const Area &area ) const;
00229
00231 typedef std::shared_ptr<Obj> SurfaceT::*unspecified_bool_type;
00232 operator unspecified_bool_type() const { return ( mObj.get() == 0 ) ? 0 : &SurfaceT::mObj; }
00233 void reset() { mObj.reset(); }
00235
00236 private:
00237 std::shared_ptr<Obj> mObj;
00238
00239 void init( ImageSourceRef imageSource, const SurfaceConstraints &constraints = SurfaceConstraintsDefault(), boost::tribool alpha = boost::logic::indeterminate );
00240
00241 void copyRawSameChannelOrder( const SurfaceT<T> &srcSurface, const Area &srcArea, const Vec2i &absoluteOffset );
00242 void copyRawRgba( const SurfaceT<T> &srcSurface, const Area &srcArea, const Vec2i &absoluteOffset );
00243 void copyRawRgbFullAlpha( const SurfaceT<T> &srcSurface, const Area &srcArea, const Vec2i &absoluteOffset );
00244 void copyRawRgb( const SurfaceT<T> &srcSurface, const Area &srcArea, const Vec2i &absoluteOffset );
00245
00246 public:
00247
00249 class Iter {
00250 public:
00251 Iter( SurfaceT<T> &SurfaceT, const Area &area )
00252 : mRedOff( SurfaceT.getRedOffset() ), mGreenOff( SurfaceT.getGreenOffset() ),
00253 mBlueOff( SurfaceT.getBlueOffset() ), mAlphaOff( SurfaceT.getAlphaOffset() ),
00254 mInc( SurfaceT.getPixelInc() ), mRowInc( SurfaceT.getRowBytes() )
00255 {
00256 Area clippedArea( area.getClipBy( SurfaceT.getBounds() ) );
00257 mWidth = clippedArea.getWidth();
00258 mHeight = clippedArea.getHeight();
00259 mLinePtr = reinterpret_cast<uint8_t*>( SurfaceT.getData( clippedArea.getUL() ) );
00260 mPtr = reinterpret_cast<T*>( mLinePtr );
00261 mStartX = mX = clippedArea.getX1();
00262 mStartY = mY = clippedArea.getY1();
00263 mEndX = clippedArea.getX2();
00264 mEndY = clippedArea.getY2();
00265
00266 mY = clippedArea.getY1() - 1;
00267 mLinePtr -= mRowInc;
00268 }
00270 T& r() const { return mPtr[mRedOff]; }
00272 T& g() const { return mPtr[mGreenOff]; }
00274 T& b() const { return mPtr[mBlueOff]; }
00276 T& a() const { return mPtr[mAlphaOff]; }
00277
00279 T& r( int32_t xOff, int32_t yOff ) const { return mPtr[mRedOff + xOff * mInc + yOff * mRowInc]; }
00281 T& g( int32_t xOff, int32_t yOff ) const { return mPtr[mGreenOff + xOff * mInc + yOff * mRowInc]; }
00283 T& b( int32_t xOff, int32_t yOff ) const { return mPtr[mBlueOff + xOff * mInc + yOff * mRowInc]; }
00285 T& a( int32_t xOff, int32_t yOff ) const { return mPtr[mAlphaOff + xOff * mInc + yOff * mRowInc]; }
00286
00288 T& rClamped( int32_t xOff, int32_t yOff ) const
00289 { xOff = std::min(std::max(mX + xOff, mStartX),mEndX - 1) - mX; yOff = std::min(std::max( mY + yOff, mStartY ), mEndY - 1) - mY;
00290 return *(T*)((uint8_t*)( mPtr + mRedOff + xOff * mInc ) + yOff * mRowInc); }
00292 T& gClamped( int32_t xOff, int32_t yOff ) const
00293 { xOff = std::min(std::max(mX + xOff, mStartX),mEndX - 1) - mX; yOff = std::min(std::max( mY + yOff, mStartY ), mEndY - 1) - mY;
00294 return *(T*)((uint8_t*)( mPtr + mGreenOff + xOff * mInc ) + yOff * mRowInc); }
00296 T& bClamped( int32_t xOff, int32_t yOff ) const
00297 { xOff = std::min(std::max(mX + xOff, mStartX),mEndX - 1) - mX; yOff = std::min(std::max( mY + yOff, mStartY ), mEndY - 1) - mY;
00298 return *(T*)((uint8_t*)( mPtr + mBlueOff + xOff * mInc ) + yOff * mRowInc); }
00300 T& aClamped( int32_t xOff, int32_t yOff ) const
00301 { xOff = std::min(std::max(mX + xOff, mStartX),mEndX - 1) - mX; yOff = std::min(std::max( mY + yOff, mStartY ), mEndY - 1) - mY;
00302 return *(T*)((uint8_t*)( mPtr + mAlphaOff + xOff * mInc ) + yOff * mRowInc); }
00303
00305 const int32_t x() const { return mX; }
00307 const int32_t y() const { return mY; }
00309 Vec2i getPos() const { return Vec2i( mX, mY ); }
00310
00312 bool pixel() {
00313 ++mX;
00314 mPtr += mInc;
00315 return mX < mEndX;
00316 }
00317
00319 bool line() {
00320 ++mY;
00321 mLinePtr += mRowInc;
00322 mPtr = reinterpret_cast<T*>( mLinePtr );
00323
00324 mPtr -= mInc;
00325 mX = mStartX - 1;
00326 return mY < mEndY;
00327 }
00328
00330 int32_t getWidth() const { return mWidth; }
00332 int32_t getHeight() const { return mHeight; }
00333
00335 uint8_t mRedOff, mGreenOff, mBlueOff, mAlphaOff, mInc;
00336 uint8_t *mLinePtr;
00337 T *mPtr;
00338 int32_t mRowInc, mWidth, mHeight;
00339 int32_t mX, mY, mStartX, mStartY, mEndX, mEndY;
00341 };
00342
00344 class ConstIter {
00345 public:
00346 ConstIter( const Iter &iter ) {
00347 mRedOff = iter.mRedOff;
00348 mGreenOff = iter.mGreenOff;
00349 mBlueOff = iter.mBlueOff;
00350 mAlphaOff = iter.mAlphaOff;
00351 mInc = iter.mInc;
00352 mRowInc = iter.mRowInc;
00353 mWidth = iter.mWidth;
00354 mHeight = iter.mHeight;
00355 mLinePtr = iter.mLinePtr;
00356 mPtr = iter.mPtr;
00357 mStartX = iter.mStartX;
00358 mX = iter.mX;
00359 mStartY = iter.mStartY;
00360 mY = iter.mY;
00361 mEndX = iter.mEndX;
00362 mEndY = iter.mEndY;
00363 }
00364
00365 ConstIter( const SurfaceT<T> &SurfaceT, const Area &area )
00366 : mRedOff( SurfaceT.getRedOffset() ), mGreenOff( SurfaceT.getGreenOffset() ),
00367 mBlueOff( SurfaceT.getBlueOffset() ), mAlphaOff( SurfaceT.getAlphaOffset() ),
00368 mInc( SurfaceT.getPixelInc() ), mRowInc( SurfaceT.getRowBytes() )
00369 {
00370 Area clippedArea( area.getClipBy( SurfaceT.getBounds() ) );
00371 mWidth = clippedArea.getWidth();
00372 mHeight = clippedArea.getHeight();
00373 mLinePtr = reinterpret_cast<const uint8_t*>( SurfaceT.getData( clippedArea.getUL() ) );
00374 mPtr = reinterpret_cast<const T*>( mLinePtr );
00375 mStartX = mX = clippedArea.getX1();
00376 mStartY = mY = clippedArea.getY1();
00377 mEndX = clippedArea.getX2();
00378 mEndY = clippedArea.getY2();
00379
00380 mY = clippedArea.getY1() - 1;
00381 mLinePtr -= mRowInc;
00382 }
00383
00385 const T& r() const { return mPtr[mRedOff]; }
00387 const T& g() const { return mPtr[mGreenOff]; }
00389 const T& b() const { return mPtr[mBlueOff]; }
00391 const T& a() const { return mPtr[mAlphaOff]; }
00392
00394 const T& r( int32_t xOff, int32_t yOff ) const { return mPtr[mRedOff + xOff * mInc + yOff * mRowInc]; }
00396 const T& g( int32_t xOff, int32_t yOff ) const { return mPtr[mGreenOff + xOff * mInc + yOff * mRowInc]; }
00398 const T& b( int32_t xOff, int32_t yOff ) const { return mPtr[mBlueOff + xOff * mInc + yOff * mRowInc]; }
00400 const T& a( int32_t xOff, int32_t yOff ) const { return mPtr[mAlphaOff + xOff * mInc + yOff * mRowInc]; }
00401
00403 const T& rClamped( int32_t xOff, int32_t yOff ) const
00404 { xOff = std::min(std::max(mX + xOff, mStartX),mEndX - 1) - mX; yOff = std::min(std::max( mY + yOff, mStartY ), mEndY - 1) - mY;
00405 return *(T*)((uint8_t*)( mPtr + mRedOff + xOff * mInc ) + yOff * mRowInc); }
00407 const T& gClamped( int32_t xOff, int32_t yOff ) const
00408 { xOff = std::min(std::max(mX + xOff, mStartX),mEndX - 1) - mX; yOff = std::min(std::max( mY + yOff, mStartY ), mEndY - 1) - mY;
00409 return *(T*)((uint8_t*)( mPtr + mGreenOff + xOff * mInc ) + yOff * mRowInc); }
00411 const T& bClamped( int32_t xOff, int32_t yOff ) const
00412 { xOff = std::min(std::max(mX + xOff, mStartX),mEndX - 1) - mX; yOff = std::min(std::max( mY + yOff, mStartY ), mEndY - 1) - mY;
00413 return *(T*)((uint8_t*)( mPtr + mBlueOff + xOff * mInc ) + yOff * mRowInc); }
00415 const T& aClamped( int32_t xOff, int32_t yOff ) const
00416 { xOff = std::min(std::max(mX + xOff, mStartX),mEndX - 1) - mX; yOff = std::min(std::max( mY + yOff, mStartY ), mEndY - 1) - mY;
00417 return *(T*)((uint8_t*)( mPtr + mAlphaOff + xOff * mInc ) + yOff * mRowInc); }
00418
00420 const int32_t x() const { return mX; }
00422 const int32_t y() const { return mY; }
00424 Vec2i getPos() const { return Vec2i( mX, mY ); }
00425
00427 bool pixel() {
00428 ++mX;
00429 mPtr += mInc;
00430 return mX < mEndX;
00431 }
00432
00434 bool line() {
00435 ++mY;
00436 mLinePtr += mRowInc;
00437 mPtr = reinterpret_cast<const T*>( mLinePtr );
00438
00439 mPtr -= mInc;
00440 mX = mStartX - 1;
00441 return mY < mEndY;
00442 }
00443
00445 int32_t getWidth() const { return mWidth; }
00447 int32_t getHeight() const { return mHeight; }
00448
00450 uint8_t mRedOff, mGreenOff, mBlueOff, mAlphaOff, mInc;
00451 const uint8_t *mLinePtr;
00452 const T *mPtr;
00453 int32_t mRowInc, mWidth, mHeight;
00454 int32_t mX, mY, mStartX, mStartY, mEndX, mEndY;
00456 };
00457
00459 Iter getIter() { return Iter( *this, this->getBounds() ); }
00461 Iter getIter( const Area &area ) { return Iter( *this, area ); }
00463 ConstIter getIter() const { return ConstIter( *this, this->getBounds() ); }
00465 ConstIter getIter( const Area &area ) const { return ConstIter( *this, area ); }
00466 };
00467
00468 class SurfaceExc : public std::exception {
00469 virtual const char* what() const throw() {
00470 return "Surface exception";
00471 }
00472 };
00473
00474 class SurfaceConstraintsExc : public SurfaceExc {
00475 virtual const char* what() const throw() {
00476 return "Surface exception: does not conform to expected SurfaceConstraints";
00477 }
00478 };
00479
00481 typedef SurfaceT<uint8_t> Surface;
00483 typedef SurfaceT<uint8_t> Surface8u;
00485 typedef SurfaceT<uint16_t> Surface16u;
00487 typedef SurfaceT<float> Surface32f;
00488
00489 }