00001 /* 00002 Copyright (c) 2010, The Barbarian Group 00003 All rights reserved. 00004 00005 Redistribution and use in source and binary forms, with or without modification, are permitted provided that 00006 the following conditions are met: 00007 00008 * Redistributions of source code must retain the above copyright notice, this list of conditions and 00009 the following disclaimer. 00010 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 00011 the following disclaimer in the documentation and/or other materials provided with the distribution. 00012 00013 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 00014 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 00015 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 00016 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 00017 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00018 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 00019 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00020 POSSIBILITY OF SUCH DAMAGE. 00021 */ 00022 00023 #pragma once 00024 00025 #include "cinder/Cinder.h" 00026 #include "cinder/Area.h" 00027 #include "cinder/Channel.h" 00028 #include "cinder/ChanTraits.h" 00029 #include "cinder/Color.h" 00030 00031 #include <boost/logic/tribool.hpp> 00032 00033 namespace cinder { 00034 00035 class SurfaceChannelOrder { 00036 public: 00037 SurfaceChannelOrder() : mCode( UNSPECIFIED ), mRed( INVALID ), mGreen( INVALID ), mBlue( INVALID ), mAlpha( INVALID ), mPixelInc( INVALID ) {} 00038 SurfaceChannelOrder( int aCode ); 00039 SurfaceChannelOrder( const SurfaceChannelOrder &aOrder ); 00040 00041 // static ChannelOrder GetPlatformNativeChannelOrder( bool includeAlpha ); 00042 00043 uint8_t getRedOffset() const { return mRed; } 00044 uint8_t getGreenOffset() const { return mGreen; } 00045 uint8_t getBlueOffset() const { return mBlue; } 00046 uint8_t getAlphaOffset() const { return mAlpha; } 00047 bool hasAlpha() const { return ( mAlpha != INVALID ) ? true : false; } 00048 uint8_t getPixelInc() const { return mPixelInc; } 00049 int getCode() const { return mCode; } 00050 00051 bool operator==( const SurfaceChannelOrder& sco ) const 00052 { 00053 return mCode == sco.mCode; 00054 } 00055 00056 enum { CHAN_RED, CHAN_GREEN, CHAN_BLUE, CHAN_ALPHA, INVALID = 255 }; 00057 enum { RGBA, BGRA, ARGB, ABGR, RGBX, BGRX, XRGB, XBGR, RGB, BGR, UNSPECIFIED }; // Codes 00058 00059 int getImageIoChannelOrder() const; 00060 00061 private: 00062 void set( uint8_t aRed, uint8_t aGreen, uint8_t aBlue, uint8_t aAlpha, uint8_t aPixelInc ); 00063 int mCode; // the enum 00064 uint8_t mRed, mGreen, mBlue, mAlpha, mPixelInc; 00065 00066 }; 00067 00069 class SurfaceConstraints { 00070 public: 00071 virtual ~SurfaceConstraints() {} 00072 00073 virtual SurfaceChannelOrder getChannelOrder( bool alpha ) const { return ( alpha ) ? SurfaceChannelOrder::RGBA : SurfaceChannelOrder::RGB; } 00074 virtual int32_t getRowBytes( int requestedWidth, const SurfaceChannelOrder &sco, int elementSize ) const { return requestedWidth * elementSize * sco.getPixelInc(); } 00075 }; 00076 00077 class SurfaceConstraintsDefault : public SurfaceConstraints { 00078 }; 00079 00080 typedef shared_ptr<class ImageSource> ImageSourceRef; 00081 00082 template<typename T> 00083 class SurfaceT { 00084 private: 00086 struct Obj { 00087 Obj( int32_t aWidth, int32_t aHeight, SurfaceChannelOrder aChannelOrder, T *aData, bool aOwnsData, int32_t aRowBytes ); 00088 ~Obj(); 00089 00090 void initChannels(); 00091 void setData( T *aData, int32_t aWidth, int32_t aHeight, int32_t aRowBytes ); 00092 void setChannelOrder( const SurfaceChannelOrder &aChannelOrder ); 00093 void setDeallocator( void(*aDeallocatorFunc)( void * ), void *aDeallocatorRefcon ); 00094 00095 int32_t mWidth, mHeight, mRowBytes; 00096 bool mIsPremultiplied; 00097 T *mData; 00098 bool mOwnsData; 00099 SurfaceChannelOrder mChannelOrder; 00100 ChannelT<T> mChannels[4]; 00101 00102 void (*mDeallocatorFunc)(void *refcon); 00103 void *mDeallocatorRefcon; 00104 }; 00106 00107 public: 00108 SurfaceT() {} 00109 SurfaceT( int32_t aWidth, int32_t aHeight, bool alpha, SurfaceChannelOrder aChannelOrder = SurfaceChannelOrder::UNSPECIFIED ); 00110 SurfaceT( int32_t aWidth, int32_t aHeight, bool alpha, const SurfaceConstraints &constraints ); 00111 SurfaceT( T *aData, int32_t aWidth, int32_t aHeight, int32_t aRowBytes, SurfaceChannelOrder aChannelOrder ); 00112 SurfaceT( shared_ptr<class ImageSource> imageSource, const SurfaceConstraints &constraints = SurfaceConstraintsDefault(), boost::tribool alpha = boost::logic::indeterminate ); 00113 00114 operator ImageSourceRef() const; 00115 00117 int32_t getWidth() const { return mObj->mWidth; } 00119 int32_t getHeight() const { return mObj->mHeight; } 00121 Vec2i getSize() const { return Vec2i( mObj->mWidth, mObj->mHeight ); } 00123 float getAspectRatio() const { return mObj->mWidth / (float)mObj->mHeight; } 00125 Area getBounds() const { return Area( 0, 0, mObj->mWidth, mObj->mHeight ); } 00127 bool hasAlpha() const { return mObj->mChannelOrder.hasAlpha(); } 00129 bool isPremultiplied() const { return mObj->mIsPremultiplied; } 00131 bool setPremultiplied( bool premult = true ) const { return mObj->mIsPremultiplied = premult; } 00133 int32_t getRowBytes() const { return mObj->mRowBytes; } 00135 uint8_t getPixelInc() const { return mObj->mChannelOrder.getPixelInc(); } 00136 00138 SurfaceT clone( bool copyPixels = true ) const; 00140 SurfaceT clone( const Area &area, bool copyPixels = true ) const; 00141 00142 T* getData() { return mObj->mData; } 00143 const T* getData() const { return mObj->mData; } 00144 T* getData( const Vec2i &offset ) { return reinterpret_cast<T*>( reinterpret_cast<unsigned char*>( mObj->mData + offset.x * getPixelInc() ) + offset.y * mObj->mRowBytes ); } 00145 const T* getData( const Vec2i &offset ) const { return reinterpret_cast<T*>( reinterpret_cast<unsigned char*>( mObj->mData + offset.x * getPixelInc() ) + offset.y * mObj->mRowBytes ); } 00146 T* getDataRed( const Vec2i &offset ) { return getData( offset ) + getRedOffset(); } 00147 const T* getDataRed( const Vec2i &offset ) const { return getData( offset ) + getRedOffset(); } 00148 T* getDataGreen( const Vec2i &offset ) { return getData( offset ) + getGreenOffset(); } 00149 const T* getDataGreen( const Vec2i &offset ) const { return getData( offset ) + getGreenOffset(); } 00150 T* getDataBlue( const Vec2i &offset ) { return getData( offset ) + getBlueOffset(); } 00151 const T* getDataBlue( const Vec2i &offset ) const { return getData( offset ) + getBlueOffset(); } 00152 T* getDataAlpha( const Vec2i &offset ) { return getData( offset ) + getAlphaOffset(); } 00153 const T* getDataAlpha( const Vec2i &offset ) const { return getData( offset ) + getAlphaOffset(); } 00154 00155 void setData( T *aData, int32_t aWidth, int32_t aHeight, int32_t aRowBytes ); 00157 void setDeallocator( void(*aDeallocatorFunc)( void * ), void *aDeallocatorRefcon ); 00158 00159 const SurfaceChannelOrder& getChannelOrder() const { return mObj->mChannelOrder; } 00160 uint8_t getRedOffset() const { return mObj->mChannelOrder.getRedOffset(); } 00161 uint8_t getGreenOffset() const { return mObj->mChannelOrder.getGreenOffset(); } 00162 uint8_t getBlueOffset() const { return mObj->mChannelOrder.getBlueOffset(); } 00163 uint8_t getAlphaOffset() const { return mObj->mChannelOrder.getAlphaOffset(); } 00164 void setChannelOrder( const SurfaceChannelOrder &aChannelOrder ); 00165 00166 ChannelT<T>* getChannel( uint8_t ChannelT ) { return &mObj->mChannels[ChannelT]; } 00167 ChannelT<T>* getChannelRed() { return &mObj->mChannels[SurfaceChannelOrder::CHAN_RED]; } 00168 ChannelT<T>* getChannelGreen() { return &mObj->mChannels[SurfaceChannelOrder::CHAN_GREEN]; } 00169 ChannelT<T>* getChannelBlue() { return &mObj->mChannels[SurfaceChannelOrder::CHAN_BLUE]; } 00170 ChannelT<T>* getChannelAlpha() { return &mObj->mChannels[SurfaceChannelOrder::CHAN_ALPHA]; } 00171 00172 const ChannelT<T>* getChannel( uint8_t ChannelT ) const { return &mObj->mChannels[ChannelT]; } 00173 const ChannelT<T>* getChannelRed() const { return &mObj->mChannels[SurfaceChannelOrder::CHAN_RED]; } 00174 const ChannelT<T>* getChannelGreen() const { return &mObj->mChannels[SurfaceChannelOrder::CHAN_GREEN]; } 00175 const ChannelT<T>* getChannelBlue() const { return &mObj->mChannels[SurfaceChannelOrder::CHAN_BLUE]; } 00176 const ChannelT<T>* getChannelAlpha() const { return &mObj->mChannels[SurfaceChannelOrder::CHAN_ALPHA]; } 00177 00179 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() ); } 00181 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; } 00183 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; } 00184 00185 void copyFrom( const SurfaceT<T> &srcSurface, const Area &srcArea, const Vec2i &relativeOffset = Vec2i::zero() ); 00186 // template<typename T2> 00187 // void copy( const SurfaceT<T2> &srcSurface, const Area &srcArea, const Offset &dstOffset = Offset::zero() ); 00188 00189 ColorT<T> areaAverage( const Area &area ) const; 00190 00192 00193 typedef shared_ptr<Obj> SurfaceT::*unspecified_bool_type; 00194 operator unspecified_bool_type() { return ( mObj.get() == 0 ) ? 0 : &SurfaceT::mObj; } 00195 void reset() { mObj.reset(); } 00197 00198 private: 00199 shared_ptr<Obj> mObj; 00200 00201 void init( shared_ptr<class ImageSource> imageSource, const SurfaceConstraints &constraints = SurfaceConstraintsDefault(), boost::tribool alpha = boost::logic::indeterminate ); 00202 00203 void copyRawSameChannelOrder( const SurfaceT<T> &srcSurface, const Area &srcArea, const Vec2i &absoluteOffset ); 00204 void copyRawRgba( const SurfaceT<T> &srcSurface, const Area &srcArea, const Vec2i &absoluteOffset ); 00205 void copyRawRgb( const SurfaceT<T> &srcSurface, const Area &srcArea, const Vec2i &absoluteOffset ); 00206 00207 public: 00208 class Iter { 00209 public: 00210 Iter( SurfaceT<T> &SurfaceT, const Area &area ) 00211 : mRedOff( SurfaceT.getRedOffset() ), mGreenOff( SurfaceT.getGreenOffset() ), 00212 mBlueOff( SurfaceT.getBlueOffset() ), mAlphaOff( SurfaceT.getAlphaOffset() ), 00213 mInc( SurfaceT.getPixelInc() ), mRowInc( SurfaceT.getRowBytes() ) 00214 { 00215 Area clippedArea( area.getClipBy( SurfaceT.getBounds() ) ); 00216 mWidth = clippedArea.getWidth(); 00217 mHeight = clippedArea.getHeight(); 00218 mLinePtr = reinterpret_cast<uint8_t*>( SurfaceT.getData( clippedArea.getUL() ) ); 00219 mPtr = reinterpret_cast<T*>( mLinePtr ); 00220 mStartX = mX = clippedArea.getX1(); 00221 mStartY = mY = clippedArea.getY1(); 00222 mEndX = clippedArea.getX2(); 00223 mEndY = clippedArea.getY2(); 00224 // in order to be at the right place after an initial call to line(), we need to back up one line 00225 mY = clippedArea.getY1() - 1; 00226 mLinePtr -= mRowInc; 00227 } 00228 00229 T& r() const { return mPtr[mRedOff]; } 00230 T& g() const { return mPtr[mGreenOff]; } 00231 T& b() const { return mPtr[mBlueOff]; } 00232 T& a() const { return mPtr[mAlphaOff]; } 00233 00234 T& r( int32_t xOff, int32_t yOff ) const { return mPtr[mRedOff + xOff * mInc + yOff * mRowInc]; } 00235 T& g( int32_t xOff, int32_t yOff ) const { return mPtr[mGreenOff + xOff * mInc + yOff * mRowInc]; } 00236 T& b( int32_t xOff, int32_t yOff ) const { return mPtr[mBlueOff + xOff * mInc + yOff * mRowInc]; } 00237 T& a( int32_t xOff, int32_t yOff ) const { return mPtr[mAlphaOff + xOff * mInc + yOff * mRowInc]; } 00238 00239 T& rClamped( int32_t xOff, int32_t yOff ) const 00240 { xOff = std::min(std::max(mX + xOff, mStartX),mEndX - 1) - mX; yOff = std::min(std::max( mY + yOff, mStartY ), mEndY - 1) - mY; 00241 return mPtr[mRedOff + xOff * mInc + yOff * mRowInc]; } 00242 T& gClamped( int32_t xOff, int32_t yOff ) const 00243 { xOff = std::min(std::max(mX + xOff, mStartX),mEndX - 1) - mX; yOff = std::min(std::max( mY + yOff, mStartY ), mEndY - 1) - mY; 00244 return mPtr[mGreenOff + xOff * mInc + yOff * mRowInc]; } 00245 T& bClamped( int32_t xOff, int32_t yOff ) const 00246 { xOff = std::min(std::max(mX + xOff, mStartX),mEndX - 1) - mX; yOff = std::min(std::max( mY + yOff, mStartY ), mEndY - 1) - mY; 00247 return mPtr[mBlueOff + xOff * mInc + yOff * mRowInc]; } 00248 T& aClamped( int32_t xOff, int32_t yOff ) const 00249 { xOff = std::min(std::max(mX + xOff, mStartX),mEndX - 1) - mX; yOff = std::min(std::max( mY + yOff, mStartY ), mEndY - 1) - mY; 00250 return mPtr[mAlphaOff + xOff * mInc + yOff * mRowInc]; } 00251 00252 const int32_t x() const { return mX; } 00253 const int32_t y() const { return mY; } 00254 Vec2i getPos() const { return Vec2i( mX, mY ); } 00255 00256 bool pixel() { 00257 ++mX; 00258 mPtr += mInc; 00259 return mX < mEndX; 00260 } 00261 00262 bool line() { 00263 ++mY; 00264 mLinePtr += mRowInc; 00265 mPtr = reinterpret_cast<T*>( mLinePtr ); 00266 // in order to be at the right place after an initial call to pixel(), we need to back up one pixel 00267 mPtr -= mInc; 00268 mX = mStartX - 1; 00269 return mY < mEndY; 00270 } 00271 00272 int32_t getWidth() const { return mWidth; } 00273 int32_t getHeight() const { return mHeight; } 00274 00275 uint8_t mRedOff, mGreenOff, mBlueOff, mAlphaOff, mInc; 00276 uint8_t *mLinePtr; 00277 T *mPtr; 00278 int32_t mRowInc, mWidth, mHeight; 00279 int32_t mX, mY, mStartX, mStartY, mEndX, mEndY; 00280 }; 00281 00282 class ConstIter { 00283 public: 00284 ConstIter( const Iter &iter ) { 00285 mRedOff = iter.mRedOff; 00286 mGreenOff = iter.mGreenOff; 00287 mBlueOff = iter.mBlueOff; 00288 mAlphaOff = iter.mAlphaOff; 00289 mInc = iter.mInc; 00290 mRowInc = iter.mRowInc; 00291 mWidth = iter.mWidth; 00292 mHeight = iter.mHeight; 00293 mLinePtr = iter.mLinePtr; 00294 mPtr = iter.mPtr; 00295 mStartX = iter.mStartX; 00296 mX = iter.mX; 00297 mStartY = iter.mStartY; 00298 mY = iter.mY; 00299 mEndX = iter.mEndX; 00300 mEndY = iter.mEndY; 00301 } 00302 00303 ConstIter( const SurfaceT<T> &SurfaceT, const Area &area ) 00304 : mRedOff( SurfaceT.getRedOffset() ), mGreenOff( SurfaceT.getGreenOffset() ), 00305 mBlueOff( SurfaceT.getBlueOffset() ), mAlphaOff( SurfaceT.getAlphaOffset() ), 00306 mInc( SurfaceT.getPixelInc() ), mRowInc( SurfaceT.getRowBytes() ) 00307 { 00308 Area clippedArea( area.getClipBy( SurfaceT.getBounds() ) ); 00309 mWidth = clippedArea.getWidth(); 00310 mHeight = clippedArea.getHeight(); 00311 mLinePtr = reinterpret_cast<const uint8_t*>( SurfaceT.getData( clippedArea.getUL() ) ); 00312 mPtr = reinterpret_cast<const T*>( mLinePtr ); 00313 mStartX = mX = clippedArea.getX1(); 00314 mStartY = mY = clippedArea.getY1(); 00315 mEndX = clippedArea.getX2(); 00316 mEndY = clippedArea.getY2(); 00317 // in order to be at the right place after an initial call to line(), we need to back up one line 00318 mY = clippedArea.getY1() - 1; 00319 mLinePtr -= mRowInc; 00320 } 00321 00322 const T& r() const { return mPtr[mRedOff]; } 00323 const T& g() const { return mPtr[mGreenOff]; } 00324 const T& b() const { return mPtr[mBlueOff]; } 00325 const T& a() const { return mPtr[mAlphaOff]; } 00326 00327 const T& r( int32_t xOff, int32_t yOff ) const { return mPtr[mRedOff + xOff * mInc + yOff * mRowInc]; } 00328 const T& g( int32_t xOff, int32_t yOff ) const { return mPtr[mGreenOff + xOff * mInc + yOff * mRowInc]; } 00329 const T& b( int32_t xOff, int32_t yOff ) const { return mPtr[mBlueOff + xOff * mInc + yOff * mRowInc]; } 00330 const T& a( int32_t xOff, int32_t yOff ) const { return mPtr[mAlphaOff + xOff * mInc + yOff * mRowInc]; } 00331 00332 const T& rClamped( int32_t xOff, int32_t yOff ) const 00333 { xOff = std::min(std::max(mX + xOff, mStartX),mEndX - 1) - mX; yOff = std::min(std::max( mY + yOff, mStartY ), mEndY - 1) - mY; 00334 return mPtr[mRedOff + xOff * mInc + yOff * mRowInc]; } 00335 const T& gClamped( int32_t xOff, int32_t yOff ) const 00336 { xOff = std::min(std::max(mX + xOff, mStartX),mEndX - 1) - mX; yOff = std::min(std::max( mY + yOff, mStartY ), mEndY - 1) - mY; 00337 return mPtr[mGreenOff + xOff * mInc + yOff * mRowInc]; } 00338 const T& bClamped( int32_t xOff, int32_t yOff ) const 00339 { xOff = std::min(std::max(mX + xOff, mStartX),mEndX - 1) - mX; yOff = std::min(std::max( mY + yOff, mStartY ), mEndY - 1) - mY; 00340 return mPtr[mBlueOff + xOff * mInc + yOff * mRowInc]; } 00341 const T& aClamped( int32_t xOff, int32_t yOff ) const 00342 { xOff = std::min(std::max(mX + xOff, mStartX),mEndX - 1) - mX; yOff = std::min(std::max( mY + yOff, mStartY ), mEndY - 1) - mY; 00343 return mPtr[mAlphaOff + xOff * mInc + yOff * mRowInc]; } 00344 00345 const int32_t x() const { return mX; } 00346 const int32_t y() const { return mY; } 00347 Vec2i getPos() const { return Vec2i( mX, mY ); } 00348 00349 bool pixel() { 00350 ++mX; 00351 mPtr += mInc; 00352 return mX < mEndX; 00353 } 00354 00355 bool line() { 00356 ++mY; 00357 mLinePtr += mRowInc; 00358 mPtr = reinterpret_cast<const T*>( mLinePtr ); 00359 // in order to be at the right place after an initial call to pixel(), we need to back up one pixel 00360 mPtr -= mInc; 00361 mX = mStartX - 1; 00362 return mY < mEndY; 00363 } 00364 00365 int32_t getWidth() const { return mWidth; } 00366 int32_t getHeight() const { return mHeight; } 00367 00368 uint8_t mRedOff, mGreenOff, mBlueOff, mAlphaOff, mInc; 00369 const uint8_t *mLinePtr; 00370 const T *mPtr; 00371 int32_t mRowInc, mWidth, mHeight; 00372 int32_t mX, mY, mStartX, mStartY, mEndX, mEndY; 00373 }; 00374 00375 Iter getIter() { return Iter( *this, this->getBounds() ); } 00376 Iter getIter( const Area &area ) { return Iter( *this, area ); } 00377 ConstIter getIter() const { return ConstIter( *this, this->getBounds() ); } 00378 ConstIter getIter( const Area &area ) const { return ConstIter( *this, area ); } 00379 }; 00380 00381 class SurfaceExc : public std::exception { 00382 virtual const char* what() const throw() { 00383 return "Surface exception"; 00384 } 00385 }; 00386 00387 class SurfaceConstraintsExc : public SurfaceExc { 00388 virtual const char* what() const throw() { 00389 return "Surface exception: does not conform to expected SurfaceConstraints"; 00390 } 00391 }; 00392 00393 typedef SurfaceT<uint8_t> Surface; 00394 typedef SurfaceT<uint8_t> Surface8u; 00395 typedef SurfaceT<float> Surface32f; 00396 00397 } // namespace cinder