編集(管理者用) | 差分 | 新規作成 | 一覧 | RSS | FrontPage | 検索 | 更新履歴
== o ==
ねこまたや web出張所
INDEX

トップページ
お知らせ
twitter
お道具箱
技術資料と雑談
ライブラリ
雑貨や(ダウンロード)
最近の更新
ねこまたやについて

twitter

ねこまたやのついった

企画モノ

psAxe情報ページ
自主制作アニメ
ぽこあぽこ られんたんど


あやかしあやし
エフェクトネタ帳



りまぴん
AE エクスプレッション
レンダー乙女とか
簡易版XPSリンカ
おまけギャラリ

BBS

ねこまたやBBS
開発室BBS

Language



開発室みたいなものでしたが
spamアクセスがはげしいのでページは凍結中です。
書き換えは管理人のみが可能です。
Nekomataya/2016

photoshop複数セレクト - [[PS_Script]]


PS_Script

photoshop(CS2以降)で複数レイヤをスクリプトで扱う

photoshopでスクリプトから選択状態の複数のレイヤを取得するコード片

以前のバージョンから大幅に軽量化できました。

そうか!undo使えば良かったんじゃあ! です。

/*
動作条件
 選択レイヤに背景レイヤが含まれているときにはエラー
 選択レイヤがない場合もエラー
実用上はあまり問題ないのでとりあえず無視
*/
getSelectedLayers=function(){ 
//--------------------------------------レイヤからグループ
 var idGrp = stringIDToTypeID( "groupLayersEvent" );
 var descGrp = new ActionDescriptor();
 var refGrp = new ActionReference();
 refGrp.putEnumerated(charIDToTypeID( "Lyr " ),charIDToTypeID( "Ordn" ),charIDToTypeID( "Trgt" ));
 descGrp.putReference(charIDToTypeID( "null" ), refGrp );
 executeAction( idGrp, descGrp, DialogModes.ALL );//外部関数にして呼び出しすると妙に遅いので注意だ
//================== トレーラーのレイヤを取得
var resultLayers=new Array();
for (var ix=0;ix<app.activeDocument.activeLayer.layers.length;ix++){resultLayers.push(app.activeDocument.activeLayer.layers[ix])}
// =================== UNDOバッファを使用して復帰
var id8 = charIDToTypeID( "slct" );
    var desc5 = new ActionDescriptor();
    var id9 = charIDToTypeID( "null" );
        var ref2 = new ActionReference();
        var id10 = charIDToTypeID( "HstS" );
        var id11 = charIDToTypeID( "Ordn" );
        var id12 = charIDToTypeID( "Prvs" );  
        ref2.putEnumerated( id10, id11, id12 );
    desc5.putReference( id9, ref2 );
executeAction( id8, desc5, DialogModes.NO );
//配列を返す
return resultLayers;
//選択レイヤの復帰はUNDOが勝手にやるので何もしない
}
//test
alert(getSelectedLayers().length);

小さめの関数一つに収まりました。

レイヤグループを作って、中身を取得した後に、UNDOで元に戻してやるだけでよかった。

選択範囲に背景レイヤが含まれているとグループがつくれないのでエラーが出ます。 選択レイヤ数が0の場合もグループが作成できなくてエラーが出ますが、必要ならこの関数の実行前に適否を判断してください。

2011.06.25


↓は以前の記事とコードです。

このコードがダメなわけじゃないけど、単純に選択状態のレイヤが欲しい場合は上のコードのほうがイロイロ軽快でラクチンです。


こちらのネタを使わせてもらっとります

http://curryegg.blog.shinobi.jp/


さて ご存知 Adobe Photoshop

バージョンCS2以降では 画面上複数のレイヤを同時に選択して操作する事が可能になりました。

ベンリベンリ

ところがじゃな、実はこれはスクリプトで処理するインターフェースが出てないのじゃ!

取得も出来なきゃ操作も出来ない。

