Skip to content

Commit 43e1371

Browse files
PAINTROID-687 Improve Zoom Window performance
1 parent a6d9041 commit 43e1371

File tree

1 file changed

+62
-145
lines changed

1 file changed

+62
-145
lines changed

Paintroid/src/main/java/org/catrobat/paintroid/ui/zoomwindow/DefaultZoomWindowController.kt

+62-145
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,11 @@
11
package org.catrobat.paintroid.ui.zoomwindow
22

3+
import android.graphics.PointF
34
import android.graphics.Bitmap
4-
import android.graphics.Color
5-
import android.graphics.Paint
65
import android.graphics.Rect
7-
import android.graphics.PorterDuffXfermode
8-
import android.graphics.PorterDuff
9-
import android.graphics.BitmapFactory
10-
import android.graphics.BitmapShader
116
import android.graphics.Canvas
12-
import android.graphics.Shader
13-
import android.graphics.Matrix
14-
import android.graphics.PointF
15-
import android.graphics.RectF
7+
import android.graphics.Paint
8+
import android.graphics.Path
169
import android.view.View
1710
import android.widget.ImageView
1811
import android.widget.RelativeLayout
@@ -24,7 +17,6 @@ import org.catrobat.paintroid.tools.Tool
2417
import org.catrobat.paintroid.tools.ToolReference
2518
import org.catrobat.paintroid.tools.ToolType
2619
import org.catrobat.paintroid.tools.Workspace
27-
import kotlin.math.roundToInt
2820

2921
class DefaultZoomWindowController
3022
(val activity: MainActivity,
@@ -34,82 +26,64 @@ class DefaultZoomWindowController
3426
val sharedPreferences: UserPreferences) :
3527
ZoomWindowController {
3628

37-
private val canvasRect = Rect()
38-
private val checkeredPattern = Paint()
39-
private val framePaint = Paint()
40-
29+
private val zoomWindow: RelativeLayout = activity.findViewById(R.id.pocketpaint_zoom_window)
30+
private val zoomWindowImage: ImageView = activity.findViewById(R.id.pocketpaint_zoom_window_image)
31+
private var coordinates: PointF? = null
4132
private val zoomWindowDiameter = activity.resources.getDimensionPixelSize(R.dimen.pocketpaint_zoom_window_diameter)
33+
private var zoomWindowBitmap: Bitmap? = null
4234

43-
private val chequeredBackgroundBitmap =
44-
Bitmap.createBitmap(layerModel.width, layerModel.height, Bitmap.Config.ARGB_8888)
45-
46-
private val greyBackgroundBitmap =
47-
Bitmap.createBitmap(
48-
layerModel.width + zoomWindowDiameter,
49-
layerModel.height + zoomWindowDiameter,
50-
Bitmap.Config.ARGB_8888
51-
)
52-
53-
private val backgroundBitmap =
54-
Bitmap.createBitmap(
55-
layerModel.width + zoomWindowDiameter,
56-
layerModel.height + zoomWindowDiameter,
57-
Bitmap.Config.ARGB_8888
58-
)
59-
60-
init {
61-
framePaint.color = Color.BLACK
62-
framePaint.style = Paint.Style.STROKE
63-
framePaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC)
64-
val checkerboard =
65-
BitmapFactory.decodeResource(activity.resources, R.drawable.pocketpaint_checkeredbg)
66-
val shader = BitmapShader(checkerboard, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT)
67-
checkeredPattern.shader = shader
68-
checkeredPattern.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC)
69-
70-
val backgroundCanvas: Canvas? = chequeredBackgroundBitmap?.let { Canvas(it) }
71-
72-
canvasRect.set(0, 0, layerModel.width, layerModel.height)
73-
74-
backgroundCanvas?.drawRect(canvasRect, checkeredPattern)
75-
backgroundCanvas?.drawRect(canvasRect, framePaint)
76-
77-
val greyBackgroundCanvas = Canvas(greyBackgroundBitmap)
78-
greyBackgroundCanvas.drawColor(
79-
activity.resources.getColor(R.color.pocketpaint_main_drawing_surface_background)
80-
)
81-
82-
val canvasBackground = Canvas(backgroundBitmap)
83-
84-
canvasBackground.drawBitmap(greyBackgroundBitmap, Matrix(), null)
85-
canvasBackground.drawBitmap(
86-
chequeredBackgroundBitmap, zoomWindowDiameter / 2f, zoomWindowDiameter / 2f, null)
35+
override fun setBitmap(bitmap: Bitmap?) {
36+
zoomWindowImage.setImageBitmap(coordinates?.let { cropBitmap(bitmap, it) })
37+
zoomWindowBitmap = bitmap
8738
}
8839

