32. Springboot 系列(八)动态Banner与图片转字符图案的手动实现

摘要:
使用过Springboot的人将熟悉上述模式。当Springboot启动时,上面的模式将打印版本号。将gif、banner.jpg或banner.png图像文件保存到类路径中,或设置spring.anner.image.Springboot2版本的spring:banner:image:location:classpath:img/cang.jpg,但效果没有显现出来。1.根据官方描述,自定义横幅可以自定义类路径中的横幅模式。我们试图在place目录中创建一个新的文件banner.txt,并写入内容_|_||_|_|| _|\___/\___/版本:${spring-boot.formatedversion}启动spring boot,并在控制台上看到以下输出。Springboot将模式转换为ASCII模式。Springboot对图像横幅的处理方式是我们上面所想的吗/**Location:org.springframework。靴子SpringApplicationBannerPrinter*///方法1:publicBannerprint(环境环境,类˂?

32. Springboot 系列(八)动态Banner与图片转字符图案的手动实现第1张

使用过 Springboot 的对上面这个图案肯定不会陌生,Springboot 启动的同时会打印上面的图案,并带有版本号。查看官方文档可以找到关于 banner 的描述

The banner that is printed on start up can be changed by adding a banner.txt file to your classpath or by setting 
the spring.banner.location property to the location of such a file. If the file has an encoding other than UTF-8,
you can set spring.banner.charset. In addition to a text file, you can also add a banner.gif, banner.jpg,
or banner.png image file to your classpath or set the spring.banner.image.location property.
Images are converted into an ASCII art representation and printed above any text banner.

就不翻译了,直接有道翻译贴过来看个大概意思。

可以通过向类路径中添加一个banner.txt文件或设置spring.banner来更改在start up上打印的banner。属性指向此类文件的位置。如果文件的编码不是UTF-8,
那么可以设置spring.banner.charset。除了文本文件,还可以添加横幅。将gif、banner.jpg或banner.png图像文件保存到类路径或设置spring.banner.image。位置属性。
图像被转换成ASCII艺术形式,并打印在任何文本横幅上面。
springboot2版本
spring: 
  banner: 
    image:
      location: classpath:img/cang.jpg

但是效果没出来

 32. Springboot 系列(八)动态Banner与图片转字符图案的手动实现第2张 1. 自定义 banner

 根据官方的描述,可以在类路径中自定义 banner 图案,我们进行尝试在放 resouce 目录下新建文件 banner.txt 并写入内容(在线字符生成)。

   (_)
  _ __  _ _   _ _ __ ___   ___   ___
 | '_ | | | | | '_ ` _  / _  / _ 
 | | | | | |_| | | | | | | (_) | (_) |
 |_| |_|_|\__,_|_| |_| |_|\___/ \___/ 版本:${spring-boot.formatted-version}

启动 Springboot 在控制台看到下面的输出。

     (_)
  _ __  _ _   _ _ __ ___   ___   ___
 | '_ | | | | | '_ ` _  / _  / _ 
 | | | | | |_| | | | | | | (_) | (_) |
 |_| |_|_|\__,_|_| |_| |_|\___/ \___/ 版本:(v2.1.3.RELEASE)
2019-02-25 14:00:31.289  INFO 12312 --- [           main] net.codingme.banner.BannerApplication    : Starting BannerApplication on LAPTOP-L1S5MKTA with PID 12312 (D:IdeaProjectMyspringboot-gitspringboot-banner	argetclasses started by Niu in D:IdeaProjectMyspringboot-gitspringboot-banner)
2019-02-25 14:00:31.291  INFO 12312 --- [           main] net.codingme.banner.BannerApplication    : No active profile set, falling back to default profiles: default
2019-02-25 14:00:32.087  INFO 12312 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)

发现自定义 banner 已经生效了,官方文档的介绍里说还可以放置图片,下面放置图片 banner.jpg 测试。
网上随便找了一个图片。

 32. Springboot 系列(八)动态Banner与图片转字符图案的手动实现第3张再次启动观察输出。

32. Springboot 系列(八)动态Banner与图片转字符图案的手动实现第4张

Springboot 把图案转成了 ASCII 图案。

2. ASCII 图案生成原理

看了上面的例子,发现 Springboot 可以把图片转换成 ASCII 图案,那么它是怎么做的呢?我们或许可以想象出一个大概流程。

  1. 获取图片。
  2. 遍历图片像素点。
  3. 分析像素点,每个像素点根据颜色深度得出一个值,根据明暗度匹配不同的字符。
  4. 输出图案。

Springboot 对图片 banner 的处理到底是不是我们上面想想的那样呢?直接去源码中寻找答案。

