查看: 2444|回复: 3

[原创发布] ImageEx

[复制链接]
梦石
0
星屑
6483
在线时间
119 小时
注册时间
2020-1-8
回帖
205
发表于 2022-11-29 17:28:16 | 显示全部楼层 |阅读模式

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

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

×
本帖最后由 RPGzh500223 于 2022-11-30 14:57 编辑

感觉很鸡肋,电脑里扔了2个多月,毕竟都写了,发贴混点经验。
ImageEx_rmxp.dll  VS2015  C++语言编写(因为有库是C++的,其实都是按C写的)。


[pre lang="ruby"]module ImageEx
  dll = "ImageEx_rmxp.dll"
  
  # ———— png 与 jpeg ——————————————————
  # 仅支持 jpeg(Gray/RGB)、png(Gray/GrayAlpha/RGB/RGBA)
  # 使用libjpeg/libpng
  # ————————————————————————————
  
  # 支持的模式会转换至BGRA,不支持的模式会返回-1。
  # filename_uft8, bitmap_obj_id
  Load_JPG = Win32API.new(dll, 'bitmap_load_jpg', 'PL', 'L')
  # filename_utf8, bitmap_obj_id
  # channels  1或3    BGRA会转换至支持的对应模式
  # quality   0..100  质量越好,文件越大
  Save_JPG = Win32API.new(dll, 'bitmap_save_to_jpg', 'PLLL', 'L')
  
  # 支持的模式会转换至BGRA,不支持的模式无效操作。
  # filename_uft8, bitmap_obj_id
  Load_PNG = Win32API.new(dll, 'bitmap_load_png', 'PL', 'L')
  # filename_utf8, bitmap_obj_id
  # channels  1..4  BGRA会转换至支持的对应模式
  Save_PNG = Win32API.new(dll, 'bitmap_save_to_png', 'PLL', 'L')
  
  
  # ———— gif 与 webp ——————————————————————————
  # 仅支持读取,无论是否使用线程(Win32 CreateThread)都需要释放(调用dispose)
  # gif中disposal_method不支持DISPOSE_PREVIOUS
  # 播放效果视文件而定,可能会“画面闪烁”
  # 效率:非线程模式,640*480大小,读取一帧 gif:1000次/秒 webp:300次/秒
  #            参照,640*480大小的Bitmap#blt    260+次/秒         
  # 使用libgif/libwebp
  # ————————————————————————————————————
  
  # filename_utf8   : utf8编码的文件名。
  # sprite_obj_id   : RMXP 精灵(Sprite) 对象的 object_id,精灵需设置好其位图。
  # gif_cell_string : GIF_CELL_SIZE字节的字符串,用来保存一些数据。
  # play_mode : 0..3位 是否抠图。抠图效果不错,对效率影响不大。      
  #                    1:抠除黑色;2:抠除白色;其他(0/3):原图。
  #             4..7位 是否线程模式。  0:不使用;非0:使用。
  # loop_count : 自定义播放次数。 0:文件默认次数;负数:一直播放;正数:指定次数。
  Show_GIF = Win32API.new(dll, 'show_gif', 'PLPLL', 'L')
  Draw_GIF = Win32API.new(dll, 'draw_gif', 'L', 'L')
  Free_GIF = Win32API.new(dll, 'free_gif', 'L', 'L')  
  GIF = 0x666967
  GIF_CELL_SIZE = Show_GIF.call(nil, 0, 0, 0, 0)
  
  # 参数四 mode : 是否线程模式。  0:不使用;非0:使用。
  # 其余参数,参见Show_GIF
  Show_WEBP = Win32API.new(dll, 'show_webp', 'PLPLL', 'L')
  Draw_WEBP = Win32API.new(dll, 'draw_webp', 'L', 'L')
  Free_WEBP = Win32API.new(dll, 'free_webp', 'L', 'L')  
  WEBP = 0x70626577
  WEBP_CELL_SIZE = Show_WEBP.call(nil, 0, 0, 0, 0)

  # 这两个方法其实没什么用……
  def self.get_png_wh(filename)
    w = h = 0
    File.open(filename, 'rb') do |f|
      if f.read(8) == "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"
        f.seek(8, IO::SEEK_CUR)
        w, h = f.read(8).unpack('NN')
      end
    end
    return w, h
  end
   
  def self.get_jpg_wh(filename)
    w = h = 0
    File.open(filename, 'rb') do |f|
      if f.read(2) == "\xFF\xD8"
        sof0 = "\xFF\xC0"
        while (buf = f.read(2)) != sof0
          size = f.read(2).unpack('n')
          f.seek(size - 2, IO::SEEK_CUR)
        end
        f.seek(3, IO::SEEK_CUR)
        h, w = f.read(4).unpack('nn')
      end
    end
    return w, h
  end
