查看: 3547|回复: 11

[原创发布] 【正式发布】RM BitmapCanvas 2D绘图插件

[复制链接]

遠航の猫咪

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

开拓者

发表于 2022-11-21 06:13:38 | 显示全部楼层 |阅读模式

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

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

×
不依赖SEP,插入默认工程应该就可以使用
提供20个绘图指令,无论如何应该够用了

Q&A:
Q:这个脚本能用来干什么?
A:绘制异型血槽(如环状)、绘制能力雷达(六边形战士)、绘制渐变转场中的渐变分隔线、异型透视窗格、等等……
Q:绘图指令具体如何调用?
A:参见脚本前面的说明。
Q:能用虚线绘制矩形和直线以外的形状吗?
A:不能。
Q:为什么可填充的形状中不包括矩形?
A:参见Bitmap#fill_rect,如果你使用RGD或者SEP Core那么还有Bitmap#gradient_fill_rect
Q:我想用平行四边形切一个位图传送怎么办?默认只有Bitmap#blt,而你这个插件也只能用正多边形切正方形或者菱形?
A:请找到四边形的四个顶点,然后调用blt_polygon,所有平行四边形都是多边形的一种。
Q:我的位图传送后被切边了,不完整?
A:只有用位图填充,即fill系指令时,你给的位图会被插件自动重复成图案以保证填满。仅单纯的传送不会复制位图,如果你的位图比传送的区域小,尝试使用填充指令而不是传送指令,或者你看看是不是参照点没有写对导致传歪了。
Q:渐变的效率怎么样?
A:肯定是不如GDI+,但是比draw_text快,反正你用足够了。

[pre lang="ruby"]#==============================================================================
# ■ [Bitmap Canvas] 位图画布处理核心 v1.1 by SailCat
#------------------------------------------------------------------------------
#   方法:本脚本插入Main之前使用
#   依赖:无
#   版本:v1.1 (Build 221121)
#   效果:
#     1. 可以用简单指令在位图对象上绘制图形,可以绘制从直线到封闭曲线的15种图形
#     2. 可以用简单指令在位图对象上叠加以不同形状(共5种)剪裁的其他位图
#   配置:画笔线形的配置
#   冲突:无
#   说明:
#     1. 绘制直线:
#        draw_line(起始x, 起始y, 结束x, 结束y, 颜色, 线形)
#     2. 绘制矩形(空心):
#       a) draw_rect(左上x, 左上y, 长, 宽, 颜色, 线形)
#       b) draw_rect(矩形, 颜色, 线形)
#     3. 绘制渐变色直线:
#        gradient_draw_line(起始x, 起始y, 结束x, 结束y, 颜色1, 颜色2)
#     4. 绘制路径(连续直线):
#        draw_path(点1x, 点1y, 点2x, 点2y, 点3x, 点3y[, ...], 颜色)
#     5. 绘制多边形(连续封闭直线):
#        draw_polygon(点1x, 点1y, 点2x, 点2y, 点3x, 点3y[, ...], 颜色)
#     6. 绘制正多边形:
#        draw_isogon(外接圆心x, 外接圆心y, 半径, 边数, 旋转角, 颜色)
#     7. 绘制椭圆:
#        draw_ellipse(圆心x, 圆心y, 半横径,半纵径, 颜色)
#     8. 绘制圆:
#        draw_circle(圆心x, 圆心y, 半径,颜色)
#     9. 绘制曲线:
#        draw_curve(起始x, 起始y, 中继点Ax, 中继点Ay, 中继点Bx, 中继点By[,
#          中继点Cx, 中继点Cy[, ...]], 结束x, 结束y, 颜色)
#     10. 绘制封闭曲线:
#        draw_closed_curve(点1x, 点1y, 中继1Ax, 中继1Ay, 中继1Bx, 中继1By,
#          点2x, 点2y[, 中继2Ax, 中继2Ay, 中继2Bx, 中继2By, 点3x, 点3y[, ...]],
#          颜色)
#     11.填涂多边形:
#       a) fill_polygon(点1x, 点1y, 点2x, 点2y, 点3x, 点3y[, ...], 边框颜色[,
#          填充颜色1[, 填充颜色2[, 渐变方向]]])
#       b) fill_polygon(点1x, 点1y, 点2x, 点2y, 点3x, 点3y[, ...], 边框颜色,
#          填充位图[, 不透明度])
#     12.填涂正多边形:
#       a) fill_isogon(外接圆心x, 外接圆心y, 半径, 边数, 旋转角, 边框颜色[,
#          填充颜色1[, 填充颜色2[, 渐变方向]]])
#       b) fill_isogon(外接圆心x, 外接圆心y, 半径, 边数, 旋转角, 边框颜色,
#          填充位图[, 不透明度])
#     13.填涂椭圆:
#       a) fill_ellipse(圆心x, 圆心y, 半横径,半纵径, 边框颜色[, 填充颜色1[,
#          填充颜色2[, 渐变方向]]])
#       b) fill_ellipse(圆心x, 圆心y, 半横径,半纵径, 边框颜色, 填充位图[,
#          不透明度])
#     14.填涂圆:
#       a) fill_circle(圆心x, 圆心y, 半径,边框颜色[, 填充颜色1[, 填充颜色2[,
#          渐变方向]]])
#       b) fill_circle(圆心x, 圆心y, 半径,边框颜色, 填充位图[, 不透明度])
#     15.填涂封闭曲线:
#       a) fill_closed_curve(点1x, 点1y, 中继1Ax, 中继1Ay, 中继1Bx, 中继1By,
#          点2x, 点2y[, 中继2Ax, 中继2Ay, 中继2Bx, 中继2By, 点3x, 点3y[, ...]],
#          边框颜色[, 填充颜色1[, 填充颜色2[, 渐变方向]]])
#       b) fill_closed_curve(点1x, 点1y, 中继1Ax, 中继1Ay, 中继1Bx, 中继1By,
#          点2x, 点2y[, 中继2Ax, 中继2Ay, 中继2Bx, 中继2By, 点3x, 点3y[, ...]],
#          边框颜色, 填充位图[, 不透明度])
#     16.传送多边形:
#        blt_polygon(点1x, 点1y, 点2x, 点2y, 点3x, 点3y[, ...], 源位图,
#          参考点1x, 参考点1y[, 不透明度])
#     17.传送正多边形:
#        blt_isogon(外接圆心x, 外接圆心y, 半径, 边数, 旋转角, 源位图, 参考心x,
#          参考心y[, 不透明度])
#     18.传送椭圆:
#        blt_ellipse(圆心x, 圆心y, 半横径,半纵径, 源位图, 参考心x, 参考心y[,
#          不透明度])
#     19.传送圆:
#        blt_circle(圆心x, 圆心y, 半径,源位图, 参考心x, 参考心y[, 不透明度])
#     20.传送封闭曲线:
#        blt_closed_curve(点1x, 点1y, 中继1Ax, 中继1Ay, 中继1Bx, 中继1By,
#          点2x, 点2y[, 中继2Ax, 中继2Ay, 中继2Bx, 中继2By, 点3x, 点3y[, ...]],
#          源位图, 参考点1x, 参考点1y[, 不透明度])
#==============================================================================
#==============================================================================
# ■ 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 = (b_index + 1) % 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 = (b_index + 1) % b_size
      end
      set_pixel(x, y.round, color)
    end
  end
  #--------------------------------------------------------------------------
  # ● 绘制空心矩形
  #     参数形式1 : 左上 X 坐标, 左上 Y 坐标, 宽度, 高度, 颜色, 线型
  #     参数形式2 : 目标矩形 (Rect), 颜色, 线型
  #--------------------------------------------------------------------------
  def draw_rect(*args)
    case args[0]
    when Numeric
      raise ArgumentError if args.size < 4
      x, y, w, h, color = args[0..4]
      style = args[5] || CANVAS_LINE_SOLID
    when Rect
      raise ArgumentError if args.size < 2
      rect = args[0]
      x, y, w, h = rect.x, rect.y, rect.width, rect.height
      color = args[1]
      style = args[2] || CANVAS_LINE_SOLID
    else
      raise ArgumentError
    end
    draw_line(x, y, x + w, y, color, style)
    draw_line(x, y, x, y + h, color, style)
    draw_line(x + w, y, x + w, y + h, color, style)
    draw_line(x, y + h, x + w, y + h, color, style)
  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 + 1).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 + 1).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[0] == 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[0] == 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 : 后续的参数,包括后续点,并在所有点之后指定填充选项,填充选项有:
  #            3个颜色 + true/false : 颜色1为边框色,颜色2和颜色3用于填充,最后
  #              true = 垂直方向渐变 false = 水平方向渐变
  #            3个颜色 : 颜色1用于描边,颜色2和颜色3用于填充,固定水平方向渐变
  #            2个颜色 : 颜色1用于描边,颜色2用于填充
  #            1个颜色 : 以该颜色描边,并实心填充
  #            颜色 + 位图 + 数值 : 颜色用于描边,位图用于填充,数值为不透明度
  #            颜色 + 位图 : 颜色用于描边,位图用于填充,不透明度固定为255
  #--------------------------------------------------------------------------
  def fill_polygon(x1, y1, x2, y2, x3, y3, *args)
    case args[-1]
    when FalseClass, TrueClass
      raise ArgumentError unless args[-4..-2].all?{|c| c.is_a?(Color)}
      fill_mode = args.pop ? 1 : 0
      fill2 = args.pop
      fill1 = args.pop
      fill_mode = 0 if fill1 == fill2
    when Color
      fill_mode = 0
      fill2 = args[-3].is_a?(Color) ? args.pop : args[-1]
      fill1 = args[-2].is_a?(Color) ? args.pop : args[-1]
    when Bitmap
      raise ArgumentError unless args[-2].is_a?(Color)
      fill_mode = 2
      fill_opacity = 255
      fill_pattern = args.pop
    when Numeric
      raise ArgumentError unless args[-3].is_a?(Color) and
        args[-2].is_a?(Bitmap)
      fill_mode = 2
      fill_opacity = args.pop
      fill_pattern = args.pop
    else
      raise ArgumentError
    end
    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_x = max_y = 0
    min_x = width
    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
      min_x = [min_x, xi].min
      max_x = [max_x, xi].max
      xj, yj = nodes[(i + 2) % nodes.size, 2]
      dx = 1.0 * (xi - xj) / (yi - yj)
      if yi < yj
        delta = -1
        delta -= 2 until (yh = nodes[(i + delta) % nodes.size]) != yi
        if yh < yi
          yi += 1
          xi += dx
        end
        edges[yi] ||= []
        edges[yi].push([xi, dx, yj])
      elsif yi > yj
        delta = 5
        delta += 2 until (yk = nodes[(i + delta) % nodes.size]) != yj
        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 and max_x - min_x > 1
    dw = max_x - min_x - 1
    dh = max_y - min_y - 1
    if fill_mode == 0
      if fill1 != fill2
        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
      aet = edges[min_y].each {|edge| edge[0] += edge[1]}.sort!
      for y in min_y + 1..max_y - 1
        aet.concat(edges[y]).sort! if edges[y]
        0.step(aet.size - 2, 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
          fill_rect(xi, y, xj - xi + 1, 1, f) if xj >= xi
        end
        aet.reject! {|edge| edge[2] == y}
        aet.each {|edge| edge[0] += edge[1]}.sort!
        f.set(f.red + dr, f.green + dg, f.blue + db, f.alpha + da) if gradient
      end
    else
      if fill_mode == 2
        src = Bitmap.new(dw, fill_pattern.height)
        0.step(dw - 1, fill_pattern.width) do |x|
          src.blt(x, 0, fill_pattern, fill_pattern.rect, fill_opacity)
        end
      else
        src = Bitmap.new(dw, 1)
        src.gradient_draw_line(0, 0, dw - 1, 0, fill1, fill2)
      end
      ox = -1 - min_x
      y_index = 0
      aet = edges[min_y].each {|edge| edge[0] += edge[1]}.sort!
      for y in min_y + 1..max_y - 1
        aet.concat(edges[y]).sort! if edges[y]
        0.step(aet.size - 2, 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
          blt(xi, y, src, Rect.new(xi + ox, y_index, xj - xi + 1, 1))
        end
        aet.reject! {|edge| edge[2] == y}
        aet.each {|edge| edge[0] += edge[1]}.sort!
        y_index = (y_index + 1) % src.height
      end
      src.dispose
    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
        delta = -1
        delta -= 2 until (yh = nodes[(i + delta) % nodes.size]) != yi
        if yh < yi
          yi += 1
          xi += dx
        end
        edges[yi] ||= []
        edges[yi].push([xi, dx, yj])
      elsif yi > yj
        delta = 5
        delta += 2 until (yk = nodes[(i + delta) % nodes.size]) != yj
        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
      aet.concat(edges[y]).sort! if edges[y]
      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)
      end
      aet.reject! {|edge| edge[2] == y}
      aet.each {|edge| edge[0] += edge[1]}.sort!
    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
    draw_polygon(*args.push(color))
  end
  #--------------------------------------------------------------------------
  # ● 绘制实心正多边形
  #     x : 正多边形外接圆心 X 坐标
  #     y : 正多边形外接圆心 Y 坐标
  #     rad : 正多边形外接圆 半径
  #     edges : 正多边形的边数
  #     angle : 正多边形旋转角度
  #     color : 边框的颜色 (Color)
  #     fill : 填充选项,具体的填充选项有:
  #            2个颜色 + true/false : 两个颜色以垂直/水平方式渐变填充
  #            2个颜色 : 两个颜色以水平方向渐变填充
  #            1个颜色 : 实心填充
  #            位图 + 数值 : 以位图图案填充,数值为不透明度
  #            位图 : 以位图图案填充,不透明度固定为255
  #            省略 : 用边框色实心填充
  #--------------------------------------------------------------------------
  def fill_isogon(x, y, rad, edges, angle, color, *fill)
    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
    fill_polygon(*args.push(color).concat(fill))
  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)
    draw_ellipse(x, y, rad, rad, color)
  end
  #--------------------------------------------------------------------------
  # ● 绘制椭圆
  #     x : 圆心 X 坐标
  #     y : 圆心 Y 坐标
  #     rad_x : 水平半径
  #     rad_y : 垂直半径
  #     color : 椭圆的颜色 (Color)
  #--------------------------------------------------------------------------
  def draw_ellipse(x, y, rad_x, rad_y, color)
    fill_ellipse(x, y, rad_x, rad_y, color, nil)
  end
  #--------------------------------------------------------------------------
  # ● 绘制实心圆
  #     x : 圆心 X 坐标
  #     y : 圆心 Y 坐标
  #     rad : 半径
  #     border_color : 圆的边框颜色 (Color)
  #     fill : 填充选项,具体的填充选项有:
  #            nil : 不填充
  #            2个颜色 + true/false : 两个颜色以垂直/水平方式渐变填充
  #            2个颜色 : 两个颜色以水平方向渐变填充
  #            1个颜色 : 实心填充
  #            位图 + 数值 : 以位图图案填充,数值为不透明度
  #            位图 : 以位图图案填充,不透明度固定为255
  #            省略 : 用边框色实心填充
  #--------------------------------------------------------------------------
  def fill_circle(x, y, rad, border_color, *fill)
    fill_ellipse(x, y, rad, rad, border_color, *fill)
  end
  #--------------------------------------------------------------------------
  # ● 绘制实心椭圆
  #     x : 圆心 X 坐标
  #     y : 圆心 Y 坐标
  #     rad_x : 水平半径
  #     rad_y : 垂直半径
  #     border_color : 椭圆的边框颜色 (Color)
  #     fill : 填充选项,具体的填充选项有:
  #            nil : 不填充
  #            2个颜色 + true/false : 两个颜色以垂直/水平方式渐变填充
  #            2个颜色 : 两个颜色以水平方向渐变填充
  #            1个颜色 : 实心填充
  #            位图 + 数值 : 以位图图案填充,数值为不透明度
  #            位图 : 以位图图案填充,不透明度固定为255
  #            省略 : 用边框色实心填充
  #--------------------------------------------------------------------------
  def fill_ellipse(x, y, rad_x, rad_y, border_color, *fill)
    raise ArgumentError if rad_x < 0 or rad_y < 0
    return set_pixel(x, y, border_color) if rad_x == 0 and rad_y == 0
    return fill_rect(x - rad_x, y, rad_x << 1, 1, border_color) if rad_y == 0
    return fill_rect(x, y - rad_y, 1, rad_y << 1, border_color) 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
    if fill.size == 0
      fill_mode = 0
      f1 = f2 = border_color.clone
    else
      case fill[0]
      when Color
        f1 = fill[0].clone
        if fill[1]
          f2 = fill[1].clone
          fill_mode = f1 == f2 ? 0 : (fill[2] ? 3 : 1)
        else
          f2 = f1
          fill_mode = 0
        end
      when Bitmap
        fill_mode = 2
      else
        fill_mode = -1
      end
    end
    case fill_mode
    when 1
      dist = rad_y - 1 << 1
      if dist > 0
        dr = (f2.red - f1.red) / dist
        dg = (f2.green - f1.green) / dist
        db = (f2.blue - f1.blue) / dist
        da = (f2.alpha - f1.alpha) / dist
      end
    when 2
      src = Bitmap.new((rad_x << 1) - 1, fill[0].height)
      0.step((rad_x << 1) - 1, fill[0].width) do |x0|
        src.blt(x0, 0, fill[0], fill[0].rect, fill[1] || 255)
      end
    when 3
      src = Bitmap.new((rad_x << 1) - 1, 1)
      src.gradient_draw_line(0, 0, rad_x - 1 << 1 , 0, f1, f2)
    end
    begin
      set_pixel(x + dx, y + dy, border_color)
      set_pixel(x + dx, y - dy, border_color)
      set_pixel(x - dx, y - dy, border_color)
      set_pixel(x - dx, y + dy, border_color)
      if d <= -1e-6
        d += ry2 * ((dx << 1) + 3)
      else
        d += ry2 * ((dx << 1) + 3) + rx2 * (1 - dy << 1)
        dy -= 1
        case fill_mode
        when 0, 1
          fill_rect(x - dx, y - dy, (dx << 1) + 1, 1, f1)
          fill_rect(x - dx, y + dy, (dx << 1) + 1, 1, f2)
          if fill_mode == 1
            f1.set(f1.red + dr, f1.green + dg, f1.blue + db, f1.alpha + da)
            f2.set(f2.red - dr, f2.green - dg, f2.blue - db, f2.alpha - da)
          end
        when 2, 3
          blt(x - dx, y - dy, src, Rect.new(rad_x - dx - 1,
            (rad_y - dy - 1) % src.height,(dx << 1) + 1, 1))
          blt(x - dx, y + dy, src, Rect.new(rad_x - dx - 1,
            (rad_y + dy - 1) % src.height,(dx << 1) + 1, 1))
        end
      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
    sy = dy
    begin
      set_pixel(x + dx, y + dy, border_color)
      set_pixel(x + dx, y - dy, border_color)
      set_pixel(x - dx, y - dy, border_color)
      set_pixel(x - dx, y + dy, border_color)
      if fill_mode >= 0 and dy < sy
        case fill_mode
        when 0, 1
          fill_rect(x - dx + 1, y - dy, (dx << 1) - 1, 1, f1)
          fill_rect(x - dx + 1, y + dy, (dx << 1) - 1, 1, f2) unless dy == 0
          if fill_mode == 1
            f1.set(f1.red + dr, f1.green + dg, f1.blue + db, f1.alpha + da)
            f2.set(f2.red - dr, f2.green - dg, f2.blue - db, f2.alpha - da)
          end
        when 2, 3
          blt(x - dx + 1, y - dy, src, Rect.new(rad_x - dx,
            (rad_y - dy - 1) % src.height,(dx << 1) - 1, 1))
          blt(x - dx + 1, y + dy, src, Rect.new(rad_x - dx,
            (rad_y + dy - 1) % src.height,(dx << 1) - 1, 1)) unless dy == 0
        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
    src.dispose if fill_mode > 1
  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
    begin
      if d <= -1e-6
        d += ry2 * ((dx << 1) + 3)
      else
        blt(x - dx, y - dy, src_bitmap, Rect.new(x0 - dx, y0 - dy, dx << 1, 1),
          opacity)
        blt(x - dx, y + dy, src_bitmap, Rect.new(x0 - dx, y0 + dy, dx << 1, 1),
          opacity)
        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
      blt(x - dx, y - dy, src_bitmap, Rect.new(x0 - dx, y0 - dy, dx << 1, 1),
        opacity)
      blt(x - dx, y + dy, src_bitmap, Rect.new(x0 - dx, y0 + dy, dx << 1, 1),
        opacity) unless dy == 0
      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
  #--------------------------------------------------------------------------
  # ● 绘制曲线
  #     x1 : 起始点 X 坐标
  #     x2 : 起始点 Y 坐标
  #     cx1 : 控制点1 X 坐标
  #     cy1 : 控制点1 Y 坐标
  #     cx2 : 控制点2 X 坐标
  #     cx2 : 控制点2 Y 坐标
  #     x2 : 终止点(或控制点3) X 坐标
  #     y2 : 终止点(或控制点3) Y 坐标
  #     args : 后续的参数,可以追加额外的控制点,以最后一个点的坐标为终止点,
  #            最后还需再指定一个颜色 (Color)
  #--------------------------------------------------------------------------
  def draw_curve(x1, y1, cx1, cy1, cx2, cy2, x2, y2, *args)
    raise ArgumentError if args.size[0] == 0 or not args[-1].is_a?(Color)
    color = args[-1]
    points = [x1, y1, cx1, cy1, cx2, cy2, x2, y2].concat(args[0...-1])
    draw_path(*get_curve_path(points).push(color))
  end
  #--------------------------------------------------------------------------
  # ● 绘制封闭曲线
  #     x1 : 起始点 X 坐标
  #     x2 : 起始点 Y 坐标
  #     cx1 : 控制点1 X 坐标
  #     cy1 : 控制点1 Y 坐标
  #     cx2 : 控制点2 X 坐标
  #     cx2 : 控制点2 Y 坐标
  #     x2 : 终止点 X 坐标
  #     y2 : 终止点 Y 坐标
  #     args : 后续的参数,每多指定一个曲线节点,增加6个参数,分别表示从前一终
  #            止点作为起始点曲线的控制点1 X/Y、控制点2 X/Y、及本曲线终点的X/Y
  #            最后还需再指定一个颜色 (Color)
  #--------------------------------------------------------------------------
  def draw_closed_curve(x1, y1, cx1, cy1, cx2, cy2, x2, y2, *args)
    raise ArgumentError if args.size % 6 != 1 or not args[-1].is_a?(Color)
    color = args[-1]
    points = [x1, y1, cx1, cy1, cx2, cy2, x2, y2].concat(args[0...-1])
    edges = []
    0.step(points.size - 8, 6) do |i|
      edges.concat(get_curve_path(points[i, 8]))
    end
    draw_polygon(*edges.push(color))
  end
  #--------------------------------------------------------------------------
  # ● 绘制填充的封闭曲线
  #     x1 : 起始点 X 坐标
  #     x2 : 起始点 Y 坐标
  #     cx1 : 控制点1 X 坐标
  #     cy1 : 控制点1 Y 坐标
  #     cx2 : 控制点2 X 坐标
  #     cx2 : 控制点2 Y 坐标
  #     x2 : 终止点 X 坐标
  #     y2 : 终止点 Y 坐标
  #     args : 后续的参数,每多指定一个曲线节点,增加6个参数,分别表示从前终止
  #            点起始的下一曲线段控制点1 X/Y、控制点2 X/Y、及该曲线终止点的X/Y
  #            最后要指定填充选项,填充选项有:
  #            3个颜色 + true/false : 颜色1为边框色,颜色2和颜色3用于填充,最后
  #              true = 垂直方向渐变 false = 水平方向渐变
  #            3个颜色 : 颜色1用于描边,颜色2和颜色3用于填充,水平方向渐变
  #            2个颜色 : 颜色1用于描边,颜色2用于填充
  #            1个颜色 : 以该颜色描边,并实心填充
  #            颜色 + 位图 + 数值 : 颜色用于描边,位图用于填充,数值为不透明度
  #            颜色 + 位图 : 颜色用于描边,位图用于填充,不透明度固定为255
  #--------------------------------------------------------------------------
  def fill_closed_curve(x1, y1, cx1, cy1, cx2, cy2, x2, y2, *args)
    case args[-1]
    when FalseClass, TrueClass
      arg_chop = -4
    when Color
      arg_chop = args[-3].is_a?(Color) ? -3 :
        args[-2].is_a?(Color) ? -2 : -1
    when Bitmap
      arg_chop = -2
    when Numeric
      arg_chop = -3
    else
      raise ArgumentError
    end
    raise ArgumentError if (args.size + arg_chop) % 6 != 0
    points = [x1, y1, cx1, cy1, cx2, cy2, x2, y2].concat(args[0...arg_chop])
    edges = []
    0.step(points.size - 8, 6) do |i|
      edges.concat(get_curve_path(points[i, 8])[0...-2])
    end
    fill_polygon(*edges.concat(args[arg_chop..-1]))
  end
  #--------------------------------------------------------------------------
  # ● 传送位图封闭曲线
  #     x1 : 起始点 X 坐标
  #     x2 : 起始点 Y 坐标
  #     cx1 : 控制点1 X 坐标
  #     cy1 : 控制点1 Y 坐标
  #     cx2 : 控制点2 X 坐标
  #     cx2 : 控制点2 Y 坐标
  #     x2 : 终止点 X 坐标
  #     y2 : 终止点 Y 坐标
  #     args : 后续的参数,每多指定一个曲线节点,增加6个参数,分别表示从前终止
  #            点起始的下一曲线段控制点1 X/Y、控制点2 X/Y、及该曲线终止点的X/Y
  #            最后指定传送的位图, 偏移x,偏移y(比照起始点),(可选)不透明度
  #--------------------------------------------------------------------------
  def blt_closed_curve(x1, y1, cx1, cy1, cx2, cy2, x2, y2, *args)
    if args[-3].is_a?(Bitmap)
      start = -3
      points = [x1, y1, cx1, cy1, cx2, cy2, x2, y2].concat(args[0...-3])
    elsif args[-4].is_a?(Bitmap)
      start = -4
      points = [x1, y1, cx1, cy1, cx2, cy2, x2, y2].concat(args[0...-4])
    else
      raise ArgumentError
    end
    raise ArgumentError unless points.size % 6 == 2
    edges = []
    0.step(points.size - 8, 6) do |i|
      edges.concat(get_curve_path(points[i, 8]))
    end
    blt_polygon(*edges.concat(args[start..-1]))
  end
  private
  #--------------------------------------------------------------------------
  # ● 内部函数:计算曲线点位
  #     points : 接继点表
  #--------------------------------------------------------------------------
  def get_curve_path(points)
    max_x = min_x = points[0]
    max_y = min_y = points[1]
    2.step(points.size - 2, 2) do |i|
      max_x = [max_x, points].max
      min_x = [min_x, points].min
      max_y = [max_y, points[i + 1]].max
      min_y = [min_y, points[i + 1]].min
    end
    curve_edge_count = [max_x + max_y - min_x - min_y >> 2, 2].max
    (0..curve_edge_count).inject([]) do |a, i|
      pt = points.clone
      t = i / curve_edge_count.to_f
      until pt.size == 2
        pt = (0...(pt.size >> 1) - 1).to_a.inject([]) do |b, j|
          x1, y1, x2, y2 = pt[j << 1, 4]
          b.push((1 - t) * x1 + t * x2, (1 - t) * y1 + t * y2)
        end
      end
      x = pt[0].round
      y = pt[1].round
      x == a[-2] && y == a[-1] ? a : a.push(x, y)
    end
  end
