6 Replies Latest reply on May 23, 2011 1:43 PM by JJMack

    Adding a screenshot via script

    jbg7

      I often have to put a screenshot in photos of computers and tvs and I want to be able to script this somehow. If I set a path, or shape, or points, is there a way to take another image and place it to fit inside those point (so all corners would meet?) Right now I just place the screenshot image on a separate layer and use Distort transform to match the corners of the screen and screenshot.

       

      Any thoughts?

        • 1. Re: Adding a screenshot via script
          JJMack Most Valuable Participant

          Off the top of my head I think you would be only be able to do a subset of what you described.  Your description is very general.  You can not use Photoshop transform to distort an image into every possible shape for I believe Photoshop's transform supports only four point shapes.  For a shape with more points you might be able to do something with warp but then there are shape like ovals.  Perhaps  your description is broader then you meant it to be.

           

          If its a four point distort transform you want. Then you may want to install the scriptlistner plugin and see if you could make a useable function from the code that the plugin generates for your distort transform. You  might be able to use it to transform your placed image which you can get the bounds of into your four point selection.

          • 2. Re: Adding a screenshot via script
            jbg7 Level 1

            Thanks. I mocked this up as an example to help clarify. I only need 4 points: http://i.imgur.com/mwCsz.jpg

            • 3. Re: Adding a screenshot via script
              c.pfaffenbichler Level 9

              Some time ago I stumbled onto something that I’m afraid I don’t quite comprehend in »Geometry.jsx«, but might help you.

              This Script should transform the activeLayer according to a path named »screen« if it has four points – the Script might need some more work …

               

              // this script attempts to fit the activeLayer into an area defined by a path named »screen«;
              // use it at your own risk;
              #target photoshop
              // from adobe’s terminology.jsx;
              const classChannel = app.charIDToTypeID('Chnl');
              const classRectangle = app.charIDToTypeID('Rctn');
              const enumNone = app.charIDToTypeID('None');
              const eventSet = app.charIDToTypeID('setd');
              const eventTransform = app.charIDToTypeID('Trnf');
              const keySelection = app.charIDToTypeID('fsel');
              const krectangleStr = app.stringIDToTypeID("rectangle");
              const kquadrilateralStr = app.stringIDToTypeID("quadrilateral");
              const keyBottom = app.charIDToTypeID('Btom');
              const keyLeft = app.charIDToTypeID('Left');
              const keyNull = app.charIDToTypeID('null');
              const keyRight = app.charIDToTypeID('Rght');
              const keyTo = app.charIDToTypeID('T   ');
              const keyTop = app.charIDToTypeID('Top ');
              const typeOrdinal = app.charIDToTypeID('Ordn');
              const unitPixels = app.charIDToTypeID('#Pxl');
              // from adobe’s geometry.jsx;
              //
              // =================================== TPoint ===================================
              //
              function TPoint( x, y )
              {
                   this.fX = x;
                   this.fY = y;
              }
              // TPoint Constants
              const kTPointOrigion = new TPoint( 0, 0 );
              TPoint.kOrigin = kTPointOrigion;
              
              const kTPointInfinite = new TPoint( Infinity, Infinity );
              TPoint.kInfinite = kTPointInfinite;
              const kTPointClassname = "TPoint";
              TPoint.prototype.className = kTPointClassname;
              // Overloaded math operators
              TPoint.prototype["=="] = function( Src )
              {
                   return (this.fX == Src.fX) && (this.fY == Src.fY);
              }
              
              TPoint.prototype["+"] = function( b )
              {
                   return new TPoint( this.fX + b.fX, this.fY + b.fY );
              }
              
              TPoint.prototype["-"] = function( b, reversed )
              {
                   if (typeof(b) == "undefined")          
              // unary minus
                        return new TPoint( -this.fX, -this.fY )
                   else
                   {
                        if (reversed)
                             return new TPoint( b.fX - this.fX, by.fY - this.fY );
                        else
                             return new TPoint( this.fX - b.fX, this.fY - b.fY);
                   }
              }
              //
              // Multiply and divide work with scalars as well as points
              //
              TPoint.prototype["*"] = function( b )
              {
                  if (typeof(b) == 'number')
                        return new TPoint( this.fX * b, this.fY * b );
                   else
                        return new TPoint( this.fX * b.fX, this.fY * b.fY );
              }
              TPoint.prototype["/"] = function( b, reversed )
              {
                   if (reversed)
                   {
                        if (typeof(b) == "number")
                             debugger;
              // Can't divide a number by a point
                        else
                             return new TPoint( b.fX / this.fX, b.fY / this.fY );
                   }
                   else
                   {
                        if (typeof(b) == 'number')
                             return new TPoint( this.fX / b, this.fY / b );
                        else
                             return new TPoint( this.fX / b.fX, this.fY / b.fY );
                   }
              }
              TPoint.prototype.toString = function()
              {
                   return "[" + this.fX.toString() + "," + this.fY.toString() + "]";
              }
              TPoint.prototype.vectorLength = function()
              {
                  return Math.sqrt( this.fX * this.fX + this.fY * this.fY );
              }
              ////////////////////////
              var myDocument = app.activeDocument;
              // assumes the activeLayer is the screenshot;
              var theScreenshot = myDocument.activeLayer;
              var screenFound = false;
              // switch units to pixels;
              var originalUnits = app.preferences.rulerUnits;
              app.preferences.rulerUnits = Units.PIXELS;
              try {myDocument.pathItems[0].select();
                   myDocument.pathItems[0].deselect()}
              catch (e) {};
              myDocument.selection.deselect();
              var thePathList = new Array;
              // verify document has paths;
              if (myDocument.pathItems.length > 0) {
              // create list of possible paths;
                   for (var g = 0; g < myDocument.pathItems.length; g++) {
                        if (myDocument.pathItems[g].subPathItems.length == 1 && myDocument.pathItems[g].subPathItems[0].pathPoints.length == 4) {
              // if a fitting path is named screen, take it;
                             if (myDocument.pathItems[g].name == "screen") {
                                  var aPath = myDocument.pathItems[g];
                                  var screenFound = true
                                  }
                             else {                    
                                  thePathList = thePathList.concat(myDocument.pathItems[g].name)
                                  }
                             }
                        };
              // alert if no useable path has been found;
              if (screenFound == false) {
                   if (thePathList.length == 0) {
                        alert ("no four-points-path available")
                        }
                   else {
              // if only one path is ok, take it;
                        if (thePathList.length == 1) {
                             var aPath = myDocument.pathItems.getByName(thePathList[0]);
                             var screenFound = true
                             }
              // else offer the selection;
                        else {
                             var aPath = selectAPath ();
                             if (aPath.kind == PathKind.NORMALPATH) {
                                  screenFound = true;
                             }
                        }
                   }
              }
              }
              // when no path has been found;
              else {
                   alert ("The document contains no paths")
                   }
              
              //////////// transformation ////////////
              try {
              //////////// corners ////////////
              // get the horicontal and vertical coordinates in pixels;
              var hor1 = Number(aPath.subPathItems[0].pathPoints[0].anchor[0]);
              var hor2 = Number(aPath.subPathItems[0].pathPoints[1].anchor[0]);
              var hor3 = Number(aPath.subPathItems[0].pathPoints[2].anchor[0]);
              var hor4 = Number(aPath.subPathItems[0].pathPoints[3].anchor[0]);
              var ver1 = Number(aPath.subPathItems[0].pathPoints[0].anchor[1]);
              var ver2 = Number(aPath.subPathItems[0].pathPoints[1].anchor[1]);
              var ver3 = Number(aPath.subPathItems[0].pathPoints[2].anchor[1]);
              var ver4 = Number(aPath.subPathItems[0].pathPoints[3].anchor[1]);
              // order the horicontal and vertical coordinates;
              var horList = [hor1, hor2, hor3, hor4];
              var verList = [ver1, ver2, ver3, ver4];
              horList.sort(sortNumber);
              verList.sort(sortNumber);
              // check the horicontal value;
              var leftPoints = new Array;
              var rightPoints = new Array;
              for (var k=0; k<aPath.subPathItems[0].pathPoints.length; k++) {
                   if (aPath.subPathItems[0].pathPoints[k].anchor[0] == horList[0] ||  aPath.subPathItems[0].pathPoints[k].anchor[0] == horList[1]) {
                        leftPoints = leftPoints.concat(aPath.subPathItems[0].pathPoints[k].anchor)
                        }
                   else {
                        rightPoints = rightPoints.concat(aPath.subPathItems[0].pathPoints[k].anchor)
                        }
                   };
              // define the four cornerpoints;
              if (leftPoints[1] <= leftPoints[3]) {
                   var aTopLeft = [leftPoints[0], leftPoints[1]]
                   var aBottomLeft = [leftPoints[2], leftPoints[3]];
                   }
              else {
                   var aTopLeft = [leftPoints[2], leftPoints[3]]
                   var aBottomLeft = [leftPoints[0], leftPoints[1]];
                   };
              if (rightPoints[1] <= rightPoints[3]) {
                   var aTopRight = [rightPoints[0], rightPoints[1]]
                   var aBottomRight = [rightPoints[2], rightPoints[3]];
                   }
              else {
                   var aTopRight = [rightPoints[2], rightPoints[3]]
                   var aBottomRight = [rightPoints[0], rightPoints[1]];
                   }
              //////////// transform to the new corners ////////////
              transformActiveLayer( [new TPoint(aTopLeft[0], aTopLeft[1]), new TPoint(aTopRight[0], aTopRight[1]), new TPoint(aBottomRight[0], aBottomRight[1]), new TPoint(aBottomLeft[0], aBottomLeft[1])]);
              }
              catch (e) {"error"};
              // resets the preferences units;
              app.preferences.rulerUnits = originalUnits;
              ////////////////////////////////////
              ////////////////////////////////////
              ////////////////////////////////////
              //////////// the  functions ////////////
              // the dialog for multiple possible paths;
              function selectAPath () {
                   var dlg = new Window('dialog', "Select a path to use for the perspective", [500,300,820,380])          
                   dlg.pathSel = dlg.add('dropdownlist', [12,13,306,35], (thePathList));
                   dlg.pathSel.selection = dlg.pathSel[0];
                   dlg.buildBtn = dlg.add('button', [13,42,158,67], 'OK', {name:'ok'});
                   dlg.cancelBtn = dlg.add('button', [168,42,313,67], 'Cancel', {name:'cancel'});
                   var myReturn = dlg.show ();
                   if (myReturn == true) {
                        var aPath = myDocument.pathItems.getByName(dlg.pathSel.selection);
                        }
                   return aPath
                   };
              // sort numbers, found at www.w3schools.com;
              function sortNumber(a,b) {
                   return a - b;
                   };
              // from adobe’s stacksupport.jsx;
              // Apply a perspective transform to the current layer, with the
              // corner TPoints given in newCorners (starts at top left, in clockwise order)
              // Potential DOM fix
              function transformActiveLayer( newCorners )
              {
                   function pxToNumber( px )
                   {
                        return px.as("px");
                   }
                   var saveUnits = app.preferences.rulerUnits;
                   app.preferences.rulerUnits = Units.PIXELS;
                   var i;
                   var setArgs = new ActionDescriptor();
                   var chanArg = new ActionReference();
                   chanArg.putProperty( classChannel, keySelection );
              //     setArgs.putReference( keyNull, chanArg );
                   var boundsDesc = new ActionDescriptor();
                   var layerBounds = app.activeDocument.activeLayer.bounds;
                   boundsDesc.putUnitDouble( keyTop, unitPixels, pxToNumber( layerBounds[1] ) );
                   boundsDesc.putUnitDouble( keyLeft, unitPixels, pxToNumber( layerBounds[0] ) );
                   boundsDesc.putUnitDouble( keyRight, unitPixels, pxToNumber( layerBounds[2] ) );
                   boundsDesc.putUnitDouble( keyBottom, unitPixels, pxToNumber( layerBounds[3] ) );
              //     setArgs.putObject( keyTo, classRectangle, boundsDesc );
              //     executeAction( eventSet, setArgs );
                   var result = new ActionDescriptor();
                   var args = new ActionDescriptor();
                   var quadRect = new ActionList();
                   quadRect.putUnitDouble( unitPixels, pxToNumber( layerBounds[0] ) );
              // ActionList put is different from ActionDescriptor put
                   quadRect.putUnitDouble( unitPixels, pxToNumber( layerBounds[1] ) );
                   quadRect.putUnitDouble( unitPixels, pxToNumber( layerBounds[2] ) );
                   quadRect.putUnitDouble( unitPixels, pxToNumber( layerBounds[3] ) );     
                   var quadCorners = new ActionList();
                   for (i = 0; i < 4; ++i)
                   {
                        quadCorners.putUnitDouble( unitPixels, newCorners[i].fX );
                        quadCorners.putUnitDouble( unitPixels, newCorners[i].fY );
                   }
                   args.putList( krectangleStr, quadRect );
                   args.putList( kquadrilateralStr, quadCorners );
                   executeAction( eventTransform, args );     
              // Deselect
                   deselArgs = new ActionDescriptor();
                   deselRef = new ActionReference();
                   deselRef.putProperty( classChannel, keySelection );
                   deselArgs.putReference( keyNull, deselRef );
                   deselArgs.putEnumerated( keyTo, typeOrdinal, enumNone );
                   executeAction( eventSet, deselArgs );
                   app.preferences.rulerUnits = saveUnits;
              }
              

              • 4. Re: Adding a screenshot via script
                jbg7 Level 1

                Thanks! That works beautifully. It'll need some tweaking for my exact purpose, but it's most of the way there.

                • 5. Re: Adding a screenshot via script
                  c.pfaffenbichler Level 9

                  I did that some time ago, so there may indeed be a lot of room for improving and shortening the code.

                  Best of luck with it.

                  • 6. Re: Adding a screenshot via script
                    JJMack Most Valuable Participant

                    I made one minor change to do a paste instead of assuming the active layer. To use the Clipboard as the Screen capture. I liked that all 4 sided path are search for and that the script presents a selection list if there are more than one four sided paths. Like how Work Path could be used to keep changing location whith out having to name a path screen. I also like to do screen captures however my laptop has a uxga display 1600x1200 and its external display 1360x768 scree capture is too large for Web so I creates a document half size capture screens using an actuon that dose a select all convert selection to work path and run the script. After all screen are caprured I do a select 800x600 and crop to the UXGA display. Expand the canvas to the hight needed to spread out the captures with the move tool. Canvas Size X00%. high relative. Here is an example of how and action I created to replace a white background works.

                    Open your image in Photoshop
                    Select the Action click on play
                    It will prompt you to place in replacement background image click on continue
                    Select image file and click on Place
                    Transform replacement image to fill document press Enter
                    Action will finish with prompt paint with black click stop
                    Paint black over subject interior
                    Done

                    ActionInUse.jpg