Skip to content

Commit

Permalink
Hello world!
Browse files Browse the repository at this point in the history
  • Loading branch information
ferhatwi committed May 28, 2022
1 parent 7a99830 commit ebeb84e
Show file tree
Hide file tree
Showing 16 changed files with 1,786 additions and 0 deletions.
7 changes: 7 additions & 0 deletions crop/callback/CropBoundsChangeListener.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.github.ferhatwi.crop.callback;

public interface CropBoundsChangeListener {

void onCropAspectRatioChanged(float cropRatio);

}
9 changes: 9 additions & 0 deletions crop/callback/OverlayViewChangeListener.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.github.ferhatwi.crop.callback;

import android.graphics.RectF;

public interface OverlayViewChangeListener {

void onCropRectUpdated(RectF cropRect);

}
19 changes: 19 additions & 0 deletions crop/model/Frame.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.github.ferhatwi.crop.model

import android.graphics.Paint
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb

data class Frame(
var visible: Boolean = true,
var edgePaint: Paint = Paint().apply {
style = Paint.Style.STROKE
color = Color.White.copy(alpha = 0.5f).toArgb()
strokeWidth = 1f
},
var cornerPaint: Paint = Paint().apply {
style = Paint.Style.STROKE
color = Color.White.copy(alpha = 0.5f).toArgb()
strokeWidth = 3f
}
)
16 changes: 16 additions & 0 deletions crop/model/Grid.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.github.ferhatwi.crop.model

import android.graphics.Paint
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb

data class Grid(
var visible: Boolean = true,
var row: Int = 3,
var column: Int = 3,
var paint: Paint = Paint().apply {
style = Paint.Style.STROKE
color = Color.White.copy(alpha = 0.5f).toArgb()
strokeWidth = 1f
}
)
10 changes: 10 additions & 0 deletions crop/model/ImageState.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.github.ferhatwi.crop.model

import android.graphics.RectF

data class ImageState(
val cropRect: RectF,
val imageRect: RectF,
var scale: Float,
val angle: Float
)
43 changes: 43 additions & 0 deletions crop/util/BitmapLoadUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.github.ferhatwi.crop.util

import android.content.res.Resources
import android.graphics.Canvas
import android.util.DisplayMetrics
import android.util.Log
import kotlin.math.min
import kotlin.math.pow
import kotlin.math.sqrt

private const val TAG = "BitmapLoadUtils"

object BitmapLoadUtils {

fun calculateMaxBitmapSize(): Int {
val displayMetrics: DisplayMetrics by lazy { Resources.getSystem().displayMetrics }
val size = displayMetrics.run { widthPixels to heightPixels }

val width = size.first
val height = size.second

// Twice the device screen diagonal as default
var maxBitmapSize =
sqrt(width.toDouble().pow(2) + height.toDouble().pow(2)).toInt()

// Check for max texture size via Canvas
val canvas = Canvas()
val maxCanvasSize = min(canvas.maximumBitmapWidth, canvas.maximumBitmapHeight)
if (maxCanvasSize > 0) {
maxBitmapSize = min(maxBitmapSize, maxCanvasSize)
}

// Check for max texture size via GL
val maxTextureSize = EglUtils.maxTextureSize
if (maxTextureSize > 0) {
maxBitmapSize = min(maxBitmapSize, maxTextureSize)
}
Log.d(TAG, "maxBitmapSize: $maxBitmapSize")
return maxBitmapSize
}

}

22 changes: 22 additions & 0 deletions crop/util/CubicEasing.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.github.ferhatwi.crop.util

object CubicEasing {
fun easeOut(time: Float, start: Float, end: Float, duration: Float): Float {
var timeX = time
return end * (((timeX / duration - 1.0f).also {
timeX = it
}) * timeX * timeX + 1.0f) + start
}

fun easeIn(time: Float, start: Float, end: Float, duration: Float): Float {
var timeX = time
return end * duration.let { timeX /= it; timeX } * timeX * timeX + start
}

fun easeInOut(time: Float, start: Float, end: Float, duration: Float): Float {
var timeX = time
return if ((duration / 2.0f).let { timeX /= it; timeX } < 1.0f) end / 2.0f * timeX * timeX * timeX + start else end / 2.0f * (2.0f.let { timeX -= it; timeX } * timeX * timeX + 2.0f) + start
}


}
8 changes: 8 additions & 0 deletions crop/util/Density.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.github.ferhatwi.crop.util

