// 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.
#target photoshop
// Main function wrapped in an anonymous function
(function() {
// Check if a document is open
if ([Link] === 0) {
alert("Error: No document open.\nPlease open an image file first.");
return; // Stop the script
}
// --- Configuration ---
// You could potentially preset a default resolution here,
// but prompting the user is generally better.
var defaultResolution = 300; // Example default DPI
// --- End Configuration ---
var doc = [Link];
var startRulerUnits = [Link];
[Link] = [Link]; // Use pixels for calculations
// Function to perform the Bitmap conversion
function convertToBitmapWithDiffusion(targetDoc, outputResolution) {
try {
// Ensure it's Grayscale first (necessary for Bitmap conversion)
if ([Link] != [Link]) {
[Link]([Link]);
}
// Conversion options
var bitmapOptions = new BitmapConversionOptions();
[Link] = [Link];
[Link] = outputResolution; // Use the provided
resolution
// Convert to Bitmap
[Link]([Link], bitmapOptions);
[Link] = [Link](/\.[^/.]+$/, "") +
" (FM Simulated)"; // Rename layer
return true; // Indicate success
} catch (e) {
alert("Error during Bitmap conversion on " + [Link] + ":\n" +
e);
// Attempt to close the potentially broken doc if it's a split channel
temp
if (targetDoc !== doc) { // Don't close original duplicate in case of
failure
try { [Link]([Link]); }
catch(closeError) {}
}
return false; // Indicate failure
}
}
// --- Main Logic ---
// Ask user for desired output resolution (controls dot density/fineness)
var userResolutionInput = prompt("Enter desired output resolution (in DPI) for
the FM simulation.\nHigher values create finer patterns.", defaultResolution);
if (userResolutionInput === null || userResolutionInput === "" ||
isNaN(parseFloat(userResolutionInput))) {
alert("Invalid resolution entered or process cancelled.");
[Link] = startRulerUnits; // Restore original ruler
units
return; // Stop script
}
var outputResolution = parseFloat(userResolutionInput);
if (outputResolution <= 0) {
alert("Resolution must be a positive number.");
[Link] = startRulerUnits; // Restore original ruler
units
return;
}
// Check the document's color mode
var docMode = [Link];
if (docMode == [Link]) {
alert("Image is already in Bitmap mode. Cannot apply FM simulation.");
[Link] = startRulerUnits;
return;
}
// --- Processing based on Color Mode ---
if (docMode == [Link]) {
alert("Processing Grayscale image.\nThe document will be duplicated and
converted.");
var dupDoc = [Link]("FM Simulated Grayscale", true); // Duplicate
the document
if (!convertToBitmapWithDiffusion(dupDoc, outputResolution)) {
alert("FM Simulation failed for Grayscale image.");
// dupDoc might have been closed by the error handler, or left open in
a failed state.
} else {
alert("Grayscale FM Simulation Complete.\nA new document has been
created.");
}
} else if (docMode == [Link] || docMode == [Link]) {
var colorSpaceName = (docMode == [Link]) ? "RGB" : "CMYK";
alert("Processing " + colorSpaceName + " image.\nChannels will be split
into separate Grayscale documents, then converted to Bitmap.");
// Duplicate the original document BEFORE splitting channels
// Splitting works on the active document and closes it.
var originalName = [Link];
var dupDocForSplitting = [Link](originalName + " (Temp for Split)",
true); // Make a safe copy to split
[Link] = dupDocForSplitting; // Ensure the duplicate is active
try {
// Store current channel names (important!)
var channelNames = [];
for (var i = 0; i < [Link]; i++) {
// Only store actual color channels, ignore masks etc.
if ( (docMode == [Link] &&
[Link][i].kind === [Link]) ||
(docMode == [Link] &&
[Link][i].kind === [Link]) ) {
[Link]([Link][i].name);
}
}
// Flatten the duplicate before splitting to avoid layer issues
[Link]();
// Split Channels - This closes dupDocForSplitting and creates new
grayscale docs
[Link](stringIDToTypeID("splitChannels")); // Use TypeID for
reliability
// Now, loop through the NEWLY opened documents (which are the
channels)
// We need to figure out which documents were just created. This is
tricky.
// A common way is to assume they are the most recently opened
documents.
var numChannels = [Link];
var openedDocs = [Link];
var processedCount = 0;
// Iterate backwards through documents as splitting opens them in a
certain order
for (var i = [Link] - 1; i >= 0 && processedCount <
numChannels; i--) {
var channelDoc = openedDocs[i];
// Check if the doc name seems to match one of the split channels
// (Photoshop usually names them like 'OriginalName_ChannelName')
var isLikelyChannel = false;
for(var j=0; j<[Link]; j++) {
// Check if doc name ends with _ChannelName (more robust check)
var expectedSuffix = "_" + channelNames[j];
if ([Link] > [Link] &&
[Link]([Link] -
[Link]) === expectedSuffix) {
isLikelyChannel = true;
break;
}
}
// Alternative check: If the document mode is Grayscale and wasn't
the original doc
if (!isLikelyChannel && [Link] === [Link]
&& [Link] !== [Link]) {
// This might be a fallback, less reliable if other grayscale
docs are open
// isLikelyChannel = true; // Uncomment cautiously if name
check fails
}
if (isLikelyChannel) {
[Link] = channelDoc; // Activate the channel
document
if (convertToBitmapWithDiffusion(channelDoc, outputResolution))
{
processedCount++;
} else {
alert("FM Simulation failed for channel: " +
[Link] + ".\nThis channel document might be closed or left in an error
state.");
// Don't increment processedCount on failure
}
}
}
if (processedCount === numChannels) {
alert(colorSpaceName + " FM Simulation Complete.\nEach channel (" +
[Link](", ") + ") has been processed into a separate Bitmap document.");
} else {
alert("Warning: Processed " + processedCount + " out of " +
numChannels + " channels. Please check the open documents. Some channel conversions
might have failed.");
}
} catch (e) {
alert("An error occurred during the channel splitting or processing:\n"
+ e);
// Attempt to close the temporary duplicate if it still exists
(unlikely after split)
try { [Link]([Link]); }
catch(closeError) {}
}
} else {
alert("Unsupported document mode: " + docMode + "\nThis script only
supports Grayscale, RGB, and CMYK modes.");
}
// Restore original ruler units
[Link] = startRulerUnits;
})(); // End of anonymous function wrapper