APNG identification

This commit is contained in:
Thibault Deckers 2023-05-21 16:47:38 +02:00
parent 01e1058f1a
commit 33841788f7
5 changed files with 50 additions and 5 deletions

View file

@ -7,7 +7,7 @@ All notable changes to this project will be documented in this file.
### Added
- option to set the Tags page as home
- support for animated PNG
- support for animated PNG (requires rescan)
- Info: added day filter with item date
- Widget: option to update image on tap
- Slideshow / Screen saver: option for random transition

View file

@ -301,7 +301,7 @@ This change eventually prevents building the app with Flutter v3.7.11.
<meta-data
android:name="flutterEmbedding"
android:value="2" />
<!-- as of Flutter v3.7.7, background blur yields black screen with Impeller -->
<!-- as of Flutter v3.10.1, Impeller badly renders text, fails to render videos, and crashes with Google Maps -->
<meta-data
android:name="io.flutter.embedding.android.EnableImpeller"
android:value="false" />

View file

@ -75,6 +75,7 @@ import deckers.thibault.aves.metadata.metadataextractor.Helper.getSafeInt
import deckers.thibault.aves.metadata.metadataextractor.Helper.getSafeRational
import deckers.thibault.aves.metadata.metadataextractor.Helper.getSafeString
import deckers.thibault.aves.metadata.metadataextractor.Helper.isPngTextDir
import deckers.thibault.aves.metadata.metadataextractor.PngActlDirectory
import deckers.thibault.aves.model.FieldMap
import deckers.thibault.aves.utils.ContextUtils.queryContentPropValue
import deckers.thibault.aves.utils.LogUtils
@ -657,6 +658,11 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
}
}
}
// identification of animated PNG
if (metadata.containsDirectoryOfType(PngActlDirectory::class.java)) {
flags = flags or MASK_IS_ANIMATED
}
}
MimeTypes.GIF -> {

View file

@ -0,0 +1,22 @@
package deckers.thibault.aves.metadata.metadataextractor
import com.drew.imaging.png.PngChunkType
import com.drew.metadata.png.PngDirectory
class PngActlDirectory : PngDirectory(chunkType) {
override fun getTagNameMap(): HashMap<Int, String> {
return tagNames
}
companion object {
val chunkType = PngChunkType("acTL")
// tags should be distinct from those already defined in `PngDirectory`
const val TAG_NUM_FRAMES = 101
const val TAG_NUM_PLAYS = 102
private val tagNames = hashMapOf(
TAG_NUM_FRAMES to "Number Of Frames",
TAG_NUM_PLAYS to "Number Of Plays",
)
}
}

View file

@ -34,9 +34,10 @@ import java.io.InputStream
import java.util.zip.InflaterInputStream
import java.util.zip.ZipException
// adapted from `PngMetadataReader` to prevent OOM from reading large chunks
// as of `metadata-extractor` v2.18.0, there is no way to customize the reader
// without copying `desiredChunkTypes` and the whole `processChunk` function
// adapted from `PngMetadataReader` to:
// - prevent OOM from reading large chunks. As of `metadata-extractor` v2.18.0, there is no way to customize the reader
// without copying `desiredChunkTypes` and the whole `processChunk` function.
// - parse `acTL` chunk to identify animated PNGs.
object SafePngMetadataReader {
private val LOG_TAG = LogUtils.createTag<SafePngMetadataReader>()
@ -60,6 +61,7 @@ object SafePngMetadataReader {
PngChunkType.pHYs,
PngChunkType.sBIT,
PngChunkType.eXIf,
PngActlDirectory.chunkType,
)
@Throws(IOException::class, PngProcessingException::class)
@ -99,6 +101,21 @@ object SafePngMetadataReader {
directory.setInt(PngDirectory.TAG_FILTER_METHOD, header.filterMethod.toInt())
directory.setInt(PngDirectory.TAG_INTERLACE_METHOD, header.interlaceMethod.toInt())
metadata.addDirectory(directory)
// TLAD insert start
} else if (chunkType == PngActlDirectory.chunkType) {
if (bytes.size != 8) {
throw PngProcessingException("Invalid number of bytes")
}
val reader = SequentialByteArrayReader(bytes)
try {
metadata.addDirectory(PngActlDirectory().apply {
setInt(PngActlDirectory.TAG_NUM_FRAMES, reader.int32)
setInt(PngActlDirectory.TAG_NUM_PLAYS, reader.int32)
})
} catch (ex: IOException) {
throw PngProcessingException(ex)
}
// TLAD insert end
} else if (chunkType == PngChunkType.PLTE) {
val directory = PngDirectory(PngChunkType.PLTE)
directory.setInt(PngDirectory.TAG_PALETTE_SIZE, bytes.size / 3)