I'm trying to show part of an image in an ImageView. I tried it with canvas.drawBitmap() but it seems like the false part of the image is displayed. My code:
MainActivity:
this.drawCharacter = new DrawCharacter(1);
this.bm = Bitmap.createBitmap(16, 32, Bitmap.Config.ARGB_8888);
this.c = new Canvas(this.bm);
this.c = this.drawCharacter.drawCharacter(this.c, this.characterBitmap);
this.bm = Bitmap.createScaledBitmap(this.bm, this.imgWidth, this.imgHeight, isChild());
this.imgv.setImageBitmap(this.bm);
DrawCharacter:
[...]
Paint localPaint = new Paint();
paramCanvas.drawBitmap(paramBitmap, new Rect(8, 8, 16, 16), new Rect(4, 0, 12, 8), localPaint);
return paramCanvas;
[...]
And at the end some strange part of the image is shown.
In your drawBitmap() call, you set the first Rect parameter to 8,8,16,16, this is the source rectangle from your bitmap to be drawn, set it to null to draw the entire bitmap.
see the documentation
if you want to apply this bounds according to the initial bitmap scale, you may need to apply the same scale on your src Rectangle.
(try to remove the scale part, createScaledBitmap() to see if your src rectangle is good without the scale ;) )
Related
so I'm in a bit of a pickle. I know how to set the alpha value of a bitmap in android. What I don't know how to do is make is reversible. So, let's say someone wanted to set the alpha of an image to 50%, so they do. Now lets say they wanted to set it 75% (keep in mind, this is of the original image alpha value). Currently, what I have is a function that will set the alpha value of the current image, so it would be 75% of the 50% alpha value if that makes sense. How can I make it so that it accounts for the original image?
public Pixmap setAlpha(float newAlpha) { //integer between 0-100
if (newAlpha != alpha) { //to check if the current alpha value of the image is equal to your desired alpha. to avoid always halving you alpha value
float test = newAlpha/100.0f;
float test2 = test * 255;
alpha = test2;
Bitmap newBM = Bitmap.createBitmap(backupImg.getBitmap().getWidth(),backupImg.getBitmap().getHeight(), Bitmap.Config.ARGB_8888);
Canvas cc = new Canvas(newBM);
cc.drawARGB(0,0,0,0);
Paint newPaint = new Paint();
newPaint.setAlpha((int)test2);
cc.drawBitmap(backupImg.getBitmap(), 0, 0, newPaint);
img.setBitmap(newBM);
return img;
} else {
return img;
}
}
The Pixmap part is just a custom Bitmap class. backupImg is just a copy of img, created in the constructor of the object this function belongs to.
please keep in mind that this will be a canvas based bitmap. If I recall correctly imageView's aren't drawn on the canvas? So, as a further example. Imagine a sprite drawn to the canvas that you want to alter the alpha of. So you do it using the function I've posted. Now, let's say you want to undo the changes and restore it to the sprite's original alpha, of some other value. Well, you can't because the alpha value of the image has been changed permanently. What I want to do is store reference to the original image with another variable, and refer to that whenever I need to adjust the alpha value of the image. Hopefully that makes sense
Why don't you set alpha to the ImageView instead of setting it to a bitmap.
By setting alpha to the ImageView or say any view you can reverse it easily.
Refer to this answer in order to do it.
You can do it from xml. Just add the below line in the imageview tag:-
android:alpha="0.5"
You can set alpha between 0 to 1. Above line will set alpha to half that is 0.5.
For bitmap:
Bitmap newBitmap = Bitmap.createBitmap(originalBitmap.getWidth(),
originalBitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(newBitmap);
Paint alphaPaint = new Paint();
alphaPaint.setAlpha(75);
canvas.drawBitmap(originalBitmap, 0, 0, alphaPaint);
I've solved the problem myself. So, there is nothing wrong with the function I wrote. The problem instead lies within how Java uses pointers. because everything is passed via reference, I was actually referencing the same object, rather than creating two separate objects. So instead of:
Bitmap oldBM = new Bitmap();
Bitmap newBM = oldBM;
you would instead want to do
Bitmap oldBM = new Bitmap();
Bitmap newBM = new Bitmap(using old bitmap's value);
I am drawing a slider with Libgdx using the following code:
Pixmap pix = new Pixmap(200, 50, Pixmap.Format.RGBA8888);
pix.setColor(Color.BLACK);
pix.fill();
skin.add("sliderBackS", new Texture(pix));
Pixmap pix1 = new Pixmap(10, 70, Pixmap.Format.RGBA8888);
pix1.setColor(Color.RED);
pix1.fill();
skin.add("knobS", new Texture(pix1));
Slider.SliderStyle sliderStyle = new Slider.SliderStyle();
sliderStyle.disabledBackground = skin.newDrawable("sliderBackS");
sliderStyle.disabledKnob = skin.newDrawable("knobS");
sliderStyle.background = skin.newDrawable("sliderBackS");
sliderStyle.knob = skin.newDrawable("knobS");
skin.add("sunSlider", sliderStyle);
sunlightSlider = new Slider(0, 100, 1, false, sliderStyle);
stage.addActor(sunlightSlider);
sunlightSlider.setBounds(300, 300, 100, 10);
sunlightSlider.setDisabled(true);
sunlightSlider.setDebug(true);
And then else where:
stage.act(Math.min(Gdx.graphics.getDeltaTime(), 1 / 30f));
stage.draw();
But for some reason the pixmaps in the slider seem to be drawn at their actual size. This can be seen in the following picture (the green line is what it should be, from the debug):
For some reason the width seems to be constrained by sunlightSlider.setBounds(300, 300, 100, 10); but the height does not.
I know that I could choose the pixmap size to what ever I need, but I want to use an image from file. If I use a big image, then it overflows the bounds similarly.
I want it to look like it is above, but constrained to the green rectangle.
What am I doing wrong?
A TextureRegionDrawable, which is what you've created with your newDrawable calls, by default has a minimum size that matches its original pixel dimensions. The minimum size of the Drawable prevents the Slider widget from drawing it as small as it would like to fit it into its own bounds. So you can reduce the minimum size:
sliderStyle.disabledBackground = skin.newDrawable("sliderBackS");
sliderStyle.disabledBackground.setMinWidth(0);
sliderStyle.disabledBackground.setMinHeight(0);
// and so on for other new drawables.
I'm not sure what your long term plans are, but typically you want all your skin's images to be part of a single Texture object so the SpriteBatch doesn't have to flush itself many times to draw your whole scene.
If for some reason you're not doing that, you can at least make all these solid color drawables use the same texture. It can simply be a one-pixel white texture that is shared by everything. Also, make sure you are disposing of the pixmap you use to create a texture, or you are leaking memory!
Pixmap pix = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
pix.setColor(Color.WHITE);
pix.fill();
Texture tex = new Texture(pix);
pix.dispose();
skin.add(tex, "white");
Drawable blackDrawable = skin.newDrawable("white", Color.BLACK);
Drawable redDrawable = skin.newDrawable("white", Color.RED);
Slider.SliderStyle sliderStyle = new Slider.SliderStyle();
sliderStyle.disabledBackground = blackDrawable;
sliderStyle.disabledKnob = redDrawable;
sliderStyle.background = blackDrawable;
sliderStyle.knob = redDrawable;
Since you passed your Texture object to the Skin, the Skin will dispose of it when the Skin is disposed. Don't forget to dispose of the Skin in the dipose() method.
This is my first question here on Stackoverflow, so go easy on me. I'm trying to draw a radial bloom effect around a point, and found RadialGradient, as well as GradientDrawable. However neither fully gives a solution.
Here is how I'm doing the drawing right now:
//global
PorterDuffXfermode xferMode = new PorterDuffXfermode(PorterDuff.Mode.ADD);
Paint mainP = new Paint();
Paint whiteP = new Paint();
whiteP.setColor(Color.WHITE);
RadialGradient gradient;
// in a drawing method with a canvas
gradient = new RadialGradient((int) x, (int) y, tempRadius, mainP.getColor(),
0x00000000, Shader.TileMode.CL
mainP.setShader(gradient);
mainP.setXfermode(xferMode);
canvas.drawCircle((int) x, (int) y, tempRadius, mainP);
gradient = new RadialGradient((int) x, (int) y, tempRadius/2, whiteP.getColor(),
0x00000000, Shader.TileMode.CLAMP);
whiteP.setXfermode(xferMode);
whiteP.setShader(gradient);
canvas.drawCircle((int) x, (int) y, tempRadius/2, whiteP);
This will give the expected result:
Screenshot
However the new RadialGradient builds up in memory every frame per dot. Which becomes a problem later obviously. The only public thing in that class is a constructor, so all you can do with RadialGradient is create a new one every time you need a new size, position or color. The reason I want to do it this way is because you assign the gradient to the Paint object your going to draw it with, which allows you to use it's setXferMode().
The other way, using the GradientDrawable, does allow you to just create one instance of itself, so you can change the size, position, and color, but the Paint object it uses to paint itself is private, so you can't set the XferMode on it. Which is necessary so that if you have more than one dot, they don't just paint over one another. It has a colorfilter, but it doesn't look like colorfilter worries about the destination, only a specified color and the source.
Since I am new, i can only post two links, and instead of posting just two pictures, I'll link back to the same question I wrote on reddit which has all the pictures for context. (and no answers yet, which is why I came here, ha ha)
https://www.reddit.com/r/learnandroid/comments/590uce/having_a_shader_issue_using_radialgradient_and/
So, how should this be done, I know I need one class or the other. From what I can tell, RadialGradient would be perfect if I didn't have to create a new one every time I needed a different size, position, or color. GradientDrawable would be equally great since you can just have one instance, but I need to be able to set the XferMode on the paint it uses. Is there another class, or am I missing something between the two of these?
Thanks!!
So, while I did find a solution, it still lead to more issues. The solution I found was partly here. The only thing it was missing was being able to change the color of the preallocated RadialGradient bitmap. So I came up with this:
// Global
int tempRadius;
Paint p = new Paint();
RadialGradient gradient;
Bitmap circleBitmap = Bitmap.createBitmap((int) (tempRadius * 2.0f), (int) (tempRadius * 2.0f),
Bitmap.Config.ARGB_8888);
Canvas tempCanvas = new Canvas(circleBitmap);
Rect gradBMPRect = new Rect(0,0,200,200);
Rect destRect = new Rect();
int[] hsv = {0,1,1};
PorterDuffColorFilter[] myColors = new PorterDuffColorFilter[360];
PorterDuffXfermode xferMode = new PorterDuffXfermode(PorterDuff.Mode.ADD);
// Initialize
gradient = new RadialGradient(tempRadius, tempRadius, tempRadius, Color.WHITE, 0x00000000, Shader.TileMode.CLAMP);
circleBitmap = Bitmap.createBitmap((int) (100 * 2.0f), (int) (100 * 2.0f),
Bitmap.Config.ARGB_8888);
tempCanvas = new Canvas(circleBitmap);
for(int i = 0; i < 360; i++){
hsv[0] = i;
myColors[i] = new PorterDuffColorFilter(Color.HSVtoColor(hsv), PorterDuff.Mode.MULTIPLY)
}
// update/draw
p.setDither(true);
p.setShader(gradient);
tempCanvas.drawCircle(100, 100, 100, p);
p.setXfermode(xferMode);
if(p.getColor() != mainP.getColor()) {
p.setColorFilter(myColors[hue]);
p.setColor(mainP.getColor());
destRect.set((int)x-tempRadius,(int)y-tempRadius,(int)x+tempRadius,(int)y+tempRadius);
canvas.drawBitmap(circleBitmap,gradBMPRect,destRect,p);
p.setColorFilter(null);
destRect.set((int)x-(tempRadius/2),(int)y-(tempRadius/2),(int)x+(tempRadius/2),(int)y+(tempRadius/2));
canvas.drawBitmap(circleBitmap,gradBMPRect,destRect,p);
With this the RadialGradient is used to create a greyscaled bitmap, then a pallette of 360 PorterDuffColorFilters is made using HSVtoColor. All 360 have their modes set to multiply. When drawing these the paint used to draw the bitmap is set use the ColorFilter specified by its hue. The colorfilter shades the greyscaled bitmap to whatever color the filter is. And no more memory leaks :)
This took longer to render the bitmap of a RadialGradient vs rendering a circle with a RadialGradient though. With 30 circles it took my Galaxy S5 15-20ms per frame draw, while the bitmaps took around 30-35ms. It could probably get worked out though with a little more work and adjustment.
I have implemented a flood fill algorithm in an android app. The way I have implemented the algorithm doesn't actually change the source bitmap, but instead creates a new bitmap of the fill area. I.E.
Flood filling this circle with red
Would produce this bitmap (where everything else in the bitmap is transparent)
Which I then combine again into a single bitmap. This works great for solid colors, but I want to be able to implement a gradient flood fill so that if a user fills the same circle, choosing red and blue, the resulting bitmap would look like this
My question is, is there a way that I can use the red circle as some sort of mask to make the desired gradient? or do I have to write a gradient generator myself?
Thanks to pskink's hint, I was able to find an answer.
The idea is that you create a canvas, draw the mask to it, create the gradient that you want, then draw the gradient on top of it using the SRC_IN PorterDuffXfermode. Here's the code:
public Bitmap addGradient(Bitmap src, int color1, int color2)
{
int w = src.getWidth();
int h = src.getHeight();
Bitmap result = Bitmap.createBitmap(w,h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
canvas.drawBitmap(src, 0, 0, null);
Paint paint = new Paint();
LinearGradient shader = new LinearGradient(0,0,0,h, color1, color2, Shader.TileMode.CLAMP);
paint.setShader(shader);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawRect(0,0,w,h,paint);
return result;
}
In this instance, the DST(destination) is the red circle and the SRC(source) is the gradient. The SRC_IN PorterDuff mode means draw the SRC everywhere that it intersects with the DST.
Note that it really doesn't matter what color the mask is, because the PorterDuff mode only pays attention to whether the DST pixel is transparent or not. The color of the resulting bitmap will be a gradient between color1 and color2.
The following code defines my Bitmap:
Resources res = context.getResources();
mBackground = BitmapFactory.decodeResource(res, R.drawable.background);
// scale bitmap
int h = 800; // height in pixels
int w = 480; // width in pixels
// Make sure w and h are in the correct order
Bitmap scaled = Bitmap.createScaledBitmap(mBackground, w, h, true);
... And the following code is used to execute/draw it (the unscaled Bitmap):
canvas.drawBitmap(mBackground, 0, 0, null);
My question is, how might I set it to draw the scaled Bitmap returned in the form of Bitmap scaled, and not the original?
Define a new class member variable:
Bitmap mScaledBackground;
Then, assign your newly created scaled bitmap to it:
mScaledBackground = scaled;
Then, call in your draw method:
canvas.drawBitmap(mScaledBackground, 0, 0, null);
Note that it is not a good idea to hard-code screen size in the way you did in your snippet above. Better would be to fetch your device screen size in the following way:
int width = getWindowManager().getDefaultDisplay().getWidth();
int height = getWindowManager().getDefaultDisplay().getHeight();
And it would be probably better not to declare a new bitmap for the only purpose of drawing your original background in a scaled way. Bitmaps consume a lot of precious resources, and usually a phone is limited to a few MB of Bitmaps you can load before your app ungracefully fails. Instead you could do something like this:
Rect src = new Rect(0, 0, bitmap.getWidth() - 1, bitmap.getHeight() - 1);
Rect dest = new Rect(0, 0, width - 1, height - 1);
canvas.drawBitmap(mBackground, src, dest, null);
To draw the scaled bitmap you want save your scaled bitmap in a field somewhere (here called mScaled) and call:
canvas.drawBitmap(mScaled, 0, 0, null);
in your draw method (or wherever you call it right now).