Skip to content

Commit 168e4b0

Browse files
committed
0.5.1
1 parent 66e28ff commit 168e4b0

File tree

12 files changed

+276
-35
lines changed

12 files changed

+276
-35
lines changed

build.gradle

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@ plugins {
66
}
77

88
group 'pl.asie.zima'
9-
version '0.5.0'
9+
version '0.5.1'
1010

1111
repositories {
1212
mavenCentral()
1313
}
1414

1515
dependencies {
1616
implementation 'com.google.code.gson:gson:2.8.6'
17+
18+
testImplementation(platform('org.junit:junit-bom:5.7.0'))
19+
testImplementation('org.junit.jupiter:junit-jupiter')
1720
}
1821

1922
license {

docs/changelog/0.5.1.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Improvements:
2+
3+
* In order to combat the issue of border halfchars forming around images, new aspect ratio preservation modes have been
4+
added as follows:
5+
* Snap to char (new default) - rounds width/height to the nearest character.
6+
* Snap to center - rounds width/height to the nearest character which allows the image to be centered.
7+
* The aspect ratio preservation mode is now saved as part of the profile.
8+
* The window title now displays the program version.
9+
10+
Bugs fixed:
11+
12+
* The window pane showing scroll bars when unnecessary on some platforms has been fixed.
13+
* The correct program version is now displayed (regression in 0.4.2+).

docs/changelog/versions.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,7 @@
22
0.2.0
33
0.3.0
44
0.4.0
5-
0.4.1
5+
0.4.1
6+
0.4.2
7+
0.5.0
8+
0.5.1

src/main/java/pl/asie/zima/Version.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@
2828
public final class Version {
2929
private static final List<String> versions;
3030

31+
public static String getCurrentWindowName(String appName) {
32+
if (appName.isBlank()) {
33+
return "zima " + getCurrent();
34+
} else {
35+
return "zima " + getCurrent() + " :: " + appName;
36+
}
37+
}
38+
3139
public static String getCurrent() {
3240
return versions != null && !versions.isEmpty() ? versions.get(versions.size() - 1) : "[unknown version]";
3341
}

src/main/java/pl/asie/zima/image/TrixImageMseCalculator.java

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
import pl.asie.libzzt.TextVisualData;
2323

2424
import java.awt.image.BufferedImage;
25+
import java.util.HashMap;
2526
import java.util.HashSet;
27+
import java.util.Map;
2628
import java.util.Set;
2729
import java.util.stream.Collectors;
2830

@@ -35,6 +37,7 @@ public class TrixImageMseCalculator implements ImageMseCalculator {
3537
private final boolean[][] charLut1x1Precalc;
3638
private final float[] colDistPrecalc;
3739
private final boolean blinkingDisabled;
40+
private final int[][] blendingColorPrecalc;
3841
private final Set<Integer> blendingChars = new HashSet<>();
3942

4043
private static class ImageLutHolder {
@@ -139,21 +142,39 @@ public TrixImageMseCalculator(TextVisualData visual, boolean blinkingDisabled, f
139142
blendingChars.add(i);
140143
}
141144
}
145+
146+
blendingColorPrecalc = new int[256][];
147+
Map<Integer, int[]> bcpMap = new HashMap<>();
148+
final int bcpMax = (visual.getCharWidth() * visual.getCharHeight());
149+
for (int i = 0; i < 256; i++) {
150+
if (blendingChars.contains(i)) {
151+
int count = 0;
152+
for (int j = 0; j < bcpMax; j++) {
153+
count += charLut1x1Precalc[i][j] ? 1 : 0;
154+
}
155+
blendingColorPrecalc[i] = bcpMap.computeIfAbsent(count, c -> {
156+
float factor = c / (float) bcpMax;
157+
int[] data = new int[256];
158+
for (int k = 0; k < 256; k++) {
159+
data[k] = ColorUtils.mix(visual.getPalette()[k >> 4], visual.getPalette()[k & 15], factor);
160+
}
161+
return data;
162+
});
163+
}
164+
}
142165
}
143166

144167
@Override
145168
public Applier applyMse(BufferedImage image, int px, int py) {
146169
final ImageLutHolder holder = new ImageLutHolder(visual, image, px, py, visual.getCharWidth(), visual.getCharHeight());
147170
float[] mseContrastPrecalc = new float[256];
148-
float[] macroRatioPrecalc = new float[256];
149171
for (int i = 0; i < 256; i++) {
150172
float imgContrast = holder.maxDistance;
151173
float chrContrast = colDistPrecalc[i];
152174
float contrastDiff = (imgContrast - chrContrast);
153175
mseContrastPrecalc[i] = contrastReduction * contrastDiff * contrastDiff;
154-
155-
macroRatioPrecalc[i] = blendingChars.contains(i) ? 1.0f : accurateApproximate;
156176
}
177+
final int[] palette = visual.getPalette();
157178
final int colorMask = blinkingDisabled ? 0xFF : 0x7F;
158179
return (proposed, maxMse) -> {
159180
int chr = proposed.getCharacter();
@@ -163,10 +184,21 @@ public Applier applyMse(BufferedImage image, int px, int py) {
163184
int[] dataMacro2x2 = holder.dataMacro2x2;
164185

165186
float mseContrastReduction = mseContrastPrecalc[col];
166-
float macroRatio = macroRatioPrecalc[chr];
187+
float macroRatio = accurateApproximate;
167188

168189
mse += dataMacro2x2.length * mseContrastReduction;
169190
if (mse <= maxMse) {
191+
int[] blendingRatio = blendingColorPrecalc[chr];
192+
if (blendingRatio != null) {
193+
for (int i = 0; i < dataMacro2x2.length; i++) {
194+
mse += ColorUtils.distance(blendingRatio[col], dataMacro2x2[i]);
195+
if (mse > maxMse) {
196+
return Float.MAX_VALUE;
197+
}
198+
}
199+
return mse;
200+
}
201+
170202
if (macroRatio < 1.0f) {
171203
float invMacroRatio = ((1 - macroRatio) * 0.25f);
172204
float[][] dataMacro1x1 = holder.dataMacro1x1;
@@ -178,7 +210,7 @@ public Applier applyMse(BufferedImage image, int px, int py) {
178210
int charColor = charData[dm1p] ? fg : bg;
179211
mse += dataMacro1x1[dm1p][charColor] * invMacroRatio;
180212
if (mse > maxMse) {
181-
return mse;
213+
return Float.MAX_VALUE;
182214
}
183215
}
184216
}
@@ -192,7 +224,7 @@ public Applier applyMse(BufferedImage image, int px, int py) {
192224
float dist2x2 = ColorUtils.distance(char2x2Lut, dataMacro2x2[i]);
193225
mse += dist2x2 * macroRatio;
194226
if (mse > maxMse) {
195-
return mse;
227+
return Float.MAX_VALUE;
196228
}
197229
}
198230
}

src/main/java/pl/asie/zima/image/gui/ZimaConversionProfile.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
package pl.asie.zima.image.gui;
2020

2121
import lombok.Getter;
22-
import pl.asie.libzzt.Board;
2322
import pl.asie.libzzt.Platform;
2423
import pl.asie.libzzt.TextVisualData;
2524
import pl.asie.libzzt.TextVisualRenderer;
@@ -54,7 +53,7 @@ public class ZimaConversionProfile {
5453
public static final Property<Float> BRIGHTNESS = Property.create("image.colorFilter.brightness", 0.0f, FILTERED_IMAGE);
5554
public static final Property<Float> CONTRAST = Property.create("image.colorFilter.contrast", 0.0f, FILTERED_IMAGE);
5655
public static final Property<Float> SATURATION = Property.create("image.colorFilter.saturation", 0.0f, FILTERED_IMAGE);
57-
public static final Property<Boolean> PRESERVE_ASPECT_RATIO = Property.create("image.preserveAspectRatio", true, SCALED_IMAGE);
56+
public static final Property<AspectRatioPreservationMode> ASPECT_RATIO_PRESERVATION_MODE = Property.create("image.preserveAspectRatio", AspectRatioPreservationMode.SNAP_CHAR, SCALED_IMAGE);
5857

5958
public static final Property<Integer> CROP_LEFT = Property.create("image.crop.left", 0, SCALED_IMAGE);
6059
public static final Property<Integer> CROP_RIGHT = Property.create("image.crop.right", 0, SCALED_IMAGE);
@@ -167,7 +166,7 @@ public void updateImage(BufferedImage input) {
167166
int width = properties.get(VISUAL_DATA).getCharWidth() * properties.get(CHARS_WIDTH);
168167
int height = properties.get(VISUAL_DATA).getCharHeight() * properties.get(CHARS_HEIGHT);
169168

170-
this.scaledImage = ImageUtils.scale(img, width, height, properties.get(PRESERVE_ASPECT_RATIO), properties.get(PLATFORM).isDoubleWide(), Color.BLACK);
169+
this.scaledImage = ImageUtils.scale(img, width, height, properties.get(ASPECT_RATIO_PRESERVATION_MODE), properties.get(PLATFORM).isDoubleWide(), Color.BLACK);
171170
localHolder.affect(FILTERED_IMAGE);
172171
}
173172

src/main/java/pl/asie/zima/image/gui/ZimaFrontendSwing.java

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,8 @@
2727
import pl.asie.libzzt.Platform;
2828
import pl.asie.libzzt.TextVisualData;
2929
import pl.asie.libzzt.ZOutputStream;
30-
import pl.asie.zima.util.FileUtils;
31-
import pl.asie.zima.util.MZMWriter;
32-
import pl.asie.zima.util.Pair;
33-
import pl.asie.zima.util.Property;
34-
import pl.asie.zima.util.PropertyHolder;
30+
import pl.asie.zima.Version;
31+
import pl.asie.zima.util.*;
3532
import pl.asie.zima.image.*;
3633
import pl.asie.zima.util.gui.ImageFileChooser;
3734
import pl.asie.zima.util.gui.SimpleCanvas;
@@ -140,7 +137,7 @@ Platform.MEGAZEUX, new ImageConverterRuleset(List.of())
140137
// "Image" tab
141138
private JLabel imageDataLabel;
142139
private JCheckBox showInputImageEdit;
143-
private JCheckBox preserveAspectRatioEdit;
140+
private JComboBox<AspectRatioPreservationMode> aspectRatioEdit;
144141
private JSlider brightnessEdit;
145142
private JButton brightnessReset;
146143
private JSlider contrastEdit;
@@ -188,7 +185,7 @@ public ZimaFrontendSwing(byte[] defaultCharset, int[] defaultPalette, String zim
188185
this.profile.getProperties().set(ZimaConversionProfile.PLATFORM, Platform.ZZT);
189186
this.profile.getProperties().set(ZimaConversionProfile.FAST_RULESET, ImageConverterRulesZZT.RULES_BLOCKS);
190187

191-
this.window = new JFrame("zima :: image converter");
188+
this.window = new JFrame(Version.getCurrentWindowName("image converter"));
192189
this.window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
193190

194191
this.previewCanvas = new SimpleCanvas();
@@ -313,8 +310,8 @@ public ZimaFrontendSwing(byte[] defaultCharset, int[] defaultPalette, String zim
313310

314311
appendTabRow(this.optionsImagePanel, gbc, "Image info", this.imageDataLabel = new JLabel(""));
315312

316-
appendTabRow(this.optionsImagePanel, gbc, "Preserve aspect ratio", this.preserveAspectRatioEdit = new JCheckBox());
317-
bindPropertyBoolean(this.profile.getProperties(), ZimaConversionProfile.PRESERVE_ASPECT_RATIO, this.preserveAspectRatioEdit);
313+
appendTabRow(this.optionsImagePanel, gbc, "Aspect ratio", this.aspectRatioEdit = createEnumComboBox(AspectRatioPreservationMode.class));
314+
bindPropertyEnum(this.profile.getProperties(), ZimaConversionProfile.ASPECT_RATIO_PRESERVATION_MODE, this.aspectRatioEdit);
318315

319316
appendTabRow(this.optionsImagePanel, gbc, "Brightness",
320317
this.brightnessEdit = new JSlider(JSlider.HORIZONTAL, -160, 160, 0),
@@ -476,6 +473,7 @@ public ZimaFrontendSwing(byte[] defaultCharset, int[] defaultPalette, String zim
476473
this.copyItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_DOWN_MASK));
477474
this.pasteItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK));
478475

476+
this.previewCanvasPane.setBorder(null);
479477
this.previewCanvas.setMinimumSize(new Dimension(480, 350));
480478
this.previewCanvasPane.setMinimumSize(this.previewCanvas.getMinimumSize());
481479
this.previewCanvas.setPreferredSize(this.previewCanvas.getMinimumSize());
@@ -942,6 +940,8 @@ public ZimaProfileSettings getSettings() {
942940
settings.setContrastReduction(this.profile.getProperties().get(ZimaConversionProfile.TRIX_CONTRAST_REDUCTION));
943941
settings.setAccurateApproximate(this.profile.getProperties().get(ZimaConversionProfile.TRIX_ACCURATE_APPROXIMATE));
944942

943+
settings.setAspectRatioPreservationMode(this.profile.getProperties().get(ZimaConversionProfile.ASPECT_RATIO_PRESERVATION_MODE));
944+
945945
return settings;
946946
}
947947

@@ -1018,11 +1018,29 @@ public void setSettings(ZimaProfileSettings settings) {
10181018
this.profile.getProperties().set(ZimaConversionProfile.TRIX_ACCURATE_APPROXIMATE, settings.getAccurateApproximate());
10191019
}
10201020

1021+
if (settings.getAspectRatioPreservationMode() != null) {
1022+
this.profile.getProperties().set(ZimaConversionProfile.ASPECT_RATIO_PRESERVATION_MODE, settings.getAspectRatioPreservationMode());
1023+
}
1024+
10211025
rerender();
10221026
}
10231027

10241028
// Re-render call listeners
10251029

1030+
public <T extends Enum<?>> JComboBox<T> createEnumComboBox(Class<T> enumClass) {
1031+
JComboBox<T> comboBox = new JComboBox<>();
1032+
for (T value : enumClass.getEnumConstants()) {
1033+
comboBox.addItem(value);
1034+
}
1035+
return comboBox;
1036+
}
1037+
1038+
public <T extends Enum<?>> void bindPropertyEnum(PropertyHolder holder, Property<T> property, JComboBox<T> comboBox) {
1039+
comboBox.setSelectedItem(property.getDefaultValue());
1040+
comboBox.addItemListener((e) -> holder.set(property, (T) comboBox.getSelectedItem()));
1041+
holder.addChangeListener(property, (k, v) -> comboBox.setSelectedItem(v));
1042+
}
1043+
10261044
public void bindPropertyBoolean(PropertyHolder holder, Property<Boolean> property, JCheckBox checkBox) {
10271045
Boolean defValue = property.getDefaultValue();
10281046
checkBox.setSelected(Objects.equals(defValue, Boolean.TRUE));

src/main/java/pl/asie/zima/image/gui/ZimaProfileSettings.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import lombok.Data;
2222
import pl.asie.zima.image.ElementRule;
23+
import pl.asie.zima.util.AspectRatioPreservationMode;
2324

2425
import java.util.List;
2526

@@ -37,4 +38,6 @@ public class ZimaProfileSettings {
3738
private Boolean colorsBlink;
3839
private Float contrastReduction;
3940
private Float accurateApproximate;
41+
42+
private AspectRatioPreservationMode aspectRatioPreservationMode;
4043
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* Copyright (c) 2020, 2021 Adrian Siekierka
3+
*
4+
* This file is part of zima.
5+
*
6+
* zima is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* zima is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with zima. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
package pl.asie.zima.util;
20+
21+
public enum AspectRatioPreservationMode {
22+
PRESERVE("Preserve"),
23+
SNAP_CHAR("Snap to character"),
24+
SNAP_CENTER("Snap to center"),
25+
IGNORE("Stretch");
26+
27+
private final String label;
28+
29+
AspectRatioPreservationMode(String label) {
30+
this.label = label;
31+
}
32+
33+
@Override
34+
public String toString() {
35+
return label;
36+
}
37+
}

0 commit comments

Comments
 (0)