SWT Transparent Button/Label - java

I have researched this for days. It appears that most folks want to place buttons on a transparent canvas or shell. I need to place transparent clickable objects over a canvas/component. In my testing I find that if I don't attempt to put the object on the canvas it simply never displays.
In the final project the application will be showing animated objects with a number of controls that I plan to use images for.
In the example I am trying to work out I have taken Snipped195 which displays a turning torus. I am attempting to place an image label over the torus such that as the torus turns it will show through the area of the label that is transparent. I have set up a gif file that is a red plus sign and has a transparent background. I also picked up some code (can't remember where it came from now) that is part of the paintControl method that looks for transparent pixels and builds a Region object. The region object obviously is doing what it needs to do to define where the image goes. Do I need to apply the region somehow to the image instead of the canvas?
At first when I tried to do this I did get the image displayed. However where the transparent areas where it displayed white. After implementing the paintControl code it at least handled the transparent area properly. Now I need to get the actual image content to display.
I built an object to take care of the image label. I called it TransparentImageLabel. It looks like:
public class TransparentImageLabel extends Canvas {
private Image labelImage;
public TransparentImageLabel(Composite parent, Image image, int style) {
super(parent, style);
this.labelImage = image;
addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
TransparentImageLabel.this.widgetDisposed(e);
}
});
addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
TransparentImageLabel.this.paintControl(e);
}
});
}
private void widgetDisposed(DisposeEvent e) {
}
private void paintControl(PaintEvent event) {
System.out.println("at paint control");
ImageData imgData = this.labelImage.getImageData();
Region region = new Region();
if (imgData.alphaData != null) {
Rectangle pixel = new Rectangle(0, 0, 1, 1);
for (int y = 0; y < imgData.height; y++) {
for (int x = 0; x < imgData.width; x++) {
if (imgData.getAlpha(x, y) == 255) {
pixel.x = imgData.x + x;
pixel.y = imgData.y + y;
region.add(pixel);
}
}
}
} else {
ImageData mask = imgData.getTransparencyMask();
Rectangle pixel = new Rectangle(0, 0, 1, 1);
for (int y = 0; y < mask.height; y++) {
for (int x = 0; x < mask.width; x++) {
if (mask.getPixel(x, y) != 0) {
pixel.x = imgData.x + x;
pixel.y = imgData.y + y;
region.add(pixel);
}
}
}
}
this.setRegion(region);
event.gc.drawImage(labelImage, this.getBounds().x, this.getBounds().y);
region.dispose();
}
}
After adding this to Snipped195 the code looks like:
public class Snippet195 {
private Image redPlus;
static void drawTorus(float r, float R, int nsides, int rings) {
float ringDelta = 2.0f * (float) Math.PI / rings;
float sideDelta = 2.0f * (float) Math.PI / nsides;
float theta = 0.0f, cosTheta = 1.0f, sinTheta = 0.0f;
for (int i = rings - 1; i >= 0; i--) {
float theta1 = theta + ringDelta;
float cosTheta1 = (float) Math.cos(theta1);
float sinTheta1 = (float) Math.sin(theta1);
GL11.glBegin(GL11.GL_QUAD_STRIP);
float phi = 0.0f;
for (int j = nsides; j >= 0; j--) {
phi += sideDelta;
float cosPhi = (float) Math.cos(phi);
float sinPhi = (float) Math.sin(phi);
float dist = R + r * cosPhi;
GL11.glNormal3f(cosTheta1 * cosPhi, -sinTheta1 * cosPhi, sinPhi);
GL11.glVertex3f(cosTheta1 * dist, -sinTheta1 * dist, r * sinPhi);
GL11.glNormal3f(cosTheta * cosPhi, -sinTheta * cosPhi, sinPhi);
GL11.glVertex3f(cosTheta * dist, -sinTheta * dist, r * sinPhi);
}
GL11.glEnd();
theta = theta1;
cosTheta = cosTheta1;
sinTheta = sinTheta1;
}
}
private Snippet195() {
final Display display = new Display();
Shell shell = new Shell(display, SWT.NO_REDRAW_RESIZE);
shell.setLayout(new FillLayout());
Composite comp = new Composite(shell, SWT.NONE);
comp.setLayout(new FillLayout());
GLData data = new GLData();
data.doubleBuffer = true;
redPlus = new Image(shell.getDisplay(), new ImageData(
Snippet237.class.getResourceAsStream("/red-plus.png")));
final GLCanvas canvas = new GLCanvas(comp, SWT.NONE, data);
canvas.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
e.gc.setAlpha(15);
e.gc.drawImage(Snippet195.this.redPlus, 0, 0);
}
});
canvas.setCurrent();
try {
GLContext.useContext(canvas);
} catch (LWJGLException e) {
e.printStackTrace();
}
canvas.addListener(SWT.Resize, new Listener() {
public void handleEvent(Event event) {
Rectangle bounds = canvas.getBounds();
float fAspect = (float) bounds.width / (float) bounds.height;
canvas.setCurrent();
try {
GLContext.useContext(canvas);
} catch (LWJGLException e) {
e.printStackTrace();
}
GL11.glViewport(0, 0, bounds.width, bounds.height);
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GLU.gluPerspective(45.0f, fAspect, 0.5f, 400.0f);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glLoadIdentity();
}
});
GL11.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
GL11.glColor3f(1.0f, 0.0f, 0.0f);
GL11.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST);
GL11.glClearDepth(1.0);
GL11.glLineWidth(2);
GL11.glEnable(GL11.GL_DEPTH_TEST);
TransparentImageLabel redPlusLabel = new TransparentImageLabel(canvas,
redPlus, SWT.NONE);
redPlusLabel.setSize(48, 48);
redPlusLabel.setLocation(500, 200);
shell.setText("SWT/LWJGL Example");
shell.setSize(880, 720);
shell.open();
final Runnable run = new Runnable() {
int rot = 0;
public void run() {
if (!canvas.isDisposed()) {
canvas.setCurrent();
try {
GLContext.useContext(canvas);
} catch (LWJGLException e) {
e.printStackTrace();
}
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT
| GL11.GL_DEPTH_BUFFER_BIT);
GL11.glClearColor(.3f, .5f, .8f, 1.0f);
GL11.glLoadIdentity();
GL11.glTranslatef(0.0f, 0.0f, -10.0f);
float frot = rot;
GL11.glRotatef(0.15f * rot, 2.0f * frot, 10.0f * frot, 1.0f);
GL11.glRotatef(0.3f * rot, 3.0f * frot, 1.0f * frot, 1.0f);
rot++;
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
GL11.glColor3f(0.9f, 0.9f, 0.9f);
drawTorus(1, 1.9f + ((float) Math.sin((0.004f * frot))), 25, 75);
canvas.swapBuffers();
display.asyncExec(this);
}
}
};
canvas.addListener(SWT.Paint, new Listener() {
public void handleEvent(Event event) {
run.run();
}
});
display.asyncExec(run);
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
public static void main(String[] args) {
new Snippet195();
}
}
I have to be close. The areas of the image that are defined as transparent are being drawn as transparent. But I'm not getting anything but a white plus instead of the red that is in the image.

