bitmapのrecycle漏れを修正
This commit is contained in:
parent
2b2f693317
commit
d57bd836a2
|
@ -26,6 +26,6 @@ interface ApngDecoderCallback {
|
||||||
// called when APNG Frame Control is detected and its bitmap was rendered.
|
// called when APNG Frame Control is detected and its bitmap was rendered.
|
||||||
// its bitmap may same to default image for first frame.
|
// its bitmap may same to default image for first frame.
|
||||||
// ( in this case, both of onDefaultImage and onAnimationFrame are called for same bitmap)
|
// ( in this case, both of onDefaultImage and onAnimationFrame are called for same bitmap)
|
||||||
fun onAnimationFrame(apng : Apng, frameControl : ApngFrameControl, bitmap : ApngBitmap)
|
fun onAnimationFrame(apng : Apng, frameControl : ApngFrameControl, frameBitmap : ApngBitmap)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,7 @@ class ApngFrames private constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
// WARNING: ownership of "src" will be moved or recycled.
|
// WARNING: ownership of "src" will be moved or recycled.
|
||||||
private fun scaleBitmap(src : Bitmap?, size_max : Int) : Bitmap? {
|
private fun scaleBitmap(src : Bitmap, size_max : Int) : Bitmap {
|
||||||
if(src == null) return null
|
|
||||||
|
|
||||||
val wSrc = src.width
|
val wSrc = src.width
|
||||||
val hSrc = src.height
|
val hSrc = src.height
|
||||||
|
@ -71,7 +70,7 @@ class ApngFrames private constructor(
|
||||||
return b2
|
return b2
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun toBitmap(src : ApngBitmap) : Bitmap {
|
private fun toAndroidBitmap(src : ApngBitmap) : Bitmap {
|
||||||
return Bitmap.createBitmap(
|
return Bitmap.createBitmap(
|
||||||
src.colors, // int[] 配列
|
src.colors, // int[] 配列
|
||||||
0, // offset
|
0, // offset
|
||||||
|
@ -82,12 +81,8 @@ class ApngFrames private constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun toBitmap(src : ApngBitmap, size_max : Int) : Bitmap? {
|
private fun toAndroidBitmap(src : ApngBitmap, size_max : Int) : Bitmap {
|
||||||
return scaleBitmap(
|
return scaleBitmap( toAndroidBitmap( src ), size_max )
|
||||||
toBitmap(
|
|
||||||
src
|
|
||||||
), size_max
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
|
@ -112,11 +107,11 @@ class ApngFrames private constructor(
|
||||||
private var header : ApngImageHeader? = null
|
private var header : ApngImageHeader? = null
|
||||||
private var animationControl : ApngAnimationControl? = null
|
private var animationControl : ApngAnimationControl? = null
|
||||||
|
|
||||||
val width : Int
|
var width : Int =1
|
||||||
get() = Math.min( pixelSizeMax, header?.width ?: 1)
|
private set
|
||||||
|
|
||||||
val height : Int
|
var height : Int = 1
|
||||||
get() = Math.min( pixelSizeMax, header?.height ?: 1)
|
private set
|
||||||
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
val numFrames : Int
|
val numFrames : Int
|
||||||
|
@ -264,6 +259,9 @@ class ApngFrames private constructor(
|
||||||
|
|
||||||
override fun onHeader(apng : Apng, header : ApngImageHeader) {
|
override fun onHeader(apng : Apng, header : ApngImageHeader) {
|
||||||
this.header = header
|
this.header = header
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAnimationInfo(
|
override fun onAnimationInfo(
|
||||||
|
@ -281,23 +279,24 @@ class ApngFrames private constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDefaultImage(apng : Apng, bitmap : ApngBitmap) {
|
override fun onDefaultImage(apng : Apng, bitmap : ApngBitmap) {
|
||||||
|
val androidBitmap = toAndroidBitmap(bitmap, pixelSizeMax)
|
||||||
|
this.width = androidBitmap.width
|
||||||
|
this.height = androidBitmap.height
|
||||||
|
|
||||||
defaultImage?.recycle()
|
defaultImage?.recycle()
|
||||||
defaultImage = toBitmap(bitmap, pixelSizeMax)
|
defaultImage = androidBitmap
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAnimationFrame(
|
override fun onAnimationFrame(
|
||||||
apng : Apng,
|
apng : Apng,
|
||||||
frameControl : ApngFrameControl,
|
frameControl : ApngFrameControl,
|
||||||
bitmap : ApngBitmap
|
frameBitmap : ApngBitmap
|
||||||
) {
|
) {
|
||||||
val frames = this.frames ?: return
|
val frames = this.frames ?: return
|
||||||
val canvasBitmap = this.canvasBitmap ?: return
|
val canvasBitmap = this.canvasBitmap ?: return
|
||||||
|
|
||||||
// APNGのフレーム画像をAndroidの形式に変換する。この段階ではリサイズしない
|
|
||||||
val bitmapNative = toBitmap(bitmap)
|
|
||||||
|
|
||||||
val previous : Bitmap? = if(frameControl.disposeOp == DisposeOp.Previous) {
|
val previous : Bitmap? = if(frameControl.disposeOp == DisposeOp.Previous) {
|
||||||
// Capture the current bitmap region IF it needs to be reverted after rendering
|
// Capture the current frameBitmap region IF it needs to be reverted after rendering
|
||||||
Bitmap.createBitmap(
|
Bitmap.createBitmap(
|
||||||
canvasBitmap,
|
canvasBitmap,
|
||||||
frameControl.xOffset,
|
frameControl.xOffset,
|
||||||
|
@ -315,30 +314,32 @@ class ApngFrames private constructor(
|
||||||
null // (for blend, leave paint null)
|
null // (for blend, leave paint null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// APNGのフレーム画像をAndroidの形式に変換する。この段階ではリサイズしない
|
||||||
|
val frameBitmapAndroid = toAndroidBitmap(frameBitmap)
|
||||||
|
|
||||||
// Draw the new frame into place
|
// Draw the new frame into place
|
||||||
canvas.drawBitmap(
|
canvas.drawBitmap(
|
||||||
bitmapNative,
|
frameBitmapAndroid,
|
||||||
frameControl.xOffset.toFloat(),
|
frameControl.xOffset.toFloat(),
|
||||||
frameControl.yOffset.toFloat(),
|
frameControl.yOffset.toFloat(),
|
||||||
paint
|
paint
|
||||||
)
|
)
|
||||||
|
|
||||||
// Extract a drawable from the canvas. Have to copy the current bitmap.
|
frameBitmapAndroid.recycle()
|
||||||
|
|
||||||
|
// Extract a drawable from the canvas. Have to copy the current frameBitmap.
|
||||||
// Store the drawable in the sequence of frames
|
// Store the drawable in the sequence of frames
|
||||||
val timeStart = timeTotal
|
val timeStart = timeTotal
|
||||||
val timeWidth = Math.max(1L, frameControl.delayMilliseconds)
|
val timeWidth = Math.max(1L, frameControl.delayMilliseconds)
|
||||||
timeTotal += timeWidth
|
timeTotal += timeWidth
|
||||||
|
|
||||||
val scaledBitmap =
|
val scaledBitmap = scaleBitmap(
|
||||||
scaleBitmap(
|
canvasBitmap.copy( Bitmap.Config.ARGB_8888, false ),
|
||||||
canvasBitmap.copy(
|
pixelSizeMax
|
||||||
Bitmap.Config.ARGB_8888,
|
|
||||||
false
|
|
||||||
), pixelSizeMax
|
|
||||||
)
|
)
|
||||||
if(scaledBitmap != null) {
|
|
||||||
frames.add(Frame(scaledBitmap, timeStart, timeWidth))
|
frames.add(Frame(scaledBitmap, timeStart, timeWidth))
|
||||||
}
|
|
||||||
|
|
||||||
// Now "dispose" of the frame in preparation for the next.
|
// Now "dispose" of the frame in preparation for the next.
|
||||||
// https://wiki.mozilla.org/APNG_Specification#.60fcTL.60:_The_Frame_Control_Chunk
|
// https://wiki.mozilla.org/APNG_Specification#.60fcTL.60:_The_Frame_Control_Chunk
|
||||||
|
|
Loading…
Reference in New Issue