diff --git a/ChangeLog b/ChangeLog index 7fd137d961..d05061d47a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,27 @@ +2003-06-02 Michael Natterer + + Implemented DISSOLVE_MODE the way it should have always been. + Fixes bug #107402. + + * app/paint-funcs-generic.h (struct apply_layer_mode_struct): added + "guchar *mask" so it can be used by the layer mode. + + (dissolve_pixels): take an additional "mask" parameter and + dissolve the pixels according to the *combined* opacity of the + pixel's alpha, the opacity and the mask. Removed a wrong comment + about why we call g_rand_int() x times before we start to use its + values. + + (layer_dissolve_mode): pass the mask to dissolve_pixels(). No need + to call add_alpha_pixels() since we overwrite the dest buffer + entirely in disolve_pixels(). + + * app/paint-funcs.c (initial_sub_region, combine_sub_region): + allocate the buffer large enough for DISSOLVE (which always needs + an alpha channel). Pass the mask to the layer mode functions. + Discard opacity and mask after applying DISSOLVE since it + "consumes" all transparency. + 2003-06-02 Sven Neumann * plug-ins/common/grid.c: fixed swapped horizontal/vertical diff --git a/app/paint-funcs/paint-funcs-generic.h b/app/paint-funcs/paint-funcs-generic.h index bee3e11d06..55eff437f6 100644 --- a/app/paint-funcs/paint-funcs-generic.h +++ b/app/paint-funcs/paint-funcs-generic.h @@ -53,9 +53,10 @@ struct apply_layer_mode_struct { guchar bytes1 : 3; - guchar bytes2 : 3; + guchar bytes2 : 3; guchar *src1; guchar *src2; + guchar *mask; guchar **dest; gint x; gint y; @@ -946,6 +947,7 @@ difference_pixels (const guchar *src1, static inline void dissolve_pixels (const guchar *src, + guchar *mask, guchar *dest, gint x, gint y, @@ -955,17 +957,17 @@ dissolve_pixels (const guchar *src, gint db, guint has_alpha) { - gint alpha, b; - gint32 rand_val; - GRand *gr; - + gint alpha, b; + gint32 rand_val; + gint combined_opacity; + GRand *gr; + gr = g_rand_new_with_seed (random_table[y % RANDOM_TABLE_SIZE]); - - /* Ignore a few random values (this probably isn't necessary - * when using g_rand_*) */ + + /* Ignore x random values so we get a deterministic result */ for (b = 0; b < x; b ++) - g_rand_int(gr); - + g_rand_int (gr); + alpha = db - 1; while (length--) @@ -977,13 +979,27 @@ dissolve_pixels (const guchar *src, /* dissolve if random value is > opacity */ rand_val = g_rand_int_range (gr, 0, 256); - if (has_alpha) - dest[alpha] = (rand_val > src[alpha]) ? 0 : src[alpha]; - else - dest[alpha] = (rand_val > opacity) ? 0 : OPAQUE_OPACITY; + if (mask) + { + if (has_alpha) + combined_opacity = opacity * src[alpha] * *mask / (255 * 255); + else + combined_opacity = opacity * *mask / 255; + + mask++; + } + else + { + if (has_alpha) + combined_opacity = opacity * src[alpha] / 255; + else + combined_opacity = opacity; + } + + dest[alpha] = (rand_val > combined_opacity) ? 0 : OPAQUE_OPACITY; - dest += db; src += sb; + dest += db; } g_rand_free (gr); @@ -1591,15 +1607,21 @@ static void layer_dissolve_mode (struct apply_layer_mode_struct *alms) { const guint has_alpha1 = HAS_ALPHA (alms->bytes1); - const guint has_alpha2 = HAS_ALPHA (alms->bytes2); + const guint has_alpha2 = HAS_ALPHA (alms->bytes2); + guint dest_bytes; /* Since dissolve requires an alpha channel... */ - if (!has_alpha2) - add_alpha_pixels (alms->src2, *(alms->dest), alms->length, alms->bytes2); + if (has_alpha2) + dest_bytes = alms->bytes2; + else + dest_bytes = alms->bytes2 + 1; + + dissolve_pixels (alms->src2, alms->mask, *(alms->dest), + alms->x, alms->y, + alms->opacity, alms->length, + alms->bytes2, dest_bytes, + has_alpha2); - dissolve_pixels (alms->src2, *(alms->dest), alms->x, alms->y, - alms->opacity, alms->length, alms->bytes2, - (has_alpha2 ? alms->bytes2 : alms->bytes2 + 1), has_alpha2); alms->combine = has_alpha1 ? COMBINE_INTEN_A_INTEN_A : COMBINE_INTEN_INTEN_A; } diff --git a/app/paint-funcs/paint-funcs.c b/app/paint-funcs/paint-funcs.c index d891978573..2f98dfd6fb 100644 --- a/app/paint-funcs/paint-funcs.c +++ b/app/paint-funcs/paint-funcs.c @@ -4296,6 +4296,7 @@ initial_sub_region (struct initial_regions_struct *st, gboolean *affect; InitialMode type; + /* use src->bytes + 1 since DISSOLVE always needs a buffer with alpha */ buf = alloca (MAX (src->w * (src->bytes + 1), dest->w * dest->bytes)); data = st->data; @@ -4306,7 +4307,7 @@ initial_sub_region (struct initial_regions_struct *st, s = src->data; d = dest->data; - m = (mask) ? mask->data : NULL; + m = mask ? mask->data : NULL; for (h = 0; h < src->h; h++) { @@ -4329,10 +4330,12 @@ initial_sub_region (struct initial_regions_struct *st, case INITIAL_INTENSITY: if (mode == GIMP_DISSOLVE_MODE) { - dissolve_pixels (s, buf, src->x, src->y + h, opacity, src->w, src->bytes, - src->bytes + 1, 0); - initial_inten_pixels (buf, d, m, opacity, affect, - src->w, src->bytes); + dissolve_pixels (s, m, buf, src->x, src->y + h, + opacity, src->w, + src->bytes, src->bytes + 1, + FALSE); + initial_inten_a_pixels (buf, d, NULL, OPAQUE_OPACITY, affect, + src->w, src->bytes + 1); } else initial_inten_pixels (s, d, m, opacity, affect, src->w, src->bytes); @@ -4341,9 +4344,11 @@ initial_sub_region (struct initial_regions_struct *st, case INITIAL_INTENSITY_ALPHA: if (mode == GIMP_DISSOLVE_MODE) { - dissolve_pixels (s, buf, src->x, src->y + h, opacity, src->w, src->bytes, - src->bytes, 1); - initial_inten_a_pixels (buf, d, m, opacity, affect, + dissolve_pixels (s, m, buf, src->x, src->y + h, + opacity, src->w, + src->bytes, src->bytes, + TRUE); + initial_inten_a_pixels (buf, d, NULL, OPAQUE_OPACITY, affect, src->w, src->bytes); } else @@ -4439,6 +4444,8 @@ combine_sub_region (struct combine_regions_struct *st, { guchar *data; guint opacity; + guint layer_mode_opacity; + guchar *layer_mode_mask; GimpLayerModeEffects mode; gboolean *affect; guint h; @@ -4452,9 +4459,11 @@ combine_sub_region (struct combine_regions_struct *st, gboolean transparency_quickskip_possible; TileRowHint hint; + /* use src2->bytes + 1 since DISSOLVE always needs a buffer with alpha */ buf = alloca (MAX (MAX (src1->w * src1->bytes, - src2->w * src2->bytes), + src2->w * (src2->bytes + 1)), dest->w * dest->bytes)); + opacity = st->opacity; mode = st->mode; affect = st->affect; @@ -4469,7 +4478,7 @@ combine_sub_region (struct combine_regions_struct *st, s1 = src1->data; s2 = src2->data; d = dest->data; - m = (mask) ? mask->data : NULL; + m = mask ? mask->data : NULL; if (transparency_quickskip_possible || opacity_quickskip_possible) { @@ -4484,6 +4493,19 @@ combine_sub_region (struct combine_regions_struct *st, } /* else it's probably a brush-composite */ + /* use separate variables for the combining opacity and the opacity + * the layer mode is applied with since DISSLOVE_MODE "consumes" + * all opacity and wants to be applied OPAQUE + */ + layer_mode_opacity = opacity; + layer_mode_mask = m; + + if (mode == GIMP_DISSOLVE_MODE) + { + opacity = OPAQUE_OPACITY; + m = NULL; + } + for (h = 0; h < src1->h; h++) { hint = TILEROWHINT_UNDEFINED; @@ -4530,24 +4552,27 @@ combine_sub_region (struct combine_regions_struct *st, { /* Now, apply the paint mode */ struct apply_layer_mode_struct alms; - alms.src1 = s1; - alms.src2 = s2; - alms.dest = &s; - alms.x = src1->x; - alms.y = src1->y + h; - alms.opacity = opacity; + + alms.src1 = s1; + alms.src2 = s2; + alms.mask = layer_mode_mask; + alms.dest = &s; + alms.x = src1->x; + alms.y = src1->y + h; + alms.opacity = layer_mode_opacity; alms.combine = combine; - alms.length = src1->w; - alms.bytes1 = src1->bytes; - alms.bytes2 = src2->bytes; + alms.length = src1->w; + alms.bytes1 = src1->bytes; + alms.bytes2 = src2->bytes; /* Determine whether the alpha channel of the destination can be * affected by the specified mode--This keeps consistency with * varying opacities */ - mode_affect = layer_modes[mode].affect_alpha; - layer_mode_funcs[mode](&alms); + + layer_mode_funcs[mode] (&alms); + combine = (alms.combine == NO_COMBINATION) ? type : alms.combine; break; } @@ -4698,7 +4723,12 @@ combine_sub_region (struct combine_regions_struct *st, s2 += src2->rowstride; d += dest->rowstride; if (mask) - m += mask->rowstride; + { + layer_mode_mask += mask->rowstride; + + if (m) + m += mask->rowstride; + } } }