#ifndef _COPYONWRITEBITMAP_HPP #define _COPYONWRITEBITMAP_HPP class CopyOnWriteBitmap : private CopyOnWrite { friend class GpBitmap; private: // Constructors CopyOnWriteBitmap(const WCHAR* filename); CopyOnWriteBitmap(IStream* stream); CopyOnWriteBitmap(INT width, INT height, PixelFormatID format); CopyOnWriteBitmap(INT width, INT height, PixelFormatID format, GpGraphics * graphics); CopyOnWriteBitmap( INT width, INT height, INT stride, // negative for bottom-up bitmaps PixelFormatID format, BYTE * scan0 ); CopyOnWriteBitmap( BITMAPINFO* gdiBitmapInfo, VOID* gdiBitmapData, BOOL ownBitmapData ); CopyOnWriteBitmap(IDirectDrawSurface7 *surface); static VOID CheckValid(CopyOnWriteBitmap *& p) { if ((p == NULL) || (!p->IsValid())) { delete p; p = NULL; } } static CopyOnWriteBitmap * Create(const WCHAR* filename) { CopyOnWriteBitmap * newBitmap = new CopyOnWriteBitmap(filename); CheckValid(newBitmap); return newBitmap; } static CopyOnWriteBitmap * Create(IStream* stream) { CopyOnWriteBitmap * newBitmap = new CopyOnWriteBitmap(stream); CheckValid(newBitmap); return newBitmap; } static CopyOnWriteBitmap * Create(INT width, INT height, PixelFormatID format) { CopyOnWriteBitmap * newBitmap = new CopyOnWriteBitmap(width, height, format); CheckValid(newBitmap); return newBitmap; } static CopyOnWriteBitmap * Create(INT width, INT height, PixelFormatID format, GpGraphics * graphics) { CopyOnWriteBitmap * newBitmap = new CopyOnWriteBitmap(width, height, format, graphics); CheckValid(newBitmap); return newBitmap; } static CopyOnWriteBitmap * Create( INT width, INT height, INT stride, // negative for bottom-up bitmaps PixelFormatID format, BYTE * scan0 ) { CopyOnWriteBitmap * newBitmap = new CopyOnWriteBitmap(width, height, stride, format, scan0); CheckValid(newBitmap); return newBitmap; } static CopyOnWriteBitmap * Create( BITMAPINFO* gdiBitmapInfo, VOID* gdiBitmapData, BOOL ownBitmapData ) { CopyOnWriteBitmap * newBitmap = new CopyOnWriteBitmap(gdiBitmapInfo, gdiBitmapData, ownBitmapData); CheckValid(newBitmap); return newBitmap; } static CopyOnWriteBitmap * Create(IDirectDrawSurface7 *surface) { CopyOnWriteBitmap * newBitmap = new CopyOnWriteBitmap(surface); CheckValid(newBitmap); return newBitmap; } CopyOnWriteBitmap* Clone( const GpRect* rect, PixelFormatID format = PixelFormat32bppPARGB ) const; virtual CopyOnWrite * Clone() const { return Clone(NULL, PixelFormatDontCare); } CopyOnWriteBitmap* CloneColorAdjusted( GpRecolor * recolor, ColorAdjustType type = ColorAdjustTypeDefault ) const; GpStatus GetEncoderParameterListSize( IN CLSID* clsidEncoder, OUT UINT* size ); GpStatus GetEncoderParameterList( IN CLSID* clsidEncoder, IN UINT size, OUT EncoderParameters* pBuffer ); GpStatus SaveToStream( IStream* stream, CLSID* clsidEncoder, EncoderParameters* encoderParams ); GpStatus SaveToFile( const WCHAR* filename, CLSID* clsidEncoder, EncoderParameters* encoderParams ); GpStatus DoSave( IStream* stream, const WCHAR* filename, CLSID* clsidEncoder, EncoderParameters* encoderParams ); GpStatus SaveAdd( const EncoderParameters* encoderParams ); GpStatus SaveAdd( CopyOnWriteBitmap* newBits, const EncoderParameters* encoderParams ); // Dispose the bitmap object VOID Dispose() { if (InterlockedDecrement(&ObjRefCount) <= 0) { this->Release(); } } // Get bitmap information VOID GetImageInfo(ImageInfo * imageInfo) { ASSERT(imageInfo != NULL); GpMemcpy(imageInfo, &SrcImageInfo, sizeof(ImageInfo)); } CopyOnWriteBitmap* GetThumbnail(UINT thumbWidth, UINT thumbHeight, GetThumbnailImageAbort callback, VOID *callbackData); GpStatus GetFrameCount(const GUID* dimensionID, UINT* count) const; GpStatus GetFrameDimensionsCount(OUT UINT* count) const; GpStatus GetFrameDimensionsList(OUT GUID* dimensionIDs, IN UINT count) const; GpStatus SelectActiveFrame(const GUID* dimensionID, UINT frameIndex); GpStatus GetPalette(ColorPalette *palette, INT size); GpStatus SetPalette(ColorPalette *palette); INT GetPaletteSize(); GpStatus GetTransparencyHint(DpTransparency* transparency); GpStatus SetTransparencyHint(DpTransparency transparency); GpStatus GetTransparencyFlags(DpTransparency* transparency, PixelFormatID loadFormat =PixelFormatDontCare, BYTE* minA = NULL, BYTE* maxA = NULL); // Property related functions GpStatus GetPropertyCount(UINT* numOfProperty); GpStatus GetPropertyIdList(UINT numOfProperty, PROPID* list); GpStatus GetPropertyItemSize(PROPID propId, UINT* size); GpStatus GetPropertyItem(PROPID propId,UINT propSize, PropertyItem* buffer); GpStatus GetPropertySize(UINT* totalBufferSize,UINT* numProperties); GpStatus GetAllPropertyItems(UINT totalBufferSize, UINT numProperties, PropertyItem* allItems); GpStatus RemovePropertyItem(PROPID propId); GpStatus SetPropertyItem(PropertyItem* item); // Check if the CopyOnWriteBitmap object is valid virtual BOOL IsValid() const { return (State != Invalid); } // Retrieve bitmap data GpStatus LockBits( const GpRect* rect, UINT flags, PixelFormatID pixelFormat, BitmapData* bmpdata, INT width = 0, INT height = 0 ) const; // Does not change the image GpStatus UnlockBits( BitmapData* bmpdata, BOOL Destroy=FALSE ) const; // Get and set pixel on the bitmap. GpStatus GetPixel(INT x, INT y, ARGB *color); GpStatus SetPixel(INT x, INT y, ARGB color); // Derive an HDC for interop on top of the bitmap object HDC GetHdc(); VOID ReleaseHdc(HDC hdc); // Serialization UINT GetDataSize() const; GpStatus GetData(IStream * stream) const; GpStatus SetData(const BYTE * dataBuffer, UINT size); GpStatus GetCompressedData( DpCompressedData * compressed_data, BOOL getJPEG = TRUE, BOOL getPNG = TRUE, HDC hdc = (HDC)NULL); GpStatus DeleteCompressedData( DpCompressedData * compressed_data) ; // Image transform GpStatus RotateFlip( RotateFlipType rfType ); // Color adjust GpStatus ColorAdjust( GpRecolor * recolor, ColorAdjustType type ); GpStatus ColorAdjust( GpRecolor * recolor, PixelFormatID pixfmt, DrawImageAbort callback, VOID *callbackData ); GpStatus GetPixelFormatID(PixelFormatID* pixfmt); enum { Invalid = 0, // bitmap object is invalid ImageRef = 1, // contains a reference only (e.g. filename) ExtStream = 2, // contains a reference to a stream DecodedImg = 3, // contains a decoded image object, // but it's not decoded yet - name is misleading. MemBitmap = 4 // contains an in-memory bitmap object }; GpStatus SetResolution(REAL xdpi, REAL ydpi); GpStatus PreDraw( INT numPoints, GpPointF *dstPoints, GpRectF *srcRect, INT numBitsPerPixel ); // Interop: static GpStatus CreateFromHBITMAP( HBITMAP hbm, HPALETTE hpal, CopyOnWriteBitmap** bitmap ); GpStatus CreateHBITMAP(HBITMAP *phbm, ARGB background); GpStatus Recolor( GpRecolor *recolor, CopyOnWriteBitmap **dstBitmap, DrawImageAbort callback, VOID *callbackData, GpRect *rect = NULL ); GpStatus ICMFrontEnd( CopyOnWriteBitmap **dstBitmap, DrawImageAbort callback, VOID *callbackData, GpRect *rect = NULL ); static GpStatus CreateFromHICON( HICON hicon, CopyOnWriteBitmap** bitmap ); GpStatus CreateHICON(HICON *phicon); static GpStatus CreateFromResource( HINSTANCE hInstance, LPWSTR lpBitmapName, CopyOnWriteBitmap** bitmap ); private: CopyOnWriteBitmap(GpMemoryBitmap* membmp); mutable INT State; // current state of the bitmap object mutable LONG ObjRefCount; // object reference count used for LockBits WCHAR* Filename; IStream* Stream; mutable GpDecodedImage* Img; mutable GpMemoryBitmap* Bmp; UINT CurrentFrameIndex; // Frame index, zero based VOID* cleanupBitmapData; // Bitmap(BITMAPINFO*, ...) ctor will // set this if dtor should cleanup buffer IImageEncoder* EncoderPtr; // Pointer to encoder pointer for saving // multi-frame images // Note: CopyOnWriteBitmap has to hold this pointer // and do the close later. This is because // 1) Both Bmp and Img will be destroied when // navigating among frames // 2) It is possible that sometime we call // Img->SaveAppend and sometime for // Bmp->SaveAppend, depends on if the frame is // dirty or not. // It make sense that 1 CopyOnWriteBitmap talks to 1 // encoder at a time BOOL SpecialJPEGSave; // TRUE if do special lossless JPEG transform BOOL ICMConvert; // TRUE if we should do ICM on this bitmap // We need to know how to handle the page transform when it is set // to UnitDisplay. If Display is TRUE (which is the default), then // the page transform will be the identity, which is what we want // most of the time. The only exception is when the image is // derived from a non-display graphics. BOOL Display; // Set UnitDisplay to identity transform? REAL XDpiOverride; // if non-zero, replaces native dpi REAL YDpiOverride; // if non-zero, replaces native dpi // supports scan interface to CopyOnWriteBitmap // when drawing to bitmap via a Graphics mutable BOOL DirtyFlag; // TRUE if the image bits got modified ImageInfo SrcImageInfo; // Image info for the source image mutable PixelFormatID PixelFormatInMem; // Pixel format in the memory // For example, if the source image is 4 bpp, we // load it into memory as 32 PARGB. This // variable will be set to 32PARGB. mutable BOOL HasChangedRequiredPixelFormat; // Flag to remember if we have hacked the color // formats or not in LoadIntoMemory(). Then this // format will be restored in ICMFrontEnd() if // this flag is TRUE struct // Interop data (information used to return { // an HDC that can draw into this CopyOnWriteBitmap) HDC Hdc; HBITMAP Hbm; VOID* Bits; INT_PTR Stride; INT Width; INT Height; } InteropData; VOID FreeData(); // called by destructor // Destructor // We don't want apps to use delete operator directly. // Instead, they should use Dispose method so that // we can take care of reference counting. ~CopyOnWriteBitmap(); // Initialize the bitmap object to its initial state VOID InitDefaults() { State = Invalid; ObjRefCount = 1; Filename = NULL; Stream = NULL; Img = NULL; Bmp = NULL; InteropData.Hdc = NULL; InteropData.Hbm = NULL; CurrentFrameIndex = 0; cleanupBitmapData = NULL; DirtyFlag = FALSE; XDpiOverride = 0.0f; // if non-zero, replaces native dpi YDpiOverride = 0.0f; // if non-zero, replaces native dpi ICMConvert = FALSE; // default is don't do ICM (it's slow) SpecialJPEGSave = FALSE; // We must always treat the bitmap as if it is a display so that // the default page transform (in a graphics constructed from // the image) is the identity. The only time we don't do this // is if the bitmap is contructed from a graphics that is not // associated with a display. In that case, we want the image // to inherit the display property from the graphics so that // drawing to the image and drawing to the original graphics // will work the same, i.e. will have a similar page transform. Display = TRUE; EncoderPtr = NULL; PixelFormatInMem = PixelFormatUndefined; HasChangedRequiredPixelFormat = FALSE; GpMemset(&SrcImageInfo, 0, sizeof(ImageInfo)); } // Convert the pixel data of a bitmap object // to the specified format GpStatus ConvertFormat(PixelFormatID format, DrawImageAbort callback = NULL, VOID *callbackData = NULL ); // Perform color adjustment by the lower level codec if it can do it GpStatus ColorAdjustByCodec( GpRecolor * recolor, DrawImageAbort callback, VOID *callbackData ); // Set decode parameters for icons GpStatus SetIconParameters( INT numPoints, GpPointF *dstPoints, GpRectF *srcRect, INT numBitsPerPixel ); CopyOnWriteBitmap() { InitDefaults(); } // Dereference the stream or file pointer and promote this bitmap object // to at least DecodedImg state. GpStatus CopyOnWriteBitmap::DereferenceStream() const; // Load bitmap image into memory // width and height are the suggested width and height to decode into. // zero means use the source width and height. GpStatus LoadIntoMemory( PixelFormatID format = PixelFormat32bppPARGB, DrawImageAbort callback = NULL, VOID *callbackData = NULL, INT width = 0, INT height = 0 ) const; VOID SetDirtyFlag(BOOL flag) const { DirtyFlag = flag; } BOOL IsDirty() const { return DirtyFlag; } VOID TerminateEncoder() { if ( EncoderPtr != NULL ) { EncoderPtr->TerminateEncoder(); EncoderPtr->Release(); EncoderPtr = NULL; } } GpStatus ValidateMultiFrameSave(); GpStatus ParseEncoderParameter( const EncoderParameters* encoderParams, BOOL* pfIsMultiFrameSave, BOOL* pfSpecialJPEG, RotateFlipType* rfType ); GpStatus TransformThumbanil( CLSID* clsidEncoder, EncoderParameters* encoderParams, PropertyItem **ppOriginalItem ); VOID CacheImageInfo(HRESULT hr); private: GpStatus SaveAppend( const EncoderParameters* encoderParams, IImageEncoder* EncoderPtr ); }; #endif