import android.view.View
import kotlin.math.roundToInt

internal fun View.toDp(int: Int) : Int = toDp(int.toFloat()).roundToInt()

fun View.toDp(float: Float) : Float = resources.displayMetrics.density*float
59 changes: 59 additions & 0 deletions crop/util/EglUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package io.github.ferhatwi.crop.util

import android.opengl.EGL14
import android.opengl.EGLConfig
import android.opengl.GLES20
import android.util.Log

private const val TAG = "EglUtils"

object EglUtils {
val maxTextureSize: Int
get() = kotlin.runCatching {
val dpy = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)
val vers = IntArray(2)
EGL14.eglInitialize(dpy, vers, 0, vers, 1)
val configAttr = intArrayOf(
EGL14.EGL_COLOR_BUFFER_TYPE, EGL14.EGL_RGB_BUFFER,
EGL14.EGL_LEVEL, 0,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT,
EGL14.EGL_NONE
)
val configs = arrayOfNulls<EGLConfig>(1)
val numConfig = IntArray(1)
EGL14.eglChooseConfig(
dpy, configAttr, 0,
configs, 0, 1, numConfig, 0
)
if (numConfig[0] == 0) {
return 0
}
val config = configs[0]
val surfAttr = intArrayOf(
EGL14.EGL_WIDTH, 64,
EGL14.EGL_HEIGHT, 64,
EGL14.EGL_NONE
)
val surf = EGL14.eglCreatePbufferSurface(dpy, config, surfAttr, 0)
val ctxAttrib = intArrayOf(
EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
EGL14.EGL_NONE
)
val ctx = EGL14.eglCreateContext(dpy, config, EGL14.EGL_NO_CONTEXT, ctxAttrib, 0)
EGL14.eglMakeCurrent(dpy, surf, surf, ctx)
val maxSize = IntArray(1)
GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxSize, 0)
EGL14.eglMakeCurrent(
dpy, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,
EGL14.EGL_NO_CONTEXT
)
EGL14.eglDestroySurface(dpy, surf)
EGL14.eglDestroyContext(dpy, ctx)
EGL14.eglTerminate(dpy)
maxSize[0]
}.getOrElse {
Log.d(TAG, "getMaxTextureSize: ", it)
0
}
}
87 changes: 87 additions & 0 deletions crop/util/RectUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package io.github.ferhatwi.crop.util

import android.graphics.RectF

object RectUtils {
/**
* Gets a float array of the 2D coordinates representing a rectangles
* corners.
* The order of the corners in the float array is:
* 0------->1
* ^ |
* | |
* | v
* 3<-------2
*
* @param r the rectangle to get the corners of
* @return the float array of corners (8 floats)
*/
fun getCornersFromRect(r: RectF): FloatArray {
return floatArrayOf(
r.left, r.top,
r.right, r.top,
r.right, r.bottom,
r.left, r.bottom
)
}

/**
* Gets a float array of two lengths representing a rectangles width and height
* The order of the corners in the input float array is:
* 0------->1
* ^ |
* | |
* | v
* 3<-------2
*
* @param corners the float array of corners (8 floats)
* @return the float array of width and height (2 floats)
*/
fun getRectSidesFromCorners(corners: FloatArray): FloatArray {
return floatArrayOf(
Math.sqrt(
Math.pow(
(corners[0] - corners[2]).toDouble(),
2.0
) + Math.pow((corners[1] - corners[3]).toDouble(), 2.0)
).toFloat(), Math.sqrt(
Math.pow(
(corners[2] - corners[4]).toDouble(),
2.0
) + Math.pow((corners[3] - corners[5]).toDouble(), 2.0)
).toFloat()
)
}

fun getCenterFromRect(r: RectF): FloatArray {
return floatArrayOf(r.centerX(), r.centerY())
}

/**
* Takes an array of 2D coordinates representing corners and returns the
* smallest rectangle containing those coordinates.
*
* @param array array of 2D coordinates
* @return smallest rectangle containing coordinates
*/
fun trapToRect(array: FloatArray): RectF {
val r = RectF(
Float.POSITIVE_INFINITY,
Float.POSITIVE_INFINITY,
Float.NEGATIVE_INFINITY,
Float.NEGATIVE_INFINITY
)
var i = 1
while (i < array.size) {
val x = Math.round(array[i - 1] * 10) / 10f
val y = Math.round(array[i] * 10) / 10f
r.left = Math.min(x, r.left)
r.top = Math.min(y, r.top)
r.right = Math.max(x, r.right)
r.bottom = Math.max(y, r.bottom)
i += 2
}
r.sort()
return r
}
}
99 changes: 99 additions & 0 deletions crop/util/RotationGestureDetector.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package io.github.ferhatwi.crop.util

