facebook / fresco

An Android library for managing images and the memory they use.
https://frescolib.org/
MIT License
17.07k stars 3.75k forks source link

GifImage - get value of transparent pixel? #1567

Closed ghassett closed 7 years ago

ghassett commented 7 years ago

I am using the GifImage class to read an animated GIF, frame by frame. I use each GifFrame to composite a new GIF, assembled using a GIF encoder (AnimatedGifEncoder).

For each frame, I need to tell the encoder what the transparent color is (for GIF's with transparency). GifFrame exports a method called "hasTransparency()" but I also need to know the RGB value of the "transparent pixel." Looking into the native C++ code, it appears that this value is known to the C++ code but not available to the Java consumer of the GifFrame class. Is there a way to get the RGB value of the "transparent" color in a frame of a GIF file, so that I can pass it on to the encoder when I add the frame to my destination GIF?

Thanks!

oprisnik commented 7 years ago

What exactly are you trying to do? Why do you need to assemble a new GIF on the client? How are you using GifImage?

Internally, we're composing the image frames together here. The transparent color should always be Color.TRANSPARENT.

ghassett commented 7 years ago

Thanks for the reply, Alexander —

I am superimposing text on the original animated GIF, to produce a new animated GIF.

Color.TRANSPARENT is integer 0, or black with alpha 0, so if the original image uses this in its actual drawing to represent black -- perhaps erroneously -- and uses a different color to represent transparency, then black areas in the original GIF become transparent in the destination GIF when I simply set the transparency color to Color.TRANSPARENT.

Some of our source GIF images use a color other than black-with-transparency (int 0) to represent transparency (frequently it is white).

My compositing logic looks like this in pseudo-code:

for each frame in original gif:
        draw frame into a bitmap
        draw text onto that bitmap
        construct new frame using new composited bitmap
        using encoder, add new composited bitmap as a frame in the destination GIF

When performing the final step, I also set the delay and disposition of the new frame to match the frame that I pulled out of the original GIF. I also need to set the transparency pixel value to match that of the frame in the original frame, which is why I need to access it through GifImage or more likely GifFrame or AnimatedDrawableFrameInfo.

I use fresco’s GifImage to parse the original GIF, and to pull out individual frames. I use a version of Kevin Weiner’s AnimatedGifEncoder class to assemble the composited frames into the destination GIF. See https://github.com/nbadal/android-gif-encoder to look at the exact encoder I am using

The animated GIF encoder exposes methods to set values for the next frame I add:

    public void setDelay(int ms);
    public void setDispose(int code);
    public void setTransparent(int c);

I can get the values for the first two (delay, dispose) from fresco's GifFrame class, but not the third.

I looked at the native C++ code and it appears that this pixel value (or at least its index into the color table) is available, but not exposed through the Java interface (all that is exposed is a boolean hasTransparency(), through GifFrame).

Any help is very much appreciated!

// thanks // greg //

oprisnik commented 7 years ago

Thanks for the very detailed explanation @ghassett ! You're right, this is not supported right now.

However, it shouldn't be too hard to expose a method GifFrame.getTransparentColor(). gif.cpp already has getColorFromTable(colorIndex, pColorMap); and you can get the transparent color index for the frame.

Feel free to send us a pull request that exposes this for GifFrame :)

ghassett commented 7 years ago

Hi @oprisnik -- I would love to contribute! I downloaded the source and got it building in both the command line and Android Studio, but I am having a very difficult time getting my existing project configured to use my local copy of fresco. To use the "official" version I just add

compile group: 'com.facebook.fresco', name: 'animated-gif', version: '0.12.0'

to my app's build.gradle.

To use my locally built version, I've tried every combination of adding/importing modules, hand editing gradle and properties files, following Stack Overflow recipes, etc., but cannot get it to work (the variety of things I've tried, and the variety of errors, are too numerous to enumerate here). Is there a standard way to switch my client application over to use a locally compiled version of fresco? I can't seem to figure it out, after about two hours of trying!

oprisnik commented 7 years ago

