mirror of
https://github.com/mitb-archive/filebot
synced 2025-03-10 14:31:04 -04:00
Experiment with @2x artwork thumbnail support
This commit is contained in:
parent
fdec4090d2
commit
a69f51036c
@ -64,17 +64,12 @@ public final class ResourceManager {
|
|||||||
image.add(ImageIO.read(r));
|
image.add(ImageIO.read(r));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Windows 10: use @2x image for non-integer scale factors 1.25 / 1.5 / 1.75
|
// Windows 10: use down-scaled @2x image for non-integer scale factors 1.25 / 1.5 / 1.75
|
||||||
if (PRIMARY_SCALE_FACTOR != 1 && PRIMARY_SCALE_FACTOR != 2) {
|
if (PRIMARY_SCALE_FACTOR > 1 && PRIMARY_SCALE_FACTOR < 2 && image.size() >= 2) {
|
||||||
BufferedImage hidpi = image.get(image.size() - 1);
|
image.add(1, scale(PRIMARY_SCALE_FACTOR / 2, image.get(1)));
|
||||||
if (PRIMARY_SCALE_FACTOR < 2) {
|
|
||||||
image.add(1, scale(PRIMARY_SCALE_FACTOR, hidpi));
|
|
||||||
} else {
|
|
||||||
image.add(scale(PRIMARY_SCALE_FACTOR, hidpi));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new BaseMultiResolutionImage(image.toArray(new Image[0]));
|
return new BaseMultiResolutionImage(image.toArray(Image[]::new));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
@ -90,7 +85,7 @@ public final class ResourceManager {
|
|||||||
|
|
||||||
public static final double PRIMARY_SCALE_FACTOR = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration().getDefaultTransform().getScaleX();
|
public static final double PRIMARY_SCALE_FACTOR = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration().getDefaultTransform().getScaleX();
|
||||||
|
|
||||||
private static BufferedImage scale(double scale, BufferedImage image) {
|
public static BufferedImage scale(double scale, BufferedImage image) {
|
||||||
int w = (int) (scale * image.getWidth());
|
int w = (int) (scale * image.getWidth());
|
||||||
int h = (int) (scale * image.getHeight());
|
int h = (int) (scale * image.getHeight());
|
||||||
return Scalr.resize(image, Method.ULTRA_QUALITY, Mode.FIT_TO_WIDTH, w, h, Scalr.OP_ANTIALIAS);
|
return Scalr.resize(image, Method.ULTRA_QUALITY, Mode.FIT_TO_WIDTH, w, h, Scalr.OP_ANTIALIAS);
|
||||||
|
@ -3,8 +3,13 @@ package net.filebot;
|
|||||||
import static java.nio.charset.StandardCharsets.*;
|
import static java.nio.charset.StandardCharsets.*;
|
||||||
import static java.util.stream.Collectors.*;
|
import static java.util.stream.Collectors.*;
|
||||||
import static net.filebot.Logging.*;
|
import static net.filebot.Logging.*;
|
||||||
|
import static net.filebot.ResourceManager.*;
|
||||||
import static net.filebot.util.RegularExpressions.*;
|
import static net.filebot.util.RegularExpressions.*;
|
||||||
|
|
||||||
|
import java.awt.Image;
|
||||||
|
import java.awt.image.BaseMultiResolutionImage;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
@ -12,6 +17,7 @@ import java.net.http.HttpRequest;
|
|||||||
import java.net.http.HttpResponse;
|
import java.net.http.HttpResponse;
|
||||||
import java.net.http.HttpResponse.BodyHandlers;
|
import java.net.http.HttpResponse.BodyHandlers;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -19,6 +25,7 @@ import java.util.Set;
|
|||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
|
|
||||||
@ -35,12 +42,17 @@ public enum ThumbnailServices implements ThumbnailProvider {
|
|||||||
return "https://api.filebot.net/images/" + name().toLowerCase() + "/thumb/poster/" + file;
|
return "https://api.filebot.net/images/" + name().toLowerCase() + "/thumb/poster/" + file;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Cache getCache() {
|
protected String getThumbnailResource(int id, ResolutionVariant variant) {
|
||||||
return Cache.getCache("thumbnail_" + ordinal(), CacheType.Persistent);
|
return variant == ResolutionVariant.NORMAL ? id + ".png" : id + "@2x.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Cache getCache(ResolutionVariant variant) {
|
||||||
|
return Cache.getCache("thumbnail_" + ordinal() + "_" + variant.ordinal(), CacheType.Persistent);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Set<Integer> getIndex() throws Exception {
|
protected Set<Integer> getIndex() throws Exception {
|
||||||
byte[] bytes = getCache().bytes("index.txt.xz", n -> new URL(getResource(n)), XZInputStream::new).expire(Cache.ONE_MONTH).get();
|
Cache cache = getCache(ResolutionVariant.NORMAL);
|
||||||
|
byte[] bytes = cache.bytes("index.txt.xz", n -> new URL(getResource(n)), XZInputStream::new).expire(Cache.ONE_MONTH).get();
|
||||||
|
|
||||||
// all data files are UTF-8 encoded XZ compressed text files
|
// all data files are UTF-8 encoded XZ compressed text files
|
||||||
return NEWLINE.splitAsStream(UTF_8.decode(ByteBuffer.wrap(bytes))).filter(s -> s.length() > 0).map(Integer::parseInt).collect(toSet());
|
return NEWLINE.splitAsStream(UTF_8.decode(ByteBuffer.wrap(bytes))).filter(s -> s.length() > 0).map(Integer::parseInt).collect(toSet());
|
||||||
@ -48,8 +60,11 @@ public enum ThumbnailServices implements ThumbnailProvider {
|
|||||||
|
|
||||||
private final Resource<Set<Integer>> index = Resource.lazy(this::getIndex);
|
private final Resource<Set<Integer>> index = Resource.lazy(this::getIndex);
|
||||||
|
|
||||||
public byte[][] getThumbnails(int[] ids) throws Exception {
|
// shared HTTP Client instance for all thumbnail requests
|
||||||
Cache cache = getCache();
|
private static final Resource<HttpClient> http = Resource.lazy(HttpClient::newHttpClient);
|
||||||
|
|
||||||
|
public byte[][] getThumbnails(int[] ids, ResolutionVariant variant) throws Exception {
|
||||||
|
Cache cache = getCache(variant);
|
||||||
byte[][] response = new byte[ids.length][];
|
byte[][] response = new byte[ids.length][];
|
||||||
|
|
||||||
synchronized (index) {
|
synchronized (index) {
|
||||||
@ -63,10 +78,10 @@ public enum ThumbnailServices implements ThumbnailProvider {
|
|||||||
|
|
||||||
for (int i = 0; i < response.length; i++) {
|
for (int i = 0; i < response.length; i++) {
|
||||||
if (response[i] == null && index.get().contains(ids[i])) {
|
if (response[i] == null && index.get().contains(ids[i])) {
|
||||||
URI r = URI.create(getResource(ids[i] + ".png"));
|
String resource = getThumbnailResource(ids[i], variant);
|
||||||
request[i] = http.get().sendAsync(HttpRequest.newBuilder(r).build(), BodyHandlers.ofByteArray());
|
request[i] = http.get().sendAsync(HttpRequest.newBuilder(URI.create(resource)).build(), BodyHandlers.ofByteArray());
|
||||||
|
|
||||||
debug.fine(format("Request %s", r));
|
debug.fine(format("Request %s", resource));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,14 +106,16 @@ public enum ThumbnailServices implements ThumbnailProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<SearchResult, Icon> getThumbnails(List<SearchResult> keys) throws Exception {
|
public Map<SearchResult, Icon> getThumbnails(List<SearchResult> keys) throws Exception {
|
||||||
|
ResolutionVariant variant = PRIMARY_SCALE_FACTOR > 1 ? ResolutionVariant.RETINA : ResolutionVariant.NORMAL;
|
||||||
|
|
||||||
int[] ids = keys.stream().mapToInt(SearchResult::getId).toArray();
|
int[] ids = keys.stream().mapToInt(SearchResult::getId).toArray();
|
||||||
byte[][] thumbnails = getThumbnails(ids);
|
byte[][] thumbnails = getThumbnails(ids, variant);
|
||||||
|
|
||||||
Map<SearchResult, Icon> icons = new HashMap<>(thumbnails.length);
|
Map<SearchResult, Icon> icons = new HashMap<>(thumbnails.length);
|
||||||
for (int i = 0; i < thumbnails.length; i++) {
|
for (int i = 0; i < thumbnails.length; i++) {
|
||||||
if (thumbnails[i] != null && thumbnails[i].length > 0) {
|
if (thumbnails[i] != null && thumbnails[i].length > 0) {
|
||||||
try {
|
try {
|
||||||
icons.put(keys.get(i), new ImageIcon(thumbnails[i]));
|
icons.put(keys.get(i), getScaledIcon(thumbnails[i], variant));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
debug.log(Level.SEVERE, e, e::toString);
|
debug.log(Level.SEVERE, e, e::toString);
|
||||||
}
|
}
|
||||||
@ -108,7 +125,40 @@ public enum ThumbnailServices implements ThumbnailProvider {
|
|||||||
return icons;
|
return icons;
|
||||||
}
|
}
|
||||||
|
|
||||||
// shared HTTP Client instance for all thumbnail requests
|
protected Icon getScaledIcon(byte[] bytes, ResolutionVariant variant) throws Exception {
|
||||||
private static final Resource<HttpClient> http = Resource.lazy(HttpClient::newHttpClient);
|
// Load multi-resolution images only if necessary
|
||||||
|
if (PRIMARY_SCALE_FACTOR == 1 && variant == ResolutionVariant.NORMAL) {
|
||||||
|
return new ImageIcon(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedImage baseImage = ImageIO.read(new ByteArrayInputStream(bytes));
|
||||||
|
double baseScale = variant.scaleFactor;
|
||||||
|
|
||||||
|
List<BufferedImage> image = new ArrayList<BufferedImage>(3);
|
||||||
|
image.add(baseImage);
|
||||||
|
|
||||||
|
// use down-scaled @2x image as @1x base image
|
||||||
|
if (baseScale > 1) {
|
||||||
|
image.add(0, scale(1 / baseScale, baseImage));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Windows 10: use down-scaled @2x image for non-integer scale factors 1.25 / 1.5 / 1.75
|
||||||
|
if (PRIMARY_SCALE_FACTOR > 1 && PRIMARY_SCALE_FACTOR < 2 && image.size() >= 2) {
|
||||||
|
image.add(1, scale(PRIMARY_SCALE_FACTOR / 2, image.get(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ImageIcon(new BaseMultiResolutionImage(image.toArray(Image[]::new)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ResolutionVariant {
|
||||||
|
|
||||||
|
NORMAL(1), RETINA(2);
|
||||||
|
|
||||||
|
public final int scaleFactor;
|
||||||
|
|
||||||
|
private ResolutionVariant(int scaleFactor) {
|
||||||
|
this.scaleFactor = scaleFactor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user