Skip to content

Commit

Permalink
PAINTROID-687 Improve Zoom Window performance
Browse files Browse the repository at this point in the history
  • Loading branch information
khalid-nasralla committed Feb 8, 2024
1 parent a6d9041 commit 43e1371
Showing 1 changed file with 62 additions and 145 deletions.
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
package org.catrobat.paintroid.ui.zoomwindow

import android.graphics.PointF
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.PorterDuffXfermode
import android.graphics.PorterDuff
import android.graphics.BitmapFactory
import android.graphics.BitmapShader
import android.graphics.Canvas
import android.graphics.Shader
import android.graphics.Matrix
import android.graphics.PointF
import android.graphics.RectF
import android.graphics.Paint
import android.graphics.Path
import android.view.View
import android.widget.ImageView
import android.widget.RelativeLayout
Expand All @@ -24,7 +17,6 @@ import org.catrobat.paintroid.tools.Tool
import org.catrobat.paintroid.tools.ToolReference
import org.catrobat.paintroid.tools.ToolType
import org.catrobat.paintroid.tools.Workspace
import kotlin.math.roundToInt

class DefaultZoomWindowController
(val activity: MainActivity,
Expand All @@ -34,82 +26,64 @@ class DefaultZoomWindowController
val sharedPreferences: UserPreferences) :
ZoomWindowController {

private val canvasRect = Rect()
private val checkeredPattern = Paint()
private val framePaint = Paint()

private val zoomWindow: RelativeLayout = activity.findViewById(R.id.pocketpaint_zoom_window)
private val zoomWindowImage: ImageView = activity.findViewById(R.id.pocketpaint_zoom_window_image)
private var coordinates: PointF? = null
private val zoomWindowDiameter = activity.resources.getDimensionPixelSize(R.dimen.pocketpaint_zoom_window_diameter)
private var zoomWindowBitmap: Bitmap? = null

private val chequeredBackgroundBitmap =
Bitmap.createBitmap(layerModel.width, layerModel.height, Bitmap.Config.ARGB_8888)

private val greyBackgroundBitmap =
Bitmap.createBitmap(
layerModel.width + zoomWindowDiameter,
layerModel.height + zoomWindowDiameter,
Bitmap.Config.ARGB_8888
)

private val backgroundBitmap =
Bitmap.createBitmap(
layerModel.width + zoomWindowDiameter,
layerModel.height + zoomWindowDiameter,
Bitmap.Config.ARGB_8888
)

init {
framePaint.color = Color.BLACK
framePaint.style = Paint.Style.STROKE
framePaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC)
val checkerboard =
BitmapFactory.decodeResource(activity.resources, R.drawable.pocketpaint_checkeredbg)
val shader = BitmapShader(checkerboard, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT)
checkeredPattern.shader = shader
checkeredPattern.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC)

val backgroundCanvas: Canvas? = chequeredBackgroundBitmap?.let { Canvas(it) }

canvasRect.set(0, 0, layerModel.width, layerModel.height)

backgroundCanvas?.drawRect(canvasRect, checkeredPattern)
backgroundCanvas?.drawRect(canvasRect, framePaint)

val greyBackgroundCanvas = Canvas(greyBackgroundBitmap)
greyBackgroundCanvas.drawColor(
activity.resources.getColor(R.color.pocketpaint_main_drawing_surface_background)
)

val canvasBackground = Canvas(backgroundBitmap)

canvasBackground.drawBitmap(greyBackgroundBitmap, Matrix(), null)
canvasBackground.drawBitmap(
chequeredBackgroundBitmap, zoomWindowDiameter / 2f, zoomWindowDiameter / 2f, null)
override fun setBitmap(bitmap: Bitmap?) {
zoomWindowImage.setImageBitmap(coordinates?.let { cropBitmap(bitmap, it) })
zoomWindowBitmap = bitmap
}

private val zoomWindow: RelativeLayout =
activity.findViewById(R.id.pocketpaint_zoom_window)
private val zoomWindowImage: ImageView =
activity.findViewById(R.id.pocketpaint_zoom_window_image)
private var zoomWindowBitmap: Bitmap? = null
private var coordinates: PointF? = null
override fun getBitmap(): Bitmap? = zoomWindowBitmap

private fun cropBitmap(bitmap: Bitmap?, coordinates: PointF): Bitmap? {
if (bitmap == null) return null

val radius = getSizeOfZoomWindow() / 2
val startX: Int = (coordinates.x - radius).toInt()
val startY: Int = (coordinates.y - radius).toInt()

val croppedBitmap: Bitmap = Bitmap.createBitmap(radius * 2, radius * 2, Bitmap.Config.ARGB_8888)
val canvas = Canvas(croppedBitmap)

val paint = Paint().apply {
isAntiAlias = true
}

val path = Path().apply {
addCircle(radius.toFloat(), radius.toFloat(), radius.toFloat(), Path.Direction.CW)
}

canvas.clipPath(path)

bitmap.let {
canvas.drawBitmap(
it,
Rect(startX, startY, startX + radius * 2, startY + radius * 2),
Rect(0, 0, radius * 2, radius * 2),
paint
)
}

return croppedBitmap
}

