From bc6b9968e85707082c2bcfb3a309f7f305a82c86 Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Tue, 3 Feb 2026 01:39:12 +0100 Subject: [PATCH] fix: blurry cover images --- lib/screens/discover_screen.dart | 5 +++-- lib/screens/media_detail_screen.dart | 5 +++-- lib/utils/plex_image_helper.dart | 23 ++++++++++++++++++++--- lib/widgets/plex_optimized_image.dart | 2 +- 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/lib/screens/discover_screen.dart b/lib/screens/discover_screen.dart index bffcf2ad..1ced379f 100644 --- a/lib/screens/discover_screen.dart +++ b/lib/screens/discover_screen.dart @@ -1375,12 +1375,13 @@ class _DiscoverScreenState extends State builder: (context) { final client = _getClientForItem(heroItem); final mediaQuery = MediaQuery.of(context); + final dpr = PlexImageHelper.effectiveDevicePixelRatio(context); final imageUrl = PlexImageHelper.getOptimizedImageUrl( client: client, thumbPath: heroItem.art ?? heroItem.grandparentArt, maxWidth: mediaQuery.size.width, maxHeight: mediaQuery.size.height * 0.7, - devicePixelRatio: mediaQuery.devicePixelRatio, + devicePixelRatio: dpr, imageType: ImageType.art, ); @@ -1442,7 +1443,7 @@ class _DiscoverScreenState extends State child: Builder( builder: (context) { final client = _getClientForItem(heroItem); - final dpr = MediaQuery.of(context).devicePixelRatio; + final dpr = PlexImageHelper.effectiveDevicePixelRatio(context); final logoUrl = PlexImageHelper.getOptimizedImageUrl( client: client, thumbPath: heroItem.clearLogo, diff --git a/lib/screens/media_detail_screen.dart b/lib/screens/media_detail_screen.dart index b2608558..17354e72 100644 --- a/lib/screens/media_detail_screen.dart +++ b/lib/screens/media_detail_screen.dart @@ -1283,12 +1283,13 @@ class _MediaDetailScreenState extends State with WatchStateAw // Online - use network image final client = _getClientForMetadata(context); final mediaQuery = MediaQuery.of(context); + final dpr = PlexImageHelper.effectiveDevicePixelRatio(context); final imageUrl = PlexImageHelper.getOptimizedImageUrl( client: client, thumbPath: metadata.art, maxWidth: mediaQuery.size.width, maxHeight: mediaQuery.size.height * 0.6, - devicePixelRatio: mediaQuery.devicePixelRatio, + devicePixelRatio: dpr, imageType: ImageType.art, ); @@ -1366,7 +1367,7 @@ class _MediaDetailScreenState extends State with WatchStateAw // Online - use network image final client = _getClientForMetadata(context); - final dpr = MediaQuery.of(context).devicePixelRatio; + final dpr = PlexImageHelper.effectiveDevicePixelRatio(context); final logoUrl = PlexImageHelper.getOptimizedImageUrl( client: client, thumbPath: metadata.clearLogo, diff --git a/lib/utils/plex_image_helper.dart b/lib/utils/plex_image_helper.dart index 5aea2ad7..6bfc93df 100644 --- a/lib/utils/plex_image_helper.dart +++ b/lib/utils/plex_image_helper.dart @@ -1,4 +1,6 @@ import 'dart:math'; +import 'dart:ui'; +import 'package:flutter/widgets.dart'; import '../services/plex_client.dart'; import 'plex_url_helper.dart'; @@ -32,6 +34,21 @@ class PlexImageHelper { ); } + /// Computes an effective device pixel ratio that accounts for displays where + /// the platform-reported DPR doesn't reflect the true physical density + /// (common on Linux X11 with compositor scaling). + static double effectiveDevicePixelRatio(BuildContext context) { + final reportedDpr = MediaQuery.of(context).devicePixelRatio; + try { + final displayWidth = View.of(context).display.size.width; + // Scale quality with display resolution: 1920px = baseline (1.0x) + final displayBasedDpr = (displayWidth / 1920).clamp(1.0, 3.0); + return max(reportedDpr, displayBasedDpr); + } catch (_) { + return reportedDpr; + } + } + /// Calculates optimal image dimensions based on image type and constraints static (int width, int height) calculateOptimalDimensions({ required double maxWidth, @@ -74,9 +91,9 @@ class PlexImageHelper { return roundDimensions(size, size); case ImageType.poster: - // For posters, maintain 2:3 aspect ratio - final calculatedWidth = min(targetWidth, targetHeight / (2 / 3)); - final calculatedHeight = calculatedWidth * (2 / 3); + // For posters, maintain 2:3 aspect ratio (width:height) + final calculatedWidth = min(targetWidth, targetHeight * (2 / 3)); + final calculatedHeight = calculatedWidth * (3 / 2); return roundDimensions(calculatedWidth, calculatedHeight); } } diff --git a/lib/widgets/plex_optimized_image.dart b/lib/widgets/plex_optimized_image.dart index 19d28483..bdd30d8c 100644 --- a/lib/widgets/plex_optimized_image.dart +++ b/lib/widgets/plex_optimized_image.dart @@ -258,7 +258,7 @@ class PlexOptimizedImage extends StatelessWidget { return LayoutBuilder( builder: (context, constraints) { - final devicePixelRatio = MediaQuery.of(context).devicePixelRatio; + final devicePixelRatio = PlexImageHelper.effectiveDevicePixelRatio(context); // Calculate effective constraints with safe fallbacks final effectiveWidth = resolvedDimension(width, constraints.maxWidth, 300.0);