Skip to content

Commit

Permalink
Refactored border creation
Browse files Browse the repository at this point in the history
  • Loading branch information
Simon Scholz committed Oct 6, 2023
1 parent c41adb1 commit d54ee68
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 51 deletions.
48 changes: 27 additions & 21 deletions java-sample/src/main/java/io/github/simonscholz/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,32 @@
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Main {
public static void main(String[] args) throws IOException {
QrCodeApi qrCodeApi = QrCodeFactory.createQrCodeApi();
var userHomeDir = System.getProperty("user.home");
createDefaultQrCode(qrCodeApi, userHomeDir);
var userHome = System.getProperty("user.home");
final Path path = Paths.get(userHome, "qr-code-samples");
path.toFile().mkdir();
var qrCodeDir = path.toAbsolutePath().toString();

createDefaultQrCode(qrCodeApi, qrCodeDir);

URL resource = Main.class.getClassLoader().getResource("avatar-60x.png");
if(resource != null) {
createDefaultQrCodeWithLogo(resource, qrCodeApi, userHomeDir);
createDefaultQrCodeWithLogo(resource, qrCodeApi, qrCodeDir);

createDefaultQrCodeWithLogoAndCustomColors(resource, qrCodeApi, userHomeDir);
createDefaultQrCodeWithLogoAndCustomColors(resource, qrCodeApi, qrCodeDir);

createQrCodeWithRoundedPositionalSquares(resource, qrCodeApi, userHomeDir);
createQrCodeWithRoundedPositionalSquares(resource, qrCodeApi, qrCodeDir);

reallyColorfulQrCode(resource, qrCodeApi, userHomeDir);
reallyColorfulQrCode(resource, qrCodeApi, qrCodeDir);
}
}

private static void reallyColorfulQrCode(final URL resource, final QrCodeApi qrCodeApi, final String userHomeDir) throws IOException {
private static void reallyColorfulQrCode(final URL resource, final QrCodeApi qrCodeApi, final String qrCodeDir) throws IOException {
BufferedImage logo = ImageIO.read(resource);

final Color transparent = new Color(0, 0, 0, 0);
Expand All @@ -43,32 +49,32 @@ private static void reallyColorfulQrCode(final URL resource, final QrCodeApi qrC
.qrCodeSize(500)
.qrLogoConfig(logo)
.qrPositionalSquaresConfig(qrPositionalSquaresConfig)
//.qrBorderConfig(Color.ORANGE.darker(), .05)
.qrBorderConfig(transparent, .05)
//.qrCodeColorConfig(Color.RED, Color.BLUE)
.qrCodeColorConfig(transparent, Color.BLUE)
.qrBorderConfig(Color.ORANGE.darker(), .05)
//.qrBorderConfig(transparent, .05)
//.qrCodeColorConfig(transparent, Color.BLUE)
.qrCodeColorConfig(Color.WHITE, Color.BLUE)
.build();

BufferedImage qrWithImage = qrCodeApi.createQrImage(qrCodeConfig);
ImageIO.write(qrWithImage, "png", new File(userHomeDir, "/qr-colorful-java.png"));
ImageIO.write(qrWithImage, "png", new File(qrCodeDir, "/qr-colorful-java.png"));
}

private static void createQrCodeWithRoundedPositionalSquares(URL resource, QrCodeApi qrCodeApi, String userHomeDir) throws IOException {
private static void createQrCodeWithRoundedPositionalSquares(URL resource, QrCodeApi qrCodeApi, String qrCodeDir) throws IOException {
BufferedImage logo = ImageIO.read(resource);

QrPositionalSquaresConfig qrPositionalSquaresConfig =
new QrPositionalSquaresConfig(Color.BLACK, Color.RED, Color.BLUE, true);
QrCodeConfig qrCodeConfig =new Builder("https://simonscholz.github.io/")
.qrLogoConfig(logo)
.qrPositionalSquaresConfig(qrPositionalSquaresConfig)
.qrBorderConfig(Color.WHITE, .0)
.qrBorderConfig(Color.WHITE, .04)
.build();

BufferedImage qrWithImage = qrCodeApi.createQrImage(qrCodeConfig);
ImageIO.write(qrWithImage, "png", new File(userHomeDir, "/qr-rounded-positionals-java.png"));
ImageIO.write(qrWithImage, "png", new File(qrCodeDir, "/qr-rounded-positionals-java.png"));
}

private static void createDefaultQrCodeWithLogoAndCustomColors(URL resource, QrCodeApi qrCodeApi, String userHomeDir) throws IOException {
private static void createDefaultQrCodeWithLogoAndCustomColors(URL resource, QrCodeApi qrCodeApi, String qrCodeDir) throws IOException {
BufferedImage logo = ImageIO.read(resource);

Color darkGreen = Color.GREEN.darker().darker().darker();
Expand All @@ -82,21 +88,21 @@ private static void createDefaultQrCodeWithLogoAndCustomColors(URL resource, QrC
.build();

BufferedImage qrWithImage = qrCodeApi.createQrImage(qrCodeConfig);
ImageIO.write(qrWithImage, "png", new File(userHomeDir, "/qr-with-logo-green-color-java.png"));
ImageIO.write(qrWithImage, "png", new File(qrCodeDir, "/qr-with-logo-green-color-java.png"));
}

private static void createDefaultQrCodeWithLogo(URL resource, QrCodeApi qrCodeApi, String userHomeDir) throws IOException {
private static void createDefaultQrCodeWithLogo(URL resource, QrCodeApi qrCodeApi, String qrCodeDir) throws IOException {
BufferedImage logo = ImageIO.read(resource);

QrCodeConfig qrCodeConfig = new QrCodeConfig("https://simonscholz.github.io/",
300,
new QrLogoConfig(logo));
BufferedImage qrWithImage = qrCodeApi.createQrImage(qrCodeConfig);
ImageIO.write(qrWithImage, "png", new File(userHomeDir, "/qr-with-logo-java.png"));
ImageIO.write(qrWithImage, "png", new File(qrCodeDir, "/qr-with-logo-java.png"));
}

private static void createDefaultQrCode(QrCodeApi qrCodeApi, String userHomeDir) throws IOException {
private static void createDefaultQrCode(QrCodeApi qrCodeApi, String qrCodeDir) throws IOException {
var qrCode = qrCodeApi.createQrImage(new QrCodeConfig("https://simonscholz.github.io/", 200));
ImageIO.write(qrCode, "png", new File(userHomeDir, "/qr-with-defaults-java.png"));
ImageIO.write(qrCode, "png", new File(qrCodeDir, "/qr-with-defaults-java.png"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ data class QrCodeConfig @JvmOverloads constructor(
val qrLogoConfig: QrLogoConfig? = null,
val qrCodeColorConfig: QrCodeColorConfig = QrCodeColorConfig(),
val qrPositionalSquaresConfig: QrPositionalSquaresConfig = QrPositionalSquaresConfig(),
val qrBorderConfig: QrBorderConfig = QrBorderConfig(),
val qrBorderConfig: QrBorderConfig? = null,
) {
init {
require(qrCodeText.isNotBlank()) { "qrCodeText must not be blank." }
Expand All @@ -31,7 +31,7 @@ data class QrCodeConfig @JvmOverloads constructor(
private var qrLogoConfig: QrLogoConfig? = null
private var qrCodeColorConfig: QrCodeColorConfig = QrCodeColorConfig()
private var qrPositionalSquaresConfig: QrPositionalSquaresConfig = QrPositionalSquaresConfig()
private var qrBorderConfig: QrBorderConfig = QrBorderConfig()
private var qrBorderConfig: QrBorderConfig? = null

fun qrCodeSize(qrCodeSize: Int) = apply { this.qrCodeSize = qrCodeSize }
fun qrLogoConfig(logo: BufferedImage, relativeSize: Double = .2) = apply { this.qrLogoConfig = QrLogoConfig(logo, relativeSize) }
Expand Down Expand Up @@ -102,8 +102,9 @@ data class QrPositionalSquaresConfig @JvmOverloads constructor(
* @param color - color of the border
* @param relativeSize - relative size of the border, defaults to 0.05
*/
data class QrBorderConfig @JvmOverloads constructor(val color: Color = Color.BLACK, val relativeSize: Double = .05) {
data class QrBorderConfig @JvmOverloads constructor(val color: Color = Color.BLACK, val relativeSize: Double = .05, val relativeBorderRound: Double = 0.2) {
init {
require(relativeSize in 0.0..1.0) { "relativeSize must be in between 0 and 1." }
require(relativeSize in 0.01..1.0) { "relativeSize must be in between 0.01 and 1." }
require(relativeBorderRound in 0.01..1.0) { "relativeSize must be in between 0.01 and 1." }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ internal class QrCodeApiImpl : QrCodeApi {
graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY)
graphics.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE)

BorderGraphics.drawBorder(
graphics = graphics,
borderColor = qrCodeConfig.qrBorderConfig.color,
bgColor = qrCodeConfig.qrCodeColorConfig.bgColor,
size = qrCodeConfig.qrCodeSize,
relativeBorderRound = .2,
borderWidth = relativeSize(qrCodeConfig.qrCodeSize, qrCodeConfig.qrBorderConfig.relativeSize),
)
qrCodeConfig.qrBorderConfig?.let {
BorderGraphics.drawBorder(
graphics = graphics,
borderColor = it.color,
size = qrCodeConfig.qrCodeSize,
relativeBorderRound = it.relativeBorderRound,
borderWidth = relativeSize(qrCodeConfig.qrCodeSize, it.relativeSize),
)
}

val qrCodeCreator = QrCodeCreator()
val qrCode = qrCodeCreator.createQrImageWithPositionals(
Expand All @@ -37,13 +38,15 @@ internal class QrCodeApiImpl : QrCodeApi {
fillColor = qrCodeConfig.qrCodeColorConfig.fillColor,
bgColor = qrCodeConfig.qrCodeColorConfig.bgColor,
internalCircleColor = qrCodeConfig.qrPositionalSquaresConfig.centerColor,
quiteZone = 1,
quietZone = qrCodeConfig.qrBorderConfig?.let { 2 } ?: 0, // have a quietZone if we have a border
borderWidth = qrCodeConfig.qrBorderConfig?.let { relativeSize(qrCodeConfig.qrCodeSize, it.relativeSize) } ?: 0,
relativeBorderRound = qrCodeConfig.qrBorderConfig?.relativeBorderRound ?: .0,
)

graphics.drawImage(qrCode, 2, 2, null)
graphics.drawImage(qrCode, 0, 0, null)

qrCodeConfig.qrLogoConfig?.let {
LogoGraphics.drawLogo(graphics, it.logo, qrCodeConfig.qrCodeSize, qrCodeConfig.qrLogoConfig.relativeSize)
LogoGraphics.drawLogo(graphics, it.logo, qrCodeConfig.qrCodeSize, it.relativeSize)
}
image
} finally {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,24 @@
package io.github.simonscholz.qrcode.border

import java.awt.BasicStroke
import java.awt.Color
import java.awt.Graphics2D
import java.awt.Stroke

internal object BorderGraphics {
fun drawBorder(graphics: Graphics2D, borderColor: Color, bgColor: Color, size: Int, relativeBorderRound: Double, borderWidth: Int) {
fun drawBorder(graphics: Graphics2D, borderColor: Color, size: Int, relativeBorderRound: Double, borderWidth: Int) {
graphics.color = borderColor
graphics.fillRoundRect(
0,
0,
size,
size,
(size * relativeBorderRound).toInt(),
(size * relativeBorderRound).toInt(),
)
graphics.color = bgColor
graphics.fillRoundRect(

val oldStroke: Stroke = graphics.stroke
graphics.stroke = BasicStroke(borderWidth.toFloat())
graphics.drawRoundRect(
borderWidth / 2,
borderWidth / 2,
size - borderWidth,
size - borderWidth,
((size - borderWidth) * relativeBorderRound).toInt(),
((size - borderWidth) * relativeBorderRound).toInt(),
(size * relativeBorderRound).toInt(),
(size * relativeBorderRound).toInt(),
)
graphics.stroke = oldStroke
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,12 @@ internal class QrCodeCreator {
fillColor: Color? = Color.BLACK,
bgColor: Color = Color(0f, 0f, 0f, 0f),
internalCircleColor: Color = Color.BLACK,
quiteZone: Int = 1,
quietZone: Int,
borderWidth: Int,
relativeBorderRound: Double,
): BufferedImage {
val qrCode: QRCode = Encoder.encode(qrCodeText, ErrorCorrectionLevel.H, encodeHintTypes())
val (positionalSquares, dataSquares) = PositionalsUtil.renderResult(qrCode, size, quiteZone)
val (positionalSquares, dataSquares) = PositionalsUtil.renderResult(qrCode, size, quietZone)

val image = BufferedImage(size, size, BufferedImage.TYPE_4BYTE_ABGR_PRE)
val graphics = image.graphics as Graphics2D
Expand All @@ -55,6 +57,16 @@ internal class QrCodeCreator {
graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY)
graphics.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE)

graphics.color = bgColor
graphics.fillRoundRect(
borderWidth / 2,
borderWidth / 2,
size - borderWidth,
size - borderWidth,
((size - borderWidth) * relativeBorderRound).toInt(),
((size - borderWidth) * relativeBorderRound).toInt(),
)

// Data Squares
dataSquares.forEach { s ->
if (s.isFilled) {
Expand Down

0 comments on commit d54ee68

Please sign in to comment.