Search code examples
androidandroid-canvas

Drawing an outer shadow when drawing an image


I currently create a rounded version of an image in my app by drawing to a canvas. I would like to draw a faint outershadow around the image, but I cant quite get it right. I have 2 questions: 1. How can I draw an outer shadow (I can only seem to draw a shadow with a x or y offset) 2. How can I draw the shadow so that it does not have the artifacts shown in the attached image. Code:

![public Bitmap getRoundedCornerBitmap(Bitmap bitmap, float cornerRadius) {
        Bitmap output = Bitmap.createBitmap(bitmap.getWidth()+6, bitmap.getHeight() +6, Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

        final int color = 0xff424242;
        int shadowRadius = getDipsFromPixel(3);
        final Rect imageRect = new Rect(shadowRadius, shadowRadius, bitmap.getWidth(), bitmap.getHeight());
        final RectF rectF = new RectF(imageRect);

        // This does not achieve the desired effect
        Paint shadowPaint = new Paint();
        shadowPaint.setAntiAlias(true);
        shadowPaint.setColor(Color.BLACK);
        shadowPaint.setShadowLayer((float)shadowRadius, 2.0f, 2.0f,Color.BLACK);
        canvas.drawOval(rectF, shadowPaint);

        canvas.drawARGB(0, 0, 0, 0);
        final Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(color);

        canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, paint);

        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
        canvas.drawBitmap(bitmap, imageRect, imageRect, paint);

        return output;
    }][1]

https://i.sstatic.net/d7DV6.png

This is an example of the effect I am trying to achieve: enter image description here


Solution

  • Here we go

    https://i.sstatic.net/CE42r.png Yup I still dig the Nexus S

    First of all, please stop masking bitmaps that way, you can accomplish this without allocating another Bitmap, checkout this blog post about how to draw rounded (and actually any shape) images.

    Second using that Drawable you probably can figure out how to add your shadow, just make sure it does not get clipped, on 18+ you could use ViewOverlays for that, also keep in mind that there are several unsupported drawing operations for hardware accelerated layers, that includes setShadowLayer and BlurMaskFilter, if performance is not an issue for you, you can disable it as always:

    if (SDK_INT >= HONEYCOMB) {
      view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    }
    

    And use setShadowLayer as you were trying already:

    somePaint.setShadowLayer(shadowSize, deltaX, deltaY, shadowColor);
    

    For a sample please check the link at the end.

    If you still want to be hardware accelerated you will have to fake it at risk of overdrawing, you could use a radial gradient or draw another oval blurring it yourself (as mentioned before can't use BlurMaskFilter) or use a pre-blurred Bitmap (more masking).

    For such a subtle shadow I would rather just go flat if performance is required, the full sauce is in the banana stand.

    Update: Starting L you can use real shadows.