不是。pipl粉饼是深圳市立美化妆品有限公司生产的一款换化妆品。该公司是一家受官网认证法律保护的正规公司,不是假的,该化妆品祛黄效果很有效,产品中添加了桑白皮等五大美白成分,有效改善暗沉暗黄。营养丰富,该化妆品带来更多的肌肤益处成分,不含任何的刺激配方,以天然植物作为原料,深受用户喜爱
在上一篇文章中,我们讲解了怎样创建一个Photoshop滤镜的项目,以及如何为滤镜嵌入PIPL资源使滤镜可以被PS识别和加载。并且我们已经建立了一个最简单最基本的滤镜框架。在这篇文章中,我们将细化滤镜和PS之间的调用流程,我们将为滤镜引入一个对话框资源,使用户可以对滤镜进行自定义参数的配置。并且我们将看到当用户从不同菜单位置发起滤镜调用时的流程区别,然后我们还将为我们的滤镜参数引入PS脚本描述系统的读写支持,将我们的参数存入PS的脚本系统中,并在以后的调用中读取出这些参数。
(1)设计我们的滤镜参数。
我们的滤镜完成的是一个最基本的任务,仅仅是“填充”,因此我们可以对填充的颜色进行配置,此外,我们还可以设置填充颜色的不透明度。因此我们引入下面的参数,把它定义为一个struct,包括一个RGB填充色,和一个不透明度(0~100):
//======================================
// 定义我们的参数
//======================================
typedef struct _MYPARAMS
{
COLORREF fillColor; //填充颜色
int opacity; //百分比(0~100)
} MYPARAMS;
(2) 现在我们添加一个对话框资源。编辑对话框模块如下所示。然后我们对主要控件设置控件ID。
注意编辑资源文件后,由于VC将会重写rc文件,因此在编译项目前,我们还需要手工打开rc文件,自己重新添加#include "FillRedpipl"。
否则编译好的滤镜将无法被PS正确识别和加载到滤镜菜单。
(3)下面我们为该对话框添加窗口过程。为此我们为项目添加 ParamDlgh 和 ParamDlgcpp文件。
注意由于窗口过程位于我们的DLL中,因此我们必须把窗口过程声明为DLL导出函数,以便让系统知道该函数的地址。
关于窗口过程的编写则完全属于 windows 编程领域的内容(这方面的知识可以参考相关书籍),这里我们不详细介绍怎样写窗口过程。但值得一提的是,我在这里引入了一个PS中的UI特性,即PS中例如它的字体设置对话框,当鼠标悬停在控件前面的Lable(Static标签)上方时,光标形状可以改变为特殊光标,按下并左右拖动鼠标,则相关控件的值就会根据鼠标移动方向自动增加或减小,类似slider控件的效果。因此我在窗口过程中为它加入了这个特性,这会使得窗口过程的代码看起来稍显复杂一些,不过这个功能(可能是PS发明的?)很有趣也很新颖。为此我还引入了一个自定义的光标文件。具体代码不贴出了,请参考项目源代码中的 ParamDlgcpp文件中的代码。
(4)在第一篇文章的基础上,我们需要改写FillRedcpp中的一些代码。
因为现在我们引入了不透明度参数,不透明度的算法是:(opacity = 0~ 100)
结果值 = 输入值 (1- opacity001) + 填充颜色 opacity001;
(a)对DoStart 和 DoContinue:我们需要知道原图中原来的颜色,因此我们的 inRect 和 inHiPlane 将不在为空矩形。这体现在 DoStart 和 DoContinue 函数中,我们对inRect 和 inHiPlane 修改为和 outRect , outHiPlane 一致,这样PS就会把原图数据通过 inData 发送给我们。
(b)当用户点击滤镜菜单时,将从 parameter 调用开始,这样我们就在这里设置一个标记,表示需要显示对话框。
(c)当用户点击“最近滤镜”菜单时,将从 prepare 调用开始,这样表示我们不需要显示对话框,而是直接取此前的缓存参数。为此我们引入 ReadParams 和 WriteParams 函数。即使用PS提供的回调函数集使我们的参数和PS Scripting System进行交换数据。
下面我们主要看一下DoContinue函数发生的变化。主要是对算法进行了改动,对 inRect , inHiPlane 这两个数据进行了变动,以请求PS发送数据。在DoStart()函数中设置了第一个贴片,对inRect 和 inHiPlane 的改动是同样的。同时,在DoStart函数中, 根据事先设置过的标志,来决定是否显示对话框。
//DLLMain
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
dllInstance = static_cast<HINSTANCE>(hModule);
if (ul_reason_for_call == DLL_PROCESS_ATTACH || ul_reason_for_call == DLL_THREAD_ATTACH)
{
//在DLL被加载时,初始化我们的参数!
gParamsfillColor = RGB(0, 0, 255);
gParamsopacity = 100;
}
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;
gFilterRecord = (FilterRecordPtr)filterRecord;
if (selector == filterSelectorAbout)
sSPBasic = ((AboutRecord)gFilterRecord)->sSPBasic;
else
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 DoAbout()
{
AboutRecord aboutPtr = (AboutRecord)gFilterRecord;
PlatformData platform = (PlatformData)(aboutPtr->platformData);
HWND hwnd = (HWND)platform->hwnd;
MessageBox(hwnd, "FillRed Filter: 填充颜色 -- by hoodlum1980", "关于 FillRed", MB_OK);
}
//这里准备参数,就这个滤镜例子来说,我们暂时不需要做任何事
void DoParameters()
{
//parameter调用说明,用户点击的是原始菜单,要求显示对话框
m_ShowUI = TRUE;
//设置参数地址
if(gFilterRecord->parameters == NULL)
gFilterRecord->parameters = (Handle)(&gParams);
}
//在此时告诉PS(宿主)滤镜需要的内存大小
void DoPrepare()
{
if(gFilterRecord != NULL)
{
gFilterRecord->bufferSpace = 0;
gFilterRecord->maxSpace = 0;
//设置参数地址
if(gFilterRecord->parameters == NULL)
gFilterRecord->parameters = (Handle)(&gParams);
}
}
//inRect : 滤镜请求PS发送的矩形区域。
//outRect : 滤镜通知PS接收的矩形区域。
//filterRect : PS通知滤镜需要处理的矩形区域。
//由于我们是使用固定的红色进行填充,实际上我们不需要请求PS发送数据
//所以这里可以把inRect设置为NULL,则PS不向滤镜传递数据。
void DoStart()
{
BOOL showDialog;
if(gFilterRecord == NULL)
return;
//从Scripting System 中读取参数值到gParams中。
OSErr err = ReadParams(&showDialog);
//是否需要显示对话框
if(!err && showDialog)
{
PlatformData platform = (PlatformData)(gFilterRecord->platformData);
HWND hWndParent = (HWND)platform->hwnd;
//显示对话框
int nResult = DialogBoxParam(dllInstance, MAKEINTRESOURCE(IDD_PARAMDLG),hWndParent,(DLGPROC)ParamDlgProc, 0);
if(nResult == IDCANCEL)
{
//选择了取消
ZeroPsRect(&gFilterRecord->inRect);
ZeroPsRect(&gFilterRecord->outRect);
ZeroPsRect(&gFilterRecord->maskRect);
WriteParams();
//注意: (1)如果通知 PS 用户选择了取消,将使PS不会发起 Finish调用!
// (2)只要 start 调用成功,则PS保证一定发起 Finish 调用。
gResult = userCanceledErr;
return;
}
}
//我们初始化第一个Tile,然后开始进行调用
m_Tileleft = gFilterRecord->filterRectleft;
m_Tiletop = gFilterRecord->filterRecttop;
m_Tileright = min(m_Tileleft + TILESIZE, gFilterRecord->filterRectright);
m_Tilebottom = min(m_Tiletop + TILESIZE, gFilterRecord->filterRectbottom);
//设置inRect, outRect
//ZeroPsRect(&gFilterRecord->inRect); //我们不需要PS告诉我们原图上是什么颜色,因为我们只是填充
CopyPsRect(&m_Tile, &gFilterRecord->inRect);//现在我们需要请求和outRect一样的区域
CopyPsRect(&m_Tile, &gFilterRecord->outRect);
//请求全部通道(则数据为interleave分布)
gFilterRecord->inLoPlane = 0;
gFilterRecord->inHiPlane = (gFilterRecord->planes -1);;
gFilterRecord->outLoPlane = 0;
gFilterRecord->outHiPlane = (gFilterRecord->planes -1);
}
//这里对当前贴片进行处理,注意如果用户按了Esc,下一次调用将是Finish
void DoContinue()
{
int index; //像素索引
if(gFilterRecord == NULL)
return;
//定位像素
int planes = gFilterRecord->outHiPlane - gFilterRecord->outLoPlane + 1; //通道数量
//填充颜色
uint8 r = GetRValue(gParamsfillColor);
uint8 g = GetGValue(gParamsfillColor);
uint8 b = GetBValue(gParamsfillColor);
int opacity = gParamsopacity;
uint8 pDataIn = (uint8)gFilterRecord->inData;
uint8 pDataOut = (uint8)gFilterRecord->outData;
//扫描行宽度(字节)
int stride = gFilterRecord->outRowBytes;
//我们把输出矩形拷贝到 m_Tile
CopyPsRect(&gFilterRecord->outRect, &m_Tile);
for(int j = 0; j< (m_Tilebottom - m_Tiletop); j++)
{
for(int i = 0; i< (m_Tileright - m_Tileleft); i++)
{
index = iplanes + jstride;
//为了简单明了,我们默认把图像当作RGB格式(实际上不应这样做)
pDataOut[ index ] =
(uint8)((pDataIn[ index ](100-opacity) + ropacity)/100); //Red
pDataOut[ index+1 ] =
(uint8)((pDataIn[ index+1 ](100-opacity) + gopacity)/100); //Green
pDataOut[ index+2 ] =
(uint8)((pDataIn[ index+2 ](100-opacity) + bopacity)/100); //Blue
}
}
//判断是否已经处理完毕
if(m_Tileright >= gFilterRecord->filterRectright && m_Tilebottom >= gFilterRecord->filterRectbottom)
{
//处理结束
ZeroPsRect(&gFilterRecord->inRect);
ZeroPsRect(&gFilterRecord->outRect);
ZeroPsRect(&gFilterRecord->maskRect);
return;
}
//设置下一个tile
if(m_Tileright < gFilterRecord->filterRectright)
{
//向右移动一格
m_Tileleft = m_Tileright;
m_Tileright = min(m_Tileright + TILESIZE, gFilterRecord->filterRectright);
}
else
{
//向下换行并回到行首处
m_Tileleft = gFilterRecord->filterRectleft;
m_Tileright = min(m_Tileleft + TILESIZE, gFilterRecord->filterRectright);
m_Tiletop = m_Tilebottom;
m_Tilebottom = min(m_Tilebottom + TILESIZE, gFilterRecord->filterRectbottom);
}
//ZeroPsRect(&gFilterRecord->inRect);
CopyPsRect(&m_Tile, &gFilterRecord->inRect);//现在我们需要请求和outRect一样的区域
CopyPsRect(&m_Tile, &gFilterRecord->outRect);
//请求全部通道(则数据为interleave分布)
gFilterRecord->inLoPlane = 0;
gFilterRecord->inHiPlane = (gFilterRecord->planes -1);;
gFilterRecord->outLoPlane = 0;
gFilterRecord->outHiPlane = (gFilterRecord->planes -1);
}
//处理结束,这里我们暂时什么也不需要做
void DoFinish()
{
//清除需要显示UI的标志
m_ShowUI = FALSE;
//记录参数
WriteParams();
} (5)从PS Scripting System中读写我们的参数,我们为项目添加 ParamsScriptingh 和 ParamsScriptingcpp,代码如下。引入ReadParams 和 WriteParams 方法,该节主要涉及 PS 的描述符回调函数集,比较复杂,但在这里限于精力原因,我也不做更多解释了。具体可以参考我以前发布的相关随笔中有关讲解PS回调函数集的一篇文章以及代码注释。其相关代码如下:
#include "stdafxh"
#include "ParamsScriptingh"
#include <stdioh>
OSErr ReadParams(BOOL showDialog)
{
OSErr err = noErr;
PIReadDescriptor token = NULL; //读操作符
DescriptorKeyID key = NULL; //uint32,即char,键名
DescriptorTypeID type = NULL;
int32 flags = 0;
int32 intValue; //接收返回值
char text[128];
//需要读取的keys
DescriptorKeyIDArray keys = { KEY_FILLCOLOR, KEY_OPACITY, NULL };
if (showDialog != NULL)
showDialog = m_ShowUI;
// For recording and playback 用于录制和播放动作
PIDescriptorParameters descParams = gFilterRecord->descriptorParameters;
if (descParams == NULL)
return err;
ReadDescriptorProcs readProcs = gFilterRecord->descriptorParameters->readDescriptorProcs;
if (readProcs == NULL)
return err;
if (descParams->descriptor != NULL)
{
欢迎分享,转载请注明来源:品搜搜测评网