查看: 3017|回复: 24

[原创发布] RM 2D绘图的插件(加了传送位图功能啦,效率测试)

[复制链接]

遠航の猫咪

梦石
3
星屑
26947
在线时间
2423 小时
注册时间
2005-10-15
回帖
987

开拓者

发表于 2022-3-1 07:07:53 | 显示全部楼层 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有账号?注册会员

×
本帖最后由 SailCat 于 2022-3-1 22:07 编辑

不依赖SEP Core运行,所以独立发一下
但是……谢谢大家的测试我只能说[泪目]……看来这个功能做出来终究还是有它的可取之处的,那就后续再更新一下吧~
就是写着顺手就写出来了……

[pre lang="ruby"]#==============================================================================
# ■ [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[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[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
[/pre]

测试用的代码:
[pre lang="ruby"]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[/pre]
20220301070605.png
20220301134058.png

效率方面没有问题,毕竟全屏也就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-2 13:15
就说如果你们觉得这个插件有用我可以继续开发其他形状,不然就放着吧……  发表于 2022-3-1 07:28

评分

参与人数 4赞 +4 收起 理由
sdgn + 1 精品文章
张咚咚 + 1 精品文章
taeckle + 1 我们都要像大神学习!
灯笼菜刀王 + 1 &amp;quot;顺手写出来的&amp;quot;

查看全部评分

SailCat (小猫子·要开心一点) 共上站 24 次,发表过 11 篇文章 上 次 在: [2006年01月28日11:41:18 星期六] 从 [162.105.120.91] 到本站一游。

老鹰

梦石
40
星屑
37812
在线时间
7091 小时
注册时间
2012-5-26
回帖
3297

极短24评委极短23参与极短22参与极短21评委老司机慢点开短篇十吟唱者组别冠军开拓者剧作品鉴家

发表于 2022-3-1 07:49:47 | 显示全部楼层
这也是可以顺手随便写出来的吗
回复

使用道具 举报

梦石
0
星屑
8026
在线时间
1051 小时
注册时间
2012-4-3
回帖
1270

开拓者

发表于 2022-3-1 08:19:09 来自手机 | 显示全部楼层
有点意思,联想起一个左右对比填涂的小游戏。
不知道像素点的描绘是否可以变为手动,不限预设形状,随着玩家操作,比如简单的跟随鼠标[单点一粒/按定涂鸦]?

点评

像素点描绘已有,剩下的核心的部分,解决这两条就基本成了:输入描绘、判定不规则图形的填涂饱腹度。  发表于 2022-3-2 08:26
是啊,像Photoshop工具的钢笔头,出墨量可以选的那种就更人性化了。如果猫大有这个扩展意愿的话,我可以搜集一些关于这个小游戏详细的材料补充。  发表于 2022-3-2 08:17
有点意思,但是一个像素点也实在太小了,要做,就得做成画笔式的,也就是描绘的线条本身是有宽度的  发表于 2022-3-1 23:19
回复

使用道具 举报

梦石
0
星屑
42235
在线时间
6017 小时
注册时间
2006-11-10
回帖
6966
发表于 2022-3-1 10:26:15 | 显示全部楼层
未标题-1.jpg

搭配半生烛光DLL里的蒙版可以搞非矩形头像框

不过, 它这个蒙版不太好用, 结果变黑白的~~

猫大是否考虑把它做成蒙版
未标题-2.jpg

点评

效果很好  发表于 2022-3-1 15:21
a.bitmap.blt_circle(50, 50, 50, RPG::Cache.is(9, "baixue_G"), 50, 50, 255)  发表于 2022-3-1 13:43
做成刀把版  发表于 2022-3-1 10:45
回复

使用道具 举报

梦石
1
星屑
24088
在线时间
3339 小时
注册时间
2011-7-8
回帖
3862

开拓者

发表于 2022-3-1 10:56:07 | 显示全部楼层
本帖最后由 guoxiaomi 于 2022-3-1 13:13 编辑

三角形ABC,然后三个点各有自己的颜色,最终三角形内点P的颜色是这三个点颜色的线性插值,插值比例就是PBC:PAC:PAB。

