Revert "[mob] Crop image instead of using scale and translate transforms on OG image in CroppedFaceImageView widget"

This reverts commit b022ef6d1e.
This commit is contained in:
ashilkn 2024-04-26 12:56:12 +05:30
parent b256bb2757
commit a0e9913f43
2 changed files with 76 additions and 103 deletions

View file

@ -1,14 +1,11 @@
import 'dart:developer' show log;
import "dart:io" show File;
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import "package:image/image.dart" as img;
import "package:logging/logging.dart";
import "package:photos/face/model/face.dart";
import "package:photos/models/file/file.dart";
import "package:photos/ui/viewer/file/thumbnail_widget.dart";
import "package:photos/utils/file_util.dart";
import "package:photos/utils/image_util.dart";
class CroppedFaceInfo {
final Image image;
@ -24,7 +21,7 @@ class CroppedFaceInfo {
});
}
class CroppedFaceImageView extends StatefulWidget {
class CroppedFaceImageView extends StatelessWidget {
final EnteFile enteFile;
final Face face;
@ -34,75 +31,86 @@ class CroppedFaceImageView extends StatefulWidget {
required this.face,
}) : super(key: key);
@override
CroppedFaceImageViewState createState() => CroppedFaceImageViewState();
}
class CroppedFaceImageViewState extends State<CroppedFaceImageView> {
ui.Image? _image;
final _logger = Logger("CroppedFaceImageView");
@override
void initState() {
super.initState();
_loadImage();
}
@override
void dispose() {
super.dispose();
_image?.dispose();
}
Future<void> _loadImage() async {
final image = await getImage();
if (mounted) {
setState(() {
_image = image;
});
}
}
@override
Widget build(BuildContext context) {
return _image != null
? LayoutBuilder(
builder: (context, constraints) {
return RawImage(
image: _image!,
return FutureBuilder(
future: getImage(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return LayoutBuilder(
builder: ((context, constraints) {
final double imageAspectRatio = enteFile.width / enteFile.height;
final Image image = snapshot.data!;
final double viewWidth = constraints.maxWidth;
final double viewHeight = constraints.maxHeight;
final faceBox = face.detection.box;
final double relativeFaceCenterX =
faceBox.xMin + faceBox.width / 2;
final double relativeFaceCenterY =
faceBox.yMin + faceBox.height / 2;
const double desiredFaceHeightRelativeToWidget = 8 / 10;
final double scale =
(1 / faceBox.height) * desiredFaceHeightRelativeToWidget;
final double widgetCenterX = viewWidth / 2;
final double widgetCenterY = viewHeight / 2;
final double widgetAspectRatio = viewWidth / viewHeight;
final double imageToWidgetRatio =
imageAspectRatio / widgetAspectRatio;
double offsetX =
(widgetCenterX - relativeFaceCenterX * viewWidth) * scale;
double offsetY =
(widgetCenterY - relativeFaceCenterY * viewHeight) * scale;
if (imageAspectRatio < widgetAspectRatio) {
// Landscape Image: Adjust offsetX more conservatively
offsetX = offsetX * imageToWidgetRatio;
} else {
// Portrait Image: Adjust offsetY more conservatively
offsetY = offsetY / imageToWidgetRatio;
}
return ClipRRect(
borderRadius: const BorderRadius.all(Radius.elliptical(16, 12)),
child: Transform.translate(
offset: Offset(
offsetX,
offsetY,
),
child: Transform.scale(
scale: scale,
child: image,
),
),
);
}),
);
} else {
if (snapshot.hasError) {
log('Error getting cover face for person: ${snapshot.error}');
}
return ThumbnailWidget(
enteFile,
);
}
},
)
: ThumbnailWidget(widget.enteFile);
);
}
Future<ui.Image?> getImage() async {
try {
final faceBox = widget.face.detection.box;
final File? ioFile = await getFile(widget.enteFile);
Future<Image?> getImage() async {
final File? ioFile = await getFile(enteFile);
if (ioFile == null) {
return null;
}
final image = await img.decodeImageFile(ioFile.path);
final imageData = await ioFile.readAsBytes();
final image = Image.memory(imageData, fit: BoxFit.contain);
if (image == null) {
throw Exception("Failed decoding image file ${widget.enteFile.title}}");
}
final croppedImage = img.copyCrop(
image,
x: (image.width * faceBox.xMin).round(),
y: (image.height * faceBox.yMin).round(),
width: (image.width * faceBox.width).round(),
height: (image.height * faceBox.height).round(),
antialias: false,
);
return convertImageToFlutterUi(croppedImage);
} catch (e, s) {
_logger.severe("Error getting image", e, s);
return null;
}
return image;
}
}

View file

@ -1,8 +1,6 @@
import 'dart:async';
import 'dart:ui' as ui;
import 'package:flutter/widgets.dart';
import 'package:image/image.dart' as img;
Future<ImageInfo> getImageInfo(ImageProvider imageProvider) {
final completer = Completer<ImageInfo>();
@ -16,36 +14,3 @@ Future<ImageInfo> getImageInfo(ImageProvider imageProvider) {
completer.future.whenComplete(() => imageStream.removeListener(listener));
return completer.future;
}
///https://github.com/brendan-duncan/image/blob/main/doc/flutter.md
Future<ui.Image> convertImageToFlutterUi(img.Image image) async {
if (image.format != img.Format.uint8 || image.numChannels != 4) {
final cmd = img.Command()
..image(image)
..convert(format: img.Format.uint8, numChannels: 4);
final rgba8 = await cmd.getImageThread();
if (rgba8 != null) {
image = rgba8;
}
}
final ui.ImmutableBuffer buffer =
await ui.ImmutableBuffer.fromUint8List(image.toUint8List());
final ui.ImageDescriptor id = ui.ImageDescriptor.raw(
buffer,
height: image.height,
width: image.width,
pixelFormat: ui.PixelFormat.rgba8888,
);
final ui.Codec codec = await id.instantiateCodec(
targetHeight: image.height,
targetWidth: image.width,
);
final ui.FrameInfo fi = await codec.getNextFrame();
final ui.Image uiImage = fi.image;
return uiImage;
}