Fresco: Low image quality using <Image/> component on RN > = 0.57 (Fresco >= 1.10.0)

Created on 22 Aug 2019  ·  57Comments  ·  Source: facebook/fresco

Description

RN issue: RN 0.57.x Bundled large images have low quality when viewing using \

There is low quality when loading large bundled (PNG, GIF and maybe more formats, NOT JPEG) images only on Android:

At the left screenshot we see the exact same code running with RN 0.56.0 and at the right screenshot we see RN 0.57.1. The code is just a simple image <Image source={require('./assets/ELHall1.png')} resizeMethod="resize" /> and the image size is 2111 x 4645 pixels. Both projects are fresh installed using react-native init RN057ImageTest and react-native init --version="0.56.0" RN056ImageTest. This continues to happen from 0.56 and all versions after and latest RN 0.60.x.

This is confirmed to be caused by RN Fresco lib change between 0.56 and 0.57 from 1.9.0 to 1.10.0 https://github.com/facebook/react-native/commit/b6f2aad9c0119d11e52978ff3fa9c6f269f04a14. Check comment https://github.com/facebook/react-native/issues/21301#issuecomment-520155609.

After some search at Fresco issues, I can see some related issues that it's suggested that large images should be divided and recomposed piece by piece, which it resolves many cases (most map related large images) but it can be very inconvenient especially for dynamic loaded/created images. This was working until RN 0.56 and from 0.57 and after it does not.

Reproduction

RN: It's the initial App.js with an <Image/> component added.

...
type Props = {};
export default class App extends Component<Props> {
  render() {
    return (
      <View style={styles.container}>
        <Image source={require('./assets/ELHall1.png')} resizeMethod="resize" />
      </View>
    );
  }
}
...

Additional Information

At this comment https://github.com/facebook/react-native/issues/21301#issuecomment-520418832, lambdapioneer writes that this maybe is related to scale down (sub-sampling) large images:

I assume it's related to how Fresco scales downs (sub-sampling) large images (which is an important feature for memory and performance concerns). There has been some changes in these area during that time mainly for removing native code dependencies to help reducing the also pressing .so unsatisfied link errors. So to say: it might be a side-effect of another large improvement.

  • Fresco version: >= 1.10.0
  • Platform version: RN >= 0.57, all Android versions
enhancement

Most helpful comment

@CaptainN it's not rude. The thing is that I have done it and faced out all the errors, so it will take much less time for me to make a starter repo with instructions on how to install and patch Fresco.

So here it is: https://github.com/clytras/RN061FrescoBuild

It has RN 0.61.5. The repo has detailed instructions. It clones Fresco, checkouts v2.1.0 and applies a patch to DecodeProducer.java so to comment out the downsample code. You only have to download the Android NDK and add a libraries/fresco/local.properties file. It's all detailed in the readme.

You can add fresco lib to an existing RN >= 0.60 (_or older versions_) project if you follow the instructions and the yarn scripts.

All 57 comments

Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as "bug" or "enhancement" and I will leave it open. Thank you for your contributions.

What Fresco versions are RN 0.56.0 and 0.57.1 using? This narrows down the number of commits that could have caused this.

Hello @oprisnik,

I'll check and see exactly in which version patch got the upgrade when I'll return back to my office, though I think https://github.com/facebook/react-native/issues/21301#issuecomment-520155609 reports exactly from which version got this behaviour change.

Hi @clytras, I found the image on screen in this repository.
I download the picture, and find the picture is 2111(width)*4645(height).

Fresco will downsample an image to fit the OpenGL limit, the default image's max width/height is hardcode in this line.

In my opinion,
The easy way is change a ResizeOption's maxBitmapSize field.
The right way is don't use Fresco to display a VERY large image.

Create an other widget, and use this library to display a VERY large Image is many android team's choice.

Hi @s1rius, I write the image size precisely if you read the open issue post:

The code is just a simple image and the image size is 2111 x 4645 pixels

