#1037 use truncated preview when opening large DNG with metadata extractor
This commit is contained in:
parent
981727cb1d
commit
1346e8867b
4 changed files with 17 additions and 13 deletions
|
@ -296,7 +296,7 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
|
||||||
byGeoTiff[false]?.map { exifTagMapper(it) }?.let { dirMap.putAll(it) }
|
byGeoTiff[false]?.map { exifTagMapper(it) }?.let { dirMap.putAll(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
mimeType == MimeTypes.DNG -> {
|
mimeType == MimeTypes.DNG || mimeType == MimeTypes.DNG_ADOBE -> {
|
||||||
// split DNG tags in their own directory
|
// split DNG tags in their own directory
|
||||||
val dngDirMap = metadataMap[DIR_DNG] ?: HashMap()
|
val dngDirMap = metadataMap[DIR_DNG] ?: HashMap()
|
||||||
metadataMap[DIR_DNG] = dngDirMap
|
metadataMap[DIR_DNG] = dngDirMap
|
||||||
|
|
|
@ -120,7 +120,7 @@ object Metadata {
|
||||||
return date.time + parseSubSecond(subSecond)
|
return date.time + parseSubSecond(subSecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opening large PSD/TIFF files yields an OOM (both with `metadata-extractor` v2.15.0 and `ExifInterface` v1.3.1),
|
// Opening some large files yields an OOM (both with `metadata-extractor` v2.15.0 and `ExifInterface` v1.3.1),
|
||||||
// so we define an arbitrary threshold to avoid a crash on launch.
|
// so we define an arbitrary threshold to avoid a crash on launch.
|
||||||
// It is not clear whether it is because of the file itself or its metadata.
|
// It is not clear whether it is because of the file itself or its metadata.
|
||||||
private const val FILE_SIZE_MAX = 100 * (1 shl 20) // MB
|
private const val FILE_SIZE_MAX = 100 * (1 shl 20) // MB
|
||||||
|
@ -136,6 +136,8 @@ object Metadata {
|
||||||
private fun getSafeUri(context: Context, uri: Uri, mimeType: String, sizeBytes: Long?): Uri {
|
private fun getSafeUri(context: Context, uri: Uri, mimeType: String, sizeBytes: Long?): Uri {
|
||||||
return when (mimeType) {
|
return when (mimeType) {
|
||||||
// formats known to yield OOM for large files
|
// formats known to yield OOM for large files
|
||||||
|
MimeTypes.DNG,
|
||||||
|
MimeTypes.DNG_ADOBE,
|
||||||
MimeTypes.HEIC,
|
MimeTypes.HEIC,
|
||||||
MimeTypes.HEIF,
|
MimeTypes.HEIF,
|
||||||
MimeTypes.MP4,
|
MimeTypes.MP4,
|
||||||
|
|
|
@ -60,19 +60,20 @@ object Helper {
|
||||||
// e.g. "exif [...] 134 [...] 4578696600004949[...]"
|
// e.g. "exif [...] 134 [...] 4578696600004949[...]"
|
||||||
private val PNG_RAW_PROFILE_PATTERN = Regex("^\\n(.*?)\\n\\s*(\\d+)\\n(.*)", RegexOption.DOT_MATCHES_ALL)
|
private val PNG_RAW_PROFILE_PATTERN = Regex("^\\n(.*?)\\n\\s*(\\d+)\\n(.*)", RegexOption.DOT_MATCHES_ALL)
|
||||||
|
|
||||||
// providing the stream length is risky, as it may crash if it is incorrect
|
|
||||||
private const val SAFE_READ_STREAM_LENGTH = -1L
|
|
||||||
|
|
||||||
fun readMimeType(input: InputStream): String? {
|
fun readMimeType(input: InputStream): String? {
|
||||||
val bufferedInputStream = if (input is BufferedInputStream) input else BufferedInputStream(input)
|
val bufferedInputStream = if (input is BufferedInputStream) input else BufferedInputStream(input)
|
||||||
return FileTypeDetector.detectFileType(bufferedInputStream).mimeType
|
return FileTypeDetector.detectFileType(bufferedInputStream).mimeType
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IOException::class, ImageProcessingException::class)
|
@Throws(IOException::class, ImageProcessingException::class)
|
||||||
fun safeRead(input: InputStream, sizeBytes: Long?): com.drew.metadata.Metadata {
|
fun safeRead(input: InputStream, @Suppress("unused_parameter") sizeBytes: Long?): com.drew.metadata.Metadata {
|
||||||
val inputStream = if (input is BufferedInputStream) input else BufferedInputStream(input)
|
val inputStream = if (input is BufferedInputStream) input else BufferedInputStream(input)
|
||||||
val fileType = FileTypeDetector.detectFileType(inputStream)
|
val fileType = FileTypeDetector.detectFileType(inputStream)
|
||||||
val streamLength = sizeBytes ?: SAFE_READ_STREAM_LENGTH
|
|
||||||
|
// Providing the stream length is risky, as it may crash if it is incorrect.
|
||||||
|
// Not providing the stream length is also risky, as it may lead to OOM
|
||||||
|
// when `RandomAccessStreamReader` reads the entire stream to validate offsets.
|
||||||
|
val undefinedStreamLength = -1L
|
||||||
|
|
||||||
val metadata = when (fileType) {
|
val metadata = when (fileType) {
|
||||||
FileType.Jpeg -> safeReadJpeg(inputStream)
|
FileType.Jpeg -> safeReadJpeg(inputStream)
|
||||||
|
@ -84,9 +85,9 @@ object Helper {
|
||||||
FileType.Cr2,
|
FileType.Cr2,
|
||||||
FileType.Nef,
|
FileType.Nef,
|
||||||
FileType.Orf,
|
FileType.Orf,
|
||||||
FileType.Rw2 -> safeReadTiff(inputStream, streamLength)
|
FileType.Rw2 -> safeReadTiff(inputStream, undefinedStreamLength)
|
||||||
|
|
||||||
else -> ImageMetadataReader.readMetadata(inputStream, streamLength, fileType)
|
else -> ImageMetadataReader.readMetadata(inputStream, undefinedStreamLength, fileType)
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata.addDirectory(FileTypeDirectory(fileType))
|
metadata.addDirectory(FileTypeDirectory(fileType))
|
||||||
|
|
|
@ -28,7 +28,8 @@ object MimeTypes {
|
||||||
private const val CR2 = "image/x-canon-cr2"
|
private const val CR2 = "image/x-canon-cr2"
|
||||||
private const val CRW = "image/x-canon-crw"
|
private const val CRW = "image/x-canon-crw"
|
||||||
private const val DCR = "image/x-kodak-dcr"
|
private const val DCR = "image/x-kodak-dcr"
|
||||||
const val DNG = "image/x-adobe-dng"
|
const val DNG = "image/dng"
|
||||||
|
const val DNG_ADOBE = "image/x-adobe-dng"
|
||||||
private const val ERF = "image/x-epson-erf"
|
private const val ERF = "image/x-epson-erf"
|
||||||
private const val K25 = "image/x-kodak-k25"
|
private const val K25 = "image/x-kodak-k25"
|
||||||
private const val KDC = "image/x-kodak-kdc"
|
private const val KDC = "image/x-kodak-kdc"
|
||||||
|
@ -71,7 +72,7 @@ object MimeTypes {
|
||||||
|
|
||||||
fun isRaw(mimeType: String): Boolean {
|
fun isRaw(mimeType: String): Boolean {
|
||||||
return when (mimeType) {
|
return when (mimeType) {
|
||||||
ARW, CR2, CRW, DCR, DNG, ERF, K25, KDC, MRW, NEF, NRW, ORF, PEF, RAF, RAW, RW2, SR2, SRF, SRW, X3F -> true
|
ARW, CR2, CRW, DCR, DNG, DNG_ADOBE, ERF, K25, KDC, MRW, NEF, NRW, ORF, PEF, RAF, RAW, RW2, SR2, SRF, SRW, X3F -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,7 +143,7 @@ object MimeTypes {
|
||||||
return if (pageId != null && MultiPageImage.isSupported(mimeType)) {
|
return if (pageId != null && MultiPageImage.isSupported(mimeType)) {
|
||||||
true
|
true
|
||||||
} else when (mimeType) {
|
} else when (mimeType) {
|
||||||
DNG, HEIC, HEIF, PNG, WEBP -> true
|
DNG, DNG_ADOBE, HEIC, HEIF, PNG, WEBP -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,7 +152,7 @@ object MimeTypes {
|
||||||
// according to EXIF orientation when decoding images of known formats
|
// according to EXIF orientation when decoding images of known formats
|
||||||
// but we need to rotate the decoded bitmap for the other formats
|
// but we need to rotate the decoded bitmap for the other formats
|
||||||
fun needRotationAfterContentResolverThumbnail(mimeType: String) = when (mimeType) {
|
fun needRotationAfterContentResolverThumbnail(mimeType: String) = when (mimeType) {
|
||||||
DNG, PNG -> true
|
DNG, DNG_ADOBE, PNG -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue