how to add alpha channel to bitmap - android

My code is below. It is exactly the same code as found in the solution for this question: Make certain area of bitmap transparent on touch
And as many others I am having the same problem with this code: the circle comes out black.
I am using a PNG file as my overlay, and this file does not have any transparent areas. But as soon as I add an arbitrary transparent area to the PNG in the Photoshop, the code starts working and the circle is displayed as transparent.
Apparently, there is something with the image and how its transparency is set, but I do not know what. I need to use a PNG without any transparent areas as my overlay.
any advice?
EDIT: good code must not depend on whether the overlay image has transparency or not, I am looking for a way to handle any kind of image as my overlay, be it JPG, PNG, or something else.
EDIT 2: if I use Config.ARGB_4444 when copying my bitmap, alpha channel gets created but this format reduces the image quality. There is API Bitmap.setHasAlpha() for API level 11 and above but I am using level 10 so far.
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new TouchView(this));
}
class TouchView extends View {
Bitmap bgr;
Bitmap overlayDefault;
Bitmap overlay;
Paint pTouch;
int X = 100;
int Y = 100;
Canvas c2;
public TouchView(Context context) {
super(context);
bgr = BitmapFactory.decodeResource(
getResources(),
R.drawable.background);
overlay = BitmapFactory.decodeResource(
getResources(),
R.drawable.foreground)
.copy(Config.ARGB_8888, true);
c2 = new Canvas(overlay);
pTouch = new Paint(Paint.ANTI_ALIAS_FLAG);
pTouch.setXfermode(
new PorterDuffXfermode(Mode.SRC_OUT));
pTouch.setColor(Color.TRANSPARENT);
}
#Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
//draw background
canvas.drawBitmap(bgr, 0, 0, null);
//copy the default overlay
// into temporary overlay and punch a hole in it
//c2.drawBitmap(overlayDefault, 0, 0, null);
c2.drawCircle(X, Y, 80, pTouch);
//draw the overlay over the background
canvas.drawBitmap(overlay, 0, 0, null);
}
}
}

From http://blog.uncommons.org/2011/01/12/adjusting-the-opacity-of-an-android-bitmap/
**
* #param bitmap The source bitmap.
* #param opacity a value between 0 (completely transparent) and 255 (completely opaque).
* #return The opacity-adjusted bitmap. If the source bitmap is mutable it will be
* adjusted and returned, otherwise a new bitmap is created.
*/
private Bitmap adjustOpacity(Bitmap bitmap, int opacity)
{
Bitmap mutableBitmap = bitmap.isMutable()
? bitmap
: bitmap.copy(Bitmap.Config.ARGB_8888, true);
Canvas canvas = new Canvas(mutableBitmap);
int colour = (opacity & 0xFF) << 24;
canvas.drawColor(colour, PorterDuff.Mode.DST_IN);
return mutableBitmap;
}
Note that the Bitmap.Config documentation for ARGB_4444 says:
This field was deprecated in API level 13. Because of the poor quality
of this configuration, it is advised to use ARGB_8888 instead.

Related

Drawing bitmap to the size of the canvas