end
[/pre]

评分

参与人数 5星屑 +360 赞 +4 收起 理由
jienluck + 1
陈大帅帅帅哥 + 1 精品文章
fux2 + 360 精品文章
guoxiaomi + 1 精品文章
灯笼菜刀王 + 1 精品文章

查看全部评分

SailCat (小猫子·要开心一点) 共上站 24 次,发表过 11 篇文章 上 次 在: [2006年01月28日11:41:18 星期六] 从 [162.105.120.91] 到本站一游。
梦石
0
星屑
42235
在线时间
6017 小时
注册时间
2006-11-10
回帖
6966
发表于 2022-11-21 10:02:25 | 显示全部楼层
正好是我需要的, 猫大威武

20种描绘图形很够用了, 不过还少了一种 "粘贴入" , 即 将 bitmapA 传送入 bitmapB 非空白像素里的绘制, 我曾经用 循环X和Y逐点blt的方式画入, 太没效率, 猫大能有更效率的方法么?

回复

使用道具 举报

遠航の猫咪

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

开拓者

 楼主| 发表于 2022-11-21 10:21:23 | 显示全部楼层
灯笼菜刀王 发表于 2022-11-21 10:02
正好是我需要的, 猫大威武

20种描绘图形很够用了, 不过还少了一种 "粘贴入" , 即 将 bit ...

