1

So I have two classes here:

PhotoComponent class:

(This class is to handle a specific image as a JComponent. When "flipped" I want to draw pen strokes instead of having an image. So I replace the image with a rectangle, attempting to draw pen strokes over it.)

public class PhotoComponent extends JComponent {

private Image pic;
private boolean flipped;

private int contentAreaWidth;
private int contentAreaHeight;
p
@Override
public void paintComponent(Graphics g) {
    //Does all the drawing and contains whatever state information is associated with the photo
    //create an action event to auto call repaint
    //call repaint anytime flip was changed to true or false

    System.out.println("Draw: " + draw + ", Pic: " + pic);
    if (draw && pic != null) {
        super.paintComponent(g);

        System.out.println("width using this: " + this.getWidth() + ", actual width of JPanel: " + contentAreaWidth);
        System.out.println("height using this: " + this.getHeight() + ", actual height of JPanel: " + contentAreaHeight);

        g2 = (Graphics2D) g;
        int x = (contentAreaWidth - pic.getWidth(null)) / 2;
        int y = (contentAreaHeight - pic.getHeight(null)) / 2;
        if (!flipped) {
            g2.drawImage(pic, x, y, null);

        } else if (flipped) {
            g2.setColor(Color.WHITE);
            g2.fillRect(x,y,pic.getWidth(null), pic.getHeight(null));
            g2.drawRect(x, y, pic.getWidth(null), pic.getHeight(null));
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            if (drawingMode) {
                g2.setPaint(Color.RED);
                if (drawOval) {
                    penStrokes.put(ovalX, ovalY);
                    if (penStrokes != null) {
                        for (Integer xCoor : penStrokes.keySet()) {
                            g2.setPaint(Color.RED);
                            int brushSize = 5;
                            g2.fillOval((xCoor - (brushSize / 2)), (penStrokes.get(xCoor) - (brushSize / 2)), brushSize, brushSize);
                            //System.out.println("SIZE OF HASHTABLE: " + penStrokes.size());
                        }
                    }
                    System.out.println("Filling an oval!" + ovalX + ", " + ovalY);
                }
            } else if (textMode) {
                g2.setPaint(Color.YELLOW);
                if (drawRect) {
                    rectDimensions.add(rectX);
                    rectDimensions.add(rectY);
                    rectDimensions.add(rectWidth);
                    rectDimensions.add(rectHeight);

                    for (int i = 0; i < rectDimensions.size(); i+=4) {
                        g2.fillRect(rectDimensions.get(i), rectDimensions.get(i+1), rectDimensions.get(i+2), rectDimensions.get(i+3));
                        g2.drawRect(rectDimensions.get(i), rectDimensions.get(i+1), rectDimensions.get(i+2), rectDimensions.get(i+3));
                    }
                }
            }

            System.out.println("This is being called again!");
        }

    }

}
public void setRectangle(int x, int y, int width, int height) {
    drawRect = true;
    rectX = x;
    rectY = y;
    rectWidth = width;
    rectHeight = height;
}

public void removeRectangle() {
    drawRect = false;
}

public int[] setOval(int currentX, int currentY) {
    drawOval = true;
    int[] toReturn = {ovalX, ovalY};
    ovalX = 

NOTE THE DRAWLINE() METHOD ABOVE. I am drawing at the given points, repainting, and setting the old variables to be the current variables.

Main class:

private static PhotoComponent img;
private static JFrame frame;

private static JPanel contentArea;

//Mouse Coordinates for PenStrokes
private static int oldX, oldY;

//Mouse Coordinates for Sticky Notes
private static Point clickPoint;

public static void main (String[] args) {
    frame = new JFrame("PhotoManip");
    img = null;
    contentArea = null;
    oldX = 0;
    oldY = 0;
    setupMenubar(frame);
    setupJFrame(frame);
}

private static void addPhotoComponent(File file) {

            }
            if (img.getTextMode()) {
                img.removeRectangle();
                clickPoint = null;
            }

        }
    });

    img.addMouseMotionListener(new MouseAdapter() {
        @Override
        public void mouseDragged(MouseEvent e) {
            if (img.getDrawingMode()) {
                if (withinRange(e.getX(), e.getY())) {
                    int[] toUpdate = img.setOval(e.getX(), e.getY());
                    oldX = toUpdate[0];
                    oldY = toUpdate[1];
                    img.repaint();
                }
            }
            if (img.getTextMode()) {
                if (withinRange(e.getX(), e.getY())) {
                    Point dragPoint = e.getPoint();
                h, height);
                    img.repaint();
                }
            }

        }
    });

    if (img!=null) {
        contentArea.add(img);
    }
}

private static boolean withinRange(int x, int y) {
    if (x > img.getX() && x < img.getX() + img.getWidth()) {
        if (y > img.getY() && y < img.getY() + img.getHeight()) {
            return true;
        }
    }
    return false;
}

private static void flipImage() {
    if (!img.isFlipped()) {
        img.setFlipped(true);
    } else if (img.isFlipped()) {
        img.setFlipped(false);
    }
}

drawLine() is called above in this main class, when a mousedrag occurs. Problem is that the strokes don't appear to show.

I know that the program is calling g2.fillOval() because I am printing out a verification statement afterwards.

Additionally, I have created print statements for when the mouse is pressed and dragged and they are getting the correct coordinates?

Why don't red strokes appear? I'm confused. Is it the way my code is structured?

