Skip to content

Instantly share code, notes, and snippets.

@thany thany/.eslintrc

Created Jan 3, 2019
Embed
What would you like to do?
Batch HDR for Photoshop
{
"env": {
"browser": true,
"amd": true,
"es6": true
},
"parserOptions": {
"sourceType": "module"
},
"extends": "eslint:recommended",
"root": true,
"globals": {
"module": true,
"$": true,
"jQuery": true,
"require": true,
"define": true,
"_": true
},
"rules": {
// enforce consistent spacing inside array brackets
"array-bracket-spacing": [2, "always"],
// enforce return statements in callbacks of array methods
"array-callback-return": [2],
// enforce the use of variables within the scope they are defined
"block-scoped-var": [2],
// enforce consistent spacing inside single-line blocks
"block-spacing": [2, "always"],
// enforce consistent brace style for blocks
"brace-style": [2, "stroustrup"],
// require or disallow trailing commas (comma-dangle)
"comma-dangle": [2, "never"],
// enforces spacing around commas (comma-spacing)
"comma-spacing": [2, { "before": false, "after": true }],
// enforce consistent comma style
"comma-style": [2, "last"],
// enforce consistent brace style for all control statements
"curly": [2],
// require the use of === and !==
"eqeqeq": [2, "allow-null"],
// enforce dot notation whenever possible
"dot-notation": [2],
// require or disallow spacing between function identifiers and their invocations
"func-call-spacing": [2, "never"],
// require for-in loops to include an if statement
"guard-for-in": [2],
// enforce consistent indentation
"indent": [2, 4, { "SwitchCase": 1 }],
// enforce consistent spacing between keys and values in object literal properties
"key-spacing": [2],
// enforce a maximum depth that blocks can be nested
"max-depth": [2, 5],
// enforce a maximum line length
"max-len": [2, { "code": 120, "ignorePattern": "^import\\W.*" }],
// disallow the use of console
"no-console": [2, { "allow": ["error"] }],
// disallow the use of debugger
"no-debugger": [2],
// disallow duplicate arguments in function definitions
"no-dupe-args": [2],
// disallow duplicate case labels
"no-duplicate-case": [2],
// disallow empty block statements
"no-empty": [2],
// disallow empty functions
"no-empty-function": [2],
// disallow the use of eval()
"no-eval": [2],
// disallow unnecessary calls to .bind()
"no-extra-bind": [2],
// disallow unnecessary labels
"no-extra-label": [2],
// disallow reassigning function declarations
"no-func-assign": [2],
// disallow the use of eval()-like methods
"no-implied-eval": [2],
// disallow variable or function declarations in nested blocks
"no-inner-declarations": [2],
// disallow the use of the __iterator__ property
"no-iterator": [2],
// disallow unnecessary nested blocks
"no-lone-blocks": [2],
// disallow function declarations and expressions inside loop statements
"no-loop-func": [2],
// disallow mixed spaces and tabs for indentation
"no-mixed-spaces-and-tabs": [2, "smart-tabs"],
// disallow multiple empty lines
"no-multiple-empty-lines": [2, { "max": 3 }],
// disallow nested ternary expressions
"no-nested-ternary": [2],
// disallow new operators with the Function object
"no-new-func": [2],
// disallow new operators with the String, Number, and Boolean objects
"no-new-wrappers": [2],
// disallow calling global object properties as functions
"no-obj-calls": [2],
// disallow variable redeclaration
"no-redeclare": [2],
// disallow assignments where both sides are exactly the same
"no-self-assign": [2],
// disallow comparisons where both sides are exactly the same
"no-self-compare": [2],
// disallow unmodified loop conditions
"no-unmodified-loop-condition": [2],
// disallow unreachable code after return, throw, continue, and break statements
"no-unreachable": [2],
// disallow unused expressions
"no-unused-expressions": [2],
// disallow unused variables
"no-unused-vars": [2, { "args": "none" }],
// disallow unnecessary calls to .call() and .apply()
"no-useless-call": [2],
// disallow unnecessary escape characters
"no-useless-escape": [0],
// disallow with statements
"no-with": [2],
// enforce placing object properties on separate lines
"object-property-newline": [
2,
{ "allowMultiplePropertiesPerLine": true }
],
// require or disallow padding within blocks
"padded-blocks": [2, "never"],
// require quotes around object literal property names
"quote-props": [2, "as-needed"],
// enforce the consistent use of either backticks, double, or single quotes
"quotes": [2, "single"],
// enforce the consistent use of the radix argument when using parseInt()
"radix": [2],
// require or disallow strict mode directives
"strict": [2, "global"]
}
}
//#target photoshop
/* global
app,
localize,
mergeToHDR,
kMergeToHDRDeghostBest,
activeDocument,
SaveOptions,
ActionDescriptor,
typeNULL,
kpreferXMPFromACRStr,
executeAction,
DialogModes,
charIDToTypeID,
JPEGSaveOptions,
FormatOptions,
MatteType,
Extension,
TiffSaveOptions,
TIFFEncoding,
stringIDToTypeID,
kmethodStr,
ActionList,
keyContinuity,
kclassContour,
classShapingCurve,
convertFromHDRNoDialog,
Folder,
kMergeToHDRDeghostOff,
ActionReference,
executeActionGet,
ResampleMethod
*/
/*********************************************************************
Batch HDR Script by David Milligan
*********************************************************************/
/*********************************************************************
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**********************************************************************/
/*
// BEGIN__HARVEST_EXCEPTION_ZSTRING
<javascriptresource>
<name>Batch HDR...</name>
<menu>automate</menu>
</javascriptresource>
// END__HARVEST_EXCEPTION_ZSTRING
*/
// These lines import the 'Merge To HDR.jsx' script that is built in to
// photoshop, we will make calls to that script and some of the scripts
// that it includes
this.runMergeToHDRFromScript = true;
const g_ScriptFolderPath = app.path + '/' + localize(
'$$$/ScriptingSupport/InstalledScripts=Presets/Scripts'
);
const g_ScriptPath = File(g_ScriptFolderPath + '/Merge To HDR.jsx');
$.evalFile(g_ScriptPath);
//default settings:
mergeToHDR.useAlignment = true;
mergeToHDR.useACRToning = false;
const settings = {
brackets: 5,
sourceFolder: '',
outputFolder: '',
saveType: 'TIFF ZIP',
saveBitDepth: 32,
jpegQuality: 10,
fileMask: '*'
};
const progress = {
indicator: undefined,
status: undefined,
window: undefined,
canceled: false,
eta: ''
};
const hdrToning = {
radius: 100,
strength: 0.5,
gamma: 1.0,
exposure: 0.0,
detail: 100,
shadow: 0,
highlights: 0,
vibrance: 20,
saturation: 30,
smooth: false,
deghosting: kMergeToHDRDeghostBest,
curve: '0,0,255,255'
};
const docs = {
preview: null,
original: null
};
function main() {
promptUser();
const initializeProgress = function() {
progress.window = new Window('palette { text:"Batch HDR Progress", \
statusText: StaticText { \
text: "Processing Images...", \
preferredSize: [350,20] \
}, \
progressGroup: Group { \
progress: Progressbar { \
minvalue: 0, \
maxvalue: 100, \
value: 0, \
preferredSize: [300,20] \
}, \
cancelButton: Button { text: "Cancel" } \
} \
}');
progress.status = progress.window.statusText;
progress.indicator = progress.window.progressGroup.progress;
progress.window.progressGroup.cancelButton.onClick = function() {
progress.canceled = true;
}
progress.window.show();
};
const bareFilename = function(filename) {
return filename
.replace(/\.[^.]$/, '') // Remove extension
.replace(/^[^\\\/][\\\/]/, ''); // Remove path
};
const zeroPad = function(number, numZeros) {
var result = '' + number;
while (result.length < numZeros) {
result = '0' + result;
}
return result;
};
// Make sure user didn't cancel
if (settings.sourceFolder != null
&& settings.outputFolder != null
&& settings.sourceFolder.exists
&& settings.outputFolder.exists
&& settings.brackets > 0) {
initializeProgress();
const files = settings.sourceFolder.getFiles(settings.fileMask || '*');
const currentFileList = [ ];
files.sort();
for (var index = 0; index < files.length; index++) {
if ((index % settings.brackets) === settings.brackets - 1) {
const start = new Date();
progress.indicator.value = 100 * index / files.length;
currentFileList.push(files[index]);
if (progress.canceled) {
break;
}
if (settings.brackets > 1) {
progress.status.text =
'Merging files ' +
(index - settings.brackets + 2) + ' - ' +
(index + 1) + ' of ' +
files.length + progress.eta;
// For braketed exposures use the mergeToHDR script to
// merge the files into a single 32 bit image
mergeToHDR.outputBitDepth = 32;
mergeToHDR.mergeFilesToHDR(
currentFileList,
mergeToHDR.useAlignment,
hdrToning.deghosting
);
progress.status.text =
'Toning files ' +
(index - settings.brackets + 2) + ' - ' +
(index + 1) + ' of ' +
files.length + progress.eta;
}
else {
progress.status.text =
'Loading file ' + (index + 1) + ' of ' +
files.length + progress.eta;
// Otherwise (non-bracketed exposure) just open the file
doOpenFile(files[index]);
progress.status.text =
'Toning file ' + (index + 1) + ' of ' +
files.length + progress.eta;
}
progress.indicator.value =
100 * (index + settings.brackets / 2) / files.length;
if (progress.canceled) {
break;
}
try {
if (app.activeDocument != null
&& settings.saveBitDepth < 32) {
// Apply the actual tone mapping to the HDR image to
// get it back down to 8 bits
doHDRToning();
}
}
catch (error) {
alert(error + '\nCheck number of files in source folder');
break;
}
// Save the result and close
if (settings.brackets > 1) {
progress.status.text =
'Saving result ' + (index - settings.brackets + 2) + ' - ' +
(index + 1) + ' of ' + files.length + progress.eta;
}
else {
progress.status.text =
'Saving result ' + (index + 1) + ' of ' +
files.length + progress.eta;
}
if (progress.canceled) {
break;
}
const
firstFilename = bareFilename(currentFileList[0]),
lastFilename = bareFilename(currentFileList[currentFileList.length - 1]),
filename =
settings.outputFolder.absoluteURI + '/' +
firstFilename + '-' +
lastFilename.substr(lastFilename.length - 4);
doSaveFile(filename);
activeDocument.close(SaveOptions.DONOTSAVECHANGES);
// Reset our file list
currentFileList.splice(0, currentFileList.length);
// Calculate time remaining
const minutes =
(new Date().getTime() - start.getTime()) /
60000 * ((files.length - index - 1) / settings.brackets);
progress.eta =
' | Remaining: ' +
zeroPad((minutes / 60).toFixed(0), 2) + ':' +
zeroPad((minutes % 60).toFixed(0), 2);
}
else {
currentFileList.push(files[index]);
}
}
progress.window.hide();
}
}
function doOpenFile(filename) {
const
eventOpen = app.charIDToTypeID('Opn '),
desc = new ActionDescriptor();
desc.putPath(typeNULL, new File(filename));
// Not sure what this does or if it is needed
desc.putBoolean(kpreferXMPFromACRStr, true);
executeAction(eventOpen, desc, DialogModes.NO);
// If we don't convert the image to 32bit the mergeToHDR script will not
// tone our image when we call it, it will simply downconvert it to 8 bit.
convertTo32Bit();
}
function convertTo32Bit() {
const idCnvM = charIDToTypeID('CnvM');
const desc6 = new ActionDescriptor();
desc6.putInteger(charIDToTypeID('Dpth'), 32);
desc6.putBoolean(charIDToTypeID('Mrge'), false);
desc6.putBoolean(charIDToTypeID('Rstr'), false);
executeAction(idCnvM, desc6, DialogModes.NO);
}
function doSaveFile(filename) {
const saveAsJPEG = function() {
const options = new JPEGSaveOptions();
options.embedColorProfile = true;
options.formatOptions = FormatOptions.STANDARDBASELINE;
options.matte = MatteType.NONE;
options.quality = settings.jpegQuality;
activeDocument.saveAs(
new File(filename),
options,
true, // Save As Copy
Extension.LOWERCASE // Append Extention
);
};
const saveAsTIFF = function() {
const options = new TiffSaveOptions();
options.embedColorProfile = true;
activeDocument.saveAs(
new File(filename),
options,
true, // Save As Copy
Extension.LOWERCASE // Append Extention
);
};
const saveAsTIFFLZW = function() {
const options = new TiffSaveOptions();
options.embedColorProfile = true;
options.imageCompression = TIFFEncoding.TIFFLZW;
activeDocument.saveAs(
new File(filename),
options,
true, // Save As Copy
Extension.LOWERCASE // Append Extention
);
};
const saveAsTIFFZIP = function() {
const tifSaveOptions = new TiffSaveOptions();
tifSaveOptions.embedColorProfile = true;
tifSaveOptions.imageCompression = TIFFEncoding.TIFFZIP;
activeDocument.saveAs(
new File(filename),
tifSaveOptions,
true, /*Save As Copy*/
Extension.LOWERCASE /*Append Extention*/
);
};
const saveAsRadiance = function() {
const descriptor = new ActionDescriptor();
descriptor.putString(charIDToTypeID('As '), '"Radiance"');
descriptor.putPath(charIDToTypeID('In '), new File(filename + '.hdr'));
descriptor.putInteger(charIDToTypeID('DocI'), 58);
descriptor.putBoolean(charIDToTypeID('LwCs'), true);
descriptor.putEnumerated(
stringIDToTypeID('saveStage'),
stringIDToTypeID('saveStageType'),
stringIDToTypeID('saveSucceeded')
);
executeAction(charIDToTypeID('save'), descriptor, DialogModes.NO);
};
const saveAsOpenEXR = function() {
const descriptor = new ActionDescriptor();
descriptor.putString(charIDToTypeID('As '), '"OpenEXR"');
descriptor.putPath(charIDToTypeID('In '), new File(filename + '.exr'));
descriptor.putInteger(charIDToTypeID('DocI'), 58);
descriptor.putBoolean(charIDToTypeID('LwCs'), true);
descriptor.putEnumerated(
stringIDToTypeID('saveStage'),
stringIDToTypeID('saveStageType'),
stringIDToTypeID('saveSucceeded')
);
executeAction(charIDToTypeID('save'), descriptor, DialogModes.NO);
};
switch (settings.saveType) {
case 'JPEG': saveAsJPEG(); break;
case 'TIFF': saveAsTIFF(); break;
case 'TIFF LZW': saveAsTIFFLZW(); break;
case 'TIFF ZIP': saveAsTIFFZIP(); break;
case 'Radiance': saveAsRadiance(); break;
case 'OpenEXR': saveAsOpenEXR(); break;
default:
activeDocument.saveAs(
new File(filename),
undefined,
true, // Save As Copy
Extension.LOWERCASE // Append Extention
);
break;
}
}
function doHDRToning() {
// Create the ActionDescriptor that describes the HDR toning settings to use
const hdDesc = new ActionDescriptor();
// I'm not sure what this does
hdDesc.putInteger(stringIDToTypeID('version'), 6);
// The toning method to use, 3 = local adaptation
hdDesc.putInteger(kmethodStr, 3);
hdDesc.putDouble(stringIDToTypeID('radius'), hdrToning.radius);
hdDesc.putDouble(stringIDToTypeID('threshold'), hdrToning.strength);
hdDesc.putDouble(stringIDToTypeID('center'), hdrToning.gamma);
hdDesc.putDouble(stringIDToTypeID('brightness'), hdrToning.exposure);
hdDesc.putDouble(stringIDToTypeID('detail'), hdrToning.detail);
hdDesc.putDouble(stringIDToTypeID('shallow'), hdrToning.shadow);
hdDesc.putDouble(stringIDToTypeID('highlights'), hdrToning.highlights);
hdDesc.putDouble(stringIDToTypeID('vibrance'), hdrToning.vibrance);
hdDesc.putDouble(stringIDToTypeID('saturation'), hdrToning.saturation);
hdDesc.putBoolean(stringIDToTypeID('smooth'), hdrToning.smooth);
hdDesc.putBoolean(stringIDToTypeID('deghosting'), hdrToning.deghosting);
// Create the tone curve
const cDesc = new ActionDescriptor();
cDesc.putString(stringIDToTypeID('name'), 'Default');
const cList = new ActionList();
const points = hdrToning.curve.split(',');
for (var i = 0; i < points.length; i++) {
if (i % 2 === 1) {
var pDesc = new ActionDescriptor();
pDesc.putDouble(stringIDToTypeID('horizontal'), points[i - 1]);
pDesc.putDouble(stringIDToTypeID('vertical'), points[i]);
pDesc.putBoolean(keyContinuity, false); // ?????
cList.putObject(charIDToTypeID('Pnt '), pDesc);
}
}
cDesc.putList(stringIDToTypeID('curve'), cList);
hdDesc.putObject(kclassContour, classShapingCurve, cDesc);
// Call the script that actually invokes the toning plugin
convertFromHDRNoDialog(settings.saveBitDepth, hdDesc);
}
function promptUser() {
const setupWindow = new Window('dialog { \
orientation: "row", \
text: "Batch HDR", \
alignChildren: "top", \
leftGroup: Group { orientation: "column", alignChildren:"fill", \
inputPanel: Panel { text: "Input", \
sourceGroup: Group { \
sourceBox: EditText { characters: 40, text: "" }, \
sourceBrowse: Button { text: "Browse" } \
}, \
bracketGroup: Group{ \
bracketLabel: StaticText { text: "Number of Brackets: " }, \
bracketBox: EditText { characters: 2 }, \
filterLabel: StaticText { text: "File Filter: " }, \
filterText: EditText { characters: 5 }, \
alignCheckBox: Checkbox { text: "Align" }\
deghostLabel: StaticText { text: "Deghost: " }\
deghostDropDown: DropDownList { }, \
} \
}, \
toningPanel: Panel { \
text: "Toning", \
orientation: "row", \
alignChildren:"top" \
} ,\
outputPanel: Panel { text: "Output", \
outputGroup: Group { \
outputBox: EditText { characters: 40, text: "" }, \
outputBrowse: Button { text: "Browse" } \
}, \
saveSettingsGroup: Group { \
saveTypeLabel: StaticText { text: "Save As: " }, \
saveDropDown: DropDownList { }, \
jpegQualityLabel: StaticText { \
text: "JPEG Quality (1-10): " \
}, \
jpegQualityText: EditText { characters: 2}, \
outputBitDepthLabel: StaticText { \
text: "Bit Depth", \
enabled:false \
}, \
outputBitDepthDropDown: DropDownList { enabled:false }, \
} \
} \
}, \
rightGroup: Group { orientation: "column", alignChildren:"fill", \
okButton: Button { text: "OK", enabled: false } \
cancelButton: Button { text: "Cancel" } \
} \
} ');
generateToningPanel(setupWindow.leftGroup.toningPanel);
// Shortcut variables
const sourceBox = setupWindow.leftGroup.inputPanel.sourceGroup.sourceBox;
const sourceBrowse = setupWindow.leftGroup.inputPanel.sourceGroup.sourceBrowse;
const bracketBox = setupWindow.leftGroup.inputPanel.bracketGroup.bracketBox;
const filterText = setupWindow.leftGroup.inputPanel.bracketGroup.filterText;
const alignCheckBox = setupWindow.leftGroup.inputPanel.bracketGroup.alignCheckBox;
const outputBox = setupWindow.leftGroup.outputPanel.outputGroup.outputBox;
const outputBrowse = setupWindow.leftGroup.outputPanel.outputGroup.outputBrowse;
const saveDropDown = setupWindow.leftGroup.outputPanel.saveSettingsGroup.saveDropDown;
const jpegQualityText = setupWindow.leftGroup.outputPanel.saveSettingsGroup.jpegQualityText;
const jpegQualityLabel = setupWindow.leftGroup.outputPanel.saveSettingsGroup.jpegQualityLabel;
const outputBitDepthDropDown = setupWindow.leftGroup.outputPanel.saveSettingsGroup.outputBitDepthDropDown;
const outputBitDepthLabel = setupWindow.leftGroup.outputPanel.saveSettingsGroup.outputBitDepthLabel;
const okButton = setupWindow.rightGroup.okButton;
const cancelButton = setupWindow.rightGroup.cancelButton;
const toningPanel = setupWindow.leftGroup.toningPanel;
const deghostDropDown = setupWindow.leftGroup.inputPanel.bracketGroup.deghostDropDown;
// Set default values
bracketBox.text = settings.brackets;
filterText.text = settings.fileMask;
alignCheckBox.value = mergeToHDR.useAlignment;
jpegQualityText.text = settings.jpegQuality;
const populateDropdown = function(dropdown, options, selection) {
for (var i = 0; i < options.length; i++) {
dropdown.add('item', options[i]);
if (options[i] === selection) {
dropdown.selection = i;
}
}
};
populateDropdown(
saveDropDown,
[
'JPEG', 'TIFF', 'TIFF LZW', 'TIFF ZIP',
'Radiance', 'OpenEXR', 'PSD'
],
settings.saveType
);
populateDropdown(
outputBitDepthDropDown,
[ 8, 16, 32 ],
settings.saveBitDepth
);
// Event handlers
sourceBox.onChange = function() {
settings.sourceFolder = new Folder(sourceBox.text);
okButton.enabled =
settings.sourceFolder != null &&
settings.outputFolder != null &&
settings.sourceFolder.exists &&
settings.outputFolder.exists;
};
sourceBrowse.onClick = function() {
settings.sourceFolder = Folder.selectDialog('Select the source folder');
if (settings.sourceFolder != null) {
sourceBox.text = settings.sourceFolder.fullName;
if (settings.outputFolder == null) {
settings.outputFolder = settings.sourceFolder;
outputBox.text = settings.outputFolder.fullName;
}
}
okButton.enabled =
settings.sourceFolder != null &&
settings.outputFolder != null &&
settings.sourceFolder.exists &&
settings.outputFolder.exists;
};
bracketBox.onChange = function() {
settings.brackets = +bracketBox.text;
deghostDropDown.removeAll()
deghostDropDown.add('item', 'Best');
deghostDropDown.add('item', 'Off');
for (var i = 0; i < settings.brackets; i++) {
deghostDropDown.add('item', i);
}
deghostDropDown.selection = 0;
};
filterText.onChange = function() {
settings.fileMask = filterText.text;
};
alignCheckBox.onClick = function() {
mergeToHDR.useAlignment = alignCheckBox.value;
};
deghostDropDown.onChange = function() {
if (this.selection.text === 'Best') {
hdrToning.deghosting = kMergeToHDRDeghostBest;
}
else if (this.selection.text === 'Off') {
hdrToning.deghosting = kMergeToHDRDeghostOff;
}
else {
hdrToning.deghosting = Number(this.selection.text);
}
};
outputBox.onChange = function() {
settings.outputFolder = new Folder(outputBox.text);
okButton.enabled =
settings.sourceFolder != null &&
settings.outputFolder != null &&
settings.sourceFolder.exists &&
settings.outputFolder.exists;
};
outputBrowse.onClick = function() {
settings.outputFolder = Folder.selectDialog('Select the output folder');
if (settings.outputFolder != null) {
outputBox.text = settings.outputFolder.fullName;
}
okButton.enabled =
settings.sourceFolder != null &&
settings.outputFolder != null &&
settings.sourceFolder.exists &&
settings.outputFolder.exists;
};
saveDropDown.onChange = function() {
settings.saveType = saveDropDown.selection.text;
jpegQualityText.enabled = saveDropDown.selection.text === 'JPEG';
jpegQualityLabel.enabled = saveDropDown.selection.text === 'JPEG';
if (saveDropDown.selection.text === 'JPEG') {
outputBitDepthDropDown.selection = 0;
}
else if (saveDropDown.selection.text === 'OpenEXR'
|| saveDropDown.selection.text === 'Radiance') {
outputBitDepthDropDown.selection = 2;
}
outputBitDepthDropDown.enabled =
saveDropDown.selection.text !== 'JPEG' &&
saveDropDown.selection.text !== 'Radiance' &&
saveDropDown.selection.text !== 'OpenEXR';
outputBitDepthLabel.enabled = outputBitDepthDropDown.enabled;
};
jpegQualityText.onChange = function() {
settings.jpegQuality = jpegQualityText.text;
};
outputBitDepthDropDown.onChange = function() {
settings.saveBitDepth = outputBitDepthDropDown.selection.text;
toningPanel.enabled = settings.saveBitDepth !== 32;
}
okButton.onClick = function() {
setupWindow.hide();
cleanUpPreviews();
};
cancelButton.onClick = function() {
settings.sourceFolder = null;
setupWindow.hide();
cleanUpPreviews();
};
saveDropDown.onChange();
outputBitDepthDropDown.onChange();
bracketBox.onChange();
setupWindow.show();
}
function cleanUpPreviews() {
if (docs.original != null) {
docs.original.close(SaveOptions.DONOTSAVECHANGES);
docs.original = null;
}
if (docs.preview != null) {
docs.preview.close(SaveOptions.DONOTSAVECHANGES);
docs.preview = null;
}
}
function generateToningPanel(toningPanel) {
// Left & right panels
const
leftToningGroup = toningPanel.add('group { orientation: "column", alignChildren: "fill" }'),
rightToningGroup = toningPanel.add('group { orientation: "column", alignChildren: "fill" }');
// Left panel
const
presetGroup = leftToningGroup.add('group{orientation:"row"}'),
presetDropDown = presetGroup.add('dropdownlist'),
loadPresetButton = presetGroup.add('button', undefined, 'Load Preset'),
edgePanel = leftToningGroup.add('panel', undefined, 'Edge Glow'),
radiusSlider = createSliderControl(
edgePanel.add('group'), ' Radius: ', 'px', 0, 500, 0,
hdrToning.radius,
function(newValue) {
hdrToning.radius = newValue;
}
),
strengthSlider = createSliderControl(
edgePanel.add('group'), 'Strength: ', '', 0, 4.0, 2,
hdrToning.strength,
function(newValue) {
hdrToning.strength = newValue;
}
),
edgeGroup = edgePanel.add('group'),
smoothEdgesBox = edgeGroup.add('checkbox', undefined, 'Smooth Edges'),
detailPanel = leftToningGroup.add('panel', undefined, 'Tone and Detail'),
gammaSlider = createSliderControl(
detailPanel.add('group'), ' Gamma: ', '', 0.1, 2.0, 2,
hdrToning.gamma,
function(newValue) {
hdrToning.gamma = newValue;
}
),
exposureSlider = createSliderControl(
detailPanel.add('group'), 'Exposure: ', '', -5.0, 5.0, 2,
hdrToning.exposure,
function(newValue) {
hdrToning.exposure = newValue;
}
),
detailSlider = createSliderControl(
detailPanel.add('group'), ' Detail: ', '%', -300, 300, 0,
hdrToning.detail,
function(newValue) {
hdrToning.detail = newValue;
}
),
advancedPanel = leftToningGroup.add('panel', undefined, 'Advanced'),
shadowSlider = createSliderControl(
advancedPanel.add('group'), ' Shadow: ', '%', -100, 100, 0,
hdrToning.shadow,
function(newValue) {
hdrToning.shadow = newValue;
}
),
highlightSlider = createSliderControl(
advancedPanel.add('group'), ' Highlight: ', '%', -100, 100, 0,
hdrToning.highlights,
function(newValue) {
hdrToning.highlights = newValue;
}
),
vibranceSlider = createSliderControl(
advancedPanel.add('group'), ' Vibrance: ', '%', -100, 100, 0,
hdrToning.vibrance,
function(newValue) {
hdrToning.vibrance = newValue;
}
),
saturationSlider = createSliderControl(
advancedPanel.add('group'), 'Saturation: ', '%', -100, 100, 0,
hdrToning.saturation,
function(newValue) {
hdrToning.saturation = newValue;
}
),
toningCurvePanel = leftToningGroup.add('panel { text: "Toning Curve", alignChildren: "fill" }'),
curveBox = toningCurvePanel.add('edittext', undefined, hdrToning.curve);
// Right panel
const
previewGroup = rightToningGroup.add('panel', undefined, 'Preview'),
selectPreviewButton = previewGroup.add('button', undefined, 'Select File(s)...'),
previewButton = previewGroup.add('button', undefined, 'Update Preview'),
zoomGroup = previewGroup.add('group');
zoomGroup.add('statictext', undefined, 'zoom');
const
zoomBox = zoomGroup.add('edittext { text: "100", characters: 3, enabled: false }'),
previewZoomSlider = previewGroup.add('slider { minvalue: 10, maxvalue: 200, value: 100, enabled: false }');
// Default values
smoothEdgesBox.value = hdrToning.smooth;
previewButton.enabled = app.documents.length > 0;
const presetFiles = getPresetFiles();
const updateSliders = function() {
radiusSlider(hdrToning.radius);
strengthSlider(hdrToning.strength);
smoothEdgesBox.value = hdrToning.smooth;
exposureSlider(hdrToning.exposure);
gammaSlider(hdrToning.gamma);
detailSlider(hdrToning.detail);
shadowSlider(hdrToning.shadow);
highlightSlider(hdrToning.highlights);
vibranceSlider(hdrToning.vibrance);
saturationSlider(hdrToning.saturation);
curveBox.text = hdrToning.curve;
};
if (presetFiles.length > 0) {
for (var i = 0; i < presetFiles.length; i++) {
presetDropDown.add('item', presetFiles[i].displayName.replace('.hdt', ''));
}
presetDropDown.selection = 0;
loadPreset(presetFiles[0]);
presetDropDown.onChange = function() {
loadPreset(presetFiles[presetDropDown.selection.index]);
updateSliders();
};
}
// Event handlers
loadPresetButton.onClick = function() {
loadPreset(null);
updateSliders();
};
smoothEdgesBox.onClick = function() {
hdrToning.smooth = smoothEdgesBox.value;
};
curveBox.onChange = function() {
hdrToning.curve = curveBox.text;
};
selectPreviewButton.onClick = function() {
const selectedFiles = File.openDialog('Select file(s) to load for preview', '*.*', true);
if (selectedFiles != null) {
cleanUpPreviews();
if (selectedFiles instanceof Array) {
if (selectedFiles.length > 1) {
mergeToHDR.outputBitDepth = 32;
mergeToHDR.mergeFilesToHDR(selectedFiles, false, -2);
}
else {
doOpenFile(selectedFiles[0].fullName);
}
}
else {
doOpenFile(selectedFiles.fullName);
}
docs.original = app.activeDocument;
previewButton.enabled = docs.original != null;
zoomBox.text = getZoomLevel();
previewZoomSlider.value = getZoomLevel();
}
};
previewButton.onClick = function() {
if (docs.original != null) {
const tempOutputBitDepth = settings.saveBitDepth;
settings.saveBitDepth = 16;
if (docs.preview != null) {
docs.preview.close(SaveOptions.DONOTSAVECHANGES);
}
docs.preview = docs.original.duplicate('HDR Preview');
app.activeDocument = docs.preview;
setZoomLevel(previewZoomSlider.value);
convertTo32Bit();
doHDRToning();
settings.saveBitDepth = tempOutputBitDepth;
waitForRedraw();
zoomBox.enabled = docs.preview != null;
previewZoomSlider.enabled = docs.preview != null;
}
};
zoomBox.onChange = function() {
if (docs.preview != null) {
previewZoomSlider.value = zoomBox.text;
setZoomLevel(previewZoomSlider.value);
}
}
previewZoomSlider.onChange = function() {
if (docs.preview != null) {
zoomBox.text = previewZoomSlider.value.toFixed(0);
setZoomLevel(previewZoomSlider.value);
}
};
updateSliders();
}
function createSliderControl(group, label, postLabel, min, max, round, value, onValueChanged) {
var ignoreChange = false;
group.add('statictext', undefined, label);
const
slider = group.add('slider', undefined, value, min, max),
box = group.add('edittext', undefined, value);
slider.alignment = 'fill';
box.characters = 6;
group.add('statictext', undefined, postLabel);
slider.onChange = function() {
if (!ignoreChange) {
ignoreChange = true;
box.text = slider.value.toFixed(round);
onValueChanged(slider.value);
ignoreChange = false;
}
};
box.onChange = function() {
if (!ignoreChange) {
ignoreChange = true;
slider.value = box.text;
onValueChanged(box.text);
ignoreChange = false;
}
};
return function(newValue) {
slider.value = newValue;
box.text = newValue.toFixed(round);
};
}
// Forces a redraw while a script dialog is active (for preview)
const waitForRedraw = function() {
const desc = new ActionDescriptor();
desc.putEnumerated(charIDToTypeID('Stte'), charIDToTypeID('Stte'), charIDToTypeID('RdCm'));
executeAction(charIDToTypeID('Wait'), desc, DialogModes.NO);
}
function getZoomLevel() {
const ref = new ActionReference();
ref.putEnumerated(charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
const desc = executeActionGet(ref);
return Number(desc.getDouble(stringIDToTypeID('zoom')) * 100).toFixed(1);
}
function setZoomLevel(zoom) {
if (zoom < 1) {
zoom = 1;
}
const ref1 = new ActionReference();
ref1.putEnumerated(charIDToTypeID('capp'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
const screenResolution = executeActionGet(ref1)
.getObjectValue(stringIDToTypeID('unitsPrefs'))
.getUnitDoubleValue(stringIDToTypeID('newDocPresetScreenResolution')) / 72;
activeDocument.resizeImage(undefined, undefined, screenResolution / (zoom / 100), ResampleMethod.NONE, 0);
const
ref2 = new ActionReference(),
desc = new ActionDescriptor();
ref2.putEnumerated(charIDToTypeID('Mn '), charIDToTypeID('MnIt'), charIDToTypeID('PrnS'));
desc.putReference(charIDToTypeID('null'), ref2);
executeAction(charIDToTypeID('slct'), desc, DialogModes.NO);
activeDocument.resizeImage(undefined, undefined, activeDocument.resolution, ResampleMethod.NONE, 0);
}
function getPresetFiles() {
const presetFolder = new Folder(app.path + '/Presets/HDR Toning');
return presetFolder.getFiles('*.hdt');
}
function loadPreset(presetFile) {
const getUInt16 = function(byteArray, offset) {
return byteArray[offset] * 0x100 + byteArray[offset + 1];
};
const getUInt32 = function(byteArray, offset) {
return byteArray[offset] * 0x1000000 + byteArray[offset + 1]
* 0x10000 + byteArray[offset + 2] * 0x100 + byteArray[offset + 3];
};
const getFloat32 = function(byteArray, offset) {
var bytes = getUInt32(byteArray, offset);
var sign = (bytes & 0x80000000) ? -1 : 1;
var exponent = ((bytes >> 23) & 0xFF) - 127;
var significand = (bytes & ~(-1 << 23));
if (exponent === 128) {
return sign * ((significand) ? Number.NaN : Number.POSITIVE_INFINITY);
}
if (exponent === -127) {
if (significand === 0) {
return sign * 0.0;
}
exponent = -126;
significand /= (1 << 22);
}
else {
significand = (significand | (1 << 23)) / (1 << 23);
}
return sign * significand * Math.pow(2, exponent);
};
if (presetFile == null) {
presetFile = File.openDialog('Select Preset', '*.hdt');
}
if (presetFile != null) {
var tmpStr = '';
const binaryData = [ ];
presetFile.encoding = 'BINARY';
presetFile.open('r');
while (!presetFile.eof) {
var ch = presetFile.readch();
tmpStr += ch.charCodeAt(0) === 0 ? ' ' : ch;
binaryData.push(ch.charCodeAt(0));
}
presetFile.close();
if (binaryData.length >= 40) {
// Init start position for reading datas
// Start position for english version (string 'D e f a u l t' is in the preset file)
var startPos = 38;
if (tmpStr.search('P a r d é f a u t') > -1) {
// start position for french preset file version
// (string 'P a r d é f a u t' is in the preset file) (==> + 6 bytes)
startPos = 44;
}
// If your preset file can't be read, try this : open it in notepad
// to see the string 'D e f a u l t' in your language and add the code
// here to set startPos to 38 + diff between the length of
// ('D e f a u l t') and length of ('D e f a u l t' in your language).
// Alternatively, have some dignity and run the English version of Photoshop.
const curvePointCount = getUInt16(binaryData, startPos);
if (binaryData.length >= 104 + curvePointCount * 4) {
var curvePointStr = '';
for (var i = 0; i < curvePointCount; i++) {
curvePointStr +=
getUInt16(binaryData, startPos + 4 + i * 4) + ',' +
getUInt16(binaryData, startPos + 2 + i * 4) +
((i < curvePointCount - 1) ? ',' : '');
}
hdrToning.curve = curvePointStr;
hdrToning.strength = getFloat32(binaryData, 8);
hdrToning.radius = getFloat32(binaryData, startPos + 10 + 5 * curvePointCount);
hdrToning.exposure = getFloat32(binaryData, startPos + 34 + 5 * curvePointCount);
hdrToning.saturation = getFloat32(binaryData, startPos + 38 + 5 * curvePointCount);
hdrToning.detail = getFloat32(binaryData, startPos + 42 + 5 * curvePointCount);
hdrToning.shadow = getFloat32(binaryData, startPos + 46 + 5 * curvePointCount);
hdrToning.highlights = getFloat32(binaryData, startPos + 50 + 5 * curvePointCount);
hdrToning.gamma = getFloat32(binaryData, startPos + 54 + 5 * curvePointCount);
hdrToning.vibrance = getFloat32(binaryData, startPos + 58 + 5 * curvePointCount);
hdrToning.smooth = getUInt16(binaryData, startPos + 62 + 5 * curvePointCount) !== 0;
}
else {
alert('Error Loading File', 'Error', true);
}
}
else {
alert('Error Loading File', 'Error', true);
}
}
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.