PhotoShop 插件开发之选区(Selection)
作者:耿海增   blog:http://genghz.spaces.live.com 

         我们的程序里用到的图都是放在一张大图里的,所以就有一个文件记录每个小图是放在这张大图的什么地方,类似这个样子:<name="button" left="10" top="30" right="24" bottom="70"/>.图要是少了还好,多到几十、几百个这样的记录,每次要更新一个图都要找半天,尤其是界面大变的时候,几乎所有的小图的位置都变了,这样就要在PhotoShop里找到每一个小图,记下它的坐标,然后在写到配置文件中。要是偶尔做做也就忍了,可是这种不幸的事情经常发生,忍无可忍,觉得这种事情计算机应该可以胜任,它能干的事情,我们坚决不能替它干。仔细研究了几天,总算研究明白了PS的插件机制,可以实现先Ctrl+C一些坐标位置,然后在PS中选中这些区域。
  
         还是Adobe比较牛,我们辛辛苦苦帮它开发插件,它还要收费。现在的PS插件开发的SDK已经不免费下载了,还好在免费的互联网上还能找到早期版本的免费SDK,我找到的是6.0的,开发的插件可以在最新的PS CS2中使用。
 
         据官方文档声明,PS大概支持9种插件,比较常见的是Filter,俗称滤镜,一般用来实现一些特殊的图像处理算法,如边缘提取等,我感兴趣的是Select插件,看名字就像是和选区有关。插件的使用很简单,放到PS安装目录下的Plug-Ins目录下的相应类别下即可,比如滤镜就放在Plug-Ins/Filters下,扩展名是.8BF,选择插件放在Plug-Ins/Select下,扩展名为.8BS.PS启动时会搜索这个目录。
 PS的SDK带了很多插件的例子,你可以找你感兴趣的那个类别的插件例子看看,然后改改就可以了。我们先看看PS 6.0 SDK 带的Selection目录下的Selectorama这个例子。它演示了如何在当前的文档上选中感兴趣的区域,不过例子似乎稍微复杂了点儿。
 PS的Windows下的插件一般是一个标准的dll,入口函数为PluginMain,原型是
 void PluginMain (const short selector,PISelectionParams *selectionParamBlock,long *data,short *result);
 其中,selector是一个类型参数,说明本次调用的目的是什么,如果是常量"selectionSelectorAbout",说明需要显示一个关于对话框。在滤镜插件中,PluginMain会被调用多次,可以根据selector来决定具体做什么操作。
selectionParamBlock  是指向一个庞大的结构的指针,里面几乎有所有你需要的东西。比如,当前文档的大小可以通过 selectionParamBlock->documentInfo->bounds 获取,如果想知道现在用户是否选择了一块区域,可以通过 selectionParamBlock->documentInfo->selection->bounds 来获取。
 剩下的两个都是输出参数,可以用来存储句柄,返回错误等,暂时可以不用理会。
在PluginMain函数中,会间接调用DoExecute这个函数,传递的参数叫globals,其实是把输入参数 selectionParamBlock 包装了一下,真正有用的还是globals->selectionParamBlock.
 在插件中,如果想从PS里读数据,需要一个叫做read port的东西,例子中使用了ReadFromWritePort这个宏来获取一个read port,这个我们暂时可以不用管它,接着向下看,会看到分配了三块缓冲区:sBuffer,dBuffer,rBuffer,如果transparency不空的话,还会分配一个mBuffer的缓冲区。我实际用到的只是sBuffer和dBuffer,其它两个高级的东东还没用到。接下来是调用 AccountChannel 计算需要处理的通道,一般会有R G B 三个通道。然后就是关键的 ApplyChannel 函数来完成实际的工作。
