[photos][mobile] Add support for viewing HEIC images with proXDR (#1171)
## Description New Oneplus devices have a "ProXDR" feature when viewing images. These images (when in HEIC format) decode fine on devices that supports ProXDR, but fails to decode on other devices. So if decoding fails, we convert it to a JPEG and use that image for viewing it. ## Tests - [x] Tries converting to jpeg only if decoding fails - [x] If converting also fails, the behaviour remains the same as before when decoding fails.
This commit is contained in:
commit
3e917bd855
1 changed files with 64 additions and 18 deletions
|
@ -3,6 +3,7 @@ import 'dart:io';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import "package:flutter_image_compress/flutter_image_compress.dart";
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photo_view/photo_view.dart';
|
||||
import 'package:photos/core/cache/thumbnail_in_memory_cache.dart';
|
||||
|
@ -50,6 +51,7 @@ class _ZoomableImageState extends State<ZoomableImage> {
|
|||
bool _loadedLargeThumbnail = false;
|
||||
bool _loadingFinalImage = false;
|
||||
bool _loadedFinalImage = false;
|
||||
bool _convertToSupportedFormat = false;
|
||||
ValueChanged<PhotoViewScaleState>? _scaleStateChangedCallback;
|
||||
bool _isZooming = false;
|
||||
PhotoViewController _photoViewController = PhotoViewController();
|
||||
|
@ -194,11 +196,8 @@ class _ZoomableImageState extends State<ZoomableImage> {
|
|||
_loadingFinalImage = true;
|
||||
getFileFromServer(_photo).then((file) {
|
||||
if (file != null) {
|
||||
_onFinalImageLoaded(
|
||||
Image.file(
|
||||
file,
|
||||
gaplessPlayback: true,
|
||||
).image,
|
||||
_onFileLoaded(
|
||||
file,
|
||||
);
|
||||
} else {
|
||||
_loadingFinalImage = false;
|
||||
|
@ -239,7 +238,9 @@ class _ZoomableImageState extends State<ZoomableImage> {
|
|||
_isGIF(), // since on iOS GIFs playback only when origin-files are loaded
|
||||
).then((file) {
|
||||
if (file != null && file.existsSync()) {
|
||||
_onFinalImageLoaded(Image.file(file).image);
|
||||
_onFileLoaded(
|
||||
file,
|
||||
);
|
||||
} else {
|
||||
_logger.info("File was deleted " + _photo.toString());
|
||||
if (_photo.uploadedFileID != null) {
|
||||
|
@ -277,24 +278,45 @@ class _ZoomableImageState extends State<ZoomableImage> {
|
|||
}
|
||||
}
|
||||
|
||||
void _onFinalImageLoaded(ImageProvider imageProvider) {
|
||||
void _onFileLoaded(File file) {
|
||||
final imageProvider = Image.file(
|
||||
file,
|
||||
gaplessPlayback: true,
|
||||
).image;
|
||||
|
||||
if (mounted) {
|
||||
precacheImage(imageProvider, context).then((value) async {
|
||||
if (mounted) {
|
||||
await _updatePhotoViewController(
|
||||
previewImageProvider: _imageProvider,
|
||||
finalImageProvider: imageProvider,
|
||||
);
|
||||
setState(() {
|
||||
_imageProvider = imageProvider;
|
||||
_loadedFinalImage = true;
|
||||
_logger.info("Final image loaded");
|
||||
});
|
||||
precacheImage(
|
||||
imageProvider,
|
||||
context,
|
||||
onError: (exception, _) async {
|
||||
_logger
|
||||
.info(exception.toString() + ". Filename: ${_photo.displayName}");
|
||||
if (exception.toString().contains(
|
||||
"Codec failed to produce an image, possibly due to invalid image data",
|
||||
)) {
|
||||
unawaited(_loadInSupportedFormat(file));
|
||||
}
|
||||
},
|
||||
).then((value) {
|
||||
if (mounted && !_loadedFinalImage && !_convertToSupportedFormat) {
|
||||
_updateViewWithFinalImage(imageProvider);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _updateViewWithFinalImage(ImageProvider imageProvider) async {
|
||||
await _updatePhotoViewController(
|
||||
previewImageProvider: _imageProvider,
|
||||
finalImageProvider: imageProvider,
|
||||
);
|
||||
setState(() {
|
||||
_imageProvider = imageProvider;
|
||||
_loadedFinalImage = true;
|
||||
_logger.info("Final image loaded");
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _updatePhotoViewController({
|
||||
required ImageProvider? previewImageProvider,
|
||||
required ImageProvider finalImageProvider,
|
||||
|
@ -348,4 +370,28 @@ class _ZoomableImageState extends State<ZoomableImage> {
|
|||
}
|
||||
|
||||
bool _isGIF() => _photo.displayName.toLowerCase().endsWith(".gif");
|
||||
|
||||
Future<void> _loadInSupportedFormat(File file) async {
|
||||
_logger.info("Compressing ${_photo.displayName} to viewable format");
|
||||
_convertToSupportedFormat = true;
|
||||
|
||||
final compressedFile =
|
||||
await FlutterImageCompress.compressWithFile(file.path);
|
||||
|
||||
if (compressedFile != null) {
|
||||
final imageProvider = MemoryImage(compressedFile);
|
||||
|
||||
unawaited(
|
||||
precacheImage(imageProvider, context).then((value) {
|
||||
if (mounted) {
|
||||
_updateViewWithFinalImage(imageProvider);
|
||||
}
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
_logger.severe(
|
||||
"Failed to compress image ${_photo.displayName} to viewable format",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue