45f9f9211373510d8a65af4abb53555d44a6ac13
braney
  Fri Jun 18 14:50:34 2021 -0700
fix alpha compositing so draw engine graphs can be composited on top of
Javascript drawings (eg. highlights)

diff --git src/inc/memgfx.h src/inc/memgfx.h
index a36a7e2..c7297c1 100644
--- src/inc/memgfx.h
+++ src/inc/memgfx.h
@@ -23,50 +23,52 @@
 #define MG_WHITE   0xffffffff
 #define MG_BLACK   0x000000ff
 #define MG_RED     0xff0000ff
 #define MG_GREEN   0x00ff00ff
 #define MG_BLUE    0x0000ffff
 #define MG_CYAN    0x00ffffff
 #define MG_MAGENTA 0xff00ffff
 #define MG_YELLOW  0xffff00ff
 #define MG_GRAY    0x808080ff
 
 #define MAKECOLOR_32_A(r,g,b,a) (((unsigned int)a) | ((unsigned int)b<<8) | ((unsigned int)g << 16) | ((unsigned int)r << 24))
 #define MAKECOLOR_32(r,g,b) (((unsigned int)0xff) | ((unsigned int)b<<8) | ((unsigned int)g << 16) | ((unsigned int)r << 24))
 #define COLOR_32_RED(c) (((c)>>24)&0xff)
 #define COLOR_32_GREEN(c) (((c)>>16)&0xff)
 #define COLOR_32_BLUE(c) (((c)>>8)&0xff)
+#define COLOR_32_ALPHA(c) (((c))&0xff)
 
 #else
 
 // LITTLE ENDIAN machines:
 
 #define MG_WHITE   0xffffffff
 #define MG_BLACK   0xff000000
 #define MG_RED     0xff0000ff
 #define MG_GREEN   0xff00ff00
 #define MG_BLUE    0xffff0000
 #define MG_CYAN    0xffffff00
 #define MG_MAGENTA 0xffff00ff
 #define MG_YELLOW  0xff00ffff
 #define MG_GRAY    0xff808080
 
 #define MAKECOLOR_32_A(r,g,b,a) (((unsigned int)a<<24) | ((unsigned int)b<<16) | ((unsigned int)g << 8) | (unsigned int)r)
 #define MAKECOLOR_32(r,g,b) (((unsigned int)0xff<<24) | ((unsigned int)b<<16) | ((unsigned int)g << 8) | (unsigned int)r)
 #define COLOR_32_RED(c) ((c)&0xff)
 #define COLOR_32_GREEN(c) (((c)>>8)&0xff)
 #define COLOR_32_BLUE(c) (((c)>>16)&0xff)
+#define COLOR_32_ALPHA(c) (((c)>>24)&0xff)
 #endif
 
 #define MG_WRITE_MODE_NORMAL    0
 #define MG_WRITE_MODE_MULTIPLY  (1 << 0)
 
 struct rgbColor
     {
     unsigned char r, g, b;
     };
 
 /* HSV and HSL structs can be used for changing lightness, darkness, or
  * color of RGB colors. Convert RGB->HS[LV], modify hue, saturation, or
  * value/lightness, then convert back to RGB.
  * The datatypes were chosen to be fast but also give accurate conversion
  * back to RGB.
@@ -406,23 +408,40 @@
 struct rgbColor mgColorIxToRgb(struct memGfx *mg, int colorIx);
 /* Return rgb value at color index. */
 
 struct rgbColor colorIxToRgb(int colorIx);
 /* Return rgb value at color index. */
 
 INLINE void mixDot(struct memGfx *img, int x, int y,  float frac, Color col)
 /* Puts a single dot on the image, mixing it with what is already there
  * based on the frac argument. */
 /* Shouldn't this pay attention to the transparency of the current pixel? */
 {
 if ((x < img->clipMinX) || (x >= img->clipMaxX) || (y < img->clipMinY) || (y >= img->clipMaxY))
     return;
 
 Color *pt = _mgPixAdr(img,x,y);
-float invFrac = 1 - frac;
 
-int r = COLOR_32_RED(*pt) * invFrac + COLOR_32_RED(col) * frac;
-int g = COLOR_32_GREEN(*pt) * invFrac + COLOR_32_GREEN(col) * frac;
-int b = COLOR_32_BLUE(*pt) * invFrac + COLOR_32_BLUE(col) * frac;
-mgPutDot(img,x,y,MAKECOLOR_32(r,g,b));
+/* algorithm borrowed from https://en.wikipedia.org/wiki/Alpha_compositing */
+int aA = frac * 255;
+int rA = COLOR_32_RED(col);
+int gA = COLOR_32_GREEN(col);
+int bA = COLOR_32_BLUE(col);
+
+int aB = COLOR_32_ALPHA(*pt);
+int rB = COLOR_32_RED(*pt);
+int gB = COLOR_32_GREEN(*pt);
+int bB = COLOR_32_BLUE(*pt);
+
+int aOut = aA + (aB * (255 - aA) / 255);
+int rOut, gOut, bOut;
+if (aOut == 0)
+    rOut = gOut = bOut = 0;
+else
+    {
+    rOut = (rA * aA + rB * aB * (255 - aA) / 255)/aOut ;
+    gOut = (gA * aA + gB * aB * (255 - aA) / 255)/aOut ;
+    bOut = (bA * aA + bB * aB * (255 - aA) / 255)/aOut ;
+    }
+mgPutDot(img,x,y,MAKECOLOR_32_A(rOut,gOut,bOut,aOut));
 }
 #endif /* MEMGFX_H */