use new Colour type for specials and gradients (#1418)

* gradient, colours: use 24-bit colour depth internally on Wayland

our Colour type assumes this anyway; lower colour depth should be handled by display backends

* core: restore gradient color computation

this was mistakenly disabled when moving to a unified Colour representation

* core, gradient: use Colour type

* tests: gradient: always test hsv/hcl

colour computations no longer depend on the display backends we compile with, so these can be tested unconditionally now

* gradients, specials: use Color type

this makes specials now accept color names as well as hex codes, and removes all notions of color depth from gradient calculations

---------

Co-authored-by: bi4k8 <bi4k8@github>
This commit is contained in:
bi4k8 2023-02-24 13:13:19 +00:00 committed by GitHub
parent e3ed817216
commit 5e98c49c4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 84 additions and 195 deletions

View File

@ -29,62 +29,8 @@
#include "conky.h"
#include "gui.h"
#include "logging.h"
#ifdef BUILD_X11
#include "x11.h"
#endif /*BUILD_X11*/
#include "x11-color.h"
/* precalculated: 31/255, and 63/255 */
#define CONST_8_TO_5_BITS 0.12156862745098
#define CONST_8_TO_6_BITS 0.247058823529412
short colour_depth = 0;
long redmask, greenmask, bluemask;
void set_up_gradient() {
#ifdef BUILD_X11
if (out_to_x.get(*state)) {
colour_depth = DisplayPlanes(display, screen);
} else
#endif /* BUILD_X11 */
{
colour_depth = 16;
}
if (colour_depth != 24 && colour_depth != 16) {
NORM_ERR(
"using non-standard colour depth, gradients may look like a "
"lolly-pop");
}
redmask = 0;
greenmask = 0;
bluemask = 0;
for (int i = (colour_depth / 3) - 1; i >= 0; i--) {
redmask |= 1 << i;
greenmask |= 1 << i;
bluemask |= 1 << i;
}
if (colour_depth % 3 == 1) { greenmask |= 1 << (colour_depth / 3); }
redmask = redmask << (2 * colour_depth / 3 + colour_depth % 3);
greenmask = greenmask << (colour_depth / 3);
}
/* adjust colour values depending on colour depth */
unsigned int adjust_colours(unsigned int colour) {
double r, g, b;
if (colour_depth == 0) { set_up_gradient(); }
if (colour_depth == 16) {
r = (colour & 0xff0000) >> 16;
g = (colour & 0xff00) >> 8;
b = colour & 0xff;
colour = static_cast<int>(r * CONST_8_TO_5_BITS) << 11;
colour |= static_cast<int>(g * CONST_8_TO_6_BITS) << 5;
colour |= static_cast<int>(b * CONST_8_TO_5_BITS);
}
return colour;
}
static int hex_nibble_value(char c) {
if (c >= '0' && c <= '9') {
return c - '0';

View File

@ -37,8 +37,6 @@
#include "x11.h"
#endif /* BUILD_X11 */
unsigned int adjust_colours(unsigned int);
struct Colour {
uint8_t red;
uint8_t green;
@ -46,6 +44,12 @@ struct Colour {
uint8_t alpha;
public:
// Compare two instances.
bool operator==(const Colour &c) const {
return c.red == red && c.green == green && c.blue == blue &&
c.alpha == alpha;
}
// Express the color as a 32-bit ARGB integer (alpha in MSB).
uint32_t to_argb32(void) {
uint32_t out;

View File

@ -407,9 +407,8 @@ int dpi_scale(int value) {
}
#ifdef BUILD_GUI
conky::gradient_factory *create_gradient_factory(int width,
unsigned long first_colour,
unsigned long last_colour) {
conky::gradient_factory *create_gradient_factory(int width, Colour first_colour,
Colour last_colour) {
switch (graph_gradient_mode.get(*state)) {
case RGB_GRADIENT:
return new conky::rgb_gradient_factory(width, first_colour, last_colour);
@ -1283,7 +1282,7 @@ int draw_each_line_inner(char *s, int special_index, int last_special_applied) {
case GRAPH:
if (display_output() && display_output()->graphical()) {
int h, by, i = 0, j = 0;
//int colour_idx = 0;
int colour_idx = 0;
Colour last_colour = current_color;
if (cur_x - text_start_x > mw && mw > 0) { break; }
h = current->height;
@ -1310,18 +1309,18 @@ int draw_each_line_inner(char *s, int special_index, int last_special_applied) {
/* in case we don't have a graph yet */
if (current->graph != nullptr) {
std::unique_ptr<unsigned long[]> tmpcolour;
std::unique_ptr<Colour[]> tmpcolour;
if (current->last_colour != 0 || current->first_colour != 0) {
if (current->colours_set) {
auto factory = create_gradient_factory(w, current->last_colour,
current->first_colour);
tmpcolour = factory->create_gradient();
delete factory;
}
//colour_idx = 0;
colour_idx = 0;
for (i = w - 2; i > -1; i--) {
if (current->last_colour != 0 || current->first_colour != 0) {
/*if (current->tempgrad != 0) {
if (current->colours_set) {
if (current->tempgrad != 0) {
set_foreground_color(tmpcolour[static_cast<int>(
static_cast<float>(w - 2) -
current->graph[j] * (w - 2) /
@ -1329,7 +1328,7 @@ int draw_each_line_inner(char *s, int special_index, int last_special_applied) {
1.0F))]);
} else {
set_foreground_color(tmpcolour[colour_idx++]);
}*/
}
}
/* this is mugfugly, but it works */
if (display_output()) {

View File

@ -27,99 +27,42 @@
*
*/
#include "gradient.h"
#include "colours.h"
#include "conky.h"
#include "logging.h"
#ifdef BUILD_X11
#include "gui.h"
#include "x11.h"
#endif /* BUILD_X11 */
namespace conky {
bool gradient_factory::is_set = false;
short gradient_factory::colour_depth = 0;
long gradient_factory::mask[3];
short gradient_factory::shift[3];
gradient_factory::gradient_factory(int width, unsigned long first_colour,
unsigned long last_colour) {
gradient_factory::gradient_factory(int width, Colour first_colour,
Colour last_colour) {
// Make sure the width is always at least 2
this->width = std::max(2, width);
this->first_colour = first_colour;
this->last_colour = last_colour;
if (!is_set) {
setup_colour_depth();
setup_shifts();
setup_masks();
is_set = true;
}
}
void gradient_factory::setup_shifts() {
shift[0] = (2 * colour_depth / 3 + colour_depth % 3);
shift[1] = (colour_depth / 3);
shift[2] = 0;
}
void gradient_factory::setup_masks() {
mask[0] = mask[1] = mask[2] = 0;
for (int i = (colour_depth / 3) - 1; i >= 0; i--) {
mask[0] |= 1 << i;
mask[1] |= 1 << i;
mask[2] |= 1 << i;
}
if (colour_depth % 3 == 1) { mask[1] |= 1 << (colour_depth / 3); }
for (int i = 0; i < 3; i++) { mask[i] = mask[i] << shift[i]; }
}
void gradient_factory::setup_colour_depth() {
#ifdef BUILD_X11
if (state == nullptr) { // testing purposes
colour_depth = 24;
} else if (out_to_x.get(*state)) {
colour_depth = DisplayPlanes(display, screen);
} else
#endif /* BUILD_X11 */
{
colour_depth = 16;
}
if (colour_depth != 24 && colour_depth != 16) {
NORM_ERR(
"using non-standard colour depth, "
"gradients may look like a lolly-pop");
}
}
void gradient_factory::convert_from_rgb(long original, long *array) {
void gradient_factory::convert_from_rgb(Colour original, long *array) {
long scaled[3];
for (int i = 0; i < 3; i++) {
auto value = (original & mask[i]) >> shift[i];
scaled[i] = value * SCALE;
}
scaled[0] = original.red * SCALE;
scaled[1] = original.green * SCALE;
scaled[2] = original.blue * SCALE;
convert_from_scaled_rgb(scaled, array);
}
int gradient_factory::convert_to_rgb(long *const array) {
Colour gradient_factory::convert_to_rgb(long *const array) {
long scaled_rgb[3];
int rgb = 0;
Colour c;
convert_to_scaled_rgb(array, scaled_rgb);
for (int i = 0; i < 3; i++) {
auto value = scaled_rgb[i] / SCALE;
rgb |= value << shift[i];
}
c.red = scaled_rgb[0] / SCALE;
c.green = scaled_rgb[1] / SCALE;
c.blue = scaled_rgb[2] / SCALE;
return rgb;
return c;
}
gradient_factory::colour_array gradient_factory::create_gradient() {
colour_array colours(new unsigned long[width]);
colour_array colours(new Colour[width]);
long first_converted[3];
long last_converted[3];

View File

@ -31,11 +31,12 @@
#define _GRADIENT_H
#include <memory>
#include "colours.h"
namespace conky {
class gradient_factory {
public:
typedef std::unique_ptr<unsigned long[]> colour_array;
typedef std::unique_ptr<Colour[]> colour_array;
static const long SCALE = 512L;
static const long SCALE2 = SCALE * 2;
static const long SCALE4 = SCALE * 4;
@ -48,8 +49,7 @@ class gradient_factory {
static const long SCALE360 = SCALE * 360;
public:
gradient_factory(int width, unsigned long first_colour,
unsigned long last_colour);
gradient_factory(int width, Colour first_colour, Colour last_colour);
virtual ~gradient_factory() {}
colour_array create_gradient();
@ -57,8 +57,8 @@ class gradient_factory {
virtual void convert_from_scaled_rgb(long *const scaled, long *target) = 0;
virtual void convert_to_scaled_rgb(long *const target, long *scaled) = 0;
void convert_from_rgb(long original, long *array);
int convert_to_rgb(long *const array);
void convert_from_rgb(Colour original, long *array);
Colour convert_to_rgb(long *const array);
protected:
virtual void fix_diff(long *) {}
@ -66,19 +66,10 @@ class gradient_factory {
static long get_hue(long *const scaled, long chroma, long value);
static long get_intermediate(long hue, long chroma);
static short colour_depth;
static long mask[3];
static short shift[3];
private:
int width;
unsigned long first_colour;
unsigned long last_colour;
static bool is_set;
static void setup_colour_depth();
static void setup_masks();
static void setup_shifts();
Colour first_colour;
Colour last_colour;
};
class rgb_gradient_factory : public gradient_factory {

View File

@ -102,7 +102,8 @@ struct graph {
int id;
char flags;
int width, height;
unsigned int first_colour, last_colour;
bool colours_set;
Colour first_colour, last_colour;
double scale;
char tempgrad;
};
@ -191,6 +192,13 @@ void scan_font(struct text_object *obj, const char *args) {
}
}
void apply_graph_colours(struct graph *g, const char *first_colour_name,
const char *last_colour_name) {
g->first_colour = parse_color(first_colour_name);
g->last_colour = parse_color(last_colour_name);
g->colours_set = true;
}
/**
* parses for [height,width] [color1 color2] [scale] [-t] [-l]
*
@ -207,6 +215,8 @@ char *scan_graph(struct text_object *obj, const char *args, double defscale) {
char quoted_cmd[1024] = {'\0'}; /* double-quoted execgraph command */
char argstr[1024] = {'\0'}; /* args minus quoted_cmd */
char buf[1024] = {'\0'}; /* first unquoted string argument in argstr */
char first_colour_name[1024] = {'\0'};
char last_colour_name[1024] = {'\0'};
auto *g = static_cast<struct graph *>(malloc(sizeof(struct graph)));
memset(g, 0, sizeof(struct graph));
@ -216,8 +226,9 @@ char *scan_graph(struct text_object *obj, const char *args, double defscale) {
g->id = ++graph_count;
g->width = default_graph_width.get(*state);
g->height = default_graph_height.get(*state);
g->first_colour = 0;
g->last_colour = 0;
g->colours_set = false;
g->first_colour = Colour();
g->last_colour = Colour();
g->scale = defscale;
g->tempgrad = FALSE;
if (args != nullptr) {
@ -264,59 +275,67 @@ char *scan_graph(struct text_object *obj, const char *args, double defscale) {
/* interpret the beginning(!) of the argument string as:
* '[height],[width] [color1] [color2] [scale]'
* This means parameters like -t and -l may not be in the beginning */
if (sscanf(argstr, "%d,%d %x %x %lf", &g->height, &g->width,
&g->first_colour, &g->last_colour, &g->scale) == 5) {
if (sscanf(argstr, "%d,%d %s %s %lf", &g->height, &g->width,
first_colour_name, last_colour_name, &g->scale) == 5) {
apply_graph_colours(g, first_colour_name, last_colour_name);
return *quoted_cmd != 0
? strndup(quoted_cmd, text_buffer_size.get(*state))
: nullptr;
}
/* [height],[width] [color1] [color2] */
g->scale = defscale;
if (sscanf(argstr, "%d,%d %x %x", &g->height, &g->width, &g->first_colour,
&g->last_colour) == 4) {
if (sscanf(argstr, "%d,%d %s %s", &g->height, &g->width, first_colour_name,
last_colour_name) == 4) {
apply_graph_colours(g, first_colour_name, last_colour_name);
return *quoted_cmd != 0
? strndup(quoted_cmd, text_buffer_size.get(*state))
: nullptr;
}
/* [command] [height],[width] [color1] [color2] [scale] */
if (sscanf(argstr, "%1023s %d,%d %x %x %lf", buf, &g->height, &g->width,
&g->first_colour, &g->last_colour, &g->scale) == 6) {
if (sscanf(argstr, "%1023s %d,%d %s %s %lf", buf, &g->height, &g->width,
first_colour_name, last_colour_name, &g->scale) == 6) {
apply_graph_colours(g, first_colour_name, last_colour_name);
return strndup(buf, text_buffer_size.get(*state));
}
g->scale = defscale;
if (sscanf(argstr, "%1023s %d,%d %x %x", buf, &g->height, &g->width,
&g->first_colour, &g->last_colour) == 5) {
if (sscanf(argstr, "%1023s %d,%d %s %s", buf, &g->height, &g->width,
first_colour_name, last_colour_name) == 5) {
apply_graph_colours(g, first_colour_name, last_colour_name);
return strndup(buf, text_buffer_size.get(*state));
}
buf[0] = '\0';
g->height = default_graph_height.get(*state);
g->width = default_graph_width.get(*state);
if (sscanf(argstr, "%x %x %lf", &g->first_colour, &g->last_colour,
if (sscanf(argstr, "%s %s %lf", first_colour_name, last_colour_name,
&g->scale) == 3) {
apply_graph_colours(g, first_colour_name, last_colour_name);
return *quoted_cmd != 0
? strndup(quoted_cmd, text_buffer_size.get(*state))
: nullptr;
}
g->scale = defscale;
if (sscanf(argstr, "%x %x", &g->first_colour, &g->last_colour) == 2) {
if (sscanf(argstr, "%s %s", first_colour_name, last_colour_name) == 2) {
apply_graph_colours(g, first_colour_name, last_colour_name);
return *quoted_cmd != 0
? strndup(quoted_cmd, text_buffer_size.get(*state))
: nullptr;
}
if (sscanf(argstr, "%1023s %x %x %lf", buf, &g->first_colour,
&g->last_colour, &g->scale) == 4) {
if (sscanf(argstr, "%1023s %s %s %lf", buf, first_colour_name,
last_colour_name, &g->scale) == 4) {
apply_graph_colours(g, first_colour_name, last_colour_name);
return strndup(buf, text_buffer_size.get(*state));
}
g->scale = defscale;
if (sscanf(argstr, "%1023s %x %x", buf, &g->first_colour,
&g->last_colour) == 3) {
if (sscanf(argstr, "%1023s %s %s", buf, first_colour_name,
last_colour_name) == 3) {
apply_graph_colours(g, first_colour_name, last_colour_name);
return strndup(buf, text_buffer_size.get(*state));
}
buf[0] = '\0';
g->first_colour = 0;
g->last_colour = 0;
first_colour_name[0] = '\0';
last_colour_name[0] = '\0';
if (sscanf(argstr, "%d,%d %lf", &g->height, &g->width, &g->scale) == 3) {
return *quoted_cmd != 0
? strndup(quoted_cmd, text_buffer_size.get(*state))
@ -584,8 +603,9 @@ void new_graph(struct text_object *obj, char *buf, int buf_max_size,
graphs[g->id] = graph;
}
s->height = dpi_scale(g->height);
s->first_colour = adjust_colours(g->first_colour);
s->last_colour = adjust_colours(g->last_colour);
s->colours_set = g->colours_set;
s->first_colour = g->first_colour;
s->last_colour = g->last_colour;
if (g->scale != 0) {
s->scaled = 0;
s->scale = g->scale;

View File

@ -29,6 +29,8 @@
#ifndef _SPECIALS_H
#define _SPECIALS_H
#include "colours.h"
/* special stuff in text_buffer */
#define SPECIAL_CHAR '\x01'
@ -69,8 +71,9 @@ struct special_t {
int graph_allocated;
int scaled; /* auto adjust maximum */
int scale_log;
unsigned long first_colour; // for graph gradient
unsigned long last_colour;
bool colours_set;
Colour first_colour; // for graph gradient
Colour last_colour;
short font_added;
char tempgrad;
struct special_t *next;

View File

@ -28,12 +28,12 @@
#include "catch2/catch.hpp"
#include <colours.h>
#include <conky.h>
#include <gradient.h>
const int width = 4;
#ifdef BUILD_X11 // 24-bit color depth
const long colour = 0x996633; // brown
const Colour colour = Colour::from_argb32(0x996633); // brown
const long expected_hue = 256;
const long expected_value = 0x99; // max(0x99, 0x66, 0x33)
const long expected_chroma = 0x66; // (0x99 - 0x33)
@ -42,17 +42,6 @@ const long expected_saturation = 122880L;
const long expected_red = 0x99;
const long expected_green = 0x66;
const long expected_blue = 0x33;
#else // 16-bit color depth
const long colour = 0x99A6; // brown
const long expected_hue = 275;
const long expected_value = 0x13; // max(0x13, 0x0d, 0x06)
const long expected_chroma = 0x0d; // (0x1a - 0x06)
const long expected_luma = 2610173L;
const long expected_saturation = 126113L;
const long expected_red = 0x13;
const long expected_green = 0x0d;
const long expected_blue = 0x06;
#endif
const long full_scale = conky::gradient_factory::SCALE360;
@ -114,11 +103,6 @@ TEST_CASE(
delete factory;
}
/*
* Due to lack of precision, the HSV and HCL functions are not reversible
* if color depth is less than 24-bit
*/
#ifdef BUILD_X11
SECTION("hsv_gradient_factory") {
long tmp[3];
auto factory = new conky::hsv_gradient_factory(width, colour, colour);
@ -140,5 +124,4 @@ TEST_CASE(
delete factory;
}
#endif
}