The problem is in your TransparentImageLabel#paintControl(..) method. Correct the second last line to the following:
event.gc.drawImage(labelImage, 0, 0);
Since you are drawing within the context of the canvas so the coordinates you specify for location should be relative to that Canvas. You are currently using the location of canvas which is returned relative to it's parent.

Related

Scaling textures in OpenGL

I wrote a method that makes it easy to draw images when you set the size of a normal picture, there is no problem, but when you try to draw an image with alpha channel image is smaller than it should be.
To give you a clearer view two pictures:
Knight left is theoretically the same size as a green square.
In the second picture you can see it clearly. I do not know where the problem lies, the usual images you can set the size of the problem but without including the alpha channel is not.
#SuppressWarnings("unused")
public class Start {
float x = 400, y = 300;
float rotation = 0;
/** time at last frame */
long lastFrame;
/** frames per second */
int fps;
/** last fps time */
long lastFPS;
/** is VSync Enabled */
boolean vsync;
public void start() {
try {
Display.setDisplayMode(new DisplayMode(1280, 720));
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
System.exit(0);
}
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 1280, 720, 0, 1, -1);
glMatrixMode(GL_MODELVIEW);
glEnable(GL_TEXTURE_2D);
getDelta(); // call once before loop to initialise lastFrame
lastFPS = getTime(); // call before loop to initialise fps timer
Texture tex = LoadTexture("res/1.png", "PNG");
Texture t2 = LoadTexture("res/image.png", "PNG");
Texture t3 = LoadTexture("res/atack1/1.png", "PNG");
while (!Display.isCloseRequested()) {
int delta = getDelta();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
update(delta);
DrawImage(t3, 0, 0, 100, 120);
glEnd();
Display.update();
Display.sync(60); // cap fps to 60fps
}
Display.destroy();
System.exit(0);
}
public void update(int delta) {
// rotate quad
rotation += 0.15f * delta;
if (Keyboard.isKeyDown(Keyboard.KEY_LEFT)) x -= 0.35f * delta;
if (Keyboard.isKeyDown(Keyboard.KEY_RIGHT)) x += 0.35f * delta;
if (Keyboard.isKeyDown(Keyboard.KEY_UP)) y -= 0.35f * delta;
if (Keyboard.isKeyDown(Keyboard.KEY_DOWN)) y += 0.35f * delta;
while (Keyboard.next()) {
if (Keyboard.getEventKeyState()) {
if (Keyboard.getEventKey() == Keyboard.KEY_F) {
setDisplayMode(1280, 720, !Display.isFullscreen());
}
else if (Keyboard.getEventKey() == Keyboard.KEY_V) {
vsync = !vsync;
Display.setVSyncEnabled(vsync);
}
}
}
// keep quad on the screen
if (x < 0) x = 0;
if (x > 800) x = 800;
if (y < 0) y = 0;
if (y > 600) y = 600;
updateFPS(); // update FPS Counter
}
/**
* Set the display mode to be used
*
* #param width The width of the display required
* #param height The height of the display required
* #param fullscreen True if we want fullscreen mode
*/
public void setDisplayMode(int width, int height, boolean fullscreen) {
// return if requested DisplayMode is already set
if ((Display.getDisplayMode().getWidth() == width) &&
(Display.getDisplayMode().getHeight() == height) &&
(Display.isFullscreen() == fullscreen)) {
return;
}
try {
DisplayMode targetDisplayMode = null;
if (fullscreen) {
DisplayMode[] modes = Display.getAvailableDisplayModes();
int freq = 0;
for (int i=0;i<modes.length;i++) {
DisplayMode current = modes[i];
if ((current.getWidth() == width) && (current.getHeight() == height)) {
if ((targetDisplayMode == null) || (current.getFrequency() >= freq)) {
if ((targetDisplayMode == null) || (current.getBitsPerPixel() > targetDisplayMode.getBitsPerPixel())) {
targetDisplayMode = current;
freq = targetDisplayMode.getFrequency();
}
}
// if we've found a match for bpp and frequence against the
// original display mode then it's probably best to go for this one
// since it's most likely compatible with the monitor
if ((current.getBitsPerPixel() == Display.getDesktopDisplayMode().getBitsPerPixel()) &&
(current.getFrequency() == Display.getDesktopDisplayMode().getFrequency())) {
targetDisplayMode = current;
break;
}
}
}
} else {
targetDisplayMode = new DisplayMode(width,height);
}
if (targetDisplayMode == null) {
System.out.println("Failed to find value mode: "+width+"x"+height+" fs="+fullscreen);
return;
}
Display.setDisplayMode(targetDisplayMode);
Display.setFullscreen(fullscreen);
} catch (LWJGLException e) {
System.out.println("Unable to setup mode "+width+"x"+height+" fullscreen="+fullscreen + e);
}
}
/**
* Calculate how many milliseconds have passed
* since last frame.
*
* #return milliseconds passed since last frame
*/
public int getDelta() {
long time = getTime();
int delta = (int) (time - lastFrame);
lastFrame = time;
return delta;
}
/**
* Get the accurate system time
*
* #return The system time in milliseconds
*/
public long getTime() {
return (Sys.getTime() * 1000) / Sys.getTimerResolution();
}
/**
* Calculate the FPS and set it in the title bar
*/
public void updateFPS() {
if (getTime() - lastFPS > 1000) {
Display.setTitle("FPS: " + fps);
fps = 0;
lastFPS += 1000;
}
fps++;
}
public void initGL() {
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, 800, 0, 600, 1, -1);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
}
public static void main(String[] argv) {
Start fullscreenExample = new Start();
fullscreenExample.start();
}
public static void DrawImage(Texture texture,float x,float y, float width, float height){
if(texture != null){
texture.bind();
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
//set transparency
glColor4f(1, 1, 1,1);
//
glTranslatef(x, y, 0);
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex2f(0, 0);
glTexCoord2f(1, 0);
glVertex2f(width, 0);
glTexCoord2f(1, 1);
glVertex2f(width, height);
glTexCoord2f(0, 1);
glVertex2f(0, height);
glEnd();
glLoadIdentity();
}
}
public static Texture LoadTexture(String path,String fileType){
Texture tex = null;
InputStream in = ResourceLoader.getResourceAsStream(path);
try {
tex = TextureLoader.getTexture(fileType, in);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return tex;
}
}
It looks like the images are different sizes but you're applying the full 0-1 texcoord on both quads. This will use the full width and height of the texture as you've loaded it.
The way to fix this is to determine how many pixels the knight sprite actually is and use that to calculate a value between 0 and 1 for your texcoord.
For example, if the knight width is 60 pixels and the full image width is 100 pixels, your X texcoord would be (60.0f / 100.0f) which is 0.6f. Do the same thing for the Y axis.
Here's a great tutorial on OpenGL textures that may also help clear a few other things up for you: https://open.gl/textures

