Bitmap Shader with alpha channel. (Alpha is drawing black..?) - android

I get my bitmap, use it as a shader tile mode.
The PNG is mostly alpha except for the shape outline to draw.
Except it draws the outline, but is surrounded by black, not seethrough (alpha).
pnt.reset();
if(backgroundColor == 1)
{
pnt.setColor(myColor);
pnt.setStyle(Paint.Style.FILL);
}
m_canvas.drawPath(path, pnt);
//fillBMP = getBitmapFromAsset(m_context, "brush.png");
fillBMP = BitmapFactory.decodeFile(mySDPath + "brush.png");
fillBMPshader = new BitmapShader(fillBMP, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
pnt.setShader(fillBMPshader);
m_canvas.drawPath(path, pnt);
Example below of the brush on left. But result it draws on right.

You should set XferMode on your Paint object. More specifically you got to use PorterDuffXferMode MULTIPLY.
Here is a similar question : Android color overlay - PorterDuff modes

Related

Android Canvas PorterDuff not masking correctly

What I'm trying to do
I'm trying to implement a view that can be masked. Let's say the user has an image like this:
which gets masked by a custom shape like this:
The resulting image should only let the shoe shine through and make the background transparent.
Implementation
I have tried to implement it this way:
Make all black pixels in the mask transparent
Override the View's draw function to draw the mask over the original image using an xfermode (PorterDuffXfermode) of DST_IN. (See Android docs for PorterDuff.Mode)
This works perfectly fine and gives me this image (green pixels mean transparent):
While this works perfectly fine, I couldn't implement a custom "drawing" functionality to let the user draw or erase the mask. I only succeeded in doing one or the other, but not both at the same time. This is what I achieved right now:
note that erasing the mask works as expected, but trying to extend the mask doesn't work and paints white pixels (instead of letting the original image (shoe) shine through).
This is the code I'm using right now:
override fun draw(baseCanvas: Canvas) {
super.draw(baseCanvas)
val image = imageBitmap
val mask = maskBitmap
val drawingBitmap = drawingBitmap
if (image != null && mask != null && drawingBitmap != null) {
run {
val canvas = Canvas(drawingBitmap)
// 1. Fill with white
canvas.drawColor(Color.WHITE)
// 2. Draw mask and only let non-transparent pixels through
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
canvas.drawBitmap(mask, 0f, 0f, paint)
// 3. Draw all lines point to point with white color and custom xfermode
paint.xfermode = null
paint.style = Paint.Style.STROKE
paint.color = Color.WHITE
paint.strokeWidth = penSize
paint.isDither = true
paint.strokeJoin = Paint.Join.ROUND
paint.strokeCap = Paint.Cap.ROUND
paint.pathEffect = CornerPathEffect(10f)
paint.isAntiAlias = true
lines.forEach { line ->
paint.xfermode = when (line.drawMode) {
DrawMode.ERASE -> PorterDuffXfermode(PorterDuff.Mode.DST_OUT)
DrawMode.DRAW -> PorterDuffXfermode(PorterDuff.Mode.SRC)
}
val path = Path().also { path ->
val points = line.points
val range = points.size - 1
for (i in 1..range) {
path.moveTo(points[i - 1].x, points[i - 1].y)
path.lineTo(points[i].x, points[i].y)
}
}
canvas.drawPath(path, paint)
}
}
run {
val canvas = Canvas(image)
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_ATOP)
canvas.drawBitmap(drawingBitmap, 0f, 0f, paint)
}
}
}
Important variables in scope:
imageBitmap: The shoe bitmap
maskBitmap: The bitmap that contains white colors for the original shape to shine through, transparent for everything that should be transparent
drawingBitmap: An empty bitmap with the same size as imageBitmap, I use this for drawing only and then draw that result onto the imageBitmap (using a Canvas, see second run block)
lines: The lines I want to draw. A line consists of a drawMode (draw or erase) and a list of all points I have tracked.
Weird Observation
The thing that's confusing me right now is that when I fill the canvas black before adding the image:
run {
val canvas = Canvas(image)
canvas.drawColor(Color.BLACK) // <-- ADD THIS HERE
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_ATOP)
canvas.drawBitmap(drawingBitmap, 0f, 0f, paint)
}
it looks like it's working fine:
Same result if I fill the canvas RED, it lets the Red color shine through instead of Black. Why does it work with the color, but not the original bitmap?
It even looks correct if inspected in the debugger:
Question
Does anyone here know why this does not work as I expect it to work? I tried to play around with all kinds of different PorterDuff modes, but couldn't manage to get it working smoothly.
Any help appreciated!
Okay I got it working. I'm not sure if there's a more efficient way to achieve this, but this is what I did:
Create a fourth Bitmap called imageDrawingBitmap.
Set imageDrawingBitmap to the actual ImageView
As in the code snippet above, first draw the mask, then paths ontop of that
Then create a Canvas for the imageDrawingBitmap
Draw original image in that Canvas
Draw mask (maskDrawingBitmap from step 3.) in that Canvas with PorterDuff DST_ATOP.
That seems to be working fine.

