penfeizhou / APNG4Android

Android animation support for APNG & Animated WebP & Gif & Animated AVIF, High performance
Apache License 2.0
570 stars 75 forks source link

decoder.getFrameBitamp() returns null bitmap in onStart() but can get delays and frame counts #163

Closed KishorJena closed 1 year ago

KishorJena commented 2 years ago

New Issue Checklist

Issue Info

Info Value
Device Info Redmi Note 7 Pro
System Version 10.0
APNG4Android Library Version 2.23.0
Repro rate (100%)
Repro with our demo project -
Demo project link -

Issue Description and Steps

I can get framCounts and duration of each frame but when I get bitmap from getFrameBitamp(index) the bitmap is null. Although IOException does not occur.

    decoder.addRenderListener(new FrameSeqDecoder.RenderListener(){
      @Override
      public void onStart(){
        android.util.Log.d(TAG2," onStart");

          android.util.Log.d(TAG2," the count "+decoder.getFrameCount());

          for(int i=0; i<decoder.getFrameCount(); i++){
            final int delay = decoder.getFrame(i).frameDuration;
            try{

              if(decoder.getFrameBitmap(i) != null){
                Log.d(TAG2,"bm not null");
              }else{
                Log.d(TAG2,"bm null");
              }
            }catch(IOException e){Log.d(TAG2,"Exception: bm null");}

          }

      }

      @Override
      public void onRender(ByteBuffer byebuffer){}
      @Override
      public void onEnd(){}
    });
    decoder.start();

    decoder.stop();
KishorJena commented 2 years ago

Okay found two solution. It seems I can only get the bitmaps in render method. need to convert ByteBuffer to Bitmap and then use it.

Another way I found is useing sharedPreference storage to store the frameCount and we can get each frames bitmaps by calling getFrameBitmap(). Beacuse we cannot get the frameCount so we cannot iterate blindly which will cause index out of bound error. sharedPreference stores the frameCount value in onStart(); using normal variable raises errors.

If you know any reliable method please share. Its ok if not reliable but share what are alternatives

KishorJena commented 2 years ago

Okay the SharedPreference is not reliable in some cases. It does not supported in older versions like ANDROID 4.1. Even my app get crashed which has min API 23. After playing with it I found that If I invoke decoder.stop() in listener's overrridden method onStart() then I can get the Bitmaps from decoder.getFrameBitmap(). An yeah I can get the frameCount in the onStart() ofcourse.

I feel this is better way then sharedPreference if your app supports older versions. Another way I imagined of Runnable and thread things and wait for to finish of the decoder. I am not sure about it.


 decoder.addRenderListener(
      new WebPDecoder.RenderListener(){
        @Override
        public void onStart(){

          int count = decoder.getFrameCount();
          for (int i = 0; i < count; i++)
            decoder.getFrame(i).frameDuration;

          decoder.stop();

          try{

            for (int i = 0; i < count; i++)
              decoder.getFrameBitmap(i);

          }catch(Exception e){

          }

        }
        @Override
        public void onRender(ByteBuffer byebuffer){

        }

        @Override
        public void onEnd(){

        }
      }
    );

I don't know what does the setDesireSize do I have to resize each frame and ecnode into awebp when input images are gif/awebp.

KishorJena commented 2 years ago

invoking stop in onStart is too slow. Loop get slower by each iteration. 69 frames takes around 30-40 seconds. Although encoding is very quick less then a second. I tried a gif decoder to check if this much frame is too much for decoding, but that decoder was quick to decode 69 frames within 2 seconds. ( but can't use that decoder as it is limited to gif only.)