Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PAINTROID-687 Improve Zoom Window performance #1331

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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