一種位圖縮放的快速算法&PSD格式的開發(fā)&PSD格式文件的讀取
一種位圖縮放的快速算法?
給定一個位圖,如何將它縮放至任意尺寸?很明顯,唯一的方法是:放大時,在像素中間添加一些重復(fù)像
素,使圖像拉寬;縮小時,把部分像素刪除掉,使圖像收縮。但是如何確定哪些像素該重復(fù),哪些像素該刪除呢?下面的方法是我自已想的,如果你有更好的方法,
請告訴我。我們只考慮水平方向(垂直方向跟水平方向是同樣的道理)。下面先從簡單的例子來說,最后再推出一個通用的算法:
若要放大1倍,應(yīng)將每一個像素都重復(fù)一次,N個像素變成了2N個像素,圖像放大為2倍。這個不難;
若要縮小1/2,應(yīng)該每隔一個像素刪除一個像素,2N個像素變成了N個像素,圖像縮小一半。這個也不難;
若要放大1.5倍,怎么辦?假設(shè)原有2N個像素,現(xiàn)在欲變成3N個像素,須添加N個像素,所以應(yīng)對原圖每隔一個像素添加一個重復(fù)像素:?
若要縮小1/3,就是C的逆過程:每隔兩個像素刪除一個像素。?
上面四個例子都是比較容易的特例。現(xiàn)在來考慮通用的算法。在四個例子的縮放過程可以這樣理解。假設(shè)欲將長度為N1的像素列變成長度為N2的像素列,首
先,設(shè)立兩個指針,一個作為源指針,指向原來的像素列,讀取源像素,另一個作為目的指針,指向變換后的像素列,寫入讀取到的像素。然后,以拉伸后像素列的
長度為循環(huán)次數(shù),循環(huán)N2次,每次循環(huán)中由源指針處COPY一個像素到目的指針處,然后目的指針加一,源指針根據(jù)每次循環(huán)的不同需要增加一定步長(放大時
步長是零或一,縮小時步長大于等于一)。
算法的框架解決了,但是中心內(nèi)容仍沒有解決:如何確定每次循環(huán)里源指針增加的步長?或者說,每次循環(huán)
里如何更新源指針的位置?容易看出,通過浮點運算很容易解決這個問題:設(shè)立一個初值為零的浮點變量,每次循環(huán)中,把這個浮點變量加上N1/N2,并將其結(jié)
果的整數(shù)部分作為源指針距離起始位置的偏移量;這樣,經(jīng)過N2次循環(huán),浮點變量的值恰好達到N1,源指針也恰好“走”到原像素列的末尾。
這個
方法可行,但是太慢。如果能將浮點運算轉(zhuǎn)化成整數(shù)運算就快多了。那么如何轉(zhuǎn)化呢?我們可以設(shè)立一個值域為N1*N2的整數(shù)計數(shù)器,每次循環(huán)遞增N1,并且
規(guī)定,計數(shù)器每增加N2,源指針就前進一個像素。這樣,經(jīng)過N2次循環(huán),計數(shù)器共增加了N1*N2,源指針則增加了N1個單元,恰好“走”完全程。實際編
程中,我們是用一個值域為N2的整數(shù)計數(shù)器,超出值域部分取模處理。算法大致如下:
void?StrechPixels(int?N1,?int?N2,?PIXEL?src_pixels[],?PIXEL?dest_pixels[])
{
ASSERT(N1?<=?N2); //?N1?must?<=?N2
int?p1?=?0,?count?=?0;
for?(int?p2?=?0;?p2?<?N2;?p2++)
{
dest_pixels[p2]?=?src_pixels[p1];
count?+=?N1;
while?(count?>=?N2)
{
count?-=?N2;
p1++;
}
}
}
上面算法只是水平縮放單行像素,對垂直方向也采用同樣的算法,便實現(xiàn)了任意比例的位圖縮放。經(jīng)過以上算法的處理,放大時圖像出現(xiàn)馬賽克,縮小時圖像出現(xiàn)
閃礫。若要獲得高質(zhì)量的縮放圖形,須采用插值、過濾等技術(shù)。但是因為這些技術(shù)所需計算量太大,在游戲中通常靠硬件加速來實現(xiàn),不宜軟件解決。??
================================================================
PSD格式的開發(fā)?
我在做游戲時,因為要用到將PSD格式轉(zhuǎn)換成BMP或者JPG格式的程序,而且,在轉(zhuǎn)換時,要將PSD中的空格轉(zhuǎn)成游戲中約定的透明色,這樣的程序,只
能自己去寫了。所以,我在網(wǎng)上搜了一陣子,找到了“中國游戲開發(fā)者”的網(wǎng)站,看到了一篇關(guān)于PSD格式的文章(這也是我開始向這個網(wǎng)站投稿的原因,也許這
就叫緣)。
本來是想偷點懶,省去了自己研究之苦,可以抄一抄別人現(xiàn)在的代碼,再自己改改,又能省時間,又能學(xué)到東西,何樂而不為呢?可是,在
抄下這篇文章的代碼之后,發(fā)現(xiàn)其運行居然是不能通過的。看來天下沒有免費的午餐,我還是得自己研究。一個多小時的苦戰(zhàn)之后,終于發(fā)現(xiàn)了問題所在,也許這并
不是一個問題,只是對于沒有這個經(jīng)驗人來說,這確實是個大問題。我現(xiàn)在將這篇文章的一些地方進行改正,望各位朋友在開發(fā)PSD格式的讀取問題上,不再有麻
煩。原文《PSD格式文件的讀取》在這里http:
//cgd.pages.com.cn/cgd/develop/effect/200109/ReadPSD.htm,各位可以看看。我只將我的改正部
分寫在下面:?
1)文件頭是4個字節(jié),只能讀4個字節(jié)。
2)Photoshop的PSD格式用的是LIT格式存儲。
這個LIT格式我以前只是聽說過,沒想到會被PSD用上。這個格式是將數(shù)據(jù)的高低位碼交換了的,如果直接用ReadFile或者fread函數(shù)將其讀出
來,它的高低位碼是被交換了的。例如:640的16進制值是1E0,用DWORD方式存在硬盤里是0001E000,用讀文件的函數(shù)讀出來以后,將變成:
00E00100。所以,其高低位碼被交換了,解決的方法是用轉(zhuǎn)換函數(shù),代碼如下:
WORD?WORDBIGtoLIT(WORD?code) //?字型的處理
{
return?((a?>>?8?&?0xFF)?|?((a?&?0x00FF)?<<?8); //?把高低位碼再交換過來
}
DWORD?DWORDBIGtoLIT(DWORD?code) //?雙字型的處理
{
WORD?HiCode,?LowCode;
HiCode?=?code?&?0xFFFF0000;
LowCode?=?code?&?0x0000FFFF;
HiCode?=?((HiCode?>>?8)?&?0xFF)?|?((HiCode?&?0x00FF)?<<?8);
LowCode?((LowCode?>>?8)?&?0xFF)?|?((LowCode?&?0x00FF)?<<?8);
return?MAKELONG((WORD)(LowCode?<<?16),?(WORD)HiCode);
}
當(dāng)然,也可以定義成宏形式,如下:
#define?BIG2LIT(a)?(WORD((a?>>?8?&?0xFF)?|?((a?&?0x00FF)?<<?8)))
#define?DWORDBIG2LIT(b)?MAKELONG(BIG2LIT(HIWORD(b)),?BIG2LIT(LOWORD(b)))
看起來簡單一些,哈哈……。其它的,原文沒有什么錯誤,不過,我建議大家還是最好自己去解決問題,呵呵,因為有一句話說得很好:老師能教你讀書寫字,但是不能教你做天下文章。?
================================================================
PSD格式文件的讀取?
PhotoShop,我想沒有人會不知道吧。如今最新的版本是6.0,其圖象文件*.PSD和5.5相比變化并不太大。以下我就介紹*.PSD文件的讀
取方法,并提供完整讀取函數(shù)。其中:m_Rect為目標(biāo)區(qū)域,m_lpDDS7為目標(biāo)DirectDraw表面,m_pbAlphaMask為目標(biāo)
Aplha通告指針。Read16函數(shù)為從指定文件當(dāng)前位置讀取一個WORD,Read32函數(shù)為從指定文件當(dāng)前位置讀取一個DWORD。
MAX_PSD_CHANNELS為24。以下就是*.PSD文件的讀取方法,有興趣的朋友可以繼續(xù)深入研究,到時可別忘了發(fā)我一份。
HRESULT?LoadPSD(?LPSTR?strFilename?)?//?讀取PSD文件
{
DWORD?dwWidth,?dwHeight;?//?寬高
long?lSurfWidth?=?m_Rect.right?-?m_Rect.left;
long?lSurfHeight?=?m_Rect.bottom?-?m_Rect.top;
WORD?CompressionType;?//?壓縮類型
HDC?hDC;
FILE?*fpPSD;
WORD?ChannelCount;?//?通道數(shù)
//?打開PSD文件
if?(?(?fpPSD?=?fopen?(?strFilename,?"rb"?)?)?==?NULL?)?{
return?E_FAIL;
}
//?頭四個字節(jié)為"8BPS"
char?signature[5];
signature[0]?=?fgetc(?fpPSD?);
signature[1]?=?fgetc(?fpPSD?);
signature[2]?=?fgetc(?fpPSD?);
signature[3]?=?fgetc(?fpPSD?);
signature[4]?=?'\0';
if?(?strcmp(?signature,"8BPS"?)?!=?0?)?{
return?E_FAIL;
}
//?版本必須為1
if?(?Read16(?fpPSD?)?!=?1?)?{
return?E_FAIL;
}
//?跳過一些數(shù)據(jù)?(總是0)
Read32(?fpPSD?);
Read16(?fpPSD?);
//?讀取通道數(shù)
ChannelCount?=?Read16(?fpPSD?);
//?確定至少有一個通道
if?(?(?ChannelCount?<?0?)?||?(?ChannelCount?>?MAX_PSD_CHANNELS?)?)?{
return?E_FAIL;
}
//?讀入寬和高
dwHeight?=?Read32(?fpPSD?);
dwWidth?=?Read32(?fpPSD?);
if?(?dwWidth?!=?(?DWORD?)lSurfWidth?||?dwHeight?!=?(?DWORD?)lSurfHeight?)?{
return?E_FAIL;
}
//?只讀入8位通道
if?(?Read16(?fpPSD?)?!=?8?)?{
return?E_FAIL;
}
//?確定模式為RGB.
//?可能值:
//?0:?位圖
//?1:?灰階
//?2:?索引
//?3:?RGB
//?4:?CMYK
//?7:?Multichannel
//?8:?Duotone
//?9:?Lab
if?(?Read16(?fpPSD?)?!=?3?)?{
return?E_FAIL;
}
//?跳過數(shù)據(jù)(如調(diào)色板)
int?ModeDataCount?=?Read32(?fpPSD?);
if?(?ModeDataCount?)
fseek(?fpPSD,?ModeDataCount,?SEEK_CUR?);
//?跳過數(shù)據(jù)(如:pen?tool?paths,?etc)
int?ResourceDataCount?=?Read32(?fpPSD?);
if?(?ResourceDataCount?)
fseek(?fpPSD,?ResourceDataCount,?SEEK_CUR?);
//?條過保留數(shù)據(jù)
int?ReservedDataCount?=?Read32(?fpPSD?);
if?(?ReservedDataCount?)
fseek(?fpPSD,?ReservedDataCount,?SEEK_CUR?);
//?0:?非壓縮
//?1:?RLE壓縮
CompressionType?=?Read16(?fpPSD?);
if?(?CompressionType?>?1?)?{
return?E_FAIL;
}
BYTE*?PSDPixels?=?new?BYTE[?(?lSurfWidth?*?lSurfHeight?)?*?4?];
//?解包數(shù)據(jù)
UnPackPSD(?fpPSD,?lSurfWidth,?lSurfHeight,?PSDPixels,?ChannelCount,?CompressionType?);
fclose(?fpPSD?);
//?復(fù)制信息
BITMAPINFO?BitmapInfo;
ZeroMemory(?&BitmapInfo,?sizeof(?BitmapInfo?)?);
BitmapInfo.bmiHeader.biSize?=?sizeof(?BitmapInfo.bmiHeader?);
BitmapInfo.bmiHeader.biWidth?=?lSurfWidth;
BitmapInfo.bmiHeader.biHeight?=?-lSurfHeight;
BitmapInfo.bmiHeader.biPlanes?=?1;
BitmapInfo.bmiHeader.biBitCount?=?32;
m_lpDDS7->GetDC(?&hDC?);
int?rc?=?StretchDIBits(?hDC,
0,
0,
lSurfWidth,
lSurfHeight,
0,
0,
lSurfWidth,
lSurfHeight,
PSDPixels,
&BitmapInfo,
DIB_RGB_COLORS,
SRCCOPY?);
m_lpDDS7->ReleaseDC(?hDC?);
if?(?rc?==?GDI_ERROR?)?{
H_ARRAY_DELETE(?PSDPixels?);
#ifdef?_DEBUG
g_pHERR->OutDebugMsg(?3,?H2DSERR_INVALID_PSD?);
#endif
return?E_FAIL;
}
//?是否讀取Alpha混合通道
if(?ChannelCount?>?3?)?{
m_pbAlphaMask?=?new?BYTE[?lSurfWidth?*?lSurfHeight?];
for?(?int?x?=?0;?x?<?lSurfWidth;?x++?)
for?(?int?y?=?0;?y?<?lSurfHeight;?y++?)?{
m_pbAlphaMask[?(?y?*?lSurfWidth?)?+?x?]?=
? PSDPixels[?(?(?(?y?*?lSurfHeight?)?+?x?)?*?4?)?+?3?];
}
}
else?{
m_pbAlphaMask?=?NULL;
}
H_ARRAY_DELETE(?PSDPixels?);
return?DD_OK;
}
//?PSD文件解包
void?CHades2DSurface::UnPackPSD(?FILE?*fp, //?fp為PSD文件指針,
?DWORD?dwWidth, ?//?dwWidth、dwHeight為寬高,
?DWORD?dwHeight,
?BYTE*?pixels, //?pixels為解包目標(biāo)指針,
?WORD?ChannelCnt, ?//?ChannelCnt為通道數(shù),
?WORD?Compression?)?//?Compression位壓縮類型。?
?
{
int?Default[4]?=?{?0,?0,?0,?255?};
int?chn[4]?=?{?2,?1,?0,?3};
int?PixelCount?=?dwWidth?*?dwHeight;
if?(?Compression?)?{
fseek(?fp,?dwHeight?*?ChannelCnt?*?2,?SEEK_CUR?);
for?(?int?c?=?0;?c?<?4;?c++?)?{
int?pn?=?0;
int?channel?=?chn[c];
if?(?channel?>=?ChannelCnt?)?{
for?(?pn=0;?pn?<?PixelCount?;pn++?)?{
pixels[?(?pn?*?4?)?+?channel?]?=?Default[?channel?];
}
}
else?//?非壓縮
{
int?count?=?0;
while(?count?<?PixelCount?)?{
int?len?=?fgetc(?fp?);
if(?len?==?128?)?{?}
else?if?(?len?<?128?)?//?非RLE
{
len++;
count?+=?len;
while(len)?{
pixels[?(?pn?*?4?)?+?channel?]?=?fgetc(?fp?);
pn++;
len--;
}
}
? ?else?if?(?len?>?128?)?//?RLE打包
{
len?^=?0x0FF;
len?+=?2;
unsigned?char?val?=?fgetc(?fp?);
count?+=?len;
while(?len?)?{
pixels[?(?pn?*?4?)?+?channel?]?=?val;
pn++;
len--;
}
}
}
}
}
}
else
{
for?(?int?c=0;?c?<?4;?c++?)?{
int?channel?=?chn[c];
if?(?channel?>?ChannelCnt?)?{
for(?int?pn?=?0;?pn?<?PixelCount;?pn++?)?{
pixels[?(?pn?*?4?)?+?channel?]?=?Default[?channel?];
}
}
else?{
for(?int?n?=?0;?n?<?PixelCount;?n++?)?{
pixels[?(?n?*?4?)?+?channel?]?=?fgetc(?fp?);
}
}
}
}
}??