Custom curved lines in Android - java

I am working on a game where I have some bezier curves. At the moment, everything is fine and it's working as it should, although I do some complicated operation on this curves. To draw the curves I use a canvas and paths, like this:
private void refresh() {
mPath = new Path();
mPath.moveTo(start_x, start_y);
mPath.cubicTo(anchor1_x, anchor1_y, anchor2_x, anchor2_y, end_x, end_y);
}
public void draw(Canvas canvas, Paint paint) {
Path newPath = new Path(mPath);
Matrix mMatrix = new Matrix();
mMatrix.setTranslate(-mCamera.x1, -.mCamera.y1);
mMatrix.postScale(mCamera.getWidthRatio(), mCamera.getHeightRatio());
newPath.transform(mMatrix);
canvas.drawPath(newPath, paint);
}
The paint parameter is ussually the following one:
curvesPaint = new Paint();
curvesPaint.setColor(0xFFFFFFFF);
curvesPaint.setAntiAlias(true);
curvesPaint.setStrokeCap(Paint.Cap.ROUND);
curvesPaint.setStrokeJoin(Paint.Join.ROUND);
curvesPaint.setStyle(Paint.Style.STROKE);
curvesPaint.setStrokeWidth(canvas.getHeight() / 100f);
if (shadow)
curvesPaint.setShadowLayer(canvas.getHeight() / 50f, 0, 0, 0xAAFFFFFF);
else
curvesPaint.setShadowLayer(0, 0, 0, 0xAAFFFFFF);
My question is: how can I make the curves more beautiful? At the moment, they're only white (or white with a white shadow). I want to be able to draw them with a nice gradient or model on them and I have no idea how to do that. I was thinking about the setShader function of the Paint class but I don't know if I can use it for my purpose or how to use it. Other solutions, like drawing multiple paths one above the other, is clearly impractical due to low fps.

I suggest you start looking at using OpenGL or third-party wrapper libraries such as libgdx. Canvas is meant for relatively simple graphics. I don't think it takes advantage of the devices GPU. To take advantage of the full power of the GPU, you will need to use OpenGL.

Related

Sprite Cropping LibDGX

I've been trying to draw an image of a block with an eye on the screen. I want to be able to animate more than one texture in the same way, so drawing the animations by hand is not an option. The problem is that when I move the eyelid sprite higher up than completely closed, part of the eyelid shows above the expected bounds like this. Obviously, this is a problem as it looks quite unnatural. I'd like to either have an alternative solution to this problem, or be able to crop the eyelid's Sprite object to fit behind the rest of the image. The final image consists of the eyeball, pupil (as I want to be able to animate this too), eyelid, main body and the outlines, drawn in that order. The render function looks like this:
public void render(float delta) {
float height = this.eyelid.getHeight();
float eyeHeight = height*0.7f;
this.eyelid.setY(this.eye.getY()+(eyeHeight*((100-this.lid)/100f)));
batch.begin();
this.eye.draw(batch);
this.pupil.draw(batch);
this.eyelid.draw(batch);
this.main.draw(batch);
this.shade.draw(batch);
batch.end();
}
this.lid is the % of how closed the eye is, and the image for the eyelid itself can be found here. How could I solve this problem, or how could I crop the sprite? I don't want to have to reload the texture as a sprite every frame.
I think what you're looking for is the ScissorStack class, which is documented on the libGDX wiki. You can use this object to clip around the eye's frame so that the top of the lid doesn't show.

Unexpected results implementing simple motion blur in Libgdx

