|
 |
|
|
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

| |
|
|
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ファイル内の画像をレイヤー毎に扱える様になる、とても便利なモジュールです。
本モジュールを利用してボーンアニメーション作成ソフトなども自作できそう!
|
|
|
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みたいなものに利用できるとよさそうですよねー。
|
|
|
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です。
|
|
|
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の部分で処理スキップした(あるいはその部分が読み込めてない)結果かもしれないですね
|
|
|
2025/12/11(Thu) 18:13:46|NO.104498
>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版試してみましたが確かにエラーも出ずに落ちますね
わざわざ意見を組んでくださってありがとうございます。
自分は通常版の方で何とかならないか、のんびりと模索してみる事にします
|
|
|
2025/12/11(Thu) 23:00:20|NO.104502
|
|
|
2025/12/11(Thu) 23:35:41|NO.104503
連投ですみません。
celbitmap命令はメモリー操作の段階(推測)で潜在的なエラーが発生して
アプリが落ちる場合がありますね。
詳細は不明です。
|
|
|
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
|
|