Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Crop/Adjust image in heart shape/frame

I am trying to crop or adjust image as per heart shape. User can adjust image as per heart shape and set image to that heart shape.

Currently, I am using Github Project library to crop the image as a square and to set to heart shape. But all images are getting cut from top side where heart is curved. So, I want to allow user to crop image as per heart border and then set the way user want. But there is no any library which I can customize or use.

Here is current code which I am using from above github project.

  1. To open Camera and Gallery :

    CropImage.activity().setGuidelines(CropImageView.Guidelines.ON) .start(this);

  2. Getting Crop Image and Setting to ImageHeart View.

    CropImage.ActivityResult result = CropImage.getActivityResult(data);
    picUri = result.getUri();
    bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), picUri);
    imvHeartRed.setImageBitmap(bitmap);

Here is image in which I am cropping glass using square cropping but while setting full glass image is not coming. But If user crop it using heart shape then full glass will be shown because User will know what part of image will get cropped.

enter image description here

Any help, reference will be really appreciated.

I don't want to set image in heart directly but user can adjust image in heart by touching/moving

like image 956
Sandip Armal Patil Avatar asked Feb 11 '18 10:02

Sandip Armal Patil


People also ask

Can you crop a picture to a particular shape?

Crop your picture to fit a shape Select a picture. Select the Picture Tools > Format tab, and then select Crop > Crop to Shape. From the shapes gallery, select the shape you want to crop to.


1 Answers

Updated to have transparency set only within the heart shape.

If you simply need to stamp out an image based upon a shape that you create, you could use a library such as mafs-image-shape but you would need to supply a way to manipulate the shape placement before cutting out the image.

I am assuming that you are relying upon the structure of Android-Image-Cropper, so the following changes apply to that library. Code shown is based upon selected code from Android-Image-Cropper which is licensed under Apache License 2.0.

Here is how a sample app looks after making the following changes. The discussion that follows explains how to change the base code to accommodate a heart shape.

enter image description here

The heart shape needs to be defined as an option in addition to a rectangle and an oval. To make a heart a shape an option, change the CropShape enum in CropImageView to add HEART as a crop shape and add heart as a cropShape option in attrs:

CropImageView.java

public enum CropShape {
    RECTANGLE,
    OVAL,
    HEART
}

attrs.xml

<attr name="cropShape">
    <enum name="rectangle" value="0"/>
    <enum name="oval" value="1"/>
    <enum name="heart" value="2"/>
</attr>

I have used two images for the heart shape. The first image is the framing image that is used to position the frame on the underlying image. The cropping image is the same as the framing image, but is solid (alpha == 1) wherever the image should be retained during the crop operation. Transparent areas should have alpha set to zero. Here are the images I used, but you will want to use your own.

enter image description hereenter image description here

CropOverlayView is a custom view that presents the cropping window and shaded background. Modify the drawBackground and drawBorders methods of this class as follows to accommodate a heart shape.

CropOverlayView.java

/**
 * Draw shadow background over the image not including the crop area.
 */
// Modifications made to accommodate heart cutouts
private void drawBackground(Canvas canvas) {

    RectF rect = mCropWindowHandler.getRect();

    float left = Math.max(BitmapUtils.getRectLeft(mBoundsPoints), 0);
    float top = Math.max(BitmapUtils.getRectTop(mBoundsPoints), 0);
    float right = Math.min(BitmapUtils.getRectRight(mBoundsPoints), getWidth());
    float bottom = Math.min(BitmapUtils.getRectBottom(mBoundsPoints), getHeight());

    if (mCropShape == CropImageView.CropShape.RECTANGLE) {
        if (!isNonStraightAngleRotated() || Build.VERSION.SDK_INT <= 17) {
            canvas.drawRect(left, top, right, rect.top, mBackgroundPaint);
            canvas.drawRect(left, rect.bottom, right, bottom, mBackgroundPaint);
            canvas.drawRect(left, rect.top, rect.left, rect.bottom, mBackgroundPaint);
            canvas.drawRect(rect.right, rect.top, right, rect.bottom, mBackgroundPaint);
        } else {
            mPath.reset();
            mPath.moveTo(mBoundsPoints[0], mBoundsPoints[1]);
            mPath.lineTo(mBoundsPoints[2], mBoundsPoints[3]);
            mPath.lineTo(mBoundsPoints[4], mBoundsPoints[5]);
            mPath.lineTo(mBoundsPoints[6], mBoundsPoints[7]);
            mPath.close();

            canvas.save();
            canvas.clipPath(mPath, Region.Op.INTERSECT);
            canvas.clipRect(rect, Region.Op.XOR);
            canvas.drawRect(left, top, right, bottom, mBackgroundPaint);
            canvas.restore();
        }
    } else if (mCropShape == CropImageView.CropShape.HEART) {
        Bitmap screen = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(),
                                            Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(screen);
        // Draw the shadow background.
        c.drawRect(0, 0, right, bottom, mBackgroundPaint);

        // Punch out the heart shape.
        Bitmap heart = BitmapFactory.decodeResource(getResources(), R.drawable.heart_image_solid);
        heart = Bitmap.createScaledBitmap(heart, (int) rect.width(), (int) rect.height(), true);
        Paint paint = new Paint();
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
        c.drawBitmap(heart, rect.left, rect.top, paint);

        // Now overdraw with the heart frame.
        heart = BitmapFactory.decodeResource(getResources(), R.drawable.heart_image_frame);
        heart = Bitmap.createScaledBitmap(heart, (int) rect.width(), (int) rect.height(), true);
        c.drawBitmap(heart, rect.left, rect.top, null);
        canvas.drawBitmap(screen, 0, 0, null);
    } else {
        mPath.reset();
        if (Build.VERSION.SDK_INT <= 17 && mCropShape == CropImageView.CropShape.OVAL) {
            mDrawRect.set(rect.left + 2, rect.top + 2, rect.right - 2, rect.bottom - 2);
        } else {
            mDrawRect.set(rect.left, rect.top, rect.right, rect.bottom);
        }
        mPath.addOval(mDrawRect, Path.Direction.CW);
        canvas.save();
        canvas.clipPath(mPath, Region.Op.XOR);
        canvas.drawRect(left, top, right, bottom, mBackgroundPaint);
        canvas.restore();
    }
}

