Skip to content

Commit

Permalink
Added basic region decoding sample
Browse files Browse the repository at this point in the history
Summary:
This adds a very simple region decoding sample to the Showcase ap.
This is not fully working yet. It only works for JPEGs and if the selected image is resized or downsampled, the decoded region will be wrong and you will see a black area around the actual region.

Reviewed By: erikandre, foghina

Differential Revision: D6759270

fbshipit-source-id: 576310f74ed85ed27a1303642ddd1cd1285d23de
  • Loading branch information
oprisnik authored and facebook-github-bot committed Jan 19, 2018
1 parent 6449ca0 commit baa629d
Show file tree
Hide file tree
Showing 6 changed files with 275 additions and 0 deletions.
Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down
@@ -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<ImageInfo> mControllerListener =
new BaseControllerListener<ImageInfo>() {
@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);
}
}
}
Expand Up @@ -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);
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
}
}
}
@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:fresco="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
>

<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>

<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/drawee_view_full"
android:layout_width="match_parent"
android:layout_height="wrap_content"
fresco:viewAspectRatio="2"
fresco:actualImageScaleType="fitCenter"
/>

<com.facebook.fresco.samples.showcase.imagepipeline.widget.ResizableFrameLayout
android:id="@+id/frame_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minHeight="8dp"
android:minWidth="8dp"
android:background="#80ffffff"
>

<View
android:id="@+id/btn_resize"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="bottom|right"
android:background="@drawable/resize_outline"
tools:ignore="RtlHardcoded"
/>

</com.facebook.fresco.samples.showcase.imagepipeline.widget.ResizableFrameLayout>
</FrameLayout>

<TextView
android:id="@+id/text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/padding_medium"
android:gravity="center"
android:text="@string/imagepipeline_region_decoding_help"
android:textAppearance="?android:attr/textAppearanceSmall"
/>

<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/drawee_view_region"
android:layout_width="match_parent"
android:layout_height="match_parent"
fresco:actualImageScaleType="fitCenter"
/>

</LinearLayout>
4 changes: 4 additions & 0 deletions samples/showcase/src/main/res/menu/activity_main_drawer.xml
Expand Up @@ -90,6 +90,10 @@
android:id="@+id/nav_imagepipeline_bitmap_factory"
android:title="@string/imagepipeline_bitmap_factory_title"
/>
<item
android:id="@+id/nav_imagepipeline_region_decoding"
android:title="@string/imagepipeline_region_decoding_title"
/>
</group>
</menu>
</item>
Expand Down
3 changes: 3 additions & 0 deletions samples/showcase/src/main/res/values/strings.xml
Expand Up @@ -102,6 +102,9 @@
<string name="imagepipeline_bitmap_factory_case_create_scaled">Create Scaled</string>
<string name="imagepipeline_bitmap_factory_case_create_transformed">Create Transformed</string>

<string name="imagepipeline_region_decoding_title">Region Decoding</string>
<string name="imagepipeline_region_decoding_help">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.</string>

<string name="category_format">Image Format</string>

<string name="format_switch_checker_board">Checker board background</string>
Expand Down

0 comments on commit baa629d

Please sign in to comment.