Imaging API

The Imaging API allows JavaScript to work directly with image data in Photoshop documents.

The Imaging API are exposed on the imaging sub-module under "photoshop". You can access these APIs by using the follow code:

const imaging = require("photoshop").imaging;

Terminology and data types

Image data is expressed as a collection of pixels. A pixel represents all color information for a single point in the image. A pixel consists of one or more components of color values and alpha information.

An RGB pixel with alpha information has four components: "red", "green", "blue", and "alpha". An opaque RGB pixel has only three components. Including an alpha channel as a fourth yields RGBA. An opaque gray scale pixel has one component. A gray scale pixel with alpha has two components.

In order to properly interpret pixel data, Photoshop needs to know which color profile the data is expressed in. An example of a color profile is "Adobe RGB (1998)". Photoshop will perform color conversions when needed. You can get the list of available color profiles by invoking getColorProfiles on the app object:

const rgbProfiles = await require("photoshop").app.getColorProfiles("RGB");

const grayProfiles = await require("photoshop").app.getColorProfiles("Gray");

Lab data uses the "Lab D50" color profile.

When working with 32 bit images, Photoshop will return color profile names that include the description 'Linear Profile'. An example is "(Linear RGB Profile)". This text should not be included when specifying a profile. If a document profile is listed as Adobe RGB (1998) (Linear RGB Profile), then you would use the string Adobe RGB (1998) when specifying a profile with a method such as createImageDataFromBuffer.

Photoshop supports two ways to store pixel information in memory:

JavaScript APIs use the chunky format by default.

Image data is represented by a PhotoshopImageData instance. This instance has the following methods and properties:

PhotoshopImageData

Name
Type
Description
width
Number
The width of the image data in pixels.
height
Number
The height of the image data in pixels.
colorSpace
String
The color space (or mode) for the image data. This can be "RGB", "Grayscale", "Lab".
colorProfile
String
The color profile for the image data. For example, "sRGB IEC61966-2.1". If the color profile is empty, then the profile of a target document will be used.
hasAlpha
Boolean
True if the image data includes an alpha channel.
components
Number
Number of components per pixel. This is 3 for RGB, 4 for RGBA and so forth.
componentSize
Number
Number of bits per component. This can be 8, 16, or 32.
pixelFormat
String
Memory layout (order) of components in a pixel. Could be "RGB", "RGBA", "Grayscale", "GrayscaleAlpha", "LAB", or "LABAlpha".
isChunky
Boolean
True if the image data internally is using the chunky format.
type
String
Type of contained data. At the moment only "image/uncompressed" is supported.
getData
Method
See documentation below.
dispose
Method
See documentation below.

When specifying bounds in the Imaging API, the resulting region will include the left and top locations up to the right and bottom locations. As an example: {left: 0, top: 0, right: 10, bottom: 10} will target ten pixels from location 0 to 9 in both directions (not including pixels from location 10). You can specify height and width instead of right and bottom - doing so can make the code easier to read: {left: 0, top: 0, width: 10, height: 10}.

The value range for pixel components depend on the componentSize.

Component Size
Value range
Comments
8
0..255
16
0..32768
32
0..1+
High dynamic range images may have component values that are below 0 or above 1.0

Instances of PhotoshopImageData cannot be created explicitly by JavaScript, but are returned from Imaging API methods.

Performance considerations

Photoshop images can be (very) large. It is therefore important to manage the image data carefully in JavaScript. If you use a large amount of JavaScript memory, then you may see warnings in the UDT debugger console such as the following:

Plugin memory usage increased to: 600MB.
Plugin exceeds memory limit. Currently used memory 600.

The potential large data space has a number of implications.

First off, all Imaging API methods that return pixels are asynchronous. This is needed because modifying and even querying pixel data may incur disk I/O.

When requesting pixels, use the smallest possible target size. Specifying a small target size allows Photoshop to optimize the retrieval (and possibly document compositing) of the source region.

When you are done working with image data, then you should call dispose on the PhotoshopImageData instance. This allow Photoshop to release memory immediately rather than waiting for garbage collection.

Converting image data between different color spaces or color profiles is somewhat time consuming. Whenever possible, work with a color space and color profile that match the document that you are operating on.

APIs

PhotoshopImageData

getData

Return pixel information from an PhotoshopImageData instance as a typed array. The return type depends on the componentSize of the image.

Component Size
Return type
8
Uint8Array
16
Uint16Array
32
Float32Array

The method takes an options argument. This argument can be used to specify attributes of the returned data. Possible options include:

Return value: Promise<Uint8Array | Uint16Array | FloatArray>

The method is asynchronous, and thus returns a promise with the described data type.

Example:

const pixelData = await imageObj.imageData.getData()

dispose

Calling this synchronous method will release the contained image data. Doing so will reduce memory usage faster than waiting for the JavaScript garbage collector to run.

const imageData = await imaging.createImageDataFromBuffer( arrayBuffer, options );
// perhaps a putPixels call
imageData.dispose();

getPixels

This API allows JavaScript to obtain pixel data from a Photoshop document. You can request pixels from an individual layer, or from the full document composite.

const imageObj = await imaging.getPixels(options);

Return value: Promise<Object>

The returned object has the following properties:

