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.
This commit is contained in:
OxygenCobalt 2021-11-26 11:12:01 -07:00
parent 22c54993ed
commit c0b2dee3ff
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
2 changed files with 32 additions and 23 deletions

View file

@ -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<InputStream>): FetchResult? {
protected fun createMosaic(context: Context, streams: List<InputStream>, 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
}
}

View file

@ -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<Artist> {
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<Genre> {
override fun create(data: Genre, options: Options, imageLoader: ImageLoader): Fetcher {
return GenreImageFetcher(options.context, data)
return GenreImageFetcher(options.context, options.size, data)
}
}
}