I have bitmap that need to draw to canvas. The image is of a fixed size, but the canvas will change according to the user's screen size and density (bitmap coule be larger/smaller than the canvas).
I need to draw the bitmap to canvas scaling all the way into the canvas size (without distorting the image), I have done the code as below but the bitmap still filling only a portion of the screen.
Rect dest = new Rect(0, 0, drawCanvas.getWidth(), drawCanvas.getHeight());
Paint paint = new Paint();
paint.setFilterBitmap(true);
drawCanvas.drawBitmap(canvasBitmap, null, dest, paint);
May I know if anybody can shed light on a good solution? Thanks.
This example is in javascript but it should still help you out scale an image
jsFiddle : https://jsfiddle.net/CanvasCode/7oghuwe2/3/
javascript
var canvas1 = document.getElementById('canvas1');
var context1 = canvas1.getContext('2d')
var canvas2 = document.getElementById('canvas2');
var context2 = canvas2.getContext('2d');
var image1 = new Image();
image1.src = "http://media.giphy.com/media/iNk83OBPzlA8o/giphy.gif";
image1.onload = function () {
context1.fillStyle = "#F00";
context1.fillRect(0, 0, canvas1.width, canvas1.height);
context2.fillStyle = "#00F";
context2.fillRect(0, 0, canvas2.width, canvas2.height);
ratio(context1, canvas1, image1);
ratio(context2, canvas2, image1);
}
function ratio(context1, canvas1, image1) {
var imageRatio = image1.width / image1.height;
var newHeight = canvas1.width / imageRatio;
var newWidth = canvas1.height * imageRatio;
var heightDiff = newHeight - canvas1.height;
var widthDiff = newWidth - canvas1.width;
if (widthDiff >= heightDiff) {
context1.drawImage(image1, 0, 0, canvas1.width, canvas1.width / imageRatio);
} else {
context1.drawImage(image1, 0, 0, canvas1.height * imageRatio, canvas1.height);
}
}
Basically you need to calculate what the width would be if you scaled the image by the canvas height and what the height would be if you scale the image by the canvas width, and which ever is smaller, then you scale by that dimension.
The reason why it might not work for you might be because the function drawBitmap() ignores the density of the bitmap. The following is from the documentation.
public void drawBitmap (Bitmap bitmap, Rect src, Rect dst, Paint
paint)
This function ignores the density associated with the bitmap. This is
because the source and destination rectangle coordinate spaces are in
their respective densities, so must already have the appropriate
scaling factor applied.
What you could do is use public void drawBitmap (Bitmap bitmap, Matrix matrix, Paint paint) instead. First you need to map the source Matrix with the desination Matrix. You do this via Matrix.setRectToRect() or Matrix.setPolyToPoly(). This will give you an accurate mapping. Just make sure you map them correctly, otherwise things will be distorted.
For more info refer here: What code should I use with 'drawMatrix.setPolyToPoly' to manipulate and draw just a rectangular part of a bitmap, rather than the entire bitmap?

Android clip Picture to Bitmap. Is Possible?

I have a Picture object, loaded from an SVG file, and I have set hardwareAccelerated=false to make it works on all devices.
Since there is a bug on android 4.0.4, I have to convert the Picture to Bitmap and I do that, in this way:
...
...
public void draw(Canvas canvas) {
...
...
//myPicture size is 9000x5000 but I want to display only this portion
clipRect.set(50, 50, 370, 530);
Bitmap bmp = getBitmapFromPicture(myPicture, clipRect);
canvas.drawBitmap(bmp, 0, 0, null);
bmp.recycle();
...
...
}
public static Bitmap getBitmapFromPicture(Picture picture, RectF clipRect) {
Bitmap bitmap = Bitmap.createBitmap(Math.round(clipRect.width()), Math.round(clipRect.height()), Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
canvas.drawPicture(picture);
}
Now I want to clip the Picture because I want to display only the visible screen part of it.
But the canvas.drawPicture does not accept srcRect parameter.
How is it possible to achieve this?
EDIT:
By translate the canvas: canvas.translate(-50, -50) it seems that translate the bitmap, too.
You need to set a transform on the Canvas.
To move the portion of the picture at 50,50 down so it is on the bitmap (ie. at 0,0), just do:
canvas.translate(-50, -50);
So your method becomes:
public static Bitmap getBitmapFromPicture(Picture picture, RectF clipRect)
{
Bitmap bitmap = Bitmap.createBitmap(Math.round(clipRect.width()),
Math.round(clipRect.height()),
Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
canvas.translate(-clipRect.left, -clipRect.top);
canvas.drawPicture(picture);
}

How to make bitmap transparent?

/**
* #param bitmap
* The source bitmap.
* #param opacity
* a value between 0 (completely transparent) and 255 (completely
* opaque).
* #return The opacity-adjusted bitmap. If the source bitmap is mutable it
* will be adjusted and returned, otherwise a new bitmap is created.
* Source : http://stackoverflow.com/questions/7392062/android-
* semitransparent-bitmap-background-is-black/14858913#14858913
*/
private Bitmap adjustOpacity(Bitmap bitmap, int opacity) {
Bitmap mutableBitmap = bitmap.isMutable() ? bitmap : bitmap.copy(Bitmap.Config.ARGB_8888, true);
Canvas canvas = new Canvas(mutableBitmap);
int colour = (opacity & 0xFF) << 24;
canvas.drawColor(colour, PorterDuff.Mode.DST_IN);
return mutableBitmap;
}
Using adjustOpacity, I make ImageView's Bitmap be semi-transparent.
Bitmap newBitmap = adjustOpacity(orignalBitmap, 10);
view.setImageBitmap(newBitmap);
view.setBackgroundColor(Color.WHITE);
However, Imageview show more darkent before not white. How do I make a semi-transparent (white background) Imageview with Bitmap?
// Convert transparentColor to be transparent in a Bitmap.
public static Bitmap makeTransparent(Bitmap bit, Color transparentColor) {
int width = bit.getWidth();
int height = bit.getHeight();
Bitmap myBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
int [] allpixels = new int [ myBitmap.getHeight()*myBitmap.getWidth()];
bit.getPixels(allpixels, 0, myBitmap.getWidth(), 0, 0, myBitmap.getWidth(),myBitmap.getHeight());
myBitmap.setPixels(allpixels, 0, width, 0, 0, width, height);
for(int i =0; i<myBitmap.getHeight()*myBitmap.getWidth();i++){
if( allpixels[i] == transparentColor)
allpixels[i] = Color.alpha(Color.TRANSPARENT);
}
myBitmap.setPixels(allpixels, 0, myBitmap.getWidth(), 0, 0, myBitmap.getWidth(), myBitmap.getHeight());
return myBitmap;
}
The above code will take a Bitmap, and return a Bitmap where every pixel which the color is transparentColor is transparent. This works in API as low as level 8, and I have not tested it in any lower.
I typically use something like Color.RED to make my transparent pixels, because I don't use a lot of RED in my assets, but if I do it's a custom red color.
Making an existing bitmap transparent is too simple.
If you want to make your bitmap transparent before further drawing follow this.
if your bitmap is mBitmap then Just use:-
mBitmap.eraseColor(Color.TRANSPARENT)
Thats it. Good luck !!
Try this. Also note that setAlpha is in 0-255 range
//bmp is your Bitmap object
BitmapDrawable bd = new BitmapDrawable(bmp);
bd.setAlpha(50);
ImageView v = (ImageView) findViewById(R.id.image);
v.setImageDrawable(bd);
Add method
Bitmap bmp; DrawView DrawView;
private static Bitmap makeTransparentBitmap(Bitmap bmp, int alpha) {
Bitmap transBmp = Bitmap.createBitmap(bmp.getWidth(),
bmp.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(transBmp);
final Paint paint = new Paint();
paint.setAlpha(alpha);
canvas.drawBitmap(bmp, 0, 0, paint);
return transBmp;
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.rgb(43,44,45));
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.mouse);
canvas.drawBitmap(bmp, pos_x, pos_y, null);
}
You event:
bmp = makeTransparentBitmap( bmp, 122 );
DrawView.invalidate();
DrawView is
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DrawView = new DrawView(this);
setContentView( DrawView );
}
I found this answer here
try this,
newBitmap.setImageResource(android.R.color.transparent);
#akaya's answer is the right way except that the constructor is deprecated. The new way since API 4 would be to use:
BitmapDrawable drawable = new BitmapDrawable(getResources(), bitmap);
drawable.setAlpha(100);