Strange PolyBatch Positioning in libGDX

Excuse the basic question, just getting into the guts of LibGDX
I'm creating a radial bar to show my countdown timer
I've found some code that does what I need it to, the problem is the radial sprite's positioning. I can't seem to get it to center in the Image object (Since it seems to be ignoring the Image's local coordinates and is defaulting to the stage's) so 0,0 places it close to the bottom left of my screen.
I've tried using a localtoStage and vice versa to calculate the correct positions, but that doesn't seem to give me the right values either.
Please advise
package com.goplayplay.klpoker.CSS.Classes;
import com.badlogic.gdx.graphics.g2d.*;
import com.badlogic.gdx.math.EarClippingTriangulator;
import com.badlogic.gdx.math.Intersector;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.utils.ShortArray;
public class ProgressCircle extends Image {
TextureRegion texture;
PolygonSpriteBatch polyBatch;
Vector2 center;
Vector2 centerTop;
Vector2 leftTop;
Vector2 leftBottom;
Vector2 rightBottom;
Vector2 rightTop;
Vector2 progressPoint;
float[] fv;
IntersectAt intersectAt;
public ProgressCircle(TextureRegion region, PolygonSpriteBatch polyBatch) {
super(region);
this.texture = region;
this.polyBatch = polyBatch;
center = new Vector2(this.getWidth() / 2, this.getHeight() / 2);
centerTop = new Vector2(this.getWidth() / 2, this.getHeight());
leftTop = new Vector2(0, this.getHeight());
leftBottom = new Vector2(0, 0);
rightBottom = new Vector2(this.getWidth(), 0);
rightTop = new Vector2(this.getWidth(), this.getHeight());
progressPoint = new Vector2(this.getWidth() / 2, this.getHeight() / 2);
setPercentage(0);
}
private Vector2 IntersectPoint(Vector2 line) {
Vector2 v = new Vector2();
boolean isIntersect;
//check top
isIntersect = Intersector.intersectSegments(leftTop, rightTop, center, line, v);
//check bottom
if (isIntersect) {
intersectAt = IntersectAt.TOP;
return v;
} else isIntersect = Intersector.intersectSegments(leftBottom, rightBottom, center, line, v);
//check left
if (isIntersect) {
intersectAt = IntersectAt.BOTTOM;
return v;
} else isIntersect = Intersector.intersectSegments(leftTop, leftBottom, center, line, v);
//check bottom
if (isIntersect) {
intersectAt = IntersectAt.LEFT;
return v;
} else isIntersect = Intersector.intersectSegments(rightTop, rightBottom, center, line, v);
if (isIntersect) {
intersectAt = IntersectAt.RIGHT;
return v;
} else {
intersectAt = IntersectAt.NONE;
return null;
}
}
public void setPercentage(float percent) {
//100 % = 360 degree
//==> percent % => (percent * 360 / 100) degree
float angle = convertToRadians(90); //percent = 0 => angle = -90
angle -= convertToRadians(percent * 360 / 100);
float len = this.getWidth() > this.getHeight() ? this.getWidth() : this.getHeight();
float dy = (float) (Math.sin(angle) * len);
float dx = (float) (Math.cos(angle) * len);
Vector2 line = new Vector2(center.x + dx, center.y + dy);
Vector2 v = IntersectPoint(line);
if (intersectAt == IntersectAt.TOP) {
if (v.x >= this.getWidth() / 2)
{
fv = new float[]{
center.x,
center.y,
centerTop.x,
centerTop.y,
leftTop.x,
leftTop.y,
leftBottom.x,
leftBottom.y,
rightBottom.x,
rightBottom.y,
rightTop.x,
rightTop.y,
v.x,
v.y
};
} else {
fv = new float[]{
center.x,
center.y,
centerTop.x,
centerTop.y,
v.x,
v.y
};
}
} else if (intersectAt == IntersectAt.BOTTOM) {
fv = new float[]{
center.x,
center.y,
centerTop.x,
centerTop.y,
leftTop.x,
leftTop.y,
leftBottom.x,
leftBottom.y,
v.x,
v.y
};
} else if (intersectAt == IntersectAt.LEFT) {
fv = new float[]{
center.x,
center.y,
centerTop.x,
centerTop.y,
leftTop.x,
leftTop.y,
v.x,
v.y
};
} else if (intersectAt == IntersectAt.RIGHT) {
fv = new float[]{
center.x,
center.y,
centerTop.x,
centerTop.y,
leftTop.x,
leftTop.y,
leftBottom.x,
leftBottom.y,
rightBottom.x,
rightBottom.y,
v.x,
v.y
};
} else // if (intersectAt == IntersectAt.NONE)
{
fv = null;
}
}
//
#Override
public void draw(Batch batch, float parentAlpha) {
// super.draw(batch, parentAlpha);
if (fv == null) return;
batch.end();
drawMe();
batch.begin();
}
public void drawMe() {
Vector2 acc = new Vector2();
acc.set(getWidth() / 2, getHeight() / 2);
localToStageCoordinates(acc);
EarClippingTriangulator e = new EarClippingTriangulator();
ShortArray sv = e.computeTriangles(fv);
PolygonRegion polyReg = new PolygonRegion(texture, fv, sv.toArray());
PolygonSprite poly = new PolygonSprite(polyReg);
poly.setOrigin(this.getOriginX(), this.getOriginY());
poly.setPosition(this.getX(), this.getY());
// poly.setPosition(acc.x, acc.y); //Attempting to calculate correct positioning - Doesnt work
poly.setRotation(this.getRotation());
poly.setColor(this.getColor());
polyBatch.begin();
poly.draw(polyBatch);
polyBatch.end();
}
float convertToDegrees(float angleInRadians) {
float angleInDegrees = angleInRadians * 57.2957795f;
return angleInDegrees;
}
//-----------------------------------------------------------------
float convertToRadians(float angleInDegrees) {
float angleInRadians = angleInDegrees * 0.0174532925f;
return angleInRadians;
}
public enum IntersectAt {
NONE, TOP, BOTTOM, LEFT, RIGHT
}
}
You forgot to set the camera's projection matrix on the polygon batch. You can get a copy of it from the Batch that's passed in:
public void draw(Batch batch, float parentAlpha) {
// super.draw(batch, parentAlpha);
if (fv == null) return;
batch.end();
drawMe(batch.getProjectionMatrix());
batch.begin();
}
public void drawMe(Matrix4 projection) {
polyBatch.setProjectionMatrix(projection);
//...
}
Or more simply, you can use a PolygonBatch as your Stage's batch, so you don't have to be swapping batches:
stage = new Stage(myViewport, new PolygonBatch());
//...
public void draw(Batch batch, float parentAlpha) {
// super.draw(batch, parentAlpha);
if (fv == null) return;
//don't need to call begin and end on the batch
drawMe((PolygonBatch)batch);
}
public void drawMe(PolygonBatch polyBatch) {
//...
//don't need to call begin or end on the batch
}
By the way, your drawMe method instantiates quite a few objects, some large. You should avoid this if you have more than a few actors that do this, or you'll get stutters from the GC. Try to instantiate objects only once in the constructor and reuse them.

