Related
`
There is an requirement in my project to set any bitmap into mug and also edit bitmap and wright some text on image then past this bitmap to mug just like attached image .
I need a solution in Android or any supported library, Before I have tried to do this with Android Canvas and its method to draw arc But not reached my requirement.
/**
* Draw one image over other using the canvas paint and path drawing.
*/
public Bitmap createMaskedImageInImageCenterRightMug(Drawable back,
Bitmap bitmapToDrawInTheCenter) {
Bitmap backgroundBitmap = ((BitmapDrawable) back).getBitmap();
int hieghtBack = backgroundBitmap.getHeight();
int widthBack = backgroundBitmap.getWidth();
int hieghtFront = bitmapToDrawInTheCenter.getHeight();
int widthFront = bitmapToDrawInTheCenter.getWidth();
int widthToDrawOnMug = widthBack / 2;
backgroundBitmap = Bitmap.createScaledBitmap(backgroundBitmap, (int) canvas_width, hieghtBack, true);
// Create mask
Bitmap backgroundBitmapMask = Bitmap.createBitmap(backgroundBitmap, 7, 0, (int) (canvas_width / 2), hieghtBack);
if (widthToDrawOnMug <= widthFront) {
bitmapToDrawInTheCenter = Bitmap.createBitmap(
bitmapToDrawInTheCenter, (widthFront * 40) / 100, 0,
(widthFront * 60) / 100, hieghtFront);
}
Bitmap backgroundBitmapScaledMask = Bitmap.createScaledBitmap(
backgroundBitmapMask, widthToDrawOnMug, hieghtBack - 50, true);
bitmapToDrawInTheCenter = Bitmap.createScaledBitmap(
bitmapToDrawInTheCenter, backgroundBitmapScaledMask.getWidth(),
backgroundBitmapScaledMask.getHeight(), true);
Bitmap resultBitmap = Bitmap.createBitmap(
backgroundBitmapScaledMask.getWidth(),
backgroundBitmapScaledMask.getHeight(),
backgroundBitmapScaledMask.getConfig());
Canvas canvas = new Canvas(resultBitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(backgroundBitmapScaledMask, 0, 0, null);
canvas.drawBitmap(bitmapToDrawInTheCenter, 0, 0, paint);
return resultBitmap;
}
/**
* Draw one image over other using the canvas paint and path drawing.
*/
public Bitmap pasteOverMugForRightMug(Drawable back,
Bitmap bitmapToDrawInTheCenter) {
Bitmap backgroundBitmap = ((BitmapDrawable) back).getBitmap();
Bitmap resultBitmap = Bitmap.createBitmap(backgroundBitmap.getWidth(), backgroundBitmap.getHeight(), backgroundBitmap.getConfig());
Canvas canvas = new Canvas(resultBitmap);
canvas.drawBitmap(backgroundBitmap, new Matrix(), null);
Paint paint = new Paint();
canvas.drawBitmap(bitmapToDrawInTheCenter, 0, 25, paint);
return resultBitmap;
}
Here is my code . Some hard code values are used according plain Mug image Aspect Ratio.
Also Tried to do this with Open GL but it was very complex .
And and could not find method for cylinder shape in one of the image processing library Image magic.
if any one have an idea please share .
I have the following gradient (generated dynamically):
GradientDrawable dynamicDrawable = new GradientDrawable();
dynamicDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);
dynamicDrawable.setUseLevel(false);
int colors[] = new int[3];
colors[0] = Color.parseColor("#711234");
colors[1] = Color.parseColor("#269869");
colors[2] = Color.parseColor("#269869");
dynamicDrawable.setColors(colors);
and I want to set that drawable in a view using onDraw method.
When I want to assign a Drawable to a bitmap I use the casting (BitmapDrawable), but in that case is not possible due the gradientDrawable cannot be cast to BitmapDrawable.
Any idea about how I solve that?
Thanks in advance
I finally found the solution from your response. I paste the code for someone could need it:
private Bitmap createDynamicGradient(String color) {
int colors[] = new int[3];
colors[0] = Color.parseColor(color);
colors[1] = Color.parseColor("#123456");
colors[2] = Color.parseColor("#123456");
LinearGradient gradient = new LinearGradient(0, 0, 0, 400, Color.RED, Color.TRANSPARENT, Shader.TileMode.CLAMP);
Paint p = new Paint();
p.setDither(true);
p.setShader(gradient);
Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawRect(new RectF(0, 0, getWidth(), getHeight()), p);
return bitmap;
}
Create a mutable bitmap using Bitmap.createBitmap()
Create a Canvas based on the bitmap using new Canvas(bitmap)
Then call draw(canvas) on your GradientDrawable
You can use below code mention in: Android: Convert Drawable to Bitmap
public Bitmap convertToBitmap(Drawable drawable, int widthPixels, int heightPixels) {
Bitmap mutableBitmap = Bitmap.createBitmap(widthPixels, heightPixels, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(mutableBitmap);
drawable.setBounds(0, 0, widthPixels, heightPixels);
drawable.draw(canvas);
return mutableBitmap;
}
Because I need my drawable as bitmap descriptor, I convert my resource to a Drawable then to a Bitmap.
But I want to change its color, So I used the following code found somewhere here:
private static Bitmap drawableToBitmap (Drawable drawable) {
Bitmap bitmap = null;
if (drawable instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
if(bitmapDrawable.getBitmap() != null) {
return bitmapDrawable.getBitmap();
}
}
if(drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); // Single color bitmap will be created of 1x1 pixel
} else {
bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
int iColor = Color.parseColor("#006f00");
drawable.setColorFilter(iColor, PorterDuff.Mode.SRC_ATOP);
drawable.draw(canvas);
return bitmap;
}
But the drawable's color remains black.
Can you please tell me why and how to get it working?
This converts a BitmapDrawable to a Bitmap.
Drawable d = ImagesArrayList.get(0);
Bitmap bitmap = ((BitmapDrawable)d).getBitmap();
And for changing bitmap's color see below link
How to change Bitmap image color in android?
Paint p = new Paint();
ColorFilter filter = new LightingColorFilter(Color.RED, 0);
p.setColorFilter(filter);
canvas.drawBitmap(bitmap, 0, 0, p);
To convert drawable into bitmap use this -
Bitmap icon = BitmapFactory.decodeResource(context.getResources(),
R.drawable.icon_resource);
You can change bitmap pixels color by this-
int[] pixels = new int[myBitmap.getHeight()*myBitmap.getWidth()];
myBitmap.getPixels(pixels, 0, myBitmap.getWidth(), 0, 0, myBitmap.getWidth(), myBitmap.getHeight());
for (int i=0; i<myBitmap.getWidth()*5; i++)
pixels[i] = Color.BLUE;
myBitmap.setPixels(pixels, 0, myBitmap.getWidth(), 0, 0, myBitmap.getWidth(), myBitmap.getHeight());
I think the problem could be PorterDuff.Mode.SRC_ATOP, the method you use for apply the filter. I don't know specifically SRC_ATOP but this link explains all of them.
I don't know how your image is composed, but I'd use DST_ATOP instead of SRC_ATOP.
EDIT: I presumed that this code won't return in the case you are analizing, 'cause if it returns the filter obviously is never reached.
if (drawable instanceof BitmapDrawable)
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
if(bitmapDrawable.getBitmap() != null) {
return bitmapDrawable.getBitmap();
}
}
I have a mask bitmap with a half is red color and ones is transparent like this
https://www.dropbox.com/s/931ixef6myzusi0/s_2.png
I want to use mask bitmap to draw content on canvas only visible in red area, code like this:
Paint paint = new Paint();
public void draw(Canvas canvas) {
// draw content here
...
//and mask bitmap here
paint.setXfermode(new PorterDuffXfermode(android.graphics.PorterDuff.Mode.DST_IN));
canvas.drawBitmap(maskBitmap, 0, 0, paint);
}
The result as my expecting (content only visible in red area, BUT THE TRANSPARENT AREA BECOME BLACK IS PROBLEM!)
this image result :https://www.dropbox.com/s/mqj48992wllfkiq/s_2%20copy.png
Anyone help me???
Here is a solution which helped me to implement masking:
public void draw(Canvas canvas) {
Bitmap original = BitmapFactory.decodeResource(getContext().getResources(),R.drawable.original_image);
Bitmap mask = BitmapFactory.decodeResource(getContext().getResources(),R.drawable.mask_image);
//You can change original image here and draw anything you want to be masked on it.
Bitmap result = Bitmap.createBitmap(mask.getWidth(), mask.getHeight(), Config.ARGB_8888);
Canvas tempCanvas = new Canvas(result);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
tempCanvas.drawBitmap(original, 0, 0, null);
tempCanvas.drawBitmap(mask, 0, 0, paint);
paint.setXfermode(null);
//Draw result after performing masking
canvas.drawBitmap(result, 0, 0, new Paint());
}
The mask should be a white image with transparency.
It will work like this:
+ =
I encountered the same problem in my custom view and instead of decoding the bitmap from a resource, I had created the original bitmap and the masking bitmap from the scratch via canvas.draw*() methods (since both the original and mask are basic shapes). I was getting the blank opaque space instead of a transparent one. I fixed it by setting a hardware layer to my view.
View.setLayerType(LAYER_TYPE_HARDWARE, paint);
More info on why this is to be done here: https://stackoverflow.com/a/33483016/4747587
Same answer as #Sergey Pekar give but I have updated it in Kotlin.
fun ImageView.getMaskBitmap(imageUrl: String? = null, mContent: Int, mMaskedImage : Int) {
runOnBackground {
// if you have https image url then use below line
//val original: Bitmap = BitmapFactory.decodeStream(URL(imageUrl).openConnection().getInputStream())
// if you have png or jpg image then use below line
val original: Bitmap = BitmapFactory.decodeResource(resources, mContent)
val mask = BitmapFactory.decodeResource(resources, mMaskedImage) // mMaskedImage Your masking image
val result: Bitmap = Bitmap.createBitmap(mask.width, mask.height, Bitmap.Config.ARGB_8888, true)
val tempCanvas = Canvas(result)
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
tempCanvas.apply {
drawBitmap(original, 0f, 0f, null)
drawBitmap(mask, 0f, 0f, paint)
}
paint.xfermode = null
//Draw result after performing masking
runOnBackground(onMainThread = {
this.apply {
setImageBitmap(result)
scaleType = ImageView.ScaleType.FIT_CENTER
}
})
}
}
Github Demo
Bitmap finalMasking = stackMaskingProcess(imageBitmap, bitmapMasking);
private Bitmap stackMaskingProcess(Bitmap _originalBitmap, Bitmap _maskingBitmap) {
try {
if (_originalBitmap != null)
{
int intWidth = _originalBitmap.getWidth();
int intHeight = _originalBitmap.getHeight();
resultMaskBitmap = Bitmap.createBitmap(intWidth, intHeight, Bitmap.Config.ARGB_8888);
getMaskBitmap = Bitmap.createScaledBitmap(_maskingBitmap, intWidth, intHeight, true);
Canvas mCanvas = new Canvas(resultMaskBitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mCanvas.drawBitmap(_originalBitmap, 0, 0, null);
mCanvas.drawBitmap(getMaskBitmap, 0, 0, paint);
paint.setXfermode(null);
paint.setStyle(Paint.Style.STROKE);
}
} catch (OutOfMemoryError o) {
o.printStackTrace();
}
return resultMaskBitmap;
}
I like the approach from Er. Praful Parmar's answer but for me it did not quite work as expected. I had problems, because some scaling was going on without intention.
My Bitmaps had a different density than my device and this messed things up.
Also I wanted to reduce the creation of Objects, so I moved the Paint object to a constant for reuse.
So here is my utils method:
public static final//
Bitmap createWithMask(final Bitmap img, final Bitmap mask) {
final Bitmap result = Bitmap.createBitmap(img.getWidth(), img.getHeight(),
Bitmap.Config.ARGB_8888);
result.setDensity(originalBitmap.getDensity()); // to avoid scaling if density of 'img' is different form the default on your device
final Canvas canvas = new Canvas(result);
canvas.drawBitmap(img, 0, 0, null);
canvas.drawBitmap(mask, 0, 0, PAINT_FOR_MASK);
return result;
}//end-method
private static final Paint PAINT_FOR_MASK = createPaintForMask();
private static final//
Paint createPaintForMask() {
final Paint paint = new Paint();
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
return paint;
}//end-method
I am developing an android application in which I set an image to imageview. Now programmatic I want to change the bitmap image color. Suppose my image have red color initially and now I need to change it to orange color. How can I do that? Please help.
Here is my code. I managed to change the opacity but I do not know how to change the color.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ImageView iv = (ImageView) findViewById(R.id.img);
Drawable d = getResources().getDrawable(R.drawable.pic1);
Bitmap mNewBitmap = ((BitmapDrawable)d).getBitmap();
Bitmap nNewBitmap = adjustOpacity(mNewBitmap);
iv.setImageBitmap(nNewBitmap);
}
private Bitmap adjustOpacity( Bitmap bitmap ) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Bitmap dest = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
int[] pixels = new int[width * height];
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
dest.setPixels(pixels, 0, width, 0, 0, width, height);
return dest;
}
I tried Josip's answer but wouldn't work for me, regardless of whether the offset parameter was 1 or 0 - the drawn bitmap just appeared in original colour.
However, this did work:
// You have to copy the bitmap as any bitmaps loaded as drawables are immutable
Bitmap bm = ImageLoader.getInstance().loadImageSync("drawable://" + drawableId, o)
.copy(Bitmap.Config.ARGB_8888, true);
Paint paint = new Paint();
ColorFilter filter = new PorterDuffColorFilter(ContextCompat.getColor(this, R.color.COLOR_1_DARK), PorterDuff.Mode.SRC_IN);
paint.setColorFilter(filter);
Canvas canvas = new Canvas(bm);
canvas.drawBitmap(bm, 0, 0, paint);
Update 1
Whilst the above works well and is useful in a lot of cases, if you just want to change the main colour of an ImageView drawable, which the op did, you can just use:
imgView.setColorFilter(ContextCompat.getColor(this, R.color.COLOR_1_DARK));
If you need more flexibility or this doesn't give the desired effect, there's an overload that allows you to change the PorterDuff Mode until you get what you're after:
imgView.setColorFilter(ContextCompat.getColor(this, R.color.COLOR_1_DARK), PorterDuff.Mode.SRC_ATOP);
Update 2
Another good use case I've had for this lately is customizing the appearance of a Google map v2 marker icon. In order to use 2 graphics to allow (for example) small/large icons on a marker, but also a range of colours on those 2 graphics by changing the colour of them dynamically. In my case I was doing this inside a ClusterRenderer as the markers were also clustered, but this can be used with a regular map marker the same way:
#Override
protected void onBeforeClusterItemRendered(MyClusterItem item, MarkerOptions markerOptions) {
try {
int markerColor = item.getColor();
Bitmap icon;
if (item.isFeatured()) {
// We must copy the bitmap or we get an exception "Immutable bitmap passed to Canvas constructor"
icon = BitmapFactory.decodeResource(context.getResources(),
R.drawable.icon_marker_large).copy(Bitmap.Config.ARGB_8888, true);
} else {
// We must copy the bitmap or we get an exception "Immutable bitmap passed to Canvas constructor"
icon = BitmapFactory.decodeResource(context.getResources(),
R.drawable.icon_marker_small).copy(Bitmap.Config.ARGB_8888, true);
}
Paint paint = new Paint();
ColorFilter filter = new PorterDuffColorFilter(ContextCompat.getColor(context, markerColor), PorterDuff.Mode.SRC_IN);
paint.setColorFilter(filter);
Canvas canvas = new Canvas(icon);
canvas.drawBitmap(icon, 0, 0, paint);
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));
} catch (Exception ex) {
ex.printStackTrace();
}
}
I got kind of solution.
Bitmap sourceBitmap = BitmapFactory.decodeFile(imgPath);
float[] colorTransform = {
0, 1f, 0, 0, 0,
0, 0, 0f, 0, 0,
0, 0, 0, 0f, 0,
0, 0, 0, 1f, 0};
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(0f); //Remove Colour
colorMatrix.set(colorTransform); //Apply the Red
ColorMatrixColorFilter colorFilter = new ColorMatrixColorFilter(colorMatrix);
Paint paint = new Paint();
paint.setColorFilter(colorFilter);
Display display = getWindowManager().getDefaultDisplay();
Bitmap resultBitmap = Bitmap.createBitmap(sourceBitmap, 0, (int)(display.getHeight() * 0.15), display.getWidth(), (int)(display.getHeight() * 0.75));
image.setImageBitmap(resultBitmap);
Canvas canvas = new Canvas(resultBitmap);
canvas.drawBitmap(resultBitmap, 0, 0, paint);
private void changeColor(){
ImageView image = (ImageView) findViewById(R.id.imageView1);
Bitmap sourceBitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher);
changeBitmapColor(sourceBitmap, image, Color.BLUE);
}
private void changeBitmapColor(Bitmap sourceBitmap, ImageView image, int color) {
Bitmap resultBitmap = Bitmap.createBitmap(sourceBitmap, 0, 0,
sourceBitmap.getWidth() - 1, sourceBitmap.getHeight() - 1);
Paint p = new Paint();
ColorFilter filter = new LightingColorFilter(color, 1);
p.setColorFilter(filter);
image.setImageBitmap(resultBitmap);
Canvas canvas = new Canvas(resultBitmap);
canvas.drawBitmap(resultBitmap, 0, 0, p);
}
It's better obtain mutable bitmap by copy, without changing size:
public static Bitmap changeBitmapColor(Bitmap sourceBitmap, int color)
{
Bitmap resultBitmap = sourceBitmap.copy(sourceBitmap.getConfig(),true);
Paint paint = new Paint();
ColorFilter filter = new LightingColorFilter(color, 1);
paint.setColorFilter(filter);
Canvas canvas = new Canvas(resultBitmap);
canvas.drawBitmap(resultBitmap, 0, 0, paint);
return resultBitmap;
}
public Bitmap replaceColor(Bitmap src,int fromColor, int targetColor) {
if(src == null) {
return null;
}
// Source image size
int width = src.getWidth();
int height = src.getHeight();
int[] pixels = new int[width * height];
//get pixels
src.getPixels(pixels, 0, width, 0, 0, width, height);
for(int x = 0; x < pixels.length; ++x) {
pixels[x] = (pixels[x] == fromColor) ? targetColor : pixels[x];
}
// create result bitmap output
Bitmap result = Bitmap.createBitmap(width, height, src.getConfig());
//set pixels
result.setPixels(pixels, 0, width, 0, 0, width, height);
return result;
}
The simplest way to change the bitmaps color is with this method:
bitmap.eraseColor(ContextCompat.getColor(this, R.color.your_color));
If you want to overlay the ImageView with color use:
imageView.setColorFilter(ContextCompat.getColor(this, R.color.your_color));
A little off topic, but considering you only want to display in changed color here is my solution. Namely, the easiest and fast way is just applying a filter by using drawColor() method on Canvas, right after drawBitmap():
m_canvas.drawColor(Color.RED, PorterDuff.Mode.ADD);
Sources: https://developer.android.com/reference/android/graphics/PorterDuff.Mode.html
Even if bitmap is immutable, it will work.
Paint paint = new Paint();
ColorFilter filter = new PorterDuffColorFilter(ContextCompat.getColor(context, R.color.whatColorNeed), PorterDuff.Mode.SRC_IN);
paint.setColorFilter(filter);
canvas.drawBitmap(bitmapToModify, some_x, some_y, paint);
I have solved the problem by using the below code
public void changeColor(Bitmap srcImage) {
Bitmap bmpRedscale = Bitmap.createBitmap(srcImage.getWidth(),
srcImage.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmpRedscale);
Paint paint = new Paint();
ColorMatrix cm = new ColorMatrix();
cm.setRGB2YUV();
paint.setColorFilter(new ColorMatrixColorFilter(cm));
canvas.drawBitmap(srcImage, 0, 0, paint);
mImgEdited.setImageBitmap(bmpRedscale);
}
In Kotlin :
private fun changeBitmapColor(oldBitmap: Bitmap, newColor: Int): Bitmap {
val paint = Paint()
val filter: ColorFilter = PorterDuffColorFilter(
newColor,
PorterDuff.Mode.SRC_IN
)
paint.colorFilter = filter
val canvas = Canvas(oldBitmap)
canvas.drawBitmap(oldBitmap, 0f, 0f, paint)
return oldBitmap
}
This function PorterDuff.Mode.SRC_IN can change due to the Bitmap file, look this link https://developer.android.com/reference/android/graphics/PorterDuff.Mode