avito-tech / krop

Small widget for image cropping in Instagram-like style
MIT License
126 stars 18 forks source link

Bug: Cropping doesn't work well on high-resolution images #9

Open AndroidDeveloperLB opened 6 years ago

AndroidDeveloperLB commented 6 years ago

When I choose a high resolution image, and zoom in, it shows it very blurry, even though in reality when I zoom in on it inside a photo app, it shows fine, and then when I choose to crop, it will stay blurry.

Here's an example image to test: http://img.gawkerassets.com/img/18tbvl3htulmyjpg/original.jpg

Try to zoom in and crop the most left car. Using a photo app, I get this:

image

Using this library, I get this: image

ntoskrnl commented 6 years ago

Current implementation does not support tiling, so it downscales large images. Currently, it works as intended.

@solkin are there plans to support large images?

AndroidDeveloperLB commented 6 years ago

Why downscale it to an image that has low quality though? And why not use partial decoding instead? This way you don't have to load the entire photo. Here: https://developer.android.com/reference/android/graphics/BitmapRegionDecoder.html

Available from Android API 10...

ntoskrnl commented 6 years ago

I was talking about the way the image is displayed: we load subsampled image. That is why you see it with such a low quality. Then current implementation simply crops loaded bitmap. This is the way it is right now.

Using BitmapRegionDecoder is one solution. It will work if the crop region is small. However, when performing crop, we will need to load it into memory and save into new file. So even with BitmapRegionDecoder, provided that you want to keep original quality, we still can get OutOfMemoryError on large image if our cropping region is too large. :(

https://stackoverflow.com/questions/12032847/crop-a-portion-of-a-large-bitmap-without-outofmemory-android

If we want to avoid losing image quality, we should probably look into some native libs.

AndroidDeveloperLB commented 6 years ago

I see. I still have a few questions:

  1. Can you at least consider avoid downsampling on Android 8.0+ when possible? On these versions, the Bitmap is stored on the native memory, so OOM is possible only when you really handle an impossibly large image.

  2. How does the gallery app do it? Is it open sourced? Maybe you can grab the code from it? How can one obtain the code of i?

  3. How does this library do it: https://github.com/ArthurHub/Android-Image-Cropper ?

  4. I think it could also work this way: You've explained about the potential of OOM, but what I did here is to crop a very small rectangle of the image. As long as it's possible to do it, it should try to decode the partial area. Only if there are detected issues while doing so, it should downsample, and tell us it's downsampled.

ntoskrnl commented 6 years ago
  1. I've heard bitmaps were in native memory on Android 2.x and earlier, then later they changed it. Didn't know they moved back to native on 8.0+. We will look into it!

  2. There are libraries, that can show large images, for example https://github.com/davemorrissey/subsampling-scale-image-view I believe standard gallery app does the same.

  3. He is catching OutOfMemoryError, interesting hack :) https://github.com/ArthurHub/Android-Image-Cropper/blob/1bf68d4b7992655a9c755e7afb824f8847df1672/cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapUtils.java#L166 He tries to load image with original scale, then downscales it until it is loaded without OOM. Looks like he will still loose quality when both image and crop region are large.

  4. I agree, when you crop small area in a large image, you can keep the original quality (or at least won't make it that blurry). Perhaps, if we use BitmapRegionDecoder together with the hack from the library you mentioned, we can improve our image cropper for your use-case. Anyway, I think in order to load and display the image, we should use SubsamplingScaleImageView or similar, otherwise it will be blurry.

AndroidDeveloperLB commented 6 years ago
  1. On 2.x and earlier, this is true, but it worked in a weird way: if you reached the heap max size, you'd still get OOM, even though the Bitmap is in the "normal" RAM. So, it was as if it's in the heap memory, and you had to call 'recycle' on it too, because it wasn't really on the heap. In short, a nightmare of memory management.

  2. I meant the cropping part, and not just displaying images...

  3. Well it's better than crashing though. I don't think it will tell you that it got the low quality crop though.

  4. You think it's possible to just use what they have on SubsamplingScaleImageView , and make it crop-able ?