diff --git a/.github/workflows/spotless-apply.yml b/.github/workflows/run-tests.yml similarity index 61% rename from .github/workflows/spotless-apply.yml rename to .github/workflows/run-tests.yml index f2a48e9..8ad5fc1 100644 --- a/.github/workflows/spotless-apply.yml +++ b/.github/workflows/run-tests.yml @@ -1,4 +1,4 @@ -name: Spotless Apply +name: Run Tests on: push: @@ -15,18 +15,12 @@ jobs: with: repository: ${{ github.event.pull_request.head.repo.full_name }} ref: ${{ github.event.pull_request.head.ref }} - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v3 with: java-version: "17" distribution: "temurin" - - name: Run Spotless Apply + - name: Run Tests uses: gradle/gradle-build-action@v2 with: - arguments: spotlessApply --force - - name: Commit Changes - uses: EndBug/add-and-commit@v9 - with: - message: "[CI] Spotless Apply" - committer_name: "GitHub Actions" - committer_email: "actions@github.com" + arguments: test diff --git a/build.gradle.kts b/build.gradle.kts index 8ae3456..577c90e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -78,7 +78,7 @@ kotlin { } } - ios() +// ios() sourceSets { val commonTest by getting { diff --git a/examples/kotlin/example01-circles.png b/examples/kotlin/example01-circles.png new file mode 100644 index 0000000..61ea5bb Binary files /dev/null and b/examples/kotlin/example01-circles.png differ diff --git a/examples/kotlin/example01-custom-jvm.png b/examples/kotlin/example01-custom-jvm.png new file mode 100644 index 0000000..f577557 Binary files /dev/null and b/examples/kotlin/example01-custom-jvm.png differ diff --git a/examples/kotlin/example01-custom.png b/examples/kotlin/example01-custom.png new file mode 100644 index 0000000..1164548 Binary files /dev/null and b/examples/kotlin/example01-custom.png differ diff --git a/examples/kotlin/example01-rounded-squares.png b/examples/kotlin/example01-rounded-squares.png new file mode 100644 index 0000000..8857dcc Binary files /dev/null and b/examples/kotlin/example01-rounded-squares.png differ diff --git a/examples/kotlin/example01-squares.png b/examples/kotlin/example01-squares.png new file mode 100644 index 0000000..073616d Binary files /dev/null and b/examples/kotlin/example01-squares.png differ diff --git a/examples/kotlin/example02-color.png b/examples/kotlin/example02-color.png new file mode 100644 index 0000000..df771d0 Binary files /dev/null and b/examples/kotlin/example02-color.png differ diff --git a/examples/kotlin/example02-dark-mode.png b/examples/kotlin/example02-dark-mode.png new file mode 100644 index 0000000..8022888 Binary files /dev/null and b/examples/kotlin/example02-dark-mode.png differ diff --git a/examples/kotlin/example02-gradient.png b/examples/kotlin/example02-gradient.png new file mode 100644 index 0000000..bc77a35 Binary files /dev/null and b/examples/kotlin/example02-gradient.png differ diff --git a/examples/kotlin/src/main/kotlin/Example01-Shapes.kt b/examples/kotlin/src/main/kotlin/Example01-Shapes.kt new file mode 100644 index 0000000..e93017d --- /dev/null +++ b/examples/kotlin/src/main/kotlin/Example01-Shapes.kt @@ -0,0 +1,97 @@ +import qrcode.QRCode +import qrcode.raw.QRCodeProcessor +import qrcode.render.QRCodeGraphics +import qrcode.shape.DefaultShapeFunction +import qrcode.shape.QRCodeShapeFunction +import java.awt.Color +import java.awt.Polygon +import java.io.FileOutputStream + +fun main() { + // All supported platforms + // ----------------------- + // Squares (default) + val squareQRCode = QRCode.ofSquares() // <- See Here + .build("Hello, Squares!") + val squarePngData = squareQRCode.render() + + // Circles + val circleQRCode = QRCode.ofCircles() // <- See Here + .build("Hello, Circles!") + val circlePngData = circleQRCode.render() + + // Rounded Squares + val roundedSquareQRCode = QRCode.ofRoundedSquares() // <- See Here + .build("Hello, Rounded Squares!") + val roundedSquarePngData = roundedSquareQRCode.render() + + // Custom Shape + // WARNING: For demonstration purposes only. My phone camera couldn't read it. + val customShapeQRCode = QRCode.ofCustomShape(TriangleShapeFunction()) // <- See Here + .build("Hello, Triangles!") + val customShapePngData = customShapeQRCode.render() + + // Custom Shape - JVM-specific implementation + // WARNING: For demonstration purposes only. My phone camera couldn't read it. + val customShapeQRCodeJVM = QRCode.ofCustomShape(JVMTriangleShapeFunction()) // <- See Here + .build("Hello, Triangles... from the JVM!") + val customShapePngDataJVM = customShapeQRCodeJVM.render() + + // --------------------------- + // JVM-only code (saves the PNG Bytes to a file) + FileOutputStream("examples/kotlin/example01-squares.png").write(squarePngData) + FileOutputStream("examples/kotlin/example01-circles.png").write(circlePngData) + FileOutputStream("examples/kotlin/example01-rounded-squares.png").write(roundedSquarePngData) + FileOutputStream("examples/kotlin/example01-custom.png").write(customShapePngData) + FileOutputStream("examples/kotlin/example01-custom-jvm.png").write(customShapePngDataJVM) +} + +class TriangleShapeFunction( + squareSize: Int = QRCodeProcessor.DEFAULT_CELL_SIZE, + innerSpace: Int = 1, +) : DefaultShapeFunction(squareSize, innerSpace) { + override fun fillRect(x: Int, y: Int, width: Int, height: Int, color: Int, canvas: QRCodeGraphics) { + val topCenterX = x + width / 2 + val topCenterY = y + val bottomLeftX = x + val bottomLeftY = y + height + val bottomRightX = x + width + val bottomRightY = y + height + + // Line from top-center to bottom-left + canvas.drawLine(topCenterX, topCenterY, bottomLeftX, bottomLeftY, color, 1.0) + // Line from top-center to bottom-right + canvas.drawLine(topCenterX, topCenterY, bottomRightX, bottomRightY, color, 1.0) + // Line from bottom-left to bottom-right + canvas.drawLine(bottomLeftX, bottomLeftY, bottomRightX, bottomRightY, color, 1.0) + } +} + +/** + * Simple [QRCodeShapeFunction] that draws filled triangles instead of squares using AWT's Polygon + Graphics2D fill(). + */ +class JVMTriangleShapeFunction( + squareSize: Int = QRCodeProcessor.DEFAULT_CELL_SIZE, + innerSpace: Int = 1, +) : DefaultShapeFunction(squareSize, innerSpace) { + override fun fillRect(x: Int, y: Int, width: Int, height: Int, color: Int, canvas: QRCodeGraphics) { + val topCenterX = x + width / 2 + val topCenterY = y + val bottomLeftX = x + val bottomLeftY = y + height + val bottomRightX = x + width + val bottomRightY = y + height + + canvas.directDraw { // Platform-specific. On the JVM, it'll receive the raw Graphics2D object to draw on. + val triangle = Polygon() + triangle.addPoint(topCenterX, topCenterY) + triangle.addPoint(bottomLeftX, bottomLeftY) + triangle.addPoint(bottomRightX, bottomRightY) + triangle.addPoint(topCenterX, topCenterY) + + it.paint = Color(color) + + it.fill(triangle) + } + } +} diff --git a/examples/kotlin/src/main/kotlin/Example02-Colors.kt b/examples/kotlin/src/main/kotlin/Example02-Colors.kt new file mode 100644 index 0000000..92a4cb6 --- /dev/null +++ b/examples/kotlin/src/main/kotlin/Example02-Colors.kt @@ -0,0 +1,37 @@ +import qrcode.QRCode +import qrcode.color.Colors +import qrcode.color.LinearGradientColorFunction +import java.io.FileOutputStream + +fun main() { + // All supported platforms + // ----------------------- + // Plain color + val orangeQRCode = QRCode.ofSquares() + .withColor(Colors.ORANGE) // <- See Here + .build("Orange") + val orangePngData = orangeQRCode.render() + + // Changing both background and foreground + val darkModeQRCode = QRCode.ofSquares() + .withColor(Colors.css("#43454a")) // <- See Here + .withBackgroundColor(Colors.css("#1e1f22")) // <- See Here + .build("Dark Mode QRCode") + val darkModePngData = darkModeQRCode.render() + + val gradientQRCode = QRCode.ofSquares() + .withCustomColorFunction( + LinearGradientColorFunction( + startForegroundColor = Colors.BISQUE, + endForegroundColor = Colors.BLUE, + ), + ) + .build("Dark Mode QRCode") + val gradientPngData = gradientQRCode.render() + + // --------------------------- + // JVM-only code (saves the PNG Bytes to a file) + FileOutputStream("examples/kotlin/example02-color.png").write(orangePngData) + FileOutputStream("examples/kotlin/example02-dark-mode.png").write(darkModePngData) + FileOutputStream("examples/kotlin/example02-gradient.png").write(gradientPngData) +} diff --git a/examples/kotlin/src/main/kotlin/HelloCircles.kt b/examples/kotlin/src/main/kotlin/HelloCircles.kt deleted file mode 100644 index 780aaa9..0000000 --- a/examples/kotlin/src/main/kotlin/HelloCircles.kt +++ /dev/null @@ -1,11 +0,0 @@ -import qrcode.QRCode -import java.io.FileOutputStream - -fun main() { - val qrCode = QRCode.ofCircles() - .build("Hello, circles!") - - val pngData = qrCode.render() - - FileOutputStream("examples/kotlin/fancy-circles.png").write(pngData) -} diff --git a/examples/kotlin/src/main/kotlin/HelloWorld.kt b/examples/kotlin/src/main/kotlin/HelloWorld.kt deleted file mode 100644 index 0f523ad..0000000 --- a/examples/kotlin/src/main/kotlin/HelloWorld.kt +++ /dev/null @@ -1,12 +0,0 @@ -import qrcode.QRCode -import java.io.FileOutputStream - -fun main() { - val qrCode = QRCode.ofSquares() - .withInnerSpacing(1) - .build("Hello, world!") - - val pngData = qrCode.render() - - FileOutputStream("examples/kotlin/fancy-hello-world.png").write(pngData) -} diff --git a/src/commonMain/kotlin/qrcode/QRCode.kt b/src/commonMain/kotlin/qrcode/QRCode.kt index 15b6b62..bcd2aa1 100644 --- a/src/commonMain/kotlin/qrcode/QRCode.kt +++ b/src/commonMain/kotlin/qrcode/QRCode.kt @@ -4,6 +4,7 @@ import qrcode.QRCode.Companion.ofCircles import qrcode.QRCode.Companion.ofRoundedSquares import qrcode.QRCode.Companion.ofSquares import qrcode.QRCodeBuilder.QRCodeShapesEnum.CIRCLE +import qrcode.QRCodeBuilder.QRCodeShapesEnum.CUSTOM import qrcode.QRCodeBuilder.QRCodeShapesEnum.ROUNDED_SQUARE import qrcode.QRCodeBuilder.QRCodeShapesEnum.SQUARE import qrcode.color.DefaultColorFunction @@ -51,7 +52,7 @@ import kotlin.jvm.JvmStatic @Suppress("NON_EXPORTABLE_TYPE", "MemberVisibilityCanBePrivate") class QRCode @JvmOverloads constructor( val data: String, - val squareSize: Int = DEFAULT_CELL_SIZE, + val squareSize: Int = DEFAULT_SQUARE_SIZE, val colorFn: QRCodeColorFunction = DefaultColorFunction(), val shapeFn: QRCodeShapeFunction = DefaultShapeFunction(squareSize), var graphicsFactory: QRCodeGraphicsFactory = QRCodeGraphicsFactory(), @@ -61,6 +62,9 @@ class QRCode @JvmOverloads constructor( companion object { internal val EMPTY_FN: QRCode.(QRCodeGraphics) -> Unit = { } + /** Default value of [squareSize]. */ + const val DEFAULT_SQUARE_SIZE = DEFAULT_CELL_SIZE + /** * Creates a new [QRCodeBuilder] to build a Fancy QRCode which uses squares as the base shape (this is the default) * @@ -86,6 +90,15 @@ class QRCode @JvmOverloads constructor( */ @JvmStatic fun ofRoundedSquares(): QRCodeBuilder = QRCodeBuilder(ROUNDED_SQUARE) + + /** + * Creates a new [QRCodeBuilder] to build a QRCode which uses a custom shape function. + * + * @see QRCodeShapeFunction + * @see DefaultShapeFunction + */ + @JvmStatic + fun ofCustomShape(customShapeFunction: QRCodeShapeFunction): QRCodeBuilder = QRCodeBuilder(CUSTOM, customShapeFunction) } /** The underlying [QRCodeProcessor] object that'll do all calculations */ @@ -117,9 +130,10 @@ class QRCode @JvmOverloads constructor( actualSquare, currentCanvas, canvas, + this, ) - else -> shapeFn.renderSquare(colorFn, currentSquare, currentCanvas, canvas) + else -> shapeFn.renderSquare(colorFn, currentSquare, currentCanvas, canvas, this) } actualSquare.rendered = true diff --git a/src/commonMain/kotlin/qrcode/color/DefaultColorFunction.kt b/src/commonMain/kotlin/qrcode/color/DefaultColorFunction.kt index 6b560a2..ea9b7a6 100644 --- a/src/commonMain/kotlin/qrcode/color/DefaultColorFunction.kt +++ b/src/commonMain/kotlin/qrcode/color/DefaultColorFunction.kt @@ -1,5 +1,7 @@ package qrcode.color +import qrcode.QRCode +import qrcode.render.QRCodeGraphics import kotlin.js.ExperimentalJsExport import kotlin.js.JsExport @@ -15,9 +17,9 @@ open class DefaultColorFunction( private val foreground: Int = Colors.BLACK, private val background: Int = Colors.WHITE, ) : QRCodeColorFunction { - override fun fg(row: Int, col: Int): Int = foreground + override fun fg(row: Int, col: Int, qrCode: QRCode, qrCodeGraphics: QRCodeGraphics): Int = foreground - override fun bg(row: Int, col: Int): Int = background + override fun bg(row: Int, col: Int, qrCode: QRCode, qrCodeGraphics: QRCodeGraphics): Int = background - override fun margin(row: Int, col: Int): Int = background + override fun margin(row: Int, col: Int, qrCode: QRCode, qrCodeGraphics: QRCodeGraphics): Int = background } diff --git a/src/commonMain/kotlin/qrcode/color/LinearGradientColorFunction.kt b/src/commonMain/kotlin/qrcode/color/LinearGradientColorFunction.kt new file mode 100644 index 0000000..17066d9 --- /dev/null +++ b/src/commonMain/kotlin/qrcode/color/LinearGradientColorFunction.kt @@ -0,0 +1,44 @@ +package qrcode.color + +import qrcode.QRCode +import qrcode.render.QRCodeGraphics +import kotlin.js.ExperimentalJsExport +import kotlin.js.JsExport +import kotlin.jvm.JvmOverloads +import kotlin.math.roundToInt + +@JsExport +@OptIn(ExperimentalJsExport::class) +@Suppress("MemberVisibilityCanBePrivate") +class LinearGradientColorFunction @JvmOverloads constructor( + val startForegroundColor: Int, + val endForegroundColor: Int, + val backgroundColor: Int = Colors.WHITE, + var vertical: Boolean = true, +) : QRCodeColorFunction { + private val startComponents = Colors.getRGBA(startForegroundColor) + private val endComponents = Colors.getRGBA(endForegroundColor) + + override fun fg(row: Int, col: Int, qrCode: QRCode, qrCodeGraphics: QRCodeGraphics): Int { + val pct: Double = if (vertical) { + row + } else { + col + } / qrCode.rawData.size.toDouble() + + val r = startComponents[0] * (1 - pct) + endComponents[0] * pct + val g = startComponents[1] * (1 - pct) + endComponents[1] * pct + val b = startComponents[2] * (1 - pct) + endComponents[2] * pct + + return Colors.rgba( + r.roundToInt().coerceIn(0..255), + g.roundToInt().coerceIn(0..255), + b.roundToInt().coerceIn(0..255), + 255, + ) + } + + override fun bg(row: Int, col: Int, qrCode: QRCode, qrCodeGraphics: QRCodeGraphics): Int = backgroundColor + + override fun margin(row: Int, col: Int, qrCode: QRCode, qrCodeGraphics: QRCodeGraphics): Int = backgroundColor +} diff --git a/src/commonMain/kotlin/qrcode/color/QRCodeColorFunction.kt b/src/commonMain/kotlin/qrcode/color/QRCodeColorFunction.kt index 49f0802..0d0c6d4 100644 --- a/src/commonMain/kotlin/qrcode/color/QRCodeColorFunction.kt +++ b/src/commonMain/kotlin/qrcode/color/QRCodeColorFunction.kt @@ -14,12 +14,12 @@ import kotlin.js.JsExport @JsExport @OptIn(ExperimentalJsExport::class) interface QRCodeColorFunction { - fun colorFn(square: QRCodeSquare): Int = + fun colorFn(square: QRCodeSquare, qrCode: QRCode, qrCodeGraphics: QRCodeGraphics): Int = when (square.squareInfo.type) { - MARGIN -> margin(square.row, square.col) + MARGIN -> margin(square.row, square.col, qrCode, qrCodeGraphics) else -> when (square.dark) { - true -> fg(square.row, square.col) - else -> bg(square.row, square.col) + true -> fg(square.row, square.col, qrCode, qrCodeGraphics) + else -> bg(square.row, square.col, qrCode, qrCodeGraphics) } } @@ -33,15 +33,15 @@ interface QRCodeColorFunction { /** * What is the foreground color */ - fun fg(row: Int, col: Int): Int + fun fg(row: Int, col: Int, qrCode: QRCode, qrCodeGraphics: QRCodeGraphics): Int /** * What is the background color */ - fun bg(row: Int, col: Int): Int + fun bg(row: Int, col: Int, qrCode: QRCode, qrCodeGraphics: QRCodeGraphics): Int /** * What is the margin color. Defaults to [bg] */ - fun margin(row: Int, col: Int): Int = bg(row, col) + fun margin(row: Int, col: Int, qrCode: QRCode, qrCodeGraphics: QRCodeGraphics): Int = bg(row, col, qrCode, qrCodeGraphics) } diff --git a/src/commonMain/kotlin/qrcode/shape/DefaultShapeFunction.kt b/src/commonMain/kotlin/qrcode/shape/DefaultShapeFunction.kt index 332530b..515c0e5 100644 --- a/src/commonMain/kotlin/qrcode/shape/DefaultShapeFunction.kt +++ b/src/commonMain/kotlin/qrcode/shape/DefaultShapeFunction.kt @@ -1,5 +1,6 @@ package qrcode.shape +import qrcode.QRCode import qrcode.color.QRCodeColorFunction import qrcode.internals.QRCodeSquare import qrcode.internals.QRCodeSquareType.POSITION_PROBE @@ -22,8 +23,12 @@ open class DefaultShapeFunction(val squareSize: Int = DEFAULT_CELL_SIZE, innerSp square: QRCodeSquare, squareCanvas: QRCodeGraphics, canvas: QRCodeGraphics, + qrCode: QRCode, ) { - squareCanvas.fill(colorFn.bg(square.row, square.col)) + val bg = colorFn.bg(square.row, square.col, qrCode, canvas) + val fg = colorFn.fg(square.row, square.col, qrCode, canvas) + + squareCanvas.fill(bg) if (square.dark) { fillRect( @@ -31,8 +36,8 @@ open class DefaultShapeFunction(val squareSize: Int = DEFAULT_CELL_SIZE, innerSp innerSpacing, squareSize - innerSpacing * 2, squareSize - innerSpacing * 2, - colorFn.fg(square.row, square.col), - squareCanvas + fg, + squareCanvas, ) } } @@ -41,17 +46,18 @@ open class DefaultShapeFunction(val squareSize: Int = DEFAULT_CELL_SIZE, innerSp colorFn: QRCodeColorFunction, square: QRCodeSquare, squareCanvas: QRCodeGraphics, - canvas: QRCodeGraphics + canvas: QRCodeGraphics, + qrCode: QRCode, ) { - val bg = colorFn.bg(square.row, square.col) - val fg = colorFn.fg(square.row, square.col) + val bg = colorFn.bg(square.row, square.col, qrCode, canvas) + val fg = colorFn.fg(square.row, square.col, qrCode, canvas) val size = squareSize * square.rowSize val startX = square.absoluteX(squareSize) val startY = square.absoluteY(squareSize) when (square.squareInfo.type) { POSITION_PROBE -> { - val margin = colorFn.margin(square.row, square.col) + val margin = colorFn.margin(square.row, square.col, qrCode, canvas) // Fill the area of the whole square canvas.fillRect(startX, startY, size + squareSize * 2, size + squareSize * 2, margin) @@ -64,7 +70,7 @@ open class DefaultShapeFunction(val squareSize: Int = DEFAULT_CELL_SIZE, innerSp size - innerSpacing * 2, fg, squareSize.toDouble(), - canvas + canvas, ) // Draw inner square @@ -74,7 +80,7 @@ open class DefaultShapeFunction(val squareSize: Int = DEFAULT_CELL_SIZE, innerSp size - squareSize * 4, size - squareSize * 4, fg, - canvas + canvas, ) } @@ -108,7 +114,7 @@ open class DefaultShapeFunction(val squareSize: Int = DEFAULT_CELL_SIZE, innerSp squareSize - innerSpacing * 2, squareSize - innerSpacing * 2, color, - canvas + canvas, ) } } diff --git a/src/commonMain/kotlin/qrcode/shape/QRCodeShapeFunction.kt b/src/commonMain/kotlin/qrcode/shape/QRCodeShapeFunction.kt index 511ad64..5284c81 100644 --- a/src/commonMain/kotlin/qrcode/shape/QRCodeShapeFunction.kt +++ b/src/commonMain/kotlin/qrcode/shape/QRCodeShapeFunction.kt @@ -28,6 +28,7 @@ interface QRCodeShapeFunction { square: QRCodeSquare, squareCanvas: QRCodeGraphics, canvas: QRCodeGraphics, + qrCode: QRCode, ) /** @@ -38,5 +39,6 @@ interface QRCodeShapeFunction { square: QRCodeSquare, squareCanvas: QRCodeGraphics, canvas: QRCodeGraphics, + qrCode: QRCode, ) } diff --git a/src/jvmMain/kotlin/qrcode/color/PaintColorFunction.kt b/src/jvmMain/kotlin/qrcode/color/PaintColorFunction.kt deleted file mode 100644 index 7cc1e87..0000000 --- a/src/jvmMain/kotlin/qrcode/color/PaintColorFunction.kt +++ /dev/null @@ -1,37 +0,0 @@ -package qrcode.color - -import qrcode.QRCode -import qrcode.render.QRCodeGraphics -import java.awt.GradientPaint -import java.awt.LinearGradientPaint -import java.awt.MultipleGradientPaint -import java.awt.Paint - -/** - * A custom color function that allows a [Paint] to be used for drawing instead of plain colors. - * - * @see Paint - * @see GradientPaint - * @see LinearGradientPaint - * @see MultipleGradientPaint - */ -open class PaintColorFunction( - val foregroundPaint: Paint, - val backgroundPaint: Paint, -) : DefaultColorFunction() { - override fun beforeRender(qrCode: QRCode, qrCodeGraphics: QRCodeGraphics) { - qrCodeGraphics.beforeRenderAction = { - it.paint = backgroundPaint - } - - qrCodeGraphics.fill(0) - - qrCodeGraphics.beforeRenderAction = { - it.paint = foregroundPaint - } - } - - override fun fg(row: Int, col: Int): Int = 0 - - override fun bg(row: Int, col: Int): Int = 0 -} diff --git a/src/jvmMain/kotlin/qrcode/render/QRCodeGraphics.kt b/src/jvmMain/kotlin/qrcode/render/QRCodeGraphics.kt index e1de214..cd9df26 100644 --- a/src/jvmMain/kotlin/qrcode/render/QRCodeGraphics.kt +++ b/src/jvmMain/kotlin/qrcode/render/QRCodeGraphics.kt @@ -2,7 +2,9 @@ package qrcode.render import java.awt.BasicStroke import java.awt.Color +import java.awt.GradientPaint import java.awt.Graphics2D +import java.awt.Paint import java.awt.RenderingHints.KEY_ANTIALIASING import java.awt.RenderingHints.VALUE_ANTIALIAS_ON import java.awt.image.BufferedImage @@ -20,6 +22,7 @@ actual open class QRCodeGraphics actual constructor( private lateinit var image: BufferedImage private val colorCache = HashMap() private var changed: Boolean = false + private var nextDrawPaint: Paint? = null var beforeRenderAction: ((Graphics2D) -> Unit)? = null @@ -45,15 +48,15 @@ actual open class QRCodeGraphics actual constructor( if (strokeThickness != null && strokeThickness > 0) { graphics.stroke = BasicStroke(strokeThickness.toFloat(), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND) } - graphics.paint = jdkColor graphics.color = jdkColor graphics.background = jdkColor + graphics.paint = nextDrawPaint ?: jdkColor graphics.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON) - beforeRenderAction?.invoke(graphics) action(graphics) graphics.dispose() + nextDrawPaint = null } /** Returns `true` if **any** drawing was performed */ @@ -252,4 +255,19 @@ actual open class QRCodeGraphics actual constructor( it.drawImage(image, x, y, null) } } + + /** + * Available so the user can implement their own, 100% custom drawing. + */ + fun directDraw(action: (Graphics2D) -> Unit) { + draw(0, null, action) + } + + /** + * An easy way to implement using [Paint]s like a [GradientPaint] for drawing something. + */ + fun usePaintForNextDraw(paint: Paint): QRCodeGraphics { + nextDrawPaint = paint + return this + } }