import android.view.MotionEvent

class RotationGestureDetector(private val mListener: OnRotationGestureListener?) {
private var fX = 0f
private var fY = 0f
private var sX = 0f
private var sY = 0f
private var mPointerIndex1: Int
private var mPointerIndex2: Int
var angle = 0f
private set
private var mIsFirstTouch = false
fun onTouchEvent(event: MotionEvent): Boolean {
when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
sX = event.x
sY = event.y
mPointerIndex1 = event.findPointerIndex(event.getPointerId(0))
angle = 0f
mIsFirstTouch = true
}
MotionEvent.ACTION_POINTER_DOWN -> {
fX = event.x
fY = event.y
mPointerIndex2 = event.findPointerIndex(event.getPointerId(event.actionIndex))
angle = 0f
mIsFirstTouch = true
}
MotionEvent.ACTION_MOVE -> if (mPointerIndex1 != INVALID_POINTER_INDEX && mPointerIndex2 != INVALID_POINTER_INDEX && event.pointerCount > mPointerIndex2) {
val nfX: Float
val nfY: Float
val nsX: Float
val nsY: Float
nsX = event.getX(mPointerIndex1)
nsY = event.getY(mPointerIndex1)
nfX = event.getX(mPointerIndex2)
nfY = event.getY(mPointerIndex2)
if (mIsFirstTouch) {
angle = 0f
mIsFirstTouch = false
} else {
calculateAngleBetweenLines(fX, fY, sX, sY, nfX, nfY, nsX, nsY)
}
mListener?.onRotation(this)
fX = nfX
fY = nfY
sX = nsX
sY = nsY
}
MotionEvent.ACTION_UP -> mPointerIndex1 = INVALID_POINTER_INDEX
MotionEvent.ACTION_POINTER_UP -> mPointerIndex2 = INVALID_POINTER_INDEX
}
return true
}

private fun calculateAngleBetweenLines(
fx1: Float, fy1: Float, fx2: Float, fy2: Float,
sx1: Float, sy1: Float, sx2: Float, sy2: Float
): Float {
return calculateAngleDelta(
Math.toDegrees(
Math.atan2((fy1 - fy2).toDouble(), (fx1 - fx2).toDouble()).toFloat().toDouble()
).toFloat(), Math.toDegrees(
Math.atan2((sy1 - sy2).toDouble(), (sx1 - sx2).toDouble()).toFloat().toDouble()
).toFloat()
)
}

private fun calculateAngleDelta(angleFrom: Float, angleTo: Float): Float {
angle = angleTo % 360.0f - angleFrom % 360.0f
if (angle < -180.0f) {
angle += 360.0f
} else if (angle > 180.0f) {
angle -= 360.0f
}
return angle
}

open class SimpleOnRotationGestureListener : OnRotationGestureListener {
override fun onRotation(rotationDetector: RotationGestureDetector): Boolean {
return false
}
}

interface OnRotationGestureListener {
fun onRotation(rotationDetector: RotationGestureDetector): Boolean
}

companion object {
private const val INVALID_POINTER_INDEX = -1
}

init {
mPointerIndex1 = INVALID_POINTER_INDEX
mPointerIndex2 = INVALID_POINTER_INDEX
}
}
Loading

0 comments on commit ebeb84e

Please sign in to comment.