Since I'm pretty new in Libgdx development, I would like to ask if it is possible to make image borders (to mark it up as selected) using it? I've read that one solution is to make 2 separate images: one with border and one without. However pretty all of my images are the same size and there are many of them so don't know if it's the best idea. Are there any other options?
EDIT
So i have chosen 2nd idea ( 1x1 image ) but there is some problem with it. First of all i can't make that borderImage to be under clicked element. Another problem is size of borderImage ( it's not same ). When i click in the center of button its smaller ( about couple pixels ) than when i click on the edge. Do u know what can cause these problems and how to fix them?
Code :
public void create(){
stage = new Stage();
Gdx.input.setInputProcessor(stage);
skin = new Skin(Gdx.files.internal("data/uiskin.json"));
Texture borderTexture = new Texture(Gdx.files.internal("data/select.png") );
final Image borderImage = new Image( borderTexture );
TextButton exitButton = new TextButton("Exit",skin);
exitButton.addListener(new ClickListener(){
public void clicked(InputEvent event, float x, float y)
{
borderImage.setSize( event.getTarget().getWidth() + 1 * 2, event.getTarget().getHeight() + 1 * 2);
Vector2 loc = new Vector2(0, 0);
Vector2 stageLoc = event.getTarget().localToStageCoordinates(loc);
borderImage.setPosition( stageLoc.x, stageLoc.y);
System.out.println("" + event.getTarget().getWidth() + " " +event.getTarget().getHeight() + " " + event.getTarget().getX() +
" " + event.getTarget().getY() + " " + stageLoc.x + " " + stageLoc.y);
stage.addActor(borderImage);
}
}
);
Table table = new Table();
table.add(exitButton).colspan(3);
table.setFillParent(true);
stage.addActor(table);
}
And the output i have :
29.0 25.0 4.0 1.0 466.0 257.0 // clicked in the center of button
37.0 27.0 462.0 256.0 462.0 256.0 // clicked on the edge of button
If you are using Scene2D and its Image class the simpliest idea is to keep a small texture of color you want the border to has. The texture can be like 1px x 1px size. It should has Nearest filtering due to avoid blending borders effect.
When click on image you can create an Image of size of the image plus border width and place it where the image is achieving border effect. On touch up you need to remove the border-actor
Texture borderTexture = new Texture( Gdx.files.internal("border.png") );
Image borderImage = new Image( borderTexture );
...
//in the clickListener
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button)
{
borderImage.setSize( event.getTarget().getWidth() + BORDER_WIDTH * 2, event.getTarget().getHeight() + BORDER_WIDTH * 2);
borderImage.setPosition( event.getTarget().getX() - BORDER_WIDTH, event.getTarget.getY() - BORDER_WIDTH);
stage.addActor( borderImage );
event.getTarget().toFront(); //to move the original image over the border
return true;
}
public void touchUp(InputEvent event, float x, float y, int pointer, int button)
{
borderImage.remove();
}
If not using the Scene2D you can achieve same effect by rendering Sprite of size of the image + border width and manage a flag saying if render the border or not
Sprite image, border;
...
//in the input processor
public boolean touchDown (int x, int y, int pointer, int button)
{
border.setSize( image.getWidth() + BORDER_WIDTH * 2 , image.getHeight() + BORDER_WIDTH * 2 );
border.setPosition( image.getX() - BORDER_WIDTH, image.getY() - BORDER_WIDTH);
renderBorder = true;
return true;
}
public boolean touchUp (int x, int y, int pointer, int button)
{
renderBorder = false;
return false;
}
//in the render method
if(renderBorder)
{
border.draw(batch);
}
image.draw(batch);
here what i'll do if it was for me :
i will use one border image for all my images (instead of image border for each one of my images)
let assume that on the left is your image and on right is the border image
when a Click event occured on the image you draw the border on the image (your image border is transparent inside you can easly do it with editor like gimp) on top of the image and you will have something like this
if the size is different you can change it and fix the same size before drawing the image at same position
Hope this will help !
Related
Faced a problem while trying to handle clicking on a moving image.
I used InputAdapter.touchDown() to handle the click and created Sprite for the image. Then I set the borders through Sprite.setBounds(). Further, in fact, the problem: if the coordinates in setBounds() are unchanged - the click is handled correctly. But if you change them (position.x++, for example) - the object comes into motion, but clicks are not read.
I can’t understand where the reason.
I tried to make a alterable variable outside the method, but this also didn't bring any result.
I tried using batch.draw(img) instead of img.draw(batch) - the effect is the same.
I tried to relocate Gdx.input.setInputProcessor() to the render() method, after img.setBounds() - nothing changed.
I even compared the coordinates of the Img and the Bounds area online, in motion - they are the same, as it should be.
Img and handler in constructor:
img = new Sprite(new Texture(finSize));
centerX = Gdx.graphics.getWidth()/2-img.getWidth()/2;
centerY = Gdx.graphics.getHeight()/2-img.getHeight()/2;
startPosition = new Vector2(centerX, centerY);
Gdx.input.setInputProcessor(new InputAdapter(){
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
if(img.getBoundingRectangle().contains(screenX, screenY))
System.out.println("Image Clicked");
return true;
}
});
Render:
public void render(SpriteBatch batch, float radius, float boost) {
speed+=boost;
nextX = radius * (float) Math.cos(speed); // Offset step X
nextY = radius * (float) Math.sin(speed); // Offset step Y
// Img is moving, but clicks is not handling
img.setBounds(startPosition.x+ nextX, startPosition.y + nextY, 100, 100);
// Handling clicks fine, but img is motionless
img.setBounds(startPosition.x, startPosition.y, 100, 100);
img.draw(batch);
// Checking coordinates - all's fine
System.out.println(img.getBoundingRectangle().getX());
System.out.println(startPosition.x + nextX);
System.out.println(img.getBoundingRectangle().getY());
System.out.println(startPosition.y + nextY);
}
So, I sequentially compared the XY coordinates of the image and the mouse click point and came to the conclusion that InputAdaper and Sprite consider Y differently - from above and from below. Therefore, X always coincided, and Y had a big difference in values.
As a result, I entered two corrected coordinates xPos \ yPos (Y subtracted from the total field height) for the center of the pic and in the touchDown() method, instead of comparing with BoundRectangle, simply compared the difference in the coordinates of the pic and the click modulo. If the result into the image size range - everything is ok.
Now clicks on the moving image works correctly.
public void render(SpriteBatch batch, float radius, float boost) {
speed+=boost; // rotational speed
nextX = radius * (float) Math.cos(speed); // Offset step X
nextY = radius * (float) Math.sin(speed); // Offset step Y
// set image size and position
img.setBounds(startPosition.x+nextX, startPosition.y+nextY, 100, 100);
img.draw(batch);
// Corrected coordinates of the image for InputAdapter coordinate system
xPos = img.getX()+img.getWidth()/2;
yPos = Gdx.graphics.getHeight()-img.getY()- img.getHeight()/2;
// Check the coincidence of the coordinates of the image area and the click point
Gdx.input.setInputProcessor(new InputAdapter(){
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
if((Math.abs(xPos-screenX)<=img.getWidth()) && (Math.abs(yPos-screenY)<=img.getHeight()))
{System.out.println("Image Clicked");}
return true;
}
});
}
I am making a game in Android that requires a list of sprites to running from right to left so I try to flip the image using the code below.
It slows the game speed down so much, moves fast running to the right but slows down so much running to the left.
public void Draw(Canvas spriteBatch, int X, int Y, int imageIndex,int flip)
{
int drawY = (imageIndex / columns);
int drawX = imageIndex - drawY * columns;
int spriteX = drawX * spriteWidth;
int spriteY = drawY * spriteHeight;
Rect src = new Rect( spriteX, spriteY,spriteX + spriteWidth, spriteY + spriteHeight);
Rect dst = new Rect(X, Y, X + spriteWidth,Y + spriteHeight);
location.X = X;
location.Y = Y;
if(flip == 1)
{
//here image is flipped
spriteBatch.save();
spriteBatch.scale(-scaleX, scaleY, X, Y);
spriteBatch.drawBitmap(texture2D,src,dst, paint);
spriteBatch.restore();
//Use simple use this to flip image canvas.scale(-1, 1)
}
else if(flip == 0)
{
//draws sprite without flipping
spriteBatch.save();
spriteBatch.scale(scaleX, scaleY, X, Y);
spriteBatch.drawBitmap(texture2D,src,dst, paint);
spriteBatch.restore();
}
this.SetFrame(imageIndex);
}
I can flip using matrix but I can't draw a sprite using matrix.
Is there a way to draw sprite using matrix and would it make it faster?
matrix.reset();
matrix.setTranslate(location.X, location.Y);
matrix.setScale(-scaleX,scaleX);
matrix.postTranslate(location.X + texture2D.getWidth(), location.Y);
Or is there another way that is faster?
So I think that the matrix solution should will work perfectly the canvas object includes it's own built in matrix which if you manipulate will affect the output of all graphics after that point. So you just set the matrxi and then do the draw.
You're solution is okay but you're doing it wrong - rather do this part ONCE:
Create the left-ward facing sprite as you do at the beginning and then store it. Then EACH FRAME: Use this 'cached' copy of the inverted bitmap that you created.
A third solution (which I haven't tried but might work) is to manipulate the destination rectangle so that the left and right edges are swapped - the canvas docs say, "Draw the specified bitmap, scaling/translating automatically to fill the destination rectangle." - I suspect that this might include negative scaling to fit the space where the right edge is smaller than the left although the docs do not explicitly say so.
I use GWT and I want to implement a simple MouseMoveHandler on an image.
So I simply implement it like this :
Image icon = new Image(Context.IMAGES.infoIcon());
icon.addMouseMoveHandler(new MouseMoveHandler() {
#Override
public void onMouseMove(MouseMoveEvent event) {
int left = event.getScreenX();
int top = event.getScreenY();
}
});
This is OK, but I want the event avoid transparent pixels.
Is there any way?
The only option I can think of is to draw the picture on an HTML5 canvas. Then analyze every pixels RGB values and decide whether to handle the event or not.
Canvas canvas = Canvas.createIfSupported();
Image imagePng = new Image("test.png");
if (_canvas != null) {
panel.add(canvas);
} else {
// No canvas support in this browser
panel.add(imagePng);
}
canvas.getContext2d().drawImage(ImageElement.as(imagePng.getElement()), 0, 0);
Then later in your MouseHandler code. Evaluate the position of the mouse handler on the image (x, y), then analyze the image color on this position.
int width = canvas.getOffsetWidth();
int height = canvas.getOffsetHeight();
ImageData imageData = canvas.getContext2d().getImageData(0, 0, width, height);
int r = imageData .getRedAt(x, y);
int g = imageData .getGreenAt(x, y);
int b = imageData .getBlueAt(x, y);
if (!isRGBValueTransparent(r, g, b)) {
// Do something;
}
The code can be improved, if you don't do the calculation every time but keep some kind of matrix where you store whether a pixel is transparent or not. Then just look it up in said matrix.
I am currently attempting to draw images on the screen at a regular rate like in a video game.
Unfortunately, because of the rate at which the image is moving, some frames are identical because the image has not yet moved a full pixel.
Is there a way to provide float values to Graphics2D for on-screen position to draw the image, rather than int values?
Initially here is what I had done:
BufferedImage srcImage = sprite.getImage ( );
Position imagePosition = ... ; //Defined elsewhere
g.drawImage ( srcImage, (int) imagePosition.getX(), (int) imagePosition.getY() );
This of course thresholds, so the picture doesn't move between pixels, but skips from one to the next.
The next method was to set the paint color to a texture instead and draw at a specified position. Unfortunately, this produced incorrect results that showed tiling rather than correct antialiasing.
g.setRenderingHint ( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
BufferedImage srcImage = sprite.getImage ( );
g.setPaint ( new TexturePaint ( srcImage, new Rectangle2D.Float ( 0, 0, srcImage.getWidth ( ), srcImage.getHeight ( ) ) ) );
AffineTransform xform = new AffineTransform ( );
xform.setToIdentity ( );
xform.translate ( onScreenPos.getX ( ), onScreenPos.getY ( ) );
g.transform ( xform );
g.fillRect(0, 0, srcImage.getWidth(), srcImage.getHeight());
What should I do to achieve the desired effect of subpixel rendering of an Image in Java?
You can use a BufferedImage and AffineTransform, draw to the buffered image, then draw the buffered image to the component in the paint event.
/* overrides the paint method */
#Override
public void paint(Graphics g) {
/* clear scene buffer */
g2d.clearRect(0, 0, (int)width, (int)height);
/* draw ball image to the memory image with transformed x/y double values */
AffineTransform t = new AffineTransform();
t.translate(ball.x, ball.y); // x/y set here, ball.x/y = double, ie: 10.33
t.scale(1, 1); // scale = 1
g2d.drawImage(image, t, null);
// draw the scene (double percision image) to the ui component
g.drawImage(scene, 0, 0, this);
}
Check my full example here: http://pastebin.com/hSAkYWqM
You can composite the image yourself using sub-pixel accuracy, but it's more work on your part. Simple bilinear interpolation should work well enough for a game. Below is psuedo-C++ code for doing it.
Normally, to draw a sprite at location (a,b), you'd do something like this:
for (x = a; x < a + sprite.width; x++)
{
for (y = b; y < b + sprite.height; y++)
{
*dstPixel = alphaBlend (*dstPixel, *spritePixel);
dstPixel++;
spritePixel++;
}
dstPixel += destLineDiff; // Move to start of next destination line
spritePixel += spriteLineDiff; // Move to start of next sprite line
}
To do sub-pixel rendering, you do the same loop, but account for the sub-pixel offset like so:
float xOffset = a - floor (a);
float yOffset = b - floor (b);
for (x = floor(a), spriteX = 0; x < floor(a) + sprite.width + 1; x++, spriteX++)
{
for (y = floor(b), spriteY = 0; y < floor (b) + sprite.height + 1; y++, spriteY++)
{
spriteInterp = bilinearInterp (sprite, spriteX + xOffset, spriteY + yOffset);
*dstPixel = alphaBlend (*dstPixel, spriteInterp);
dstPixel++;
spritePixel++;
}
dstPixel += destLineDiff; // Move to start of next destination line
spritePixel += spriteLineDiff; // Move to start of next sprite line
}
The bilinearInterp() function would look something like this:
Pixel bilinearInterp (Sprite* sprite, float x, float y)
{
// Interpolate the upper row of pixels
Pixel* topPtr = sprite->dataPtr + ((floor (y) + 1) * sprite->rowBytes) + floor(x) * sizeof (Pixel);
Pixel* bottomPtr = sprite->dataPtr + (floor (y) * sprite->rowBytes) + floor (x) * sizeof (Pixel);
float xOffset = x - floor (x);
float yOffset = y - floor (y);
Pixel top = *topPtr + ((*(topPtr + 1) - *topPtr) * xOffset;
Pixel bottom = *bottomPtr + ((*(bottomPtr + 1) - *bottomPtr) * xOffset;
return bottom + (top - bottom) * yOffset;
}
This should use no additional memory, but will take additional time to render.
I successfully solved my problem after doing something like lawrencealan proposed.
Originally, I had the following code, where g is transformed to a 16:9 coordinate system before the method is called:
private void drawStar(Graphics2D g, Star s) {
double radius = s.getRadius();
double x = s.getX() - radius;
double y = s.getY() - radius;
double width = radius*2;
double height = radius*2;
try {
BufferedImage image = ImageIO.read(this.getClass().getResource("/images/star.png"));
g.drawImage(image, (int)x, (int)y, (int)width, (int)height, this);
} catch (IOException ex) {
Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex);
}
}
However, as noted by the questioner Kaushik Shankar, turning the double positions into integers makes the image "jump" around, and turning the double dimensions into integers makes it scale "jumpy" (why the hell does g.drawImage not accept doubles?!). What I found working for me was the following:
private void drawStar(Graphics2D g, Star s) {
AffineTransform originalTransform = g.getTransform();
double radius = s.getRadius();
double x = s.getX() - radius;
double y = s.getY() - radius;
double width = radius*2;
double height = radius*2;
try {
BufferedImage image = ImageIO.read(this.getClass().getResource("/images/star.png"));
g.translate(x, y);
g.scale(width/image.getWidth(), height/image.getHeight());
g.drawImage(image, 0, 0, this);
} catch (IOException ex) {
Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex);
}
g.setTransform(originalTransform);
}
Seems like a stupid way of doing it though.
Change the resolution of your image accordingly, there's no such thing as a bitmap with sub-pixel coordinates, so basically what you can do is create an in memory image larger than what you want rendered to the screen, but allows you "sub-pixel" accuracy.
When you draw to the larger image in memory, you copy and resample that into the smaller render visible to the end user.
For example: a 100x100 image and it's 50x50 resized / resampled counterpart:
See: http://en.wikipedia.org/wiki/Resampling_%28bitmap%29
I've been trying to chop up a larger picture and use it as tiles for a game. I had the program working using fillRect() to simulate the image. However, when I replace the fillRect code, it crashes. Here is what I've been using:
buffer.drawImage(section[i][j].getSectionImage(i, j),
sectionSize * i + OFFSETx,
sectionSize * j + OFFSETy,
this);
public class Section{
private static ImageIcon ii;
private static Image mainImage;
private Image sectionImage;
public Section(){
if (ii == null){
ii = new ImageIcon(this.getClass().getResource("images/Mossy_rocks.png"));
mainImage = ii.getImage();
}
}
public Image getSectionImage(int x, int y){
sectionImage = createImage(new FilteredImageSource(mainImage.getSource(),
new CropImageFilter(1,1,20,20))); //test values
return sectionImage;
}
}
I tried "extends JApplet/JFrame/JComponent" for the Section Class, but it didn't seem to help.
Edit: I would also like to mention that if I would just return mainImage from getSectionImage(), I get the image. I think the biggest problem is the rest of that function... but I'm not sure, so I included everything I needed to add when replacing fillRect().
I found a much better way to crop this image. Here is how I did it:
screenImage.drawImage(Image sprite,
int (x position on screen),
int (y position on screen),
int (x position on screen + width),
int (y position on screen + height),
int (x position from sprite),
int (y position from sprite),
int (x position from sprite + width),
int (y position from sprite + height),
null);