I'm trying to implement a way to sample a texture in Java. I want this to work the same way it would by using GLSL's "texture" function. I want the neighboring pixels to be taken into account when the color is calcuated.
For example, say I use a float to get the image color at a certain pixel, and the number falls between two pixels. How can I calculate a mix of the neighboring pixels? Here's an image showing my goal.
Is there an easy way to do this using java's BufferedImage class?
Here's the code I have so far, it only works for the x position at the moment.
public static final java.awt.Color getColor(final BufferedImage image, final float x, final int y) {
final float imageX = x * image.getWidth();
final float decimal = imageX % 1f;
final java.awt.Color left = new java.awt.Color(image.getRGB(Maths.clamp((int) imageX - 1, 0, image.getWidth() - 1), y));
final java.awt.Color center = new java.awt.Color(image.getRGB(Maths.clamp((int) imageX, 0, image.getWidth() - 1), y));
final java.awt.Color right = new java.awt.Color(image.getRGB(Maths.clamp((int) imageX + 1, 0, image.getWidth() - 1), y));
if (decimal == 0.5f) return center;
if (decimal < 0.5f) {
final float distanceFromCenter = 0.5f - decimal;
final float distanceFromLeft = decimal + 0.5f;
final Vector3f leftColor = new Vector3f(left.getRed() / 255f, left.getGreen() / 255f, left.getBlue() / 255f);
final Vector3f centerColor = new Vector3f(center.getRed() / 255f, center.getGreen() / 255f, center.getBlue() / 255f);
leftColor.scale(1f - distanceFromLeft);
centerColor.scale(1f - distanceFromCenter);
final Vector3f color = Vector3f.add(leftColor, centerColor, null);
return new java.awt.Color(color.getX(), color.getY(), color.getZ());
} else {
final float distanceFromCenter = decimal - 0.5f;
final float distanceFromRight = 1f - decimal + 0.5f;
final Vector3f rightColor = new Vector3f(right.getRed() / 255f, right.getGreen() / 255f, right.getBlue() / 255f);
final Vector3f centerColor = new Vector3f(center.getRed() / 255f, center.getGreen() / 255f, center.getBlue() / 255f);
rightColor.scale(1f - distanceFromRight);
centerColor.scale(1f - distanceFromCenter);
final Vector3f color = Vector3f.add(rightColor, centerColor, null);
return new java.awt.Color(color.getX(), color.getY(), color.getZ());
}
}
hope this code help, if you use this appropriately. This function extract pixels from buffered image and we can replace it what ever we want:
private BufferedImage changeColor(BufferedImage image, int srcColor, int replaceColor)
{
BufferedImage destImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = destImage.createGraphics();
g.drawImage(image, null, 0, 0);
g.dispose();
for (int width = 0; width < image.getWidth(); width++)
{
for (int height = 0; height < image.getHeight(); height++)
{
if (destImage.getRGB(width, height) == srcColor)
{
destImage.setRGB(width, height, replaceColor);
}
}
}
return destImage;
}
Related
I'm facing an issue that I'm drawing an image with PDFbox, when I draw it without rotation I got the correct dimensions but when I draw it with rotation the dimensions will be smaller.
Here are the images and the code:
Without rotation
With rotation 45 degree
The code:
public void placeImage(String imagePath, float x, float y, float rotation, float width, float height) throws Exception {
File imageFile;
boolean isTemp = false;
if (imagePath.contains("http")) {
imageFile = saveImageFromUrlToTempFile(imagePath);
isTemp = true;
} else {
imageFile = new File(imagePath);
}
PDImageXObject imageToPutInside = PDImageXObject.createFromFileByExtension(imageFile, outputDocument);
float newWidth;
float newHeight;
float imageHeight = imageToPutInside.getHeight();
float imageWidth = imageToPutInside.getWidth();
if (imageHeight > imageWidth) {
newHeight = height > imageHeight ? imageHeight : height;
newWidth = (newHeight * imageToPutInside.getWidth()) / imageToPutInside.getHeight();
} else {
newWidth = width > imageWidth ? imageWidth : width;
newHeight = (newWidth * imageToPutInside.getHeight()) / imageToPutInside.getWidth();
}
x = x + ((width - newWidth) / 2);
y = y + ((height - newHeight) / 2);
y = calculateYAxisFromTop(y) - newHeight;
outputPageContentStream.saveGraphicsState();
AffineTransform transform = new AffineTransform(newWidth, 0, 0, newHeight, x, y);
if(rotation != 0) {
transform.concatenate(AffineTransform.getRotateInstance(rotation, 0.5, 0.5));
}
outputPageContentStream.drawImage(imageToPutInside, new Matrix(transform));
outputPageContentStream.restoreGraphicsState();
if (isTemp) {
imageFile.delete();
}
}
Could you please help me with this?
I have just begin to create some custom views in Android but I am facing problem while drawing bitmap (arrows) outside the circle.
Here is my code:
Canvas osCanvas = new Canvas(windowFrame); // Create a canvas to draw onto the new image
RectF outerRectangle = new RectF(0, 0, getWidth(), getHeight());
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); // Anti alias allows for smooth corners
paint.setColor(Color.parseColor("#FAFAFA")); // This is the color of your activity background
osCanvas.drawRect(outerRectangle, paint);
final Paint stroke = new Paint();
//paint.setColor(Color.TRANSPARENT); // An obvious color to help debugging
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT)); // A out B http://en.wikipedia.org/wiki/File:Alpha_compositing.svg
float centerX = getWidth() / 2;
float centerY = getHeight() / 2;
double rad = Math.min(getWidth(), getHeight()) / 2.5 - menuInnerPadding;
float radius = (float) rad;
stroke.setColor(Color.parseColor("#999999"));
stroke.setStyle(Paint.Style.STROKE);
stroke.setAntiAlias(true);
stroke.setStrokeWidth(5f);
osCanvas.drawCircle(centerX ,
centerY , radius - stroke.getStrokeWidth() +5f, stroke);
for(int i=0;i<elements.size();i++){
double angle =0;
if(i==0){
angle = startAngle;
}else{
angle = startAngle+(i * ((2 * Math.PI) / elements.size()));
}
elements.get(i).x = (int) (centerX + Math.cos(angle)*(radius));
elements.get(i).y = (int) (centerY + Math.sin(angle)*(radius));
float ang = (float) Math.cos(angle)*(radius);
Path path = new Path();
path.addCircle(elements.get(i).x,elements.get(i).y, radius, Path.Direction.CW);
if(BLINKER_ID == i){
if(blinkerPain != null){
osCanvas.drawBitmap(elements.get(i).bitmap,elements.get(i).x,elements.get(i).y,blinkerPain);
blinkerPain = null;
}
}else{
// here i am drawing the red arrows (bitmap images) But it's not fitting outside the circle.
osCanvas.drawBitmap(elements.get(i).bitmap,elements.get(i).x ,elements.get(i).y ,textPaint);
}
}
Here is my output
I think issue is with the padding.
I did code new write.
You should check this code.
#Override
protected void onDraw(Canvas canvas) {
Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth( 2 );
paint.setStyle(Paint.Style.STROKE );
canvas.drawColor(Color.WHITE);
float x = 400;
float y = 400;
float r = 200;
canvas.drawCircle( x , y , r , paint );
Bitmap icon = BitmapFactory.decodeResource(getContext().getResources(), R.mipmap.ic_launcher);
int length = 8;
float radian = (float)(Math.PI * 2f) / length;
for( int i = 0; i<length; i++ ) {
drawBitmap( canvas , icon , x , y , r, radian * i);
}
}
private void drawBitmap( Canvas canvas, Bitmap bitmap, float pivotX, float pivotY, float radius, float radian ) {
Matrix m = new Matrix();
m.postTranslate( -bitmap.getWidth()/2 , -bitmap.getHeight()/2 );
m.postRotate( (float)(radian * 180 / Math.PI) + 90 );
float drawHeight = radius + (bitmap.getHeight()/2);
m.postTranslate( ((float)Math.cos( radian ) * drawHeight) + pivotX
, ((float)Math.sin( radian ) * drawHeight) + pivotY );
canvas.drawBitmap( bitmap , m , null );
}
Is there any way to bend a BufferedImage in Java?
I thought that if I crop the image into smaller pieces and rotate them then I would essentially bend the image, but it doesn't seem to work.
Here is the method I created:
/**
* This is a recursive method that will accept an image the point where the bending will start and the point where the bending will end, as well as the angle of bending
*
* #param original:the original image
* #param startingPoint: the point where the bending should start
* #param endingPoint: the point where the bending should end
* #param radiands: the angle
* #return the bent image
*/
public static BufferedImage getBentImage(BufferedImage original, int startingPoint, int endingPoint, double radians) {
if (startingPoint >= endingPoint)
return original;
int type = BufferedImage.TYPE_INT_ARGB;
int width = original.getWidth();
int height = original.getHeight();
BufferedImage crop = original.getSubimage(0, 0, startingPoint, height);
BufferedImage crop0 = original.getSubimage(startingPoint, 0, width - startingPoint, height);
BufferedImage bendCrop = new BufferedImage(width, height, type);
BufferedImage image = new BufferedImage(width, height, type);
AffineTransform rotation = new AffineTransform();
rotation.translate(0, 0);
rotation.rotate(radians);
Graphics2D g = bendCrop.createGraphics();
g.drawImage(crop0, rotation, null);
g.dispose();
g = image.createGraphics();
g.drawImage(crop, 0, 0, null);
g.drawImage(bendCrop, startingPoint, 0, null);
g.dispose();
return getBentImage(image, startingPoint + 1, endingPoint, radians);
}
This is the original Image:
And this is the result of this getBentImage(image, 200, 220, Math.toRadians(1)):
I was expecting something closer to:
Any ideas on how to actually implement a getBentImage() method?
As suggested in the comments, a simple approach is to divide the image into 3 parts:
Identical to the original.
Bent according to the bending transformation.
Constant diagonal continuation.
Here is a quick and a bit messy example that shows the original shape and the resulting shape below it. I just used a label icon for the images instead of doing custom painting. (Also I didn't adhere to the Java naming conventions with final variables because it's math and not typical coding.)
Since there are quite a few variables in the calculation code, I added a sketch at the end that shows what the variables represent.
public class Main extends JFrame {
static BufferedImage image;
public static void main(String[] args) {
try {
image = ImageIO.read(ClassLoader.getSystemResource("img.png"));
} catch (IOException e) {
e.printStackTrace();
}
new Main();
}
public Main() {
getContentPane().setLayout(new BorderLayout(5, 10));
BufferedImage img2 = transform(15, 100, 300);
JLabel label1 = new JLabel(new ImageIcon(image));
label1.setHorizontalAlignment(JLabel.LEFT);
label1.setOpaque(true);
label1.setBackground(Color.YELLOW);
add(label1, BorderLayout.NORTH);
JLabel label2 = new JLabel(new ImageIcon(img2));
label2.setHorizontalAlignment(JLabel.LEFT);
label2.setOpaque(true);
label2.setBackground(Color.CYAN);
add(label2);
pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
static BufferedImage transform(int t, int x1, int x2) {
final double TH = Math.toRadians(t);
final int D = x2 - x1;
final int W = image.getWidth();
final int H = image.getHeight();
final int dD = (int) (D / (2 * TH) * Math.sin(2 * TH));
final int dH = (int) (D / TH * Math.pow(Math.sin(TH), 2));
final int pH = (int) ((W - x2) * Math.tan(2 * TH));
final int width = W - (D - dD);
final int height = (int) (H + dH + pH);
System.out.println(W + " " + H + " -> " + width + " " + height);
BufferedImage img2 = new BufferedImage(width, height, image.getType());
for (int x = 0; x < x1; x++) {
for (int y = 0; y < H; y++) {
int rgb = image.getRGB(x, y);
img2.setRGB(x, y, rgb);
}
}
for (int x = x1; x < x2; x++) {
for (int y = 0; y < H; y++) {
int rgb = image.getRGB(x, y);
int dx = (int) (D / (2 * TH) * Math.sin(2 * (x-x1) * TH / D));
int dy = (int) (D / TH * Math.pow(Math.sin((x-x1) * TH / D), 2));
img2.setRGB(x1 + dx, y + dy, rgb);
}
}
for (int x = x2; x < W; x++) {
for (int y = 0; y < H; y++) {
int rgb = image.getRGB(x, y);
int dp = (int) ((x - x2) * Math.tan(2 * TH));
img2.setRGB(x - (D - dD), y + dH + dp, rgb);
}
}
return img2;
}
}
As for the calculations, I'll leave it for you as homework; it's just geometry/trigonometry which belongs on Math.SE more than on SO. If you can't figure it out I'll give you a direction.
Note that this method might not be fast at all and could certainly be optimized, I'll leave that to you also. Oh, and rounding doubles to ints carelessly, so the result is not pixel-perfect.
I dont know what you mean by bending but essentially you have a rectangle and you break one piece of it and rotate it:
so the algorithm is as follows:
rotate line(x, 0, width-1, 0)
rotate line(x, height-1, width-1, height-1)
connect the pieces
So essentially you are looking for rotate line.
When I create a Body and draw a Texture where that body is, if the angle is set to 0.0f, it appears exactly where expected. Upright and at the centre x and y of the Player (the black circle). When this angle changes, the x and y of the texture appear to be completely off. In fact they tend to be off the screen as I can only see them visibly sometimes as they fall with the gravity.
Here's the method where I create a new bullet:
private void shoot(final float delta) {
gameTime += delta;
final float timeSinceLastShot = gameTime - lastBulletShotTime;
//5 bullets a second, kerpow
if (timeSinceLastShot > 0.2f) {
lastBulletShotTime = gameTime;
shot.play();
final float shotX = player.getX() + ((player.getWidth() / 2) - (bulletTexture.getWidth() / 2));
final float shotY = player.getY() + ((player.getHeight() / 2) - (bulletTexture.getHeight() / 2));
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.DynamicBody;
bodyDef.position.set(shotX, shotY);
Body body = world.createBody(bodyDef);
float angle = player.getRotation() * MathUtils.degreesToRadians;
body.setTransform(body.getPosition().x, body.getPosition().y, angle);
System.out.println("angle rad: " + angle);
bullets.add(body);
PolygonShape shape = new PolygonShape();
shape.setAsBox(bulletTexture.getWidth() / 2, bulletTexture.getHeight() / 2);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = shape;
fixtureDef.density = 1f;
Fixture fixture = body.createFixture(fixtureDef);
shape.dispose();
}
}
And here's the part of the render method where I loop and draw the bullets:
for (Body bullet : bullets) {
final float x = bullet.getPosition().x;
final float y = bullet.getPosition().y;
final float originX = x + (bulletTexture.getWidth() / 2);
final float originY = y + (bulletTexture.getHeight() / 2);
final float width = bulletTexture.getWidth();
final float height = bulletTexture.getHeight();
final float scaleX = 1.0f;
final float scaleY = 1.0f;
final float rotation = bullet.getAngle() * MathUtils.radiansToDegrees;
final int sourceX = 0;
final int sourceY = 0;
final int sourceWidth = bulletTexture.getTextureData().getWidth();
final int sourceHeight = bulletTexture.getTextureData().getHeight();
final boolean flipX = false;
final boolean flipY = false;
batch.draw(bulletTexture,
x, y,
originX, originY,
width, height,
scaleX, scaleY,
rotation,
sourceX, sourceY,
sourceWidth, sourceHeight,
flipX, flipY);
}
Any tips for what I'm doing wrong? I want the bullets to always start at the centre of the Player Circle, but also at the correct angle. I then intend to add some velocity so they 'shoot'.
The full code can be found here on Github https://github.com/SamRuffleColes/PlayerCircle/blob/master/core/src/es/rufflecol/sam/playercircle/PlayerCircle.java
I've also attached a screenshot, everything has fallen a little with the gravity between shooting and taking this, but the bottom left bullets appeared in the right place initially, while all the others have fallen from off the screen (some others which I 'shot' were never visible).
My mistake was with the originX and originY. Corrected to the below, it now works:
final float originX = bulletTexture.getWidth() / 2;
final float originY = bulletTexture.getHeight() / 2;
I am having a bit of an issue, I have a rectangle from contour detection but I want to re-size the rectangle whilst maintaining the centroid I noticed when I resize it, its obtaining a new centroid, this is because I want it to be at the center of my object not but when re-sized its moving to the sizes here is an image before resize, my centroid is marked by the red and blue markers
Before resize:
After resize:
Here is how I am resizing:
private static Rect rotateRect(Rect rect, int heightPercentage, int widthPercetange)
{
int originalX = 0;
int originalY = 0;
originalX = rect.x;
originalY = rect.y;
rect.height = resizeObject(rect.height, heightPercentage);
rect.width = resizeObject(rect.width, widthPercetange);
rect.x = originalX;
rect.y = originalY;
return rect;
}
private static int resizeObject(int resize, int percentage)
{
return (int)(resize *(percentage/100.0f));
}
You simply need to add to x and y coordinates half the amount you remove from width and height.
private static Rect rotateRect(Rect rect, int heightPercentage, int widthPercetange)
{
int rwidth = rect.width;
int rheight = rect.height;
rect.width = Math.round((rect.width * widthPercetange) / 100.0f);
rect.height = Math.round((rect.height * heightPercentage) / 100.0f);
rect.x += (rwidth - rect.width) / 2;
rect.y += (rheight - rect.height) / 2;
return rect;
}