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


HSPTV!掲示板


未解決 解決 停止 削除要請

2023
1025
名後パレットモードで半透明コピーを行うには?6解決


名後

リンク

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

ご回答ありがとうございます。
色差が激しいとドットが気になってしまいますが、
それがパレットモードの限界ではあるんでしょうし、受け入れることにします。
「それっぽい半透明コピー」という目標は達成できそうなので、解決とします。



Drip

リンク

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



記事削除

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

NO.100348への返信

マスコット

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

名前

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

削除用パスワード

解決したら質問者本人がここをチェックしてください。

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

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