Android how to apply mask on ImageView?

So I tried the code from here: Creating an ImageView with a mask. I'm using the following images as original and mask:
However, the result I get is this:
Note that the window background is not black, but holo light (which on the galaxy nexus looks like a very pale gray, not completely white). The second image is the result I get when an item is selected on a list view.
If instead I create a new Bitmap using the same algorithm and then pass it to the image view instead of overriding onDraw(), it draws correctly:
Canvas canvas = new Canvas();
Bitmap mainImage = //get original image
Bitmap maskImage = //get mask image
Bitmap result = Bitmap.createBitmap(mainImage.getWidth(), mainImage.getHeight(), Bitmap.Config.ARGB_8888);
canvas.setBitmap(result);
Paint paint = new Paint();
paint.setFilterBitmap(false);
canvas.drawBitmap(mainImage, 0, 0, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
canvas.drawBitmap(maskImage, 0, 0, paint);
paint.setXfermode(null);
imageView.setImageBitmap(result);
I get the expected result:
Note the fade is correctly applied. This is more evident when a selection is made.
So what's going on on ImageView's onDraw method to create this black backdrop instead of letting the window background show through? What's interesting is that if the original image itself has some transparency, that transparency is respected, for example:
I can't figure it out by myself. I'd rather be able to do it on onDraw instead of pre-creating the bitmap because it only works for bitmaps as source and mask. I want to be able to do it with other drawables like gradients and solid colours but on those cases the width and height are not set.
I have found the perfect combination for creating masking without black border after researching through all the stackoverflow posts. It suits my purpose quite well.
Currently I'm creating a draggable view using one normal image and a masking image (a png with transparency), so I'll need to override the onDraw function.
private Bitmap mImage = ...;
private Bitmap mMask = ...; // png mask with transparency
private int mPosX = 0;
private int mPosY = 0;
private final Paint maskPaint;
private final Paint imagePaint;
public CustomView (final Context context) {
maskPaint = new Paint();
maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
imagePaint = new Paint();
imagePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OVER));
}
/* TODO
if you have more constructors, make sure you initialize maskPaint and imagePaint
Declaring these as final means that all your constructors have to initialize them.
Failure to do so = your code won't compile.
*/
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.drawBitmap(mMask, 0, 0, maskPaint);
canvas.drawBitmap(mImage, mPosX, mPosY, imagePaint);
canvas.restore();
}
Answering my own question. The Xfermode was working as intended. The paint was making the resulting are of the canvas transparent (which was the canvas used by the window activity). Since the canvas itself was being set transparent, the window was showing what was behind it: the black background.
To do it properly, indeed a new Bitmap has to be created to hold the result of the alpha mask. I updated the code to take into account drawables of all types.
In this Code Apply:
mask_over = BitmapFactory.decodeResource(
getResources(), mask_over1[0]);
icon = Bitmap.createScaledBitmap(icon, screenwidth, screenwidth, false);
mask_over = Bitmap.createScaledBitmap(mask_over, screenwidth, screenwidth, false);
back_img=createBitmap_ScriptIntrinsicBlur(Bitmap.createScaledBitmap(cropview.croppedImage, screenwidth, screenwidth, false),25.0f);
LinearLayout.LayoutParams layoutParams111 = new LinearLayout.LayoutParams(screenwidth, screenwidth);

