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; import androidx.annotation.ColorRes; import androidx.annotation.NonNull; import androidx.core.content.res.ResourcesCompat; import network.loki.messenger.R; 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}. *

* 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) { return !Bounds.contains(x, y); } public static final Creator CREATOR = new Creator() { @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); } }