From 361a0393cdafad510717128373ff03dc7873220f Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Fri, 5 Aug 2022 18:44:31 -0400 Subject: [PATCH] try gradients in Lab space, add pref to use them --- app/src/processing/app/ui/Theme.java | 161 +++++++++++++++++++++++++++ build/shared/lib/defaults.txt | 4 + todo.txt | 27 +++-- 3 files changed, 184 insertions(+), 8 deletions(-) diff --git a/app/src/processing/app/ui/Theme.java b/app/src/processing/app/ui/Theme.java index c24d956cf..edc14bf58 100644 --- a/app/src/processing/app/ui/Theme.java +++ b/app/src/processing/app/ui/Theme.java @@ -202,6 +202,15 @@ public class Theme { static public Image makeGradient(String attribute, int wide, int high) { + if ("lab".equals(Preferences.get("theme.gradient.method"))) { + return makeGradientLab(attribute, wide, high); + } else { // otherwise go with the default + return makeGradientRGB(attribute, wide, high); + } + } + + + static private Image makeGradientRGB(String attribute, int wide, int high) { int top = getColor(attribute + ".gradient.top").getRGB(); int bot = getColor(attribute + ".gradient.bottom").getRGB(); @@ -216,4 +225,156 @@ public class Theme { } return outgoing; } + + + static private Image makeGradientLab(String attribute, int wide, int high) { + double[] top = xyzToLab(rgbToXyz(getColor(attribute + ".gradient.top"))); + double[] bot = xyzToLab(rgbToXyz(getColor(attribute + ".gradient.bottom"))); + + double diffL = bot[0] - top[0]; + double diffA = bot[1] - top[1]; + double diffB = bot[2] - top[2]; + + BufferedImage outgoing = + new BufferedImage(wide, high, BufferedImage.TYPE_INT_RGB); + int[] row = new int[wide]; + WritableRaster wr = outgoing.getRaster(); + for (int i = 0; i < high; i++) { + double amt = i / (high - 1.0); + double el = top[0] + amt * diffL; + double ay = top[1] + amt * diffA; + double be = top[2] + amt * diffB; + int rgb = argb(xzyToRgb(labToXyz(el, ay, be))); + Arrays.fill(row, rgb); + wr.setDataElements(0, i, wide, 1, row); + } + return outgoing; + } + + + // https://web.archive.org/web/20060213080500/http://www.easyrgb.com/math.php?MATH=M2#text2 + + // Observer= 2°, Illuminant= D65; + static final double REF_X = 95.047; + static final double REF_Y = 100.0; + static final double REF_Z = 108.883; + + + static private double[] rgbToXyz(Color color) { + double var_R = color.getRed() / 255.0; + double var_G = color.getGreen() / 255.0; + double var_B = color.getBlue() / 255.0; + + var_R = 100 * ((var_R > 0.04045) ? + Math.pow((var_R + 0.055) / 1.055, 2.4) : var_R / 12.92); + var_G = 100 * ((var_G > 0.04045) ? + Math.pow((var_G + 0.055) / 1.055, 2.4) : var_G / 12.92); + var_B = 100 * ((var_B > 0.04045) ? + Math.pow((var_B + 0.055) / 1.055, 2.4) : var_B / 12.92); + + // Observer = 2°, Illuminant = D65 + return new double[] { + var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805, + var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722, + var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505 + }; + } + + + static private double[] xyzToLab(double[] xyz) { + double var_X = xyz[0] / REF_X; // Observer= 2°, Illuminant= D65 + double var_Y = xyz[1] / REF_Y; + double var_Z = xyz[2] / REF_Z; + + var_X = (var_X > 0.008856) ? + Math.pow(var_X, 1/3.0) : (7.787*var_X + 16/116.0); + + var_Y = (var_Y > 0.008856) ? + Math.pow(var_Y, 1/3.0) : (7.787*var_Y + 16/116.0); + + var_Z = (var_Z > 0.008856) ? + Math.pow(var_Z, 1/3.0) : (7.787*var_Z + 16/116.0); + + return new double[] { + (116 * var_Y) - 16, + 500 * (var_X - var_Y), + 200 * (var_Y - var_Z) + }; + } + +// static private double[] labToXyz(double[] lab) { +// double var_Y = (lab[0] + 16) / 116.0; +// double var_X = lab[1] / 500 + var_Y; +// double var_Z = var_Y - lab[2] / 200.0; + static private double[] labToXyz(double el, double ay, double be) { + double var_Y = (el + 16) / 116; + double var_X = ay / 500 + var_Y; + double var_Z = var_Y - be / 200; + +// if ( var_Y^3 > 0.008856 ) var_Y = var_Y^3 +// else var_Y = ( var_Y - 16 / 116 ) / 7.787 +// if ( var_X^3 > 0.008856 ) var_X = var_X^3 +// else var_X = ( var_X - 16 / 116 ) / 7.787 +// if ( var_Z^3 > 0.008856 ) var_Z = var_Z^3 +// else var_Z = ( var_Z - 16 / 116 ) / 7.787 + + final double amt = Math.pow(0.008856, 1/3.0); + var_Y = (var_Y > amt) ? + Math.pow(var_Y, 3) : (var_Y - 16/116.0) / 7.787; + var_X = (var_X > amt) ? + Math.pow(var_X, 3) : (var_X - 16/116.0) / 7.787; + var_Z = (var_Z > amt) ? + Math.pow(var_Z, 3) : (var_Z - 16/116.0) / 7.787; + +// X = ref_X * var_X //ref_X = 95.047 Observer= 2°, Illuminant= D65 +// Y = ref_Y * var_Y //ref_Y = 100.000 +// Z = ref_Z * var_Z //ref_Z = 108.883 + return new double[] { + REF_X * var_X, + REF_Y * var_Y, + REF_Z * var_Z + }; + } + + + static private double[] xzyToRgb(double[] xyz) { + double var_X = xyz[0] / 100; // Where X = 0 ÷ 95.047 + double var_Y = xyz[1] / 100; // Where Y = 0 ÷ 100.000 + double var_Z = xyz[2] / 100; // Where Z = 0 ÷ 108.883 + + double var_R = var_X * 3.2406 + var_Y * -1.5372 + var_Z * -0.4986; + double var_G = var_X * -0.9689 + var_Y * 1.8758 + var_Z * 0.0415; + double var_B = var_X * 0.0557 + var_Y * -0.2040 + var_Z * 1.0570; + +// if ( var_R > 0.0031308 ) var_R = 1.055 * ( var_R ^ ( 1 / 2.4 ) ) - 0.055 +// else var_R = 12.92 * var_R +// if ( var_G > 0.0031308 ) var_G = 1.055 * ( var_G ^ ( 1 / 2.4 ) ) - 0.055 +// else var_G = 12.92 * var_G +// if ( var_B > 0.0031308 ) var_B = 1.055 * ( var_B ^ ( 1 / 2.4 ) ) - 0.055 +// else var_B = 12.92 * var_B + + var_R = (var_R > 0.0031308) ? + 1.055 * Math.pow(var_R, 1/2.4) - 0.055 : 12.92 * var_R; + var_G = (var_G > 0.0031308) ? + 1.055 * Math.pow(var_G, 1/2.4) - 0.055 : 12.92 * var_G; + var_B = (var_B > 0.0031308) ? + 1.055 * Math.pow(var_B, 1/2.4) - 0.055 : 12.92 * var_B; + + return new double[] { + var_R * 255, + var_G * 255, + var_B * 255 + }; + } + + + static private int bounded(double amount) { + return Math.max(0, Math.min((int) Math.round(amount), 255)); + } + + + static private int argb(double[] rgb) { + return 0xff000000 | + bounded(rgb[0]) << 16 | bounded(rgb[1]) << 8 | bounded(rgb[2]); + } } \ No newline at end of file diff --git a/build/shared/lib/defaults.txt b/build/shared/lib/defaults.txt index dc1e8b9be..ed5b2eda6 100644 --- a/build/shared/lib/defaults.txt +++ b/build/shared/lib/defaults.txt @@ -81,6 +81,9 @@ chooser.files.native = true # https://github.com/processing/processing4/issues/77 #chooser.files.native.macos = false +# set to 'lab' to interpolate theme gradients using L*a*b* color space +theme.gradient.method = rgb + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -93,6 +96,7 @@ update.check = true # on windows, automatically associate .pde files with processing.exe platform.auto_file_type_associations = true + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # default font used for UI elements (no longer in use as of beta 9) diff --git a/todo.txt b/todo.txt index 362ae2d3b..1be7b5a22 100755 --- a/todo.txt +++ b/todo.txt @@ -1,6 +1,18 @@ 1285 (4.0?) +X try gradients in Lab space +X not an obvious win, so set as a pref and come back to it later + +docs X update theme instructions X https://github.com/processing/processing4/wiki/Themes +X update the platforms page +X https://github.com/processing/processing4/wiki/Supported-Platforms +o rename to 'platforms'? + +cleaning +X the gradient.top and gradient.bottom lines are used by makeGradient(), +X so search for makeGradient() calls to see whether they're still in use. +X having brought back some gradient themes for beta 9, we'll keep this in place known issues @@ -169,17 +181,13 @@ _ theme_selector.combo_box.enabled.bgcolor _ theme_selector.combo_box.enabled.bgcolor _ don’t update dynamically unless you (a) select ‘Minerals’ or ‘Alloys’ _ or (b) open, then close the dropdown -_ build new welcome/examples screen for startup _ implement document icons on Windows _ implement document icons on Linux _ add notes about params to the wiki -_ the gradient.top and gradient.bottom lines are used by makeGradient(), -_ so search for makeGradient() calls to see whether they're still in use. 4.0 _ how to handle contrib manager download/retry/etc and showing progress -_ do gradients in Lab space _ adding a file to an untitled sketch should require a save _ Cannot type quotes with an international keyboard on MacOS _ fixed by turning on "Enable complex text input" @@ -195,19 +203,22 @@ _ really a drag for version control _ https://github.com/processing/processing/issues/5848 _ or at least avoid the multiple _ https://github.com/processing/processing4/issues/330 -_ update the platforms page -_ https://github.com/processing/processing4/wiki/Supported-Platforms _ if error in the code during export, it's just saying "error during export" _ is this still the case? -_ chars[] array is never used in ascii video example _ ui scaling in the PDE is important for classes/seeing at a distance -_ Export to Application was broken with OpenGL (Casey) _ add a version of this to the 4.x repo _ https://github.com/processing/processing/wiki/troubleshooting _ and update the Help menu to link to it _ and maybe the FAQ too? +4.0 / with Casey +_ Export to Application was broken with OpenGL (Casey) +_ resolution for the About screen +_ examples still coming from processing-docs repo +_ chars[] array is never used in ascii video example + + 4.0 / movie maker _ break Movie Maker out to its own library? _ another big leap in file size reduction for the PDE download