I basically have an ImageView which got modified with Canvas and looks like a cirlce. (original image had the dimensions of a square (500x500))
Image what the starting position looks like:
http://imgur.com/bvXdLoP
(red is transparent and got removed with help of the Canvas method)
The animation should take aroud 1000 miliseconds and during this time step for step restore to the original picture. So in the end there should be a sqaure again.
In other words the cut off corners, which are the differnce between a square and a circle (and red marked in the image), get step for step restored in around 1000 milliseconds, with a sort of spreading looking animation.
Don't really have any clue on how to achieve this, so I can just share the Canvas method I used to cut off the corners (if it helps :X):
private Bitmap createCircleImage(Bitmap bitmap) {
Bitmap bmp;
bmp = Bitmap.createBitmap(bitmap.getWidth(),
bitmap.getHeight(), Bitmap.Config.ARGB_8888);
BitmapShader shader = new BitmapShader(bitmap,
BitmapShader.TileMode.CLAMP,
BitmapShader.TileMode.CLAMP);
float radius = bitmap.getWidth() / 2f;
Canvas canvas = new Canvas(bmp);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(shader);
canvas.drawCircle(bitmap.getWidth()/2,bitmap.getHeight()/2,bitmap.getHeight()/2, paint);
return bmp;
}
Appreciate any help,
thank you!
There are many ways to achieve such behavior. For example you can create custom view and override its onDraw method such as
#Override
public void onDraw(Canvas canvas) {
canvas.drawCircle(viewWidth/2, viewHeight/2, currentRadius, paint);
}
and then you can just increase the radius little by little and invalidate the view.Since view is by default will clip on its clipBounds (you can only draw inside viewidth x viewheight rectangle unless you set it otherwise) you will get the effect that you want. And then you can tweak the interpolation to achieve smoother and more natural animation.
note: a bitmap shader is attached to the paint (you already know chow to create it). I don't include it in the code, since you shouldn't initialize it inside onDraw method for performance reason.
Related
This problem seemed very obvious for me to solve, but whatever I try, it doesn't work. What I'm trying to do is to incorporate a mini-version of my PlayScreen in a ScrollPane as a tutorial where you can read text and try it out immediately.
Because I didn't find any better solution to add this to the Table inside the ScrollPane, I edited the draw() method of the PlayScreen to take the ScrollPane.getScrollPercentY() and offset the camera of the PlayScreen accordingly.
What I want to do now is to only render only part of the viewport that would be normally visible in the real game. Subsequently, I want to be able to control the size and position of this "window".
I also want to be able to resize and move the content, while cutting off the edges that are not visible to the camera. This is what I tried inside the PlayScreenDraw:
public void draw(final float yOffset,
final int xTiles,
final int yTiles) {
view.getCamera().position.y = yTiles / 2f - yOffset * yTiles / HEIGHT; // HEIGHT = 800
view.getCamera().position.x = xTiles / 2f;
view.setWorldSize(xTiles, yTiles); //Do i even need to change the world size?
b.setProjectionMatrix(view.getCamera().combined);
b.begin();
...
b.end();
view.update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
}
What this gives me, in terms of the picture above, is this
How do I need to change the viewport and/or the camera? Btw., this is how i set the two up:
cam = new OrthographicCamera();
cam.setToOrtho(false, WIDTH, HEIGHT); // WIDTH = 8, HEIGHT = 16
batch.setProjectionMatrix(cam.combined);
view = new FitViewport(WIDTH, HEIGHT, cam);
The Pixmap class can help you achieve what you want since you stated that you wanted to "cut off" the parts outside of the green selection box.
You need to render what the camera sees to an FBO and then get the pixmap from the FBO itself.
Framebuffer Objects are OpenGL Objects, which allow for the creation of user-defined Framebuffers. With them, one can render to non-Default Framebuffer locations, and thus render without disturbing the main screen.
-- OpenGL wiki
// Construct an FBO and keep a reference to it. Remember to dispose of it.
FrameBuffer fbo = new FrameBuffer(Format.RGBA8888, width, height, false);
public void render() {
//Start rendering to the fbo.
fbo.begin();
//From the camera's perspective.
batch.setProjectionMatrix(camera.combined);
batch.begin();
//Draw whatever you want to draw with the camera.
batch.end();
// Finished drawing, get pixmap.
Pixmap pixmap = ScreenUtils.getFrameBufferPixmap(0, 0, width, height);
//Stop drawing to your fbo.
fbo.end();
}
After getting the pixmap you can iterate through the pixels and set the alpha of the pixels outside your green selection window to 0 making them invisible or "cutting them off"
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.
I have a bitmap that I would like to rotate about a point on a canvas. The point I want to rotate it about is not the center of the bitmap. I am using a matrix. Here is an example of what I have.
Bitmap image = ContentManager.getInstance().getImage(imageId);
Matrix matrix = new Matrix();
matrix.setTranslate(-image.getWidth()/2f, -image.getHeight()/2f);
matrix.postRotate(rotationDegrees);
matrix.postTranslate(x / scaleX, y / scaleY);
matrix.postScale(scaleX, scaleY);
paint.setAlpha(alpha);
canvas.drawBitmap(image, matrix, paint);
I want to manipulate this code slightly to not rotate around the bitmap's center point but a different point. To illustrate more clearly I have created this picture:
.
I have tried everything I can think of from setting
matrix.setTranslate(-image.getWidth()/2f, -image.getHeight()/2f);
to
matrix.setTranslate(pivotPoint.x, pivotPoint.y);
and a lot of other stuff. The result is the bitmap is always way off from where I expected it. (eg. rotate it about the center of the screen 90 degrees would put the bitmap 90 degrees from where it was and consequently would be rotated.) The bitmap always seems to rotate about its center point and then ends up in a random spot on the screen.
After much messing around and find this very difficult or buggy, not sure which, found the easiest solution to this is to find the new center of the bitmap and drop the image there instead, seem more complicated but it works where plain rotation/translation wasn't.
float angle;
float radius;
Bitmap wheelSelectBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);
Matrix matrix = new Matrix();
Point pivotPoint = new Point();
pivotPoint.set(pivotPoint.x, pivotPoint.y)
Point newCenter = new Point();
newCenter.set((int)(pivotPoint.x + (radius * Math.cos(angle)), (int)(pivotPoint.y - (radius * Math.sin(angle)));
matrix.postTranslate(newCenter.x - (bitmap.getWidth()/2f), newCenter.y - (bitmap.getHeight()/2f));
matrix.postRotate(angle, newCenter.x, newCenter.y);
Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), null, true);
canvas.drawBitmap(bitmap, matrix, null);
This might not be perfect but working this way solved the problem for me and stopped bitmaps flying around all over the shop. It also rotates clockwise by default unlike your diagram so just use -angle for counter-CW. Not sure what is going on here as this code definitely failed to get the desired effect for me:
matrix.postTranslate(pivotPoint.x, pivotPoint.y);
matrix.postRotate(angle);
matrix.postTranslate(radius - (bitmap.getWidth()/2f), -bitmap.getHeight()/2f);
Cant see what is wrong with logic here ? But in my case the bitmap was not visible in the view here.
When rotating an object, it will rotate around the origin (upper left corner). You'll want to first translate the bitmap so the pivot point becomes (0, 0), rotate the bitmap then translate back to it's original position.
you could try something like this:
matrix.setTranslate(-px, -py);
matrix.postRotate(rotationDegrees);
matrix.setTranslate(px, py);
// Then do other things.
So I seem to have a conundrum. I need to add multiple custom views to a framelayout. The code for this is working just fine. However, I wish to access the underlying bitmap that the canvas uses in the views onDraw method. Like this one (in a class that extends View):
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
canvas.drawARGB(Color.alpha(bgColor), Color.red(bgColor),
Color.green(bgColor), Color.blue(bgColor));
m.reset();
m.setTranslate(imgPosX - ((float) userImage.getWidth() / 2.0f), imgPosY
- ((float) userImage.getHeight() / 2.0f));
m.postRotate(angle);
m.postScale(1.0f, 1.0f);
canvas.drawBitmap(userImage, m, null);
}
I wish to erase certain pixels essentially. Now, I know I can do this via the setPixel method which is fine but it is exceptionally slow and not satisfactory. I have a working ndk function that does exactly what I want, but it passes in a bitmap. I know I can use a SurfaceView instead of a View to access the bitmap like that, however as mentioned here multiple SurfaceViews in a FrameLayout isn't an option. So, I would think I need to manipulate the bitmap itself used by the canvas in the onDraw method. How would I go about doing this? or alternatively I don't mind creating another bitmap, passing it into the ndk function and returning/drawing that, however would I do a canvas.drawBitmap with transparent pixels?
Have you looked into setting the PorterDuff mode on a Paint object?
use paint.setXfermode(new PorterDuffXfermode(Mode.CLEAR)) then draw over the pixels that you want to erase
edit: What exactly are you trying to clear? There are multiple modes and you may need to select a different one depending on what you are trying to do. Here is an example of a function I currently use to crop Bitmaps to a circle.
public static Bitmap crop_circle_center(Bitmap bitmap) {
final int diameter = Math.min(bitmap.getWidth(), bitmap.getHeight());
Bitmap output = Bitmap.createBitmap(diameter,
diameter, Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final int color = 0xff424242;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
rect.offset(-(bitmap.getWidth()-diameter)/2, -(bitmap.getHeight()-diameter)/2);
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawCircle(diameter/ 2, diameter/ 2,
diameter / 2, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(bitmap, null, rect, paint);
return output;
}
I'm trying to create a function that tints a Bitmap,
this works...
imgPaint = new Paint();
imgPaint.setColorFilter(new LightingColorFilter(color,0));
//when image is being drawn
canvas.drawBitmap(img,matrix,imgPaint);
However, when the bitmap has to be drawn constantly (every frame) , I start to see screen lag, because this didn't occur before the color filter was set, I believe that it is applying the filter every time I need the canvas drawn.
Is there a way to apply the paint once to the bitmap and have it permanently changed?
Any help appreciated :)
Create a second bitmap and draw the first bitmap into it using the color filter. Then use the second bitmap for the high-volume rendering.
EDIT: Per request, here is code that would do this:
public Bitmap makeTintedBitmap(Bitmap src, int color) {
Bitmap result = Bitmap.createBitmap(src.getWidth(), src.getHeight(), src.getConfig());
Canvas c = new Canvas(result);
Paint paint = new Paint();
paint.setColorFilter(new LightingColorFilter(color,0));
c.drawBitmap(src, 0, 0, paint);
return result;
}
You would then call this method once to convert a bitmap to a tinted bitmap and save the result in an instance variable. You would then use the tinted bitmap directly (without a color filter) in your method that draws to canvas. (It would also be a good idea to pre-allocate the Paint object you will be using in the main draw method and save it in an instance variable as well, rather than allocating a new Paint on every draw.)