Viewer: TIFF subsampling & tiling

This commit is contained in:
Thibault Deckers 2020-12-01 10:36:12 +09:00
parent 0d946b5a43
commit 4d9df75c46
4 changed files with 69 additions and 17 deletions

View file

@ -10,6 +10,7 @@ import deckers.thibault.aves.model.provider.FieldMap
import deckers.thibault.aves.model.provider.ImageProvider.ImageOpCallback
import deckers.thibault.aves.model.provider.ImageProviderFactory.getProvider
import deckers.thibault.aves.model.provider.MediaStoreImageProvider
import deckers.thibault.aves.utils.MimeTypes
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
@ -95,14 +96,24 @@ class ImageFileHandler(private val activity: Activity) : MethodCallHandler {
return
}
regionFetcher.fetch(
uri,
mimeType,
sampleSize,
Rect(x, y, x + width, y + height),
Size(imageWidth, imageHeight),
result,
)
val regionRect = Rect(x, y, x + width, y + height)
when (mimeType) {
MimeTypes.TIFF -> TiffRegionFetcher(activity).fetch(
uri,
sampleSize,
regionRect,
page = 0,
result,
)
else -> regionFetcher.fetch(
uri,
mimeType,
sampleSize,
regionRect,
Size(imageWidth, imageHeight),
result,
)
}
}
private suspend fun getImageEntry(call: MethodCall, result: MethodChannel.Result) {

View file

@ -0,0 +1,40 @@
package deckers.thibault.aves.channel.calls
import android.content.Context
import android.graphics.Rect
import android.net.Uri
import deckers.thibault.aves.utils.BitmapUtils.getBytes
import io.flutter.plugin.common.MethodChannel
import org.beyka.tiffbitmapfactory.DecodeArea
import org.beyka.tiffbitmapfactory.TiffBitmapFactory
class TiffRegionFetcher internal constructor(
private val context: Context,
) {
fun fetch(
uri: Uri,
sampleSize: Int,
regionRect: Rect,
page: Int = 0,
result: MethodChannel.Result,
) {
val resolver = context.contentResolver
try {
resolver.openFileDescriptor(uri, "r")?.use { descriptor ->
val options = TiffBitmapFactory.Options().apply {
inDirectoryNumber = page
inSampleSize = sampleSize
inDecodeArea = DecodeArea(regionRect.left, regionRect.top, regionRect.width(), regionRect.height())
}
val bitmap = TiffBitmapFactory.decodeFileDescriptor(descriptor.fd, options)
if (bitmap != null) {
result.success(bitmap.getBytes(canHaveAlpha = true, recycle = true))
} else {
result.error("getRegion-tiff-null", "failed to decode region for uri=$uri page=$page regionRect=$regionRect", null)
}
}
} catch (e: Exception) {
result.error("getRegion-tiff-read-exception", "failed to read from uri=$uri page=$page regionRect=$regionRect", e.message)
}
}
}

View file

@ -95,7 +95,7 @@ class ImageByteStreamHandler(private val activity: Activity, private val argumen
private fun streamImageByGlide(uri: Uri, mimeType: String, rotationDegrees: Int, isFlipped: Boolean) {
val target = Glide.with(activity)
.asBitmap()
.apply(options)
.apply(glideOptions)
.load(uri)
.submit()
try {
@ -118,7 +118,7 @@ class ImageByteStreamHandler(private val activity: Activity, private val argumen
private fun streamVideoByGlide(uri: Uri) {
val target = Glide.with(activity)
.asBitmap()
.apply(options)
.apply(glideOptions)
.load(VideoThumbnail(activity, uri))
.submit()
try {
@ -135,7 +135,7 @@ class ImageByteStreamHandler(private val activity: Activity, private val argumen
}
}
private fun streamTiffImage(uri: Uri) {
private fun streamTiffImage(uri: Uri, page: Int = 0) {
val resolver = activity.contentResolver
try {
var dirCount = 0
@ -148,18 +148,17 @@ class ImageByteStreamHandler(private val activity: Activity, private val argumen
}
// TODO TLAD handle multipage TIFF
if (dirCount > 0) {
val i = 0
if (dirCount > page) {
resolver.openFileDescriptor(uri, "r")?.use { descriptor ->
val options = TiffBitmapFactory.Options().apply {
inJustDecodeBounds = false
inDirectoryNumber = i
inDirectoryNumber = page
}
val bitmap = TiffBitmapFactory.decodeFileDescriptor(descriptor.fd, options)
if (bitmap != null) {
success(bitmap.getBytes(canHaveAlpha = true, recycle = true))
} else {
error("streamImage-tiff-null", "failed to get tiff image (dir=$i) from uri=$uri", null)
error("streamImage-tiff-null", "failed to get tiff image (dir=$page) from uri=$uri", null)
}
}
}
@ -192,7 +191,7 @@ class ImageByteStreamHandler(private val activity: Activity, private val argumen
const val bufferSize = 2 shl 17 // 256kB
// request a fresh image with the highest quality format
val options = RequestOptions()
val glideOptions = RequestOptions()
.format(DecodeFormat.PREFER_ARGB_8888)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)

View file

@ -178,7 +178,7 @@ class ImageEntry {
// Android's `BitmapRegionDecoder` documentation states that "only the JPEG and PNG formats are supported"
// but in practice (tested on API 25, 27, 29), it successfully decodes the formats listed below,
// and it actually fails to decode GIF, DNG and animated WEBP. Other formats were not tested.
bool get canTile =>
bool get _supportedByBitmapRegionDecoder =>
[
MimeTypes.heic,
MimeTypes.heif,
@ -196,6 +196,8 @@ class ImageEntry {
].contains(mimeType) &&
!isAnimated;
bool get canTile => _supportedByBitmapRegionDecoder || mimeType == MimeTypes.tiff;
bool get isRaw => MimeTypes.rawImages.contains(mimeType);
bool get isVideo => mimeType.startsWith('video');