Skip to content

Maintain aspect ratio when changing the cache size of an image. #52802

Closed

Description

@taosimple

Use case

flutter version: Flutter 1.12.13+hotfix.8
A widget Image width:100 height:100
A picture width:1000 height:1500

  1. display the picture into the Image :
    Image.file(new File(path) , fit: BoxFit.cover, cacheWidth: 100, cacheHeight: 100)
    the displayed picture is distorted with size:100*100
  2. if I modify as below:
    Image.file(new File(path) , fit: BoxFit.cover, cacheWidth: 100)
    the displayed picture with size:100*150

Proposal

In 1 the picture is distorted , 2 is waste memory
I wanted is the picture center clip and keep aspect like BoxFit.cover

String _instantiateImageCodec(Uint8List list, 
_Callback<Codec> callback, 
_ImageInfo imageInfo, 
int targetWidth, int targetHeight)
  native 'instantiateImageCodec';

If codec with a params like Image.fit(BoxFit.cover, BoxFit.fill), the codec image can fit like widget Image, What you see is what you get, is the best.

Activity

  1. added
    a: imagesLoading, displaying, rendering images
    f: inspectorPart of widget inspector in framework.
    perf: memoryPerformance issues related to memory
    c: proposalA detailed proposal for a change to Flutter
    frameworkflutter/packages/flutter repository. See also f: labels.
    and removed
    f: inspectorPart of widget inspector in framework.
    on Mar 18, 2020
  2. goderbauer commented on Mar 18, 2020

    @goderbauer
    Member

    /cc @dnfield You were thinking about making this better :)

  3. changed the title [-]_instantiateImageCodec optimization wanted[/-] [+]Maintain aspect ratio when changing the cache size of an image.[/+] on Mar 18, 2020
  4. dnfield commented on Mar 18, 2020

    @dnfield
    Contributor

    Yes - see https://flutter.dev/go/encoded-image-dimensions. The trick is finding some way to make this useful for people with the current Image widget API.

  5. self-assigned this
    on Mar 18, 2020
  6. added this to the July 2020 milestone on Mar 18, 2020
  7. dnfield commented on Mar 18, 2020

    @dnfield
    Contributor

    I have ideas for this. Assigning a conservative milestone for now.

  8. modified the milestones: July 2020, August 2020 on Mar 18, 2020
  9. taosimple commented on Mar 19, 2020

    @taosimple
    Author

    @dnfield Thank you for your reply
    I have tested package:image mentioned in https://flutter.dev/go/encoded-image-dimensions , It can achieve the purpose by resize and crop, but have 2 problems:

    1. Can't work with flutter MemoryCache

    2. About 100 times slower than instantiateImageCodec

  10. 1 remaining item

  11. dnfield commented on Mar 19, 2020

    @dnfield
    Contributor

    In the mean time, you could probably use Image.memory with the code I linked in that document to read the size ahead of time, and do your own work to properly use cacheHeight and cacheWidth as a work around.

  12. dnfield commented on Mar 19, 2020

    @dnfield
    Contributor

    Oh, and I should add for completeness - it would really be best if you just shipped the asset at the right scale. You're still going to use more CPU and memory (and network or disk bandwidth) to scale the image on device than if it was just decoded at the right size and scale to begin with.

  13. taosimple commented on Mar 19, 2020

    @taosimple
    Author

    Oh, and I should add for completeness - it would really be best if you just shipped the asset at the right scale. You're still going to use more CPU and memory (and network or disk bandwidth) to scale the image on device than if it was just decoded at the right size and scale to begin with.

    My English is not good. I'm not sure what you mean by messages above.I'm just use local image files, so don't need to use the network, the main impact is memory usage and performance

  14. dnfield commented on Mar 19, 2020

    @dnfield
    Contributor

    Can you just resize the images ahead of time, and include images that are the correct size/scale in your application bundle?

  15. taosimple commented on Mar 19, 2020

    @taosimple
    Author

    Can you just resize the images ahead of time, and include images that are the correct size/scale in your application bundle?

    In principle, it is feasible, but the processing is more complicated, which needs create a map between thumbnail and src image.

  16. taosimple commented on Mar 19, 2020

    @taosimple
    Author

    Can you just resize the images ahead of time, and include images that are the correct size/scale in your application bundle?

    I referenced the principle of the Glide, I think i should resize the images ahead of time and generate thumbnail, Thanks again.

  17. added
    c: performanceRelates to speed or footprint issues (see "perf:" labels)
    on Mar 30, 2020
  18. added
    P1High-priority issues at the top of the work list
    on May 29, 2020
  19. dnfield commented on Aug 19, 2020

    @dnfield
    Contributor

    Changes needed in the engine have landed for this. I'm going to look at how we can make ResizeImage better utilize them.

  20. dnfield commented on Aug 21, 2020

    @dnfield
    Contributor

    I'm realizing there are actually two requests here: one is to better be able to maintain the aspect ratio, the other is to decode part of an image.

    I'm not sure we can fulfill that second part, because I don't think all the codecs we support even have the option to do partial decoding without decoding the full image.

  21. dnfield commented on Aug 21, 2020

    @dnfield
    Contributor

    Ok. I'm going to close this issue at this point. Firstly because it's just not feasible for us to support decoding a subset of an image without decoding the full image, and secondly because we do support maintaining aspect ratio while resizing. While the image API could use some improvements, I don't think this bug is a good place to track those.

  22. MatrixDev commented on Mar 4, 2021

    @MatrixDev

    I will leave it here in case someone needs this in the future:

    class SmartImageProvider extends ImageProvider<String> {
      final int width;
      final int height;
      final BoxFit fit;
      final BaseImageProvider provider;
    
      SmartImageProvider({
        @required this.width,
        @required this.height,
        @required this.provider,
        this.fit = BoxFit.cover,
      });
    
      @override
      ImageStreamCompleter load(String key, DecoderCallback decode) {
        return provider.load(key, _smartDecode);
      }
    
      Future<ui.Codec> _smartDecode(
        Uint8List bytes, {
        int cacheWidth,
        int cacheHeight,
        bool allowUpscaling,
      }) async {
        final buffer = await ui.ImmutableBuffer.fromUint8List(bytes);
        final descriptor = await ui.ImageDescriptor.encoded(buffer);
    
        int sourceWidth = descriptor.width;
        int sourceHeight = descriptor.height;
        if (sourceWidth == null || sourceHeight == null) {
          // sometimes ImageDescriptor doesn't return width/height
          // will manually decode image to get real image dimensions
          final codec = await descriptor.instantiateCodec();
          try {
            final frame = await codec.getNextFrame();
            sourceWidth = frame.image.width;
            sourceHeight = frame.image.height;
          } finally {
            codec.dispose();
          }
        }
    
        int targetWidth;
        int targetHeight;
        if (sourceWidth != null && sourceHeight != null) {
          final inputSize = Size(sourceWidth.toDouble(), sourceHeight.toDouble());
          final outputSize = Size(width.toDouble(), height.toDouble());
    
          final sizes = applyBoxFit(fit, inputSize, outputSize);
          targetWidth = (inputSize.width / sizes.source.width * sizes.destination.width).round();
          targetHeight = (inputSize.height / sizes.source.height * sizes.destination.height).round();
        }
    
        return descriptor.instantiateCodec(
          targetWidth: targetWidth,
          targetHeight: targetHeight,
        );
      }
    
      // TODO obtainKey implementation, not relevant to my comment
    }
  23. github-actions commented on Aug 5, 2021

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  24. locked as resolved and limited conversation to collaborators on Aug 5, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

P1High-priority issues at the top of the work lista: imagesLoading, displaying, rendering imagesc: performanceRelates to speed or footprint issues (see "perf:" labels)c: proposalA detailed proposal for a change to Flutterframeworkflutter/packages/flutter repository. See also f: labels.perf: memoryPerformance issues related to memory

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions