/** * Clever contrast function * * It will try to adjust the foreground color such that it contrasts well with the background * It won't modify the hue of fg unless absolutely necessary * @return the adjusted form of fg */ QColor ensureContrast( const QColor &bg, const QColor &fg, uint _amount = 150 ) { class OutputOnExit { public: OutputOnExit( const QColor &color ) : c( color ) {} ~OutputOnExit() { int h,s,v; c.getHsv( &h, &s, &v ); } private: const QColor &c; }; // hack so I don't have to cast everywhere #define amount static_cast<int>(_amount) // #define STAMP debug() << (QValueList<int>() << fh << fs << fv); // #define STAMP1( string ) debug() << string << ": " << (QValueList<int>() << fh << fs << fv); // #define STAMP2( string, value ) debug() << string << "=" << value << ": " << (QValueList<int>() << fh << fs << fv); OutputOnExit allocateOnTheStack( fg ); int bh, bs, bv; int fh, fs, fv; bg.getHsv( &bh, &bs, &bv ); fg.getHsv( &fh, &fs, &fv ); int dv = abs( bv - fv ); // STAMP2( "DV", dv ); // value is the best measure of contrast // if there is enough difference in value already, return fg unchanged if( dv > amount ) return fg; int ds = abs( bs - fs ); // STAMP2( "DS", ds ); // saturation is good enough too. But not as good. TODO adapt this a little if( ds > amount ) return fg; int dh = abs( bh - fh ); // STAMP2( "DH", dh ); if( dh > 120 ) { // a third of the colour wheel automatically guarentees contrast // but only if the values are high enough and saturations significant enough // to allow the colours to be visible and not be shades of grey or black // check the saturation for the two colours is sufficient that hue alone can // provide sufficient contrast if( ds > amount / 2 && (bs > 125 && fs > 125) ) // STAMP1( "Sufficient saturation difference, and hues are compliemtary" ); return fg; else if( dv > amount / 2 && (bv > 125 && fv > 125) ) // STAMP1( "Sufficient value difference, and hues are compliemtary" ); return fg; // STAMP1( "Hues are complimentary but we must modify the value or saturation of the contrasting colour" ); //but either the colours are two desaturated, or too dark //so we need to adjust the system, although not as much ///_amount /= 2; } if( fs < 50 && ds < 40 ) { // low saturation on a low saturation is sad const int tmp = 50 - fs; fs = 50; if( amount > tmp ) _amount -= tmp; else _amount = 0; } // test that there is available value to honor our contrast requirement if( 255 - dv < amount ) { // we have to modify the value and saturation of fg //adjustToLimits( bv, fv, amount ); // STAMP // see if we need to adjust the saturation if( amount > 0 ) adjustToLimits( bs, fs, _amount ); // STAMP // see if we need to adjust the hue if( amount > 0 ) fh += amount; // cycles around; // STAMP return QColor::fromHsv( fh, fs, fv ); } // STAMP if( fv > bv && bv > amount ) return QColor::fromHsv( fh, fs, bv - amount ); // STAMP if( fv < bv && fv > amount ) return QColor::fromHsv( fh, fs, fv - amount ); // STAMP if( fv > bv && (255 - fv > amount) ) return QColor::fromHsv( fh, fs, fv + amount ); // STAMP if( fv < bv && (255 - bv > amount ) ) return QColor::fromHsv( fh, fs, bv + amount ); // STAMP // debug() << "Something went wrong!\n"; return Qt::blue; #undef amount // #undef STAMP }
/** * Clever contrast function * * It will try to adjust the foreground color such that it contrasts well with *the background * It won't modify the hue of fg unless absolutely necessary * @return the adjusted form of fg */ QColor ensureContrast(const QColor& bg, const QColor& fg, uint _amount = 150) { class OutputOnExit { public: explicit OutputOnExit(const QColor& color) : c(color) {} ~OutputOnExit() { int h, s, v; c.getHsv(&h, &s, &v); } private: const QColor& c; }; OutputOnExit allocateOnTheStack(fg); int bh, bs, bv; int fh, fs, fv; bg.getHsv(&bh, &bs, &bv); fg.getHsv(&fh, &fs, &fv); int dv = abs(bv - fv); // value is the best measure of contrast // if there is enough difference in value already, return fg unchanged if (dv > static_cast<int>(_amount)) return fg; int ds = abs(bs - fs); // saturation is good enough too. But not as good. TODO adapt this a little if (ds > static_cast<int>(_amount)) return fg; int dh = abs(bh - fh); if (dh > 120) { // a third of the colour wheel automatically guarentees contrast // but only if the values are high enough and saturations significant enough // to allow the colours to be visible and not be shades of grey or black // check the saturation for the two colours is sufficient that hue alone can // provide sufficient contrast if (ds > static_cast<int>(_amount) / 2 && (bs > 125 && fs > 125)) return fg; else if (dv > static_cast<int>(_amount) / 2 && (bv > 125 && fv > 125)) return fg; } if (fs < 50 && ds < 40) { // low saturation on a low saturation is sad const int tmp = 50 - fs; fs = 50; if (static_cast<int>(_amount) > tmp) _amount -= tmp; else _amount = 0; } // test that there is available value to honor our contrast requirement if (255 - dv < static_cast<int>(_amount)) { // we have to modify the value and saturation of fg // adjustToLimits( bv, fv, amount ); // see if we need to adjust the saturation if (static_cast<int>(_amount) > 0) adjustToLimits(bs, fs, _amount); // see if we need to adjust the hue if (static_cast<int>(_amount) > 0) fh += static_cast<int>(_amount); // cycles around; return QColor::fromHsv(fh, fs, fv); } if (fv > bv && bv > static_cast<int>(_amount)) return QColor::fromHsv(fh, fs, bv - static_cast<int>(_amount)); if (fv < bv && fv > static_cast<int>(_amount)) return QColor::fromHsv(fh, fs, fv - static_cast<int>(_amount)); if (fv > bv && (255 - fv > static_cast<int>(_amount))) return QColor::fromHsv(fh, fs, fv + static_cast<int>(_amount)); if (fv < bv && (255 - bv > static_cast<int>(_amount))) return QColor::fromHsv(fh, fs, bv + static_cast<int>(_amount)); return Qt::blue; }