Skip to content

Commit

Permalink
Conditionally handle linear sRGB in stroke_to func
Browse files Browse the repository at this point in the history
API-breaking change!
This is a temporary measure until the brush dynamics are
made color agnostic. Required to get correct HSV/HSL transformations
when the brush color is stored as linear sRGB.
  • Loading branch information
jplloyd committed Apr 18, 2020
1 parent 5b358df commit 3463f67
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 11 deletions.
3 changes: 2 additions & 1 deletion examples/minimal.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ stroke_to(MyPaintBrush *brush, MyPaintSurface *surf, float x, float y)
{
float viewzoom = 1.0, viewrotation = 0.0, barrel_rotation = 0.0;
float pressure = 1.0, ytilt = 0.0, xtilt = 0.0, dtime = 1.0/10;
gboolean linear = FALSE
mypaint_brush_stroke_to
(brush, surf, x, y, pressure, xtilt, ytilt, dtime, viewzoom, viewrotation, barrel_rotation);
(brush, surf, x, y, pressure, xtilt, ytilt, dtime, viewzoom, viewrotation, barrel_rotation, linear);
}

int
Expand Down
37 changes: 30 additions & 7 deletions mypaint-brush.c
Original file line number Diff line number Diff line change
Expand Up @@ -1039,7 +1039,7 @@ void print_inputs(MyPaintBrush *self, float* inputs)
//
// This is only gets called right after update_states_and_setting_values().
// Returns TRUE if the surface was modified.
gboolean prepare_and_draw_dab (MyPaintBrush *self, MyPaintSurface * surface)
gboolean prepare_and_draw_dab (MyPaintBrush *self, MyPaintSurface * surface, gboolean linear)
{
const float opaque_fac = SETTING(self, OPAQUE_MULTIPLY);
// ensure we don't get a positive result with two negative opaque values
Expand Down Expand Up @@ -1145,8 +1145,25 @@ void print_inputs(MyPaintBrush *self, float* inputs)
eraser_target_alpha *= (1.0-SETTING(self, ERASER));
}

/*
If the colors are stored as linear sRGB, they need to be transformed to
the form compatible with the hsv/hsl conversion functions, and then
transformed back after the adjustments.
*/
gboolean using_hsv_dynamics =
SETTING(self, CHANGE_COLOR_H) || SETTING(self, CHANGE_COLOR_HSV_S) || SETTING(self, CHANGE_COLOR_V);
gboolean using_hsl_dynamics = SETTING(self, CHANGE_COLOR_L) || SETTING(self, CHANGE_COLOR_HSL_S);
gboolean using_color_dynamics = using_hsv_dynamics || using_hsl_dynamics;

// delinearize
if (linear && using_color_dynamics) {
color_h = powf(color_h, 1 / 2.2);
color_s = powf(color_s, 1 / 2.2);
color_v = powf(color_v, 1 / 2.2);
}

