// "Therefore those skilled at the unorthodox // are infinite as heaven and earth, // inexhaustible as the great rivers. // When they come to an end, // they begin again, // like the days and months; // they die and are reborn, // like the four seasons." // // - Sun Tsu, // "The Art of War" using System; using TheArtOfDev.HtmlRenderer.Adapters; using TheArtOfDev.HtmlRenderer.Adapters.Entities; using TheArtOfDev.HtmlRenderer.Core.Dom; namespace TheArtOfDev.HtmlRenderer.Core.Handlers { /// /// Contains all the paint code to paint different background images. /// internal static class BackgroundImageDrawHandler { /// /// Draw the background image of the given box in the given rectangle.
/// Handle background-repeat and background-position values. ///
/// the device to draw into /// the box to draw its background image /// the handler that loads image to draw /// the rectangle to draw image in 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(); } #region Private methods /// /// Get top-left location to start drawing the image at depending on background-position value. /// /// the background-position value /// the rectangle to position image in /// the size of the image /// the top-left location 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); } /// /// Draw the background image at the required location repeating it over the X axis.
/// Adjust location to left if starting location doesn't include all the range (adjusted to center or right). ///
private static void DrawRepeatX(RGraphics g, ImageLoadHandler imageLoadHandler, RRect rectangle, RRect srcRect, RRect destRect, RSize imgSize) { while (destRect.X > rectangle.X) destRect.X -= imgSize.Width; using (var brush = g.GetTextureBrush(imageLoadHandler.Image, srcRect, destRect.Location)) { g.DrawRectangle(brush, rectangle.X, destRect.Y, rectangle.Width, srcRect.Height); } } /// /// Draw the background image at the required location repeating it over the Y axis.
/// Adjust location to top if starting location doesn't include all the range (adjusted to center or bottom). ///
private static void DrawRepeatY(RGraphics g, ImageLoadHandler imageLoadHandler, RRect rectangle, RRect srcRect, RRect destRect, RSize imgSize) { while (destRect.Y > rectangle.Y) destRect.Y -= imgSize.Height; using (var brush = g.GetTextureBrush(imageLoadHandler.Image, srcRect, destRect.Location)) { g.DrawRectangle(brush, destRect.X, rectangle.Y, srcRect.Width, rectangle.Height); } } /// /// Draw the background image at the required location repeating it over the X and Y axis.
/// Adjust location to left-top if starting location doesn't include all the range (adjusted to center or bottom/right). ///
private static void DrawRepeat(RGraphics g, ImageLoadHandler imageLoadHandler, RRect rectangle, RRect srcRect, RRect destRect, RSize imgSize) { while (destRect.X > rectangle.X) destRect.X -= imgSize.Width; while (destRect.Y > rectangle.Y) destRect.Y -= imgSize.Height; using (var brush = g.GetTextureBrush(imageLoadHandler.Image, srcRect, destRect.Location)) { g.DrawRectangle(brush, rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); } } #endregion } }