How to adjust graphics on Swing progress indicator?

The lower source code, with the pictured example, from the post
Circular Progress Bar for Java Swing not working, is a great Swing feature.
I'd like to be able to use it with a "transparent" JFrame or glass pane
but the graphic "petals", in paint(), want to interact with the background,
so if the opacity of the background is very low, you can barely
see the "petals". Not being familiar with the Graphics2D functions there, I've taken many stabs in the dark to try to adjust the code, but no luck, so could someone who knows how those functions work,
suggest changes so that the "petals" don't interact with the background,
and start out solid white, and gradually fade, as the code does?
I also don't need any fade-in or fade-out delays, and I'm also
having difficulty with that, but if someone could just suggest
modifications for the "petals", that would be great!
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import javax.swing.*;
import javax.swing.plaf.LayerUI;
public class Loading_Test {
static final WaitLayerUI layerUI = new WaitLayerUI();
JFrame frame = new JFrame("JLayer With Animated Gif");
public Loading_Test() {
JPanel panel = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 300);
}
};
JLayer<JPanel> jlayer = new JLayer<>(panel, layerUI);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(jlayer);
frame.pack();
frame.setVisible(true);
layerUI.start();
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Loading_Test loading_Test = new Loading_Test();
}
});
}
}
class WaitLayerUI extends LayerUI<JPanel> implements ActionListener {
private boolean mIsRunning;
private boolean mIsFadingOut;
private Timer mTimer;
private int mAngle;
private int mFadeCount;
private int mFadeLimit = 15;
#Override
public void paint(Graphics g, JComponent c) {
int w = c.getWidth();
int h = c.getHeight();
super.paint(g, c); // Paint the view.
if (!mIsRunning) {
return;
}
Graphics2D g2 = (Graphics2D) g.create();
float fade = (float) mFadeCount / (float) mFadeLimit;
Composite urComposite = g2.getComposite(); // Gray it out.
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .5f * fade));
g2.fillRect(0, 0, w, h);
g2.setComposite(urComposite);
int s = Math.min(w, h) / 5;// Paint the wait indicator.
int cx = w / 2;
int cy = h / 2;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(new BasicStroke(s / 4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2.setPaint(Color.white);
g2.rotate(Math.PI * mAngle / 180, cx, cy);
for (int i = 0; i < 12; i++) {
float scale = (11.0f - (float) i) / 11.0f;
g2.drawLine(cx + s, cy, cx + s * 2, cy);
g2.rotate(-Math.PI / 6, cx, cy);
g2.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, scale * fade));
}
g2.dispose();
}
#Override
public void actionPerformed(ActionEvent e) {
if (mIsRunning) {
firePropertyChange("tick", 0, 1);
mAngle += 3;
if (mAngle >= 360) {
mAngle = 0;
}
if (mIsFadingOut) {
if (--mFadeCount == 0) {
mIsRunning = false;
mTimer.stop();
}
} else if (mFadeCount < mFadeLimit) {
mFadeCount++;
}
}
}
public void start() {
if (mIsRunning) {
return;
}
mIsRunning = true;// Run a thread for animation.
mIsFadingOut = false;
mFadeCount = 0;
int fps = 24;
int tick = 1000 / fps;
mTimer = new Timer(tick, this);
mTimer.start();
}
public void stop() {
mIsFadingOut = true;
}
#Override
public void applyPropertyChange(PropertyChangeEvent pce, JLayer l) {
if ("tick".equals(pce.getPropertyName())) {
l.repaint();
}
}
}
One problem I see is that the code is setting the composite in the wrong place in the loop. It works, but as you've discovered, it's difficult to maintain or change.
g2.setComposite is being called at the end of the loop. This sets the alpha for the next petal drawn. This means there is no easy change you can make to adjust the alpha of the very first petal.
First, I would make the code more in line with the way humans think (at least, the way I think): Set the alpha of the line you're about to draw, right before you draw it:
for (int i = 0; i < 12; i++) {
float scale = (12 - i) / 12f;
g2.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, scale * fade));
g2.drawLine(cx + s, cy, cx + s * 2, cy);
g2.rotate(-Math.PI / 6, cx, cy);
}
Now, making it work with any arbitrary background alpha is easy. We merely adjust the value of scale:
float componentAlpha = 0.5f;
for (int i = 0; i < 12; i++) {
float scale = (12 - i) / 12f;
// Give petals the same relative alpha as the component
// they're overlaying.
scale *= componentAlpha;
g2.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, scale * fade));
g2.drawLine(cx + s, cy, cx + s * 2, cy);
g2.rotate(-Math.PI / 6, cx, cy);
}

Rotating image on top of another image

I have a method that I want to rotate an image when the user enters the number of degrees to rotate it. The method does that, but the thing is it's rotating the image so that the new image lays on top of the old one (which I don't want it to do; I just want the new image by itself). The RotateTest is just an abbreviated form of the class with all of the methods needed in the ButtonListener class that should be relevant to rotating the image.
public class RotateTest {
public Rotate() {
try {
String path = "default.jpg";
File imageFile = new File(path);
imageURL = imageFile.toURI().toURL();
image = ImageIO.read(imageURL);
this.imageLabel = new JLabel(imageLabel);
} catch (IOException e) { }
}
public void setAngle(double angle) {
this.angle = angle;
}
public void setImage(BufferedImage img) {
this.image = img;
}
private class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
boolean doAgain = true;
Artsy artsy = new Artsy();
if(event.getSource() == rotate) {
//need something for cancel!
do {
String angleString = JOptionPane.showInputDialog("Please enter your angle in degrees.");
double angle = Double.parseDouble(angleString);
if(angle < 0) {
angle = 360 + angle;
}
setAngle(getAngle() + angle);
setImage(artsy.doRotate(image, angle));
revalidate();
repaint();
System.out.println("The angle is " + getAngle());
} while(JOptionPane.OK_OPTION == 1);
}
else {
if(doAgain) {
setImage(artsy.doRotate(image, 360 - getAngle()));
doAgain = false;
setAngle(0);
}
revalidate();
repaint();
System.out.println("The angle is " + getAngle());
}
}
}
And this is the other class with the method that rotates the image:
public class Artsy {
public BufferedImage doRotate(BufferedImage src, double angle) {
angle = Math.toRadians(angle);
Graphics2D g = (Graphics2D) src.getGraphics();
int w = src.getWidth();
int h = src.getHeight();
AffineTransform trans = new AffineTransform();
trans.rotate(angle, w / 2, h / 2);
AffineTransformOp scaleOp = new AffineTransformOp(trans, AffineTransformOp.TYPE_BILINEAR);
g.drawImage(scaleOp.filter(src, null), 0, 0, null);
g.dispose();
return src;
}
}
Thank you!!
Your code seems correct to me, i can't see an obvious mistake. Nevertheless the following function is working for my to rotate images so it should also be a solution for you:
public static BufferedImage rotate(BufferedImage srcImage, double angle)
{
double sin = Math.abs(Math.sin(Math.toRadians(angle))), cos = Math.abs(Math.cos(Math.toRadians(angle)));
int originWidth = srcImage.getWidth(), originHeight = srcImage.getHeight();
int newWidth = (int) Math.floor(originWidth * cos + originHeight * sin), newHeight = (int) Math.floor(originHeight * cos + originWidth * sin);
BufferedImage newImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = newImage.createGraphics();
g.translate((newWidth - originWidth) / 2, (newHeight - originHeight) / 2);
g.rotate(Math.toRadians(angle), originWidth / 2, originHeight / 2);
g.drawImage(srcImage, 0, 0, null);
g.dispose();
return newImage;
}
A helper function:
/**
* Converts an Icon to an Image
*/
public static Image iconToImage(Icon icon) {
if (icon instanceof ImageIcon) {
return ((ImageIcon) icon).getImage();
}
else {
int w = icon.getIconWidth();
int h = icon.getIconHeight();
GraphicsEnvironment ge =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gd.getDefaultConfiguration();
BufferedImage image = gc.createCompatibleImage(w, h);
Graphics2D g = image.createGraphics();
icon.paintIcon(null, g, 0, 0);
g.dispose();
return image;
}
}
Example usage to rotate the JLabels icon for 90 degrees clockwise:
BufferedImage buImg = new BufferedImage(imageLabel.getIcon().getIconWidth(), imageLabel.getIcon().getIconHeight(), BufferedImage.TYPE_INT_ARGB);
buImg.getGraphics().drawImage(iconToImage(imageLabel.getIcon()), 0, 0, null);
imageLabel.setIcon(new ImageIcon(rotate(buImg, 90)));

