mirror of
https://gitlab.com/annimon/imagetagger.git
synced 2024-09-19 14:34:21 +03:00
Add ResizableImage to reduce scalings count due to endless repaint
This commit is contained in:
parent
385f6d7c13
commit
27ab04293a
@ -1,6 +1,7 @@
|
||||
package com.annimon.imagetagger;
|
||||
|
||||
import com.annimon.imagetagger.beans.Config;
|
||||
import com.annimon.imagetagger.logic.FilesProvider;
|
||||
import com.annimon.imagetagger.logic.ImageProcessor;
|
||||
import com.annimon.imagetagger.logic.KeyProcessor;
|
||||
import com.annimon.imagetagger.views.ImagePanel;
|
||||
@ -33,7 +34,8 @@ public class Main extends JFrame {
|
||||
|
||||
final var tagButtons = config.getButtons(config.getProfile());
|
||||
final var tagPanel = new TagPanel(tagButtons);
|
||||
final var imageProcessor = new ImageProcessor(config.getDir(), config.getFilter(), config.getSort());
|
||||
final var files = new FilesProvider(config.getDir());
|
||||
final var imageProcessor = new ImageProcessor(files, config.getFilter(), config.getSort());
|
||||
final var imagePanel = new ImagePanel(imageProcessor);
|
||||
final var keyProcessor = new KeyProcessor(tagButtons);
|
||||
keyProcessor.setTagPanel(tagPanel);
|
||||
|
@ -0,0 +1,35 @@
|
||||
package com.annimon.imagetagger.logic;
|
||||
|
||||
import com.annimon.imagetagger.views.ResizableImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Stream;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
public class FilesProvider {
|
||||
|
||||
protected final File dir;
|
||||
|
||||
public FilesProvider(String dir) {
|
||||
this.dir = new File(dir);
|
||||
}
|
||||
|
||||
public Stream<File> stream() {
|
||||
final var files = dir.listFiles();
|
||||
if (files == null) {
|
||||
throw new RuntimeException("There are no files in directory " + dir);
|
||||
}
|
||||
return Arrays.stream(files);
|
||||
}
|
||||
|
||||
public ResizableImage loadImage(File file) {
|
||||
try {
|
||||
return new ResizableImage(ImageIO.read(file));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void preloadImage(File file) { }
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
package com.annimon.imagetagger.logic;
|
||||
|
||||
import com.annimon.imagetagger.beans.ImageInfo;
|
||||
import java.awt.*;
|
||||
import com.annimon.imagetagger.views.ResizableImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@ -13,28 +12,27 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
public class ImageProcessor {
|
||||
|
||||
private final File dir;
|
||||
private final FilesProvider files;
|
||||
private final String filter;
|
||||
private final String sort;
|
||||
private final List<ImageInfo> imageInfos;
|
||||
private int imagesCount;
|
||||
private int index;
|
||||
private ImageInfo currentInfo;
|
||||
private Image image;
|
||||
private ResizableImage image;
|
||||
|
||||
public ImageProcessor(String dir, String filter, String sort) {
|
||||
this.dir = new File(dir);
|
||||
public ImageProcessor(FilesProvider files, String filter, String sort) {
|
||||
this.files = files;
|
||||
this.filter = filter;
|
||||
this.sort = sort;
|
||||
imageInfos = new ArrayList<>();
|
||||
imagesCount = 0;
|
||||
}
|
||||
|
||||
public Image getImage() {
|
||||
public ResizableImage getImage() {
|
||||
return image;
|
||||
}
|
||||
|
||||
@ -57,6 +55,7 @@ public class ImageProcessor {
|
||||
index = imagesCount - 1;
|
||||
}
|
||||
loadImage();
|
||||
preloadImage(-1);
|
||||
}
|
||||
|
||||
public void nextImage() {
|
||||
@ -65,6 +64,7 @@ public class ImageProcessor {
|
||||
index = 0;
|
||||
}
|
||||
loadImage();
|
||||
preloadImage(1);
|
||||
}
|
||||
|
||||
public void writeTagsToFile() {
|
||||
@ -90,11 +90,7 @@ public class ImageProcessor {
|
||||
}
|
||||
|
||||
public void populateFiles() {
|
||||
final var files = dir.listFiles();
|
||||
if (files == null) {
|
||||
throw new RuntimeException("There are no files in directory " + dir);
|
||||
}
|
||||
final var infos = Arrays.stream(files)
|
||||
final var infos = files.stream()
|
||||
.filter(this::allowedFiles)
|
||||
.map(ImageInfo::new)
|
||||
.map(ImageInfo::loadTagFile)
|
||||
@ -114,11 +110,17 @@ public class ImageProcessor {
|
||||
if (index < 0 || index >= imagesCount) return;
|
||||
|
||||
currentInfo = imageInfos.get(index);
|
||||
try {
|
||||
image = ImageIO.read(currentInfo.getFile());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
image = files.loadImage(currentInfo.getFile());
|
||||
}
|
||||
|
||||
private void preloadImage(int delta) {
|
||||
int newIndex = index + delta;
|
||||
if (newIndex < 0) {
|
||||
newIndex = imagesCount - 1;
|
||||
} else if (newIndex >= imagesCount) {
|
||||
newIndex = 0;
|
||||
}
|
||||
files.preloadImage(imageInfos.get(newIndex).getFile());
|
||||
}
|
||||
|
||||
private boolean allowedFiles(File file) {
|
||||
|
@ -2,9 +2,8 @@ package com.annimon.imagetagger.views;
|
||||
|
||||
import com.annimon.imagetagger.logic.ImageProcessor;
|
||||
import java.awt.*;
|
||||
import javax.swing.*;
|
||||
|
||||
public class ImagePanel extends JPanel {
|
||||
public class ImagePanel extends Component {
|
||||
|
||||
private static final Color TEXT_COLOR = new Color(0x80FFFFFF, true);
|
||||
private final ImageProcessor imageProcessor;
|
||||
@ -13,38 +12,27 @@ public class ImagePanel extends JPanel {
|
||||
this.imageProcessor = imageProcessor;
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedAssignment")
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
public void paint(Graphics g) {
|
||||
super.paint(g);
|
||||
final var g2d = (Graphics2D) g;
|
||||
final int width = getWidth();
|
||||
final int height = getHeight();
|
||||
var image = imageProcessor.getImage();
|
||||
if ((width <= 5) || (height <= 5) || (image == null)) {
|
||||
final var resizableImage = imageProcessor.getImage();
|
||||
if ((width <= 5) || (height <= 5) || (resizableImage == null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int imgWidth = image.getWidth(this);
|
||||
final int imgHeight = image.getHeight(this);
|
||||
int scaleX = imgWidth;
|
||||
int scaleY = imgHeight;
|
||||
var image = resizableImage.getOriginal();
|
||||
int imgWidth = resizableImage.getOriginalWidth();
|
||||
int imgHeight = resizableImage.getOriginalHeight();
|
||||
if (imgWidth > width || imgHeight > height) {
|
||||
if (scaleX > width) {
|
||||
// Fit by width
|
||||
scaleX = width;
|
||||
scaleY = imgHeight * scaleX / imgWidth;
|
||||
}
|
||||
if (scaleY > height) {
|
||||
// Fit by height
|
||||
scaleY = height;
|
||||
scaleX = imgWidth * scaleY / imgHeight;
|
||||
}
|
||||
image = image.getScaledInstance(scaleX, scaleY, Image.SCALE_SMOOTH);
|
||||
image = resizableImage.getResized(width, height);
|
||||
imgWidth = resizableImage.getResizedWidth();
|
||||
imgHeight = resizableImage.getResizedHeight();
|
||||
}
|
||||
|
||||
final int x = (width - scaleX) / 2;
|
||||
final int y = (height - scaleY) / 2;
|
||||
final int x = (width - imgWidth) / 2;
|
||||
final int y = (height - imgHeight) / 2;
|
||||
g2d.drawImage(image, x, y, this);
|
||||
|
||||
int yy = 5;
|
||||
|
@ -0,0 +1,82 @@
|
||||
package com.annimon.imagetagger.views;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
public class ResizableImage {
|
||||
|
||||
private final Image original;
|
||||
private Image resizedImage;
|
||||
private int resizedWidth, resizedHeight;
|
||||
|
||||
public ResizableImage(Image original) {
|
||||
this.original = original;
|
||||
}
|
||||
|
||||
public Image getOriginal() {
|
||||
return original;
|
||||
}
|
||||
|
||||
public BufferedImage getOriginalAsBufferedImage() {
|
||||
return toBufferedImage(original);
|
||||
}
|
||||
|
||||
public int getOriginalWidth() {
|
||||
return original.getWidth(null);
|
||||
}
|
||||
|
||||
public int getOriginalHeight() {
|
||||
return original.getHeight(null);
|
||||
}
|
||||
|
||||
public Image getResized(int prefWidth, int prefHeight) {
|
||||
boolean sizeMatched = (resizedWidth == prefWidth) || (resizedHeight == prefHeight);
|
||||
if (resizedImage == null || !sizeMatched) {
|
||||
resizedImage = resizeIfNecessary(original, prefWidth, prefHeight);
|
||||
resizedWidth = resizedImage.getWidth(null);
|
||||
resizedHeight = resizedImage.getHeight(null);
|
||||
}
|
||||
return resizedImage;
|
||||
}
|
||||
|
||||
public BufferedImage getResizedAsBufferedImage(int prefWidth, int prefHeight) {
|
||||
return toBufferedImage(getResized(prefWidth, prefHeight));
|
||||
}
|
||||
|
||||
public int getResizedWidth() {
|
||||
return resizedWidth;
|
||||
}
|
||||
|
||||
public int getResizedHeight() {
|
||||
return resizedHeight;
|
||||
}
|
||||
|
||||
private static Image resizeIfNecessary(Image image, int prefWidth, int prefHeight) {
|
||||
final int width = image.getWidth(null);
|
||||
final int height = image.getHeight(null);
|
||||
if (width <= prefWidth && height <= prefHeight) {
|
||||
return image;
|
||||
}
|
||||
int newWidth = width;
|
||||
int newHeight = height;
|
||||
if (width > prefWidth) {
|
||||
newWidth = prefWidth;
|
||||
newHeight = height * newWidth / width;
|
||||
}
|
||||
if (newHeight > prefHeight) {
|
||||
newHeight = prefHeight;
|
||||
newWidth = width * newHeight / height;
|
||||
}
|
||||
return image.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH);
|
||||
}
|
||||
|
||||
private static BufferedImage toBufferedImage(Image image) {
|
||||
final int width = image.getWidth(null);
|
||||
final int height = image.getHeight(null);
|
||||
final var img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
final var g = img.createGraphics();
|
||||
g.drawImage(image, 0, 0, null);
|
||||
g.dispose();
|
||||
return img;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user