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; iclipMinY || 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= 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= 0) + for (int j=y; j= 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 */