[Java] Java生成二維碼帶LOGO, LOGO加圓角白框

先來看看效果:

在這裏插入圖片描述

  • 實現:
  1. 生成指定文字內容的二維碼
  2. 二維碼中間嵌入LOGO
  3. 二維碼做圓角和白色邊框處理

新需求不斷, 這不, 又來了個想生成帶用戶頭像的需求. 蠻簡單的… 在這裏造完輪子分享給大家

因爲公司後端主要是Kotlin寫的. 請大家自行翻譯.
Kotlin和Java本質是一種東西… 有關kotlin寫後端是否合適…自己看吧. 人家官網直接支持
在這裏插入圖片描述

不廢話了, 上代碼

需引入:

        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>core</artifactId>
            <version>3.3.3</version>
        </dependency>
        
        <dependency>
            <groupId>net.coobird</groupId>
            <artifactId>thumbnailator</artifactId>
            <version>0.4.8</version>
        </dependency>

運行方法:


fun main() {
    val resBase64 = QrCodeUtil.generateLogoQrCode("I Love U!", "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")
    println(resBase64)
}

即可得到我們想要的二維碼Logo圖片!

因爲Base64字符串非常通用, 所以這裏的圖片入參都爲Base64字符串.

import com.google.zxing.BarcodeFormat
import com.google.zxing.EncodeHintType
import com.google.zxing.MultiFormatWriter
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
import net.coobird.thumbnailator.Thumbnails
import sun.misc.BASE64Decoder
import sun.misc.BASE64Encoder
import java.awt.AlphaComposite
import java.awt.BasicStroke
import java.awt.Color
import java.awt.RenderingHints
import java.awt.geom.RoundRectangle2D
import java.awt.image.BufferedImage
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import javax.imageio.ImageIO


class QrCodeUtil {
    companion object {

        /**
         * 生成帶logo的二維碼
         * @param content 內容
         * @param logoBase64 內嵌logo圖片
         * @param qrColor16bit 16位二維碼顏色 (默認黑色)
         * @param bgColor16bit 16位背景顏色  (默認白色)
         */
        fun generateLogoQrCode(content: String,
                               logoBase64: String,
                               qrColor16bit: Int = -0x1000000,
                               bgColor16bit: Int = -0x1,
                               width: Int = 258,
                               height: Int = 258): String {


            // logo base64 轉 bufferedImg
            val logoImg = base64ToBufferedImage(logoBase64)

            // 二維碼圖片
            val qrImg = generateBufferedQrImg(
                    content = content,
                    width = width,
                    height = height,
                    qrColor16bit = qrColor16bit,
                    bgColor16bit = bgColor16bit)

            // 組合好的二維碼圖片
            val finalImg = addLogoToQrCode(qrImg, logoImg)

            // 圖片轉base64, 剔除換行
            return bufferedImageToBase64(finalImg).replace("\r", "").replace("\n", "")

        }

        /**
         * 生成二維碼bufferedImage圖片
         *
         * @param content       編碼內容
         * @param barcodeFormat 編碼類型
         * @param width         圖片寬度 (默認258)
         * @param height        圖片高度 (默認258)
         * @param hints         二維碼配置
         * @return
         */
        private fun generateBufferedQrImg(content: String,
                                          barcodeFormat: BarcodeFormat = BarcodeFormat.QR_CODE,
                                          width: Int,
                                          height: Int,
                                          qrColor16bit: Int,
                                          bgColor16bit: Int,
                                          hints: Map<EncodeHintType, Any> = defaultHint()): BufferedImage {
            val multiFormatWriter = MultiFormatWriter()
            val bm = multiFormatWriter.encode(content, barcodeFormat, width, height, hints)

            // 創建bufferedImage對象
            val image = BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)

            // 開始利用二維碼數據創建Bitmap圖片,分別設爲黑(0xFFFFFFFF)白(0xFF000000)兩色
            for (x in 0 until width) {
                for (y in 0 until height) {
                    image.setRGB(x, y, if (bm.get(x, y)) qrColor16bit else bgColor16bit)
                }
            }

            return image

        }

