草庐IT

生成裂变海报

crazy_dev 2023-03-28 原文

使用用户图像、昵称和邀请二维码,及企业logo、名称生成用户的分享裂变海报。
适用场景:拉新裂变活动,生成用户自己的分享裂变海报。

相关代码

ImageProcessException

public class ImageProcessException extends RuntimeException {
    public ImageProcessException() {
    }

    public ImageProcessException(String message) {
        super(message);
    }

    public ImageProcessException(String message, Throwable cause) {
        super(message, cause);
    }

    public ImageProcessException(Throwable cause) {
        super(cause);
    }

    public ImageProcessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

ImageUtils

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URL;

public class ImageUtils {

    /**
     * 读取本地图片
     *
     * @param imgPath 图片读取路径,示例:"D:\\test.png"
     */
    public static BufferedImage readFromLocal(String imgPath) {
        checkValue(imgPath, "imgPath == null!");
        try {
            return ImageIO.read(new File(imgPath));
        } catch (IOException e) {
            throw new ImageProcessException("读取本地图片出错", e);
        }
    }

    /**
     * 读取图片数据流
     */
    public static BufferedImage readFromStream(InputStream imgStream) {
        checkValue(imgStream, "imgStream == null!");
        try {
            return ImageIO.read(imgStream);
        } catch (IOException e) {
            throw new ImageProcessException("读取图片数据流出错", e);
        }
    }

    /**
     * 读取网络图片
     */
    public static BufferedImage readFromUrl(String imgUrl) {
        checkValue(imgUrl, "imgUrl == null!");
        try {
            URL url = new URL(imgUrl);
            return ImageIO.read(url);
        } catch (IOException e) {
            throw new ImageProcessException("读取网络图片出错", e);
        }
    }

    /**
     * 保存到本地
     *
     * @param imgPath 图片存储路径,示例:"D:\\test.png"
     */
    public static void writeToLocal(BufferedImage image, String imgPath) {
        checkValue(image, "image == null!");
        checkValue(imgPath, "imgPath == null!");

        int index = imgPath.lastIndexOf(".");
        if (index < 0){
            throw new ImageProcessException("图片没有设置保存格式后缀");
        }

        String suffix = imgPath.substring(index + 1);
        image = imageFormat(image, suffix);
        try {
            ImageIO.write(image, suffix, new File(imgPath));
        } catch (IOException e) {
            throw new ImageProcessException("图片保存到本地出错", e);
        }
    }

    /**
     * 保存为 byte数组
     *
     * @param formatName 图片格式,示例:png、jpg、jpeg 等
     */
    public static byte[] writeToBytes(BufferedImage image, String formatName) {
        checkValue(image, "image == null!");
        checkValue(formatName, "formatName == null!");

        image = imageFormat(image, formatName);
        try {
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            ImageIO.write(image, formatName, os);
            return os.toByteArray();
        } catch (IOException e) {
            throw new ImageProcessException("图片保存为 byte 数组出错", e);
        }
    }

    /**
     * 保存到数据流
     *
     * @param formatName 图片格式,示例:png、jpg、jpeg 等
     */
    public static boolean writeToStream(BufferedImage image, String formatName, OutputStream os) {
        checkValue(image, "image == null!");
        checkValue(formatName, "formatName == null!");
        checkValue(os, "os == null!");

        image = imageFormat(image, formatName);
        try {
            return ImageIO.write(image, formatName, os);
        } catch (IOException e) {
            throw new ImageProcessException("图片保存到数据流出错", e);
        }
    }

    /**
     * 添加文字
     *
     * @param source    源图片
     * @param text      添加的文字
     * @param color     文字颜色
     * @param x         距离左上角的X偏移量
     * @param y         距离左上角的Y偏移量
     */
    public static void addText(final BufferedImage source, String text, Color color, Font font, int x, int y) {
        Graphics2D g = source.createGraphics();
        g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
        g.setStroke(new BasicStroke(3));
        g.setColor(color);
        if (font != null) {
            g.setFont(font);
        }
        if (text != null) {
            g.translate(0, 0);
            g.drawString(text, x, y);
        }
        g.dispose();
    }
 
    /**
     * 添加图片
     *
     * @param source       源图片
     * @param addImage     添加的图片
     * @param alpha        透明度。0.0~1.0: 完全透明~完全不透明
     * @param x            距离左上角的X偏移量
     * @param y            距离左上角的Y偏移量
     */
    public static void addImage(final BufferedImage source, BufferedImage addImage, float alpha, int x, int y) {
        Graphics2D g2d = source.createGraphics();
        int width = addImage.getWidth();
        int height = addImage.getHeight();
        // 在图形和图像中实现混合和透明效果
        g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
        g2d.drawImage(addImage, x, y, width, height, null);
        g2d.dispose();
    }

