2

I am working on Photography app like this : https://play.google.com/store/apps/details?id=com.photo.editor.collage.maker.photoblender&hl=en

I have to implement functionality like this : enter image description here

I have 2 functionalities with this view...

1) with splash background

enter image description here enter image description here enter image description here

2) with blurred background

enter image description here enter image description here enter image description here

Both are using 2 shapes.... Now as you are using path to draw shape....i wants to draw image bitmap shape like above.

I have more shapes like below (All having 2 images as above):

3)

enter image description here enter image description here

I wants to replace this portion of code you are using :

private void createPath() {
            path.reset();
            path.moveTo(114, 156);
            float[] points = {68, 138, 19, 136, 21, 87, 8, 39, 56, 26, 97, -2, 123, 40, 163, 71, 131, 109};
            for (int i = 0; i < points.length; i += 2) {
                path.lineTo(points[i], points[i + 1]);
            }
            path.close();
            Matrix m = new Matrix();
            m.setRectToRect(new RectF(0, 0, 160, 160), clip, Matrix.ScaleToFit.CENTER);
            path.transform(m);
            transformedPath.set(path);
        }

i did above functionality by adding shape as path, but when i am trying to use shapes as bitmap i didn't get complete result.... here is my code for view.

class MotionImageView extends View implements MatrixGestureDetector.OnMatrixChangeListener {
    private final Bitmap shapeMaskBitmap;
    private final Bitmap shapeShadowBitmap;
    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Paint monoPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Paint borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Bitmap bitmap, blur;
    Matrix matrix = new Matrix();
    Matrix pathMatrix = new Matrix();
//    Path path = new Path();
//    Path transformedPath = new Path();
    MatrixGestureDetector detector;
    RectF clip = new RectF();

    public MotionImageView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        Log.e("~~~~", "1111");
        bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.model);
        blur = blur(context, bitmap, 8);
        ColorMatrix cm = new ColorMatrix();
        cm.setSaturation(0.25f);
        monoPaint.setColorFilter(new ColorMatrixColorFilter(cm));
        borderPaint.setStyle(Paint.Style.STROKE);
        borderPaint.setColor(0xccffffff);
        borderPaint.setStrokeWidth(4);
        borderPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        detector = new MatrixGestureDetector(pathMatrix, this);

        shapeMaskBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.shape_mask);
        shapeShadowBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.shape_shadow);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        Shader shader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
        RectF src = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());
        RectF dst = new RectF(0, 0, w, h);
        matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
        shader.setLocalMatrix(matrix);
        paint.setShader(shader);
        matrix.mapRect(clip, src);
//        createPath();
    }

    /*private void createPath() {
        path.reset();
        path.moveTo(114, 156);
        float[] points = {68, 138, 19, 136, 21, 87, 8, 39, 56, 26, 97, -2, 123, 40, 163, 71, 131, 109};
        for (int i = 0; i < points.length; i += 2) {
            path.lineTo(points[i], points[i + 1]);
        }
        path.close();
        Matrix m = new Matrix();
        m.setRectToRect(new RectF(0, 0, 160, 160), clip, Matrix.ScaleToFit.CENTER);
        path.transform(m);
        transformedPath.set(path);
    }*/

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(blur, matrix, monoPaint);
        canvas.save();
        canvas.clipRect(clip);
//        canvas.drawPath(transformedPath, paint);
        canvas.drawBitmap(shapeMaskBitmap, pathMatrix, paint);
        canvas.restore();
//        canvas.drawPath(transformedPath, borderPaint);
        canvas.drawBitmap(shapeShadowBitmap, pathMatrix, borderPaint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        detector.onTouchEvent(event);
        return true;
    }

    @Override
    public void onChange(Matrix matrix) {
//        path.transform(matrix, transformedPath);
        pathMatrix.set(matrix);
        invalidate();
    }

    Bitmap blur(Context ctx, Bitmap src, float radius) {
        Bitmap bitmap = src.copy(src.getConfig(), true);

        RenderScript renderScript = RenderScript.create(ctx);
        Allocation blurInput = Allocation.createFromBitmap(renderScript, src);
        Allocation blurOutput = Allocation.createFromBitmap(renderScript, bitmap);

        ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript));
        blur.setInput(blurInput);
        blur.setRadius(radius);
        blur.forEach(blurOutput);

        blurOutput.copyTo(bitmap);
        renderScript.destroy();
        return bitmap;
    }
}

My output was like this :

enter image description here