blt系的那些指令就是叠加粘贴啊,甚至你可以指定叠加不透明度的
bitmapB.blt_ellipse(x, y, rx, ry, bitmapA, x0, y0, opacity)
将bitmapA以x0,y0为圆心,rx, ry为长短轴的椭圆传送到bitmapB以x,y为圆心的位置
你看一下实现就知道,除了被blt的那些东西,并没有什么原来的像素被清除掉。

如果说真缺什么功能,那就是缺异型的stretch_blt,但这个我真的做不到,效率太低了。

点评

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

使用道具 举报

老黄鸡

梦石
4
星屑
45695
在线时间
7947 小时
注册时间
2009-7-6
回帖
13337

MZ评测员RM创作大赛01组委会开拓者贵宾

发表于 2022-11-21 11:50:39 | 显示全部楼层
很清晰地展示了各个方法的实现是最有参考价值的
不过效率方面一直是个麻烦的难题
RGDirect - DirectX驱动的RGSS,点我了解.
(排满,暂停)RM全系列成套系统定制请联系QQ1213237796
不接受对其他插件维护的委托
回复

使用道具 举报

梦石
2
星屑
13779
在线时间
2319 小时
注册时间
2011-6-4
回帖
586
发表于 2022-11-21 17:30:59 | 显示全部楼层
强大如猫佬
回复

使用道具 举报

梦石
0
星屑
39236
在线时间
11155 小时
注册时间
2009-3-15
回帖
4768
发表于 2022-11-21 18:07:02 | 显示全部楼层
有没拋物线的画法
回复

使用道具 举报

梦石
0
星屑
2337
在线时间
325 小时
注册时间
2018-7-12
回帖
378
发表于 2022-11-21 19:35:10 | 显示全部楼层
环形计量槽是个非常有意思的设定,欲望之花开发初期也准备使用环形计量槽来着,但当时只使用切图和位移的手法来伪造环形,看样子现在有全新的处理方式了
回复 0 1

使用道具 举报

梦石
0
星屑
923
在线时间
125 小时
注册时间
2014-12-5
回帖
95
发表于 2023-1-26 03:36:35 | 显示全部楼层
有没有范例工程?

点评

没有  发表于 2023-1-26 11:29
回复

使用道具 举报

梦石
0
星屑
5374
在线时间
273 小时
注册时间
2022-12-18
回帖
265
发表于 2023-1-26 15:59:56 | 显示全部楼层
很好很强大
回复

使用道具 举报

梦石
0
星屑
7989
在线时间
1319 小时
注册时间
2006-7-18
回帖
459
发表于 2023-1-26 16:35:37 | 显示全部楼层
有没有教学,例子啊!
回复

使用道具 举报

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

本版积分规则

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

在本版发帖返回顶部