Released a new Mac app: Wallpaper by Behance

Released a new Mac app: Wallpaper by Behance


Lee and I, together with the Behance folks, just finished and published a new Mac app.

Wallpaper by Behance let’s you discover the worlds creative work through your desktop.

Each wallpaper has an underlying project on Behance, so you can see the whole story – how it was created or additional artwork/photos from the same series.

You can login with your Adobe ID to follow creatives and appreciate projects directly from within the app.

See the Behance project

Download

*Wallpaper by Behance is a native desktop Mac app written in Objective-C

Adobe Generator: Return an ExtendScript (JSX) value from Photoshop

Adobe Generator: Return an ExtendScript (JSX) value from Photoshop


Often you want to call Photoshop with a JSX and return a value back to Generator. It can be as simple as getting an active document width.

In order to do that, you can create JSX string/command that you invoke on Photoshop, you can be setting anything inside Photoshop (like background and foreground colors, etc), now if you want to return back any data, you just put them in the last line.

This example returns back width and height in pixels as a string, e.g.: “1000px,2000px”. You can then split it into an array with a comma (,) delimiter and parse integers out of it to get width and height values. Setting app.preferences.rulerUnits will force Photoshop to use units of your choice, often people use points in Photoshop, but your app works only with pixels, so it’s good to return the value in Units.PIXELS.

function getActiveDocumentSize(){
    console.log("getActiveDocumentSize");
 
    var str = 'app.preferences.rulerUnits = Units.PIXELS;'+
              'app.activeDocument.width+","+app.activeDocument.height;';
 
    _generator.evaluateJSXString(str).then(
        function(result){
            // get width and height
            var obj = result.split(",");
            var width = parseInt(obj[0]);
            var height = parseInt(obj[1]);
 
            console.log("width: "+width+", height:"+height);
        },
        function(err){
            console.log(err);
        });
}
Join us for CreateNow in North America

Join us for CreateNow in North America


Adobe evangelists are coming to USA and Canada over the upcoming weeks and will be showing all the latest cool stuff in Photoshop, Illustrator, InDesign, Premiere Pro, After Effects, Muse and other Creative Cloud tools.

Cities scheduled:
San Francisco, Boston, Portland, Denver, Toronto, Chicago, Los Angeles, Washington, Atlanta, Seattle

Check out the schedule and get registered.

I am sure it will be a lot of fun, so if you are a creative professional, a student or just want to dig into the Creative Cloud tools, master or polish skills in designing, editing videos, adjusting photos, making digital magazines or simply stay up to date and learn new stuff, this is the place to go.

And the best of all, the admission is free.

Adobe Generator: Save a layer bitmap as JPG, PNG or GIF programatically with settings


generator_appicon_256In the previous tutorial I’ve shown you how to save a layer bitmap into a PNG using pngjs node.js library so you understand the basics of working with pixels if you want to have more controls. But sometimes, you just want to easily spit out the bitmap.For that you can use Generator’s native function and save into GIF, PNG, JPG with numerous settings like quality and ppi.


Generator has a function called savePixmap that takes a pixmap, path and settings. In order to save a full quality JPEG, you will just pass quality 100. For PNG you can set the quality to 8 (PNG-8) or 24 (PNG-24 with alpha).

function getFirstLayerBitmapAndSaveWithOptions(document){
    _document = document;
 
    console.log(_document.id,_document.layers[0].id);
    _generator.getPixmap(_document.id,_document.layers[0].id,{}).then(
    function(pixmap){
        console.log("got Pixmap: "+pixmap.width+" x "+pixmap.height);
        _generator.savePixmap(pixmap,path.resolve(__dirname, 'out.png'),{format:"jpg",quality:100,ppi:72});
    },
    function(err){
        console.error("err pixmap:",err);
    }).done();
}

This will generate a png from the first layer of currently opened document and save it into your plugin’s root folder as out.png.

Adobe Generator: Getting a layer bitmap

Adobe Generator: Getting a layer bitmap


Now you understand the Adobe Generator scripting basics: Script Your First Adobe Generator Plugin For Photoshop, and it’s time to move on to something real.

In this tutorial, you will learn how to receive bitmaps from Photoshop and process them.

The complete script for this tutorial can be found here: generator-bitmaps repository on GitHub.

Getting a layer bitmap

