APNG identification
This commit is contained in:
parent
01e1058f1a
commit
33841788f7
5 changed files with 50 additions and 5 deletions
|
@ -7,7 +7,7 @@ All notable changes to this project will be documented in this file.
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- option to set the Tags page as home
|
- 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
|
- Info: added day filter with item date
|
||||||
- Widget: option to update image on tap
|
- Widget: option to update image on tap
|
||||||
- Slideshow / Screen saver: option for random transition
|
- Slideshow / Screen saver: option for random transition
|
||||||
|
|
|
@ -301,7 +301,7 @@ This change eventually prevents building the app with Flutter v3.7.11.
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="flutterEmbedding"
|
android:name="flutterEmbedding"
|
||||||
android:value="2" />
|
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
|
<meta-data
|
||||||
android:name="io.flutter.embedding.android.EnableImpeller"
|
android:name="io.flutter.embedding.android.EnableImpeller"
|
||||||
android:value="false" />
|
android:value="false" />
|
||||||
|
|
|
@ -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.getSafeRational
|
||||||
import deckers.thibault.aves.metadata.metadataextractor.Helper.getSafeString
|
import deckers.thibault.aves.metadata.metadataextractor.Helper.getSafeString
|
||||||
import deckers.thibault.aves.metadata.metadataextractor.Helper.isPngTextDir
|
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.model.FieldMap
|
||||||
import deckers.thibault.aves.utils.ContextUtils.queryContentPropValue
|
import deckers.thibault.aves.utils.ContextUtils.queryContentPropValue
|
||||||
import deckers.thibault.aves.utils.LogUtils
|
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 -> {
|
MimeTypes.GIF -> {
|
||||||
|
|
|
@ -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",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,9 +34,10 @@ import java.io.InputStream
|
||||||
import java.util.zip.InflaterInputStream
|
import java.util.zip.InflaterInputStream
|
||||||
import java.util.zip.ZipException
|
import java.util.zip.ZipException
|
||||||
|
|
||||||
// adapted from `PngMetadataReader` to prevent OOM from reading large chunks
|
// adapted from `PngMetadataReader` to:
|
||||||
// as of `metadata-extractor` v2.18.0, there is no way to customize the reader
|
// - 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
|
// without copying `desiredChunkTypes` and the whole `processChunk` function.
|
||||||
|
// - parse `acTL` chunk to identify animated PNGs.
|
||||||
object SafePngMetadataReader {
|
object SafePngMetadataReader {
|
||||||
private val LOG_TAG = LogUtils.createTag<SafePngMetadataReader>()
|
private val LOG_TAG = LogUtils.createTag<SafePngMetadataReader>()
|
||||||
|
|
||||||
|
@ -60,6 +61,7 @@ object SafePngMetadataReader {
|
||||||
PngChunkType.pHYs,
|
PngChunkType.pHYs,
|
||||||
PngChunkType.sBIT,
|
PngChunkType.sBIT,
|
||||||
PngChunkType.eXIf,
|
PngChunkType.eXIf,
|
||||||
|
PngActlDirectory.chunkType,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Throws(IOException::class, PngProcessingException::class)
|
@Throws(IOException::class, PngProcessingException::class)
|
||||||
|
@ -99,6 +101,21 @@ object SafePngMetadataReader {
|
||||||
directory.setInt(PngDirectory.TAG_FILTER_METHOD, header.filterMethod.toInt())
|
directory.setInt(PngDirectory.TAG_FILTER_METHOD, header.filterMethod.toInt())
|
||||||
directory.setInt(PngDirectory.TAG_INTERLACE_METHOD, header.interlaceMethod.toInt())
|
directory.setInt(PngDirectory.TAG_INTERLACE_METHOD, header.interlaceMethod.toInt())
|
||||||
metadata.addDirectory(directory)
|
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) {
|
} else if (chunkType == PngChunkType.PLTE) {
|
||||||
val directory = PngDirectory(PngChunkType.PLTE)
|
val directory = PngDirectory(PngChunkType.PLTE)
|
||||||
directory.setInt(PngDirectory.TAG_PALETTE_SIZE, bytes.size / 3)
|
directory.setInt(PngDirectory.TAG_PALETTE_SIZE, bytes.size / 3)
|
||||||
|
|
Loading…
Reference in a new issue