2 Copyright 2003, The AROS Development Team.
9 #include <intuition/imageclass.h>
10 #include <cybergraphx/cybergraphics.h>
11 #include <proto/graphics.h>
12 #include <proto/cybergraphics.h>
15 #include <proto/dos.h>
16 #include <proto/exec.h>
17 #include <proto/utility.h>
20 #include "imspec_intern.h"
25 extern struct Library
*MUIMasterBase
;
28 sizeof(LONG)*8 gives the size in bits of the LONG type,
29 then 8+1 bits (a color component size in bits, plus one bit
30 to account for the sign of the delta) are subtracted from it,
31 the result is the number of bits which can be used for the fractional
32 part to do fixed point math with the maximum precision.
34 Using all the remaining bits for the fractional part, IN THIS SPECIAL CASE,
35 does not incurr in overflow problems for the integer part, for the way
36 we use fractional numbers in this algorithm. Basically, we first scale up
37 a number A, and then we divide A by the number B. Successively,
38 the only operation we do on the number A is adding it to the number C,
39 initially equal to 0, _at maximum_ B times, which means that overflow
42 UPDATE: I've come up with a solution which makes this algorithm
43 twice as fast as before, by taking advantage of the fact that
44 a + b/c = (ac + b)/c, which means that now the number C is set
45 to an initial value different than 0. There's no need to diminish
46 the number of fractional bits as, for the way the algorithm
47 works, there's still no overflow.
51 #define SHIFT (sizeof(ULONG)*8 - (8 + 1))
53 STATIC VOID TrueDitherV
55 WORD x1
, WORD y1
, WORD x2
, WORD y2
,
56 WORD oy1
, WORD oy2
, ULONG
* start_rgb
, ULONG
* end_rgb
)
58 LONG max_delta_y
= (oy2
- oy1
> 0) ? oy2
- oy1
: 1;
59 LONG width
= x2
- x1
+ 1;
61 LONG delta_r
= end_rgb
[0] - start_rgb
[0];
62 LONG delta_g
= end_rgb
[1] - start_rgb
[1];
63 LONG delta_b
= end_rgb
[2] - start_rgb
[2];
65 LONG step_r
= (delta_r
<< SHIFT
) / max_delta_y
;
66 LONG step_g
= (delta_g
<< SHIFT
) / max_delta_y
;
67 LONG step_b
= (delta_b
<< SHIFT
) / max_delta_y
;
69 LONG y
, offset_y
= y1
- oy1
;
72 ((1 << SHIFT
) >> 1) + (start_rgb
[0] << SHIFT
) + offset_y
* step_r
;
74 ((1 << SHIFT
) >> 1) + (start_rgb
[1] << SHIFT
) + offset_y
* step_g
;
76 ((1 << SHIFT
) >> 1) + (start_rgb
[2] << SHIFT
) + offset_y
* step_b
;
78 for (y
= y1
; y
<= y2
; y
++)
80 FillPixelArray(rp
, x1
, y
, width
, 1,
81 ((red
>> SHIFT
) << 16) + ((green
>> SHIFT
) << 8) +
90 STATIC VOID TrueDitherH
92 WORD x1
, WORD y1
, WORD x2
, WORD y2
,
93 WORD ox1
, WORD ox2
, ULONG
*start_rgb
, ULONG
*end_rgb
)
95 LONG max_delta_x
= (ox2
- ox1
> 0) ? ox2
- ox1
: 1;
96 LONG height
= y2
- y1
+ 1;
98 LONG delta_r
= end_rgb
[0] - start_rgb
[0];
99 LONG delta_g
= end_rgb
[1] - start_rgb
[1];
100 LONG delta_b
= end_rgb
[2] - start_rgb
[2];
102 LONG step_r
= (delta_r
<< SHIFT
) / max_delta_x
;
103 LONG step_g
= (delta_g
<< SHIFT
) / max_delta_x
;
104 LONG step_b
= (delta_b
<< SHIFT
) / max_delta_x
;
106 LONG x
, offset_x
= x1
- ox1
;
108 /* 1 << (SHIFT - 1) is 0.5 in fixed point math. We add it to the variable
109 so that, at the moment in which the variable is converted to integer,
110 rounding is done properly. That is, a+x, with 0 < x < 0.5, is rounded
111 down to a, and a+x, with 0.5 <= x < 1, is rounded up to a+1. */
114 ((1 << SHIFT
) >> 1) + (start_rgb
[0] << SHIFT
) + offset_x
* step_r
;
116 ((1 << SHIFT
) >> 1) + (start_rgb
[1] << SHIFT
) + offset_x
* step_g
;
118 ((1 << SHIFT
) >> 1) + (start_rgb
[2] << SHIFT
) + offset_x
* step_b
;
120 for (x
= x1
; x
<= x2
; x
++)
122 FillPixelArray(rp
, x
, y1
, 1, height
,
123 ((red
>> SHIFT
) << 16) + ((green
>> SHIFT
) << 8) +
134 int red
, green
, blue
;
137 void FillPixelArrayGradientRelative(struct RastPort
*rp
, int xt
, int yt
,
138 int xb
, int yb
, int xp
, int yp
, int w
, int h
, ULONG
*start_rgb
,
139 ULONG
*end_rgb
, int angle
, int xoff
, int yoff
)
141 /* The basic idea of this algorithm is to calc the intersection between
142 * the diagonal of the rectangle (xs,ys) with dimension (xw,yw) a with
143 * the line starting at (x,y) (every pixel inside the rectangle) and
144 * angle angle with direction vector (vx,vy).
146 * Having the intersection point we then know the color of the pixel.
148 * TODO: Turn the algorithm into a incremental one
149 * Remove the use of floating point variables
151 double rad
= angle
* M_PI
/ 180;
152 double cosarc
= cos(rad
);
153 double sinarc
= sin(rad
);
155 struct myrgb startRGB
, endRGB
;
157 int diffR
, diffG
, diffB
;
159 int r
, t
; /* some helper variables to shorten the code */
161 int y1
; /* The intersection point */
162 int incr_y1
; /* increment of y1 */
164 int xadd
, ystart
, yadd
;
165 // double vx = -cosarc;
166 // double vy = sinarc;
167 int vx
= (int)(-cosarc
* 0xff);
168 int vy
= (int)(sinarc
* 0xff);
170 int width
= xb
- xt
+ 1;
171 int height
= yb
- yt
+ 1;
173 if ((w
<= 0) || (h
<= 0))
175 UBYTE
*buf
= AllocVec(w
* h
* 3, 0);
179 startRGB
.red
= start_rgb
[0];
180 startRGB
.green
= start_rgb
[1];
181 startRGB
.blue
= start_rgb
[2];
183 endRGB
.red
= end_rgb
[0];
184 endRGB
.green
= end_rgb
[1];
185 endRGB
.blue
= end_rgb
[2];
187 diffR
= endRGB
.red
- startRGB
.red
;
188 diffG
= endRGB
.green
- startRGB
.green
;
189 diffB
= endRGB
.blue
- startRGB
.blue
;
191 /* Normalize the angle */
193 angle
= 360 - ((-angle
) % 360);
197 if (angle
<= 90 || (angle
> 180 && angle
<= 270))
199 /* The to be intersected diagonal goes from the top left edge to the
208 /* The to be intersected diagonal goes from the bottom left edge to
209 the top right edge */
216 if (angle
> 90 && angle
<= 270)
218 /* for these angle we have y1 = height - y1. Instead of
220 * y1 = height - (-vy*(yw* xs -xw* ys)
221 * + yw*(vy* x -vx* y)) / (-yw*vx + xw*vy);
225 * y1 = (-vy*(yw*(-xs)-xw*(-ys+height))
226 * + yw*(vy*(-x)-vx*(-y+height)))/(-yw*vx + xw*vy);
228 * so height - y1 can be expressed with the normal formula adapting
231 * Note that if one would exchanging startRGB/endRGB the values would
232 * only work for linear color gradients
248 r
= -vy
* (yw
* xs
- xw
* ys
);
249 t
= -yw
* vx
+ xw
* vy
;
251 /* The formula as shown above is
253 * y1 = ((-vy*(yw*xs-xw*ys) + yw*(vy*x-vx*y)) /(-yw*vx + xw*vy));
255 * We see that only yw*(vy*x-vx*y) changes during the loop.
259 * Current Pixel: y1(x,y) = (r + yw*(vy*x-vx*y))/t = r/t + yw*(vy*x-vx*y)/t
260 * Next Pixel: y1(x+xadd,y) = (r + vw*(vy*(x+xadd)-vx*y))/t
262 * t*(y1(x+xadd,y) - y1(x,y)) = yw*(vy*(x+xadd)-vx*y) - yw*(vy*x-vx*y)
267 incr_y1
= yw
* vy
* xadd
;
269 for (l
= 0, y
= ystart
+ ((yp
- yt
) * yadd
); l
< h
; l
++, y
+= yadd
)
272 /* Calculate initial y1 accu, can be brought out of the loop as
273 * well (x=0). It's probably a good idea to add here also a value of
274 * (t-1)/2 to ensure the correct rounding.
275 * This (and for r) is also a place were actually a overflow can
276 * happen |yw|=16 |y|=16. So for vx nothing is left, currently 9 bits
277 * are used for vx or vy */
278 int y1_mul_t_accu
= r
- yw
* vx
* y
;
280 y1_mul_t_accu
+= (incr_y1
* (xp
- xt
));
282 for (c
= 0, x
= ((xp
- xt
) * xadd
); c
< w
; c
++, x
+= xadd
)
284 int red
, green
, blue
;
286 /* Calculate the intersection of two lines, this is not the
287 * fastest way to do but it is intuitive. Note: very slow! Will
288 * be optimzed later (remove FFP usage and making it incremental)
289 * ...update: it's now incremental and no FFP is used but it
290 * probably can be optimized more by removing some more of the
291 * divisions and further specialize the stuff here (use of three
293 /* y1 = (int)((-vy*(yw*xs-xw*ys) + yw*(vy*x-vx*y)) /(-yw*vx + xw*vy)); */
294 y1
= y1_mul_t_accu
/ t
;
296 red
= startRGB
.red
+ (int)(diffR
* y1
/ height
);
297 green
= startRGB
.green
+ (int)(diffG
* y1
/ height
);
298 blue
= startRGB
.blue
+ (int)(diffB
* y1
/ height
);
299 /* By using full 32 bits this can be made faster as well */
304 y1_mul_t_accu
+= incr_y1
;
306 /* By bringing building the gradient array in the same format as the
307 * RastPort BitMap a call to WritePixelArray() can be made also
310 WritePixelArray(buf
, 0, 0, w
* 3, rp
, 0, 0, w
, h
, RECTFMT_RGB
);
315 /*****************************************************************
316 Fill the given rectangle with a angle oriented gradient. The
317 unit angle uses are degrees
318 ******************************************************************/
319 STATIC
int FillPixelArrayGradient(struct RastPort
*rp
, int xt
, int yt
,
320 int xb
, int yb
, ULONG
*start_rgb
, ULONG
*end_rgb
, int angle
)
322 /* The basic idea of this algorithm is to calc the intersection between the
323 * diagonal of the rectangle (xs,ys) with dimension (xw,yw) a with the line
324 * starting at (x,y) (every pixel inside the rectangle) and angle angle
325 * with direction vector (vx,vy).
327 * Having the intersection point we then know the color of the pixel.
329 * TODO: Turn the algorithm into a incremental one
330 * Remove the use of floating point variables
333 double rad
= angle
* M_PI
/ 180;
334 double cosarc
= cos(rad
);
335 double sinarc
= sin(rad
);
337 struct myrgb startRGB
, endRGB
;
338 int diffR
, diffG
, diffB
;
340 int r
, t
; /* some helper variables to shorten the code */
342 int y1
; /* The intersection point */
343 int incr_y1
; /* increment of y1 */
345 int xadd
, ystart
, yadd
;
346 // double vx = -cosarc;
347 // double vy = sinarc;
348 int vx
= (int)(-cosarc
* 0xff);
349 int vy
= (int)(sinarc
* 0xff);
351 int width
= xb
- xt
+ 1;
352 int height
= yb
- yt
+ 1;
354 UBYTE
*buf
= (UBYTE
*) AllocVec(width
* 3, 0);
358 startRGB
.red
= start_rgb
[0];
359 startRGB
.green
= start_rgb
[1];
360 startRGB
.blue
= start_rgb
[2];
362 endRGB
.red
= end_rgb
[0];
363 endRGB
.green
= end_rgb
[1];
364 endRGB
.blue
= end_rgb
[2];
366 diffR
= endRGB
.red
- startRGB
.red
;
367 diffG
= endRGB
.green
- startRGB
.green
;
368 diffB
= endRGB
.blue
- startRGB
.blue
;
370 /* Normalize the angle */
372 angle
= 360 - ((-angle
) % 360);
376 if (angle
<= 90 || (angle
> 180 && angle
<= 270))
378 /* The to be intersected diagonal goes from the top left edge to the
387 /* The to be intersected diagonal goes from the bottom left edge to
388 the top right edge */
395 if (angle
> 90 && angle
<= 270)
397 /* for these angle we have y1 = height - y1. Instead of
399 * y1 = height - (-vy*(yw* xs -xw* ys) + yw*(vy* x -vx* y)) /(-yw*vx + xw*vy);
403 * y1 = (-vy*(yw*(-xs)-xw*(-ys+height)) + yw*(vy*(-x)-vx*(-y+height)))/(-yw*vx + xw*vy);
405 * so height - y1 can be expressed with the normal formular adapting some parameters.
407 * Note that if one would exchanging startRGB/endRGB the values would only work
408 * for linear color gradients
424 r
= -vy
* (yw
* xs
- xw
* ys
);
425 t
= -yw
* vx
+ xw
* vy
;
427 /* The formular as shown above is
429 * y1 = ((-vy*(yw*xs-xw*ys) + yw*(vy*x-vx*y)) /(-yw*vx + xw*vy));
431 * We see that only yw*(vy*x-vx*y) changes during the loop.
435 * Current Pixel: y1(x,y) = (r + yw*(vy*x-vx*y))/t = r/t + yw*(vy*x-vx*y)/t
436 * Next Pixel: y1(x+xadd,y) = (r + vw*(vy*(x+xadd)-vx*y))/t
438 * t*(y1(x+xadd,y) - y1(x,y)) = yw*(vy*(x+xadd)-vx*y) - yw*(vy*x-vx*y) = yw*vy*xadd;
442 incr_y1
= yw
* vy
* xadd
;
444 for (l
= 0, y
= ystart
; l
< height
; l
++, y
+= yadd
)
448 /* Calculate initial y1 accu, can be brought out of the loop as well (x=0). It's probably a
449 * a good idea to add here also a value of (t-1)/2 to ensure the correct rounding
450 * This (and for r) is also a place were actually a overflow can happen |yw|=16 |y|=16. So for
451 * vx nothing is left, currently 9 bits are used for vx or vy */
452 int y1_mul_t_accu
= r
- yw
* vx
* y
;
454 for (c
= 0, x
= 0; c
< width
; c
++, x
+= xadd
)
456 int red
, green
, blue
;
458 /* Calculate the intersection of two lines, this is not the fastet way to do but
459 * it is intuitive. Note: very slow! Will be optimzed later (remove FFP usage
460 * and making it incremental)...update: it's now incremental and no FFP is used
461 * but it probably can be optimized more by removing some more of the divisions and
462 * further specialize the stuff here (use of three accus). */
463 /* y1 = (int)((-vy*(yw*xs-xw*ys) + yw*(vy*x-vx*y)) /(-yw*vx + xw*vy));*/
464 y1
= y1_mul_t_accu
/ t
;
466 red
= startRGB
.red
+ (int)(diffR
* y1
/ height
);
467 green
= startRGB
.green
+ (int)(diffG
* y1
/ height
);
468 blue
= startRGB
.blue
+ (int)(diffB
* y1
/ height
);
470 /* By using full 32 bits this can be made faster as well */
475 y1_mul_t_accu
+= incr_y1
;
477 /* By bringing building the gradient array in the same format as the RastPort BitMap a call
478 * to WritePixelArray() can be made also faster */
479 WritePixelArray(buf
, 0, 0, width
* 3 /* srcMod */ ,
480 rp
, xt
, yt
+ l
, width
, 1, RECTFMT_RGB
);
486 /***************************************************************************************************/
488 VOID
zune_gradient_drawfast(struct MUI_ImageSpec_intern
*spec
,
489 struct RastPort
*rp
, struct MUI_RenderInfo
*mri
, WORD mode
, WORD abs_l
,
490 WORD abs_t
, WORD abs_r
, WORD abs_b
, WORD x1
, WORD y1
, WORD x2
, WORD y2
,
491 WORD xoff
, WORD yoff
)
493 ULONG
*start_rgb
= spec
->u
.gradient
.start_rgb
;
494 ULONG
*end_rgb
= spec
->u
.gradient
.end_rgb
;
496 if (!(CyberGfxBase
&& (mri
->mri_Flags
& MUIMRI_TRUECOLOR
)
497 && spec
->u
.gradient
.obj
))
499 SetAPen(rp
, spec
->u
.gradient
.start_pen
);
500 RectFill(rp
, x1
, y1
, x2
, y2
);
505 FillPixelArrayGradientRelative(rp
, abs_l
, abs_t
, abs_r
,
506 abs_b
, x1
, y1
, x2
- x1
+ 1, y2
- y1
+ 1, start_rgb
, end_rgb
,
507 spec
->u
.gradient
.angle
, xoff
, yoff
);
511 switch (spec
->u
.gradient
.angle
)
515 LONG oy1
= _top(spec
->u
.gradient
.obj
), oy2
=
516 _bottom(spec
->u
.gradient
.obj
);
517 LONG delta_oy
= (oy2
- oy1
> 0) ? oy2
- oy1
: 1;
518 LONG hh
= (delta_oy
+ 1) * 2;
531 mid_y
= y1
+ delta_oy
- yoff
;
535 ULONG
*tmp
= start_rgb
;
550 (rp
, x1
, y1
, x2
, mid_y
, oy1
, oy2
, start_rgb
, end_rgb
);
556 x1
, mid_y
+ 1, x2
, y2
,
557 oy1
+ delta_oy
, oy2
+ delta_oy
, end_rgb
, start_rgb
);
564 LONG ox1
= _left(spec
->u
.gradient
.obj
), ox2
=
565 _right(spec
->u
.gradient
.obj
);
566 LONG delta_ox
= (ox2
- ox1
> 0) ? ox2
- ox1
: 1;
567 LONG ww
= (delta_ox
+ 1) * 2;
580 mid_x
= x1
+ delta_ox
- xoff
;
584 ULONG
*tmp
= start_rgb
;
599 (rp
, x1
, y1
, mid_x
, y2
, ox1
, ox2
, start_rgb
, end_rgb
);
605 mid_x
+ 1, y1
, x2
, y2
,
606 ox1
+ delta_ox
, ox2
+ delta_ox
, end_rgb
, start_rgb
);
612 FillPixelArrayGradient(rp
, x1
, y1
, x2
, y2
, start_rgb
, end_rgb
,
613 spec
->u
.gradient
.angle
);
615 } /* switch(angle) */
617 VOID
zune_gradient_draw(struct MUI_ImageSpec_intern
*spec
,
618 struct MUI_RenderInfo
*mri
, WORD x1
, WORD y1
, WORD x2
, WORD y2
,
619 WORD xoff
, WORD yoff
)
621 zune_gradient_drawfast(spec
, mri
->mri_RastPort
,
622 mri
, 0, x1
, y1
, x2
, y2
, x1
, y1
, x2
, y2
, xoff
, yoff
);
624 /*************************************************************************
625 Converts a gradient string to a internal image spec
627 The format of the string is:
628 (h|H|v|V|<angle>),<start_r>,<start_g>,<start_b>-<end_r>,<end_g>,<end_b>
629 where <angle> is given decimal. The rest represents hexadecimal 32 bit
631 **************************************************************************/
632 BOOL
zune_gradient_string_to_intern(CONST_STRPTR str
,
633 struct MUI_ImageSpec_intern
*spec
)
637 ULONG start_r
, start_g
, start_b
;
638 ULONG end_r
, end_g
, end_b
;
643 /* Find out about the gradient angle */
659 converted
= StrToLong((STRPTR
) str
, (LONG
*) & angle
);
667 /* convert the color information */
671 converted
= HexToLong((STRPTR
) str
, &start_r
);
679 converted
= HexToLong((STRPTR
) str
, &start_g
);
687 converted
= HexToLong((STRPTR
) str
, &start_b
);
695 converted
= HexToLong((STRPTR
) str
, &end_r
);
703 converted
= HexToLong((STRPTR
) str
, &end_g
);
711 converted
= HexToLong((STRPTR
) str
, &end_b
);
715 /* Fill in the spec */
716 spec
->u
.gradient
.angle
= angle
;
718 spec
->u
.gradient
.start_rgb
[0] = start_r
>> 24;
719 spec
->u
.gradient
.start_rgb
[1] = start_g
>> 24;
720 spec
->u
.gradient
.start_rgb
[2] = start_b
>> 24;
722 spec
->u
.gradient
.end_rgb
[0] = end_r
>> 24;
723 spec
->u
.gradient
.end_rgb
[1] = end_g
>> 24;
724 spec
->u
.gradient
.end_rgb
[2] = end_b
>> 24;
729 VOID
zune_scaled_gradient_intern_to_string(struct MUI_ImageSpec_intern
*
732 sprintf(buf
, "7:%d,%08x,%08x,%08x-%08x,%08x,%08x",
733 (int)spec
->u
.gradient
.angle
,
734 (unsigned int)spec
->u
.gradient
.start_rgb
[0] * 0x01010101,
735 (unsigned int)spec
->u
.gradient
.start_rgb
[1] * 0x01010101,
736 (unsigned int)spec
->u
.gradient
.start_rgb
[2] * 0x01010101,
737 (unsigned int)spec
->u
.gradient
.end_rgb
[0] * 0x01010101,
738 (unsigned int)spec
->u
.gradient
.end_rgb
[1] * 0x01010101,
739 (unsigned int)spec
->u
.gradient
.end_rgb
[2] * 0x01010101);
742 VOID
zune_tiled_gradient_intern_to_string(struct MUI_ImageSpec_intern
*spec
,
745 sprintf(buf
, "8:%d,%08x,%08x,%08x-%08x,%08x,%08x",
746 (int)spec
->u
.gradient
.angle
,
747 (unsigned int)spec
->u
.gradient
.start_rgb
[0] * 0x01010101,
748 (unsigned int)spec
->u
.gradient
.start_rgb
[1] * 0x01010101,
749 (unsigned int)spec
->u
.gradient
.start_rgb
[2] * 0x01010101,
750 (unsigned int)spec
->u
.gradient
.end_rgb
[0] * 0x01010101,
751 (unsigned int)spec
->u
.gradient
.end_rgb
[1] * 0x01010101,
752 (unsigned int)spec
->u
.gradient
.end_rgb
[2] * 0x01010101);
755 BOOL
zune_gradientspec_setup(struct MUI_ImageSpec_intern
*spec
,
756 struct MUI_RenderInfo
*mri
)
758 spec
->u
.gradient
.mri
= mri
;
760 if (!(mri
->mri_Flags
& MUIMRI_TRUECOLOR
))
762 ULONG
*rgbptr
= spec
->u
.gradient
.start_rgb
;
763 ULONG
*penptr
= &spec
->u
.gradient
.start_pen
;
764 BOOL
*flagptr
= &spec
->u
.gradient
.start_pen_is_allocated
;
767 struct TagItem obp_tags
[] = {
768 {OBP_FailIfBad
, FALSE
},
772 for (i
= 0; i
< 2; i
++)
774 ULONG r
= rgbptr
[0] * 0x01010101;
775 ULONG g
= rgbptr
[1] * 0x01010101;
776 ULONG b
= rgbptr
[2] * 0x01010101;
778 pen
= ObtainBestPenA(mri
->mri_Colormap
, r
, g
, b
, obp_tags
);
783 *penptr
= FindColor(mri
->mri_Colormap
, r
, g
, b
, -1);
791 rgbptr
= spec
->u
.gradient
.end_rgb
;
792 penptr
= &spec
->u
.gradient
.end_pen
;
793 flagptr
= &spec
->u
.gradient
.end_pen_is_allocated
;
799 spec
->u
.gradient
.start_pen_is_allocated
= FALSE
;
800 spec
->u
.gradient
.end_pen_is_allocated
= FALSE
;
806 void zune_gradientspec_cleanup(struct MUI_ImageSpec_intern
*spec
)
808 if (spec
->u
.gradient
.start_pen_is_allocated
)
810 ReleasePen(spec
->u
.gradient
.mri
->mri_Colormap
,
811 spec
->u
.gradient
.start_pen
);
812 spec
->u
.gradient
.start_pen_is_allocated
= FALSE
;
815 if (spec
->u
.gradient
.end_pen_is_allocated
)
817 ReleasePen(spec
->u
.gradient
.mri
->mri_Colormap
,
818 spec
->u
.gradient
.end_pen
);
819 spec
->u
.gradient
.end_pen_is_allocated
= FALSE
;
822 spec
->u
.gradient
.mri
= NULL
;