/// <summary> /// Init. /// </summary> protected RGraphics(RAdapter adapter, RRect initialClip) { ArgChecker.AssertArgNotNull(adapter, "global"); _adapter = adapter; _clipStack.Push(initialClip); }
/// <summary> /// Get top-left location to start drawing the image at depending on background-position value. /// </summary> /// <param name="backgroundPosition">the background-position value</param> /// <param name="rectangle">the rectangle to position image in</param> /// <param name="imgSize">the size of the image</param> /// <returns>the top-left location</returns> private static RPoint GetLocation(string backgroundPosition, RRect rectangle, RSize imgSize) { double left = rectangle.Left; if (backgroundPosition.IndexOf("left", StringComparison.OrdinalIgnoreCase) > -1) { left = (rectangle.Left + .5f); } else if (backgroundPosition.IndexOf("right", StringComparison.OrdinalIgnoreCase) > -1) { left = rectangle.Right - imgSize.Width; } else if (backgroundPosition.IndexOf("0", StringComparison.OrdinalIgnoreCase) < 0) { left = (rectangle.Left + (rectangle.Width - imgSize.Width) / 2 + .5f); } double top = rectangle.Top; if (backgroundPosition.IndexOf("top", StringComparison.OrdinalIgnoreCase) > -1) { top = rectangle.Top; } else if (backgroundPosition.IndexOf("bottom", StringComparison.OrdinalIgnoreCase) > -1) { top = rectangle.Bottom - imgSize.Height; } else if (backgroundPosition.IndexOf("0", StringComparison.OrdinalIgnoreCase) < 0) { top = (rectangle.Top + (rectangle.Height - imgSize.Height) / 2 + .5f); } return new RPoint(left, top); }
/// <summary> /// Init. /// </summary> /// <param name="g">the Perspex graphics object to use</param> /// <param name="initialClip">the initial clip of the graphics</param> /// <param name="releaseGraphics">optional: if to release the graphics object on dispose (default - false)</param> public GraphicsAdapter(IDrawingContext g, RRect initialClip, bool releaseGraphics = false) : base(PerspexAdapter.Instance, initialClip) { ArgChecker.AssertArgNotNull(g, "g"); _g = g; _releaseGraphics = releaseGraphics; }
protected override RBrush CreateLinearGradientBrush(RRect rect, RColor color1, RColor color2, double angle) { var startColor = angle <= 180 ? Utils.Convert(color1) : Utils.Convert(color2); var endColor = angle <= 180 ? Utils.Convert(color2) : Utils.Convert(color1); angle = angle <= 180 ? angle : angle - 180; double x = angle < 135 ? Math.Max((angle - 45) / 90, 0) : 1; double y = angle <= 45 ? Math.Max(0.5 - angle / 90, 0) : angle > 135 ? Math.Abs(1.5 - angle / 90) : 0; return new BrushAdapter(new LinearGradientBrush(startColor, endColor, new Point(x, y), new Point(1 - x, 1 - y))); }
public override void PushClipExclude(RRect rect) { var geometry = new CombinedGeometry(); geometry.Geometry1 = new RectangleGeometry(Utils.Convert(_clipStack.Peek())); geometry.Geometry2 = new RectangleGeometry(Utils.Convert(rect)); geometry.GeometryCombineMode = GeometryCombineMode.Exclude; _clipStack.Push(_clipStack.Peek()); _g.PushClip(geometry); }
protected override RBrush CreateLinearGradientBrush(RRect rect, RColor color1, RColor color2, double angle) { XLinearGradientMode mode; if (angle < 45) mode = XLinearGradientMode.ForwardDiagonal; else if (angle < 90) mode = XLinearGradientMode.Vertical; else if (angle < 135) mode = XLinearGradientMode.BackwardDiagonal; else mode = XLinearGradientMode.Horizontal; return new BrushAdapter(new XLinearGradientBrush(Utils.Convert(rect), Utils.Convert(color1), Utils.Convert(color2), mode)); }
protected override RBrush CreateLinearGradientBrush(RRect rect, RColor color1, RColor color2, double angle) { var startColor = angle <= 180 ? Util.Convert(color1) : Util.Convert(color2); var endColor = angle <= 180 ? Util.Convert(color2) : Util.Convert(color1); angle = angle <= 180 ? angle : angle - 180; double x = angle < 135 ? Math.Max((angle - 45) / 90, 0) : 1; double y = angle <= 45 ? Math.Max(0.5 - angle / 90, 0) : angle > 135 ? Math.Abs(1.5 - angle / 90) : 0; return new BrushAdapter(new LinearGradientBrush { StartPoint = new RelativePoint(x, y, RelativeUnit.Relative), EndPoint = new RelativePoint(1 - x, 1 - y, RelativeUnit.Relative), GradientStops = { new GradientStop(startColor, 0), new GradientStop(endColor, 1) } }); }
/// <summary> /// Draw the background image of the given box in the given rectangle.<br/> /// Handle background-repeat and background-position values. /// </summary> /// <param name="g">the device to draw into</param> /// <param name="box">the box to draw its background image</param> /// <param name="imageLoadHandler">the handler that loads image to draw</param> /// <param name="rectangle">the rectangle to draw image in</param> public static void DrawBackgroundImage(RGraphics g, CssBox box, ImageLoadHandler imageLoadHandler, RRect rectangle) { // image size depends if specific rectangle given in image loader var imgSize = new RSize(imageLoadHandler.Rectangle == RRect.Empty ? imageLoadHandler.Image.Width : imageLoadHandler.Rectangle.Width, imageLoadHandler.Rectangle == RRect.Empty ? imageLoadHandler.Image.Height : imageLoadHandler.Rectangle.Height); // get the location by BackgroundPosition value var location = GetLocation(box.BackgroundPosition, rectangle, imgSize); var srcRect = imageLoadHandler.Rectangle == RRect.Empty ? new RRect(0, 0, imgSize.Width, imgSize.Height) : new RRect(imageLoadHandler.Rectangle.Left, imageLoadHandler.Rectangle.Top, imgSize.Width, imgSize.Height); // initial image destination rectangle var destRect = new RRect(location, imgSize); // need to clip so repeated image will be cut on rectangle var lRectangle = rectangle; lRectangle.Intersect(g.GetClip()); g.PushClip(lRectangle); switch (box.BackgroundRepeat) { case "no-repeat": g.DrawImage(imageLoadHandler.Image, destRect, srcRect); break; case "repeat-x": DrawRepeatX(g, imageLoadHandler, rectangle, srcRect, destRect, imgSize); break; case "repeat-y": DrawRepeatY(g, imageLoadHandler, rectangle, srcRect, destRect, imgSize); break; default: DrawRepeat(g, imageLoadHandler, rectangle, srcRect, destRect, imgSize); break; } g.PopClip(); }
/// <summary> /// Draws all the border of the box with respect to style, width, etc. /// </summary> /// <param name="g">the device to draw into</param> /// <param name="box">the box to draw borders for</param> /// <param name="rect">the bounding rectangle to draw in</param> /// <param name="isFirst">is it the first rectangle of the element</param> /// <param name="isLast">is it the last rectangle of the element</param> public static void DrawBoxBorders(RGraphics g, CssBox box, RRect rect, bool isFirst, bool isLast) { if (rect.Width > 0 && rect.Height > 0) { if (!(string.IsNullOrEmpty(box.BorderTopStyle) || box.BorderTopStyle == CssConstants.None || box.BorderTopStyle == CssConstants.Hidden) && box.ActualBorderTopWidth > 0) { DrawBorder(Border.Top, box, g, rect, isFirst, isLast); } if (isFirst && !(string.IsNullOrEmpty(box.BorderLeftStyle) || box.BorderLeftStyle == CssConstants.None || box.BorderLeftStyle == CssConstants.Hidden) && box.ActualBorderLeftWidth > 0) { DrawBorder(Border.Left, box, g, rect, true, isLast); } if (!(string.IsNullOrEmpty(box.BorderBottomStyle) || box.BorderBottomStyle == CssConstants.None || box.BorderBottomStyle == CssConstants.Hidden) && box.ActualBorderBottomWidth > 0) { DrawBorder(Border.Bottom, box, g, rect, isFirst, isLast); } if (isLast && !(string.IsNullOrEmpty(box.BorderRightStyle) || box.BorderRightStyle == CssConstants.None || box.BorderRightStyle == CssConstants.Hidden) && box.ActualBorderRightWidth > 0) { DrawBorder(Border.Right, box, g, rect, isFirst, true); } } }
/// <summary> /// On image load process is complete with image or without update the image box. /// </summary> /// <param name="image">the image loaded or null if failed</param> /// <param name="rectangle">the source rectangle to draw in the image (empty - draw everything)</param> /// <param name="async">is the callback was called async to load image call</param> private void OnLoadImageComplete(RImage image, RRect rectangle, bool async) { _imageWord.Image = image; _imageWord.ImageRectangle = rectangle; _imageLoadingComplete = true; _wordsSizeMeasured = false; if (_imageLoadingComplete && image == null) { SetErrorBorder(); } if (!HtmlContainer.AvoidImagesLateLoading || async) { var width = new CssLength(Width); var height = new CssLength(Height); var layout = (width.Number <= 0 || width.Unit != CssUnit.Pixels) || (height.Number <= 0 || height.Unit != CssUnit.Pixels); HtmlContainer.RequestRefresh(layout); } }
public override void DrawImage(RImage image, RRect destRect) { _g.DrawImage(((ImageAdapter) image).Image, 1, new Rect(0, 0, image.Width, image.Height), Util.Convert(destRect)); }
public override void DrawImage(RImage image, RRect destRect, RRect srcRect) { _g.DrawImage(((ImageAdapter) image).Image, 1, Util.Convert(srcRect), Util.Convert(destRect)); }
public override RBrush GetTextureBrush(RImage image, RRect dstRect, RPoint translateTransformLocation) { //TODO: Implement texture brush return PerspexAdapter.Instance.GetSolidBrush(Util.Convert(Colors.Magenta)); //var brush = new ImageBrush(((ImageAdapter)image).Image); //brush.Stretch = Stretch.None; //brush.TileMode = TileMode.Tile; //brush.Viewport = Utils.Convert(dstRect); //brush.ViewportUnits = BrushMappingMode.Absolute; //brush.Transform = new TranslateTransform(translateTransformLocation.X, translateTransformLocation.Y); //brush.Freeze(); //return new BrushAdapter(brush); }
/// <summary> /// Draw image failed to load icon. /// </summary> /// <param name="g">the device to draw into</param> /// <param name="htmlContainer"></param> /// <param name="r">the rectangle to draw icon in</param> public static void DrawImageErrorIcon(RGraphics g, HtmlContainerInt htmlContainer, RRect r) { g.DrawRectangle(g.GetPen(RColor.LightGray), r.Left + 2, r.Top + 2, 15, 15); var image = htmlContainer.Adapter.GetLoadingFailedImage(); g.DrawImage(image, new RRect(r.Left + 3, r.Top + 3, image.Width, image.Height)); }
/// <summary> /// Creates a rounded rectangle using the specified corner radius<br/> /// NW-----NE /// | | /// | | /// SW-----SE /// </summary> /// <param name="g">the device to draw into</param> /// <param name="rect">Rectangle to round</param> /// <param name="nwRadius">Radius of the north east corner</param> /// <param name="neRadius">Radius of the north west corner</param> /// <param name="seRadius">Radius of the south east corner</param> /// <param name="swRadius">Radius of the south west corner</param> /// <returns>GraphicsPath with the lines of the rounded rectangle ready to be painted</returns> public static RGraphicsPath GetRoundRect(RGraphics g, RRect rect, double nwRadius, double neRadius, double seRadius, double swRadius) { var path = g.GetGraphicsPath(); path.Start(rect.Left + nwRadius, rect.Top); path.LineTo(rect.Right - neRadius, rect.Y); if (neRadius > 0f) path.ArcTo(rect.Right, rect.Top + neRadius, neRadius, RGraphicsPath.Corner.TopRight); path.LineTo(rect.Right, rect.Bottom - seRadius); if (seRadius > 0f) path.ArcTo(rect.Right - seRadius, rect.Bottom, seRadius, RGraphicsPath.Corner.BottomRight); path.LineTo(rect.Left + swRadius, rect.Bottom); if (swRadius > 0f) path.ArcTo(rect.Left, rect.Bottom - swRadius, swRadius, RGraphicsPath.Corner.BottomLeft); path.LineTo(rect.Left, rect.Top + nwRadius); if (nwRadius > 0f) path.ArcTo(rect.Left + nwRadius, rect.Top, nwRadius, RGraphicsPath.Corner.TopLeft); return path; }
/// <summary> /// Paints the background of the box /// </summary> /// <param name="g">the device to draw into</param> /// <param name="rect">the bounding rectangle to draw in</param> /// <param name="isFirst">is it the first rectangle of the element</param> /// <param name="isLast">is it the last rectangle of the element</param> protected void PaintBackground(RGraphics g, RRect rect, bool isFirst, bool isLast) { if (rect.Width > 0 && rect.Height > 0) { RBrush brush = null; if (BackgroundGradient != CssConstants.None) { brush = g.GetLinearGradientBrush(rect, ActualBackgroundColor, ActualBackgroundGradient, ActualBackgroundGradientAngle); } else if (RenderUtils.IsColorVisible(ActualBackgroundColor)) { brush = g.GetSolidBrush(ActualBackgroundColor); } if (brush != null) { // TODO:a handle it correctly (tables background) // if (isLast) // rectangle.Width -= ActualWordSpacing + CssUtils.GetWordEndWhitespace(ActualFont); RGraphicsPath roundrect = null; if (IsRounded) { roundrect = RenderUtils.GetRoundRect(g, rect, ActualCornerNw, ActualCornerNe, ActualCornerSe, ActualCornerSw); } Object prevMode = null; if (HtmlContainer != null && !HtmlContainer.AvoidGeometryAntialias && IsRounded) { prevMode = g.SetAntiAliasSmoothingMode(); } if (roundrect != null) { g.DrawPath(brush, roundrect); } else { g.DrawRectangle(brush, Math.Ceiling(rect.X), Math.Ceiling(rect.Y), rect.Width, rect.Height); } g.ReturnPreviousSmoothingMode(prevMode); if (roundrect != null) roundrect.Dispose(); brush.Dispose(); } if (_imageLoadHandler != null && _imageLoadHandler.Image != null && isFirst) { BackgroundImageDrawHandler.DrawBackgroundImage(g, this, _imageLoadHandler, rect); } } }
/// <summary> /// Offsets the rectangle of the specified linebox by the specified gap, /// and goes deep for rectangles of children in that linebox. /// </summary> /// <param name="lineBox"></param> /// <param name="gap"></param> internal void OffsetRectangle(CssLineBox lineBox, double gap) { if (Rectangles.ContainsKey(lineBox)) { var r = Rectangles[lineBox]; Rectangles[lineBox] = new RRect(r.X, r.Y + gap, r.Width, r.Height); } }
/// <summary> /// On image load process complete with image request refresh for it to be painted. /// </summary> /// <param name="image">the image loaded or null if failed</param> /// <param name="rectangle">the source rectangle to draw in the image (empty - draw everything)</param> /// <param name="async">is the callback was called async to load image call</param> private void OnImageLoadComplete(RImage image, RRect rectangle, bool async) { if (image != null && async) HtmlContainer.RequestRefresh(false); }
public override void PushClip(RRect rect) { _clipStack.Push(_g.PushClip(Util.Convert(rect))); //_clipStack.Push(rect); //_g.PushClip(new RectangleGeometry(Utils.Convert(rect))); }
/// <summary> /// Get linear gradient color brush from <paramref name="color1"/> to <paramref name="color2"/>. /// </summary> /// <param name="rect">the rectangle to get the brush for</param> /// <param name="color1">the start color of the gradient</param> /// <param name="color2">the end color of the gradient</param> /// <param name="angle">the angle to move the gradient from start color to end color in the rectangle</param> /// <returns>linear gradient color brush instance</returns> public RBrush GetLinearGradientBrush(RRect rect, RColor color1, RColor color2, double angle) { return _adapter.GetLinearGradientBrush(rect, color1, color2, angle); }
/// <summary> /// Draws the specified Image at the specified location and with the specified size. /// </summary> /// <param name="image">Image to draw. </param> /// <param name="destRect">Rectangle structure that specifies the location and size of the drawn image. </param> public abstract void DrawImage(RImage image, RRect destRect);
/// <summary> /// Get TextureBrush object that uses the specified image and bounding rectangle. /// </summary> /// <param name="image">The Image object with which this TextureBrush object fills interiors.</param> /// <param name="dstRect">A Rectangle structure that represents the bounding rectangle for this TextureBrush object.</param> /// <param name="translateTransformLocation">The dimension by which to translate the transformation</param> public abstract RBrush GetTextureBrush(RImage image, RRect dstRect, RPoint translateTransformLocation);
public override void PushClipExclude(RRect rect) { _clipStack.Push(null); //TODO: Implement exclude rect, see #128 //var geometry = new CombinedGeometry(); //geometry.Geometry1 = new RectangleGeometry(Utils.Convert(_clipStack.Peek())); //geometry.Geometry2 = new RectangleGeometry(Utils.Convert(rect)); //geometry.GeometryCombineMode = GeometryCombineMode.Exclude; //_clipStack.Push(_clipStack.Peek()); //_g.PushClip(geometry); }
/// <summary> /// Draw video image over the iframe if found. /// </summary> private void DrawImage(RGraphics g, RPoint offset, RRect rect) { if (_imageWord.Image != null) { if (rect.Width > 0 && rect.Height > 0) { if (_imageWord.ImageRectangle == RRect.Empty) g.DrawImage(_imageWord.Image, rect); else g.DrawImage(_imageWord.Image, rect, _imageWord.ImageRectangle); if (_imageWord.Selected) { g.DrawRectangle(GetSelectionBackBrush(g, true), _imageWord.Left + offset.X, _imageWord.Top + offset.Y, _imageWord.Width + 2, DomUtils.GetCssLineBoxByWord(_imageWord).LineHeight); } } } else if (_isVideo && !_imageLoadingComplete) { RenderUtils.DrawImageLoadingIcon(g, HtmlContainer, rect); if (rect.Width > 19 && rect.Height > 19) { g.DrawRectangle(g.GetPen(RColor.LightGray), rect.X, rect.Y, rect.Width, rect.Height); } } }
/// <summary> /// Paint all the words in the box. /// </summary> /// <param name="g">the device to draw into</param> /// <param name="offset">the current scroll offset to offset the words</param> private void PaintWords(RGraphics g, RPoint offset) { if (Width.Length > 0) { var isRtl = Direction == CssConstants.Rtl; foreach (var word in Words) { if (!word.IsLineBreak) { var wordPoint = new RPoint(word.Left + offset.X, word.Top + offset.Y); if (word.Selected) { // handle paint selected word background and with partial word selection var wordLine = DomUtils.GetCssLineBoxByWord(word); var left = word.SelectedStartOffset > -1 ? word.SelectedStartOffset : (wordLine.Words[0] != word && word.HasSpaceBefore ? -ActualWordSpacing : 0); var padWordRight = word.HasSpaceAfter && !wordLine.IsLastSelectedWord(word); var width = word.SelectedEndOffset > -1 ? word.SelectedEndOffset : word.Width + (padWordRight ? ActualWordSpacing : 0); var rect = new RRect(word.Left + offset.X + left, word.Top + offset.Y, width - left, wordLine.LineHeight); g.DrawRectangle(GetSelectionBackBrush(g, false), rect.X, rect.Y, rect.Width, rect.Height); if (HtmlContainer.SelectionForeColor != RColor.Empty && (word.SelectedStartOffset > 0 || word.SelectedEndIndexOffset > -1)) { g.PushClipExclude(rect); g.DrawString(word.Text, ActualFont, ActualColor, wordPoint, new RSize(word.Width, word.Height), isRtl); g.PopClip(); g.PushClip(rect); g.DrawString(word.Text, ActualFont, GetSelectionForeBrush(), wordPoint, new RSize(word.Width, word.Height), isRtl); g.PopClip(); } else { g.DrawString(word.Text, ActualFont, GetSelectionForeBrush(), wordPoint, new RSize(word.Width, word.Height), isRtl); } } else { // g.DrawRectangle(HtmlContainer.Adapter.GetPen(RColor.Black), wordPoint.X, wordPoint.Y, word.Width - 1, word.Height - 1); g.DrawString(word.Text, ActualFont, ActualColor, wordPoint, new RSize(word.Width, word.Height), isRtl); } } } } }
/// <summary> /// Draw video title on top of the iframe if found. /// </summary> private void DrawTitle(RGraphics g, RRect rect) { if (_videoTitle != null && _imageWord.Width > 40 && _imageWord.Height > 40) { var font = HtmlContainer.Adapter.GetFont("Arial", 9f, RFontStyle.Regular); g.DrawRectangle(g.GetSolidBrush(RColor.FromArgb(160, 0, 0, 0)), rect.Left, rect.Top, rect.Width, ActualFont.Height + 7); var titleRect = new RRect(rect.Left + 3, rect.Top + 3, rect.Width - 6, rect.Height - 6); g.DrawString(_videoTitle, font, RColor.WhiteSmoke, titleRect.Location, RSize.Empty, false); } }
/// <summary> /// Deeply offsets the top of the box and its contents /// </summary> /// <param name="amount"></param> internal void OffsetTop(double amount) { List<CssLineBox> lines = new List<CssLineBox>(); foreach (CssLineBox line in Rectangles.Keys) lines.Add(line); foreach (CssLineBox line in lines) { RRect r = Rectangles[line]; Rectangles[line] = new RRect(r.X, r.Y + amount, r.Width, r.Height); } foreach (CssRect word in Words) { word.Top += amount; } foreach (CssBox b in Boxes) { b.OffsetTop(amount); } if (_listItemBox != null) _listItemBox.OffsetTop(amount); Location = new RPoint(Location.X, Location.Y + amount); }
/// <summary> /// Draw play over the iframe if we found link url. /// </summary> private void DrawPlay(RGraphics g, RRect rect) { if (_isVideo && _imageWord.Width > 70 && _imageWord.Height > 50) { var prevMode = g.SetAntiAliasSmoothingMode(); var size = new RSize(60, 40); var left = rect.Left + (rect.Width - size.Width) / 2; var top = rect.Top + (rect.Height - size.Height) / 2; g.DrawRectangle(g.GetSolidBrush(RColor.FromArgb(160, 0, 0, 0)), left, top, size.Width, size.Height); RPoint[] points = { new RPoint(left + size.Width / 3f + 1,top + 3 * size.Height / 4f), new RPoint(left + size.Width / 3f + 1, top + size.Height / 4f), new RPoint(left + 2 * size.Width / 3f + 1, top + size.Height / 2f) }; g.DrawPolygon(g.GetSolidBrush(RColor.White), points); g.ReturnPreviousSmoothingMode(prevMode); } }
/// <summary> /// Paints the text decoration (underline/strike-through/over-line) /// </summary> /// <param name="g">the device to draw into</param> /// <param name="rectangle"> </param> /// <param name="isFirst"> </param> /// <param name="isLast"> </param> protected void PaintDecoration(RGraphics g, RRect rectangle, bool isFirst, bool isLast) { if (string.IsNullOrEmpty(TextDecoration) || TextDecoration == CssConstants.None) return; double y = 0f; if (TextDecoration == CssConstants.Underline) { y = Math.Round(rectangle.Top + ActualFont.UnderlineOffset); } else if (TextDecoration == CssConstants.LineThrough) { y = rectangle.Top + rectangle.Height / 2f; } else if (TextDecoration == CssConstants.Overline) { y = rectangle.Top; } y -= ActualPaddingBottom - ActualBorderBottomWidth; double x1 = rectangle.X; if (isFirst) x1 += ActualPaddingLeft + ActualBorderLeftWidth; double x2 = rectangle.Right; if (isLast) x2 -= ActualPaddingRight + ActualBorderRightWidth; var pen = g.GetPen(ActualColor); pen.Width = 1; pen.DashStyle = RDashStyle.Solid; g.DrawLine(pen, x1, y, x2, y); }
/// <summary> /// On image load process is complete with image or without update the image box. /// </summary> /// <param name="image">the image loaded or null if failed</param> /// <param name="rectangle">the source rectangle to draw in the image (empty - draw everything)</param> /// <param name="async">is the callback was called async to load image call</param> private void OnLoadImageComplete(RImage image, RRect rectangle, bool async) { _imageWord.Image = image; _imageWord.ImageRectangle = rectangle; _imageLoadingComplete = true; _wordsSizeMeasured = false; if (_imageLoadingComplete && image == null) { SetErrorBorder(); } if (async) { HtmlContainer.RequestRefresh(IsLayoutRequired()); } }