/** * @module PhotoshopScripts */ /** * Top-level class providing core functionality and to set up the application state for processing images to video. * @class ImageProcessor * @constructor * @uses Util * @param {Object} options settable instance properties * @param {int} options.cols number of frames across image * @param {String} options.destDocName destination document base filename without the path, extension, for frame number * @param {String} options.destFolder absolute system path to the output folder * @param {int} options.rows number of frames down image * @param {Object} [options.destDocFill] (default DocumentFill.TRANSPARENT) * @param {UnitValue} [options.destDocHeight] destination document height (default 480px) * @param {Object} [options.destDocMode] (default NewDocumentMode.RGB) * @param {int} [options.destDocRez] resolution in pixels per inch (default 72) * @param {UnitValue} [options.destDocWidth] destination document width (default 720px) * @param {int} [options.frameNumberLength] how many frame-number digits to use, with leading zeros, e.g. frame_0001 (default 4) * @param {int} [options.frameNumberNdx] the frame number (default 0) * @param {Number} [options.jpgQuality=8] 1 - 12 (default 8) * @return {void} */ function ImageProcessor(options) { var opts = options || {}; var jpgQuality = opts.jpgQuality || 8; this.cols = opts.cols || 7; this.destDocFill = opts.destDocFill || DocumentFill.TRANSPARENT; this.destDocName = opts.destDocName || ""; this.destDocHeight = opts.destDocHeight || UnitValue(534, "px"); this.destDocMode = opts.destDocMode || NewDocumentMode.RGB; this.destDocRez = opts.destDocRez || 72; this.destDocWidth = opts.destDocWidth || UnitValue(720, "px"); this.destFolder = opts.destFolder || ""; this.frameNumberLength = opts.frameNumberLength || 4; this.frameNumberNdx = opts.frameNumberNdx || 0; this.rows = opts.rows || 33; /** * Top left and bottom right coordinates of the selection: [ x, y, x', y' ]. * @property coords * @type {Array} */ this.coords = []; /** * Amount to move the selection horizontally for each new frame. * @property deltaX * @type {Number} */ this.deltaX = 0; /** * Amount to move the selection horizontally for each new frame. * @property deltaY * @type {Number} */ this.deltaY = 0; /** * Destination (target) image document in which to paste frames. * @property destDoc * @type {Document} */ this.destDoc = null; /** * JPEG file save-as options (including JPEG quality). * @property jpgOpts * @type {JPEGSaveOptions} */ this.jpgOpts = new JPEGSaveOptions(); this.jpgOpts.quality = jpgQuality; /** * Dimensions of the selection: width and height. * @property selDim * @type {Object} */ this.selDim = null; /** * Source image document. * @property srcDoc * @type {Document} */ this.srcDoc = null; // skip processing if there are no options (e.g. when subclassed) if (!options) { return; } if (confirm("Purge all caches (improves performance)?")) { purge(PurgeTarget.ALLCACHES); } try { this.srcDoc = app.activeDocument; } catch (e) { alert("Please open a file in Photoshop ..."); return; } try { this.coords = this.srcDoc.selection.bounds; } catch (e) { alert("Select a region to be copied"); return; } this.selDim = Util.getDims(this.coords); try { this.destDoc = app.documents[1]; } catch (e) { try { this.destDoc = documents.add( this.destDocWidth || this.selDim.width, this.destDocHeight || this.selDim.height, this.destDocRez, this.destDocName, this.destDocMode, this.destDocFill ); activeDocument = this.srcDoc; } catch (e) { alert("Error: trying to add document to ImageProcessor::destDoc. See console."); $.writeln(e); return; } } this.deltaX = this.srcDoc.width / this.cols; this.deltaY = this.srcDoc.height / this.rows; $.writeln("ImageProcessor instantiated."); } /** * Builds and displays a progress bar. * @method makeProgressBar * @static * @param {Number} maxValue the max number towards which the bar is progessing * @return {Window} of type palette */ ImageProcessor.makeProgressBar = function (maxValue) { var palette = new Window("palette", "Progress ..."); palette.pbar = palette.add("progressbar", undefined, 0, numFrames); palette.pbar.preferredSize = [300, 15]; palette.show(); return palette; }; /** * Restores the starting state of Photoshop; must be preceded by a call to ImageProcessor.saveState(). * @method restoreState * @static * @return {void} */ ImageProcessor.restoreState = function () { try { app.preferences.numberOfHistoryStates = pop(); app.preferences.rulerUnits = pop(); } catch (e) { alert("Error: try ImageProcessor::restoreState. See console."); $.writeln(e); } }; /** * Saves the starting state of Photoshop. * @method saveState * @static * @return {void} */ ImageProcessor.saveState = function () { push(app.preferences.rulerUnits); push(app.preferences.numberOfHistoryStates); app.preferences.rulerUnits = Units.PIXELS; app.preferences.numberOfHistoryStates = 1; }; /** * Flips the destination document’s canvas vertically. * @method fxFlipVertical * @return {void} */ ImageProcessor.prototype.fxFlipVertical = function () { this.destDoc.flipCanvas(Direction.VERTICAL); }; /** * Saves the current selection as a jpeg in the output folder; increments frameNumberNdx. * @method saveFrame * @param {Function} [fxFunc] function to apply effects; expects the destination document as its argument * @return {void} */ ImageProcessor.prototype.saveFrame = function (fxFunc) { var outputFolder = new Folder(this.destFolder); var filepath = this.destFolder + this.destDocName + Util.prefixZeros(this.frameNumberNdx++, this.frameNumberLength) + ".jpg"; if (!outputFolder.exists) { if (!outputFolder.create()) { alert("Error: see console."); $.writeln("Error : ImageProcessor.prototype.saveFrame(", $.line, ") : could not create folder."); } } this.srcDoc.selection.copy(); activeDocument = this.destDoc; this.destDoc.paste(); if (typeof fxFunc == "function") { fxFunc(this.destDoc); } this.destDoc.flatten(); this.destDoc.saveAs(new File(filepath), this.jpgOpts); }; /** * Copies the selection, saves it, and moves the selection down for the next frame. Moves to top of next column * when the selection reaches th bottom. Deselects when done. * @class ScanDownOver * @extends ImageProcessor * @constructor * @param {Object} options @see ImageProcessor * @return {void} */ function ScanDownOver(options) { ImageProcessor.apply(this, [options]); var x1 = this.coords[0], y1 = this.coords[1], x2 = this.coords[2], y2 = this.coords[3]; var minY = y1; while (x2 <= this.srcDoc.width) { while (y2 <= this.srcDoc.height) { activeDocument = this.srcDoc; this.srcDoc.selection.select([ [x1, y1], [x2, y1], [x2, y2], [x1, y2] ]); this.saveFrame(); y1 += this.deltaY; y2 += this.deltaY; } y1 = minY; y2 = y1 + this.selDim.height; x1 += this.deltaX; x2 += this.deltaX; } activeDocument = this.srcDoc; this.srcDoc.selection.deselect(); } ScanDownOver.prototype = new ImageProcessor; // ScanDownOver.prototype.constructor = ScanDownOver; (function () { if (!confirm ("Have you set the processor options?")) { return; } var options = { cols : 5, destDocName : "title_", destFolder : "~/Documents/art%20work/max%27s%20bar%20mitzvah/video_frames/title/", frameNumberNdx : 1, rows : 33 } ImageProcessor.saveState(); try { new ScanDownOver(options); $.writeln("Success"); } catch (e) { alert("Error: try new ScanDownOver failed. See console."); $.writeln(e); return; } finally { ImageProcessor.restoreState(); $.writeln("Finally"); } })();