Skip to content

Commit

Permalink
Update subsampling library, Update coil and add avif support
Browse files Browse the repository at this point in the history
Also fixes Using double tap to zoom #399

Signed-off-by: IacobIonut01 <[email protected]>
  • Loading branch information
IacobIonut01 committed Aug 4, 2024
1 parent 4856020 commit f36f32f
Show file tree
Hide file tree
Showing 10 changed files with 325 additions and 107 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ android {
applicationId = "com.dot.gallery"
minSdk = 30
targetSdk = 34
versionCode = 30011
versionCode = 30013
versionName = "3.0.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import androidx.core.graphics.createBitmap
import androidx.core.graphics.drawable.toDrawable
import coil3.ImageLoader
import coil3.annotation.ExperimentalCoilApi
import coil3.asCoilImage
import coil3.asImage
import coil3.decode.ContentMetadata
import coil3.decode.DecodeResult
import coil3.decode.DecodeUtils
Expand All @@ -19,6 +19,7 @@ import coil3.decode.ImageSource
import coil3.fetch.SourceFetchResult
import coil3.request.Options
import coil3.request.bitmapConfig
import coil3.size.Precision
import coil3.size.Size
import coil3.size.pxOrElse
import coil3.svg.internal.MIME_TYPE_SVG
Expand Down Expand Up @@ -46,7 +47,7 @@ class ThumbnailDecoder(


return DecodeResult(
image = normalizedBitmap.toDrawable(options.context.resources).asCoilImage(),
image = normalizedBitmap.toDrawable(options.context.resources).asImage(),
isSampled = true,
)
}
Expand Down Expand Up @@ -91,7 +92,7 @@ class ThumbnailDecoder(
}

private fun isSizeValid(bitmap: Bitmap, options: Options, size: Size): Boolean {
if (options.allowInexactSize) return true
if (options.precision == Precision.INEXACT) return true
val multiplier = DecodeUtils.computeSizeMultiplier(
srcWidth = bitmap.width,
srcHeight = bitmap.height,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import com.dot.gallery.core.Settings
import com.dot.gallery.core.presentation.components.util.LocalBatteryStatus
import com.dot.gallery.core.presentation.components.util.ProvideBatteryStatus
import com.dot.gallery.feature_node.domain.model.Media
import me.saket.telephoto.zoomable.DoubleClickToZoomListener
import me.saket.telephoto.zoomable.ZoomSpec
import me.saket.telephoto.zoomable.ZoomableImage
import me.saket.telephoto.zoomable.ZoomableImageSource
Expand Down Expand Up @@ -104,7 +105,8 @@ fun ZoomablePagerImage(
.build()
),
contentScale = ContentScale.Fit,
contentDescription = media.label
contentDescription = media.label,
onDoubleClick = DoubleClickToZoomListener.cycle(3f)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ package com.dot.gallery.feature_node.presentation.util

import coil3.ImageLoader
import coil3.annotation.ExperimentalCoilApi
import coil3.asCoilImage
import coil3.asImage
import coil3.decode.DecodeResult
import coil3.decode.Decoder
import coil3.fetch.SourceFetchResult
Expand Down Expand Up @@ -63,7 +63,7 @@ class JxlDecoder(
preferredColorConfig = PreferredColorConfig.DEFAULT
)
return@runInterruptible DecodeResult(
image = originalImage.asCoilImage(),
image = originalImage.asImage(),
isSampled = false
)
}
Expand All @@ -85,7 +85,7 @@ class JxlDecoder(
JxlResizeFilter.BILINEAR,
)
DecodeResult(
image = originalImage.asCoilImage(),
image = originalImage.asImage(),
isSampled = true
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.dot.gallery.feature_node.presentation.util

import android.app.ActivityManager
import android.graphics.Bitmap
import androidx.core.content.getSystemService
import coil3.ImageLoader
import coil3.PlatformContext
Expand All @@ -9,10 +10,11 @@ import coil3.disk.directory
import coil3.gif.AnimatedImageDecoder
import coil3.memory.MemoryCache
import coil3.request.allowRgb565
import coil3.request.bitmapConfig
import coil3.request.crossfade
import coil3.svg.SvgDecoder
import coil3.util.DebugLogger
import com.dot.gallery.core.coil.ThumbnailDecoder
import com.github.awxkee.avifcoil.decoder.HeifDecoder3

fun newImageLoader(
context: PlatformContext
Expand All @@ -21,6 +23,7 @@ fun newImageLoader(
val memoryPercent = if (activityManager.isLowRamDevice) 0.25 else 0.75
return ImageLoader.Builder(context)
.components {
add(HeifDecoder3.Factory(context))
// SVGs
add(SvgDecoder.Factory(false))
add(JxlDecoder.Factory())
Expand All @@ -43,6 +46,6 @@ fun newImageLoader(
// Show a short crossfade when loading images asynchronously.
.crossfade(100)
.allowRgb565(true)
.logger(DebugLogger())
.bitmapConfig(Bitmap.Config.HARDWARE)
.build()
}
145 changes: 145 additions & 0 deletions app/src/main/kotlin/com/github/awxkee/avifcoil/decoder/Heif3Decoder.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* MIT License
*
* Copyright (c) 2023 Radzivon Bartoshyk
* avif-coder [https://github.com/awxkee/avif-coder]
*
* Created by Radzivon Bartoshyk on 23/09/2023
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
// Same HeifDecoder but for coil3

package com.github.awxkee.avifcoil.decoder

import android.content.Context
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.os.Build
import coil3.ImageLoader
import coil3.annotation.ExperimentalCoilApi
import coil3.asImage
import coil3.decode.DecodeResult
import coil3.decode.Decoder
import coil3.fetch.SourceFetchResult
import coil3.request.Options
import coil3.request.allowRgb565
import coil3.request.bitmapConfig
import coil3.size.Scale
import coil3.size.Size
import coil3.size.pxOrElse
import com.radzivon.bartoshyk.avif.coder.HeifCoder
import com.radzivon.bartoshyk.avif.coder.PreferredColorConfig
import com.radzivon.bartoshyk.avif.coder.ScaleMode
import kotlinx.coroutines.runInterruptible
import okio.ByteString.Companion.encodeUtf8

class HeifDecoder3(
context: Context?,
private val source: SourceFetchResult,
private val options: Options,
) : Decoder {

private val coder = HeifCoder(context)

@OptIn(ExperimentalCoilApi::class)
override suspend fun decode(): DecodeResult = runInterruptible {
// ColorSpace is preferred to be ignored due to lib is trying to handle all color profile by itself
val sourceData = source.source.source().readByteArray()

var mPreferredColorConfig: PreferredColorConfig = when (options.bitmapConfig) {
Bitmap.Config.ALPHA_8 -> PreferredColorConfig.RGBA_8888
Bitmap.Config.RGB_565 -> if (options.allowRgb565) PreferredColorConfig.RGB_565 else PreferredColorConfig.DEFAULT
Bitmap.Config.ARGB_8888 -> PreferredColorConfig.RGBA_8888
else -> PreferredColorConfig.DEFAULT
}
if (options.bitmapConfig == Bitmap.Config.RGBA_F16) {
mPreferredColorConfig = PreferredColorConfig.RGBA_F16
} else if (options.bitmapConfig == Bitmap.Config.HARDWARE) {
mPreferredColorConfig = PreferredColorConfig.HARDWARE
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && options.bitmapConfig == Bitmap.Config.RGBA_1010102) {
mPreferredColorConfig = PreferredColorConfig.RGBA_1010102
}

if (options.size == Size.ORIGINAL) {
val originalImage =
coder.decode(
sourceData,
preferredColorConfig = mPreferredColorConfig
)
return@runInterruptible DecodeResult(
BitmapDrawable(
options.context.resources,
originalImage
).asImage(), false
)
}

val dstWidth = options.size.width.pxOrElse { 0 }
val dstHeight = options.size.height.pxOrElse { 0 }
val scaleMode = when (options.scale) {
Scale.FILL -> ScaleMode.FILL
Scale.FIT -> ScaleMode.FIT
}

val originalImage =
coder.decodeSampled(
sourceData,
dstWidth,
dstHeight,
preferredColorConfig = mPreferredColorConfig,
scaleMode,
)
return@runInterruptible DecodeResult(
image = BitmapDrawable(
options.context.resources,
originalImage
).asImage(), isSampled = true
)
}

/**
* @param context is preferred to be set when displaying an HDR content to apply Vulkan shaders
*/
class Factory(private val context: Context? = null) : Decoder.Factory {
override fun create(
result: SourceFetchResult,
options: Options,
imageLoader: ImageLoader
): Decoder? {
return if (AVAILABLE_BRANDS.any {
result.source.source().rangeEquals(4, it)
}) HeifDecoder3(context, result, options) else null
}

companion object {
private val MIF = "ftypmif1".encodeUtf8()
private val MSF = "ftypmsf1".encodeUtf8()
private val HEIC = "ftypheic".encodeUtf8()
private val HEIX = "ftypheix".encodeUtf8()
private val HEVC = "ftyphevc".encodeUtf8()
private val HEVX = "ftyphevx".encodeUtf8()
private val AVIF = "ftypavif".encodeUtf8()
private val AVIS = "ftypavis".encodeUtf8()

private val AVAILABLE_BRANDS = listOf(MIF, MSF, HEIC, HEIX, HEVC, HEVX, AVIF, AVIS)
}
}
}
Loading

0 comments on commit f36f32f

Please sign in to comment.