b8180d9f6d41dc708a2f249ba892cbca311e7a06
jcasper
  Mon Feb 27 11:38:55 2023 -0800
Adding transparency support for colors refs #30569

diff --git src/lib/memgfx.c src/lib/memgfx.c
index a921d44..f87fa1d 100644
--- src/lib/memgfx.c
+++ src/lib/memgfx.c
@@ -114,42 +114,52 @@
 #ifdef MEMGFX_BIGENDIAN
     *ptr = 0xffffff00;
 #else
     *ptr = 0x00ffffff;  // transparent white
 #endif
 }
 
 Color mgFindColor(struct memGfx *mg, unsigned char r, unsigned char g, unsigned char b)
 /* Returns closest color in color map to rgb values.  If it doesn't
  * already exist in color map and there's room, it will create
  * exact color in map. */
 {
 return MAKECOLOR_32(r,g,b);
 }
 
+Color mgFindAlphaColor(struct memGfx *mg, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
+/* Returns closest color in color map to rgba values.  If it doesn't
+ * already exist in color map and there's room, it will create
+ * exact color in map. */
+{
+return MAKECOLOR_32_A(r,g,b,a);
+}
+
 struct rgbColor colorIxToRgb(int colorIx)
 /* Return rgb value at color index. */
 {
-static struct rgbColor rgb;
+struct rgbColor rgb;
 #ifdef MEMGFX_BIGENDIAN
 rgb.r = (colorIx >> 24) & 0xff;
 rgb.g = (colorIx >> 16) & 0xff;
 rgb.b = (colorIx >> 8) & 0xff;
+rgb.a = (colorIx >> 0) & 0xff;
 #else
 rgb.r = (colorIx >> 0) & 0xff;
 rgb.g = (colorIx >> 8) & 0xff;
 rgb.b = (colorIx >> 16) & 0xff;
+rgb.a = (colorIx >> 24) & 0xff;
 #endif
 return rgb;
 }
 
 struct rgbColor mgColorIxToRgb(struct memGfx *mg, int colorIx)
 /* Return rgb value at color index. */
 {
 return colorIxToRgb(colorIx);
 }
 
 Color mgClosestColor(struct memGfx *mg, unsigned char r, unsigned char g, unsigned char b)
 /* Returns closest color in color map to r,g,b */
 {
 return MAKECOLOR_32(r,g,b);
 }
@@ -178,116 +188,113 @@
         colHashFree(&mg->colorHash);
     zeroBytes(mg, sizeof(*mg));
     freeMem(mg);
     }
 *pmg = NULL;
 }
 
 static void nonZeroCopy(Color *d, Color *s, int width)
 /* Copy non-zero colors. */
 {
 Color c;
 int i;
 for (i=0; i<width; ++i)
     {
     if ((c = s[i]) != MG_WHITE)
+        {
+        if (COLOR_32_ALPHA(c) == 0xff)
             d[i] = c;
+        else
+            mixColor(&d[i], c);
+        }
     }
 }
 
 static void mgPutSegMaybeZeroClear(struct memGfx *mg, int x, int y, int width, Color *dots, boolean zeroClear)
 /* Put a series of dots starting at x, y and going to right width pixels.
  * Possibly don't put zero dots though. */
 {
 int x2;
 Color *pt;
 if (y < mg->clipMinY || y > mg->clipMaxY)
     return;
 x2 = x + width;
 if (x2 > mg->clipMaxX)
     x2 = mg->clipMaxX;
 if (x < mg->clipMinX)
     {
     dots += mg->clipMinX - x;
     x = mg->clipMinX;
     }
 width = x2 - x;
 if (width > 0)
     {
     pt = _mgPixAdr(mg, x, y);
     if (zeroClear)
         nonZeroCopy(pt, dots, width);
     else
         {
-        width *= sizeof(Color);
-        memcpy(pt, dots, width * sizeof(Color));
+        int i;
+        for (i=0; i<width; i++)
+            mixColor(&pt[i], dots[i]);
         }
     }
 }
 
 void mgVerticalSmear(struct memGfx *mg,
 	int xOff, int yOff, int width, int height, 
 	Color *dots, boolean zeroClear)
 /* Put a series of one 'pixel' width vertical lines. */
 {
 while (--height >= 0)
     {
     mgPutSegMaybeZeroClear(mg, xOff, yOff, width, dots, zeroClear);
     ++yOff;
     }
 }
 
 
 void mgDrawBoxNormal(struct memGfx *mg, int x, int y, int width, int height, Color color)
 {
-int i;
-Color *pt;
 int x2 = x + width;
 int y2 = y + height;
-int wrapCount;
 
 if (x < mg->clipMinX)
     x = mg->clipMinX;
 if (y < mg->clipMinY)
     y = mg->clipMinY;
 if (x2 < mg->clipMinX)
     x2 = mg->clipMinX;
 if (y2 < mg->clipMinY)
     y2 = mg->clipMinY;
 
 if (x > mg->clipMaxX)
     x = mg->clipMaxX;
 if (y > mg->clipMaxY)
     y = mg->clipMaxY;
 if (x2 > mg->clipMaxX)
     x2 = mg->clipMaxX;
 if (y2 > mg->clipMaxY)
     y2 = mg->clipMaxY;
 width = x2-x;
 height = y2-y;
-if (width > 0 && height > 0)
+
+for (int i=x; i<x2; i++)
     {
-    pt = _mgPixAdr(mg,x,y);
-    /*colorBin[x][color]++;  increment color count for this pixel */
-    wrapCount = _mgBpr(mg) - width;
-    while (--height >= 0)
+    for (int j=y; j<y2; j++)
         {
-        //Color src = *pt;
-	i = width;
-	while (--i >= 0)
-	    *pt++ = color;
-	pt += wrapCount;
+        mixDot(mg, i, j, COLOR_32_ALPHA(color)/255.0, color);
         }
     }
 }
 
 void mgDrawBoxMultiply(struct memGfx *mg, int x, int y, int width, int height, Color color)
 {
 int i;
 Color *pt;
 int x2 = x + width;
 int y2 = y + height;
 int wrapCount;
 
 if (x < mg->clipMinX)
     x = mg->clipMinX;
 if (y < mg->clipMinY)
@@ -499,52 +506,52 @@
 	x1 = x2;
 	y1 = y2;
 	}
     duty_cycle = (delta_x - delta_y)/2;
     if (delta_x >= delta_y)
 	{
 	dots = delta_x+1;
 	while (--dots >= 0)
 	    {
 	    if (fillFromBase)
 		{
 		if (y1 < yBase)
 		    mgDrawBox(mg,x1,y1,1,yBase-y1,color);
 		}
 	    else
-		mgPutDot(mg,x1,y1,color);
+		mixDot(mg,x1,y1,COLOR_32_ALPHA(color)/255.0,color);
 	    duty_cycle -= delta_y;
 	    x1 += 1;
 	    if (duty_cycle < 0)
 		{
 		duty_cycle += delta_x;	  /* update duty cycle */
 		y1+=incy;
 		}
 	    }
 	}
     else
 	{
 	dots = delta_y+1;
 	while (--dots >= 0)
 	    {
 	    if (fillFromBase)
 		{
 		if (y1 < yBase)
 		    mgDrawBox(mg,x1,y1,1,yBase-y1,color);
 		}
 	    else
-		mgPutDot(mg,x1,y1,color);
+		mixDot(mg,x1,y1,COLOR_32_ALPHA(color)/255.0,color);
 	    duty_cycle += delta_x;
 	    y1+=incy;
 	    if (duty_cycle > 0)
 		{
 		duty_cycle -= delta_y;	  /* update duty cycle */
 		x1 += 1;
 		}
 	    }
 	}
     }
 }
 
 void mgDrawLine(struct memGfx *mg, int x1, int y1, int x2, int y2, Color color)
 /* Draw a line from one point to another. Draws it antialiased if
  * it's not horizontal or vertical. */
