mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
6f06f76e38
Summary: Prior to this patch DrawCommands weren't get clipped by parent DrawCommands at all. For example, a <View> element with size 200x200 with overflow:hidden should clip its child which is 400x400, but this didn't happen. However, if parent <View> would mount to an Android View, it would clip the child regardless of the overflow attribute value (because Android Views always clip whatever is drawing inside that View against its boundaries). This diff is fixing these issue, implementing overflow attribute support and making clipping behavior consistent between nodes that mount to View and nodes that don't. Reviewed By: ahmedre Differential Revision: D2768643
198 lines
5.5 KiB
Java
198 lines
5.5 KiB
Java
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*/
|
|
|
|
package com.facebook.react.flat;
|
|
|
|
import javax.annotation.Nullable;
|
|
|
|
import android.graphics.Bitmap;
|
|
import android.graphics.BitmapShader;
|
|
import android.graphics.Canvas;
|
|
import android.graphics.Matrix;
|
|
import android.graphics.Paint;
|
|
import android.graphics.Path;
|
|
import android.graphics.PorterDuff;
|
|
import android.graphics.PorterDuffColorFilter;
|
|
import android.graphics.Shader;
|
|
|
|
import com.facebook.drawee.drawable.ScalingUtils.ScaleType;
|
|
import com.facebook.imagepipeline.request.ImageRequest;
|
|
import com.facebook.infer.annotation.Assertions;
|
|
import com.facebook.react.views.image.ImageResizeMode;
|
|
|
|
/**
|
|
* DrawImageWithPipeline is DrawCommand that can draw a local or remote image.
|
|
* It uses BitmapRequestHelper internally to fetch and cache the images.
|
|
*/
|
|
/* package */ final class DrawImageWithPipeline extends AbstractDrawBorder implements DrawImage {
|
|
|
|
private static final Paint PAINT = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
|
|
private static final int BORDER_BITMAP_PATH_DIRTY = 1 << 1;
|
|
|
|
private final Matrix mTransform = new Matrix();
|
|
private ScaleType mScaleType = ImageResizeMode.defaultValue();
|
|
private @Nullable BitmapRequestHelper mBitmapRequestHelper;
|
|
private @Nullable PorterDuffColorFilter mColorFilter;
|
|
private @Nullable FlatViewGroup.InvalidateCallback mCallback;
|
|
private @Nullable Path mPathForRoundedBitmap;
|
|
private @Nullable BitmapShader mBitmapShader;
|
|
private boolean mForceClip;
|
|
|
|
@Override
|
|
public boolean hasImageRequest() {
|
|
return mBitmapRequestHelper != null;
|
|
}
|
|
|
|
@Override
|
|
public void setImageRequest(@Nullable ImageRequest imageRequest) {
|
|
mBitmapShader = null;
|
|
|
|
if (imageRequest == null) {
|
|
mBitmapRequestHelper = null;
|
|
} else {
|
|
mBitmapRequestHelper = new BitmapRequestHelper(imageRequest, this);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setTintColor(int tintColor) {
|
|
if (tintColor == 0) {
|
|
mColorFilter = null;
|
|
} else {
|
|
mColorFilter = new PorterDuffColorFilter(tintColor, PorterDuff.Mode.SRC_ATOP);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setScaleType(ScaleType scaleType) {
|
|
mScaleType = scaleType;
|
|
}
|
|
|
|
@Override
|
|
public ScaleType getScaleType() {
|
|
return mScaleType;
|
|
}
|
|
|
|
@Override
|
|
protected void onDraw(Canvas canvas) {
|
|
Bitmap bitmap = Assertions.assumeNotNull(mBitmapRequestHelper).getBitmap();
|
|
if (bitmap == null) {
|
|
return;
|
|
}
|
|
|
|
PAINT.setColorFilter(mColorFilter);
|
|
|
|
if (getBorderRadius() < 0.5f) {
|
|
canvas.drawBitmap(bitmap, mTransform, PAINT);
|
|
} else {
|
|
if (mBitmapShader == null) {
|
|
mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
|
|
mBitmapShader.setLocalMatrix(mTransform);
|
|
}
|
|
PAINT.setShader(mBitmapShader);
|
|
canvas.drawPath(getPathForRoundedBitmap(), PAINT);
|
|
}
|
|
|
|
drawBorders(canvas);
|
|
}
|
|
|
|
@Override
|
|
protected boolean shouldClip() {
|
|
return mForceClip || super.shouldClip();
|
|
}
|
|
|
|
@Override
|
|
public void setBorderRadius(float borderRadius) {
|
|
super.setBorderRadius(borderRadius);
|
|
setFlag(BORDER_BITMAP_PATH_DIRTY);
|
|
}
|
|
|
|
@Override
|
|
public void onAttached(FlatViewGroup.InvalidateCallback callback) {
|
|
mCallback = callback;
|
|
Assertions.assumeNotNull(mBitmapRequestHelper).attach();
|
|
}
|
|
|
|
@Override
|
|
public void onDetached() {
|
|
Assertions.assumeNotNull(mBitmapRequestHelper).detach();
|
|
|
|
if (mBitmapRequestHelper.isDetached()) {
|
|
// Make sure we don't hold on to the Bitmap.
|
|
mBitmapShader = null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onBoundsChanged() {
|
|
super.onBoundsChanged();
|
|
setFlag(BORDER_BITMAP_PATH_DIRTY);
|
|
}
|
|
|
|
/* package */ void updateBounds(Bitmap bitmap) {
|
|
Assertions.assumeNotNull(mCallback).invalidate();
|
|
|
|
float left = getLeft();
|
|
float top = getTop();
|
|
|
|
float containerWidth = getRight() - left;
|
|
float containerHeight = getBottom() - top;
|
|
|
|
float imageWidth = (float) bitmap.getWidth();
|
|
float imageHeight = (float) bitmap.getHeight();
|
|
|
|
mForceClip = false;
|
|
|
|
if (mScaleType == ScaleType.FIT_XY) {
|
|
mTransform.setScale(containerWidth / imageWidth, containerHeight / imageHeight);
|
|
mTransform.postTranslate(left, top);
|
|
return;
|
|
}
|
|
|
|
final float scale;
|
|
|
|
if (mScaleType == ScaleType.CENTER_INSIDE) {
|
|
final float ratio;
|
|
if (containerWidth >= imageWidth && containerHeight >= imageHeight) {
|
|
scale = 1.0f;
|
|
} else {
|
|
scale = Math.min(containerWidth / imageWidth, containerHeight / imageHeight);
|
|
}
|
|
} else {
|
|
scale = Math.max(containerWidth / imageWidth, containerHeight / imageHeight);
|
|
}
|
|
|
|
float paddingLeft = (containerWidth - imageWidth * scale) / 2;
|
|
float paddingTop = (containerHeight - imageHeight * scale) / 2;
|
|
|
|
mForceClip = paddingLeft < 0 || paddingTop < 0;
|
|
|
|
mTransform.setScale(scale, scale);
|
|
mTransform.postTranslate(left + paddingLeft, top + paddingTop);
|
|
|
|
if (mBitmapShader != null) {
|
|
mBitmapShader.setLocalMatrix(mTransform);
|
|
}
|
|
}
|
|
|
|
private Path getPathForRoundedBitmap() {
|
|
if (isFlagSet(BORDER_BITMAP_PATH_DIRTY)) {
|
|
if (mPathForRoundedBitmap == null) {
|
|
mPathForRoundedBitmap = new Path();
|
|
}
|
|
|
|
updatePath(mPathForRoundedBitmap, 1.0f);
|
|
|
|
resetFlag(BORDER_BITMAP_PATH_DIRTY);
|
|
}
|
|
|
|
return mPathForRoundedBitmap;
|
|
}
|
|
}
|