从PS插件源码入手:手把手教你读懂并修改那个‘秋色效果’的JSX脚本
从零解析PS脚本如何拆解并定制秋色效果滤镜的JSX代码当你第一次看到Photoshop脚本代码时那些密密麻麻的ActionDescriptor和cTID函数调用可能让你望而生畏。但别担心每个PS脚本高手都曾经历过这个阶段。本文将带你像侦探破案一样逐行解剖一个完整的秋色效果脚本不仅让你理解它的工作原理还能教会你如何修改它来创造属于自己的独特滤镜效果。无论你是设计师想扩展PS能力还是开发者想进入插件创作领域这篇指南都将成为你的实用手册。1. 脚本开发基础理解PS的JSX环境Photoshop的脚本系统基于ECMAScript(类似JavaScript)的扩展版本称为ExtendScript。与网页中的JavaScript不同PS脚本可以直接调用Photoshop的各种功能从简单的图层操作到复杂的图像处理都能实现。1.1 脚本文件基础结构每个PS脚本通常包含以下几个核心部分初始化函数脚本的入口点如示例中的StartAutumnColours.main工具函数如cTID和sTID这类辅助转换函数操作步骤按顺序执行的一系列PS命令示例中的step1到step5错误处理try/catch块用于捕获执行中的问题// 典型的PS脚本结构示例 function mainFunction() { // 步骤1 // 步骤2 // ... } mainFunction.main function() { mainFunction(); }; mainFunction.main();1.2 关键工具函数解析让我们先看看脚本开头的两个神秘函数cTID function(s) { return app.charIDToTypeID(s); }; sTID function(s) { return app.stringIDToTypeID(s); };这两个函数是PS脚本中的常客cTID将4字符ID转换为PS内部使用的类型IDsTID将字符串ID转换为PS内部类型ID提示PS内部使用4字符代码(如Lyr 代表图层)和字符串ID(如mergeLayersNew)来标识各种操作和属性。这些转换函数让代码更简洁。2. 逐行拆解秋色效果的实现逻辑现在让我们深入脚本的每个步骤看看它如何一步步创造出秋色效果。2.1 第一步设置基础图层属性function step1(enabled, withDialog) { if (enabled ! undefined !enabled) return; var dialogMode (withDialog ? DialogModes.ALL : DialogModes.NO); var desc1 new ActionDescriptor(); var ref1 new ActionReference(); ref1.putProperty(cTID(Lyr ), cTID(Bckg)); desc1.putReference(cTID(null), ref1); var desc2 new ActionDescriptor(); desc2.putString(cTID(Nm ), Base Layer); desc2.putUnitDouble(cTID(Opct), cTID(#Prc), 100); desc2.putEnumerated(cTID(Md ), cTID(BlnM), cTID(Nrml)); desc1.putObject(cTID(T ), cTID(Lyr ), desc2); executeAction(cTID(setd), desc1, dialogMode); };这段代码做了以下工作创建一个ActionDescriptor(动作描述符)来定义图层属性设置图层名称为Base Layer设置不透明度为100%设置混合模式为Normal执行setd(set descriptor)动作应用这些设置2.2 第二步复制基础图层function step2(enabled, withDialog) { if (enabled ! undefined !enabled) return; var dialogMode (withDialog ? DialogModes.ALL : DialogModes.NO); var desc1 new ActionDescriptor(); var ref1 new ActionReference(); ref1.putEnumerated(cTID(Lyr ), cTID(Ordn), cTID(Trgt)); desc1.putReference(cTID(null), ref1); desc1.putString(cTID(Nm ), Base Layer copy); desc1.putInteger(cTID(Vrsn), 5); executeAction(cTID(Dplc), desc1, dialogMode); };这一步创建了基础图层的副本关键点包括ref1.putEnumerated指定了当前目标图层Dplc动作代表duplicate(复制)新图层被命名为Base Layer copy2.3 第三步创建调整图层function step3(enabled, withDialog) { if (enabled ! undefined !enabled) return; var dialogMode (withDialog ? DialogModes.ALL : DialogModes.NO); var desc1 new ActionDescriptor(); var ref1 new ActionReference(); ref1.putClass(cTID(AdjL)); desc1.putReference(cTID(null), ref1); var desc2 new ActionDescriptor(); var desc3 new ActionDescriptor(); desc3.putEnumerated(sTID(presetKind), sTID(presetKindType), sTID(presetKindDefault)); desc3.putBoolean(cTID(Clrz), false); desc2.putObject(cTID(Type), cTID(HStr), desc3); desc1.putObject(cTID(Usng), cTID(AdjL), desc2); executeAction(cTID(Mk ), desc1, dialogMode); };这里创建了一个色相/饱和度调整图层AdjL代表Adjustment Layer(调整图层)HStr指Hue/Saturation(色相/饱和度)Mk是make的缩写表示创建操作2.4 第四步设置色相/饱和度参数这是实现秋色效果的核心步骤function step4(enabled, withDialog) { if (enabled ! undefined !enabled) return; var dialogMode (withDialog ? DialogModes.ALL : DialogModes.NO); var desc1 new ActionDescriptor(); var ref1 new ActionReference(); ref1.putEnumerated(cTID(AdjL), cTID(Ordn), cTID(Trgt)); desc1.putReference(cTID(null), ref1); var desc2 new ActionDescriptor(); desc2.putEnumerated(sTID(presetKind), sTID(presetKindType), sTID(presetKindCustom)); var list1 new ActionList(); var desc3 new ActionDescriptor(); desc3.putInteger(cTID(LclR), 3); desc3.putInteger(cTID(BgnR), 24); desc3.putInteger(cTID(BgnS), 105); desc3.putInteger(cTID(EndS), 135); desc3.putInteger(cTID(EndR), 195); desc3.putInteger(cTID(H ), -95); desc3.putInteger(cTID(Strt), 0); desc3.putInteger(cTID(Lght), 0); list1.putObject(cTID(Hst2), desc3); desc2.putList(cTID(Adjs), list1); desc1.putObject(cTID(T ), cTID(HStr), desc2); executeAction(cTID(setd), desc1, dialogMode); };关键参数解析参数代码含义示例值作用LclR本地化范围3控制色彩调整的范围BgnR开始范围24色相调整起始点BgnS开始饱和度105饱和度调整起始点EndS结束饱和度135饱和度调整结束点EndR结束范围195色相调整结束点H色相偏移-95产生暖色调的关键参数Strt开始亮度0亮度调整起始点Lght亮度0亮度调整量这些参数的组合产生了典型的秋色效果暖色调、高饱和度的黄色和橙色。2.5 第五步合并图层function step5(enabled, withDialog) { if (enabled ! undefined !enabled) return; var dialogMode (withDialog ? DialogModes.ALL : DialogModes.NO); var desc1 new ActionDescriptor(); executeAction(sTID(mergeLayersNew), desc1, dialogMode); };这一步简单地将所有图层合并完成效果应用。3. 自定义你的专属滤镜效果理解了脚本的工作原理后我们可以开始修改它来创建不同的色彩效果。3.1 创建青橙色调效果青橙色调是另一种流行的色彩风格我们可以通过修改step4中的参数来实现// 修改step4中的desc3部分 desc3.putInteger(cTID(LclR), 3); desc3.putInteger(cTID(BgnR), 180); // 调整为青色范围 desc3.putInteger(cTID(BgnS), 90); desc3.putInteger(cTID(EndS), 240); // 调整为橙色范围 desc3.putInteger(cTID(EndR), 30); desc3.putInteger(cTID(H ), 20); // 适度调整色相 desc3.putInteger(cTID(Strt), 0); desc3.putInteger(cTID(Lght), 5); // 稍微提亮关键调整点将BgnR(开始范围)设为180对应青色区域EndR(结束范围)设为30对应橙色区域调整H(色相)为正值增强冷暖对比轻微提高Lght(亮度)使效果更通透3.2 创建复古胶片效果复古效果通常需要降低某些颜色的饱和度并添加轻微色偏desc3.putInteger(cTID(LclR), 2); // 减小调整范围 desc3.putInteger(cTID(BgnR), 30); desc3.putInteger(cTID(BgnS), 80); // 降低饱和度起始值 desc3.putInteger(cTID(EndS), 210); desc3.putInteger(cTID(EndR), 60); desc3.putInteger(cTID(H ), -15); // 轻微暖色偏 desc3.putInteger(cTID(Strt), 0); desc3.putInteger(cTID(Lght), -5); // 稍微降低亮度3.3 参数调整参考表下表列出常见色彩风格的关键参数设置效果类型LclRBgnREndRHBgnSEndSLght秋色效果324195-951051350青橙色调31803020902405复古胶片23060-1580210-5冷色调3180240307012010粉彩效果4036003060154. 进阶技巧提升脚本的实用性和灵活性4.1 添加用户界面控制原始脚本的所有参数都是硬编码的我们可以扩展它添加简单的用户界面// 添加对话框函数 function showDialog() { var dlg new Window(dialog, 自定义色彩效果); // 添加控制元素 dlg.hueSlider dlg.add(slider, undefined, 0, -180, 180); dlg.hueSlider.text 色相偏移:; dlg.satSlider dlg.add(slider, undefined, 100, 0, 200); dlg.satSlider.text 饱和度:; // 确定和取消按钮 dlg.btnGroup dlg.add(group); dlg.btnGroup.add(button, undefined, 确定); dlg.btnGroup.add(button, undefined, 取消); if (dlg.show() 1) { return { hue: dlg.hueSlider.value, saturation: dlg.satSlider.value }; } return null; } // 修改主函数 StartAutumnColours.main function() { var settings showDialog(); if (settings) { // 使用用户设置的值 globalHueValue settings.hue; globalSatValue settings.saturation; StartAutumnColours(); } };4.2 添加错误处理和日志功能增强脚本的健壮性var log []; function logStep(stepName) { log.push(stepName - 执行时间: new Date().toLocaleTimeString()); } function handleError(e, stepName) { log.push(错误在 stepName : e.message); alert(执行 stepName 时出错: e.message); } // 修改try-catch块 try{ logStep(step1); step1(); } catch(e){ handleError(e, step1); };4.3 创建可重用的脚本框架将通用功能抽象出来方便创建新效果// 基础效果框架 function PS_Effect(name) { this.name name; this.steps []; this.log []; this.addStep function(stepFunc, stepName) { this.steps.push({ func: stepFunc, name: stepName || (步骤 (this.steps.length 1)) }); }; this.execute function() { this.log.push(开始执行效果: this.name); for (var i 0; i this.steps.length; i) { try { this.log.push(执行: this.steps[i].name); this.steps[i].func(); } catch(e) { this.log.push(错误: e.message); break; } } this.log.push(效果执行完成); }; } // 使用框架创建秋色效果 var autumnEffect new PS_Effect(秋色效果); autumnEffect.addStep(step1, 设置基础图层); autumnEffect.addStep(step2, 复制图层); autumnEffect.addStep(step3, 创建调整层); autumnEffect.addStep(step4, 设置色彩参数); autumnEffect.addStep(step5, 合并图层); autumnEffect.execute();