From bfb530c1ac7a2d4e255160d677136640b4be98ef Mon Sep 17 00:00:00 2001 From: lecoanet Date: Mon, 16 Jun 2003 14:55:56 +0000 Subject: * Added a new gradient type: conical which is described by a center and an angle or a vector. * Added alternative vector based gradient syntax. This is effective in axial and radial gradient types. * (ZnGetGradient): angle, position, control can be real numbers --- generic/Color.c | 814 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 603 insertions(+), 211 deletions(-) (limited to 'generic/Color.c') diff --git a/generic/Color.c b/generic/Color.c index c83631d..ed2d17f 100644 --- a/generic/Color.c +++ b/generic/Color.c @@ -43,6 +43,7 @@ #include "Image.h" #include "Color.h" #include "Geo.h" +#include "Transfo.h" /* @@ -91,7 +92,8 @@ ColorInit() /* *---------------------------------------------------------------------- * - * ZnGetGradientColor -- + * ZnGetGradientColor + * ZnInterpGradientColor -- * *---------------------------------------------------------------------- */ @@ -100,28 +102,28 @@ ZnGetGradientColor(ZnGradient *grad, ZnReal position, unsigned short *alpha) { - int index, min, max; - XColor *shade=NULL; + int index, min, max; + XColor *shade=NULL; - if ((grad->num_colors == 1) || (position <= 0.0)) { + if ((grad->num_actual_colors == 1) || (position <= 0.0)) { if (alpha) { - *alpha = grad->colors[0].alpha; + *alpha = grad->actual_colors[0].alpha; } - return grad->colors[0].rgb; + return grad->actual_colors[0].rgb; } if (position >= 100.0) { if (alpha) { - *alpha = grad->colors[grad->num_colors-1].alpha; + *alpha = grad->actual_colors[grad->num_actual_colors-1].alpha; } - shade = grad->colors[grad->num_colors-1].rgb; + shade = grad->actual_colors[grad->num_actual_colors-1].rgb; } else { min = 0; - max = grad->num_colors-1; + max = grad->num_actual_colors-1; index = (max + min) / 2; while (max - min != 1) { /*printf("color index %d, min: %d, max: %d\n", index, min, max);*/ - if (grad->colors[index].position < position) { + if (grad->actual_colors[index].position < position) { min = index; } else { @@ -129,15 +131,76 @@ ZnGetGradientColor(ZnGradient *grad, } index = (max + min) / 2; } - shade = grad->colors[index].rgb; + shade = grad->actual_colors[index].rgb; if (alpha) { - *alpha = grad->colors[index].alpha; + *alpha = grad->actual_colors[index].alpha; } } return shade; } +void +ZnInterpGradientColor(ZnGradient *grad, + ZnReal position, + XColor *color, + unsigned short *alpha) +{ + int index, min, max; + ZnGradientColor *gc1, *gc2; + ZnReal rel_pos; + + if ((grad->num_actual_colors == 1) || (position <= 0.0)) { + *alpha = grad->actual_colors[0].alpha; + *color = *grad->actual_colors[0].rgb; + } + else if (position >= 100.0) { + *alpha = grad->actual_colors[grad->num_actual_colors-1].alpha; + *color = *grad->actual_colors[grad->num_actual_colors-1].rgb; + } + else { + min = 0; + max = grad->num_actual_colors-1; + index = (max + min) / 2; + while (max - min != 1) { + /*printf("color index %d, min: %d, max: %d\n", index, min, max);*/ + if (grad->actual_colors[index].position < position) { + min = index; + } + else { + max = index; + } + index = (max + min) / 2; + } + gc1 = &grad->actual_colors[index]; + gc2 = &grad->actual_colors[index+1]; + rel_pos = (position - gc1->position) * 100.0 / (gc2->position - gc1->position); + + if (rel_pos > gc1->control) { + rel_pos = (rel_pos - gc1->control) * 100.0 / (100.0 - gc1->control); + color->red = gc1->mid_rgb->red + + (gc2->rgb->red - gc1->mid_rgb->red) * rel_pos / 100.0; + color->green = gc1->mid_rgb->green + + (gc2->rgb->green - gc1->mid_rgb->green) * rel_pos / 100.0; + color->blue = gc1->mid_rgb->blue + + (gc2->rgb->blue - gc1->mid_rgb->blue) * rel_pos / 100.0; + *alpha = gc1->mid_alpha + + (gc2->alpha - gc1->mid_alpha) * rel_pos / 100.0; + } + else { + rel_pos = rel_pos * 100.0 / gc1->control; + color->red = gc1->rgb->red + + (gc1->mid_rgb->red - gc1->rgb->red) * rel_pos / 100.0; + color->green = gc1->rgb->green + + (gc1->mid_rgb->green - gc1->rgb->green) * rel_pos / 100.0; + color->blue = gc1->rgb->blue + + (gc1->mid_rgb->blue - gc1->rgb->blue) * rel_pos / 100.0; + *alpha = gc1->alpha + + (gc1->mid_alpha - gc1->alpha) * rel_pos / 100.0; + } + } +} + /* *-------------------------------------------------------------- @@ -152,7 +215,7 @@ ZnGetGradientColor(ZnGradient *grad, ZnBool ZnGradientFlat(ZnGradient *grad) { - return (grad->num_colors == 1); + return (grad->num_actual_colors == 1); } @@ -305,12 +368,6 @@ ZnNameGradient(Tcl_Interp *interp, ZnGradient *grad; XColor color; - grad = ZnGetGradient(interp, tkwin, grad_descr); - if (!grad) { - Tcl_AppendResult(interp, "gradient specification \"", grad_descr, - "\", is invalid", NULL); - return False; - } /* * First try to find if the name interfere with a color name, * this must be avoided. Gradients may be described by a single @@ -322,8 +379,15 @@ ZnNameGradient(Tcl_Interp *interp, "\", is a color name", NULL); return False; } + grad = ZnGetGradient(interp, tkwin, grad_descr); + if (!grad) { + Tcl_AppendResult(interp, "gradient specification \"", grad_descr, + "\", is invalid", NULL); + return False; + } hash = Tcl_CreateHashEntry(&gradient_table, Tk_GetUid(name), &new); if (!new) { + ZnFreeGradient(grad); Tcl_AppendResult(interp, "gradient name \"", name, "\", is already in use", NULL); return False; @@ -360,6 +424,290 @@ ZnDeleteGradientName(char *name) } } +static void +InterpolateGradientColor(Tk_Window tkwin, + ZnGradientColor *gc1, /* First color */ + ZnGradientColor *gc2, /* Next color */ + ZnGradientColor *gc_interp,/* New interpolated color */ + ZnGradientColor *gc_adjust,/* Adjusted first color. + * Needed if interested in + * the range color1 interp + * color. */ + int interp_pos, + int min_pos, + int span) +{ + int pos1, pos2, interp_rel_pos, tmp; + XColor rgb; + + pos1 = (gc1->position-min_pos)/span; + pos2 = (gc2->position-min_pos)/span; + interp_rel_pos = (interp_pos-pos1)*100/(pos2-pos1); + if (interp_rel_pos < gc1->control) { + tmp = interp_rel_pos*gc1->control; + rgb.red = gc1->rgb->red + (gc1->mid_rgb->red - gc1->rgb->red)*tmp/10000; + rgb.green = gc1->rgb->green + (gc1->mid_rgb->green - gc1->rgb->green)*tmp/10000; + rgb.blue = gc1->rgb->blue + (gc1->mid_rgb->blue - gc1->rgb->blue)*tmp/10000; + } + else if (interp_rel_pos > gc1->control) { + tmp = (interp_rel_pos-gc1->control)*gc1->control; + rgb.red = gc1->rgb->red + (gc1->mid_rgb->red - gc1->rgb->red)*tmp/10000; + rgb.green = gc1->rgb->green + (gc1->mid_rgb->green - gc1->rgb->green)*tmp/10000; + rgb.blue = gc1->rgb->blue + (gc1->mid_rgb->blue - gc1->rgb->blue)*tmp/10000; + } + else { + rgb = *gc1->mid_rgb; + gc_interp->alpha = gc1->mid_alpha; + } + rgb.red = gc1->rgb->red+(gc2->rgb->red-gc1->rgb->red)*interp_rel_pos/100; + rgb.green = gc1->rgb->green+(gc2->rgb->green-gc1->rgb->green)*interp_rel_pos/100; + rgb.blue = gc1->rgb->blue+(gc2->rgb->blue-gc1->rgb->blue)*interp_rel_pos/100; + gc_interp->rgb = Tk_GetColorByValue(tkwin, &rgb); + gc_interp->alpha = gc1->alpha+(gc2->alpha-gc1->alpha)*interp_rel_pos/100; + if (!gc_adjust) { + /* + * Interested in the segment from the interpolated color + * to color 2. + */ + gc_interp->position = 0; + if (interp_rel_pos < gc1->control) { + gc_interp->control = gc1->control-interp_rel_pos; + gc_interp->mid_rgb = Tk_GetColorByValue(tkwin, gc1->mid_rgb); + gc_interp->mid_alpha = gc1->mid_alpha; + } + else { + rgb.red = gc_interp->rgb->red+(gc2->rgb->red-gc_interp->rgb->red)/2; + rgb.green = gc_interp->rgb->green+(gc2->rgb->green-gc_interp->rgb->green)/2; + rgb.blue = gc_interp->rgb->blue+(gc2->rgb->blue-gc_interp->rgb->blue)/2; + gc_interp->mid_rgb = Tk_GetColorByValue(tkwin, &rgb); + gc_interp->mid_alpha = gc_interp->mid_alpha + + (gc2->mid_alpha - gc_interp->mid_alpha)/2; + gc_interp->control = 50; + } + } + else { + /* + * Interested in the segment form color 1 (color adjusted) to + * interpolated color. + */ + gc_interp->position = 100; + gc_interp->mid_rgb = NULL; + if (interp_rel_pos > gc1->control) { + gc_adjust->control = gc1->control+interp_rel_pos; + } + else { + rgb.red = gc1->rgb->red+(gc_interp->rgb->red-gc1->rgb->red)/2; + rgb.green = gc1->rgb->green+(gc_interp->rgb->green-gc1->rgb->green)/2; + rgb.blue = gc1->rgb->blue+(gc_interp->rgb->blue-gc1->rgb->blue)/2; + Tk_FreeColor(gc_adjust->mid_rgb); + gc_adjust->mid_rgb = Tk_GetColorByValue(tkwin, &rgb); + gc_adjust->mid_alpha = gc1->mid_alpha + + (gc_interp->mid_alpha - gc1->mid_alpha)/2; + gc_adjust->control = 50; + } + } +} + + +static void +ReduceGradient(Tk_Window tkwin, + ZnGradient *grad) +{ + ZnReal dx, dy, len, angle; + ZnTransfo t; + ZnPoint pbbox[4], pgrad[4]; + ZnReal maxx, minx, span, start_in_new, end_in_new; + int minx100, maxx100, span100; + int i, j, first_color, last_color; + ZnBool interpolate_first, interpolate_last; + + dx = grad->e.x - grad->p.x; + dy = grad->e.y - grad->p.y; + len = sqrt(dx*dx+dy*dy); + angle = acos(dx/len); + if (dy < 0) { + angle = 2*M_PI - angle; + } + grad->angle = (int) -ZnRadDeg(angle); + + if (grad->type == ZN_CONICAL_GRADIENT) { + unchanged: + grad->actual_colors = grad->colors_in; + grad->num_actual_colors = grad->num_colors_in; + return; + } + + ZnTransfoSetIdentity(&t); + ZnTranslate(&t, -grad->p.x, -grad->p.y); + ZnRotateRad(&t, -angle); + ZnScale(&t, 1/len, 1/len); + pbbox[0].x = -50; + pbbox[0].y = 50; + pbbox[1].x = 50; + pbbox[1].y = 50; + pbbox[2].x = 50; + pbbox[2].y = -50; + pbbox[3].x = -50; + pbbox[3].y = -50; + ZnTransformPoints(&t, pbbox, pgrad, 4); + maxx = minx = pgrad[0].x; + for (i = 1; i < 4; i++) { + if (pgrad[i].x > maxx) { + maxx = pgrad[i].x; + } + if (pgrad[i].x < minx) { + minx = pgrad[i].x; + } + } + + span = maxx-minx; + if (grad->type == ZN_RADIAL_GRADIENT) { + start_in_new = 0; + end_in_new = 100/span; + } + else { + start_in_new = -minx*100/span; + end_in_new = (1-minx)*100/span; + } + + /*printf("minx: %g, maxx: %g, start%%: %g, end%%: %g\n", + minx, maxx, start_in_new, end_in_new);*/ + + /* + * Gradient is unchanged + */ + if ((ABS(start_in_new) < PRECISION_LIMIT) && + (ABS(end_in_new-100.0) < PRECISION_LIMIT)) { + goto unchanged; + } + if ((start_in_new > 100.0) || (end_in_new < 0.0)) { + grad->num_actual_colors = 1; + grad->actual_colors = ZnMalloc(sizeof(ZnGradientColor)); + if (end_in_new < 0.0) { + grad->actual_colors[0].alpha = grad->colors_in[0].alpha; + grad->actual_colors[0].rgb = Tk_GetColorByValue(tkwin, + grad->colors_in[0].rgb); + } + else { + grad->actual_colors[0].alpha = grad->colors_in[grad->num_colors_in].alpha; + grad->actual_colors[0].rgb = Tk_GetColorByValue(tkwin, + grad->colors_in[grad->num_colors_in].rgb); + } + } + + grad->num_actual_colors = grad->num_colors_in; + interpolate_first = False; + minx100 = (int) (minx*100); + maxx100 = (int) (maxx*100); + span100 = (int) (span*100); + if (start_in_new < 0.0) { + /* Find the closest color inside the active area */ + first_color = 1; + while ((first_color < (int) grad->num_colors_in) && + (grad->colors_in[first_color].position < minx100)) { + first_color++; + grad->num_actual_colors--; + } + if (grad->colors_in[first_color].position == minx100) { + grad->num_actual_colors--; + } + else { + interpolate_first = True; + /*printf("interpolate first color\n");*/ + } + } + else { + first_color = 0; + if (grad->type != ZN_RADIAL_GRADIENT) { + grad->num_actual_colors++; + } + } + interpolate_last = False; + if (end_in_new > 100.0) { + /* Find the closest color inside the active area */ + last_color = grad->num_colors_in-2; + while ((last_color >= 0) && + (grad->colors_in[last_color].position > maxx100)) { + last_color--; + grad->num_actual_colors--; + } + if (grad->colors_in[last_color].position == maxx100) { + grad->num_actual_colors--; + } + else { + interpolate_last = True; + /*printf("interpolate last color\n");*/ + } + } + else { + last_color = grad->num_colors_in-1; + grad->num_actual_colors++; + } + + grad->actual_colors = ZnMalloc(grad->num_actual_colors*sizeof(ZnGradientColor)); + j = 0; + if (interpolate_first) { + InterpolateGradientColor(tkwin, + &grad->colors_in[first_color-1], + &grad->colors_in[first_color], + &grad->actual_colors[j], + NULL, + minx100, minx100, span100); + j++; + } + else if ((first_color == 0) && (grad->type != ZN_RADIAL_GRADIENT)) { + grad->actual_colors[j] = grad->colors_in[0]; + grad->actual_colors[j].rgb = + Tk_GetColorByValue(tkwin, grad->colors_in[0].rgb); + if (grad->colors_in[0].mid_rgb) { + grad->actual_colors[j].mid_rgb = + Tk_GetColorByValue(tkwin, grad->colors_in[0].mid_rgb); + } + grad->actual_colors[0].position = 0; + grad->actual_colors[0].control = 50; + j++; + /*printf("adding a color at start\n");*/ + } + + /*printf("first color: %d, last color: %d, num colors: %d\n", + first_color, last_color, grad->num_actual_colors);*/ + for (i = first_color; i <= last_color; i++, j++) { + grad->actual_colors[j] = grad->colors_in[i]; + grad->actual_colors[j].rgb = + Tk_GetColorByValue(tkwin, grad->colors_in[i].rgb); + if (grad->colors_in[i].mid_rgb) { + grad->actual_colors[j].mid_rgb = + Tk_GetColorByValue(tkwin, grad->colors_in[i].mid_rgb); + } + grad->actual_colors[j].position = + (grad->colors_in[i].position-minx100)*100/span100; + /*printf("i: %d, j: %d, minx: %d, span: %d, position av: %d position ap: %d\n", + i, j, minx100, span100, grad->colors_in[i].position, grad->actual_colors[j].position);*/ + } + + if (interpolate_last) { + InterpolateGradientColor(tkwin, + &grad->colors_in[last_color], + &grad->colors_in[last_color+1], + &grad->actual_colors[j], + &grad->actual_colors[j-1], + maxx100, minx100, span100); + } + else if (last_color == ((int) grad->num_colors_in)-1) { + i = grad->num_colors_in-1; + grad->actual_colors[j] = grad->colors_in[i]; + grad->actual_colors[j].rgb = + Tk_GetColorByValue(tkwin, grad->colors_in[i].rgb); + if (grad->colors_in[i].mid_rgb) { + grad->actual_colors[j].mid_rgb = + Tk_GetColorByValue(tkwin, grad->colors_in[i].mid_rgb); + } + /*printf("adding a color at end\n");*/ + } + grad->actual_colors[j].position = 100; +} + + /* *-------------------------------------------------------------- * @@ -463,15 +811,16 @@ ZnGetGradient(Tcl_Interp *interp, Tcl_HashEntry *hash; ZnGradient *grad; unsigned int i, j, nspace, num_colors; - unsigned int size, num_coords; + unsigned int size, num_coords=0; char type; char const *scan_ptr, *next_ptr, *str_ptr; - int angle, position, control; + ZnReal angle, position, control; ZnReal coords[4]; char *color_ptr, *end, segment[SEGMENT_SIZE]; ZnGradientColor *first, *last; XColor color; int new, red_range, green_range, blue_range; + ZnBool simple; /* printf("ZnGetGradient : %s\n", desc);*/ if (!desc || !*desc) { @@ -487,213 +836,231 @@ ZnGetGradient(Tcl_Interp *interp, */ desc = Tk_GetUid(desc); + /*printf("get gradient: %s\n", desc);*/ hash = Tcl_CreateHashEntry(&gradient_table, desc, &new); if (!new) { grad = (ZnGradient *) Tcl_GetHashValue(hash); grad->ref_count++; + return grad; } - else { - /* - * No satisfactory gradient exists yet. Initialize a new one. - */ - type = ZN_AXIAL_GRADIENT; - angle = 0; - /* - * Skip the trailing spaces. - */ - while (*desc == ' ') { - desc++; - } - /* - * Count the sections in the description. It should give - * the number of colors plus may be the gradient description. - */ - scan_ptr = desc; - /* - * If the first section is the gradient description, start color - * counts up from zero. - */ - num_colors = (*scan_ptr == '=') ? 0 : 1; - while ((scan_ptr = strchr(scan_ptr, '|'))) { - num_colors++; - scan_ptr++; - } - if (num_colors == 0) { - Tcl_AppendResult(interp, "gradient should have at least one color \"", - desc, "\",", NULL); - grad_err1: - Tcl_DeleteHashEntry(hash); - /*printf("ZnGetGradient error : %s\n", desc);*/ - return NULL; - } - /* - * Then look at the gradient type. - */ - scan_ptr = desc; - /* - * next_ptr can't be NULL in the following code, - * we checked that at least one color was specified - * after the gradient description. - */ - next_ptr = strchr(scan_ptr, '|'); - if (*scan_ptr == '=') { - scan_ptr++; - if ((*scan_ptr == 'a') && (strncmp(scan_ptr, "axial", 5) == 0)) { + + /* + * No satisfactory gradient exists yet. Initialize a new one. + */ + type = ZN_AXIAL_GRADIENT; + angle = 0.0; + /* + * Skip the trailing spaces. + */ + while (*desc == ' ') { + desc++; + } + /* + * Count the sections in the description. It should give + * the number of colors plus may be the gradient description. + */ + scan_ptr = desc; + /* + * If the first section is the gradient description, start color + * counts up from zero. + */ + num_colors = (*scan_ptr == '=') ? 0 : 1; + while ((scan_ptr = strchr(scan_ptr, '|'))) { + num_colors++; + scan_ptr++; + } + if (num_colors == 0) { + Tcl_AppendResult(interp, "gradient should have at least one color \"", + desc, "\",", NULL); + grad_err1: + Tcl_DeleteHashEntry(hash); + /*printf("ZnGetGradient error : %s\n", desc);*/ + return NULL; + } + /* + * Then look at the gradient type. + */ + scan_ptr = desc; + /* + * next_ptr can't be NULL in the following code, + * we checked that at least one color was specified + * after the gradient description. + */ + next_ptr = strchr(scan_ptr, '|'); + if (*scan_ptr == '=') { + scan_ptr++; + if (((*scan_ptr == 'a') && (strncmp(scan_ptr, "axial", 5) == 0)) || + ((*scan_ptr == 'c') && (strncmp(scan_ptr, "conical", 7) == 0))) { + if (*scan_ptr == 'a') { scan_ptr += 5; - if (ParseRealList(scan_ptr, next_ptr, coords, 1) != 1) { - grad_err3: - Tcl_AppendResult(interp, "invalid gradient parameter \"", - desc, "\",", NULL); - goto grad_err1; - } - angle = (int) coords[0]; - } - else if (((*scan_ptr == 'r') && (strncmp(scan_ptr, "radial", 6) == 0)) || - ((*scan_ptr == 'p') && (strncmp(scan_ptr, "path", 4) == 0))) { - if (*scan_ptr == 'r') { - type = ZN_RADIAL_GRADIENT; - scan_ptr += 6; - } - else { - type = ZN_PATH_GRADIENT; - scan_ptr += 4; - } - num_coords = ParseRealList(scan_ptr, next_ptr, coords, 4); - if ((num_coords != 2) && (num_coords != 4)) { - goto grad_err3; - } } else { - Tcl_AppendResult(interp, "invalid gradient type \"", + scan_ptr += 7; + type = ZN_CONICAL_GRADIENT; + } + num_coords = ParseRealList(scan_ptr, next_ptr, coords, 4); + if ((num_coords != 1) && (num_coords != 4)) { + grad_err3: + Tcl_AppendResult(interp, "invalid gradient parameter \"", desc, "\",", NULL); goto grad_err1; } - scan_ptr = next_ptr + 1; - next_ptr = strchr(scan_ptr, '|'); + angle = (init) coords[0]; } - /* - * Create the gradient structure. - */ - grad = (ZnGradient *) ZnMalloc(sizeof(ZnGradient) + - sizeof(ZnGradientColor)*(num_colors-1)); - grad->ref_count = 1; - grad->simple = True; - grad->num_colors = num_colors; - grad->type = type; - if (type == ZN_AXIAL_GRADIENT) { - if (num_coords == 4) { - grad->simple = False; - grad->g.p.x = coords[0]; - grad->g.p.y = coords[1]; - grad->e.x = coords[2]; - grad->e.y = coords[3]; + else if (((*scan_ptr == 'r') && (strncmp(scan_ptr, "radial", 6) == 0)) || + ((*scan_ptr == 'p') && (strncmp(scan_ptr, "path", 4) == 0))) { + if (*scan_ptr == 'r') { + type = ZN_RADIAL_GRADIENT; + scan_ptr += 6; } else { - grad->g.angle = angle; + type = ZN_PATH_GRADIENT; + scan_ptr += 4; + } + num_coords = ParseRealList(scan_ptr, next_ptr, coords, 4); + if ((num_coords != 2) && (num_coords != 4)) { + goto grad_err3; } } else { - grad->g.p.x = coords[0]; - grad->g.p.y = coords[1]; - if (num_coords == 4) { - grad->simple = False; - grad->e.x = coords[2]; - grad->e.y = coords[3]; - } + Tcl_AppendResult(interp, "invalid gradient type \"", + desc, "\",", NULL); + goto grad_err1; } - grad->hash = hash; - Tcl_SetHashValue(hash, grad); - - for (i = 0; i < num_colors; i++) { - grad->colors[i].alpha = 100; - /* - * Try to parse the color name. - */ - nspace = strspn(scan_ptr, " \t"); - scan_ptr += nspace; - str_ptr = strpbrk(scan_ptr, " \t|"); - if (str_ptr) { - size = str_ptr - scan_ptr; - } - else { - size = strlen(scan_ptr); - } - if (size > (SEGMENT_SIZE-1)) { - Tcl_AppendResult(interp, "color name too long in gradient \"", - desc, "\",", NULL); - grad_err2: - for (j = 0; j < i; j++) { - Tk_FreeColor(grad->colors[j].rgb); - } - ZnFree(grad); - goto grad_err1; + scan_ptr = next_ptr + 1; + next_ptr = strchr(scan_ptr, '|'); + } + /* + * Create the gradient structure. + */ + grad = (ZnGradient *) ZnMalloc(sizeof(ZnGradient) + + sizeof(ZnGradientColor)*(num_colors-1)); + grad->ref_count = 1; + simple = True; + grad->num_colors_in = num_colors; + grad->type = type; + switch (type) { + case ZN_AXIAL_GRADIENT: + case ZN_CONICAL_GRADIENT: + if ((num_coords == 4) && + ((coords[0] != coords[2]) || (coords[1] != coords[3]))) { + grad->p.x = coords[0]; + grad->p.y = coords[1]; + simple = False; + grad->e.x = coords[2]; + grad->e.y = coords[3]; + } + else { + grad->angle = (int) angle; + } + break; + case ZN_RADIAL_GRADIENT: + grad->p.x = coords[0]; + grad->p.y = coords[1]; + if ((num_coords == 4) && + ((coords[0] != coords[2]) || (coords[1] != coords[3]))) { + simple = False; + grad->e.x = coords[2]; + grad->e.y = coords[3]; + } + break; + case ZN_PATH_GRADIENT: + grad->p.x = coords[0]; + grad->p.y = coords[1]; + break; + } + grad->hash = hash; + Tcl_SetHashValue(hash, grad); + + for (i = 0; i < num_colors; i++) { + grad->colors_in[i].alpha = 100; + /* + * Try to parse the color name. + */ + nspace = strspn(scan_ptr, " \t"); + scan_ptr += nspace; + str_ptr = strpbrk(scan_ptr, " \t|"); + if (str_ptr) { + size = str_ptr - scan_ptr; + } + else { + size = strlen(scan_ptr); + } + if (size > (SEGMENT_SIZE-1)) { + Tcl_AppendResult(interp, "color name too long in gradient \"", + desc, "\",", NULL); + grad_err2: + for (j = 0; j < i; j++) { + Tk_FreeColor(grad->colors_in[j].rgb); } - strncpy(segment, scan_ptr, size); - segment[size] = 0; - scan_ptr += size; + ZnFree(grad); + goto grad_err1; + } + strncpy(segment, scan_ptr, size); + segment[size] = 0; + scan_ptr += size; + /* + * Try to parse the color position. + */ + grad->colors_in[i].position = 0; + grad->colors_in[i].control = 50; + position = strtod(scan_ptr, &end); + if (end != scan_ptr) { + grad->colors_in[i].position = (int) position; + scan_ptr = end; /* - * Try to parse the color position. + * Try to parse the control point */ - grad->colors[i].position = 0; - grad->colors[i].control = 50; - position = strtol(scan_ptr, &end, 10); + control = strtod(scan_ptr, &end); if (end != scan_ptr) { - grad->colors[i].position = position; + grad->colors_in[i].control = (int) control; scan_ptr = end; - /* - * Try to parse the control point - */ - control = strtol(scan_ptr, &end, 10); - if (end != scan_ptr) { - grad->colors[i].control = control; - scan_ptr = end; - } - } - nspace = strspn(scan_ptr, " \t"); - if ((scan_ptr[nspace] != 0) && (scan_ptr+nspace != next_ptr)) { - Tcl_AppendResult(interp, "incorrect color description in gradient \"", - desc, "\",", NULL); - goto grad_err2; - } - - color_ptr = strchr(segment, ';'); - if (color_ptr) { - *color_ptr = 0; - } - grad->colors[i].rgb = Tk_GetColor(interp, tkwin, Tk_GetUid(segment)); - if (grad->colors[i].rgb == NULL) { - Tcl_AppendResult(interp, "incorrect color value in gradient \"", - desc, "\",", NULL); - goto grad_err2; - } - if (color_ptr) { - color_ptr++; - grad->colors[i].alpha = atoi(color_ptr); - } - if (i == 0) { - grad->colors[i].position = 0; - } - else if (i == num_colors - 1) { - grad->colors[i].position = 100; - } - if ((i > 0) && - ((grad->colors[i].position > 100) || - (grad->colors[i].position < grad->colors[i-1].position))) { - Tcl_AppendResult(interp, "incorrect color position in gradient \"", - desc, "\",", NULL); - goto grad_err2; - } - if (grad->colors[i].control > 100) { - grad->colors[i].control = 100; - } - if (grad->colors[i].alpha > 100) { - grad->colors[i].alpha = 100; - } - if (next_ptr) { - scan_ptr = next_ptr + 1; - next_ptr = strchr(scan_ptr, '|'); } } + nspace = strspn(scan_ptr, " \t"); + if ((scan_ptr[nspace] != 0) && (scan_ptr+nspace != next_ptr)) { + Tcl_AppendResult(interp, "incorrect color description in gradient \"", + desc, "\",", NULL); + goto grad_err2; + } + + color_ptr = strchr(segment, ';'); + if (color_ptr) { + *color_ptr = 0; + } + grad->colors_in[i].rgb = Tk_GetColor(interp, tkwin, Tk_GetUid(segment)); + if (grad->colors_in[i].rgb == NULL) { + Tcl_AppendResult(interp, "incorrect color value in gradient \"", + desc, "\",", NULL); + goto grad_err2; + } + if (color_ptr) { + color_ptr++; + grad->colors_in[i].alpha = atoi(color_ptr); + } + if (i == 0) { + grad->colors_in[i].position = 0; + } + else if (i == num_colors - 1) { + grad->colors_in[i].position = 100; + } + if ((i > 0) && + ((grad->colors_in[i].position > 100) || + (grad->colors_in[i].position < grad->colors_in[i-1].position))) { + Tcl_AppendResult(interp, "incorrect color position in gradient \"", + desc, "\",", NULL); + goto grad_err2; + } + if (grad->colors_in[i].control > 100) { + grad->colors_in[i].control = 100; + } + if (grad->colors_in[i].alpha > 100) { + grad->colors_in[i].alpha = 100; + } + if (next_ptr) { + scan_ptr = next_ptr + 1; + next_ptr = strchr(scan_ptr, '|'); + } } /* @@ -701,9 +1068,9 @@ ZnGetGradient(Tcl_Interp *interp, * used by the gradient rendering primitives when a control * is not at mid range. The last color has no mid_* values. */ - for (i = 0; i < grad->num_colors-1; i++) { - first = &grad->colors[i]; - last = &grad->colors[i+1]; + for (i = 0; i < grad->num_colors_in-1; i++) { + first = &grad->colors_in[i]; + last = &grad->colors_in[i+1]; red_range = (int) last->rgb->red - (int) first->rgb->red; green_range = (int) last->rgb->green - (int) first->rgb->green; blue_range = (int) last->rgb->blue - (int) first->rgb->blue; @@ -713,8 +1080,24 @@ ZnGetGradient(Tcl_Interp *interp, first->mid_rgb = Tk_GetColorByValue(tkwin, &color); first->mid_alpha = first->alpha + (last->alpha-first->alpha)/2; } - grad->colors[grad->num_colors-1].mid_rgb = NULL; + grad->colors_in[grad->num_colors_in-1].mid_rgb = NULL; + + /* + * If the gradient is 'simple' ie. described by a single point + * or an angle for axial gradients, the processing is finished. + * If not, we have to reduce the gradient to a simple one by adding + * or suppressing colors and adjusting the relative position of + * each remaining color. + */ + if (simple) { + grad->num_actual_colors = grad->num_colors_in; + grad->actual_colors = grad->colors_in; + } + else if (type != ZN_PATH_GRADIENT) { + ReduceGradient(tkwin, grad); + } + /*printf("num in: %d, num actual: %d\n", grad->num_colors_in,grad->num_actual_colors);*/ /* printf("ZnGetGradient end : %s\n", desc);*/ return grad; } @@ -770,11 +1153,20 @@ ZnFreeGradient(ZnGradient *grad) grad->ref_count--; if (grad->ref_count == 0) { Tcl_DeleteHashEntry(grad->hash); - for (i = 0; i < grad->num_colors; i++) { - Tk_FreeColor(grad->colors[i].rgb); - if (grad->colors[i].mid_rgb) { - Tk_FreeColor(grad->colors[i].mid_rgb); + for (i = 0; i < grad->num_colors_in; i++) { + Tk_FreeColor(grad->colors_in[i].rgb); + if (grad->colors_in[i].mid_rgb) { + Tk_FreeColor(grad->colors_in[i].mid_rgb); + } + } + if (grad->actual_colors != grad->colors_in) { + for (i = 0; i < grad->num_actual_colors; i++) { + Tk_FreeColor(grad->actual_colors[i].rgb); + if (grad->actual_colors[i].mid_rgb) { + Tk_FreeColor(grad->actual_colors[i].mid_rgb); + } } + ZnFree(grad->actual_colors); } ZnFree(grad); } -- cgit v1.1