Milan Hirpara
  • 534
  • 4
  • 18
  • yes i need it, if you have it. – Milan Hirpara Feb 23 '18 at 10:55
  • so instead of `canvas.drawPath(transformedPath, paint);` and `canvas.drawPath(transformedPath, borderPaint);` call `drawBitmap` twice - the second time with porter duff `Mode.SRC_IN` - see many solutions on how to mask one image with the another, like [this](https://badoo.com/techblog/blog/2016/10/17/masking-bitmaps/) for example – pskink Feb 27 '18 at 12:00
  • or you can use `BitmapShader` but the mask image has to be in `ALPHA_8` format - see `Bitmap#extractAlpha()` method – pskink Feb 27 '18 at 12:29
  • i said:`ALPHA_8` is it `ALPHA_8`? did you read a link i posted above? – pskink Feb 27 '18 at 13:53
  • hey bro...i was just trying what you said, i wants to do it first myself, i don't wants to bother you bro... now as i didn't get it send me your code if possible. well thanks for it bro. – Milan Hirpara Feb 28 '18 at 06:36
  • i have read all your above comments and tried it my self.. thanks for your time and share me how to do it with bitmaps instead of paths. – Milan Hirpara Feb 28 '18 at 06:46
  • so what is the format of your mask `Bitmap`? is it `ALPHA_8`? – pskink Feb 28 '18 at 08:19
  • YES, it is alpha_8 image – Milan Hirpara Feb 28 '18 at 08:22
  • i mean `shapeMaskBitmap`, not `shapeShadowBitmap` - post the code how are you checking that `shapeMaskBitmap` has `ALPHA_8` format – pskink Feb 28 '18 at 08:38
  • i wants code urgent give me if you have. i have just above two bitmaps 1 for mask and 1 for shadow, use that images and give me result if you have. i didn't get the concept of ALPHA_8. – Milan Hirpara Feb 28 '18 at 08:43

1 Answers1

4

what you are trying to do with PorterDuff.Mode.CLEAR and BlurMaskFilter will not work, if you want effect like this:

enter image description here

or this:

enter image description here

you have to use BitmapShader and ScriptIntrinsicBlur, see this sample custom View:

class V extends View implements MatrixGestureDetector.OnMatrixChangeListener {
    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Paint monoPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Paint borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Bitmap bitmap, blur;
    Matrix matrix = new Matrix();
    Matrix pathMatrix = new Matrix();
    Path path = new Path();
    Path transformedPath = new Path();
    MatrixGestureDetector detector;
    RectF clip = new RectF();

    public V(Context context) {
        super(context);
        bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.forest);
        blur = blur(context, bitmap, 8);
        ColorMatrix cm = new ColorMatrix();
        cm.setSaturation(0.25f);
        monoPaint.setColorFilter(new ColorMatrixColorFilter(cm));
        borderPaint.setStyle(Paint.Style.STROKE);
        borderPaint.setColor(0xccffffff);
        borderPaint.setStrokeWidth(4);
        detector = new MatrixGestureDetector(pathMatrix, this);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        Shader shader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
        RectF src = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());
        RectF dst = new RectF(0, 0, w, h);
        matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
        shader.setLocalMatrix(matrix);
        paint.setShader(shader);
        matrix.mapRect(clip, src);
        createPath();
    }

    private void createPath() {
        path.reset();
        path.moveTo(114, 156);
        float[] points = {68, 138, 19, 136, 21, 87, 8, 39, 56, 26, 97, -2, 123, 40, 163, 71, 131, 109};
        for (int i = 0; i < points.length; i += 2) {
            path.lineTo(points[i], points[i + 1]);
        }
        path.close();
        Matrix m = new Matrix();
        m.setRectToRect(new RectF(0, 0, 160, 160), clip, Matrix.ScaleToFit.CENTER);
        path.transform(m);
        transformedPath.set(path);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(blur, matrix, monoPaint);
        canvas.save();
        canvas.clipRect(clip);
        canvas.drawPath(transformedPath, paint);
        canvas.restore();
        canvas.drawPath(transformedPath, borderPaint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        detector.onTouchEvent(event);
        return true;
    }

    @Override
    public void onChange(Matrix matrix) {
        path.transform(matrix, transformedPath);
        invalidate();
    }

    Bitmap blur(Context ctx, Bitmap src, float radius) {
        Bitmap bitmap = src.copy(src.getConfig(), true);

        RenderScript renderScript = RenderScript.create(ctx);
        Allocation blurInput = Allocation.createFromBitmap(renderScript, src);
        Allocation blurOutput = Allocation.createFromBitmap(renderScript, bitmap);

        ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript));
        blur.setInput(blurInput);
        blur.setRadius(radius);
        blur.forEach(blurOutput);

        blurOutput.copyTo(bitmap);
        renderScript.destroy();
        return bitmap;
    }
}

