Home > Software engineering >  In Flutter, how to extract frame by frame from an animated gif?
In Flutter, how to extract frame by frame from an animated gif?

Time:12-28

I'm trying to extract frames from an animated gif using Flutter. By researching the Flutter API, I thought the following code should work but it only gives me the first frame although it gives me the correct frames count.

static Future<List<ui.Image>> loadAnimatedGif(String assetPath) async {
    List<ui.Image> gifImage = [];
    ByteData data = await rootBundle.load(assetPath);
    var codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
    int frmCnt = codec.frameCount;
    print("frmcnt is $frmCnt"); //it gives correct frames count
    for (int i = 0; i < frmCnt; i  ) {
      var frame = await codec.getNextFrame();
      gifImage.add(frame.Image);
     
    }
   
    return gifImage;
  }

CodePudding user response:

I made a simple app out of this and it works using the same code but added a converter in it. I hope this helps you and if it's correct please accept the answer.

What I notice at first because nothing

  1. Using the ui.Image loads the frames but it delays for about 10secs before showing all of them using a breakpoint to debug it.
  2. Converting ui.Image to Uint8List takes time as much as the frames but the format shows all frames as soon the extraction is done.

Here is the dart pad to quickly run and test it.

Here is the code if you will like to scan through it, I am loading the gif from Network.

class GifFramesDisplay extends StatefulWidget {
  const GifFramesDisplay({super.key});

  @override
  State<GifFramesDisplay> createState() => _GifFramesDisplayState();
}

class _GifFramesDisplayState extends State<GifFramesDisplay> {
  List<Uint8List> _frames = [];

  @override
  void initState() {
    super.initState();

    loadGif();
  }

  Future<void> loadGif() async {
    // Load the gif image from the network url and store the bytes in a ByteData object
    final url = Uri.parse(
        'https://raw.githubusercontent.com/Aman-Malhotra/animate_icons/main/demo/animate_icons.gif');
    final ByteData data = await NetworkAssetBundle(url).load(url.path);

    // Using the _extractGifFrames function to extract the frames
    _extractGifFrames(data).then((List<Uint8List> frames) {
      // Set the state to update the UI
      setState(() {
        _frames = frames;
      });
    });
  }

  // Function to extract gif image frames
  Future<List<Uint8List>> _extractGifFrames(ByteData data) async {
    // Create a list to store the frames
    final List<Uint8List> frames = <Uint8List>[];

    // Create a codec to decode the gif
    final ui.Codec codec =
        await ui.instantiateImageCodec(data.buffer.asUint8List());

    // Count the number of frames in the gif
    final int frameCount = codec.frameCount;
    print('Total frameCount: $frameCount');

    // Loop through the frames and add them to the list
    for (int i = 0; i < frameCount; i  ) {
      // Get the next frame
      final ui.FrameInfo fi = await codec.getNextFrame();

      // Add the frame to the list
      final frame = await loadImage(fi.image);

      // Add the frame to the list if it is not null
      if (frame != null) {
        frames.add(frame);
      }
    }

    return frames;
  }

  Future<Uint8List?> loadImage(ui.Image image) async {
    final ByteData? byteData =
        await image.toByteData(format: ui.ImageByteFormat.png);
    return byteData?.buffer.asUint8List();
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: _frames.isEmpty
          ? const CircularProgressIndicator()
          : ListView.builder(
              itemCount: _frames.length,
              itemBuilder: (BuildContext context, int index) {
                return Image.memory(_frames[index]);
              },
            ),
    );
  }
}

Click to watch a preview of the loaded gif frames of the code above

  • Related