这个函数的参数很多,不过你只要记住刚才提到的sBuffer和dBuffer就够了。sBuffer用来保存从当前的图像中读来的图像数据,dBuffer用来保存你的选区信息,和sBuffer一一对应,如果某个象素需要选中,直接赋值为255即可。原例中需要选择的部分赋值是原来图像的内容,经过实践发现这样会造成魔棒选区的特效,我用不着这个高级功能,所以就直接赋成255了,可以精确的按我的要求工作。在这个函数里,考虑到图像可能会比较大,一次读过来可能受不了,所以先用了两个循环,按64×64的块大小循环读取处理,我们就可以再来一次循环,对每个64×64块的每个象素处理,根据剪贴板里设定的选区信息,判断当前象素的位置是否在这个选区内,如果是,就把dBuffer中的相应位置置为255,否则就是0。详情请参阅代码,为了使程序流程清楚,代码做了适当的整理。
//=============================PluginMain Start======================
DLLExport MACPASCAL void PluginMain (const short selector,PISelectionParams *selectionParamBlock, long *data,short *result) {  //显示About对话框  if (selector == selectionSelectorAbout)  {   DoAbout((AboutRecordPtr)selectionParamBlock);  }  else  {    static const FProc routineForSelector [] =   {    /* selectionSelectorAbout    DoAbout, */    /* selectionSelectorExecute */ DoExecute   };

   Ptr globalPtr = NULL;  // Pointer for global structure   GPtr globals = NULL;   // actual globals

   //包装selectionParamBlock到globals中,真正有用的还是globals->selectionParamBlock   globalPtr = AllocateGlobals ((uint32)result,    (uint32)selectionParamBlock,    selectionParamBlock->handleProcs,    sizeof(Globals),    data,    InitGlobals);

   if (globalPtr == NULL)   {        *result = memFullErr;return;   }

   globals = (GPtr)globalPtr;

   //调用 DoExecute 函数   if (selector > selectionSelectorAbout && selector <= selectionSelectorExecute)    (routineForSelector[selector-1])(globals);   else    gResult = selectionBadParameters;

   if ((Handle)*data != NULL)    PIUnlockHandle((Handle)*data);

   } // about selector special  

 } //=============================PluginMain End=================================
//=============================DoExecute Start================================= void DoExecute (GPtr globals) {  //一些变量声明,省略...  //...  //

  //从剪贴板中读取自己定义格式的选区信息,保存到全局变量中,我加的
//省略部分内容

gQueryForParameters = ReadScriptParams (globals);
  gStuff->treatment = 0;//KeyToEnum(EnumToKey(gCreate,typeMyCreate),typeMyPISel); //忽略原程序的UI参数处理
   
  //获取读取端口
  gResult = ReadFromWritePort(&selectionRead, selection->port);

//省略部分内容
  
  //分配内存
   gResult = AllocateBuffer (kBufferSize, &sBuffer);
  if (gResult != noErr) goto CleanUp;
  
  gResult = AllocateBuffer (kBufferSize, &dBuffer);
  if (gResult != noErr) goto CleanUp;
  
  gResult = AllocateBuffer (kBufferSize, &rBuffer);
  if (gResult != noErr) goto CleanUp;

sData = LockBuffer (sBuffer, false);
  dData = LockBuffer (dBuffer, false);
  rData = LockBuffer (rBuffer, false);
  
  //省略部分内容

//统计要处理的通道
  curChannel = composite;
  while (curChannel != NULL)
  {
   if (DoTarget ? curChannel->target : curChannel->shown)
    total += AccountChannel (curChannel, transparency, selection);
           
   curChannel = curChannel->next;
  }

//进行实际的处理工作
  while (curChannel != NULL)
  {
   if (DoTarget ? curChannel->target : curChannel->shown)
   {
    ApplyChannel (globals, curChannel, &sDesc,
          transparency, &mDesc,
          selection, selectionRead, &dDesc,
          &rDesc, &done, total);
    if (gResult != noErr) goto CleanUp;
   }
   curChannel = curChannel->next;
  }
  
  //善后工作...
 }
 //=============================DoExecute End=====================================
//=============================ApplyChannel Start==================================
 static void ApplyChannel (GPtr globals,ReadChannelDesc *source,PixelMemoryDesc *sDesc,ReadChannelDesc *mask,
        PixelMemoryDesc *mDesc,WriteChannelDesc *dest,ChannelReadPort destRead,PixelMemoryDesc *dDesc,
        PixelMemoryDesc *rDesc,int32 *done,int32 total)
 {
  //声明变量,参数检查,省略

//内层循环中,每次读取64×64的块处理
  //#define kBlockRows 64
  for (row = limit.top; row < limit.bottom; row += kBlockRows)
   for (col = limit.left; col < limit.right; col += kBlockCols)
   {
    //省略部分内容

gResult = ReadPixels (destRead, &scaling, &area, dDesc, &wrote);

//省略部分内容

gResult = ReadPixels (source->port, &scaling, &area, sDesc, &wrote);

s = (unsigned8 *) sDesc->data;  //这里是原图象数据
    d = (unsigned8 *) dDesc->data;  //这里保存处理结果

//逐个象素处理64×64的块
    for (row2 = 0; row2 < kBlockRows; ++row2)
    {
     int y = row + row2;
     for (col2 = 0; col2 < kBlockCols; ++col2)
     {
      int x = col + col2;

int nRc = 0;
      bool bFound = false;
      while(nRc < g_rcCount) //g_rcCount是一共要显示的区域数,通过剪贴板传递计算
      {
       if(PtInRect(&g_rcArr[nRc],x,y)) //g_rcArr存放所有要显示的区域
       {
        *d = 255; //这个象素处于选区内
        bFound = true;
        break;
       }
       ++nRc;
      }
    //  if(!bFound) *d = 0;

++s;
      ++d;
      ++r;
     }
    }

//处理完毕一小块,写回
    gResult = WritePixels (dest->port, &area, dDesc);

//省略部分内容
   }


 }
 //=============================ApplyChannel End======================================

