一種位圖縮放的快速算法&PSD格式的開(kāi)發(fā)&PSD格式文件的讀取
一種位圖縮放的快速算法?
給定一個(gè)位圖,如何將它縮放至任意尺寸?很明顯,唯一的方法是:放大時(shí),在像素中間添加一些重復(fù)像
素,使圖像拉寬;縮小時(shí),把部分像素刪除掉,使圖像收縮。但是如何確定哪些像素該重復(fù),哪些像素該刪除呢?下面的方法是我自已想的,如果你有更好的方法,
請(qǐng)告訴我。我們只考慮水平方向(垂直方向跟水平方向是同樣的道理)。下面先從簡(jiǎn)單的例子來(lái)說(shuō),最后再推出一個(gè)通用的算法:
若要放大1倍,應(yīng)將每一個(gè)像素都重復(fù)一次,N個(gè)像素變成了2N個(gè)像素,圖像放大為2倍。這個(gè)不難;
若要縮小1/2,應(yīng)該每隔一個(gè)像素刪除一個(gè)像素,2N個(gè)像素變成了N個(gè)像素,圖像縮小一半。這個(gè)也不難;
若要放大1.5倍,怎么辦?假設(shè)原有2N個(gè)像素,現(xiàn)在欲變成3N個(gè)像素,須添加N個(gè)像素,所以應(yīng)對(duì)原圖每隔一個(gè)像素添加一個(gè)重復(fù)像素:?
若要縮小1/3,就是C的逆過(guò)程:每隔兩個(gè)像素刪除一個(gè)像素。?
上面四個(gè)例子都是比較容易的特例。現(xiàn)在來(lái)考慮通用的算法。在四個(gè)例子的縮放過(guò)程可以這樣理解。假設(shè)欲將長(zhǎng)度為N1的像素列變成長(zhǎng)度為N2的像素列,首
先,設(shè)立兩個(gè)指針,一個(gè)作為源指針,指向原來(lái)的像素列,讀取源像素,另一個(gè)作為目的指針,指向變換后的像素列,寫(xiě)入讀取到的像素。然后,以拉伸后像素列的
長(zhǎng)度為循環(huán)次數(shù),循環(huán)N2次,每次循環(huán)中由源指針處COPY一個(gè)像素到目的指針處,然后目的指針加一,源指針根據(jù)每次循環(huán)的不同需要增加一定步長(zhǎng)(放大時(shí)
步長(zhǎng)是零或一,縮小時(shí)步長(zhǎng)大于等于一)。
算法的框架解決了,但是中心內(nèi)容仍沒(méi)有解決:如何確定每次循環(huán)里源指針增加的步長(zhǎng)?或者說(shuō),每次循環(huán)
里如何更新源指針的位置?容易看出,通過(guò)浮點(diǎn)運(yùn)算很容易解決這個(gè)問(wèn)題:設(shè)立一個(gè)初值為零的浮點(diǎn)變量,每次循環(huán)中,把這個(gè)浮點(diǎn)變量加上N1/N2,并將其結(jié)
果的整數(shù)部分作為源指針距離起始位置的偏移量;這樣,經(jīng)過(guò)N2次循環(huán),浮點(diǎn)變量的值恰好達(dá)到N1,源指針也恰好“走”到原像素列的末尾。
這個(gè)
方法可行,但是太慢。如果能將浮點(diǎn)運(yùn)算轉(zhuǎn)化成整數(shù)運(yùn)算就快多了。那么如何轉(zhuǎn)化呢?我們可以設(shè)立一個(gè)值域?yàn)镹1*N2的整數(shù)計(jì)數(shù)器,每次循環(huán)遞增N1,并且
規(guī)定,計(jì)數(shù)器每增加N2,源指針就前進(jìn)一個(gè)像素。這樣,經(jīng)過(guò)N2次循環(huán),計(jì)數(shù)器共增加了N1*N2,源指針則增加了N1個(gè)單元,恰好“走”完全程。實(shí)際編
程中,我們是用一個(gè)值域?yàn)镹2的整數(shù)計(jì)數(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++;
}
}
}
上面算法只是水平縮放單行像素,對(duì)垂直方向也采用同樣的算法,便實(shí)現(xiàn)了任意比例的位圖縮放。經(jīng)過(guò)以上算法的處理,放大時(shí)圖像出現(xiàn)馬賽克,縮小時(shí)圖像出現(xiàn)
閃礫。若要獲得高質(zhì)量的縮放圖形,須采用插值、過(guò)濾等技術(shù)。但是因?yàn)檫@些技術(shù)所需計(jì)算量太大,在游戲中通常靠硬件加速來(lái)實(shí)現(xiàn),不宜軟件解決。??
================================================================
PSD格式的開(kāi)發(fā)?
我在做游戲時(shí),因?yàn)橐玫綄SD格式轉(zhuǎn)換成BMP或者JPG格式的程序,而且,在轉(zhuǎn)換時(shí),要將PSD中的空格轉(zhuǎn)成游戲中約定的透明色,這樣的程序,只
能自己去寫(xiě)了。所以,我在網(wǎng)上搜了一陣子,找到了“中國(guó)游戲開(kāi)發(fā)者”的網(wǎng)站,看到了一篇關(guān)于PSD格式的文章(這也是我開(kāi)始向這個(gè)網(wǎng)站投稿的原因,也許這
就叫緣)。
本來(lái)是想偷點(diǎn)懶,省去了自己研究之苦,可以抄一抄別人現(xiàn)在的代碼,再自己改改,又能省時(shí)間,又能學(xué)到東西,何樂(lè)而不為呢?可是,在
抄下這篇文章的代碼之后,發(fā)現(xiàn)其運(yùn)行居然是不能通過(guò)的。看來(lái)天下沒(méi)有免費(fèi)的午餐,我還是得自己研究。一個(gè)多小時(shí)的苦戰(zhàn)之后,終于發(fā)現(xiàn)了問(wèn)題所在,也許這并
不是一個(gè)問(wèn)題,只是對(duì)于沒(méi)有這個(gè)經(jīng)驗(yàn)人來(lái)說(shuō),這確實(shí)是個(gè)大問(wèn)題。我現(xiàn)在將這篇文章的一些地方進(jìn)行改正,望各位朋友在開(kāi)發(fā)PSD格式的讀取問(wèn)題上,不再有麻
煩。原文《PSD格式文件的讀取》在這里http:
//cgd.pages.com.cn/cgd/develop/effect/200109/ReadPSD.htm,各位可以看看。我只將我的改正部
分寫(xiě)在下面:?
1)文件頭是4個(gè)字節(jié),只能讀4個(gè)字節(jié)。
2)Photoshop的PSD格式用的是LIT格式存儲(chǔ)。
這個(gè)LIT格式我以前只是聽(tīng)說(shuō)過(guò),沒(méi)想到會(huì)被PSD用上。這個(gè)格式是將數(shù)據(jù)的高低位碼交換了的,如果直接用ReadFile或者fread函數(shù)將其讀出
來(lái),它的高低位碼是被交換了的。例如:640的16進(jìn)制值是1E0,用DWORD方式存在硬盤(pán)里是0001E000,用讀文件的函數(shù)讀出來(lái)以后,將變成:
00E00100。所以,其高低位碼被交換了,解決的方法是用轉(zhuǎn)換函數(shù),代碼如下:
WORD?WORDBIGtoLIT(WORD?code) //?字型的處理
{
return?((a?>>?8?&?0xFF)?|?((a?&?0x00FF)?<<?8); //?把高低位碼再交換過(guò)來(lái)
}
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)))
看起來(lái)簡(jiǎn)單一些,哈哈……。其它的,原文沒(méi)有什么錯(cuò)誤,不過(guò),我建議大家還是最好自己去解決問(wèn)題,呵呵,因?yàn)橛幸痪湓捳f(shuō)得很好:老師能教你讀書(shū)寫(xiě)字,但是不能教你做天下文章。?
================================================================
PSD格式文件的讀取?
PhotoShop,我想沒(méi)有人會(huì)不知道吧。如今最新的版本是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)前位置讀取一個(gè)WORD,Read32函數(shù)為從指定文件當(dāng)前位置讀取一個(gè)DWORD。
MAX_PSD_CHANNELS為24。以下就是*.PSD文件的讀取方法,有興趣的朋友可以繼續(xù)深入研究,到時(shí)可別忘了發(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;?//?壓縮類(lèi)型
HDC?hDC;
FILE?*fpPSD;
WORD?ChannelCount;?//?通道數(shù)
//?打開(kāi)PSD文件
if?(?(?fpPSD?=?fopen?(?strFilename,?"rb"?)?)?==?NULL?)?{
return?E_FAIL;
}
//?頭四個(gè)字節(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;
}
//?跳過(guò)一些數(shù)據(jù)?(總是0)
Read32(?fpPSD?);
Read16(?fpPSD?);
//?讀取通道數(shù)
ChannelCount?=?Read16(?fpPSD?);
//?確定至少有一個(gè)通道
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;
}
//?跳過(guò)數(shù)據(jù)(如調(diào)色板)
int?ModeDataCount?=?Read32(?fpPSD?);
if?(?ModeDataCount?)
fseek(?fpPSD,?ModeDataCount,?SEEK_CUR?);
//?跳過(guò)數(shù)據(jù)(如:pen?tool?paths,?etc)
int?ResourceDataCount?=?Read32(?fpPSD?);
if?(?ResourceDataCount?)
fseek(?fpPSD,?ResourceDataCount,?SEEK_CUR?);
//?條過(guò)保留數(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位壓縮類(lèi)型。?
?
{
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?);
}
}
}
}
}??