我能否以更高分辨率提供 Java 中的图像图标以避免缩放后图标模糊?
Can I supply image icons in Java in a higher resolution to avoid blurred icons after scaling?
我正在使用 Java Swing 和 AWT (Java 8) 设计一个 GUI,并且我正在努力使用我使用的图标。
我加载了一个大的 PNG 图像并将其缩放为 18x18 像素,然后在按钮或标签中使用它。当操作系统不放大时,它在所有分辨率下都能正常工作。
但是,随着大屏幕分辨率 (hidpi) 的出现,通常的做法是使用操作系统设置来放大用户界面控件,包括 Java 应用程序中的按钮和此类内容。例如,在 Windows 上,我使用 4K 分辨率对用户元素进行 150% 或 200% 缩放,以确保用户界面仍然可用。我想很多用户也会这样做。
然而,在这种情况下,图标只是在缩小到 18x18 像素后尺寸有所增加。也就是说,我首先将它们缩小,然后操作系统尝试使用图像中仍然存在的少量信息再次将它们放大。
在使用操作系统的zooming/scaling功能时,有什么方法可以设计基于更高分辨率的Java中的图像图标,以避免它们显得模糊吗?
这是一个工作示例:
import java.awt.Container;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
@SuppressWarnings("serial")
class Example extends JFrame {
public static void main(String[] args) {
new Example();
}
public Example() {
Container c = getContentPane();
JPanel panel = new JPanel();
ImageIcon icon = new ImageIcon(new ImageIcon(getClass().getResource("tabler-icon-beach.png")).getImage().getScaledInstance(18, 18, Image.SCALE_SMOOTH));
JButton button = new JButton("Test button", icon);
panel.add(button);
c.add(panel);
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
}
您可以找到图标 here。所有图标均以 PNG 或 SVG 文件形式提供。
为了说明问题,先给大家看两张正常100%屏幕分辨率的截图:
开启 Linux 100% 缩放:
开启 Windows 100% 缩放:
而现在当我设置Windows 7 将布局元素放大200% 时,显然只是18x18px 版本被拉伸,变得模糊:
有什么方法可以提供更高分辨率的图像图标,以便在操作系统使用大于 100% 的缩放比例时使用?此外,您可以看到即使是 100% 的图像质量也不完美;还有什么办法可以改善吗?
Java 8 不支持高 DPI,UI 被放大 Windows。您应该使用支持 per-monitor 高 DPI 设置的 Java 11 或更高版本。
如果您的目标是使图标看起来清晰,请使用 BaseMultiResolutionImage
(the basic implementation of MultiResolutionImage
) 准备一组不同分辨率的图标以提供更高分辨率的替代方案。 (这些在 Java 8 中不可用。)
你说你把原图(240×240)缩小到18×18px。如果UI根据系统设置需要更高的分辨率,它现在只有你的小图标(18×18)会被放大,导致质量很差。您应该使用 MultiResolutionImage
或将原始图像绘制成所需的大小,让 Graphics
为您缩小它。
没有Down-Scale
这是我在不缩小原始图像尺寸的情况下制作 18×18 图标的最简单方法:
private static final String IMAGE_URL =
"https://tabler-icons.io/static/tabler-icons/icons-png/beach.png";
private static ImageIcon getIcon() {
return new ImageIcon(Toolkit.getDefaultToolkit()
.getImage(new URL(IMAGE_URL))) {
@Override
public int getIconWidth() {
return 18;
}
@Override
public int getIconHeight() {
return 18;
}
@Override
public synchronized void paintIcon(Component c, Graphics g,
int x, int y) {
g.drawImage(getImage(), x, y, 18, 18, null);
}
};
}
我省略了 MalformedURLException
的异常处理代码,它可以从 URL
构造函数中抛出。
在这种情况下,绘制的图像每次绘制都会得到down-scaled,这是无效的。不过质量更好。好吧,对于标准分辨率屏幕,它几乎与加载图像时 down-scaled 相同。但在高 DPI 情况下,它看起来更好。这是因为对于 200% UI 比例,图像将被渲染为 36×36 像素,这些像素将从 240×240 的源创建而不是 up-scaling down-scaled 版本失去了它的质量。
为了获得更好的结果,我建议使用 MultiResolutionImage
。
多分辨率图像
下面的应用程序从 base64 编码的字符串加载图像(为简单起见,因此没有外部依赖项)。提供了三种变体:24×24(100%,96dpi),36×36(150%,144dpi),48×48(200%,192dpi)。
如果当前比例因子设置为任何提供的分辨率,图像将按原样呈现。如果使用 125% 或 175%,则较大的图像将按比例缩小;如果比例大于 200%,则 200% 的图像将按比例放大。如果需要,您可以添加更多分辨率。
该应用无法在 Java 8 中编译,因为 MultiResolutionImage
在那里不可用。要用 JDK 11 编译它,您必须用常规字符串连接替换文本块。
import java.awt.Image;
import java.awt.image.BaseMultiResolutionImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Base64;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class BeachIconButton {
public static void main(String[] args) {
SwingUtilities.invokeLater(BeachIconButton::new);
}
private BeachIconButton() {
JPanel panel = new JPanel();
ImageIcon icon = getIcon();
JButton button = new JButton("Test button", icon);
panel.add(button);
JFrame frame = new JFrame("Beach Icon Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static ImageIcon getIcon() {
return new ImageIcon(
new BaseMultiResolutionImage(
Arrays.stream(new String[] { BEACH_100, BEACH_150, BEACH_200})
.map(BeachIconButton::loadImage)
.toArray(Image[]::new)));
}
private static Image loadImage(String base64) {
try {
return ImageIO.read(new ByteArrayInputStream(
Base64.getMimeDecoder().decode(base64)));
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private static final String BEACH_100 = """
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAA7DAAAO
wwHHb6hkAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAXxJ
REFUSInl1E9LVVEUBfAfVJBIIoWGIDiRGtagN7Yg+gTZXPwICU4FyUGDahBRCTrK
qUVNIxrkJHjWJMKZikIjSyHoYd0GZ186vOef7n2zWnB4e591WXudt/c5/G8Ywiw+
4gd28QGLuNCt+C18RXHI+onJuuLjIVBgGWOYyPb2syI3q4qfzZw/ir1raMXeNBay
k+zgfNUC25nTBWxGPhfffI78XfzOVD1FP+5lrgusoweXIv+C6xE3qxYocRFrWZHv
eBPxA5yJeK9ugRJPdU7QGPoi3+1GfFBqZIFn+BVxC88jft9NgdL9q8gH8MSfUa3V
5BKXQ6gl9aOdey1N2GBV4VO4ik/h8CWG67rMcQ53Hf5ErOBGXfEGtjKxVSxhHi/w
LeMe4mQV8ROZ67fSZWrHadyW7kKBqaonuC89Bcc5uyKNZ+M4wTvS9d/AY/RmXK80
iht/wa+HVgfKB61cTYzGaups8FH89kEFhjBSUzDnR0LrSFT5Sw7i/yH8BmQ0mnmX
f2wqAAAAAElFTkSuQmCC""";
private static final String BEACH_150 = """
iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAACXBIWXMAABYlAAAW
JQFJUiTwAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAoVJ
REFUWIXt1kuoTVEYB/DfdZWiq7gykZBHHokyw0QGREpKLgMDGSojQ6+JARFi4JFn
ySPyyoCJR8zcIUVJiPLopiMhLoO1d3ftc+7Z95xrnYHyr1V7feu/1/ff3/etby/+
4x9DW8K9ZmE9FmTP7fiBp3iE03iS0F9dTMQN/G5gvMLMVopZhi8NisnHD6xohZiV
2ea5o584h+WYgAclor5jaUoxE9ATOXiOednaUMUU9mKPkK5YVCXbJwkuKkZmYbS2
t8rxpsz+QW2krqYSdF5tCnZjCX5F9h0Zf25k66nizEghaBSOV22cRyt/vhnxd0b2
E7gQzXelEJRjnv6L96dQ9IR+9CxaWymcsnx+L6UgQnPtUizyfDwRaiiff8JwjIts
71MLytGBy0I9xaLitG7LuCMi2+d4kyEJBVWwWkhPfz568UaI6Mho/XtCDTXYqO/L
v+GF2jQ+wNZofrdVYjrwNnK0XYjOYSEt9bp20lMWY0/k5LVQJzlGCn2qur56Mb0V
YiYLKcodddXhTcX1iJesU1cj/nc9NPA9axE2Cw02GUYITe6UYgoOCv+29pTOyjAH
VxRT1N/4iP0Y2yohHTij9v810Khgiwavy43eqSfhGmZHtt/oxm2h4fUI0Zim75IW
4wI24GuDPutiCF4q1sklAx/XJXisGK2zfysGhum7N3/F2ibebce+SNCdFIJgMQ4J
F63BYBUOYEqzL87HLaEeKrivPCLrMk5lEPyezNf8MkHv9H9STioegDbF/vM3/Hdl
grpLnBxFJ8bgWAmvWX53maBOId/jMRpHGnB8JOM2yx+f+eosE1SNNiH89TY/oTY1
zfAHjS7hMv45G3exJiH/P/5d/AHE21JDZYKOHAAAAABJRU5ErkJggg==""";
private static final String BEACH_200 = """
iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAAB2HAAAd
hwGP5fFlAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAA5FJ
REFUaIHt2ctrXFUcB/BPYoWGVGOwBZtYQQoqhUp9IFEoulC04spGExQEpYoiVBBc
qVAVwYX/gEWqooKboItqN4oV0frWPgR3XRSl9mHT2GjaJh0X5w6ZOXPuzO08roL5
wlnM+T3O93cev3vOb1jCEv7f6CtpnBswjg1Yh2HM4wx+wh58jC9K4lMIfXgIB1Ap
2H7FC7jwX+Bbh2uwW3HiqUDWlE26io040YJgkXYKd5TM3Z34K0HmNKYwgavxgLD/
WwUxi1vKIr8GfyRIfIAravTuxtmE3g+Jvkrm86pek+/XuOfP4elIbz1mIr15bBUm
IG8lPtHjjLkpMeg+jNboDOKXSOcsJjP5bU0CqODBsgOoHsRnsRyvJ+RP1vh4PpId
in7v7WUA/diRE0QFhxN92yMfeyL5MxoP+nW9DAJuxY85QdS2AxiosVsrnJmqfAGX
4/3I7uVeBwAX4DEcaRLAFFbV2LwSyXdn/ZNR/86es6/BCmyTTpnVWX4Tl2pMv49k
PtZF/QfLIl+L9fhN/mrEK3XE4vYajGQzZRKPcTuOyw+k2rbV2FwSyY7GTvt7yTjC
UQwV0FspEKf+nMBcVxmdJz7VeEVYkF6F43gKD0f9H5bOOsN9Gkluws04lpBV21z0
+7myiRO+wgcjInE6HE/opNpYOZTrEV8RzghX6hjLhWvHKWny35RBNsZogtCrLWxG
8JrG8zHRO5r5eCci8bvFDNMKY/hMeNRsF77wpWJM/f2mgke7PciyLvtbKby4NuJe
9Y+QGdyEv7FLSJX/CfThfqGmM691Jqlkep8L6bWs2lQSd+F7xUjnte+EYkCpGMQb
HRKP247M73mhneVbhY9wY0J2TpjRnUKV4bBQIxrGalyPe4RSY+oe9q1who61wasQ
+vC1xtlbwFu4sqCftXhb+i70lR6ei+HEgPtxbZv+NuDnhM/hjpnmoE+YoepAu7Sx
byOsELZkWyvQzlINCTWaGbwnpMROsUx4/16Md3GyE2cDQqXsS0wLRPfhJfWFqjyM
Zrr78WcX7KczLlvVVzJyMSU/1U1brKSlMJnp9Mp+qkgAs00cVL+imxN244p9iTux
ny0SQLMVqLY5PCHs2SGhRHi6gF2n9oVWYABbhOLUZbgoGyx+3rUi+Hhm26n96ozL
FgXPQB4262yLdGrfFUxo/vfRCc1fTp3adwUjeFEod5/M2l7h38WREuyXsIQl1OAf
9zFZ1uiy3BkAAAAASUVORK5CYII=""";
}
我正在使用 Java Swing 和 AWT (Java 8) 设计一个 GUI,并且我正在努力使用我使用的图标。
我加载了一个大的 PNG 图像并将其缩放为 18x18 像素,然后在按钮或标签中使用它。当操作系统不放大时,它在所有分辨率下都能正常工作。
但是,随着大屏幕分辨率 (hidpi) 的出现,通常的做法是使用操作系统设置来放大用户界面控件,包括 Java 应用程序中的按钮和此类内容。例如,在 Windows 上,我使用 4K 分辨率对用户元素进行 150% 或 200% 缩放,以确保用户界面仍然可用。我想很多用户也会这样做。
然而,在这种情况下,图标只是在缩小到 18x18 像素后尺寸有所增加。也就是说,我首先将它们缩小,然后操作系统尝试使用图像中仍然存在的少量信息再次将它们放大。
在使用操作系统的zooming/scaling功能时,有什么方法可以设计基于更高分辨率的Java中的图像图标,以避免它们显得模糊吗?
这是一个工作示例:
import java.awt.Container;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
@SuppressWarnings("serial")
class Example extends JFrame {
public static void main(String[] args) {
new Example();
}
public Example() {
Container c = getContentPane();
JPanel panel = new JPanel();
ImageIcon icon = new ImageIcon(new ImageIcon(getClass().getResource("tabler-icon-beach.png")).getImage().getScaledInstance(18, 18, Image.SCALE_SMOOTH));
JButton button = new JButton("Test button", icon);
panel.add(button);
c.add(panel);
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
}
您可以找到图标 here。所有图标均以 PNG 或 SVG 文件形式提供。
为了说明问题,先给大家看两张正常100%屏幕分辨率的截图:
开启 Linux 100% 缩放:
开启 Windows 100% 缩放:
而现在当我设置Windows 7 将布局元素放大200% 时,显然只是18x18px 版本被拉伸,变得模糊:
有什么方法可以提供更高分辨率的图像图标,以便在操作系统使用大于 100% 的缩放比例时使用?此外,您可以看到即使是 100% 的图像质量也不完美;还有什么办法可以改善吗?
Java 8 不支持高 DPI,UI 被放大 Windows。您应该使用支持 per-monitor 高 DPI 设置的 Java 11 或更高版本。
如果您的目标是使图标看起来清晰,请使用 BaseMultiResolutionImage
(the basic implementation of MultiResolutionImage
) 准备一组不同分辨率的图标以提供更高分辨率的替代方案。 (这些在 Java 8 中不可用。)
你说你把原图(240×240)缩小到18×18px。如果UI根据系统设置需要更高的分辨率,它现在只有你的小图标(18×18)会被放大,导致质量很差。您应该使用 MultiResolutionImage
或将原始图像绘制成所需的大小,让 Graphics
为您缩小它。
没有Down-Scale
这是我在不缩小原始图像尺寸的情况下制作 18×18 图标的最简单方法:
private static final String IMAGE_URL =
"https://tabler-icons.io/static/tabler-icons/icons-png/beach.png";
private static ImageIcon getIcon() {
return new ImageIcon(Toolkit.getDefaultToolkit()
.getImage(new URL(IMAGE_URL))) {
@Override
public int getIconWidth() {
return 18;
}
@Override
public int getIconHeight() {
return 18;
}
@Override
public synchronized void paintIcon(Component c, Graphics g,
int x, int y) {
g.drawImage(getImage(), x, y, 18, 18, null);
}
};
}
我省略了 MalformedURLException
的异常处理代码,它可以从 URL
构造函数中抛出。
在这种情况下,绘制的图像每次绘制都会得到down-scaled,这是无效的。不过质量更好。好吧,对于标准分辨率屏幕,它几乎与加载图像时 down-scaled 相同。但在高 DPI 情况下,它看起来更好。这是因为对于 200% UI 比例,图像将被渲染为 36×36 像素,这些像素将从 240×240 的源创建而不是 up-scaling down-scaled 版本失去了它的质量。
为了获得更好的结果,我建议使用 MultiResolutionImage
。
多分辨率图像
下面的应用程序从 base64 编码的字符串加载图像(为简单起见,因此没有外部依赖项)。提供了三种变体:24×24(100%,96dpi),36×36(150%,144dpi),48×48(200%,192dpi)。
如果当前比例因子设置为任何提供的分辨率,图像将按原样呈现。如果使用 125% 或 175%,则较大的图像将按比例缩小;如果比例大于 200%,则 200% 的图像将按比例放大。如果需要,您可以添加更多分辨率。
该应用无法在 Java 8 中编译,因为 MultiResolutionImage
在那里不可用。要用 JDK 11 编译它,您必须用常规字符串连接替换文本块。
import java.awt.Image;
import java.awt.image.BaseMultiResolutionImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Base64;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class BeachIconButton {
public static void main(String[] args) {
SwingUtilities.invokeLater(BeachIconButton::new);
}
private BeachIconButton() {
JPanel panel = new JPanel();
ImageIcon icon = getIcon();
JButton button = new JButton("Test button", icon);
panel.add(button);
JFrame frame = new JFrame("Beach Icon Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static ImageIcon getIcon() {
return new ImageIcon(
new BaseMultiResolutionImage(
Arrays.stream(new String[] { BEACH_100, BEACH_150, BEACH_200})
.map(BeachIconButton::loadImage)
.toArray(Image[]::new)));
}
private static Image loadImage(String base64) {
try {
return ImageIO.read(new ByteArrayInputStream(
Base64.getMimeDecoder().decode(base64)));
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private static final String BEACH_100 = """
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAA7DAAAO
wwHHb6hkAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAXxJ
REFUSInl1E9LVVEUBfAfVJBIIoWGIDiRGtagN7Yg+gTZXPwICU4FyUGDahBRCTrK
qUVNIxrkJHjWJMKZikIjSyHoYd0GZ186vOef7n2zWnB4e591WXudt/c5/G8Ywiw+
4gd28QGLuNCt+C18RXHI+onJuuLjIVBgGWOYyPb2syI3q4qfzZw/ir1raMXeNBay
k+zgfNUC25nTBWxGPhfffI78XfzOVD1FP+5lrgusoweXIv+C6xE3qxYocRFrWZHv
eBPxA5yJeK9ugRJPdU7QGPoi3+1GfFBqZIFn+BVxC88jft9NgdL9q8gH8MSfUa3V
5BKXQ6gl9aOdey1N2GBV4VO4ik/h8CWG67rMcQ53Hf5ErOBGXfEGtjKxVSxhHi/w
LeMe4mQV8ROZ67fSZWrHadyW7kKBqaonuC89Bcc5uyKNZ+M4wTvS9d/AY/RmXK80
iht/wa+HVgfKB61cTYzGaups8FH89kEFhjBSUzDnR0LrSFT5Sw7i/yH8BmQ0mnmX
f2wqAAAAAElFTkSuQmCC""";
private static final String BEACH_150 = """
iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAACXBIWXMAABYlAAAW
JQFJUiTwAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAoVJ
REFUWIXt1kuoTVEYB/DfdZWiq7gykZBHHokyw0QGREpKLgMDGSojQ6+JARFi4JFn
ySPyyoCJR8zcIUVJiPLopiMhLoO1d3ftc+7Z95xrnYHyr1V7feu/1/ff3/etby/+
4x9DW8K9ZmE9FmTP7fiBp3iE03iS0F9dTMQN/G5gvMLMVopZhi8NisnHD6xohZiV
2ea5o584h+WYgAclor5jaUoxE9ATOXiOednaUMUU9mKPkK5YVCXbJwkuKkZmYbS2
t8rxpsz+QW2krqYSdF5tCnZjCX5F9h0Zf25k66nizEghaBSOV22cRyt/vhnxd0b2
E7gQzXelEJRjnv6L96dQ9IR+9CxaWymcsnx+L6UgQnPtUizyfDwRaiiff8JwjIts
71MLytGBy0I9xaLitG7LuCMi2+d4kyEJBVWwWkhPfz568UaI6Mho/XtCDTXYqO/L
v+GF2jQ+wNZofrdVYjrwNnK0XYjOYSEt9bp20lMWY0/k5LVQJzlGCn2qur56Mb0V
YiYLKcodddXhTcX1iJesU1cj/nc9NPA9axE2Cw02GUYITe6UYgoOCv+29pTOyjAH
VxRT1N/4iP0Y2yohHTij9v810Khgiwavy43eqSfhGmZHtt/oxm2h4fUI0Zim75IW
4wI24GuDPutiCF4q1sklAx/XJXisGK2zfysGhum7N3/F2ibebce+SNCdFIJgMQ4J
F63BYBUOYEqzL87HLaEeKrivPCLrMk5lEPyezNf8MkHv9H9STioegDbF/vM3/Hdl
grpLnBxFJ8bgWAmvWX53maBOId/jMRpHGnB8JOM2yx+f+eosE1SNNiH89TY/oTY1
zfAHjS7hMv45G3exJiH/P/5d/AHE21JDZYKOHAAAAABJRU5ErkJggg==""";
private static final String BEACH_200 = """
iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAAB2HAAAd
hwGP5fFlAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAA5FJ
REFUaIHt2ctrXFUcB/BPYoWGVGOwBZtYQQoqhUp9IFEoulC04spGExQEpYoiVBBc
qVAVwYX/gEWqooKboItqN4oV0frWPgR3XRSl9mHT2GjaJh0X5w6ZOXPuzO08roL5
wlnM+T3O93cev3vOb1jCEv7f6CtpnBswjg1Yh2HM4wx+wh58jC9K4lMIfXgIB1Ap
2H7FC7jwX+Bbh2uwW3HiqUDWlE26io040YJgkXYKd5TM3Z34K0HmNKYwgavxgLD/
WwUxi1vKIr8GfyRIfIAravTuxtmE3g+Jvkrm86pek+/XuOfP4elIbz1mIr15bBUm
IG8lPtHjjLkpMeg+jNboDOKXSOcsJjP5bU0CqODBsgOoHsRnsRyvJ+RP1vh4PpId
in7v7WUA/diRE0QFhxN92yMfeyL5MxoP+nW9DAJuxY85QdS2AxiosVsrnJmqfAGX
4/3I7uVeBwAX4DEcaRLAFFbV2LwSyXdn/ZNR/86es6/BCmyTTpnVWX4Tl2pMv49k
PtZF/QfLIl+L9fhN/mrEK3XE4vYajGQzZRKPcTuOyw+k2rbV2FwSyY7GTvt7yTjC
UQwV0FspEKf+nMBcVxmdJz7VeEVYkF6F43gKD0f9H5bOOsN9Gkluws04lpBV21z0
+7myiRO+wgcjInE6HE/opNpYOZTrEV8RzghX6hjLhWvHKWny35RBNsZogtCrLWxG
8JrG8zHRO5r5eCci8bvFDNMKY/hMeNRsF77wpWJM/f2mgke7PciyLvtbKby4NuJe
9Y+QGdyEv7FLSJX/CfThfqGmM691Jqlkep8L6bWs2lQSd+F7xUjnte+EYkCpGMQb
HRKP247M73mhneVbhY9wY0J2TpjRnUKV4bBQIxrGalyPe4RSY+oe9q1who61wasQ
+vC1xtlbwFu4sqCftXhb+i70lR6ei+HEgPtxbZv+NuDnhM/hjpnmoE+YoepAu7Sx
byOsELZkWyvQzlINCTWaGbwnpMROsUx4/16Md3GyE2cDQqXsS0wLRPfhJfWFqjyM
Zrr78WcX7KczLlvVVzJyMSU/1U1brKSlMJnp9Mp+qkgAs00cVL+imxN244p9iTux
ny0SQLMVqLY5PCHs2SGhRHi6gF2n9oVWYABbhOLUZbgoGyx+3rUi+Hhm26n96ozL
FgXPQB4262yLdGrfFUxo/vfRCc1fTp3adwUjeFEod5/M2l7h38WREuyXsIQl1OAf
9zFZ1uiy3BkAAAAASUVORK5CYII=""";
}