dan139
  • 47
  • 6
  • 1
    If you want to draw a line the way to do it is store the line co-ords as some sort of variable and reference that within `paintComponent`. Don't try to save a graphics object and draw on it outside of `paintComponent`. Whatever you have will probably be overwritten as soon as `paintComponent` gets called again (which is immediately). – nhouser9 Oct 06 '16 at 16:30
  • I've added a bit more context to my problem. So you're saying that I should move my painted strokes all within the paintComponent() method? – dan139 Oct 06 '16 at 16:33
  • I posted an answer below. As I do not have your full code, I can't actually compile and run it, but I think it should work. Please let me know if not and I will help you figure out what to change to get it to work. – nhouser9 Oct 06 '16 at 16:57

1 Answers1

2

The crux of your problem is that you are trying to draw something outside the paintComponent method, which is never supported. Whatever you draw will get overwritten by the next call of paintComponent, which will happen almost instantly. We can solve this by storing the co-ordinates of the oval and drawing it within paintComponent instead of trying to draw on a graphics object outside of the paintComponent method. See code below:

First we are going to add the following variables to your PhotoComponent class:

private boolean drawOval = false;
private int ovalX = 0;
private int ovalY = 0;

Then we will add methods for controlling them:

public int[] setOval(int currentX, int currentY) {
    drawOval = true;
    int[] toReturn = {ovalX, ovalY};
    ovalX = currentX;
    ovalY = currentY;
    return toReturn;
}

public void removeOval() {
    drawOval = false;
}

After that we can change the paintComponent method to have it draw the oval based on those variables:

@Override
public void paintComponent(Graphics g) {
    //Does all the drawing and contains whatever state information is associated with the photo
    //create an action event to auto call repaint
    //call repaint anytime flip was changed to true or false
    super.paintComponent(g);
    g2 = (Graphics2D) g;
    int x = (contentAreaWidth - pic.getWidth(null)) / 2;
    int y = (contentAreaHeight - pic.getHeight(null)) / 2;
    if (!flipped) {
        g2.drawImage(pic, x, y, null);

    } else if (flipped) {
        g2.setColor(Color.WHITE);
        g2.fillRect(x, y, pic.getWidth(null), pic.getHeight(null));
        g2.drawRect(x, y, pic.getWidth(null), pic.getHeight(null));
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setPaint(Color.RED);
    }

    //took the code you already used for drawing the oval and moved it here
    if (drawOval) {
        g2.setPaint(Color.RED);
        int brushSize = 5;
        g2.fillOval((ovalX - (brushSize / 2)), (ovalY - (brushSize / 2)), brushSize, brushSize);
    }
}

Finally change the addPhotoComponent method to update those variables instead of trying to draw the oval directly:

private static void addPhotoComponent(File file) {
    Image image = null;
    try {
        image = ImageIO.read(file);
    } catch (IOException e2) {
        System.out.println("ERROR: Couldn't get image.");
    }

    img = new PhotoComponent(image, contentArea);
    img.revalidate();

    img.addMouseListener(new MouseAdapter() {
        @Override
        public void mouseClicked(MouseEvent e) {
            if (e.getClickCount() == 2) {
                // your code here
                System.out.println("You flipped the photo!!!");
                flipImage();
                img.repaint();
            }
        }

        @Override
        public void mousePressed(MouseEvent e) {
            img.setOval(e.getX(), e.getY());
        }
        @Override
        public void mouseReleased(MouseEvent e) {
            img.removeOval();
        }
    });

    img.addMouseMotionListener(new MouseAdapter() {
        @Override
        public void mouseDragged(MouseEvent e) {
            int[] toUpdate = img.setOval(e.getX(), e.getY());
            oldX = toUpdate[0];
            oldY = toUpdate[1];
        }
    });

    if (img != null) {
        contentArea.add(img);
    }
}
nhouser9
  • 6,730
  • 3
  • 21
  • 42
  • Thanks for your answer. This works...however, it just draws a red dot and as I drag it moves the red oval - it doesn't actually draw a new oval one each change in coordinate. Is there a way for it to be an actual stroke as opposed to a red dot? – dan139 Oct 06 '16 at 17:21
  • @dan139 sure, you would just have to do something similar to what we did here. simply store the coordinates you want to draw at and then in the `paintComponent` method reference those co-ords to draw something. Please remember to upvote and accept if this helped, i spent quite some time editing the code for you. – nhouser9 Oct 06 '16 at 17:35
  • To whoever downvoted this: I spent 20 minutes editing the code to make sure it worked right. The least you could do is take 5 seconds to leave a comment as to why you thought this was a bad answer. – nhouser9 Oct 06 '16 at 17:36
  • I didn't downvote you. will upvote. I implemented a HashTable to store the coordinates and then iterate over the coordinates, but the more I draw, other strokes tend to disappear. Is there a size limit to HashTables or is this being caused by another issue? – dan139 Oct 06 '16 at 17:51
  • @dan139 This shouldn't be caused by size limitations. To check for sure, print out the size of the hashtable every time you try to add an element. Also, if you post your updated code, I will see if I can find any other issues with it. – nhouser9 Oct 06 '16 at 18:20
  • I printed the size of the hashtable and it seems for each point, the length of the table increments. But this value repeats if I move the cursor quickly. Not sure if this is because the computer can't handle that many strokes at a time. I've also updated my code on the original post for your convenience. thanks. – dan139 Oct 06 '16 at 18:55
  • @dan139 I feel dumb for not thinking of this before. Each key in the hashmap must be unique, so I suspect that some new entries are overwriting old entries in your hashmap. Maybe it would be better to use a `List`? If you try that out let me know how it works. – nhouser9 Oct 06 '16 at 20:39