java.io.IOException Difficulty when adding "player" texture into screen

I get this strange error from loading "Player.png" file from the Player Class.
I understand that my organization of the code is very sloppy and the methods I use are terrible. half of the code is borrowed from tutorials.
What 2 classes are supposed to do is create a screen with green colored tiles filling up the screen with a "player" sprite that can move right, left, up, and down with the W,A,S,D keys.
error:
java.io.IOException: Attempt to allocate a texture to big for the current hardware
at org.newdawn.slick.opengl.InternalTextureLoader.getTexture(InternalTextureLoader.java:293)
at org.newdawn.slick.opengl.InternalTextureLoader.getTexture(InternalTextureLoader.java:231)
at
org.newdawn.slick.opengl.InternalTextureLoader.getTexture(InternalTextureLoader.java:184)
at org.newdawn.slick.opengl.TextureLoader.getTexture(TextureLoader.java:64)
at org.newdawn.slick.opengl.TextureLoader.getTexture(TextureLoader.java:24)
at test.PlayerClass.render(PlayerClass.java:69)
at test.Main.render(Main.java:110)
at test.Main.run(Main.java:82)
at test.Main.main(Main.java:27)
Main Class
public class Main{
private static boolean running = true;
public static final int WIDTH = 1024;
public static final int HEIGHT = 768;
private static Texture tile;
static PlayerClass playerClass = new PlayerClass(100, 100, 32, 32);
public static void main(String[] args){
Main main = new Main();
main.run();
}
//Initialize Method
public static void init(int width, int height ) throws LWJGLException
{
DisplayMode[] m = Display.getAvailableDisplayModes();
for(DisplayMode mode : m)
{
if(mode.getWidth() == 1024 && mode.getHeight() == 768 && mode.getBitsPerPixel() == 32)
{
Display.setDisplayMode(mode);
}
}
Display.setTitle("Game");
Display.setVSyncEnabled(true);
Display.sync(100);
Display.create();
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, Display.getDisplayMode().getWidth(), Display.getDisplayMode().getHeight(), 0, -1, 1 );
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glLoadIdentity();
GL11.glEnable(GL11.GL_ALPHA_TEST);
GL11.glAlphaFunc(GL11.GL_GREATER, 0.2f);
GL11.glEnable(GL11.GL_TEXTURE_2D);
try {
tile = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("RPG/tile.png"));
}
catch (IOException e)
{
e.printStackTrace();
}
}
public void run()
{
try {
init(1024, 768);
} catch (LWJGLException e) {
e.printStackTrace();
}
while(running)
{
Display.update();
drawTiled(WIDTH, HEIGHT);
input();
update();
render();
}
cleanup();
}
public static void input()
{
if(Keyboard.isKeyDown(Keyboard.KEY_ESCAPE))
{
running = false;
}
playerClass.input();
}
public static void update()
{
}
public static void render()
{
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
GL11.glLoadIdentity();
try {
playerClass.render();
} catch (IOException e) {
e.printStackTrace();
}
Display.update();
}
public static void cleanup()
{
Display.destroy();
}
public void drawTiled(int screenWidth, int screenHeight) {
Color.white.bind();
tile.bind();
int numberPerRow = screenWidth / tile.getTextureWidth();
int numberOfRows = screenHeight / tile.getTextureHeight();
GL11.glBegin(GL11.GL_QUADS);
for (int j = 0; j < numberOfRows; j++) {
//System.out.print("{");
for (int i = 0; i < numberPerRow; i++)
{
//top left
GL11.glTexCoord2f(0, 0);
GL11.glVertex2f(tile.getTextureWidth() * i, tile.getTextureHeight() * j);
//top right
GL11.glTexCoord2f(1, 0);
GL11.glVertex2f(tile.getTextureWidth() * (i + 1), tile.getTextureHeight() * j);
//bottom right
GL11.glTexCoord2f(1, 1);
GL11.glVertex2f(tile.getTextureWidth() * (i + 1), tile.getTextureHeight() * (j + 1));
//bottom left
GL11.glTexCoord2f(0, 1);
GL11.glVertex2f(tile.getTextureWidth() * i, tile.getTextureHeight() * (j + 1));
}
}
}
}
Player Class
public class PlayerClass {
private float x, y;
private int w, h;
private Texture player;
private FloatBuffer verts = BufferUtils.createFloatBuffer(2 * 4);
private FloatBuffer tex = BufferUtils.createFloatBuffer(2 * 4);
public PlayerClass(float X, float Y, int W, int H)
{
x = X;
y = Y;
w = W;
h = H;
verts.put(new float[]{
0.0f, 0.0f,
32.0f, 0.0f,
32.0f, 32.0f,
0.0f, 32.0f
});
tex.put(new float[]{
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f,
0.0f, 1.0f
});
}
public void input()
{
if(Keyboard.isKeyDown(Keyboard.KEY_W))
{
y -= 10;
}
else if(Keyboard.isKeyDown(Keyboard.KEY_S))
{
y += 10;
}
if(Keyboard.isKeyDown(Keyboard.KEY_A))
{
x -= 10;
}
else if(Keyboard.isKeyDown(Keyboard.KEY_D))
{
x += 10;
}
}
public void render() throws IOException
{
player = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("RPG/player.png"));
player.bind();
verts.rewind();
tex.rewind();
GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
GL11.glTranslatef(x, y, 0.0f);
GL11.glVertexPointer(2, 0, verts);
GL11.glTexCoordPointer(2, 0, tex);
GL11.glDrawArrays(GL11.GL_QUADS, 0, 4);
GL11.glTranslatef(-x, -y, 0.0f);
GL11.glDisableClientState(GL11.GL_VERTEX_ARRAY);
GL11.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
}
}
Run this:
System.out.println(GL11.glGetInteger(GL11.GL_MAX_TEXTURE_SIZE));
It'll tell you the max texture size allowed by your Graphics card. Make sure that your graphics card can handle a texture of the size that you are trying to use. According to the error, it can't.
How large is tile.png - the maximum texture size can vary depending your card, and I think, some other factors. Your file is probably outside this dimension.
Cody's answer gives you the way to get the max texture size for your card. If you're writing a game for use by many other people, you'll probably want to integrate that code into your game code in such a way that it loads different resolution textures based on the capabilities of the card it's running on, or simply find a default that works on any card that you want to support, and use that for all installations.
Here's some additional reading that will explain more about this issue, some history about it, and more technical info that you might find useful:
Official OpenGL wiki on 'Texture'
List of maximum texture size by video card (from 2006, but you'll get the idea)
An FAQ of texture mapping

Categories