diff --git a/app/paint/Makefile.am b/app/paint/Makefile.am index 22e4b25b14..06c64bb311 100644 --- a/app/paint/Makefile.am +++ b/app/paint/Makefile.am @@ -56,6 +56,8 @@ libapppaint_a_sources = \ gimpmybrush.h \ gimpmybrushoptions.c \ gimpmybrushoptions.h \ + gimpmybrushsurface.c \ + gimpmybrushsurface.h \ gimppaintcore.c \ gimppaintcore.h \ gimppaintcore-loops.c \ diff --git a/app/paint/gimpmybrush.c b/app/paint/gimpmybrush.c index 69aa5d92d3..2e47ca2de8 100644 --- a/app/paint/gimpmybrush.c +++ b/app/paint/gimpmybrush.c @@ -26,8 +26,10 @@ #include #include +#if 0 #include #include +#endif #include "libgimpmath/gimpmath.h" #include "libgimpcolor/gimpcolor.h" @@ -45,6 +47,7 @@ #include "core/gimpimage-undo.h" #include "core/gimptempbuf.h" +#include "gimpmybrushsurface.h" #include "gimpmybrushoptions.h" #include "gimpmybrush.h" @@ -53,8 +56,13 @@ struct _GimpMybrushPrivate { +#if 0 MyPaintGeglTiledSurface *surface; +#else + GimpMybrushSurface *surface; +#endif MyPaintBrush *brush; + gint64 lastTime; }; @@ -119,7 +127,13 @@ gimp_mybrush_paint (GimpPaintCore *paint_core, { GimpMybrush *mybrush = GIMP_MYBRUSH (paint_core); GimpMybrushOptions *options = GIMP_MYBRUSH_OPTIONS (paint_options); + const gchar *brush_data; +#if 0 GeglBuffer *buffer; + GimpComponentMask active_mask; +#endif + GimpRGB fg; + GimpHSV hsv; switch (paint_state) { @@ -132,6 +146,7 @@ gimp_mybrush_paint (GimpPaintCore *paint_core, gimp_palettes_add_color_history (context->gimp, &foreground); +#if 0 mybrush->private->surface = mypaint_gegl_tiled_surface_new (); buffer = mypaint_gegl_tiled_surface_get_buffer (mybrush->private->surface); @@ -144,26 +159,51 @@ gimp_mybrush_paint (GimpPaintCore *paint_core, buffer, NULL); mypaint_gegl_tiled_surface_set_buffer (mybrush->private->surface, buffer); g_object_unref (buffer); +#else + mybrush->private->surface = gimp_mypaint_surface_new (gimp_drawable_get_buffer (drawable), + gimp_drawable_get_active_mask (drawable)); +#endif mybrush->private->brush = mypaint_brush_new (); mypaint_brush_from_defaults (mybrush->private->brush); + brush_data = gimp_mybrush_options_get_brush_data (options); + if (brush_data) + mypaint_brush_from_string (mybrush->private->brush, brush_data); - if (options->mybrush) - { - gchar *string; - gsize length; +#if 0 + active_mask = gimp_drawable_get_active_mask (drawable); - if (g_file_get_contents (options->mybrush, - &string, &length, NULL)) - { - if (! mypaint_brush_from_string (mybrush->private->brush, string)) - g_printerr ("Failed to deserialize MyPaint brush\n"); + mypaint_brush_set_base_value (mybrush->private->brush, + MYPAINT_BRUSH_SETTING_LOCK_ALPHA, + (active_mask & GIMP_COMPONENT_MASK_ALPHA) ? + FALSE : TRUE); +#endif - g_free (string); - } - } + gimp_context_get_foreground (context, &fg); + gimp_rgb_to_hsv (&fg, &hsv); + + mypaint_brush_set_base_value (mybrush->private->brush, + MYPAINT_BRUSH_SETTING_COLOR_H, + hsv.h); + mypaint_brush_set_base_value (mybrush->private->brush, + MYPAINT_BRUSH_SETTING_COLOR_S, + hsv.s); + mypaint_brush_set_base_value (mybrush->private->brush, + MYPAINT_BRUSH_SETTING_COLOR_V, + hsv.v); + + mypaint_brush_set_base_value (mybrush->private->brush, + MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC, + options->radius); + mypaint_brush_set_base_value (mybrush->private->brush, + MYPAINT_BRUSH_SETTING_OPAQUE, + options->opaque * gimp_context_get_opacity (context)); + mypaint_brush_set_base_value (mybrush->private->brush, + MYPAINT_BRUSH_SETTING_HARDNESS, + options->hardness); mypaint_brush_new_stroke (mybrush->private->brush); + mybrush->private->lastTime = 0; } break; @@ -189,45 +229,26 @@ gimp_mybrush_motion (GimpPaintCore *paint_core, guint32 time) { GimpMybrush *mybrush = GIMP_MYBRUSH (paint_core); - GimpMybrushOptions *options = GIMP_MYBRUSH_OPTIONS (paint_options); - GimpContext *context = GIMP_CONTEXT (paint_options); - GimpComponentMask active_mask; - GimpRGB fg; - GimpHSV hsv; MyPaintRectangle rect; - active_mask = gimp_drawable_get_active_mask (drawable); - - mypaint_brush_set_base_value (mybrush->private->brush, - MYPAINT_BRUSH_SETTING_LOCK_ALPHA, - (active_mask & GIMP_COMPONENT_MASK_ALPHA) ? - FALSE : TRUE); - - gimp_context_get_foreground (context, &fg); - gimp_rgb_to_hsv (&fg, &hsv); - - mypaint_brush_set_base_value (mybrush->private->brush, - MYPAINT_BRUSH_SETTING_COLOR_H, - hsv.h); - mypaint_brush_set_base_value (mybrush->private->brush, - MYPAINT_BRUSH_SETTING_COLOR_S, - hsv.s); - mypaint_brush_set_base_value (mybrush->private->brush, - MYPAINT_BRUSH_SETTING_COLOR_V, - hsv.v); - - mypaint_brush_set_base_value (mybrush->private->brush, - MYPAINT_BRUSH_SETTING_OPAQUE, - gimp_context_get_opacity (context)); - mypaint_brush_set_base_value (mybrush->private->brush, - MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC, - options->radius); - mypaint_brush_set_base_value (mybrush->private->brush, - MYPAINT_BRUSH_SETTING_HARDNESS, - options->hardness); - mypaint_surface_begin_atomic ((MyPaintSurface *) mybrush->private->surface); + + if (mybrush->private->lastTime == 0) + { + /* First motion, so we need a zero pressure event to start the stroke */ + mybrush->private->lastTime = (gint64)time - 15; + + mypaint_brush_stroke_to (mybrush->private->brush, + (MyPaintSurface *) mybrush->private->surface, + coords->x, + coords->y, + 0.0f, + coords->xtilt, + coords->ytilt, + 1.0f /* Pretend the cursor hasn't moved in a while */); + } + mypaint_brush_stroke_to (mybrush->private->brush, (MyPaintSurface *) mybrush->private->surface, coords->x, @@ -235,16 +256,15 @@ gimp_mybrush_motion (GimpPaintCore *paint_core, coords->pressure, coords->xtilt, coords->ytilt, - 1); + (time - mybrush->private->lastTime) * 0.001f); + mybrush->private->lastTime = time; mypaint_surface_end_atomic ((MyPaintSurface *) mybrush->private->surface, &rect); - g_printerr ("painted rect: %d %d %d %d\n", - rect.x, rect.y, rect.width, rect.height); - if (rect.width > 0 && rect.height > 0) { +#if 0 GeglBuffer *src; src = mypaint_gegl_tiled_surface_get_buffer (mybrush->private->surface); @@ -254,7 +274,7 @@ gimp_mybrush_motion (GimpPaintCore *paint_core, GEGL_ABYSS_NONE, gimp_drawable_get_buffer (drawable), NULL); - +#endif paint_core->x1 = MIN (paint_core->x1, rect.x); paint_core->y1 = MIN (paint_core->y1, rect.y); paint_core->x2 = MAX (paint_core->x2, rect.x + rect.width); diff --git a/app/paint/gimpmybrushoptions.c b/app/paint/gimpmybrushoptions.c index 1e05dddc4c..a089b08dbc 100644 --- a/app/paint/gimpmybrushoptions.c +++ b/app/paint/gimpmybrushoptions.c @@ -34,15 +34,26 @@ #include "gimp-intl.h" +#include enum { PROP_0, PROP_RADIUS, + PROP_OPAQUE, PROP_HARDNESS, PROP_MYBRUSH }; +typedef struct +{ + gdouble radius; + gdouble opaque; + gdouble hardness; + gchar *brush_json; +} OptionsState; + +static GHashTable *loaded_myb; static void gimp_mybrush_options_set_property (GObject *object, guint property_id, @@ -52,6 +63,12 @@ static void gimp_mybrush_options_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); +static void options_state_free (gpointer options) +{ + OptionsState *state = options; + g_free (state->brush_json); + g_free (state); +} G_DEFINE_TYPE (GimpMybrushOptions, gimp_mybrush_options, GIMP_TYPE_PAINT_OPTIONS) @@ -69,6 +86,10 @@ gimp_mybrush_options_class_init (GimpMybrushOptionsClass *klass) "radius", _("Radius"), -2.0, 6.0, 1.0, GIMP_PARAM_STATIC_STRINGS); + GIMP_CONFIG_INSTALL_PROP_DOUBLE (object_class, PROP_OPAQUE, + "opaque", _("Base Opacity"), + 0.0, 2.0, 1.0, + GIMP_PARAM_STATIC_STRINGS); GIMP_CONFIG_INSTALL_PROP_DOUBLE (object_class, PROP_HARDNESS, "hardness", NULL, 0.0, 1.0, 1.0, @@ -77,6 +98,50 @@ gimp_mybrush_options_class_init (GimpMybrushOptionsClass *klass) "mybrush", NULL, NULL, GIMP_PARAM_STATIC_STRINGS); + + loaded_myb = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, options_state_free); +} + +static void +gimp_mybrush_options_load_path (GObject *object, + gchar *path) +{ + GimpMybrushOptions *options = GIMP_MYBRUSH_OPTIONS (object); + + OptionsState *state = g_hash_table_lookup (loaded_myb, path); + if (!state) + { + gchar *brush_json = NULL; + MyPaintBrush *brush = mypaint_brush_new (); + + state = g_new0 (OptionsState, 1); + mypaint_brush_from_defaults (brush); + + if (g_file_get_contents (path, &brush_json, NULL, NULL)) + { + if (! mypaint_brush_from_string (brush, brush_json)) + { + g_printerr ("Failed to deserialize MyPaint brush\n"); + g_free (brush_json); + brush_json = NULL; + } + } + + state->brush_json = brush_json; + state->radius = mypaint_brush_get_base_value (brush, MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC); + state->opaque = mypaint_brush_get_base_value (brush, MYPAINT_BRUSH_SETTING_OPAQUE); + state->hardness = mypaint_brush_get_base_value (brush, MYPAINT_BRUSH_SETTING_HARDNESS); + + g_hash_table_insert (loaded_myb, g_strdup(path), state); + } + + options->radius = state->radius; + options->opaque = state->opaque; + options->hardness = state->hardness; + + g_object_notify (object, "radius"); + g_object_notify (object, "opaque"); + g_object_notify (object, "hardness"); } static void @@ -100,9 +165,14 @@ gimp_mybrush_options_set_property (GObject *object, case PROP_HARDNESS: options->hardness = g_value_get_double (value); break; + case PROP_OPAQUE: + options->opaque = g_value_get_double (value); + break; case PROP_MYBRUSH: g_free (options->mybrush); options->mybrush = g_value_dup_string (value); + if (options->mybrush) + gimp_mybrush_options_load_path (object, options->mybrush); break; default: @@ -124,6 +194,9 @@ gimp_mybrush_options_get_property (GObject *object, case PROP_RADIUS: g_value_set_double (value, options->radius); break; + case PROP_OPAQUE: + g_value_set_double (value, options->opaque); + break; case PROP_HARDNESS: g_value_set_double (value, options->hardness); break; @@ -137,4 +210,13 @@ gimp_mybrush_options_get_property (GObject *object, } } +const gchar * +gimp_mybrush_options_get_brush_data (GimpMybrushOptions *options) +{ + OptionsState *state = g_hash_table_lookup (loaded_myb, options->mybrush); + if (state) + return state->brush_json; + return NULL; +} + #endif diff --git a/app/paint/gimpmybrushoptions.h b/app/paint/gimpmybrushoptions.h index 0d069cc93a..a7bea73414 100644 --- a/app/paint/gimpmybrushoptions.h +++ b/app/paint/gimpmybrushoptions.h @@ -39,6 +39,7 @@ struct _GimpMybrushOptions GimpPaintOptions parent_instance; gdouble radius; + gdouble opaque; gdouble hardness; gchar *mybrush; }; @@ -51,6 +52,8 @@ struct _GimpMybrushOptionsClass GType gimp_mybrush_options_get_type (void) G_GNUC_CONST; +const gchar * +gimp_mybrush_options_get_brush_data (GimpMybrushOptions *options); #endif /* __GIMP_MYBRUSH_OPTIONS_H__ */ diff --git a/app/paint/gimpmybrushsurface.c b/app/paint/gimpmybrushsurface.c new file mode 100644 index 0000000000..26a4290575 --- /dev/null +++ b/app/paint/gimpmybrushsurface.c @@ -0,0 +1,453 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "config.h" + +#ifdef HAVE_LIBMYPAINT + +#include +#include "gimpmybrushsurface.h" +#include +#include + +struct _GimpMybrushSurface +{ + MyPaintSurface surface; + GeglBuffer *buffer; + GeglRectangle dirty; + GimpComponentMask component_mask; +}; + +/* --- Taken from mypaint-tiled-surface.c --- */ +static inline float +calculate_rr (int xp, + int yp, + float x, + float y, + float aspect_ratio, + float sn, + float cs, + float one_over_radius2) +{ + /* code duplication, see brush::count_dabs_to() */ + const float yy = (yp + 0.5f - y); + const float xx = (xp + 0.5f - x); + const float yyr=(yy*cs-xx*sn)*aspect_ratio; + const float xxr=yy*sn+xx*cs; + const float rr = (yyr*yyr + xxr*xxr) * one_over_radius2; + /* rr is in range 0.0..1.0*sqrt(2) */ + return rr; +} + +static inline float +calculate_r_sample (float x, + float y, + float aspect_ratio, + float sn, + float cs) +{ + const float yyr=(y*cs-x*sn)*aspect_ratio; + const float xxr=y*sn+x*cs; + const float r = (yyr*yyr + xxr*xxr); + return r; +} + +static inline float +sign_point_in_line (float px, + float py, + float vx, + float vy) +{ + return (px - vx) * (-vy) - (vx) * (py - vy); +} + +static inline void +closest_point_to_line (float lx, + float ly, + float px, + float py, + float *ox, + float *oy) +{ + const float l2 = lx*lx + ly*ly; + const float ltp_dot = px*lx + py*ly; + const float t = ltp_dot / l2; + *ox = lx * t; + *oy = ly * t; +} + + +/* This works by taking the visibility at the nearest point + * and dividing by 1.0 + delta. + * + * - nearest point: point where the dab has more influence + * - farthest point: point at a fixed distance away from + * the nearest point + * - delta: how much occluded is the farthest point relative + * to the nearest point + */ +static inline float +calculate_rr_antialiased (int xp, + int yp, + float x, + float y, + float aspect_ratio, + float sn, + float cs, + float one_over_radius2, + float r_aa_start) +{ + /* calculate pixel position and borders in a way + * that the dab's center is always at zero */ + float pixel_right = x - (float)xp; + float pixel_bottom = y - (float)yp; + float pixel_center_x = pixel_right - 0.5f; + float pixel_center_y = pixel_bottom - 0.5f; + float pixel_left = pixel_right - 1.0f; + float pixel_top = pixel_bottom - 1.0f; + + float nearest_x, nearest_y; /* nearest to origin, but still inside pixel */ + float farthest_x, farthest_y; /* farthest from origin, but still inside pixel */ + float r_near, r_far, rr_near, rr_far; + float center_sign, rad_area_1, visibilityNear, delta, delta2; + + /* Dab's center is inside pixel? */ + if( pixel_left<0 && pixel_right>0 && + pixel_top<0 && pixel_bottom>0 ) + { + nearest_x = 0; + nearest_y = 0; + r_near = rr_near = 0; + } + else + { + closest_point_to_line( cs, sn, pixel_center_x, pixel_center_y, &nearest_x, &nearest_y ); + nearest_x = CLAMP( nearest_x, pixel_left, pixel_right ); + nearest_y = CLAMP( nearest_y, pixel_top, pixel_bottom ); + /* XXX: precision of "nearest" values could be improved + * by intersecting the line that goes from nearest_x/Y to 0 + * with the pixel's borders here, however the improvements + * would probably not justify the perdormance cost. + */ + r_near = calculate_r_sample( nearest_x, nearest_y, aspect_ratio, sn, cs ); + rr_near = r_near * one_over_radius2; + } + + /* out of dab's reach? */ + if( rr_near > 1.0f ) + return rr_near; + + /* check on which side of the dab's line is the pixel center */ + center_sign = sign_point_in_line( pixel_center_x, pixel_center_y, cs, -sn ); + + /* radius of a circle with area=1 + * A = pi * r * r + * r = sqrt(1/pi) + */ + rad_area_1 = sqrtf( 1.0f / M_PI ); + + /* center is below dab */ + if( center_sign < 0 ) + { + farthest_x = nearest_x - sn*rad_area_1; + farthest_y = nearest_y + cs*rad_area_1; + } + /* above dab */ + else + { + farthest_x = nearest_x + sn*rad_area_1; + farthest_y = nearest_y - cs*rad_area_1; + } + + r_far = calculate_r_sample( farthest_x, farthest_y, aspect_ratio, sn, cs ); + rr_far = r_far * one_over_radius2; + + /* check if we can skip heavier AA */ + if( r_far < r_aa_start ) + return (rr_far+rr_near) * 0.5f; + + /* calculate AA approximate */ + visibilityNear = 1.0f - rr_near; + delta = rr_far - rr_near; + delta2 = 1.0f + delta; + visibilityNear /= delta2; + + return 1.0f - visibilityNear; +} +/* -- end mypaint code */ + +static inline float +calculate_alpha_for_rr (float rr, + float hardness, + float slope1, + float slope2) +{ + if (rr > 1.0f) + return 0.0f; + else if (rr <= hardness) + return 1.0f + rr * slope1; + else + return rr * slope2 - slope2; +} + +static GeglRectangle +calculate_dab_roi (float x, + float y, + float radius) +{ + int x0 = floor (x - radius); + int x1 = ceil (x + radius); + int y0 = floor (y - radius); + int y1 = ceil (y + radius); + + return *GEGL_RECTANGLE (x0, y0, x1 - x0, y1 - y0); +} + +static void +gimp_mypaint_surface_get_color (MyPaintSurface *base_surface, + float x, + float y, + float radius, + float *color_r, + float *color_g, + float *color_b, + float *color_a) +{ + GimpMybrushSurface *surface = (GimpMybrushSurface *)base_surface; + GeglRectangle dabRect; + + if (radius < 1.0f) + radius = 1.0f; + + dabRect = calculate_dab_roi (x, y, radius); + + *color_r = 0.0f; + *color_g = 0.0f; + *color_b = 0.0f; + *color_a = 0.0f; + + if (dabRect.width > 0 || dabRect.height > 0) + { + const float one_over_radius2 = 1.0f / (radius * radius); + float sum_weight = 0.0f; + float sum_r = 0.0f; + float sum_g = 0.0f; + float sum_b = 0.0f; + float sum_a = 0.0f; + + /* Read in clamp mode to avoid transparency bleeding in at the edges */ + GeglBufferIterator *iter = gegl_buffer_iterator_new (surface->buffer, &dabRect, 0, + babl_format ("R'aG'aB'aA float"), + GEGL_BUFFER_READ, + GEGL_ABYSS_CLAMP); + while (gegl_buffer_iterator_next (iter)) + { + float *pixel = (float *)iter->data[0]; + int iy, ix; + + for (iy = iter->roi[0].y; iy < iter->roi[0].y + iter->roi[0].height; iy++) + { + float yy = (iy + 0.5f - y); + for (ix = iter->roi[0].x; ix < iter->roi[0].x + iter->roi[0].width; ix++) + { + /* pixel_weight == a standard dab with hardness = 0.5, aspect_ratio = 1.0, and angle = 0.0 */ + float xx = (ix + 0.5f - x); + float rr = (yy * yy + xx * xx) * one_over_radius2; + float pixel_weight = 0.0f; + if (rr <= 1.0f) + pixel_weight = 1.0f - rr; + + 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; + + pixel += 4; + } + } + } + + 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; + + sum_r /= sum_a; + sum_g /= sum_a; + sum_b /= sum_a; + + /* 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); + *color_a = CLAMP(sum_a, 0.0f, 1.0f); + } + } + +} + +static int +gimp_mypaint_surface_draw_dab (MyPaintSurface *base_surface, + float x, + float y, + float radius, + float color_r, + float color_g, + float color_b, + float opaque, + float hardness, + float color_a, + float aspect_ratio, + float angle, + float lock_alpha, + float colorize) +{ + GimpMybrushSurface *surface = (GimpMybrushSurface *)base_surface; + GeglBufferIterator *iter; + GeglRectangle dabRect; + GimpComponentMask component_mask = surface->component_mask; + + const float one_over_radius2 = 1.0f / (radius * radius); + const double angle_rad = angle / 360 * 2 * M_PI; + const float cs = cos(angle_rad); + const float sn = sin(angle_rad); + float segment1_slope; + float segment2_slope; + float r_aa_start; + float mixbuf[4]; + + hardness = CLAMP (hardness, 0.0f, 1.0f); + segment1_slope = -(1.0f / hardness - 1.0f); + segment2_slope = -hardness / (1.0f - hardness); + aspect_ratio = MAX (1.0f, aspect_ratio); + + r_aa_start = radius - 1.0f; + r_aa_start = MAX (r_aa_start, 0); + r_aa_start = (r_aa_start * r_aa_start) / aspect_ratio; + + /* FIXME: This should use the real matrix values to trim aspect_ratio dabs */ + dabRect = calculate_dab_roi (x, y, radius); + gegl_rectangle_intersect (&dabRect, &dabRect, gegl_buffer_get_extent (surface->buffer)); + + if (dabRect.width <= 0 || dabRect.height <= 0) + return 0; + + gegl_rectangle_bounding_box (&surface->dirty, &surface->dirty, &dabRect); + + iter = gegl_buffer_iterator_new (surface->buffer, &dabRect, 0, + babl_format ("R'G'B'A float"), + GEGL_BUFFER_READWRITE, + GEGL_ABYSS_NONE); + + while (gegl_buffer_iterator_next (iter)) + { + float *pixel = (float *)iter->data[0]; + int iy, ix; + for (iy = iter->roi[0].y; iy < iter->roi[0].y + iter->roi[0].height; iy++) + { + for (ix = iter->roi[0].x; ix < iter->roi[0].x + iter->roi[0].width; ix++) + { + float rr, alpha, dst_alpha, a; + float *dst_pixel = (component_mask != GIMP_COMPONENT_MASK_ALL) ? mixbuf : pixel; + if (radius < 3.0f) + rr = calculate_rr_antialiased (ix, iy, x, y, aspect_ratio, sn, cs, one_over_radius2, r_aa_start); + else + rr = calculate_rr (ix, iy, x, y, aspect_ratio, sn, cs, one_over_radius2); + alpha = calculate_alpha_for_rr (rr, hardness, segment1_slope, segment2_slope) * opaque; + dst_alpha = pixel[ALPHA]; + a = alpha * color_a + dst_alpha * (1.0f - alpha); + + if (a > 0.0f) + { + float a_term = dst_alpha * (1.0f - alpha); + dst_pixel[RED] = (color_r * alpha * color_a + pixel[RED] * a_term) / a; + dst_pixel[GREEN] = (color_g * alpha * color_a + pixel[GREEN] * a_term) / a; + dst_pixel[BLUE] = (color_b * alpha * color_a + pixel[BLUE] * a_term) / a; + } + dst_pixel[ALPHA] = a; + + /* FIXME: Implement mypaint style lock-alpha mode */ + /* FIXME: Implement colorize mode */ + + if (component_mask != GIMP_COMPONENT_MASK_ALL) + { + if (component_mask & GIMP_COMPONENT_MASK_RED) + pixel[RED] = mixbuf[RED]; + if (component_mask & GIMP_COMPONENT_MASK_GREEN) + pixel[GREEN] = mixbuf[GREEN]; + if (component_mask & GIMP_COMPONENT_MASK_BLUE) + pixel[BLUE] = mixbuf[BLUE]; + if (component_mask & GIMP_COMPONENT_MASK_ALPHA) + pixel[ALPHA] = mixbuf[ALPHA]; + } + pixel += 4; + } + } + } + + return 1; +} + +static void +gimp_mypaint_surface_begin_atomic (MyPaintSurface *base_surface) +{ + +} + +static void +gimp_mypaint_surface_end_atomic (MyPaintSurface *base_surface, + MyPaintRectangle *roi) +{ + GimpMybrushSurface *surface = (GimpMybrushSurface *)base_surface; + roi->x = surface->dirty.x; + roi->y = surface->dirty.y; + roi->width = surface->dirty.width; + roi->height = surface->dirty.height; + surface->dirty = *GEGL_RECTANGLE (0, 0, 0, 0); +} + +static void +gimp_mypaint_surface_destroy (MyPaintSurface *base_surface) +{ + GimpMybrushSurface *surface = (GimpMybrushSurface *)base_surface; + g_object_unref (surface->buffer); + surface->buffer = NULL; +} + +GimpMybrushSurface * +gimp_mypaint_surface_new (GeglBuffer *buffer, + GimpComponentMask component_mask) +{ + GimpMybrushSurface *surface = g_malloc0 (sizeof (GimpMybrushSurface)); + mypaint_surface_init ((MyPaintSurface *)surface); + surface->surface.get_color = gimp_mypaint_surface_get_color; + surface->surface.draw_dab = gimp_mypaint_surface_draw_dab; + surface->surface.begin_atomic = gimp_mypaint_surface_begin_atomic; + surface->surface.end_atomic = gimp_mypaint_surface_end_atomic; + surface->surface.destroy = gimp_mypaint_surface_destroy; + surface->component_mask = component_mask; + surface->buffer = g_object_ref (buffer); + surface->dirty = *GEGL_RECTANGLE (0, 0, 0, 0); + + return surface; +} + +#endif \ No newline at end of file diff --git a/app/paint/gimpmybrushsurface.h b/app/paint/gimpmybrushsurface.h new file mode 100644 index 0000000000..124fbbc8e6 --- /dev/null +++ b/app/paint/gimpmybrushsurface.h @@ -0,0 +1,35 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_LIBMYPAINT + +#ifndef __GIMP_MYBRUSH_SURFACE_H__ +#define __GIMP_MYBRUSH_SURFACE_H__ + +#include "paint-types.h" +#include "core/gimpobject.h" + +typedef struct _GimpMybrushSurface GimpMybrushSurface; + +GimpMybrushSurface * +gimp_mypaint_surface_new (GeglBuffer *buffer, + GimpComponentMask component_mask); + + +#endif /* __GIMP_MYBRUSH_SURFACE_H__ */ + +#endif diff --git a/app/tools/gimpmybrushoptions-gui.c b/app/tools/gimpmybrushoptions-gui.c index ae3b6eeac0..b37fce784f 100644 --- a/app/tools/gimpmybrushoptions-gui.c +++ b/app/tools/gimpmybrushoptions-gui.c @@ -168,6 +168,13 @@ gimp_mybrush_options_gui (GimpToolOptions *tool_options) gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0); gtk_widget_show (scale); + /* opaque */ + scale = gimp_prop_spin_scale_new (config, "opaque", + _("Base Opacity"), + 0.1, 1.0, 2); + gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0); + gtk_widget_show (scale); + /* hardness */ scale = gimp_prop_spin_scale_new (config, "hardness", _("Hardness"),