[gimp/wip/alxsa/mypaint-brush-v2] Use view zoom and rotation in brush stroke



commit e1ed3f2dd2a154fa8b809643f2facccabe81844f
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]