• 记一次用rmagick绘制图像


    最近做了个需求,大概要求是将几个元素(分享人头像,小程序码,商户名称,商户头像,背景图片),根据页面配置的形式绘制出分享图片,大概效果如下:

    为此引用rmagick的gem包,其中所需gem包有

    gem 'rmagick'
    gem 'mini_magick'

    写了个图片拼接工具类,其中代码中用到的 ContentAgent::Content 对象是我们公司自己封装的一个内容中心服务器,其主要用于图片的上传,下载

    # encoding: utf-8
    # magic image 合并图片工具
    class MagicImageTools
      class << self
        #
        # == 根据configs合并为一个图片
        #
        # @param [Array<Hash>] configs 配置
        #
        def composite_by_configs(configs)
          configs = configs.deep_symbolize_keys
          container = configs.shift
          container_magic_img = container[:type] == 'blank' ?
            blank_container_magic_image(container[:options]) : prepare_image_for_composite(container)
    
          # 子项
          configs.each do |config|
            next if config[:value].blank?
            log_info "现在的配置是 #{config}"
            case config[:type]
              when 'image'
                magic_img = prepare_image_for_composite(config)
                composite_images(container_magic_img, magic_img, config[:options])
              when 'text'
                add_text(container_magic_img, config[:value], config[:options])
              else
                log_info "composite_by_configs 子项的type#{config[:type]}不认识"
            end
          end
          cid = to_normal_image(container_magic_img)
          log_info "composite_by_configs cid: #{cid}"
    
          cid
        end
    
        def prepare_image_for_composite(config)
          options = config[:options] || {}
          case options[:image_type]
            when 'magic_image'
              magic_img = config[:value]
            when 'path'
              img = ContentAgent::Content.create_by_file(config[:value]).urid
            else
              img = config[:value]
          end
          magic_img = to_magic_image(img) unless magic_img.present?
          if !options[:skip_resize]
            options[:force_resize] ?
                force_resize_magic_image(magic_img, options[:width], options[:height]) : resize_magic_image(magic_img, options)
          end
    
          magic_img
        end
    
        #
        # == 合并图片, 在容器图container_img中添加img
        #
        # @param container_img 容器图
        # @param img 图片
        #
        def composite_images(container_img, img, options = {})
          options.symbolize_keys!
          x         = options[:x] || 0
          y         = options[:y] || 0
          diameter  = options[:width] || 103
          radius    = diameter / 2
    
          # 此处可以传是否需要对图片做 方 转 圆型
          if options[:radius]
            pr = Magick::Draw.new
            pr.define_clip_path('circle') {
              pr.circle radius, radius, radius, 0
            }
            pr.push
            pr.clip_path('circle')
            pr.composite(0, 0, diameter, diameter, img)
            pr.pop
            # 这一步是画个跟目标图片同样大小对透明图片,这样就可以将裁剪后多余部分隐掉
            canvas = Magick::Image.new(diameter, diameter) { |c| c.background_color = "Transparent" }
            pr.draw(canvas)
            # 以容器图片的左上角作为x, y轴的起点
            container_img.composite!(canvas, x, y, Magick::OverCompositeOp)
          else
            container_img.composite!(img, x, y, Magick::OverCompositeOp)
          end
        end
    
        # 在container_img中添加文字, 默认以容器图片的左上角作为x, y轴的起点
        def add_text(container_img, text, options = {})
          options.symbolize_keys!
          width = options[:width] || 0
          height = options[:height] || 0
          x = options[:x] || 0
          y = options[:y] || 0
          # Magick::NorthWestGravity 表示图片的西北点(即左上角)作为x, y轴的起点
          # Magick::CenterGravity 表示图片的中心点作为x, y轴的起点
          gravity = options[:gravity] || Magick::NorthWestGravity
          font_size = options[:font_size] || 16
          color = options[:color] || 'black'
          font = text_font
          copyright = Magick::Draw.new
          copyright.annotate(container_img, width, height, x, y, text) do
            # 每次写字之前设置stroke为'transparent', 防止之前设置stroke为其他值
            self.stroke = 'transparent'
            self.gravity = gravity
            self.pointsize = font_size
            self.fill = color
            self.font = font
          end
        end
    
        # 将 图片content_id 转为 Magick::Image类型图片
        def to_magic_image(url)
          path = Tools.download_img(url)
          magic_img = Magick::Image.read(path).first
          File.delete(path) if File.exist?(path)
    
          magic_img
        end
    
        # 将 Magick::Image类型图片 转为 图片content_id
        def to_normal_image(magic_img)
          path = "tmp/magic_img_#{Tools.fill_code}.png"
          magic_img.write(path)
          img = ContentAgent::Content.create_by_file(path)
          File.delete(path) if File.exist?(path)
    
          img.urid
        end
    
        # 等比例转换图片尺寸
        def resize_magic_image(magic_img, options = {})
          options.symbolize_keys!
          max_width = options[:max_width] || 100
          max_height = options[:max_height] || 100
          img_width, img_height = get_image_size(magic_img)
          if img_width*max_height > img_height*max_width
            width, height = [max_width, max_width*img_height/img_width]
          else
            width, height = [max_height*img_width/img_height, max_height]
          end
    
          force_resize_magic_image(magic_img, width, height) # 将图片的尺寸转换成为width*height
        end
    
        # 强制转换图片尺寸为width, height
        def force_resize_magic_image(magic_img, width, height)
          magic_img.resize!(width, height) # 将图片的尺寸转换成为width*height
        end
    
        # 获取图片的width, height
        def get_image_size(magic_img)
          width = magic_img.columns
          height = magic_img.rows
          [width, height]
        end
    
        # 创建空白的背景容器图片
        def blank_container_magic_image(options = {})
          options.symbolize_keys!
          width = options[:width] || 375
          height = options[:height] || 667
          bg_color = options[:bg_color] || 'white'
          bg_magic_color = Magick::HatchFill.new(bg_color)
          Magick::Image.new(width, height, bg_magic_color)
        end
    
        # '暂无图片'
        def no_image_magic_image
          Magick::Image.read('app/assets/images/common/common-no-image.png').first
        end
    
        # 字体
        def text_font
          'app/assets/fonts/simsun.ttc'
        end
      end
    end

    工具使用侧

    # 1.导购头像
      # 2.小程序码
      # 3.背景图
      # 4.商户名称
      def generate_shard_img(task, guider)
    
        bg_img_width              = 750
        bg_img_height             = 1334
        width_size                = 375
        qrcode_size               = 105
        avatar_size               = 50
        font_size                 = 24
        retailer_name_width       = 130
        retailer_name_height      = 200
    
        img_location              = task.img_location.as_smart
        guide_photo_x             = img_location[:guide_photo_x]   || 318
        guide_photo_y             = img_location[:guide_photo_y]   || 1116
        weapp_x                   = img_location[:weapp_x]         || 532
        weapp_y                   = img_location[:weapp_y]         || 1080
        logo_x                    = img_location[:logo_x]          || 188
        logo_y                    = img_location[:logo_y]          || 22
        retailer_name_x           = img_location[:retailer_name_x] || 390
        retailer_name_y           = img_location[:retailer_name_y] || 20
    
        bg_img                    = get_bg_img(task, bg_img_width, bg_img_height)
        weapp_url                 = get_weapp_url(task, guider)
        avatar                    = get_avatar(guider, avatar_size)
        retailer_logo             = get_retailer_log
        retailer_name             = get_retailer_name
        configs = [
          {
            value: bg_img,
            type: "image",
            flag: "背景图片",
            options: {
              image_type: "magic_image",
               width_size,
              height: 620,
              skip_resize: true
            }
          },
          {
            value: weapp_url,
            type: "image",
            flag: "小程序码",
            options: {
              image_type: "cid",
              force_resize: true,
              radius: true,
               qrcode_size,
              height: qrcode_size,
              x: weapp_x,
              y: weapp_y
            }
          },
          {
            value: retailer_logo,
            flag: "商户logo",
            type: "image",
            options: {
              image_type: "cid",
              force_resize: true,
               130,
              height: 130,
              x: logo_x,
              y: logo_y,
              gravity: Magick::CenterGravity
            }
          },
          # 商品名称只显示前10个字
          {
            value: (retailer_name.length > 10 ? (retailer_name[0..-4] + '...') : retailer_name),
            flag: "商户名称",
            type: "text",
            options: {
              font_size: font_size,
              skip_resize: true,
               retailer_name_width,
              height: retailer_name_height,
              x: retailer_name_x,
              y: retailer_name_y,
              gravity: Magick::CenterGravity
            }
          },
          {
            value: avatar,
            flag: "导购头像",
            type: "image",
            options: {
              image_type: "cid",
              force_resize: true,
              radius: true,
               avatar_size,
              height: avatar_size,
              x: guide_photo_x,
              y: guide_photo_y
            }
          }
        ]
        MagicImageTools.composite_by_configs(configs)
      end

  • 相关阅读:
    c++
    zjoi 力
    poj 3415
    [SDOI2014]旅行
    模板测试
    [WC2006]水管局长
    HDU5730
    [NOI2014]魔法森林
    [NOI2012]骑行川藏(未完成)
    [NOI2012]随机数生成器
  • 原文地址:https://www.cnblogs.com/lwh-note/p/13524500.html
Copyright © 2020-2023  润新知