设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
查看: 2510|回复: 24
打印 上一主题 下一主题

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

[复制链接]

Lv5.捕梦者 (版主)

遠航の猫咪

梦石
3
星屑
23206
在线时间
2387 小时
注册时间
2005-10-15
帖子
1166

开拓者

跳转到指定楼层
1
发表于 2022-3-1 07:07:53 | 只看该作者 |只看大图 回帖奖励 |正序浏览 |阅读模式

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

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

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

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

RUBY 代码复制
  1. #==============================================================================
  2. # ■ [Bitmap Canvas] 位图画布处理核心 v1.0 by SailCat
  3. #------------------------------------------------------------------------------
  4. #   方法:本脚本插入Main之前使用
  5. #   依赖:无
  6. #   版本:v1.0 (Build 220228)
  7. #   效果:
  8. #     1. 可以用简单指令在位图对象上画图
  9. #   配置:画笔线形的配置
  10. #   冲突:无
  11. #   说明:
  12. #     1. 绘制直线:
  13. #        draw_line(起始x, 起始y, 结束x, 结束y, 颜色, 线形)
  14. #     2. 绘制渐变色直线:
  15. #        gradient_draw_line(起始x, 起始y, 结束x, 结束y, 颜色1, 颜色2)
  16. #     3. 绘制路径(连续直线):
  17. #        draw_path(点1x, 点1y, 点2x, 点2y, 点3x, 点3y[, ...], 颜色)
  18. #     4. 绘制多边形(连续封闭直线):
  19. #        draw_polygon(点1x, 点1y, 点2x, 点2y, 点3x, 点3y[, ...], 颜色)
  20. #     5. 绘制正多边形:
  21. #        draw_isogon(外接圆心x, 外接圆心y, 半径, 边数, 旋转角, 颜色)
  22. #     6. 绘制椭圆:
  23. #        draw_ellipse(圆心x, 圆心y, 半横径,半纵径, 颜色)
  24. #     7. 绘制圆:
  25. #        draw_circle(圆心x, 圆心y, 半径,颜色)
  26. #     8. 填涂多边形:
  27. #        fill_polygon(点1x, 点1y, 点2x, 点2y, 点3x, 点3y[, ...], 边框颜色[,
  28. #          填充颜色1[, 填充颜色2]])
  29. #     9. 填涂正多边形:
  30. #        fill_isogon(外接圆心x, 外接圆心y, 半径, 边数, 旋转角, 边框颜色[,
  31. #          填充颜色1[, 填充颜色2]])
  32. #     10.填涂椭圆:
  33. #        fill_ellipse(圆心x, 圆心y, 半横径,半纵径, 边框颜色[, 填充颜色1[,
  34. #          填充颜色2]])
  35. #     11.填涂圆:
  36. #        fill_circle(圆心x, 圆心y, 半径,边框颜色[, 填充颜色1[, 填充颜色2]])
  37. #     12.传送多边形:
  38. #        blt_polygon(点1x, 点1y, 点2x, 点2y, 点3x, 点3y[, ...], 源位图,
  39. #          参考点1, 参考点1y[, 不透明度])
  40. #     13.传送正多边形:
  41. #        blt_isogon(外接圆心x, 外接圆心y, 半径, 边数, 旋转角, 源位图, 参考心x,
  42. #          参考心y[, 不透明度])
  43. #     14.传送椭圆:
  44. #        blt_ellipse(圆心x, 圆心y, 半横径,半纵径, 源位图, 参考心x, 参考心y[,
  45. #          不透明度])
  46. #     15.传送圆:
  47. #        blt_circle(圆心x, 圆心y, 半径,源位图, 参考心x, 参考心y[, 不透明度])
  48. #==============================================================================
  49. #==============================================================================
  50. # ■ SailCat's 插件公用
  51. #==============================================================================
  52. module SailCat
  53.   #--------------------------------------------------------------------------
  54.   # ● 脚本配置区
  55.   #--------------------------------------------------------------------------
  56.   module Canvas_Config
  57.     CANVAS_LINE_DOTTED = [1, 0]               # 点线画笔
  58.     CANVAS_LINE_DASHED = [1, 1, 0, 1]         # 短虚线画笔
  59.     CANVAS_LINE_LONGDASH = [1, 1, 0, 0, 1, 1] # 长虚线画笔
  60.     CANVAS_LINE_DASHDOT = [1, 0, 1, 1, 1, 0]  # 点划线画笔
  61.     CANVAS_LINE_BROKEN = [1, 0, 0]            # 散点画笔
  62.   end
  63. end
  64.  
  65. #==============================================================================
  66. # ■ Bitmap
  67. #==============================================================================
  68. class Bitmap
  69.   include SailCat::Canvas_Config
  70.   #--------------------------------------------------------------------------
  71.   # ● 常量
  72.   #--------------------------------------------------------------------------
  73.   CANVAS_LINE_SOLID = [1]                     # 实心画笔,该常量禁止修改配置
  74.   #--------------------------------------------------------------------------
  75.   # ● 绘制直线
  76.   #     x1 : 起始的 X 坐标
  77.   #     y1 : 起始的 Y 坐标
  78.   #     x2 : 结束的 X 坐标
  79.   #     y2 : 结束的 Y 坐标
  80.   #     color : 直线的颜色 (Color)
  81.   #     style : 直线的线型
  82.   #--------------------------------------------------------------------------
  83.   def draw_line(x1, y1, x2, y2, color, style = CANVAS_LINE_SOLID)
  84.     return set_pixel(x1, y1, color) if x1 == x2 and y1 == y2
  85.     dx = (x2 - x1).abs
  86.     dy = (y2 - y1).abs
  87.     x = x1
  88.     y = y1
  89.     rx = x2 - x1 <=> 0
  90.     ry = y2 - y1 <=> 0
  91.     b_index = 0
  92.     b_size = style.size
  93.     if dx == 0 and style == CANVAS_LINE_SOLID
  94.       fill_rect(x1, [y1, y2].min, 1, dy + 1, color)
  95.     elsif dy == 0 and style == CANVAS_LINE_SOLID
  96.       fill_rect([x1, x2].min, y1, dx + 1, 1, color)
  97.     elsif dx <= dy
  98.       f = dx.to_f * rx / dy
  99.       dy.times do
  100.         set_pixel(x.round, y, color) if style[b_index] == 1
  101.         x += f
  102.         y += ry
  103.         b_index += 1
  104.         b_index %= b_size
  105.       end
  106.       set_pixel(x.round, y, color)
  107.     else
  108.       f = dy.to_f * ry / dx
  109.       dx.times do
  110.         set_pixel(x, y.round, color) if style[b_index] == 1
  111.         x += rx
  112.         y += f
  113.         b_index += 1
  114.         b_index %= b_size
  115.       end
  116.       set_pixel(x, y.round, color)
  117.     end
  118.   end
  119.   #--------------------------------------------------------------------------
  120.   # ● 绘制渐变直线
  121.   #     x1 : 起始的 X 坐标
  122.   #     y1 : 起始的 Y 坐标
  123.   #     x2 : 结束的 X 坐标
  124.   #     y2 : 结束的 Y 坐标
  125.   #     color1 : 直线的起始颜色 (Color)
  126.   #     color2 : 直线的终止颜色 (Color)
  127.   #--------------------------------------------------------------------------
  128.   def gradient_draw_line(x1, y1, x2, y2, color1, color2)
  129.     return set_pixel(x1, y1, color) if x1 == x2 and y1 == y2
  130.     return draw_line(x1, y1, x2, y2, color1) if color1 == color2
  131.     dx = (x2 - x1).abs
  132.     dy = (y2 - y1).abs
  133.     rx = x2 - x1 <=> 0
  134.     ry = y2 - y1 <=> 0
  135.     c = color1.clone
  136.     if dx <= dy
  137.       f = dx.to_f / dy * rx
  138.       x = x1.to_f
  139.       y = y1
  140.       dr = (color2.red - color1.red) / dy
  141.       dg = (color2.green - color1.green) / dy
  142.       db = (color2.blue - color1.blue) / dy
  143.       da = (color2.alpha - color1.alpha) / dy
  144.       dy.times do
  145.         set_pixel(x.round, y, c)
  146.         x += f
  147.         y += ry
  148.         c.set(c.red + dr, c.green + dg, c.blue + db, c.alpha + da)
  149.       end
  150.     else
  151.       f = dy.to_f / dx * ry
  152.       x = x1
  153.       y = y1.to_f
  154.       dr = (color2.red - color1.red) / dx
  155.       dg = (color2.green - color1.green) / dx
  156.       db = (color2.blue - color1.blue) / dx
  157.       da = (color2.alpha - color1.alpha) / dx
  158.       dx.times do
  159.         set_pixel(x, y.round, c)
  160.         x += rx
  161.         y += f
  162.         c.set(c.red + dr, c.green + dg, c.blue + db, c.alpha + da)
  163.       end
  164.     end
  165.   end
  166.   #--------------------------------------------------------------------------
  167.   # ● 绘制路径
  168.   #     x1 : 第一点的 X 坐标
  169.   #     y1 : 第一点的 Y 坐标
  170.   #     x2 : 第二点的 X 坐标
  171.   #     y2 : 第二点的 Y 坐标
  172.   #     x3 : 第三点的 X 坐标
  173.   #     y3 : 第三点的 Y 坐标
  174.   #     args : 后续的参数(可以加多个点,至少要包括颜色)
  175.   #--------------------------------------------------------------------------
  176.   def draw_path(x1, y1, x2, y2, x3, y3, *args)
  177.     raise ArgumentError if args.size % 2 == 0 or not args[-1].is_a?(Color)
  178.     color = args[-1]
  179.     nodes = [x1, y1, x2, y2, x3, y3].concat(args[0...-1])
  180.     0.step(nodes.size - 4, 2) {|i| draw_line(*(nodes[i, 4].concat([color])))}
  181.   end
  182.   #--------------------------------------------------------------------------
  183.   # ● 绘制多边形
  184.   #     x1 : 第一点的 X 坐标
  185.   #     y1 : 第一点的 Y 坐标
  186.   #     x2 : 第二点的 X 坐标
  187.   #     y2 : 第二点的 Y 坐标
  188.   #     x3 : 第三点的 X 坐标
  189.   #     y3 : 第三点的 Y 坐标
  190.   #     args : 后续的参数(可以加多个点,至少要包括颜色)
  191.   #--------------------------------------------------------------------------
  192.   def draw_polygon(x1, y1, x2, y2, x3, y3, *args)
  193.     raise ArgumentError if args.size % 2 == 0 or not args[-1].is_a?(Color)
  194.     args.insert(-2, x1, y1)
  195.     draw_path(x1, y1, x2, y2, x3, y3, *args)
  196.   end
  197.   #--------------------------------------------------------------------------
  198.   # ● 绘制实心多边形
  199.   #     x1 : 第一点的 X 坐标
  200.   #     y1 : 第一点的 Y 坐标
  201.   #     x2 : 第二点的 X 坐标
  202.   #     y2 : 第二点的 Y 坐标
  203.   #     x3 : 第三点的 X 坐标
  204.   #     y3 : 第三点的 Y 坐标
  205.   #     args : 后续的参数(可以加多个点,至少要包括一个颜色,至多可有三个颜色)
  206.   #--------------------------------------------------------------------------
  207.   def fill_polygon(x1, y1, x2, y2, x3, y3, *args)
  208.     raise ArgumentError unless args[-1].is_a?(Color)
  209.     fill2 = args[-3].is_a?(Color) ? args.pop : args[-1]
  210.     fill1 = args[-2].is_a?(Color) ? args.pop : args[-1]
  211.     draw_polygon(x1, y1, x2, y2, x3, y3, *args)
  212.     color = args[-1]
  213.     nodes = [x1, y1, x2, y2, x3, y3].concat(args[0...-1])
  214.     edges = {}
  215.     min_y = height
  216.     max_y = 0
  217.     0.step(nodes.size - 2, 2) do |i|
  218.       xi, yi = nodes[i, 2]
  219.       min_y = [min_y, yi].min
  220.       max_y = [max_y, yi].max
  221.       xj, yj = nodes[(i + 2) % nodes.size, 2]
  222.       dx = 1.0 * (xi - xj) / (yi - yj)
  223.       if yi < yj
  224.         yh = nodes[i - 1]
  225.         if yh < yi
  226.           yi += 1
  227.           xi += dx
  228.         end
  229.         edges[yi] ||= []
  230.         edges[yi].push([xi, dx, yj])
  231.       elsif yi > yj
  232.         yk = nodes[(i + 5) % nodes.size]
  233.         if yj > yk
  234.           yj += 1
  235.           xj += dx
  236.         end
  237.         edges[yj] ||= []
  238.         edges[yj].push([xj, dx, yi])
  239.       end
  240.     end
  241.     return unless max_y - min_y > 1
  242.     aet = edges[min_y]
  243.     aet.each {|edge| edge[0] += edge[1]}
  244.     aet.sort!
  245.     if fill1 != fill2
  246.       dh = max_y - min_y - 1
  247.       dr = (fill2.red - fill1.red) / dh
  248.       dg = (fill2.green - fill1.green) / dh
  249.       db = (fill2.blue - fill1.blue) / dh
  250.       da = (fill2.alpha - fill1.alpha) / dh
  251.       gradient = true
  252.     end
  253.     f = fill1.clone
  254.     for y in min_y + 1..max_y - 1
  255.       if edges[y]
  256.         aet.concat(edges[y])
  257.         aet.sort!
  258.       end
  259.       0.step(aet.size - 1, 2) do |i|
  260.         xi = aet[i][0].round
  261.         xj = aet[i + 1][0].round
  262.         xi += 1 until xi == width - 1 or get_pixel(xi, y) != color
  263.         xj -= 1 until xj == 0 or get_pixel(xj, y) != color
  264.         if xj >= xi
  265.           fill_rect(xi, y, xj - xi + 1, 1, f)
  266.         end
  267.         aet.reject! {|edge| edge[2] == y}
  268.         aet.each {|edge| edge[0] += edge[1]}
  269.         aet.sort!
  270.         f.set(f.red + dr, f.green + dg, f.blue + db, f.alpha + da) if gradient
  271.       end
  272.     end
  273.   end
  274.   #--------------------------------------------------------------------------
  275.   # ● 传送位图多边形
  276.   #     x1 : 第一点的 X 坐标
  277.   #     y1 : 第一点的 Y 坐标
  278.   #     x2 : 第二点的 X 坐标
  279.   #     y2 : 第二点的 Y 坐标
  280.   #     x3 : 第三点的 X 坐标
  281.   #     y3 : 第三点的 Y 坐标
  282.   #     args : 后续的参数(加多个点,最后是位图, 偏移x,偏移y,(可选)不透明度)
  283.   #            偏移x和偏移y的位置参考第一个点
  284.   #--------------------------------------------------------------------------
  285.   def blt_polygon(x1, y1, x2, y2, x3, y3, *args)
  286.     if args[-3].is_a?(Bitmap)
  287.       src, x0, y0 = args[-3, 3]
  288.       opacity = 255
  289.       nodes = [x1, y1, x2, y2, x3, y3].concat(args[0...-3])
  290.     elsif args[-4].is_a?(Bitmap)
  291.       src, x0, y0, opacity = args[-4, 4]
  292.       nodes = [x1, y1, x2, y2, x3, y3].concat(args[0...-4])
  293.     else
  294.       raise ArgumentError
  295.     end
  296.     raise ArgumentError unless nodes.size[0] == 0
  297.     edges = {}
  298.     min_y = height
  299.     max_y = 0
  300.     ox = x0 - x1
  301.     oy = y0 - y1
  302.     0.step(nodes.size - 2, 2) do |i|
  303.       xi, yi = nodes[i, 2]
  304.       min_y = [min_y, yi].min
  305.       max_y = [max_y, yi].max
  306.       xj, yj = nodes[(i + 2) % nodes.size, 2]
  307.       dx = 1.0 * (xi - xj) / (yi - yj)
  308.       if yi < yj
  309.         yh = nodes[i - 1]
  310.         if yh < yi
  311.           yi += 1
  312.           xi += dx
  313.         end
  314.         edges[yi] ||= []
  315.         edges[yi].push([xi, dx, yj])
  316.       elsif yi > yj
  317.         yk = nodes[(i + 5) % nodes.size]
  318.         if yj > yk
  319.           yj += 1
  320.           xj += dx
  321.         end
  322.         edges[yj] ||= []
  323.         edges[yj].push([xj, dx, yi])
  324.       end
  325.     end
  326.     aet = []
  327.     for y in min_y..max_y
  328.       if edges[y]
  329.         aet.concat(edges[y])
  330.         aet.sort!
  331.       end
  332.       0.step(aet.size - 1, 2) do |i|
  333.         xi = aet[i][0].round
  334.         xj = aet[i + 1][0].round
  335.         blt(xi, y, src, Rect.new(xi + ox, y + oy, xj - xi + 1, 1), opacity)
  336.         aet.reject! {|edge| edge[2] == y}
  337.         aet.each {|edge| edge[0] += edge[1]}
  338.         aet.sort!
  339.       end
  340.     end
  341.   end
  342.   #--------------------------------------------------------------------------
  343.   # ● 绘制正多边形
  344.   #     x : 正多边形外接圆心 X 坐标
  345.   #     y : 正多边形外接圆心 Y 坐标
  346.   #     rad : 正多边形外接圆 半径
  347.   #     edges : 正多边形的边数
  348.   #     angle : 正多边形旋转角度
  349.   #     color : 正多边形的颜色 (Color)
  350.   #--------------------------------------------------------------------------
  351.   def draw_isogon(x, y, rad, edges, angle, color)
  352.     raise ArgumentError if edges < 3
  353.     step = Math::PI * 2 / edges
  354.     args = []
  355.     angle_i = Math::PI * (270 + angle) / 180
  356.     edges.times do
  357.       args.push((x + rad * Math.cos(angle_i)).round,
  358.         (y + rad * Math.sin(angle_i)).round)
  359.       angle_i += step
  360.     end
  361.     args.push(color)
  362.     draw_polygon(*args)
  363.   end
  364.   #--------------------------------------------------------------------------
  365.   # ● 绘制实心正多边形
  366.   #     x : 正多边形外接圆心 X 坐标
  367.   #     y : 正多边形外接圆心 Y 坐标
  368.   #     rad : 正多边形外接圆 半径
  369.   #     edges : 正多边形的边数
  370.   #     angle : 正多边形旋转角度
  371.   #     border_color : 正多边形的边框颜色 (Color)
  372.   #     fill_color1 : 填充渐变的上颜色 (Color)
  373.   #     fill_color2 : 填充渐变的下颜色 (Color)
  374.   #--------------------------------------------------------------------------
  375.   def fill_isogon(x, y, rad, edges, angle, border_color, fill_color1 =
  376.     border_color, fill_color2 = fill_color1)
  377.     raise ArgumentError if edges < 3
  378.     step = Math::PI * 2 / edges
  379.     args = []
  380.     angle_i = Math::PI * (270 + angle) / 180
  381.     edges.times do
  382.       args.push((x + rad * Math.cos(angle_i)).round,
  383.         (y + rad * Math.sin(angle_i)).round)
  384.       angle_i += step
  385.     end
  386.     args.push(border_color, fill_color1, fill_color2)
  387.     fill_polygon(*args)
  388.   end
  389.   #--------------------------------------------------------------------------
  390.   # ● 传送位图正多边形
  391.   #     x : 正多边形外接圆心 X 坐标
  392.   #     y : 正多边形外接圆心 Y 坐标
  393.   #     rad : 正多边形外接圆 半径
  394.   #     edges : 正多边形的边数
  395.   #     angle : 正多边形旋转角度
  396.   #     src_bitmap : 传送元位图
  397.   #     x0 : 传送圆心 X 坐标
  398.   #     y0 : 传送圆心 Y 坐标
  399.   #     opacity : 不透明度
  400.   #--------------------------------------------------------------------------
  401.   def blt_isogon(x, y, rad, edges, angle, src_bitmap, x0, y0, opacity = 255)
  402.     raise ArgumentError if edges < 3
  403.     step = Math::PI * 2 / edges
  404.     args = []
  405.     angle_i = Math::PI * (270 + angle) / 180
  406.     edges.times do
  407.       args.push((x + rad * Math.cos(angle_i)).round,
  408.         (y + rad * Math.sin(angle_i)).round)
  409.       angle_i += step
  410.     end
  411.     args.push(src_bitmap, x0 - x + args[0], y0 - y + args[1], opacity)
  412.     blt_polygon(*args)
  413.   end
  414.   #--------------------------------------------------------------------------
  415.   # ● 绘制圆
  416.   #     x : 圆心 X 坐标
  417.   #     y : 圆心 Y 坐标
  418.   #     rad : 半径
  419.   #     color : 圆的颜色 (Color)
  420.   #--------------------------------------------------------------------------
  421.   def draw_circle(x, y, rad, color)
  422.     raise ArgumentError if rad < 0
  423.     return set_pixel(x, y, color) if rad == 0
  424.     draw_ellipse(x, y, rad, rad, color)
  425.   end
  426.   #--------------------------------------------------------------------------
  427.   # ● 绘制椭圆
  428.   #     x : 圆心 X 坐标
  429.   #     y : 圆心 Y 坐标
  430.   #     rad_x : 水平半径
  431.   #     rad_y : 垂直半径
  432.   #     color : 椭圆的颜色 (Color)
  433.   #     fill1 : 填充的颜色1 (Color)
  434.   #     fill2 : 填充的颜色2 (Color)
  435.   #--------------------------------------------------------------------------
  436.   def draw_ellipse(x, y, rad_x, rad_y, color, fill1 = nil, fill2 = nil)
  437.     raise ArgumentError if rad_x < 0 or rad_y < 0
  438.     return set_pixel(x, y, color) if rad_x == 0 and rad_y == 0
  439.     return fill_rect(x - rad_x, y, rad_x << 1, 1, color) if rad_y == 0
  440.     return fill_rect(x, y - rad_y, 1, rad_y << 1, color) if rad_x == 0
  441.     ry2 = rad_y * rad_y
  442.     rx2 = rad_x * rad_x
  443.     d = ry2 + rx2 * (0.25 - rad_y)
  444.     dx = 0
  445.     last_dy = dy = rad_y
  446.     f = fill1 && fill1.clone
  447.     if fill2
  448.       f2 = fill2.clone
  449.       dr = (fill2.red - fill1.red) / ((rad_y << 1) - 1)
  450.       dg = (fill2.green - fill1.green) / ((rad_y << 1) - 1)
  451.       db = (fill2.blue - fill1.blue) / ((rad_y << 1) - 1)
  452.       da = (fill2.alpha - fill1.alpha) / ((rad_y << 1) - 1)
  453.     end
  454.     begin
  455.       set_pixel(x + dx, y + dy, color)
  456.       set_pixel(x + dx, y - dy, color)
  457.       set_pixel(x - dx, y - dy, color)
  458.       set_pixel(x - dx, y + dy, color)
  459.       if f and dx > 1 and dy < rad_y and last_dy != dy
  460.         last_dy = dy
  461.         fill_rect(x - dx + 1, y - dy, (dx << 1) - 1, 1, f)
  462.         fill_rect(x - dx + 1, y + dy, (dx << 1) - 1, 1, f2)
  463.         if fill2
  464.           f.set(f.red + dr, f.green + dg, f.blue + db, f.alpha + da)
  465.           f2.set(f2.red - dr, f2.green - dg, f2.blue - db, f2.alpha - da)
  466.         end
  467.       end
  468.       if d <= -1e-6
  469.         d += ry2 * ((dx << 1) + 3)
  470.       else
  471.         d += ry2 * ((dx << 1) + 3) + rx2 * (1 - dy << 1)
  472.         dy -= 1
  473.       end
  474.       dx += 1
  475.     end while ry2 * (dx + 1) < rx2 * (dy - 0.5)
  476.     d = ry2 * (dx + 0.5) * (dx + 0.5) + rx2 * (dy - 1) * (dy - 1) - rx2 * ry2
  477.     begin
  478.       set_pixel(x + dx, y + dy, color)
  479.       set_pixel(x + dx, y - dy, color)
  480.       set_pixel(x - dx, y - dy, color)
  481.       set_pixel(x - dx, y + dy, color)
  482.       if f and dx > 1 and dy < rad_y and last_dy != dy
  483.         last_dy = dy
  484.         fill_rect(x - dx + 1, y - dy, (dx << 1) - 1, 1, f)
  485.         fill_rect(x - dx + 1, y + dy, (dx << 1) - 1, 1, f2)
  486.         if fill2
  487.           f.set(f.red + dr, f.green + dg, f.blue + db, f.alpha + da)
  488.           f2.set(f2.red - dr, f2.green - dg, f2.blue - db, f2.alpha - da)
  489.         end
  490.       end
  491.       if d <= -1e-6
  492.         d += ry2 * (dx + 1 << 1) + rx2 * (3 - (dy << 1))
  493.         dx += 1
  494.       else
  495.         d += rx2 * (3 - (dy << 1))
  496.       end
  497.       dy -= 1
  498.     end while dy >= 0
  499.   end
  500.   #--------------------------------------------------------------------------
  501.   # ● 绘制实心圆
  502.   #     x : 圆心 X 坐标
  503.   #     y : 圆心 Y 坐标
  504.   #     rad : 半径
  505.   #     border_color : 圆的边框颜色 (Color)
  506.   #     fill_color1 : 填充渐变的上颜色 (Color)
  507.   #     fill_color2 : 填充渐变的下颜色 (Color)
  508.   #--------------------------------------------------------------------------
  509.   def fill_circle(x, y, rad, border_color, fill_color1 = border_color,
  510.     fill_color2 = fill_color1)
  511.     draw_ellipse(x, y, rad, rad, border_color, fill_color1, fill_color2)
  512.   end
  513.   #--------------------------------------------------------------------------
  514.   # ● 绘制实心椭圆
  515.   #     x : 圆心 X 坐标
  516.   #     y : 圆心 Y 坐标
  517.   #     rad_x : 水平半径
  518.   #     rad_y : 垂直半径
  519.   #     border_color : 椭圆的边框颜色 (Color)
  520.   #     fill_color1 : 填充渐变的上颜色 (Color)
  521.   #     fill_color2 : 填充渐变的下颜色 (Color)
  522.   #--------------------------------------------------------------------------
  523.   def fill_ellipse(x, y, rad_x, rad_y, border_color, fill_color1 =
  524.     border_color, fill_color2 = fill_color1)
  525.     draw_ellipse(x, y, rad_x, rad_y, border_color, fill_color1, fill_color2)
  526.   end
  527.   #--------------------------------------------------------------------------
  528.   # ● 传送位图圆
  529.   #     x : 圆心 X 坐标
  530.   #     y : 圆心 Y 坐标
  531.   #     rad : 半径
  532.   #     src_bitmap : 传送元位图
  533.   #     x0 : 传送圆心 X 坐标
  534.   #     y0 : 传送圆心 Y 坐标
  535.   #     opacity : 不透明度
  536.   #--------------------------------------------------------------------------
  537.   def blt_circle(x, y, rad, src_bitmap, x0, y0, opacity = 255)
  538.     blt_ellipse(x, y, rad, rad, src_bitmap, x0, y0, opacity)
  539.   end
  540.   #--------------------------------------------------------------------------
  541.   # ● 传送位图椭圆
  542.   #     x : 圆心 X 坐标
  543.   #     y : 圆心 Y 坐标
  544.   #     rad_x : 水平半径
  545.   #     rad_y : 垂直半径
  546.   #     src_bitmap : 传送元位图
  547.   #     x0 : 传送圆心 X 坐标
  548.   #     y0 : 传送圆心 Y 坐标
  549.   #     opacity : 不透明度
  550.   #--------------------------------------------------------------------------
  551.   def blt_ellipse(x, y, rad_x, rad_y, src_bitmap, x0, y0, opacity = 255)
  552.     raise ArgumentError if rad_x < 0 or rad_y < 0
  553.     return blt(x - rad_x, y, src_bitmap, Rect.new(x0 - rad_x, y0,
  554.       rad_x << 1, 1), opacity) if rad_y == 0
  555.     return blt(x, y - rad_y, src_bitmap, Rect.new(x0, y0 - rad_y,
  556.       1, rad_y << 1), opacity) if rad_x == 0
  557.     ry2 = rad_y * rad_y
  558.     rx2 = rad_x * rad_x
  559.     d = ry2 + rx2 * (0.25 - rad_y)
  560.     dx = 0
  561.     dy = rad_y
  562.     last_dy = nil
  563.     begin
  564.       if last_dy != dy
  565.         last_dy = dy
  566.         blt(x - dx + 1, y - dy, src_bitmap, Rect.new(x0 - dx + 1, y0 - dy,
  567.           (dx << 1) - 1, 1), opacity)
  568.         blt(x - dx + 1, y + dy, src_bitmap, Rect.new(x0 - dx + 1, y0 + dy,
  569.           (dx << 1) - 1, 1), opacity)
  570.       end
  571.       if d <= -1e-6
  572.         d += ry2 * ((dx << 1) + 3)
  573.       else
  574.         d += ry2 * ((dx << 1) + 3) + rx2 * (1 - dy << 1)
  575.         dy -= 1
  576.       end
  577.       dx += 1
  578.     end while ry2 * (dx + 1) < rx2 * (dy - 0.5)
  579.     d = ry2 * (dx + 0.5) * (dx + 0.5) + rx2 * (dy - 1) * (dy - 1) - rx2 * ry2
  580.     begin
  581.       if last_dy != dy
  582.         last_dy = dy
  583.         blt(x - dx + 1, y - dy, src_bitmap, Rect.new(x0 - dx + 1, y0 - dy,
  584.           (dx << 1) - 1, 1), opacity)
  585.         blt(x - dx + 1, y + dy, src_bitmap, Rect.new(x0 - dx + 1, y0 + dy,
  586.           (dx << 1) - 1, 1), opacity)
  587.       end
  588.       if d <= -1e-6
  589.         d += ry2 * (dx + 1 << 1) + rx2 * (3 - (dy << 1))
  590.         dx += 1
  591.       else
  592.         d += rx2 * (3 - (dy << 1))
  593.       end
  594.       dy -= 1
  595.     end while dy >= 0
  596.   end
  597. end