/** 位置:org.springframework.boot.SpringApplicationBannerPrinter */
//方法1:
public Banner print(Environment environment, Class<?> sourceClass, Log logger) {
        // 获取 banner  调用方法记为2
        Banner banner = getBanner(environment);
        try {
            logger.info(createStringFromBanner(banner, environment, sourceClass));
        }
        catch (UnsupportedEncodingException ex) {
            logger.warn("Failed to create String for banner", ex);
        }
        // 打印 banner
        return new PrintedBanner(banner, sourceClass);
}
// 方法2
private Banner getBanner(Environment environment) {
        Banners banners = new Banners();
        // 获取图片banner,我们只关注这个,调用方法记为3
        banners.addIfNotNull(getImageBanner(environment));
        banners.addIfNotNull(getTextBanner(environment));
        if (banners.hasAtLeastOneBanner()) {
            return banners;
        }
        if (this.fallbackBanner != null) {
            return this.fallbackBanner;
        }
        return DEFAULT_BANNER;
    }
// 方法3
/** 获取自定义banner文件信息 */
private Banner getImageBanner(Environment environment) {
    // BANNER_IMAGE_LOCATION_PROPERTY = "spring.banner.image.location";
        String location = environment.getProperty(BANNER_IMAGE_LOCATION_PROPERTY);
        if (StringUtils.hasLength(location)) {
            Resource resource = this.resourceLoader.getResource(location);
            return resource.exists() ? new ImageBanner(resource) : null;
        }
        // IMAGE_EXTENSION = { "gif", "jpg", "png" };
        for (String ext : IMAGE_EXTENSION) {
            Resource resource = this.resourceLoader.getResource("banner." + ext);
            if (resource.exists()) {
                return new ImageBanner(resource);
            }
        }
        return null;
}

上面是寻找自定义图片 banner 文件源码,如果把图片转换成 ASCII 图案继续跟进,追踪方法1中的PrintedBanner(banner, sourceClass)方法。最终查找输出图案的主要方法。

// 位置:org.springframework.boot.ImageBanner#printBanner
private void printBanner(BufferedImage image, int margin, boolean invert,
            PrintStream out) {
        AnsiElement background = invert ? AnsiBackground.BLACK : AnsiBackground.DEFAULT;
        out.print(AnsiOutput.encode(AnsiColor.DEFAULT));
        out.print(AnsiOutput.encode(background));
        out.println();
        out.println();
        AnsiColor lastColor = AnsiColor.DEFAULT;
        // 图片高度遍历
        for (int y = 0; y < image.getHeight(); y++) {
            for (int i = 0; i < margin; i++) {
                out.print(" ");
            }
            // 图片宽度遍历
            for (int x = 0; x < image.getWidth(); x++) {
                // 获取每一个像素点
                Color color = new Color(image.getRGB(x, y), false);
                AnsiColor ansiColor = AnsiColors.getClosest(color);
                if (ansiColor != lastColor) {
                    out.print(AnsiOutput.encode(ansiColor));
                    lastColor = ansiColor;
                }
                // 像素点转换成字符输出,调用方法记为2
                out.print(getAsciiPixel(color, invert));
            }
            out.println();
        }
        out.print(AnsiOutput.encode(AnsiColor.DEFAULT));
        out.print(AnsiOutput.encode(AnsiBackground.DEFAULT));
        out.println();
    }
// 方法2,像素点转换成字符
    private char getAsciiPixel(Color color, boolean dark) {
        // 根据 color 算出一个亮度值
        double luminance = getLuminance(color, dark);
        for (int i = 0; i < PIXEL.length; i++) {
            // 寻找亮度值匹配的字符
            if (luminance >= (LUMINANCE_START - (i * LUMINANCE_INCREMENT))) {
                // PIXEL = { ' ', '.', '*', ':', 'o', '&', '8', '#', '@' };
                return PIXEL[i];
            }
        }
        return PIXEL[PIXEL.length - 1];
    }
3.自己实现图片转 ASCII字符

根据上面的分析,总结一下思路,我们也可以手动写一个图片转 ASCII 字符图案。
思路如下:

  1. 图片大小缩放,调整到合适大小。
  2. 遍历图片像素。
  3. 获取图片像素点亮度(RGB颜色通过公式可以得到亮度数值)。
  4. 匹配字符。
  5. 输出图案。

上面的5个步骤直接使用 Java 代码就可以完整实现,下面是编写的源码。

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.imageio.ImageIO;

/**
 * <p>
 * 根据图片生成字符图案
 * 1.图片大小缩放
 * 2.遍历图片像素点
 * 3.获取图片像素点亮度
 * 4.匹配字符
 * 5.输出图案
 *
 * @author  niujinpeng
 * @website www.codingme.net
 * @date 2019-02-25 23:03:01
 */
public class GeneratorTextImage {
    private static final char[] PIXEL = {'@', '#', '8', '&', 'o', ':', '*', '.', ' '};
    public static void main(String[] args) throws Exception {
        // 图片缩放
        BufferedImage bufferedImage = makeSmallImage("src/main/resources/banner.jpg");
        // 输出
        printImage(bufferedImage);
    }