Android: Drawing tiled bitmaps with bottom or some other alignments similar to css background-position

I want to set a background of a View with a tiled bitmap, but the tiling needs to be anchored to the bottom-left, instead of the top-left corner (the default). For example, if the tiles are the smiley faces below, I want it to be tiled like:
Using xml drawables I could achieve either tiling (using tileMode="repeat") or bottom positioning (using gravity="bottom"), but combining both is not possible, even the documentation says so:
android:tileMode
Keyword. Defines the tile mode. When the tile mode is
enabled, the bitmap is repeated. Gravity is ignored when the tile mode
is enabled.
Although it's not internally supported, is there any way to achieve this, perhaps using custom views?
Another way would be to extend BitmapDrawable and override the paint() method:
In this method we avoid creating a new bitmap having the size of the view.
class MyBitmapDrawable extends BitmapDrawable {
private Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
private boolean mRebuildShader = true;
private Matrix mMatrix = new Matrix();
#Override
public void draw(Canvas canvas) {
Bitmap bitmap = getBitmap();
if (bitmap == null) {
return;
}
if (mRebuildShader) {
mPaint.setShader(new BitmapShader(bitmap, TileMode.REPEAT, TileMode.REPEAT));
mRebuildShader = false;
}
// Translate down by the remainder
mMatrix.setTranslate(0, getBounds().bottom % getIntrinsicHeight());
canvas.save();
canvas.setMatrix(mMatrix);
canvas.drawRect(getBounds(), mPaint);
canvas.restore();
}
}
It can be set to the view like this:
view.setBackgroundDrawable(new MyBitmapDrawable(getResources().getDrawable(R.drawable.smiley).getBitmap()));
Just a thought, and it's pretty roundabout, but could you flip your image vertically, and then apply a transform to your background to flip that vertically as well?
Using a custom view might involve handling all the drawing yourself, not just the background image.
Instead, I propose to set the view's background programmatically as shown:
// This drawable refers to an image directly and NOT an XML
BitmapDrawable smiley = (BitmapDrawable) getResources().getDrawable(R.drawable.smiley);
// Create a new bitmap with the size of the view
Bitmap bgBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bgBitmap);
// Translate down by the remainder
Matrix matrix = new Matrix();
matrix.setTranslate(0, view.getHeight() % smiley.getIntrinsicHeight());
canvas.setMatrix(matrix);
// Tile the smileys
Paint paint = new Paint();
paint.setShader(new BitmapShader(smiley.getBitmap(), TileMode.REPEAT, TileMode.REPEAT));
canvas.drawPaint(paint);
view.setBackgroundDrawable(new BitmapDrawable(bgBitmap));
Points to consider:
I'm not sure if view.getWidth() & view.getHeight() are the correct
methods to get the dimensions.
What if smiley size is bigger than the view?

Categories

Resources