@@ -599,31 +606,31 @@
     w = x2 - x1;
     if (w > 0)
         {
 	Color *pt = _mgPixAdr(mg,x1,y);
 	if (mg->writeMode == MG_WRITE_MODE_MULTIPLY)
 	    {
 	    while (--w >= 0)
 		{
 		*pt = multiply(*pt, color);
 		pt += 1;
 		}
 	    }
 	else
 	    {
 	    while (--w >= 0)
-		*pt++ = color;
+                mixColor(pt++, color);
 	    }
 	}
     }
 }
 
 
 boolean mgClipForBlit(int *w, int *h, int *sx, int *sy,
 	struct memGfx *dest, int *dx, int *dy)
 {
 /* Make sure we don't overwrite destination. */
 int over;
 
 if ((over = dest->clipMinX - *dx) > 0)
     {
     *w -= over;
@@ -632,30 +639,32 @@
     }
 if ((over = dest->clipMinY - *dy) > 0)
     {
     *h -= over;
     *sy += over;
     *dy = dest->clipMinY;
     }
 if ((over = *w + *dx - dest->clipMaxX) > 0)
     *w -= over; 
 if ((over = *h + *dy - dest->clipMaxY) > 0)
     *h -= over;
 return (*h > 0 && *w > 0);
 }
 
 #ifdef SOON /* Simple but as yet untested blit function. */
