Skip to content

Commit

Permalink
[v4.0.0] 4.0.0 Preparation
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaellins-swile committed Nov 1, 2023
1 parent 7837b27 commit 26e6abb
Show file tree
Hide file tree
Showing 22 changed files with 249 additions and 95 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Spotless Apply
name: Run Tests

on:
push:
Expand All @@ -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
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ kotlin {
}
}

ios()
// ios()

sourceSets {
val commonTest by getting {
Expand Down
Binary file added examples/kotlin/example01-circles.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/kotlin/example01-custom-jvm.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/kotlin/example01-custom.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/kotlin/example01-rounded-squares.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/kotlin/example01-squares.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/kotlin/example02-color.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/kotlin/example02-dark-mode.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/kotlin/example02-gradient.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
97 changes: 97 additions & 0 deletions examples/kotlin/src/main/kotlin/Example01-Shapes.kt
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
37 changes: 37 additions & 0 deletions examples/kotlin/src/main/kotlin/Example02-Colors.kt
Original file line number Diff line number Diff line change
@@ -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)
}
11 changes: 0 additions & 11 deletions examples/kotlin/src/main/kotlin/HelloCircles.kt

This file was deleted.

12 changes: 0 additions & 12 deletions examples/kotlin/src/main/kotlin/HelloWorld.kt

This file was deleted.

18 changes: 16 additions & 2 deletions src/commonMain/kotlin/qrcode/QRCode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(),
Expand All @@ -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)
*
Expand All @@ -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 */
Expand Down Expand Up @@ -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
Expand Down
8 changes: 5 additions & 3 deletions src/commonMain/kotlin/qrcode/color/DefaultColorFunction.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package qrcode.color

import qrcode.QRCode
import qrcode.render.QRCodeGraphics
import kotlin.js.ExperimentalJsExport
import kotlin.js.JsExport

Expand All @@ -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
}
44 changes: 44 additions & 0 deletions src/commonMain/kotlin/qrcode/color/LinearGradientColorFunction.kt
Original file line number Diff line number Diff line change
@@ -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
}
14 changes: 7 additions & 7 deletions src/commonMain/kotlin/qrcode/color/QRCodeColorFunction.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand All @@ -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)
}
Loading

0 comments on commit 26e6abb

Please sign in to comment.