测试用的代码:
RUBY 代码复制
  1. r = Sprite.new
  2. b = Bitmap.new(640, 480)
  3. r.bitmap = b
  4. b.gradient_draw_line(14, 41, 99, 78, Color.new(254, 243, 239), Color.new(37, 48, 65))
  5. b.gradient_draw_line(99, 41, 14, 78, Color.new(254, 243, 239), Color.new(37, 48, 65))
  6. b.gradient_draw_line(114, 78, 199, 41, Color.new(254, 243, 239), Color.new(37, 48, 65))
  7. b.gradient_draw_line(199, 78, 114, 41, Color.new(254, 243, 239), Color.new(37, 48, 65))
  8. 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))
  9. b.fill_ellipse(220,320,100,100,Color.new(255,255,255), Color.new(255, 0, 0), Color.new(0, 255, 0))
  10. b.fill_isogon(320,240,85,7,45, Color.new(234,110,86), Color.new(0,78,89), Color.new(153, 57, 217))
  11. b.draw_line(0, 420, 420, 420, Color.new(120,240,120), SailCat::Canvas_Config::CANVAS_LINE_BROKEN)
  12. r.x = 0
  13. r.y = 0
  14. loop do
  15.   Graphics.update
  16.   Input.update
  17.   exit if Input.trigger?(Input::B)
  18. 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-2 13:15
