From 51ba398bea702e68ef439ea66181568b0f85f875 Mon Sep 17 00:00:00 2001 From: Maximus5 Date: Fri, 22 Apr 2016 20:19:40 +0300 Subject: [PATCH] =?UTF-8?q?New=20feature=20=E2=80=98Adjust=20lightness=20o?= =?UTF-8?q?f=20indistinguishable=20text=E2=80=99=20on=20the=20=E2=80=98Col?= =?UTF-8?q?ors=E2=80=99=20settings=20page.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The idea is to make text readable if certain foreground and background colors are indistinguishable for current palette. ConEmu will try to change the lightness of the foreground text. --- src/ConEmu/ColorFix.cpp | 529 +++++++++++++++++++++++++++++++++++ src/ConEmu/ColorFix.h | 65 +++++ src/ConEmu/ConEmu.rc | 1 + src/ConEmu/ConEmu09.vcproj | 2 + src/ConEmu/ConEmu10.vcxproj | 2 + src/ConEmu/ConEmu11.vcxproj | 2 + src/ConEmu/ConEmu12.vcxproj | 2 + src/ConEmu/ConEmu14.vcxproj | 2 + src/ConEmu/LngDataHints.h | 1 + src/ConEmu/Options.cpp | 4 + src/ConEmu/Options.h | 3 + src/ConEmu/RConPalette.cpp | 28 ++ src/ConEmu/RConPalette.h | 2 + src/ConEmu/RealBuffer.cpp | 1 + src/ConEmu/SetDlgButtons.cpp | 14 + src/ConEmu/SetDlgButtons.h | 1 + src/ConEmu/SetPgColors.cpp | 1 + src/ConEmu/makefile_gcc | 1 + src/ConEmu/makefile_vc | 3 + src/ConEmu/resource.h | 3 +- 20 files changed, 666 insertions(+), 1 deletion(-) create mode 100644 src/ConEmu/ColorFix.cpp create mode 100644 src/ConEmu/ColorFix.h diff --git a/src/ConEmu/ColorFix.cpp b/src/ConEmu/ColorFix.cpp new file mode 100644 index 0000000000..b206c8a126 --- /dev/null +++ b/src/ConEmu/ColorFix.cpp @@ -0,0 +1,529 @@ + +/* +Copyright (c) 2016 Maximus5 +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#define _USE_MATH_DEFINES +#define HIDE_USE_EXCEPTION_INFO +#define SHOWDEBUGSTR + + +#include +#include +#include "ColorFix.h" + +const real_type g_min_thrashold = 12.0; +const real_type g_exp_thrashold = 20.0; +const real_type g_L_step = 5.0; + +// DeltaE 2000 +// Source: https://github.com/zschuessler/DeltaE +struct dE00 +{ + ColorFix x1, x2; + real_type ksubL, ksubC, ksubH; + real_type deltaLPrime, LBar; + real_type C1, C2, CBar; + real_type aPrime1, aPrime2; + real_type CPrime1, CPrime2; + real_type CBarPrime, deltaCPrime; + real_type SsubL, SsubC; + real_type hPrime1, hPrime2, deltahPrime, deltaHPrime; + real_type HBarPrime, T, SsubH, RsubT; + + static real_type _abs(real_type v) + { + return (v < 0) ? (-v) : (v); + }; + + dE00(ColorFix ax1, ColorFix ax2, real_type weight_lightness = 1, real_type weight_chroma = 1, real_type weight_hue = 1) + { + this->x1 = ax1; + this->x2 = ax2; + + this->ksubL = weight_lightness; + this->ksubC = weight_chroma; + this->ksubH = weight_hue; + + // Delta L Prime + this->deltaLPrime = x2.L - x1.L; + + // L Bar + this->LBar = (x1.L + x2.L) / 2; + + // C1 & C2 + this->C1 = sqrt(pow(x1.A, 2) + pow(x1.B, 2)); + this->C2 = sqrt(pow(x2.A, 2) + pow(x2.B, 2)); + + // C Bar + this->CBar = (this->C1 + this->C2) / 2; + + // A Prime 1 + this->aPrime1 = x1.A + + (x1.A / 2) * + (1 - sqrt( + pow(this->CBar, 7) / + (pow(this->CBar, 7) + pow((real_type)25, 7)) + )); + + // A Prime 2 + this->aPrime2 = x2.A + + (x2.A / 2) * + (1 - sqrt( + pow(this->CBar, 7) / + (pow(this->CBar, 7) + pow((real_type)25, 7)) + )); + + // C Prime 1 + this->CPrime1 = sqrt( + pow(this->aPrime1, 2) + + pow(x1.B, 2) + ); + + // C Prime 2 + this->CPrime2 = sqrt( + pow(this->aPrime2, 2) + + pow(x2.B, 2) + ); + + // C Bar Prime + this->CBarPrime = (this->CPrime1 + this->CPrime2) / 2; + + // Delta C Prime + this->deltaCPrime = this->CPrime2 - this->CPrime1; + + // S sub L + this->SsubL = 1 + ( + (0.015 * pow(this->LBar - 50, 2)) / + sqrt(20 + pow(this->LBar - 50, 2)) + ); + + // S sub C + this->SsubC = 1 + 0.045 * this->CBarPrime; + + /** + * Properties set in getDeltaE method, for access to convenience functions + */ + // h Prime 1 + this->hPrime1 = 0; + + // h Prime 2 + this->hPrime2 = 0; + + // Delta h Prime + this->deltahPrime = 0; + + // Delta H Prime + this->deltaHPrime = 0; + + // H Bar Prime + this->HBarPrime = 0; + + // T + this->T = 0; + + // S sub H + this->SsubH = 0; + + // R sub T + this->RsubT = 0; + } + + /** + * Returns the deltaE value. + * @method + * @returns {number} + */ + real_type getDeltaE() + { + // h Prime 1 + this->hPrime1 = this->gethPrime1(); + + // h Prime 2 + this->hPrime2 = this->gethPrime2(); + + // Delta h Prime + this->deltahPrime = this->getDeltahPrime(); + + // Delta H Prime + this->deltaHPrime = 2 * sqrt(this->CPrime1 * this->CPrime2) * sin(this->degreesToRadians(this->deltahPrime) / 2); + + // H Bar Prime + this->HBarPrime = this->getHBarPrime(); + + // T + this->T = this->getT(); + + // S sub H + this->SsubH = 1 + 0.015 * this->CBarPrime * this->T; + + // R sub T + this->RsubT = this->getRsubT(); + + // Put it all together! + real_type lightness = this->deltaLPrime / (this->ksubL * this->SsubL); + real_type chroma = this->deltaCPrime / (this->ksubC * this->SsubC); + real_type hue = this->deltaHPrime / (this->ksubH * this->SsubH); + + return sqrt( + pow(lightness, 2) + + pow(chroma, 2) + + pow(hue, 2) + + this->RsubT * chroma * hue + ); + }; + + /** + * Returns the RT variable calculation. + * @method + * @returns {number} + */ + real_type getRsubT() + { + return -2 * + sqrt( + pow(this->CBarPrime, 7) / + (pow(this->CBarPrime, 7) + pow((real_type)25, 7)) + ) * + sin(this->degreesToRadians( + 60 * + exp( + -( + pow( + (this->HBarPrime - 275) / 25, 2 + ) + ) + ) + )); + }; + + /** + * Returns the T variable calculation. + * @method + * @returns {number} + */ + real_type getT() + { + return 1 - + 0.17 * cos(this->degreesToRadians(this->HBarPrime - 30)) + + 0.24 * cos(this->degreesToRadians(2 * this->HBarPrime)) + + 0.32 * cos(this->degreesToRadians(3 * this->HBarPrime + 6)) - + 0.20 * cos(this->degreesToRadians(4 * this->HBarPrime - 63)); + }; + + /** + * Returns the H Bar Prime variable calculation. + * @method + * @returns {number} + */ + real_type getHBarPrime() + { + if (_abs(this->hPrime1 - this->hPrime2) > 180) { + return (this->hPrime1 + this->hPrime2 + 360) / 2; + } + + return (this->hPrime1 + this->hPrime2) / 2; + }; + + /** + * Returns the Delta h Prime variable calculation. + * @method + * @returns {number} + */ + real_type getDeltahPrime() + { + // When either C′1 or C′2 is zero, then Δh′ is irrelevant and may be set to + // zero. + if (0 == this->C1 || 0 == this->C2) { + return 0; + } + + if (_abs(this->hPrime1 - this->hPrime2) <= 180) { + return this->hPrime2 - this->hPrime1; + } + + if (this->hPrime2 <= this->hPrime1) { + return this->hPrime2 - this->hPrime1 + 360; + } else { + return this->hPrime2 - this->hPrime1 - 360; + } + }; + + /** + * Returns the h Prime 1 variable calculation. + * @method + * @returns {number} + */ + real_type gethPrime1() + { + return this->_gethPrimeFn(this->x1.B, this->aPrime1); + } + + /** + * Returns the h Prime 2 variable calculation. + * @method + * @returns {number} + */ + real_type gethPrime2() + { + return this->_gethPrimeFn(this->x2.B, this->aPrime2); + }; + + /** + * A helper function to calculate the h Prime 1 and h Prime 2 values. + * @method + * @private + * @returns {number} + */ + real_type _gethPrimeFn(real_type x, real_type y) + { + real_type hueAngle; + + if (x == 0 && y == 0) { + return 0; + } + + hueAngle = this->radiansToDegrees(atan2(x, y)); + + if (hueAngle >= 0) { + return hueAngle; + } else { + return hueAngle + 360; + } + }; + + /** + * Gives the radian equivalent of a specified degree angle. + * @method + * @returns {number} + */ + real_type radiansToDegrees(real_type radians) + { + return radians * (180 / M_PI); + }; + + /** + * Gives the degree equivalent of a specified radian. + * @method + * @returns {number} + */ + real_type degreesToRadians(real_type degrees) + { + return degrees * (M_PI / 180); + }; +}; + + +// RGB <--> Lab +// http://stackoverflow.com/a/8433985/1405560 +// http://www.easyrgb.com/index.php?X=MATH&H=08#text8 +namespace ColorSpace +{ + // using http://www.easyrgb.com/index.php?X=MATH&H=01#text1 + void rgb2lab( real_type R, real_type G, real_type B, real_type & l_s, real_type &a_s, real_type &b_s ) + { + real_type var_R = R/255.0; + real_type var_G = G/255.0; + real_type var_B = B/255.0; + + if ( var_R > 0.04045 ) var_R = pow( (( var_R + 0.055 ) / 1.055 ), 2.4 ); + else var_R = var_R / 12.92; + if ( var_G > 0.04045 ) var_G = pow( ( ( var_G + 0.055 ) / 1.055 ), 2.4); + else var_G = var_G / 12.92; + if ( var_B > 0.04045 ) var_B = pow( ( ( var_B + 0.055 ) / 1.055 ), 2.4); + else var_B = var_B / 12.92; + + var_R = var_R * 100.; + var_G = var_G * 100.; + var_B = var_B * 100.; + + //Observer. = 2°, Illuminant = D65 + real_type X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805; + real_type Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722; + real_type Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505; + + + real_type var_X = X / 95.047 ; //ref_X = 95.047 (Observer= 2°, Illuminant= D65) + real_type var_Y = Y / 100.000; //ref_Y = 100.000 + real_type var_Z = Z / 108.883; //ref_Z = 108.883 + + if ( var_X > 0.008856 ) var_X = pow(var_X , ( 1./3. ) ); + else var_X = ( 7.787 * var_X ) + ( 16. / 116. ); + if ( var_Y > 0.008856 ) var_Y = pow(var_Y , ( 1./3. )); + else var_Y = ( 7.787 * var_Y ) + ( 16. / 116. ); + if ( var_Z > 0.008856 ) var_Z = pow(var_Z , ( 1./3. )); + else var_Z = ( 7.787 * var_Z ) + ( 16. / 116. ); + + l_s = ( 116. * var_Y ) - 16.; + a_s = 500. * ( var_X - var_Y ); + b_s = 200. * ( var_Y - var_Z ); + }; + + // http://www.easyrgb.com/index.php?X=MATH&H=01#text1 + void lab2rgb( real_type l_s, real_type a_s, real_type b_s, real_type& R, real_type& G, real_type& B ) + { + real_type var_Y = ( l_s + 16. ) / 116.; + real_type var_X = a_s / 500. + var_Y; + real_type var_Z = var_Y - b_s / 200.; + + if ( pow(var_Y,3) > 0.008856 ) var_Y = pow(var_Y,3); + else var_Y = ( var_Y - 16. / 116. ) / 7.787; + if ( pow(var_X,3) > 0.008856 ) var_X = pow(var_X,3); + else var_X = ( var_X - 16. / 116. ) / 7.787; + if ( pow(var_Z,3) > 0.008856 ) var_Z = pow(var_Z,3); + else var_Z = ( var_Z - 16. / 116. ) / 7.787; + + real_type X = 95.047 * var_X ; //ref_X = 95.047 (Observer= 2°, Illuminant= D65) + real_type Y = 100.000 * var_Y ; //ref_Y = 100.000 + real_type Z = 108.883 * var_Z ; //ref_Z = 108.883 + + + var_X = X / 100. ; //X from 0 to 95.047 (Observer = 2°, Illuminant = D65) + var_Y = Y / 100. ; //Y from 0 to 100.000 + var_Z = Z / 100. ; //Z from 0 to 108.883 + + real_type var_R = var_X * 3.2406 + var_Y * -1.5372 + var_Z * -0.4986; + real_type var_G = var_X * -0.9689 + var_Y * 1.8758 + var_Z * 0.0415; + real_type var_B = var_X * 0.0557 + var_Y * -0.2040 + var_Z * 1.0570; + + if ( var_R > 0.0031308 ) var_R = 1.055 * pow(var_R , ( 1 / 2.4 )) - 0.055; + else var_R = 12.92 * var_R; + if ( var_G > 0.0031308 ) var_G = 1.055 * pow(var_G , ( 1 / 2.4 ) ) - 0.055; + else var_G = 12.92 * var_G; + if ( var_B > 0.0031308 ) var_B = 1.055 * pow( var_B , ( 1 / 2.4 ) ) - 0.055; + else var_B = 12.92 * var_B; + + R = var_R * 255.; + G = var_G * 255.; + B = var_B * 255.; + }; + + real_type DeltaE(ColorFix lab1, ColorFix lab2) + { + dE00 delta(lab1, lab2); + real_type de = delta.getDeltaE(); + return de; + }; + + BYTE min_max(real_type v) + { + if (v <= 0) + return 0; + else if (v >= 255) + return 255; + else + return (BYTE)v; + } + + void lab2rgb(real_type l_s, real_type a_s, real_type b_s, COLORREF& rgb) + { + real_type _r = 0, _g = 0, _b = 0; + lab2rgb(l_s, a_s, b_s, _r, _g, _b); + rgb = RGB(min_max(_r), min_max(_g), min_max(_b)); + }; +}; + + + +// The ColorFix implementation + +ColorFix::ColorFix() +{ + rgb = 0; + L = 0; A = 0; B = 0; +} + +ColorFix::ColorFix(COLORREF a_rgb) +{ + rgb = a_rgb; + ToLab(); +} + +ColorFix::ColorFix(real_type a_L, real_type a_a, real_type a_b) +{ + L = a_L; A = a_a; B = a_b; + ToRGB(); +} + +ColorFix::ColorFix(const ColorFix& clr) +{ + L = clr.L; A = clr.A; B = clr.B; + rgb = clr.rgb; +} + +void ColorFix::ToLab() +{ + ColorSpace::rgb2lab(r, g, b, L, A, B); +} + +void ColorFix::ToRGB() +{ + ColorSpace::lab2rgb(L, A, B, rgb); +} + +real_type ColorFix::DeltaE(ColorFix color) +{ + return ColorSpace::DeltaE(*this, color); +} + +bool ColorFix::PerceivableColor(COLORREF back/*, COLORREF alt*/, ColorFix& pColor, real_type* oldDE /*= NULL*/, real_type* newDE /*= NULL*/) +{ + bool bChanged = false; + ColorFix backLab(back); + real_type de1 = DeltaE(backLab); + if (oldDE) + *oldDE = de1; + if (newDE) + *newDE = de1; + if (de1 < g_min_thrashold) + { + for (int i = 0; i <= 1; i++) + { + real_type step = (i == 0) ? g_L_step : -g_L_step; + pColor.L = L + step; pColor.A = A; pColor.B = B; + + while (((i == 0) && (pColor.L <= 100)) || ((i == 1) && (pColor.L >= 0))) + { + real_type de2 = pColor.DeltaE(backLab); + if (de2 >= g_exp_thrashold) + { + if (newDE) + *newDE = de2; + bChanged = true; + goto wrap; + } + pColor.L += step; + } + } + } +wrap: + if (bChanged) + pColor.ToRGB(); + else + pColor = *this; + return bChanged; +} diff --git a/src/ConEmu/ColorFix.h b/src/ConEmu/ColorFix.h new file mode 100644 index 0000000000..22e110cd77 --- /dev/null +++ b/src/ConEmu/ColorFix.h @@ -0,0 +1,65 @@ + +/* +Copyright (c) 2016 Maximus5 +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#pragma once + +#include + +typedef double real_type; + +struct ColorFix +{ + // RGB + union + { + struct + { + BYTE r, g, b, dummy; + }; + COLORREF rgb; + }; + + // Lab + struct + { + real_type L, A, B; + }; + + ColorFix(); + ColorFix(COLORREF a_rgb); + ColorFix(real_type a_L, real_type a_a, real_type a_b); + ColorFix(const ColorFix& clr); + + void ToLab(); + void ToRGB(); + + real_type DeltaE(ColorFix color); + + bool PerceivableColor(COLORREF back/*, COLORREF alt*/, ColorFix& pColor, real_type* oldDE = NULL, real_type* newDE = NULL); +}; diff --git a/src/ConEmu/ConEmu.rc b/src/ConEmu/ConEmu.rc index cb08a146b1..d9ffe0a819 100644 --- a/src/ConEmu/ConEmu.rc +++ b/src/ConEmu/ConEmu.rc @@ -576,6 +576,7 @@ BEGIN RTEXT "High:",stColorFadeHigh,79,223,43,8 EDITTEXT tFadeHigh,123,220,24,12,ES_AUTOHSCROLL CONTROL "TrueMod (24bit color) support",cbTrueColorer,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,193,209,182,8 + CONTROL "Adjust lightness of indistinguishable text",cbVividColors,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,193,223,182,8 PUSHBUTTON "Save",cbColorSchemeSave,276,2,50,14,WS_DISABLED PUSHBUTTON "Delete...",cbColorSchemeDelete,328,2,50,14,WS_DISABLED RTEXT "Text:",stColorText,7,89,42,8 diff --git a/src/ConEmu/ConEmu09.vcproj b/src/ConEmu/ConEmu09.vcproj index a8c77a716d..abb7d8c6e4 100644 --- a/src/ConEmu/ConEmu09.vcproj +++ b/src/ConEmu/ConEmu09.vcproj @@ -440,6 +440,7 @@ + @@ -619,6 +620,7 @@ + diff --git a/src/ConEmu/ConEmu10.vcxproj b/src/ConEmu/ConEmu10.vcxproj index ae0a4cba12..93c5f24aa9 100644 --- a/src/ConEmu/ConEmu10.vcxproj +++ b/src/ConEmu/ConEmu10.vcxproj @@ -981,6 +981,7 @@ + @@ -1156,6 +1157,7 @@ + diff --git a/src/ConEmu/ConEmu11.vcxproj b/src/ConEmu/ConEmu11.vcxproj index 54468b3be3..238194dc0e 100644 --- a/src/ConEmu/ConEmu11.vcxproj +++ b/src/ConEmu/ConEmu11.vcxproj @@ -939,6 +939,7 @@ + @@ -1119,6 +1120,7 @@ + diff --git a/src/ConEmu/ConEmu12.vcxproj b/src/ConEmu/ConEmu12.vcxproj index 46aaec4916..8457a73747 100644 --- a/src/ConEmu/ConEmu12.vcxproj +++ b/src/ConEmu/ConEmu12.vcxproj @@ -1006,6 +1006,7 @@ copy "$(TargetPath)" Z:\ + @@ -1186,6 +1187,7 @@ copy "$(TargetPath)" Z:\ + diff --git a/src/ConEmu/ConEmu14.vcxproj b/src/ConEmu/ConEmu14.vcxproj index 0d8f8287e5..52f4ef1dfa 100644 --- a/src/ConEmu/ConEmu14.vcxproj +++ b/src/ConEmu/ConEmu14.vcxproj @@ -569,6 +569,7 @@ + @@ -749,6 +750,7 @@ + diff --git a/src/ConEmu/LngDataHints.h b/src/ConEmu/LngDataHints.h index dddd4efdc7..b96228e8c8 100644 --- a/src/ConEmu/LngDataHints.h +++ b/src/ConEmu/LngDataHints.h @@ -180,6 +180,7 @@ static LngPredefined gsDataHints[] = { { cbUseWinNumber, L"Enables switching of tabs (30 consoles) by their numbers (1,2,...,9,10,11,...). ‘Host-key’ is ‘Win’ key, by default." }, { cbUseWinTab, L"Disable Windows 7 Aero switch (Win+Tab) when ConEmu in foreground" }, { cbVisible, L"Show real console on startup" }, + { cbVividColors, L"Change lightness of text if color difference\r\nbetween text and background is indistinguishable" }, { lbCmdOutputCP, L"Windows command processor (cmd.exe) may cause then output of internal commands to be OEM or Unicode.\r\nYou may force this selection, or use automatic selection (FAR2 -> Unicode)." }, { lbCTSBlockSelection, L"Choose modifier to starting ‘Block selection’ with mouse LeftClick+Drag" }, { lbCTSEOL, L"Choose preferred line separator (\\r\\n, \\n or \\r)" }, diff --git a/src/ConEmu/Options.cpp b/src/ConEmu/Options.cpp index 51c4a2eaa1..6c351bdf71 100644 --- a/src/ConEmu/Options.cpp +++ b/src/ConEmu/Options.cpp @@ -555,6 +555,8 @@ void Settings::InitSettings() isTrueColorer = true; // включим по умолчанию, ибо Far3 + isVividColors = true; + AppStd.isExtendColors = false; AppStd.nExtendColorIdx = CEDEF_ExtendColorIdx/*14*/; AppStd.nTextColorIdx = AppStd.nBackColorIdx = CEDEF_BackColorAuto/*16*/; @@ -2416,6 +2418,7 @@ void Settings::LoadSettings(bool& rbNeedCreateVanilla, const SettingsStorage* ap LoadAppSettings(reg, &AppStd/*, Colors*/); reg->Load(L"TrueColorerSupport", isTrueColorer); + reg->Load(L"VividColors", isVividColors); reg->Load(L"FadeInactive", isFadeInactive); reg->Load(L"FadeInactiveLow", mn_FadeLow); reg->Load(L"FadeInactiveHigh", mn_FadeHigh); @@ -3516,6 +3519,7 @@ BOOL Settings::SaveSettings(BOOL abSilent /*= FALSE*/, const SettingsStorage* ap SaveAppSettings(reg, &AppStd/*, Colors*/); reg->Save(L"TrueColorerSupport", isTrueColorer); + reg->Save(L"VividColors", isVividColors); reg->Save(L"FadeInactive", isFadeInactive); reg->Save(L"FadeInactiveLow", mn_FadeLow); reg->Save(L"FadeInactiveHigh", mn_FadeHigh); diff --git a/src/ConEmu/Options.h b/src/ConEmu/Options.h index cdf5f0b246..cf46e3c64d 100644 --- a/src/ConEmu/Options.h +++ b/src/ConEmu/Options.h @@ -327,6 +327,9 @@ struct Settings //reg->Load(L"TrueColorerSupport", isTrueColorer); bool isTrueColorer; + //reg->Load(L"VividColors", isVividColors); + bool isVividColors; + /* *** Background image *** */ //reg->Load(L"BackGround Image show", isShowBgImage); diff --git a/src/ConEmu/RConPalette.cpp b/src/ConEmu/RConPalette.cpp index 99180b357c..2c66a87dc0 100644 --- a/src/ConEmu/RConPalette.cpp +++ b/src/ConEmu/RConPalette.cpp @@ -32,6 +32,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "Header.h" +#include "ColorFix.h" #include "Options.h" #include "RConPalette.h" #include "RealConsole.h" @@ -40,6 +41,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. CRConPalette::CRConPalette(CRealConsole* apRCon) : mp_RCon(apRCon) , mb_Initialized(false) + , mb_VividColors(false) , mn_FontNormalColor(0) , mn_FontBoldColor(0) , mn_FontItalicColor(0) @@ -54,9 +56,11 @@ CRConPalette::~CRConPalette() } void CRConPalette::UpdateColorTable(COLORREF *apColors/*[32]*/, + bool bVividColors, bool bExtendFonts, BYTE nFontNormalColor, BYTE nFontBoldColor, BYTE nFontItalicColor) { bool bMainChanged = !mb_Initialized + || (mb_VividColors != bVividColors) || (memcmp(m_Colors, apColors, sizeof(m_Colors)) != 0); bool bExtChanged = bMainChanged || ((mb_ExtendFonts != bExtendFonts) @@ -74,6 +78,8 @@ void CRConPalette::UpdateColorTable(COLORREF *apColors/*[32]*/, if (bMainChanged) { + real_type oldDE = 0, newDE = 0; + wchar_t szLog[200]; u32 nColorIndex = 0; for (u32 nBack = 0; nBack <= 0xF; nBack++) @@ -86,6 +92,27 @@ void CRConPalette::UpdateColorTable(COLORREF *apColors/*[32]*/, lca.crForeColor = lca.crOrigForeColor = apColors[lca.nForeIdx]; lca.crBackColor = lca.crOrigBackColor = apColors[lca.nBackIdx]; + // New feature, apply modified lightness for text colors + // if text color is indistinguishable with background + if (bVividColors && (lca.crForeColor != lca.crBackColor)) + { + ColorFix textColor(lca.crForeColor); + ColorFix vivid; + if (textColor.PerceivableColor(lca.crBackColor, vivid, &oldDE, &newDE)) + { + if (mp_RCon->isLogging()) + { + swprintf_s(szLog, L"VividColor [Bg=%u] {%u %u %u} [Fg=%u] {%u %u %u} [DE=%.2f] >> {%u %u %u} [DE=%.2f]", + nBack, getR(lca.crBackColor), getG(lca.crBackColor), getB(lca.crBackColor), + nFore, textColor.r, textColor.g, textColor.b, oldDE, + vivid.r, vivid.g, vivid.b, newDE); + mp_RCon->LogString(szLog); + } + + lca.crForeColor = vivid.rgb; + } + } + m_TableOrg[nColorIndex] = lca; } } @@ -140,6 +167,7 @@ void CRConPalette::UpdateColorTable(COLORREF *apColors/*[32]*/, // Done, remember settings mb_Initialized = true; + mb_VividColors = bVividColors; mb_ExtendFonts = bExtendFonts; mn_FontNormalColor = nFontNormalColor; mn_FontBoldColor = nFontBoldColor; diff --git a/src/ConEmu/RConPalette.h b/src/ConEmu/RConPalette.h index 8b242ce074..eff9a3ea5f 100644 --- a/src/ConEmu/RConPalette.h +++ b/src/ConEmu/RConPalette.h @@ -45,6 +45,7 @@ class CRConPalette protected: bool mb_Initialized; + bool mb_VividColors; bool mb_ExtendFonts; BYTE mn_FontNormalColor, mn_FontBoldColor, mn_FontItalicColor; @@ -55,6 +56,7 @@ class CRConPalette public: // Methods void UpdateColorTable(COLORREF *apColors/*[32]*/, + bool bVividColors, bool bExtendFonts, BYTE nFontNormalColor, BYTE nFontBoldColor, BYTE nFontItalicColor); }; diff --git a/src/ConEmu/RealBuffer.cpp b/src/ConEmu/RealBuffer.cpp index bb1b099299..14dc6a9fe8 100644 --- a/src/ConEmu/RealBuffer.cpp +++ b/src/ConEmu/RealBuffer.cpp @@ -5671,6 +5671,7 @@ void CRealBuffer::PrepareColorTable(const AppSettings* pApp, CharAttr** pcaTable // Optimized, palettes are recalculated only when changes are detected mp_RCon->mp_Palette->UpdateColorTable(mp_RCon->mp_VCon->GetColors()/*[16+]*/, + gpSet->isVividColors, bExtendFonts, nFontNormalColor, nFontBoldColor, nFontItalicColor); if (pcaTableOrg) diff --git a/src/ConEmu/SetDlgButtons.cpp b/src/ConEmu/SetDlgButtons.cpp index 84fc111ca8..0820ac49e6 100644 --- a/src/ConEmu/SetDlgButtons.cpp +++ b/src/ConEmu/SetDlgButtons.cpp @@ -599,6 +599,9 @@ bool CSetDlgButtons::ProcessButtonClick(HWND hDlg, WORD CB, BYTE uCheck) case cbTrueColorer: OnBtn_TrueColorer(hDlg, CB, uCheck); break; + case cbVividColors: + OnBtn_VividColors(hDlg, CB, uCheck); + break; case cbFadeInactive: OnBtn_FadeInactive(hDlg, CB, uCheck); break; @@ -3707,6 +3710,17 @@ void CSetDlgButtons::OnBtn_TrueColorer(HWND hDlg, WORD CB, BYTE uCheck) } // cbTrueColorer +// cbVividColors +void CSetDlgButtons::OnBtn_VividColors(HWND hDlg, WORD CB, BYTE uCheck) +{ + _ASSERTE(CB==cbVividColors); + + gpSet->isVividColors = _bool(uCheck); + gpConEmu->Update(true); + +} // cbVividColors + + // cbFadeInactive void CSetDlgButtons::OnBtn_FadeInactive(HWND hDlg, WORD CB, BYTE uCheck) { diff --git a/src/ConEmu/SetDlgButtons.h b/src/ConEmu/SetDlgButtons.h index 6bef697915..f125fec640 100644 --- a/src/ConEmu/SetDlgButtons.h +++ b/src/ConEmu/SetDlgButtons.h @@ -213,6 +213,7 @@ class CSetDlgButtons : public CDlgItemHelper static void OnBtn_ColorResetExt(HWND hDlg, WORD CB, BYTE uCheck); static void OnBtn_ColorSchemeSaveDelete(HWND hDlg, WORD CB, BYTE uCheck); static void OnBtn_TrueColorer(HWND hDlg, WORD CB, BYTE uCheck); + static void OnBtn_VividColors(HWND hDlg, WORD CB, BYTE uCheck); static void OnBtn_FadeInactive(HWND hDlg, WORD CB, BYTE uCheck); static void OnBtn_Transparent(HWND hDlg, WORD CB, BYTE uCheck); static void OnBtn_TransparentSeparate(HWND hDlg, WORD CB, BYTE uCheck); diff --git a/src/ConEmu/SetPgColors.cpp b/src/ConEmu/SetPgColors.cpp index 4d19d20761..1d73469163 100644 --- a/src/ConEmu/SetPgColors.cpp +++ b/src/ConEmu/SetPgColors.cpp @@ -81,6 +81,7 @@ LRESULT CSetPgColors::OnInitDialog(HWND hDlg, bool abInitial) checkDlgButton(hDlg, cbExtendColors, gpSet->AppStd.isExtendColors ? BST_CHECKED : BST_UNCHECKED); CSetDlgButtons::OnButtonClicked(hDlg, cbExtendColors, 0); checkDlgButton(hDlg, cbTrueColorer, gpSet->isTrueColorer ? BST_CHECKED : BST_UNCHECKED); + checkDlgButton(hDlg, cbVividColors, gpSet->isVividColors ? BST_CHECKED : BST_UNCHECKED); checkDlgButton(hDlg, cbFadeInactive, gpSet->isFadeInactive ? BST_CHECKED : BST_UNCHECKED); SetDlgItemInt(hDlg, tFadeLow, gpSet->mn_FadeLow, FALSE); SetDlgItemInt(hDlg, tFadeHigh, gpSet->mn_FadeHigh, FALSE); diff --git a/src/ConEmu/makefile_gcc b/src/ConEmu/makefile_gcc index 0cb906ef46..8f2632456e 100644 --- a/src/ConEmu/makefile_gcc +++ b/src/ConEmu/makefile_gcc @@ -72,6 +72,7 @@ SRCS = \ Background.cpp \ BaseDragDrops.cpp \ CmdHistory.cpp \ + ColorFix.cpp \ ConEmu.cpp \ ConEmuApp.cpp \ ConEmuCtrl.cpp \ diff --git a/src/ConEmu/makefile_vc b/src/ConEmu/makefile_vc index fc9d4db482..cc00a8cb55 100644 --- a/src/ConEmu/makefile_vc +++ b/src/ConEmu/makefile_vc @@ -41,6 +41,7 @@ $(INTDIR)\BaseDragDrops.obj \ $(INTDIR)\CEStr.obj \ $(INTDIR)\CmdHistory.obj \ $(INTDIR)\CmdLine.obj \ +$(INTDIR)\ColorFix.obj \ $(INTDIR)\Common.obj \ $(INTDIR)\ConEmu.obj \ $(INTDIR)\ConEmuApp.obj \ @@ -350,6 +351,8 @@ $(INTDIR)\CmdHistory.obj: CmdHistory.cpp CmdHistory.h $(INTDIR)\CmdLine.obj: ../common/CmdLine.cpp ../common/CmdLine.h +$(INTDIR)\ColorFix.obj: ColorFix.cpp ColorFix.h + $(INTDIR)\Common.obj: ../common/Common.cpp ../common/Common.h $(INTDIR)\ConEmu.obj: ConEmu.cpp Version.h GuiServer.h GestureEngine.h ../common/PipeServer.h diff --git a/src/ConEmu/resource.h b/src/ConEmu/resource.h index c760d86544..3c7111f6c8 100644 --- a/src/ConEmu/resource.h +++ b/src/ConEmu/resource.h @@ -1330,6 +1330,7 @@ #define lbHotKeyFilter 3053 #define stElevatedConsolesGroup 3054 #define gbTabDblClkActions 3055 +#define cbVividColors 3056 #define IDC_STATIC -1 // Next default values for new objects @@ -1338,7 +1339,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 235 #define _APS_NEXT_COMMAND_VALUE 40010 -#define _APS_NEXT_CONTROL_VALUE 3056 +#define _APS_NEXT_CONTROL_VALUE 3057 #define _APS_NEXT_SYMED_VALUE 130 #endif #endif