以前疑似アルファのタイル転送を作った後、いろいろ弄っていて気付いた点を二つほど。
関数テーブルもどき
うーん? Python が内部でC言語が走っているってのはなんとなく聞いていたのですが…なら関数テーブルを作れないかな?と、思ったらクラスのメンバー関数を辞書登録する方法が見つかりまして。ただ、流石に1ドット打つごとに文字列をキーとした検索を行っていたら重すぎると思っていたら、数字でもできると判り試してみました…
ソースここから
import pyxel
SCREEN_SIZE_W = 160
SCREEN_SIZE_H = 120
CENTER_X = 80
CENTER_Y = 60
EMPTY = -32767
ALPHA_NON = 0
ALPHA_HALF_1 = 1
ALPHA_HALF_2 = 2
ALPHA_QUARTER_1 = 3
ALPHA_QUARTER_2 = 4
ALPHA_QUARTER_3 = 5
ALPHA_QUARTER_4 = 6
my_table = (
0, 6, 13, 19, 25, 31, 37, 43, 49, 55, 60, 66, 71, 76, 81, 86,
91, 95, 99, 103, 106, 110, 113, 116, 118, 121, 122, 124, 126, 127, 127, 128,
128, 128, 127, 127, 126, 124, 122, 121, 118, 116, 113, 110, 106, 103, 99, 95,
91, 86, 81, 76, 71, 66, 60, 55, 49, 43, 37, 31, 25, 19, 13, 6,
0, -6, -13, -19, -25, -31, -37, -43, -49, -55, -60, -66, -71, -76, -81, -86,
-91, -95, -99,-103,-106,-110,-113,-116,-118,-121,-122,-124,-126,-127,-127,-128,
-128,-128,-127,-127,-126,-124,-122,-121,-118,-116,-113,-110,-106,-103,- 99, -95,
-91, -86, -81, -76, -71, -66, -60, -55, -49, -43, -37, -31, -25, -19, -13, -6,
0, 6, 13, 19, 25, 31, 37, 43, 49, 55, 60, 66, 71, 76, 81, 86,
91, 95, 99, 103, 106, 110, 113, 116, 118, 121, 122, 124, 126, 127, 127, 128
)
def my_sin(angle,r):
angle &= 127
return r * my_table[angle]>>7
def my_cos(angle,r):
angle &= 127
angle += 32
return r * my_table[angle]>>7
class VBresenham:
def __init__(self):
self.l_side = 0
self.s_side = 0
# horizonatial side is longer
self.b_long_hs = True
# Direction (1 or -1)
self.dct_x = 1
self.dct_y = 1
self.counter = 0
self.adder_x = 0
self.adder_y = 0
self.start_x = 0
self.start_y = 0
# start x,y terminus x,y
def setup(self,s_x,s_y,t_x,t_y):
self.b_long_hs = True
self.dct_x = 1
self.dct_y = 1
self.counter = 0
self.adder_x = 0
self.adder_y = 0
self.start_x = s_x
self.start_y = s_y
if (s_x > t_x):
self.l_side = s_x - t_x
self.dct_x = -1
else:
self.l_side = t_x - s_x
if (s_y > t_y):
self.s_side = s_y - t_y
self.dct_y = -1
else:
self.s_side = t_y - s_y
if (self.s_side > self.l_side):
# swap
self.s_side,self.l_side = self.l_side,self.s_side
self.b_long_hs = False
self.l_side += 1
self.s_side += 1
return self.l_side
def step(self):
self.counter += self.s_side
i = self.counter - self.l_side
ret_x = self.start_x+self.adder_x
ret_y = self.start_y+self.adder_y
if self.b_long_hs:
self.adder_x += self.dct_x
if i >= 0:
self.counter = i
self.adder_y += self.dct_y
else:
self.adder_y += self.dct_y
if i >= 0:
self.counter = i
self.adder_x += self.dct_x
return ret_x,ret_y
class Bresenham:
def __init__(self):
self.l_side = 0
self.s_side = 0
self.counter = 0
self.adder = 0
# Src side is longer
self.b_long_ss = True
def setup(self,Dest_side,Src_side):
self.counter = 0
self.adder = 0
if Dest_side > Src_side:
self.b_long_ss = False
self.l_side = Dest_side+1
self.s_side = Src_side+1
return self.l_side
self.b_long_ss = True
self.l_side = Src_side+1
self.s_side = Dest_side+1
return self.s_side
def step(self):
ret = self.adder
if self.b_long_ss:
self.counter += self.s_side
i = self.counter - self.l_side
if i >= 0:
self.counter = i
self.adder += 1
else:
while self.counter <= self.l_side:
self.adder += 1
self.counter += self.s_side
self.counter -= self.l_side
return ret
class LineBuffer:
def __init__(self):
self.l = 0 # left
self.r = 0 # right
# 始点
self.src_sx = 0
self.src_sy = 0
# 終点
self.src_tx = 0
self.src_ty = 0
class RollBlt:
def __init__(self):
self.buff = []
for i in range(SCREEN_SIZE_H):
self.buff.append(LineBuffer())
self.top = 0
self.bottom = 0
self.vbre = VBresenham()
self.bre = Bresenham()
self.once = True
self.tile = {
ALPHA_NON :self._non,
ALPHA_HALF_1 :self._half1,
ALPHA_HALF_2 :self._half2,
ALPHA_QUARTER_1 :self._quarter1,
ALPHA_QUARTER_2 :self._quarter2,
ALPHA_QUARTER_3 :self._quarter3,
ALPHA_QUARTER_4 :self._quarter4,
}
def setup(self):
pass
# タイル転送用内部関数群
def _non(self,x,y,u,v):
pyxel.blt(x, y, 0, u, v, 1, 1)
def _half1(self,x,y,u,v):
if x&1 == y&1:
pyxel.blt(x, y, 0, u, v, 1, 1)
def _half2(self,x,y,u,v):
if x&1 != y&1:
pyxel.blt(x, y, 0, u, v, 1, 1)
def _quarter1(self,x,y,u,v):
if (x&1) + (y&1)==0:
pyxel.blt(x, y, 0, u, v, 1, 1)
def _quarter2(self,x,y,u,v):
if ((x+1)&1) + (y&1)==0:
pyxel.blt(x, y, 0, u, v, 1, 1)
def _quarter3(self,x,y,u,v):
if (x&1) + ((y+1)&1)==0:
pyxel.blt(x, y, 0, u, v, 1, 1)
def _quarter4(self,x,y,u,v):
if (x&1) + (y&1)==2:
pyxel.blt(x, y, 0, u, v, 1, 1)
# 原点座標中央位置補正コピー
def blt_c(self,dest_x, dest_y, bank, src_x, src_y, src_w, src_h, angle, scale_w=128, scale_h=128,alpha=ALPHA_NON):
h = src_h * scale_h >> 8
w = src_w * scale_w >> 8
x = dest_x - my_sin(angle,h) - my_sin(angle+32,w)
y = dest_y - my_cos(angle,h) - my_cos(angle+32,w)
self.blt(x, y, bank, src_x, src_y, src_w, src_h, angle, scale_w, scale_h, alpha)
def blt(self,dest_x, dest_y, bank, src_x, src_y, src_w, src_h, angle, scale_w=128, scale_h=128, alpha=ALPHA_NON):
# 角度のまるめ
angle &= 127
# 原点から見た縦方向の端の座標
h = src_h * scale_h >> 7
tv_x = my_sin(angle,h-1)+dest_x
tv_y = my_cos(angle,h-1)+dest_y
# 原点から見た横方向の端の座標
w = src_w * scale_w >> 7
th_x = my_sin(angle+32,w-1)+dest_x
th_y = my_cos(angle+32,w-1)+dest_y
# 4頂点と比較し上下の座標を求める
self.top = dest_y
if tv_y < self.top:
self.top = tv_y
if th_y < self.top:
self.top = th_y
if (tv_y + th_y - dest_y) < self.top:
self.top = tv_y + th_y - dest_y
if self.top < 0:
self.top = 0
self.bottom = dest_y
if tv_y > self.bottom:
self.bottom = tv_y
if th_y > self.bottom:
self.bottom = th_y
if (tv_y + th_y - dest_y) > self.bottom:
self.bottom = tv_y + th_y - dest_y
if self.bottom > (SCREEN_SIZE_H-1):
self.bottom = SCREEN_SIZE_H-1
# テーブル初期化
for y in range (self.top, self.bottom+1):
self.buff[y].l = EMPTY
self.buff[y].r = EMPTY
# 画像縦方向処理
reg = self.vbre.setup(dest_x,dest_y,tv_x,tv_y)
self.bre.setup(src_h-1,reg-1)
match angle:
# 垂直にコピーする時は縦方向の処理を行わない
case a if a == 32 or a == 96:
pass
case a if a < 32:
for i in range (reg):
x,y = self.vbre.step()
add_src_y = self.bre.step()
# MainSide
if y >= 0 and y < SCREEN_SIZE_H:
if self.buff[y].l == EMPTY:
self.buff[y].l = x
#pyxel.pset(x,y,6)
self.buff[y].src_sx = src_x
self.buff[y].src_sy = src_y + add_src_y
x = x - dest_x + th_x
y = y - dest_y + th_y
# SubSide
if y >= 0 and y < SCREEN_SIZE_H:
self.buff[y].r = x
#pyxel.pset(x,y,6)
self.buff[y].src_tx = src_x + src_w-1
self.buff[y].src_ty = src_y + add_src_y
case a if a < 64:
for i in range (reg):
x,y = self.vbre.step()
add_src_y = self.bre.step()
# MainSide
if y >= 0 and y < SCREEN_SIZE_H:
self.buff[y].r = x
#pyxel.pset(x,y,6)
self.buff[y].src_tx = src_x
self.buff[y].src_ty = src_y + add_src_y
x = x - dest_x + th_x
y = y - dest_y + th_y
# SubSide
if y >= 0 and y < SCREEN_SIZE_H:
if self.buff[y].l == EMPTY:
self.buff[y].l = x
#pyxel.pset(x,y,6)
self.buff[y].src_sx = src_x + src_w-1
self.buff[y].src_sy = src_y + add_src_y
case a if a < 96:
for i in range (reg):
x,y = self.vbre.step()
add_src_y = self.bre.step()
# MainSide
if y >= 0 and y < SCREEN_SIZE_H:
if self.buff[y].r == EMPTY:
self.buff[y].r = x
#pyxel.pset(x,y,6)
self.buff[y].src_tx = src_x
self.buff[y].src_ty = src_y + add_src_y
x = x - dest_x + th_x
y = y - dest_y + th_y
# SubSide
if y >= 0 and y < SCREEN_SIZE_H:
self.buff[y].l = x
#pyxel.pset(x,y,6)
self.buff[y].src_sx = src_x + src_w-1
self.buff[y].src_sy = src_y + add_src_y
case _:
for i in range (reg):
x,y = self.vbre.step()
add_src_y = self.bre.step()
# MainSide
if y >= 0 and y < SCREEN_SIZE_H:
self.buff[y].l = x
#pyxel.pset(x,y,6)
self.buff[y].src_sx = src_x
self.buff[y].src_sy = src_y + add_src_y
x = x - dest_x + th_x
y = y - dest_y + th_y
# SubSide
if y >= 0 and y < SCREEN_SIZE_H:
if self.buff[y].r == EMPTY:
self.buff[y].r = x
#pyxel.pset(x,y,6)
self.buff[y].src_tx = src_x + src_w-1
self.buff[y].src_ty = src_y + add_src_y
# 画像横方向処理
reg = self.vbre.setup(dest_x,dest_y,th_x,th_y)
self.bre.setup(src_w-1,reg-1)
match angle:
# 水平にコピーする時は横方向の処理を行わない
case a if angle == 0 or angle == 64:
pass
case a if a < 32:
for i in range (reg):
x,y = self.vbre.step()
add_src_x = self.bre.step()
# MainSide
if y >= 0 and y < SCREEN_SIZE_H:
if self.buff[y].l == EMPTY:
self.buff[y].l = x
#pyxel.pset(x,y,6)
self.buff[y].src_sx = src_x + add_src_x
self.buff[y].src_sy = src_y
x = x - dest_x + tv_x
y = y - dest_y + tv_y
# SubSide
if y >= 0 and y < SCREEN_SIZE_H:
self.buff[y].r = x
#pyxel.pset(x,y,6)
self.buff[y].src_tx = src_x + add_src_x
self.buff[y].src_ty = src_y + src_h-1
case a if a < 64:
for i in range (reg):
x,y = self.vbre.step()
add_src_x = self.bre.step()
# MainSide
if y >= 0 and y < SCREEN_SIZE_H:
self.buff[y].l = x
#pyxel.pset(x,y,6)
self.buff[y].src_sx = src_x + add_src_x
self.buff[y].src_sy = src_y
x = x - dest_x + tv_x
y = y - dest_y + tv_y
# SubSide
if y >= 0 and y < SCREEN_SIZE_H:
if self.buff[y].r == EMPTY:
self.buff[y].r = x
#pyxel.pset(x,y,6)
self.buff[y].src_tx = src_x + add_src_x
self.buff[y].src_ty = src_y + src_h-1
case a if a < 96:
for i in range (reg):
x,y = self.vbre.step()
add_src_x = self.bre.step()
# MainSide
if y >= 0 and y < SCREEN_SIZE_H:
if self.buff[y].r == EMPTY:
self.buff[y].r = x
#pyxel.pset(x,y,6)
self.buff[y].src_tx = src_x + add_src_x
self.buff[y].src_ty = src_y
x = x - dest_x + tv_x
y = y - dest_y + tv_y
# SubSide
if y >= 0 and y < SCREEN_SIZE_H:
self.buff[y].l = x
#pyxel.pset(x,y,6)
self.buff[y].src_sx = src_x + add_src_x
self.buff[y].src_sy = src_y + src_h-1
case _:
for i in range (reg):
x,y = self.vbre.step()
add_src_x = self.bre.step()
# MainSide
if y >= 0 and y < SCREEN_SIZE_H:
self.buff[y].r = x
#pyxel.pset(x,y,6)
self.buff[y].src_tx = src_x + add_src_x
self.buff[y].src_ty = src_y
x = x - dest_x + tv_x
y = y - dest_y + tv_y
# SubSide
if y >= 0 and y < SCREEN_SIZE_H:
if self.buff[y].l == EMPTY:
self.buff[y].l = x
#pyxel.pset(x,y,6)
self.buff[y].src_sx = src_x + add_src_x
self.buff[y].src_sy = src_y + src_h-1
# バッファを基に転送を行う
for y in range (self.top, self.bottom+1):
reg = self.vbre.setup(self.buff[y].src_sx,self.buff[y].src_sy,
self.buff[y].src_tx,self.buff[y].src_ty)
self.bre.setup(reg-1,self.buff[y].r - self.buff[y].l)
u,v = self.vbre.step()
new_src = self.bre.step()
old_src = new_src
for x in range (self.buff[y].l,self.buff[y].r+1):
new_src = self.bre.step()
for step_src in range (old_src, new_src):
u,v = self.vbre.step()
old_src = new_src
# 画面左右端処理
if x < 0:
continue
if x >= SCREEN_SIZE_W:
break
self.tile[alpha](x,y,u,v)
#pyxel.pset(x,y,pyxel.images[0].pget(sx,sy))
class App:
def __init__(self):
# 画面サイズ 160x120 で初期化
pyxel.init(SCREEN_SIZE_W, SCREEN_SIZE_H, title="RollBlt")
self.add_x = 0
pyxel.load("yajirushi.pyxres")
self.rb = RollBlt()
self.angle = 0
self.angle2 = 0
pyxel.run(self.update, self.draw)
def update(self):
self.angle += 1
self.angle &= 127
self.angle2 -= 1
self.angle2 &= 127
def draw(self):
# 画面を黒(色番号0)でクリア
pyxel.cls(0)
#self.rb.blt(CENTER_X, CENTER_Y, 0, 0, 0, 32, 16, 16,512,512)
self.rb.blt_c(CENTER_X, CENTER_Y, 0, 0, 0, 32, 16, self.angle,512,512)
#self.rb.blt_c(CENTER_X, CENTER_Y, 0, 0, 0, 32, 16, self.angle,scale_w=64,scale_h=64)
#self.rb.blt_c(CENTER_X, CENTER_Y, 0, 0, 0, 32, 16, self.angle,scale_w=256,scale_h=256)
"""
self.rb.blt_c(CENTER_X, CENTER_Y, 0, 0, 0, 32, 16, self.angle,scale_w=512,scale_h=512, alpha=ALPHA_HALF_1)
self.rb.blt_c(CENTER_X, CENTER_Y, 0, 0, 0, 32, 16, self.angle2,scale_w=512,scale_h=512, alpha=ALPHA_HALF_2)
"""
"""
self.rb.blt_c(CENTER_X-30, CENTER_Y-20, 0, 0, 0, 32, 16, self.angle,scale_w=512,scale_h=512, alpha=ALPHA_QUARTER_1)
self.rb.blt_c(CENTER_X-30, CENTER_Y+20, 0, 0, 0, 32, 16, self.angle2,scale_w=512,scale_h=512, alpha=ALPHA_QUARTER_4)
self.rb.blt_c(CENTER_X+30, CENTER_Y-20, 0, 0, 0, 32, 16, self.angle,scale_w=512,scale_h=512, alpha=ALPHA_QUARTER_2)
self.rb.blt_c(CENTER_X+30, CENTER_Y+20, 0, 0, 0, 32, 16, self.angle2,scale_w=512,scale_h=512, alpha=ALPHA_QUARTER_3)
"""
App()
ソースここまで
微妙に三角関数テーブルを修正していたり、描画部を変えていたりしますが、基本的には前回と出力結果は変わりません。
ただ、実際に1ドットずつ画像を転送する部分を関数テーブルに分けた事でソースコードは大分短くなっています。
気になる実行速度の鈍化ですが、思ったよりも重くない様子…
いや、1ドット打つごとに遅くなるので数万回のオーバーヘッドがかかっており、確実に遅くはなっているハズなのですがそれでも影響がないと言う事は機材が進歩したって事ですかね。
点描に変えてみたら
ふと、1ドット毎に Blt 関数使っているなら1ドット毎に元画像のパレット取得して、転送先に1ドットずつ描画した方が早くない?と、思ってやってみたのですが…
たとえばココを…
def _non(self,x,y,u,v):
pyxel.blt(x, y, 0, u, v, 1, 1)
こうする
def _non(self,x,y,u,v):
col = pyxel.images[0].pget(u, v)
pyxel.pset(x,y,col)
めちゃくちゃ遅い
どういうわけだか Pyxel に関して言えば1ドット毎に blt 命令を繰り返す事に対して1ドット毎に pget-pset を繰り返す方が断然遅い。
自動的に画面が拡大される仕様上、Blt の方は内部的にハードウェアを使った拡大を行っている可能性は高いのだが…ここまであからさまに処理落ちが起きるとは思っていなかったので意外でした。これだと実際にゲームで使用する際に多用されるパレット0の時は透過させるといった処理を組むととても重いのでは…と思いました。


















