ホームページ > 記事 > ウェブフロントエンド > Photoshopフィルターの書き方(1)
昔、Photoshopのフィルター開発の基本的な考え方について簡単に記事を書き、フィルターとPSの連携関係について解説し、雨粒効果フィルターのデモも提供しました。しかし、ソースコードがありません。そして、Photoshop フィルターを最初からプログラムするにはどうすればよいでしょうか? PS フィルター プラグインの最も単純な基本フレームワークを構築し、これをベースに必要な機能を追加していくにはどうすればよいでしょうか?ここでは、例として、最も基本的な建設プロジェクトから始めて、ネットユーザーからの質問に答えます。この例 (このネチズンの問題でもあります) は、画像を「赤」で塗りつぶすだけの最も単純なフィルターを作成したいということです。もちろん、PS ユーザーにとって、これはショートカット キーの操作だけで済みます (フィルターは通常、より複雑なタスクを完了するために使用されます)。ミラーの作成方法を説明するために、この最も基本的な例から始めます。プロセス。この例のソース コードのダウンロード リンクは、記事の最後に添付されます。
(1) 私たちが使用する開発ツールは、Photoshop SDK CS と組み合わせた Visual Studio .NET 2005 バージョンです (基本的に、いくつかの C++ コード、リソース、およびその他のファイルで構成される配布パッケージ)。使用する開発言語はCおよびC++です。 c#または他の言語を使用しても大丈夫ですか?現時点ではそれは実現不可能です。したがって、Photoshop フィルターを開発するには、開発者は最も重要な C および C++ の十分な基礎を持っている必要があります。もちろん、開発者が画像処理やデジタル信号処理の基礎知識に精通している方が良いでしょう。(2) ツールの準備ができたら、VS2005 を開いて新しいプロジェクトを作成します。プロジェクトテンプレートとしてVisual C++のWin32を選択します。プロジェクト名には、以下に示すように、赤を塗りつぶすためにこのフィルターが使用されることを示す「Fillred」フィルターなど、作成するフィルター名を入力します。 [OK] をクリックした後、ポップアップ ダイアログ ボックス、ポップアップ ダイアログ ボックス、ポップアップ ダイアログ ボックス、ポップアップ ダイアログ ボックス、ポップアップ ダイアログ ボックス、ポップアップ ダイアログ ボックス、ポップアップ ダイアログ ボックス、ポップアップ ダイアログ ボックス、ポップアップ ダイアログ ボックス、ポップアップ ダイアログ ボックス、ポップアップ ダイアログ ボックス、ポップアップ ダイアログ ボックス、ポップアップ ダイアログ ボックス、ポップ-アップ ダイアログ ボックス、ポップアップ ダイアログ ボックス、ポップアップ ダイアログ ボックス、ポップアップ ダイアログ ボックス、ポップアップ ダイアログ ボックス、ポップアップ ダイアログ ボックス 「アプリケーションの設定」をクリックし、「DLL」を選択します。アプリケーションの種類に「」を入力し、「OK」をクリックします。
(a) ルーチン では、プロジェクトで使用される文字セットを「マルチバイト文字セットを使用」に変更したいと思います。これにより、二重引用符を使用して char* と文字列型を直接使用できるようになります。もちろん Unicode も使用できますが、両者で使用する文字列関数は異なります。お好みに合わせて設定できます。
(b) Photoshopのフィルターファイルの拡張子はデフォルトでDLLとして出力されているので、リンカー→一般で出力ファイルの拡張子を8bfに設定します。以下のように変更します。下 (C) 下の [ツール] オプションをクリックします。ポップアップ ダイアログ ボックスで、[プロジェクトとソリューション] - [VC ++ ディレクトリ] を選択し、右側のドロップダウン ボックスで [ファイルを含む] を選択します。 SDK を追加します。これにより、ファイルが見つからないというエラーを報告することなく、プロジェクトをコンパイルすることが容易になります。下の写真に示すように: // 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()
{
}
= min(m_Tile.top + TILESIZE, gFilterRecord->filterRect.bottom);
// set inRect,そうです
ZeroPsRect(&gFilterRecord->inRect); //ただ塗りつぶすだけなので、PS に伝える必要はありません
CopyPsRect( &m_Tile, &gFilterRecord-> gFilterRecord
->
in LoPlane ; gFilterRecord-&g t;アウトロプレーン
= 0 ; gFilterRecord->outHiPlane =
(gFilterRecord->飛行機 - 1);}
// 現在のパッチユーザーが Esc を押すと、次の呼び出しは FinishvoidDoContinue()
{ if(gFilterRecord ==] になることに注意してください。 NULL ) gFilterRecord-> outHiPlane -
gFilterRecord
->outLoPlane +
1;
//
; *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); :わかりやすく明確にするため、デフォルトでは画像を RGB 形式として扱います (実際にはこれは行われません) *
飛行機 + j *ストライド + 2 ] = 255;処理が完了したかどうかを終了 if(m_Tile.right >= gFilterRecord->filterRect.right && m_Tile.bottom >= gFilterRecord-> filterRect.bottom)
{
->inRect); ZeroPsRect(&
gFilterRecord -> } //次を設定タイル
if(m_Tile.right gFilterRecord->filterRect.right) {
//右に1スペース移動
m_Tile .left = m_Tile.right; filterRect.right);
else ->filterRect.left; m_Tile = min(m_Tile.left)
+
タイルサイズ、gFilterRecord->filterRect.right); m_Tile.top
= m_Tile.bottom; m_Tile.bottom =
min(m_Tile.bottom +) タイルサイズ、gFilterRecord ->filterRect.bottom); } ZeroPsRect(
&
gFilterRecord->
inRect);CopyPsRect(&m_Tile, &gFilterRecord->outRect);
//全チャンネルリクエスト(データはインターリーブ配信)
gフィルターレコード ->inLoPlane = 0;
gFilterRecord->inHiPlane =0 ;
gFilterRecord->outLoPlane = 0;
gFilterRecord -> -1);} //当面void
DoFinish()
{}
フレームワーク、 DLL が提供するエクスポート関数の 1 つが PluginMain 関数であることがわかります。これにより、画像を 128*128 ピクセルのスライスに分割します。これにより、毎回 PS とフィルターの間で転送されるデータの量が減ります。特に非常に大きな画像の場合、スライス処理はメモリ不足の状況に対処するのに役立ちます。これは Photoshop によって推奨されている一般的なサイズです。 呼び出しプロセスでは、開始呼び出しで最初のタイル (Tile) を初期化してから、最初のタイルを outRect に設定します。これは、PS に対して、四角形の位置の処理を受け取るためのバッファーを提供するように要求することを意味します。データは、inRect と同様に、単に塗りつぶしているだけなので、画像の元の色は気にしないため、inRect を「空の長方形」に設定できます。画像チャネル ここでは、コードを直感的に理解しやすくするために、RGB 画像、つまり 3 つのチャネルを持つ画像のみを考慮します。最初の outRect を設定した後、PS は順番に continue の呼び出しを開始し、すべてのパッチが処理されるまで、パッチは左から右、上から下にコラージュされます。 inData と outData は、PS によってフィルターに提供されるアプリケーション データと「ライトバック バッファ」であり、1 つは読み取り用で、もう 1 つは書き込み用で、フィルターが PS にリクエストを通知するときに埋められるデータによって制御されることに注意してください。操作時間は決してその境界を越えてはなりません。 【
注意事項
】
Photoshopプログラムが崩れる原因となりますので、注意して操作してください。主 フィルタのメインコードの詳細については、コードの注釈と前回の記事の説明を参照してください。そしてPS SDKの公式ドキュメント。コードは非常に単純なので、ここではコードの原理については詳しく説明しません。上記のコードはすべて、ps sdk のヘッダー ファイルを参照する必要があります。
(4) この時点で、プロジェクトは正常にコンパイルできます。しかし、次のステップは PIPL リソースに投資することです。
Photoshop でフィルターが正しく認識され、読み込まれるようにするには、フィルターを PIPL リソースに挿入する必要があります。 PS が提供するドキュメントによると、PIPL の発音は「ピップル」で、プラグイン プロパティ リストを意味します。これは、プラグイン モジュールのメタデータ (メタデータ) を表すために使用される、柔軟で拡張可能なデータ構造です。 pipl には、いくつかのタグ、各プラグインを制御するさまざまな静的プロパティなど、Photoshop がプラグイン モジュールを認識して読み込むためのすべての情報が含まれています。フィルタには 1 つ以上の「pipl」構造を含めることができます。
フィルタ用の pipl リソースを挿入するプロセスは次のとおりです。まず、*.r (Macintosh の Rez ファイル) ファイルをプロジェクトに追加し、次に cl.exe を使用してこのファイルを *.rr ファイルにコンパイルする必要があります。最後に Ps SDK を使用します。提供されているリソース変換ツール CnvtPipl.Exe は、*.rr ファイルを *.pipl ファイルに変換し、フィルター用の *.rc リソース ファイルを追加し、rc ファイルの末尾に pipl ファイルを含めます。 PS SDK は、汎用の属性定義を含むユニバーサル R ファイル (Pigneral ファイル) を提供しています。就 (A) 以下では、プロジェクトに R ファイルを追加します。リソース マネージャーで [リソース ファイル] フォルダーを右クリックし、クリックして新しいファイルを追加し、ファイル名に「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文件内で我们確認可能滤镜の名前、位置する滤镜分別名(その後、右のボタンでそのファイルを閉じ、そのファイルの独自に生成されたコマンド行で、次の内容を入力します: 在「命令行」一栏点击编辑按钮、出てきた窗口中に入力以下の二つの行:cl /I E:CodesAdobe~1samplecodeCommoninclude /IE:CodesAdobe~1PhotoshopAPIPhotoshop /IE:CodesAdobe~1PhotoshopAPIPICA_SP /IE:CodesAdobe~1samplecodeCommonResources /EP /DWIN32=1 /Tc"$ (入力パス)" > "$(ProjectDir)$(InputName).rr"
Cnvtpipl.exe "$(ProjectDir)$(InputName).rr" "$(ProjectDir)$(InputName).pipl"
第二行表示使用PS SDK中的 Cnvtpipl.exe 工具把 rr文件编译为 pipl文件,请注意为了简单,我把该工具复制到了项目的源文件所在文件夹下面。它位于SDK的路径是:“\samplecode\resources\cnvtpipl.exe”。
(b)下面我们为项目添加一个 rc文件,同样右键点击“资源文件”,添加一个FillRed.rc文件。
这是一个windows的资源文件,我们暂时还不需要任何资源,所以我们直接用文本方式打开IDE自动生成的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フィルター…」をクリックすると表示されます。 s 's' - −----を使用する外に出ますPS SDK が提供するリソース変換ツールもプロジェクト フォルダーに含まれています。 (注: 完全な PS SDK は添付ファイルには含まれていません) http://files.cnblogs.com/hoodlum1980/FillRed.rar
(7) 概要: pipl リソースを埋め込むと、基本的なフィルター フレームワークが作成されます。ただし、その機能は非常に基本的で単純です。将来的には、ダイアログ リソースの導入、PS によるフィルターのパラメーターの読み込み、ダイアログ サーフェス描画フィルター プレビュー グラフィックスなどを含めて、この例をさらに充実させる必要があるかもしれません。すぐ。
Photoshop フィルターの書き方 (1) の関連記事は、PHP 中国語 Web サイトに注目してください。