this is beyond me (be forgiving, it's late). I'm subclassing a WindowsSliderUI, because I want it to draw a bigger thumb (is that the right word?)(that's working) and also display the value of the slider just above it (like, for example, gtk look and feel does)(that's broken). I'm overriding the paint() method, and at the moment it looks like this: (it's long, my changes are near the bottom):
Rectangle knobBounds = thumbRect;
int w = knobBounds.width;
int h = knobBounds.height;
g.translate(knobBounds.x, knobBounds.y);
if ( slider.isEnabled() ) {
g.setColor(slider.getBackground());
}
else {
g.setColor(slider.getBackground().darker());
}
Boolean paintThumbArrowShape =
(Boolean)slider.getClientProperty("Slider.paintThumbArrowShape");
if ((!slider.getPaintTicks() && paintThumbArrowShape == null) ||
paintThumbArrowShape == Boolean.FALSE) {
// "plain" version
g.fillRect(0, 0, w, h);
//THE ONES THAT MATTER
g.setColor(Color.BLACK);
String val = slider.getValue()+"";
g.drawString(val, 0, 0);
g.setColor(Color.RED);
g.drawLine(0, 0, 30, 8);
//END
g.setColor(Color.black);
g.drawLine(0, h-1, w-1, h-1);
g.drawLine(w-1, 0, w-1, h-1);
g.setColor(getHighlightColor());
g.drawLine(0, 0, 0, h-2);
g.drawLine(1, 0, w-2, 0);
g.setColor(getShadowColor());
g.drawLine(1, h-2, w-2, h-2);
g.drawLine(w-2, 1, w-2, h-3);
}
all i want is to get the value displayed just above the thumb. however, what happens now is the string gets displayed initially, but when i drag the slider, it stays in the same place (not changing value) until i release the mouse button, at which point it is redrawn at the right place with the right value. and just to make my head go even funnier, the drawLine() method works fine - the line is always on the thumb when i drag it.
now, this is probably trivial mistake (i'm really no good, and tired), but please help me find it. mind you, if you see a better approach to the whole problem, let me know as well. as i said, i'm really no good with this, and i tend to make things more complicated than they are.
thanks a lot
I don't know the complete answer, but your call to
g.drawString(val, 0, 0);
looks dodgy. The co-ordinates you specify to drawString are the baseline of the leftmost character in the string, not the top-left hand corner of the string. Typically a call like yours results in nothing appearing, because all or most of the string is drawn outside of the clipping rectangle.
Perhaps you meant something like this:
final int height = g.getFontMetrics().getAscent();
g.drawString(s, 0, height);
Related
For some reason I have found a glitch that makes no sense at all and only appears on the Galaxy Note 4, as I think now. The only test devices I have is a Nexus 7 (2012), blue stacks beta and the Note 4. This glitch only is appearing on the note 4. Here is the drawing code:
if(!started){
continue;
}
h = getHolder();
if (!h.getSurface().isValid()){
continue;
}
Canvas c = h.lockCanvas();
c.drawRect(0, 0, this.getWidth(), this.getHeight(), white);
//draw walls
for(int i=0;i<walls.length;i++){
c.drawRect(walls[i].x, walls[i].y, walls[i].bottom, walls[i].right, red);
}
//draw portals
for(int i=0;i<portals.length;i++){
Log.d("S", i+"");
Log.d("S", portalimages[i].toString());
c.drawBitmap(portalimages[i], portals[i].x1, portals[i].y1, null);
c.drawBitmap(portalimages[i], portals[i].x2, portals[i].y2, null);
}
//draw startlocations
for(int i=0;i<startlocations.length;i++){
c.drawBitmap(player, startlocations[i].x, startlocations[i].y, null);
}
//draw checkpoints
for(int i=0;i<checkpoints.length;i++){
c.drawRect(checkpoints[i].x, checkpoints[i].y, checkpoints[i].x+this.getHeight()/18, checkpoints[i].y+this.getHeight()/18, blue);
}
//draw middle line
c.drawRect(0, getHeight()/2-getHeight()/80, getWidth(), getHeight()/2+getHeight()/80, black);
menu.draw(c);
h.unlockCanvasAndPost(c);
The glitch happens once my onClick is called. it just adds a new startlocation:
startlocations[location] = new Coord(lastx, lasty);
if(location>0){
location = 0;
}else{
location++;
}
break;
Note: startlocations is setup with startlocations = new Coord[2];
Sorry for the video quality. The Note 4 is my Dad's so I had to send it over a messaging service and it compressed it, then YouTube compressed it more. Then it breaks out into this: https://www.youtube.com/watch?v=Ig9mflYBPaI
There is nothing else that changes on click of the player start locations button. I will try to look more into this but I decided to post it here in case I missed something. Thanks!
Edit:
I now know that the problem goes away when you do not have this line : c.drawBitmap(player, startlocations[i].x, startlocations[i].y, null); which makes no sense. There is no error and it is loaded with this code. BitmapFactory.decodeResource(getResources(), R.drawable.portal); and later is adjusted with Bitmap.createScaledBitmap(player, this.getHeight()/18, this.getHeight()/18, true);
I found the answer and it makes NO sense at all. For some reason, just in this class, the image needs to have transparency or else it glitches. Later on I draw other non transparent images. It makes no sense but it works. please tell me if you know any reason why this happens.
My guess is that in the other class I used the main package but in this class it's a different package. This could be the reason why it's only this class (or method). I'll have to do more testing.
Thanks.
My problem is quite simple to understand. I have a JPanel inside a JFrame in order to display some graphics using the drawFormula() method below to display 3d points in screen using perspective projection. Everytime drawFormula() reaches its end I just recall itself to draw the shape again and again and again and because I dont want to have any image flickering problems I dont use the paintComponent method but i call drawImage() method from panelG which I get from this.getGraphics() method of my JPanel. Everything runs fine, but the problem is that after a certain amount of time it stops rendering and i believe that it has to do with the list of BufferedImages it holds everytime i call drawImage(). Is there a way to remove the previous not needed images from the stack? Thanks in advance!
public void drawFormula(){
for(double i=latMin;i<latMax;i+= 0.05){
for(double j=longMin;j<longMax;j+= 0.05){
calc(m,n1,n2,n3,i,j);
applyRotationX();
applyRotationY();
applyRotationZ();
if(outX>xxmin && outX<xxmax && outY>yymin && outY<yymax){
xxx = (int)((outX-xxmin)*xinc);
yyy = (int)((outY-yymin)*yinc);
zzz = (int)((outZ-zzmin)*zinc);
//img_g.drawRect(xxx, yyy, 1, 1);
//img_g.drawRect((int) (planeX.getOffset(new Vector3D(xxx,yyy,zzz)))+600,(int) (planeY.getOffset(new Vector3D(x[i],y[i],z[i])))+350+j,1,1);
//img_g.setColor(new Color(Color.HSBtoRGB((float)(outX/outY), (float)(outY), (float)(outZ))));
drawPoint(xxx, yyy, zzz);
//panelG.drawImage(img, 0, 0, null);
}
}
}
//panelG.dispose();
//panelG = getGraphics().create();
panelG.drawImage(img, 0, 0, null);
thetaX += 1;
thetaX %= 360;
img_g.setColor(Color.black);
img_g.fillRect(0, 0, getWidth(), getHeight());
drawFormula();
}
I think it stops rendering due to a stackoverflow. You have an unconditioned recursion in your code (the drawFormula() at the end of drawFormula), which will cause a stackoverflow at some point. For the flickering: use setDoubleBuffered(true), this should solve your problem aswell.
I am implementing layers in a 2D engine of mine and I would like layers to be stackable, and I would also like a feature to 'cut holes' in layers - this means using alpha. However when writing it standalone I can't seem to get anything to use true alpha, it tried to fake it by blending colours together, for instance (my code):
BufferedImage background, foreground;
public GraphicsTest() {
background = new BufferedImage(500,500,BufferedImage.TYPE_INT_ARGB);
foreground = new BufferedImage(500,500,BufferedImage.TYPE_INT_ARGB); // Fully capable of alpha.
Random r = new Random();
int white = Color.white.getRGB();
// Draw random white dots
for (int i=0; i<500; i++) {
int
x = r.nextInt(500),
y = r.nextInt(500);
background.setRGB(x, y, white);
}
}
#Override
protected void paintComponent(Graphics g) {
BufferedImage output = new BufferedImage(500,500,BufferedImage.TYPE_INT_ARGB); // Fully capable of alpha.
Graphics2D canvas = output.createGraphics();
Graphics2D overlay = foreground.createGraphics();
canvas.drawImage(background, 0, 0, null);
overlay.setColor(Color.white);
overlay.fillRect(0, 0, 500, 500);
// Start drawing with alpha
overlay.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f));
overlay.setColor(new Color(0xFF000000)); // Overwrite/draw full alpha, colour doesn't matter when alpha is 100%
overlay.fillRect(100, 100, 125, 87);
//canvas.setColor(Color.red);
//canvas.fillRect(0, 0, 500, 500);
canvas.drawImage(foreground, 0, 0, null);
overlay.dispose();
canvas.dispose();
g.drawImage(output, 0, 0, null);
// Also write to a file for manual raw pixel checking
try {
// Does output a 32-bit depth image
ImageIO.write(output, "PNG", new File("c:/output.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
This is the most basic test I could think of, I have two images, one for the background and one for the foreground, I am needing to see the background through the foreground in the 127x87 (random sized) box.
Currently it results in a white screen with a black box, the back box should be that part of the background.
I have tried all sorts of methods here, settings the composite and drawing black with full alpha but when combined I never see the background, the setColor(0xFF000000) doesn't seem to be doing anything but drawing black and the composite is the culprit of this 'faking', instead of overwriting FF000000 (black) on a 00FFFFFF (white with no alpha) background yielding FF000000 (what I set it to) it is instead 007F7F7F (grey with no alpha).
The only reason I can see for this is the fact that everything is going through a Graphics2D object, I cannot use output.setRGB as it is slow and I wouldn't know how to draw custom shapes with it.
You need to change 3 things for your code to behave properly.
As #vandale said, you need to use a color with actual alpha with the boolean constructor.
Second, the alpha is the opacity (so it should be 0 for transparent color). You get:
overlay.setColor(new Color(0x0000000, true));
Third, you need to say that your paint operation will actually override whatever was already there (you don't want to draw on top (ATOP) of the existing but rather replace with the transparent color (IN) what was there). You get:
overlay.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, 1f));
Very simple thing I'm trying to do here. I would like to have 2 images on top of one another. When i use my mouse event dragged and clicked on the top image, the area of the top level image selected will fade and make the lower image visible.
The way I see it, there are 2 ways I can do this:
I can make the top image Transparent over time (within the selected area)
or
I can delete the pixels individually in a spray can style fashion. Think the spray can tool from MS paint back in the day.
Heres some very basic code that i started which just lays the images on top of eachother
PImage sand;
PImage fossil;
void setup()
{
size(400,400);
background(255,255,0);
frameRate(30);
fossil = loadImage("foss.jpg");
sand = loadImage("sand.jpeg");
}
void draw()
{
image(fossil, 0, 0, width,height);
image(sand, 0, 0, width,height);
smooth();
if (mousePressed) {
fill(0);
tint(255,127); //the opacity function
} else {
fill(255);
}
}
So has anyone any comments on these 2 ways of creating opacity or perhaps there an easier way I've overlooked?
Perhaps I wasn't clear in my Spec as the 2 comments below are asking for clarification.
In its simplest terms, I have 2 images on top of each other. I would like to be able to make some modification to the top level image which would make the bottom image visible. However I need to make this modification to only part of the top level image.
I would like to know which is the better option. To make part of the top image become transparent using tint() or to delete the pixels from the top layer.
Then I will proceed with that approach. Any indication as to how to do it is also appreciated.
I hope this clears up any confusion.
If you simply want to crossfade between images, it can be with tint() as you code suggest. You were in fact quite close:
PImage sand;
PImage fossil;
void setup()
{
size(400, 400);
fossil = loadImage("CellNoise.jpg");
sand = loadImage("CellVoronoi.jpg");
}
void draw()
{
//tint from 255 to 0 for the top image
tint(255,map(mouseX,0,width,255,0));
image(fossil, 0, 0, width, height);
//tint from 0 to 255 for the bottom image - 'cross fade'
tint(255,map(mouseX,0,width,0,255));
image(sand, 0, 0, width, height);
}
For the "spray can style " erosion you can simply copy pixels from a source image into the destination image. It's up to you how you loop through pixels (how many, what order, etc.) to get the "spray" like effect you want, but here's a basic example of how to use the copy() function:
PImage sand,fossil;
int side = 40;//size of square 'brush'
void setup()
{
size(400, 400);
fossil = loadImage("CellNoise.jpg");
sand = loadImage("CellVoronoi.jpg");
}
void draw()
{
image(fossil, 0, 0, 400, 400);
if(mousePressed) {
for(int y = 0 ; y < side ; y++){
for(int x = 0; x < side; x++){
//copy pixel from 'bottom' image to the top one
//map sketch dimensions to sand/fossil an dimensions to copy from/to right coords
int srcX = (int)map(mouseX+x,0,width+side,0,sand.width);
int srcY = (int)map(mouseY+y,0,height+side,0,sand.height);
int dstX = (int)map(mouseX+x,0,width+side,0,fossil.width);
int dstY = (int)map(mouseY+y,0,height+side,0,fossil.height);
fossil.set(dstX, dstY, sand.get(srcX,srcY));
}
}
}
}
Note what I am simply looping to copy a square (40x40 in my case), but you can find other fun ways to loop and get different effects.
Have fun!
Is there a way to use a Graphics object's 'setClip()' method to clip using a Line-ish shape? Right now I'm trying to use a Polygon shape but I'm having problems simulating the "width" of the line. I basically draw the line, and when I reach the end, I redraw it but this time subtract the line width from y-coordinate:
Polygon poly = new Polygon();
for(int i = 0; i < points.length; i++)
poly.addPoint(points.[i].x, points.[i].y);
// Retrace line to add 'width'
for(int i = points.length - 1; i >=0; i--)
poly.addPoint(points[i].x, points[i].y - lineHeight);
It almost works but the width of the line varies based upon its slope.
I can't use the BrushStroke and drawLine() methods because the line can change color once it passes some arbitrary reference line. Is there some implementation of Shape that I overlooked, or an easy one I can create, that will let me do this more easily?
If there is a better way, I've never run across it. The best I can think of is to use some trigonometry to make the line width more consistent.
OK, I managed to come up with a pretty nice solution without using the setClip() method. It involves drawing my background to an intermediate Graphics2D object, using setComposite() to specify how I want to mask the pixels, THEN drawing my line using drawLine() on top. Once I have this line, I draw it back on top of my original Graphics object via drawImage. Here's an example:
BufferedImage mask = g2d.getDeviceConfiguration().createCompatibleImage(width, height, BufferedImage.TRANSLUCENT);
Graphics2D maskGraphics = (Graphics2D) mask.getGraphics();
maskGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
maskGraphics.setStroke(new BasicStroke(lineWidth));
maskGraphics.setPaint(Color.BLACK);
// Draw line onto mask surface first.
Point prev = line.get(0);
for(int i = 1; i < line.size(); i++)
{
Point current = line.get(i);
maskGraphics.drawLine(prev.x, prev.y, current.x, current.y);
prev = current;
}
// AlphaComposite.SrcIn: "If pixels in the source and the destination overlap, only the source pixels
// in the overlapping area are rendered."
maskGraphics.setComposite(AlphaComposite.SrcIn);
maskGraphics.setPaint(top);
maskGraphics.fillRect(0, 0, width, referenceY);
maskGraphics.setPaint(bottom);
maskGraphics.fillRect(0, referenceY, width, height);
g2d.drawImage(mask, null, 0, 0);
maskGraphics.dispose();
Maybe you could use a Stroke.createClippedShape to do this? (May need to use an Area to add subtract the stroked shape from/to your original shape depending on what exactly you are trying to do.