顺便看到set_pixel我其实是很慌的,是不是可以靠之前某位朋友写的汇编来高速操作?

点评

这是顶点着色??挺好奇怎么算(插值几乎都是浮点运算……) 简单的周边渐变填充GDI+可以直接画在RMXP的bitmap上,就是效率很低……  发表于 2022-3-1 16:43
我再看看!  发表于 2022-3-1 15:01
一帧能做7000次 而凡是用到set_pixel的算法其复杂度都是o(w+h) 所以……复杂度是o(wh)的我全部是用的fill_rect  发表于 2022-3-1 13:29
熟悉rgss和ruby,xp区版主~
正在填坑:《膜拜组传奇》讲述膜拜组和学霸们的故事。
已上steam:与TXBD合作的Reformers《变革者》
* 战斗调用公共事件 *
* RGSOS 网络脚本 *
回复

使用道具 举报

梦石
0
星屑
6483
在线时间
119 小时
注册时间
2020-1-8
回帖
205
发表于 2022-3-1 16:59:30 | 显示全部楼层
本帖最后由 RPGzh500223 于 2022-3-2 13:05 编辑

楼主考虑一下支持水平渐变,效率为垂直的一半(用Bitmap#stretch_blt,尽管看起来有点怪……)
用Bitmap#blt模拟水平渐变,效率大概垂直的58%,看起来正常多了
[pre lang="ruby"]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[/pre]

这RGB颜色渐变……

回复楼上的示意图GDI+50次/秒

回复楼上的示意图GDI+50次/秒
h.png
v.png

点评

昨天我把blt实现了以后就知道结合原始blt做水平渐变根本不是问题了,只不过就是填充一个w*1那么长的然后一行行做进去  发表于 2022-3-2 17:00
50次秒得gdi+的速度吧,纯RGSS应该到不了,因为所有不走fill_rect或者没法走fill_rect的像素点描绘,极限就是几千个点/帧,1/50秒这还不到一帧  发表于 2022-3-1 19:47
回复

使用道具 举报

梦石
0
星屑
42235
在线时间
6017 小时
注册时间
2006-11-10
回帖
6966
发表于 2022-3-2 10:02:27 | 显示全部楼层
本帖最后由 灯笼菜刀王 于 2022-3-2 10:05 编辑

另外想到个用途, 只要不刷新, 可以无限往 bitmap 上画图形, 那也可用于"战争迷雾" ,  即时描绘可能影响效率, 但是战棋是不需要即时描绘的

以前我就想过这种用法, 然而只能画矩形, 太不像迷雾了, 就放弃了

为了比较有气氛,  猫大能否让描绘的图形带羽化边缘呢?(就是边缘半透明递减, 能自定义模糊半径更好 )

点评

sepcore里有bitmap#blur  发表于 2022-3-2 11:49
回复

使用道具 举报

遠航の猫咪

梦石
3
星屑
26947
在线时间
2423 小时
注册时间
2005-10-15
回帖
987

开拓者

 楼主| 发表于 2022-3-6 02:19:39 | 显示全部楼层
已初步搞定了:绘制曲线、封闭曲线填充、使用图案(包括水平渐变样式)快速填充

SEP Debug 2022_3_6 2_15_48.png

就还得写几个接口用方法,明天来继续更新

点评

画笔粗度用set_pixel重载效果不理想,大概有一半的常数复杂度被浪费掉了(因为画笔是一格一格前进的,重载set_pixel导致全方向重涂,实际没必要)  发表于 2022-3-6 02:21

评分

参与人数 1赞 +1 收起 理由
guoxiaomi + 1 塞糖

查看全部评分

SailCat (小猫子·要开心一点) 共上站 24 次,发表过 11 篇文章 上 次 在: [2006年01月28日11:41:18 星期六] 从 [162.105.120.91] 到本站一游。
回复

使用道具 举报

梦石
0
星屑
4657
在线时间
1069 小时
注册时间
2013-3-28
回帖
309

开拓者

发表于 2022-3-26 17:28:53 | 显示全部楼层
有没有办法绘制边缘渐变到透明的图形
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖返回顶部