Чтобы масштабировать изображение, вам нужно создать новое изображение и нарисовать в нем.Одним из способов является использование filter()
метода AffineTransferOp
, как предлагается здесь .Это позволяет вам выбрать метод интерполяции.
private static BufferedImage scale1(BufferedImage before, double scale) {
int w = before.getWidth();
int h = before.getHeight();
// Create a new image of the proper size
int w2 = (int) (w * scale);
int h2 = (int) (h * scale);
BufferedImage after = new BufferedImage(w2, h2, BufferedImage.TYPE_INT_ARGB);
AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale);
AffineTransformOp scaleOp
= new AffineTransformOp(scaleInstance, AffineTransformOp.TYPE_BILINEAR);
scaleOp.filter(before, after);
return after;
}
Другой способ - просто нарисовать исходное изображение в новом изображении, используя операцию масштабирования для масштабирования.Этот метод очень похож, но он также иллюстрирует, как вы можете нарисовать все, что вы хотите в конечном изображении.(Я вставил пустую строку, где два метода начинают различаться.)
private static BufferedImage scale2(BufferedImage before, double scale) {
int w = before.getWidth();
int h = before.getHeight();
// Create a new image of the proper size
int w2 = (int) (w * scale);
int h2 = (int) (h * scale);
BufferedImage after = new BufferedImage(w2, h2, BufferedImage.TYPE_INT_ARGB);
AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale);
AffineTransformOp scaleOp
= new AffineTransformOp(scaleInstance, AffineTransformOp.TYPE_BILINEAR);
Graphics2D g2 = (Graphics2D) after.getGraphics();
// Here, you may draw anything you want into the new image, but we're
// drawing a scaled version of the original image.
g2.drawImage(before, scaleOp, 0, 0);
g2.dispose();
return after;
}
Приложение: результаты
Чтобы проиллюстрировать различия, я сравнил результатыпять методов ниже.Вот как выглядят результаты, увеличенные и уменьшенные вместе с данными о производительности.(Производительность варьируется от одного прогона к другому, поэтому используйте эти цифры только в качестве приблизительных ориентиров.) Верхнее изображение - оригинал.Я масштабирую его вдвое и наполовину.
Как видите, AffineTransformOp.filter()
, используемый в scaleBilinear()
, быстрее, чем стандартный метод рисования Graphics2D.drawImage()
в scale2()
.Также BiCubic интерполяция является самой медленной, но дает лучшие результаты при расширении изображения.(По производительности его следует сравнивать только с scaleBilinear()
и scaleNearest().
). Билинейный режим лучше подходит для сжатия изображения, хотя это сложный вызов.И NearestNeighbor самый быстрый, с худшими результатами.Билинейный, кажется, лучший компромисс между скоростью и качеством.Image.getScaledInstance()
, вызванный методом questionable()
, работал очень плохо и возвращал то же низкое качество, что и NearestNeighbor.(Номера производительности приведены только для увеличения изображения.)
public static BufferedImage scaleBilinear(BufferedImage before, double scale) {
final int interpolation = AffineTransformOp.TYPE_BILINEAR;
return scale(before, scale, interpolation);
}
public static BufferedImage scaleBicubic(BufferedImage before, double scale) {
final int interpolation = AffineTransformOp.TYPE_BICUBIC;
return scale(before, scale, interpolation);
}
public static BufferedImage scaleNearest(BufferedImage before, double scale) {
final int interpolation = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;
return scale(before, scale, interpolation);
}
@NotNull
private static
BufferedImage scale(final BufferedImage before, final double scale, final int type) {
int w = before.getWidth();
int h = before.getHeight();
int w2 = (int) (w * scale);
int h2 = (int) (h * scale);
BufferedImage after = new BufferedImage(w2, h2, before.getType());
AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale);
AffineTransformOp scaleOp = new AffineTransformOp(scaleInstance, type);
scaleOp.filter(before, after);
return after;
}
/**
* This is a more generic solution. It produces the same result, but it shows how you
* can draw anything you want into the newly created image. It's slower
* than scaleBilinear().
* @param before The original image
* @param scale The scale factor
* @return A scaled version of the original image
*/
private static BufferedImage scale2(BufferedImage before, double scale) {
int w = before.getWidth();
int h = before.getHeight();
// Create a new image of the proper size
int w2 = (int) (w * scale);
int h2 = (int) (h * scale);
BufferedImage after = new BufferedImage(w2, h2, before.getType());
AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale);
AffineTransformOp scaleOp
= new AffineTransformOp(scaleInstance, AffineTransformOp.TYPE_BILINEAR);
Graphics2D g2 = (Graphics2D) after.getGraphics();
// Here, you may draw anything you want into the new image, but we're just drawing
// a scaled version of the original image. This is slower than
// calling scaleOp.filter().
g2.drawImage(before, scaleOp, 0, 0);
g2.dispose();
return after;
}
/**
* I call this one "questionable" because it uses the questionable getScaledImage()
* method. This method is no longer favored because it's slow, as my tests confirm.
* @param before The original image
* @param scale The scale factor
* @return The scaled image.
*/
private static Image questionable(final BufferedImage before, double scale) {
int w2 = (int) (before.getWidth() * scale);
int h2 = (int) (before.getHeight() * scale);
return before.getScaledInstance(w2, h2, Image.SCALE_FAST);
}