end


class Bitmap
  def to_int
    self.__id__
  end
  
  def save_jpg(filename, channels = 3, quality = 90)
    ImageEx::Save_JPG.call(filename, self, channels, quality)
  end
  
  def save_png(filename, channels = 4)
    ImageEx::Save_PNG.call(filename, self, channels)
  end
  
  # 读取图片至位图,主要是同样大小的单帧静态图片的动画,避免重复新建Bitmap  
  def load_jpg(filename)
    ImageEx::Load_JPG.call(filename, self)
  end
  
  def load_png(filename)
    ImageEx::Load_PNG.call(filename, self)
  end   
end

# GifSprite/WebpSprite并没打算复用,用完就释放掉,再新建
class GifSprite < Sprite

  def initialize(filename, mode = 0, loop_count = 0, viewport = nil)
   
    w = h = 0
    File.open(filename, "rb") do |f|
      if (f.read(6) =~ /GIF8[79]a/)
        w, h = f.read(4).unpack('SS')
      end
    end
   
    if w == 0 || h == 0
      raise "#{self.class} ERR: #{filename} 可能不是 gif 图片"
    end
   
    super(viewport)
   
    self.bitmap = Bitmap.new(w, h)
    self.instance_eval do
      def bitmap=(obj)
      end
    end
   
    self.bitmap.instance_eval do
      alias :dispose_ori :dispose
      def dispose(tag = 0)
        dispose_ori if tag == ImageEx::GIF
      end
    end
   
    @cell = "\0" * ImageEx::GIF_CELL_SIZE
    err = ImageEx::Show_GIF.call(filename, self.__id__, @cell.__id__,
      mode, loop_count)
   
    case err
    when -1   then raise "#{self.class} ERR: 打开 #{filename} 文件失败"
    when -2   then raise "#{self.class} ERR: 位图 与 #{filename} 不符"
    when -3   then raise "#{self.class} ERR: @cell 参数不符合要求"
    when -100 then raise "#{self.class} ERR: 解析 #{filename} 失败"
    when -101 then raise "#{self.class} ERR: 内存不足"
    end
   
    @cell_ptr = [@cell].pack('p').unpack('L')[0]
    @cell.freeze
   
    @use_thread = (mode & 0xF0 != 0) && (err != -99)
    @delay = 0
    @loop_over = false
  end

  def over?
    return @loop_over if @use_thread == false
    i, c = @cell.unpack('@16LL')
    return c != 0 && i >= c
  end
   
  def update_frame
    return if (@loop_over | @use_thread) == true
    return if (self.disposed? | self.bitmap.disposed?) == true
    return if self.visible == false || self.opacity == 0
   
    if @delay > 0
      @delay -= 1
    else
      ms = ImageEx::Draw_GIF.call(@cell_ptr)
      if ms < 0
        @loop_over = true
      else
        @delay = (ms * Graphics.frame_rate + 999) / 1000
      end
    end
  end

  def dispose
    return if self.disposed? == true
    ImageEx::Free_GIF.call(@cell_ptr)
    self.bitmap.dispose(ImageEx::GIF)
    super
  end
end