class MatrixGestureDetector {
    private static final String TAG = "MatrixGestureDetector";

    interface OnMatrixChangeListener {
        void onChange(Matrix matrix);
    }

    private int ptpIdx = 0;
    private Matrix mTempMatrix = new Matrix();
    private Matrix mMatrix;
    private OnMatrixChangeListener mListener;
    private float[] mSrc = new float[4];
    private float[] mDst = new float[4];
    private int mCount;

    public MatrixGestureDetector(Matrix matrix, MatrixGestureDetector.OnMatrixChangeListener listener) {
        if (matrix == null) throw new RuntimeException("Matrix cannot be null");
        if (listener == null) throw new RuntimeException("OnMatrixChangeListener cannot be null");
        mMatrix = matrix;
        mListener = listener;
    }

    public void onTouchEvent(MotionEvent event) {
        if (event.getPointerCount() > 2) {
            return;
        }

        int action = event.getActionMasked();
        int index = event.getActionIndex();

        switch (action) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_POINTER_DOWN:
                int idx = index * 2;
                mSrc[idx] = event.getX(index);
                mSrc[idx + 1] = event.getY(index);
                mCount++;
                ptpIdx = 0;
                break;

            case MotionEvent.ACTION_MOVE:
                for (int i = 0; i < mCount; i++) {
                    idx = ptpIdx + i * 2;
                    mDst[idx] = event.getX(i);
                    mDst[idx + 1] = event.getY(i);
                }
                mTempMatrix.setPolyToPoly(mSrc, ptpIdx, mDst, ptpIdx, mCount);
                mMatrix.postConcat(mTempMatrix);
                mListener.onChange(mMatrix);
                System.arraycopy(mDst, 0, mSrc, 0, mDst.length);
                break;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
                if (event.getPointerId(index) == 0) ptpIdx = 2;
                mCount--;
                break;
        }
    }
}

EDIT: when using Bitmaps instead of Paths the code is shorter by couple of lines:

class V extends View implements MatrixGestureDetector.OnMatrixChangeListener {
    Paint monoPaint = new Paint();
    Paint srcInPaint = new Paint();
    Bitmap mask, maskShadow, bitmap, blur;
    Matrix matrix = new Matrix();
    Matrix maskMatrix = new Matrix();
    MatrixGestureDetector detector;
    RectF clip = new RectF();

    public V(Context context) {
        super(context);
        mask = BitmapFactory.decodeResource(context.getResources(), R.drawable.mask).extractAlpha();
        maskShadow = BitmapFactory.decodeResource(context.getResources(), R.drawable.mask_shadow);
        bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.forest);
        blur = blur(context, bitmap, 8);
        ColorMatrix cm = new ColorMatrix();
        cm.setSaturation(0.25f);
        monoPaint.setColorFilter(new ColorMatrixColorFilter(cm));
        srcInPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        detector = new MatrixGestureDetector(maskMatrix, this);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        RectF src = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());
        RectF dst = new RectF(0, 0, w, h);
        matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
        matrix.mapRect(dst, src);

        src.set(0, 0, mask.getWidth(), mask.getHeight());
        maskMatrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
        setupClip();
    }

    private void setupClip() {
        clip.set(0, 0, mask.getWidth(), mask.getHeight());
        maskMatrix.mapRect(clip);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(blur, matrix, monoPaint);
        drawMask(canvas);
    }

    private void drawMask(Canvas canvas) {
        canvas.clipRect(clip);
        canvas.saveLayer(clip, null, 0);
        canvas.drawBitmap(mask, maskMatrix, null);
        canvas.drawBitmap(bitmap, matrix, srcInPaint);
        canvas.drawBitmap(maskShadow, maskMatrix, null);
        canvas.restore();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        detector.onTouchEvent(event);
        return true;
    }

    @Override
    public void onChange(Matrix matrix) {
        setupClip();
        invalidate();
    }

    Bitmap blur(Context ctx, Bitmap src, float radius) {
        Bitmap bitmap = src.copy(src.getConfig(), true);

        RenderScript renderScript = RenderScript.create(ctx);
        Allocation blurInput = Allocation.createFromBitmap(renderScript, src);
        Allocation blurOutput = Allocation.createFromBitmap(renderScript, bitmap);

        ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript));
        blur.setInput(blurInput);
        blur.setRadius(radius);
        blur.forEach(blurOutput);

        blurOutput.copyTo(bitmap);
        renderScript.destroy();
        return bitmap;
    }
}

and the result is like this:

enter image description here

pskink
  • 23,874
  • 6
  • 66
  • 77