diff --git a/vtm-android-example/src/org/oscim/android/test/ThemeStylerActivity.java b/vtm-android-example/src/org/oscim/android/test/ThemeStylerActivity.java index b6650d70..65584b00 100644 --- a/vtm-android-example/src/org/oscim/android/test/ThemeStylerActivity.java +++ b/vtm-android-example/src/org/oscim/android/test/ThemeStylerActivity.java @@ -7,6 +7,7 @@ import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.ToggleButton; +import org.oscim.backend.canvas.Color; import org.oscim.layers.tile.buildings.BuildingLayer; import org.oscim.layers.tile.vector.VectorTileLayer; import org.oscim.layers.tile.vector.labeling.LabelLayer; @@ -24,9 +25,6 @@ import org.oscim.theme.styles.RenderStyle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static org.oscim.utils.ColorUtil.modHsv; -import static org.oscim.utils.ColorUtil.shiftHue; - public class ThemeStylerActivity extends BaseMapActivity implements OnSeekBarChangeListener { final Logger log = LoggerFactory.getLogger(ThemeStylerActivity.class); @@ -90,13 +88,10 @@ public class ThemeStylerActivity extends BaseMapActivity implements OnSeekBarCha } int modColor(int color, HSV hsv) { - return modHsv(shiftHue(color, hsv.hue), 1, hsv.sat, hsv.val, true); + return hsv.mod(color, true); } - public static class HSV { - public double hue = 0; - public double sat = 1; - public double val = 1; + public static class HSV extends Color.HSV { public boolean changed; } @@ -124,16 +119,16 @@ public class ThemeStylerActivity extends BaseMapActivity implements OnSeekBarCha c = outlineColor; if (id == R.id.seekBarS) - c.sat = progress / 50f; + c.saturation = progress / 50f; else if (id == R.id.seekBarV) - c.val = progress / 50f; + c.value = progress / 50f; else if (id == R.id.seekBarH) c.hue = progress / 100f; log.debug((modArea ? "area" : "line") + " h:" + c.hue - + " s:" + c.sat - + " v:" + c.val); + + " s:" + c.saturation + + " v:" + c.value); VectorTileLayer l = (VectorTileLayer) mMap.layers().get(1); RenderTheme t = (RenderTheme) l.getTheme(); @@ -181,8 +176,8 @@ public class ThemeStylerActivity extends BaseMapActivity implements OnSeekBarCha } if (c == null) return; - ((SeekBar) findViewById(R.id.seekBarS)).setProgress((int) (c.sat * 50)); - ((SeekBar) findViewById(R.id.seekBarV)).setProgress((int) (c.val * 50)); + ((SeekBar) findViewById(R.id.seekBarS)).setProgress((int) (c.saturation * 50)); + ((SeekBar) findViewById(R.id.seekBarV)).setProgress((int) (c.value * 50)); ((SeekBar) findViewById(R.id.seekBarH)).setProgress((int) (c.hue * 100)); } } diff --git a/vtm-tests/test/org/oscim/utils/ColorTest.java b/vtm-tests/test/org/oscim/utils/ColorTest.java new file mode 100644 index 00000000..05951178 --- /dev/null +++ b/vtm-tests/test/org/oscim/utils/ColorTest.java @@ -0,0 +1,119 @@ +/* + * Copyright 2019 Gustl22 + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * this program. If not, see . + */ +package org.oscim.utils; + +import org.junit.Test; +import org.oscim.backend.canvas.Color; + +import java.awt.Desktop; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static org.oscim.backend.canvas.Color.b; +import static org.oscim.backend.canvas.Color.g; +import static org.oscim.backend.canvas.Color.r; + +public class ColorTest { + + // See: https://en.wikipedia.org/wiki/ANSI_escape_code + public static final String ANSI_RESET = "\033[0m"; + public static final String ANSI_PREFIX_BACKGROUND_24Bit = "\033[48;2;"; + public static final String ANSI_PREFIX_FOREGROUND_24Bit = "\033[38;2;"; + + @Test + public void testColorHSV() { + List colors = initHSVTest(); + for (int color : colors) { + // Try to display them in terminal (Intellij not supports 24 bit colors) + System.out.println(ANSI_PREFIX_BACKGROUND_24Bit + Color.r(color) + ";" + Color.g(color) + ";" + Color.b(color) + "m " + + Color.toString(color) + "\t" + (new Color.HSV(ColorUtil.rgbToHsv(r(color), g(color), b(color)))).toString() + + ANSI_RESET); + } + } + + public List initHSVTest() { + List colors = new ArrayList<>(); + int color; + + // Hue + color = Color.get(255, 0, 0); + colors.add(color); + colors.add(ColorUtil.modHsv(color, 0.33f, 1f, 1f, false)); + + color = Color.get(0, 0, 255); + colors.add(color); + colors.add(ColorUtil.modHsv(color, 0.33f, 1f, 1f, false)); + + // Saturation + color = Color.get(255, 200, 200); + colors.add(color); + colors.add(ColorUtil.modHsv(color, 0f, 1.5f, 1f, false)); + + color = Color.get(255, 0, 0); + colors.add(color); + colors.add(ColorUtil.modHsv(color, 0f, 0.5f, 1f, false)); + + // Lightness (value) + color = Color.get(255, 255, 255); + colors.add(color); + colors.add(ColorUtil.modHsv(color, 0f, 1f, 0.8f, false)); + + color = Color.get(0, 0, 0); + colors.add(color); + colors.add(ColorUtil.modHsv(color, 0f, 1f, 1.5f, false)); + + return colors; + } + + private void displayHSVColorsInBrowser(List colors) { + try { + File tempFile; + tempFile = File.createTempFile("test-color-", ".html"); + tempFile.deleteOnExit(); + StringBuilder builder = new StringBuilder(""); + + for (int color : colors) { + builder.append(String.format("
", Color.r(color), Color.g(color), Color.b(color)));
+                builder.append(Color.toString(color));
+                builder.append("\t");
+                builder.append((new Color.HSV(ColorUtil.rgbToHsv(r(color), g(color), b(color)))).toString());
+                builder.append("
"); + } + builder.append(""); + + BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile)); + writer.write(builder.toString()); + writer.close(); + + Desktop.getDesktop().browse(tempFile.toURI()); + + Thread.sleep(2000); + } catch (IOException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public static void main(String[] args) { + ColorTest test = new ColorTest(); + List colors = test.initHSVTest(); + test.displayHSVColorsInBrowser(colors); + } +} diff --git a/vtm/src/org/oscim/backend/canvas/Color.java b/vtm/src/org/oscim/backend/canvas/Color.java index 5e4e8efc..ff221676 100644 --- a/vtm/src/org/oscim/backend/canvas/Color.java +++ b/vtm/src/org/oscim/backend/canvas/Color.java @@ -1,6 +1,6 @@ /* * Copyright 2013 Hannes Janetzek - * Copyright 2018 Gustl22 + * Copyright 2018-2019 Gustl22 * * This file is part of the OpenScienceMap project (http://www.opensciencemap.org). * @@ -18,10 +18,45 @@ package org.oscim.backend.canvas; +import org.oscim.utils.ColorUtil; import org.oscim.utils.FastMath; +import org.oscim.utils.math.Vec3; public final class Color { + public static class HSV { + public double hue; + public double saturation; + public double value; + + public HSV() { + hue = 0; + saturation = 1; + value = 1; + } + + public HSV(double hue, double saturation, double value) { + this.hue = hue; + this.saturation = saturation; + this.value = value; + } + + public HSV(Vec3 hsv) { + hue = hsv.x; + saturation = hsv.y; + value = hsv.z; + } + + public int mod(int color, boolean relative) { + return ColorUtil.modHsv(color, hue, saturation, value, relative); + } + + @Override + public String toString() { + return "HSV: " + hue + ", " + saturation + ", " + value; + } + } + private static final int OPAQUE = 0xff000000; public static int fadePremul(int color, double alpha) { @@ -262,6 +297,10 @@ public final class Color { return (color & OPAQUE) == OPAQUE; } + public static String toString(int color) { + return "RGB: " + Color.r(color) + ", " + Color.g(color) + ", " + Color.b(color); + } + private Color() { } } diff --git a/vtm/src/org/oscim/renderer/bucket/MeshBucket.java b/vtm/src/org/oscim/renderer/bucket/MeshBucket.java index feeb71e9..4ee5a99e 100644 --- a/vtm/src/org/oscim/renderer/bucket/MeshBucket.java +++ b/vtm/src/org/oscim/renderer/bucket/MeshBucket.java @@ -212,7 +212,7 @@ public class MeshBucket extends RenderBucket { int c = (ml.area == null) ? Color.BLUE : ml.area.color; gl.lineWidth(1); //c = ColorUtil.shiftHue(c, 0.5); - c = ColorUtil.modHsv(c, 1.1, 1.0, 0.8, true); + c = ColorUtil.modHsv(c, 0.1, 1.0, 0.8, true); GLUtils.setColor(s.uColor, c, 1); gl.drawElements(GL.LINES, ml.numIndices, diff --git a/vtm/src/org/oscim/utils/ColorUtil.java b/vtm/src/org/oscim/utils/ColorUtil.java index fe4a4687..a1f96241 100644 --- a/vtm/src/org/oscim/utils/ColorUtil.java +++ b/vtm/src/org/oscim/utils/ColorUtil.java @@ -63,19 +63,28 @@ public class ColorUtil { } /** - * @param hue the hue + * @param hue the hue from 0 to 1 (exclusive) + * 0: no color shift + * 0.5: opposite hue * @param saturation the saturation - * @param value the lightness (usual a range from 0 to 2) + * 0 to 1: desaturate + * 1 to 2: saturate + * @param value the lightness + * 0 to 1: darken + * 1 to 2: lighten * @param relative indicate if colors are modified relative to their values * (e.g black not changes if relative) */ public static synchronized int modHsv(int color, double hue, double saturation, double value, boolean relative) { + if ((hue == 0 || hue == 1) && saturation == 1 && value == 1) + return color; Vec3 hsl = TMP_VEC; rgbToHsv(r(color), g(color), b(color), hsl); - return hsvToRgb(clamp(hue * hsl.x, 0, 1), - clamp(saturation * hsl.y, 0, 1), - clamp(relative || (value - 1) < 0 ? value * hsl.z : + return hsvToRgb(clamp((hue + hsl.x) % 1, 0, 1), + clamp(relative || saturation <= 1 ? saturation * hsl.y : + hsl.y + (saturation - 1) * (1 - hsl.y), 0, 1), + clamp(relative || value <= 1 ? value * hsl.z : hsl.z + (value - 1) * (1 - hsl.z), 0, 1)); }