class WebpSprite < Sprite
  
  def initialize(filename, mode = 0, loop_count = 0, viewport = nil)
   
    w = h = 0
    File.open(filename, "rb") do |f|
      if (f.read(4) == "RIFF")
        f.seek(4, IO::SEEK_CUR)
        
        if (f.read(4) == "WEBP")        
          chunk = f.read(4)
          f.seek(4, IO::SEEK_CUR)

          if (chunk == "VP8 ")
            if (f.read(1).unpack('C')[0][0] == 0)
              f.seek(2, IO::SEEK_CUR)
              if (f.read(3) == "\x9D\x01\x2A")
                w, h = f.read(4).unpack('SS')
              end
            end
          elsif (chunk == "VP8L")
            f.seek(1, IO::SEEK_CUR)
            n = f.read(4).unpack('L')[0]
         
            w = (n & 0x3FFF) + 1
            h = ((n & 0xFFFC000) >> 14) + 1
          elsif (chunk == "VP8X")
            f.seek(4, IO::SEEK_CUR)
        
            w, w16, h, h16 = f.read(6).unpack('SCSC')
            w += (w16 << 16) + 1
            h += (h16 << 16) + 1         
          end
        end
      end
    end
   
    if w == 0 || h == 0
      raise "#{self.class} ERR: #{filename} 可能不是 webp 图片"
    end
   
    super(viewport)
   
    self.bitmap = Bitmap.new(w, h)
    self.instance_eval do
      def bitmap=(obj)
      end
    end
   
    self.bitmap.instance_eval do
      alias :dispose_ori :dispose
      def dispose(tag = 0)
        dispose_ori if tag == ImageEx::WEBP
      end
    end
   
    @cell = "\0" * ImageEx::WEBP_CELL_SIZE
    err = ImageEx::Show_WEBP.call(filename, self.__id__, @cell.__id__,
      mode, loop_count)
   
    case err
    when -1   then raise "#{self.class} ERR: 打开 #{filename} 文件失败"
    when -2   then raise "#{self.class} ERR: 位图 与 #{filename} 不符"
    when -3   then raise "#{self.class} ERR: @cell 参数不符合要求"
    when -100 then raise "#{self.class} ERR: 解析 #{filename} 失败"
    when -101 then raise "#{self.class} ERR: 内存不足"
    end
   
    @cell_ptr = [@cell].pack('p').unpack('L')[0]
    @cell.freeze
   
    @use_thread = (mode != 0) && (err != -99)
    @delay = 0
    @loop_over = false
  end
   
  def update_frame
    return if (@loop_over | @use_thread) == true
    return if (self.disposed? | self.bitmap.disposed?) == true
    return if self.visible == false || self.opacity == 0
   
    if @delay > 0
      @delay -= 1
    else
      ms = ImageEx::Draw_WEBP.call(@cell_ptr)
      if ms < 0
        @loop_over = true
      else
        @delay = (ms * Graphics.frame_rate + 999) / 1000
      end
    end
  end
  
  def dispose
    return if self.disposed? == true
    ImageEx::Free_WEBP.call(@cell_ptr)
    self.bitmap.dispose(ImageEx::WEBP)
    super
  end
end[/pre]

PS:示意图中的gif图片来自网络。
       修复DLL中有关gif的一个bug。该脚本中关于线程模式瞎写的,不好用,不推荐。
       自己写脚本喜欢越简单越好,使用脚本喜欢越全面越好,从使用角度说,这脚本很糟糕,所以就不写使用示例了。
       至于抠除黑色的计算,以前发的帖子写过 Bitmap#remove_black!
temp.png

ImageEx_rmxp.zip

282.67 KB, 下载次数: 10

ImageEx_rmxp.dll

点评

技能公共事件加你这个效果还不美翻了  发表于 2022-11-29 18:39

评分

参与人数 1星屑 +600 收起 理由
fux2 + 600 精品文章

查看全部评分

老黄鸡

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

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

发表于 2022-11-29 18:55:26 | 显示全部楼层
好东西,但我觉得塞一点使用说明更好
列出功能一览方便一些作者直接使用而不用去仔细看一遍内容
RGDirect - DirectX驱动的RGSS,点我了解.
(排满,暂停)RM全系列成套系统定制请联系QQ1213237796
不接受对其他插件维护的委托
回复

使用道具 举报

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

开拓者

发表于 2022-11-29 19:04:19 | 显示全部楼层
挺好的,gif和webp这种有序列帧的格式,就应该直接动画播放出来嘛
熟悉rgss和ruby,xp区版主~
正在填坑:《膜拜组传奇》讲述膜拜组和学霸们的故事。
已上steam:与TXBD合作的Reformers《变革者》
* 战斗调用公共事件 *
* RGSOS 网络脚本 *
回复

使用道具 举报

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

本版积分规则

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

在本版发帖返回顶部