@@ -7,65 +7,151 @@ import android.graphics.Color
77import android.graphics.Paint
88import android.graphics.Path
99import android.graphics.RectF
10+ import android.util.TypedValue
1011import android.view.ContextThemeWrapper
1112import androidx.core.graphics.createBitmap
1213import com.google.android.material.color.MaterialColors
13- import io.nayuki.qrcodegen.QrCode
1414import me.alllexey123.itmowidgets.R
1515
1616class QrBitmapRenderer (
1717 private val context : Context
1818) {
1919
20- fun render (qrCode : QrCode , pixelsPerModule : Int , dynamic : Boolean ): Bitmap {
20+
21+ fun render (
22+ qrCode : List <List <Boolean >>,
23+ qrSidePixels : Int = defaultSidePixels(),
24+ relativePadding : Float = defaultRelativePadding(),
25+ bgRoundingPixels : Float = defaultBgRounding(),
26+ dynamic : Boolean
27+ ): Bitmap {
28+ val colors = getQrColors(dynamic )
29+ return render(
30+ qrCode,
31+ qrSidePixels,
32+ colors.first,
33+ colors.second,
34+ relativePadding,
35+ bgRoundingPixels
36+ )
37+ }
38+
39+ fun renderEmpty (
40+ qrSidePixels : Int = defaultSidePixels(),
41+ relativePadding : Float = defaultRelativePadding(),
42+ bgRoundingPixels : Float = defaultBgRounding(),
43+ dynamic : Boolean
44+ ): Bitmap {
2145 val colors = getQrColors(dynamic )
46+ return renderFilled(
47+ qrSidePixels,
48+ colors.first,
49+ colors.first,
50+ relativePadding,
51+ bgRoundingPixels
52+ )
53+ }
54+
55+ fun renderFull (
56+ qrSidePixels : Int = defaultSidePixels(),
57+ relativePadding : Float = defaultRelativePadding(),
58+ bgRoundingPixels : Float = defaultBgRounding(),
59+ dynamic : Boolean
60+ ): Bitmap {
61+ val colors = getQrColors(dynamic )
62+ return renderFilled(
63+ qrSidePixels,
64+ colors.first,
65+ colors.second,
66+ relativePadding,
67+ bgRoundingPixels
68+ )
69+ }
2270
71+ fun renderFilled (
72+ qrSidePixels : Int = defaultSidePixels(),
73+ backgroundColor : Int ,
74+ foregroundColor : Int ,
75+ relativePadding : Float = defaultRelativePadding(),
76+ bgRoundingPixels : Float = defaultBgRounding()
77+ ): Bitmap {
78+ val booleans = List (21 ) { MutableList (21 ) { true } }
79+ return render(
80+ booleans,
81+ qrSidePixels,
82+ backgroundColor,
83+ foregroundColor,
84+ relativePadding,
85+ bgRoundingPixels
86+ )
87+ }
88+
89+
90+ // padding: [0, 0.5]
91+ fun render (
92+ qrCode : List <List <Boolean >>,
93+ qrSidePixels : Int ,
94+ backgroundColor : Int ,
95+ foregroundColor : Int ,
96+ relativePadding : Float ,
97+ bgRoundingPixels : Float
98+ ): Bitmap {
2399 val qrSize = qrCode.size
24- val bitmap = createBitmap(qrSize * pixelsPerModule, qrSize * pixelsPerModule )
100+ val bitmap = createBitmap(qrSidePixels, qrSidePixels )
25101 val canvas = Canvas (bitmap)
26102
27- val bgColor = colors.first
28- val fgColor = colors.second
29- canvas.drawColor(bgColor)
30-
31103 val foregroundPaint = Paint ().apply {
32- color = fgColor
104+ color = foregroundColor
33105 isAntiAlias = true
34106 }
35107
36108 val backgroundPaint = Paint ().apply {
37- color = bgColor
109+ color = backgroundColor
38110 isAntiAlias = true
39111 }
40112
41- val cornerRadius = pixelsPerModule * 0.5f
113+ val moduleSidePixels = qrSidePixels * (1 - relativePadding * 2 ) / qrSize
114+ val moduleCornerRadius = moduleSidePixels * 0.5f
115+
116+ // draw rounded background
117+ val bg = Path ().apply {
118+ addRoundRect(
119+ RectF (0F , 0F , qrSidePixels.toFloat(), qrSidePixels.toFloat()),
120+ bgRoundingPixels,
121+ bgRoundingPixels,
122+ Path .Direction .CW
123+ )
124+ }
125+
126+ val paddingPixels = qrSidePixels * relativePadding
127+ canvas.drawPath(bg, backgroundPaint)
42128
43129 // draw the black modules
44130 for (x in 0 until qrSize) {
45131 for (y in 0 until qrSize) {
46- if (qrCode.getModule(x, y) ) {
47- val left = ( x * pixelsPerModule).toFloat()
48- val top = ( y * pixelsPerModule).toFloat()
49- val right = left + pixelsPerModule
50- val bottom = top + pixelsPerModule
132+ if (qrCode[x][y] ) {
133+ val left = x * moduleSidePixels + paddingPixels
134+ val top = y * moduleSidePixels + paddingPixels
135+ val right = left + moduleSidePixels
136+ val bottom = top + moduleSidePixels
51137
52- val topN = qrCode.getModule(x, y - 1 )
53- val bottomN = qrCode.getModule(x, y + 1 )
54- val leftN = qrCode.getModule (x - 1 , y)
55- val rightN = qrCode.getModule (x + 1 , y)
138+ val topN = qrCode.getOrNull(x)?.getOrNull( y - 1 ) == true
139+ val bottomN = qrCode.getOrNull(x)?.getOrNull( y + 1 ) == true
140+ val leftN = qrCode.getOrNull (x - 1 )?.getOrNull(y) == true
141+ val rightN = qrCode.getOrNull (x + 1 )?.getOrNull(y) == true
56142
57143 val radii = floatArrayOf(
58- if (! topN && ! leftN) cornerRadius else 0f ,
59- if (! topN && ! leftN) cornerRadius else 0f ,
144+ if (! topN && ! leftN) moduleCornerRadius else 0f ,
145+ if (! topN && ! leftN) moduleCornerRadius else 0f ,
60146
61- if (! topN && ! rightN) cornerRadius else 0f ,
62- if (! topN && ! rightN) cornerRadius else 0f ,
147+ if (! topN && ! rightN) moduleCornerRadius else 0f ,
148+ if (! topN && ! rightN) moduleCornerRadius else 0f ,
63149
64- if (! bottomN && ! rightN) cornerRadius else 0f ,
65- if (! bottomN && ! rightN) cornerRadius else 0f ,
150+ if (! bottomN && ! rightN) moduleCornerRadius else 0f ,
151+ if (! bottomN && ! rightN) moduleCornerRadius else 0f ,
66152
67- if (! bottomN && ! leftN) cornerRadius else 0f ,
68- if (! bottomN && ! leftN) cornerRadius else 0f
153+ if (! bottomN && ! leftN) moduleCornerRadius else 0f ,
154+ if (! bottomN && ! leftN) moduleCornerRadius else 0f
69155 )
70156
71157 val path = Path ().apply {
@@ -79,37 +165,37 @@ class QrBitmapRenderer(
79165 // redraw the white modules (round corners)
80166 for (x in 0 until qrSize) {
81167 for (y in 0 until qrSize) {
82- if (! qrCode.getModule(x, y) ) {
83- val x1 = ( x * pixelsPerModule).toFloat()
84- val y1 = ( y * pixelsPerModule).toFloat()
85- val x2 = x1 + pixelsPerModule
86- val y2 = y1 + pixelsPerModule
168+ if (! qrCode[x][y] ) {
169+ val x1 = x * moduleSidePixels + paddingPixels
170+ val y1 = y * moduleSidePixels + paddingPixels
171+ val x2 = x1 + moduleSidePixels
172+ val y2 = y1 + moduleSidePixels
87173 val rect = RectF (x1, y1, x2, y2)
88174
89175 var bottomRight = 0f
90176 var bottomLeft = 0f
91177 var topLeft = 0f
92178 var topRight = 0f
93179
94- val top = qrCode.getModule(x, y - 1 )
95- val right = qrCode.getModule (x + 1 , y)
96- val bottom = qrCode.getModule(x, y + 1 )
97- val left = qrCode.getModule (x - 1 , y)
180+ val top = qrCode.getOrNull(x)?.getOrNull( y - 1 ) == true
181+ val right = qrCode.getOrNull (x + 1 )?.getOrNull(y) == true
182+ val bottom = qrCode.getOrNull(x)?.getOrNull( y + 1 ) == true
183+ val left = qrCode.getOrNull (x - 1 )?.getOrNull(y) == true
98184
99- if (right && bottom && qrCode.getModule (x + 1 , y + 1 )) {
100- bottomRight = cornerRadius
185+ if (right && bottom && qrCode.getOrNull (x + 1 )?.getOrNull( y + 1 ) == true ) {
186+ bottomRight = moduleCornerRadius
101187 }
102188
103- if (left && bottom && qrCode.getModule (x - 1 , y + 1 )) {
104- bottomLeft = cornerRadius
189+ if (left && bottom && qrCode.getOrNull (x - 1 )?.getOrNull( y + 1 ) == true ) {
190+ bottomLeft = moduleCornerRadius
105191 }
106192
107- if (right && top && qrCode.getModule (x + 1 , y - 1 )) {
108- topRight = cornerRadius
193+ if (right && top && qrCode.getOrNull (x + 1 )?.getOrNull( y - 1 ) == true ) {
194+ topRight = moduleCornerRadius
109195 }
110196
111- if (left && top && qrCode.getModule (x - 1 , y - 1 )) {
112- topLeft = cornerRadius
197+ if (left && top && qrCode.getOrNull (x - 1 )?.getOrNull( y - 1 ) == true ) {
198+ topLeft = moduleCornerRadius
113199 }
114200
115201 canvas.drawRect(rect, foregroundPaint)
@@ -128,38 +214,24 @@ class QrBitmapRenderer(
128214 return bitmap
129215 }
130216
131- fun renderEmpty (side : Int , rounding : Float , dynamic : Boolean ): Bitmap {
132- val colors = getQrColors(dynamic )
133- return renderFilled(side, rounding, colors.first, colors.first)
217+ fun dpToPx (dp : Float ): Float {
218+ return TypedValue .applyDimension(
219+ TypedValue .COMPLEX_UNIT_DIP ,
220+ dp,
221+ context.resources.displayMetrics
222+ )
134223 }
135224
136- fun renderFull (side : Int , rounding : Float , dynamic : Boolean ): Bitmap {
137- val colors = getQrColors(dynamic )
138- return renderFilled(side, rounding, colors.first, colors.second)
225+ fun defaultBgRounding (): Float {
226+ return dpToPx(16F )
139227 }
140228
141- fun renderFilled (side : Int , rounding : Float , bgColor : Int , fillColor : Int ): Bitmap {
142- val bitmap = createBitmap(side, side, Bitmap .Config .RGB_565 )
143- val canvas = Canvas (bitmap)
144- canvas.drawColor(bgColor)
145-
146- val paint = Paint ().apply {
147- color = fillColor
148- isAntiAlias = true
149- }
150-
151- val path = Path ().apply {
152- addRoundRect(
153- RectF (0F , 0F , side.toFloat(), side.toFloat()),
154- rounding,
155- rounding,
156- Path .Direction .CW
157- )
158- }
159-
160- canvas.drawPath(path, paint)
229+ fun defaultSidePixels (): Int {
230+ return 420
231+ }
161232
162- return bitmap
233+ fun defaultRelativePadding (): Float {
234+ return 0.05F
163235 }
164236
165237 // [background, foreground]
0 commit comments