This is about a behaviour change that happened from RN >= 0.57 which changed from Fresco 1.9.0 to 1.10.0. Large images like that one were working and loading in full quality before that version. Contributors from RN repo closed the issue as they state that this is not a RN issue but a Fresco lib issue. If that behavious change is intentionally made, then we can close this issue. Yes there are workarounds but it's that this was working until a version and suddenly, and with no info on why, stopped working.

Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as "bug" or "enhancement" and I will leave it open. Thank you for your contributions.

Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please feel free to reopen with up-to-date information.

A potential change that could have caused this is https://github.com/facebook/fresco/commit/fa71901055a38a810c190862c7fd582fd3dad2b3

Can you verify if that's the offending change?

Hello again @oprisnik and thanks for investigating this.

I am not so familiar with gradle and how I can change RN to compile fresco directly from source instead of downloading the library. Steps I've taken with no success:

  1. Create RN 0.57 project using react-native-cli
  2. Clone fresco lib to the project root directory
  3. Checkout to v1.10.0 branch inside fresco lib
  4. Add android-ndk-r20 path to <Project>\android\local.properties (ndk.dir=G:\\Dev\\Android\\android-ndk-r20)

Then I found this answer at SO https://stackoverflow.com/a/52861379/1889685 which compiles RN with fresco from source and overrides the library:

cd android
./gradlew assembleDebug --include-build /e/Sandbox/RN057ImageTest/fresco/

but I get the following error:

> Task :fresco:imagepipeline:ndk_build_bitmaps FAILED
A problem was found with the configuration of task ':fresco:imagepipeline:ndk_build_bitmaps'. Registering invalid inputs and outputs via TaskInputs and TaskOutputs methods has been deprecated and is scheduled to be removed in Gradle 5.0.
 - File 'E:\Sandbox\RN057ImageTest\fresco\imagepipeline\src\main\jni\bitmaps' specified for property '$1' is not a file.

Maybe @sunnylqm can test this if he has some time, because he is way more familiar with this process.

If it helps, I can create a repo with what I have so far.

Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as "bug" or "enhancement" and I will leave it open. Thank you for your contributions.

I tried to build but also got the ndk error. Do you guys have nightly builds that we can test on?

Since I can compile v2.0.0 without any modification, I tried another way to verify. I disabled downsampling which is enabled by the commit above, and here are the results

test image uri:
"https://wagonsclub.oss-cn-beijing.aliyuncs.com/static/carousel/carousel1_bg.jpg"

fragment_drawee_simple.xml

  <com.facebook.drawee.view.SimpleDraweeView
      android:id="@+id/drawee_view"
      android:layout_width="match_parent"
      android:layout_height="1000dp"
      />

original v2.0.0 showcase
image

disabled downsampling v2.0.0 showcase
image

@sunnylqm thanks for debugging this. How can I disable downsampling? I've tried the following methods with no success:

1st in MainApplication.java onCreate:

@Override
public void onCreate() {
  super.onCreate();

  ImagePipelineConfig config = ImagePipelineConfig.newBuilder(this)
    .setDownsampleEnabled(false)
    .build();

  Fresco.initialize(this, config);

  SoLoader.init(this, /* native exopackage */ false);
}

With this I get a message at Logcat that Fresco is already initialized.

2nd method I've used by initializing MainReactPackage with MainPackageConfig but it didn't work either:

protected List<ReactPackage> getPackages() {
  Context context = getApplicationContext();
  ImagePipelineConfig pipelineConfig = ImagePipelineConfig
    .newBuilder(context.getApplicationContext())
    .setDownsampleEnabled(false)
    .build();
  MainPackageConfig config = new MainPackageConfig.Builder().setFrescoConfig(pipelineConfig).build();

  return new ArrayList<>(Arrays.<ReactPackage>asList(
    new MainReactPackage(config),
    new ReactNativeFirebaseAppPackage(),
    new FastImageViewPackage(),
    new RNGestureHandlerPackage(),
    new ReanimatedPackage(),
    new SvgPackage()
  ));
}

@clytras I dont know. I disabled it in source code.

ping @oprisnik

