diff --git a/src/dlangui/graphics/colors.d b/src/dlangui/graphics/colors.d index 5678dc4c..c16913b6 100644 --- a/src/dlangui/graphics/colors.d +++ b/src/dlangui/graphics/colors.d @@ -195,7 +195,7 @@ uint blendARGB(uint dst, uint src, uint alpha) pure nothrow { uint dstr = (dst >> 16) & 0xFF; uint dstg = (dst >> 8) & 0xFF; uint dstb = (dst >> 0) & 0xFF; - uint ialpha = 256 - alpha; + uint ialpha = 255 - alpha; uint r = ((srcr * ialpha + dstr * alpha) >> 8) & 0xFF; uint g = ((srcg * ialpha + dstg * alpha) >> 8) & 0xFF; uint b = ((srcb * ialpha + dstb * alpha) >> 8) & 0xFF; diff --git a/src/dlangui/graphics/drawbuf.d b/src/dlangui/graphics/drawbuf.d index 0feb73f9..b6ce2019 100644 --- a/src/dlangui/graphics/drawbuf.d +++ b/src/dlangui/graphics/drawbuf.d @@ -288,6 +288,8 @@ class DrawBuf : RefCountedObject { abstract void fill(uint color); /// fill rectangle with solid color (clipping is applied) abstract void fillRect(Rect rc, uint color); + /// fill rectangle with a gradient (clipping is applied) + abstract void fillGradientRect(Rect rc, uint color1, uint color2, uint color3, uint color4); /// fill rectangle with solid color and pattern (clipping is applied) 0=solid fill, 1 = dotted void fillRectPattern(Rect rc, uint color, int pattern) { // default implementation: does not support patterns @@ -1161,6 +1163,11 @@ class ColorDrawBufBase : DrawBuf { } } + override void fillGradientRect(Rect rc, uint color1, uint color2, uint color3, uint color4) { + // TODO + fillRect(rc, color1); + } + /// fill rectangle with solid color and pattern (clipping is applied) 0=solid fill, 1 = dotted override void fillRectPattern(Rect rc, uint color, int pattern) { uint alpha = color >> 24; @@ -1406,6 +1413,11 @@ class GrayDrawBuf : DrawBuf { } } + override void fillGradientRect(Rect rc, uint color1, uint color2, uint color3, uint color4) { + // TODO + fillRect(rc, color1); + } + /// draw pixel at (x, y) with specified color override void drawPixel(int x, int y, uint color) { if (!_clipRect.isPointInside(x, y)) diff --git a/src/dlangui/graphics/gldrawbuf.d b/src/dlangui/graphics/gldrawbuf.d index 2966ac9e..5bb8f511 100644 --- a/src/dlangui/graphics/gldrawbuf.d +++ b/src/dlangui/graphics/gldrawbuf.d @@ -114,6 +114,17 @@ class GLDrawBuf : DrawBuf, GLConfigCallback { _scene.add(new SolidRectSceneItem(rc, color)); } + /// fill rectangle with a gradient (clipping is applied) + override void fillGradientRect(Rect rc, uint color1, uint color2, uint color3, uint color4) { + assert(_scene !is null); + color1 = applyAlpha(color1); + color2 = applyAlpha(color2); + color3 = applyAlpha(color3); + color4 = applyAlpha(color4); + if (!(isFullyTransparentColor(color1) && isFullyTransparentColor(color3)) && applyClipping(rc)) + _scene.add(new GradientRectSceneItem(rc, color1, color2, color3, color4)); + } + /// fill rectangle with solid color and pattern (clipping is applied) 0=solid fill, 1 = dotted override void fillRectPattern(Rect rc, uint color, int pattern) { if (pattern == PatternType.solid) @@ -749,6 +760,27 @@ public: } } +private class GradientRectSceneItem : SceneItem { +private: + Rect _rc; + uint _color1; + uint _color2; + uint _color3; + uint _color4; + +public: + this(Rect rc, uint color1, uint color2, uint color3, uint color4) { + _rc = rc; + _color1 = color1; + _color2 = color2; + _color3 = color3; + _color4 = color4; + } + override void draw() { + glSupport.queue.addGradientRect(_rc, _color1, _color2, _color3, _color4); + } +} + private class PatternRectSceneItem : SceneItem { private: Rect _rc; diff --git a/src/dlangui/graphics/resources.d b/src/dlangui/graphics/resources.d index 81532a9e..bd29ea3b 100644 --- a/src/dlangui/graphics/resources.d +++ b/src/dlangui/graphics/resources.d @@ -322,13 +322,61 @@ class SolidFillDrawable : Drawable { _color = color; } override void drawTo(DrawBuf buf, Rect rc, uint state = 0, int tilex0 = 0, int tiley0 = 0) { - if ((_color >> 24) != 0xFF) // not fully transparent + if (!_color.isFullyTransparentColor) buf.fillRect(rc, _color); } @property override int width() { return 1; } @property override int height() { return 1; } } +class GradientDrawable : Drawable { + protected uint _color1; // top left + protected uint _color2; // bottom left + protected uint _color3; // top right + protected uint _color4; // bottom right + this(uint angle, uint color1, uint color2) { + // rotate a gradient; angle goes clockwise + import std.math; + float radians = angle * PI / 180; + float c = cos(radians); + float s = sin(radians); + if (s >= 0) { + if (c >= 0) { + // 0-90 degrees + _color1 = blendARGB(color1, color2, cast(uint)(255 * c)); + _color2 = color2; + _color3 = color1; + _color4 = blendARGB(color1, color2, cast(uint)(255 * s)); + } else { + // 90-180 degrees + _color1 = color2; + _color2 = blendARGB(color1, color2, cast(uint)(255 * -c)); + _color3 = blendARGB(color1, color2, cast(uint)(255 * s)); + _color4 = color1; + } + } else { + if (c < 0) { + // 180-270 degrees + _color1 = blendARGB(color1, color2, cast(uint)(255 * -s)); + _color2 = color1; + _color3 = color2; + _color4 = blendARGB(color1, color2, cast(uint)(255 * -c)); + } else { + // 270-360 degrees + _color1 = color1; + _color2 = blendARGB(color1, color2, cast(uint)(255 * -s)); + _color3 = blendARGB(color1, color2, cast(uint)(255 * c)); + _color4 = color2; + } + } + } + override void drawTo(DrawBuf buf, Rect rc, uint state = 0, int tilex0 = 0, int tiley0 = 0) { + buf.fillGradientRect(rc, _color1, _color2, _color3, _color4); + } + @property override int width() { return 1; } + @property override int height() { return 1; } +} + /// solid borders (may be of different width) and, optionally, solid inner area class FrameDrawable : Drawable { protected uint _frameColor; // frame color @@ -399,6 +447,18 @@ static uint decodeDimension(string s) { return value; } +/// decode angle; only Ndeg format for now +static uint decodeAngle(string s) { + int angle; + if (s.endsWith("deg")) + angle = to!int(s[0 .. $ - 3]); + else + Log.e("Invalid angle format: ", s); + + // transform the angle to [0, 360) + return ((angle % 360) + 360) % 360; +} + static if (BACKEND_CONSOLE) { /** Sample format: @@ -423,39 +483,48 @@ static if (BACKEND_CONSOLE) { /// decode solid color / gradient / frame drawable from string like #AARRGGBB, e.g. #5599AA /// /// SolidFillDrawable: #AARRGGBB - e.g. #8090A0 or #80ffffff +/// GradientDrawable: #linear,Ndeg,#firstColor,#secondColor /// FrameDrawable: #frameColor,frameWidth[,#middleColor] /// or #frameColor,leftBorderWidth,topBorderWidth,rightBorderWidth,bottomBorderWidth[,#middleColor] /// e.g. #000000,2,#C0FFFFFF - black frame of width 2 with 75% transparent white middle /// e.g. #0000FF,2,3,4,5,#FFFFFF - blue frame with left,top,right,bottom borders of width 2,3,4,5 and white inner area static Drawable createColorDrawable(string s) { Log.d("creating color drawable ", s); - uint[6] values; - int valueCount = 0; - int start = 0; - for (int i = 0; i <= s.length; i++) { - if (i == s.length || s[i] == ',') { - if (i > start) { - string item = s[start .. i]; - if (item.startsWith("#")) - values[valueCount++] = decodeHexColor(item); - else - values[valueCount++] = decodeDimension(item); - if (valueCount >= 6) - break; - } - start = i + 1; + + enum DrawableType { SolidColor, LinearGradient, Frame } + auto type = DrawableType.SolidColor; + + string[] items = s.split(','); + uint[] values; + foreach (i, item; items) { + if (item == "#linear") + type = DrawableType.LinearGradient; + else if (item.startsWith("#")) + values ~= decodeHexColor(item); + else if (item.endsWith("deg")) + values ~= decodeAngle(item); + else { + values ~= decodeDimension(item); + type = DrawableType.Frame; } + if (i >= 6) + break; } - if (valueCount == 1) // only color #AARRGGBB + + if (type == DrawableType.SolidColor && values.length == 1) // only color #AARRGGBB return new SolidFillDrawable(values[0]); - else if (valueCount == 2) // frame color and frame width, with transparent inner area - #AARRGGBB,NN - return new FrameDrawable(values[0], values[1]); - else if (valueCount == 3) // frame color, frame width, inner area color - #AARRGGBB,NN,#AARRGGBB - return new FrameDrawable(values[0], values[1], values[2]); - else if (valueCount == 5) // frame color, frame widths for left,top,right,bottom and transparent inner area - #AARRGGBB,NNleft,NNtop,NNright,NNbottom - return new FrameDrawable(values[0], Rect(values[1], values[2], values[3], values[4])); - else if (valueCount == 6) // frame color, frame widths for left,top,right,bottom, inner area color - #AARRGGBB,NNleft,NNtop,NNright,NNbottom,#AARRGGBB - return new FrameDrawable(values[0], Rect(values[1], values[2], values[3], values[4]), values[5]); + else if (type == DrawableType.LinearGradient && values.length == 3) // angle and two gradient colors + return new GradientDrawable(values[0], values[1], values[2]); + else if (type == DrawableType.Frame) { + if (values.length == 2) // frame color and frame width, with transparent inner area - #AARRGGBB,NN + return new FrameDrawable(values[0], values[1]); + else if (values.length == 3) // frame color, frame width, inner area color - #AARRGGBB,NN,#AARRGGBB + return new FrameDrawable(values[0], values[1], values[2]); + else if (values.length == 5) // frame color, frame widths for left,top,right,bottom and transparent inner area - #AARRGGBB,NNleft,NNtop,NNright,NNbottom + return new FrameDrawable(values[0], Rect(values[1], values[2], values[3], values[4])); + else if (values.length == 6) // frame color, frame widths for left,top,right,bottom, inner area color - #AARRGGBB,NNleft,NNtop,NNright,NNbottom,#AARRGGBB + return new FrameDrawable(values[0], Rect(values[1], values[2], values[3], values[4]), values[5]); + } Log.e("Invalid drawable string format: ", s); return new EmptyDrawable(); // invalid format - just return empty drawable } diff --git a/src/dlangui/platforms/console/consoleapp.d b/src/dlangui/platforms/console/consoleapp.d index eb0c746c..06b7d8c1 100644 --- a/src/dlangui/platforms/console/consoleapp.d +++ b/src/dlangui/platforms/console/consoleapp.d @@ -406,6 +406,11 @@ class ConsoleDrawBuf : DrawBuf { } } + override void fillGradientRect(Rect rc, uint color1, uint color2, uint color3, uint color4) { + // TODO + fillRect(rc, color1); + } + /// fill rectangle with solid color and pattern (clipping is applied) 0=solid fill, 1 = dotted override void fillRectPattern(Rect rc, uint color, int pattern) { // default implementation: does not support patterns