Generator has a function getPixmap(documentId,layerId,settingsObject) that gets you a defined layer bitmap of a defined document. If you have more documents open, you can still get bitmaps even from documents that are not currently in front (selected).

By calling this function, you get a pixmap object that contains data such as width, height, channelCount (how many channels per pixel, usually 4: ARGB), pixels (an actual buffer of pixels), length (the length of the pixels buffer).

Now, because Photoshop returns pixels in ARGB space, you need to convert the pixels buffer into RGBA space using a simple loop that reorganizes pixel values.

The example below gets the first layer in the document and saves it as a PNG.

Request a pixmap:

_generator.getPixmap(_document.id,_document.layers[0].id,{}).then(
function(pixmap){
    savePixmap(pixmap)
},
function(err){
    console.error("err pixmap:",err);
}).done();

You can put the function above into requestEntireDocument that calls _generator.getDocumentInfo and gives you a document instance.

Save a pixmap:

function savePixmap(pixmap){
    var pixels = pixmap.pixels;
    var len = pixels.length,
        channels = pixmap.channelCount;
 
    // convert from ARGB to RGBA, we do this every 4 pixel values (channelCount)
    for(var i=0;i<len;i+=channels){
        var a = pixels[i];
        pixels[i]   = pixels[i+1];
        pixels[i+1] = pixels[i+2];
        pixels[i+2] = pixels[i+3];
        pixels[i+3] = a;
    }
 
    // init a new PNG
    var png = new PNG({
        width: pixmap.width,
        height: pixmap.height
    });
 
    // set pixel data
    png.data = pixmap.pixels;
    // write to a file (will write out.png to the same directory as this *.js file
    png.pack().pipe(fs.createWriteStream(path.resolve(__dirname, 'out.png')));
}

In order to save the PNG, this example uses pngjs Node.js library. In your plugin root (generator-bitmaps folder), simply run from Terminal (Mac) or Command line (Win):
npm install pngjs
This will download pngjs packages and you will able to use them in your project via:

var PNG = require('pngjs').PNG;

Checkout the next tutorial on how to use savePixmap function in Generator without using pngjs.

Script your first Adobe Generator plugin for Photoshop

Script your first Adobe Generator plugin for Photoshop


Tutorial GitHub repository.

Introduction

Today we have released an update to Photoshop CC that includes Adobe Generator. Read the announcement here.

Adobe Generator is a Node.js based server plugged into Photoshop via Kevlar API (ExtendScript). Generator is also open-sourced on GitHub.

This extends Photoshop scripting layers to three complementary options:

  • ExtendScript (write and run *.jsx scripts directly from File -> Scripts)
  • Photoshop Server (remote connection to Photoshop over TCP/IP, e.g. control Photoshop from iPhone/Android or other apps on the same machine)
  • Generator

Each one of them have a bit different use-case, however ExtendScript is still used in the end for invoking commands on Photoshop. Check out ExtendScript scripting guide.

Relationship between Photoshop scripting layers

Adobe_Photoshop_Generator_Scheme2

Primary use-case of Generator is to generate data or connect to resources from inside Photoshop, which is the OUTGOING direction.

On the other hand with Photoshop Server, you are connecting to Photoshop, which is the INCOMING direction and that requires a password set by a Photoshop user.

(Practically you could setup a socket-server (like a Node.js web-server) in Generator and allow INCOMING connections as well for a specific use-case to enable other apps or devices to connect to Photoshop, but that’s more of a secondary use-case.)

Your First Generator Plugin

Adobe Generator comes with Photoshop CC (version 14.1+), but for the development it’s better to get it from GitHub and launch it separately.

Step 0: Install Node.js

Go to Nodejs.org, download and install Node.js. Photoshop comes with Node.js version v0.10.15 (node –version will retrieve what version you use, if you install a new version of node, it will override the old one).

Step 1: Download The Generator-Core Project

Go to https://github.com/adobe-photoshop/generator-core and click Download ZIP or Clone in Desktop.

Open Terminal.app (on Mac) or Command Line (on Windows), navigate to generator-core-master folder that you have just downloaded and type npm install. If you don’t have NPM (Node Packaged Modules), you need to install Node.js (Step 0).

NPM Install Adobe Generator

Step 2: Setup Photoshop for Remote Connections

