9

I've managed to rotate images to landscape/portrait after selecting them from Image picker (gallery/camera) ..

This works fine, and will continue set new images to my desired orientation ..

However, I'm trying to use the same method to rotate an already selected/set image and it doesn't work ..

Here is the logic I'm using:

import 'package:image/image.dart' as img;

  void _rotateImage(File file) async {
    print('>>> rotating image');
    try {
      List<int> imageBytes = await file.readAsBytes();
      final originalImage = img.decodeImage(imageBytes);
      print('>>> original width: ${originalImage.width}');
      img.Image fixedImage;
      fixedImage = img.copyRotate(originalImage, 90);
      print('>>> fixed width: ${fixedImage.width}');
      final fixedFile = await file.writeAsBytes(img.encodeJpg(fixedImage));
      setState(() {
        print('>>> setting state');
        _image = fixedFile;
      });
    } catch (e) {
      print(e);
    }
  }

I can even see that the image is getting rotated before setting state, but it still doesn't update on screen (this is showing two attempts, not multiple in one)

I/flutter (18314): >>> rotating image
I/flutter (18314): >>> original width: 450
I/flutter (18314): >>> fixed width: 360
I/flutter (18314): >>> setting state
I/flutter (18314): >>> rotating image
I/flutter (18314): >>> original width: 360
I/flutter (18314): >>> fixed width: 450
I/flutter (18314): >>> setting state

Does anyone has any idea why this method works when picking a new image from the camera/gallery but won't when using a file that's already in the state?

[EDIT] I thought it may be something to do with the same file path being used. So I added this code below and although it makes the image refresh, for a fraction of a second, it still doesn't show the rotated image [/EDIT]

import 'package:image/image.dart' as img;

  void _rotateImage(File file) async {
    try {
      Random random = new Random();
      int randomNumber = random.nextInt(1000000);

      final newFile = await file.copy(
          '/data/user/0/!PRIVATE!/cache/rotatedImage$randomNumber.jpg');

      List<int> imageBytes = await newFile.readAsBytes();

      final originalImage = img.decodeImage(imageBytes);

      img.Image fixedImage;
      fixedImage = img.copyRotate(originalImage, 90);

      final fixedFile = await newFile.writeAsBytes(img.encodeJpg(fixedImage),
          mode: FileMode.append, flush: true);

      setState(() {
        _image = fixedFile;
      });
    } catch (e) {
      print(e);
    }
  }

Below is some code to show what's happening when selecting an image and choosing to rotate

import 'package:image/image.dart' as img;

  void _pickImage() async {
    Navigator.pop(context);
    try {
      final pickedFile =
          await _imagePicker.getImage(source: ImageSource.gallery);
      File file = File(pickedFile.path);
      if (pickedFile != null && _rotateToLandscape) {
        await _setImageToLandscape(file);
      } else if (pickedFile != null) {
        await _setImageToPortrait(file);
      }
    } catch (e) {
      print(e);
    }
  }

  Future<void> _setImageToLandscape(File file) async {
    print('>>> setting image to landscape');
    try {
      setState(() {
        _loading = true;
      });
      var decodedImage = await decodeImageFromList(file.readAsBytesSync());
      int width = decodedImage.width;
      int height = decodedImage.height;
      if (width > height) {
        print('>>> returing original image');
        _setSelectedImage(file);
      } else if (width < height) {
        print('>>> rotating image');
        List<int> imageBytes = await file.readAsBytes();
        final originalImage = img.decodeImage(imageBytes);
        img.Image fixedImage;
        fixedImage = img.copyRotate(originalImage, -90);
        final fixedFile = await file.writeAsBytes(img.encodeJpg(fixedImage));
        _setSelectedImage(fixedFile);
      }
    } catch (e) {
      print(e);
    } finally {
      setState(() {
        _loading = false;
      });
    }
  }



 void _setSelectedImage(File file) {
    switch (_selectedImage) {
      case 1:
        setState(() {
          _image = file;
          widget.setImage(image: file);
        });
        break;
      case 2:
        setState(() {
          _image2 = file;
          widget.setImage(image2: file);
        });
        break;
      case 3:
        setState(() {
          _image3 = file;
          widget.setImage(image3: file);
        });
        break;
    }
  }