それどころか CS2ではスクリプトからDOM経由でオブジェクトの操作をすると複数セレクトが解除されて一番上のレイヤだけがアクティブで残る(昔ながらの1個セレクト)   そもそも「レイヤの選択」という概念が実装されてないのだな うん あれはレイヤのアクティベートであってセレクトではないと、そういう感じ

ちょいとトリッキーなコードになりますけど http://curryegg.blog.shinobi.jp/ こちらのサイトに紹介されている方法を使って、現在セレクトされているレイヤを配列で返す関数を作ってみました。

おミソはDOM経由でなくマクロっぽい(アクションの記録で使う方)処理をすると複数セレクトのオブジェクトが扱えるあたりで、 なんだかハイブリッドな操作になります。(こういうと何となく聞こえがいいけど〜ごちゃ混ぜ操作ダネ)

ちなみに既知の障害があって、複数レイヤをセレクトしていないときにこの関数を呼ぶとphotoshopの一般エラーが発生してスクリプトの動作が止まります。 エラー回避はどうもうまく行かなかったので組み込んでないです。

とっても不完全な関数群なので注意して使ってくださいね。

以下、ソース 最後の方は動作確認用兼関数の使用サンプルです。応用するときは下の方は捨てちゃってください。


/*
	 複数レイヤの選択を支援する関数イロイロ
*/
/*
むつかしい

えーっと
Photoshopのレイヤのリンク状態は、レイヤセット発生以来かなり(あやしげに)複雑なので互換の構造を作るのがキモになりそう?

リンクの挙動を見るかぎりは、3.0のころの最初のレイヤのリンクとデータ互換のあるフラットモデルのようです。
その上にレイヤセットで発生するリンクと類似する(一部同一の)動作を似たような構造でオーバーライドさせているカンジに見えます。

スクリプト側から見える状況は、むかしのフラットモデルのままなのでこの状況の再現をしてあげればなんとなく済みかなぁ。

ポイントを並べると

現在のリンク状況を模したオブジェクトを作ってそこにリンク状態を記録する


	保存する情報は、レイヤオブジェクトへの参照
	リンク情報自体がフラットなモデルなので格納用オブジェクトは単純な配列で良い
	最初に当たったレイヤからレイヤを記録するので最初にチェック済みか否かの判定が必要
	フラットなトレーラはあるのか?>ない 少なくともでてない
	再帰的に処理するファンクションが必要

結局書いてしまった。2008/11/12

*/
/*
	LinkSelLys()
	選択されたレイヤをリンクする
*/
function LinkSelLys()
{
//選択レイヤーをリンク付け
    var lID1 = stringIDToTypeID( "linkSelectedLayers" );
    var lDesc1 = new ActionDescriptor();
    var lID2 = charIDToTypeID( "null" );
    var lRef1 = new ActionReference();
    var lID3 = charIDToTypeID( "Lyr " );
    var lID4 = charIDToTypeID( "Ordn" );
    var lID5 = charIDToTypeID( "Trgt" );
    lRef1.putEnumerated( lID3, lID4, lID5 );
    lDesc1.putReference( lID2, lRef1 );
    executeAction( lID1, lDesc1, DialogModes.NO );

}
//******************************************************************************

function UnLinkSelLys()
{
//選択レイヤーのリンク解除
    var uID1 = stringIDToTypeID( "unlinkSelectedLayers" );
    var uDesc1 = new ActionDescriptor();
    var uID2 = charIDToTypeID( "null" );
    var uRef1 = new ActionReference();
    var uID3 = charIDToTypeID( "Lyr " );
    var uID4 = charIDToTypeID( "Ordn" );
    var uID5 = charIDToTypeID( "Trgt" );
    uRef1.putEnumerated( uID3, uID4, uID5 );
    uDesc1.putReference( uID2, uRef1 );
    executeAction( uID1, uDesc1, DialogModes.NO );

}
//******************************************************************************

