2019-05-09 14:11:11 -03:00
|
|
|
package org.thoughtcrime.securesms.imageeditor.renderers;
|
|
|
|
|
|
|
|
import android.content.res.Resources;
|
|
|
|
import android.graphics.Canvas;
|
|
|
|
import android.graphics.Paint;
|
|
|
|
import android.graphics.Path;
|
|
|
|
import android.graphics.RectF;
|
|
|
|
import android.os.Parcel;
|
2020-08-19 10:06:26 +10:00
|
|
|
import androidx.annotation.ColorRes;
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
import androidx.core.content.res.ResourcesCompat;
|
2019-05-09 14:11:11 -03:00
|
|
|
|
2019-07-24 12:30:23 +10:00
|
|
|
import network.loki.messenger.R;
|
2019-05-09 14:11:11 -03:00
|
|
|
import org.thoughtcrime.securesms.imageeditor.Bounds;
|
|
|
|
import org.thoughtcrime.securesms.imageeditor.Renderer;
|
|
|
|
import org.thoughtcrime.securesms.imageeditor.RendererContext;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Renders a box outside of the current crop area using {@link R.color#crop_area_renderer_outer_color}
|
|
|
|
* and around the edge it renders the markers for the thumbs using {@link R.color#crop_area_renderer_edge_color},
|
|
|
|
* {@link R.dimen#crop_area_renderer_edge_thickness} and {@link R.dimen#crop_area_renderer_edge_size}.
|
|
|
|
* <p>
|
|
|
|
* Hit tests outside of the bounds.
|
|
|
|
*/
|
|
|
|
public final class CropAreaRenderer implements Renderer {
|
|
|
|
|
|
|
|
@ColorRes
|
|
|
|
private final int color;
|
|
|
|
|
|
|
|
private final Path cropClipPath = new Path();
|
|
|
|
private final Path screenClipPath = new Path();
|
|
|
|
|
|
|
|
private final RectF dst = new RectF();
|
|
|
|
private final Paint paint = new Paint();
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void render(@NonNull RendererContext rendererContext) {
|
|
|
|
rendererContext.save();
|
|
|
|
|
|
|
|
Canvas canvas = rendererContext.canvas;
|
|
|
|
Resources resources = rendererContext.context.getResources();
|
|
|
|
|
|
|
|
canvas.clipPath(cropClipPath);
|
|
|
|
canvas.drawColor(ResourcesCompat.getColor(resources, color, null));
|
|
|
|
|
|
|
|
rendererContext.mapRect(dst, Bounds.FULL_BOUNDS);
|
|
|
|
|
|
|
|
final int thickness = resources.getDimensionPixelSize(R.dimen.crop_area_renderer_edge_thickness);
|
|
|
|
final int size = (int) Math.min(resources.getDimensionPixelSize(R.dimen.crop_area_renderer_edge_size), Math.min(dst.width(), dst.height()) / 3f - 10);
|
|
|
|
|
|
|
|
paint.setColor(ResourcesCompat.getColor(resources, R.color.crop_area_renderer_edge_color, null));
|
|
|
|
|
|
|
|
rendererContext.canvasMatrix.setToIdentity();
|
|
|
|
screenClipPath.reset();
|
|
|
|
screenClipPath.moveTo(dst.left, dst.top);
|
|
|
|
screenClipPath.lineTo(dst.right, dst.top);
|
|
|
|
screenClipPath.lineTo(dst.right, dst.bottom);
|
|
|
|
screenClipPath.lineTo(dst.left, dst.bottom);
|
|
|
|
screenClipPath.close();
|
|
|
|
canvas.clipPath(screenClipPath);
|
|
|
|
canvas.translate(dst.left, dst.top);
|
|
|
|
|
|
|
|
float halfDx = (dst.right - dst.left - size + thickness) / 2;
|
|
|
|
float halfDy = (dst.bottom - dst.top - size + thickness) / 2;
|
|
|
|
|
|
|
|
canvas.drawRect(-thickness, -thickness, size, size, paint);
|
|
|
|
|
|
|
|
canvas.translate(0, halfDy);
|
|
|
|
canvas.drawRect(-thickness, -thickness, size, size, paint);
|
|
|
|
|
|
|
|
canvas.translate(0, halfDy);
|
|
|
|
canvas.drawRect(-thickness, -thickness, size, size, paint);
|
|
|
|
|
|
|
|
canvas.translate(halfDx, 0);
|
|
|
|
canvas.drawRect(-thickness, -thickness, size, size, paint);
|
|
|
|
|
|
|
|
canvas.translate(halfDx, 0);
|
|
|
|
canvas.drawRect(-thickness, -thickness, size, size, paint);
|
|
|
|
|
|
|
|
canvas.translate(0, -halfDy);
|
|
|
|
canvas.drawRect(-thickness, -thickness, size, size, paint);
|
|
|
|
|
|
|
|
canvas.translate(0, -halfDy);
|
|
|
|
canvas.drawRect(-thickness, -thickness, size, size, paint);
|
|
|
|
|
|
|
|
canvas.translate(-halfDx, 0);
|
|
|
|
canvas.drawRect(-thickness, -thickness, size, size, paint);
|
|
|
|
|
|
|
|
rendererContext.restore();
|
|
|
|
}
|
|
|
|
|
|
|
|
public CropAreaRenderer(@ColorRes int color) {
|
|
|
|
this.color = color;
|
|
|
|
cropClipPath.toggleInverseFillType();
|
|
|
|
cropClipPath.moveTo(Bounds.LEFT, Bounds.TOP);
|
|
|
|
cropClipPath.lineTo(Bounds.RIGHT, Bounds.TOP);
|
|
|
|
cropClipPath.lineTo(Bounds.RIGHT, Bounds.BOTTOM);
|
|
|
|
cropClipPath.lineTo(Bounds.LEFT, Bounds.BOTTOM);
|
|
|
|
cropClipPath.close();
|
|
|
|
screenClipPath.toggleInverseFillType();
|
|
|
|
}
|
|
|
|
|
|
|
|
private CropAreaRenderer(Parcel in) {
|
|
|
|
this(in.readInt());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean hitTest(float x, float y) {
|
2019-05-16 17:50:49 -03:00
|
|
|
return !Bounds.contains(x, y);
|
2019-05-09 14:11:11 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
public static final Creator<CropAreaRenderer> CREATOR = new Creator<CropAreaRenderer>() {
|
|
|
|
@Override
|
|
|
|
public CropAreaRenderer createFromParcel(Parcel in) {
|
|
|
|
return new CropAreaRenderer(in);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public CropAreaRenderer[] newArray(int size) {
|
|
|
|
return new CropAreaRenderer[size];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int describeContents() {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void writeToParcel(Parcel dest, int flags) {
|
|
|
|
dest.writeInt(color);
|
|
|
|
}
|
|
|
|
}
|