Chris
  • 1,720
  • 16
  • 35

3 Answers3

3

You've set the FileMode when writing to FileMode.append so it will add the new image in the same file after the old image (since you copied the old file) which means that when decoding the new image only the first part will get decoded (the original image)

So to fix it you should just be able to remove the mode from the write

Pieter van Loon
  • 5,318
  • 1
  • 6
  • 15
1
Future<File?> _rotateImage(
      String url, String fileExt, AttachmentModel item) async {
    try {
      File file;
      if (item.file == null) {
        file = await urlToFile(url, fileExt, true);
      } else {
        file = await urlToFile(url, fileExt, false);
        List<int> temp = await item.file!.readAsBytes();
        await file.writeAsBytes(temp);
      }

      List<int> imageBytes = await file.readAsBytes();
      final originalImage = img.decodeImage(imageBytes);
      print('previous width: ${originalImage?.width}');
      img.Image newImage;
      newImage = img.copyRotate(originalImage!, 90);
      print('width: ${newImage.width}');
      final fixedFile = await file.writeAsBytes(img.encodeJpg(newImage));
      int index = attachments!.indexOf(item);
      attachments![index] = AttachmentModel(
        attachmentUrl: fixedFile.path,
        fileExt: fileExt,
        file: fixedFile,
      );
      setState(() {});
      return fixedFile;
    } catch (e) {
      print(e);
      return null;
    }
  }

  Future<File> urlToFile(String imageUrl, String fileExt, bool isNet) async {
    Directory tempDir = await getTemporaryDirectory();
    String tempPath = tempDir.path;
    File file = File(tempPath + DateTime.now().toString() + '.$fileExt');
    if (!isNet) return file;
    Uri uri = Uri.parse(imageUrl);
    http.Response response = await http.get(uri);
    await file.writeAsBytes(response.bodyBytes);
    return file;
  }

Don't use original file, create new one over the original file, I put this code for an example. I know it's to late but maybe It can help someone

Lala Naibova
  • 396
  • 1
  • 3
  • 22
0

I had the same problem with my photo capture application which uses both cameras (Front and Back).

The problem was that the photo captured by the front camera was flipped horizontally.

What I did was detect if the front camera is in use and flip the image horizontally, if not; return the image as it is.

Future<File?> takePicture() async {
 final CameraController? cameraController = controller;
 if (cameraController == null || !cameraController.value.isInitialized) {
   print('Error >>>>: select a camera first.');
   return null;
 }

 if (cameraController.value.isTakingPicture) {
   // A capture is already pending, do nothing.
   return null;
 }

 try {
   final XFile file = await cameraController.takePicture();
   print(file);

   List<int> imageBytes = await file.readAsBytes();
   File file2 = File(file.path);

   if(controller!.description.lensDirection == CameraLensDirection.front){ // When using the front camera; flip the image
     img.Image? originalImage = img.decodeImage(imageBytes);
     img.Image fixedImage = img.flipHorizontal(originalImage!);
     File fixedFile = await file2.writeAsBytes(
       img.encodeJpg(fixedImage),
       flush: true,
     );
     // When using the back camara, don't flip the image
     return fixedFile;
   }
   return file2;


 } on CameraException catch (e) {
   print(e);
   return null;
 }
}
Gedeon Mutshipayi
  • 2,871
  • 3
  • 21
  • 42
  • 1
    This is great, I would just use (_controller.description.lensDirection == CameraLensDirection.front) instead of (_selectedCamera==1) as the the number of cameras and position in the list could be arbitrary based on the type of phone and no. of lenses. – baek Dec 13 '22 at 02:20
  • Perfect, I tested it and it works fine. I edited the post. – Gedeon Mutshipayi Dec 13 '22 at 08:05