unable to transparent pixels of a bitmap image

I am working with bitmap images whose transparent parts are colored in magenta (in some languages it is possible to set a color as transparent). I try to transparent pixels which are in magenta in the original bitmap image.
I load the bitmap from SD-card:
Bitmap bitmap = BitmapFactory.decodeFile(myImagePath);
copy it to another bitmap to make it mutable:
Bitmap bitmap2 = bitmap.copy(Bitmap.Config.ARGB_8888,true);
Then scan it pixel by pixel to find pixels in magenta and try to change their transparency.
for(int x=0;x<bitmap2.getWidth();x++){
for(int y=0;y<bitmap2.getHeight();y++){
if(bitmap2.getPixel(x, y)==Color.rgb(0xff, 0x00, 0xff))
{
int alpha = 0x00;
bitmap2.setPixel(x, y , Color.argb(alpha,0xff,0xff,0xff)); // changing the transparency of pixel(x,y)
}
}
}
But those pixels which I expect to become transparent are converted to black. By changing the alpha, I found that the final color varies from the mentioned color in argb() (without mentioning the alpha) to black. For instance, Color.argb(0xff,0xff,0xff,0xff) gets white, Color.argb(0x80,0xff,0xff,0xff) gets gray and Color.argb(0x00,0xff,0xff,0xff) gets black.
I don't undrestand what's wrong.
Could it be possible that there is no alpha channel and I should first set/define it? if yes, how?
EDIT1:
According to the comment of Der Gol...lum I have modified my code:
Paint mPaint = new Paint();
mPaint.setAlpha(0);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
mPaint.setAntiAlias(true);
Bitmap bitmap = BitmapFactory.decodeFile(myBackImagePath).copy(Bitmap.Config.ARGB_8888 , true);
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(bitmap, 0, 0, mPaint);
if(bitmap.getPixel(0, 0)==Color.rgb(0xff, 0x00, 0xff))
{
for(int x=0;x<bitmap.getWidth();x++){
for(int y=0;y<bitmap.getHeight();y++){
if(bitmap.getPixel(x, y)==Color.rgb(0xff, 0x00, 0xff))
{
bitmap.setPixel(x, y,Color.TRANSPARENT);
}
}
}
But the result is more or less the same. Using different PorterDuffModes causes either transparency of the entire bitmap or make the targeted pixels black:
Would anybody have any idea?
I could finally find the problem.
My png images had no alpha channel or maybe their alpha channel were not activated.
what I did to solve this problem is to add:
bitmap.setHasAlpha(true);
and it works how I expected.

Android SweepGradient

I have a SweepGradient defined as
circle_paint.setShader(new SweepGradient(getWidth()/2, getHeight()/2, new int[] { circle_start_color, circle_end_color}, new float[] { 0f, 1f}))
applied to a arch defined as
canvas.drawArc(circle_bounds, circle_start_perc*360f, circle_end_perc*360f, true, circle_paint);
This workes well, but I need the arch to start drawing from the top of the screen, i.e.
canvas.drawArc(circle_bounds, ((circle_start_perc*360f)-90f)%360, circle_end_perc*360f, true, circle_paint);
The problem is that the SweepGradient seems to still start at 0 degrees, and i need it to start at 270 degrees (similar to the translation done on the drawing of the arc). In other words if I have a gradient from white to blue, I need the top of the arc painted white en the last part of the arc painted blue. How can I do this?
Rotating the origin of the SweepGradient using a Matrix.preRotate:
final int[] colors = {circle_start_color, circle_end_color};
final float[] positions = {0f, 1f};
Gradient gradient = new SweepGradient(circle_bounds.centerX(), circle_bounds.centerY(), colors, positions);
float rotate = 270f;
Matrix gradientMatrix = new Matrix();
gradientMatrix.preRotate(rotate, circle_bounds.centerX(), circle_bounds.centerY());
gradient.setLocalMatrix(gradientMatrix);
mPaint.setShader(gradient);
You might try using getLocalMatrix() and setLocalMatrix() on the SweepGradient to apply a rotation to the shader. You can get the current Matrix, post the appropriate rotation with postRotate() and then set it back to the shader element.
Another option would be to rotate the Canvas instead. You could pre-rotate the Canvas, draw the content, and then restore it; or draw the content first and then rotate the canvas after the fact.

Android Canvas.drawTextOnPath does not look correct when paint is set to Stroke

I have some code setup in an extended view which does some drawings which are easily scaled (vector-like). (My scale is setup as 0-1.0)
I noticed that when I set my paint fill to FILL, the text drawn on a path looks correct, but when I set the fill to stroke (I just want the outline of the text) the image looks like it is on some LSD trip. Here is my sample code :
Paint yellowPaint = Paints.getFillTextPaint(0.01f, 0xFFffea3e, 0.065f);
canvas.drawTextOnPath(mContext.getString(R.string.building_a_partnership),
Paths.getRoundedTextPath(mOuterCircleRectF, 280f, 350f),
0, -0.025f, yellowPaint);
public static Paint getFillTextPaint(float f, int color, float textSize) {
Paint textPaint = new Paint();
textPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
textPaint.setStyle(Paint.Style.FILL);
textPaint.setColor(color);
textPaint.setTextSize(textSize);
textPaint.setStrokeWidth(f);
textPaint.setShadowLayer(0.002f, 0.0f, 0.0f, 0xFF000000);
textPaint.setTypeface(Typeface.SANS_SERIF);
return textPaint;
}
If I change the Paint.Style from FILL to STROKE I get the images below. I have used the canvas.drawText() and it works fine showing the stroked letters. It is only when it is applied to a Path, when it seems to get all weird.
Apparently this is due to the fact that my scale factor is 0-1.. There appears to be a bug w/ how rendering a stroke with size < 1.0 is treated. Suggested solution is to use a scale of 0-100..

Question about changing alpha value of a specified bitmap region in Android

I'm having trouble setting an alpha value of a region path drawn on a bitmap, using SurfaceView.
I'm creating my bitmap and erase with a black color (and 255 as alpha)
bitmap = Bitmap.createBitmap(480, 724, Bitmap.Config.ARGB_8888);
bitmap.eraseColor(0xFF000000);
Canvas canvas = new Canvas(bitmap);
Next, in my onDraw method I draw a path on the bitmap with his own alpha, white color as stroke, black as fill color and 1 as alpha value :
Paint border = new Paint();
border.setStyle(Paint.Style.STROKE);
border.setStrokeWidth(1);
border.setStrokeCap(Paint.Cap.BUTT);
border.setStrokeJoin(Paint.Join.MITER);
border.setColor(Color.WHITE);
Paint inside = new Paint();
inside.setAntiAlias(false);
inside.setStyle(Paint.Style.FILL);
int alpha = 1<<24;
inside.setColor(alpha);
canvas.drawBitmap(bitmap,0,0, null);
canvas.drawPath(path, border);
canvas.drawPath(path, inside);
Finally, in my onTouchEvent method, i'm doing this :
int index = bitmap.getPixel((int)event.getX(), (int)event.getY());
int alpha = index >>> 24;
Log.v("TAG", " Region path alpha :"+ String.valueOf(alpha));
So, when I click inside the path region, I'm expecting to have 1 (0x01) as alpha value,
but instead I'm still have 255 (0xFF). What's wrong with that ? Thanks in advance.
I am trying this in usual Java, and getting no errors ! probably you should try debugging the value of index. Output the value of index and check, or just try
Color.alpha(index)
http://developer.android.com/reference/android/graphics/Color.html#alpha(int)

Categories

Resources