/*
* Copyright (C) =USTC= Fu Li
*
* Author : Fu Li
* Create : 2001-4-27
* Home : http://www.crazy-bit.com/
* Mail : crazybit@263.net
* History :
*/
#ifndef __PCL_OBJECT_IMAGE__2001_04_27__H__
#define __PCL_OBJECT_IMAGE__2001_04_27__H__
#include "ObjBase.h"
#include "FColor.h"
#include "ObjProgress.h"
#include "oxo_helper.h"
#include "imagefile/Interface_ImageHandle.h"
#include "imagefile/Interface_ImageHandleFactory.h"
#include "pixelprocessor/Interface_PixelProcessor.h"
//=============================================================================
/**
* Basic image object.
@verbatim
1) the origin (0,0) of image lies at left-top point.
2) all coordinate in this class (POINT, RECT...) relative to image's (0,0).
3) after image be created, pixel's data are automatically initialized to 0. and if
image's bpp <= 8, a gray palette will be set automatically.
4) if you want to use Save / Load, you must call SetImageHandleFactory to set a handle factory.
@endverbatim
*/
class FCObjImage : public FCObjGraph
{
public:
/**
* @name Constructor.
*/
//@{
/// Create an empty image.
FCObjImage () ;
/// Copy constructor.
FCObjImage (const FCObjImage& img) ;
/// Create an image.
FCObjImage (int nWidth, int nHeight, int nColorBit) ;
/// Create an image from a image file.
FCObjImage (const char* szFileName) ;
FCObjImage& operator= (const FCObjImage& img) ;
virtual ~FCObjImage() {Destroy();}
//@}
/**
* Serialize image object in memory (bpp > 8).
* @see FCObject::Serialize
*/
virtual int Serialize (bool bSave, BYTE* pSave) ;
/**
* @name Create/Destroy image.
*/
//@{
/**
* Create image.
@verbatim
hint:
1) if image's bpp <= 8, a gray palette will be set automatically.
2) after image be created, all pixels are automatically initialized to 0.
@endverbatim
*/
bool Create (const BITMAPINFOHEADER* pBmif) ;
/// Create image.
bool Create (int nWidth, int nHeight, int nColorBit) ;
/// Destroy image object.
void Destroy() ;
//@}
/**
* @name Basic attributes.
*/
//@{
/// Is current image object is valid.
bool IsValidImage() const {return (m_pByte != 0);}
/// this function don't do boundary check, so crash if iLine exceed.
BYTE* GetBits (int iLine) const ;
/// this function don't do boundary check, so crash if y exceed.
BYTE* GetBits (int x, int y) const ;
/// Get image's pixel start-address (address of left-bottom point).
BYTE* GetMemStart() const {return m_pByte;}
/// Pixel width of image.
int Width() const {return m_DibInfo.biWidth;}
/// Pixel height of image.
int Height() const {return m_DibInfo.biHeight;}
/// Image's bpp (bit per pixel), available : 1,4,8,16,24,32.
WORD ColorBits() const {return m_DibInfo.biBitCount;}
/// Bytes pitch between two lines (the value is 4-bytes rounded).
int GetPitch() const {return 4 * ((Width() * ColorBits() + 31) / 32);}
/**
* Create BITMAPINFOHEADER struct.
* palette(<=8bit) or 3-bit-fields(16bit) was appended.
* you must use delete[] to delete returned point, high recommended to use PCL_Array wrap returned pointer.
@code
PCL_array imgInfo (img.NewImgInfoWithPalette()) ;
imgInfo.get()->biWidth ;
pPalette = imgInfo.get() + 1 ;
@endcode
*/
BITMAPINFOHEADER* NewImgInfoWithPalette() const ;
/// bound rc into image.
/// @param rc : coordinate in image.
void BoundRect (RECT& rc) const ;
//@}
/**
* @name Pixel access.
*/
//@{
/// Is point (x,y) in image.
bool IsInside (int x, int y) const {return (x>=0) && (x=0) && (yzero-based color table index.
bool GetColorTable (int iFirstIndex, int iNumber, RGBQUAD* pColors) const ;
/// Set palette of image.
/// the iFirstIndex is A zero-based color table index.
bool SetColorTable (int iFirstIndex, int iNumber, const RGBQUAD* pColors) ;
/// Duplicate palette from imgSrc.
void CopyPalette (const FCObjImage& imgSrc) ;
//@}
/**
* @name Color convert.
*/
//@{
/// Convert current image's bpp to 24.
void ConvertTo24Bit() {__ConvertToTrueColor(24);}
/// Convert current image's bpp to 32 (alpha channel will set 0xFF).
void ConvertTo32Bit() {__ConvertToTrueColor(32);}
//@}
/**
* @name Channel operations (bpp == 32)
*/
//@{
/// Get alpha channel of 32bpp image (imgAlpha's bpp == 8).
void GetAlphaChannel (FCObjImage* imgAlpha) const ;
/// Set alpha channel of 32bpp image (alpha8's bpp == 8).
void AppendAlphaChannel (const FCObjImage& alpha8) ;
/// Set 32bpp image's alpha channel value.
void SetAlphaChannelValue (int nValue) ;
//@}
/**
* @name Basic transform.
*/
//@{
/// Get a block of image (bpp >= 8).
/// if rcBlock exceed image, it will be bounded in image.
bool GetSubBlock (FCObjImage* SubImg, RECT rcBlock) const ;
/// Cover Img on position (x,y) (bpp >= 8).
bool CoverBlock (const FCObjImage& Img, int x, int y) ;
void TileBlock (const FCObjImage& Img, int x, int y) ;
/// Alpha blend image (bpp >= 24).
void AlphaBlend (const FCObjImage& img32, const RECT& rcDest, const RECT& rcSrc, int nAlphaPercent) ;
/// Combine image (bpp == 32).
void CombineImage (const FCObjImage& img32, int x=0, int y=0, int nAlphaPercent=100) ;
void LogicalBlend (const FCObjImage & MaskImg, LOGICAL_OP LogOP, int x=0, int y=0) ;
/// bpp >= 8.
void ExpandFrame (bool bCopyEdge, int iLeft, int iTop, int iRight, int iBottom) ; // add frame
/// bpp >= 8.
void Stretch (int nNewWidth, int nNewHeight) ; // stretch
/// bpp >= 24.
void Stretch_Smooth (int nNewWidth, int nNewHeight, FCObjProgress * progress = NULL) ; // stretch
/// Perform a processor. more detail refer PixelProcessorBase.h
void SinglePixelProcessProc (FCInterface_PixelProcess& rProcessor, FCObjProgress* pProgress=NULL) ;
//@}
/**
* @name Read/Write image file.
*/
//@{
/**
* Set image handle factory.
* You must use new to create object and after the object be setted, you can't delete it later.
* it's a global setting, you can set at startup of program.
@code
FCObjImage::SetImageHandleFactory (new FCImageHandleFactory_FreeImage) ;
@endcode
*/
static void SetImageHandleFactory (FCImageHandleFactory* pFactory) {__ManageImageHandleFactory(false,pFactory);}
/// Get image handle factory.
static FCImageHandleFactory* GetImageHandleFactory() {return __ManageImageHandleFactory(true,0);}
/**
* Load image file.
* this function determine image format by file's ext name.
* @param pProperty : optional, you can pass NULL if you don't care.
*/
bool Load (const char* szFileName, FCImageProperty* pProperty = 0) ;
/**
* Load image from memory.
* @param pProperty : optional, you can pass NULL if you don't care.
*/
bool Load (BYTE* pStart, int nMemSize, IMAGE_TYPE imgType, FCImageProperty* pProperty = 0) ;
/// Load from DIB format memory.
bool LoadDIBStream (const void* pDIB, int nBufferSize) ;
/**
* Save image to file.
* this function determine image format by file's ext name.
* @param nFlag : depends on the image format.
@verbatim
JPG : compress quality [1..100], default(82)
GIF : transparent color's index in palette
TGA : 1(use RLE compress) / -1(not use), default(not use)
@endverbatim
*/
bool Save (const char* szFileName, int nFlag = -1) const
{
// save property
FCImageProperty imgProp ;
imgProp.SetPropertyValue (PROPERTY_TAG_SAVE_FLAG, FCOXOHelper::X2A(nFlag).c_str()) ;
return Save (szFileName, imgProp) ;
}
/// Save image to file.
bool Save (const char* szFileName, const FCImageProperty& rProp) const ;
//@}
private:
BITMAPINFOHEADER m_DibInfo ; // DIB Info
BYTE * m_pByte ; // Bitmap start bits, from left-bottom start
BYTE ** m_ppLine ; // Line-pointer, ppLine[] ; from top to bottom
DWORD m_dwBitFields[3] ; // only 16bit image, order R,G,B
RGBQUAD * m_pPalette ; // palette
private:
void __SetGrayPalette() ;
void __InitClassMember() ; // initialize the member variant
void __ConvertToTrueColor (int iColor) ; // iColor == 24 or 32
static RGBQUAD __Split16Bit_565 (WORD wPixel) ;
static RGBQUAD __Split16Bit_555 (WORD wPixel) ;
static void __FillImageRect (const FCObjImage& img, const RECT& rcBlock, const void* pSrc) ;
static FCImageHandleFactory* __ManageImageHandleFactory (bool bGet, FCImageHandleFactory* pFactory) ;
} ;
//=============================================================================
// inline Implement
//=============================================================================
inline void FCObjImage::__InitClassMember()
{
memset (&m_DibInfo, 0, sizeof(m_DibInfo)) ;
m_dwBitFields[0]=m_dwBitFields[1]=m_dwBitFields[2]=0 ;
m_pByte=0 ; m_ppLine=0 ; m_pPalette=0 ;
}
//-----------------------------------------------------------------------------
inline FCObjImage::FCObjImage()
{
__InitClassMember() ;
}
inline FCObjImage::FCObjImage (const FCObjImage& img)
{
__InitClassMember() ;
*this = img ;
}
inline FCObjImage::FCObjImage (int nWidth, int nHeight, int nColorBit)
{
__InitClassMember() ;
Create (nWidth, nHeight, nColorBit) ;
}
inline FCObjImage::FCObjImage (const char* szFileName)
{
__InitClassMember() ;
Load (szFileName) ;
}
//-----------------------------------------------------------------------------
inline bool FCObjImage::Create (const BITMAPINFOHEADER* pBmif)
{
// unsupported store format
if (!pBmif || (pBmif->biHeight <= 0) || (pBmif->biWidth <= 0))
{assert(false); return false;}
if (!((pBmif->biCompression == BI_RGB) || (pBmif->biCompression == BI_BITFIELDS)))
{assert(false); return false;}
switch (pBmif->biBitCount) // validate bpp
{
case 1 :
case 4 :
case 8 :
case 16 :
case 24 :
case 32 : break ;
default : assert(false); return false;
}
if (IsValidImage())
Destroy() ;
// init struct
memset (&m_DibInfo, 0, sizeof(m_DibInfo)) ;
m_DibInfo.biSize = sizeof(BITMAPINFOHEADER) ;
m_DibInfo.biWidth = pBmif->biWidth ;
m_DibInfo.biHeight = pBmif->biHeight ;
m_DibInfo.biPlanes = 1 ;
m_DibInfo.biBitCount = pBmif->biBitCount ;
m_DibInfo.biCompression = pBmif->biCompression ;
m_DibInfo.biXPelsPerMeter = pBmif->biXPelsPerMeter ;
m_DibInfo.biYPelsPerMeter = pBmif->biYPelsPerMeter ;
// now flag BI_BITFIELDS is only valid in 16bit image
if (pBmif->biBitCount == 16)
{
m_dwBitFields[0] = MASK16_RED_555 ; // 16-bit default format : 5-5-5
m_dwBitFields[1] = MASK16_GREEN_555 ;
m_dwBitFields[2] = MASK16_BLUE_555 ;
if (pBmif->biCompression == BI_BITFIELDS) // custom
memcpy (m_dwBitFields, pBmif + 1, 12) ;
}
else
m_DibInfo.biCompression = BI_RGB ; // i think it's unnecessary to use mask in 32bit image
// create pixel buffer, pixel must must must initialized to zero !!!
m_pByte = FCOXOHelper::ZeroMalloc (GetPitch()*Height()) ;
assert (((int)m_pByte % 4) == 0) ; // DWORD align
// create a line pointer, to accelerate pixel access
m_ppLine = (BYTE **) new BYTE [sizeof(BYTE*) * Height()] ;
const int nPitch = GetPitch() ;
m_ppLine[0] = m_pByte + (Height() - 1) * nPitch ;
for (int y = 1 ; y < Height() ; y++)
m_ppLine[y] = m_ppLine[y - 1] - nPitch ;
// 8bit color image default set a gray palette
if (ColorBits() <= 8)
{
m_pPalette = new RGBQUAD[1 << ColorBits()] ;
__SetGrayPalette() ;
}
return true ;
}
//-----------------------------------------------------------------------------
inline bool FCObjImage::Create (int nWidth, int nHeight, int nColorBit)
{
BITMAPINFOHEADER bmih ;
memset (&bmih, 0, sizeof(bmih)) ;
bmih.biWidth = nWidth ;
bmih.biHeight = nHeight ;
bmih.biBitCount = nColorBit ;
bmih.biCompression = BI_RGB ;
return this->Create (&bmih) ;
}
//-----------------------------------------------------------------------------
inline void FCObjImage::Destroy()
{
if (m_ppLine)
delete[] m_ppLine ;
FCOXOHelper::ZeroFree (m_pByte) ;
if (m_pPalette)
delete[] m_pPalette ;
__InitClassMember() ;
}
//-----------------------------------------------------------------------------
inline BYTE* FCObjImage::GetBits (int iLine) const
{
assert (IsInside(0,iLine)) ;
return m_ppLine[iLine] ;
}
//-----------------------------------------------------------------------------
inline BYTE* FCObjImage::GetBits (int x, int y) const
{
assert (IsInside(x,y)) ;
if (ColorBits() == 32)
return (m_ppLine[y] + x * 4) ;
if (ColorBits() == 8)
return (m_ppLine[y] + x) ;
return (m_ppLine[y] + x * ColorBits() / 8) ;
}
//-----------------------------------------------------------------------------
inline BITMAPINFOHEADER* FCObjImage::NewImgInfoWithPalette() const
{
// prepare info
const int nColorNum = 1 << ColorBits(),
nPalBytes = ((ColorBits() <= 8) ? (4*nColorNum) : 0) ;
BITMAPINFOHEADER * pBmfh = (BITMAPINFOHEADER*) new BYTE[16 + sizeof(BITMAPINFOHEADER) + nPalBytes] ;
*pBmfh = m_DibInfo ;
// append palette(<=8bit) or bit-fields(16bit)
if (ColorBits() <= 8)
GetColorTable (0, nColorNum, (RGBQUAD*)(pBmfh + 1)) ;
else
memcpy (pBmfh + 1, m_dwBitFields, 12) ;
return pBmfh ;
}
//-----------------------------------------------------------------------------
inline void FCObjImage::BoundRect (RECT& rc) const
{
RECT rcImg = {0, 0, Width(), Height()} ;
::IntersectRect (&rc, &rcImg, &rc) ;
}
//-----------------------------------------------------------------------------
inline DWORD FCObjImage::GetPixelData (int x, int y) const
{
if (!IsInside(x, y))
{assert(false); return 0;}
const BYTE * pPixel = GetBits (x,y) ;
switch (ColorBits())
{
case 1 : return 0x01 & (*pPixel >> (7 - (x & 7))) ;
case 4 : return 0x0F & (*pPixel >> (x & 1 ? 0 : 4)) ;
case 8 : return *(BYTE*)pPixel ;
case 16 : return *(WORD*)pPixel ;
case 24 :
{
DWORD dwrgb = 0 ;
FCColor::CopyPixel (&dwrgb, pPixel, 3) ;
return dwrgb ;
}
case 32 : return *(DWORD*)pPixel ;
default : assert(false) ;
}
return 0 ;
}
//-----------------------------------------------------------------------------
inline void FCObjImage::SetPixelData (int x, int y, DWORD dwPixel)
{
if (!IsInside(x, y))
{assert(false); return;}
BYTE * pPixel = GetBits (x,y) ;
switch (ColorBits())
{
case 1 : *pPixel &= ~(1 << (7 - (x & 7))) ;
*pPixel |= dwPixel << (7 - (x & 7)) ;
break ;
case 4 : *pPixel &= 0x0F << (x & 1 ? 4 : 0) ;
*pPixel |= dwPixel << (x & 1 ? 0 : 4) ;
break ;
case 8 :
case 16 :
case 24 :
case 32 : FCColor::CopyPixel (pPixel, &dwPixel, ColorBits() / 8) ;
break ;
default : assert(false) ;
}
}
//-----------------------------------------------------------------------------
inline bool FCObjImage::GetColorTable (int iFirstIndex, int iNumber, RGBQUAD* pColors) const
{
if (!IsValidImage() || (ColorBits() > 8) || (iFirstIndex < 0) || !pColors || !m_pPalette)
{assert(false); return false;}
const int nColorNum = 1 << ColorBits() ;
for (int i=0 ; i < iNumber ; i++)
{
int nIndex = iFirstIndex + i ;
if (nIndex < nColorNum)
pColors[i] = m_pPalette[nIndex] ;
}
return true ;
}
//-----------------------------------------------------------------------------
inline bool FCObjImage::SetColorTable (int iFirstIndex, int iNumber, const RGBQUAD* pColors)
{
if (!IsValidImage() || (ColorBits() > 8) || (iFirstIndex < 0) || !pColors || !m_pPalette)
{assert(false); return false;}
const int nColorNum = 1 << ColorBits() ;
for (int i=0 ; i < iNumber ; i++)
{
int nIndex = iFirstIndex + i ;
if (nIndex < nColorNum)
m_pPalette[nIndex] = pColors[i] ;
}
return true ;
}
//-----------------------------------------------------------------------------
inline void FCObjImage::CopyPalette (const FCObjImage& imgSrc)
{
if (!IsValidImage() || (ColorBits() > 8) || (ColorBits() != imgSrc.ColorBits()))
{assert(false); return;}
RGBQUAD pPal[256] ;
int nNum = 1 << imgSrc.ColorBits() ;
imgSrc.GetColorTable (0, nNum, pPal) ;
SetColorTable (0, nNum, pPal) ;
}
//-----------------------------------------------------------------------------
inline void FCObjImage::__SetGrayPalette()
{
if (!IsValidImage() || (ColorBits() > 8))
{assert(false); return;}
// calculate palette
RGBQUAD pPal[256] ;
const int nNum = 1 << ColorBits(),
nSpan = 255 / (nNum - 1) ;
for (int i=0 ; i < nNum ; i++)
{
PCL_R(&pPal[i]) = PCL_G(&pPal[i]) = PCL_B(&pPal[i]) = i * nSpan ;
}
SetColorTable (0, nNum, pPal) ;
}
//-----------------------------------------------------------------------------
inline RGBQUAD FCObjImage::__Split16Bit_565 (WORD wPixel)
{
RGBQUAD rgb ;
PCL_R(&rgb) = (MASK16_RED_565 & wPixel) >> 8 ;
PCL_G(&rgb) = (MASK16_GREEN_565 & wPixel) >> 3 ;
PCL_B(&rgb) = (MASK16_BLUE_565 & wPixel) << 3 ;
return rgb ;
}
inline RGBQUAD FCObjImage::__Split16Bit_555 (WORD wPixel)
{
RGBQUAD rgb ;
PCL_R(&rgb) = (MASK16_RED_555 & wPixel) >> 7 ;
PCL_G(&rgb) = (MASK16_GREEN_555 & wPixel) >> 2 ;
PCL_B(&rgb) = (MASK16_BLUE_555 & wPixel) << 3 ;
return rgb ;
}
//-----------------------------------------------------------------------------
inline void FCObjImage::__ConvertToTrueColor (int iColor)
{
if (!IsValidImage() || (ColorBits() == iColor))
return ;
if ((iColor != 24) && (iColor != 32))
{assert(false); return;}
// backup image
const FCObjImage OldPic (*this) ;
if (!Create (OldPic.Width(), OldPic.Height(), iColor))
return ;
// get palette
RGBQUAD pPal[256] ;
if (OldPic.ColorBits() <= 8)
OldPic.GetColorTable (0, 1 << OldPic.ColorBits(), pPal) ;
// start color convert
const int nNewSpan = this->ColorBits() / 8, // 3 or 4
nOldSpan = OldPic.ColorBits() / 8 ;
for (int y=0 ; y < Height() ; y++)
{
const BYTE * pOld = OldPic.GetBits (y) ;
BYTE * pNew = this->GetBits (y) ;
for (int x=0 ; x < Width() ; x++, pNew+=nNewSpan, pOld+=nOldSpan)
{
switch (OldPic.ColorBits())
{
case 1 :
case 4 :
case 8 : // 1,4,8 ==> 24,32
FCColor::CopyPixel (pNew, &pPal[OldPic.GetPixelData(x,y)], 3) ;
break ;
case 16 : // 16 ==> 24,32
{
RGBQUAD crTrans ;
if (OldPic.m_dwBitFields[1] == MASK16_GREEN_555)
crTrans = __Split16Bit_555 (*(WORD*)pOld) ;
else if (OldPic.m_dwBitFields[1] == MASK16_GREEN_565)
crTrans = __Split16Bit_565 (*(WORD*)pOld) ;
FCColor::CopyPixel (pNew, &crTrans, 3) ;
}
break ;
case 24 :
case 32 : // 24,32 ==> 32,24
FCColor::CopyPixel (pNew, pOld, 3) ;
break ;
}
}
}
if (iColor == 32)
SetAlphaChannelValue (0xFF) ; // set alpha to 0xFF
}
//-----------------------------------------------------------------------------
inline void FCObjImage::GetAlphaChannel (FCObjImage* imgAlpha) const
{
// create alpha-channel image, it's a 8-bit color image
if (!imgAlpha || !IsValidImage() || (ColorBits() != 32) || (imgAlpha == this)
|| !imgAlpha->Create (Width(), Height(), 8))
{
assert(false) ; return ;
}
// get alpha channel
for (int y=0 ; y < Height() ; y++)
for (int x=0 ; x < Width() ; x++)
*imgAlpha->GetBits(x,y) = PCL_A(GetBits(x,y)) ;
}
//-----------------------------------------------------------------------------
inline void FCObjImage::AppendAlphaChannel (const FCObjImage& alpha)
{
if (!IsValidImage() || !alpha.IsValidImage() || (ColorBits() != 32) || (alpha.ColorBits() != 8) ||
(Width() != alpha.Width()) || (Height() != alpha.Height()))
{
assert(false) ; return ;
}
// append alpha channel
for (int y=0 ; y < Height() ; y++)
for (int x=0 ; x < Width() ; x++)
PCL_A(GetBits(x,y)) = *alpha.GetBits(x,y) ;
}
//-----------------------------------------------------------------------------
inline void FCObjImage::SetAlphaChannelValue (int nValue)
{
if (!IsValidImage() || (ColorBits() != 32))
{
assert(false) ; return ;
}
for (int y=0 ; y < Height() ; y++)
for (int x=0 ; x < Width() ; x++)
PCL_A(GetBits(x,y)) = nValue ;
}
//-----------------------------------------------------------------------------
inline FCObjImage& FCObjImage::operator= (const FCObjImage& img)
{
if (!img.IsValidImage() || (&img == this))
return (*this) ;
PCL_array bmfh (img.NewImgInfoWithPalette()) ;
if (Create (bmfh.get()))
{
// copy the pixels
memcpy (GetMemStart(), img.GetMemStart(), img.GetPitch()*img.Height()) ;
// copy the palette
if (img.ColorBits() <= 8)
CopyPalette (img) ;
// copy position
FCObjGraph::operator=(img) ;
}
return *this ;
}
//-----------------------------------------------------------------------------
inline bool FCObjImage::GetSubBlock (FCObjImage* SubImg, RECT rcBlock) const
{
if (!IsValidImage() || !SubImg || (SubImg == this) || (ColorBits() < 8))
{
assert(false); return false;
}
const RECT rcImage = {0, 0, Width(), Height()} ;
RECT rcD ;
assert (IsRectInRect (rcImage, rcBlock)) ;
if (::IntersectRect (&rcD, &rcImage, &rcBlock) == 0)
{
assert(false); return false; // rect of destination is empty
}
if (!SubImg->Create (RECTWIDTH(rcD), RECTHEIGHT(rcD), ColorBits()))
return false ;
// copy pixel
const int nSubPitch = SubImg->Width() * ColorBits() / 8 ;
for (int i=0 ; i < SubImg->Height() ; i++)
memcpy (SubImg->GetBits(i), GetBits(rcD.left, rcD.top + i), nSubPitch) ;
// copy palette
if (ColorBits() <= 8)
SubImg->CopyPalette (*this) ;
// set relative position
SubImg->SetGraphObjPos (rcD.left, rcD.top) ;
return true ;
}
//-----------------------------------------------------------------------------
inline bool FCObjImage::CoverBlock (const FCObjImage& Img, int x, int y)
{
if (!IsValidImage() || !Img.IsValidImage() || (ColorBits() != Img.ColorBits()) || (ColorBits() < 8))
{
assert(false); return false;
}
// calculate covered RECT
const RECT rcImage = {0, 0, Width(), Height()},
rcCover = {x, y, x+Img.Width(), y+Img.Height()} ;
RECT rcD ;
if (::IntersectRect (&rcD, &rcImage, &rcCover) == 0)
return false ; // rect of destination is empty
// copy pixel
const int nSubPitch = RECTWIDTH(rcD) * Img.ColorBits() / 8 ;
for (int cy=rcD.top ; cy < rcD.bottom ; cy++) // copy
{
const BYTE * pS = Img.GetBits (rcD.left-x, cy-y) ; // calculate edge
BYTE * pD = this->GetBits (rcD.left, cy) ;
memcpy (pD, pS, nSubPitch) ;
}
return true ;
}
//-----------------------------------------------------------------------------
inline void FCObjImage::TileBlock (const FCObjImage & Img, int x, int y)
{
int nYStart = y ;
while (nYStart < Height())
{
int nXStart = x ;
while (nXStart < Width())
{
CoverBlock (Img, nXStart, nYStart) ; // security ensured by CoverBlock
nXStart += Img.Width() ;
}
nYStart += Img.Height() ;
}
}
//-----------------------------------------------------------------------------
inline void FCObjImage::CombineImage (const FCObjImage& Img32, int x, int y, int nAlphaPercent)
{
RECT rcD ;
{
RECT rcImg = {0, 0, Width(), Height()},
rcMask = {x, y, x+Img32.Width(), y+Img32.Height()} ;
::IntersectRect (&rcD, &rcImg, &rcMask) ;
}
if ((Img32.ColorBits() != 32) || (ColorBits() != 32) || IsRectEmpty(&rcD))
{
assert(false); return;
}
nAlphaPercent = FClamp (nAlphaPercent, 0, 100) ;
for (int cy=rcD.top ; cy < rcD.bottom ; cy++)
{
RGBQUAD * pDest = (RGBQUAD*)this->GetBits (rcD.left, cy),
* pSrc = (RGBQUAD*)Img32.GetBits (rcD.left-x, cy-y) ; // calculate edge
for (int cx=rcD.left ; cx < rcD.right ; cx++, pDest++, pSrc++)
FCColor::CombineAlphaPixel(pDest, *pDest, pSrc,
(nAlphaPercent == 100) ? PCL_A(pSrc)
: (PCL_A(pSrc)*nAlphaPercent/100)) ;
}
}
//-----------------------------------------------------------------------------
// alpha混和是图像处理的心脏,它的效率和安全性关系着整个软件
// 因此我才这么麻烦的处理之
// rcSrc一定要在MaskImg32内部,rcDest可以相交
inline void FCObjImage::AlphaBlend (const FCObjImage& Img32, const RECT& rcDest, const RECT& rcSrc, int nAlphaPercent)
{
if (nAlphaPercent == 0)
return ;
// parameter check, rcSrc一定要在MaskImg32内部
const RECT rcMask = {0, 0, Img32.Width(), Img32.Height()},
rcImg = {0, 0, Width(), Height()} ;
RECT rcT ;
IntersectRect(&rcT, &rcDest, &rcImg) ;
if (!IsValidImage() || (ColorBits() < 24) || !Img32.IsValidImage() || (Img32.ColorBits() != 32) ||
!IsRectInRect (rcMask, rcSrc) || IsRectEmpty(&rcT))
{
assert(false); return;
}
nAlphaPercent = FClamp (nAlphaPercent, 0, 100) ;
const int nSpan = ColorBits() / 8 ; // 3 or 4
if ((RECTWIDTH(rcDest) == RECTWIDTH(rcSrc)) && (RECTHEIGHT(rcDest) == RECTHEIGHT(rcSrc)))
{
const int nSrcX = rcT.left - rcDest.left + rcSrc.left ;
for (int y=rcT.top ; y < rcT.bottom ; y++)
{
const BYTE * pSrc = Img32.GetBits (nSrcX, y-rcDest.top+rcSrc.top) ; // calculate edge
BYTE * pDest = this->GetBits (rcT.left, y) ;
for (int x=rcT.left ; x < rcT.right ; x++, pDest+=nSpan, pSrc+=4)
FCColor::AlphaBlendPixel (pDest, pSrc, (nAlphaPercent==100) ? PCL_A(pSrc)
: PCL_A(pSrc)*nAlphaPercent/100) ;
}
}
else
{
PCL_array pX (RECTWIDTH(rcT)),
pY (RECTHEIGHT(rcT)) ;
int x, y ;
for (y=rcT.top ; y < rcT.bottom ; y++)
pY[y-rcT.top] = rcSrc.top+(y-rcDest.top)*RECTHEIGHT(rcSrc)/RECTHEIGHT(rcDest) ;
for (x=rcT.left ; x < rcT.right ; x++)
pX[x-rcT.left] = rcSrc.left+(x-rcDest.left)*RECTWIDTH(rcSrc)/RECTWIDTH(rcDest) ;
for (y=rcT.top ; y < rcT.bottom ; y++)
{
BYTE * pDest = this->GetBits (rcT.left, y) ;
for (x=rcT.left ; x < rcT.right ; x++, pDest+=nSpan)
{
const BYTE * pSrc = Img32.GetBits (pX[x-rcT.left], pY[y-rcT.top]) ;
FCColor::AlphaBlendPixel (pDest, pSrc, (nAlphaPercent==100) ? PCL_A(pSrc)
: PCL_A(pSrc)*nAlphaPercent/100) ;
}
}
}
}
//-----------------------------------------------------------------------------
inline void FCObjImage::LogicalBlend (const FCObjImage & MaskImg, LOGICAL_OP LogOP, int x, int y)
{
if (!IsValidImage() || !MaskImg.IsValidImage() || (ColorBits() != MaskImg.ColorBits()) || (this == &MaskImg))
{
assert(false) ; return ;
}
assert (ColorBits() == 8) ;
const RECT rcSrc1 = {0, 0, Width(), Height()},
rcSrc2 = {x, y, x+MaskImg.Width(), y+MaskImg.Height()} ;
RECT rcDest ;
if (::IntersectRect (&rcDest, &rcSrc1, &rcSrc2) == 0)
return ; // no intersected rect
const int nSpan = ColorBits() / 8; // 1,2,3,4
for (int cy=rcDest.top ; cy < rcDest.bottom ; cy++)
{
const BYTE * pSrc = MaskImg.GetBits (rcDest.left-x, cy-y) ; // calculate edge
BYTE * pDest = this->GetBits (rcDest.left, cy) ;
for (int cx=rcDest.left ; cx < rcDest.right ; cx++, pDest+=nSpan, pSrc+=nSpan)
switch (LogOP)
{
case LOGI_OR : *pDest |= *pSrc ; break ;
case LOGI_AND : *pDest &= *pSrc ; break ;
case LOGI_XOR : *pDest ^= *pSrc ; break ;
case LOGI_SEL_ADD :
if (*pSrc == 0xFF)
*pDest = 0xFF ;
break ;
case LOGI_SEL_SUB :
if (*pSrc == 0xFF)
*pDest = 0 ;
break ;
default : assert(false);
}
}
}
//-----------------------------------------------------------------------------
inline int FCObjImage::Serialize (bool bSave, BYTE* pSave)
{
const BYTE * pBak = pSave ;
if (bSave) // save
{
assert (ColorBits() > 8) ; // must true color image
memcpy (pSave, &m_DibInfo, sizeof(m_DibInfo)) ; pSave += sizeof(m_DibInfo) ;
memcpy (pSave, m_dwBitFields, 12) ; pSave += 12 ;
int nWrite = GetPitch() * Height() ;
memcpy (pSave, GetMemStart(), nWrite) ; pSave += nWrite ;
}
else // load
{
Create ((BITMAPINFOHEADER*)pSave) ;
pSave += sizeof(m_DibInfo) + 12 ;
int nWrite = GetPitch() * Height() ;
memcpy (GetMemStart(), pSave, nWrite) ;
pSave += nWrite ;
}
pSave += FCObjGraph::Serialize (bSave, pSave) ;
return (int)(pSave - pBak) ;
}
//-----------------------------------------------------------------------------
inline bool FCObjImage::Load (const char* szFileName, FCImageProperty* pProperty)
{
IMAGE_TYPE imgType = GetImageHandleFactory()->QueryImageFileType(szFileName) ;
std::auto_ptr pHandler (GetImageHandleFactory()->CreateImageHandle(imgType)) ;
if (!pHandler.get())
return false ;
PCL_Interface_Composite listImage ;
std::auto_ptr pImgProp ;
bool bRet = pHandler->LoadImageFile (szFileName, listImage, pImgProp) ;
if (bRet)
{
if (listImage.PCL_GetObjectCount())
*this = *listImage.PCL_GetObject(0) ;
if (pImgProp.get() && pProperty)
*pProperty = *pImgProp ;
}
assert (bRet) ;
return bRet ;
}
//-----------------------------------------------------------------------------
inline bool FCObjImage::Load (BYTE* pStart, int nMemSize, IMAGE_TYPE imgType, FCImageProperty* pProperty)
{
std::auto_ptr pHandler (GetImageHandleFactory()->CreateImageHandle(imgType)) ;
if (!pHandler.get())
return false ;
PCL_Interface_Composite listImage ;
std::auto_ptr pImgProp ;
bool bRet = pHandler->LoadImageMemory (pStart, nMemSize, listImage, pImgProp) ;
if (bRet)
{
if (listImage.PCL_GetObjectCount())
*this = *listImage.PCL_GetObject(0) ;
if (pImgProp.get() && pProperty)
*pProperty = *pImgProp ;
}
assert (bRet) ;
return bRet ;
}
//-----------------------------------------------------------------------------
inline bool FCObjImage::LoadDIBStream (const void* pDIB, int nBufferSize)
{
const BITMAPINFOHEADER * pBmif = (const BITMAPINFOHEADER*)pDIB ;
if (!Create(pBmif))
{assert(false); return false;}
const BYTE * p = (const BYTE*)pDIB + pBmif->biSize ;
if (ColorBits() <= 8)
{
int n = 1 << ColorBits() ;
SetColorTable (0, n, (const RGBQUAD*)p) ;
p += (4 * n) ;
}
else if (pBmif->biCompression == BI_BITFIELDS)
{
p += 12 ;
}
// copy pixel
int nLeave = nBufferSize - (p - (BYTE*)pDIB) ;
assert (nLeave >= GetPitch()*Height()) ;
memcpy (GetMemStart(), p, FMin (nLeave, GetPitch()*Height())) ;
return true ;
}
//-----------------------------------------------------------------------------
inline bool FCObjImage::Save (const char* szFileName, const FCImageProperty& rProp) const
{
if (!IsValidImage() || !szFileName)
return false ;
IMAGE_TYPE imgType = GetImageHandleFactory()->QueryImageFileType(szFileName) ;
std::auto_ptr pHandler (GetImageHandleFactory()->CreateImageHandle(imgType)) ;
if (!pHandler.get())
return false ;
// save list
std::deque saveList ;
saveList.push_back (this) ;
return pHandler->SaveImageFile (szFileName, saveList, rProp) ;
}
//-----------------------------------------------------------------------------
inline void FCObjImage::__FillImageRect (const FCObjImage& img, const RECT& rcBlock, const void* pSrc)
{
RECT rc = {0, 0, img.Width(), img.Height()} ;
IntersectRect (&rc, &rc, &rcBlock) ;
if (IsRectEmpty(&rc))
return ;
const int nSpan = img.ColorBits() / 8 ; // 1, 2, 3, 4
for (int y=rc.top ; y < rc.bottom ; y++)
{
BYTE * pPixel = img.GetBits (rc.left, y) ;
for (int x=rc.left ; x < rc.right ; x++, pPixel += nSpan)
FCColor::CopyPixel (pPixel, pSrc, nSpan) ;
}
}
// add frame
// bCopyEdge: duplicate edge during copying
inline void FCObjImage::ExpandFrame (bool bCopyEdge, int iLeft, int iTop, int iRight, int iBottom)
{
if ((ColorBits() < 8) || (iLeft < 0) || (iTop < 0) || (iRight < 0) || (iBottom < 0))
{
assert(false) ; return ;
}
if ((iLeft == 0) && (iTop == 0) && (iRight == 0) && (iBottom == 0))
return ;
// backup image then create expanded image
const FCObjImage imgOld(*this) ;
if (!Create (imgOld.Width()+iLeft+iRight, imgOld.Height()+iTop+iBottom, imgOld.ColorBits()))
{
assert(false) ; return ;
}
// adjust image's position
SetGraphObjPos (imgOld.GetGraphObjPos().x - iLeft, imgOld.GetGraphObjPos().y - iTop) ;
// duplicate source image
CoverBlock (imgOld, iLeft, iTop) ;
// edge disposal
if (!bCopyEdge)
return ;
// duplicate corner
const RECT rcUL = {0, 0, iTop, iLeft},
rcUR = {Width()-iRight, 0, Width(), iTop},
rcDL = {0, Height()-iBottom, iLeft, Height()},
rcDR = {Width()-iRight, Height()-iBottom, Width(), Height()} ;
__FillImageRect (*this, rcUL, imgOld.GetBits (0, 0)) ;
__FillImageRect (*this, rcUR, imgOld.GetBits (imgOld.Width()-1, 0)) ;
__FillImageRect (*this, rcDL, imgOld.GetBits (0, imgOld.Height()-1)) ;
__FillImageRect (*this, rcDR, imgOld.GetBits (imgOld.Width()-1, imgOld.Height()-1)) ;
// duplicate four-edge
const int dwPitch = GetPitch(),
nSpan = ColorBits() / 8,
nOldLineBytes = imgOld.Width() * nSpan ;
int m ;
BYTE * pSrc, * pDest ;
// up
pSrc = GetBits (iLeft, iTop) ;
pDest = pSrc + dwPitch ;
for (m=0 ; m < iTop ; m++, pDest += dwPitch)
memcpy (pDest, pSrc, nOldLineBytes) ;
// bottom
pSrc = GetBits (iLeft, imgOld.Height() + iTop - 1) ;
pDest = pSrc - dwPitch ;
for (m=0 ; m < iBottom ; m++, pDest -= dwPitch)
memcpy (pDest, pSrc, nOldLineBytes) ;
// left
pSrc = GetBits (iLeft, iTop) ;
pDest = GetBits (0, iTop) ;
for (m=0 ; m < imgOld.Height() ; m++, pDest -= dwPitch, pSrc -= dwPitch)
{
BYTE * pTemp = pDest ;
for (int i=0 ; i < iLeft ; i++, pTemp += nSpan)
FCColor::CopyPixel (pTemp, pSrc, nSpan) ;
}
// right
pSrc = GetBits (iLeft + imgOld.Width() - 1, iTop) ;
pDest = pSrc + nSpan ;
for (m=0 ; m < imgOld.Height() ; m++, pDest -= dwPitch, pSrc -= dwPitch)
{
BYTE * pTemp = pDest ;
for (int i=0 ; i < iRight ; i++, pTemp += nSpan)
FCColor::CopyPixel (pTemp, pSrc, nSpan) ;
}
}
//-----------------------------------------------------------------------------
// stretch (>=8 bit)
inline void FCObjImage::Stretch (int nNewWidth, int nNewHeight)
{
// parameter check
if (!IsValidImage() || (nNewWidth <= 0) || (nNewHeight <= 0) || (ColorBits() < 8))
{
// assert(false) ;
return ;
}
if ((nNewWidth == Width()) && (nNewHeight == Height()))
return ;
// first backup image
const FCObjImage imgOld(*this) ;
if (!Create (nNewWidth, nNewHeight, imgOld.ColorBits()))
{
assert(false) ; return ;
}
// duplicate palette
if (ColorBits() <= 8)
CopyPalette (imgOld) ;
// initialize index table
const int nSpan = ColorBits() / 8 ;
PCL_array pTabX (Width()) ;
for (int xx=0 ; xx < Width() ; xx++)
{
pTabX[xx] = xx * imgOld.Width() / Width() ; // force to omit float
assert (pTabX[xx] < imgOld.Width()) ;
}
for (int mm=0 ; mm < (Width() - 1) ; mm++)
pTabX[mm] = (pTabX[mm+1] - pTabX[mm]) * nSpan ;
// pTabX[i] put X byte span
for (int y=0 ; y < Height() ; y++)
{
const BYTE * pOld = imgOld.GetBits (y * imgOld.Height() / Height()) ;
BYTE * pPixel = GetBits (y) ;
for (int x=0 ; x < Width() ; x++)
{
FCColor::CopyPixel (pPixel, pOld, nSpan) ;
pOld += pTabX[x] ;
pPixel += nSpan ;
}
}
}
//-----------------------------------------------------------------------------
// stretch (>=24 bit)
inline void FCObjImage::Stretch_Smooth (int nNewWidth, int nNewHeight, FCObjProgress * progress)
{
// parameter check
if (!IsValidImage() || (nNewWidth <= 0) || (nNewHeight <= 0) || (ColorBits() < 24))
{
assert(false) ; return ;
}
if ((nNewWidth == Width()) && (nNewHeight == Height()))
return ;
// in order to bilinear, the source image's W/H must larger than 2
if ((Width() == 1) || (Height() == 1))
{
ExpandFrame (true, 0, 0, (Width()==1) ? 1 : 0, (Height()==1) ? 1 : 0) ;
}
// first backup image
const FCObjImage imgOld(*this) ;
if (!Create (nNewWidth, nNewHeight, imgOld.ColorBits()))
{
assert(false) ; return ;
}
// initialize index table, to accelerate
PCL_array pTabX (Width()),
pXMod (Width()) ;
for (int i=0 ; i < Width() ; i++)
{
pTabX[i] = i * imgOld.Width() / Width() ;
pXMod[i] = (i * imgOld.Width()) % Width() ;
// approximate to last col
if (pTabX[i] >= imgOld.Width()-1)
{
pTabX[i] = imgOld.Width() - 2 ;
pXMod[i] = Width() - 1 ;
}
}
// stretch pixel
const int nSpan = ColorBits() / 8,
nPitch = imgOld.GetPitch() ;
if (progress)
progress->ResetProgress() ; // reset to 0
for (int y=0 ; y < Height() ; y++)
{
int nSrcY = y * imgOld.Height() / Height(),
nYMod = (y * imgOld.Height()) % Height() ;
if (nSrcY >= imgOld.Height()-1) // approximate to last row
{
nSrcY = imgOld.Height() - 2 ;
nYMod = Height() - 1 ;
}
const double un_y = nYMod / (double)Height() ;
BYTE * pWrite = GetBits(y) ;
for (int x=0 ; x < Width() ; x++, pWrite += nSpan)
{
// 计算原图对应点
const int nSrcX = pTabX[x],
nXMod = pXMod[x] ;
const BYTE * pOldPix = imgOld.GetBits (nSrcX, nSrcY) ;
if ((nXMod == 0) && (nYMod == 0))
{
FCColor::CopyPixel (pWrite, pOldPix, nSpan) ;
}
else
{
const BYTE * pcrPixel[4] =
{
pOldPix, pOldPix + nSpan,
pOldPix - nPitch, pOldPix - nPitch + nSpan
} ;
RGBQUAD crRet = FCColor::Get_Bilinear_Pixel (nXMod/(double)Width(), un_y, ColorBits() == 32, pcrPixel) ;
FCColor::CopyPixel (pWrite, &crRet, nSpan) ;
}
}
if (progress)
progress->SetProgress (y * 100 / Height()) ;
}
}
//-----------------------------------------------------------------------------
inline void FCObjImage::SinglePixelProcessProc (FCInterface_PixelProcess& rProcessor, FCObjProgress* pProgress)
{
if (!rProcessor.ValidateColorBits (this))
{assert(false); return;}
// before
rProcessor.OnEnterProcess (this) ;
if (pProgress)
pProgress->ResetProgress() ; // reset to 0
switch (rProcessor.QueryProcessType())
{
case FCInterface_PixelProcess::PROCESS_TYPE_PIXEL :
{
for (int y=0 ; y < Height() ; y++)
{
for (int x=0 ; x < Width() ; x++)
{
rProcessor.ProcessPixel (this, x, y, GetBits(x,y)) ;
}
if (pProgress)
pProgress->SetProgress ((y+1) * 100 / Height()) ;
}
}
break ;
case FCInterface_PixelProcess::PROCESS_TYPE_WHOLE :
rProcessor.ProcessWholeImage (this, pProgress) ;
break ;
}
// after
rProcessor.OnLeaveProcess (this) ;
}
#endif