221 lines
6.7 KiB
Kotlin
221 lines
6.7 KiB
Kotlin
/*
|
|
* Copyright 2022 Tarsin Norbin
|
|
*
|
|
* This file is part of EhViewer
|
|
*
|
|
* EhViewer is free software: you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* EhViewer is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with EhViewer.
|
|
* If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
package com.hippo.image
|
|
|
|
import android.content.Context
|
|
import android.content.res.Resources
|
|
import android.graphics.Bitmap
|
|
import android.graphics.Canvas
|
|
import android.graphics.ImageDecoder
|
|
import android.graphics.ImageDecoder.ALLOCATOR_DEFAULT
|
|
import android.graphics.ImageDecoder.ALLOCATOR_SOFTWARE
|
|
import android.graphics.ImageDecoder.DecodeException
|
|
import android.graphics.ImageDecoder.ImageInfo
|
|
import android.graphics.ImageDecoder.Source
|
|
import android.graphics.PixelFormat
|
|
import android.graphics.drawable.AnimatedImageDrawable
|
|
import android.graphics.drawable.BitmapDrawable
|
|
import android.graphics.drawable.Drawable
|
|
import androidx.core.graphics.drawable.toDrawable
|
|
import java.io.FileInputStream
|
|
import java.nio.ByteBuffer
|
|
import java.nio.channels.FileChannel
|
|
import kotlin.math.max
|
|
|
|
class Image private constructor(
|
|
source: Source?, drawable: Drawable? = null,
|
|
val hardware: Boolean = true,
|
|
val release: () -> Unit? = {}
|
|
) {
|
|
internal var mObtainedDrawable: Drawable?
|
|
private var mBitmap: Bitmap? = null
|
|
private var mCanvas: Canvas? = null
|
|
|
|
init {
|
|
mObtainedDrawable = null
|
|
source?.let {
|
|
mObtainedDrawable =
|
|
ImageDecoder.decodeDrawable(source) { decoder: ImageDecoder, info: ImageInfo, src: Source ->
|
|
decoder.allocator = if (hardware) ALLOCATOR_DEFAULT else ALLOCATOR_SOFTWARE
|
|
// Sadly we must use software memory since we need copy it to tile buffer, fuck glgallery
|
|
// Idk it will cause how much performance regression
|
|
|
|
decoder.setTargetSampleSize(
|
|
max(
|
|
max(
|
|
info.size.width / (2 * screenWidth),
|
|
info.size.height / (2 * screenHeight)
|
|
), 1
|
|
)
|
|
)
|
|
// Don't
|
|
} // Should we lazy decode it?
|
|
}
|
|
if (mObtainedDrawable == null) {
|
|
mObtainedDrawable = drawable!!
|
|
}
|
|
}
|
|
|
|
val animated = mObtainedDrawable is AnimatedImageDrawable
|
|
val width = mObtainedDrawable!!.intrinsicWidth
|
|
val height = mObtainedDrawable!!.intrinsicHeight
|
|
val isRecycled = mObtainedDrawable == null
|
|
|
|
var started = false
|
|
|
|
@Synchronized
|
|
fun recycle() {
|
|
if (mObtainedDrawable is AnimatedImageDrawable) {
|
|
(mObtainedDrawable as AnimatedImageDrawable?)?.stop()
|
|
}
|
|
if (mObtainedDrawable is BitmapDrawable) {
|
|
(mObtainedDrawable as BitmapDrawable?)?.bitmap?.recycle()
|
|
}
|
|
mObtainedDrawable?.callback = null
|
|
mObtainedDrawable = null
|
|
mCanvas = null
|
|
mBitmap?.recycle()
|
|
mBitmap = null
|
|
release()
|
|
}
|
|
|
|
private fun prepareBitmap() {
|
|
if (mBitmap != null) return
|
|
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
|
mCanvas = Canvas(mBitmap!!)
|
|
}
|
|
|
|
private fun updateBitmap() {
|
|
prepareBitmap()
|
|
mObtainedDrawable!!.draw(mCanvas!!)
|
|
}
|
|
|
|
fun render(
|
|
srcX: Int, srcY: Int, dst: Bitmap, dstX: Int, dstY: Int,
|
|
width: Int, height: Int
|
|
) {
|
|
check(!hardware) { "Hardware buffer cannot be used in glgallery" }
|
|
val bitmap: Bitmap = if (animated) {
|
|
updateBitmap()
|
|
mBitmap!!
|
|
} else {
|
|
(mObtainedDrawable as BitmapDrawable).bitmap
|
|
}
|
|
nativeRender(
|
|
bitmap,
|
|
srcX,
|
|
srcY,
|
|
dst,
|
|
dstX,
|
|
dstY,
|
|
width,
|
|
height
|
|
)
|
|
}
|
|
|
|
fun texImage(init: Boolean, offsetX: Int, offsetY: Int, width: Int, height: Int) {
|
|
check(!hardware) { "Hardware buffer cannot be used in glgallery" }
|
|
val bitmap: Bitmap? = if (animated) {
|
|
updateBitmap()
|
|
mBitmap
|
|
} else {
|
|
(mObtainedDrawable as BitmapDrawable?)?.bitmap
|
|
}
|
|
bitmap ?: return
|
|
nativeTexImage(
|
|
bitmap,
|
|
init,
|
|
offsetX,
|
|
offsetY,
|
|
width,
|
|
height
|
|
)
|
|
}
|
|
|
|
fun start() {
|
|
if (!started) {
|
|
(mObtainedDrawable as AnimatedImageDrawable?)?.start()
|
|
}
|
|
}
|
|
|
|
val delay: Int
|
|
get() {
|
|
if (animated)
|
|
return 10
|
|
return 0
|
|
}
|
|
|
|
val isOpaque: Boolean
|
|
get() {
|
|
return mObtainedDrawable?.opacity == PixelFormat.OPAQUE
|
|
}
|
|
|
|
companion object {
|
|
var screenWidth: Int = 0
|
|
var screenHeight: Int = 0
|
|
|
|
@JvmStatic
|
|
fun initialize(context: Context) {
|
|
screenWidth = context.resources.displayMetrics.widthPixels
|
|
screenHeight = context.resources.displayMetrics.heightPixels
|
|
}
|
|
|
|
@Throws(DecodeException::class)
|
|
@JvmStatic
|
|
fun decode(stream: FileInputStream, hardware: Boolean = true): Image {
|
|
val src = ImageDecoder.createSource(
|
|
stream.channel.map(
|
|
FileChannel.MapMode.READ_ONLY, 0,
|
|
stream.available().toLong()
|
|
)
|
|
)
|
|
return Image(src, hardware = hardware)
|
|
}
|
|
|
|
@Throws(DecodeException::class)
|
|
@JvmStatic
|
|
fun decode(buffer: ByteBuffer, hardware: Boolean = true, release: () -> Unit? = {}): Image {
|
|
val src = ImageDecoder.createSource(buffer)
|
|
return Image(src, hardware = hardware) {
|
|
release()
|
|
}
|
|
}
|
|
|
|
@JvmStatic
|
|
fun create(bitmap: Bitmap): Image {
|
|
return Image(null, bitmap.toDrawable(Resources.getSystem()), false)
|
|
}
|
|
|
|
@JvmStatic
|
|
private external fun nativeRender(
|
|
bitmap: Bitmap,
|
|
srcX: Int, srcY: Int, dst: Bitmap, dstX: Int, dstY: Int,
|
|
width: Int, height: Int
|
|
)
|
|
|
|
@JvmStatic
|
|
private external fun nativeTexImage(
|
|
bitmap: Bitmap,
|
|
init: Boolean,
|
|
offsetX: Int,
|
|
offsetY: Int,
|
|
width: Int,
|
|
height: Int
|
|
)
|
|
}
|
|
} |