Photoshop is an outstanding software in the field of digital image processing. At the same time, it also allows third parties to extend its functions in the form of plug-ins. Photoshop plug-ins can currently be divided into the following nine types: automation (batch processing) (appears under the 'Auto' submenu), color pickup, import, export (appears under the 'Import' 'Export' submenu), extension, Filters, File Format (appears under Open, Save As), Parsing (with Export function), Selection (appears under 'Select' menu). Here we take the most familiar filter as an example.
(1) Introduction to the general part of the plug-in:
We become the host by calling the main program of the plug-in. In most cases, it is Photoshop (hereinafter referred to as PS). A plug-in is actually a dynamic link library under the Windows system. (just with a different extension). PS uses LoadLibray to load plug-in modules. When the user takes the corresponding action, it will cause a series of PS calls to the plug-in module. All these calls call the same entry point. The entry point is such a function, defined as follows: (Since PS is compatible with windows and Macs, here we only give the definition on the windows system)
void ENTRYPOINT (
short selector,
use use using with using use using ’ through through ’ s ’ through use ’ s ’ s ’ through ‐ ‐ ‐‐‐ void* pluginParamBlock,
long* pluginData,
short* result); When selector=0, it has the same meaning for all types of plug-ins, that is, asking to display an About dialog box. Other values have different meanings depending on the plug-in type.
pluginParamBlock:
This is a pointer to a large structure, which is used to transfer information and data between the host and the plug-in. It has different structures for different types of plugins.
pluginData:
A pointer to int32 type. It is a value that PS saves across multiple calls to the plugin. One of its standard uses is that the plug-in can pass some global data pointers to this parameter to save,
result:
A pointer to int16, it must set the result every time the plug-in is called. Returning 0 indicates that no error occurred in the plugin code. When an error occurs, this value returns an error code. Regarding error codes, ps divides the error code ranges for different types of plug-ins and predefines some values in the SDK.
About dialog:
All plugins should respond to about calls. Plug-ins can display a custom dialog box. However, in order to maintain consistency, the following conventions should be adhered to:
(1) Display in the horizontal center of the main screen and vertically 1/3 of the height.
(2) There is no need to include an OK button, but respond to clicks at any location and the Enter key.
(2) Introduction to the filter plug-in
The function of the filter plug-in is to modify the selected area of the image. Filter behaviors range from adjusting saturation and brightness to filtering images and more. The extension of the filter under Windows is ".8BF".
The following figure shows the calling sequence between PS and filter plug-ins. It is very important. This is a picture in the SDK document. There is such a picture for each type of plug-in. What is shown here is the filter. The calling sequence of the plug-in.
Filters can be called using the filter menu, which is the top calling starting point. After calling it once, Photoshop will put the latest filter operation on the "Last Filter" submenu of the filter menu. Clicking this menu in the future will correspond to the "Last Filter Command" in the picture above. Below we will briefly introduce the process shown above. First, let’s look at the “template” of a filter’s entry point function:
EntryPoint Of Plugin :PlugInMain
// Create a definition for exported functions
#define DLLExport extern "C" __declspec(dllexport)
#define SPAPI
DLLExport SPAPI void PluginMain(const int16 selector,
void * filterRecord,
int32 * data,
int16 * result)
{
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;
}
}
Note that the above function is the most important function of our filter, because this function is provided for PS calls, we can see that this function is declared as a Dll export function. As can be seen from the calling sequence, this mechanism makes the function of this function very similar to the window procedure of the window. The window procedure is used to process messages based on MSG ID, and this function is mainly used to perform corresponding operations based on the selector. Therefore, they all contain a switch-case branch processing structure.
filterRecord
The second parameter used in the above function is a pointer to the AboutRecord structure when it is called about (that is, selector=0). When it is not called about, it is A pointer to the FilterRecord structure. The FilterRecord structure is a very large and complex structure. It is the key carrier for communication and data transfer between ps and filters. Its sizeof=452 bytes contains approximately more than 100 members. , there are a total of 7 pages in the document used to introduce the meaning of the members of this structure. The complete definition of FilterRecord is located in the header file:pifilter.h in sdk. Below I will explain some of its most basic and important members as they are mentioned.
(3) Introduction to the calling process.
(3.1) filterSelectorParameters call:
If the filter has some parameters that need to be set by the user, then it should save the parameters to a location. Then set the address to the third parameter data. PS will initialize this parameter to NULL. Whether this call occurs depends on the user's calling method. When a filter has just been called, the filter will appear in the filter's most recent command menu. The user can use this menu with the same parameters (no dialog box will be displayed at this time to request User sets new parameters) and calls again. This call does not occur when the user calls it with the last filter command. (See picture above). Therefore, parameters should be checked, verified, and initialized every time if incorrect parameters may create a risk of crashing the program.
Notice! : Since the same parameters can be used for images of different sizes, the parameters should not depend on the image size. For example, a parameter should not depend on the image width or height. It is usually more appropriate to use a percentage or scale factor as the parameter.
Therefore, your parameter data block should contain the following information:
1. A signature so that the filter can quickly confirm that this is the parameter data of it.
2. A version number so that the plug-in can be upgraded freely without changing the signature.
3. Byte order identification. (For cross-platform purposes) Indicates what endianness is currently in use.
Parameter block (parameter data block) and scripting system (script description system)
The script description system is used to cache our parameters and will be used in each call type. It is passed to the plugin so you can use it to store all your parameters. Once your Parameter block is validated, you should read the data from the passed parameters and then update your parameters. For example:
1. First call ValidateMyParameters to verify or initialize your global parameters.
2. Then call the ReadScriptingParameters method to read the parameters and write them into your global parameter data structure.
(3.2) filterSelectorPrepare call:
This call allows your plug-in module to adjust the memory allocation algorithm of ps. The "Last Filter" command will start from this call. PS sets maxSpace (which is a member of the FilterRecord structure (second parameter), new members that appear thereafter will not be specially explained) to the maximum number of bytes he can allocate for the plug-in.
ImageSize, planes and filterRect members:
These members are now defined (referring to sdk 6.0) and can be used to calculate your memory requirements. imageSize, image size. planes, number of channels.
filterRect: filter rectangle.
Here I would like to emphasize this filterRect, which is the Rect type defined by PS (similar to the RECT structure of the windows api). This concept is also the concept of "selection enclosing rectangle" mentioned and repeatedly emphasized in my research post on "Principles of Displacement Filters". At that time, I had not yet come into contact with PS SDK. Here we see that in Photoshop's code, it's called filterRect.
bufferSpace:
If the filter wants to allocate more than 32K of space, then this member should be set to the number of bytes you want to apply for. ps will try to release a space of this size before the next call (start call) to ensure that your next call is successful.
(3.3)filterSelectorStart call:
In this call, the parameter data block should be verified, and based on the parameters passed by ps, update your own parameters and display your UI if necessary. Then go into your data processing process.
AdvanceState callback: (used to request PS to update the corresponding data)
This is a very important callback function provided by PS to the filter. Its definition is as follows:
# typedef short OSErr;
typedef MACPASCAL OSErr (*AdvanceStateProc) (void);
His function is to require PS to immediately update the outdated data in FilterRecord. For example, we can set our new processing rectangle and then call this function, and then we can get the new data we need after this call. If you use this callback, then your core processing can all be done in the start call without using a continue call. When the processing is completed, you can set inRect=outRect=maskRect=NULL.If you do not use this callback, then you should set the first rectangular area and then use continue to call the loop processing.
For example, we can use the following loop in the start call to process the image until the entire image processing is completed.
advanceState callback example
##Code highlighting produced by Actipro CodeHighlighter (freeware)
//m.sbmmt.com/
for
(..){ SetOutRect(out_recc); gFilterRecord
->
outLoPlane=0; gFilterRecord->
outHiPlane=(g_Planes-1);
//
Request PS to update data!
##*gResult = gFilterRecord- >advanceState(); if
(*gResult ! = kNoErr) goto
done; //
Processing data . . . . . }
Inrect, Outress & Maskrect
Set up and outRect (Maskrect) to request the first processing area when using a mask) to request the first processing area. If possible, you should cut the image into smaller pieces to reduce the amount of memory required when passing data. It is a common practice to use 64x64 or 128x128 patches.
(3.4) filterSelectorContinue call:
When any one of inRect, outRect, and maskRect is not an empty rectangle, this call will continue to occur.
inData, outData & maskData
These three members are void * pointers, pointing to the starting point of the image data you requested. Our main job is to calculate and then set the data here. When calling, inData and outData will be filled with the image data at the corresponding position according to the rectangular area you requested. Then your processing task is mainly to modify the outData data area and tell PS your processing results, and PS will update the results to the image. Note that you do not need to consider the passed in selection and selection shape, because this working PS will automatically protect the data outside the selection, you only need to pay attention to your own algorithm. After the processing is completed, set the inRect and outRect for the next processing. If it's over, just leave them blank. Note that inRect is not necessarily the same as outRect.
progressProc callback: (used to set the PS progress bar)
Its type is defined as:
typedef MACPASCAL void (*ProgressProc) (int32 done, int32 total);
This is the callback function provided by PS for setting the progress bar. The plug-in can use this callback to let PS update the progress bar below. It receives two parameters, one is the number of completed tasks and the other is the total number of tasks. When you need to give users feedback on your processing progress, you can set the progress like this:
gFilterRecord->progressProc ( progress_complete, progress_total); The host queries whether the user has taken a cancellation action)
The type is defined as: typedef Boolean (*TestAbortProc) (void);
This callback is used to query whether the user has canceled the operation. In a process that takes a long time During processing, the plug-in should call this function several times per second to confirm whether the user has canceled (such as pressing the Esc key, etc.). If TRUE is returned, the current operation should be canceled and a positive error code returned.
(3.5) filterSelectorFinish call:
This call allows the plug-in to clean up after processing. finish will be called if and only if the start call succeeds (no error is returned). Even if an error occurs in continue, the finish call will still occur.
Notice! : Be careful about user cancellation during a continue call, usually when you are expecting the next continue call. If the user cancels, the next call will be a finish call, not continue! ! ! .
Rules: If the start call is successful, Photoshop guarantees that a Finish call will be initiated.
(3.6) Error codes
The plug-in can return standard operating system error codes, or report its own errors (positive integers). It is defined in the sdk:
#define filterBadParameters –30100 //
#define filterBadMode –30101 // This mode image is not supported
(4) Raindrop filter DEMO
We mainly talked about the basic knowledge of a filter. Then this only extracts the most important parts to explain, and more technical details cannot be estimated due to space limitations. The main basis of the above is the document of PS SDK6.0. The main rules belong to the translation of the original text, and a small amount of content belongs to my personal practice and understanding (I will use color to distinguish the personal understanding when I have time). bright). Now we start to explain the demo! ! ! really tired. . . .
The algorithm of the raindrop filter mainly refers to a foreign website. This is the address reported to me by a netizen in a reply to another article. The origin of this filter is the spherization algorithm (there is this built-in filter in PS). We will not introduce the algorithm because although it is the core of the filter, it is not the focus of this article. We give the pseudo code of the water drop effect based on this:
---Water drop effect --------------------
At the position cx, cy, a radius R is randomly generated,
Make a spherical distortion with R as the radius at cx, cy.
Add the highlight and shadow of the water drop at this location.
Make a 3*3 template Gaussian blur inside the water droplet.
with with with the help of # Repeat the above process to produce multiple water droplets. This is the core algorithm of this filter. The specific formula is not given. But in order to process the data, we must understand how to locate a pixel data in the data passed by PS. For example, we want to obtain the pixel data of the R channel at the (x, y) position on the original image. How do we get it? Here we also introduce the important data members related to pixel positioning in FilterRecord,
int32
inRowBytes, outRowBytes, maskRowBytes,
This is the corresponding scan line width in inData, outData, and maskData. It is of type int32 and belongs to the data provided by PS to the plug-in. Equivalent to BitmapData.Stride in c#. But please note that in inData and outData, the data may not be aligned according to 4byte! But ps doesn't say that there are no redundant bytes at the end of the line. In short, it is the number of bytes (span) occupied by a row of image data in memory.
int16 Inloplane, Inhiplane, Outloplane, Outhiplane, ## belong to the data to the PS when the plug -in is PS request. , note that this value is an index value with 0 as the base. For example, for RGB images, there are three channels, 0 to 2 corresponding to B, G, and R respectively (note that the order in the file is maintained here, not the customary RGB order in PS!!!). We can request one channel at a time or multiple channels at a time, and the data from multiple channels will be interleaved in sequence. For example, if we set inLoPlane=0, inHiPlane=2, the inData data arrangement provided by PS to us is:
. 1. Then the inData provided by PS to us is: Locate a pixel as follows:
First, the number of channels we request is set to planes: then: planes=inHiPlane-inLoPlane+1; //Number of channels uint8 *pixels=(uint8*)inData ;
We get the channel data expression with index k at the (x, y) position as follows:
pixels [ y * inRowBytes + x * planes + k ]; + y * inRowBytes + x * planes + k);
, then the pixel data at the (x, y) position is as follows:
pixels [ y * inRowBytes + x * 3 ]; // p(x,y).B
pixels [ y * inRowBytes + x * 3 + 1 ]; // p(x,y).G
pixels [ y * inRowBytes + x * 3 + 2 ]; // p(x,y).R
Okay, with the above foundation, we can look at the following Gaussian 3*3 template processing, The Gaussian 3*3 template is as follows:
1 2 1
2 4 2 /16
1 2 1
We use the above pixel positioning method to easily write the content of the following loop processing :
Gaussian Blur (3*3 template)
sum=0;
// Process each channel in turn
for(k=0;kg_Planes;k++)
{
//blur it!
## sum+=bufferPixels[(j-1)*##rowBytes+(i-1)*g_Planes +k]; sum
+=bufferPixels[(j-1)*rowBytes+(i)*##g_Planes +k]*2; sum+=
bufferPixels[(j- 1)*rowBytes+(i+ 1)*##g_Planes +k]; sum+=
bufferPixels[j*rowBytes+(i- 1)*##g_Planes +k]* 2; sum+=bufferPixels[j*rowBytes+i*g_Planes +k]*4;
sum+=bufferPixels[j*rowBytes+(i+1)*g_Planes +k]*2;
sum+=bufferPixels[(j+1)*rowBytes+(i-1)*g_Planes +k];
sum+=bufferPixels[(j+1)*rowBytes+(i)*g_Planes +k]*2;
sum+=bufferPixels[(j+1)*rowBytes+(i+1)*g_Planes +k];
sum=sum>>4;//即除以16
pixels[j*rowBytes+(i)*g_Planes +k]=sum;
}
(5) Conclusion:
Finally, let’s take a look at the screenshot of the effect of using the filter: When PS starts, it will scan the plug-ins in each plug-in directory and load them into the corresponding menu.
using off ’s ’ out ’s out through ‐‐ ‐‐‐ together's, ‐ to , just put it in the filter installation directory of Photoshop. For example, for Photoshop CS, its filter installation directory may be in the shape of:
“C:\Program Files\Adobe\Photoshop CS\Plug-in\Filter Mirror\”
About the PS SDK, you can get it from Adobe official website. I don’t know if it is free at the moment. . . . .
(6) References: (1) Photoshop SDK 6.0.
(2)Photoshop SDK CS.
(3) (Raindrop filter algorithm) Filter: Raindrops:
//m.sbmmt.com/
-------- -------------------------------------------------- ------------------ Appendix: Statement from Adobe SDK! -------------------------------------------------- --------------------------
// ADOBE SYSTEMS INCORPORATED// Copyright 1993 - 2002 Adobe Systems Incorporated
// All Rights Reserved
//
// ADOBE Systems Incorporated// Copyright 1993 - 2002 Adobe 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.
//
// Note: Adobe permits you to use, modify, and distribute this file subject to the terms of the applicable Adobe license agreement.
// If you obtained this file from a non-Adobe party, your use, modification, and distribution require a previously signed Adobe License Agreement.
//------------------------------------------------ ----------------------------------
For more introduction to the development of third-party filter plug-ins for Photoshop, please pay attention to the PHP Chinese website!