In the two attached pictures, the desktop screenshot of libgdx functions as expected. The screenshot from my Galaxy Nexus is unfortunately not as expected. I am attempting to create a simple motion blur or trail effect.
Rendering as I expected on my desktop.
Not rendering as I expected on my Galaxy nexus.
The circle textures are drawn in a for loop during rendering and the effect is achieved with a pixmap using the RGBA of 0, 0, 0, 0.1f that is drawn before the circles.
screenClearSprite creation
Pixmap screenClearPixmap = new Pixmap(256, 256, Format.RGBA8888);
screenClearPixmap.setColor(Color.rgba8888(0, 0, 0, 0.1f));
screenClearPixmap.fillRectangle(0, 0, 256, 256);
screenClearTexture = new Texture(screenClearPixmap);
screenClearSprite = new Sprite(screenClearTexture);
screenClearSprite.setSize(screenWidth, screenHeight);
screenClearPixmap.dispose();
Render
batch.begin();
font.draw(batch, "fps:" + Gdx.graphics.getFramesPerSecond(), 0, 20);
screenClearSprite.draw(batch);
for (int i = 0; i < circleBodies.size(); i++) {
tempPos = circleBodies.get(i).getPosition();
batch.draw(circleTexture, (tempPos.x * SCALE) + screenWidthHalf
- circleSizeHalf, (tempPos.y * SCALE) + screenHeightHalf
- circleSizeHalf);
}
batch.end();
So, what did I do wrong? Perhaps there is a better way to get the 'motion blur' effect of movement?
Here is a different approach, where you clear your screen each time with solid color and no alpha.
This means that you will have to modify your code some. The good thing about this, is that the way you are doing it has some flaws: It will blur everything in motion, not just the balls. And can quickly produce ugly results/artefacts unless you are careful.
Do the same as you are doing now, but instead of drawing the balls to the batch, draw them onto a texture/bitmap/whatever. Then each frame add an alpha-blended image over the balls-image, and then draw the balls in their current position on top of that. Then add that image to your screen. Very much like you are doing now, except you draw to something else and keep it. This way you don't have to rely on the viewport you are drawing onto, and can keep everything separated.
This method is similar to drawing to an accumulation buffer.
Instead of doing it the way you are doing, you can keep track of the n latest positions of each ball. And then draw all of them each frame, with different alpha. This is very easy to implement. Can result in many drawing calls if you have many balls or a large n, but if it's not too much it shouldn't limit your fps and gives nice control.
Perhaps there is a better way to get the 'motion blur' effect of
movement?
in order to make motion blur in my game i use another approch "The particle effect" it works realy fine with me and i didn't have Android/Desktop problems or with different android devices
all you have to do is to use "Particle Effect Editor" of Libgdx and make your effect then load it in your project finally draw it at the same position you draw your object (and alos draw your object)
Tips to make the right effect file with Paticle Editor :
set (use) the same image of the object that you want to blur it motion in the particle effect
try to limit the count : the max number of particle allowed
Disable the "velocity" and "Angle"
parameter
Particle effect help to do motion effect
Hope this will help someone !

How to draw a triangular portion of a bitmap

I am attempting to draw a triangular portion of a bitmap. I already know how to draw a filled triangle using path, and I already know that the answer may involve something called BitmapShader, but I can not find any clear documentation or examples to put it all together.
EDIT: After much flailing and experimentation, I am now nearly there. My code looks like this:
Paint paint;
Path path = new Path();
BitmapShader bms = new BitmapShader(shrub_bitmap,TileMode.REPEAT ,TileMode.REPEAT );
paint.setStyle(Style.FILL);
paint.setShader(bms);
path.reset();
path.setFillType(Path.FillType.EVEN_ODD);
path.moveTo(x1,y1);
path.lineTo(x2,y2);
path.lineTo(x3,y3);
path.close();
canvas.drawPath(path, paint);
paint.setShader(null);
The only remaining problem is that the bitmap from which the triangle is drawn is rooted to the screen coordinates. This means that when the triangle is being animated (i.e. being drawn at various points around the screen), it has the appearance of being a window allowing us to see a static image underneath. What I actually want is for the bitmap to be tied to the triangle so that the triangle looks like a solid object moving around. Any idea how to fix that?
for your second question:
try using canvas.translate(x1, y1) and moveTo(0, 0), lineTo(x2 - x1, y2 - y1) ...

How can you produce sharp paint results when rotating a BufferedImage?