function addSelect(TslIndex)
{
//追加選択(Shit+Ctrl+Click)
//Indexは数値下から順に0からレイヤ数-1 まで
    var oID12 = charIDToTypeID( "slct" );
    var oDesc3 = new ActionDescriptor();
    var oID13 = charIDToTypeID( "null" );
    var oRef3 = new ActionReference();
    var oID14 = charIDToTypeID( "Lyr " );
    oRef3.putIndex( oID14, TslIndex );
    oDesc3.putReference( oID13, oRef3 );
    var oID15 = stringIDToTypeID( "selectionModifier" );
    var oID16 = stringIDToTypeID( "selectionModifierType" );
    var oID17 = stringIDToTypeID( "addToSelectionContinuous" );
    oDesc3.putEnumerated( oID15, oID16, oID17 );
    var oID18 = charIDToTypeID( "MkVs" );
    oDesc3.putBoolean( oID18, false );
    executeAction( oID12, oDesc3, DialogModes.NO );

}

/*
汎用的にすべてのレイヤをフラットモデルで返す関数の方がベンリそうである。なにかと
と。いうわけで書く
ドキュメントというよりトレーラ単位

	getAllLayers(trailer)
	引数	:レイヤコレクション Document.layers または LayerSets.layers
	戻り値	:そのトレーラ配下のレイヤをフラットな配列で

トレーラ自身は含まない
レイヤトレーラを引数に再帰的に
この方式だと配列のインデックスが反転するので注意
上から0番
*/
function getAllLayers(myTrailer){
	var myResult=new Array();//戻り値ローカルに
	for(
		var layerId=0;
		layerId < myTrailer.length;
		layerId++
	){
		//	レイヤをリザルトに積む
		myResult.push(myTrailer[layerId]);
		//	さらにレイヤがトレーラだった場合 再帰呼び出しをかけてリザルトを結合
		if((myTrailer[layerId] instanceof LayerSet)&&(myTrailer[layerId].layers.length)){
			myResult=myResult.concat(getAllLayers(myTrailer[layerId].layers));
		}
	}
//	各要素に現状の反転IDをのっける(後で使える)
	for(var idx=0;idx<myResult.length;idx++){myResult[idx].index=(myResult.length-idx-1);};
//	フラットなレイヤトレーラ配列を返す
	return myResult;
}
/*
	getLinkePattern(myTrailer)
	引数	:レイヤトレーラ
	戻り値	:リンク状態配列
*/
function getLinkePattern(myTrailer){
	var targetLayers=getAllLayers(myTrailer);

	var myLinks=new Array();//リンク情報格納用配列を初期化
		myLinks.linkGroupId=0;//便宜上のリンクグループID初期値
		myLinks.checkedLayers=new Array();//チェック済みのレイヤ参照
		myLinks.isChecked=function(myLayer){
		//	引数がチェック済みか否か判定
			var myResult=false;
			for(var lid=0;lid<this.checkedLayers.length;lid++){
				if(this.checkedLayers[lid]===myLayer){myResult=true;break;}
			}
			return myResult;
		}
/*
	フラットモデルレイヤトレーラをチェックしてリンク配列を保存
*/
for(
	var layerId=0;
	layerId < targetLayers.length;
	layerId++
){
	if((targetLayers[layerId].linkedLayers.length)&&(! myLinks.isChecked(targetLayers[layerId]))){
		myLinks[myLinks.linkGroupId]=new Array();
//	未チェックでリンク相手が存在する場合、まず該当レイヤ自体を登録
		myLinks[myLinks.linkGroupId].push(targetLayers[layerId]);
		myLinks.checkedLayers.push(targetLayers[layerId]);
//	リンクのあるレイヤをリンク配列から記録
		for(
			var subLayerId=0;
			subLayerId < targetLayers[layerId].linkedLayers.length;
			subLayerId++
		){
			myLinks[myLinks.linkGroupId].push(targetLayers[layerId].linkedLayers[subLayerId]);
			//配列にレイヤをプッシュ
			myLinks.checkedLayers.push(targetLayers[layerId].linkedLayers[subLayerId]);
			//チェック配列にレイヤをプッシュ
		}
		myLinks.linkGroupId++;//インクリメント
	}
}

	return	myLinks;
}
/*
	getSelectedLayers(myDocument)
	引数	:ドキュメントオブジェクト 省略時はアクティブドキュメント
	戻り値	:選択されたレイヤの配列 非選択状態では空配列が戻る
*/

