HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2025
1210
usagiPSDのレイヤーを読み込むモジュールを作ってみました13解決


usagi

リンク

2025/12/10(Wed) 15:45:11|NO.104475

こんにちわ。
PSDのレイヤーを読み込むモジュールを作ってみました。(現状はArtlet2Dに依存)
私はPhotoshopを持っていない為、Gimpでエクスポートしたpsdで確認してます。
もっとこうする方が良い、これは良くないなどご意見頂けると嬉しいです。
改変、組み込みもご自由にどうぞ。
※レイヤー情報ってなにか使い道あったりするんですかね。。ツールとかデスクトップマスコットとか?

; ========================================================= ; PSD_簡易読み込みくん author: usagi ; ========================================================= #ifndef __PSD__ #define __PSD__ #include "a2d.hsp" #module PSD_FILE m_count, m_width, m_height, m_name, m_blend, m_type, m_left, m_top, m_right, m_bottom, m_opacity, m_flags #defcfunc local int16 int _i return _i<<16>>16 #defcfunc local wpeekB var _buf, int _ofs __v = wpeek(_buf, _ofs) return __v<<8 | __v>>8 & $FFFF #defcfunc local lpeekB var _buf, int _ofs __v = lpeek(_buf, _ofs) return __v<<24 | (__v<<8&$ff0000) | (__v>>8&$ff00) | (__v>>24&$ff) #defcfunc local padded int _num, int _bytes __mod = _bytes-_num\_bytes if __mod { __res = _num+__mod } else { __res = _num } return __res #defcfunc local pascalStr var _buf, int _ofs sdim __s, 256 : __n = peek(_buf, _ofs) : __i = _ofs+1 repeat __n : poke __s, cnt, peek(_buf, __i+cnt) : loop mref __st, 64 : __st = padded(__n, 4) return __s #defcfunc local pascalStrU16 var _buf, int _ofs sdim __s, 256 : __n = lpeekB(_buf, _ofs) : __i = _ofs+4 repeat __n : wpoke __s, cnt*2, wpeekB(_buf, __i+cnt*2) : loop __s = cnvwtos(__s) mref __st, 64 : __st = padded(__n, 4) return __s #defcfunc local keycode4 var _buf, int _ofs __s = " " : lpoke __s, 0, lpeek(_buf, _ofs) return __s #deffunc local unpackbits array _out, var _in, int _h, int _id __c = 16 - _id * 8 ; Writing color bit (AARRGGBB) __i = 2 * _h ; Skip (2byte * height) __j = 0 ; Start value for writing while __i < varsize(_in) ; RLE compression __n = peek(_in, __i) : __i++ ; Header byte if __n >= 128 { ; repeated byte __b = peek(_in, __i)<<__c : repeat 257-__n : _out.__j |= __b : __j++ : loop : __i++ } else { ; literal bytes repeat __n+1 : _out.__j |= (peek(_in, __i)<<__c) : __j++ : __i++ : loop } wend return ; ★Getter ------------------------------ #modcfunc psdWidth return m_width #modcfunc psdHeight return m_height #modcfunc psdCount return m_count #modcfunc psdName int _idx return m_name._idx #modcfunc psdBlend int _idx return m_blend._idx #modcfunc psdType int _idx return m_type._idx #modcfunc psdLeft int _idx return m_left._idx #modcfunc psdTop int _idx return m_top._idx #modcfunc psdRight int _idx return m_right._idx #modcfunc psdBottom int _idx return m_bottom._idx #modcfunc psdOpacity int _idx return m_opacity._idx #modcfunc psdFlags int _idx return m_flags._idx #modfunc psdSetFlags int _idx, int _flags m_flags._idx = _flags : return #modfunc psdSetFolder int _idx, int _flags if psdIsFolder(thismod, _idx) { if _flags { m_type._idx == -1 } else { m_type._idx == -2 } } : return #modcfunc psdIsLayer int _idx return m_type._idx >= 0 #modcfunc psdIsFolder int _idx return m_type._idx == -1 || m_type._idx == -2 #modcfunc psdIsFolderOpen int _idx return m_type._idx == -1 #modcfunc psdIsDivider int _idx return m_type._idx == -3 #modcfunc psdIsVisible int _idx return (m_flags._idx & 2) == 0 ; ★PSDファイル読み込み ファイル名, イメージid ;------------------------------------------------ #define global psdLoad(%1,%2,%3) newmod %1, PSD_FILE : _psdload %1, %2, %3 #modfunc _psdLoad str _file, int _img_id assert _img_id >= 0 exist _file : if strsize == -1 : return -1 sdim buf, strsize : bload _file, buf ; References: Adobe Photoshop File Formats Specification ; https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/ ; File Header Section ;------------------------------------------------ if keycode4(buf, 0) != "8BPS" : return -1 ; Signature if wpeekB(buf, 4) != 1 : return -1 ; Version (1 only) if wpeekB(buf, 24) != 3 : return -1 ; Color mode (RGB only) if lpeekB(buf, 26) != 0 : return -1 ; Color Mode Data Section is Not Supported idx = 34 + lpeekB(buf, 30) ; Skip Image Resources Section m_height = lpeekB(buf, 14) ; Height m_width = lpeekB(buf, 18) ; Width ; Layer and Mask Information Section ;------------------------------------------------ idx+=4 ; Skip Section Length ; + Layer info ---------------------------------- idx+=4 ; Skip Section Length m_count = abs(int16(wpeekB(buf, idx))) : idx+=2 ; Layer Count sdim m_name, 255, m_count sdim m_blend, 4, m_count dim m_type, m_count dim m_left, m_count dim m_top, m_count dim m_right, m_count dim m_bottom, m_count dim m_opacity, m_count dim m_flags, m_count dim layer_chid, m_count, 4 dim layer_chlen, m_count, 4 dim layer_sct, m_count ; ++ Layer records ------------------------------ repeat m_count : i = cnt m_top.i = lpeekB(buf, idx) : idx+=4 ; Top m_left.i = lpeekB(buf, idx) : idx+=4 ; Left m_bottom.i = lpeekB(buf, idx) : idx+=4 ; Bottom m_right.i = lpeekB(buf, idx) : idx+=4 ; Right layer_ch.i = wpeekB(buf, idx) : idx+=2 ; Channel count repeat layer_ch.i ; Channel information layer_chid.i.cnt = int16(wpeekB(buf, idx)) : idx+=2 ; id layer_chlen.i.cnt = lpeekB(buf, idx) : idx+=4 ; length loop idx+=4 ; Skip Blend mode signature m_blend.i = keycode4(buf, idx) : idx+=4 ; Blend mode key m_opacity.i = peek(buf, idx) : idx+=1 ; Opacity idx+=1 ; Skip Clipping (0 = base, 1 = non-base) m_flags.i = peek(buf, idx) : idx+=1 ; Flags idx+=1 ; Skip Filler (0) edf_len = lpeekB(buf, idx) : idx+=4 ; Length of the extra data field next_idx = edf_len + idx ; +++ Layer mask / adjustment Layer data ---- idx+= lpeekB(buf, idx) +4 ; Skip ; +++ Layer blending ranges data ------------ idx+= lpeekB(buf, idx) +4 ; Skip ; +++ Layer name ---------------------------- m_name.i = pascalStr(buf, idx) : idx+=stat ; Name ; +++ Additional Layer info ----------------- repeat if keycode4(buf, idx) != "8BIM" { break } : idx+=4 ali_key = keycode4(buf,idx) : idx+=4 ali_len = lpeekB(buf,idx) : idx+=4 dupptr ali_data, varptr(buf)+idx, ali_len, 2 switch ali_key case "luni" ; Unicode layer name (Photoshop 5.0) m_name.i = pascalStrU16(ali_data, 0) : swbreak case "lsct" ; Section divider setting (Photoshop 6.0) m_type.i = -lpeekB(ali_data, 0) : swbreak default ; Not supported logmes strf("追加レイヤー情報をスキップしました。[Key: %s, Len: %d]", ali_key, ali_len) : swbreak swend idx+= ali_len loop idx = next_idx loop ; ++ Channel image data ------------------------- img_num = _img_id repeat m_count : i = cnt if m_type.i == 0 { ; Read image m_type.i = img_num : img_num++ alCreateImage m_type.i, m_right.i - m_left.i, m_bottom.i - m_top.i alGetBitmapVData ps, pv repeat layer_ch.i chimg_comp = wpeekB(buf, idx) dupptr chimg_data, varptr(buf)+idx+2, layer_chlen.i.cnt-2, 2 idx += layer_chlen.i.cnt if chimg_comp == 1 { ; RLE compressed the image data unpackbits pv, chimg_data, alGetHeight(), layer_chid.i.cnt } else { ; Not supported (0: Raw, 2 or 3: Zip) logmes "未サポートの圧縮です。" } loop } else { repeat layer_ch.i : idx += layer_chlen.i.cnt : loop } loop ; + Global layer mask info ---------------------- ; Image Data Section ;------------------------------------------------ sdim buf return img_num #global #endif ; ★ここからテスト★ #uselib "user32.dll" ; マウス位置取得用 #func ScreenToClient "ScreenToClient" int, var #define boxs(%1,%2,%3,%4) line (%3),(%2),(%1),(%2):line (%3),(%4):line (%1),(%4):line (%1),(%2) #define SCR_SIZE 512 #define FONT_SIZE 24 screen 0, SCR_SIZE, SCR_SIZE : font "", FONT_SIZE ; 背景の市松模様を作成 buffer 1, ginfo_sy, ginfo_sx : rgbcolor $dddddd : div = 16 repeat ginfo_sy/div : y = cnt : repeat ginfo_sx/div : x = cnt if (x+y)\2=0 { boxf x*div, y*div, x*div+div, y*div+div } loop : loop : gsel 0 ; ★PSD読み込み dialog "psd",16,"PSDファイル" : psdload psd, refstr, 0 : layer_ofs = 0,0 *MAIN ; 処理 ------------------------------------------ cursor = ginfo_mx, ginfo_my : ScreenToClient hwnd, cursor : stick pad, 512 ; ★左クリックで表示やフォルダ折り畳み切り替え if cur_layer>-1 && pad==256 { if cursor.0<FONT_SIZE { psdSetFlags psd, cur_layer, psdFlags(psd, cur_layer)^2 } else { psdSetFolder psd, cur_layer, psdIsFolderOpen(psd, cur_layer)^1 } } ; ★右クリックで画像移動 click_r = (click_r<<1 | pad==512) & 3 if click_r == 1 : old_cursor = cursor.0, cursor.1 if click_r == 3 : repeat 2 : layer_ofs.cnt -= old_cursor.cnt - cursor.cnt : loop : old_cursor = cursor.0, cursor.1 if click_r == 2 : layer_ofs = limit(layer_ofs.0, -psdWidth(psd), psdWidth(psd)), limit(layer_ofs.1, -psdHeight(psd), psdHeight(psd)) ; ★ホイールでレイヤー情報の表示位置移動 _mousew = mousew : tree_pos = limit(tree_pos+FONT_SIZE*((_mousew>0)-(_mousew<0)), -tree_cnt*FONT_SIZE+FONT_SIZE,0) ; 階層情報を求める level = 0 repeat psdCount(psd) : i = cnt if psdIsFolder(psd, i) : level-- ; フォルダなら階層を下げる if psdIsDivider(psd, i) : level++ ; セクション区切りなら階層を戻してスキップ psd_level.i = level loop ; 階層の表示状態を求める repeat psdCount(psd) : i = cnt psd_hidden.i = psdIsVisible(psd, i) if psdIsFolder(psd, i) == 0 : continue target_level = psd_level.i + 1 repeat i, 1 : j = i - cnt if psdIsDivider(psd, j) && (target_level == psd_level.j) : break if (target_level <= psd_level.j) : psd_hidden.j &= psdIsVisible(psd, i) loop loop ; フォルダの折り畳み状態を求める repeat psdCount(psd) : i = cnt psd_fold.i = 0 if psdIsFolder(psd, i) == 0 : continue if psdIsFolderOpen(psd, i) : continue target_level = psd_level.i + 1 repeat i, 1 : j = i - cnt if psdIsDivider(psd, j) && (target_level == psd_level.j) : break if (target_level <= psd_level.j) : psd_fold.j = 1 loop loop ; 描画 ------------------------------------------ redraw 0 : gmode 0, ginfo_sx, ginfo_sy : pos 0, 0 : gcopy 1 ; ★レイヤー画像表示:PSDのレイヤー情報は最背面から記憶されています。 repeat psdCount(psd) : i = cnt if psdIsLayer(psd, i) == 0 : continue if psd_hidden.i == 0 : continue alCopyModeAlpha 255.0/psdOpacity(psd, i) alCopyImageToScreen psdType(psd, i), 0, psdLeft(psd, i)+layer_ofs.0, psdTop(psd, i)+layer_ofs.1 loop ; ★レイヤー階層表示:最前面からの方が分かりやすい為、逆順で処理します。 pos 0, tree_pos cur_layer = -1 : target_level = -1 : tree_cnt = 0 repeat psdCount(psd), 1 : i = psdCount(psd) - cnt if psd_fold.i : continue if psdIsDivider(psd, i) : continue ; カーソル if (ginfo_cy<cursor.1) && (cursor.1<ginfo_cy+FONT_SIZE) && (0<cursor.0) && (cursor.0<ginfo_winx) { cur_layer = i } ; 情報表示 rgbcolor $111111 if psdIsVisible(psd, i) { mes "○", 1 : if psd_hidden.i == 0 { pos 0 : mes "/", 1 } } else { mes "−", 1 } pos ginfo_cx+psd_level.i*FONT_SIZE if psdIsLayer(psd, i) { ; サムネ w = psdRight(psd, i)-psdLeft(psd, i) : h = psdBottom(psd, i)-psdTop(psd, i) if w > h { s = 1.0, double(h)/w } else { s = double(w)/h, 1.0 } ofs = (1.0-s.0) * FONT_SIZE, (1.0-s.1) * FONT_SIZE, double(ginfo_cx), double(ginfo_cy) gzoom FONT_SIZE, FONT_SIZE, 1,,,FONT_SIZE*4, FONT_SIZE*4 alStretchImageToScreen psdType(psd, i), 0, 0,0,w,h, ofs.2+ofs.0, ofs.3+ofs.1, s.0*FONT_SIZE, s.1*FONT_SIZE boxs ofs.2, ofs.3, ofs.2+FONT_SIZE, ofs.3+FONT_SIZE pos ginfo_cx+FONT_SIZE } else { if psdIsFolderOpen(psd, i) { mes "∨", 1 } else { mes ">", 1 } } mes psdName(psd, i) pos 0 : tree_cnt++ loop redraw 1 : await 16 goto *MAIN



