Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
13 views6 pages

SimulateFMScreening JSX

The script simulates FM screening in Photoshop by converting image channels to Bitmap mode using Diffusion Dither, ensuring non-destructive processing on duplicates or split channels. It checks for document compatibility, prompts the user for output resolution, and processes Grayscale, RGB, or CMYK images accordingly. The final output consists of new Bitmap documents representing the FM simulation for each channel, while the original document remains unchanged.

Uploaded by

brandonc
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
13 views6 pages

SimulateFMScreening JSX

The script simulates FM screening in Photoshop by converting image channels to Bitmap mode using Diffusion Dither, ensuring non-destructive processing on duplicates or split channels. It checks for document compatibility, prompts the user for output resolution, and processes Grayscale, RGB, or CMYK images accordingly. The final output consists of new Bitmap documents representing the FM simulation for each channel, while the original document remains unchanged.

Uploaded by

brandonc
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 6

// SimulateFMScreening.

jsx
//
// This script simulates the effect of FM (Stochastic) screening
// by converting image channels to Bitmap mode using Diffusion Dither.
// It works non-destructively by operating on duplicates or split channels.
// Version: 1.0

#target photoshop
app.bringToFront(); // Bring Photoshop to the front

// Main function wrapped in an anonymous function for scope isolation


(function() {

// --- Basic Checks ---

// Check if a document is open


if (app.documents.length === 0) {
alert("Error: No document open.\nPlease open an image file first to
simulate FM screening.");
return; // Stop the script
}

var doc = app.activeDocument; // Get the currently active document


var startRulerUnits = app.preferences.rulerUnits; // Store original ruler units
app.preferences.rulerUnits = Units.PIXELS; // Use pixels for internal
consistency

// Check if the document mode is supported


var docMode = doc.mode;
if (docMode == DocumentMode.INDEXEDCOLOR || docMode == DocumentMode.DUOTONE ||
docMode == DocumentMode.MULTICHANNEL || docMode == DocumentMode.LAB) {
alert("Error: Unsupported document mode (" + docMode + ").\nPlease use
Grayscale, RGB, or CMYK images.");
app.preferences.rulerUnits = startRulerUnits; // Restore ruler units
before exiting
return;
}
if (docMode == DocumentMode.BITMAP) {
alert("Info: Image is already in Bitmap mode.\nNo further FM simulation
needed or possible with this script.");
app.preferences.rulerUnits = startRulerUnits; // Restore ruler units before
exiting
return;
}

// --- Configuration & User Input ---

var defaultResolution = 300; // Default DPI suggestion for the prompt


var outputResolution; // Variable to hold the user's choice

// Loop to get valid resolution input


while (true) {
var userInput = prompt(
"Enter desired output resolution (in DPI) for the FM simulation.\n" +
"This controls the fineness of the pattern (dot density).\n" +
"Higher values (e.g., 600, 1200) create finer textures.",
defaultResolution
);
if (userInput === null) { // User pressed Cancel
alert("Process cancelled by user.");
app.preferences.rulerUnits = startRulerUnits; // Restore ruler units
return; // Stop script
}

outputResolution = parseFloat(userInput);

if (!isNaN(outputResolution) && outputResolution > 0) {


break; // Valid input, exit loop
} else {
alert("Invalid input. Please enter a positive number for the resolution
(DPI).");
// Loop continues
}
}

// --- Core Function: Convert to Bitmap using Diffusion Dither ---

function convertToBitmapWithDiffusion(targetDoc, resolution) {


// This function takes a document and converts it to Bitmap
try {
// Ensure it's Grayscale first (required for Bitmap conversion)
if (targetDoc.mode !== DocumentMode.GRAYSCALE) {
targetDoc.changeMode(ChangeMode.GRAYSCALE); // Convert to Grayscale
}

// Define Bitmap Conversion options


var bitmapOptions = new BitmapConversionOptions();
bitmapOptions.method = BitmapConversionType.DIFFUSIONDITHER; // The key
step for FM simulation!
bitmapOptions.resolution = resolution; // Use the user-defined
resolution

// Perform the conversion


targetDoc.changeMode(ChangeMode.BITMAP, bitmapOptions);

// Rename the layer for clarity


try {
targetDoc.activeLayer.name = targetDoc.name.replace(/\.[^/.]+$/,
"") + " (FM Simulated)";
} catch(layerError) {
// Ignore error if layer renaming fails (e.g., background layer
issues)
// $.writeln("Note: Could not rename layer for " + targetDoc.name);
// For debugging
}
return true; // Indicate success

} catch (e) {
alert("Error during Bitmap conversion on document '" + targetDoc.name +
"':\n" + e.message);
// Attempt to close the potentially broken doc ONLY if it's a temporary
split channel document
// Check if the targetDoc is NOT the original document (or its direct
duplicate in grayscale case)
if (targetDoc !== doc && targetDoc.name !== (doc.name + " (FM
Simulated Grayscale)")) {
try {
targetDoc.close(SaveOptions.DONOTSAVECHANGES);
// $.writeln("Closed potentially corrupted temp doc: " +
targetDoc.name); // For debugging
} catch(closeError) {
// $.writeln("Could not close temp doc: " +
targetDoc.name); // For debugging
}
}
return false; // Indicate failure
}
}

// --- Main Processing Logic based on Color Mode ---

try { // Wrap main logic in try/catch for unexpected errors


if (docMode == DocumentMode.GRAYSCALE) {
// --- Grayscale Processing ---
alert("Processing Grayscale image.\nA duplicate document will be
created and converted.");
var dupDoc = doc.duplicate(doc.name + " (FM Simulated Grayscale)",
true); // Duplicate the document, including merged layers
app.activeDocument = dupDoc; // Make sure the duplicate is active

if (convertToBitmapWithDiffusion(dupDoc, outputResolution)) {
alert("Grayscale FM Simulation Complete.\nCheck the new document:
'" + dupDoc.name + "'");
} else {
alert("Grayscale FM Simulation Failed. The duplicate document
might be closed or in an error state.");
}

} else if (docMode == DocumentMode.RGB || docMode == DocumentMode.CMYK) {


// --- Color Processing (RGB/CMYK) ---
var colorSpaceName = (docMode == DocumentMode.RGB) ? "RGB" : "CMYK";
alert("Processing " + colorSpaceName + " image.\n" +
"1. A temporary duplicate will be made.\n" +
"2. Channels will be split into NEW Grayscale documents.\n" +
"3. Each new channel document will be converted to Bitmap.");

// Duplicate the original document BEFORE splitting channels.


// Splitting works on the active document and closes it.
var originalName = doc.name;
var tempDupName = originalName.replace(/\.[^/.]+$/, "") + " (Temp for
Split)"; // Create a base name for the temp doc
var dupDocForSplitting = doc.duplicate(tempDupName, true); // Make a
safe copy to split
app.activeDocument = dupDocForSplitting; // Ensure the duplicate is
active

try {
// Store expected channel names (important for identifying results)
var expectedChannelNames = [];
var channelObjects = dupDocForSplitting.componentChannels; // Get
only R,G,B or C,M,Y,K
for (var i = 0; i < channelObjects.length; i++) {
expectedChannelNames.push(channelObjects[i].name);
}

// Flatten the duplicate before splitting to ensure clean


separation
dupDocForSplitting.flatten();

// Execute the "Split Channels" command using its Type ID for


reliability
// This closes dupDocForSplitting and creates new grayscale docs.
app.runMenuItem(stringIDToTypeID("splitChannels"));

// Process the newly opened channel documents


var numChannels = expectedChannelNames.length;
var processedCount = 0;
var failedChannels = [];

// Loop through documents to find and process the split channels.


// It's safer to iterate a fixed number of times based on expected
channels
// and rely on Photoshop opening them predictably. We check names
for confirmation.
var openDocs = app.documents;
var baseNameForChannels = tempDupName; // The name base Photoshop
uses for split channels

// Iterate backwards through open documents, as new docs often


appear at the end
for (var i = openDocs.length - 1; i >= 0 && processedCount <
numChannels; i--) {
var channelDoc = openDocs[i];
var isExpectedChannel = false;

// Check if the document name matches the pattern


"BaseName_ChannelName"
for (var j = 0; j < expectedChannelNames.length; j++) {
var expectedName = baseNameForChannels + "_" +
expectedChannelNames[j];
// Allow for potential file extension added by Photoshop
(.tif, .psd etc.)
if (channelDoc.name.indexOf(expectedName) === 0 &&
channelDoc.mode === DocumentMode.GRAYSCALE) {
isExpectedChannel = true;
break;
}
}

if (isExpectedChannel) {
app.activeDocument = channelDoc; // Activate the channel
document
if (convertToBitmapWithDiffusion(channelDoc,
outputResolution)) {
processedCount++;
} else {
// Record failure but continue trying other channels
failedChannels.push(channelDoc.name);
}
// Remove the processed/failed channel name from expected
list to avoid processing duplicates if names are ambiguous
expectedChannelNames.splice(j, 1);
}
} // End loop through open documents

// --- Final Report for Color Images ---


if (processedCount === numChannels) {
alert(colorSpaceName + " FM Simulation Complete.\n" +
processedCount + " channel(s) were processed into
separate Bitmap documents.");
} else {
var alertMessage = "Warning: FM Simulation for " +
colorSpaceName + " image completed with issues.\n" +
"Processed " + processedCount + " out of " +
numChannels + " expected channels.\n";
if (failedChannels.length > 0) {
alertMessage += "\nFailed or could not process:\n- " +
failedChannels.join("\n- ");
}
if (expectedChannelNames.length > 0) {
alertMessage += "\nCould not find or match documents for
expected channels:\n- " + expectedChannelNames.join("\n- ");
}
alertMessage += "\nPlease check your open documents.";
alert(alertMessage);
}

} catch (splitError) {
alert("An error occurred during the channel splitting or processing
stage:\n" + splitError.message);
// The temporary duplicate might still be open if split failed
early
try {
// Attempt to find and close the temp duplicate if it exists
for(var k=0; k < app.documents.length; k++){
if(app.documents[k].name.indexOf(tempDupName) === 0){
app.documents[k].close(SaveOptions.DONOTSAVECHANGES);
break;
}
}
} catch(closeError) {} // Ignore errors during cleanup
}
} // End color processing block

} catch (mainError) {
alert("An unexpected error occurred in the main script:\n" +
mainError.message);
} finally {
// --- Cleanup ---
// Restore original ruler units regardless of success or failure
app.preferences.rulerUnits = startRulerUnits;
// $.writeln("Script finished. Restored ruler units."); // For debugging
}

})(); // End of anonymous function wrapper


```

**How to Use This Script:**

1. **Save:** Copy the code above. Paste it into a plain text editor (like Notepad
on Windows, TextEdit on Mac - in plain text mode, or a code editor like VS Code).
Save the file with a `.jsx` extension (e.g., `FMSimulator.jsx`).
2. **Place (Optional but Recommended):** For easy access, place the saved `.jsx`
file into your Photoshop Scripts folder:
* **Windows:** `C:\Program Files\Adobe\Adobe Photoshop [Your Version]\Presets\
Scripts\`
* **macOS:** `/Applications/Adobe Photoshop [Your Version]/Presets/Scripts/`
* (Replace `[Your Version]` with your specific Photoshop version, like `2024`
or `2025`).
* Restart Photoshop if it was running. The script will now appear directly in
the `File > Scripts` menu.
3. **Run:**
* Open the Grayscale, RGB, or CMYK image you want to process in Photoshop.
* Go to `File > Scripts`. If you placed the file in the Scripts folder, select
`FMSimulator` (or whatever you named it).
* If you didn't place it in the Scripts folder, select `Browse...` and navigate
to where you saved the `.jsx` file.
4. **Follow Prompts:** The script will ask for the desired output resolution
(DPI). Higher values create a finer, denser pattern, simulating higher LPI (lines
per inch) equivalents in traditional screening. Enter a value and click OK.
5. **Review Results:**
* **Grayscale:** A new document, named like `YourFileName (FM Simulated
Grayscale)`, will be created in Bitmap mode.
* **RGB/CMYK:** Several new Grayscale documents will be created, one for each
color channel (e.g., `YourFileName (Temp for Split)_Red`, `..._Green`, `..._Blue`).
Each of these will be in Bitmap mode, showing the FM simulation for that specific
channel. Your original color document remains untouched.

**Testing and Accuracy:**

* **Test Files:** Use images with smooth gradients, fine details, and solid color
areas to see how the simulation behaves. Test Grayscale, RGB, and CMYK files.
* **Resolution:** Experiment with different resolutions (e.g., 150, 300, 600, 1200
DPI) on the same image to understand its impact on the pattern's fineness.
* **Visual Check:** Zoom in closely on the resulting Bitmap images. You should see
only black and white pixels. The *density* of black pixels should correspond to the
darkness of the original image areas. The pattern should look somewhat random or
stochastic, not like the regular grid of traditional halftone dots.
* **Accuracy:** Remember, this is a *simulation* using Photoshop's Diffusion
Dither. It visually mimics the *principle* of FM screening very well but won't be a
mathematically perfect replica of a specific commercial FM screening technology
(like Kodak Staccato, Agfa CristalRaster, etc.). It's excellent for previewing the
effect or for creative purposes.

This script provides a reliable way to simulate FM screening directly within


Photoshop using its built-in capabiliti

You might also like