Laboratorium 5
Prosty program do rysowania (Paint)
Zadanie główne (5 pkt)
Stwórz program pozwalający na rysowanie prostych kształtów (linia, krzywa, kwadrat…). Program powinien mieć możliwość zmiany koloru i grubości konturu rysowanego kształtu i przechowywać wszystkie narysowane kształty na obszarze rysowania. Program powinien także posiadać opcję wczytania obrazu z pliku, zapisu obszaru rysowania do pliku i czyszczenia obszaru rysowania.
Wymagania techniczne:
- Umiejętne korzystanie z wybranych layoutów (komponenty znajdują się dokładnie tam, gdzie tego chce twórca, a nie „jak layout układa”).
- Wykorzystanie odpowiednich komponentów JSwing do odpowiedniego zadania (np. wybór koloru przez JColorChooser).
- Obsługa zdarzeń powinna być intuicyjna – jeżeli klient chce zmienić narzędzie rysowania, powinien to móc zrobić jednym kliknięciem.
Punktacja:
- Stworzenie okna aplikacji (odpowiedni Layout, wszystkie komponenty JSwing) – 1 pkt
- Rysowanie – 2 pkt
- Przynajmniej 2 różne narzędzia rysowania (gumka, linia, krzywa, pusty/pełny kwadrat…), przy czym jednym z nich musi być tzw. „ołówek”
- Krzywe i figury rysują się „za ruchem myszy”
- Możliwość wyczyszczenia obszaru rysowania
- Zapis obszaru rysowania do pliku i możliwość dodania obrazu z pliku (lub adresu URL) do obszaru rysowania, realizowany przez pasek menu (JMenu) – 1 pkt
- Zmiana koloru i grubości konturu rysowanego kształtu – 1 pkt
To zadanie możesz wykonać tak, jak do tej pory – spełniając kolejne wymagania postawione w sekcji Punktacja, albo przejść przez kolejne kamienie milowe w poniższym scenariuszu. Każdy kolejny krok dodaje nową funkcjonalność, ale robi to stopniowo, dzięki czemu nie powinieneś pogubić się w pisaniu programu.
Scenariusz:
-
Stwórz okno (JFrame) zawierające panel (JPanel), w którym po naciśnięciu myszy (wykorzystaj MouseListener), w miejscu kliknięcia, będzie rysowany pewien konkretny kształt (np. kwadrat o określonej długości boku).
-
Dodaj i oprogramuj elementy pozwalające na zmianę koloru i grubości linii rysowanego kształtu. Kolor powinien być wybierany za pomocą komponentu JColorChooser.
-
Zadbaj o to, aby kolejne kliknięcia myszy nie usuwały poprzednio narysowanych kształtów. (N kliknięć= N kształtów w panelu). Na tym etapie upewnij się też, że wybierając kolor, nie zmieniasz koloru linii narysowanych już elementów.
-
Zmodyfikuj rysowany kształt tak, aby jego wielkość i kształt dało się ustalić za pomocą zdarzeń z myszy. Dodaj drugie narzędzie – ołówek. Zadbaj o to, aby oba kształty były rysowane poprawnie.
-
Stwórz menu (JMenuBar), dzięki któremu będzie można
-
zapisać zawartość panelu do pliku graficznego,
-
wczytać dowolną grafikę,
-
wyczyścić panel rysowania.
-
Za osiągnięcie każdego kamienia milowego otrzymasz 1 punkt.
W obu przypadkach możesz otrzymać dodatkowe punkty za „nadprogramowe” narzędzia – inne figury, gumka… . Za każde dodatkowe narzędzie otrzymasz 0,5 punktu. Narzędzia muszą spełniać założenia z podpunktu 2b.
Dobrym ćwiczeniem będzie otwieranie i zapis plików za pomocą JFileChooser, ale nie jest to konieczne do ukończenia tego zadania.
Wskazówki:
-
- Do rysowania kształtów możesz wykorzystać klasy Graphics lub Graphics2D.
- Rysowanie linii http://www.codejava.net/java-se/graphics/drawing-lines-examples-with-graphics2d
- Rysowanie punkt po punkcie („za ruchem myszy” ) :
public class Linia { private List<Integer> xList; // Lista współrzędnych x private List<Integer> yList; // Lista współrzędnych y // Konstruktor public Linia() { xList = new ArrayList<Integer>(); yList = new ArrayList<Integer>(); } // Dodawanie punktów do linii public void addPoint(int x, int y) { xList.add(x); yList.add(y); } // Metoda pozwalająca linii na rysowanie siebie samej, jeżeli będzie miała dostęp do Graphics2D/Graphics public void draw(Graphics2D g2d) { for (int i = 0; i < xList.size() - 1; ++i) { g2d.drawLine(xList.get(i), yList.get(i), xList.get(i + 1), yList.get(i + 1)); } } }
Analogiczną klasę możemy stworzyć dla dowolnego rysowanego kształtu.
Proponujemy dwa sposoby podejścia do problemu przechowywania kształtów:
1. Zapamiętywanie parametrów każdego kształtu osobno.
W panelu będącym obszarem rysowania możemy stworzyć listy przechowujące wszystkie narysowane kształty:
ArrayList<Linia> lines= new ArrayList<Linia>();
które potem należy rysować w metodzie paintComponent:
super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; for (linia line: lines) line.draw(g2d);
- Dodajemy kształty do odpowiednich list w listenerach (mousePressed, mouseDragged). Należy pamiętać o repaint()!
- To podejście umożliwia rozszerzenie funkcjonalności programu o modyfikację już istniejących kształtów.
2. Przechowywania wszystkich informacji w samym obrazku.
W panelu będącym obszarem rysowania możemy stworzyć obiekt BufferedImage
, np:
BufferedImage image;
który będzie modyfikowany w odpowiednich metodach (zdarzenie kliknięcia myszą, puszczenie przycisku, itd), np:
Graphics2D g2 = image.createGraphics(); g2.fillRect(e.getX(), e.getY(), 2, 2); repaint();i rysowany jako całość w paintComponent.
public void paintComponent(Graphics g) { if(image==null){ image = (BufferedImage)this.createImage(400, 400); Graphics2D gc = image.createGraphics(); gc.setColor(Color.white); gc.fillRect(0, 0, 400, 400); } Graphics2D g2d = (Graphics2D) g; g2d.drawImage(image, 0, 0, this); }
Odczyt i zapis obrazków do pliku.
Zapis do pliku możesz wykonać w taki prosty sposób:
//Tworzenie obiektu BufferedImage, w którym znajdzie się nasza grafika z panelu paintPanel BufferedImage image = new BufferedImage(paintPanel.getWidth(), paintPanel.getHeight(),BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = image.createGraphics(); paintPanel.paintAll(g2d); //Tworzenie pliku zapisu w folderze projektu File outputfile = new File("saved.png"); //Zapis do pliku try { ImageIO.write(image, "png", outputfile); } catch (IOException e) { System.out.println(e.getMessage()); }
Wczytywanie z pliku z kolei może wyglądać następująco:
//Tworzenie obiektu BufferedImage BufferedImage image = null; //Tworzenie pliku wejściowego File inputFile = new File("saved.png"); //Odczytywanie pliku wejściowego try { image = ImageIO.read(inputFile); } catch(IOException ex) { System.out.println(ex.getMessage()); } //Autorska funkcja dodająca odczytany obraz do panelu rysowania paintPanel paintPanel.setBackgroudImage(image);
Więcej informacji:
- Nasłuchiwanie zdarzeń z myszy:
Dzięki nasłuchiwaniu zdarzeń z myszy, wiemy dokładnie, gdzie znajduje się kursor myszy i możemy rysować kształty podążając za nim (np. przekazując kolejne współrzędne położenia myszy do listy z przykładu, używając np. e.getX(), e.getY()
).
- Wczytywanie obrazu i zapis obrazu do pliku: