15 Replies Latest reply on Nov 25, 2012 2:38 AM by c.pfaffenbichler

    Using color sampler values with curves adjustment layer

    Jbean25

      Hi, I have just started trying to teach myself javascript.  Based on preliminary research, it's become apparent that there are many functions in Photoshop that are either extremely cumbersome or impossible to code by hand without using the Script Listener.

       

      My  goal is as follows: first, I will manually load two photos as layers in a single document.  Then I will manually place two or more color sampler points on the document.  At this point I would like the script to create a curves adjustment layer (ideally clipped to layer 2) and place as individual channel anchor points  the RGB data from the color sampler points on Layer 2, and then adjust the output of the points on each channel to the color sampler RGB values of layer 1.  

       

      As my first script, I realize this is probably going to be a lot of work.

      I did find some code that returns the average value of manually placed color sampler points.  Conceptually then, I would need to add code which creates a new curves adjustment layer and adds those RGB values (from a specific layer)  as anchor points on the individual channels,  and then hides one layer and looks at the RGB values of the color sampler points, and uses them as the output values for each anchor point.

       

      Sounds simple enough from a conceptual standpoint.

      I'm looking for some guidance on how to get started.

      Which parts will I definitely need Scriptlistener for and will that be adequate to do the job?

      How would you recommend I get started on this?

       

      Thanks very much for any input.

        • 1. Re: Using color sampler values with curves adjustment layer
          c.pfaffenbichler Level 9

          it's become apparent that there are many functions in Photoshop that are either extremely cumbersome or impossible to code by hand without using the Script Listener.

          Actually you may not need to use ScriptingListener.plugin yourself if you can find the code elsewhere but the

          Document Object Model

          will indeed not suffice for the task at hand (as far as I can tell) and you will have to use

          Action Manager code.

           

          DOM code may look neater and be more easy to read but it has drawbacks – that not all functions can be controlled thus being the probably most significant one.

           

          I guess you need to record the creation of a Curves Layer with the appropriate number of points for the appropriate number of Channels and feed the numbers you collect from the ColorSamplers for the two Layers into that.

          As you are vague about the actual number of ColorSamplers the task becomes more complicated and you may have to utilize a for-clause and a function for creating the points.

           

          In my opinion this task is a problematic choice for a »first script«.

          • 2. Re: Using color sampler values with curves adjustment layer
            Michael L Hale Level 5

            You should be able to get an average color from the colorSampler itself without any additional code as long as the area you want to average is one of the sizes in the sampler tool options. If the layers overlap you will need to hide/show the top layer to get the color from both layers as colorSampler reads the composite image.

             

            Because you are doing most of the steps manually I don't think it would be too hard to script. I think an outline of the script would look something like this.

            1. hide the top layer - DOM

            2. read the color from the samplers and store the solidColor objects - DOM

            3. show the top layer - DOM

            4. read the colors again and store those - DOM

            5. create a curve adjustment layer - Action Manager(scriptlistener)

            6. move the adjustment layer to the top(if needed) and create clipping group - DOM

            7. edit the adjustment layer using the stored color values - Action Manager

             

            The hard part will be step 7. Especially if you want to have a flexible number of colorSampler/curve points. But with a little effort and maybe some help from this forum I think you can come up with a script that does what you want.

            • 3. Re: Using color sampler values with curves adjustment layer
              Jbean25 Level 1

              Thank you both for your thoughts.  I see Action Manager = ScriptListener, which is good to know.  I will be working almost always with two color sampler points, so based on what you've both indicated, that could simplify things.  Is the Adobe Scripting Guide the best place to go to find the necessary commands to do steps 1-4, and 6?

              In step 7, if I use the action Manager, how do I indicate I want to recall the stored color sampler values?  I could be (and am probably) way off here, but I thought Action manager records scripting code based on what happens on the screen, so if the color sampler points are stored as variables in the script, it seems like I wouldn't be able to access them in the Photoshop interface to record with Action Manager. I.E. Action Manager would record the numerical value of the points that I move in the curves without tying them to the color sampler variables.

               

              Conceptually it seems like it should be easy to write code that sets the color sampler points as variables, then calls the channels of the curves layer and sets the input and output numbers to the desired variables. 

               

              Thanks again for giving me some guidance.

              • 4. Re: Using color sampler values with curves adjustment layer
                Michael L Hale Level 5

                I think that I was somewhat miss-leading. The scriptlistener plug-in outputs Action Manger code but that is not the same as it being equal. Yes, scirptlistener only records the settings used for that recorded acton. Sometimes you can use the code straight from the scriptlistener log but most of the time you will want to edit it so it uses variables instead of the hard coded settings.

                 

                I have been thinking about how this script would work. For one thing it would be limited to 4 points on the curve because of the ColorSampler limit of four samples. The ActionDescriptor for a curve expects the points to be in order so you would either need to place the colorSamplers in order i.e. the first sampler is for the black point of the curve or come up with a way to determine what sample is to be used for each curve point. Creating the curve descriptor will be the hardest part of writing a script to do this task.

                 

                As for the rest, yes, it is easy. I would do something like this...

                 

                // create a function fo hold most of the code
                function createCurveAdjustmetFromColorSamplers(){
                    // first add some condition checks
                    // needs an open document in a color mode that supports layers
                    if(app.documents.length == 0 || ( app.activeDocument.mode == DocumentMode.BITMAP || app.activeDocument.mode == DocumentMode.INDEXEDCOLOR ) ){    
                        alert('This script requires a document in Greyscale, RGB, CMYK, or Lab mode.');
                        return;
                    }
                    // check for at least two colorSamplers
                    if(app.activeDocument.colorSamplers.length < 2 ){
                        alert('This script requires at least two colorSamplers.');
                        return;
                    }
                    // last check for at least two layers - assume they will be on same level( not in layerSet )
                    if(app.activeDocument.layers.length < 2 ){
                        alert('This script requires at least two layers.');
                        return;
                    }
                    // create varaibles to hold the colorSampler's color property for each layer
                    // for the bottom layer
                    var outputArray = new Array();
                    // for top layer - array could also be created this way
                    var inputArray = [];
                    // store the number of samples because it will be needed in more than one place
                    var numberOfSamples = app.activeDocument.colorSamplers.length;
                    
                    // hide the top layer
                    app.activeDocument.layers[0].visible = false;
                    // collect the samples from the bottom layer
                    for(var sampleIndex = 0; sampleIndex < numberOfSamples; sampleIndex++ ){
                        outputArray.push(app.activeDocument.colorSamplers[sampleIndex].color);
                    }
                    // turn the top layer back on
                    app.activeDocument.layers[0].visible = true;
                    // collect those samples
                    for(var sampleIndex = 0; sampleIndex < numberOfSamples; sampleIndex++ ){
                        inputArray.push(app.activeDocument.colorSamplers[sampleIndex].color);
                    }
                    // make sure the top layer is the activeLayer
                    app.activeDocument.activeLayer = app.activeDocument.layers[0];
                    // use Action Manager code to create new curve adjustment layer
                    makeCurveAdjustmentLayer();
                }
                function makeCurveAdjustmentLayer(){//works with any mode
                   var desc = new ActionDescriptor();
                   var ref = new ActionReference();
                     ref.putClass( charIDToTypeID( "AdjL" ) );
                   desc.putReference( charIDToTypeID( "null" ), ref );
                   var desc1 = new ActionDescriptor();
                     desc1.putClass( charIDToTypeID( "Type" ), charIDToTypeID("Crvs" ) );
                   desc.putObject( charIDToTypeID( "Usng" ), charIDToTypeID( "AdjL" ), desc1 );
                   executeAction( charIDToTypeID( "Mk  " ), desc, DialogModes.NO );
                }
                // call the function to run the script
                createCurveAdjustmetFromColorSamplers();
                
                • 5. Re: Using color sampler values with curves adjustment layer
                  Jbean25 Level 1

                  Thank you Michael! That's very helpful.  I will look over the code and do some more research and come back when I have more specific questions. 

                  • 6. Re: Using color sampler values with curves adjustment layer
                    c.pfaffenbichler Level 9

                    Another possible issue might be if two sampled points have identical source values but different target values.

                    • 7. Re: Using color sampler values with curves adjustment layer
                      c.pfaffenbichler Level 9

                      The following function can be used to create RGB Curves Layers with varying numbers of points.

                      The Arrays for the source- and target values for the RGB-Channels are just listed in an Array themselves, so the first and second (red), third and fourth (green), fifth and sixth (blue) arrays should have identical numbers of elements.

                       

                      And like I pointed out in the last post the source numbers in one Array should be different, I guess.

                       

                      #target photoshop
                      var theArray = [[0, 128, 255], [0, 150, 255], [0, 128, 255], [0, 90, 255], [0, 75, 230, 255], [0, 30, 180, 255]];
                      rgbCurvesLayer (theArray);
                      ////// make rgb curves layer //////
                      function rgbCurvesLayer (theArray) {
                      // =======================================================
                      var idMk = charIDToTypeID( "Mk  " );
                          var desc5 = new ActionDescriptor();
                          var idnull = charIDToTypeID( "null" );
                              var ref2 = new ActionReference();
                              var idAdjL = charIDToTypeID( "AdjL" );
                              ref2.putClass( idAdjL );
                          desc5.putReference( idnull, ref2 );
                          var idUsng = charIDToTypeID( "Usng" );
                              var desc6 = new ActionDescriptor();
                              var idType = charIDToTypeID( "Type" );
                                  var desc7 = new ActionDescriptor();
                                  var idpresetKind = stringIDToTypeID( "presetKind" );
                                  var idpresetKindType = stringIDToTypeID( "presetKindType" );
                                  var idpresetKindDefault = stringIDToTypeID( "presetKindDefault" );
                                  desc7.putEnumerated( idpresetKind, idpresetKindType, idpresetKindDefault );
                              var idCrvs = charIDToTypeID( "Crvs" );
                              desc6.putObject( idType, idCrvs, desc7 );
                          var idAdjL = charIDToTypeID( "AdjL" );
                          desc5.putObject( idUsng, idAdjL, desc6 );
                      executeAction( idMk, desc5, DialogModes.NO );
                      // =======================================================
                      var idsetd = charIDToTypeID( "setd" );
                          var desc8 = new ActionDescriptor();
                          var idnull = charIDToTypeID( "null" );
                              var ref3 = new ActionReference();
                              var idAdjL = charIDToTypeID( "AdjL" );
                              var idOrdn = charIDToTypeID( "Ordn" );
                              var idTrgt = charIDToTypeID( "Trgt" );
                              ref3.putEnumerated( idAdjL, idOrdn, idTrgt );
                          desc8.putReference( idnull, ref3 );
                          var idT = charIDToTypeID( "T   " );
                              var desc9 = new ActionDescriptor();
                              var idpresetKind = stringIDToTypeID( "presetKind" );
                              var idpresetKindType = stringIDToTypeID( "presetKindType" );
                              var idpresetKindCustom = stringIDToTypeID( "presetKindCustom" );
                              desc9.putEnumerated( idpresetKind, idpresetKindType, idpresetKindCustom );
                              var idAdjs = charIDToTypeID( "Adjs" );
                                  var list1 = new ActionList();
                                      var desc10 = new ActionDescriptor();
                                      var idChnl = charIDToTypeID( "Chnl" );
                                          var ref4 = new ActionReference();
                                          var idChnl = charIDToTypeID( "Chnl" );
                                          var idChnl = charIDToTypeID( "Chnl" );
                                          var idRd = charIDToTypeID( "Rd  " );
                                          ref4.putEnumerated( idChnl, idChnl, idRd );
                                      desc10.putReference( idChnl, ref4 );
                                      var idCrv = charIDToTypeID( "Crv " );
                                          var list2 = new ActionList();
                      // add r points;
                      for (var m = 0; m < theArray[0].length; m++) {
                                addCurvePoint (list2, theArray[0][m], theArray[1][m])
                                }; 
                                      desc10.putList( idCrv, list2 );
                                  var idCrvA = charIDToTypeID( "CrvA" );
                                  list1.putObject( idCrvA, desc10 );
                                      var desc15 = new ActionDescriptor();
                                      var idChnl = charIDToTypeID( "Chnl" );
                                          var ref5 = new ActionReference();
                                          var idChnl = charIDToTypeID( "Chnl" );
                                          var idChnl = charIDToTypeID( "Chnl" );
                                          var idGrn = charIDToTypeID( "Grn " );
                                          ref5.putEnumerated( idChnl, idChnl, idGrn );
                                      desc15.putReference( idChnl, ref5 );
                                      var idCrv = charIDToTypeID( "Crv " );
                                          var list3 = new ActionList();
                      // add g points;
                      for (var m = 0; m < theArray[2].length; m++) {
                                addCurvePoint (list3, theArray[2][m], theArray[3][m])
                                };
                                      desc15.putList( idCrv, list3 );
                                  var idCrvA = charIDToTypeID( "CrvA" );
                                  list1.putObject( idCrvA, desc15 );
                                      var desc20 = new ActionDescriptor();
                                      var idChnl = charIDToTypeID( "Chnl" );
                                          var ref6 = new ActionReference();
                                          var idChnl = charIDToTypeID( "Chnl" );
                                          var idChnl = charIDToTypeID( "Chnl" );
                                          var idBl = charIDToTypeID( "Bl  " );
                                          ref6.putEnumerated( idChnl, idChnl, idBl );
                                      desc20.putReference( idChnl, ref6 );
                                      var idCrv = charIDToTypeID( "Crv " );
                                          var list4 = new ActionList();
                      // add b points;
                      for (var m = 0; m < theArray[4].length; m++) {
                                addCurvePoint (list4, theArray[4][m], theArray[5][m])
                                };
                                      desc20.putList( idCrv, list4 );
                                  var idCrvA = charIDToTypeID( "CrvA" );
                                  list1.putObject( idCrvA, desc20 );
                              desc9.putList( idAdjs, list1 );
                          var idCrvs = charIDToTypeID( "Crvs" );
                          desc8.putObject( idT, idCrvs, desc9 );
                      executeAction( idsetd, desc8, DialogModes.NO );
                      //
                      return app.activeDocument.activeLayer;
                      ////// add curve point //////
                      function addCurvePoint (theList, valueHor, valueVer) {
                      var desc11 = new ActionDescriptor();
                      var idHrzn = charIDToTypeID( "Hrzn" );
                      desc11.putDouble( idHrzn, valueHor );
                      var idVrtc = charIDToTypeID( "Vrtc" );
                      desc11.putDouble( idVrtc, valueVer );
                      var idPnt = charIDToTypeID( "Pnt " );
                      theList.putObject( idPnt, desc11 );
                      };
                      };
                      
                      • 8. Re: Using color sampler values with curves adjustment layer
                        c.pfaffenbichler Level 9

                        Creating the curve descriptor will be the hardest part of writing a script to do this task.

                        I think getting the color values in order and checking if 0 or 255 have been picked or if they should be added to the ColorSamplers’ values and making sure no identical input values appear for any channel may be the tougher part.

                         

                        Jbean25, could you post a pair of images (with the ColorSamplers) to show what a likely combination of actual images may be?

                        • 9. Re: Using color sampler values with curves adjustment layer
                          Michael L Hale Level 5

                          I think getting the color values in order and checking if 0 or 255 have been picked or if they should be added to the ColorSamplers’ values and making sure no identical input values appear for any channel may be the tougher part.

                          I guess I was thinking of creating an edit curve adjustment that could be used to create any curve adjustment layer in any color mode. A general function that could be used by any scripts.

                           

                          But yes, all of the points you listed need to be addressed as part of creating the arguments to what ever function is used.

                          • 10. Re: Using color sampler values with curves adjustment layer
                            Jbean25 Level 1

                            Here are a couple photos.  The color sampler points did not show up in the photos when inserted, so I've marked them on the top photo.  The goal is ultimately to blend the sky of the darker photo in the brighter exposure.  And the best way to do this is actually to use the color sampler points from both layers and take the average of them, and set the average  as the output (not swap the values as I indicated in my initial post). I didn't think this extra step of figuring out the average would require much effort once I figured out how to do the heavy lifting, so please excuse me if that confused things. For the photos below,  I would load the photos as layers.  Then using the sampler point on the left, I would look at the RGB values of it and then hide that layer and look at the RGB sampler values of the same point on the photo below it.  Then I would take the average of the R value for the top and bottom and use as the output for the curve layers I create, one for each photo.  (the input is obtained by shift+clicking on the color sampler on each layer).  I would repeat the process for the right most color sampler point, so that each curve now has two anchor points on each RGB channel.

                            That sounds long winded, but I hope it makes sense. And again, I apologize for any inconvenience.

                             

                             

                             

                            DSC_0036 dnsz to pst adobe forum J.jpg0037 dnsz to pst adobe forum j.jpg

                            • 11. Re: Using color sampler values with curves adjustment layer
                              c.pfaffenbichler Level 9

                              I guess I was thinking of creating an edit curve adjustment that could be used to create any curve adjustment layer in any color mode.

                              You are right (edit: a Curves Layer function for all elligible color modes seems demanding).

                              I just assumed RGB would be the natural choice – which of course it need not be.

                              • 12. Re: Using color sampler values with curves adjustment layer
                                c.pfaffenbichler Level 9

                                The markings were unneccesary (and disruptive) as the Color Pickers were saved with the image.

                                Could you show what result you intend?

                                 

                                As the images seem to be RGB anyway, have you tried combining Michael’s code with the function I posted?

                                • 13. Re: Using color sampler values with curves adjustment layer
                                  Jbean25 Level 1

                                  Hi c.pfaffenbichler,

                                  I have had some computer issues the last few days and am just now able to work on this in earnest. 

                                  First, I tried to run the  Michael's code in the script editor, but it returned an error. I believe this is because of the conditions set, which required layers to be active, etc.  I'm nost sure then, what use the script editor will be in this particular case.  Next I then ran it in CS5, and separately ran your code in CS5 as well. 

                                  Michael's code created a new curves layer, and if I understand it correctly, set the groundwork for me to be able to add in curves points using the color sampler variables he created. 

                                   

                                  Your code returned a curves layer with anchor points in the R,G,B channels, the input and output values are not entirely clear to me upon trying to parse the code.  The B channel for some reason was assigned two anchor points with output values. The net effect of the curves adjustment was to make the photo much more red in appearance.  I realize I need to learn a lot more code to really understand these scripts that have been writtenn

                                   

                                  I'm reattaching the photos with a 3rd, which is the result of the adjustment.  I have removed the painted circles around the color sampler points.  The top most photo below is the adjusted photo, the second is the darker sky exposure and the lowest photo is a brighter exposure for the hills and river. The brightness of the sky in the  top photo is kind of mid-way between the brighter and darker photos.  The main thing is that it, and the background hills retains detail, which can be adjusted later in photoshop. DSC_0036 blnd jw tech_no adj_ dnsz850 adobe forum j.jpg0037 dnsz to pst adobe forum j.jpgDSC_0036 dnsz to pst adobe forum J_clr samplers.jpg

                                  • 14. Re: Using color sampler values with curves adjustment layer
                                    Jbean25 Level 1

                                    I should note that masks were created to reveal the adjustment selectively.

                                    Also, thank you very much for your help and the time you  have spent on this.

                                    • 15. Re: Using color sampler values with curves adjustment layer
                                      c.pfaffenbichler Level 9

                                      The function I had provided was an example into which you would need to feed the values you got with Mike’s code.

                                      The code below would create a Curves Layer as shown in the screenshot, but I’m not sure it would work reasonably for all cases.

                                      curvesColorPickerScr.jpg

                                      // with code by mike hale;
                                      // 2012, use it at your own risk;
                                      // call the function to run the script
                                      #target photoshop
                                      createCurveAdjustmetFromColorSamplers();
                                      // create a function fo hold most of the code
                                      function createCurveAdjustmetFromColorSamplers(){
                                          // first add some condition checks
                                          // needs an open document in a color mode that supports layers
                                          if(app.documents.length == 0 || ( app.activeDocument.mode == DocumentMode.BITMAP || app.activeDocument.mode == DocumentMode.INDEXEDCOLOR ) ){    
                                              alert('This script requires a document in Greyscale, RGB, CMYK, or Lab mode.');
                                              return;
                                          }
                                          // check for at least two colorSamplers
                                          if(app.activeDocument.colorSamplers.length < 2 ){
                                              alert('This script requires at least two colorSamplers.');
                                              return;
                                          }
                                          // last check for at least two layers - assume they will be on same level( not in layerSet )
                                          if(app.activeDocument.layers.length < 2 ){
                                              alert('This script requires at least two layers.');
                                              return;
                                          }
                                          // create varaibles to hold the colorSampler's color property for each layer
                                          // for the bottom layer
                                          var outputArray = new Array();
                                          // for top layer - array could also be created this way
                                          var inputArray = [];
                                          // store the number of samples because it will be needed in more than one place
                                          var numberOfSamples = app.activeDocument.colorSamplers.length;
                                      
                                          // hide the top layer
                                          app.activeDocument.layers[0].visible = false;
                                          // collect the samples from the bottom layer
                                          for(var sampleIndex = 0; sampleIndex < numberOfSamples; sampleIndex++ ){
                                              outputArray.push(app.activeDocument.colorSamplers[sampleIndex].color);
                                          }
                                          // turn the top layer back on
                                          app.activeDocument.layers[0].visible = true;
                                          // collect those samples
                                          for(var sampleIndex = 0; sampleIndex < numberOfSamples; sampleIndex++ ){
                                              inputArray.push(app.activeDocument.colorSamplers[sampleIndex].color);
                                          }
                                          // make sure the top layer is the activeLayer
                                          app.activeDocument.activeLayer = app.activeDocument.layers[0];
                                      // create arrays of the color values:
                                      var theArray = [[0, 0, 0, 0, 0, 0]];
                                      for (var m = 0; m < inputArray.length; m++) {
                                      theArray.push([inputArray[m].rgb.red, outputArray[m].rgb.red, inputArray[m].rgb.green, outputArray[m].rgb.green, inputArray[m].rgb.blue, outputArray[m].rgb.blue]);
                                      };
                                      theArray.push([255, 255, 255, 255, 255, 255]);
                                      // sort;
                                      theArray.sort(sortArrayByIndexedItem);
                                      // makeCurveAdjustmentLayer();
                                      rgbCurvesLayer (theArray)
                                      };
                                      ////// make rgb curves layer //////
                                      function rgbCurvesLayer (theArray) {
                                      // =======================================================
                                      var idMk = charIDToTypeID( "Mk  " );
                                          var desc5 = new ActionDescriptor();
                                          var idnull = charIDToTypeID( "null" );
                                              var ref2 = new ActionReference();
                                              var idAdjL = charIDToTypeID( "AdjL" );
                                              ref2.putClass( idAdjL );
                                          desc5.putReference( idnull, ref2 );
                                          var idUsng = charIDToTypeID( "Usng" );
                                              var desc6 = new ActionDescriptor();
                                              var idType = charIDToTypeID( "Type" );
                                                  var desc7 = new ActionDescriptor();
                                                  var idpresetKind = stringIDToTypeID( "presetKind" );
                                                  var idpresetKindType = stringIDToTypeID( "presetKindType" );
                                                  var idpresetKindDefault = stringIDToTypeID( "presetKindDefault" );
                                                  desc7.putEnumerated( idpresetKind, idpresetKindType, idpresetKindDefault );
                                              var idCrvs = charIDToTypeID( "Crvs" );
                                              desc6.putObject( idType, idCrvs, desc7 );
                                          var idAdjL = charIDToTypeID( "AdjL" );
                                          desc5.putObject( idUsng, idAdjL, desc6 );
                                      executeAction( idMk, desc5, DialogModes.NO );
                                      // =======================================================
                                      var idsetd = charIDToTypeID( "setd" );
                                          var desc8 = new ActionDescriptor();
                                          var idnull = charIDToTypeID( "null" );
                                              var ref3 = new ActionReference();
                                              var idAdjL = charIDToTypeID( "AdjL" );
                                              var idOrdn = charIDToTypeID( "Ordn" );
                                              var idTrgt = charIDToTypeID( "Trgt" );
                                              ref3.putEnumerated( idAdjL, idOrdn, idTrgt );
                                          desc8.putReference( idnull, ref3 );
                                          var idT = charIDToTypeID( "T   " );
                                              var desc9 = new ActionDescriptor();
                                              var idpresetKind = stringIDToTypeID( "presetKind" );
                                              var idpresetKindType = stringIDToTypeID( "presetKindType" );
                                              var idpresetKindCustom = stringIDToTypeID( "presetKindCustom" );
                                              desc9.putEnumerated( idpresetKind, idpresetKindType, idpresetKindCustom );
                                              var idAdjs = charIDToTypeID( "Adjs" );
                                                  var list1 = new ActionList();
                                                      var desc10 = new ActionDescriptor();
                                                      var idChnl = charIDToTypeID( "Chnl" );
                                                          var ref4 = new ActionReference();
                                                          var idChnl = charIDToTypeID( "Chnl" );
                                                          var idChnl = charIDToTypeID( "Chnl" );
                                                          var idRd = charIDToTypeID( "Rd  " );
                                                          ref4.putEnumerated( idChnl, idChnl, idRd );
                                                      desc10.putReference( idChnl, ref4 );
                                                      var idCrv = charIDToTypeID( "Crv " );
                                                          var list2 = new ActionList();
                                      // add r points;
                                      for (var m = 0; m < theArray.length; m++) {
                                                addCurvePoint (list2, theArray[m], 0)
                                                }; 
                                                      desc10.putList( idCrv, list2 );
                                                  var idCrvA = charIDToTypeID( "CrvA" );
                                                  list1.putObject( idCrvA, desc10 );
                                                      var desc15 = new ActionDescriptor();
                                                      var idChnl = charIDToTypeID( "Chnl" );
                                                          var ref5 = new ActionReference();
                                                          var idChnl = charIDToTypeID( "Chnl" );
                                                          var idChnl = charIDToTypeID( "Chnl" );
                                                          var idGrn = charIDToTypeID( "Grn " );
                                                          ref5.putEnumerated( idChnl, idChnl, idGrn );
                                                      desc15.putReference( idChnl, ref5 );
                                                      var idCrv = charIDToTypeID( "Crv " );
                                                          var list3 = new ActionList();
                                      // add g points;
                                      for (var m = 0; m < theArray.length; m++) {
                                                addCurvePoint (list3, theArray[m], 2)
                                                };
                                                      desc15.putList( idCrv, list3 );
                                                  var idCrvA = charIDToTypeID( "CrvA" );
                                                  list1.putObject( idCrvA, desc15 );
                                                      var desc20 = new ActionDescriptor();
                                                      var idChnl = charIDToTypeID( "Chnl" );
                                                          var ref6 = new ActionReference();
                                                          var idChnl = charIDToTypeID( "Chnl" );
                                                          var idChnl = charIDToTypeID( "Chnl" );
                                                          var idBl = charIDToTypeID( "Bl  " );
                                                          ref6.putEnumerated( idChnl, idChnl, idBl );
                                                      desc20.putReference( idChnl, ref6 );
                                                      var idCrv = charIDToTypeID( "Crv " );
                                                          var list4 = new ActionList();
                                      // add b points;
                                      for (var m = 0; m < theArray.length; m++) {
                                                addCurvePoint (list4, theArray[m], 4)
                                                };
                                                      desc20.putList( idCrv, list4 );
                                                  var idCrvA = charIDToTypeID( "CrvA" );
                                                  list1.putObject( idCrvA, desc20 );
                                              desc9.putList( idAdjs, list1 );
                                          var idCrvs = charIDToTypeID( "Crvs" );
                                          desc8.putObject( idT, idCrvs, desc9 );
                                      executeAction( idsetd, desc8, DialogModes.NO );
                                      //
                                      return app.activeDocument.activeLayer;
                                      ////// add curve point //////
                                      function addCurvePoint (theList, valueHor, theNumber) {
                                      var desc11 = new ActionDescriptor();
                                      var idHrzn = charIDToTypeID( "Hrzn" );
                                      desc11.putDouble( idHrzn, valueHor[theNumber] );
                                      var idVrtc = charIDToTypeID( "Vrtc" );
                                      desc11.putDouble( idVrtc, valueHor[theNumber+1] );
                                      var idPnt = charIDToTypeID( "Pnt " );
                                      theList.putObject( idPnt, desc11 );
                                      };
                                      };
                                      ////// sort a double array, thanks to sam, http://www.rhinocerus.net/forum/lang-javascript/ //////
                                      function sortArrayByIndexedItem(a,b) {
                                      var theIndex = 0;
                                      if (a[theIndex]<b[theIndex]) return -1;
                                      if (a[theIndex]>b[theIndex]) return 1;
                                      return 0;
                                      };