Skip to content

Commit

Permalink
Rename PooledImageRegionDecoder -> PooledAndroidImageRegionDecoder
Browse files Browse the repository at this point in the history
  • Loading branch information
saket committed Dec 23, 2024
1 parent 92d60b7 commit add45a7
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ import me.saket.telephoto.subsamplingimage.SubSamplingImage
import me.saket.telephoto.subsamplingimage.SubSamplingImageSource
import me.saket.telephoto.subsamplingimage.SubSamplingImageState
import me.saket.telephoto.subsamplingimage.internal.ImageRegionDecoder
import me.saket.telephoto.subsamplingimage.internal.PooledImageRegionDecoder
import me.saket.telephoto.subsamplingimage.internal.PooledAndroidImageRegionDecoder
import me.saket.telephoto.subsamplingimage.rememberSubSamplingImageState
import me.saket.telephoto.subsamplingimage.test.R
import me.saket.telephoto.util.CiScreenshotValidator
Expand Down Expand Up @@ -98,7 +98,7 @@ class SubSamplingImageTest {

@After
fun tearDown() {
PooledImageRegionDecoder.overriddenPoolCount = null
PooledAndroidImageRegionDecoder.overriddenPoolCount = null
LeakAssertions.assertNoLeaks()
}

Expand Down Expand Up @@ -243,7 +243,7 @@ class SubSamplingImageTest {

@Test fun draw_base_tile_to_fill_gaps_in_foreground_tiles() {
// This test blocks 2 decoders indefinitely so at least 3 decoders are needed.
PooledImageRegionDecoder.overriddenPoolCount = 3
PooledAndroidImageRegionDecoder.overriddenPoolCount = 3

// This fake image decoder will only decode the base tile.
val imageSource = SubSamplingImageSource.asset("pahade.jpg")
Expand Down Expand Up @@ -294,7 +294,7 @@ class SubSamplingImageTest {

@Test fun draw_tile_under_centroid_first() {
// This test only allows 1 decoder to work so at least 2 decoders are needed.
PooledImageRegionDecoder.overriddenPoolCount = 2
PooledAndroidImageRegionDecoder.overriddenPoolCount = 2

// This fake decoder will ignore decoding of all but the first tiles.
val firstNonBaseTileReceived = AtomicBoolean(false)
Expand Down Expand Up @@ -630,7 +630,7 @@ class SubSamplingImageTest {

@Test fun do_not_draw_base_tile_after_foreground_tiles_images_are_loaded() {
// This test blocks 1 decoders so at least 2 decoders are needed.
PooledImageRegionDecoder.overriddenPoolCount = 2
PooledAndroidImageRegionDecoder.overriddenPoolCount = 2

val mutexForDecodingLastTile = Mutex(locked = true)
val imageSource = SubSamplingImageSource.asset("pahade.jpg")
Expand Down Expand Up @@ -758,7 +758,7 @@ class SubSamplingImageTest {
}

@Test fun raw_stream_works_with_multiple_decoders() {
PooledImageRegionDecoder.overriddenPoolCount = 2
PooledAndroidImageRegionDecoder.overriddenPoolCount = 2

rule.setContent {
val zoomableState = rememberZoomableState()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.ui.graphics.ImageBitmap
import me.saket.telephoto.subsamplingimage.internal.ImageRegionDecoder
import me.saket.telephoto.subsamplingimage.internal.PooledAndroidImageRegionDecoderFactory
import me.saket.telephoto.subsamplingimage.internal.PooledAndroidImageRegionDecoder
import okio.BufferedSource
import okio.Closeable
import okio.FileSystem
Expand Down Expand Up @@ -164,7 +164,7 @@ internal data class FileImageSource(
}

override suspend fun decoder(): ImageRegionDecoder.Factory {
return PooledAndroidImageRegionDecoderFactory(this) {
return PooledAndroidImageRegionDecoder.Factory(this) {
ParcelFileDescriptor.open(path.toFile(), ParcelFileDescriptor.MODE_READ_ONLY).use { fd ->
@Suppress("DEPRECATION")
BitmapRegionDecoder.newInstance(fd.fileDescriptor, /* ignored */ false)
Expand All @@ -188,7 +188,7 @@ internal data class AssetImageSource(
}

override suspend fun decoder(): ImageRegionDecoder.Factory {
return PooledAndroidImageRegionDecoderFactory(this) { context ->
return PooledAndroidImageRegionDecoder.Factory(this) { context ->
inputStream(context).use { stream ->
check(stream is AssetManager.AssetInputStream) {
error("BitmapRegionDecoder won't be able to optimize reading of this asset")
Expand All @@ -215,7 +215,7 @@ internal data class ResourceImageSource(
}

override suspend fun decoder(): ImageRegionDecoder.Factory {
return PooledAndroidImageRegionDecoderFactory(this) { context ->
return PooledAndroidImageRegionDecoder.Factory(this) { context ->
inputStream(context).use { stream ->
@Suppress("DEPRECATION")
BitmapRegionDecoder.newInstance(stream, /* ignored */ false)!!
Expand All @@ -240,7 +240,7 @@ internal data class UriImageSource(
}

override suspend fun decoder(): ImageRegionDecoder.Factory {
return PooledAndroidImageRegionDecoderFactory(this) { context ->
return PooledAndroidImageRegionDecoder.Factory(this) { context ->
inputStream(context).use { stream ->
@Suppress("DEPRECATION")
BitmapRegionDecoder.newInstance(stream, /* ignored */ false)!!
Expand Down Expand Up @@ -272,7 +272,7 @@ internal data class RawImageSource(
// This uses a peeking source because the image source can be decoded
// by multiple decoders in parallel. A downside of this is that the
// upstream source will never be consumed, which is probably okay?
return PooledAndroidImageRegionDecoderFactory(this) { context ->
return PooledAndroidImageRegionDecoder.Factory(this) { context ->
peek(context).inputStream().use { stream ->
@Suppress("DEPRECATION")
BitmapRegionDecoder.newInstance(stream, /* ignored */ false)!!
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package me.saket.telephoto.subsamplingimage.internal

import android.content.Context
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.IntSize
Expand All @@ -11,9 +10,10 @@ import kotlin.reflect.KClass
import kotlin.reflect.cast

/**
* [ImageBitmap] decoder, responsible for loading regions of an image for [SubSamplingImage]'s tiles.
* An image decoder, responsible for loading partial regions for
* [SubSamplingImage][me.saket.telephoto.subsamplingimage.SubSamplingImage]'s tiles.
*
* Also see: [AndroidImageRegionDecoder] and [PooledImageRegionDecoder].
* Also see: [AndroidImageRegionDecoder] and [PooledAndroidImageRegionDecoder].
*/
interface ImageRegionDecoder {
/** Raw size of the image, without any scaling applied. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,12 @@ import me.saket.telephoto.subsamplingimage.SubSamplingImageSource
import me.saket.telephoto.subsamplingimage.internal.ImageRegionDecoder.DecodeResult
import me.saket.telephoto.subsamplingimage.internal.ImageRegionDecoder.FactoryParams

internal fun PooledAndroidImageRegionDecoderFactory(
imageSource: SubSamplingImageSource,
createDecoder: (context: Context) -> BitmapRegionDecoder
): ImageRegionDecoder.Factory = PooledImageRegionDecoder.Factory(
imageSource = imageSource,
delegate = AndroidImageRegionDecoder.Factory(imageSource, createDecoder),
)

/**
* Maintains a pool of decoders to load multiple bitmap regions in parallel. Without this,
* a single [BitmapRegionDecoder] can only be used for one region at a time because it
* synchronizes its APIs internally.
* */
internal class PooledImageRegionDecoder private constructor(
internal class PooledAndroidImageRegionDecoder private constructor(
override val imageSize: IntSize,
private val decoders: ResourcePool<ImageRegionDecoder>,
) : ImageRegionDecoder {
Expand All @@ -48,7 +40,7 @@ internal class PooledImageRegionDecoder private constructor(

fun Factory(
imageSource: SubSamplingImageSource,
delegate: ImageRegionDecoder.Factory
createDecoder: (context: Context) -> BitmapRegionDecoder,
) = ImageRegionDecoder.Factory { params ->
val exif = ExifMetadata.read(params.context, imageSource)
val params = FactoryParams(
Expand All @@ -57,13 +49,18 @@ internal class PooledImageRegionDecoder private constructor(
extras = params.extras.plus(ExifMetadata::class to exif),
)

val delegate = AndroidImageRegionDecoder.Factory(
imageSource = imageSource,
createDecoder = createDecoder,
)

val decoders = buildList<ImageRegionDecoder> {
add(delegate.create(params))
repeat(calculatePoolCount(params, first().imageSize) - 1) {
add(delegate.create(params))
}
}
PooledImageRegionDecoder(
PooledAndroidImageRegionDecoder(
imageSize = decoders.first().imageSize,
decoders = ResourcePool(decoders),
)
Expand Down

0 comments on commit add45a7

Please sign in to comment.