private fun getSizeOfZoomWindow(): Int {
val zoomIndex = (sharedPreferences.preferenceZoomWindowZoomPercentage - initialZoomValue) / zoomPercentStepValue
return (zoomWindowDiameter - zoomIndex * zoomFactor)
}

override fun show(drawingSurfaceCoordinates: PointF, displayCoordinates: PointF) {
if (checkIfToolCompatibleWithZoomWindow(toolReference.tool) == Constants.COMPATIBLE &&
isPointOnCanvas(drawingSurfaceCoordinates.x, drawingSurfaceCoordinates.y)) {
setZoomWindowPosition(displayCoordinates)
zoomWindow.visibility = View.VISIBLE
zoomWindowImage.setImageBitmap(cropBitmap(workspace.bitmapOfAllLayers, drawingSurfaceCoordinates))
}
}

override fun dismiss() {
zoomWindow.visibility = View.GONE
}

override fun dismissOnPinch() {
zoomWindow.visibility = View.GONE
}

override fun onMove(drawingSurfaceCoordinates: PointF, displayCoordinates: PointF) {
if (checkIfToolCompatibleWithZoomWindow(toolReference.tool) == Constants.COMPATIBLE) {
setZoomWindowPosition(displayCoordinates)
Expand All @@ -124,6 +98,17 @@ class DefaultZoomWindowController
}
}

private fun isPointOnCanvas(pointX: Float, pointY: Float): Boolean =
pointX > 0 && pointX < layerModel.width && pointY > 0 && pointY < layerModel.height

override fun dismiss() {
zoomWindow.visibility = View.GONE
}

override fun dismissOnPinch() {
zoomWindow.visibility = View.GONE
}

private fun setZoomWindowPosition(displayCoordinates: PointF) {
if (shouldBeInTheRight(coordinates = displayCoordinates)) {
setLayoutAlignment(right = true)
Expand All @@ -132,23 +117,9 @@ class DefaultZoomWindowController
}
}

override fun setBitmap(bitmap: Bitmap?) {
zoomWindowImage.setImageBitmap(coordinates?.let { cropBitmap(bitmap, it) })
zoomWindowBitmap = bitmap
}

override fun getBitmap(): Bitmap? = zoomWindowBitmap

private fun isPointOnCanvas(pointX: Float, pointY: Float): Boolean =
pointX > 0 && pointX < layerModel.width && pointY > 0 && pointY < layerModel.height

private fun shouldBeInTheRight(coordinates: PointF): Boolean {
if (coordinates.x < activity.resources.displayMetrics.widthPixels / 2 &&
coordinates.y < activity.resources.displayMetrics.heightPixels / 2) {
return true
}
return false
}
private fun shouldBeInTheRight(coordinates: PointF): Boolean =
coordinates.x < activity.resources.displayMetrics.widthPixels / 2 &&
coordinates.y < activity.resources.displayMetrics.heightPixels / 2

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

private fun cropBitmap(bitmap: Bitmap?, coordinates: PointF): Bitmap? {

val bitmapWithBackground: Bitmap? = mergeBackground(bitmap)

val startX: Int = coordinates.x.roundToInt() + zoomWindowDiameter / 2 - getSizeOfZoomWindow() / 2
val startY: Int = coordinates.y.roundToInt() + zoomWindowDiameter / 2 - getSizeOfZoomWindow() / 2

val croppedBitmap: Bitmap? =
Bitmap.createBitmap(getSizeOfZoomWindow(), getSizeOfZoomWindow(), Bitmap.Config.ARGB_8888)

val canvas: Canvas? = croppedBitmap?.let { Canvas(it) }

val paint = Paint()
paint.isAntiAlias = true

val rect = Rect(0, 0, getSizeOfZoomWindow(), getSizeOfZoomWindow())
val rectF = RectF(rect)

canvas?.drawOval(rectF, paint)

paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)

bitmapWithBackground?.let {
canvas?.drawBitmap(it,
Rect(startX, startY, startX + getSizeOfZoomWindow(), startY + getSizeOfZoomWindow()),
rect,
paint
) }

return croppedBitmap
}

private fun mergeBackground(bitmap: Bitmap?): Bitmap? {

val bitmapOverlay =
Bitmap.createBitmap(
layerModel.width + zoomWindowDiameter,
layerModel.height + zoomWindowDiameter,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmapOverlay)

canvas.drawBitmap(backgroundBitmap, Matrix(), null)

bitmap?.let { canvas.drawBitmap(it, zoomWindowDiameter / 2f, zoomWindowDiameter / 2f, null) }

return bitmapOverlay
}

private fun getSizeOfZoomWindow(): Int {
val zoomIndex = (sharedPreferences.preferenceZoomWindowZoomPercentage - initialZoomValue) / zoomPercentStepValue
return zoomWindowDiameter - zoomIndex * zoomFactor
}

override fun checkIfToolCompatibleWithZoomWindow(tool: Tool?): Constants {
return when (tool?.toolType?.name) {
ToolType.LINE.name,
Expand Down

0 comments on commit 43e1371

Please sign in to comment.