89-
private val zoomWindow: RelativeLayout =
90-
activity.findViewById(R.id.pocketpaint_zoom_window)
91-
private val zoomWindowImage: ImageView =
92-
activity.findViewById(R.id.pocketpaint_zoom_window_image)
93-
private var zoomWindowBitmap: Bitmap? = null
94-
private var coordinates: PointF? = null
40+
override fun getBitmap(): Bitmap? = zoomWindowBitmap
41+
42+
private fun cropBitmap(bitmap: Bitmap?, coordinates: PointF): Bitmap? {
43+
if (bitmap == null) return null
44+
45+
val radius = getSizeOfZoomWindow() / 2
46+
val startX: Int = (coordinates.x - radius).toInt()
47+
val startY: Int = (coordinates.y - radius).toInt()
48+
49+
val croppedBitmap: Bitmap = Bitmap.createBitmap(radius * 2, radius * 2, Bitmap.Config.ARGB_8888)
50+
val canvas = Canvas(croppedBitmap)
51+
52+
val paint = Paint().apply {
53+
isAntiAlias = true
54+
}
55+
56+
val path = Path().apply {
57+
addCircle(radius.toFloat(), radius.toFloat(), radius.toFloat(), Path.Direction.CW)
58+
}
59+
60+
canvas.clipPath(path)
61+
62+
bitmap.let {
63+
canvas.drawBitmap(
64+
it,
65+
Rect(startX, startY, startX + radius * 2, startY + radius * 2),
66+
Rect(0, 0, radius * 2, radius * 2),
67+
paint
68+
)
69+
}
70+
71+
return croppedBitmap
72+
}
73+
74+
private fun getSizeOfZoomWindow(): Int {
75+
val zoomIndex = (sharedPreferences.preferenceZoomWindowZoomPercentage - initialZoomValue) / zoomPercentStepValue
76+
return (zoomWindowDiameter - zoomIndex * zoomFactor)
77+
}
9578

9679
override fun show(drawingSurfaceCoordinates: PointF, displayCoordinates: PointF) {
9780
if (checkIfToolCompatibleWithZoomWindow(toolReference.tool) == Constants.COMPATIBLE &&
9881
isPointOnCanvas(drawingSurfaceCoordinates.x, drawingSurfaceCoordinates.y)) {
9982
setZoomWindowPosition(displayCoordinates)
10083
zoomWindow.visibility = View.VISIBLE
101-
zoomWindowImage.setImageBitmap(cropBitmap(workspace.bitmapOfAllLayers, drawingSurfaceCoordinates))
10284
}
10385
}
10486

105-
override fun dismiss() {
106-
zoomWindow.visibility = View.GONE
107-
}
108-
109-
override fun dismissOnPinch() {
110-
zoomWindow.visibility = View.GONE
111-
}
112-
11387
override fun onMove(drawingSurfaceCoordinates: PointF, displayCoordinates: PointF) {
11488
if (checkIfToolCompatibleWithZoomWindow(toolReference.tool) == Constants.COMPATIBLE) {
11589
setZoomWindowPosition(displayCoordinates)
@@ -124,6 +98,17 @@ class DefaultZoomWindowController
12498
}
12599
}
126100

101+
private fun isPointOnCanvas(pointX: Float, pointY: Float): Boolean =
102+
pointX > 0 && pointX < layerModel.width && pointY > 0 && pointY < layerModel.height
103+
104+
override fun dismiss() {
105+
zoomWindow.visibility = View.GONE
106+
}
107+
108+
override fun dismissOnPinch() {
109+
zoomWindow.visibility = View.GONE
110+
}
111+
127112
private fun setZoomWindowPosition(displayCoordinates: PointF) {
128113
if (shouldBeInTheRight(coordinates = displayCoordinates)) {
129114
setLayoutAlignment(right = true)
@@ -132,23 +117,9 @@ class DefaultZoomWindowController
132117
}
133118
}
134119