// HSV color change
if (SETTING(self, CHANGE_COLOR_H) || SETTING(self, CHANGE_COLOR_HSV_S) || SETTING(self, CHANGE_COLOR_V)) {
if (using_hsv_dynamics) {
rgb_to_hsv_float(&color_h, &color_s, &color_v);
color_h += SETTING(self, CHANGE_COLOR_H);
color_s += color_s * color_v * SETTING(self, CHANGE_COLOR_HSV_S);
Expand All @@ -1155,17 +1172,23 @@ void print_inputs(MyPaintBrush *self, float* inputs)
}

// HSL color change
if (SETTING(self, CHANGE_COLOR_L) || SETTING(self, CHANGE_COLOR_HSL_S)) {
if (using_hsl_dynamics) {
// (calculating way too much here, can be optimized if necessary)
// this function will CLAMP the inputs

rgb_to_hsl_float (&color_h, &color_s, &color_v);
color_v += SETTING(self, CHANGE_COLOR_L);
color_s += color_s * MIN(fabsf(1.0f - color_v), fabsf(color_v)) * 2.0f
* SETTING(self, CHANGE_COLOR_HSL_S);
hsl_to_rgb_float (&color_h, &color_s, &color_v);
}

// linearize
if (linear && using_color_dynamics) {
color_h = powf(color_h, 2.2);
color_s = powf(color_s, 2.2);
color_v = powf(color_v, 2.2);
}

float hardness = CLAMP(SETTING(self, HARDNESS), 0.0f, 1.0f);
float softness = CLAMP(SETTING(self, SOFTNESS), 0.0f, 1.0f);

Expand Down Expand Up @@ -1276,7 +1299,7 @@ void print_inputs(MyPaintBrush *self, float* inputs)
*/
int mypaint_brush_stroke_to (MyPaintBrush *self, MyPaintSurface *surface,
float x, float y, float pressure,
float xtilt, float ytilt, double dtime, float viewzoom, float viewrotation, float barrel_rotation)
float xtilt, float ytilt, double dtime, float viewzoom, float viewrotation, float barrel_rotation, gboolean linear)
{
const float max_dtime = 5;

Expand Down Expand Up @@ -1323,7 +1346,7 @@ void print_inputs(MyPaintBrush *self, float* inputs)
if (dtime > 0.100 && pressure && STATE(self, PRESSURE) == 0) {
// Workaround for tablets that don't report motion events without pressure.
// This is to avoid linear interpolation of the pressure between two events.
mypaint_brush_stroke_to (self, surface, x, y, 0.0, 90.0, 0.0, dtime-0.0001, viewzoom, viewrotation, 0.0);
mypaint_brush_stroke_to (self, surface, x, y, 0.0, 90.0, 0.0, dtime-0.0001, viewzoom, viewrotation, 0.0, linear);
dtime = 0.0001;
}

Expand Down Expand Up @@ -1436,7 +1459,7 @@ void print_inputs(MyPaintBrush *self, float* inputs)

// Flips between 1 and -1, used for "mirrored" offsets.
STATE(self, FLIP) *= -1;
gboolean painted_now = prepare_and_draw_dab (self, surface);
gboolean painted_now = prepare_and_draw_dab (self, surface, linear);
if (painted_now) {
painted = YES;
} else if (painted == UNKNOWN) {
Expand Down
3 changes: 2 additions & 1 deletion mypaint-brush.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ mypaint_brush_new_stroke(MyPaintBrush *self);

int
mypaint_brush_stroke_to(MyPaintBrush *self, MyPaintSurface *surface, float x, float y,
float pressure, float xtilt, float ytilt, double dtime, float viewzoom, float viewrotation, float barrel_rotation);
float pressure, float xtilt, float ytilt, double dtime, float viewzoom,
float viewrotation, float barrel_rotation, gboolean linear);

void
mypaint_brush_set_base_value(MyPaintBrush *self, MyPaintBrushSetting id, float value);
Expand Down
6 changes: 4 additions & 2 deletions tests/mypaint-utils-stroke-player.c
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ mypaint_utils_stroke_player_iterate(MyPaintUtilsStrokePlayer *self)
last_event_time = last_event->time;
}
const float dtime = event->time - last_event_time;

const float linear = FALSE;
if (event->valid) {
if (self->transaction_on_stroke) {
mypaint_surface_begin_atomic(self->surface);
Expand All @@ -148,7 +148,9 @@ mypaint_utils_stroke_player_iterate(MyPaintUtilsStrokePlayer *self)
mypaint_brush_stroke_to(self->brush, self->surface,
event->x*self->scale, event->y*self->scale,
event->pressure,
event->xtilt, event->ytilt, dtime, event->viewzoom, event->viewrotation, event->barrel_rotation);
event->xtilt, event->ytilt, dtime,
event->viewzoom, event->viewrotation,
event->barrel_rotation, linear);

if (self->transaction_on_stroke) {
mypaint_surface_end_atomic(self->surface, NULL);
Expand Down

0 comments on commit 3463f67

Please sign in to comment.