Note: the components property of the image data depends on whether or not the pixel source includes an alpha channel, e.g., 4 for RGBA.

If the targetSize is smaller than the requested region, then the resulting image data will be scaled down. When scaling, Photoshop may use a smaller (cached) version of the image canvas. This is known as a pyramid level. The number of pyramid levels that are available in a document is determined by the preference: "Performance Cache Levels". Using a cache level may result in dramatic performance improvements. The returned level indicates which level that was used. Level 0 indicates the full resolution canvas. Level 1 indicates a cache that is half of the size of the full resolution, and so forth. The returned sourceBounds are relative to the bounds of the source cache level (not relative to the full resolution bounds).

The valid bounds for the sourceBounds depend on the pixel source. The origin of the composite image is (0, 0),and the size is given by the properties width and height on the DOM object for the source document. The origin of a pixel layer can be different from (0, 0). You can get the valid pixel bounds for a layer by calling boundsNoEffects on the DOM object corresponding to the source layer.

Example - create a thumbnail of an region of the target document that is 100 pixels tall.

const thumbnail = await imaging.getPixels({
    sourceBounds: { left: 0, top: 0, right: 300, bottom: 300 },
    targetSize: { height: 100 }
});

putPixels

This API allows JavaScript to change pixel data in a layer. You can replace all pixels in a layer or a region of the layer.

await imaging.putPixels(options);

Example:

await imaging.putPixels({
    layerID: 123
    imageData: imageData
});

getLayerMask

This API allows JavaScript to retrieve the pixel data representing a layer's mask.

const imageObj = await imaging.getLayerMask(options);

Return value: Promise<Object>

The returned object has the following properties:

Example - get the user mask for a layer

const imageObj = await imaging.getLayerMask({
    documentID: 123,
    layerID: 5,
    kind: "user"
    sourceBounds: { left: 0, top: 0, right: 300, bottom: 300 },
    targetSize: { height: 100 }
});

putLayerMask

This API allows JavaScript to edit the pixels of a layer's mask. At this time, only pixel masks are editable. In the UI, they are what is referred to as a "Layer Mask".

await imaging.putLayerMask(options);

Example:

await imaging.putLayerMask({
    layerID: 123
    imageData: grayImageData
});

getSelection

This API allows JavaScript to obtain a pixel representation of the active selection. Think of it like entering Quick Mask mode.

const imageObj = await imaging.getSelection(options);

Return value: Promise<Object>

The returned object has the following properties:

Example - get the document selection

const imageObj = await imaging.getSelection({
    documentID: 123,
    sourceBounds: { left: 0, top: 0, right: 300, bottom: 300 }
});

putSelection

This API allows JavaScript to change the selection itself using a provided pixel data representation. Think of it like exiting Quick Mask mode.

await imaging.putSelection(options);

Example:

await imaging.putSelection({ imageData: grayImageData });

createImageDataFromBuffer

This API allows JavaScript to create arbitrary image data from a memory buffer.

const imageData = await imaging.createImageDataFromBuffer(arrayBuffer, options);

Return value: Promise<PhotoshopImageData>

The number of elements in imageData must be equal to: width * height * components.

Example:

const width = 30;
const height = 40;
const components = 4;  // RGBA
const componentCount = width * height;
const dataSize = componentCount * components;
const arrayBuffer = new Uint8Array(dataSize);

// Add some (chunky) data to the buffer
for (let i = 0 ; i < componentCount; i += components) {
    arrayBuffer[index]   = 255;      // red
    arrayBuffer[index+1] = 0;        // green
    arrayBuffer[index+2] = 0;        // blue
    arrayBuffer[index+3] = 127;      // alpha
}

const options = {
   width: width,
   height: height,
   components: components,
   colorProfile: "sRGB IEC61966-2.1",
   colorSpace: "RGB"
};
const imageData = await imaging.createImageDataFromBuffer(arrayBuffer, options)

Image data that is used for layer masks or document selections uses a single grayscale component. When creating such data, use components: 1, colorSpace: "Grayscale" and colorProfile: "Gray Gamma 2.2" as shown in the following example:

const width = 30;
const height = 40;
const componentCount = width * height;
const arrayBuffer = new Uint8Array(componentCount);

for (let i = 0 ; i < componentCount; ++i) {
   arrayBuffer[i] = 127; // all set to the median value
}

const options = {
   width: width,
   height: height,
   components: 1,  // masks are grayscale
   chunky: false,
   colorProfile: "Gray Gamma 2.2",
   colorSpace: "Grayscale"
};
const maskData = await imaging.createImageDataFromBuffer(arrayBuffer, options)

encodeImageData

This API is exposed to allow image data to be used with UXP image elements. With the current version of UXP you must use jpeg/base64 encoding when assigning to an image element.

const dataImage = await imaging.encodeImageData(options);

Return value: Promise<Number[] | string>

Example:

const imageElement = document.createElement('img');

const jpegData = await imaging.encodeImageData({"imageData": imgObj.imageData, "base64": true});

const dataUrl = "data:image/jpeg;base64," + jpegData;
imageElement.src = dataUrl;
document.body.appendChild(imageElement);

Sample files

Here you will find a sample plugin (how to load) and UXP JavaScript file (.psjs) that exercise most of the above.