createSolidFillLayer Extended

Discussion of the xtools Toolkit

Moderators:xbytor, DavideBarranca, Paul MR

Post Reply
SzopeN
createSolidFillLayer Extended

Post by SzopeN » Mon Jul 20, 2009 6:46 pm

In my code I often create new layer ( using Stdlib.createSolidFillLayer for example) then set its name and opacity through properties:
Code: Select allvar l = Stdlib.createSolidFillLayer();
l.name = 'foo'
l.opacity = 69;

When there is many (>10) layer, this operations tend to be slow. So I've extended createSolidFillLayer in order to speed up things a bit:

Code: Select allStdlib.layerColorsEnum = {none: 'None', red: 'Rd  ', orange: 'Orng', yellow: 'Ylw ', green: 'Grn ', blue: 'Bl  ', violet: 'Vlt ', gray: 'Gry '};
Stdlib.blendModesEnum = {passThrough: 'passThrough', normal: 'normal', dissolve: 'dissolve', darken: 'darken', multipl: 'multipl', colorBurn: 'colorBurn', linearBurn: 'linearBurn', darkerColor: 'darkerColor', lighten: 'lighten', screen: 'screen', colorDodge: 'colorDodge', linearDodge: 'linearDodge', lighterColor: 'lighterColor', overlay: 'overlay', softLight: 'softLight', hardLight: 'hardLight', vividLight: 'vividLight', linearLight: 'linearLight', pinLight: 'pinLight', hardMix: 'hardMix', difference: 'difference', exclusion: 'exclusion', hue: 'hue', saturation: 'saturation', color: 'color', luminosity: 'luminosity'};

/*
 * Creates a solid fill layer using low-level functions
 * [in*] name: String -- layer name, def: auto PS name
 * [in*] opacity: Integer from range [0,100] -- layer opacity, def: 100
 * [in*] color: enum Stdlib.layerColorsEnum -- color of layer (on layers panel), def: Stdlib.layerColorsEnum.none
 * [in*] blandMode: enum Stdlib.blendModesEnum -- blending mode of the layer, def: Stdlib.blendModesEnum.normal
 * Returns void
 * Remark: It is faster than standard API at least in CS4.
 * Remark: Do not pass an argument or pass undefined to get default value
 * Example: Stdlib.createLayerGroup('my group', 50, Stdlib.layerColorsEnum.red, Stdlib.blendModesEnum.passThrough);
 */
Stdlib.createSolidFillLayer = function(doc, color, name, opacity, layerColor, blendMode, clipToPrevious) {
  // in original createSolidFillLayer author forgot to add 'doc' argument ;)
  // add it w/o braking compatibility
  if(doc instanceof SolidColor) {
     color=doc; doc=app.activeDocument};
  if (!color) {
    color = Stdlib.createRGBColor(0, 0, 0);
  }
  function _ftn() {
    var desc = new ActionDescriptor();
    var clref = new ActionReference();
    clref.putClass(sTID('contentLayer'));
    desc.putReference(cTID('null'), clref);
    var tdesc = new ActionDescriptor();
   if (name) {
      tdesc.putString( cTID( "Nm  " ), name );
   }
   if (opacity) {
      tdesc.putUnitDouble( cTID( "Opct" ), cTID( "#Prc" ), opacity );
   }
   if (layerColor) {
      tdesc.putEnumerated( cTID( "Clr " ), cTID( "Clr " ), cTID( layerColor ) );
   }
   if (blendMode) {
      tdesc.putEnumerated( cTID( "Md  " ), cTID( "BlnM" ), cTID( blendMode ) );
   }
   if (clipToPrevious) {
      tdesc.putBoolean( cTID( "Grup" ), !!clipToPrevious );
   }

    var scldesc = new ActionDescriptor();
    var rgbdesc = new ActionDescriptor();
    rgbdesc.putDouble(cTID('Rd  '), color.rgb.red);
    rgbdesc.putDouble(cTID('Grn '), color.rgb.green);
    rgbdesc.putDouble(cTID('Bl  '), color.rgb.blue);
    scldesc.putObject(cTID('Clr '), cTID('RGBC'), rgbdesc);
    tdesc.putObject(cTID('Type'), sTID('solidColorLayer'), scldesc);
    desc.putObject(cTID('Usng'), sTID('contentLayer'), tdesc);
    executeAction(cTID('Mk  '), desc, DialogModes.NO);
  }
  Stdlib.wrapLC(doc, _ftn);
  return doc.activeLayer;
};