欢迎转载,转载请注明出处并保持文章的完整性,谢谢。

PhotoShop 插件开发之选区(Selection)相关推荐

  1. Photoshop脚本 给选区添加描边效果

    源自:http://coolketang.com/tutorials/menu5lesson18.php 本节将演示如何使用脚本,给选区添加描边效果.首先创建一个空白的脚本文档,并保存在硬盘上某个位置 ...

  2. Photoshop如何使用选区之实例演示?

    文章目录 0.引言 1.利用快速选择工具抠图 2.制作网店产品优惠券 3.利用选区改变眼睛颜色 4.抠取复杂的花束 5.制作丁达尔光照效果 6.利用选区调整图像局部颜色 0.引言   因科研等多场景需 ...

  3. Photoshop CC 2019选区的基本操作(快捷键)

    1.全选与反选 ctrl+A 2.取消选择和重新选择 取消:ctrl + D 重新选择: ctrl + shift + D 3.运算选区 shift + 拖动鼠标 ,添加到选区 alt + 拖动鼠标, ...

  4. PhotoShop 之设置选区的羽化

    有两种方式: 1.通过选项栏中的羽化,这个只能在绘制图像之前设置. 2.选择--> 修改 -->  羽化

  5. [Photoshop] ps中选区新建为图层的快捷键

    把选取直接新建为新图层,快捷键是 ctrl +j

  6. 笔刷为何是php文件,Photoshop定义画笔选区为空的原因

    From Disk partition to PostgreSQL installation From Disk partition to PostgreSQLinstallation [root@c ...

  7. Photoshop CS2 视频教程-PS移动选区(转)

    Photoshop CS2 视频教程-PS移动选区(转)[@more@] Photoshop CS2是Photoshop最新版本,也是目前图像处理功能最强大的软件之一.在教程安排上注重由浅入深,循序渐 ...

  8. PHOTOSHOP初学者成长教材

    PHOTOSHOP初学者成长教材 2010年08月05日 [b]第一课:一个PS高手的话 这是一个PS高手的体会,希望能对大家有所启迪. 1.不要看到别人的作品第一句话就说:给个教程吧!你应该想这个是 ...

  9. Photoshop初学者必读十课

    Photoshop初学者必读十课 2010年06月15日 [b]一课:一个PS高手的话[/b] 这是一个PS高手的体会,希望能对大家有所启迪. 1.不要看到别人的作品第一句话就说:给个教程吧!你应该想 ...

最新文章

  1. 内网虚拟服务器怎么固定域名,如何让自己的局域网用(虚拟的)域名访问?
  2. CCF CSP201709-1打酱油
  3. kmeans聚类算法_聚类算法入门:k-means
  4. 什么是php数组,php 数组写法是什么
  5. 哈尔滨学院 计算机等级考试证书,2018年9月黑龙江哈尔滨学院计算机等级考试证书领取通知...
  6. python 中的理解x[:]
  7. [leveldb] 3.put/delete操作
  8. Vue:You may use special comments to disable some warnings.
  9. L2-004 这是二叉搜索树吗?(二叉树)
  10. 适配器模式之访问者模式
  11. iOS开发-Bug锦囊
  12. rtmp协议 java_rtmp协议详解 (一) handshake
  13. 简单聊聊离散数学是什么
  14. 【网络架构理论系列】简述分布式的定义、分类、技术发展历史进程
  15. 3GPP TS 29244-g30 中英文对照 | 7.3 Message Types
  16. SQLyog安装地址
  17. w7计算机文件夹打开怎么设置密码,w7文件夹怎么设密码_w7文件夹设置密码操作方法...
  18. 2018年大连海事大学校赛(ACM竞赛高校联盟训练赛 第11场)题解
  19. 改进YOLOv5系列:首发结合 RepLKNet 构建 最新 RepLKDeXt 结构|CVPR2022 超大卷积核, 越大越暴力,大到31x31, 涨点高效
  20. 文件上传 webshell 各类型 一句话木马 图片马 制作 教程

热门文章

  1. 学会导出XMind 7资源包很重要
  2. 约战手游服务器维护中,梦幻手游1月13日维护更新 帮派约战测试
  3. python基础(上篇)【原创真实个人记录】
  4. java面试必会题——==与equals的区别
  5. Mac浏览器打不开百度网页
  6. idea淇℃伅 乱码
  7. 七牛 js JDK使用 - 上传APP
  8. python 使用session_Python(Django之Session详解及使用)
  9. cad的三维多段线怎么批量转成多段线
  10. 世界上各种壮观震撼奇景。也许你这辈子都看不到了!(欢迎转载分享)