119 lines
3.8 KiB
Dart
119 lines
3.8 KiB
Dart
import 'dart:ui';
|
|
|
|
import 'package:aves/model/entry/entry.dart';
|
|
import 'package:aves/model/settings/settings.dart';
|
|
import 'package:aves/ref/mime_types.dart';
|
|
import 'package:aves/utils/android_file_utils.dart';
|
|
|
|
extension ExtraAvesEntryProps on AvesEntry {
|
|
String get mimeTypeAnySubtype => mimeType.replaceAll(RegExp('/.*'), '/*');
|
|
|
|
bool get isSvg => mimeType == MimeTypes.svg;
|
|
|
|
// guess whether this is a photo, according to file type (used as a hint to e.g. display megapixels)
|
|
bool get isPhoto => [MimeTypes.heic, MimeTypes.heif, MimeTypes.jpeg, MimeTypes.tiff].contains(mimeType) || isRaw;
|
|
|
|
// Android's `BitmapRegionDecoder` documentation states that "only the JPEG and PNG formats are supported"
|
|
// but in practice (tested on API 25, 27, 29), it successfully decodes the formats listed below,
|
|
// and it actually fails to decode GIF, DNG and animated WEBP. Other formats were not tested.
|
|
bool get _supportedByBitmapRegionDecoder =>
|
|
[
|
|
MimeTypes.heic,
|
|
MimeTypes.heif,
|
|
MimeTypes.jpeg,
|
|
MimeTypes.png,
|
|
MimeTypes.webp,
|
|
MimeTypes.arw,
|
|
MimeTypes.cr2,
|
|
MimeTypes.nef,
|
|
MimeTypes.nrw,
|
|
MimeTypes.orf,
|
|
MimeTypes.pef,
|
|
MimeTypes.raf,
|
|
MimeTypes.rw2,
|
|
MimeTypes.srw,
|
|
].contains(mimeType) &&
|
|
!isAnimated;
|
|
|
|
bool get supportTiling => _supportedByBitmapRegionDecoder || mimeType == MimeTypes.tiff;
|
|
|
|
bool get useTiles => supportTiling && (width > 4096 || height > 4096);
|
|
|
|
bool get isRaw => MimeTypes.rawImages.contains(mimeType);
|
|
|
|
bool get isImage => MimeTypes.isImage(mimeType);
|
|
|
|
bool get isVideo => MimeTypes.isVideo(mimeType);
|
|
|
|
bool get isAnimated => catalogMetadata?.isAnimated ?? false;
|
|
|
|
bool get isGeotiff => catalogMetadata?.isGeotiff ?? false;
|
|
|
|
bool get is360 => catalogMetadata?.is360 ?? false;
|
|
|
|
bool get isMediaStoreContent => uri.startsWith('content://media/');
|
|
|
|
bool get isMediaStoreMediaContent => isMediaStoreContent && {'/external/images/', '/external/video/'}.any(uri.contains);
|
|
|
|
bool get isVaultContent => path?.startsWith(androidFileUtils.vaultRoot) ?? false;
|
|
|
|
bool get canEdit => !settings.isReadOnly && path != null && !trashed && (isMediaStoreContent || isVaultContent);
|
|
|
|
bool get canEditDate => canEdit && (canEditExif || canEditXmp);
|
|
|
|
bool get canEditLocation => canEdit && (canEditExif || mimeType == MimeTypes.mp4);
|
|
|
|
bool get canEditTitleDescription => canEdit && canEditXmp;
|
|
|
|
bool get canEditRating => canEdit && canEditXmp;
|
|
|
|
bool get canEditTags => canEdit && canEditXmp;
|
|
|
|
bool get canRotate => canEdit && (canEditExif || mimeType == MimeTypes.mp4);
|
|
|
|
bool get canFlip => canEdit && canEditExif;
|
|
|
|
bool get canEditExif => MimeTypes.canEditExif(mimeType);
|
|
|
|
bool get canEditIptc => MimeTypes.canEditIptc(mimeType);
|
|
|
|
bool get canEditXmp => MimeTypes.canEditXmp(mimeType);
|
|
|
|
bool get canRemoveMetadata => MimeTypes.canRemoveMetadata(mimeType);
|
|
|
|
static const ratioSeparator = '\u2236';
|
|
static const resolutionSeparator = ' \u00D7 ';
|
|
|
|
bool get isSized => width > 0 && height > 0;
|
|
|
|
String get resolutionText {
|
|
final ws = width;
|
|
final hs = height;
|
|
return isRotated ? '$hs$resolutionSeparator$ws' : '$ws$resolutionSeparator$hs';
|
|
}
|
|
|
|
String get aspectRatioText {
|
|
if (width > 0 && height > 0) {
|
|
final gcd = width.gcd(height);
|
|
final w = width ~/ gcd;
|
|
final h = height ~/ gcd;
|
|
return isRotated ? '$h$ratioSeparator$w' : '$w$ratioSeparator$h';
|
|
} else {
|
|
return '?$ratioSeparator?';
|
|
}
|
|
}
|
|
|
|
Size videoDisplaySize(double sar) {
|
|
final size = displaySize;
|
|
if (sar != 1) {
|
|
final dar = displayAspectRatio * sar;
|
|
final w = size.width;
|
|
final h = size.height;
|
|
if (w >= h) return Size(w, w / dar);
|
|
if (h > w) return Size(h * dar, h);
|
|
}
|
|
return size;
|
|
}
|
|
|
|
int get megaPixels => (width * height / 1000000).round();
|
|
}
|