就说如果你们觉得这个插件有用我可以继续开发其他形状,不然就放着吧……  发表于 2022-3-1 07:28

评分

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

查看全部评分

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

Lv3.寻梦者

梦石
0
星屑
4481
在线时间
1053 小时
注册时间
2013-3-28
帖子
390

开拓者

9
发表于 2022-3-26 17:28:53 | 只看该作者
有没有办法绘制边缘渐变到透明的图形
回复 支持 反对

使用道具 举报

Lv5.捕梦者 (版主)

遠航の猫咪

梦石
3
星屑
23206
在线时间
2387 小时
注册时间
2005-10-15
帖子
1166

开拓者

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



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

点评

画笔粗度用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] 到本站一游。
回复 支持 反对

使用道具 举报

Lv5.捕梦者

梦石
0
星屑
39028
在线时间
5722 小时
注册时间
2006-11-10
帖子
6621
7
发表于 2022-3-2 10:02:27 | 只看该作者
本帖最后由 灯笼菜刀王 于 2022-3-2 10:05 编辑

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

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

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

点评

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

使用道具 举报

Lv4.逐梦者

梦石
0
星屑
6483
在线时间
119 小时
注册时间
2020-1-8
帖子
234
6
发表于 2022-3-1 16:59:30 | 只看该作者
本帖最后由 RPGzh500223 于 2022-3-2 13:05 编辑

楼主考虑一下支持水平渐变,效率为垂直的一半(用Bitmap#stretch_blt,尽管看起来有点怪……)
用Bitmap#blt模拟水平渐变,效率大概垂直的58%,看起来正常多了
RUBY 代码复制
  1. class Bitmap
  2.   # cx    椭圆中心点X           基本整型
  3.   # cy    椭圆中心点Y           基本整型
  4.   # ax    椭圆X半轴长度         无符号整型
  5.   # by    椭圆Y半轴长度         无符号整型
  6.   # mode  椭圆颜色填充方向(0:左->右;1:上->下)   无符号整型
  7.   # color 椭圆边框颜色          RMXP的Color实例对象
  8.   # fillB 椭圆填充的起始颜色    RMXP的Color实例对象
  9.   # fillE 椭圆填充的终止颜色    RMXP的Color实例对象 或 nil(单色填充)
  10.   def fill_ellipse2(cx, cy, ax, by, mode, color, fillB, fillE=nil)
  11.  
  12.     if fillE
  13.       if mode == 0 then step = ax + ax - 1
  14.       elsif mode == 1 then step, fE = by + by - 1, fillE.clone
  15.       end
  16.       gR = (fillE.red   - fillB.red  ) / step
  17.       gG = (fillE.green - fillB.green) / step
  18.       gB = (fillE.blue  - fillB.blue ) / step
  19.       gA = (fillE.alpha - fillB.alpha) / step
  20.     end  
  21.     if mode == 0
  22.       if fillE
  23.         src, src_rect = Bitmap.new(ax + ax, 1), Rect.new(0, 0, 0, 0)
  24.         fB = fillB.clone
  25.         src.width.times do |x|
  26.           src.set_pixel(x, 0, fB)
  27.           fB.set(fB.red + gR, fB.green + gG, fB.blue + gB, fB.alpha + gA)
  28.         end
  29.       else
  30.         mode = 1
  31.       end
  32.     end   
  33.  
  34.     ratio, fB, w2 = ax.to_f / by, fillB.clone, 0
  35.     (by - 1).downto(0) do |y|
  36.       w = (Math.sqrt((by + y) * (by - y)) * ratio).to_i
  37.  
  38.       if mode == 1
  39.         self.fill_rect(cx - w, cy - y, w + w, 1, fB)
  40.  
  41.         if fillE
  42.           self.fill_rect(cx - w, cy + y, w + w, 1, fE)
  43.           fB.set(fB.red + gR, fB.green + gG, fB.blue + gB, fB.alpha + gA)
  44.           fE.set(fE.red - gR, fE.green - gG, fE.blue - gB, fE.alpha - gA)
  45.         else
  46.           self.fill_rect(cx - w, cy + y, w + w, 1, fB)
  47.         end
  48.       elsif mode == 0
  49.         src_rect.set(ax - w, 0, w + w, 1)
  50.         self.blt(cx - w, cy - y, src, src_rect)
  51.         self.blt(cx + w, cy + y, src, src_rect)
  52.       end
  53.  
  54.       self.set_pixel(cx - w, cy - y, color)
  55.       self.set_pixel(cx + w, cy - y, color)
  56.       self.set_pixel(cx - w, cy + y, color)
  57.       self.set_pixel(cx + w, cy + y, color)
  58.  
  59.       while w2 < w
  60.         self.set_pixel(cx - w2, cy - y - 1, color)
  61.         self.set_pixel(cx + w2, cy - y - 1, color)
  62.         self.set_pixel(cx - w2, cy + y + 1, color)
  63.         self.set_pixel(cx + w2, cy + y + 1, color)
  64.         w2 += 1
  65.       end
  66.     end
  67.   end
  68. end


这RGB颜色渐变……

draw_radar.png (45.83 KB, 下载次数: 33)

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

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

h.png (8.57 KB, 下载次数: 33)

h.png

v.png (9.57 KB, 下载次数: 35)

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
回复 支持 反对

使用道具 举报

Lv5.捕梦者 (版主)

梦石
1
星屑
23999
在线时间
3339 小时
注册时间
2011-7-8
帖子
3926

开拓者

5
发表于 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 网络脚本 *
回复 支持 反对

使用道具 举报

Lv5.捕梦者

梦石
0
星屑
39028
在线时间
5722 小时
注册时间
2006-11-10
帖子
6621
4
发表于 2022-3-1 10:26:15 | 只看该作者


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

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

猫大是否考虑把它做成蒙版

未标题-2.jpg (31.4 KB, 下载次数: 36)

未标题-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
回复 支持 反对

使用道具 举报

Lv4.逐梦者

梦石
0
星屑
7921
在线时间
1049 小时
注册时间
2012-4-3
帖子
1271

开拓者

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

点评

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

使用道具 举报

Lv6.析梦学徒

老鹰

梦石
40
星屑
34750
在线时间
6741 小时
注册时间
2012-5-26
帖子
3259

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

2
发表于 2022-3-1 07:49:47 | 只看该作者
这也是可以顺手随便写出来的吗
回复 支持 反对

使用道具 举报

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

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-11-29 07:00

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表