Open Photoshop, go to Photoshop -> Preferences -> Plug-ins and click Enable Remote Connections and set password to “password” string. (Note: Even-though deployed Adobe Generator lies right inside Photoshop, for development it’s preferred to use remotely sitting Generator.)

Photoshop Preferences

Step 3: Download The Getting started project

Download Generator Getting Started Plugin
Unpack and place it next to generator-master into plugins folder like this:
Screen Shot 2013-09-09 at 3.05.03 PM

View the complete main.js file.

To run Generator, type in Terminal from within the generator-master folder:

node app -f ../plugins
(which essentially means, run node with app.js file with -f parameter (plugins folder).

Step 4: Calling functions on Photoshop

The most important file here is main.js. Open it and locate the init function.
It shows three things, how to invoke ExtendScript in Photoshop via evaluateJSXString, retrieve an entire document structure in JSON and listen for changes in document.

function init(generator, config) {
    _generator = generator;
    _config = config;
 
    function initLater() {
        // Set data in Photoshop using ExtendScript via _generator.evaluateJSXString
        // Flip foreground color
        var flipColorsExtendScript = "var color = app.foregroundColor; color.rgb.red = 255 - color.rgb.red; color.rgb.green = 255 - color.rgb.green; color.rgb.blue = 255 - color.rgb.blue; app.foregroundColor = color;";
        sendJavascript(flipColorsExtendScript);
 
        // Get data from Photoshop via _generator.getDocumentInfo
        requestEntireDocument();
 
        // Register events on Photoshop
        _generator.onPhotoshopEvent("currentDocumentChanged", handleCurrentDocumentChanged);
        _generator.onPhotoshopEvent("imageChanged", handleImageChanged);
        _generator.onPhotoshopEvent("toolChanged", handleToolChanged);
    }
 
    process.nextTick(initLater);
}

Most of these features are actually implemented via evaluateJSXString on Photoshop at the end. The event registrations and the calls just invoke JSX and retrieve data via promises. Generator makes it easy for you and hides all that logic behind much a nicer API in Javascript.

Step 5: Requesting the document structure as a JSON

Let’s dig into requestEntireDocument.
This function retrieves the document by calling _generator.getDocumentInfo that uses sendDocumentInfoToNetworkClient to retrieve a document structure in JSON (read about the structure here).

function requestEntireDocument(documentId) {
    if (!documentId) {
        console.log("Determining the current document ID");
    }
 
    _generator.getDocumentInfo(documentId).then(
        function (document) {
            console.log("Received complete document:", stringify(document));
        },
        function (err) {
            console.error("[Tutorial] Error in getDocumentInfo:", err);
        }
    ).done();
}

Step 6: Photoshop Events

To register an event, call _generator.onPhotoshopEvent with an event name and handler.

We have registered three events in the init function: currentDocumentChanged (when we switch between the documents), imageChanged (when we change something in the image), toolChanged (when we pick a different tool).

Now we will just add handlers:
(uncomment and add stringify(document) if you want to see the new document structure)

function handleCurrentDocumentChanged(id) {
    console.log("handleCurrentDocumentChanged: "+id)
    setCurrentDocumentId(id);
}
 
function handleImageChanged(document) {
    console.log("Image " + document.id + " was changed:");//, stringify(document));
}
 
function handleToolChanged(document){
    console.log("Tool changed " + document.id + " was changed:");//, stringify(document));
}

Step 7: Photoshop Menu and Events

Now we need to add our own item in the File->Generate Menu in Photoshop.
Photoshop Menu Changed

You can see the pre-installed Generator plugins separated from ad-hoc development plugins. If you want to be pre-installed, you need to deploy your plugin into Applications/Adobe Photoshop CC/Plug-ins/Generator.

Now, in order to register your own menu item, call _generator.addMenuItem in the init function and register generatorMenuChanged event.

_generator.addMenuItem(MENU_ID, MENU_LABEL, true, false).then(
    function () {
        console.log("Menu created", MENU_ID);
    }, function () {
        console.error("Menu creation failed", MENU_ID);
    }
);
_generator.onPhotoshopEvent("generatorMenuChanged", handleGeneratorMenuClicked);

Where MENU_ID and MENU_LABEL are predefined strings:

MENU_ID = "tutorial",
MENU_LABEL = "$$$/JavaScripts/Generator/Tutorial/Menu=Tutorial",

Now when the user clicks on the menu item, we can determine the menu state using _generator.getMenuState:

function handleGeneratorMenuClicked(event) {
    // Ignore changes to other menus
    var menu = event.generatorMenuChanged;
    if (!menu || menu.name !== MENU_ID) {
        return;
    }
 
    var startingMenuState = _generator.getMenuState(menu.name);
    console.log("Menu event %s, starting state %s", stringify(event), stringify(startingMenuState));
}

Finally you can use _generator.toggleMenu to display the check sign.

function updateMenuState(enabled) {
    console.log("Setting menu state to", enabled);
    _generator.toggleMenu(MENU_ID, true, enabled);
}

Step 8: Retrieving a bitmap from Photoshop

The last step is to retrieve and process a bitmap that we get in the image changed event.

For this, please go to the next tutorial: Adobe Generator: Getting a layer bitmap

Video tutorial

Check out the getting started video tutorial about Adobe Generator by Lee Brimelow covering things covered in this article.

Add a color to swatches panel in Photoshop using ExtendScript/JSX


A little *.jsx script (ExtendScript) that shows how to add a color to swatches panel in Photoshop.

var red = 255;
var green = 0;
var blue = 0;
var name = "Red";
//
var addColorDescriptor = new ActionDescriptor();
// Get reference to Swatches panel
var swatchesPanelReference = new ActionReference();
swatchesPanelReference.putClass(stringIDToTypeID('colors'));
addColorDescriptor.putReference(stringIDToTypeID('null'), swatchesPanelReference);
// Setup a swatch and give it a name
var descriptorSwatch = new ActionDescriptor();
descriptorSwatch.putString( stringIDToTypeID('name'), name);
// Add RGB color information to the swatch
var descriptorColor = new ActionDescriptor();
descriptorColor.putDouble(stringIDToTypeID('red'), red);
descriptorColor.putDouble(stringIDToTypeID('grain'), green); // grain = green
descriptorColor.putDouble(stringIDToTypeID('blue'), blue);
// Add RGB to the swatch
descriptorSwatch.putObject( stringIDToTypeID('color'), stringIDToTypeID('RGBColor'), descriptorColor);
// Add swatch to the color descriptor
addColorDescriptor.putObject( stringIDToTypeID('using'), stringIDToTypeID('colors'), descriptorSwatch);
// Send to Photoshop
executeAction( stringIDToTypeID('make'), addColorDescriptor, DialogModes.NO);
Grab CSS from Photoshop files visually in Brackets

Grab CSS from Photoshop files visually in Brackets


NJ sneaked at Adobe MAX 2013 an extension for Brackets that extracts CSS styles from PSDs. In the lower panel you can see a PSD; in the upper panel you can see the CSS code. This code is grabbed via auto-complete from the PSD below. Brackets just grabs the styles for a selected object on the PSD, including position, text format, gradients, colors, and more.

Watch the video:

PSD Lens

Photoshop export to Edge Reflow at Adobe MAX keynote

Photoshop export to Edge Reflow at Adobe MAX keynote


If you’ve missed Photoshop to Edge Reflow export at Adobe MAX, definitely check it out. It exports the PSD structure including the text content and the formatting, the layer styles and the bitmaps that are composed into a CSS and *.rflw document, which can be later on exported into HTML for a preview from Edge Reflow.

Here is the direct URL (30:15 min) to the ‘Photoshop to Edge Reflow’ part from the MAX keynote.

Photoshop to Edge Reflow

Sneak peek: Project Generator: Live assets generation in Photoshop

Sneak peek: Project Generator: Live assets generation in Photoshop


Photoshop team at Adobe is working on a new feature code-named Project Generator. This is a little sneak peek of what it does and how it helps you to save the time when designing user interfaces and preparing graphical assets for production or development. Together with that it rapidly simplifies the designer/developer workflow within Creative Cloud.

Web Assets generation process is defined using a naming convention of layers and groups. If you add suffix .jpg .png .gif or .svg (yes, shapes) to the layer name, Generator will understand it as an asset to export. You can also export out file in different scale by setting @2x, @1.5x or similar to the layer name.

Generate

Project Generator is a fully programmable and extensible pipeline based on Node.js allowing you to completely customize the output using JavaScript. Together with the assets you can get the structure as well and process it into different format e.g. HTML/CSS.

Stay tuned for more and let us know what you think, ping me and @timriot.

Note: this is a sneak peek and the final workflow can differ

ToTop