# Render layer example

**URL:** <https://forums.creativeclouddeveloper.com/t/render-layer-example/4268>\
**Category:** UXP Plugin API\
**Tags:** react, typescript\
**Created:** [February 9, 2022, 7:51am UTC](https://forums.creativeclouddeveloper.com/t/render-layer-example/4268 "2022-02-09T07:51:58Z")\
**Posts on this page:** 18\
**Page:** 1

<div class="post-metadata">

**Author:** ![OliverOBrien](https://avatars.discourse-cdn.com/v4/letter/o/838e76/32.png) [@OliverOBrien](https://forums.creativeclouddeveloper.com/u/OliverOBrien)\
**Post date:** [February 9, 2022, 7:51am UTC](https://forums.creativeclouddeveloper.com/t/render-layer-example/4268/1 "2022-02-09T07:51:58Z")

</div>

Good day everyone!

I have some wonderful news for anyone interested!

I present to you: **Layer display**

 ![image](https://us1.discourse-cdn.com/flex015/uploads/xdplugins/original/2X/0/0bbdf37fd10cf888525d499f3e6d29e4ac4f1010.png)

It is nothing special really. Select the layer, export the layer, load the file as ArrayBuffer, convert to uInt8Array and make a url for it.  
Though I spent some time searching, in order to find this solution.  
Aside from a humble brag ( 🤏 🤏 🤏 brag), I would like to share for posterity.  
Any improvements will be greatly appreciated.

**The Export as PNG function**

```auto
export const exportLayerAsPngAndLoad = async (layer: Layer): Promise<string> => {
  const { id, name } = layer;
  await core.executeAsModal(
    async () => {
      const destFolder = (await storage.localFileSystem.getDataFolder()).nativePath;
      await selectLayerByID(id);
      const exportCommand = {
        _obj: 'exportSelectionAsFileTypePressed',
        _target: { _ref: 'layer', _enum: 'ordinal', _value: 'targetEnum' },
        fileType: 'png',
        quality: 32,
        metadata: 0,
        destFolder,
        sRGB: true,
        openWindow: false,
        _options: { dialogOptions: 'dontDisplay' },
      };
      await action.batchPlay([exportCommand], { synchronousExecution: true, modalBehavior: 'execute' });
    },
    { commandName: 'Export Layer As PNG' }
  );
 try {
   return loadImageFromDataFolder(name);
 } catch (e) {
   console.error(`Error loading image: ${e}`);
}
  return null;
};

```

**Load Image function**

```auto
export const loadImageFromDataFolder = async (imageName: string): Promise<string> => {
  const dataFolder = await storage.localFileSystem.getDataFolder();

  const name = imageName.concat('.png');
  const entriesArray = Array.from(await dataFolder.getEntries());
  const image = entriesArray.find((entry) => entry.name === name);

  if (!image) throw new Error(`Image ${imageName}.png Not Found`);
  if (!image.isFile) throw new Error(`${imageName}.png is not a file`);

  const file = image as storage.File;
  const data = (await file.read({ format: storage.formats.binary })) as ArrayBuffer;
  if (data.byteLength === 0) throw new Error(`${imageName}.png is empty`);
  const uInt8Array = new Uint8Array(data);
  const blob = new Blob([uInt8Array]);
  const url = URL.createObjectURL(blob);

  await image.delete();

  return url;
};

```

**Select Layer By ID**

```auto
export const selectLayerByID = async (layerID: number) => {
  const actionObject = [
    {
      _obj: 'select',
      _target: [
        {
          _ref: 'layer',
          _id: layerID,
        },
      ],
      makeVisible: false,
      layerID: [layerID],
      _isCommand: false,
    },
  ];
  await app.batchPlay(actionObject, { modalBehavior: 'execute' });
};

```

React Image Display

```auto
import React, { FC } from 'react';
import { ImageDisplayStyle, ImageDisplayContainer } from './styled';
import { Layer } from 'Photoshop'; // Credit to Hans Otto Wirtz for the Typescript library

interface Props {
  exportLayer: Layer;
}

export const ImageDisplay: FC<Props> = (props: Props) => {
  const { exportLayer } = props;
  const [loading, setLoading] = useState(false);
  const [imageUrl, setImageUrl] = useState(null);

 useEffect(() => {
    if(exportLayer) {
       setLoading(true);
       const url = await exportLayerAsPngAndLoad(exportLayer);
       setLoading(false);
       setImageUrl(url);
    }
 }, [exportLayer])

  return (
    <ImageDisplayStyle>
      {(imageUrl && (
        <ImageDisplayContainer>
          <img className="background" src="/images/BackGround.png" alt="Background" />
          {(!loading && <img className="image" src={imageUrl} alt="BaseLayer" />) || (
            <img className="loading" src="LOADING.gif" alt="loading" />
          )}
        </ImageDisplayContainer>
      )) || <ImageDisplayContainer>Select a Choice</ImageDisplayContainer>}
    </ImageDisplayStyle>
  );
};

export default ImageDisplay;

```

---

<div class="post-metadata">

**Author:** ![Jarda](https://sea1.discourse-cdn.com/flex015/user_avatar/forums.creativeclouddeveloper.com/jarda/32/1909_2.png) [@Jarda](https://forums.creativeclouddeveloper.com/u/Jarda)\
**Post date:** [February 9, 2022, 5:10pm UTC](https://forums.creativeclouddeveloper.com/t/render-layer-example/4268/2 "2022-02-09T17:10:38Z")

</div>

There is also a way to do that without saving the layer as a file in the first place. But that API is not documented and could change at any time. So I am not sure whether it would be good for production use.  
And with Canvas support, we could even edit image before being shown in panel.

---

<div class="post-metadata">

**Author:** ![Erin\_Finnegan](https://sea1.discourse-cdn.com/flex015/user_avatar/forums.creativeclouddeveloper.com/erin_finnegan/32/100_2.png) [@Erin\_Finnegan](https://forums.creativeclouddeveloper.com/u/Erin_Finnegan)\
**Post date:** [February 9, 2022, 10:46pm UTC](https://forums.creativeclouddeveloper.com/t/render-layer-example/4268/3 "2022-02-09T22:46:46Z")

</div>

@pkrishna another user who needs Canvas 👀

---

<div class="post-metadata">

**Author:** ![OliverOBrien](https://avatars.discourse-cdn.com/v4/letter/o/838e76/32.png) [@OliverOBrien](https://forums.creativeclouddeveloper.com/u/OliverOBrien)\
**Post date:** [February 10, 2022, 6:53am UTC](https://forums.creativeclouddeveloper.com/t/render-layer-example/4268/4 "2022-02-10T06:53:51Z")

</div>

Could you please point me in the right direction for this implementation?

Thank you 😊

---

<div class="post-metadata">

**Author:** ![DavideBarranca](https://sea1.discourse-cdn.com/flex015/user_avatar/forums.creativeclouddeveloper.com/davidebarranca/32/1523_2.png) [@DavideBarranca](https://forums.creativeclouddeveloper.com/u/DavideBarranca)\
**Post date:** [February 10, 2022, 8:06pm UTC](https://forums.creativeclouddeveloper.com/t/render-layer-example/4268/5 "2022-02-10T20:06:12Z")

</div>

JB, do you mean the API for the scripted trees etc.? 🙂

---

<div class="post-metadata">

**Author:** ![Jarda](https://sea1.discourse-cdn.com/flex015/user_avatar/forums.creativeclouddeveloper.com/jarda/32/1909_2.png) [@Jarda](https://forums.creativeclouddeveloper.com/u/Jarda)\
**Post date:** [February 10, 2022, 8:13pm UTC](https://forums.creativeclouddeveloper.com/t/render-layer-example/4268/6 "2022-02-10T20:13:53Z")

</div>

I am talking about `require("photoshop").exportmanager.getPreviewAsync` and his “brother” `exportImageAsync`

---

<div class="post-metadata">

**Author:** ![OliverOBrien](https://avatars.discourse-cdn.com/v4/letter/o/838e76/32.png) [@OliverOBrien](https://forums.creativeclouddeveloper.com/u/OliverOBrien)\
**Post date:** [February 12, 2022, 5:36pm UTC](https://forums.creativeclouddeveloper.com/t/render-layer-example/4268/7 "2022-02-12T17:36:00Z")

</div>

Thank you, I will require some information regarding the usage, variables and so on 😅

> [@Jarda](#):
>
> require(“photoshop”).exportmanager.getPreviewAsync

---

<div class="post-metadata">

**Author:** ![Jarda](https://sea1.discourse-cdn.com/flex015/user_avatar/forums.creativeclouddeveloper.com/jarda/32/1909_2.png) [@Jarda](https://forums.creativeclouddeveloper.com/u/Jarda)\
**Post date:** [February 12, 2022, 5:44pm UTC](https://forums.creativeclouddeveloper.com/t/render-layer-example/4268/8 "2022-02-12T17:44:23Z")

</div>

Yes, that is the thing. Everybody here wants that feature. And it is working in Photoshop for at least year. There were a few documentation updates but nothing about this feature. And it could be intentional and if so then I don’t know why. And that raises the question of whether we should use it or not. @kerrishotts @Barkin

---

<div class="post-metadata">

**Author:** ![kerrishotts](https://sea1.discourse-cdn.com/flex015/user_avatar/forums.creativeclouddeveloper.com/kerrishotts/32/8_2.png) [@kerrishotts](https://forums.creativeclouddeveloper.com/u/kerrishotts)\
**Post date:** [February 15, 2022, 10:10pm UTC](https://forums.creativeclouddeveloper.com/t/render-layer-example/4268/9 "2022-02-15T22:10:47Z")

</div>

`exportmanager` should be considered _private_ for now. (It’s there to support “Export As”).

---

<div class="post-metadata">

**Author:** ![Jarda](https://sea1.discourse-cdn.com/flex015/user_avatar/forums.creativeclouddeveloper.com/jarda/32/1909_2.png) [@Jarda](https://forums.creativeclouddeveloper.com/u/Jarda)\
**Post date:** [February 16, 2022, 12:06am UTC](https://forums.creativeclouddeveloper.com/t/render-layer-example/4268/10 "2022-02-16T00:06:55Z")

</div>

“Export As”… isn’t it supported by batchplay these days? 😃

---

<div class="post-metadata">

**Author:** ![ddbell](https://sea1.discourse-cdn.com/flex015/user_avatar/forums.creativeclouddeveloper.com/ddbell/32/1030_2.png) [@ddbell](https://forums.creativeclouddeveloper.com/u/ddbell)\
**Post date:** [February 16, 2022, 1:20am UTC](https://forums.creativeclouddeveloper.com/t/render-layer-example/4268/11 "2022-02-16T01:20:16Z")

</div>

Interesting… so any way to do the reverse and import modified data back into the layer? So it could be used to get/set pixel data?

---

<div class="post-metadata">

**Author:** ![OliverOBrien](https://avatars.discourse-cdn.com/v4/letter/o/838e76/32.png) [@OliverOBrien](https://forums.creativeclouddeveloper.com/u/OliverOBrien)\
**Post date:** [February 16, 2022, 7:58am UTC](https://forums.creativeclouddeveloper.com/t/render-layer-example/4268/12 "2022-02-16T07:58:28Z")

</div>

Just found out that there are some bugs in my code.  
It is now saved in a temporary folder that gets deleted after load.

```auto
import { action, core, Layer } from 'photoshop';
import { storage } from 'uxp';
import { selectLayerByID } from '../layer/selectLayer';
import loadImageFromFolder from './loadImage';

export const exportLayerAsPngAndLoad = async (layer: Layer): Promise<string> => {
  const { id, name } = layer;
  const tempFolder = await storage.localFileSystem.getTemporaryFolder();
  const destFolder = await tempFolder.createFolder(`${Math.round(Math.random() * 1000000)}`);
  await core.executeAsModal(
    async () => {
      await selectLayerByID(id);
      const exportCommand = {
        _obj: 'exportSelectionAsFileTypePressed',
        _target: { _ref: 'layer', _enum: 'ordinal', _value: 'targetEnum' },
        fileType: 'png',
        quality: 32,
        metadata: 0,
        destFolder: destFolder.nativePath,
        sRGB: true,
        openWindow: false,
        _options: { dialogOptions: 'dontDisplay' },
      };
      await action.batchPlay([exportCommand], { modalBehavior: 'execute' });
    },
    { commandName: `Export ${layer.name} Layer As PNG` }
  );
  try {
    const image = await loadImageFromFolder(destFolder, name, 10);
    return image;
  } catch (e) {
    console.error(`Error loading image: ${e}`);
  }
  await destFolder.delete();
  return '';
};

```

The load now has multiple loading attempts

```auto
import { storage } from 'uxp';

const getImageData = async (image: storage.Entry) => {
  if (!image) return null;
  if (!image.isFile) throw new Error(`${image.name} is not a file`);

  const file = image as storage.File;
  const data = (await file.read({ format: storage.formats.binary })) as ArrayBuffer;
  console.log('bytelength', data.byteLength);
  if (data.byteLength === 0) return null;
  return data;
};

export const loadImageFromFolder = async (
  folder: storage.Folder,
  imageName: string,
  attempts = 100
): Promise<string> => {
  const name = imageName.concat('.png');

  let image: storage.Entry;
  let attemptsLeft = attempts;
  let data: ArrayBuffer | false;

  console.log((await folder.getEntries()).map((entry) => entry.name));

  while ((!image || !data) && attemptsLeft > 0) {
    const entriesArray = await folder.getEntries();
    image = entriesArray.find((entry) => entry.name === name);
    data = await getImageData(image);
    await new Promise((res) => setTimeout(res, 50));
    attemptsLeft -= 1;
  }

  console.warn('loaded file with', attemptsLeft, 'attempts left');

  if (!image) throw new Error(`Image ${imageName}.png not found`);
  if (!data) throw new Error(`No data in image: ${imageName}.png`);

  const uInt8Array = new Uint8Array(data);
  const blob = new Blob([uInt8Array]);
  const url = URL.createObjectURL(blob);

  await image.delete();

  return url;
};

```

Also Great help from Hans Otto Wirtz

---

<div class="post-metadata">

**Author:** ![Dawn](https://avatars.discourse-cdn.com/v4/letter/d/71e660/32.png) [@Dawn](https://forums.creativeclouddeveloper.com/u/Dawn)\
**Post date:** [February 23, 2022, 12:44pm UTC](https://forums.creativeclouddeveloper.com/t/render-layer-example/4268/15 "2022-02-23T12:44:44Z")

</div>

question: What is the maximum value of image quality? I found it only supports 32% picture quality 😇

---

<div class="post-metadata">

**Author:** ![OliverOBrien](https://avatars.discourse-cdn.com/v4/letter/o/838e76/32.png) [@OliverOBrien](https://forums.creativeclouddeveloper.com/u/OliverOBrien)\
**Post date:** [February 24, 2022, 5:10am UTC](https://forums.creativeclouddeveloper.com/t/render-layer-example/4268/16 "2022-02-24T05:10:11Z")

</div>

To be brutally honest, I found that function from another post.  
I have tried other values to the quality attribute, but it never exported aside from 32.

---

<div class="post-metadata">

**Author:** ![kerrishotts](https://sea1.discourse-cdn.com/flex015/user_avatar/forums.creativeclouddeveloper.com/kerrishotts/32/8_2.png) [@kerrishotts](https://forums.creativeclouddeveloper.com/u/kerrishotts)\
**Post date:** [February 24, 2022, 9:17pm UTC](https://forums.creativeclouddeveloper.com/t/render-layer-example/4268/17 "2022-02-24T21:17:07Z")

</div>

Ps has many different ways to export content, and all of these have widely varying ranges for quality ranges. `32` may be the maximum range for PNGs using this method, but if you check w/ a manually exported version at best quality, they should hopefully match. (That is, 32 may be equivalent to 100%.)

---

<div class="post-metadata">

**Author:** ![ddbell](https://sea1.discourse-cdn.com/flex015/user_avatar/forums.creativeclouddeveloper.com/ddbell/32/1030_2.png) [@ddbell](https://forums.creativeclouddeveloper.com/u/ddbell)\
**Post date:** [February 24, 2022, 10:22pm UTC](https://forums.creativeclouddeveloper.com/t/render-layer-example/4268/18 "2022-02-24T22:22:39Z")

</div>

I thought PNG was always lossless compression and the only difference was higher compression was higher time, but image quality was the same regardless as the images always unpacked the same, pixel for pixel. So I guess having the quality parameter for PNG is confusing to me. Is the value actually a quality setting? Or is it tied to PNG compression and just called “quality”?.. so it is actually comparable to quick / small / medium / large compression?

---

<div class="post-metadata">

**Author:** ![Jarda](https://sea1.discourse-cdn.com/flex015/user_avatar/forums.creativeclouddeveloper.com/jarda/32/1909_2.png) [@Jarda](https://forums.creativeclouddeveloper.com/u/Jarda)\
**Post date:** [February 25, 2022, 6:59pm UTC](https://forums.creativeclouddeveloper.com/t/render-layer-example/4268/19 "2022-02-25T18:59:56Z")

</div>

It is not lossless if you save 32bit PNG into 8bit (color table)

---

<div class="post-metadata">

**Author:** ![ddbell](https://sea1.discourse-cdn.com/flex015/user_avatar/forums.creativeclouddeveloper.com/ddbell/32/1030_2.png) [@ddbell](https://forums.creativeclouddeveloper.com/u/ddbell)\
**Post date:** [February 25, 2022, 7:08pm UTC](https://forums.creativeclouddeveloper.com/t/render-layer-example/4268/20 "2022-02-25T19:08:16Z")

</div>

Ah, I see, that make sense now. The quality setting in this case is bit depth. So in that setting you would set that value to either 8 or 32 but nothing else. Quality 32 is actually RGBA 8 bit per channel. Quality 8 would be 8 bit PNG which is actually indexed like a GIF with 256 colors with 1 bit transparency, but not 8 bit per channel.