        /**
         * 默認二維碼配置
         */
        private fun defaultHint(): Map<EncodeHintType, Any> {
            val hints = HashMap<EncodeHintType, Any>()

            // 二維碼糾錯級別(H爲最高)
            hints[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.H

            // 編碼
            hints[EncodeHintType.CHARACTER_SET] = "utf-8"
            hints[EncodeHintType.MARGIN] = 0

            return hints
        }

        /**
         * 給二維碼圖片添加LOGO
         * @param img 二維碼原圖
         * @param
         */
        private fun addLogoToQrCode(qrImg: BufferedImage,
                                    logoImg: BufferedImage): BufferedImage {


            /**
             * 讀取二維碼圖片, 構建繪圖對象
             */
            val graphic = qrImg.createGraphics()

            /**
             * 爲logo進行壓縮處理
             */
            val logoThumb =
                    Thumbnails.of(logoImg).scale(0.5).outputQuality(1f).asBufferedImage()

            /**
             * 設置logo寬高
             */
            val logoWidth = if (logoThumb.width > qrImg.width * 3 / 10) qrImg.width * 3 / 10 else logoThumb.width
            val logoHeight = if (logoThumb.height > qrImg.height * 3 / 10) qrImg.height * 3 / 10 else logoThumb.height

            /**
             * logo 放在中心
             */
            val logoX = (qrImg.width - logoWidth) / 2
            val logoY = (qrImg.height - logoHeight) / 2

            /**
             * 對LOGO進行裁剪並加白邊框
             */
            val logoCut = imageCutRoundAndBackGround(logoThumb)

            /**
             * 繪製圖片
             */
            graphic.drawImage(logoCut, logoX, logoY, logoWidth, logoHeight, null)
            graphic.dispose()


            qrImg.flush()
            logoThumb.flush()

            return qrImg
        }
        /**
         * Base64字符串轉BufferedImage
         */
        private fun base64ToBufferedImage(base64: String): BufferedImage {

            val decoder = BASE64Decoder()
            val logoByteData = decoder.decodeBuffer(base64)
            val inputStream = ByteArrayInputStream(logoByteData)
            return ImageIO.read(inputStream)
        }

        /**
         * BufferedImage轉Base64字符串
         */
        private fun bufferedImageToBase64(image: BufferedImage): String {
            /**
             * BufferedImage轉byte[]
             */
            val bos = ByteArrayOutputStream()
            ImageIO.write(image, "jpg", bos)
            val byteArray = bos.toByteArray()

            /**
             * byte[]轉base64
             */
            val encoder = BASE64Encoder()
            return encoder.encode(byteArray)
        }

        /**
         * 圖片切圓角且帶邊框
         * 本方法待優化
         */
        private fun imageCutRoundAndBackGround(img: BufferedImage,
                                       border: Int = 0,
                                       padding: Int = 5): BufferedImage {
            val radius = (img.width + img.height) / 6


            val width = img.width
            val height = img.height
            val canvasWidth = width + padding * 2
            val canvasHeight = height + padding * 2

            val resImage = BufferedImage(canvasWidth, canvasHeight, BufferedImage.TYPE_INT_ARGB)
            val gs = resImage.createGraphics()
            gs.composite = AlphaComposite.Src
            gs.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
            gs.color = Color.WHITE
            gs.fill(RoundRectangle2D.Float(0f, 0f, canvasWidth.toFloat(), canvasHeight.toFloat(), radius.toFloat(), radius.toFloat()))
            gs.composite = AlphaComposite.SrcAtop
            gs.drawImage(setClip(img, radius), padding, padding, null)

            if (border != 0) {
                gs.color = Color.GRAY
                gs.stroke = BasicStroke(border.toFloat())
                gs.drawRoundRect(padding, padding, canvasWidth - 2 * padding, canvasHeight - 2 * padding, radius, radius)
            }
            gs.dispose()
            return resImage
        }

        private fun setClip(img: BufferedImage, radius: Int): BufferedImage {
            val width = img.width
            val height = img.height
            val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
            val gs = image.createGraphics()

            gs.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
            gs.clip = RoundRectangle2D.Double(0.0, 0.0, width.toDouble(), height.toDouble(), radius.toDouble(), radius.toDouble())
            gs.drawImage(img, 0, 0, null)
            gs.dispose()
            return image
        }
    }
}

時間緊, 任務重! 所以方法肯定還有需要優化的地方, 大家可以自行嘗試! 希望大家永無BUG!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章