怎样编写一个Photoshop滤镜(1)
????????????在很久前我曾經寫過一篇文章簡要講述了 Photoshop 的濾鏡開發的基本概念,并描述了濾鏡和 PS之間的協作關系,也提供了一個雨滴效果濾鏡的 Demo。但是缺少源代碼。而且我們將要產生疑問,我們如何從頭開始編寫一個 Photoshop 濾鏡呢?我們如何建立一個最簡單的 PS 濾鏡插件的基本框架,然后在這個基礎上繼續添加我們想要的功能呢?這里,我就以回答一個網友向我提出的問題為例,從最基本的建立項目開始講起。這個例子(也是這個網友的問題)是,他想做一個最簡單的濾鏡,也就是僅僅把圖像填充為“紅色”。對于 PS 用戶來說,這當然是非常簡單容易的事情,只需要一個快捷鍵操作而已,(濾鏡通常是用于完成比較復雜的任務的),我們就從這個最基本的例子出發講解編寫濾鏡的過程。在文章最后將附上范例的源碼下載鏈接。
????????????(1)我們使用的開發工具是 Visual Studio .NET 2005 版本,搭配 Photoshop SDK CS(本質上就是一些C++代碼和資源等文件組成的發行包)。開發語言使用的是C和C++。
????????????那么使用C#或者其他語言行嗎?目前來看可行性不大。所以要開發 Photoshop 濾鏡,則要求開發者必須具有較好的C 和 C++ 基礎,這是最重要的。當然如果開發者熟悉圖像處理,數字信號處理的基本知識將會更好。
????????????(2)準備好工具后,我們打開 VS2005,新建項目。項目模板我們選擇 Visual C++?的 Win32。項目名稱我們輸入我們想要創建的濾鏡名稱,例如“FillRed”濾鏡,表示這個濾鏡用于填充紅色,如下圖:
??????????????????
??????????????????點擊確定以后,在彈出的設置對話框上,點擊“應用程序設置”,在應用程序類型中選擇選擇“DLL”,然后點擊確定。
??????????????????
??????????????????(3)項目建立為一個標準DLL項目,我們在“解決方案資源管理器”中右鍵點擊項目名稱,在項目屬性中我們做如下一些設置:
??????????????????(a)在常規中,我喜歡把項目使用的字符集改為“使用多字節字符集”,這使我們可以用char*以及直接使用雙引號的字符串類型。當然也可以使用Unicode,但是兩者的使用的字符串函數會有所不同??梢噪S你的喜歡設定。
??????????????????(b)項目已經默認輸出為DLL, 由于Photoshop的濾鏡文件的擴展名是8bf,所以我們在鏈接器->常規中,把輸出文件的擴展名8bf。改為如下圖所示。
??????????????????
??????????????????(c)下面點擊工具-》選項,在彈出的對話框中,選擇“項目和解決方案”-》VC++目錄, 在右側下拉框中選取“包含文件”:把幾個Photoshop SDK的文件夾添加到 選項的VC++包含目錄中,這將會方便我們編譯項目時不會報告找不到文件的錯誤。如下圖所示:
??????????????????
? ? $(PS_SDK)\photoshopapi\photoshop
?? $(PS_SDK)\photoshopapi\pica_sp
? ?? $(PS_SDK)\samplecode\common\includes
?
??????????????????(4)到這里我們已經基本設置好了項目,然后我們打開DLL的主要源文件“FillRed.cpp”,我們把該源文件的代碼替換為如下:
?
Code_FillRed.cpp//?FillRed.cpp?:?定義?DLL?應用程序的入口點。
//
#include?"stdafx.h"
#include?"PiFilter.h"
#include?<stdio.h>
#ifdef?_MANAGED
#pragma?managed(push,?off)
#endif
#ifndef?DLLExport
#define?DLLExport?extern?"C"?__declspec(dllexport)
#endif
//=======================================
//????????全局變量
//=======================================
//dll?instance
HINSTANCE????????dllInstance;
FilterRecord*????gFilterRecord;
int32*????????????gData;
int16*????????????gResult;
SPBasicSuite*????sSPBasic?=?NULL;
#define????????????TILESIZE????128?//貼片大小:128?*?128?
Rect????????????m_Tile;????????????//當前圖像貼片(128*128)
//=======================================
//????????函數列表
//=======================================
//輔助函數,拷貝矩形
void?CopyPsRect(Rect*?src,?Rect*?dest);
//輔助函數,把一個矩形置為空矩形
void?ZeroPsRect(Rect*?dest);
void?DoParameters();
void?DoPrepare();
void?DoStart();
void?DoContinue();
void?DoFinish();
//輔助函數,拷貝矩形
void?CopyPsRect(Rect*?src,?Rect*?dest)
{
????dest->left?=?src->left;
????dest->top?=?src->top;
????dest->right?=?src->right;
????dest->bottom?=?src->bottom;
}
//輔助函數,把一個矩形置為空矩形
void?ZeroPsRect(Rect*?dest)
{
????dest->left?=?0;
????dest->top?=?0;
????dest->right?=?0;
????dest->bottom?=?0;
}
//DLLMain
BOOL?APIENTRY?DllMain(?HMODULE?hModule,
???????????????????????DWORD??ul_reason_for_call,
???????????????????????LPVOID?lpReserved
?????????????????????)
{
????dllInstance?=?static_cast<HINSTANCE>(hModule);
????return?TRUE;
}
#ifdef?_MANAGED
#pragma?managed(pop)
#endif
//===================================================================================================
//------------------------------------?濾鏡被ps調用的函數?-------------------------------------------
//===================================================================================================
DLLExport?void?PluginMain(const?int16?selector,????void?*?filterRecord,?int32?*data,?int16?*result)
{
????gData?=?data;
????gResult?=?result;
????
????if?(selector?==?filterSelectorAbout)
????{
????????//顯示關于對話框
????????MessageBox(GetActiveWindow(),?"FillRed?Filter:?填充紅色--?by?hoodlum1980",?"關于?FillRed",?MB_OK);
????}?
????else?
????{
????????gFilterRecord?=?(FilterRecordPtr)filterRecord;
????????sSPBasic?=?gFilterRecord->sSPBasic;
????}
????switch?(selector)
????{
????????case?filterSelectorAbout:
????????????//DoAbout();
????????????break;
????????case?filterSelectorParameters:
????????????DoParameters();
????????????break;
????????case?filterSelectorPrepare:
????????????DoPrepare();
????????????break;
????????case?filterSelectorStart:
????????????DoStart();
????????????break;
????????case?filterSelectorContinue:
????????????DoContinue();
????????????break;
????????case?filterSelectorFinish:
????????????DoFinish();
????????????break;
????????default:
????????????*gResult?=?filterBadParameters;
????????????break;
????}
}
//這里準備參數,就這個濾鏡例子來說,我們暫時不需要做任何事
void?DoParameters()
{
}
//在此時告訴PS(宿主)濾鏡需要的內存大小
void?DoPrepare()
{
????if(gFilterRecord?!=?NULL)
????{
????????gFilterRecord->bufferSpace?=?0;
????????gFilterRecord->maxSpace?=?0;
????}
}
//inRect?????:?濾鏡請求PS發送的矩形區域。
//outRect????:?濾鏡通知PS接收的矩形區域。
//filterRect?:?PS通知濾鏡需要處理的矩形區域。
//由于我們是使用固定的紅色進行填充,實際上我們不需要請求PS發送數據
//所以這里可以把inRect設置為NULL,則PS不向濾鏡傳遞數據。
void?DoStart()
{
????if(gFilterRecord?==?NULL)
????????return;
????//我們初始化第一個Tile,然后開始進行調用
????m_Tile.left?=?gFilterRecord->filterRect.left;
????m_Tile.top?=?gFilterRecord->filterRect.top;
????m_Tile.right?=?min(m_Tile.left?+?TILESIZE,?gFilterRecord->filterRect.right);
????m_Tile.bottom?=?min(m_Tile.top?+?TILESIZE,?gFilterRecord->filterRect.bottom);
????//設置inRect,?outRect
????ZeroPsRect(&gFilterRecord->inRect);?//我們不需要PS告訴我們原圖上是什么顏色,因為我們只是填充
????CopyPsRect(&m_Tile,?&gFilterRecord->outRect);
????//請求全部通道(則數據為interleave分布)
????gFilterRecord->inLoPlane?=?0;
????gFilterRecord->inHiPlane?=?0;
????gFilterRecord->outLoPlane?=?0;
????gFilterRecord->outHiPlane?=?(gFilterRecord->planes?-1);
}
//這里對當前貼片進行處理,注意如果用戶按了Esc,下一次調用將是Finish
void?DoContinue()
{
????if(gFilterRecord?==?NULL)
????????return;
????//定位像素
????int?planes?=?gFilterRecord->outHiPlane?-?gFilterRecord->outLoPlane?+?1;?//通道數量?????????
????uint8?*pData=(uint8*)gFilterRecord->outData;
????//掃描行寬度(字節)
????int?stride?=?gFilterRecord->outRowBytes;
????//我們把輸出矩形拷貝到?m_Tile
????CopyPsRect(&gFilterRecord->outRect,?&m_Tile);
????for(int?j?=?0;?j<?(m_Tile.bottom?-?m_Tile.top);?j++)
????{
????????for(int?i?=?0;?i<?(m_Tile.right?-?m_Tile.left);?i++)
????????{
????????????//為了簡單明了,我們默認把圖像當作RGB格式(實際上不應這樣做)
????????????//pData[?i*planes?+?j*stride?+?0?]?=?0;????//Red??
????????????//pData[?i*planes?+?j*stride?+?1?]?=?0;????//Green?
????????????pData[?i*planes?+?j*stride?+?2?]?=?255;????//Blue
????????}
????}
????//判斷是否已經處理完畢
????if(m_Tile.right?>=?gFilterRecord->filterRect.right?&&?m_Tile.bottom?>=?gFilterRecord->filterRect.bottom)
????{
????????//處理結束
????????ZeroPsRect(&gFilterRecord->inRect);
????????ZeroPsRect(&gFilterRecord->outRect);
????????ZeroPsRect(&gFilterRecord->maskRect);
????????return;
????}
????//設置下一個tile
????if(m_Tile.right?<?gFilterRecord->filterRect.right)
????{
????????//向右移動一格
????????m_Tile.left?=?m_Tile.right;
????????m_Tile.right?=?min(m_Tile.right?+?TILESIZE,?gFilterRecord->filterRect.right);
????????
????}
????else
????{
????????//向下換行并回到行首處
????????m_Tile.left?=?gFilterRecord->filterRect.left;
????????m_Tile.right?=?min(m_Tile.left?+?TILESIZE,?gFilterRecord->filterRect.right);
????????m_Tile.top?=?m_Tile.bottom;
????????m_Tile.bottom?=?min(m_Tile.bottom?+?TILESIZE,?gFilterRecord->filterRect.bottom);
????}
????ZeroPsRect(&gFilterRecord->inRect);
????CopyPsRect(&m_Tile,?&gFilterRecord->outRect);
????//請求全部通道(則數據為interleave分布)
????gFilterRecord->inLoPlane?=?0;
????gFilterRecord->inHiPlane?=?0;
????gFilterRecord->outLoPlane?=?0;
????gFilterRecord->outHiPlane?=?(gFilterRecord->planes?-1);
}
//處理結束,這里我們暫時什么也不需要做
void?DoFinish()
{
}
?
??????????????????上面的代碼也就是濾鏡的一個基本框架,我們可以看到DLL將提供的一個導出函數是PluginMain函數,我們將把圖像用 128*128 像素的切片進行分割處理,這可以使PS和濾鏡之間每次傳遞較少量數據,尤其是對很大的圖像來說,切片處理將有利于應對內存緊張的情況,也是 Photoshop 所提倡的,64*64 或者 128*128 是比較典型的尺寸。
??????????????????其調用過程是,在 start 調用中初始化第一個貼片(Tile),然后我們把第一個貼片設置為outRect,表示我們請求PS提供一個緩沖區用以接收該矩形所在位置的處理后數據,至于inRect,由于我們僅僅是填充,所以我們不關心圖像的原來顏色如何,所以 inRect 可以設置為“空矩形”。圖像的通道這里我們為了代碼的直觀和簡單起見,我們就僅僅考慮RGB圖像,即有3個通道的圖像。設置好第一個 outRect,然后PS就會依次不停的開始進行 continue 調用,貼片從左至右,從上到下的順序拼貼,直到貼片全部處理完成。注意 inData 和 outData 是 PS 向濾鏡提供的申請數據和“回寫緩沖區”,一個用于讀,一個用于寫,它們的大小是由濾鏡向PS通知請求時填寫的數據控制的,操作時絕不能越出其邊界。
??????????????????【注意】在濾鏡處理數據的代碼中請務必小心操作,一旦指針越界將會導致 Photoshop 程序崩潰!
??????????????????有關該濾鏡主體代碼詳細請參見代碼注釋,以及我此前一篇文章的解釋。以及PS SDK的官方文檔。這里我們就不再細述代碼的原理了,因為它是很簡單的。上面的一切代碼里面需要引用ps sdk中的頭文件。
?
??????????????????(4)這時,項目已經可以成功編譯。但下面一步是是欠入 PIPL 資源。
??????????????????必須為濾鏡插入 PIPL 資源,才能被Photoshop所正確識別和加載。根據PS提供的文檔介紹,PIPL的發音讀作"pipple",它表示 Plug-In Property List。它是一個靈活,可擴展的用于表示插件模塊元數據(metadata)的數據結構。pipl包括了Photoshop識別和加載插件模塊的所有信息,包含一些標記,以及控制每個插件的各種靜態屬性等等,你的濾鏡可以包含一個或者多個“pipl”結構。
??????????????????為濾鏡插入pipl資源的過程如下,首先我們需要給項目添加一個*.r(Macintosh的Rez文件)文件,然后使用 cl.exe 把這個文件編譯為一個 *.rr文件,最后用 Ps SDK提供的一個資源轉換工具 CnvtPipl.Exe 把*.rr 文件轉換為 *.pipl 文件,然后為濾鏡添加一個 *.rc資源文件,在rc文件的末尾把 pipl文件包含進來即可。
??????????????????ps sdk已經為我們提供了一個通用的 r文件,包括了通用的屬性定義,它是 PIGeneral.r 文件。
??????????????????(a)下面我們就為項目添加一個 r文件,在資源管理器中右鍵點擊“資源文件”文件夾,點擊添加新的文件,文件名輸入“FillRed.r”。雙擊打開該文件,復制以下內容:
?
Code_FillRed.r//?ADOBE?SYSTEMS?INCORPORATED
//?Copyright??1993?-?2002?Adobe?Systems?Incorporated
//?All?Rights?Reserved
//
//?NOTICE:??Adobe?permits?you?to?use,?modify,?and?distribute?this?
//?file?in?accordance?with?the?terms?of?the?Adobe?license?agreement
//?accompanying?it.??If?you?have?received?this?file?from?a?source
//?other?than?Adobe,?then?your?use,?modification,?or?distribution
//?of?it?requires?the?prior?written?permission?of?Adobe.
//-------------------------------------------------------------------------------
#define?plugInName????????????"FillRed?Filter"
#define????plugInCopyrightYear?"2009"
#define?plugInDescription?\
????"FillRed?Filter.\n\t?-?http:\\www.cnblogs.com\hoodlum1980"
#include?"E:\Codes\Adobe?Photoshop?CS2?SDK\samplecode\common\includes\PIDefines.h"
#ifdef?__PIMac__
????#include?"Types.r"
????#include?"SysTypes.r"
????#include?"PIGeneral.r"
????#include?"PIUtilities.r"
????#include?"DialogUtilities.r"
#elif?defined(__PIWin__)
????#define?Rez
????#include?"PIGeneral.h"
????#include?"E:\Codes\Adobe?Photoshop?CS2?SDK\samplecode\common\resources\PIUtilities.r"
????#include?"E:\Codes\Adobe?Photoshop?CS2?SDK\samplecode\common\resources\WinDialogUtils.r"
#endif
resource?'PiPL'?(?16000,?"FillRed",?purgeable?)
{
????{
????????Kind?{?Filter?},
????????Name?{?plugInName?},
????????Category?{?"Demo?By?hoodlum1980"?},
????????Version?{?(latestFilterVersion?<<?16)?|?latestFilterSubVersion?},
????????#ifdef?__PIWin__
????????????CodeWin32X86?{?"PluginMain"?},
????????#else
????????????CodeMachOPowerPC?{?0,?0,?"PluginMain"?},
????????#endif
????????SupportedModes
????????{
????????????noBitmap,?doesSupportGrayScale,
????????????noIndexedColor,?doesSupportRGBColor,
????????????doesSupportCMYKColor,?doesSupportHSLColor,
????????????doesSupportHSBColor,?doesSupportMultichannel,
????????????doesSupportDuotone,?doesSupportLABColor
????????},
????????????
????????EnableInfo
????????{
????????????"in?(PSHOP_ImageMode,?GrayScaleMode,?RGBMode,"
????????????"CMYKMode,?HSLMode,?HSBMode,?MultichannelMode,"
????????????"DuotoneMode,?LabMode,"
????????????"Gray16Mode,?RGB48Mode,?CMYK64Mode,?Lab48Mode)"
????????},
????????PlugInMaxSize?{?2000000,?2000000?},
????????FilterCaseInfo?{
????????????{????/*?array:?7?elements?*/
????????????????/*?Flat?data,?no?selection?*/
????????????????inStraightData,
????????????????outStraightData,
????????????????doNotWriteOutsideSelection,
????????????????doesNotFilterLayerMasks,
????????????????doesNotWorkWithBlankData,
????????????????copySourceToDestination,
????????????????/*?Flat?data?with?selection?*/
????????????????inStraightData,
????????????????outStraightData,
????????????????doNotWriteOutsideSelection,
????????????????doesNotFilterLayerMasks,
????????????????doesNotWorkWithBlankData,
????????????????copySourceToDestination,
????????????????/*?Floating?selection?*/
????????????????inStraightData,
????????????????outStraightData,
????????????????doNotWriteOutsideSelection,
????????????????doesNotFilterLayerMasks,
????????????????doesNotWorkWithBlankData,
????????????????copySourceToDestination,
????????????????/*?Editable?transparency,?no?selection?*/
????????????????inStraightData,
????????????????outStraightData,
????????????????doNotWriteOutsideSelection,
????????????????doesNotFilterLayerMasks,
????????????????doesNotWorkWithBlankData,
????????????????copySourceToDestination,
????????????????/*?Editable?transparency,?with?selection?*/
????????????????inStraightData,
????????????????outStraightData,
????????????????doNotWriteOutsideSelection,
????????????????doesNotFilterLayerMasks,
????????????????doesNotWorkWithBlankData,
????????????????copySourceToDestination,
????????????????/*?Preserved?transparency,?no?selection?*/
????????????????inStraightData,
????????????????outStraightData,
????????????????doNotWriteOutsideSelection,
????????????????doesNotFilterLayerMasks,
????????????????doesNotWorkWithBlankData,
????????????????copySourceToDestination,
????????????????/*?Preserved?transparency,?with?selection?*/
????????????????inStraightData,
????????????????outStraightData,
????????????????doNotWriteOutsideSelection,
????????????????doesNotFilterLayerMasks,
????????????????doesNotWorkWithBlankData,
????????????????copySourceToDestination
????????????}
????????}
????}
};
?
??????????????????在FillRed.r文件中我們可以看到我們定義了濾鏡的名稱,所在的濾鏡分組名稱(Category),可以啟用的圖像模式等信息。然后右鍵點擊該文件,在該文件的自定義生成規則的命令行中,點擊編輯,輸入以下內容:
??????????????????
??????????????????在“命令行”一欄點擊編輯按鈕,在彈出的窗口中輸入以下兩行:
cl /I E:\Codes\Adobe~1\samplecode\Common\Includes /IE:\Codes\Adobe~1\PhotoshopAPI\Photoshop /IE:\Codes\Adobe~1\PhotoshopAPI\PICA_SP /IE:\Codes\Adobe~1\samplecode\Common\Resources /EP /DWIN32=1 /Tc"$(InputPath)" > "$(ProjectDir)\$(InputName).rr"
Cnvtpipl.exe "$(ProjectDir)\$(InputName).rr" "$(ProjectDir)\$(InputName).pipl"
?
????????????其中,第一行表示使用 CL.EXE 把該文件編譯為 *.rr文件,上面的“/I”選項表示需要的包含目錄。
????????????第二行表示使用PS SDK中的 Cnvtpipl.exe 工具把 rr文件編譯為 pipl文件,請注意為了簡單,我把該工具復制到了項目的源文件所在文件夾下面。它位于SDK的路徑是:“\samplecode\resources\cnvtpipl.exe”。
????????????(b)下面我們為項目添加一個 rc文件,同樣右鍵點擊“資源文件”,添加一個FillRed.rc文件。
????????????這是一個windows的資源文件,我們暫時還不需要任何資源,所以我們直接用文本方式打開IDE自動生成的RC文件,在結尾處添加下面的一行:
????????????
Code_FillRed.rc//
#endif????//?APSTUDIO_INVOKED
#endif????//?英語(美國)資源
/////
#ifndef?APSTUDIO_INVOKED
/////
//
//?從?TEXTINCLUDE?3?資源生成。
//
#include?"FillRed.pipl"
/////
#endif????//?不是?APSTUDIO_INVOKED
?
????????????(5)我們編譯項目,即可在項目的輸出目錄中看到 生成 FillRed.8bf 文件,下面我們把這個文件復制到 Photoshop的濾鏡文件夾下面,例如我的Photoshop CS的濾鏡所在目錄是:“D:\Program Files\Adobe\Photoshop CS\增效工具\濾鏡”
??????????????????
?
????????????最后我們啟動Photoshop,Photoshop會掃描插件目錄,并把我們的濾鏡加載到相應的菜單上,我們選擇一個矩形選區,然后點擊我們制作的濾鏡相應菜單,即可看到效果,如下圖所示。注意,下面的例子的效果是我僅僅把藍通道填充了255。
??????????????????
??????????????????在Photoshop 的幫助菜單- 關于增效工具 - 的子菜單中,可以看到我們編寫的“FillRed Filter ...”一項,當點擊它時PS即發起 about 調用,即可看到彈出的 MessageBox。
?
??????????????????(6)最后,還是附上這個小例子的源代碼下載鏈接:
??????????????????請注意這個項目中有一些設置,比如 PhotoshopSDK的目錄等,需要依據具體環境做出相應調整。PS SDK提供的資源轉換工具也包含在項目文件夾內。(注意:附件中并未包含完整PS SDK)
???????????????????http://files.cnblogs.com/hoodlum1980/FillRed.rar
??????????????????(7)總結:
??????????????????這一節講述了從項目創建,到嵌入pipl資源,建立了一個基本的濾鏡框架。但它的功能是非?;竞秃唵蔚?#xff0c;在以后的時間里,我們可能需要繼續豐富這個例子,包括為它引入對話框資源,令PS為我們的濾鏡緩存和讀取我們的參數,包括在對話框表面繪制濾鏡的預覽圖形等等。
?
??????????????????我的相關文章:
??????????????? ? 《Photoshop第三方濾鏡開發的簡介》????
??????????????????《Photoshop濾鏡開發簡介(2)--Photoshop回調函數》
??????????????????
????????????????????????????????????????????????????????????????????????????????????????????????????????????--hoodlum1980??? 2009.05.11
?
轉載于:https://www.cnblogs.com/hoodlum1980/archive/2009/05/11/1453870.html
總結
以上是生活随笔為你收集整理的怎样编写一个Photoshop滤镜(1)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mootools脚本框架下载
- 下一篇: roundcube邮箱手机端_求一款能够