135-
override fun setBitmap(bitmap: Bitmap?) {
136-
zoomWindowImage.setImageBitmap(coordinates?.let { cropBitmap(bitmap, it) })
137-
zoomWindowBitmap = bitmap
138-
}
139-
140-
override fun getBitmap(): Bitmap? = zoomWindowBitmap
141-
142-
private fun isPointOnCanvas(pointX: Float, pointY: Float): Boolean =
143-
pointX > 0 && pointX < layerModel.width && pointY > 0 && pointY < layerModel.height
144-
145-
private fun shouldBeInTheRight(coordinates: PointF): Boolean {
146-
if (coordinates.x < activity.resources.displayMetrics.widthPixels / 2 &&
147-
coordinates.y < activity.resources.displayMetrics.heightPixels / 2) {
148-
return true
149-
}
150-
return false
151-
}
120+
private fun shouldBeInTheRight(coordinates: PointF): Boolean =
121+
coordinates.x < activity.resources.displayMetrics.widthPixels / 2 &&
122+
coordinates.y < activity.resources.displayMetrics.heightPixels / 2
152123

153124
private fun setLayoutAlignment(right: Boolean) {
154125
val params: RelativeLayout.LayoutParams =
@@ -163,60 +134,6 @@ class DefaultZoomWindowController
163134
zoomWindowImage.layoutParams = params
164135
}
165136

166-
private fun cropBitmap(bitmap: Bitmap?, coordinates: PointF): Bitmap? {
167-
168-
val bitmapWithBackground: Bitmap? = mergeBackground(bitmap)
169-
170-
val startX: Int = coordinates.x.roundToInt() + zoomWindowDiameter / 2 - getSizeOfZoomWindow() / 2
171-
val startY: Int = coordinates.y.roundToInt() + zoomWindowDiameter / 2 - getSizeOfZoomWindow() / 2
172-
173-
val croppedBitmap: Bitmap? =
174-
Bitmap.createBitmap(getSizeOfZoomWindow(), getSizeOfZoomWindow(), Bitmap.Config.ARGB_8888)
175-
176-
val canvas: Canvas? = croppedBitmap?.let { Canvas(it) }
177-
178-
val paint = Paint()
179-
paint.isAntiAlias = true
180-
181-
val rect = Rect(0, 0, getSizeOfZoomWindow(), getSizeOfZoomWindow())
182-
val rectF = RectF(rect)
183-
184-
canvas?.drawOval(rectF, paint)
185-
186-
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
187-
188-
bitmapWithBackground?.let {
189-
canvas?.drawBitmap(it,
190-
Rect(startX, startY, startX + getSizeOfZoomWindow(), startY + getSizeOfZoomWindow()),
191-
rect,
192-
paint
193-
) }
194-
195-
return croppedBitmap
196-
}
197-
198-
private fun mergeBackground(bitmap: Bitmap?): Bitmap? {
199-
200-
val bitmapOverlay =
201-
Bitmap.createBitmap(
202-
layerModel.width + zoomWindowDiameter,
203-
layerModel.height + zoomWindowDiameter,
204-
Bitmap.Config.ARGB_8888
205-
)
206-
val canvas = Canvas(bitmapOverlay)
207-
208-
canvas.drawBitmap(backgroundBitmap, Matrix(), null)
209-
210-
bitmap?.let { canvas.drawBitmap(it, zoomWindowDiameter / 2f, zoomWindowDiameter / 2f, null) }
211-
212-
return bitmapOverlay
213-
}
214-
215-
private fun getSizeOfZoomWindow(): Int {
216-
val zoomIndex = (sharedPreferences.preferenceZoomWindowZoomPercentage - initialZoomValue) / zoomPercentStepValue
217-
return zoomWindowDiameter - zoomIndex * zoomFactor
218-
}
219-
220137
override fun checkIfToolCompatibleWithZoomWindow(tool: Tool?): Constants {
221138
return when (tool?.toolType?.name) {
222139
ToolType.LINE.name,

0 commit comments

Comments
 (0)