Skip to content

Commit ee3f681

Browse files
feat(ecl-auto): More documentation and "useCustom" methods for JVM+Android
1 parent cd62e0c commit ee3f681

File tree

2 files changed

+132
-31
lines changed

2 files changed

+132
-31
lines changed

src/androidMain/kotlin/qrcode/render/QRCodeGraphics.android.kt

+71-17
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,45 @@ actual open class QRCodeGraphics actual constructor(
2323
val height: Int,
2424
) {
2525
companion object {
26-
val AVAILABLE_FORMATS: Array<String> = CompressFormat.values().map { it.name }.toTypedArray()
26+
val AVAILABLE_FORMATS: Array<String> = CompressFormat.entries.map { it.name }.toTypedArray()
2727
}
2828

2929
protected fun createCanvas(image: Bitmap) = Canvas(image)
3030

31+
/** [Bitmap] being used for the drawing operations. */
3132
private var image: Bitmap = Bitmap.createBitmap(width, height, ARGB_8888)
33+
34+
/** [Canvas] that handles the presenting. */
3235
private var canvas: Canvas = createCanvas(image)
36+
37+
/** Cache of [Paint] objects being used. Just to try and use the least amount of CPU/memory possible. */
3338
private val paintCache = mutableMapOf<Int, Paint>()
39+
40+
/** Whether any drawing operations were done or not. */
3441
private var changed: Boolean = false
3542

43+
/** Whether the user set the value of [canvas]. */
44+
private var customCanvas = false
45+
private var customCanvasOffsetX: Int = 0
46+
private var customCanvasOffsetY: Int = 0
47+
48+
/**
49+
* Allows this object to draw directly to a user specified [Canvas].
50+
*
51+
* It'll invoke [Canvas.setBitmap] with the [Bitmap] being used to draw.
52+
*
53+
* @see image
54+
*/
55+
fun useCustomCanvas(canvas: Canvas, offsetX: Int = 0, offsetY: Int = 0): QRCodeGraphics {
56+
customCanvas = true
57+
this.canvas = canvas
58+
this.canvas.setBitmap(image)
59+
customCanvasOffsetX = offsetX
60+
customCanvasOffsetY = offsetY
61+
62+
return this
63+
}
64+
3665
/**
3766
* Keeps a simple color cache. The default style is [FILL].
3867
*/
@@ -48,8 +77,13 @@ actual open class QRCodeGraphics actual constructor(
4877
}
4978
}
5079

51-
protected fun rect(x: Int, y: Int, w: Int, h: Int): RectF =
52-
RectF(x.toFloat(), y.toFloat(), (x + w).toFloat(), (y + h).toFloat())
80+
protected fun buildRectF(x: Int, y: Int, w: Int, h: Int): RectF =
81+
RectF(
82+
(x + customCanvasOffsetX).toFloat(),
83+
(y + customCanvasOffsetY).toFloat(),
84+
(x + w + customCanvasOffsetX).toFloat(),
85+
(y + h + customCanvasOffsetY).toFloat(),
86+
)
5387

5488
/** Returns `true` if **any** drawing was performed */
5589
actual open fun changed() = changed
@@ -59,7 +93,12 @@ actual open class QRCodeGraphics actual constructor(
5993
if (changed) {
6094
changed = false
6195
image = Bitmap.createBitmap(width, height, ARGB_8888)
62-
canvas = createCanvas(image)
96+
97+
if (!customCanvas) {
98+
canvas = createCanvas(image)
99+
} else {
100+
this.canvas.setBitmap(image)
101+
}
63102
}
64103
}
65104

