Project1
标题: RM 2D绘图的插件(加了传送位图功能啦,效率测试) [打印本页]
作者: SailCat 时间: 2022-3-1 07:07
标题: RM 2D绘图的插件(加了传送位图功能啦,效率测试)
本帖最后由 SailCat 于 2022-3-1 22:07 编辑
不依赖SEP Core运行,所以独立发一下
但是……谢谢大家的测试我只能说[泪目]……看来这个功能做出来终究还是有它的可取之处的,那就后续再更新一下吧~
就是写着顺手就写出来了……
#==============================================================================
# ■ [Bitmap Canvas] 位图画布处理核心 v1.0 by SailCat
#------------------------------------------------------------------------------
# 方法:本脚本插入Main之前使用
# 依赖:无
# 版本:v1.0 (Build 220228)
# 效果:
# 1. 可以用简单指令在位图对象上画图
# 配置:画笔线形的配置
# 冲突:无
# 说明:
# 1. 绘制直线:
# draw_line(起始x, 起始y, 结束x, 结束y, 颜色, 线形)
# 2. 绘制渐变色直线:
# gradient_draw_line(起始x, 起始y, 结束x, 结束y, 颜色1, 颜色2)
# 3. 绘制路径(连续直线):
# draw_path(点1x, 点1y, 点2x, 点2y, 点3x, 点3y[, ...], 颜色)
# 4. 绘制多边形(连续封闭直线):
# draw_polygon(点1x, 点1y, 点2x, 点2y, 点3x, 点3y[, ...], 颜色)
# 5. 绘制正多边形:
# draw_isogon(外接圆心x, 外接圆心y, 半径, 边数, 旋转角, 颜色)
# 6. 绘制椭圆:
# draw_ellipse(圆心x, 圆心y, 半横径,半纵径, 颜色)
# 7. 绘制圆:
# draw_circle(圆心x, 圆心y, 半径,颜色)
# 8. 填涂多边形:
# fill_polygon(点1x, 点1y, 点2x, 点2y, 点3x, 点3y[, ...], 边框颜色[,
# 填充颜色1[, 填充颜色2]])
# 9. 填涂正多边形:
# fill_isogon(外接圆心x, 外接圆心y, 半径, 边数, 旋转角, 边框颜色[,
# 填充颜色1[, 填充颜色2]])
# 10.填涂椭圆:
# fill_ellipse(圆心x, 圆心y, 半横径,半纵径, 边框颜色[, 填充颜色1[,
# 填充颜色2]])
# 11.填涂圆:
# fill_circle(圆心x, 圆心y, 半径,边框颜色[, 填充颜色1[, 填充颜色2]])
# 12.传送多边形:
# blt_polygon(点1x, 点1y, 点2x, 点2y, 点3x, 点3y[, ...], 源位图,
# 参考点1, 参考点1y[, 不透明度])
# 13.传送正多边形:
# blt_isogon(外接圆心x, 外接圆心y, 半径, 边数, 旋转角, 源位图, 参考心x,
# 参考心y[, 不透明度])
# 14.传送椭圆:
# blt_ellipse(圆心x, 圆心y, 半横径,半纵径, 源位图, 参考心x, 参考心y[,
# 不透明度])
# 15.传送圆:
# blt_circle(圆心x, 圆心y, 半径,源位图, 参考心x, 参考心y[, 不透明度])
#==============================================================================
#==============================================================================
# ■ SailCat's 插件公用
#==============================================================================
module SailCat
#--------------------------------------------------------------------------
# ● 脚本配置区
#--------------------------------------------------------------------------
module Canvas_Config
CANVAS_LINE_DOTTED = [1, 0] # 点线画笔
CANVAS_LINE_DASHED = [1, 1, 0, 1] # 短虚线画笔
CANVAS_LINE_LONGDASH = [1, 1, 0, 0, 1, 1] # 长虚线画笔
CANVAS_LINE_DASHDOT = [1, 0, 1, 1, 1, 0] # 点划线画笔
CANVAS_LINE_BROKEN = [1, 0, 0] # 散点画笔
end
end
#==============================================================================
# ■ Bitmap
#==============================================================================
class Bitmap
include SailCat::Canvas_Config
#--------------------------------------------------------------------------
# ● 常量
#--------------------------------------------------------------------------
CANVAS_LINE_SOLID = [1] # 实心画笔,该常量禁止修改配置
#--------------------------------------------------------------------------
# ● 绘制直线
# x1 : 起始的 X 坐标
# y1 : 起始的 Y 坐标
# x2 : 结束的 X 坐标
# y2 : 结束的 Y 坐标
# color : 直线的颜色 (Color)
# style : 直线的线型
#--------------------------------------------------------------------------
def draw_line(x1, y1, x2, y2, color, style = CANVAS_LINE_SOLID)
return set_pixel(x1, y1, color) if x1 == x2 and y1 == y2
dx = (x2 - x1).abs
dy = (y2 - y1).abs
x = x1
y = y1
rx = x2 - x1 <=> 0
ry = y2 - y1 <=> 0
b_index = 0
b_size = style.size
if dx == 0 and style == CANVAS_LINE_SOLID
fill_rect(x1, [y1, y2].min, 1, dy + 1, color)
elsif dy == 0 and style == CANVAS_LINE_SOLID
fill_rect([x1, x2].min, y1, dx + 1, 1, color)
elsif dx <= dy
f = dx.to_f * rx / dy
dy.times do
set_pixel(x.round, y, color) if style[b_index] == 1
x += f
y += ry
b_index += 1
b_index %= b_size
end
set_pixel(x.round, y, color)
else
f = dy.to_f * ry / dx
dx.times do
set_pixel(x, y.round, color) if style[b_index] == 1
x += rx
y += f
b_index += 1
b_index %= b_size
end
set_pixel(x, y.round, color)
end
end
#--------------------------------------------------------------------------
# ● 绘制渐变直线
# x1 : 起始的 X 坐标
# y1 : 起始的 Y 坐标
# x2 : 结束的 X 坐标
# y2 : 结束的 Y 坐标
# color1 : 直线的起始颜色 (Color)
# color2 : 直线的终止颜色 (Color)
#--------------------------------------------------------------------------
def gradient_draw_line(x1, y1, x2, y2, color1, color2)
return set_pixel(x1, y1, color) if x1 == x2 and y1 == y2
return draw_line(x1, y1, x2, y2, color1) if color1 == color2
dx = (x2 - x1).abs
dy = (y2 - y1).abs
rx = x2 - x1 <=> 0
ry = y2 - y1 <=> 0
c = color1.clone
if dx <= dy
f = dx.to_f / dy * rx
x = x1.to_f
y = y1
dr = (color2.red - color1.red) / dy
dg = (color2.green - color1.green) / dy
db = (color2.blue - color1.blue) / dy
da = (color2.alpha - color1.alpha) / dy
dy.times do
set_pixel(x.round, y, c)
x += f
y += ry
c.set(c.red + dr, c.green + dg, c.blue + db, c.alpha + da)
end
else
f = dy.to_f / dx * ry
x = x1
y = y1.to_f
dr = (color2.red - color1.red) / dx
dg = (color2.green - color1.green) / dx
db = (color2.blue - color1.blue) / dx
da = (color2.alpha - color1.alpha) / dx
dx.times do
set_pixel(x, y.round, c)
x += rx
y += f
c.set(c.red + dr, c.green + dg, c.blue + db, c.alpha + da)
end
end
end
#--------------------------------------------------------------------------
# ● 绘制路径
# x1 : 第一点的 X 坐标
# y1 : 第一点的 Y 坐标
# x2 : 第二点的 X 坐标
# y2 : 第二点的 Y 坐标
# x3 : 第三点的 X 坐标
# y3 : 第三点的 Y 坐标
# args : 后续的参数(可以加多个点,至少要包括颜色)
#--------------------------------------------------------------------------
def draw_path(x1, y1, x2, y2, x3, y3, *args)
raise ArgumentError if args.size % 2 == 0 or not args[-1].is_a?(Color)
color = args[-1]
nodes = [x1, y1, x2, y2, x3, y3].concat(args[0...-1])
0.step(nodes.size - 4, 2) {|i| draw_line(*(nodes[i, 4].concat([color])))}
end
#--------------------------------------------------------------------------
# ● 绘制多边形
# x1 : 第一点的 X 坐标
# y1 : 第一点的 Y 坐标
# x2 : 第二点的 X 坐标
# y2 : 第二点的 Y 坐标
# x3 : 第三点的 X 坐标
# y3 : 第三点的 Y 坐标
# args : 后续的参数(可以加多个点,至少要包括颜色)
#--------------------------------------------------------------------------
def draw_polygon(x1, y1, x2, y2, x3, y3, *args)
raise ArgumentError if args.size % 2 == 0 or not args[-1].is_a?(Color)
args.insert(-2, x1, y1)
draw_path(x1, y1, x2, y2, x3, y3, *args)
end
#--------------------------------------------------------------------------
# ● 绘制实心多边形
# x1 : 第一点的 X 坐标
# y1 : 第一点的 Y 坐标
# x2 : 第二点的 X 坐标
# y2 : 第二点的 Y 坐标
# x3 : 第三点的 X 坐标
# y3 : 第三点的 Y 坐标
# args : 后续的参数(可以加多个点,至少要包括一个颜色,至多可有三个颜色)
#--------------------------------------------------------------------------
def fill_polygon(x1, y1, x2, y2, x3, y3, *args)
raise ArgumentError unless args[-1].is_a?(Color)
fill2 = args[-3].is_a?(Color) ? args.pop : args[-1]
fill1 = args[-2].is_a?(Color) ? args.pop : args[-1]
draw_polygon(x1, y1, x2, y2, x3, y3, *args)
color = args[-1]
nodes = [x1, y1, x2, y2, x3, y3].concat(args[0...-1])
edges = {}
min_y = height
max_y = 0
0.step(nodes.size - 2, 2) do |i|
xi, yi = nodes[i, 2]
min_y = [min_y, yi].min
max_y = [max_y, yi].max
xj, yj = nodes[(i + 2) % nodes.size, 2]
dx = 1.0 * (xi - xj) / (yi - yj)
if yi < yj
yh = nodes[i - 1]
if yh < yi
yi += 1
xi += dx
end
edges[yi] ||= []
edges[yi].push([xi, dx, yj])
elsif yi > yj
yk = nodes[(i + 5) % nodes.size]
if yj > yk
yj += 1
xj += dx
end
edges[yj] ||= []
edges[yj].push([xj, dx, yi])
end
end
return unless max_y - min_y > 1
aet = edges[min_y]
aet.each {|edge| edge[0] += edge[1]}
aet.sort!
if fill1 != fill2
dh = max_y - min_y - 1
dr = (fill2.red - fill1.red) / dh
dg = (fill2.green - fill1.green) / dh
db = (fill2.blue - fill1.blue) / dh
da = (fill2.alpha - fill1.alpha) / dh
gradient = true
end
f = fill1.clone
for y in min_y + 1..max_y - 1
if edges[y]
aet.concat(edges[y])
aet.sort!
end
0.step(aet.size - 1, 2) do |i|
xi = aet[i][0].round
xj = aet[i + 1][0].round
xi += 1 until xi == width - 1 or get_pixel(xi, y) != color
xj -= 1 until xj == 0 or get_pixel(xj, y) != color
if xj >= xi
fill_rect(xi, y, xj - xi + 1, 1, f)
end
aet.reject! {|edge| edge[2] == y}
aet.each {|edge| edge[0] += edge[1]}
aet.sort!
f.set(f.red + dr, f.green + dg, f.blue + db, f.alpha + da) if gradient
end
end
end
#--------------------------------------------------------------------------
# ● 传送位图多边形
# x1 : 第一点的 X 坐标
# y1 : 第一点的 Y 坐标
# x2 : 第二点的 X 坐标
# y2 : 第二点的 Y 坐标
# x3 : 第三点的 X 坐标
# y3 : 第三点的 Y 坐标
# args : 后续的参数(加多个点,最后是位图, 偏移x,偏移y,(可选)不透明度)
# 偏移x和偏移y的位置参考第一个点
#--------------------------------------------------------------------------
def blt_polygon(x1, y1, x2, y2, x3, y3, *args)
if args[-3].is_a?(Bitmap)
src, x0, y0 = args[-3, 3]
opacity = 255
nodes = [x1, y1, x2, y2, x3, y3].concat(args[0...-3])
elsif args[-4].is_a?(Bitmap)
src, x0, y0, opacity = args[-4, 4]
nodes = [x1, y1, x2, y2, x3, y3].concat(args[0...-4])
else
raise ArgumentError
end
raise ArgumentError unless nodes.size[0] == 0
edges = {}
min_y = height
max_y = 0
ox = x0 - x1
oy = y0 - y1
0.step(nodes.size - 2, 2) do |i|
xi, yi = nodes[i, 2]
min_y = [min_y, yi].min
max_y = [max_y, yi].max
xj, yj = nodes[(i + 2) % nodes.size, 2]
dx = 1.0 * (xi - xj) / (yi - yj)
if yi < yj
yh = nodes[i - 1]
if yh < yi
yi += 1
xi += dx
end
edges[yi] ||= []
edges[yi].push([xi, dx, yj])
elsif yi > yj
yk = nodes[(i + 5) % nodes.size]
if yj > yk
yj += 1
xj += dx
end
edges[yj] ||= []
edges[yj].push([xj, dx, yi])
end
end
aet = []
for y in min_y..max_y
if edges[y]
aet.concat(edges[y])
aet.sort!
end
0.step(aet.size - 1, 2) do |i|
xi = aet[i][0].round
xj = aet[i + 1][0].round
blt(xi, y, src, Rect.new(xi + ox, y + oy, xj - xi + 1, 1), opacity)
aet.reject! {|edge| edge[2] == y}
aet.each {|edge| edge[0] += edge[1]}
aet.sort!
end
end
end
#--------------------------------------------------------------------------
# ● 绘制正多边形
# x : 正多边形外接圆心 X 坐标
# y : 正多边形外接圆心 Y 坐标
# rad : 正多边形外接圆 半径
# edges : 正多边形的边数
# angle : 正多边形旋转角度
# color : 正多边形的颜色 (Color)
#--------------------------------------------------------------------------
def draw_isogon(x, y, rad, edges, angle, color)
raise ArgumentError if edges < 3
step = Math::PI * 2 / edges
args = []
angle_i = Math::PI * (270 + angle) / 180
edges.times do
args.push((x + rad * Math.cos(angle_i)).round,
(y + rad * Math.sin(angle_i)).round)
angle_i += step
end
args.push(color)
draw_polygon(*args)
end
#--------------------------------------------------------------------------
# ● 绘制实心正多边形
# x : 正多边形外接圆心 X 坐标
# y : 正多边形外接圆心 Y 坐标
# rad : 正多边形外接圆 半径
# edges : 正多边形的边数
# angle : 正多边形旋转角度
# border_color : 正多边形的边框颜色 (Color)
# fill_color1 : 填充渐变的上颜色 (Color)
# fill_color2 : 填充渐变的下颜色 (Color)
#--------------------------------------------------------------------------
def fill_isogon(x, y, rad, edges, angle, border_color, fill_color1 =
border_color, fill_color2 = fill_color1)
raise ArgumentError if edges < 3
step = Math::PI * 2 / edges
args = []
angle_i = Math::PI * (270 + angle) / 180
edges.times do
args.push((x + rad * Math.cos(angle_i)).round,
(y + rad * Math.sin(angle_i)).round)
angle_i += step
end
args.push(border_color, fill_color1, fill_color2)
fill_polygon(*args)
end
#--------------------------------------------------------------------------
# ● 传送位图正多边形
# x : 正多边形外接圆心 X 坐标
# y : 正多边形外接圆心 Y 坐标
# rad : 正多边形外接圆 半径
# edges : 正多边形的边数
# angle : 正多边形旋转角度
# src_bitmap : 传送元位图
# x0 : 传送圆心 X 坐标
# y0 : 传送圆心 Y 坐标
# opacity : 不透明度
#--------------------------------------------------------------------------
def blt_isogon(x, y, rad, edges, angle, src_bitmap, x0, y0, opacity = 255)
raise ArgumentError if edges < 3
step = Math::PI * 2 / edges
args = []
angle_i = Math::PI * (270 + angle) / 180
edges.times do
args.push((x + rad * Math.cos(angle_i)).round,
(y + rad * Math.sin(angle_i)).round)
angle_i += step
end
args.push(src_bitmap, x0 - x + args[0], y0 - y + args[1], opacity)
blt_polygon(*args)
end
#--------------------------------------------------------------------------
# ● 绘制圆
# x : 圆心 X 坐标
# y : 圆心 Y 坐标
# rad : 半径
# color : 圆的颜色 (Color)
#--------------------------------------------------------------------------
def draw_circle(x, y, rad, color)
raise ArgumentError if rad < 0
return set_pixel(x, y, color) if rad == 0
draw_ellipse(x, y, rad, rad, color)
end
#--------------------------------------------------------------------------
# ● 绘制椭圆
# x : 圆心 X 坐标
# y : 圆心 Y 坐标
# rad_x : 水平半径
# rad_y : 垂直半径
# color : 椭圆的颜色 (Color)
# fill1 : 填充的颜色1 (Color)
# fill2 : 填充的颜色2 (Color)
#--------------------------------------------------------------------------
def draw_ellipse(x, y, rad_x, rad_y, color, fill1 = nil, fill2 = nil)
raise ArgumentError if rad_x < 0 or rad_y < 0
return set_pixel(x, y, color) if rad_x == 0 and rad_y == 0
return fill_rect(x - rad_x, y, rad_x << 1, 1, color) if rad_y == 0
return fill_rect(x, y - rad_y, 1, rad_y << 1, color) if rad_x == 0
ry2 = rad_y * rad_y
rx2 = rad_x * rad_x
d = ry2 + rx2 * (0.25 - rad_y)
dx = 0
last_dy = dy = rad_y
f = fill1 && fill1.clone
if fill2
f2 = fill2.clone
dr = (fill2.red - fill1.red) / ((rad_y << 1) - 1)
dg = (fill2.green - fill1.green) / ((rad_y << 1) - 1)
db = (fill2.blue - fill1.blue) / ((rad_y << 1) - 1)
da = (fill2.alpha - fill1.alpha) / ((rad_y << 1) - 1)
end
begin
set_pixel(x + dx, y + dy, color)
set_pixel(x + dx, y - dy, color)
set_pixel(x - dx, y - dy, color)
set_pixel(x - dx, y + dy, color)
if f and dx > 1 and dy < rad_y and last_dy != dy
last_dy = dy
fill_rect(x - dx + 1, y - dy, (dx << 1) - 1, 1, f)
fill_rect(x - dx + 1, y + dy, (dx << 1) - 1, 1, f2)
if fill2
f.set(f.red + dr, f.green + dg, f.blue + db, f.alpha + da)
f2.set(f2.red - dr, f2.green - dg, f2.blue - db, f2.alpha - da)
end
end
if d <= -1e-6
d += ry2 * ((dx << 1) + 3)
else
d += ry2 * ((dx << 1) + 3) + rx2 * (1 - dy << 1)
dy -= 1
end
dx += 1
end while ry2 * (dx + 1) < rx2 * (dy - 0.5)
d = ry2 * (dx + 0.5) * (dx + 0.5) + rx2 * (dy - 1) * (dy - 1) - rx2 * ry2
begin
set_pixel(x + dx, y + dy, color)
set_pixel(x + dx, y - dy, color)
set_pixel(x - dx, y - dy, color)
set_pixel(x - dx, y + dy, color)
if f and dx > 1 and dy < rad_y and last_dy != dy
last_dy = dy
fill_rect(x - dx + 1, y - dy, (dx << 1) - 1, 1, f)
fill_rect(x - dx + 1, y + dy, (dx << 1) - 1, 1, f2)
if fill2
f.set(f.red + dr, f.green + dg, f.blue + db, f.alpha + da)
f2.set(f2.red - dr, f2.green - dg, f2.blue - db, f2.alpha - da)
end
end
if d <= -1e-6
d += ry2 * (dx + 1 << 1) + rx2 * (3 - (dy << 1))
dx += 1
else
d += rx2 * (3 - (dy << 1))
end
dy -= 1
end while dy >= 0
end
#--------------------------------------------------------------------------
# ● 绘制实心圆
# x : 圆心 X 坐标
# y : 圆心 Y 坐标
# rad : 半径
# border_color : 圆的边框颜色 (Color)
# fill_color1 : 填充渐变的上颜色 (Color)
# fill_color2 : 填充渐变的下颜色 (Color)
#--------------------------------------------------------------------------
def fill_circle(x, y, rad, border_color, fill_color1 = border_color,
fill_color2 = fill_color1)
draw_ellipse(x, y, rad, rad, border_color, fill_color1, fill_color2)
end
#--------------------------------------------------------------------------
# ● 绘制实心椭圆
# x : 圆心 X 坐标
# y : 圆心 Y 坐标
# rad_x : 水平半径
# rad_y : 垂直半径
# border_color : 椭圆的边框颜色 (Color)
# fill_color1 : 填充渐变的上颜色 (Color)
# fill_color2 : 填充渐变的下颜色 (Color)
#--------------------------------------------------------------------------
def fill_ellipse(x, y, rad_x, rad_y, border_color, fill_color1 =
border_color, fill_color2 = fill_color1)
draw_ellipse(x, y, rad_x, rad_y, border_color, fill_color1, fill_color2)
end
#--------------------------------------------------------------------------
# ● 传送位图圆
# x : 圆心 X 坐标
# y : 圆心 Y 坐标
# rad : 半径
# src_bitmap : 传送元位图
# x0 : 传送圆心 X 坐标
# y0 : 传送圆心 Y 坐标
# opacity : 不透明度
#--------------------------------------------------------------------------
def blt_circle(x, y, rad, src_bitmap, x0, y0, opacity = 255)
blt_ellipse(x, y, rad, rad, src_bitmap, x0, y0, opacity)
end
#--------------------------------------------------------------------------
# ● 传送位图椭圆
# x : 圆心 X 坐标
# y : 圆心 Y 坐标
# rad_x : 水平半径
# rad_y : 垂直半径
# src_bitmap : 传送元位图
# x0 : 传送圆心 X 坐标
# y0 : 传送圆心 Y 坐标
# opacity : 不透明度
#--------------------------------------------------------------------------
def blt_ellipse(x, y, rad_x, rad_y, src_bitmap, x0, y0, opacity = 255)
raise ArgumentError if rad_x < 0 or rad_y < 0
return blt(x - rad_x, y, src_bitmap, Rect.new(x0 - rad_x, y0,
rad_x << 1, 1), opacity) if rad_y == 0
return blt(x, y - rad_y, src_bitmap, Rect.new(x0, y0 - rad_y,
1, rad_y << 1), opacity) if rad_x == 0
ry2 = rad_y * rad_y
rx2 = rad_x * rad_x
d = ry2 + rx2 * (0.25 - rad_y)
dx = 0
dy = rad_y
last_dy = nil
begin
if last_dy != dy
last_dy = dy
blt(x - dx + 1, y - dy, src_bitmap, Rect.new(x0 - dx + 1, y0 - dy,
(dx << 1) - 1, 1), opacity)
blt(x - dx + 1, y + dy, src_bitmap, Rect.new(x0 - dx + 1, y0 + dy,
(dx << 1) - 1, 1), opacity)
end
if d <= -1e-6
d += ry2 * ((dx << 1) + 3)
else
d += ry2 * ((dx << 1) + 3) + rx2 * (1 - dy << 1)
dy -= 1
end
dx += 1
end while ry2 * (dx + 1) < rx2 * (dy - 0.5)
d = ry2 * (dx + 0.5) * (dx + 0.5) + rx2 * (dy - 1) * (dy - 1) - rx2 * ry2
begin
if last_dy != dy
last_dy = dy
blt(x - dx + 1, y - dy, src_bitmap, Rect.new(x0 - dx + 1, y0 - dy,
(dx << 1) - 1, 1), opacity)
blt(x - dx + 1, y + dy, src_bitmap, Rect.new(x0 - dx + 1, y0 + dy,
(dx << 1) - 1, 1), opacity)
end
if d <= -1e-6
d += ry2 * (dx + 1 << 1) + rx2 * (3 - (dy << 1))
dx += 1
else
d += rx2 * (3 - (dy << 1))
end
dy -= 1
end while dy >= 0
end
end
#==============================================================================
# ■ [Bitmap Canvas] 位图画布处理核心 v1.0 by SailCat
#------------------------------------------------------------------------------
# 方法:本脚本插入Main之前使用
# 依赖:无
# 版本:v1.0 (Build 220228)
# 效果:
# 1. 可以用简单指令在位图对象上画图
# 配置:画笔线形的配置
# 冲突:无
# 说明:
# 1. 绘制直线:
# draw_line(起始x, 起始y, 结束x, 结束y, 颜色, 线形)
# 2. 绘制渐变色直线:
# gradient_draw_line(起始x, 起始y, 结束x, 结束y, 颜色1, 颜色2)
# 3. 绘制路径(连续直线):
# draw_path(点1x, 点1y, 点2x, 点2y, 点3x, 点3y[, ...], 颜色)
# 4. 绘制多边形(连续封闭直线):
# draw_polygon(点1x, 点1y, 点2x, 点2y, 点3x, 点3y[, ...], 颜色)
# 5. 绘制正多边形:
# draw_isogon(外接圆心x, 外接圆心y, 半径, 边数, 旋转角, 颜色)
# 6. 绘制椭圆:
# draw_ellipse(圆心x, 圆心y, 半横径,半纵径, 颜色)
# 7. 绘制圆:
# draw_circle(圆心x, 圆心y, 半径,颜色)
# 8. 填涂多边形:
# fill_polygon(点1x, 点1y, 点2x, 点2y, 点3x, 点3y[, ...], 边框颜色[,
# 填充颜色1[, 填充颜色2]])
# 9. 填涂正多边形:
# fill_isogon(外接圆心x, 外接圆心y, 半径, 边数, 旋转角, 边框颜色[,
# 填充颜色1[, 填充颜色2]])
# 10.填涂椭圆:
# fill_ellipse(圆心x, 圆心y, 半横径,半纵径, 边框颜色[, 填充颜色1[,
# 填充颜色2]])
# 11.填涂圆:
# fill_circle(圆心x, 圆心y, 半径,边框颜色[, 填充颜色1[, 填充颜色2]])
# 12.传送多边形:
# blt_polygon(点1x, 点1y, 点2x, 点2y, 点3x, 点3y[, ...], 源位图,
# 参考点1, 参考点1y[, 不透明度])
# 13.传送正多边形:
# blt_isogon(外接圆心x, 外接圆心y, 半径, 边数, 旋转角, 源位图, 参考心x,
# 参考心y[, 不透明度])
# 14.传送椭圆:
# blt_ellipse(圆心x, 圆心y, 半横径,半纵径, 源位图, 参考心x, 参考心y[,
# 不透明度])
# 15.传送圆:
# blt_circle(圆心x, 圆心y, 半径,源位图, 参考心x, 参考心y[, 不透明度])
#==============================================================================
#==============================================================================
# ■ SailCat's 插件公用
#==============================================================================
module SailCat
#--------------------------------------------------------------------------
# ● 脚本配置区
#--------------------------------------------------------------------------
module Canvas_Config
CANVAS_LINE_DOTTED = [1, 0] # 点线画笔
CANVAS_LINE_DASHED = [1, 1, 0, 1] # 短虚线画笔
CANVAS_LINE_LONGDASH = [1, 1, 0, 0, 1, 1] # 长虚线画笔
CANVAS_LINE_DASHDOT = [1, 0, 1, 1, 1, 0] # 点划线画笔
CANVAS_LINE_BROKEN = [1, 0, 0] # 散点画笔
end
end
#==============================================================================
# ■ Bitmap
#==============================================================================
class Bitmap
include SailCat::Canvas_Config
#--------------------------------------------------------------------------
# ● 常量
#--------------------------------------------------------------------------
CANVAS_LINE_SOLID = [1] # 实心画笔,该常量禁止修改配置
#--------------------------------------------------------------------------
# ● 绘制直线
# x1 : 起始的 X 坐标
# y1 : 起始的 Y 坐标
# x2 : 结束的 X 坐标
# y2 : 结束的 Y 坐标
# color : 直线的颜色 (Color)
# style : 直线的线型
#--------------------------------------------------------------------------
def draw_line(x1, y1, x2, y2, color, style = CANVAS_LINE_SOLID)
return set_pixel(x1, y1, color) if x1 == x2 and y1 == y2
dx = (x2 - x1).abs
dy = (y2 - y1).abs
x = x1
y = y1
rx = x2 - x1 <=> 0
ry = y2 - y1 <=> 0
b_index = 0
b_size = style.size
if dx == 0 and style == CANVAS_LINE_SOLID
fill_rect(x1, [y1, y2].min, 1, dy + 1, color)
elsif dy == 0 and style == CANVAS_LINE_SOLID
fill_rect([x1, x2].min, y1, dx + 1, 1, color)
elsif dx <= dy
f = dx.to_f * rx / dy
dy.times do
set_pixel(x.round, y, color) if style[b_index] == 1
x += f
y += ry
b_index += 1
b_index %= b_size
end
set_pixel(x.round, y, color)
else
f = dy.to_f * ry / dx
dx.times do
set_pixel(x, y.round, color) if style[b_index] == 1
x += rx
y += f
b_index += 1
b_index %= b_size
end
set_pixel(x, y.round, color)
end
end
#--------------------------------------------------------------------------
# ● 绘制渐变直线
# x1 : 起始的 X 坐标
# y1 : 起始的 Y 坐标
# x2 : 结束的 X 坐标
# y2 : 结束的 Y 坐标
# color1 : 直线的起始颜色 (Color)
# color2 : 直线的终止颜色 (Color)
#--------------------------------------------------------------------------
def gradient_draw_line(x1, y1, x2, y2, color1, color2)
return set_pixel(x1, y1, color) if x1 == x2 and y1 == y2
return draw_line(x1, y1, x2, y2, color1) if color1 == color2
dx = (x2 - x1).abs
dy = (y2 - y1).abs
rx = x2 - x1 <=> 0
ry = y2 - y1 <=> 0
c = color1.clone
if dx <= dy
f = dx.to_f / dy * rx
x = x1.to_f
y = y1
dr = (color2.red - color1.red) / dy
dg = (color2.green - color1.green) / dy
db = (color2.blue - color1.blue) / dy
da = (color2.alpha - color1.alpha) / dy
dy.times do
set_pixel(x.round, y, c)
x += f
y += ry
c.set(c.red + dr, c.green + dg, c.blue + db, c.alpha + da)
end
else
f = dy.to_f / dx * ry
x = x1
y = y1.to_f
dr = (color2.red - color1.red) / dx
dg = (color2.green - color1.green) / dx
db = (color2.blue - color1.blue) / dx
da = (color2.alpha - color1.alpha) / dx
dx.times do
set_pixel(x, y.round, c)
x += rx
y += f
c.set(c.red + dr, c.green + dg, c.blue + db, c.alpha + da)
end
end
end
#--------------------------------------------------------------------------
# ● 绘制路径
# x1 : 第一点的 X 坐标
# y1 : 第一点的 Y 坐标
# x2 : 第二点的 X 坐标
# y2 : 第二点的 Y 坐标
# x3 : 第三点的 X 坐标
# y3 : 第三点的 Y 坐标
# args : 后续的参数(可以加多个点,至少要包括颜色)
#--------------------------------------------------------------------------
def draw_path(x1, y1, x2, y2, x3, y3, *args)
raise ArgumentError if args.size % 2 == 0 or not args[-1].is_a?(Color)
color = args[-1]
nodes = [x1, y1, x2, y2, x3, y3].concat(args[0...-1])
0.step(nodes.size - 4, 2) {|i| draw_line(*(nodes[i, 4].concat([color])))}
end
#--------------------------------------------------------------------------
# ● 绘制多边形
# x1 : 第一点的 X 坐标
# y1 : 第一点的 Y 坐标
# x2 : 第二点的 X 坐标
# y2 : 第二点的 Y 坐标
# x3 : 第三点的 X 坐标
# y3 : 第三点的 Y 坐标
# args : 后续的参数(可以加多个点,至少要包括颜色)
#--------------------------------------------------------------------------
def draw_polygon(x1, y1, x2, y2, x3, y3, *args)
raise ArgumentError if args.size % 2 == 0 or not args[-1].is_a?(Color)
args.insert(-2, x1, y1)
draw_path(x1, y1, x2, y2, x3, y3, *args)
end
#--------------------------------------------------------------------------
# ● 绘制实心多边形
# x1 : 第一点的 X 坐标
# y1 : 第一点的 Y 坐标
# x2 : 第二点的 X 坐标
# y2 : 第二点的 Y 坐标
# x3 : 第三点的 X 坐标
# y3 : 第三点的 Y 坐标
# args : 后续的参数(可以加多个点,至少要包括一个颜色,至多可有三个颜色)
#--------------------------------------------------------------------------
def fill_polygon(x1, y1, x2, y2, x3, y3, *args)
raise ArgumentError unless args[-1].is_a?(Color)
fill2 = args[-3].is_a?(Color) ? args.pop : args[-1]
fill1 = args[-2].is_a?(Color) ? args.pop : args[-1]
draw_polygon(x1, y1, x2, y2, x3, y3, *args)
color = args[-1]
nodes = [x1, y1, x2, y2, x3, y3].concat(args[0...-1])
edges = {}
min_y = height
max_y = 0
0.step(nodes.size - 2, 2) do |i|
xi, yi = nodes[i, 2]
min_y = [min_y, yi].min
max_y = [max_y, yi].max
xj, yj = nodes[(i + 2) % nodes.size, 2]
dx = 1.0 * (xi - xj) / (yi - yj)
if yi < yj
yh = nodes[i - 1]
if yh < yi
yi += 1
xi += dx
end
edges[yi] ||= []
edges[yi].push([xi, dx, yj])
elsif yi > yj
yk = nodes[(i + 5) % nodes.size]
if yj > yk
yj += 1
xj += dx
end
edges[yj] ||= []
edges[yj].push([xj, dx, yi])
end
end
return unless max_y - min_y > 1
aet = edges[min_y]
aet.each {|edge| edge[0] += edge[1]}
aet.sort!
if fill1 != fill2
dh = max_y - min_y - 1
dr = (fill2.red - fill1.red) / dh
dg = (fill2.green - fill1.green) / dh
db = (fill2.blue - fill1.blue) / dh
da = (fill2.alpha - fill1.alpha) / dh
gradient = true
end
f = fill1.clone
for y in min_y + 1..max_y - 1
if edges[y]
aet.concat(edges[y])
aet.sort!
end
0.step(aet.size - 1, 2) do |i|
xi = aet[i][0].round
xj = aet[i + 1][0].round
xi += 1 until xi == width - 1 or get_pixel(xi, y) != color
xj -= 1 until xj == 0 or get_pixel(xj, y) != color
if xj >= xi
fill_rect(xi, y, xj - xi + 1, 1, f)
end
aet.reject! {|edge| edge[2] == y}
aet.each {|edge| edge[0] += edge[1]}
aet.sort!
f.set(f.red + dr, f.green + dg, f.blue + db, f.alpha + da) if gradient
end
end
end
#--------------------------------------------------------------------------
# ● 传送位图多边形
# x1 : 第一点的 X 坐标
# y1 : 第一点的 Y 坐标
# x2 : 第二点的 X 坐标
# y2 : 第二点的 Y 坐标
# x3 : 第三点的 X 坐标
# y3 : 第三点的 Y 坐标
# args : 后续的参数(加多个点,最后是位图, 偏移x,偏移y,(可选)不透明度)
# 偏移x和偏移y的位置参考第一个点
#--------------------------------------------------------------------------
def blt_polygon(x1, y1, x2, y2, x3, y3, *args)
if args[-3].is_a?(Bitmap)
src, x0, y0 = args[-3, 3]
opacity = 255
nodes = [x1, y1, x2, y2, x3, y3].concat(args[0...-3])
elsif args[-4].is_a?(Bitmap)
src, x0, y0, opacity = args[-4, 4]
nodes = [x1, y1, x2, y2, x3, y3].concat(args[0...-4])
else
raise ArgumentError
end
raise ArgumentError unless nodes.size[0] == 0
edges = {}
min_y = height
max_y = 0
ox = x0 - x1
oy = y0 - y1
0.step(nodes.size - 2, 2) do |i|
xi, yi = nodes[i, 2]
min_y = [min_y, yi].min
max_y = [max_y, yi].max
xj, yj = nodes[(i + 2) % nodes.size, 2]
dx = 1.0 * (xi - xj) / (yi - yj)
if yi < yj
yh = nodes[i - 1]
if yh < yi
yi += 1
xi += dx
end
edges[yi] ||= []
edges[yi].push([xi, dx, yj])
elsif yi > yj
yk = nodes[(i + 5) % nodes.size]
if yj > yk
yj += 1
xj += dx
end
edges[yj] ||= []
edges[yj].push([xj, dx, yi])
end
end
aet = []
for y in min_y..max_y
if edges[y]
aet.concat(edges[y])
aet.sort!
end
0.step(aet.size - 1, 2) do |i|
xi = aet[i][0].round
xj = aet[i + 1][0].round
blt(xi, y, src, Rect.new(xi + ox, y + oy, xj - xi + 1, 1), opacity)
aet.reject! {|edge| edge[2] == y}
aet.each {|edge| edge[0] += edge[1]}
aet.sort!
end
end
end
#--------------------------------------------------------------------------
# ● 绘制正多边形
# x : 正多边形外接圆心 X 坐标
# y : 正多边形外接圆心 Y 坐标
# rad : 正多边形外接圆 半径
# edges : 正多边形的边数
# angle : 正多边形旋转角度
# color : 正多边形的颜色 (Color)
#--------------------------------------------------------------------------
def draw_isogon(x, y, rad, edges, angle, color)
raise ArgumentError if edges < 3
step = Math::PI * 2 / edges
args = []
angle_i = Math::PI * (270 + angle) / 180
edges.times do
args.push((x + rad * Math.cos(angle_i)).round,
(y + rad * Math.sin(angle_i)).round)
angle_i += step
end
args.push(color)
draw_polygon(*args)
end
#--------------------------------------------------------------------------
# ● 绘制实心正多边形
# x : 正多边形外接圆心 X 坐标
# y : 正多边形外接圆心 Y 坐标
# rad : 正多边形外接圆 半径
# edges : 正多边形的边数
# angle : 正多边形旋转角度
# border_color : 正多边形的边框颜色 (Color)
# fill_color1 : 填充渐变的上颜色 (Color)
# fill_color2 : 填充渐变的下颜色 (Color)
#--------------------------------------------------------------------------
def fill_isogon(x, y, rad, edges, angle, border_color, fill_color1 =
border_color, fill_color2 = fill_color1)
raise ArgumentError if edges < 3
step = Math::PI * 2 / edges
args = []
angle_i = Math::PI * (270 + angle) / 180
edges.times do
args.push((x + rad * Math.cos(angle_i)).round,
(y + rad * Math.sin(angle_i)).round)
angle_i += step
end
args.push(border_color, fill_color1, fill_color2)
fill_polygon(*args)
end
#--------------------------------------------------------------------------
# ● 传送位图正多边形
# x : 正多边形外接圆心 X 坐标
# y : 正多边形外接圆心 Y 坐标
# rad : 正多边形外接圆 半径
# edges : 正多边形的边数
# angle : 正多边形旋转角度
# src_bitmap : 传送元位图
# x0 : 传送圆心 X 坐标
# y0 : 传送圆心 Y 坐标
# opacity : 不透明度
#--------------------------------------------------------------------------
def blt_isogon(x, y, rad, edges, angle, src_bitmap, x0, y0, opacity = 255)
raise ArgumentError if edges < 3
step = Math::PI * 2 / edges
args = []
angle_i = Math::PI * (270 + angle) / 180
edges.times do
args.push((x + rad * Math.cos(angle_i)).round,
(y + rad * Math.sin(angle_i)).round)
angle_i += step
end
args.push(src_bitmap, x0 - x + args[0], y0 - y + args[1], opacity)
blt_polygon(*args)
end
#--------------------------------------------------------------------------
# ● 绘制圆
# x : 圆心 X 坐标
# y : 圆心 Y 坐标
# rad : 半径
# color : 圆的颜色 (Color)
#--------------------------------------------------------------------------
def draw_circle(x, y, rad, color)
raise ArgumentError if rad < 0
return set_pixel(x, y, color) if rad == 0
draw_ellipse(x, y, rad, rad, color)
end
#--------------------------------------------------------------------------
# ● 绘制椭圆
# x : 圆心 X 坐标
# y : 圆心 Y 坐标
# rad_x : 水平半径
# rad_y : 垂直半径
# color : 椭圆的颜色 (Color)
# fill1 : 填充的颜色1 (Color)
# fill2 : 填充的颜色2 (Color)
#--------------------------------------------------------------------------
def draw_ellipse(x, y, rad_x, rad_y, color, fill1 = nil, fill2 = nil)
raise ArgumentError if rad_x < 0 or rad_y < 0
return set_pixel(x, y, color) if rad_x == 0 and rad_y == 0
return fill_rect(x - rad_x, y, rad_x << 1, 1, color) if rad_y == 0
return fill_rect(x, y - rad_y, 1, rad_y << 1, color) if rad_x == 0
ry2 = rad_y * rad_y
rx2 = rad_x * rad_x
d = ry2 + rx2 * (0.25 - rad_y)
dx = 0
last_dy = dy = rad_y
f = fill1 && fill1.clone
if fill2
f2 = fill2.clone
dr = (fill2.red - fill1.red) / ((rad_y << 1) - 1)
dg = (fill2.green - fill1.green) / ((rad_y << 1) - 1)
db = (fill2.blue - fill1.blue) / ((rad_y << 1) - 1)
da = (fill2.alpha - fill1.alpha) / ((rad_y << 1) - 1)
end
begin
set_pixel(x + dx, y + dy, color)
set_pixel(x + dx, y - dy, color)
set_pixel(x - dx, y - dy, color)
set_pixel(x - dx, y + dy, color)
if f and dx > 1 and dy < rad_y and last_dy != dy
last_dy = dy
fill_rect(x - dx + 1, y - dy, (dx << 1) - 1, 1, f)
fill_rect(x - dx + 1, y + dy, (dx << 1) - 1, 1, f2)
if fill2
f.set(f.red + dr, f.green + dg, f.blue + db, f.alpha + da)
f2.set(f2.red - dr, f2.green - dg, f2.blue - db, f2.alpha - da)
end
end
if d <= -1e-6
d += ry2 * ((dx << 1) + 3)
else
d += ry2 * ((dx << 1) + 3) + rx2 * (1 - dy << 1)
dy -= 1
end
dx += 1
end while ry2 * (dx + 1) < rx2 * (dy - 0.5)
d = ry2 * (dx + 0.5) * (dx + 0.5) + rx2 * (dy - 1) * (dy - 1) - rx2 * ry2
begin
set_pixel(x + dx, y + dy, color)
set_pixel(x + dx, y - dy, color)
set_pixel(x - dx, y - dy, color)
set_pixel(x - dx, y + dy, color)
if f and dx > 1 and dy < rad_y and last_dy != dy
last_dy = dy
fill_rect(x - dx + 1, y - dy, (dx << 1) - 1, 1, f)
fill_rect(x - dx + 1, y + dy, (dx << 1) - 1, 1, f2)
if fill2
f.set(f.red + dr, f.green + dg, f.blue + db, f.alpha + da)
f2.set(f2.red - dr, f2.green - dg, f2.blue - db, f2.alpha - da)
end
end
if d <= -1e-6
d += ry2 * (dx + 1 << 1) + rx2 * (3 - (dy << 1))
dx += 1
else
d += rx2 * (3 - (dy << 1))
end
dy -= 1
end while dy >= 0
end
#--------------------------------------------------------------------------
# ● 绘制实心圆
# x : 圆心 X 坐标
# y : 圆心 Y 坐标
# rad : 半径
# border_color : 圆的边框颜色 (Color)
# fill_color1 : 填充渐变的上颜色 (Color)
# fill_color2 : 填充渐变的下颜色 (Color)
#--------------------------------------------------------------------------
def fill_circle(x, y, rad, border_color, fill_color1 = border_color,
fill_color2 = fill_color1)
draw_ellipse(x, y, rad, rad, border_color, fill_color1, fill_color2)
end
#--------------------------------------------------------------------------
# ● 绘制实心椭圆
# x : 圆心 X 坐标
# y : 圆心 Y 坐标
# rad_x : 水平半径
# rad_y : 垂直半径
# border_color : 椭圆的边框颜色 (Color)
# fill_color1 : 填充渐变的上颜色 (Color)
# fill_color2 : 填充渐变的下颜色 (Color)
#--------------------------------------------------------------------------
def fill_ellipse(x, y, rad_x, rad_y, border_color, fill_color1 =
border_color, fill_color2 = fill_color1)
draw_ellipse(x, y, rad_x, rad_y, border_color, fill_color1, fill_color2)
end
#--------------------------------------------------------------------------
# ● 传送位图圆
# x : 圆心 X 坐标
# y : 圆心 Y 坐标
# rad : 半径
# src_bitmap : 传送元位图
# x0 : 传送圆心 X 坐标
# y0 : 传送圆心 Y 坐标
# opacity : 不透明度
#--------------------------------------------------------------------------
def blt_circle(x, y, rad, src_bitmap, x0, y0, opacity = 255)
blt_ellipse(x, y, rad, rad, src_bitmap, x0, y0, opacity)
end
#--------------------------------------------------------------------------
# ● 传送位图椭圆
# x : 圆心 X 坐标
# y : 圆心 Y 坐标
# rad_x : 水平半径
# rad_y : 垂直半径
# src_bitmap : 传送元位图
# x0 : 传送圆心 X 坐标
# y0 : 传送圆心 Y 坐标
# opacity : 不透明度
#--------------------------------------------------------------------------
def blt_ellipse(x, y, rad_x, rad_y, src_bitmap, x0, y0, opacity = 255)
raise ArgumentError if rad_x < 0 or rad_y < 0
return blt(x - rad_x, y, src_bitmap, Rect.new(x0 - rad_x, y0,
rad_x << 1, 1), opacity) if rad_y == 0
return blt(x, y - rad_y, src_bitmap, Rect.new(x0, y0 - rad_y,
1, rad_y << 1), opacity) if rad_x == 0
ry2 = rad_y * rad_y
rx2 = rad_x * rad_x
d = ry2 + rx2 * (0.25 - rad_y)
dx = 0
dy = rad_y
last_dy = nil
begin
if last_dy != dy
last_dy = dy
blt(x - dx + 1, y - dy, src_bitmap, Rect.new(x0 - dx + 1, y0 - dy,
(dx << 1) - 1, 1), opacity)
blt(x - dx + 1, y + dy, src_bitmap, Rect.new(x0 - dx + 1, y0 + dy,
(dx << 1) - 1, 1), opacity)
end
if d <= -1e-6
d += ry2 * ((dx << 1) + 3)
else
d += ry2 * ((dx << 1) + 3) + rx2 * (1 - dy << 1)
dy -= 1
end
dx += 1
end while ry2 * (dx + 1) < rx2 * (dy - 0.5)
d = ry2 * (dx + 0.5) * (dx + 0.5) + rx2 * (dy - 1) * (dy - 1) - rx2 * ry2
begin
if last_dy != dy
last_dy = dy
blt(x - dx + 1, y - dy, src_bitmap, Rect.new(x0 - dx + 1, y0 - dy,
(dx << 1) - 1, 1), opacity)
blt(x - dx + 1, y + dy, src_bitmap, Rect.new(x0 - dx + 1, y0 + dy,
(dx << 1) - 1, 1), opacity)
end
if d <= -1e-6
d += ry2 * (dx + 1 << 1) + rx2 * (3 - (dy << 1))
dx += 1
else
d += rx2 * (3 - (dy << 1))
end
dy -= 1
end while dy >= 0
end
end
测试用的代码:
r = Sprite.new
b = Bitmap.new(640, 480)
r.bitmap = b
b.gradient_draw_line(14, 41, 99, 78, Color.new(254, 243, 239), Color.new(37, 48, 65))
b.gradient_draw_line(99, 41, 14, 78, Color.new(254, 243, 239), Color.new(37, 48, 65))
b.gradient_draw_line(114, 78, 199, 41, Color.new(254, 243, 239), Color.new(37, 48, 65))
b.gradient_draw_line(199, 78, 114, 41, Color.new(254, 243, 239), Color.new(37, 48, 65))
b.fill_polygon(27,68,155,261,167,116,605,18,Color.new(255, 255, 255), Color.new(255,0,0), Color.new(232, 141, 232))
b.fill_ellipse(220,320,100,100,Color.new(255,255,255), Color.new(255, 0, 0), Color.new(0, 255, 0))
b.fill_isogon(320,240,85,7,45, Color.new(234,110,86), Color.new(0,78,89), Color.new(153, 57, 217))
b.draw_line(0, 420, 420, 420, Color.new(120,240,120), SailCat::Canvas_Config::CANVAS_LINE_BROKEN)
r.x = 0
r.y = 0
loop do
Graphics.update
Input.update
exit if Input.trigger?(Input::B)
end
r = Sprite.new
b = Bitmap.new(640, 480)
r.bitmap = b
b.gradient_draw_line(14, 41, 99, 78, Color.new(254, 243, 239), Color.new(37, 48, 65))
b.gradient_draw_line(99, 41, 14, 78, Color.new(254, 243, 239), Color.new(37, 48, 65))
b.gradient_draw_line(114, 78, 199, 41, Color.new(254, 243, 239), Color.new(37, 48, 65))
b.gradient_draw_line(199, 78, 114, 41, Color.new(254, 243, 239), Color.new(37, 48, 65))
b.fill_polygon(27,68,155,261,167,116,605,18,Color.new(255, 255, 255), Color.new(255,0,0), Color.new(232, 141, 232))
b.fill_ellipse(220,320,100,100,Color.new(255,255,255), Color.new(255, 0, 0), Color.new(0, 255, 0))
b.fill_isogon(320,240,85,7,45, Color.new(234,110,86), Color.new(0,78,89), Color.new(153, 57, 217))
b.draw_line(0, 420, 420, 420, Color.new(120,240,120), SailCat::Canvas_Config::CANVAS_LINE_BROKEN)
r.x = 0
r.y = 0
loop do
Graphics.update
Input.update
exit if Input.trigger?(Input::B)
end
效率方面没有问题,毕竟全屏也就640x480而已,而填充矩形操作其实很快,横向渐变本质是填充1高矩形,所以很快……纵向我知道慢8倍左右所以就不做了……
实际效率测试(i7-1065G7)
Bitmap#draw_ellipse 画和屏幕一样大的椭圆,约40次/帧(1600次/秒)
Bitmap#fill_ellipse,填涂和屏幕一样大的椭圆(无渐变),约20次/帧(808次/秒)
Bitmap#fill_ellipse,填涂和屏幕一样大的椭圆(有渐变),约16次/帧(638次/秒)
Bitmap#draw_isogon,画和屏幕一样大的正十六边形,约31次/帧(1240次/秒)
Bitmap#fill_isogon,填涂和屏幕一样大的正十六边形,约8次/帧(292次/秒)
Bitmap#blt_isogon,裁切和屏幕一样大的正十六边形,约12次/帧(474次/秒)
评价一下
ruby的效率,实话说确实不能算高,尤其是1.x版本,这也是为什么RMXP把分辨率限在640x480,VX和VA为了做能到60帧/秒,把分辨率降到了544x416的原因,太大的屏幕确实很难渲染过来(VX和XP是一样的1.81,VA是1.92)
但ruby的效率虽然不高,内建的Bitmap提供的接口方法效率却并不差,因为很早之前我就测试过,单是set_pixel这一个操作,每一帧(注意是帧,不是秒,是1/40秒)可以做大几千次,get_pixel能做上万次
有童鞋说这没法和WINAPI32调RtlMovememory来比,那个不在我们的讨论范围里,想想我就是为了在屏幕上画点儿东西,犯得着上WinAPI32吗?
而且RGSS从1到3,Bitmap类的方法和存储方式连变都没有变过,不得不说这是十几年前的制作工具至少还算得上“能用”的一个好处了。
这次写的插件,其实真的就是随便写的(我最开始只是需要一个Bitmap#draw_line方法,写着写着就跑偏了……),不算后面blt(那个也就花了半小时)的修改,大概总体上用了三四个小时修改+调试
细节的优化之处肯定还存在,比如draw_ellipse里面那8个很扎眼的set_pixel,可以封装的,但是意义不大了。这种代码封装得好看点未必说效率就能多高。减少不必要的执行量才是最重要的。
我倒是没有想过大家这么热情,反复测试,菜刀还提出了实际的应用场景,看起来这确实还是有一些用的吧。
效率的话上面已经测试过了,实话说,两个字:够用。要知道连draw_text做“屏幕一样大的”文字渲染都能给你卡的要死要活的,我这里所有的测试都是直接上和屏幕一样大的绘图。
这里面draw系列的方法,其复杂度是o(w+h),说人话就是,和你绘制的图形的周长正相关,因为屏幕640+480=1120,所以你就是画满了一圈2240个点,事实上和屏幕一样大的不论是圆,椭圆,矩形还是别的什么,还真的就是2240个点
fill系列的方法(包括blt),其复杂度从算法上讲都是o(w*h),也就是说和你绘制的图形的面积正相关(废话,要填充啊),这个时候,set_pixel是绝对不能用了,一帧能做8000次,一秒就是480000次,一秒只能渲染一个半屏幕的像素,那样会卡到没法用。而这个前提是除了set_pixel什么也别做,什么也别计算。
但是这些fill的方法,涉及到查找边缘,正交等复杂数学算法,全都是慢的不行不行的浮点计算,所以一秒能渲染一个屏幕就是顶天了,卡一秒刷一个图片,绝对没法忍受。
幸好,所有的fill方法,我全部调用的是逐行填充的fill_rect,由于Bitmap内部的存储方式,做横行填充要比纵列填充快近8倍。所以默认的实现,全部都使用了横行填充的方式,不论你渐变还是不渐变。
事实上所有的计算机图形学教程也都是教你怎么横行填充,内存的速度是一方面,还有另一个纵列填充很要命的问题:数学计算没法表示tan90度的值,绕不开的,需要你自己写东西来特别处理
fill_rect已经被证明一帧可以在全屏尺度(640*1像素)上执行两千多次,所以这里在加了形状限制的情况下,连上计算,大约每帧能做十几次,已经足够用了。
因为你实际使用是不需要填涂这么大的矩形的
像draw_ellipse(320,240,320,240)一帧能做40次,那draw_ellipse(320,240,32,24)一帧也不过就是做400次
而fill_ellipse(320,240,320,240)一帧能16次,fill_ellipse(320,240,32,24)一帧能做的次数就有200次之多。菜刀那个50大小的头像,真的是随便渲染,还有以前我记得有人提出的半环式血条什么的,也是随便搞,完全都不会卡,大概就是在你渲染的时候帧率会从40变成38或者39这个样子。
要知道自写界面最卡的是什么?是Sprite的旋转和缩放,是永远回避不了的draw_text方法
而不是这些简单的2D绘图指令
感谢大家给了我动力,我可能会研究一波曲线的画法然后这几天更新出来
能不能用,相信你们的测试和兴趣已经说明,这个插件并没那么莫名其妙了
所以标题改啦~
另(理论上VA可以用,我没试,因为Bitmap结构是一样的,VA区的童鞋有兴趣可以拿走拿走别客气)
作者: 百里_飞柳 时间: 2022-3-1 07:49
这也是可以顺手随便写出来的吗![](static/image/smiley/guapi/guapi9.jpg)
作者: Im剑侠客 时间: 2022-3-1 08:19
有点意思,联想起一个左右对比填涂的小游戏。
不知道像素点的描绘是否可以变为手动,不限预设形状,随着玩家操作,比如简单的跟随鼠标[单点一粒/按定涂鸦]?
作者: 灯笼菜刀王 时间: 2022-3-1 10:26
搭配半生烛光DLL里的蒙版可以搞非矩形头像框
不过, 它这个蒙版不太好用, 结果变黑白的~~
猫大是否考虑把它做成蒙版
-
未标题-2.jpg
(31.4 KB, 下载次数: 16)
作者: guoxiaomi 时间: 2022-3-1 10:56
本帖最后由 guoxiaomi 于 2022-3-1 13:13 编辑
三角形ABC,然后三个点各有自己的颜色,最终三角形内点P的颜色是这三个点颜色的线性插值,插值比例就是PBC:PAC:PAB。
顺便看到set_pixel我其实是很慌的,是不是可以靠之前某位朋友写的汇编来高速操作?
作者: RPGzh500223 时间: 2022-3-1 16:59
本帖最后由 RPGzh500223 于 2022-3-2 13:05 编辑
楼主考虑一下支持水平渐变,效率为垂直的一半(用Bitmap#stretch_blt,尽管看起来有点怪……)
用Bitmap#blt模拟水平渐变,效率大概垂直的58%,看起来正常多了
class Bitmap
# cx 椭圆中心点X 基本整型
# cy 椭圆中心点Y 基本整型
# ax 椭圆X半轴长度 无符号整型
# by 椭圆Y半轴长度 无符号整型
# mode 椭圆颜色填充方向(0:左->右;1:上->下) 无符号整型
# color 椭圆边框颜色 RMXP的Color实例对象
# fillB 椭圆填充的起始颜色 RMXP的Color实例对象
# fillE 椭圆填充的终止颜色 RMXP的Color实例对象 或 nil(单色填充)
def fill_ellipse2(cx, cy, ax, by, mode, color, fillB, fillE=nil)
if fillE
if mode == 0 then step = ax + ax - 1
elsif mode == 1 then step, fE = by + by - 1, fillE.clone
end
gR = (fillE.red - fillB.red ) / step
gG = (fillE.green - fillB.green) / step
gB = (fillE.blue - fillB.blue ) / step
gA = (fillE.alpha - fillB.alpha) / step
end
if mode == 0
if fillE
src, src_rect = Bitmap.new(ax + ax, 1), Rect.new(0, 0, 0, 0)
fB = fillB.clone
src.width.times do |x|
src.set_pixel(x, 0, fB)
fB.set(fB.red + gR, fB.green + gG, fB.blue + gB, fB.alpha + gA)
end
else
mode = 1
end
end
ratio, fB, w2 = ax.to_f / by, fillB.clone, 0
(by - 1).downto(0) do |y|
w = (Math.sqrt((by + y) * (by - y)) * ratio).to_i
if mode == 1
self.fill_rect(cx - w, cy - y, w + w, 1, fB)
if fillE
self.fill_rect(cx - w, cy + y, w + w, 1, fE)
fB.set(fB.red + gR, fB.green + gG, fB.blue + gB, fB.alpha + gA)
fE.set(fE.red - gR, fE.green - gG, fE.blue - gB, fE.alpha - gA)
else
self.fill_rect(cx - w, cy + y, w + w, 1, fB)
end
elsif mode == 0
src_rect.set(ax - w, 0, w + w, 1)
self.blt(cx - w, cy - y, src, src_rect)
self.blt(cx + w, cy + y, src, src_rect)
end
self.set_pixel(cx - w, cy - y, color)
self.set_pixel(cx + w, cy - y, color)
self.set_pixel(cx - w, cy + y, color)
self.set_pixel(cx + w, cy + y, color)
while w2 < w
self.set_pixel(cx - w2, cy - y - 1, color)
self.set_pixel(cx + w2, cy - y - 1, color)
self.set_pixel(cx - w2, cy + y + 1, color)
self.set_pixel(cx + w2, cy + y + 1, color)
w2 += 1
end
end
end
end
class Bitmap
# cx 椭圆中心点X 基本整型
# cy 椭圆中心点Y 基本整型
# ax 椭圆X半轴长度 无符号整型
# by 椭圆Y半轴长度 无符号整型
# mode 椭圆颜色填充方向(0:左->右;1:上->下) 无符号整型
# color 椭圆边框颜色 RMXP的Color实例对象
# fillB 椭圆填充的起始颜色 RMXP的Color实例对象
# fillE 椭圆填充的终止颜色 RMXP的Color实例对象 或 nil(单色填充)
def fill_ellipse2(cx, cy, ax, by, mode, color, fillB, fillE=nil)
if fillE
if mode == 0 then step = ax + ax - 1
elsif mode == 1 then step, fE = by + by - 1, fillE.clone
end
gR = (fillE.red - fillB.red ) / step
gG = (fillE.green - fillB.green) / step
gB = (fillE.blue - fillB.blue ) / step
gA = (fillE.alpha - fillB.alpha) / step
end
if mode == 0
if fillE
src, src_rect = Bitmap.new(ax + ax, 1), Rect.new(0, 0, 0, 0)
fB = fillB.clone
src.width.times do |x|
src.set_pixel(x, 0, fB)
fB.set(fB.red + gR, fB.green + gG, fB.blue + gB, fB.alpha + gA)
end
else
mode = 1
end
end
ratio, fB, w2 = ax.to_f / by, fillB.clone, 0
(by - 1).downto(0) do |y|
w = (Math.sqrt((by + y) * (by - y)) * ratio).to_i
if mode == 1
self.fill_rect(cx - w, cy - y, w + w, 1, fB)
if fillE
self.fill_rect(cx - w, cy + y, w + w, 1, fE)
fB.set(fB.red + gR, fB.green + gG, fB.blue + gB, fB.alpha + gA)
fE.set(fE.red - gR, fE.green - gG, fE.blue - gB, fE.alpha - gA)
else
self.fill_rect(cx - w, cy + y, w + w, 1, fB)
end
elsif mode == 0
src_rect.set(ax - w, 0, w + w, 1)
self.blt(cx - w, cy - y, src, src_rect)
self.blt(cx + w, cy + y, src, src_rect)
end
self.set_pixel(cx - w, cy - y, color)
self.set_pixel(cx + w, cy - y, color)
self.set_pixel(cx - w, cy + y, color)
self.set_pixel(cx + w, cy + y, color)
while w2 < w
self.set_pixel(cx - w2, cy - y - 1, color)
self.set_pixel(cx + w2, cy - y - 1, color)
self.set_pixel(cx - w2, cy + y + 1, color)
self.set_pixel(cx + w2, cy + y + 1, color)
w2 += 1
end
end
end
end
这RGB颜色渐变……
-
draw_radar.png
(45.83 KB, 下载次数: 14)
回复楼上的示意图GDI+50次/秒
-
h.png
(8.57 KB, 下载次数: 13)
-
v.png
(9.57 KB, 下载次数: 12)
作者: 灯笼菜刀王 时间: 2022-3-2 10:02
本帖最后由 灯笼菜刀王 于 2022-3-2 10:05 编辑
另外想到个用途, 只要不刷新, 可以无限往 bitmap 上画图形, 那也可用于"战争迷雾" , 即时描绘可能影响效率, 但是战棋是不需要即时描绘的![](static/image/smiley/yct/A002.gif)
以前我就想过这种用法, 然而只能画矩形, 太不像迷雾了, 就放弃了
为了比较有气氛, 猫大能否让描绘的图形带羽化边缘呢?(就是边缘半透明递减, 能自定义模糊半径更好
)
作者: SailCat 时间: 2022-3-6 02:19
已初步搞定了:绘制曲线、封闭曲线填充、使用图案(包括水平渐变样式)快速填充
就还得写几个接口用方法,明天来继续更新
作者: 陈大帅帅帅哥 时间: 2022-3-26 17:28
有没有办法绘制边缘渐变到透明的图形
欢迎光临 Project1 (https://rpg.blue/) |
Powered by Discuz! X3.1 |