Looks like this is set via the React Native main package config: https://github.com/facebook/react-native/blob/6c0f73b3223968448bb186b82f06f6819068a21d/ReactAndroid/src/main/java/com/facebook/react/shell/MainPackageConfig.java

Not sure how you have to set that up with RN, never tried that.

@oprisnik Is there any plan to fix this issue?

I believe this issue still belongs to the RN repo. Image downsampling is something that without a doubt is for reducing memory cost, gain efficiency and having less crasses when loading large images. I think this issues should be handled by RN in a way that it allows end developers to choose if they want to have image downsampling or not in a form of an option/property. For example in my app, where I use large images for presenting some floor plans, I need the high-res when the user zooms in the floor plan. I always check the device capabilities and if it's a low-end device with less RAM or low-res, then I load smaller and lower res images.

There is the resizeMethod property for the <Image/> component which has scale and resize. RN currenlty does not respect the scale value of that property, which I think it should disable image downsampling and only enable it when setting it to resize.

resizeMethod

The mechanism that should be used to resize the image when the image's dimensions differ from the image view's dimensions. Defaults to auto.

resize: A software operation which changes the encoded image in memory before it gets decoded. This should be used instead of scale when the image is much larger than the view.

scale: The image gets drawn downscaled or upscaled. Compared to resize, scale is faster (usually hardware accelerated) and produces higher quality images. This should be used if the image is smaller than the view. It should also be used if the image is slightly bigger than the view.

I'd really like to see your thoughts on this @oprisnik @sunnylqm.

@clytras As you've said, it's a behavior change in fresco without explanation. (The demo I show is the example app in fresco repo) So I don't think it has anything to do with RN.

@sunnylqm of course it is a behavior change in fresco without explanation but RN should leave the choice of enabling/disabling this fresco feature to the end devs.

Also don't forget that this change of behavior does not of course happen in iOS, so we have to do something about it and since fresco configuration happens in react native, then I think this configuration should be exposed to end RN developers.

BTW I managed to compile Fresco 2.0.0 (master branch) with latest RN 0.61.2 and disabled downsampling by commenting out downsampling condition check at DecodeProducer.java as you did.

I created a local.properties file inside fresco repo directory and used Android NDK Revision 19c x86_64 for windows 64-bit.

ndk.dir=G:\\Dev\\Android\\android-ndk-r19c
org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.configureondemand=true

Fresco binaries compiled successfully and image downsampling has been disabled for the RN app built by running:

cd android
.\gradlew assembleDebug --include-build ..\fresco\

yeah, the solution for this would be to disable downsampling for your applications, via the RN initialization logic or when Fresco is initialized.

@oprisnik then I think we need a better, clean and easy way to initialize fresco within the generated android project, which I couldn't achieve with any method I tried. I have seen some projects use com.facebook.imagepipeline.core.ImagePipelineConfig, com.facebook.react.shell.MainReactPackage and com.facebook.react.shell.MainPackageConfig to initialize it inside getPackages method by passing the config to MainReactPackage, but that didn't work for me neither in the latest RN 0.61 nor RN 0.57 that I've tried:

protected List<ReactPackage> getPackages() {
  Context context = getApplicationContext();

  ImagePipelineConfig pipelineConfig = ImagePipelineConfig
    .newBuilder(context.getApplicationContext())
    .setDownsampleEnabled(false)
    .build();

  MainPackageConfig config = new MainPackageConfig.Builder()
    .setFrescoConfig(pipelineConfig)
    .build();

  return new ArrayList<>(Arrays.<ReactPackage>asList(
    new MainReactPackage(config),
    ...
  ));
}

Maybe I'm doing something wrong there, and that definitely didn't work with the new >= 0.60 linking system which generates a PackageList.java file inside <project>/android/app/build/generated/rncli/src/main/java/com/facebook/react/ directory because MainReactPackage is passed without any arguments and I can't see a way to pass any kind of arguments using the auto linking system, but I tried by manually passing the MainReactPackage with fresco config and that didn't work either.

We need a clean and documented way on how to initialize fresco lib with custom configuration inside the generated android project for React Native.

After reading https://frescolib.org/docs/resizing.html and if I understand correctly, downsampling is a replacement for resizing. So @clytras I think you are right, if resizeMethod set to scale it should not enable downsampling.

The code in RN seems correct to me https://github.com/facebook/react-native/blob/aee88b6843cea63d6aa0b5879ad6ef9da4701846/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java#L505 (it would pass a null value to imagepipeline, which indicate no resizing/downsampling)
But in fresco it does not respect the null resizeOptions https://github.com/facebook/fresco/blob/v2.0.0/imagepipeline/src/main/java/com/facebook/imagepipeline/producers/DecodeProducer.java#L158-L169
If both downsampleEnabled and downsampleEnabledForNetwork are true, it will get downsampled, regardless resizeOptions are null
https://github.com/facebook/fresco/blob/master/imagepipeline-base/src/main/java/com/facebook/imagepipeline/transcoder/DownsampleUtil.java#L56
So it seems a mismatch between the doc and the code to me. And we need a more flexible way to control whether we want a specific image get downsampled. (now it's controlled by 2 global configs) @clytras @oprisnik

Nice job there @sunnylqm. I started to looking and following the RN config for <Image/> component to check those things, but you got beyond me!

Since there is such a behaviour change, I think that either fresco should check and apply downsampling only if resizeOptions is not null; which is set only if resizeMethod is set to resize (or auto and for local images), which is checked in https://github.com/facebook/react-native/blob/aee88b6843cea63d6aa0b5879ad6ef9da4701846/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java#L606 shouldResize function, or there should be a new resizeMethod enum option for downsampling specifically.

Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as "bug" or "enhancement" and I will leave it open. Thank you for your contributions.

@clytras could you tell your solution ?

@club9822 the solution for now is to compile fresco binaries from source after first disabling image downsampling as I describe here https://github.com/facebook/fresco/issues/2397#issuecomment-541802753

Is there a workaround for this without recompiling core libraries?

@CaptainN There is not an easy solution to this issue. The process of compiling the sources may seem hard and tedious, but it's actually a matter of making some minor changes and executing commands. Follow my posts in this issue to find out how you do it.

@clytras You can pass config in the second argument in new PackageList(this, config)
And I tried it actually worked as expected. The problem is that the logic here just ignored the config https://github.com/facebook/fresco/blob/v2.0.0/imagepipeline/src/main/java/com/facebook/imagepipeline/producers/DecodeProducer.java#L158-L169 if it was false
It should be like this right?

- if (mDownsampleEnabled || !statusHasFlag(status, Consumer.IS_RESIZING_DONE))
+ if (mDownsampleEnabled && !statusHasFlag(status, Consumer.IS_RESIZING_DONE))

@oprisnik

@sunnylqm I am away from office and I can't check now, but I remeber I couldn't find a way to pass the config using the new _RN >= 60_ auto link system. I'm not sure though. It didn't work even for older _RN_ versions for me. I'll check it in a couple of days when I return from vacations.

I beleive that OR to AND change will solve the problem as it's quite obvious that downsampling now applies always if the status has not the IS_RESIZING_DONE flag so it completely bypasses mDownsampleEnabled. Of course that will be the case if the custom fresco config disabling downsample gets applied and passed to fresco.

@clytras I'm getting all sorts of errors with gradle deprecation and all that. Any chance you can put up a prebuild binary (and tell me where to install it)? I'd really appreciate it.

Ah forget it. It's rude to even ask. I'll figure it out, and maybe put in a PR.

@CaptainN it's not rude. The thing is that I have done it and faced out all the errors, so it will take much less time for me to make a starter repo with instructions on how to install and patch Fresco.

So here it is: https://github.com/clytras/RN061FrescoBuild

It has RN 0.61.5. The repo has detailed instructions. It clones Fresco, checkouts v2.1.0 and applies a patch to DecodeProducer.java so to comment out the downsample code. You only have to download the Android NDK and add a libraries/fresco/local.properties file. It's all detailed in the readme.

You can add fresco lib to an existing RN >= 0.60 (_or older versions_) project if you follow the instructions and the yarn scripts.

@clytras Thanks so much for putting that together. I really appreciate it.

Is there any word as to whether the comment above will address the issue in the future?

I have tested out the change @sunnylqm suggested:

- if (mDownsampleEnabled || !statusHasFlag(status, Consumer.IS_RESIZING_DONE))
+ if (mDownsampleEnabled && !statusHasFlag(status, Consumer.IS_RESIZING_DONE))

and it turns out that after that change, I was able to disable image downsample using just configuration inside MainApplication.java, which means that nobody will have to compile fresco and only have to change fresco settings there:

@Override
protected List<ReactPackage> getPackages() {
  Context context = getApplicationContext();

  ImagePipelineConfig frescoConfig = ImagePipelineConfig
    .newBuilder(context)
    .setDownsampleEnabled(false)
    .build();

  MainPackageConfig appConfig = new MainPackageConfig
    .Builder()
    .setFrescoConfig(frescoConfig)
    .build();

  @SuppressWarnings("UnnecessaryLocalVariable")
  List<ReactPackage> packages = new PackageList(this, appConfig).getPackages();
  // Packages that cannot be autolinked yet can be added manually here, for example:
  // packages.add(new MyReactNativePackage());
  return packages;
}

After that single change, the configuration code above will work and disable downsampling. If we remove that configuration or set .setDownsampleEnabled(true) then it will properly enable downsample. That looks like a bug to me @oprisnik. Of course the ideal thing for me will be RN to respect resizeMethod and apply downsampling dynamically, but I'm not sure if that's possible.

EDIT

Actually I was partly wrong. With that change (_which makes total sense_), RN sets .setDownsampleEnabled(false) as we can see here ReactAndroid/src/main/java/com/facebook/react/modules/fresco/FrescoModule.java#L155, so, in order to enable downsampling, we have to configure fresco with the above code and set .setDownsampleEnabled(true). RN has downsampling disabled by default!

@clytras @CaptainN Many thanks for your work on this issue. I'm experiencing it, using Expo and I'm really looking forward to find a solution, without ejecting my app, if possible.

react-native-cli: 2.0.1
react-native: 0.61.2

Small update. I've not encountered this issue for a long time now. But I am uncertain if this is because I've switched to use 3 different scales of the same image (@1x, @2x, @3x) for most images.

@enguerranws I have no experience using Expo at all. @gorvinsky I have just tested it using RN 0.61.5 and it doesn't work. Unfortunatelly bundled image sizes never worked. It was the first thing I've tested when I first faced this issue.

I have created a react-native-community/cli template that has RN 0.61.5 project and the required modifications to build Fresco from source. This is an easy and quick way to have a new RN project crafted with custom project name and with the changes needed to compile Fresco from source. It also uses Android NDK Revision 21 and I have tested it on macOS and Windows using yarn 1.21.

Github repository: clytras/react-native-fresco
NPM template: @lytrax/react-native-fresco

It can be installed like this:

npx @react-native-community/cli@next init --template=@lytrax/react-native-fresco <ProjectName>

There are detailed installation instruction in the README. You need to clone/patch Fresco using yarn fresco-setup and then install Android NDK and create android/libraries/fresco/local.properties with Android NDK path.

Is this commit in react-native fixing this ? (not released for now)

https://github.com/facebook/react-native/commit/f535c8b4bb4474ffe0a0765270cbca8d839deca8

In the description it say we can pass a PipelineConfig which have a "setDownsampleEnabled" attribute.

https://frescolib.org/docs/configure-image-pipeline.html

@enguerranws I have no experience using Expo at all. @gorvinsky I have just tested it using RN 0.61.5 and it doesn't work. Unfortunatelly bundled image sizes never worked. It was the first thing I've tested when I first faced this issue.

I have created a react-native-community/cli template that has RN 0.61.5 project and the required modifications to build Fresco from source. This is an easy and quick way to have a new RN project crafted with custom project name and with the changes needed to compile Fresco from source. It also uses Android NDK Revision 21 and I have tested it on macOS and Windows using yarn 1.21.

Github repository: clytras/react-native-fresco
NPM template: @lytrax/react-native-fresco

It can be installed like this:

npx @react-native-community/cli@next init --template=@lytrax/react-native-fresco <ProjectName>

There are detailed installation instruction in the README. You need to clone/patch Fresco using yarn fresco-setup and then install Android NDK and create android/libraries/fresco/local.properties with Android NDK path.

Is there a way to implement it to an existing project?

@kalmahik
Try this way. (My RN is v0.61.4)
It is the way to apply @clytras' patch to an existing project.
And it cannot run android simulator anymore. I am always testing on real devices. 😢

  1. Add scripts to package.json
"fresco-clone": "git clone https://github.com/facebook/fresco.git android/libraries/fresco && cd android/libraries/fresco && git checkout tags/v2.1.0",
"fresco-patch": "yarn file-patch ./patches/DecodeProducer.java.diff android/libraries/fresco/imagepipeline/src/main/java/com/facebook/imagepipeline/producers/DecodeProducer.java",
"fresco-setup": "yarn fresco-clone && yarn fresco-patch"
  1. Create patches/DecodeProducer.java.diff

'yarn fresco-patch'

Result

@@ -7279,32 +7279,35 @@

+// 
 if (mDownsampleE
@@ -7381,24 +7381,27 @@

+// 
   ImageReque
@@ -7460,24 +7460,27 @@

+// 
   if (mDowns
@@ -7513,24 +7513,27 @@

+ //
        %7C%7C !U
@@ -7587,36 +7587,39 @@

+//

+ 
 encodedImage.set
@@ -7637,32 +7637,35 @@
 %0A               
+ //
          Downsam
@@ -7700,32 +7700,35 @@
 %0A               
+ //
              req
@@ -7762,32 +7762,35 @@

+// 
             requ
@@ -7820,32 +7820,35 @@
 %0A               
+ //
              enc
@@ -7866,32 +7866,35 @@

+// 
             maxB
@@ -7914,32 +7914,35 @@

+// 
   %7D%0A            
@@ -7937,32 +7937,35 @@

+// 
 %7D%0A%0A             
@@ -7962,24 +7962,27 @@

+ //
  if (produce
@@ -8002,28 +8002,31 @@

+//

+ 
 .getImagePip
@@ -8050,24 +8050,27 @@

+ //
      .getExp
@@ -8091,24 +8091,27 @@

+ //
      .should
@@ -8151,24 +8151,27 @@

+// 
   maybeIncre
@@ -8206,32 +8206,35 @@
 %0A               
+ //
  %7D%0A%0A            
  1. Edit android/settings.gradle

Add includeBuild ('libraries/fresco') before include ':app'

rootProject.name = 'YOURPROJECT'
...
includeBuild ('libraries/fresco') 

include ':app'

  1. android/build.gradle

Use gradle dependency is 3.4.1

dependencies {
  ...
  classpath("com.android.tools.build:gradle:3.4.1")
  ...
}
  1. Run script

yarn fresco-setup

  1. Download android ndk

I used r21 version.

https://developer.android.com/ndk/downloads

  1. Unzip and move the ndk

Unzip NDK
I unzipped ndk to Users/YOURNAME/Library/Android/android-ndk-r21
And create android/libraries/fresco/local.properties in your project

ndk.dir=/Users/YOURNAME/Library/Android/android-ndk-r21
org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.configureondemand=true
  1. Run android

That's all.

yarn android and check the large image quality.

Thanks to @clytras

https://github.com/clytras/react-native-fresco

@JeffGuKang
what is the file-patch command?

What is keeping this from being merged? Why do we require these patches instead of just fixing it (assuming it's not just a time constraint - I totally get that, not trying to be pushy)?

@kalmahik file-patch is a NPM package CLI patch tool.

@JeffGuKang you must include the changes in package.json that are required for the template setup scripts to work.

@CaptainN I don't think it's that simple, though I didn't have to time to test the above commits yet.

@clytras oh this one? thx)

@kalmahik
Try this way. (My RN is v0.61.4)
It is the way to apply @clytras' patch to an existing project.
And it cannot run android simulator anymore. I am always testing on real devices. 😢

  1. Add scripts to package.json
"fresco-clone": "git clone https://github.com/facebook/fresco.git android/libraries/fresco && cd android/libraries/fresco && git checkout tags/v2.1.0",
"fresco-patch": "yarn file-patch ./patches/DecodeProducer.java.diff android/libraries/fresco/imagepipeline/src/main/java/com/facebook/imagepipeline/producers/DecodeProducer.java",
"fresco-setup": "yarn fresco-clone && yarn fresco-patch"
  1. Create patches/DecodeProducer.java.diff

'yarn fresco-patch'

Result

@@ -7279,32 +7279,35 @@

+// 
 if (mDownsampleE
@@ -7381,24 +7381,27 @@

+// 
   ImageReque
@@ -7460,24 +7460,27 @@

+// 
   if (mDowns
@@ -7513,24 +7513,27 @@

+ //
        %7C%7C !U
@@ -7587,36 +7587,39 @@

+//

+ 
 encodedImage.set
@@ -7637,32 +7637,35 @@
 %0A               
+ //
          Downsam
@@ -7700,32 +7700,35 @@
 %0A               
+ //
              req
@@ -7762,32 +7762,35 @@

+// 
             requ
@@ -7820,32 +7820,35 @@
 %0A               
+ //
              enc
@@ -7866,32 +7866,35 @@

+// 
             maxB
@@ -7914,32 +7914,35 @@

+// 
   %7D%0A            
@@ -7937,32 +7937,35 @@

+// 
 %7D%0A%0A             
@@ -7962,24 +7962,27 @@

+ //
  if (produce
@@ -8002,28 +8002,31 @@

+//

+ 
 .getImagePip
@@ -8050,24 +8050,27 @@

+ //
      .getExp
@@ -8091,24 +8091,27 @@

+ //
      .should
@@ -8151,24 +8151,27 @@

+// 
   maybeIncre
@@ -8206,32 +8206,35 @@
 %0A               
+ //
  %7D%0A%0A            
  1. Edit android/settings.gradle

Add includeBuild ('libraries/fresco') before include ':app'

rootProject.name = 'YOURPROJECT'
...
includeBuild ('libraries/fresco') 

include ':app'
  1. android/build.gradle

Use gradle dependency is 3.4.1

dependencies {
  ...
  classpath("com.android.tools.build:gradle:3.4.1")
  ...
}
  1. Run script

yarn fresco-setup

  1. Download android ndk

I used r21 version.

https://developer.android.com/ndk/downloads

  1. Unzip and move the ndk

Unzip NDK
I unzipped ndk to Users/YOURNAME/Library/Android/android-ndk-r21
And create android/libraries/fresco/local.properties in your project

ndk.dir=/Users/YOURNAME/Library/Android/android-ndk-r21
org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.configureondemand=true
  1. Run android

That's all.

yarn android and check the large image quality.

Thanks to @clytras

https://github.com/clytras/react-native-fresco

I am facing this issue when trying to compile Fresco from source following @clytras script. I am compiling it into an existing RN(v0.59.9) project

Screen Shot 2020-05-31 at 1 11 34 PM

Also, tried add Fresco Config into MainApplication.java but not luck there

@ravali121 These steps are for RN >= 0.60.x, not for RN 0.59 or older versions. It can be patched for older RN versions, but the steps and the Fresco checkout should be following the version that comes with that specific RN version. I'm checking out Fresco 2.1.0 for RN 0.61 and RN 0.59 has a different version and of course a different patch though the patch is not necessary, the code changes can be applied manually by editing DecodeProducer.java. Also, there are additional gradle configuration to handle dependencies.

Although you can make it work with 0.59, I suggest you upgrade your project/s to at least 0.61 (or better 0.62).

Hello there! Any update on an ETA for the patch to be merged? (Thanks @clytras for the awesome work, I need to try your patch asap but I'd like to know if this is going to get fixed)

@FRizzonelli the patch is a workaround, it is not really a fix for the issue and that's why this issue stays open for so long.
I have to check the patch and the @react-native-community/cli template and upgrade it to latest RN version.

@clytras I understand :( Problem is right now I'm trying to setup a small POC with react-native-web on Expo, which has this issue for Android. And for a small demo, I'd like not to eject. But I think I need to :(

@clytras I understand :( Problem is right now I'm trying to setup a small POC with react-native-web on Expo, which has this issue for Android. And for a small demo, I'd like not to eject. But I think I need to :(

How about to use jpg format instead of png? I am not sure it can be solved the issue.

@clytras I understand :( Problem is right now I'm trying to setup a small POC with react-native-web on Expo, which has this issue for Android. And for a small demo, I'd like not to eject. But I think I need to :(

How about to use jpg format instead of png? I am not sure it can be solved the issue.

Unfortunately doesn't work either, and I need png for alpha layer :(

I have same problem with RN 0.63.4.
The image size of 960 x 13983 is down sampled much, the texts in the image can hardly read.
The image size of 680 X 2538 is also down sampled but quite decent.
I used Image.getSize to find out the size of the image, and calculate height from it (width is 100%)
Images are all jpg.
I did @clytras 's method with gradle 3.5.4, NDK 21.4.7075529. But, same issue.

        <Image
          style={{ width: SCREEN_WIDTH, height }}
          resizeMode="contain"
          source={{ uri }}
          resizeMethod="scale"
        />

We solved the problem with FastImage. I hope it would help.

 <FastImage
          style={{ width: SCREEN_WIDTH, height: height }}
          source={{
            uri: uri,
            priority: FastImage.priority.normal,
          }}
          resizeMode={FastImage.resizeMode.center}
 />

@JJMoon Do you know how to force FastImage re-render the image when I change image size in the style.
The Image always re-render but FastImage doesn't.

Hello together,

I tried out all possible solutions from this thread, but with RN 0.64.2 and Fresco 2.5.0.
I am using NDK v22.1.7171670 and gradle 4.2.1.

I am currently stuck at the following error (is the problem related to android-emulator?):

E/AndroidRuntime: FATAL EXCEPTION: mqt_native_modules
    Process: com.veerle.wiener.debug, PID: 10813
    java.lang.NoSuchMethodError: No static method initialize(Lcom/facebook/imagepipeline/core/ImagePipelineConfig;)V in class Lcom/facebook/imagepipeline/core/ImagePipelineFactory; or its super classes (declaration of 'com.facebook.imagepipeline.core.ImagePipelineFactory' appears in /data/app/<project>/base.apk!classes16.dex)
        at com.facebook.drawee.backends.pipeline.Fresco.initialize(Fresco.java:83)
        at com.facebook.drawee.backends.pipeline.Fresco.initialize(Fresco.java:45)
        at com.facebook.react.modules.fresco.FrescoModule.initialize(FrescoModule.java:114)
        at com.facebook.react.bridge.ModuleHolder.doInitialize(ModuleHolder.java:236)
        at com.facebook.react.bridge.ModuleHolder.markInitializable(ModuleHolder.java:100)
        at com.facebook.react.bridge.NativeModuleRegistry.notifyJSInstanceInitialized(NativeModuleRegistry.java:103)
        at com.facebook.react.bridge.CatalystInstanceImpl$2.run(CatalystInstanceImpl.java:438)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:27)
        at android.os.Looper.loop(Looper.java:223)
        at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run(MessageQueueThreadImpl.java:226)
        at java.lang.Thread.run(Thread.java:923)
I/Process: Sending signal. PID: 10813 SIG: 9

@JJMoon using FastImage with local images (require) gave me a bad flickering experience.

While waiting the team fix this issue, I am using both Image and FastImage like that
if(height < blurSize){ return <Image/> }else{ return <FastImage/> }

Was this page helpful?
0 / 5 - 0 ratings