この記事に返信する


naya

リンク

2025/12/10(Wed) 20:25:47|NO.104482

usagiさん、こんにちは!
以前に自作していた二つのpsdファイルでテストしてみました。

イラスト(2894x4093px、レイヤー数1、FireAlpaca(64bit) ver2.11.14)
エラーで終了しました。
”#Error 3 in Line 14(psd.hsp) パラメータの値が異常です”

ゲーム用キャラクター(400x400px、レイヤー数13、AzPainter2 ver2.12)
正常に表示できました。

psdファイル内の画像をレイヤー毎に扱える様になる、とても便利なモジュールです。
本モジュールを利用してボーンアニメーション作成ソフトなども自作できそう!



usagi

リンク

2025/12/11(Thu) 01:13:38|NO.104489

>nayaさん
テストありがとうございます。
同じような画像を用意してみましたがエラーは出ませんでした。もう少し原因探ってみますね。

また、その過程でいろいろ試していたところ、
アルファチャンネルの無いレイヤー(背景)などを読み込んだ時に表示されない不具合がありました。
基本は4バイトのAARRGGBB画像を使うと思うのですが、念のため修正しました。

171行目の下したに追加お願いします。

alGetBitmapVData ps, pv if layer_ch.i != 4 : foreach pv : pv.cnt = $FF000000 : loop ; ☆追加

>ボーンアニメーション作成ソフト
Live2Dみたいなものに利用できるとよさそうですよねー。



naya

リンク

2025/12/11(Thu) 01:31:28|NO.104490

>usagiさん
返信いただき、どうもありがとうございます!

教えていただいた行を追加して実行しましたが、前回と同じエラーになりました。
でも、ペイントソフトで背景レイヤーを追加して保存しなおしたら、表示できました!

普通にモジュールを使用する場合、ここまで大きなサイズの画像は使用しないと思いますので、お気になさらずに♪



名無し

リンク

2025/12/11(Thu) 02:33:17|NO.104491

相変わらずいろんなものを作ってコードを公開して凄いですね...

手持ちのpsdデータではa2d.hspの488行目でパラメータの値が異常ですとエラーが発生しました。
原因を探したところ、モジュールの方の171行目にある「alCreateImage」で、
4ループ目の幅と高さの指定が「0」となっていたようで、
その次の行で処理に失敗しているようでした。
ですので、その少し前にあるrepeat以降の行を
		if m_type.i == 0 {	; Read image
m_type.i = img_num : img_num++ if m_right.i>0 & m_bottom.i>0 { ;追加 if m_right.i>0 { alCreateImage m_type.i, m_right.i - m_left.i, m_bottom.i - m_top.i alGetBitmapVData ps, pv if layer_ch.i != 4 : foreach pv : pv.cnt = $FF000000 : loop ; ☆追加 repeat layer_ch.i chimg_comp = wpeekB(buf, idx) dupptr chimg_data, varptr(buf)+idx+2, layer_chlen.i.cnt-2, 2 idx += layer_chlen.i.cnt if chimg_comp == 1 { ; RLE compressed the image data unpackbits pv, chimg_data, alGetHeight(), layer_chid.i.cnt } else { ; Not supported (0: Raw, 2 or 3: Zip) logmes "未サポートの圧縮です。" } loop } } ;追加 } else {
と書き換える事で一応エラーは解消してレイヤーは表示はされたのですが、
イラストの方は一切表示されず、ファイルサイズやレイヤー数の関係か動作も結構重くでした...

幅と高さのどちらかが0以下の場合は処理をスルーするようにしただけですが、
これって対応自体は問題ないですかね?
3840*2160、レイヤー数が31、ファイルサイズが51MBです。



usagi

リンク

2025/12/11(Thu) 04:58:04|NO.104494

>名無しさん
お試しいただきありがとうございます。
高さや幅が0のレイヤーを作る事はペイントソフト上では出来ないと思いますので、データが壊れていなければ何か読み込みに不備がありあそうです。
0の場合はフォルダレイヤーかセクションディバイダーレイヤーの可能性があるのでその対応だと問題があると思います。
何か再現できるファイルがあればよいのですが。。。

重さに関しては、読み込みはHSP自体の速さもありますので巨大なファイルは難しいかもしれません。
ゲームキャラの立ち絵表情差分的な用途で考えてましたので、512x512でレイヤー50枚などは私の環境では特に問題なかったです。(目は50x50口は32x32など最適化済のレイヤーで)
ボトルネックとなるレイヤー圧縮の解凍部分は非常にシンプルなロジックなので高速化は見込めないかもですがチャレンジしてみても面白いかもです(unpackbitsの部分)
それ以外の処理は大した時間はかからないスクリプトです。
また、描画に関してはGDIが原因かもしれませんのでDish版とか時間があったら試してみますね。
ペイントソフトなみを求めるとクリップとかキャッシュとか工夫が必要になるかとは思います。

>nayaさん FireAlpaca 2894x4093 1枚
イントールしてみて同様のエラー確認できました。このツールだとレイヤーが1枚の場合、レイヤーが無いPSDデータとして保存されるようです。(GMPなどではレイヤー1枚として保存されるので開ける)
レイヤー情報がなくなってますのでレイヤー名や位置情報などこのモジュールでは対応が難しいです。(ただの1枚の画像として読むルーチンが別途必要になる。)
代わりとして106行目あたりに以下を追加いただくとエラー-2を返すようにしてますので、非対応にするか、通常のpicloadなどで読み込んでくださいませ。

; Layer and Mask Information Section ;------------------------------------------------ if lpeekB(buf, idx) == 0 : return -2 idx+=4 ; Skip Section Length

もしくはFireAlpaca側で保存する時レイヤー1枚でもフォルダに入れて保存してみてくだいませ。
フォルダ: ほげ
┗レイヤー: ふが
そうするとスクリプト変更無しで読み込める事を確認できました。


それにしても皆さんごついpsdファイル持ってますねぇ。。。いってもHDくらいかと思ってたのですが解像度の進化はすごいですね。



名無し

リンク

2025/12/11(Thu) 10:16:15|NO.104495

>高さや幅が0のレイヤーを作る事はペイントソフト上では出来ないと思いますので、データが壊れていなければ何か読み込みに不備がありあそうです。
そりゃそうですよね。
自分で作ったものなら別にファイルを出してもいいんですけど
イラストレーターさんに個人使用前提で依頼して描いてもらった物なのでファイル共有は憚られますね...
描画についても0の部分で処理スキップした(あるいはその部分が読み込めてない)結果かもしれないですね



naya

リンク

2025/12/11(Thu) 18:13:46|NO.104498

>usagiさん
検証していただきありがとうございます!
レイヤーの追加で表示できましたので解決ですね♪



usagi

リンク

2025/12/11(Thu) 19:39:36|NO.104500

ご指摘を反映しつつDish版にしたものです。
読込速度に関してはCにしないと改善は難しそうです。
Dishにしたところ描画は改善しまして、サイズにもよりますが100枚でもサクサクでした。
(おそらくGDIの文字表示がかなり遅かった)

ただ、celbitmapがサイズが大きくなると落ちてしまいまして、
こちらは私には修正は難しいので512とか1024くらいまでが現実的かと思いました。


; ========================================================= ; PSD_簡易読み込みくんDish author: usagi ; ========================================================= #ifndef __PSD__ #define __PSD__ #include "hsp3dish.as" #module PSD_FILE m_count, m_width, m_height, m_name, m_blend, m_type, m_left, m_top, m_right, m_bottom, m_opacity, m_flags #defcfunc local int16 int _i return _i<<16>>16 #defcfunc local wpeekB var _buf, int _ofs __v = wpeek(_buf, _ofs) return __v<<8 | __v>>8 & $FFFF #defcfunc local lpeekB var _buf, int _ofs __v = lpeek(_buf, _ofs) return __v<<24 | (__v<<8&$ff0000) | (__v>>8&$ff00) | (__v>>24&$ff) #defcfunc local padded int _num, int _bytes __mod = _bytes-_num\_bytes if __mod { __res = _num+__mod } else { __res = _num } return __res #defcfunc local pascalStr var _buf, int _ofs sdim __s, 256 : __n = peek(_buf, _ofs) : __i = _ofs+1 repeat __n : poke __s, cnt, peek(_buf, __i+cnt) : loop mref __st, 64 : __st = padded(__n, 4) return __s #defcfunc local pascalStrU16 var _buf, int _ofs sdim __s, 256 : __n = lpeekB(_buf, _ofs) : __i = _ofs+4 repeat __n : wpoke __s, cnt*2, wpeekB(_buf, __i+cnt*2) : loop __s = cnvwtos(__s) mref __st, 64 : __st = padded(__n, 4) return __s #defcfunc local keycode4 var _buf, int _ofs __s = " " : lpoke __s, 0, lpeek(_buf, _ofs) return __s #deffunc local unpackbits array _out, var _in, int _h, int _id __c = 16 - _id * 8 ; Writing color bit (AARRGGBB) __i = 2 * _h ; Skip (2byte * height) __j = 0 ; Start value for writing while __i < varsize(_in) ; RLE compression __n = peek(_in, __i) : __i++ ; Header byte if __n >= 128 { ; repeated byte __b = peek(_in, __i)<<__c : repeat 257-__n : _out.__j |= __b : __j++ : loop : __i++ } else { ; literal bytes repeat __n+1 : _out.__j |= (peek(_in, __i)<<__c) : __j++ : __i++ : loop } wend return ; ★Getter ------------------------------ #modcfunc psdWidth return m_width #modcfunc psdHeight return m_height #modcfunc psdCount return m_count #modcfunc psdName int _idx return m_name._idx #modcfunc psdBlend int _idx return m_blend._idx #modcfunc psdType int _idx return m_type._idx #modcfunc psdLeft int _idx return m_left._idx #modcfunc psdTop int _idx return m_top._idx #modcfunc psdRight int _idx return m_right._idx #modcfunc psdBottom int _idx return m_bottom._idx #modcfunc psdOpacity int _idx return m_opacity._idx #modcfunc psdFlags int _idx return m_flags._idx #modfunc psdSetFlags int _idx, int _flags m_flags._idx = _flags : return #modfunc psdSetFolder int _idx, int _flags if psdIsFolder(thismod, _idx) { if _flags { m_type._idx == -1 } else { m_type._idx == -2 } } : return #modcfunc psdIsLayer int _idx return m_type._idx >= 0 #modcfunc psdIsFolder int _idx return m_type._idx == -1 || m_type._idx == -2 #modcfunc psdIsFolderOpen int _idx return m_type._idx == -1 #modcfunc psdIsDivider int _idx return m_type._idx == -3 #modcfunc psdIsVisible int _idx return (m_flags._idx & 2) == 0 ; ★PSDファイル読み込み ファイル名, イメージid ;------------------------------------------------ #define global psdLoad(%1,%2,%3) newmod %1, PSD_FILE : _psdload %1, %2, %3 #modfunc _psdLoad str _file, int _img_id assert _img_id >= 0 exist _file : if strsize == -1 : return -1 sdim buf, strsize : bload _file, buf ; References: Adobe Photoshop File Formats Specification ; https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/ ; File Header Section ;------------------------------------------------ if keycode4(buf, 0) != "8BPS" : return -1 ; Signature if wpeekB(buf, 4) != 1 : return -1 ; Version (1 only) if wpeekB(buf, 24) != 3 : return -1 ; Color mode (RGB only) if lpeekB(buf, 26) != 0 : return -1 ; Color Mode Data Section is Not Supported idx = 34 + lpeekB(buf, 30) ; Skip Image Resources Section m_height = lpeekB(buf, 14) ; Height m_width = lpeekB(buf, 18) ; Width ; Layer and Mask Information Section ;------------------------------------------------ if lpeekB(buf, idx) == 0 : return -2 idx+=4 ; Skip Section Length ; + Layer info ---------------------------------- idx+=4 ; Skip Section Length m_count = abs(int16(wpeekB(buf, idx))) : idx+=2 ; Layer Count sdim m_name, 255, m_count sdim m_blend, 4, m_count dim m_type, m_count dim m_left, m_count dim m_top, m_count dim m_right, m_count dim m_bottom, m_count dim m_opacity, m_count dim m_flags, m_count dim layer_chid, m_count, 4 dim layer_chlen, m_count, 4 dim layer_sct, m_count ; ++ Layer records ------------------------------ repeat m_count : i = cnt m_top.i = lpeekB(buf, idx) : idx+=4 ; Top m_left.i = lpeekB(buf, idx) : idx+=4 ; Left m_bottom.i = lpeekB(buf, idx) : idx+=4 ; Bottom m_right.i = lpeekB(buf, idx) : idx+=4 ; Right layer_ch.i = wpeekB(buf, idx) : idx+=2 ; Channel count repeat layer_ch.i ; Channel information layer_chid.i.cnt = int16(wpeekB(buf, idx)) : idx+=2 ; id layer_chlen.i.cnt = lpeekB(buf, idx) : idx+=4 ; length loop idx+=4 ; Skip Blend mode signature m_blend.i = keycode4(buf, idx) : idx+=4 ; Blend mode key m_opacity.i = peek(buf, idx) : idx+=1 ; Opacity idx+=1 ; Skip Clipping (0 = base, 1 = non-base) m_flags.i = peek(buf, idx) : idx+=1 ; Flags idx+=1 ; Skip Filler (0) edf_len = lpeekB(buf, idx) : idx+=4 ; Length of the extra data field next_idx = edf_len + idx ; +++ Layer mask / adjustment Layer data ---- idx+= lpeekB(buf, idx) +4 ; Skip ; +++ Layer blending ranges data ------------ idx+= lpeekB(buf, idx) +4 ; Skip ; +++ Layer name ---------------------------- m_name.i = pascalStr(buf, idx) : idx+=stat ; Name ; +++ Additional Layer info ----------------- repeat if keycode4(buf, idx) != "8BIM" { break } : idx+=4 ali_key = keycode4(buf,idx) : idx+=4 ali_len = lpeekB(buf,idx) : idx+=4 dupptr ali_data, varptr(buf)+idx, ali_len, 2 switch ali_key case "luni" ; Unicode layer name (Photoshop 5.0) m_name.i = pascalStrU16(ali_data, 0) : swbreak case "lsct" ; Section divider setting (Photoshop 6.0) m_type.i = -lpeekB(ali_data, 0) : swbreak default ; Not supported logmes strf("追加レイヤー情報をスキップしました。[Key: %s, Len: %d]", ali_key, ali_len) : swbreak swend idx+= ali_len loop idx = next_idx loop ; ++ Channel image data ------------------------- img_num = _img_id repeat m_count : i = cnt if m_type.i == 0 { ; Read image m_type.i = img_num : img_num++ w = m_right.i - m_left.i h = m_bottom.i - m_top.i buffer m_type.i, w, h, screen_offscreen dim bitmap, w*h if layer_ch.i != 4 : repeat length(bitmap) : bitmap.cnt = $FF000000 : loop ; ☆追加 repeat layer_ch.i chimg_comp = wpeekB(buf, idx) dupptr chimg_data, varptr(buf)+idx+2, layer_chlen.i.cnt-2, 2 idx += layer_chlen.i.cnt if chimg_comp == 1 { ; RLE compressed the image data unpackbits bitmap, chimg_data, h, layer_chid.i.cnt } else { ; Not supported (0: Raw, 2 or 3: Zip) logmes "未サポートの圧縮です。" } loop celbitmap m_type.i, bitmap, 1; ☆4000pxなどの大きい画像は落ちる模様HSP側なので修正不可能 dim bitmap } else { repeat layer_ch.i : idx += layer_chlen.i.cnt : loop } loop ; + Global layer mask info ---------------------- ; Image Data Section ;------------------------------------------------ sdim buf : gsel 0 return img_num #global #endif ; ★ここからテスト★ #uselib "user32.dll" ; マウス位置取得用 #func ScreenToClient "ScreenToClient" int, var #define boxs(%1,%2,%3,%4) line (%3),(%2),(%1),(%2):line (%3),(%4):line (%1),(%4):line (%1),(%2) #define SCR_SIZE 512 #define FONT_SIZE 24 screen 0, SCR_SIZE, SCR_SIZE : font "", FONT_SIZE ; 背景の市松模様を作成 buffer 1, ginfo_sy, ginfo_sx, screen_offscreen : div = 16 dim bitmap, ginfo_sy*ginfo_sx repeat length(bitmap) : x = cnt/ginfo_sx/div\2 : y = cnt/div\2 : bitmap.cnt = $ffffffff - ((x^y)*$222222) : loop celbitmap 1, bitmap : dim bitmap ; ★PSD読み込み dialog "psd",16,"PSDファイル" : psdload psd, refstr, 2 : layer_ofs = 0,0 *MAIN ; 処理 ------------------------------------------ cursor = ginfo_mx, ginfo_my : ScreenToClient hwnd, cursor : stick pad, 512 ; ★左クリックで表示やフォルダ折り畳み切り替え if cur_layer>-1 && pad==256 { if cursor.0<FONT_SIZE { psdSetFlags psd, cur_layer, psdFlags(psd, cur_layer)^2 } else { psdSetFolder psd, cur_layer, psdIsFolderOpen(psd, cur_layer)^1 } } ; ★右クリックで画像移動 click_r = (click_r<<1 | pad==512) & 3 if click_r == 1 : old_cursor = cursor.0, cursor.1 if click_r == 3 : repeat 2 : layer_ofs.cnt -= old_cursor.cnt - cursor.cnt : loop : old_cursor = cursor.0, cursor.1 if click_r == 2 : layer_ofs = limit(layer_ofs.0, -psdWidth(psd), psdWidth(psd)), limit(layer_ofs.1, -psdHeight(psd), psdHeight(psd)) ; ★ホイールでレイヤー情報の表示位置移動 _mousew = mousew : tree_pos = limit(tree_pos+FONT_SIZE*((_mousew>0)-(_mousew<0)), -tree_cnt*FONT_SIZE+FONT_SIZE,0) ; 階層情報を求める level = 0 repeat psdCount(psd) : i = cnt if psdIsFolder(psd, i) : level-- ; フォルダなら階層を下げる if psdIsDivider(psd, i) : level++ ; セクション区切りなら階層を戻してスキップ psd_level.i = level loop ; 階層の表示状態を求める repeat psdCount(psd) : i = cnt psd_hidden.i = psdIsVisible(psd, i) if psdIsFolder(psd, i) == 0 : continue target_level = psd_level.i + 1 repeat i, 1 : j = i - cnt if psdIsDivider(psd, j) && (target_level == psd_level.j) : break if (target_level <= psd_level.j) : psd_hidden.j &= psdIsVisible(psd, i) loop loop ; フォルダの折り畳み状態を求める repeat psdCount(psd) : i = cnt psd_fold.i = 0 if psdIsFolder(psd, i) == 0 : continue if psdIsFolderOpen(psd, i) : continue target_level = psd_level.i + 1 repeat i, 1 : j = i - cnt if psdIsDivider(psd, j) && (target_level == psd_level.j) : break if (target_level <= psd_level.j) : psd_fold.j = 1 loop loop ; 描画 ------------------------------------------ redraw 0 gmode 0 : pos 0,0 : gcopy 1, 0,0,ginfo_sx,ginfo_sy ; ★レイヤー画像表示:PSDのレイヤー情報は最背面から記憶されています。 repeat psdCount(psd) : i = cnt if psdIsLayer(psd, i) == 0 : continue if psd_hidden.i == 0 : continue if psdBlend(psd, i) == "norm" { gmode 3, w, h, psdOpacity(psd, i) } ; ついでにHSPにある合成モードテスト else:if psdBlend(psd, i) == "lddg" { gmode 5, w, h, psdOpacity(psd, i) } ; 加算合成の計算が多少違う模様 else:if psdBlend(psd, i) == "fsub" { gmode 6, w, h, psdOpacity(psd, i) } ; 減算合成の計算が多少違う模様 pos layer_ofs.0, layer_ofs.1 : gcopy psdType(psd, i) loop ; ★レイヤー階層表示:最前面からの方が分かりやすい為、逆順で処理します。 pos 0, tree_pos cur_layer = -1 : target_level = -1 : tree_cnt = 0 repeat psdCount(psd), 1 : i = psdCount(psd) - cnt if psd_fold.i : continue if psdIsDivider(psd, i) : continue ; カーソル if (ginfo_cy<cursor.1) && (cursor.1<ginfo_cy+FONT_SIZE) && (0<cursor.0) && (cursor.0<ginfo_winx) { cur_layer = i } ; 情報表示 rgbcolor $111111 if psdIsVisible(psd, i) { mes "○", 1 : if psd_hidden.i == 0 { pos 0 : mes "/", 1 } } else { mes "−", 1 } pos ginfo_cx+psd_level.i*FONT_SIZE if psdIsLayer(psd, i) { ; サムネ w = psdRight(psd, i)-psdLeft(psd, i) : h = psdBottom(psd, i)-psdTop(psd, i) if w > h { s = 1.0, double(h)/w } else { s = double(w)/h, 1.0 } ofs = (1.0-s.0) * FONT_SIZE, (1.0-s.1) * FONT_SIZE, double(ginfo_cx), double(ginfo_cy) gmode 3, 0, 0, 255 gzoom FONT_SIZE, FONT_SIZE, 1,,,FONT_SIZE*4, FONT_SIZE*4 pos ofs.2+ofs.0, ofs.3+ofs.1 gzoom s.0*FONT_SIZE, s.1*FONT_SIZE, psdType(psd, i),0,0,w, h boxs ofs.2, ofs.3, ofs.2+FONT_SIZE, ofs.3+FONT_SIZE pos ofs.2+FONT_SIZE, ofs.3 } else { if psdIsFolderOpen(psd, i) { mes "∨", 1 } else { mes ">", 1 } } mes psdName(psd, i) pos 0 : tree_cnt++ loop redraw 1 : await 16 goto *MAIN



名無し

リンク

2025/12/11(Thu) 21:17:55|NO.104501

dish版試してみましたが確かにエラーも出ずに落ちますね
わざわざ意見を組んでくださってありがとうございます。
自分は通常版の方で何とかならないか、のんびりと模索してみる事にします



naya

リンク

2025/12/11(Thu) 23:00:20|NO.104502

Dish版でテストしてみました。
だいたい256x256px以上のサイズではエラー表示にならずに落ちるようです。

テスト画像(AzPainter2で作成)
https://ss1.xrea.com/nayalabo.g1.xrea.com/test/test2_480.psd(表示不可)
https://ss1.xrea.com/nayalabo.g1.xrea.com/test/test2_256.psd(表示可)



naya

リンク

2025/12/11(Thu) 23:35:41|NO.104503

連投ですみません。
celbitmap命令はメモリー操作の段階(推測)で潜在的なエラーが発生して
アプリが落ちる場合がありますね。
詳細は不明です。



usagi

リンク

2025/12/12(Fri) 03:10:39|NO.104506

わわっ、とってもステキなPSDサンプルですね。そうそうこういうのって感じでした。

おっしゃる通りcelbitmapで落ちてしまう為、私にはどうにも解決できず。。。たぶん2の倍数の必要があるのかもしれません。
ただ、発見としてDishからhgimg4にすると落ちなくなります。celbitmapの内部的な挙動が違うのかもしれませんね。

それで、これでもうダイジョブ!と思ったのですが、なんと上下反転されてしまい、いろもaとbが逆転してしまいます。
(p2が効かないしテクスチャの反転してるので不具合報告でもいいのかも)
対応として以下の修正をしました。

1)6行目;hgimg4に変更

