[gimp/wip/alxsa/mypaint-brush-v2: 60/66] Use view zoom and rotation in brush stroke
- From: Alx Sa <sawyeralex src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/wip/alxsa/mypaint-brush-v2: 60/66] Use view zoom and rotation in brush stroke
- Date: Sun, 16 Oct 2022 17:45:30 +0000 (UTC)
commit ff9c10adf1f9385606288aee2f3547737141590f
Author: Alx Sa <cmyk student gmail com>
Date: Thu Oct 6 04:33:07 2022 +0000
Use view zoom and rotation in brush stroke
app/core/gimpmybrush.c | 6 +-
app/paint/gimpmybrushcore.c | 16 ++-
app/paint/gimpmybrushoptions.c | 36 +++++-
app/paint/gimpmybrushoptions.h | 2 +
app/paint/gimpmybrushsurface.c | 270 ++++++++++++++++++++++++++++++++++++++---
app/tools/gimpmybrushtool.c | 21 ++++
6 files changed, 322 insertions(+), 29 deletions(-)
---
diff --git a/app/core/gimpmybrush.c b/app/core/gimpmybrush.c
index 71af5c9568..5117a3ead3 100644
--- a/app/core/gimpmybrush.c
+++ b/app/core/gimpmybrush.c
@@ -102,7 +102,7 @@ gimp_mybrush_init (GimpMybrush *brush)
brush->priv->opaque = 1.0;
brush->priv->hardness = 1.0;
brush->priv->pigment = 0.0;
- brush->priv->posterize = 1.0;
+ brush->priv->posterize = 0.0;
brush->priv->posterize_num = 1.0;
brush->priv->eraser = FALSE;
}
@@ -270,7 +270,7 @@ gimp_mybrush_get_hardness (GimpMybrush *brush)
gdouble
gimp_mybrush_get_pigment (GimpMybrush *brush)
{
- g_return_val_if_fail (GIMP_IS_MYBRUSH (brush), 0.0);
+ g_return_val_if_fail (GIMP_IS_MYBRUSH (brush), -1.0);
return brush->priv->pigment;
}
@@ -278,7 +278,7 @@ gimp_mybrush_get_pigment (GimpMybrush *brush)
gdouble
gimp_mybrush_get_posterize (GimpMybrush *brush)
{
- g_return_val_if_fail (GIMP_IS_MYBRUSH (brush), 1.0);
+ g_return_val_if_fail (GIMP_IS_MYBRUSH (brush), 0.0);
return brush->priv->posterize;
}
diff --git a/app/paint/gimpmybrushcore.c b/app/paint/gimpmybrushcore.c
index 9fb2f19da7..fcae5a3dab 100644
--- a/app/paint/gimpmybrushcore.c
+++ b/app/paint/gimpmybrushcore.c
@@ -203,11 +203,12 @@ gimp_mybrush_core_paint (GimpPaintCore *paint_core,
GimpPaintState paint_state,
guint32 time)
{
- GimpMybrushCore *mybrush = GIMP_MYBRUSH_CORE (paint_core);
- GimpContext *context = GIMP_CONTEXT (paint_options);
- gint offset_x;
- gint offset_y;
- GimpRGB fg;
+ GimpMybrushCore *mybrush = GIMP_MYBRUSH_CORE (paint_core);
+ GimpMybrushOptions *mybrush_options = GIMP_MYBRUSH_OPTIONS (paint_options);
+ GimpContext *context = GIMP_CONTEXT (paint_options);
+ gint offset_x;
+ gint offset_y;
+ GimpRGB fg;
g_return_if_fail (g_list_length (drawables) == 1);
@@ -234,11 +235,14 @@ gimp_mybrush_core_paint (GimpPaintCore *paint_core,
case GIMP_PAINT_STATE_MOTION:
gimp_mybrush_core_motion (paint_core, drawables->data, paint_options,
- sym, time, 1.0f, 0.0f, 0.0f);
+ sym, time, mybrush_options->view_zoom,
+ mybrush_options->view_rotation, 1.0f);
break;
case GIMP_PAINT_STATE_FINISH:
gimp_symmetry_set_stateful (sym, FALSE);
+ /*mypaint_surface_unref (mypaint_surface2_to_surface (
+ (MyPaintSurface2 *) mybrush->private->surface));*/
mybrush->private->surface = NULL;
g_list_free_full (mybrush->private->brushes,
diff --git a/app/paint/gimpmybrushoptions.c b/app/paint/gimpmybrushoptions.c
index 38dc73a76d..73dd1d1a95 100644
--- a/app/paint/gimpmybrushoptions.c
+++ b/app/paint/gimpmybrushoptions.c
@@ -38,6 +38,8 @@
enum
{
PROP_0,
+ PROP_VIEW_ZOOM,
+ PROP_VIEW_ROTATION,
PROP_RADIUS,
PROP_OPAQUE,
PROP_HARDNESS,
@@ -85,6 +87,20 @@ gimp_mybrush_options_class_init (GimpMybrushOptionsClass *klass)
context_class->mybrush_changed = gimp_mybrush_options_mybrush_changed;
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_RADIUS,
+ "viewzoom",
+ _("View Zoom"),
+ NULL,
+ 0.0001, G_MAXFLOAT, 1.0,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_RADIUS,
+ "viewrotation",
+ _("View Rotation"),
+ NULL,
+ 0.0, 360.0, 0.0,
+ GIMP_PARAM_STATIC_STRINGS);
+
GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_RADIUS,
"radius",
_("Radius"),
@@ -110,21 +126,21 @@ gimp_mybrush_options_class_init (GimpMybrushOptionsClass *klass)
"pigment",
_("Pigment"),
NULL,
- -1.0, 1.0, 0.0,
+ -1.0, 1.0, -1.0,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_POSTERIZE,
"posterize",
_("Posterize"),
NULL,
- -1.0, 1.0, 0.0,
+ 0.0, 1.0, 0.0,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_POSTERIZE_NUM,
"posterizenum",
_("Posterize Number"),
NULL,
- -1.0, 1.0, 0.0,
+ 0.0, 1.0, 1.0,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_ERASER,
@@ -165,6 +181,12 @@ gimp_mybrush_options_set_property (GObject *object,
switch (property_id)
{
+ case PROP_VIEW_ZOOM:
+ options->view_zoom = g_value_get_double (value);
+ break;
+ case PROP_VIEW_ROTATION:
+ options->view_rotation = g_value_get_double (value);
+ break;
case PROP_RADIUS:
options->radius = g_value_get_double (value);
break;
@@ -206,6 +228,12 @@ gimp_mybrush_options_get_property (GObject *object,
switch (property_id)
{
+ case PROP_VIEW_ZOOM:
+ g_value_set_double (value, options->view_zoom);
+ break;
+ case PROP_VIEW_ROTATION:
+ g_value_set_double (value, options->view_rotation);
+ break;
case PROP_RADIUS:
g_value_set_double (value, options->radius);
break;
@@ -243,6 +271,8 @@ gimp_mybrush_options_mybrush_changed (GimpContext *context,
{
if (brush)
g_object_set (context,
+ "viewzoom", 1.0f,
+ "viewrotation", 0.0f,
"radius", gimp_mybrush_get_radius (brush),
"opaque", gimp_mybrush_get_opaque (brush),
"hardness", gimp_mybrush_get_hardness (brush),
diff --git a/app/paint/gimpmybrushoptions.h b/app/paint/gimpmybrushoptions.h
index 166c5f9176..d6305efd7a 100644
--- a/app/paint/gimpmybrushoptions.h
+++ b/app/paint/gimpmybrushoptions.h
@@ -36,6 +36,8 @@ struct _GimpMybrushOptions
{
GimpPaintOptions parent_instance;
+ gdouble view_zoom;
+ gdouble view_rotation;
gdouble radius;
gdouble opaque;
gdouble hardness;
diff --git a/app/paint/gimpmybrushsurface.c b/app/paint/gimpmybrushsurface.c
index 7f0b78ac8e..f8a01da1b0 100644
--- a/app/paint/gimpmybrushsurface.c
+++ b/app/paint/gimpmybrushsurface.c
@@ -17,6 +17,7 @@
#include "config.h"
#include <gegl.h>
+#include <stdint.h>
#include <mypaint-surface.h>
@@ -31,6 +32,7 @@
#include "gimpmybrushoptions.h"
#include "gimpmybrushsurface.h"
+#define WGM_EPSILON 0.001
struct _GimpMybrushSurface
{
@@ -56,6 +58,57 @@ struct _GimpMybrushSurface2
GimpMybrushOptions *options;
};
+static const float T_MATRIX_SMALL[3][10] =
{{0.026595621243689,0.049779426257903,0.022449850859496,-0.218453689278271
+,-0.256894883201278,0.445881722194840,0.772365886289756,0.194498761382537
+,0.014038157587820,0.007687264480513}
+,{-0.032601672674412,-0.061021043498478,-0.052490001018404
+,0.206659098273522,0.572496335158169,0.317837248815438,-0.021216624031211
+,-0.019387668756117,-0.001521339050858,-0.000835181622534}
+,{0.339475473216284,0.635401374177222,0.771520797089589,0.113222640692379
+,-0.055251113343776,-0.048222578468680,-0.012966666339586
+,-0.001523814504223,-0.000094718948810,-0.000051604594741}};
+
+static const float spectral_r_small[10] =
{0.009281362787953,0.009732627042016,0.011254252737167,0.015105578649573
+,0.024797924177217,0.083622585502406,0.977865045723212,1.000000000000000
+,0.999961046144372,0.999999992756822};
+
+static const float spectral_g_small[10] =
{0.002854127435775,0.003917589679914,0.012132151699187,0.748259205918013
+,1.000000000000000,0.865695937531795,0.037477469241101,0.022816789725717
+,0.021747419446456,0.021384940572308};
+
+static const float spectral_b_small[10] =
{0.537052150373386,0.546646402401469,0.575501819073983,0.258778829633924
+,0.041709923751716,0.012662638828324,0.007485593127390,0.006766900622462
+,0.006699764779016,0.006676219883241};
+
+void
+rgb_to_spectral (float r,
+ float g,
+ float b,
+ float *spectral_);
+void
+spectral_to_rgb (float *spectral,
+ float *rgb_);
+
+void
+get_color_pixels_legacy (float *mask,
+ float *rgba,
+ float *sum_weight,
+ float *sum_r,
+ float *sum_g,
+ float *sum_b,
+ float *sum_a);
+void get_color_pixels_accumulate (float *mask,
+ float *rgba,
+ float *sum_weight,
+ float *sum_r,
+ float *sum_g,
+ float *sum_b,
+ float *sum_a,
+ float paint,
+ uint16_t sample_interval,
+ float random_sample_rate
+ );
+
/* --- Taken from mypaint-tiled-surface.c --- */
static inline float
calculate_rr (int xp,
@@ -212,6 +265,187 @@ calculate_rr_antialiased (int xp,
return 1.0f - visibilityNear;
}
+
+/* -- Taken from helpers.c -- */
+void
+rgb_to_spectral (float r,
+ float g,
+ float b,
+ float *spectral_)
+{
+ float offset = 1.0 - WGM_EPSILON;
+ float spec_r[10] = {0};
+ float spec_g[10] = {0};
+ float spec_b[10] = {0};
+
+ r = r * offset + WGM_EPSILON;
+ g = g * offset + WGM_EPSILON;
+ b = b * offset + WGM_EPSILON;
+ //upsample rgb to spectral primaries
+
+ for (int i=0; i < 10; i++) {
+ spec_r[i] = spectral_r_small[i] * r;
+ }
+ for (int i=0; i < 10; i++) {
+ spec_g[i] = spectral_g_small[i] * g;
+ }
+ for (int i=0; i < 10; i++) {
+ spec_b[i] = spectral_b_small[i] * b;
+ }
+ //collapse into one spd
+ for (int i=0; i<10; i++) {
+ spectral_[i] += spec_r[i] + spec_g[i] + spec_b[i];
+ }
+
+}
+
+void
+spectral_to_rgb (float *spectral,
+ float *rgb_) {
+ float offset = 1.0 - WGM_EPSILON;
+ // We need this tmp. array to allow auto vectorization.
+ float tmp[3] = {0};
+ for (int i=0; i<10; i++) {
+ tmp[0] += T_MATRIX_SMALL[0][i] * spectral[i];
+ tmp[1] += T_MATRIX_SMALL[1][i] * spectral[i];
+ tmp[2] += T_MATRIX_SMALL[2][i] * spectral[i];
+ }
+ for (int i=0; i<3; i++) {
+ rgb_[i] = CLAMP((tmp[i] - WGM_EPSILON) / offset, 0.0f, 1.0f);
+ }
+}
+
+
+/* -- Taken from brushmode.c -- */
+void
+get_color_pixels_legacy (float *mask,
+ float *rgba,
+ float *sum_weight,
+ float *sum_r,
+ float *sum_g,
+ float *sum_b,
+ float *sum_a)
+{
+ // The sum of a 64x64 tile fits into a 32 bit integer, but the sum
+ // of an arbitrary number of tiles may not fit. We assume that we
+ // are processing a single tile at a time, so we can use integers.
+ // But for the result we need floats.
+ uint32_t weight = 0;
+ uint32_t r = 0;
+ uint32_t g = 0;
+ uint32_t b = 0;
+ uint32_t a = 0;
+
+ //while (1) {
+ //for (; mask[0]; mask++, rgba += 4)
+ // {
+ uint32_t opa = (uint16_t) mask[0];
+ weight += opa;
+ r += ((uint16_t) rgba[RED])/(1<<15);
+ g += ((uint16_t) rgba[GREEN])/(1<<15);
+ b += ((uint16_t) rgba[BLUE])/(1<<15);
+ //a += rgba[ALPHA]/(1<<15);
+ //}
+ //if (! ((uint16_t) mask[1]))
+ //break;
+ rgba += (uint16_t) mask[1];
+ mask += 2;
+ //}
+
+ // convert integer to float outside the performance critical loop
+ *sum_weight += weight;
+ *sum_r += r;
+ *sum_g += g;
+ *sum_b += b;
+ *sum_a += a;
+};
+
+void
+get_color_pixels_accumulate (float *mask,
+ float *rgba,
+ float *sum_weight,
+ float *sum_r,
+ float *sum_g,
+ float *sum_b,
+ float *sum_a,
+ float paint,
+ uint16_t sample_interval,
+ float random_sample_rate)
+{
+ float avg_spectral[10] = {0};
+ float avg_rgb[3] = {*sum_r, *sum_g, *sum_b};
+ // Rolling counter determining which pixels to sample
+ // This sampling _is_ biased (but hopefully not too bad).
+ // Ideally, the selection of pixels to be sampled should
+ // be determined before this function is called.
+ uint16_t interval_counter = 0;
+ const int random_sample_threshold = (int)(random_sample_rate * RAND_MAX);
+ float spec_rgb[3] = {0};
+
+ // Fall back to legacy sampling if using static 0 paint setting
+ // Indicated by passing a negative paint factor (normal range 0..1)
+ if (paint < 0.0) {
+ get_color_pixels_legacy (mask, rgba, sum_weight, sum_r, sum_g, sum_b, sum_a);
+ return;
+ }
+
+ // Sample the canvas as additive and subtractive
+ // According to paint parameter
+ // Average the results normally
+ // Only sample a partially random subset of pixels
+
+ if (paint > 0.0f) {
+ rgb_to_spectral(*sum_r, *sum_g, *sum_b, avg_spectral);
+ }
+
+ //while (1) {
+ //for (; mask[0]; mask++, rgba+=4) {
+ // Sample every n pixels, and a percentage of the rest.
+ // At least one pixel (the first) will always be sampled.
+ if (interval_counter == 0 || rand() < random_sample_threshold) {
+
+ //float a = (float) mask[0] * rgba[ALPHA] / (1 << 30);
+ //float alpha_sums = a + *sum_a;
+ float fac_a, fac_b;
+
+ *sum_weight += (float) mask[0] / (1 << 15);
+
+ fac_a = fac_b = 1.0f;
+ //if (alpha_sums > 0.0f) {
+ //fac_a = a / alpha_sums;
+ fac_b = 1.0 - fac_a;
+ //}
+
+ /*if (paint > 0.0f && rgba[ALPHA] > 0) {
+ float spectral[10] = {0};
+ rgb_to_spectral((float)rgba[RED] / rgba[ALPHA], (float)rgba[GREEN] / rgba[ALPHA],
(float)rgba[BLUE] / rgba[ALPHA], spectral);
+
+ for (int i = 0; i < 10; i++) {
+ avg_spectral[i] = pow (spectral[i], fac_a) * pow(avg_spectral[i], fac_b);
+ }
+ }
+ if (paint < 1.0f && rgba[ALPHA] > 0) {
+ for (int i = 0; i < 3; i++) {
+ avg_rgb[i] = (float)rgba[i] * fac_a / rgba[ALPHA] + (float)avg_rgb[i] * fac_b;
+ }
+ }*/
+ //*sum_a += a;
+ }
+ interval_counter = (interval_counter + 1) % sample_interval;
+ //}
+ //if (!mask[1])
+ //break;
+ rgba += (uint16_t) mask[1];
+ mask += 2;
+ //}
+ // Convert the spectral average to rgb and write the result
+ // back weighted with the rgb average.
+ spectral_to_rgb (avg_spectral, spec_rgb);
+
+ *sum_r = spec_rgb[0] * paint + (1.0 - paint) * avg_rgb[0];
+ *sum_g = spec_rgb[1] * paint + (1.0 - paint) * avg_rgb[1];
+ *sum_b = spec_rgb[2] * paint + (1.0 - paint) * avg_rgb[2];
+};
/* -- end mypaint code */
static inline float
@@ -799,6 +1033,8 @@ gimp_mypaint_surface_get_color_2 (MyPaintSurface2 *base_surface,
if (dabRect.width > 0 || dabRect.height > 0)
{
const float one_over_radius2 = 1.0f / (radius * radius);
+ const int sample_interval = radius <= 2.0f ? 1 : (int)(radius * 7);
+ const float random_sample_rate = 1.0f / (7 * radius);
float sum_weight = 0.0f;
float sum_r = 0.0f;
float sum_g = 0.0f;
@@ -845,11 +1081,10 @@ gimp_mypaint_surface_get_color_2 (MyPaintSurface2 *base_surface,
if (mask)
pixel_weight *= *mask;
- sum_r += pixel_weight * pixel[RED];
- sum_g += pixel_weight * pixel[GREEN];
- sum_b += pixel_weight * pixel[BLUE];
- sum_a += pixel_weight * pixel[ALPHA];
- sum_weight += pixel_weight;
+ get_color_pixels_accumulate (mask, pixel, &sum_weight,
+ &sum_r, &sum_g, &sum_b, &sum_a,
+ paint, sample_interval,
+ random_sample_rate);
pixel += 4;
if (mask)
@@ -860,19 +1095,20 @@ gimp_mypaint_surface_get_color_2 (MyPaintSurface2 *base_surface,
if (sum_a > 0.0f && sum_weight > 0.0f)
{
- sum_r /= sum_weight;
- sum_g /= sum_weight;
- sum_b /= sum_weight;
- sum_a /= sum_weight;
+ float demul;
- sum_r /= sum_a;
- sum_g /= sum_a;
- sum_b /= sum_a;
+ sum_a /= sum_weight;
+ if (paint < 0.0)
+ {
+ sum_r /= sum_weight;
+ sum_g /= sum_weight;
+ sum_b /= sum_weight;
+ }
- /* FIXME: Clamping is wrong because GEGL allows alpha > 1, this should probably re-multipy things */
- *color_r = CLAMP(sum_r, 0.0f, 1.0f);
- *color_g = CLAMP(sum_g, 0.0f, 1.0f);
- *color_b = CLAMP(sum_b, 0.0f, 1.0f);
+ demul = paint < 0.0 ? sum_a : 1.0f;
+ *color_r = CLAMP(sum_r / demul, 0.0f, 1.0f);
+ *color_g = CLAMP(sum_g / demul, 0.0f, 1.0f);
+ *color_b = CLAMP(sum_b / demul, 0.0f, 1.0f);
*color_a = CLAMP(sum_a, 0.0f, 1.0f);
}
}
@@ -958,4 +1194,4 @@ gimp_mypaint_surface2_new (GeglBuffer *buffer,
surface->dirty = *GEGL_RECTANGLE (0, 0, 0, 0);
return surface;
-}
+}
\ No newline at end of file
diff --git a/app/tools/gimpmybrushtool.c b/app/tools/gimpmybrushtool.c
index 2a7378802f..d8964b67d7 100644
--- a/app/tools/gimpmybrushtool.c
+++ b/app/tools/gimpmybrushtool.c
@@ -56,6 +56,10 @@ static GimpCanvasItem * gimp_mybrush_tool_get_outline (GimpPaintTool *paint_tool
gdouble x,
gdouble y);
+static void gimp_mybrush_tool_cursor_update (GimpTool *tool,
+ const GimpCoords *coords,
+ GdkModifierType state,
+ GimpDisplay *display);
void
gimp_mybrush_tool_register (GimpToolRegisterCallback callback,
@@ -84,6 +88,7 @@ gimp_mybrush_tool_class_init (GimpMybrushToolClass *klass)
GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass);
GimpPaintToolClass *paint_tool_class = GIMP_PAINT_TOOL_CLASS (klass);
+ tool_class->cursor_update = gimp_mybrush_tool_cursor_update;
tool_class->options_notify = gimp_mybrush_tool_options_notify;
paint_tool_class->get_outline = gimp_mybrush_tool_get_outline;
@@ -176,3 +181,19 @@ gimp_mybrush_tool_create_cursor (GimpPaintTool *paint_tool,
return NULL;
}
+
+static void gimp_mybrush_tool_cursor_update (GimpTool *tool,
+ const GimpCoords *coords,
+ GdkModifierType state,
+ GimpDisplay *display)
+{
+ GimpDisplayShell *shell;
+ GimpMybrushOptions *options = GIMP_MYBRUSH_TOOL_GET_OPTIONS (tool);
+
+ g_return_if_fail (GIMP_IS_DISPLAY (display));
+
+ shell = gimp_display_get_shell (display);
+
+ options->view_zoom = gimp_zoom_model_get_factor (shell->zoom);
+ options->view_rotation = shell->rotate_angle;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]