From c0b2dee3ffeaaa8aa3c85adf0560e3416e79c33b Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Fri, 26 Nov 2021 11:12:01 -0700 Subject: [PATCH] coil: use image size when creating mosaics Use the image size provided by coil when creating our mosaics. This allows both better transformations and better-quality mosaics in the detail view itself. --- .../org/oxycblt/auxio/coil/AuxioFetcher.kt | 40 +++++++++++-------- .../java/org/oxycblt/auxio/coil/Fetchers.kt | 15 ++++--- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/coil/AuxioFetcher.kt b/app/src/main/java/org/oxycblt/auxio/coil/AuxioFetcher.kt index 76e016148..6e45eda44 100644 --- a/app/src/main/java/org/oxycblt/auxio/coil/AuxioFetcher.kt +++ b/app/src/main/java/org/oxycblt/auxio/coil/AuxioFetcher.kt @@ -12,6 +12,8 @@ import coil.fetch.DrawableResult import coil.fetch.FetchResult import coil.fetch.Fetcher import coil.fetch.SourceResult +import coil.size.PixelSize +import coil.size.Size import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.MediaMetadata import com.google.android.exoplayer2.MetadataRetriever @@ -199,7 +201,7 @@ abstract class AuxioFetcher : Fetcher { * Create a mosaic image from multiple streams of image data, Code adapted from Phonograph * https://github.com/kabouzeid/Phonograph */ - protected fun createMosaic(context: Context, streams: List): FetchResult? { + protected fun createMosaic(context: Context, streams: List, size: Size): FetchResult? { if (streams.size < 4) { return streams.getOrNull(0)?.let { stream -> return SourceResult( @@ -210,11 +212,17 @@ abstract class AuxioFetcher : Fetcher { } } - // Use a fixed 512x512 canvas for the mosaics. Preferably we would adapt this mosaic to - // target ImageView size, but Coil seems to start image loading before we can even get - // a width/height for the view, making that impractical. + // Use whatever size coil gives us to create the mosaic. If there is no size, default + // to a 512x512 mosaic. + val mosaicSize = when (size) { + is PixelSize -> size + else -> PixelSize(512, 512) + } + + val increment = PixelSize(mosaicSize.width / 2, mosaicSize.height / 2) + val mosaicBitmap = Bitmap.createBitmap( - MOSAIC_BITMAP_SIZE, MOSAIC_BITMAP_SIZE, Bitmap.Config.ARGB_8888 + mosaicSize.width, mosaicSize.height, Bitmap.Config.ARGB_8888 ) val canvas = Canvas(mosaicBitmap) @@ -225,36 +233,34 @@ abstract class AuxioFetcher : Fetcher { // For each stream, create a bitmap scaled to 1/4th of the mosaics combined size // and place it on a corner of the canvas. for (stream in streams) { - if (y == MOSAIC_BITMAP_SIZE) { + if (y == mosaicSize.height) { break } val bitmap = Bitmap.createScaledBitmap( BitmapFactory.decodeStream(stream), - MOSAIC_BITMAP_INCREMENT, - MOSAIC_BITMAP_INCREMENT, + increment.width, + increment.height, true ) canvas.drawBitmap(bitmap, x.toFloat(), y.toFloat(), null) - x += MOSAIC_BITMAP_INCREMENT + x += increment.width - if (x == MOSAIC_BITMAP_SIZE) { + if (x == mosaicSize.width) { x = 0 - y += MOSAIC_BITMAP_INCREMENT + y += increment.height } } + // It's way easier to map this into a drawable then try to serialize it into an + // BufferedSource. Just make sure we mark it as "sampled" so Coil doesn't try to + // load low-res mosaics into high-res ImageViews. return DrawableResult( drawable = mosaicBitmap.toDrawable(context.resources), - isSampled = false, + isSampled = true, dataSource = DataSource.DISK ) } - - companion object { - private const val MOSAIC_BITMAP_SIZE = 512 - private const val MOSAIC_BITMAP_INCREMENT = 256 - } } diff --git a/app/src/main/java/org/oxycblt/auxio/coil/Fetchers.kt b/app/src/main/java/org/oxycblt/auxio/coil/Fetchers.kt index 41dbfe685..7ba606a7c 100644 --- a/app/src/main/java/org/oxycblt/auxio/coil/Fetchers.kt +++ b/app/src/main/java/org/oxycblt/auxio/coil/Fetchers.kt @@ -26,6 +26,7 @@ import coil.fetch.FetchResult import coil.fetch.Fetcher import coil.fetch.SourceResult import coil.request.Options +import coil.size.Size import okio.buffer import okio.source import org.oxycblt.auxio.music.Album @@ -71,19 +72,20 @@ class AlbumArtFetcher private constructor( */ class ArtistImageFetcher private constructor( private val context: Context, - private val artist: Artist + private val size: Size, + private val artist: Artist, ) : AuxioFetcher() { override suspend fun fetch(): FetchResult? { val results = artist.albums.mapAtMost(4) { album -> fetchArt(context, album) } - return createMosaic(context, results) + return createMosaic(context, results, size) } class Factory : Fetcher.Factory { override fun create(data: Artist, options: Options, imageLoader: ImageLoader): Fetcher { - return ArtistImageFetcher(options.context, data) + return ArtistImageFetcher(options.context, options.size, data) } } } @@ -94,7 +96,8 @@ class ArtistImageFetcher private constructor( */ class GenreImageFetcher private constructor( private val context: Context, - private val genre: Genre + private val size: Size, + private val genre: Genre, ) : AuxioFetcher() { override suspend fun fetch(): FetchResult? { val albums = genre.songs.groupBy { it.album }.keys @@ -102,12 +105,12 @@ class GenreImageFetcher private constructor( fetchArt(context, album) } - return createMosaic(context, results) + return createMosaic(context, results, size) } class Factory : Fetcher.Factory { override fun create(data: Genre, options: Options, imageLoader: ImageLoader): Fetcher { - return GenreImageFetcher(options.context, data) + return GenreImageFetcher(options.context, options.size, data) } } }