Изменить размер изображения без увеличения - PullRequest
1 голос
/ 20 февраля 2020

У меня есть следующее изображение

Image to resize

Теперь я хочу изменить его размер, чтобы рисовать в качестве фона для метки, чтобы границы оставались оригинальными size.

Вот код:

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Insets;
import java.awt.image.BufferedImage;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.border.EmptyBorder;

/**
 * <code>BackgroundTest</code>.
 */
public class BackgroundTest extends JLabel {

    // probably you need to download it and change the URL
    private static final Image BACKGROUND = read(
            "https://i.stack.imgur.com/MIwyR.png");

    private static Image read(String url) {
        try {
            return ImageIO.read(new URL(url));
        } catch (Exception e) {
            throw new IllegalArgumentException("Cannot resolve image!", e);
        }
    }

    @Override
    protected void paintComponent(Graphics g) {
        g.drawImage(scaleRect(BACKGROUND, new Insets(7, 7, 7, 7), getWidth(), getHeight()), 0, 0, this);
        super.paintComponent(g);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(BackgroundTest::startUp);
    }

    private static void startUp() {
        BackgroundTest label = new BackgroundTest();
        label.setOpaque(false);
        label.setText("<html>Simple multiline test to check<br>the painting</html>");
        label.setBorder(new EmptyBorder(7, 7, 7, 7));
        JFrame frm = new JFrame("Test");
        JPanel panel = new JPanel();
        panel.add(label);
        panel.add(new JLabel(new ImageIcon(BACKGROUND)));
        frm.add(panel);
        frm.setSize(300, 300);
        frm.setLocationRelativeTo(null);
        frm.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        frm.setVisible(true);
    }

    private Image scaleRect(Image src, Insets safePart, int width, int height) {
        BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = result.createGraphics();

        g.drawImage(src, 0, 0, width, height, this);
        g.dispose();
        return result;
    }
}

Результат выглядит ужасно, потому что границы увеличены:

Ugly result

В этом примере кода мне нужна только правильная реализация функции scaleRect, чтобы она учитывала вставки границ (параметр safePart). В классе Graphics2D есть и другие методы drawImage, но я не знаю, как их использовать для достижения моей цели.

Ответы [ 2 ]

3 голосов
/ 21 февраля 2020

Вы можете использовать 9-patch подход к изображению - в основном, разделите ваше изображение на 9 частей, некоторые из которых можно растянуть без каких-либо визуальных артефактов, а некоторые останутся с фиксированным размером.

По сути, обрежьте его по следующим линиям:

enter image description here

Затем вы можете растянуть:

  • верхний центр и нижний центр по горизонтали (при необходимости)
  • по левому центру и по центру вправо по вертикали (при необходимости)
  • средний можно растягивать как по горизонтали, так и по вертикали
  • углы должны оставаться фиксированных размеров, в противном случае вы получите артефакты

Также вам не нужно вырезать изображение, вы можете просто нарисовать определенную c часть изображения:

public class SampleComponent extends JLabel
{
    @Override
    protected void paintComponent ( final Graphics g )
    {
        final Graphics2D g2d = ( Graphics2D ) g;

        final Image image = ...; // Your image
        g2d.drawImage (
                image,
                dstX1, dstY1, dstX2, dstY2,
                imgX1, imgY1, imgX2, imgY2,
                null
        );

        super.paintComponent ( g );
    }
}
  • dst* - координаты прямоугольника назначения рисования
  • img* - координаты прямоугольника на вашем изображении

Обратите внимание, что этот подход будет работать только с изображениями, которые вы на самом деле может быть разделен, как показано Впрочем, очевидно, что это не сработает для чего-то вроде фотографий или картин, но в этих случаях я не знаю, зачем вам в первую очередь их масштабировать.

1 голос
/ 21 февраля 2020

Большое спасибо @MikleGarin за идею. Вот полное решение:

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Insets;
import java.awt.image.BufferedImage;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.border.EmptyBorder;

/**
 * <code>BackgroundTest</code>.
 */
public class BackgroundTest extends JLabel {

    // probably you need to download it and change the URL
    private static final Image BACKGROUND = read(
            "https://i.stack.imgur.com/MIwyR.png");

    private static Image read(String url) {
        try {
            return ImageIO.read(new URL(url));
        } catch (Exception e) {
            throw new IllegalArgumentException("Cannot resolve image!", e);
        }
    }

    @Override
    protected void paintComponent(Graphics g) {
        g.drawImage(scaleRect(BACKGROUND, new Insets(7, 7, 7, 7), getWidth(), getHeight()), 0, 0, this);
        super.paintComponent(g);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(BackgroundTest::startUp);
    }

    private static void startUp() {
        BackgroundTest label = new BackgroundTest();
        label.setOpaque(false);
        label.setText("<html>Simple multiline test to check<br>the painting</html>");
        label.setBorder(new EmptyBorder(7, 7, 7, 7));
        JFrame frm = new JFrame("Test");
        JPanel panel = new JPanel();
        panel.add(label);
        panel.add(new JLabel(new ImageIcon(BACKGROUND)));
        frm.add(panel);
        frm.setSize(300, 300);
        frm.setLocationRelativeTo(null);
        frm.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        frm.setVisible(true);
    }

    private Image scaleRect(Image src, Insets safePart, int width, int height) {
        BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = result.createGraphics();

        int srcWidth = src.getWidth(null);
        int srcHeight = src.getHeight(null);
        // top left
        g.drawImage(src, 0, 0, safePart.left, safePart.right, 0, 0, safePart.left, safePart.top, null);
        // top center
        g.drawImage(src, safePart.left, 0, width - safePart.right, safePart.top, safePart.left, 0,
                srcWidth - safePart.right, safePart.top, null);
        // top right
        g.drawImage(src, width - safePart.right, 0, width, safePart.top, srcWidth - safePart.right, 0,
                srcWidth, safePart.top, null);
        // center left
        g.drawImage(src, 0, safePart.top, safePart.left, height - safePart.bottom, 0, safePart.top,
                safePart.left, srcHeight - safePart.bottom, null);
        // center
        g.drawImage(src, safePart.left, safePart.top, width - safePart.right, height - safePart.bottom, safePart.left, safePart.top,
                srcWidth - safePart.right, srcHeight - safePart.bottom, null);
        // center right
        g.drawImage(src, width - safePart.right, safePart.top, width, height - safePart.bottom, srcWidth - safePart.right, safePart.top,
                srcWidth, srcHeight - safePart.bottom, null);
        // bottom left
        g.drawImage(src, 0, height - safePart.bottom, safePart.left, height, 0, srcHeight - safePart.bottom, safePart.left, srcHeight,
                null);
        // bottom middle
        g.drawImage(src, safePart.left, height - safePart.bottom, width - safePart.right, height, safePart.left,
                srcHeight - safePart.bottom, srcWidth - safePart.right, srcHeight, null);
        // bottom right
        g.drawImage(src, width - safePart.right, height - safePart.bottom, width, height, srcWidth - safePart.right,
                srcHeight - safePart.bottom, srcWidth, srcHeight, null);
        g.dispose();
        return result;
    }
}

Теперь это выглядит так, как нужно:

Correct Border

...