Yeah, it's not easy with Android Studio or Gradle to do this. I think an easy way would be to either export .aars and include them in your project or use a local maven repository.

You can also try and import required modules in your existing project, e.g. by adding something like

include ':animated-gif'
project(':animated-gif').projectDir = new File(settingsDir, '../fresco/animated-gif')

to your settings.gradle for all Fresco modules, see for example this StackOverflow thread If everything fails, you could consider using symlinks.

Or you can modify one of our sample apps and add your code there to make sure it works.

ghassett commented 7 years ago

Ok, I am trying to run a sample ("animation") from within Android Studio. Stock and current installation of Android Studio, NDK, etc. Once I get it to run, I am going to modify it to test the enhancement I intend to write. But no joy "out of the box" -- here are the errors, and the only solutions I've been able to find in my research involve pretty tedious stuff like finding server certificates and installing them into my "trust store," etc.

As a workaround I added acceptAnyCertificate true to the download tasks referenced below, and I can build now, but I thought you might have a better solution?

FAILURE: Build completed with 3 failures.

1: Task failed with an exception.
-----------
* What went wrong:
Execution failed for task ':imagepipeline:downloadLibjpeg'.
> javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
==============================================================================

2: Task failed with an exception.
-----------
* What went wrong:
Execution failed for task ':static-webp:downloadLibjpeg'.
> javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
==============================================================================

3: Task failed with an exception.
-----------
* What went wrong:
Execution failed for task ':animated-gif:downloadGiflib'.
> javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
==============================================================================
oprisnik commented 7 years ago

Thanks for letting us know. We currently don't have a better workaround for this unfortunately and our CircleCI tests have the same issue. We'll try to fix this next week. You can also manually download the library and put them in the destination folder in nativedeps. Once the file is there, it should work.

ghassett commented 7 years ago

Hi @oprisnik I submitted a pull request -- there are two minor changes.

I tested this against various GIF's by modifying one of the sample apps (I did not commit that temporary modification).

Because it is challenging to get my own code linked against my custom fresco build, I will probably wait until the pull request is approved and merged. Any idea how long that might take?

oprisnik commented 7 years ago

Thanks @ghassett ! We'll take a look at it and we'll probably release a new version that includes your code in the next couple of days.

oprisnik commented 7 years ago

Thanks again for your PR. It will be included in the next release!

ghassett commented 7 years ago

You’re welcome, thank you for all your help!

// g //

On Dec 12, 2016, at 8:43 AM, Alexander Oprisnik notifications@github.com wrote:

Thanks again for your PR. It will be included in the next release!

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/facebook/fresco/issues/1567#issuecomment-266433836, or mute the thread https://github.com/notifications/unsubscribe-auth/AAdO78iP2KP1gyUrnG6Kn4W9wbVxB5enks5rHU-XgaJpZM4LEuxK.

ghassett commented 7 years ago

Hi Alexander,

I’m not 100% clear on how this works — I see that my PR has been merged into the library, but it’s not part of any official numbered “release” that I can reference in my build.gradle file (the latest release appears to be V0.14.01, released on Sep. 30).

Is there a way to modify my build.gradle file so that it grabs the fresco code that includes my change (for exposing access to the transparent pixel color)?

// thanks // greg //

On Dec 12, 2016, at 8:48 AM, Gregory Hassett greg@bulldogtechnologies.com wrote:

You’re welcome, thank you for all your help!

// g //

<Bulldog Logo 2 (sig size).png>

On Dec 12, 2016, at 8:43 AM, Alexander Oprisnik <notifications@github.com mailto:notifications@github.com> wrote:

Thanks again for your PR. It will be included in the next release!

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/facebook/fresco/issues/1567#issuecomment-266433836, or mute the thread https://github.com/notifications/unsubscribe-auth/AAdO78iP2KP1gyUrnG6Kn4W9wbVxB5enks5rHU-XgaJpZM4LEuxK.

oprisnik commented 7 years ago

We currently don't offer snapshot releases. However, we plan to do a release that includes your changes very soon.

oprisnik commented 7 years ago

This is now available in v1.0.0