@@ -106,7 +145,7 @@ actual open class QRCodeGraphics actual constructor(
106145
private fun toCompressFormat(format: String) =
107146
try {
108147
CompressFormat.valueOf(format.uppercase())
109-
} catch (e: Throwable) {
148+
} catch (_: Throwable) {
110149
PNG
111150
}
112151

@@ -116,7 +155,6 @@ actual open class QRCodeGraphics actual constructor(
116155
* @see CompressFormat
117156
* @see CompressFormat.PNG
118157
* @see CompressFormat.JPEG
119-
* @see CompressFormat.WEBP
120158
*/
121159
actual open fun availableFormats(): Array<String> = AVAILABLE_FORMATS
122160

@@ -126,24 +164,37 @@ actual open class QRCodeGraphics actual constructor(
126164
/** Draw a straight line from point `(x1,y1)` to `(x2,y2)`. */
127165
actual open fun drawLine(x1: Int, y1: Int, x2: Int, y2: Int, color: Int, thickness: Double) {
128166
canvas.drawLine(
129-
x1.toFloat(),
130-
y1.toFloat(),
131-
x2.toFloat(),
132-
y2.toFloat(),
167+
(x1 + customCanvasOffsetX).toFloat(),
168+
(y1 + customCanvasOffsetY).toFloat(),
169+
(x2 + customCanvasOffsetX).toFloat(),
170+
(y2 + customCanvasOffsetY).toFloat(),
133171
paintFromCache(color, STROKE, thickness),
134172
)
135173
}
136174

137175
/** Draw the edges of a rectangle starting at point `(x,y)` and having `width` by `height`. */
138176
actual open fun drawRect(x: Int, y: Int, width: Int, height: Int, color: Int, thickness: Double) {
139177
val halfThickness = (thickness / 2.0).roundToInt()
140-
val rect = Rect(x + halfThickness, y + halfThickness, x + width - halfThickness, y + height - halfThickness)
178+
val rect = Rect(
179+
x + halfThickness + customCanvasOffsetX,
180+
y + halfThickness + customCanvasOffsetY,
181+
x + width - halfThickness + customCanvasOffsetX,
182+
y + height - halfThickness + customCanvasOffsetY,
183+
)
141184
canvas.drawRect(rect, paintFromCache(color, STROKE, thickness))
142185
}
143186

144187
/** Fills the rectangle starting at point `(x,y)` and having `width` by `height`. */
145188
actual open fun fillRect(x: Int, y: Int, width: Int, height: Int, color: Int) {
146-
canvas.drawRect(Rect(x, y, x + width, y + height), paintFromCache(color))
189+
canvas.drawRect(
190+
Rect(
191+
x + customCanvasOffsetX,
192+
y + customCanvasOffsetY,
193+
x + width + customCanvasOffsetX,
194+
y + height + customCanvasOffsetY,
195+
),
196+
paintFromCache(color),
197+
)
147198
}
148199

149200
/** Fill the whole area of this canvas with the specified [color]. */
@@ -184,7 +235,7 @@ actual open class QRCodeGraphics actual constructor(
184235
val halfThickness = (thickness / 2.0).roundToInt()
185236

186237
canvas.drawRoundRect(
187-
rect(x + halfThickness, y + halfThickness, width - halfThickness * 2, height - halfThickness * 2),
238+
buildRectF(x + halfThickness, y + halfThickness, width - halfThickness * 2, height - halfThickness * 2),
188239
borderRadius.toFloat(),
189240
borderRadius.toFloat(),
190241
paintFromCache(color, STROKE, thickness),
@@ -214,7 +265,7 @@ actual open class QRCodeGraphics actual constructor(
214265
*/
215266
actual open fun fillRoundRect(x: Int, y: Int, width: Int, height: Int, borderRadius: Int, color: Int) {
216267
canvas.drawRoundRect(
217-
rect(x, y, width, height),
268+
buildRectF(x, y, width, height),
218269
borderRadius.toFloat(),
219270
borderRadius.toFloat(),
220271
paintFromCache(color),
@@ -226,7 +277,7 @@ actual open class QRCodeGraphics actual constructor(
226277
*/
227278
actual fun drawEllipse(x: Int, y: Int, width: Int, height: Int, color: Int, thickness: Double) {
228279
canvas.drawOval(
229-
rect(x, y, width, height),
280+
buildRectF(x, y, width, height),
230281
paintFromCache(color, STROKE, thickness),
231282
)
232283
}
@@ -237,7 +288,7 @@ actual open class QRCodeGraphics actual constructor(
237288
*/
238289
actual fun fillEllipse(x: Int, y: Int, width: Int, height: Int, color: Int) {
239290
canvas.drawOval(
240-
rect(x, y, width, height),
291+
buildRectF(x, y, width, height),
241292
paintFromCache(color),
242293
)
243294
}
@@ -246,12 +297,15 @@ actual open class QRCodeGraphics actual constructor(
246297
* Reads the specified image from [rawData] and draws it at `(x,y)`
247298
*/
248299
actual fun drawImage(rawData: ByteArray?, x: Int, y: Int) {
249-
if (rawData != null && rawData.isNotEmpty()) {
300+
if (rawData != null && rawData.isNotEmpty()) { // NOSONAR
250301
val imgBitmap = BitmapFactory.decodeByteArray(rawData, 0, rawData.size)
251302
drawImage(imgBitmap, x, y)
252303
}
253304
}
254305

306+
/**
307+
* Draws a [Bitmap] at the specified `(x,y)`
308+
*/
255309
open fun drawImage(img: Bitmap, x: Int, y: Int) {
256310
changed = true
257311
canvas.drawBitmap(img, x.toFloat(), y.toFloat(), null)

src/jvmMain/kotlin/qrcode/render/QRCodeGraphics.jvm.kt

+61-14
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,41 @@ actual open class QRCodeGraphics actual constructor(
2121
private lateinit var image: BufferedImage
2222
private val colorCache = HashMap<Int, Color>()
2323
private var changed: Boolean = false
24+
private var customImage = false
25+
private var customImageOffsetX: Int = 0
26+
private var customImageOffsetY: Int = 0
2427

28+
/**
29+
* Use a custom, user-defined [BufferedImage] instead of the internal, default one.
30+
*
31+
* After calling this method, all drawing operations will be offset by [offsetX], [offsetY] as to make the QRCode
32+
* look like it was drawn at that position within the image.
33+
*/
34+
fun useCustomBufferedImage(bufferedImage: BufferedImage, offsetX: Int = 0, offsetY: Int = 0): QRCodeGraphics {
35+
customImage = true
36+
image = bufferedImage
37+
customImageOffsetX = offsetX
38+
customImageOffsetY = offsetY
39+
40+
return this
41+
}
42+
43+
/**
44+
* Creates a [BufferedImage] if needed. Does nothing if [customImage] is `true`.
45+
*/
2546
protected open fun createImage(force: Boolean = false): BufferedImage {
26-
if (force || !this::image.isInitialized) {
47+
if (!customImage && (force || !this::image.isInitialized)) {
2748
image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
49+
customImageOffsetX = 0
50+
customImageOffsetY = 0
2851
}
2952

3053
return image
3154
}
3255

56+
/**
57+
* Create instances of [Graphics2D] so that drawing operations can be done.
58+
*/
3359
protected open fun createGraphics(): Graphics2D = createImage().createGraphics()
3460

3561
/**
@@ -114,25 +140,37 @@ actual open class QRCodeGraphics actual constructor(
114140

115141
/** Draw a straight line from point `(x1,y1)` to `(x2,y2)`. */
116142
actual open fun drawLine(x1: Int, y1: Int, x2: Int, y2: Int, color: Int, thickness: Double) {
117-
draw(color, thickness) { it.drawLine(x1, y1, x2, y2) }
143+
draw(color, thickness) {
144+
it.drawLine(
145+
x1 + customImageOffsetX,
146+
y1 + customImageOffsetY,
147+
x2 + customImageOffsetX,
148+
y2 + customImageOffsetY,
149+
)
150+
}
118151
}
119152

120153
/** Draw the edges of a rectangle starting at point `(x,y)` and having `width` by `height`. */
121154
actual open fun drawRect(x: Int, y: Int, width: Int, height: Int, color: Int, thickness: Double) {
122155
draw(color, thickness) {
123156
val halfThickness = (thickness / 2.0).roundToInt().coerceAtLeast(0)
124-
it.drawRect(x + halfThickness, y + halfThickness, width - halfThickness * 2, height - halfThickness * 2)
157+
it.drawRect(
158+
x + halfThickness + customImageOffsetX,
159+
y + halfThickness + customImageOffsetY,
160+
width - halfThickness * 2,
161+
height - halfThickness * 2,
162+
)
125163
}
126164
}
127165

128166
/** Fills the rectangle starting at point `(x,y)` and having `width` by `height`. */
129167
actual open fun fillRect(x: Int, y: Int, width: Int, height: Int, color: Int) {
130-
draw(color) { it.fillRect(x, y, width, height) }
168+
draw(color) { it.fillRect(x + customImageOffsetX, y + customImageOffsetY, width, height) }
131169
}
132170

133171
/** Fill the whole area of this canvas with the specified [color]. */
134172
actual open fun fill(color: Int) {
135-
fillRect(0, 0, width, height, color)
173+
fillRect(0 + customImageOffsetX, 0 + customImageOffsetY, width, height, color)
136174
}
137175

138176
/**
@@ -168,8 +206,8 @@ actual open class QRCodeGraphics actual constructor(
168206
draw(color, thickness) {
169207
val halfThickness = (thickness / 2.0).roundToInt().coerceAtLeast(0)
170208
it.drawRoundRect(
171-
x + halfThickness,
172-
y + halfThickness,
209+
x + halfThickness + customImageOffsetX,
210+
y + halfThickness + customImageOffsetY,
173211
width - halfThickness * 2,
174212
height - halfThickness * 2,
175213
borderRadius,
@@ -200,7 +238,16 @@ actual open class QRCodeGraphics actual constructor(
200238
*
201239
*/
202240
actual open fun fillRoundRect(x: Int, y: Int, width: Int, height: Int, borderRadius: Int, color: Int) {
203-
draw(color) { it.fillRoundRect(x, y, width, height, borderRadius, borderRadius) }
241+
draw(color) {
242+
it.fillRoundRect(
243+
x + customImageOffsetX,
244+
y + customImageOffsetY,
245+
width,
246+
height,
247+
borderRadius,
248+
borderRadius,
249+
)
250+
}
204251
}
205252

206253
/**
@@ -211,8 +258,8 @@ actual open class QRCodeGraphics actual constructor(
211258
val halfThickness = (thickness / 2.0).roundToInt().coerceAtLeast(0)
212259
// The docs say the dimensions are width+1 and height+1... why? because f.u.
213260
it.drawOval(
214-
x + halfThickness,
215-
y + halfThickness,
261+
x + halfThickness + customImageOffsetX,
262+
y + halfThickness + customImageOffsetY,
216263
width - 1 - halfThickness * 2,
217264
height - 1 - halfThickness * 2,
218265
)
@@ -225,18 +272,18 @@ actual open class QRCodeGraphics actual constructor(
225272
*/
226273
actual open fun fillEllipse(x: Int, y: Int, width: Int, height: Int, color: Int) {
227274
draw(color) {
228-
it.fillOval(x, y, width, height)
275+
it.fillOval(x + customImageOffsetX, y + customImageOffsetY, width, height)
229276
}
230277
}
231278

232279
/**
233280
* Reads the specified image from [rawData] and draws it at `(x,y)`
234281
*/
235282
actual open fun drawImage(rawData: ByteArray?, x: Int, y: Int) {
236-
if (rawData != null && rawData.isNotEmpty()) {
283+
if (rawData != null && rawData.isNotEmpty()) { // NOSONAR
237284
draw(0) {
238285
ByteArrayInputStream(rawData).use { inStream ->
239-
drawImage(ImageIO.read(inStream), x, y)
286+
drawImage(ImageIO.read(inStream), x + customImageOffsetX, y + customImageOffsetY)
240287
}
241288
}
242289
}
@@ -245,7 +292,7 @@ actual open class QRCodeGraphics actual constructor(
245292
open fun drawImage(image: BufferedImage?, x: Int, y: Int) {
246293
if (image != null) {
247294
draw(0) {
248-
it.drawImage(image, x, y, null)
295+
it.drawImage(image, x + customImageOffsetX, y + customImageOffsetY, null)
249296
}
250297
}
251298
}

0 commit comments

Comments
 (0)