One attempted approach was to use TexturePaint and g.fillRect() to paint the image. This however requires you to create a new TexturePaint and Rectangle2D object each time you paint an image, which isn't ideal - and doesn't help anyway.
When I use g.drawImage(BufferedImage,...), the rotated images appear to be blurred/soft.
I'm familiar with RenderingHints and double-buffering (which is what I'm doing, I think), I just find it difficult to believe that you can't easily and efficiently rotate an image in Java that produces sharp results.
Code for using TexturePaint looks something like this.
Grahics2D g2d = (Graphics2D)g;
g2d.setPaint(new TexturePaint(bufferedImage, new Rectangle2D.Float(0,0,50,50)));
g2d.fillRect(0,0,50,50);
I'm using AffineTransform to rotate a hand of cards into a fan.
What would be the best approach to paint good-looking images quickly?
Here is a screenshot:
The 9 is crisp but the rest of the cards are definitely not as sharp.
It could be possible that the problem lies in when I create each card image and store it in an array.
Here's how I'm doing it at the moment:
// i from 0 to 52, card codes.
...
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
BufferedImage img = gc.createCompatibleImage(86, 126, Transparency.TRANSLUCENT);
Graphics2D g = img.createGraphics();
setRenderingHints(g);
g.drawImage(shadow, 0, 0, 86, 126, null);
g.drawImage(white, 3, 3, 80, 120, null);
g.drawImage(suit, 3, 3, 80, 120, null);
g.drawImage(value, 3, 3, 80, 120, null);
g.dispose();
cardImages[i] = img;
}
private void setRenderingHints(Graphics2D g){
g.setRenderingHint(KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
}
How should I approach this differently?
Thanks.
Edit:
Without RenderingHints
Setting AA hints made no difference. Also, setting RenderingHints when creating the images makes no difference either. It's only when they are being rotated with AffineTransform and painted using g.drawImage(...) that they seem to blur. The image above shows the difference between default (nearest neighbor) and bilinear interpolation.
Here is how I'm currently painting them (much faster than TexturePaint):
// GamePanel.java
private void paintCard(Graphics2D g, int code, int x, int y){
g.drawImage(imageLoader.getCard(code), x, y, 86, 126, null);
}
// ImageLoader.java
public BufferedImage getCard(int code){
return cardImages[code];
}
All my cards are 80x120 and the shadow .png is 86x126, so as to leave 3px semi-transparent shadow around the card. It's not a realistic shadow I know, but it looks okay.
And so the question becomes... How can you produce sharp paint results when rotating a BufferedImage?
Reference to a previous question also regarding a fanned card hand:
How can you detect a mouse-click event on an Image object in Java?
Bounty-Edit:
Okay so after much discussion I made a few test .svg cards to see how SVG Salamander would go about rendering them. Unfortunately, the performance is terrible. My implementation is clean enough, seeing as with double-buffered BufferedImage's the painting was incredibly fast. Which means I have come full circle and I'm back to my original problem.
I'll give the 50 bounty to whoever can give me a solution to get sharp BufferedImage rotations. Suggestions have been to make the images bigger than they need to be and downscale before painting, and to use bicubic interpolation. If these are the only possible solutions, then I really don't know where to go from here and I may just have to deal with the blurred rotations - because both of those impose performance setbacks.
I can finish my game if I can find a way to do this well.
Thanks to everyone. :)
When you rotate a rasterized image (such as a BufferedImage), you lose data. The best solution is to save your images larger than you'll need them, and downscale on the fly when you paint them. I've found that 1.5x the size you need is a good starting point.
Then, when you're painting the image, resize on the fly:
g.drawImage(bufferedImage, x, y, desiredWidth, desiredHeight, observer);
Rotations using bilinear interpolation is recommended.
Credit for suggestion goes to guido.
This advice is probably a little late in your design, but may be worth mentioning.
Rasterized images is probably the wrong technology to use if a lot of rotations and animations are a part of your UI; especially with complicated images with lots of curves. Just wait until you try and scale your canvass. I might suggest looking at a vector based graphical library. They will render the sorts of effects you want with less potential for artifacts.
http://xmlgraphics.apache.org/batik/using/swing.htm
Setting the interpolation type, as well as anti-aliasing value, in an AffineTransformOp may offer some improvement. Type TYPE_BICUBIC, while slower, is typically the best quality; an example is outlined here. Note that you can supply multiple RenderingHints. Another pitfall arises from failing to apply the hints each time the image is rendered. You may also need to adjust the transparency of the background, as suggested here. Finally, consider creating an sscce that includes one of your actual images.

Trying to create spinning propeller. Keeps spinning around origin, not itself

I'm trying to create a spinning airplane propeller in Java 3D. At the moment, it is rotating around the origin. However, I need it to rotate around itself. I haven't done much 3D graphics in Java, so I'm pretty much stumped.
TransformGroup rotateTheBlades = new TransformGroup();
rotateTheBlades.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
Alpha rotationAlpha = new Alpha(-1,5000);
RotationInterpolator rotator = new RotationInterpolator(
rotationAlpha,rotateTheBlades);
Transform3D abc = new Transform3D();
abc.rotZ(Math.PI/2);
rotator.setTransformAxis(abc);
rotator.setSchedulingBounds(new BoundingSphere());
rotateTheBlades.addChild(rotator);
rotateTheBlades.addChild(propeller);
sceneBG.addChild(rotateTheBlades);
Any help would be greatly appreciated. PS: I tried to translate it to the origin before and after but it doesn't seem to do anything whatsoever.
Without knowing enough Java to give a complete answer; the correct solution in maths terms is as you suspect: translate centre of the object to origin, rotate, then translate it back to its original position.
However, it's likely (ie, I'm guessing) that when you combine transformations, they're post-multiplied to give the effect that transformations happen in model space. What you probably want is pre-multiplication, to give the effect that transformations occur in world space.
Consider the sails of a windmill. In code you'd want to be able to translate to the top of the windmill, then call the routine that draws the sails. That routine might apply a rotation and then draw. But in terms of transformations, you actually you want to rotate the sails while at the origin so that they rotate around their centre, then move them out. So the transformations are applied in the opposite order to the order in which you request them.
What that means is, you want to apply the transformations as:
move away from the origin
rotate
move to the origin
For example, if you were in OpenGL (which is also a post-multiplier, and easy enough to follow in this example even if you don't actually know it) you might do:
glTranslatef(centreOfBody.x, centreOfBody.y, 0.0f);
glRotatef(angle, 0.0f, 0.0f, 1.0f);
glTranslatef(-centreOfBody.x, -centreOfBody.y, 0.0f);
The approach suggested by #Tommy is correct. Like GL, transformations of the Java graphics context are concatenated "in the most commonly useful way," which I think of as last-in, first-out. A typical paintComponent() method is shown below and in this complete example.
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.translate(this.getWidth() / 2, this.getHeight() / 2);
g2d.rotate(theta);
g2d.translate(-image.getWidth(null) / 2, -image.getHeight(null) / 2);
g2d.drawImage(image, 0, 0, null);
}

Categories

Resources