function getSelectedLayers(myDocument){

var origLinks=getLinkePattern(myDocument.layers);//最初のリンク状態を記録

var mySelectedLayers=new Array()//リザルト配列;

var myLayers=getAllLayers(myDocument.layers);//フラットモデルを別に取得
var checkBoard=new Array(myLayers.length);

	UnLinkSelLys();//選択レイヤのリンクを解除

for(var idx=0;idx<myLayers.length;idx++){
	if(myLayers[idx].linkedLayers.length){
		checkBoard[idx]=false;
	}else{
		checkBoard[idx]=true;
	};
}

	LinkSelLys();//選択部分のリンクを作る

for(var idx=0;idx<myLayers.length;idx++){
	if((myLayers[idx].linkedLayers.length)&&(checkBoard[idx])){
		mySelectedLayers.push(myLayers[idx]);
	};
}

	UnLinkSelLys();//選択レイヤのリンクを解除

/*
	もとのリンクを再生
*/
	for(
		var layerId=0;
		layerId < origLinks.length;
		layerId++
	){
//復帰の際は0番をターゲットにして1番以降をリンクさせる。
		for(
		var subLayerId=1;
		subLayerId < origLinks[layerId].length;
		subLayerId++
		){
			origLinks[layerId][subLayerId].link(origLinks[layerId][0]);
		}
	};
//壊れた選択状態を復帰
//indexは idxから計算 length-index
for(var idx=0;idx<mySelectedLayers.length;idx++){addSelect(mySelectedLayers[idx].index);}

return mySelectedLayers;
}

/*
	LinkSelLys()
	引数	:なし
	戻り値	:なし
		現在選択されているレイヤをリンクに

	UnLinkSelLys()
	引数	:なし
	戻り値	:なし
		現在選択されているレイヤのリンクを解除

	addSelect(TslIndex)
	引数	:レイヤID(下から順)
	戻り値	:なし
		レイヤを選択状態にする

	getAllLayers(trailer)
	引数	:レイヤコレクション Document.layers または LayerSets.layers
	戻り値	:指定コレクション以下のすべてのレイヤを含む配列
		指定のトレーラ配下のレイヤをフラットな配列で返す。
		Layer.indexプロパティが追加されていますが、このindexが有効なのは
		指定のレイヤトレーラがDocument.layersの場合のみです。
		LayerSet.layersを指定した場合は、無効な値が付加されます。
		
	getLinkePattern(myTrailer)
	引数	:レイヤトレーラ
	戻り値	:リンク状態配列
		現在のリンク状態を配列で返す。
		[[Layer,Layer,Layer],[Layer,Layer]]
		配列の要素は呼び出し時点で相互に連結されたレイヤへの参照の配列です。

	getSelectedLayers(myDocument)
	引数	:ドキュメントオブジェクト 省略時はアクティブドキュメント
	戻り値	:選択されたレイヤの配列 非選択状態では空配列が戻る
		現在選択されているレイヤへの参照が格納された配列
		それぞれの要素にはindexプロパティが付加されている。
		このindexはアクション操作を行う際にレイヤインデックスとして使用可能です。
		(addSelect()の引数として使用可能)

このプログラムは関数の集合なのでご使用の際は以下の部分を切り取ってご使用くださいね
*/
// テスト用使用参考例
/*
	以下のように現在選択中のレイヤを配列で取得
	元来のレイヤオブジェクトに加えてindexプロパティが上乗せされている
	indexの値はファイルを下側から順に0で開始して1づつ増加する番号
	
*/
var mySelectedLayers=getSelectedLayers(app.activeDocument);
myText="selectedLayers:\n";
for(var idx=0;idx<mySelectedLayers.length;idx++){
	myText+=idx.toString()+"t:";
	myText+=mySelectedLayers[idx].toString()+"\t";
	myText+=mySelectedLayers[idx].index+"\n";
};
alert(myText);