|
|
2023/10/25(Wed) 02:07:59|NO.100348
パレットモードで半透明コピーを行うにはどうすれば良いでしょうか?
もちろん、パレットモードですから、正確な半透明コピーはできないことは承知しています。
なので、「それっぽい半透明コピー」ができれば十分です。
調べてみると、過去にはage.dll(あーじゅ)と呼ばれているものがあり、
HSP2ではこれを使って実現することができるようでした。
HSP3で実装しようとするならば
フルカラーモードで半透明合成→フルカラーモードでの表示を、パレットモードに減色させる
という手順を踏む必要があるものと考えています。
その際に、フルカラーモードでのドットをなるべく近い値のパレットの色に置き換えるという
操作が必要になるかと思いますが、どのようにして近い値を選べばよいか、
またその処理をどうやって高速に行えばよいか見当がつきません。
なお、このやり方にこだわるわけではないので、パレットモードでそれっぽい半透明コピーが
できる方法があるならば、その方法でも構いません。
どなたかご教示いただけますでしょうか。
|
|
2023/10/25(Wed) 11:52:21|NO.100350
原始的なやり方ですが、コピーしたい画像に市松模様状に透過ドットを並べ、
擬似的な半透明コピーを行うというのはどうでしょうか?
//Bayerマトリクスを作成
dim bayer,4,4
bayer(0,0)= 0, 8, 2,10
bayer(0,1)=12, 4,14, 6
bayer(0,2)= 3,11, 1, 9
bayer(0,3)=15, 7,13, 5
screen 0,640,480,1
//パレットの作成
repeat 8
cnt0=cnt : r=cnt*255/7
repeat 8
cnt1=cnt : g=cnt*255/7
repeat 4
b=cnt*255/3
x=cnt0*32 + cnt1*4 + cnt
palette x,r,g,b,x=255
loop
loop
loop
mref mpal,69 //パレットコピー用
buffer 1,640,480,1 : gsel 1
mref pal1,69 : memcpy pal1,mpal,768,0,0 : palette 0,0,0,0,1 //パレットをコピー
//画像を用意
palcolor 0 : boxf
palcolor 224 : circle 0,0,640,480,1 //赤い楕円を作ってみる
//Bayerマトリクスを元に透過ドット(0,0,0)を並べる
alpha=8 //不透明度。0〜16の範囲で指定。中間の8だと市松模様に。
//以下、psetだと時間がかかるので、VRAMに直接書き込む。
mref vram,66
vram_wid=(640+3)&0xFFFFFFFC //パレットのモードの場合、VRAMの横幅は「(横幅+3)&0xFFFFFFFC」
repeat 480
cnt0=cnt : bas=vram_wid*cnt
repeat 640
if bayer(cnt\4,cnt0\4)>=alpha : poke vram,bas+cnt,0 //0番パレットが(0,0,0)のパレット
loop
loop
//以下、実際に擬似的な透過コピーを行う
gsel 0
palcolor 28 : boxf //緑で塗り潰してみる
gmode 2
pos 0,0 : gcopy 1,0,0,640,480
|
|
2023/10/26(Thu) 02:09:27|NO.100351
ご回答ありがとうございます。
色差が激しいとドットが気になってしまいますが、
それがパレットモードの限界ではあるんでしょうし、受け入れることにします。
「それっぽい半透明コピー」という目標は達成できそうなので、解決とします。
|
|
2023/10/26(Thu) 12:12:09|NO.100352
名後さん
こんにちは。Dripです。
もっと色々な手段が論議されるのかと思いきや解決されてしまいそうだったので…;
どのような目的でパレットモード上で半透明コピーを行いたいかによって解決の仕方が全く変わってくるかと思うのですが、私の意見を書かせていただこうと思います。
【A】処理速度はどんなに遅くてもいいのでとにかく高画質な半透明近似色コピーを行いたい
この場合は減色処理のアルゴリズムを自分で調べて1ピクセルずつpget/psetで処理して近似色を配置していきましょう。
時間はかかりますがアルゴリズム次第ではこれが一番美しい描画結果になるはずです。
パレットモードのツールアプリやご褒美イラストの表示等、限定的な利用方法ではこのやり方で良いと思います。
【B】速度を重視しつつディザ等も併用して高速で美しい半透明近似色コピーを行いたい
Aの方法に近いですが、速度も重視する場合はマシン語を使用したり、プラグインを利用して高度なコピーを実装する形になります。
【C】メモリや手間を食ってもいいのでパレットモードの速度的優位性を最大限重視したコピーを行いたい
この場合は自分で半透明になる画像パターンを全種類用意し、コピーする状況に応じてどの画像をコピーするか選んでgmode 1や2で高速コピーします。
実質的には半透明コピーではなく、予め半透明の描画結果を作成して画像を通常コピーするだけです。
この方法はパレットモードで描画速度を重視するゲーム製作などで最も利用する解決策になるはずです。
【D】ある程度精度を犠牲にしてもいいので高速で簡単に近似色コピーを行いたい
この場合はgmode 0を使って近似色コピーを行うのが簡単です。
ただし、パレットID管理を度外視しているため全パレットの中から近似色が適当に割り当てられます。
指定範囲のパレットを利用したい等の場合は更に手の込んだ応用を利かせる必要があります。
常用手段ではない点に注意してください。
ある程度高速ではありますが、フルカラーの通常gcopyより更に鈍足になるためパレットモードの速度的優位性は発揮されません。
また近似色がパレットに殆ど存在しない場合、コピー品質は相当劣化することになります。
この方法は標準HSPの描画ウィンドウでのみ利用できます。通常、DirectX等の描画では利用できません。
以下のサンプルではgcopy_pal命令に定義した処理がこの手段の中核となります。
動作結果と合わせて参考にしていただければ幸いです。
#module
//現在のバッファの次に一時バッファを用意する命令
#deffunc gcopy_pal_init
wx=ginfo_winx:wy=ginfo_winy //現在のバッファの解像度取得
buffer ginfo_sel+1,wx,wy*2 //新規バッファに指定バッファのフルカラー作成
//↑このbufferをscreenにすると一時バッファの様子がわかります
gmode 0,wx,wy:pos 0,0:gcopy ginfo_sel-1,0,0 //元バッファの内容を上半分にコピー
gsel ginfo_sel-1 //選択ウィンドウを戻す
return
//パレットモードでも強制的にmodeのコピーモードとalpの濃度で描画する命令定義
//gcopy_pal buf,px,py,sx,sy,mode,alp
// buf: コピー元バッファID
// px, py: コピー元X, コピー元Y
// sx, sy: サイズX, サイズY,
// mode, alp: コピーモード, コピー濃度
#deffunc gcopy_pal int buf,int px,int py,int sx,int sy,int mode,int alp
nbuf=ginfo_sel:cx=ginfo_cx:cy=ginfo_cy //現在の選択バッファIDと描画位置
gsel buf+1:wy=ginfo_winy //一時バッファの下半分を選択
pos 0,wy/2:gmode 0,sx,sy:gcopy nbuf,cx,cy,sx,sy //一時バッファに現在のコピー先の状態を切り出す
gmode mode,sx,sy,alp:gcopy buf+1,px,py,sx,sy //コピー元のフルカラー版から指定範囲を合成
gsel nbuf:gmode 0,sx,sy:gcopy buf+1,0,wy/2,sx,sy //結果をコピー先へgmode 0で近似色転送する
return
#global
#include "hsp3util.as" //パレット配置用
buffer 3,,,1
bmppalette dir_exe+"\\sample\\hgimg3\\testchr.bmp" //パレット配置
picload dir_exe+"\\sample\\hgimg3\\testchr.bmp" //256色bmp
gcopy_pal_init //現在のバッファの状態で近似色コピー(gcopy_pal)の準備をする
celdiv 3,64,64 //通常のコピーではcelputも可なので定義
screen 0,400,300,1
wx=ginfo_winx, wx/64+(wx\64!0) //現在の解像度とセル解像度
wy=ginfo_winy, wy/64+(wy\64!0)
repeat //メインループ
redraw 0
gmode 1 //背景をクリア(パレットモードで高速)
repeat wx(1)*wy(1)
pos cnt\wx(1)*64,cnt/wx(1)*64:celput 3,1
loop
color 255,255,255 //文字色
//青い火をgmode 5と指定濃度で加算コピー
pos cnt*2\wx,cos(0.01*cnt)*wy/3+wy/2
gcopy_pal 3,128,64,64,64, 5,128+sin(0.02*cnt)*126
mes "gmode 5"
//赤い火をgmode 4と指定濃度で半透明コピー
pos cnt\wx,sin(0.01*cnt)*wy/3+wy/2
gcopy_pal 3,196,64,64,64, 4,128+sin(0.01*cnt)*126
mes "gmode 4"
pos 10,10:mes "パレットモードで描画中"
redraw 1
await 30
loop
| |
|
2023/10/27(Fri) 21:29:49|NO.100358
追加でご回答ありがとうございます。
選択肢は多いにこしたことはないので助かります。Cの考え方は目からウロコでした。
市松模様かDの考え方で必要に応じてCの考え方を使うんだろうと思いますが、なんとかやってみます。
|
|
2023/10/28(Sat) 11:46:39|NO.100361
【A】のやり方ならメディアンカット法やk平均法が使えますね。
以下は自分が考えた【D】のやり方になります、一応フルカラーを減色して描写しています。
;フルカラーウィンドウ描写
title"フルカラー"
picload ""+dir_exe+"/sample/hgimg3/sb_bg1.bmp"
imgw = ginfo(12)
imgh = ginfo(13)
buffer 1,200,200
color 255:boxf
gsel 0
gmode 3,,,128
pos 100,100
gcopy 1,,,200,200
;フルカラーを減色
divr = 37
divg = 37
divb = 37
dim colorflg,256,256,256
dim colorpalid,256,256,256
dim imgpalid,imgw,imgh
screen 2,imgw,imgh,1
title"パレットカラー"
colid = 0
repeat imgh : j = cnt
repeat imgw : i = cnt
gsel 0
pget i,j
tr = ginfo(16)/divr
tg = ginfo(17)/divg
tb = ginfo(18)/divb
if colorflg(tr,tg,tb){
imgpalid(i,j) = colorpalid(tr,tg,tb)
continue
}
colorflg(tr,tg,tb) = 1
colorpalid(tr,tg,tb) = colid
imgpalid(i,j) = colorpalid(tr,tg,tb)
gsel 2
palette colid, tr*divr, tg*divg, tb*divb
colid++
loop
loop
;パレットウィンドウ描写
gsel 2
palette ,,,,1
redraw 0
repeat imgh : j = cnt
repeat imgw : i = cnt
palcolor imgpalid(i,j)
pset i,j
loop
loop
redraw
実行してもらうと分かる通り処理は早くないし画質も低いので、もし使う場合は改良することを勧めます。
|
|
2023/10/28(Sat) 20:41:37|NO.100368
何度も消しては再投稿を繰り返しててすみません。
たぶんこれで終わりだと思います。
GetNearestPaletteIndexなるAPIの存在を知り、これを使えば
誤差拡散法による減色の速度が予想してたよりも速くなるのではないかと思い、
ちょっとやってみました。
(もしかすると何かバグがあるかもしれませんが……)
#include "gdi32.as"
#module
//gcopy_ed p1,p2,p3,p4,p5,p6,p7
//p1(0) : コピー元のバッファ
//p2(0),p3(0) : コピー元の左上のx,y座標
//p4,p5 : コピーする横幅、縦幅。省略時はgmodeで指定した横幅・縦幅が適用される、
//p6(300),p7(301) : 必要に応じて使用する作業用バッファの番号
//
//通常のgcopyと基本的に同じだが、コピー先のウィンドウがパレットモードの場合、
//必要に応じて誤差拡散法を用いた減色処理を行う。(その際少し時間がかかります)
//gmodeで指定したコピーモードやブレンド率も反映されます。
#define global gcopy_ed(%1=0,%2=0,%3=0,%4=0xFFFFFFFF,%5=0xFFFFFFFF,%6=300,%7=301) _gcopy_ed %1,%2,%3,%4,%5,%6,%7
#deffunc _gcopy_ed int buf0,int _x0,int _y0,int w0,int h0,int buf2,int buf3
mref bmscr,67
if w0=0xFFFFFFFF : w=bmscr(33) : else : w=w0
if h0=0xFFFFFFFF : h=bmscr(34) : else : h=h0
if w<=0 | h<=0 : return
if bmscr(3)=0 {
//コピー先がフルカラーの場合はそのままコピーして終わり
gcopy buf0,_x0,_y0,w,h
return
}
buf1=ginfo_sel : x1=ginfo_cx : y1=ginfo_cy
mode=bmscr(35) : alpha=bmscr(65) //gmodeで指定されたモードとアルファ値を取得
if _x0<0 : w+=_x0 : x1-=_x0 : x0=0 : else : x0=_x0
if x1<0 : x0-=x1 : w+=x1 : x1=0
if _y0<0 : h+=_y0 : y1-=_y0 : y0=0 : else : y0=_y0
if y1<0 : y0-=y1 : h+=y1 : y1=0
w=limit(w,,ginfo_sx-x1) : h=limit(h,,ginfo_sy-y1)
if w<=0 | h<=0 : return //もしx1がginfo_sx以上だったりy1がginfo_sy以上だったりした場合はここで引っかかる
gsel buf0
w=limit(w,,ginfo_sx-x0) : h=limit(h,,ginfo_sy-y0)
if w<=0 | h<=0 : gsel buf1 : return //同上
buffer buf2,w,h,0 : gsel buf2
if mode>=2 {
gmode 0 : pos 0,0 : gcopy buf1,x1,y1,w,h
}
if mode>=3 {
buffer buf3,w,h,0 : gsel buf3 : gmode 0
pos 0,0 : gcopy buf0,x0,y0,w,h
gsel buf2 : gmode mode,,,alpha : pos 0,0 : gcopy buf3,x0,y0,w,h
buffer buf3,1,1 : gsel buf2
} else {
gmode (mode=2)*2
pos 0,0 : gcopy buf0,x0,y0,w,h
}
mref vram2,66 : vram_wid2=(w*3+3)&0xFFFFFFFC
gsel buf1
mref vram1,66 : mref pal1,69 : vram_wid1=(ginfo_sx+3)&0xFFFFFFFC
vram_start1=(ginfo_sy-y1-h)*vram_wid1+x1
err_flag=0
dim gap,3 //青・緑・赤の順
repeat h
vram_bas1=vram_start1+cnt*vram_wid1
vram_bas2=cnt*vram_wid2
last_y=(cnt=h-1)
r2l=cnt&0x1 //これが1の時は右から左へのサーチ
repeat w
if r2l : cnt0=w-1-cnt : else : cnt0=cnt
vram_pos2=vram_bas2+cnt0*3
repeat 3 : gap(cnt)=peek(vram2,vram_pos2+cnt) : loop //元の絵の色を取得
a=gap(0)<<16 | gap(1)<<8 | gap(2)
GetNearestPaletteIndex bmscr(10),a : p_num=stat //最も近いパレットを探す
if p_num=0xFFFFFFFF : err_flag=1 : break
poke vram1,vram_bas1+cnt0,p_num //パレット番号をvramに書き込む
repeat 3 : gap(cnt)-=peek(pal1,p_num*3+2-cnt) : loop //元の色と実際の色の差分
//誤差拡散
//次のドットへの拡散
if cnt!=w-1 {
repeat 3
if gap(cnt)>=-2 & gap(cnt)<=2 : continue
vram_tgt2=vram_pos2+cnt+3-6*r2l
poke vram2,vram_tgt2,limit(peek(vram2,vram_tgt2)+gap(cnt)*7/16,0,255)
loop
}
if last_y : continue
vram_pos20=vram_pos2+vram_wid2
//上後方への拡散
if cnt!=0 {
repeat 3
if gap(cnt)>=-5 & gap(cnt)<=5 : continue
vram_tgt2=vram_pos20+cnt-3+6*r2l
poke vram2,vram_tgt2,limit(peek(vram2,vram_tgt2)+gap(cnt)*3/16,0,255)
loop
}
//真上への拡散
repeat 3
if gap(cnt)>=-3 & gap(cnt)<=3 : continue
vram_tgt2=vram_pos20+cnt
poke vram2,vram_tgt2,limit(peek(vram2,vram_tgt2)+gap(cnt)*5/16,0,255)
loop
//上前方への拡散
if cnt!=w-1 {
repeat 3
if gap(cnt)>=-15 & gap(cnt)<=15 : continue
vram_tgt2=vram_pos20+cnt+3-6*r2l
poke vram2,vram_tgt2,limit(peek(vram2,vram_tgt2)+gap(cnt)/16,0,255)
loop
}
loop
if err_flag : break
loop
buffer buf2,1,1,0 : gsel buf1 //作業用バッファを解放
if (bmscr(19)>>16)&0xFFFF : redraw 1,x1,y1,w,h //redrawの必要があるのなら
return
#global
//以下、使用例
screen 0,640,480,1
//パレットの作成
repeat 8
cnt0=cnt : r=cnt*255/7
repeat 8
cnt1=cnt : g=cnt*255/7
repeat 4
b=cnt*255/3
x=cnt0*32 + cnt1*4 + cnt
palette x,r,g,b,x=255
loop
loop
loop
mref mpal,69 //パレットコピー用
buffer 1,640,480,1 : gsel 1
mref pal1,69 : memcpy pal1,mpal,768,0,0 : palette 0,0,0,0,1 //パレットをコピー
//画像を用意
palcolor 146 : boxf
palcolor 224 : circle 0,0,640,480,1 //赤い楕円を作ってみる
//以下、実際に透過コピーを行う
gsel 0
palcolor 28 : boxf //緑で塗り潰してみる
gmode 3,,,128 //半透明合成コピーで、ブレンド率は128にする
pos 0,0 : gcopy_ed 1,0,0,640,480
| |
|