97 lines
2.6 KiB
Dart
97 lines
2.6 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:image/image.dart' as img;
|
|
import 'package:path/path.dart' as p;
|
|
import 'package:path_provider/path_provider.dart';
|
|
|
|
import 'gallery_source.dart';
|
|
|
|
const int kPhotoUploadLimitBytes = 5 * 1024 * 1024;
|
|
|
|
const int _photoMaxDimension = 2560;
|
|
const int _photoJpegQuality = 85;
|
|
const int _photoTargetBytes = 4700 * 1024;
|
|
|
|
const Set<String> _heicExtensions = {'.heic', '.heif'};
|
|
|
|
bool isHeicPath(String path) {
|
|
final dot = path.lastIndexOf('.');
|
|
if (dot < 0) return false;
|
|
return _heicExtensions.contains(path.substring(dot).toLowerCase());
|
|
}
|
|
|
|
Future<File> optimizePhotoForUpload(File source, {GalleryItem? item}) async {
|
|
final heic = isHeicPath(source.path);
|
|
var length = 0;
|
|
try {
|
|
length = await source.length();
|
|
} catch (_) {}
|
|
|
|
if (!heic && length > 0 && length <= kPhotoUploadLimitBytes) {
|
|
return source;
|
|
}
|
|
|
|
if (item != null) {
|
|
final native = await item.encodeForUpload(
|
|
maxDimension: _photoMaxDimension,
|
|
quality: _photoJpegQuality,
|
|
);
|
|
if (native != null) return writePhotoJpeg(native);
|
|
}
|
|
|
|
final fallback = await _encodeWithImagePackage(source);
|
|
return fallback ?? source;
|
|
}
|
|
|
|
Future<File?> _encodeWithImagePackage(File source) async {
|
|
Uint8List bytes;
|
|
try {
|
|
bytes = await source.readAsBytes();
|
|
} catch (_) {
|
|
return null;
|
|
}
|
|
final jpeg = await compute(_encodePhotoIsolate, (
|
|
bytes,
|
|
_photoMaxDimension,
|
|
_photoJpegQuality,
|
|
_photoTargetBytes,
|
|
));
|
|
if (jpeg == null) return null;
|
|
return writePhotoJpeg(jpeg);
|
|
}
|
|
|
|
Uint8List? _encodePhotoIsolate((Uint8List, int, int, int) args) {
|
|
final (bytes, maxDim, quality, target) = args;
|
|
final decoded = img.decodeImage(bytes);
|
|
if (decoded == null) return null;
|
|
final oriented = img.bakeOrientation(decoded);
|
|
final scaled = oriented.width > maxDim || oriented.height > maxDim
|
|
? img.copyResize(
|
|
oriented,
|
|
width: oriented.width >= oriented.height ? maxDim : null,
|
|
height: oriented.height > oriented.width ? maxDim : null,
|
|
interpolation: img.Interpolation.average,
|
|
)
|
|
: oriented;
|
|
var quality0 = quality;
|
|
var out = img.encodeJpg(scaled, quality: quality0);
|
|
while (out.lengthInBytes > target && quality0 > 40) {
|
|
quality0 -= 10;
|
|
out = img.encodeJpg(scaled, quality: quality0);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
Future<File> writePhotoJpeg(Uint8List bytes) async {
|
|
final dir = await getTemporaryDirectory();
|
|
final file = File(
|
|
p.join(
|
|
dir.path,
|
|
'komet_photo_${DateTime.now().microsecondsSinceEpoch}.jpg',
|
|
),
|
|
);
|
|
await file.writeAsBytes(bytes, flush: true);
|
|
return file;
|
|
}
|