mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-10-19 22:17:29 +02:00
fix(artwork): preserve thumbnail aspect ratio during decoding
This commit is contained in:
1 parent
26d713915f
commit
c91d12dbf0
3 files changed
+96
-7
No files matched your search
@@ -644,7 +644,6 @@ extension _QueueTabItemWidgets on _QueueTabState {
|
||||
width: size,
|
||||
height: size,
|
||||
memCacheWidth: cacheSize,
|
||||
memCacheHeight: cacheSize,
|
||||
placeholder: (context, url) => buildPlaceholder(),
|
||||
errorWidget: (context, url, error) => buildPlaceholder(),
|
||||
);
|
||||
@@ -680,7 +679,6 @@ extension _QueueTabItemWidgets on _QueueTabState {
|
||||
height: size,
|
||||
fit: BoxFit.cover,
|
||||
cacheWidth: cacheSize,
|
||||
cacheHeight: cacheSize,
|
||||
gaplessPlayback: true,
|
||||
frameBuilder: (context, child, frame, wasSynchronouslyLoaded) =>
|
||||
fadeInFileImage(child, frame, wasSynchronouslyLoaded),
|
||||
@@ -696,7 +694,6 @@ extension _QueueTabItemWidgets on _QueueTabState {
|
||||
width: size,
|
||||
height: size,
|
||||
memCacheWidth: cacheSize,
|
||||
memCacheHeight: cacheSize,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
placeholder: (context, url) => buildPlaceholder(),
|
||||
errorWidget: (context, url, error) => buildPlaceholder(),
|
||||
@@ -714,7 +711,6 @@ extension _QueueTabItemWidgets on _QueueTabState {
|
||||
height: size,
|
||||
fit: BoxFit.cover,
|
||||
cacheWidth: cacheSize,
|
||||
cacheHeight: cacheSize,
|
||||
gaplessPlayback: true,
|
||||
frameBuilder: (context, child, frame, wasSynchronouslyLoaded) =>
|
||||
fadeInFileImage(child, frame, wasSynchronouslyLoaded),
|
||||
|
||||
@@ -56,9 +56,15 @@ class CachedCoverImage extends StatelessWidget {
|
||||
|
||||
Widget _buildImage(BuildContext context, BoxConstraints constraints) {
|
||||
var autoMemCacheWidth =
|
||||
memCacheWidth ?? _cacheExtentForLogicalSize(context, width);
|
||||
var autoMemCacheHeight =
|
||||
memCacheHeight ?? _cacheExtentForLogicalSize(context, height);
|
||||
memCacheWidth ??
|
||||
(memCacheHeight == null
|
||||
? _cacheExtentForLogicalSize(context, width)
|
||||
: null);
|
||||
// ResizeImage stretches the decoded bitmap when both axes are supplied.
|
||||
// Keep one decode axis so BoxFit can crop the original proportions.
|
||||
var autoMemCacheHeight = autoMemCacheWidth == null
|
||||
? memCacheHeight ?? _cacheExtentForLogicalSize(context, height)
|
||||
: null;
|
||||
if (autoMemCacheWidth == null && autoMemCacheHeight == null) {
|
||||
// Infer one axis to preserve the source aspect ratio and respect any
|
||||
// explicit decode override used by large artwork/header consumers.
|
||||
|
||||
@@ -45,6 +45,93 @@ void main() {
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('explicit height decode keeps one axis even with square bounds', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.pumpWidget(
|
||||
const MaterialApp(
|
||||
home: Center(
|
||||
child: CachedCoverImage(
|
||||
imageUrl: 'https://example.invalid/height-cover.png',
|
||||
width: 56,
|
||||
height: 56,
|
||||
memCacheHeight: 128,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
final image = tester.widget<CachedNetworkImage>(
|
||||
find.byType(CachedNetworkImage),
|
||||
);
|
||||
expect(image.memCacheWidth, isNull);
|
||||
expect(image.memCacheHeight, 128);
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
});
|
||||
|
||||
for (final override in [false, true]) {
|
||||
testWidgets(
|
||||
'playlist thumbnail preserves network proportions ($override)',
|
||||
(tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: MediaQuery(
|
||||
data: const MediaQueryData(devicePixelRatio: 2),
|
||||
child: Center(
|
||||
child: SizedBox.square(
|
||||
dimension: 56,
|
||||
child: LocalOrNetworkCoverImage(
|
||||
url: 'https://example.invalid/playlist-cover.png',
|
||||
width: 56,
|
||||
height: 56,
|
||||
networkCacheWidth: override ? 112 : null,
|
||||
placeholder: (_) => const SizedBox(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
final image = tester.widget<CachedNetworkImage>(
|
||||
find.byType(CachedNetworkImage),
|
||||
);
|
||||
expect(image.memCacheWidth, 112);
|
||||
expect(image.memCacheHeight, isNull);
|
||||
expect(image.fit, BoxFit.cover);
|
||||
expect(
|
||||
tester.getSize(find.byType(LocalOrNetworkCoverImage)),
|
||||
const Size(56, 56),
|
||||
);
|
||||
// Exercise the same Flutter decode sizing used by CachedNetworkImage,
|
||||
// with non-square artwork rather than checking only the widget bounds.
|
||||
final directory = await tester.runAsync(() async {
|
||||
final directory = await Directory.systemTemp.createTemp(
|
||||
'playlist-cover-',
|
||||
);
|
||||
await _writeCover(directory, const Size(320, 160));
|
||||
return directory;
|
||||
});
|
||||
try {
|
||||
final decoded = await tester.runAsync(() async {
|
||||
final bytes = await File(
|
||||
'${directory!.path}/cover.png',
|
||||
).readAsBytes();
|
||||
return _decodedImageSize(
|
||||
ResizeImage.resizeIfNeeded(
|
||||
image.memCacheWidth,
|
||||
image.memCacheHeight,
|
||||
MemoryImage(bytes),
|
||||
),
|
||||
);
|
||||
});
|
||||
expect(decoded, const Size(112, 56));
|
||||
} finally {
|
||||
await tester.runAsync(() => directory!.delete(recursive: true));
|
||||
}
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
for (final scenario in [
|
||||
(
|
||||
name: 'explicit row size',
|
||||
|
||||
Reference in new issue
Block a user