    /**
     *  图片尺寸调整
     *
     * @param source       源图片
     * @param width        宽
     * @param height       高
     */
    public static BufferedImage resizeImage(BufferedImage source, int width, int height){
        // 获取源图片透明度类型
        int type = source.getColorModel().getTransparency();
        int sourceWidth = source.getWidth();
        int sourceHeight = source.getHeight();
        BufferedImage image = new BufferedImage(width, height, type);
        Graphics2D graphics2d = image.createGraphics();
        // 抗锯齿
        graphics2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        // 笔画归一化控制提示值 - 几何应归一化,以提高线条的均匀性或间距以及整体美感。
        graphics2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
        // 渲染提示值 - 选择渲染算法以优化输出质量。
        graphics2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        Image tmp = source.getScaledInstance(sourceWidth, sourceHeight, Image.SCALE_SMOOTH);
        graphics2d.drawImage(tmp, 0, 0, width, height, 0, 0, sourceWidth, sourceHeight, null);
        graphics2d.dispose();
        return image;
    }

    /**
     * 正方形图片转圆形
     * 
     * @param source       源图片
     * @param diameter     直径
     */
    public static BufferedImage convertCircular(BufferedImage source, int diameter){
        // 透明底的图片
        BufferedImage image = new BufferedImage(diameter, diameter, BufferedImage.TYPE_4BYTE_ABGR);
        Ellipse2D.Double shape = new Ellipse2D.Double(0, 0,diameter, diameter);
        Graphics2D g2 = image.createGraphics();
        g2.setClip(shape);
        // 抗锯齿
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        // 笔画归一化控制提示值 - 几何应归一化,以提高线条的均匀性或间距以及整体美感。
        g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
        // 渲染提示值 - 选择渲染算法以优化输出质量。
        g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        // 抖动提示值 - 渲染几何时不要抖动。
        g2.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
        g2.drawImage(source, 0, 0,diameter, diameter, null);
        // 设置颜色
        g2.setBackground(Color.green);
        g2.dispose();
        return image;
    }

    private static void checkValue(Object value, String errorMsg) {
        if (value == null) {
            throw new IllegalArgumentException(errorMsg);
        }
    }

    private static BufferedImage imageFormat(BufferedImage image, String formatName) {

        if (!formatName.equalsIgnoreCase("jpg") && !formatName.equalsIgnoreCase("jpeg")) {
            return image;
        }

        // 处理 jpg 和 jpeg 格式
        BufferedImage formatImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_BGR);
        Graphics g = formatImage.getGraphics();
        g.drawImage(image, 0, 0, null);
        g.dispose();
        return formatImage;
    }
}

测试

public class ImageUtilsTest {

    public static void main(String[] args) {
        ClassLoader loader = ImageUtilsTest.class.getClassLoader();
        BufferedImage image = readFromStream(loader.getResourceAsStream("海报模板.png"));
        image = resizeImage(image, 828, 1792);

        BufferedImage qrCodeImage = readFromStream(loader.getResourceAsStream("qrCode.png"));
        qrCodeImage = resizeImage(qrCodeImage, 150, 150);

        BufferedImage logoImage = readFromStream(loader.getResourceAsStream("logo.png"));
        logoImage = resizeImage(logoImage, 70, 70);

        BufferedImage userImage = readFromStream(loader.getResourceAsStream("用户图像.jpg"));
        userImage = convertCircular(userImage, userImage.getHeight());
        userImage = resizeImage(userImage, 70, 70);

        Font font = new Font("Microsoft YaHei UI Light", Font.BOLD, 26);
        addText(image,"测试",  new Color(0,0,0), font,131, 127);
        addText(image,"张三股份有限公司",  new Color(0,0,0),font,  560, 127);
        addImage(image, userImage, 1, 40, 80);
        addImage(image, logoImage, 1, 470, 80);
        addImage(image, qrCodeImage, 1,40, 1571);
        writeToLocal(image, getProjectPath() + File.separator + "Image-" + System.currentTimeMillis() +".jpeg");
    }

    private static String getProjectPath() {
        return System.getProperty("user.dir");
    }
}

测试结果

有关生成裂变海报的更多相关文章

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  3. ruby - 如何使用 Ruby aws/s3 Gem 生成安全 URL 以从 s3 下载文件 - 2

    我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A

  4. ruby-on-rails - Ruby on Rails - 为文本区域和图片生成列 - 2

    我是Rails的新手,所以请原谅简单的问题。我正在为一家公司创建一个网站。那家公司想在网站上展示它的客户。我想让客户自己管理这个。我正在为“客户”生成一个表格,我想要的三列是:公司名称、公司描述和Logo。对于名称,我使用的是name:string但不确定如何在脚本/生成脚手架终端命令中最好地创建描述列(因为我打算将其设置为文本区域)和图片。我怀疑描述(我想成为一个文本区域)应该仍然是描述:字符串,然后以实际形式进行调整。不确定如何处理图片字段。那么……说来话长:我在脚手架命令中输入什么来生成描述和图片列? 最佳答案 对于“文本”数

  5. ruby-on-rails - 如何生成传递一些自定义参数的 `link_to` URL? - 2

    我正在使用RubyonRails3.0.9,我想生成一个传递一些自定义参数的link_toURL。也就是说,有一个articles_path(www.my_web_site_name.com/articles)我想生成如下内容:link_to'Samplelinktitle',...#HereIshouldimplementthecode#=>'http://www.my_web_site_name.com/articles?param1=value1¶m2=value2&...我如何编写link_to语句“alàRubyonRailsWay”以实现该目的?如果我想通过传递一些

  6. ruby-on-rails - 如何在 Rails 3 中创建自定义脚手架生成器? - 2

    有这些railscast。http://railscasts.com/episodes/218-making-generators-in-rails-3有了这个,你就会知道如何创建样式表和脚手架生成器。http://railscasts.com/episodes/216-generators-in-rails-3通过这个,您可以了解如何添加一些文件来修改脚手架View。我想把两者结合起来。我想创建一个生成器,它也可以创建脚手架View。有点像RyanBates漂亮的生成器或web_app_themegem(https://github.com/pilu/web-app-theme)。我

  7. 报告回顾丨模型进化狂飙,DetectGPT能否识别最新模型生成结果? - 2

    导读语言模型给我们的生产生活带来了极大便利,但同时不少人也利用他们从事作弊工作。如何规避这些难辨真伪的文字所产生的负面影响也成为一大难题。在3月9日智源Live第33期活动「DetectGPT:判断文本是否为机器生成的工具」中,主讲人Eric为我们讲解了DetectGPT工作背后的思路——一种基于概率曲率检测的用于检测模型生成文本的工具,它可以帮助我们更好地分辨文章的来源和可信度,对保护信息真实、防止欺诈等方面具有重要意义。本次报告主要围绕其功能,实现和效果等展开。(文末点击“阅读原文”,查看活动回放。)Ericmitchell斯坦福大学计算机系四年级博士生,由ChelseaFinn和Chri

  8. python - 帮我找到合适的 ruby​​/python 解析器生成器 - 2

    我使用的第一个解析器生成器是Parse::RecDescent,它的指南/教程很棒,但它最有用的功能是它的调试工具,特别是tracing功能(通过将$RD_TRACE设置为1来激活)。我正在寻找可以帮助您调试其规则的解析器生成器。问题是,它必须用python或ruby​​编写,并且具有详细模式/跟踪模式或非常有用的调试技术。有人知道这样的解析器生成器吗?编辑:当我说调试时,我并不是指调试python或ruby​​。我指的是调试解析器生成器,查看它在每一步都在做什么,查看它正在读取的每个字符,它试图匹配的规则。希望你明白这一点。赏金编辑:要赢得赏金,请展示一个解析器生成器框架,并说明它的

  9. ruby - 如何为 pbcopy 生成富文本链接 - 2

    我一直在玩一个脚本,它在Chrome中获取选定的文本并在Google中查找它,提供四个最佳选择,然后粘贴相关链接。它以不同的格式粘贴,具体取决于当前在Chrome中打开的页面-DokuWiki打开的DokuWiki格式,普通网站的HTML,我想要我的WordPress所见即所得编辑器的富文本。我尝试使用pbpaste-Preferrtf来查看没有其他样式的富文本链接在粘贴板上的样子,但它仍然输出纯文本。在文本编辑中保存文件并进行试验后,我想出了以下内容text=%q|{\rtf1{\field{\*\fldinst{HYPERLINK"URL"}}{\fldrsltTEXT}}}|te

  10. Ruby 等同于 Sphinx 文档生成器? - 2

    Ruby有一些不错的文档生成器,例如Yard、rDoc,甚至Glyph。问题是Sphinx可以做网站、PDF、epub、LaTex等。它在重组文本中完成所有这些事情。在Ruby世界中有替​​代方案吗?也许是程序的组合?如果我也能使用Markdown就更好了。 最佳答案 自1.0版以来,Sphinx有了“域”的概念,它是从Python和/或C以外的语言标记代码实体(如方法调用、对象、函数等)的方法。有一个rubydomain,所以你可以只使用Sphinx本身。您唯一会缺少的(我认为)是Sphinx使用autodoc从源代码自动创建文档

随机推荐