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:
parent
22c54993ed
commit
c0b2dee3ff
2 changed files with 32 additions and 23 deletions
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue