Вот как это работает:
Изображение импортируется путем перетаскивания. Мы применяем к нему маску (re: MaskPanel), а затем, когда мышь отпускается, новый BufferedImage создается из исходного изображения и маски (объединенных вместе) и отображается в imageLabel в JLayeredPane. И тогда маскаPanel очищается. Чего я не понимаю, так это того, что как только я отпускаю мышь, маска исчезает. Моя идея состоит в том, что к тому времени новое замаскированное изображение должно быть отображено в imageLabel. Что я делаю не так?
Вот код:
package com.company.gui;
import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.event.*;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
public class MaskWindow extends JFrame {
private JLabel imageLabel;
private JLabel statusLabel;
private final int TARGET_WIDTH = 600;
private final int STATUS_HEIGHT = 30;
private BufferedImage originalImage;
private ArrayList
maskPoints;
private static final int BRUSH_SIZE = 40;
private static final Color MASK_COLOR = new Color(255, 0, 0, 10); // Semi-transparent red
private MaskPanel maskPanel;
private JLayeredPane layeredPane;
public MaskWindow() {
setTitle("Image Mask Window");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
imageLabel = new JLabel("Drop image here", JLabel.CENTER);
imageLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
imageLabel.setPreferredSize(new Dimension(TARGET_WIDTH, TARGET_WIDTH));
statusLabel = new JLabel("Ready for drop", JLabel.CENTER);
statusLabel.setPreferredSize(new Dimension(TARGET_WIDTH, STATUS_HEIGHT));
statusLabel.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
add(imageLabel, BorderLayout.CENTER);
add(statusLabel, BorderLayout.SOUTH);
maskPoints = new ArrayList();
maskPanel = new MaskPanel();
maskPanel.setOpaque(false);
layeredPane = new JLayeredPane();
layeredPane.setPreferredSize(new Dimension(TARGET_WIDTH, TARGET_WIDTH));
layeredPane.add(imageLabel, JLayeredPane.DEFAULT_LAYER);
layeredPane.add(maskPanel, JLayeredPane.PALETTE_LAYER);
add(layeredPane, BorderLayout.CENTER);
setupTransferHandler();
setupMouseListeners();
pack();
setLocationRelativeTo(null);
}
private void setupMouseListeners() {
maskPanel.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
if (originalImage != null)
{
maskPoints.clear();
maskPanel.clearMask();
addPoint(e.getPoint());
}
}
@Override
public void mouseReleased(MouseEvent e) {
if (originalImage != null) applyMask();
// maskPanel.clearMask();
// maskPoints.clear();
// maskPanel.repaint();
}
});
maskPanel.addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
addPoint(e.getPoint());
maskPanel.repaint();
}
});
}
private void addPoint(Point p) {
double scaleX = (double) originalImage.getWidth() / maskPanel.getWidth();
double scaleY = (double) originalImage.getHeight() / maskPanel.getHeight();
Point scaledPoint = new Point((int)(p.x * scaleX), (int)(p.y * scaleY));
if (!maskPoints.isEmpty()) {
Point lastPoint = maskPoints.get(maskPoints.size() - 1);
interpolatePoints(lastPoint, scaledPoint);
}
maskPoints.add(scaledPoint);
maskPanel.addPoint(p); // This calls the new addPoint method in MaskPanel
}
private void interpolatePoints(Point start, Point end) {
int steps = 5; // Adjust this for smoother or rougher interpolation
for (int i = 1; i < steps; i++) {
int x = start.x + (end.x - start.x) * i / steps;
int y = start.y + (end.y - start.y) * i / steps;
Point interpolatedPoint = new Point(x, y);
maskPoints.add(interpolatedPoint);
// Scale back down for display
double scaleX = (double) maskPanel.getWidth() / originalImage.getWidth();
double scaleY = (double) maskPanel.getHeight() / originalImage.getHeight();
Point displayPoint = new Point((int)(x * scaleX), (int)(y * scaleY));
maskPanel.addPoint(displayPoint);
}
}
private void applyMask() {
if (originalImage == null) return;
BufferedImage maskedImage = new BufferedImage(originalImage.getWidth(), originalImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = maskedImage.createGraphics();
g2d.drawImage(originalImage, 0, 0, null);
g2d.setColor(MASK_COLOR);
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.1f));
for (Point p : maskPoints) {
int x = (int) (p.getX() * originalImage.getWidth() / imageLabel.getWidth());
int y = (int) (p.getY() * originalImage.getHeight() / imageLabel.getHeight());
g2d.fill(new Ellipse2D.Double(x - BRUSH_SIZE/2.0, y - BRUSH_SIZE/2.0, BRUSH_SIZE, BRUSH_SIZE));
}
g2d.dispose();
displayMaskedImage(maskedImage);
}
private void displayImage(File file) throws IOException {
originalImage = ImageIO.read(file);
if (originalImage != null) {
maskPoints.clear();
maskPanel.clearMask();
int originalWidth = originalImage.getWidth();
int originalHeight = originalImage.getHeight();
int newHeight = (int) ((double) TARGET_WIDTH / originalWidth * originalHeight);
Image scaledImg = originalImage.getScaledInstance(TARGET_WIDTH, newHeight, Image.SCALE_SMOOTH);
ImageIcon icon = new ImageIcon(scaledImg);
imageLabel.setIcon(icon);
imageLabel.setText("");
imageLabel.setBounds(0, 0, TARGET_WIDTH, newHeight);
maskPanel.setBounds(0, 0, TARGET_WIDTH, newHeight);
layeredPane.setPreferredSize(new Dimension(TARGET_WIDTH, newHeight));
pack();
setLocationRelativeTo(null);
statusLabel.setText("Image loaded: " + file.getName());
}
}
private void displayMaskedImage(BufferedImage mImage) {
if (mImage != null) {
int newHeight = (int) ((double) TARGET_WIDTH / mImage.getWidth() * mImage.getHeight());
Image scaledImg = mImage.getScaledInstance(TARGET_WIDTH, newHeight, Image.SCALE_SMOOTH);
ImageIcon icon = new ImageIcon(scaledImg);
imageLabel.setIcon(icon);
imageLabel.setPreferredSize(new Dimension(TARGET_WIDTH, newHeight));
pack();
setLocationRelativeTo(null);
}
}
private void setupTransferHandler() {
layeredPane.setTransferHandler(new TransferHandler() {
@Override
public boolean canImport(TransferSupport support) {
return support.isDataFlavorSupported(DataFlavor.javaFileListFlavor);
}
@Override
public boolean importData(TransferSupport support) {
if (!canImport(support)) {
return false;
}
Transferable transferable = support.getTransferable();
try {
List files = (List) transferable.getTransferData(DataFlavor.javaFileListFlavor);
for (File file : files) {
if (isImageFile(file)) {
displayImage(file);
statusLabel.setText("Image loaded: " + file.getName());
return true;
}
}
statusLabel.setText("Drop failed: Not a valid image file");
} catch (Exception e) {
e.printStackTrace();
statusLabel.setText("Drop failed: " + e.getMessage());
}
return false;
}
});
}
private boolean isImageFile(File file) {
String name = file.getName().toLowerCase();
return name.endsWith(".jpg") || name.endsWith(".jpeg") || name.endsWith(".png") ||
name.endsWith(".gif") || name.endsWith(".bmp") || name.endsWith(".webp");
}
private class MaskPanel extends JPanel {
private BufferedImage maskImage;
public MaskPanel() {
maskImage = new BufferedImage(TARGET_WIDTH, TARGET_WIDTH, BufferedImage.TYPE_INT_ARGB);
}
public void clearMask() {
Graphics2D g2d = maskImage.createGraphics();
g2d.setComposite(AlphaComposite.Clear);
g2d.fillRect(0, 0, maskImage.getWidth(), maskImage.getHeight());
g2d.dispose();
}
public void addPoint(Point p) {
Graphics2D g2d = maskImage.createGraphics();
g2d.setColor(MASK_COLOR);
g2d.setComposite(AlphaComposite.SrcOver.derive(0.1f)); // 10% opacity
g2d.fill(new Ellipse2D.Double(p.x - BRUSH_SIZE/2.0, p.y - BRUSH_SIZE/2.0, BRUSH_SIZE, BRUSH_SIZE));
g2d.dispose();
repaint();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(maskImage, 0, 0, null);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new MaskWindow().setVisible(true);
});
}
}
Я попробовал SwingUtilities.invokelater для события mousePressed. ChatGPT предложил Swingworker, который не сработал. Даже вместо getScaledInstance в applyMask() я попытался использовать drawImage() и сначала клонировать замаскированную копию. Ничего не помогло!
Вот как это работает: Изображение импортируется путем перетаскивания. Мы применяем к нему маску (re: MaskPanel), а затем, когда мышь отпускается, новый BufferedImage создается из исходного изображения и маски (объединенных вместе) и отображается в imageLabel в JLayeredPane. И тогда маскаPanel очищается. Чего я не понимаю, так это того, что как только я отпускаю мышь, маска исчезает. Моя идея состоит в том, что к тому времени новое замаскированное изображение должно быть отображено в imageLabel. Что я делаю не так? Вот код: [code]package com.company.gui;
public class MaskWindow extends JFrame { private JLabel imageLabel; private JLabel statusLabel; private final int TARGET_WIDTH = 600; private final int STATUS_HEIGHT = 30; private BufferedImage originalImage; private ArrayList maskPoints; private static final int BRUSH_SIZE = 40; private static final Color MASK_COLOR = new Color(255, 0, 0, 10); // Semi-transparent red private MaskPanel maskPanel; private JLayeredPane layeredPane;
public MaskWindow() { setTitle("Image Mask Window"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new BorderLayout());
if (!maskPoints.isEmpty()) { Point lastPoint = maskPoints.get(maskPoints.size() - 1); interpolatePoints(lastPoint, scaledPoint); } maskPoints.add(scaledPoint); maskPanel.addPoint(p); // This calls the new addPoint method in MaskPanel } private void interpolatePoints(Point start, Point end) { int steps = 5; // Adjust this for smoother or rougher interpolation for (int i = 1; i < steps; i++) { int x = start.x + (end.x - start.x) * i / steps; int y = start.y + (end.y - start.y) * i / steps; Point interpolatedPoint = new Point(x, y); maskPoints.add(interpolatedPoint);
// Scale back down for display double scaleX = (double) maskPanel.getWidth() / originalImage.getWidth(); double scaleY = (double) maskPanel.getHeight() / originalImage.getHeight(); Point displayPoint = new Point((int)(x * scaleX), (int)(y * scaleY)); maskPanel.addPoint(displayPoint); } } private void applyMask() { if (originalImage == null) return;
for (Point p : maskPoints) { int x = (int) (p.getX() * originalImage.getWidth() / imageLabel.getWidth()); int y = (int) (p.getY() * originalImage.getHeight() / imageLabel.getHeight()); g2d.fill(new Ellipse2D.Double(x - BRUSH_SIZE/2.0, y - BRUSH_SIZE/2.0, BRUSH_SIZE, BRUSH_SIZE)); }
public static void main(String[] args) { SwingUtilities.invokeLater(() -> { new MaskWindow().setVisible(true); }); } } [/code] Я попробовал SwingUtilities.invokelater для события mousePressed. ChatGPT предложил Swingworker, который не сработал. Даже вместо getScaledInstance в applyMask() я попытался использовать drawImage() и сначала клонировать замаскированную копию. Ничего не помогло!