+/* Note - these haven't been modified to have blit respect alpha in the blitted object - they
+ * would need to start calling mixDot instead of just smashing the previous content with memcpy. */
 void mgBlit(int width, int height, 
     struct memGfx *source, int sourceX, int sourceY,
     struct memGfx *dest, int destX, int destY)
 /* Copy pixels in a rectangle from source to destination */
 {
 if (!mgClipForBlit(&width, &height, &sourceX, &sourceY, dest, &destX, &destY))
     return;
 while (--height >= 0)
     {
     Color *dLine = _mgPixAdr(dest,destX,destY++);
     Color *sLine = _mgPixAdr(source,sourceX,sourceY++);
     memcpy(dLine, sLine, width * sizeof(Color));
     }
 }
 #endif /* SOON */
@@ -675,31 +684,31 @@
     return;
 
 inLine = bitData + (bitX>>3) + bitY * bitDataRowBytes;
 inLineBit = (0x80 >> (bitX&7));
 outLine = _mgPixAdr(dest,destX,destY);
 while (--height >= 0)
     {
     UBYTE *in = inLine;
     Color *out = outLine;
     UBYTE inBit = inLineBit;
     UBYTE inByte = *in++;
     int i = width;
     while (--i >= 0)
 	{
 	if (inBit & inByte)
-	    *out = color;
+            mixColor(out, color);
 	++out;
 	if ((inBit >>= 1) == 0)
 	    {
 	    inByte = *in++;
 	    inBit = 0x80;
 	    }
 	}
     inLine += bitDataRowBytes;
     outLine += _mgBpr(dest);
     }
 }
 
 void mgTextBlitSolid(int width, int height, int bitX, int bitY,
 	unsigned char *bitData, int bitDataRowBytes, 
 	struct memGfx *dest, int destX, int destY, 
@@ -711,31 +720,35 @@
 
 if (!mgClipForBlit(&width, &height, &bitX, &bitY, dest, &destX, &destY))
     return;
 inLine = bitData + (bitX>>3) + bitY * bitDataRowBytes;
 inLineBit = (0x80 >> (bitX&7));
 outLine = _mgPixAdr(dest,destX,destY);
 while (--height >= 0)
     {
     UBYTE *in = inLine;
     Color *out = outLine;
     UBYTE inBit = inLineBit;
     UBYTE inByte = *in++;
     int i = width;
     while (--i >= 0)
 	{
-	*out++ = ((inBit & inByte) ? color : backgroundColor);
+        if (inBit & inByte)
+            mixColor(out, color);
+        else
+            mixColor(out, backgroundColor);
+        out++;
 	if ((inBit >>= 1) == 0)
 	    {
 	    inByte = *in++;
 	    inBit = 0x80;
 	    }
 	}
     inLine += bitDataRowBytes;
     outLine += _mgBpr(dest);
     }
 }
 
 
 void mgText(struct memGfx *mg, int x, int y, Color color, 
 	MgFont *font, char *text)
 /* Draw a line of text with upper left corner x,y. */
@@ -955,31 +968,31 @@
 return font;
 }
 
 MgFont *mgFontForSize(char *textSize)
 /* Get a font of given size and style.  Abort with error message if not found.
  * The textSize should be 6,8,10,12,14,18,24 or 34.  For backwards compatibility
  * textSizes of "tiny" "small", "medium", "large" and "huge" are also ok. */
 {
 return mgFontForSizeAndStyle(textSize, "medium");
 }
 
 
 void mgSlowDot(struct memGfx *mg, int x, int y, int colorIx)
 /* Draw a dot when a macro won't do. */
 {
-mgPutDot(mg, x, y, colorIx);
+mixDot(mg, x, y, COLOR_32_ALPHA(colorIx)/255.0, colorIx);
 }
 
 int mgSlowGetDot(struct memGfx *mg, int x, int y)
 /* Fetch a dot when a macro won't do. */
 {
 return mgGetDot(mg, x, y);
 }
 
 struct memGfx *mgRotate90(struct memGfx *in)
 /* Create a copy of input that is rotated 90 degrees clockwise. */
 {
 int iWidth = in->width, iHeight = in->height;
 struct memGfx *out = mgNew(iHeight, iWidth);
 Color *inCol, *outRow, *outRowStart;
 int i,j;
@@ -1015,60 +1028,61 @@
 
 void vgMgMethods(struct vGfx *vg)
 /* Fill in virtual graphics methods for memory based drawing. */
 {
 vg->pixelBased = TRUE;
 vg->close = (vg_close)mgFree;
 vg->dot = (vg_dot)mgSlowDot;
 vg->getDot = (vg_getDot)mgSlowGetDot;
 vg->box = (vg_box)mgDrawBox;
 vg->line = (vg_line)mgDrawLine;
 vg->text = (vg_text)mgText;
 vg->textRight = (vg_textRight)mgTextRight;
 vg->textCentered = (vg_textCentered)mgTextCentered;
 vg->textInBox = (vg_textInBox)mgTextInBox;
 vg->findColorIx = (vg_findColorIx)mgFindColor;
+vg->findAlphaColorIx = (vg_findAlphaColorIx)mgFindAlphaColor;
 vg->colorIxToRgb = (vg_colorIxToRgb)mgColorIxToRgb;
 vg->setWriteMode = (vg_setWriteMode)mgSetWriteMode;
 vg->setClip = (vg_setClip)mgSetClip;
 vg->unclip = (vg_unclip)mgUnclip;
 vg->verticalSmear = (vg_verticalSmear)mgVerticalSmear;
 vg->fillUnder = (vg_fillUnder)mgFillUnder;
 vg->drawPoly = (vg_drawPoly)mgDrawPoly;
 vg->circle = (vg_circle)mgCircle;
 vg->ellipse = (vg_ellipse)mgEllipse;
 vg->curve = (vg_curve)mgCurve;
 vg->setHint = (vg_setHint)mgSetHint;
 vg->getHint = (vg_getHint)mgGetHint;
 vg->getFontPixelHeight = (vg_getFontPixelHeight)mgGetFontPixelHeight;
 vg->getFontStringWidth = (vg_getFontStringWidth)mgGetFontStringWidth;
 vg->setFontMethod = (vg_setFontMethod)mgSetFontMethod;
 }
 
 
 struct hslColor mgRgbToHsl(struct rgbColor rgb)
 /* Convert RGB to HSL colorspace (see http://en.wikipedia.org/wiki/HSL_and_HSV)
  * In HSL, Hue is the color in the range [0,360) with 0=red 120=green 240=blue,
  * Saturation goes from a shade of grey (0) to fully saturated color (1000), and
  * Lightness goes from black (0) through the hue (500) to white (1000). */
 {
 unsigned char rgbMax = max3(rgb.r, rgb.g, rgb.b);
 unsigned char rgbMin = min3(rgb.r, rgb.g, rgb.b);
 unsigned char delta = rgbMax - rgbMin;
 unsigned short minMax = rgbMax + rgbMin;
 int divisor;
-struct hslColor hsl = { 0.0, 0, (1000*minMax+255)/(2*255) }; // round up
+struct hslColor hsl = { 0.0, 0, (1000*minMax+255)/(2*255), rgb.a }; // round up
 
 // if max=min then no saturation, and this is gray
 if (rgbMax == rgbMin)
     return hsl;
 else if (hsl.l <= 500)
     divisor = minMax;
 else
     divisor = (2*255-minMax);
 hsl.s = (1000*delta + divisor/2)/divisor; // round up
 
 // Saturation so compute hue 0..360 degrees (same as for HSV)
 if (rgbMax == rgb.r) // red is 0 +/- offset in blue or green direction
     {
     hsl.h = 0 + 60.0*(rgb.g - rgb.b)/delta;
     }
@@ -1086,31 +1100,31 @@
 else if (hsl.h >= 360.0)
     hsl.h -= 360.0;
 return hsl;
 }
 
 
 struct hsvColor mgRgbToHsv(struct rgbColor rgb) 
 /* Convert RGB to HSV colorspace (see http://en.wikipedia.org/wiki/HSL_and_HSV)
  * In HSV, Hue is the color in the range [0,360) with 0=red 120=green 240=blue,
  * Saturation goes from white (0) to fully saturated color (1000), and
  * Value goes from black (0) through to the hue (1000). */
 {
 unsigned char rgbMax = max3(rgb.r, rgb.g, rgb.b);
 unsigned char rgbMin = min3(rgb.r, rgb.g, rgb.b);
 unsigned char delta = rgbMax - rgbMin;
-struct hsvColor hsv = {0.0, 0, 1000*rgbMax/255};
+struct hsvColor hsv = {0.0, 0, 1000*rgbMax/255, rgb.a};
 
 if (hsv.v == 0) 
     return hsv;
 hsv.s = 1000*delta/rgbMax;
 // if no saturation, then this is gray
 if (hsv.s == 0) 
     return hsv;
 // Saturation so compute hue 0..360 degrees (same as for HSL)
 if (rgbMax == rgb.r) // red is 0 +/- offset in blue or green direction
     {
     hsv.h = 0 + 60.0*(rgb.g - rgb.b)/delta;
     } 
 else if (rgbMax == rgb.g) // green is 120 +/- offset in blue or red direction
     {
     hsv.h = 120 + 60.0*(rgb.b - rgb.r)/delta;
@@ -1125,31 +1139,31 @@
 else if (hsv.h >= 360.0)
     hsv.h -= 360.0;
 return hsv;
 }
 
 
 struct rgbColor mgHslToRgb(struct hslColor hsl)
 /* Convert HSL to RGB colorspace http://en.wikipedia.org/wiki/HSL_and_HSV */
 {
 int p, q;
 double r, g, b;
 //unsigned short p, q, r, g, b;
 double tR, tG, tB;
 
 if( hsl.s == 0 ) // achromatic (grey)
-    return (struct rgbColor) {(255*hsl.l+500)/1000, (255*hsl.l+500)/1000, (255*hsl.l+500)/1000};
+    return (struct rgbColor) {(255*hsl.l+500)/1000, (255*hsl.l+500)/1000, (255*hsl.l+500)/1000, hsl.alpha};
 if (hsl.l <= 500)
     q = hsl.l + (hsl.l*hsl.s+500)/1000;
 else
     q = hsl.l + hsl.s - (hsl.l*hsl.s+500)/1000;
 p = 2 * hsl.l - q;
 hsl.h /= 360.0; // normalize h to 0..1
 tR = hsl.h + 1/3.0;
 tG = hsl.h;
 tB = hsl.h - 1/3.0;
 if (tR < 0.0)
     tR += 1.0;
 else if (tR > 1.0)
     tR -= 1.0;
 if (tG < 0.0)
     tG += 1.0;
@@ -1174,43 +1188,43 @@
 else if (tG < 0.5)
     g = q;
 else if (tG < 2/3.0)
     g = p + (q-p)*6*(2/3.0 - tG);
 else
     g = p;
 // Blue
 if (tB < 1/6.0)
     b = p + (q-p)*6*tB;
 else if (tB < 0.5)
     b = q;
 else if (tB < 2/3.0)
     b = p + (q-p)*6*(2/3.0 - tB);
 else
     b = p;
-return (struct rgbColor) {(255*r+500)/1000, (255*g+500)/1000, (255*b+500)/1000}; // round up
+return (struct rgbColor) {(255*r+500)/1000, (255*g+500)/1000, (255*b+500)/1000, hsl.alpha}; // round up
 }
 
 
 struct rgbColor mgHsvToRgb(struct hsvColor hsv)
 /* Convert HSV to RGB colorspace http://en.wikipedia.org/wiki/HSL_and_HSV */
 {
 int i;
 double f;
 unsigned short low, q, t, r, g, b;
 
 if( hsv.s == 0 ) // achromatic (grey)
-    return (struct rgbColor) {(255*hsv.v+500)/1000, (255*hsv.v+500)/1000, (255*hsv.v+500)/1000};
+    return (struct rgbColor) {(255*hsv.v+500)/1000, (255*hsv.v+500)/1000, (255*hsv.v+500)/1000, hsv.alpha};
 hsv.h /= 60.0; 
 i = (int) hsv.h;                     // floor the floating point value, sector 0 to 5
 f = hsv.h - i;                       // fractional part (distance) from hue 
 // hsv.v is highest r,g, or b value
 // low value is related to saturation
 low = hsv.v - (hsv.v * hsv.s / 1000); // lowest r,g, or b value
 t = low + (hsv.v - low) * f;         // scaled value from low..high
 q = low + (hsv.v - low) * (1.0-f);   // scaled value from low..high
 
 switch( i )
     {
     case 0:
 	r = hsv.v;
         g = t;
         b = low;
@@ -1229,31 +1243,31 @@
         r = low;
         g = q;
         b = hsv.v;
         break;
     case 4:
         r = t;
         g = low;
         b = hsv.v;
         break;
     default:                // case 5:
         r = hsv.v;
         g = low;
         b = q;
         break;
     }
-return (struct rgbColor) {(255*r+500)/1000, (255*g+500)/1000, (255*b+500)/1000};
+return (struct rgbColor) {(255*r+500)/1000, (255*g+500)/1000, (255*b+500)/1000, hsv.alpha};
 }
 
 
 struct rgbColor mgRgbTransformHsl(struct rgbColor in, double h, double s, double l)
 /* Transform rgb 'in' value using
  *   hue shift 'h' (0..360 degrees), 
  *   saturation scale 's', and 
  *   lightness scale 'l'
  * Returns the transformed rgb value 
  * Use H=0, S=L=1 for identity transformation
  */
 {
 struct hslColor hsl = mgRgbToHsl(in);
 hsl.h += h;
 while (hsl.h < 0.0)
@@ -1298,51 +1312,51 @@
    int a = abs(x1-x0), b = abs(y1-y0), b1 = b&1; /* values of diameter */
    long dx = 4*(1-a)*b*b, dy = 4*(b1+1)*a*a; /* error increment */
    long err = dx+dy+b1*a*a, e2; /* error of 1.step */
 
    if (x0 > x1) { x0 = x1; x1 += a; } /* if called with swapped points */
    if (y0 > y1) y0 = y1; /* .. exchange them */
    y0 += (b+1)/2; y1 = y0-b1;   /* starting pixel */
    a *= 8*a; b1 = 8*b*b;
 
    int dots = 0;
    do {
        if (!isDashed || (++dots % 3))
            {
            if (mode == ELLIPSE_BOTTOM || mode == ELLIPSE_FULL) 
                {
-               mgPutDot(mg, x1, y0, color); /*   I. Quadrant */
-               mgPutDot(mg, x0, y0, color); /*  II. Quadrant */
+               mixDot(mg, x1, y0, COLOR_32_ALPHA(color)/255.0, color); /*   I. Quadrant */
+               mixDot(mg, x0, y0, COLOR_32_ALPHA(color)/255.0, color); /*  II. Quadrant */
                }
            if (mode == ELLIPSE_TOP || mode == ELLIPSE_FULL) 
                {
-               mgPutDot(mg, x0, y1, color); /* III. Quadrant */
-               mgPutDot(mg, x1, y1, color); /*  IV. Quadrant */
+               mixDot(mg, x0, y1, COLOR_32_ALPHA(color)/255.0, color); /* III. Quadrant */
+               mixDot(mg, x1, y1, COLOR_32_ALPHA(color)/255.0, color); /*  IV. Quadrant */
                }
            }
        e2 = 2*err;
        if (e2 <= dy) { y0++; y1--; err += dy += a; }  /* y step */
        if (e2 >= dx || 2*err > dy) { x0++; x1--; err += dx += b1; } /* x step */
    } while (x0 <= x1);
 
    while (y0-y1 < b) {  /* too early stop of flat ellipses a=1 */
        if (!isDashed && (++dots % 3))
            {
-           mgPutDot(mg, x0-1, y0, color); /* -> finish tip of ellipse */
-           mgPutDot(mg, x1+1, y0++, color);
-           mgPutDot(mg, x0-1, y1, color);
-           mgPutDot(mg, x1+1, y1--, color);
+           mixDot(mg, x0-1, y0, COLOR_32_ALPHA(color)/255.0, color); /* -> finish tip of ellipse */
+           mixDot(mg, x1+1, y0++, COLOR_32_ALPHA(color)/255.0, color);
+           mixDot(mg, x0-1, y1, COLOR_32_ALPHA(color)/255.0, color);
+           mixDot(mg, x1+1, y1--, COLOR_32_ALPHA(color)/255.0, color);
            }
    }
 }
 
 static int mgCurveSegAA(struct memGfx *mg, int x0, int y0, int x1, int y1, int x2, int y2, 
                         Color color, boolean isDashed)
 /* Draw a segment of an anti-aliased curve within 3 points (quadratic Bezier)
  * Return max y value. Optionally alternate dots.
  * Adapted trivially from code posted on github and at http://members.chello.at/~easyfilter/bresenham.html */
  /* Thanks to author  * @author Zingl Alois
  * @date 22.08.2016 */
 {
    int yMax = 0;
    int sx = x2-x1, sy = y2-y1;
    long xx = x0-x1, yy = y0-y1, xy;             /* relative values for checks */