#include "hgimg4.as"
2)35行目;色の順番をかえる

__c = _id * 8 & $1F ; ★Writing color bit (AABBGGRR)
3)186行目:上下反転する

dim swap, w*h : ws = w*4 : vs = varsize(swap) ; ★上下反転する repeat h : memcpy swap, bitmap, ws, cnt*ws, vs-cnt*ws-ws : loop celbitmap m_type.i, swap, 1 : dim bitmap: dim swap



じゅんちゃんおじ

リンク

2025/12/12(Fri) 10:41:42|NO.104507

HSP3が盛り上がるいろんな実験があって良いなと思ってみています。

初心者の方に参考ヒント: Qiita
【解説】PSDファイルの仕様(基本構造編)

https://qiita.com/vram/items/2632cb9ed2dec9430f59

.psd の画像ファイルが手元に無いときは、
オリジナルTシャツとか名刺をつくってくれる印刷会社のサイトに行くと
適当なサンプルファイルが置いてあります。

「レイヤー」って何?

https://www.adobe.com/jp/learn/photoshop/web/newyearcard-dl

現在では、Googleドライブにアップロードしてプレビュー(中身確認)でそのままPSDファイルを開ける時代ですが、
このスレッドやサンプルスクリプトは、「アプリ独自の保存形式ファイルに見えても、やろうと思えばなんでも開けるし、
解析すればファイルコンバータも自分で作ることができるんだな」
という発想の手がかり(気づき)になるところが勉強になってとても良いと思いました。



記事削除

記事NO.パスワード
(質問が解決したスレッドは他の利用者に活用してもらうため、削除しないようお願いします)

NO.104475への返信

マスコット

好きなマスコットを選んでください。

名前

e-mail
HOME
  1. 初めて利用する方は、HSP3掲示板の使い方をお読みください。
  2. 不要部分の多い長いスクリプトの投稿は ご遠慮ください。
  3. 書き込みは自動改行されません。適度に改行を入れてください。
  4. スクリプトは小文字の<pre>〜</pre>で囲むと見やすく表示できます。

削除用パスワード

エラー発生時、再送信すると二重送信になることがあります。
回答が得られたら、お礼書き込み時に[解決]チェックしてください。
SPAM防止のためURLから始まる文章は投稿できません。
SPAM防止のため英文字のみの本文を投稿することはできません。

ONION software Copyright 1997-2025(c) All rights reserved.