    public static void printImage(BufferedImage image) throws IOException {
        int width = image.getWidth();
        int height = image.getHeight();
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                int rgb = image.getRGB(j, i);
                Color color = new Color(rgb);
                int red = color.getRed();
                int green = color.getGreen();
                int blue = color.getBlue();
                // 一个用于计算RGB像素点亮度的公式
                Double luminace = 0.2126 * red + 0.7152 * green + 0.0722 * blue;
                double index = luminace / (Math.ceil(255 / PIXEL.length) + 0.5);
                System.out.print(PIXEL[(int)(Math.floor(index))]);
            }
            System.out.println();
        }
    }

    public static BufferedImage makeSmallImage(String srcImageName) throws Exception {
        File srcImageFile = new File(srcImageName);
        if (srcImageFile == null) {
            System.out.println("文件不存在");
            return null;
        }
        FileOutputStream fileOutputStream = null;
        BufferedImage tagImage = null;
        Image srcImage = null;
        try {
            srcImage = ImageIO.read(srcImageFile);
            int srcWidth = srcImage.getWidth(null);// 原图片宽度
            int srcHeight = srcImage.getHeight(null);// 原图片高度
            int dstMaxSize = 90;// 目标缩略图的最大宽度/高度,宽度与高度将按比例缩写
            int dstWidth = srcWidth;// 缩略图宽度
            int dstHeight = srcHeight;// 缩略图高度
            float scale = 0;
            // 计算缩略图的宽和高
            if (srcWidth > dstMaxSize) {
                dstWidth = dstMaxSize;
                scale = (float)srcWidth / (float)dstMaxSize;
                dstHeight = Math.round((float)srcHeight / scale);
            }
            srcHeight = dstHeight;
            if (srcHeight > dstMaxSize) {
                dstHeight = dstMaxSize;
                scale = (float)srcHeight / (float)dstMaxSize;
                dstWidth = Math.round((float)dstWidth / scale);
            }
            // 生成缩略图
            tagImage = new BufferedImage(dstWidth, dstHeight, BufferedImage.TYPE_INT_RGB);
            tagImage.getGraphics().drawImage(srcImage, 0, 0, dstWidth, dstHeight, null);
            return tagImage;
        } finally {
            if (fileOutputStream != null) {
                try {
                    fileOutputStream.close();
                } catch (Exception e) {
                }
                fileOutputStream = null;
            }
            tagImage = null;
            srcImage = null;
            System.gc();
        }
    }
}

还是拿上面的 Google log 图片作为实验对象,运行得到字符图案输出。

32. Springboot 系列(八)动态Banner与图片转字符图案的手动实现第5张

免责声明:文章转载自《32. Springboot 系列(八)动态Banner与图片转字符图案的手动实现》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇第二章:NHibernate配置的总体流程Ubuntu20.04.1LTS系统Super simple美化与配置下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

linux下的二进制文件的编辑和查看

linux下的二进制文件的编辑和查看 一、在Linux下查看二进制文件的软件: xxd (默认2进制) hexdump  (默认16进制,可转其他进制)  od (默认8进制,可转其他进制) 二、编辑: 1、biew 2、hexedit 3、vim Vim 来编辑二进制文件。Vim 本非为此而设计的,因而有若干局限。但你能读取一个文件,改动一个字符,...

第十三节、SURF特征提取算法

上一节我们已经介绍了SIFT算法,SIFT算法对旋转、尺度缩放、亮度变化等保持不变性,对视角变换、仿射变化、噪声也保持一定程度的稳定性,是一种非常优秀的局部特征描述算法。但是其实时性相对不高。 SURF(Speeded Up Robust Features)算法改进了特征了提取和描述方式,用一种更为高效的方式完成特征点的提取和描述。 一 使用快速Hessi...

JAVA转义字符

JAVA中转义字符2009-08-12 17:40JAVA中转义字符: 1.八进制转义序列:\ + 1到3位5数字;范围'\000'~'\377' \0:空字符 2.Unicode转义字符:\u + 四个十六进制数字;0~65535 \u0000:空字符 3.特殊字符:就3个 \":双引号 \':单引号 \\:反斜线 4.控制字符:5个...

OpenGL教程一

引自:https://blog.csdn.net/u013654125/article/details/73613644 GLEW, GLFW和GLM介绍 现在你有了工程,就让我们开始介绍下工程所用到的开源库和为啥需要这些。 The OpenGL Extension Wrangler (GLEW)是用来访问OpenGL 3.2 API函数的。不幸的是你不能...

ES6(正则扩展)

ES6中正则的扩展 正则新增特性 一、构造函数的变化 1.ES5中new一个正则对象方法 (一行中2个参数,二行中1个参数) (第一行中的第一个参数必须是字符串) 2.ES6中新增一种方法(构造函数) (输出:i ) 第二个修饰符会覆盖第一个修饰符,flags是新增用来获取正则对象修饰符的属性。 二、y修饰符 2-1.描述 第一步都可以匹配到b...

Linux 的字符串截取很有用。有八种方法。

假设有变量 var=http://www.aaa.com/123.htm 1. # 号截取,删除左边字符,保留右边字符。 echo ${var#*//} 其中 var 是变量名,# 号是运算符,*// 表示从左边开始删除第一个 // 号及左边的所有字符 即删除 http:// 结果是 :www.aaa.com/123.htm 2. ## 号截取,删除左...