diff --git a/samples/showcase/src/main/java/com/facebook/fresco/samples/showcase/MainActivity.java b/samples/showcase/src/main/java/com/facebook/fresco/samples/showcase/MainActivity.java index 397f695e6b..6dbdf09560 100644 --- a/samples/showcase/src/main/java/com/facebook/fresco/samples/showcase/MainActivity.java +++ b/samples/showcase/src/main/java/com/facebook/fresco/samples/showcase/MainActivity.java @@ -52,6 +52,7 @@ import com.facebook.fresco.samples.showcase.imagepipeline.ImagePipelinePostProcessorFragment; import com.facebook.fresco.samples.showcase.imagepipeline.ImagePipelinePrefetchFragment; import com.facebook.fresco.samples.showcase.imagepipeline.ImagePipelineQualifiedResourceFragment; +import com.facebook.fresco.samples.showcase.imagepipeline.ImagePipelineRegionDecodingFragment; import com.facebook.fresco.samples.showcase.imagepipeline.ImagePipelineResizingFragment; import com.facebook.fresco.samples.showcase.imagepipeline.MediaVariationsFragment; import com.facebook.fresco.samples.showcase.imagepipeline.PartialRequestFragment; @@ -203,6 +204,9 @@ private void handleNavigationItemClick(int itemId) { case R.id.nav_imagepipeline_bitmap_factory: fragment = new ImagePipelineBitmapFactoryFragment(); break; + case R.id.nav_imagepipeline_region_decoding: + fragment = new ImagePipelineRegionDecodingFragment(); + break; // Image Formats case R.id.nav_format_pjpeg: diff --git a/samples/showcase/src/main/java/com/facebook/fresco/samples/showcase/imagepipeline/ImagePipelineRegionDecodingFragment.java b/samples/showcase/src/main/java/com/facebook/fresco/samples/showcase/imagepipeline/ImagePipelineRegionDecodingFragment.java new file mode 100644 index 0000000000..354867b05f --- /dev/null +++ b/samples/showcase/src/main/java/com/facebook/fresco/samples/showcase/imagepipeline/ImagePipelineRegionDecodingFragment.java @@ -0,0 +1,170 @@ +/* + * This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.facebook.fresco.samples.showcase.imagepipeline; + +import android.graphics.Rect; +import android.graphics.drawable.Animatable; +import android.net.Uri; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import com.facebook.drawee.backends.pipeline.Fresco; +import com.facebook.drawee.controller.BaseControllerListener; +import com.facebook.drawee.controller.ControllerListener; +import com.facebook.drawee.view.SimpleDraweeView; +import com.facebook.fresco.samples.showcase.BaseShowcaseFragment; +import com.facebook.fresco.samples.showcase.R; +import com.facebook.fresco.samples.showcase.imagepipeline.widget.ResizableFrameLayout; +import com.facebook.fresco.samples.showcase.misc.ImageUriProvider; +import com.facebook.imagepipeline.common.ImageDecodeOptions; +import com.facebook.imagepipeline.decoder.ImageDecoder; +import com.facebook.imagepipeline.image.CloseableImage; +import com.facebook.imagepipeline.image.CloseableStaticBitmap; +import com.facebook.imagepipeline.image.EncodedImage; +import com.facebook.imagepipeline.image.ImageInfo; +import com.facebook.imagepipeline.image.QualityInfo; +import com.facebook.imagepipeline.platform.PlatformDecoder; +import com.facebook.imagepipeline.request.ImageRequestBuilder; + +/** Simple region decoding example that renders the original image and a selected region. */ +public class ImagePipelineRegionDecodingFragment extends BaseShowcaseFragment { + + private SimpleDraweeView mFullDraweeView; + private ResizableFrameLayout mSelectedRegion; + private SimpleDraweeView mRegionDraweeView; + private Uri mUri; + private @Nullable ImageInfo mImageInfo; + + private final ControllerListener mControllerListener = + new BaseControllerListener() { + @Override + public void onFinalImageSet( + String id, + @javax.annotation.Nullable ImageInfo imageInfo, + @javax.annotation.Nullable Animatable animatable) { + mImageInfo = imageInfo; + mSelectedRegion.setUpdateMaximumDimensionOnNextSizeChange(true); + if (imageInfo != null) { + mFullDraweeView.setAspectRatio(imageInfo.getWidth() / (float) imageInfo.getHeight()); + mFullDraweeView.requestLayout(); + updateRegion(); + } + } + }; + + private final ResizableFrameLayout.SizeChangedListener mSizeChangedListener = + new ResizableFrameLayout.SizeChangedListener() { + @Override + public void onSizeChanged(int widthPx, int heightPx) { + updateRegion(); + } + }; + + @Nullable + @Override + public View onCreateView( + LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_imagepipeline_region_decoding, container, false); + } + + @Override + public void onViewCreated(final View view, @Nullable Bundle savedInstanceState) { + final ImageUriProvider imageUriProvider = ImageUriProvider.getInstance(getContext()); + mUri = + imageUriProvider.createSampleUri( + ImageUriProvider.ImageSize.L, ImageUriProvider.Orientation.LANDSCAPE); + + mFullDraweeView = (SimpleDraweeView) view.findViewById(R.id.drawee_view_full); + mFullDraweeView.setController( + Fresco.newDraweeControllerBuilder() + .setUri(mUri) + .setControllerListener(mControllerListener) + .build()); + + mSelectedRegion = (ResizableFrameLayout) view.findViewById(R.id.frame_main); + mSelectedRegion.init(view.findViewById(R.id.btn_resize)); + mSelectedRegion.setSizeChangedListener(mSizeChangedListener); + + mRegionDraweeView = (SimpleDraweeView) view.findViewById(R.id.drawee_view_region); + mRegionDraweeView.setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View v) { + updateRegion(); + } + }); + } + + private void updateRegion() { + if (mImageInfo == null) { + return; + } + int left = 0; + int top = 0; + int right = + mSelectedRegion.getMeasuredWidth() + * mImageInfo.getWidth() + / mFullDraweeView.getMeasuredWidth(); + int bottom = + mSelectedRegion.getMeasuredHeight() + * mImageInfo.getHeight() + / mFullDraweeView.getMeasuredHeight(); + + ImageDecoder regionDecoder = createRegionDecoder(left, top, right, bottom); + mRegionDraweeView.setController( + Fresco.newDraweeControllerBuilder() + .setImageRequest( + ImageRequestBuilder.newBuilderWithSource(mUri) + .setImageDecodeOptions( + ImageDecodeOptions.newBuilder() + .setCustomImageDecoder(regionDecoder) + .build()) + .build()) + .build()); + } + + @Override + public int getTitleId() { + return R.string.imagepipeline_region_decoding_title; + } + + private ImageDecoder createRegionDecoder(int left, int top, int right, int bottom) { + return new RegionDecoder( + Fresco.getImagePipelineFactory().getPlatformDecoder(), new Rect(left, top, right, bottom)); + } + + public static class RegionDecoder implements ImageDecoder { + + private final PlatformDecoder mPlatformDecoder; + private final Rect mRegion; + + public RegionDecoder(PlatformDecoder platformDecoder, Rect region) { + mPlatformDecoder = platformDecoder; + mRegion = region; + } + + @Override + public CloseableImage decode( + EncodedImage encodedImage, + int length, + QualityInfo qualityInfo, + ImageDecodeOptions options) { + return new CloseableStaticBitmap( + mPlatformDecoder.decodeJPEGFromEncodedImage( + encodedImage, options.bitmapConfig, mRegion, length), + qualityInfo, + 0); + } + } +} diff --git a/samples/showcase/src/main/java/com/facebook/fresco/samples/showcase/imagepipeline/widget/ResizableFrameLayout.java b/samples/showcase/src/main/java/com/facebook/fresco/samples/showcase/imagepipeline/widget/ResizableFrameLayout.java index 6516cea741..4a7824435d 100644 --- a/samples/showcase/src/main/java/com/facebook/fresco/samples/showcase/imagepipeline/widget/ResizableFrameLayout.java +++ b/samples/showcase/src/main/java/com/facebook/fresco/samples/showcase/imagepipeline/widget/ResizableFrameLayout.java @@ -19,15 +19,24 @@ import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; +import javax.annotation.Nullable; public class ResizableFrameLayout extends FrameLayout { + public interface SizeChangedListener { + + void onSizeChanged(int widthPx, int heightPx); + } + private View mCornerIndicator; private boolean mResizing; private float mLastX; private float mLastY; private int mMaximumWidth; private int mMaximumHeight; + private boolean mUpdateMaximumDimensionOnNextSizeChange; + + private @Nullable SizeChangedListener mSizeChangedListener; public ResizableFrameLayout(Context context) { super(context); @@ -48,6 +57,15 @@ public void init(View cornerIndicator) { mMaximumHeight = getHeight(); } + public void setUpdateMaximumDimensionOnNextSizeChange( + boolean updateMaximumDimensionOnNextSizeChange) { + mUpdateMaximumDimensionOnNextSizeChange = updateMaximumDimensionOnNextSizeChange; + } + + public void setSizeChangedListener(@Nullable SizeChangedListener sizeChangedListener) { + mSizeChangedListener = sizeChangedListener; + } + @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB) @Override public boolean onInterceptTouchEvent(MotionEvent event) { @@ -94,4 +112,17 @@ public boolean onTouchEvent(MotionEvent event) { getMinimumHeight() - mCornerIndicator.getHeight()); return true; } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + if (mUpdateMaximumDimensionOnNextSizeChange) { + mMaximumWidth = w; + mMaximumHeight = h; + mUpdateMaximumDimensionOnNextSizeChange = false; + } + if (mSizeChangedListener != null) { + mSizeChangedListener.onSizeChanged(w, h); + } + } } diff --git a/samples/showcase/src/main/res/layout/fragment_imagepipeline_region_decoding.xml b/samples/showcase/src/main/res/layout/fragment_imagepipeline_region_decoding.xml new file mode 100644 index 0000000000..e7f38ad7bc --- /dev/null +++ b/samples/showcase/src/main/res/layout/fragment_imagepipeline_region_decoding.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + diff --git a/samples/showcase/src/main/res/menu/activity_main_drawer.xml b/samples/showcase/src/main/res/menu/activity_main_drawer.xml index ab00f7c38a..564d0a581b 100644 --- a/samples/showcase/src/main/res/menu/activity_main_drawer.xml +++ b/samples/showcase/src/main/res/menu/activity_main_drawer.xml @@ -90,6 +90,10 @@ android:id="@+id/nav_imagepipeline_bitmap_factory" android:title="@string/imagepipeline_bitmap_factory_title" /> + diff --git a/samples/showcase/src/main/res/values/strings.xml b/samples/showcase/src/main/res/values/strings.xml index 517ee7af0f..3bd030b920 100644 --- a/samples/showcase/src/main/res/values/strings.xml +++ b/samples/showcase/src/main/res/values/strings.xml @@ -102,6 +102,9 @@ Create Scaled Create Transformed + Region Decoding + Select a region for the full image above. The selected region will be rendered below. NOTE: This currently only works for JPEG images that are not resized / downsampled. + Image Format Checker board background