It can be used to replace original function or just-another-lib-function after adding 'Ex' suffix to its name. Some tests

Code: Select all// (7258418+7289173+7138296+10416049+10483223+10368110)/6=8825545
// Remark: number of layers DO affect efficiency
doc = activeDocument;
function testCaseAPI() {
   for (var i=1; i<=10; ++i) {
      var g = Stdlib.createSolidFillLayer(doc, Stdlib.createRGBColor(0,255,0));
      g.name = "Fill " + i;
      g.opacity = 10*i;
   }
}

// (5114606+4017009+4198933+3083992+3119524+2943980)/6=3746340
// Remark: number of layers do NOT affect efficiency
// Conclusion: 57,55% faster than AIP version
function testCaseStdlib() {
   for (var i=1; i<=10; ++i) {
      Stdlib.createSolidFillLayer_new(doc, Stdlib.createRGBColor(255,0,0), "Fill " + i,10*i);
      var g = doc.activeLayer;
   }
}

// first 3 tests
/*
testCaseAPI();
testCaseAPI();
testCaseAPI();
testCaseStdlib();
testCaseStdlib();
testCaseStdlib(); //*/

// next 3 tests
//*
testCaseStdlib();
testCaseStdlib();
testCaseStdlib();
testCaseAPI();
testCaseAPI();
testCaseAPI(); //*/


It would be nice to add 'create layer mask' argument, but I have no idea how to create layer with layer mask (not simply adding it to created layer).

Mike Hale

createSolidFillLayer Extended

Post by Mike Hale » Mon Jul 20, 2009 7:03 pm

SzopeN wrote:It would be nice to add 'create layer mask' argument, but I have no idea how to create layer with layer mask (not simply adding it to created layer).

If you have an active selection or path when you call the Action Manager function it will create a channel or vector mask when the solidfill layer is created.

Or you could use a function similar to the on below to add a channel mask to an existing layer

createLayerMask = function(){
var desc = new ActionDescriptor();
desc.putClass( charIDToTypeID( "Nw " ), charIDToTypeID( "Chnl" ) );
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID( "Chnl" ), charIDToTypeID( "Chnl" ), charIDToTypeID( "Msk " ) );
desc.putReference( charIDToTypeID( "At " ), ref );
desc.putEnumerated( charIDToTypeID( "Usng" ), charIDToTypeID( "UsrM" ), charIDToTypeID( "RvlA" ) );
executeAction( charIDToTypeID( "Mk " ), desc, DialogModes.NO );
};

SzopeN

createSolidFillLayer Extended

Post by SzopeN » Mon Jul 20, 2009 9:52 pm

Yes, that part I've figured out already. But the problem remains: how to CREATE layer with (or w/o) layer mask using additional entries in ActionDescriptor. Naturally I can make a full-canvas selection before creating fill layer, but it is (at least) one operation more. On the other hand, when 'use default masks on fill layers' is enabled, deleting mask after layer creation is the only way I know for creating bare fill layer.
In other words the problems are:

How (and whether it is possible) to:
create fill layer w/ user mask ('use default masks on fill layers' disabled) create fill layer w/o user mask ('use default masks on fill layers' enabled) create fill layer w/ or w/o mask regardless selection (hide/reveal all) create fill layer with mask mode 'hide selection'
using only one call to executeAction. Of course, there are simple solution of this problems using 'create and then do sth.' method. I'm pretty sure, there is no one-step way of doing that, but I'm still searching...

Mike Hale

createSolidFillLayer Extended

Post by Mike Hale » Tue Jul 21, 2009 1:46 am

SzopeN wrote:I'm pretty sure, there is no one-step way of doing that, but I'm still searching...

I don't think there is a one-step way either. Unlike the layer properties that you added to the layer creation above adding a mask would require making a channel or path. I don't see how that could be done with one executeAction.

The channel or path is not part of the layer's descriptor so I don't think that you could even add an existing channel or path.

Post Reply