/**
 * Draw borders of the crop area.
 */
private void drawBorders(Canvas canvas) {
    if (mBorderPaint != null) {
        float w = mBorderPaint.getStrokeWidth();
        RectF rect = mCropWindowHandler.getRect();
        rect.inset(w / 2, w / 2);

        if (mCropShape == CropImageView.CropShape.RECTANGLE) {
            // Draw rectangle crop window border.
            canvas.drawRect(rect, mBorderPaint);
        } else if (mCropShape == CropImageView.CropShape.OVAL) {
            // Draw circular crop window border
            canvas.drawOval(rect, mBorderPaint);
        }
    }
}

Add the following method to CropImage.java. This method will crop out the heart image and frame it.

CropImage

/**
 * Create a new bitmap that has all pixels beyond the heart shape transparent. Old bitmap is
 * recycled.
 */
public static Bitmap toHeartBitmap(@NonNull Bitmap bitmap, @NonNull Context context) {
    int width = bitmap.getWidth();
    int height = bitmap.getHeight();
    Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

    Canvas canvas = new Canvas(output);

    int color = 0xff424242;
    Paint paint = new Paint();

    paint.setAntiAlias(true);
    canvas.drawARGB(0, 0, 0, 0);
    paint.setColor(color);

    RectF rect = new RectF(0, 0, width, height);

    // Get solid heart to mask out the portion of the image we want to keep.
    Bitmap heart = BitmapFactory.decodeResource(context.getResources(), R.drawable.heart_image_solid);
    heart = Bitmap.createScaledBitmap(heart, width, height, true);
    canvas.drawBitmap(heart, 0, 0, null);

    // SRC_IN means to keep the portion of the bitmap that overlaps the solid heart. All pixels
    // from the solid heart and outside the solid heart area of the bitmap are tossed.
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
    canvas.drawBitmap(bitmap, 0, 0, paint);

    // We now have an unframed heart shape. Get the heart frame and apply it.
    heart = BitmapFactory.decodeResource(context.getResources(), R.drawable.heart_image_frame);
    heart = Bitmap.createScaledBitmap(heart, width, height, true);
    canvas.drawBitmap(heart, 0, 0, null);
    bitmap.recycle();
    return output;
}

Here is the main activity of the demo app showing how to invoke the cropping library using the heart shape:

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.retry).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                cropImage();
            }
        });
        cropImage();
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        CropImage.ActivityResult result = CropImage.getActivityResult(data);
        if (result == null) {
            return;
        }
        Uri picUri = result.getUri();
        if (picUri == null) {
            return;
        }
        Bitmap bitmap;
        bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), picUri);
        }
        if (bitmap == null) {
            return;
        }
        ImageView imageView = findViewById(R.id.imageView);
        imageView.setImageBitmap(CropImage.toHeartBitmap(bitmap, this));
    }

    private void cropImage() {
        CropImage.activity().setGuidelines(CropImageView.Guidelines.ON)
            .setCropShape(CropImageView.CropShape.HEART)
            .start(MainActivity.this);
    }
}
like image 72
Cheticamp Avatar answered Sep 24 '22 12:09

Cheticamp