/// <summary> /// Creates an XForm from an image brush. /// </summary> public static XForm FromImageBrush(DocumentRenderingContext context, ImageBrush brush) { XPImage xpImage = ImageBuilder.FromImageBrush(context, brush); XImage ximage = xpImage.XImage; double width = ximage.PixelWidth; double height = ximage.PixelHeight; // view box in point // XRect box = new XRect(brush.Viewbox.X * 0.75, brush.Viewbox.Y * 0.75, brush.Viewbox.Width * 0.75, brush.Viewbox.Height * 0.75); XRect box = new XRect(0, 0, width, height); XForm xform = new XForm(context.PdfDocument, box); PdfContentWriter formWriter = new PdfContentWriter(context, xform, RenderMode.Default); Debug.Assert(ximage != null); PdfFormXObject pdfForm = xform.PdfForm; pdfForm.Elements.SetMatrix(PdfFormXObject.Keys.Matrix, new XMatrix()); //formWriter.Size = brush.Viewport.Size; formWriter.BeginContentRaw(); string imageID = formWriter.Resources.AddImage(new PdfImage(context.PdfDocument, ximage)); XMatrix matrix = new XMatrix(); double scaleX = brush.Viewport.Width / brush.Viewbox.Width * 4 / 3 * ximage.PointWidth; double scaleY = brush.Viewport.Height / brush.Viewbox.Height * 4 / 3 * ximage.PointHeight; matrix.TranslatePrepend(-brush.Viewbox.X, -brush.Viewbox.Y); matrix.ScalePrepend(scaleX, scaleY); matrix.TranslatePrepend(brush.Viewport.X / scaleX, brush.Viewport.Y / scaleY); matrix = new XMatrix(width, 0, 0, -height, 0, height); formWriter.WriteLiteral("q\n"); // TODO:WriteClip(path.Data); //formWriter.WriteLiteral("{0:0.###} 0 0 -{1:0.###} {2:0.###} {3:0.###} cm 100 Tz {4} Do Q\n", // matrix.M11, matrix.M22, matrix.OffsetX, matrix.OffsetY + brush.Viewport.Height, imageID); formWriter.WriteMatrix(matrix); formWriter.WriteLiteral(imageID + " Do Q\n"); formWriter.EndContent(); return xform; }
public static void RenderTile(RenderContext ctx) { DateTime dtStart = DateTime.Now; List<Timer> timers = new List<Timer>(); if (ctx.resourceManager == null) throw new ArgumentNullException("resourceManager"); if (ctx.graphics == null) throw new ArgumentNullException("graphics"); if (ctx.selector == null) throw new ArgumentNullException("selector"); XSolidBrush solidBrush = new XSolidBrush(); XPen pen = new XPen(XColor.Empty); using (var fonts = new FontCache(ctx.styles)) { #region resources lock (s_imageInitLock) { if (ctx.styles.useBackgroundImage && s_backgroundImage == null) s_backgroundImage = XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Nebula.png")); if (ctx.styles.showRifts && s_riftImage == null) s_riftImage = new ImageHolder(Image.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Rifts.png"))); if (ctx.styles.useGalaxyImage && s_galaxyImage == null) { s_galaxyImage = new ImageHolder(Image.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Galaxy.png"))); s_galaxyImageGray = new ImageHolder(Image.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Galaxy_Gray.png"))); } if (ctx.styles.useWorldImages && s_worldImages == null) { s_worldImages = new Dictionary<string, XImage> { { "Hyd0", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Hyd0.png")) }, { "Hyd1", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Hyd1.png")) }, { "Hyd2", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Hyd2.png")) }, { "Hyd3", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Hyd3.png")) }, { "Hyd4", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Hyd4.png")) }, { "Hyd5", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Hyd5.png")) }, { "Hyd6", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Hyd6.png")) }, { "Hyd7", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Hyd7.png")) }, { "Hyd8", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Hyd8.png")) }, { "Hyd9", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Hyd9.png")) }, { "HydA", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/HydA.png")) }, { "Belt", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Belt.png")) } }; } if (ctx.silly && s_sillyImageColor == null) { // Happy face c/o http://bighappyfaces.com/ s_sillyImageColor = XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/AprilFools/Starburst.png")); s_sillyImageGray = XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/AprilFools/Starburst_Gray.png")); } } #endregion timers.Add(new Timer("preload")); ////////////////////////////////////////////////////////////// // // Image-Space Rendering // ////////////////////////////////////////////////////////////// using (Maps.Rendering.RenderUtil.SaveState(ctx.graphics)) { if (ctx.clipPath != null) { XMatrix m = ctx.ImageSpaceToWorldSpace; ctx.graphics.MultiplyTransform(m); ctx.graphics.IntersectClip(ctx.clipPath); m.Invert(); ctx.graphics.MultiplyTransform(m); } // Fill ctx.graphics.SmoothingMode = XSmoothingMode.HighSpeed; solidBrush.Color = ctx.styles.backgroundColor; ctx.graphics.DrawRectangle(solidBrush, 0, 0, ctx.tileSize.Width, ctx.tileSize.Height); //// Draw tile # //using( var font = new Font( FontFamily.GenericSansSerif, 10 ) ) //{ // graphics.DrawString( String.Format( "({0},{1})", x, y ), font, foregroundBrush, 0, 0 ); // graphics.DrawString( String.Format( "{0},{1}-{2}x{3}", tileRect.X, tileRect.Y, tileRect.Width, tileRect.Height ), font, foregroundBrush, 0, 20 ); //} // Frame it //graphics.DrawRectangle( Pens.Green, 0, 0, tileSize.Width-1, tileSize.Height-1 ); } timers.Add(new Timer("imagespace")); ////////////////////////////////////////////////////////////// // // World-Space Rendering // ////////////////////////////////////////////////////////////// // Transform from image-space to world-space. Set up a reverse transform as well. XMatrix imageSpaceToWorldSpace = ctx.ImageSpaceToWorldSpace; XMatrix worldSpaceToImageSpace = imageSpaceToWorldSpace; worldSpaceToImageSpace.Invert(); ctx.graphics.MultiplyTransform(imageSpaceToWorldSpace); using (Maps.Rendering.RenderUtil.SaveState(ctx.graphics)) { //------------------------------------------------------------ // Explicit Clipping //------------------------------------------------------------ if (ctx.clipPath != null) ctx.graphics.IntersectClip(ctx.clipPath); //ctx.styles.showPseudoRandomStars = true; //------------------------------------------------------------ // Backgrounds //------------------------------------------------------------ RectangleF galacticBounds = new RectangleF(-14598.67f, -23084.26f, 29234.1133f, 25662.4746f); // TODO: Don't hardcode Rectangle galaxyImageRect = new Rectangle(-18257, -26234, 36551, 32462); // Chosen to match T5 pp.416 // This transforms the Linehan galactic structure to the Mikesh galactic structure // See http://travellermap.blogspot.com/2009/03/galaxy-scale-mismatch.html Matrix xformLinehanToMikesh = new Matrix(0.9181034f, 0.0f, 0.0f, 0.855192542f, 120.672432f, 86.34569f); timers.Add(new Timer("prep")); //------------------------------------------------------------ // Local background (Nebula) //------------------------------------------------------------ #region nebula-background // NOTE: Since alpha texture brushes aren't supported without // creating a new image (slow!) we render the local background // first, then overlay the deep background over it, for // basically the same effect since the alphas sum to 1. if (ctx.styles.useBackgroundImage && galacticBounds.IntersectsWith(ctx.tileRect)) { // Image-space rendering, so save current context using (RenderUtil.SaveState(ctx.graphics)) { // Never fill outside the galaxy ctx.graphics.IntersectClip(galacticBounds); // Map back to image space so it scales/tiles nicely ctx.graphics.MultiplyTransform(worldSpaceToImageSpace); const float backgroundImageScale = 2.0f; lock (s_backgroundImage) { // Scaled size of the background double w = s_backgroundImage.PixelWidth * backgroundImageScale; double h = s_backgroundImage.PixelHeight * backgroundImageScale; // Offset of the background, relative to the canvas double ox = (float)(-ctx.tileRect.Left * ctx.scale * Astrometrics.ParsecScaleX) % w; double oy = (float)(-ctx.tileRect.Top * ctx.scale * Astrometrics.ParsecScaleY) % h; if (ox > 0) ox -= w; if (oy > 0) oy -= h; // Number of copies needed to cover the canvas int nx = 1 + (int)Math.Floor(ctx.tileSize.Width / w); int ny = 1 + (int)Math.Floor(ctx.tileSize.Height / h); if (ox + nx * w < ctx.tileSize.Width) nx += 1; if (oy + ny * h < ctx.tileSize.Height) ny += 1; for (int x = 0; x < nx; ++x) { for (int y = 0; y < ny; ++y) { ctx.graphics.DrawImage(s_backgroundImage, ox + x * w, oy + y * h, w + 1, h + 1); //ctx.graphics.DrawRectangle( XPens.Orange, ox + x * w, oy + y * h, w, h ); } } } } } #endregion timers.Add(new Timer("background (nebula)")); //------------------------------------------------------------ // Deep background (Galaxy) //------------------------------------------------------------ #region galaxy-background if (ctx.styles.useGalaxyImage && ctx.styles.deepBackgroundOpacity > 0f) { using (RenderUtil.SaveState(ctx.graphics)) { ctx.graphics.MultiplyTransform(xformLinehanToMikesh); ImageHolder galaxyImage = ctx.styles.lightBackground ? s_galaxyImageGray : s_galaxyImage; lock (galaxyImage) { RenderUtil.DrawImageAlpha(ctx.graphics, ctx.styles.deepBackgroundOpacity, galaxyImage, galaxyImageRect); } } } #endregion timers.Add(new Timer("background (galaxy)")); //------------------------------------------------------------ // Pseudo-Random Stars //------------------------------------------------------------ #region pseudorandom-stars if (ctx.styles.pseudoRandomStars.visible) { // Render pseudorandom stars based on the tile # and // scale factor. Note that these are positioned in // screen space, not world space. //const int nStars = 75; int nMinStars = ctx.tileSize.Width * ctx.tileSize.Height / 300; int nStars = ctx.scale >= 1 ? nMinStars : (int)(nMinStars / ctx.scale); // NOTE: For performance's sake, three different cases are considered: // (1) Tile is entirely within charted space (most common) - just render // the pseudorandom stars into the tile // (2) Tile intersects the galaxy bounds - render pseudorandom stars // into a texture, then fill the galaxy vector with it // (3) Tile is entire outside the galaxy - don't render stars using (RenderUtil.SaveState(ctx.graphics)) { ctx.graphics.SmoothingMode = XSmoothingMode.HighQuality; solidBrush.Color = ctx.styles.pseudoRandomStars.fillColor; Random rand = new Random((((int)ctx.tileRect.Left) << 8) ^ (int)ctx.tileRect.Top); for (int i = 0; i < nStars; i++) { float starX = (float)rand.NextDouble() * ctx.tileRect.Width + ctx.tileRect.X; float starY = (float)rand.NextDouble() * ctx.tileRect.Height + ctx.tileRect.Y; float d = (float)rand.NextDouble() * 2; //ctx.graphics.DrawRectangle( fonts.foregroundBrush, starX, starY, (float)( d / ctx.scale * Astrometrics.ParsecScaleX ), (float)( d / ctx.scale * Astrometrics.ParsecScaleY ) ); ctx.graphics.DrawEllipse(solidBrush, starX, starY, (float)(d / ctx.scale * Astrometrics.ParsecScaleX), (float)(d / ctx.scale * Astrometrics.ParsecScaleY)); } } } #endregion timers.Add(new Timer("pseudorandom")); //------------------------------------------------------------ // Rifts in Charted Space //------------------------------------------------------------ #region rifts if (ctx.styles.showRifts && ctx.styles.riftOpacity > 0f) { Rectangle riftImageRect; riftImageRect = new Rectangle(-1374, -827, 2769, 1754); // Correct lock (s_riftImage) { RenderUtil.DrawImageAlpha(ctx.graphics, ctx.styles.riftOpacity, s_riftImage, riftImageRect); } } #endregion timers.Add(new Timer("rifts")); //------------------------------------------------------------ // April Fool's Day //------------------------------------------------------------ #region april-fools if (ctx.silly) { using (RenderUtil.SaveState(ctx.graphics)) { // Render in image-space ctx.graphics.MultiplyTransform(worldSpaceToImageSpace); XImage sillyImage = ctx.styles.grayscale ? s_sillyImageGray : s_sillyImageColor; lock (sillyImage) { ctx.graphics.DrawImage(sillyImage, 0, 0, ctx.tileSize.Width, ctx.tileSize.Height); } } timers.Add(new Timer("silly")); } #endregion //------------------------------------------------------------ // Macro: Borders object //------------------------------------------------------------ #region macro-borders if (ctx.styles.macroBorders.visible) { ctx.styles.macroBorders.pen.Apply(ref pen); ctx.graphics.SmoothingMode = XSmoothingMode.AntiAlias; foreach (var vec in borderFiles .Select(file => ctx.resourceManager.GetXmlFileObject(file, typeof(VectorObject))) .OfType<VectorObject>() .Where(vec => (vec.MapOptions & ctx.options & MapOptions.BordersMask) != 0)) { vec.Draw(ctx.graphics, ctx.tileRect, ctx.options, pen); } } #endregion timers.Add(new Timer("macro-borders")); //------------------------------------------------------------ // Macro: Route object //------------------------------------------------------------ #region macro-routes if (ctx.styles.macroRoutes.visible) { ctx.styles.macroRoutes.pen.Apply(ref pen); ctx.graphics.SmoothingMode = XSmoothingMode.AntiAlias; foreach (var vec in routeFiles .Select(file => ctx.resourceManager.GetXmlFileObject(file, typeof(VectorObject))) .OfType<VectorObject>() .Where(vec => (vec.MapOptions & ctx.options & MapOptions.BordersMask) != 0)) { vec.Draw(ctx.graphics, ctx.tileRect, ctx.options, pen); } } #endregion timers.Add(new Timer("macro-routes")); //------------------------------------------------------------ // Sector Grid //------------------------------------------------------------ #region sector-grid ctx.graphics.SmoothingMode = XSmoothingMode.HighSpeed; if (ctx.styles.sectorGrid.visible) { const int gridSlop = 10; ctx.styles.sectorGrid.pen.Apply(ref pen); for (float h = ((float)(Math.Floor((ctx.tileRect.Left) / Astrometrics.SectorWidth) - 1) - Astrometrics.ReferenceSector.X) * Astrometrics.SectorWidth - Astrometrics.ReferenceHex.X; h <= ctx.tileRect.Right + Astrometrics.SectorWidth; h += Astrometrics.SectorWidth) ctx.graphics.DrawLine(pen, h, ctx.tileRect.Top - gridSlop, h, ctx.tileRect.Bottom + gridSlop); for (float v = ((float)(Math.Floor((ctx.tileRect.Top) / Astrometrics.SectorHeight) - 1) - Astrometrics.ReferenceSector.Y) * Astrometrics.SectorHeight - Astrometrics.ReferenceHex.Y; v <= ctx.tileRect.Bottom + Astrometrics.SectorHeight; v += Astrometrics.SectorHeight) ctx.graphics.DrawLine(pen, ctx.tileRect.Left - gridSlop, v, ctx.tileRect.Right + gridSlop, v); } #endregion timers.Add(new Timer("sector grid")); //------------------------------------------------------------ // Subsector Grid //------------------------------------------------------------ #region subsector-grid ctx.graphics.SmoothingMode = XSmoothingMode.HighSpeed; if (ctx.styles.subsectorGrid.visible) { const int gridSlop = 10; ctx.styles.subsectorGrid.pen.Apply(ref pen); int hmin = (int)Math.Floor(ctx.tileRect.Left / Astrometrics.SubsectorWidth) - 1 - Astrometrics.ReferenceSector.X, hmax = (int)Math.Ceiling((ctx.tileRect.Right + Astrometrics.SubsectorWidth + Astrometrics.ReferenceHex.X) / Astrometrics.SubsectorWidth); for (int hi = hmin; hi <= hmax; ++hi) { if (hi % 4 == 0) continue; float h = hi * Astrometrics.SubsectorWidth - Astrometrics.ReferenceHex.X; ctx.graphics.DrawLine(pen, h, ctx.tileRect.Top - gridSlop, h, ctx.tileRect.Bottom + gridSlop); } int vmin = (int)Math.Floor(ctx.tileRect.Top / Astrometrics.SubsectorHeight) - 1 - Astrometrics.ReferenceSector.Y, vmax = (int)Math.Ceiling((ctx.tileRect.Bottom + Astrometrics.SubsectorHeight + Astrometrics.ReferenceHex.Y) / Astrometrics.SubsectorHeight); for (int vi = vmin; vi <= vmax; ++vi) { if (vi % 4 == 0) continue; float v = vi * Astrometrics.SubsectorHeight - Astrometrics.ReferenceHex.Y; ctx.graphics.DrawLine(pen, ctx.tileRect.Left - gridSlop, v, ctx.tileRect.Right + gridSlop, v); } } #endregion timers.Add(new Timer("subsector grid")); //------------------------------------------------------------ // Parsec Grid //------------------------------------------------------------ #region parsec-grid // TODO: Optimize - timers indicate this is slow ctx.graphics.SmoothingMode = XSmoothingMode.HighQuality; if (ctx.styles.parsecGrid.visible) { const int parsecSlop = 1; int hx = (int)Math.Floor(ctx.tileRect.Left); int hw = (int)Math.Ceiling(ctx.tileRect.Width); int hy = (int)Math.Floor(ctx.tileRect.Top); int hh = (int)Math.Ceiling(ctx.tileRect.Height); ctx.styles.parsecGrid.pen.Apply(ref pen); switch (ctx.styles.hexStyle) { case HexStyle.Square: for (int px = hx - parsecSlop; px < hx + hw + parsecSlop; px++) { float yOffset = ((px % 2) != 0) ? 0.0f : 0.5f; for (int py = hy - parsecSlop; py < hy + hh + parsecSlop; py++) { // TODO: use RenderUtil.(Square|Hex)Edges(X|Y) arrays const float inset = 0.1f; ctx.graphics.DrawRectangle(pen, px + inset, py + inset + yOffset, 1 - inset * 2, 1 - inset * 2); } } break; case HexStyle.Hex: XPoint[] points = new XPoint[4]; for (int px = hx - parsecSlop; px < hx + hw + parsecSlop; px++) { double yOffset = ((px % 2) != 0) ? 0.0 : 0.5; for (int py = hy - parsecSlop; py < hy + hh + parsecSlop; py++) { points[0] = new XPoint(px + -RenderUtil.HEX_EDGE, py + 0.5 + yOffset); points[1] = new XPoint(px + RenderUtil.HEX_EDGE, py + 1.0 + yOffset); points[2] = new XPoint(px + 1.0 - RenderUtil.HEX_EDGE, py + 1.0 + yOffset); points[3] = new XPoint(px + 1.0 + RenderUtil.HEX_EDGE, py + 0.5 + yOffset); ctx.graphics.DrawLines(pen, points); } } break; case HexStyle.None: // none break; } if (ctx.styles.numberAllHexes && ctx.styles.worldDetails.HasFlag(WorldDetails.Hex)) { solidBrush.Color = ctx.styles.hexNumber.textColor; for (int px = hx - parsecSlop; px < hx + hw + parsecSlop; px++) { double yOffset = ((px % 2) != 0) ? 0.0 : 0.5; for (int py = hy - parsecSlop; py < hy + hh + parsecSlop; py++) { Location loc = Astrometrics.CoordinatesToLocation(px + 1, py + 1); string hex; switch (ctx.styles.hexCoordinateStyle) { default: case Stylesheet.HexCoordinateStyle.Sector: hex = loc.HexString; break; case Stylesheet.HexCoordinateStyle.Subsector: hex = loc.SubsectorHexString; break; } using (RenderUtil.SaveState(ctx.graphics)) { XMatrix matrix = new XMatrix(); matrix.TranslatePrepend(px + 0.5f, py + yOffset); matrix.ScalePrepend(ctx.styles.hexContentScale / Astrometrics.ParsecScaleX, ctx.styles.hexContentScale / Astrometrics.ParsecScaleY); ctx.graphics.MultiplyTransform(matrix, XMatrixOrder.Prepend); ctx.graphics.DrawString(hex, ctx.styles.hexNumber.Font, solidBrush, 0, 0, RenderUtil.StringFormatTopCenter); } } } } } #endregion timers.Add(new Timer("parsec grid")); //------------------------------------------------------------ // Subsector Names //------------------------------------------------------------ #region subsector-names if (ctx.styles.subsectorNames.visible) { solidBrush.Color = ctx.styles.subsectorNames.textColor; foreach (Sector sector in ctx.selector.Sectors) { for (int i = 0; i < 16; i++) { int ssx = i % 4; int ssy = i / 4; Subsector ss = sector[i]; if (ss == null || String.IsNullOrEmpty(ss.Name)) continue; Point center = sector.SubsectorCenter(i); RenderUtil.DrawLabel(ctx.graphics, ss.Name, center, ctx.styles.subsectorNames.Font, solidBrush, ctx.styles.subsectorNames.textStyle); } } } #endregion timers.Add(new Timer("subsector names")); //------------------------------------------------------------ // Micro: Borders //------------------------------------------------------------ #region micro-borders if (ctx.styles.microBorders.visible) { if (ctx.styles.fillMicroBorders) DrawMicroBorders(ctx, BorderLayer.Fill); DrawMicroBorders(ctx, BorderLayer.Stroke); } #endregion timers.Add(new Timer("micro-borders")); //------------------------------------------------------------ // Micro: Routes //------------------------------------------------------------ #region micro-routes if (ctx.styles.microRoutes.visible) DrawRoutes(ctx, fonts); #endregion timers.Add(new Timer("micro-routes")); //------------------------------------------------------------ // Sector Names //------------------------------------------------------------ #region sector-names if (ctx.styles.showSomeSectorNames || ctx.styles.showAllSectorNames) { foreach (Sector sector in ctx.selector.Sectors .Where(sector => ctx.styles.showAllSectorNames || (ctx.styles.showSomeSectorNames && sector.Selected)) .Where(sector => sector.Names.Any() || sector.Label != null)) { solidBrush.Color = ctx.styles.sectorName.textColor; string name = sector.Label ?? sector.Names[0].Text; RenderUtil.DrawLabel(ctx.graphics, name, sector.Center, ctx.styles.sectorName.Font, solidBrush, ctx.styles.sectorName.textStyle); } } #endregion timers.Add(new Timer("sector names")); //------------------------------------------------------------ // Mega: Galaxy-Scale Labels //------------------------------------------------------------ #region mega-names if (ctx.styles.megaNames.visible) { solidBrush.Color = ctx.styles.megaNames.textColor; foreach (var label in megaLabels) { using (RenderUtil.SaveState(ctx.graphics)) { XMatrix matrix = new XMatrix(); matrix.ScalePrepend(1.0f / Astrometrics.ParsecScaleX, 1.0f / Astrometrics.ParsecScaleY); matrix.TranslatePrepend(label.position.X, label.position.Y); ctx.graphics.MultiplyTransform(matrix, XMatrixOrder.Prepend); XFont font = label.minor ? ctx.styles.megaNames.SmallFont : ctx.styles.megaNames.Font; XSize size = ctx.graphics.MeasureString(label.text, font); ctx.graphics.TranslateTransform(-size.Width / 2, -size.Height / 2); // Center the text RectangleF textBounds = new RectangleF(0, 0, (float)size.Width * 1.01f, (float)size.Height * 2); // *2 or it gets cut off at high sizes XTextFormatter formatter = new XTextFormatter(ctx.graphics); formatter.Alignment = XParagraphAlignment.Center; formatter.DrawString(label.text, font, solidBrush, textBounds); } } } #endregion timers.Add(new Timer("mega names")); //------------------------------------------------------------ // Macro: Government / Rift / Route Names //------------------------------------------------------------ #region government-rift-names if (ctx.styles.macroNames.visible) { foreach (var vec in borderFiles .Select(file => ctx.resourceManager.GetXmlFileObject(file, typeof(VectorObject))) .OfType<VectorObject>() .Where(vec => (vec.MapOptions & ctx.options & MapOptions.NamesMask) != 0)) { bool major = vec.MapOptions.HasFlag(MapOptions.NamesMajor); LabelStyle labelStyle = new LabelStyle(); labelStyle.Uppercase = major; XFont font = major ? ctx.styles.macroNames.Font : ctx.styles.macroNames.SmallFont; solidBrush.Color = major ? ctx.styles.macroNames.textColor : ctx.styles.macroNames.textHighlightColor; vec.DrawName(ctx.graphics, ctx.tileRect, ctx.options, font, solidBrush, labelStyle); } foreach (var vec in riftFiles .Select(file => ctx.resourceManager.GetXmlFileObject(file, typeof(VectorObject))) .OfType<VectorObject>() .Where(vec => (vec.MapOptions & ctx.options & MapOptions.NamesMask) != 0)) { bool major = vec.MapOptions.HasFlag(MapOptions.NamesMajor); LabelStyle labelStyle = new LabelStyle(); labelStyle.Rotation = 35; labelStyle.Uppercase = major; XFont font = major ? ctx.styles.macroNames.Font : ctx.styles.macroNames.SmallFont; solidBrush.Color = major ? ctx.styles.macroNames.textColor : ctx.styles.macroNames.textHighlightColor; vec.DrawName(ctx.graphics, ctx.tileRect, ctx.options, font, solidBrush, labelStyle); } if (ctx.styles.macroRoutes.visible) { foreach (var vec in routeFiles .Select(file => ctx.resourceManager.GetXmlFileObject(file, typeof(VectorObject))) .OfType<VectorObject>() .Where(vec => (vec.MapOptions & ctx.options & MapOptions.NamesMask) != 0)) { bool major = vec.MapOptions.HasFlag(MapOptions.NamesMajor); LabelStyle labelStyle = new LabelStyle(); labelStyle.Uppercase = major; XFont font = major ? ctx.styles.macroNames.Font : ctx.styles.macroNames.SmallFont; solidBrush.Color = major ? ctx.styles.macroRoutes.textColor : ctx.styles.macroRoutes.textHighlightColor; vec.DrawName(ctx.graphics, ctx.tileRect, ctx.options, font, solidBrush, labelStyle); } } if (ctx.options.HasFlag(MapOptions.NamesMinor)) { XFont font = ctx.styles.macroNames.MediumFont; solidBrush.Color = ctx.styles.macroRoutes.textHighlightColor; foreach (var label in labels) { using (RenderUtil.SaveState(ctx.graphics)) { XMatrix matrix = new XMatrix(); matrix.ScalePrepend(1.0f / Astrometrics.ParsecScaleX, 1.0f / Astrometrics.ParsecScaleY); matrix.TranslatePrepend(label.position.X, label.position.Y); ctx.graphics.MultiplyTransform(matrix, XMatrixOrder.Prepend); XSize size = ctx.graphics.MeasureString(label.text, font); ctx.graphics.TranslateTransform(-size.Width / 2, -size.Height / 2); // Center the text RectangleF textBounds = new RectangleF(0, 0, (float)size.Width, (float)size.Height * 2); // *2 or it gets cut off at high sizes XTextFormatter formatter = new XTextFormatter(ctx.graphics); formatter.Alignment = XParagraphAlignment.Center; formatter.DrawString(label.text, font, solidBrush, textBounds); } } } } #endregion timers.Add(new Timer("macro names")); //------------------------------------------------------------ // Macro: Capitals & Home Worlds //------------------------------------------------------------ #region capitals-homeworlds if (ctx.styles.capitals.visible && (ctx.options & MapOptions.WorldsMask) != 0) { WorldObjectCollection worlds = ctx.resourceManager.GetXmlFileObject(@"~/res/Worlds.xml", typeof(WorldObjectCollection)) as WorldObjectCollection; if (worlds != null) { solidBrush.Color = ctx.styles.capitals.textColor; foreach (WorldObject world in worlds.Worlds.Where(world => (world.MapOptions & ctx.options) != 0)) { world.Paint(ctx.graphics, ctx.tileRect, ctx.options, ctx.styles.capitals.fillColor, solidBrush, ctx.styles.macroNames.SmallFont); } } } #endregion timers.Add(new Timer("macro worlds")); //------------------------------------------------------------ // Micro: Border Labels & Explicit Labels //------------------------------------------------------------ #region micro-border-labels if (ctx.styles.showMicroNames) DrawLabels(ctx, fonts); #endregion timers.Add(new Timer("micro-border labels")); } // End of clipping, so world names are not clipped in jumpmaps. //------------------------------------------------------------ // Worlds //------------------------------------------------------------ #region worlds if (ctx.styles.worlds.visible) { // TODO: selector may be expensive foreach (World world in ctx.selector.Worlds) { DrawWorld(ctx, fonts, world, WorldLayer.Background); } foreach (World world in ctx.selector.Worlds) { DrawWorld(ctx, fonts, world, WorldLayer.Foreground); } } #endregion timers.Add(new Timer("worlds")); //------------------------------------------------------------ // Unofficial //------------------------------------------------------------ #region unofficial if (ctx.styles.dimUnofficialSectors && ctx.styles.worlds.visible) { solidBrush.Color = Color.FromArgb(128, ctx.styles.backgroundColor); foreach (Sector sector in ctx.selector.Sectors .Where(sector => !sector.Tags.Contains("Official") && !sector.Tags.Contains("Preserve") && !sector.Tags.Contains("InReview"))) ctx.graphics.DrawRectangle(solidBrush, sector.Bounds); } #endregion #if SHOW_TIMING using( RenderUtil.SaveState( ctx.graphics ) ) { XFont font = new XFont( FontFamily.GenericSansSerif, 12, XFontStyle.Regular, new XPdfFontOptions(PdfSharp.Pdf.PdfFontEncoding.Unicode) ); ctx.graphics.MultiplyTransform( worldSpaceToImageSpace ); double cursorX = 20.0, cursorY = 20.0; DateTime last = dtStart; foreach( Timer s in timers ) { TimeSpan ts = s.dt - last; last = s.dt; for( int dx = -1; dx <= 1; ++dx ) { for( int dy = -1; dy <= 1; ++dy ) { ctx.graphics.DrawString( String.Format( "{0} {1}", Math.Round( ts.TotalMilliseconds ), s.label ), font, XBrushes.Black, cursorX + dx, cursorY + dy ); } } ctx.graphics.DrawString( String.Format("{0} {1}", Math.Round(ts.TotalMilliseconds), s.label), font, XBrushes.Yellow, cursorX, cursorY ); cursorY += 14; } } #endif } }
// #region Realizing graphical state /// <summary> /// Initializes the default view transformation, i.e. the transformation from the user page /// space to the PDF page space. /// </summary> public void BeginContent(bool hacks4softmask) { if (!this.contentInitialized) { //this.defaultViewMatrix = XMatrix.Identity; //// Take TrimBox into account double pageHeight = Size.Height; //XPoint trimOffset = new XPoint(); //if (this.page != null && this.page.TrimMargins.AreSet) //{ // pageHeight += this.page.TrimMargins.Top.Point + this.page.TrimMargins.Bottom.Point; // trimOffset = new XPoint(this.page.TrimMargins.Left.Point, this.page.TrimMargins.Top.Point); //} //if (this.page != null && this.page.Elements.GetInteger("/Rotate") == 90) // HACK for InDesign flyer //{ // defaultViewMatrix.RotatePrepend(90); // defaultViewMatrix.ScalePrepend(1, -1); //} //else //{ // // Recall that the value of Height depends on Orientation. // Flip page horizontaly and mirror text. XMatrix defaultViewMatrix = new XMatrix(); if (!hacks4softmask) { defaultViewMatrix.TranslatePrepend(0, pageHeight); defaultViewMatrix.ScalePrepend(0.75, -0.75); } //if (!trimOffset.IsEmpty) //{ // Debug.Assert(this.gfx.PageUnit == XGraphicsUnit.Point, "With TrimMargins set the page units must be Point. Ohter cases nyi."); // defaultViewMatrix.TranslatePrepend(trimOffset.x, trimOffset.y); //} // Save initial graphic state WriteSaveState("BeginContent", null); // Set page transformation WriteRenderTransform(defaultViewMatrix); this.graphicsState.DefaultPageTransform = defaultViewMatrix; MultiplyTransform(defaultViewMatrix); if (!hacks4softmask) WriteLiteral("-100 Tz\n"); this.contentInitialized = true; } }
/// <summary> /// Draws the vertical Y axis. /// </summary> internal override void Draw() { AxisRendererInfo yari = ((ChartRendererInfo)this.rendererParms.RendererInfo).yAxisRendererInfo; double yMin = yari.MinimumScale; double yMax = yari.MaximumScale; double yMajorTick = yari.MajorTick; double yMinorTick = yari.MinorTick; XMatrix matrix = new XMatrix(); //XMatrix.Identity; matrix.TranslatePrepend(-yari.InnerRect.X, yMax); matrix.Scale(1, yari.InnerRect.Height / (yMax - yMin), XMatrixOrder.Append); matrix.ScalePrepend(1, -1); // mirror horizontal matrix.Translate(yari.InnerRect.X, yari.InnerRect.Y, XMatrixOrder.Append); // Draw axis. // First draw tick marks, second draw axis. double majorTickMarkStart = 0, majorTickMarkEnd = 0, minorTickMarkStart = 0, minorTickMarkEnd = 0; GetTickMarkPos(yari, ref majorTickMarkStart, ref majorTickMarkEnd, ref minorTickMarkStart, ref minorTickMarkEnd); XGraphics gfx = this.rendererParms.Graphics; LineFormatRenderer lineFormatRenderer = new LineFormatRenderer(gfx, yari.LineFormat); LineFormatRenderer minorTickMarkLineFormat = new LineFormatRenderer(gfx, yari.MinorTickMarkLineFormat); LineFormatRenderer majorTickMarkLineFormat = new LineFormatRenderer(gfx, yari.MajorTickMarkLineFormat); XPoint[] points = new XPoint[2]; // Draw minor tick marks. if (yari.MinorTickMark != TickMarkType.None) { for (double y = yMin + yMinorTick; y < yMax; y += yMinorTick) { points[0].X = minorTickMarkStart; points[0].Y = y; points[1].X = minorTickMarkEnd; points[1].Y = y; matrix.TransformPoints(points); minorTickMarkLineFormat.DrawLine(points[0], points[1]); } } double lineSpace = yari.TickLabelsFont.GetHeight(gfx); int cellSpace = yari.TickLabelsFont.FontFamily.GetLineSpacing(yari.TickLabelsFont.Style); double xHeight = yari.TickLabelsFont.Metrics.XHeight; XSize labelSize = new XSize(0, 0); labelSize.Height = lineSpace * xHeight / cellSpace; int countTickLabels = (int)((yMax - yMin) / yMajorTick) + 1; for (int i = 0; i < countTickLabels; ++i) { double y = yMin + yMajorTick * i; string str = y.ToString(yari.TickLabelsFormat); labelSize.Width = gfx.MeasureString(str, yari.TickLabelsFont).Width; // Draw major tick marks. if (yari.MajorTickMark != TickMarkType.None) { labelSize.Width += yari.MajorTickMarkWidth * 1.5; points[0].X = majorTickMarkStart; points[0].Y = y; points[1].X = majorTickMarkEnd; points[1].Y = y; matrix.TransformPoints(points); majorTickMarkLineFormat.DrawLine(points[0], points[1]); } else labelSize.Width += SpaceBetweenLabelAndTickmark; // Draw label text. XPoint[] layoutText = new XPoint[1]; layoutText[0].X = yari.InnerRect.X + yari.InnerRect.Width - labelSize.Width; layoutText[0].Y = y; matrix.TransformPoints(layoutText); layoutText[0].Y += labelSize.Height / 2; // Center text vertically. gfx.DrawString(str, yari.TickLabelsFont, yari.TickLabelsBrush, layoutText[0]); } // Draw axis. if (yari.LineFormat != null && yari.LineFormat.Width > 0) { points[0].X = yari.InnerRect.X + yari.InnerRect.Width; points[0].Y = yMin; points[1].X = yari.InnerRect.X + yari.InnerRect.Width; points[1].Y = yMax; matrix.TransformPoints(points); if (yari.MajorTickMark != TickMarkType.None) { // yMax is at the upper side of the axis points[1].Y -= yari.LineFormat.Width / 2; points[0].Y += yari.LineFormat.Width / 2; } lineFormatRenderer.DrawLine(points[0], points[1]); } // Draw axis title if (yari.axisTitleRendererInfo != null && yari.axisTitleRendererInfo.AxisTitleText != "") { RendererParameters parms = new RendererParameters(); parms.Graphics = gfx; parms.RendererInfo = yari; double width = yari.axisTitleRendererInfo.Width; yari.axisTitleRendererInfo.Rect = yari.InnerRect; yari.axisTitleRendererInfo.Width = width; AxisTitleRenderer atr = new AxisTitleRenderer(parms); atr.Draw(); } }
public void Fill(XGraphics graphics, RectangleF rect, Brush fillBrush) { if (graphics == null) throw new ArgumentNullException("graphics"); RectangleF bounds = TransformedBounds; if (bounds.IntersectsWith(rect)) { XGraphicsPath path = Path; using (RenderUtil.SaveState(graphics)) { XMatrix matrix = new XMatrix(); matrix.ScalePrepend(ScaleX, ScaleY); matrix.TranslatePrepend(-OriginX, -OriginY); graphics.MultiplyTransform(matrix); graphics.DrawPath(fillBrush, path); } } }
private void DrawStars(World world) { using (RenderUtil.SaveState(graphics)) { graphics.SmoothingMode = XSmoothingMode.AntiAlias; PointF center = Astrometrics.HexToCenter(world.Coordinates); XMatrix matrix = new XMatrix(); matrix.TranslatePrepend(center.X, center.Y); matrix.ScalePrepend(styles.hexContentScale / Astrometrics.ParsecScaleX, styles.hexContentScale / Astrometrics.ParsecScaleY); graphics.MultiplyTransform(matrix, XMatrixOrder.Prepend); // TODO: Proper stellar parsing List<string> ss = new List<string>(); foreach (Match m in STELLAR_REGEX.Matches(world.Stellar)) { ss.Add(m.Value); } int i = 0; foreach (var props in ss.Select(s => StellarRendering.star2props(s)).OrderByDescending(p => p.radius)) { solidBrush.Color = props.color; PointF offset = StellarRendering.Offset(i++); const float offsetScale = 0.3f; float r = 0.15f * props.radius; graphics.DrawEllipse(solidBrush, offset.X * offsetScale - r, offset.Y * offsetScale - r, r*2, r*2); } } }
/// <summary> /// Internal setup. /// </summary> void Initialize() { this.pageOrigin = new XPoint(); XMatrix matrix = new XMatrix(); //XMatrix.Identity; double pageHeight = this.pageSize.height; PdfPage targetPage = PdfPage; XPoint trimOffset = new XPoint(); if (targetPage != null && targetPage.TrimMargins.AreSet) { pageHeight += targetPage.TrimMargins.Top.Point + targetPage.TrimMargins.Bottom.Point; trimOffset = new XPoint(targetPage.TrimMargins.Left.Point, targetPage.TrimMargins.Top.Point); } #if GDI if (this.targetContext == XGraphicTargetContext.GDI) { if (this.gfx != null) matrix = (XMatrix)gfx.Transform; if (this.pageUnit != XGraphicsUnit.Point) { switch (this.pageUnit) { case XGraphicsUnit.Inch: matrix.ScalePrepend(XUnit.InchFactor); break; case XGraphicsUnit.Millimeter: matrix.ScalePrepend(XUnit.MillimeterFactor); break; case XGraphicsUnit.Centimeter: matrix.ScalePrepend(XUnit.CentimeterFactor); break; case XGraphicsUnit.Presentation: matrix.ScalePrepend(XUnit.PresentationFactor); break; } } } #endif #if WPF if (this.targetContext == XGraphicTargetContext.WPF) { if (this.pageUnit != XGraphicsUnit.Presentation) { switch (this.pageUnit) { case XGraphicsUnit.Point: matrix.ScalePrepend(XUnit.PointFactorWpf); break; case XGraphicsUnit.Inch: matrix.ScalePrepend(XUnit.InchFactorWpf); break; case XGraphicsUnit.Millimeter: matrix.ScalePrepend(XUnit.MillimeterFactorWpf); break; case XGraphicsUnit.Centimeter: matrix.ScalePrepend(XUnit.CentimeterFactorWpf); break; } if (!matrix.IsIdentity) { #if !SILVERLIGHT MatrixTransform transform = new MatrixTransform((System.Windows.Media.Matrix)matrix); dc.PushTransform(transform); #else MatrixTransform transform2 = new MatrixTransform(); transform2.Matrix = (System.Windows.Media.Matrix)matrix; dc.PushTransform(transform2); #endif } } } #endif if (this.pageDirection == XPageDirection.Upwards) matrix.Prepend(new XMatrix(1, 0, 0, -1, 0, pageHeight)); if (trimOffset != new XPoint()) matrix.TranslatePrepend(trimOffset.x, trimOffset.y); this.defaultViewMatrix = matrix; this.transform = new XMatrix(); //XMatrix.Identity; }
//public Region Clip { get; set; } //public RectangleF ClipBounds { get; } //public CompositingMode CompositingMode { get; set; } //public CompositingQuality CompositingQuality { get; set; } //public float DpiX { get; } //public float DpiY { get; } //public InterpolationMode InterpolationMode { get; set; } //public bool IsClipEmpty { get; } //public bool IsVisibleClipEmpty { get; } //public float PageScale { get; set; } //public GraphicsUnit PageUnit { get; set; } //public PixelOffsetMode PixelOffsetMode { get; set; } //public Point RenderingOrigin { get; set; } //public SmoothingMode SmoothingMode { get; set; } //public int TextContrast { get; set; } //public TextRenderingHint TextRenderingHint { get; set; } //public Matrix Transform { get; set; } //public RectangleF VisibleClipBounds { get; } #endregion // -------------------------------------------------------------------------------------------- #region Transformation /// <summary> /// Applies the specified translation operation to the transformation matrix of this object by /// prepending it to the object's transformation matrix. /// </summary> public void TranslateTransform(double dx, double dy) { XMatrix matrix = new XMatrix(); //XMatrix.Identity; matrix.TranslatePrepend(dx, dy); AddTransform(matrix, XMatrixOrder.Prepend); }
internal PdfFormXObject(PdfDocument thisDocument, PdfImportedObjectTable importedObjectTable, XPdfForm form) : base(thisDocument) { Debug.Assert(Object.ReferenceEquals(thisDocument, importedObjectTable.Owner)); Elements.SetName(Keys.Type, "/XObject"); Elements.SetName(Keys.Subtype, "/Form"); if (form.IsTemplate) { Debug.Assert(importedObjectTable == null); // TODO more initialization here??? return; } Debug.Assert(importedObjectTable != null); XPdfForm pdfForm = (XPdfForm)form; // Get import page PdfPages importPages = importedObjectTable.ExternalDocument.Pages; if (pdfForm.PageNumber < 1 || pdfForm.PageNumber > importPages.Count) PSSR.ImportPageNumberOutOfRange(pdfForm.PageNumber, importPages.Count, form.path); PdfPage importPage = importPages[pdfForm.PageNumber - 1]; // Import resources PdfItem res = importPage.Elements["/Resources"]; if (res != null) // unlikely but possible { #if true // Get root object PdfObject root; if (res is PdfReference) root = ((PdfReference)res).Value; else root = (PdfDictionary)res; root = ImportClosure(importedObjectTable, thisDocument, root); // If the root was a direct object, make it indirect. if (root.Reference == null) thisDocument.irefTable.Add(root); Debug.Assert(root.Reference != null); Elements["/Resources"] = root.Reference; #else // Get transitive closure PdfObject[] resources = importPage.Owner.Internals.GetClosure(resourcesRoot); int count = resources.Length; #if DEBUG_ for (int idx = 0; idx < count; idx++) { Debug.Assert(resources[idx].XRef != null); Debug.Assert(resources[idx].XRef.Document != null); Debug.Assert(resources[idx].Document != null); if (resources[idx].ObjectID.ObjectNumber == 12) GetType(); } #endif // 1st step. Already imported objects are reused and new ones are cloned. for (int idx = 0; idx < count; idx++) { PdfObject obj = resources[idx]; if (importedObjectTable.Contains(obj.ObjectID)) { // external object was already imported PdfReference iref = importedObjectTable[obj.ObjectID]; Debug.Assert(iref != null); Debug.Assert(iref.Value != null); Debug.Assert(iref.Document == this.Owner); // replace external object by the already clone counterpart resources[idx] = iref.Value; } else { // External object was not imported ealier and must be cloned PdfObject clone = obj.Clone(); Debug.Assert(clone.Reference == null); clone.Document = this.Owner; if (obj.Reference != null) { // add it to this (the importer) document this.Owner.irefTable.Add(clone); Debug.Assert(clone.Reference != null); // save old object identifier importedObjectTable.Add(obj.ObjectID, clone.Reference); //Debug.WriteLine("Cloned: " + obj.ObjectID.ToString()); } else { // The root object (the /Resources value) is not an indirect object Debug.Assert(idx == 0); // add it to this (the importer) document this.Owner.irefTable.Add(clone); Debug.Assert(clone.Reference != null); } // replace external object by its clone resources[idx] = clone; } } #if DEBUG_ for (int idx = 0; idx < count; idx++) { Debug.Assert(resources[idx].XRef != null); Debug.Assert(resources[idx].XRef.Document != null); Debug.Assert(resources[idx].Document != null); if (resources[idx].ObjectID.ObjectNumber == 12) GetType(); } #endif // 2nd step. Fix up indirect references that still refers to the import document. for (int idx = 0; idx < count; idx++) { PdfObject obj = resources[idx]; Debug.Assert(obj.Owner != null); FixUpObject(importedObjectTable, importedObjectTable.Owner, obj); } // Set resources key to the root of the clones Elements["/Resources"] = resources[0].Reference; #endif } // Take /Rotate into account PdfRectangle rect = importPage.Elements.GetRectangle(PdfPage.Keys.MediaBox); int rotate = importPage.Elements.GetInteger(PdfPage.Keys.Rotate); //rotate = 0; if (rotate == 0) { // Set bounding box to media box this.Elements["/BBox"] = rect; } else { // TODO: Have to adjust bounding box? (I think not, but I'm not sure -> wait for problem) this.Elements["/BBox"] = rect; // Rotate the image such that it is upright XMatrix matrix = new XMatrix(); //XMatrix.Identity; double width = rect.Width; double height = rect.Height; matrix.RotateAtPrepend(-rotate, new XPoint(width / 2, height / 2)); // Translate the image such that its center lies on the center of the rotated bounding box double offset = (height - width) / 2; if (height > width) matrix.TranslatePrepend(offset, offset); else matrix.TranslatePrepend(-offset, -offset); //string item = "[" + PdfEncoders.ToString(matrix) + "]"; //Elements[Keys.Matrix] = new PdfLiteral(item); Elements.SetMatrix(Keys.Matrix, matrix); } // Preserve filter because the content keeps unmodified PdfContent content = importPage.Contents.CreateSingleContent(); #if !DEBUG content.Compressed = true; #endif PdfItem filter = content.Elements["/Filter"]; if (filter != null) this.Elements["/Filter"] = filter.Clone(); // (no cloning needed because the bytes keep untouched) this.Stream = content.Stream; // new PdfStream(bytes, this); Elements.SetInteger("/Length", content.Stream.Value.Length); }
private void DrawParsecGrid() { const int parsecSlop = 1; int hx = (int)Math.Floor(tileRect.Left); int hw = (int)Math.Ceiling(tileRect.Width); int hy = (int)Math.Floor(tileRect.Top); int hh = (int)Math.Ceiling(tileRect.Height); styles.parsecGrid.pen.Apply(ref pen); switch (styles.hexStyle) { case HexStyle.Square: for (int px = hx - parsecSlop; px < hx + hw + parsecSlop; px++) { float yOffset = ((px % 2) != 0) ? 0.0f : 0.5f; for (int py = hy - parsecSlop; py < hy + hh + parsecSlop; py++) { // TODO: use RenderUtil.(Square|Hex)Edges(X|Y) arrays const float inset = 0.1f; graphics.DrawRectangle(pen, px + inset, py + inset + yOffset, 1 - inset * 2, 1 - inset * 2); } } break; case HexStyle.Hex: XPoint[] points = new XPoint[4]; for (int px = hx - parsecSlop; px < hx + hw + parsecSlop; px++) { double yOffset = ((px % 2) != 0) ? 0.0 : 0.5; for (int py = hy - parsecSlop; py < hy + hh + parsecSlop; py++) { points[0] = new XPoint(px + -RenderUtil.HEX_EDGE, py + 0.5 + yOffset); points[1] = new XPoint(px + RenderUtil.HEX_EDGE, py + 1.0 + yOffset); points[2] = new XPoint(px + 1.0 - RenderUtil.HEX_EDGE, py + 1.0 + yOffset); points[3] = new XPoint(px + 1.0 + RenderUtil.HEX_EDGE, py + 0.5 + yOffset); graphics.DrawLines(pen, points); } } break; case HexStyle.None: // none break; } if (styles.numberAllHexes && styles.worldDetails.HasFlag(WorldDetails.Hex)) { solidBrush.Color = styles.hexNumber.textColor; for (int px = hx - parsecSlop; px < hx + hw + parsecSlop; px++) { double yOffset = ((px % 2) != 0) ? 0.0 : 0.5; for (int py = hy - parsecSlop; py < hy + hh + parsecSlop; py++) { Location loc = Astrometrics.CoordinatesToLocation(px + 1, py + 1); string hex; switch (styles.hexCoordinateStyle) { default: case Stylesheet.HexCoordinateStyle.Sector: hex = loc.HexString; break; case Stylesheet.HexCoordinateStyle.Subsector: hex = loc.SubsectorHexString; break; } using (RenderUtil.SaveState(graphics)) { XMatrix matrix = new XMatrix(); matrix.TranslatePrepend(px + 0.5f, py + yOffset); matrix.ScalePrepend(styles.hexContentScale / Astrometrics.ParsecScaleX, styles.hexContentScale / Astrometrics.ParsecScaleY); graphics.MultiplyTransform(matrix, XMatrixOrder.Prepend); graphics.DrawString(hex, styles.hexNumber.Font, solidBrush, 0, 0, RenderUtil.StringFormatTopCenter); } } } } }
private void DrawMacroNames() { foreach (var vec in borderFiles .Select(file => resourceManager.GetXmlFileObject(file, typeof(VectorObject))) .OfType<VectorObject>() .Where(vec => (vec.MapOptions & options & MapOptions.NamesMask) != 0)) { bool major = vec.MapOptions.HasFlag(MapOptions.NamesMajor); LabelStyle labelStyle = new LabelStyle(); labelStyle.Uppercase = major; XFont font = major ? styles.macroNames.Font : styles.macroNames.SmallFont; solidBrush.Color = major ? styles.macroNames.textColor : styles.macroNames.textHighlightColor; vec.DrawName(graphics, tileRect, font, solidBrush, labelStyle); } foreach (var vec in riftFiles .Select(file => resourceManager.GetXmlFileObject(file, typeof(VectorObject))) .OfType<VectorObject>() .Where(vec => (vec.MapOptions & options & MapOptions.NamesMask) != 0)) { bool major = vec.MapOptions.HasFlag(MapOptions.NamesMajor); LabelStyle labelStyle = new LabelStyle(); labelStyle.Rotation = 35; labelStyle.Uppercase = major; XFont font = major ? styles.macroNames.Font : styles.macroNames.SmallFont; solidBrush.Color = major ? styles.macroNames.textColor : styles.macroNames.textHighlightColor; vec.DrawName(graphics, tileRect, font, solidBrush, labelStyle); } if (styles.macroRoutes.visible) { foreach (var vec in routeFiles .Select(file => resourceManager.GetXmlFileObject(file, typeof(VectorObject))) .OfType<VectorObject>() .Where(vec => (vec.MapOptions & options & MapOptions.NamesMask) != 0)) { bool major = vec.MapOptions.HasFlag(MapOptions.NamesMajor); LabelStyle labelStyle = new LabelStyle(); labelStyle.Uppercase = major; XFont font = major ? styles.macroNames.Font : styles.macroNames.SmallFont; solidBrush.Color = major ? styles.macroRoutes.textColor : styles.macroRoutes.textHighlightColor; vec.DrawName(graphics, tileRect, font, solidBrush, labelStyle); } } if (options.HasFlag(MapOptions.NamesMinor)) { XFont font = styles.macroNames.MediumFont; solidBrush.Color = styles.macroRoutes.textHighlightColor; foreach (var label in labels) { using (RenderUtil.SaveState(graphics)) { XMatrix matrix = new XMatrix(); matrix.ScalePrepend(1.0f / Astrometrics.ParsecScaleX, 1.0f / Astrometrics.ParsecScaleY); matrix.TranslatePrepend(label.position.X, label.position.Y); graphics.MultiplyTransform(matrix, XMatrixOrder.Prepend); XSize size = graphics.MeasureString(label.text, font); graphics.TranslateTransform(-size.Width / 2, -size.Height / 2); // Center the text RectangleF textBounds = new RectangleF(0, 0, (float)size.Width, (float)size.Height * 2); // *2 or it gets cut off at high sizes XTextFormatter formatter = new XTextFormatter(graphics); formatter.Alignment = XParagraphAlignment.Center; formatter.DrawString(label.text, font, solidBrush, textBounds); } } } }
public void Render(XGraphics graphics) { this.graphics = graphics; solidBrush = new XSolidBrush(); pen = new XPen(XColor.Empty); List<Timer> timers = new List<Timer>(); using (var fonts = new FontCache(styles)) { #region resources lock (s_imageInitLock) { if (styles.showNebulaBackground && s_nebulaImage == null) s_nebulaImage = XImage.FromFile(resourceManager.Server.MapPath(@"~/res/Candy/Nebula.png")); if (styles.showRiftOverlay && s_riftImage == null) s_riftImage = new ImageHolder(Image.FromFile(resourceManager.Server.MapPath(@"~/res/Candy/Rifts.png"))); if (styles.showGalaxyBackground && s_galaxyImage == null) { // TODO: Don't load both unless necessary s_galaxyImage = new ImageHolder(Image.FromFile(resourceManager.Server.MapPath(@"~/res/Candy/Galaxy.png"))); s_galaxyImageGray = new ImageHolder(Image.FromFile(resourceManager.Server.MapPath(@"~/res/Candy/Galaxy_Gray.png"))); } if (styles.useWorldImages && s_worldImages == null) { s_worldImages = new Dictionary<string, XImage> { { "Hyd0", XImage.FromFile(resourceManager.Server.MapPath(@"~/res/Candy/Hyd0.png")) }, { "Hyd1", XImage.FromFile(resourceManager.Server.MapPath(@"~/res/Candy/Hyd1.png")) }, { "Hyd2", XImage.FromFile(resourceManager.Server.MapPath(@"~/res/Candy/Hyd2.png")) }, { "Hyd3", XImage.FromFile(resourceManager.Server.MapPath(@"~/res/Candy/Hyd3.png")) }, { "Hyd4", XImage.FromFile(resourceManager.Server.MapPath(@"~/res/Candy/Hyd4.png")) }, { "Hyd5", XImage.FromFile(resourceManager.Server.MapPath(@"~/res/Candy/Hyd5.png")) }, { "Hyd6", XImage.FromFile(resourceManager.Server.MapPath(@"~/res/Candy/Hyd6.png")) }, { "Hyd7", XImage.FromFile(resourceManager.Server.MapPath(@"~/res/Candy/Hyd7.png")) }, { "Hyd8", XImage.FromFile(resourceManager.Server.MapPath(@"~/res/Candy/Hyd8.png")) }, { "Hyd9", XImage.FromFile(resourceManager.Server.MapPath(@"~/res/Candy/Hyd9.png")) }, { "HydA", XImage.FromFile(resourceManager.Server.MapPath(@"~/res/Candy/HydA.png")) }, { "Belt", XImage.FromFile(resourceManager.Server.MapPath(@"~/res/Candy/Belt.png")) } }; } if (Silly && s_sillyImageColor == null) { // Happy face c/o http://bighappyfaces.com/ s_sillyImageColor = XImage.FromFile(resourceManager.Server.MapPath(@"~/res/AprilFools/Starburst.png")); s_sillyImageGray = XImage.FromFile(resourceManager.Server.MapPath(@"~/res/AprilFools/Starburst_Gray.png")); } } #endregion timers.Add(new Timer("preload")); ////////////////////////////////////////////////////////////// // // Image-Space Rendering // ////////////////////////////////////////////////////////////// using (RenderUtil.SaveState(graphics)) { if (ClipPath != null) { graphics.MultiplyTransform(imageSpaceToWorldSpace); graphics.IntersectClip(ClipPath); graphics.MultiplyTransform(worldSpaceToImageSpace); } // Fill graphics.SmoothingMode = XSmoothingMode.HighSpeed; solidBrush.Color = styles.backgroundColor; graphics.DrawRectangle(solidBrush, 0, 0, tileSize.Width, tileSize.Height); } timers.Add(new Timer("imagespace")); ////////////////////////////////////////////////////////////// // // World-Space Rendering // ////////////////////////////////////////////////////////////// graphics.MultiplyTransform(imageSpaceToWorldSpace); using (RenderUtil.SaveState(graphics)) { //------------------------------------------------------------ // Explicit Clipping //------------------------------------------------------------ if (ClipPath != null) graphics.IntersectClip(ClipPath); //------------------------------------------------------------ // Background //------------------------------------------------------------ timers.Add(new Timer("prep")); #region nebula-background //------------------------------------------------------------ // Local background (Nebula) //------------------------------------------------------------ // NOTE: Since alpha texture brushes aren't supported without // creating a new image (slow!) we render the local background // first, then overlay the deep background over it, for // basically the same effect since the alphas sum to 1. if (styles.showNebulaBackground) DrawNebulaBackground(); timers.Add(new Timer("background (nebula)")); #endregion #region galaxy-background //------------------------------------------------------------ // Deep background (Galaxy) //------------------------------------------------------------ if (styles.showGalaxyBackground && styles.deepBackgroundOpacity > 0f && galacticBounds.IntersectsWith(tileRect)) { using (RenderUtil.SaveState(graphics)) { graphics.MultiplyTransform(xformLinehanToMikesh); ImageHolder galaxyImage = styles.lightBackground ? s_galaxyImageGray : s_galaxyImage; RenderUtil.DrawImageAlpha(graphics, styles.deepBackgroundOpacity, galaxyImage, galaxyImageRect); } } timers.Add(new Timer("background (galaxy)")); #endregion #region pseudorandom-stars //------------------------------------------------------------ // Pseudo-Random Stars //------------------------------------------------------------ if (styles.pseudoRandomStars.visible) DrawPseudoRandomStars(); timers.Add(new Timer("pseudorandom")); #endregion #region rifts //------------------------------------------------------------ // Rifts in Charted Space //------------------------------------------------------------ if (styles.showRiftOverlay && styles.riftOpacity > 0f) RenderUtil.DrawImageAlpha(graphics, styles.riftOpacity, s_riftImage, riftImageRect); timers.Add(new Timer("rifts")); #endregion #region april-fools //------------------------------------------------------------ // April Fool's Day //------------------------------------------------------------ if (Silly) { using (RenderUtil.SaveState(graphics)) { // Render in image-space graphics.MultiplyTransform(worldSpaceToImageSpace); XImage sillyImage = styles.grayscale ? s_sillyImageGray : s_sillyImageColor; lock (sillyImage) { graphics.DrawImage(sillyImage, 0, 0, tileSize.Width, tileSize.Height); } } timers.Add(new Timer("silly")); } #endregion //------------------------------------------------------------ // Foreground //------------------------------------------------------------ #region macro-borders //------------------------------------------------------------ // Macro: Borders object //------------------------------------------------------------ if (styles.macroBorders.visible) { styles.macroBorders.pen.Apply(ref pen); graphics.SmoothingMode = XSmoothingMode.AntiAlias; foreach (var vec in borderFiles .Select(file => resourceManager.GetXmlFileObject(file, typeof(VectorObject))) .OfType<VectorObject>() .Where(vec => (vec.MapOptions & options & MapOptions.BordersMask) != 0)) { vec.Draw(graphics, tileRect, pen); } } timers.Add(new Timer("macro-borders")); #endregion #region macro-routes //------------------------------------------------------------ // Macro: Route object //------------------------------------------------------------ if (styles.macroRoutes.visible) { styles.macroRoutes.pen.Apply(ref pen); graphics.SmoothingMode = XSmoothingMode.AntiAlias; foreach (var vec in routeFiles .Select(file => resourceManager.GetXmlFileObject(file, typeof(VectorObject))) .OfType<VectorObject>() .Where(vec => (vec.MapOptions & options & MapOptions.BordersMask) != 0)) { vec.Draw(graphics, tileRect, pen); } } timers.Add(new Timer("macro-routes")); #endregion #region sector-grid //------------------------------------------------------------ // Sector Grid //------------------------------------------------------------ graphics.SmoothingMode = XSmoothingMode.HighSpeed; if (styles.sectorGrid.visible) { const int gridSlop = 10; styles.sectorGrid.pen.Apply(ref pen); for (float h = ((float)(Math.Floor((tileRect.Left) / Astrometrics.SectorWidth) - 1) - Astrometrics.ReferenceSector.X) * Astrometrics.SectorWidth - Astrometrics.ReferenceHex.X; h <= tileRect.Right + Astrometrics.SectorWidth; h += Astrometrics.SectorWidth) graphics.DrawLine(pen, h, tileRect.Top - gridSlop, h, tileRect.Bottom + gridSlop); for (float v = ((float)(Math.Floor((tileRect.Top) / Astrometrics.SectorHeight) - 1) - Astrometrics.ReferenceSector.Y) * Astrometrics.SectorHeight - Astrometrics.ReferenceHex.Y; v <= tileRect.Bottom + Astrometrics.SectorHeight; v += Astrometrics.SectorHeight) graphics.DrawLine(pen, tileRect.Left - gridSlop, v, tileRect.Right + gridSlop, v); } timers.Add(new Timer("sector grid")); #endregion #region subsector-grid //------------------------------------------------------------ // Subsector Grid //------------------------------------------------------------ graphics.SmoothingMode = XSmoothingMode.HighSpeed; if (styles.subsectorGrid.visible) { const int gridSlop = 10; styles.subsectorGrid.pen.Apply(ref pen); int hmin = (int)Math.Floor(tileRect.Left / Astrometrics.SubsectorWidth) - 1 - Astrometrics.ReferenceSector.X, hmax = (int)Math.Ceiling((tileRect.Right + Astrometrics.SubsectorWidth + Astrometrics.ReferenceHex.X) / Astrometrics.SubsectorWidth); for (int hi = hmin; hi <= hmax; ++hi) { if (hi % 4 == 0) continue; float h = hi * Astrometrics.SubsectorWidth - Astrometrics.ReferenceHex.X; graphics.DrawLine(pen, h, tileRect.Top - gridSlop, h, tileRect.Bottom + gridSlop); } int vmin = (int)Math.Floor(tileRect.Top / Astrometrics.SubsectorHeight) - 1 - Astrometrics.ReferenceSector.Y, vmax = (int)Math.Ceiling((tileRect.Bottom + Astrometrics.SubsectorHeight + Astrometrics.ReferenceHex.Y) / Astrometrics.SubsectorHeight); for (int vi = vmin; vi <= vmax; ++vi) { if (vi % 4 == 0) continue; float v = vi * Astrometrics.SubsectorHeight - Astrometrics.ReferenceHex.Y; graphics.DrawLine(pen, tileRect.Left - gridSlop, v, tileRect.Right + gridSlop, v); } } timers.Add(new Timer("subsector grid")); #endregion #region parsec-grid //------------------------------------------------------------ // Parsec Grid //------------------------------------------------------------ // TODO: Optimize - timers indicate this is slow graphics.SmoothingMode = XSmoothingMode.HighQuality; if (styles.parsecGrid.visible) DrawParsecGrid(); timers.Add(new Timer("parsec grid")); #endregion #region subsector-names //------------------------------------------------------------ // Subsector Names //------------------------------------------------------------ if (styles.subsectorNames.visible) { solidBrush.Color = styles.subsectorNames.textColor; foreach (Sector sector in selector.Sectors) { for (int i = 0; i < 16; i++) { Subsector ss = sector.Subsector(i); if (ss == null || string.IsNullOrEmpty(ss.Name)) continue; Point center = sector.SubsectorCenter(i); RenderUtil.DrawLabel(graphics, ss.Name, center, styles.subsectorNames.Font, solidBrush, styles.subsectorNames.textStyle); } } } timers.Add(new Timer("subsector names")); #endregion #region micro-borders //------------------------------------------------------------ // Micro: Borders //------------------------------------------------------------ if (styles.microBorders.visible) { if (styles.fillMicroBorders) DrawMicroBorders(BorderLayer.Fill); DrawMicroBorders(BorderLayer.Stroke); } timers.Add(new Timer("micro-borders")); #endregion #region micro-routes //------------------------------------------------------------ // Micro: Routes //------------------------------------------------------------ if (styles.microRoutes.visible) DrawRoutes(); timers.Add(new Timer("micro-routes")); #endregion #region micro-border-labels //------------------------------------------------------------ // Micro: Border Labels & Explicit Labels //------------------------------------------------------------ if (styles.showMicroNames) DrawLabels(); timers.Add(new Timer("micro-border labels")); #endregion #region sector-names //------------------------------------------------------------ // Sector Names //------------------------------------------------------------ if (styles.showSomeSectorNames || styles.showAllSectorNames) { foreach (Sector sector in selector.Sectors .Where(sector => styles.showAllSectorNames || (styles.showSomeSectorNames && sector.Selected)) .Where(sector => sector.Names.Any() || sector.Label != null)) { solidBrush.Color = styles.sectorName.textColor; string name = sector.Label ?? sector.Names[0].Text; RenderUtil.DrawLabel(graphics, name, sector.Center, styles.sectorName.Font, solidBrush, styles.sectorName.textStyle); } } timers.Add(new Timer("sector names")); #endregion #region government-rift-names //------------------------------------------------------------ // Macro: Government / Rift / Route Names //------------------------------------------------------------ if (styles.macroNames.visible) DrawMacroNames(); timers.Add(new Timer("macro names")); #endregion #region capitals-homeworlds //------------------------------------------------------------ // Macro: Capitals & Home Worlds //------------------------------------------------------------ if (styles.capitals.visible && (options & MapOptions.WorldsMask) != 0) { WorldObjectCollection worlds = resourceManager.GetXmlFileObject(@"~/res/Worlds.xml", typeof(WorldObjectCollection)) as WorldObjectCollection; if (worlds != null && worlds.Worlds != null) { solidBrush.Color = styles.capitals.textColor; foreach (WorldObject world in worlds.Worlds.Where(world => (world.MapOptions & options) != 0)) { world.Paint(graphics, styles.capitals.fillColor, solidBrush, styles.macroNames.SmallFont); } } } timers.Add(new Timer("macro worlds")); #endregion #region mega-names //------------------------------------------------------------ // Mega: Galaxy-Scale Labels //------------------------------------------------------------ if (styles.megaNames.visible) { solidBrush.Color = styles.megaNames.textColor; foreach (var label in megaLabels) { using (RenderUtil.SaveState(graphics)) { XMatrix matrix = new XMatrix(); matrix.ScalePrepend(1.0f / Astrometrics.ParsecScaleX, 1.0f / Astrometrics.ParsecScaleY); matrix.TranslatePrepend(label.position.X, label.position.Y); graphics.MultiplyTransform(matrix, XMatrixOrder.Prepend); XFont font = label.minor ? styles.megaNames.SmallFont : styles.megaNames.Font; XSize size = graphics.MeasureString(label.text, font); graphics.TranslateTransform(-size.Width / 2, -size.Height / 2); // Center the text RectangleF textBounds = new RectangleF(0, 0, (float)size.Width * 1.01f, (float)size.Height * 2); // *2 or it gets cut off at high sizes XTextFormatter formatter = new XTextFormatter(graphics); formatter.Alignment = XParagraphAlignment.Center; formatter.DrawString(label.text, font, solidBrush, textBounds); } } } timers.Add(new Timer("mega names")); #endregion } // End of clipping, so world names are not clipped in jumpmaps. #region worlds //------------------------------------------------------------ // Worlds //------------------------------------------------------------ if (styles.worlds.visible) { // TODO: selector may be expensive foreach (World world in selector.Worlds) { DrawWorld(fonts, world, WorldLayer.Background); } foreach (World world in selector.Worlds) { DrawWorld(fonts, world, WorldLayer.Foreground); } } timers.Add(new Timer("worlds")); #endregion //------------------------------------------------------------ // Overlays //------------------------------------------------------------ #region droyne //------------------------------------------------------------ // Droyne/Chirper Worlds //------------------------------------------------------------ if (styles.droyneWorlds.visible) { solidBrush.Color = styles.droyneWorlds.textColor; foreach (World world in selector.Worlds) { bool droyne = world.HasCodePrefix("Droy") != null; bool chirpers = world.HasCodePrefix("Chir") != null; if (droyne || chirpers) { string glyph = droyne ? "\u2605" : "\u2606"; PointF center = Astrometrics.HexToCenter(world.Coordinates); using (RenderUtil.SaveState(graphics)) { XMatrix matrix = new XMatrix(); matrix.TranslatePrepend(center.X, center.Y); matrix.ScalePrepend(1 / Astrometrics.ParsecScaleX, 1 / Astrometrics.ParsecScaleY); graphics.MultiplyTransform(matrix, XMatrixOrder.Prepend); graphics.DrawString(glyph, styles.droyneWorlds.Font, solidBrush, 0, 0, RenderUtil.StringFormatCentered); } } } } timers.Add(new Timer("droyne")); #endregion #region unofficial //------------------------------------------------------------ // Unofficial //------------------------------------------------------------ if (styles.dimUnofficialSectors && styles.worlds.visible) { solidBrush.Color = Color.FromArgb(128, styles.backgroundColor); foreach (Sector sector in selector.Sectors .Where(sector => !sector.Tags.Contains("Official") && !sector.Tags.Contains("Preserve") && !sector.Tags.Contains("InReview"))) graphics.DrawRectangle(solidBrush, sector.Bounds); } timers.Add(new Timer("unofficial")); #endregion #region timing #if SHOW_TIMING using( RenderUtil.SaveState( graphics ) ) { XFont font = new XFont( FontFamily.GenericSansSerif, 12, XFontStyle.Regular, new XPdfFontOptions(PdfSharp.Pdf.PdfFontEncoding.Unicode) ); graphics.MultiplyTransform( worldSpaceToImageSpace ); double cursorX = 20.0, cursorY = 20.0; DateTime last = dtStart; foreach( Timer s in timers ) { TimeSpan ts = s.dt - last; last = s.dt; for( int dx = -1; dx <= 1; ++dx ) { for( int dy = -1; dy <= 1; ++dy ) { graphics.DrawString( String.Format( "{0} {1}", Math.Round( ts.TotalMilliseconds ), s.label ), font, XBrushes.Black, cursorX + dx, cursorY + dy ); } } graphics.DrawString( String.Format("{0} {1}", Math.Round(ts.TotalMilliseconds), s.label), font, XBrushes.Yellow, cursorX, cursorY ); cursorY += 14; } } #endif #endregion } }
public RenderContext(ResourceManager resourceManager, Selector selector, RectangleF tileRect, double scale, MapOptions options, Stylesheet styles, Size tileSize) { this.resourceManager = resourceManager; this.selector = selector; this.tileRect = tileRect; this.scale = scale; this.options = options; this.styles = styles; this.tileSize = tileSize; XMatrix m = new XMatrix(); m.TranslatePrepend((float)(-tileRect.Left * scale * Astrometrics.ParsecScaleX), (float)(-tileRect.Top * scale * Astrometrics.ParsecScaleY)); m.ScalePrepend((float)scale * Astrometrics.ParsecScaleX, (float)scale * Astrometrics.ParsecScaleY); imageSpaceToWorldSpace = m; m.Invert(); worldSpaceToImageSpace = m; }
/// <summary> /// Writes a Glyphs to the content stream. /// </summary> private void WriteGlyphs(Glyphs glyphs) { WriteSaveState("begin Glyphs", glyphs.Name); // Transform also affects clipping and opacity mask bool transformed = glyphs.RenderTransform != null; if (transformed) WriteRenderTransform(glyphs.RenderTransform); bool clipped = glyphs.Clip != null; if (clipped) WriteClip(glyphs.Clip); if (glyphs.Opacity < 1) MultiplyOpacity(glyphs.Opacity); if (glyphs.OpacityMask != null) WriteOpacityMask(glyphs.OpacityMask); XMatrix textMatrix = new XMatrix(); textMatrix.TranslatePrepend(glyphs.OriginX, glyphs.OriginY); glyphs.OriginX = glyphs.OriginY = 0; // HACK: do not change model double emSize = glyphs.FontRenderingEmSize; textMatrix.ScalePrepend(glyphs.FontRenderingEmSize); glyphs.FontRenderingEmSize = 1; // HACK: do not change model bool boldSimulation = (glyphs.StyleSimulations & StyleSimulations.BoldSimulation) == StyleSimulations.BoldSimulation; // just a draft... if (boldSimulation) { boldSimulation = true; // draw black stroke if it is not a solid color brush XColor color = XColor.FromArgb(0, 0, 0); if (glyphs.Fill is SolidColorBrush) { SolidColorBrush brush = glyphs.Fill as SolidColorBrush; color = brush.Color; } WriteLiteral(String.Format(CultureInfo.InvariantCulture, "{0:0.###} {1:0.###} {2:0.###} RG\n", color.R / 255.0, color.G / 255.0, color.B / 255.0)); WriteLiteral("{0:0.###} w\n", emSize / 50); } if ((glyphs.StyleSimulations & StyleSimulations.ItalicSimulation) == StyleSimulations.ItalicSimulation) { textMatrix.SkewPrepend(-20, 0); } XForm xform = null; XImage ximage = null; RealizeFill(glyphs.Fill, ref xform, ref ximage); RealizeFont(glyphs); if (boldSimulation) WriteLiteral("2 Tr\n", 1); double x = glyphs.OriginX; double y = glyphs.OriginY; //switch (format.Alignment) //{ // case XStringAlignment.Near: // // nothing to do // break; // case XStringAlignment.Center: // x += (rect.Width - width) / 2; // break; // case XStringAlignment.Far: // x += rect.Width - width; // break; //} PdfFont realizedFont = this.graphicsState.realizedFont; Debug.Assert(realizedFont != null); realizedFont.AddChars(glyphs.UnicodeString); OpenTypeDescriptor descriptor = realizedFont.FontDescriptor.descriptor; //if (bold && !descriptor.IsBoldFace) //{ // // TODO: emulate bold by thicker outline //} //if (italic && !descriptor.IsBoldFace) //{ // // TODO: emulate italic by shearing transformation //} #if true string s2 = ""; string s = glyphs.UnicodeString; if (!String.IsNullOrEmpty(s)) { int length = s.Length; for (int idx = 0; idx < length; idx++) { char ch = s[idx]; int glyphID = 0; if (descriptor.fontData.cmap.symbol) { glyphID = (int)ch + (descriptor.fontData.os2.usFirstCharIndex & 0xFF00); glyphID = descriptor.CharCodeToGlyphIndex((char)glyphID); } else glyphID = descriptor.CharCodeToGlyphIndex(ch); s2 += (char)glyphID; } } s = s2; #endif byte[] bytes = PdfEncoders.RawUnicodeEncoding.GetBytes(s); bytes = PdfEncoders.FormatStringLiteral(bytes, true, false, true, null); string text = PdfEncoders.RawEncoding.GetString(bytes); if (glyphs.IsSideways) { textMatrix.RotateAtPrepend(-90, new XPoint(x, y)); XPoint pos = new XPoint(x, y); AdjustTextMatrix(ref pos); //WriteTextTransform(textMatrix); WriteLiteral("{0} Tj\n", text); } else { #if true //if (glyphs.BidiLevel % 2 == 1) // WriteLiteral("-1 Tc\n"); if (!textMatrix.IsIdentity) WriteTextTransform(textMatrix); WriteGlyphsInternal(glyphs, null); #else XPoint pos = new XPoint(x, y); AdjustTextMatrix(ref pos); WriteLiteral("{0:0.###} {1:0.###} Td {2} Tj\n", pos.x, pos.y, text); //PdfEncoders.ToStringLiteral(s, PdfStringEncoding.RawEncoding, null)); #endif } WriteRestoreState("end Glyphs", glyphs.Name); }
private static void DrawWorld(RenderContext ctx, FontCache styleRes, World world, WorldLayer layer) { bool isPlaceholder = world.IsPlaceholder; bool isCapital = world.IsCapital; bool isHiPop = world.IsHi; bool renderName = ctx.styles.worldDetails.HasFlag(WorldDetails.AllNames) || (ctx.styles.worldDetails.HasFlag(WorldDetails.KeyNames) && (isCapital || isHiPop)); bool renderUWP = ctx.styles.worldDetails.HasFlag(WorldDetails.Uwp); using (RenderUtil.SaveState(ctx.graphics)) { XPen pen = new XPen(XColor.Empty); XSolidBrush solidBrush = new XSolidBrush(); ctx.graphics.SmoothingMode = XSmoothingMode.AntiAlias; // Center on the parsec PointF center = Astrometrics.HexToCenter(world.Coordinates); XMatrix matrix = new XMatrix(); matrix.TranslatePrepend(center.X, center.Y); matrix.ScalePrepend(ctx.styles.hexContentScale / Astrometrics.ParsecScaleX, ctx.styles.hexContentScale / Astrometrics.ParsecScaleY); ctx.graphics.MultiplyTransform(matrix, XMatrixOrder.Prepend); if (!ctx.styles.useWorldImages) { if (layer == WorldLayer.Background) { #region Zone if (ctx.styles.worldDetails.HasFlag(WorldDetails.Zone)) { Stylesheet.StyleElement? maybeElem = ZoneStyle(ctx, world); if (maybeElem.HasValue) { Stylesheet.StyleElement elem = maybeElem.Value; if (!elem.fillColor.IsEmpty) { solidBrush.Color = elem.fillColor; ctx.graphics.DrawEllipse(solidBrush, -0.4f, -0.4f, 0.8f, 0.8f); } PenInfo pi = elem.pen; if (!pi.color.IsEmpty) { pi.Apply(ref pen); if (renderName && ctx.styles.fillMicroBorders) { using (RenderUtil.SaveState(ctx.graphics)) { ctx.graphics.IntersectClip(new RectangleF(-.5f, -.5f, 1f, renderUWP ? 0.65f : 0.75f)); ctx.graphics.DrawEllipse(pen, -0.4f, -0.4f, 0.8f, 0.8f); } } else { ctx.graphics.DrawEllipse(pen, -0.4f, -0.4f, 0.8f, 0.8f); } } } } #endregion #region Hex if (!ctx.styles.numberAllHexes && ctx.styles.worldDetails.HasFlag(WorldDetails.Hex)) { string hex; switch (ctx.styles.hexCoordinateStyle) { default: case Stylesheet.HexCoordinateStyle.Sector: hex = world.Hex; break; case Stylesheet.HexCoordinateStyle.Subsector: hex = world.SubsectorHex; break; } solidBrush.Color = ctx.styles.hexNumber.textColor; ctx.graphics.DrawString(hex, ctx.styles.hexNumber.Font, solidBrush, 0.0f, -0.5f, RenderUtil.StringFormatTopCenter); } #endregion } if (layer == WorldLayer.Foreground) { Stylesheet.StyleElement? elem = ZoneStyle(ctx, world); TextBackgroundStyle worldTextBackgroundStyle = (elem.HasValue && !elem.Value.fillColor.IsEmpty) ? TextBackgroundStyle.None : ctx.styles.worlds.textBackgroundStyle; #region Name if (renderName) { string name = world.Name; if ((isHiPop && ctx.styles.worldDetails.HasFlag(WorldDetails.Highlight)) || ctx.styles.worlds.textStyle.Uppercase) name = name.ToUpperInvariant(); Color textColor = (isCapital && ctx.styles.worldDetails.HasFlag(WorldDetails.Highlight)) ? ctx.styles.worlds.textHighlightColor : ctx.styles.worlds.textColor; XFont font = ((isHiPop || isCapital) && ctx.styles.worldDetails.HasFlag(WorldDetails.Highlight)) ? ctx.styles.worlds.LargeFont : ctx.styles.worlds.Font; DrawWorldLabel(ctx, worldTextBackgroundStyle, solidBrush, textColor, ctx.styles.worlds.textStyle.Translation, font, name); } #endregion #region Allegiance // TODO: Mask off background for allegiance if (ctx.styles.worldDetails.HasFlag(WorldDetails.Allegiance)) { string alleg = world.Allegiance; if (!SecondSurvey.IsDefaultAllegiance(alleg)) { if (!ctx.styles.t5AllegianceCodes && alleg.Length > 2) alleg = SecondSurvey.T5AllegianceCodeToLegacyCode(alleg); solidBrush.Color = ctx.styles.worlds.textColor; if (ctx.styles.lowerCaseAllegiance) alleg = alleg.ToLowerInvariant(); ctx.graphics.DrawString(alleg, ctx.styles.worlds.SmallFont, solidBrush, ctx.styles.AllegiancePosition.X, ctx.styles.AllegiancePosition.Y, RenderUtil.StringFormatCentered); } } #endregion if (!isPlaceholder) { #region GasGiant if (ctx.styles.worldDetails.HasFlag(WorldDetails.GasGiant)) { if (world.GasGiants > 0) { solidBrush.Color = ctx.styles.worlds.textColor; RenderUtil.DrawGlyph(ctx.graphics, Glyph.Circle, styleRes, solidBrush, ctx.styles.GasGiantPosition.X, ctx.styles.GasGiantPosition.Y); } } #endregion #region Starport if (ctx.styles.worldDetails.HasFlag(WorldDetails.Starport)) { string starport = world.Starport.ToString(); DrawWorldLabel(ctx, worldTextBackgroundStyle, solidBrush, ctx.styles.worlds.textColor, ctx.styles.StarportPosition, styleRes.StarportFont, starport); } #endregion #region UWP if (renderUWP) { string uwp = world.UWP; solidBrush.Color = ctx.styles.worlds.textColor; ctx.graphics.DrawString(uwp, ctx.styles.hexNumber.Font, solidBrush, ctx.styles.StarportPosition.X, -ctx.styles.StarportPosition.Y, RenderUtil.StringFormatCentered); } #endregion #region Bases // TODO: Mask off background for glyphs if (ctx.styles.worldDetails.HasFlag(WorldDetails.Bases)) { string bases = world.Bases; // Special case: Show Zho Naval+Military as diamond if (world.BaseAllegiance == "Zh" && bases == "KM") bases = "Z"; // Base 1 bool bottomUsed = false; if (bases.Length > 0) { Glyph glyph = Glyph.FromBaseCode(world.BaseAllegiance, bases[0]); if (glyph.Printable) { PointF pt = ctx.styles.BaseTopPosition; if (glyph.Bias == Glyph.GlyphBias.Bottom) { pt = ctx.styles.BaseBottomPosition; bottomUsed = true; } solidBrush.Color = glyph.IsHighlighted ? ctx.styles.worlds.textHighlightColor : ctx.styles.worlds.textColor; RenderUtil.DrawGlyph(ctx.graphics, glyph, styleRes, solidBrush, pt.X, pt.Y); } } // Base 2 if (bases.Length > 1) { Glyph glyph = Glyph.FromBaseCode(world.LegacyAllegiance, bases[1]); if (glyph.Printable) { PointF pt = bottomUsed ? ctx.styles.BaseTopPosition : ctx.styles.BaseBottomPosition; solidBrush.Color = glyph.IsHighlighted ? ctx.styles.worlds.textHighlightColor : ctx.styles.worlds.textColor; RenderUtil.DrawGlyph(ctx.graphics, glyph, styleRes, solidBrush, pt.X, pt.Y); } } // Research Stations string rs; if ((rs = world.ResearchStation) != null) { Glyph glyph = Glyph.FromResearchCode(rs); solidBrush.Color = glyph.IsHighlighted ? ctx.styles.worlds.textHighlightColor : ctx.styles.worlds.textColor; RenderUtil.DrawGlyph(ctx.graphics, glyph, styleRes, solidBrush, ctx.styles.BaseMiddlePosition.X, ctx.styles.BaseMiddlePosition.Y); } else if (world.IsReserve) { Glyph glyph = Glyph.Reserve; solidBrush.Color = glyph.IsHighlighted ? ctx.styles.worlds.textHighlightColor : ctx.styles.worlds.textColor; RenderUtil.DrawGlyph(ctx.graphics, glyph, styleRes, solidBrush, ctx.styles.BaseMiddlePosition.X, 0); } else if (world.IsPenalColony) { Glyph glyph = Glyph.Prison; solidBrush.Color = glyph.IsHighlighted ? ctx.styles.worlds.textHighlightColor : ctx.styles.worlds.textColor; RenderUtil.DrawGlyph(ctx.graphics, glyph, styleRes, solidBrush, ctx.styles.BaseMiddlePosition.X, 0); } else if (world.IsPrisonExileCamp) { Glyph glyph = Glyph.ExileCamp; solidBrush.Color = glyph.IsHighlighted ? ctx.styles.worlds.textHighlightColor : ctx.styles.worlds.textColor; RenderUtil.DrawGlyph(ctx.graphics, glyph, styleRes, solidBrush, ctx.styles.BaseMiddlePosition.X, 0); } } #endregion } #region Disc if (ctx.styles.worldDetails.HasFlag(WorldDetails.Type)) { if (isPlaceholder) { DrawWorldLabel(ctx, ctx.styles.placeholder.textBackgroundStyle, solidBrush, ctx.styles.placeholder.textColor, ctx.styles.placeholder.position, ctx.styles.placeholder.Font, ctx.styles.placeholder.content); } else { if (world.Size <= 0) { #region Asteroid-Belt if (ctx.styles.worldDetails.HasFlag(WorldDetails.Asteroids)) { // Basic pattern, with probability varying per position: // o o o // o o o o // o o o int[] lpx = { -2, 0, 2, -3, -1, 1, 3, -2, 0, 2 }; int[] lpy = { -2, -2, -2, 0, 0, 0, 0, 2, 2, 2 }; float[] lpr = { 0.5f, 0.9f, 0.5f, 0.6f, 0.9f, 0.9f, 0.6f, 0.5f, 0.9f, 0.5f }; solidBrush.Color = ctx.styles.worlds.textColor; // Random generator is seeded with world location so it is always the same Random rand = new Random(world.Coordinates.X ^ world.Coordinates.Y); for (int i = 0; i < lpx.Length; ++i) { if (rand.NextDouble() < lpr[i]) { float px = lpx[i] * 0.035f; float py = lpy[i] * 0.035f; float w = 0.04f + (float)rand.NextDouble() * 0.03f; float h = 0.04f + (float)rand.NextDouble() * 0.03f; // If necessary, add jitter here float dx = 0, dy = 0; ctx.graphics.DrawEllipse(solidBrush, px + dx - w / 2, py + dy - h / 2, w, h); } } } else { // Just a glyph solidBrush.Color = ctx.styles.worlds.textColor; RenderUtil.DrawGlyph(ctx.graphics, Glyph.DiamondX, styleRes, solidBrush, 0.0f, 0.0f); } #endregion } else { XColor penColor, brushColor; ctx.styles.WorldColors(world, out penColor, out brushColor); if (!brushColor.IsEmpty) { solidBrush.Color = brushColor; ctx.graphics.DrawEllipse(solidBrush, -0.1f, -0.1f, 0.2f, 0.2f); } if (!penColor.IsEmpty) { ctx.styles.worldWater.pen.Apply(ref pen); pen.Color = penColor; ctx.graphics.DrawEllipse(pen, -0.1f, -0.1f, 0.2f, 0.2f); } } } } else { // Dotmap solidBrush.Color = ctx.styles.worlds.textColor; ctx.graphics.DrawEllipse(solidBrush, -0.2f, -0.2f, 0.4f, 0.4f); } #endregion } } else // ctx.styles.useWorldImages { float imageRadius = ((world.Size <= 0) ? 0.6f : (0.3f * (world.Size / 5.0f + 0.2f))) / 2; float decorationRadius = imageRadius; if (layer == WorldLayer.Background) { #region Disc if (ctx.styles.worldDetails.HasFlag(WorldDetails.Type)) { if (isPlaceholder) { DrawWorldLabel(ctx, ctx.styles.placeholder.textBackgroundStyle, solidBrush, ctx.styles.placeholder.textColor, ctx.styles.placeholder.position, ctx.styles.placeholder.Font, ctx.styles.placeholder.content); } else if (world.Size <= 0) { const float scaleX = 1.5f; const float scaleY = 1.0f; XImage img = s_worldImages["Belt"]; lock (img) { ctx.graphics.DrawImage(img, -imageRadius * scaleX, -imageRadius * scaleY, imageRadius * 2 * scaleX, imageRadius * 2 * scaleY); } } else { XImage img; switch (world.Hydrographics) { default: case 0x0: img = s_worldImages["Hyd0"]; break; case 0x1: img = s_worldImages["Hyd1"]; break; case 0x2: img = s_worldImages["Hyd2"]; break; case 0x3: img = s_worldImages["Hyd3"]; break; case 0x4: img = s_worldImages["Hyd4"]; break; case 0x5: img = s_worldImages["Hyd5"]; break; case 0x6: img = s_worldImages["Hyd6"]; break; case 0x7: img = s_worldImages["Hyd7"]; break; case 0x8: img = s_worldImages["Hyd8"]; break; case 0x9: img = s_worldImages["Hyd9"]; break; case 0xA: img = s_worldImages["HydA"]; break; } if (img != null) { lock (img) { ctx.graphics.DrawImage(img, -imageRadius, -imageRadius, imageRadius * 2, imageRadius * 2); } } } } else { // Dotmap solidBrush.Color = ctx.styles.worlds.textColor; ctx.graphics.DrawEllipse(solidBrush, -0.2f, -0.2f, 0.4f, 0.4f); } #endregion } if (isPlaceholder) return; if (layer == WorldLayer.Foreground) { #region Zone if (ctx.styles.worldDetails.HasFlag(WorldDetails.Zone)) { if (world.IsAmber || world.IsRed || world.IsBlue) { PenInfo pi = world.IsAmber ? ctx.styles.amberZone.pen : world.IsRed ? ctx.styles.redZone.pen : ctx.styles.blueZone.pen; pi.Apply(ref pen); // TODO: Try and accomplish this using dash pattern decorationRadius += 0.1f; ctx.graphics.DrawArc(pen, -decorationRadius, -decorationRadius, decorationRadius * 2, decorationRadius * 2, 5, 80); ctx.graphics.DrawArc(pen, -decorationRadius, -decorationRadius, decorationRadius * 2, decorationRadius * 2, 95, 80); ctx.graphics.DrawArc(pen, -decorationRadius, -decorationRadius, decorationRadius * 2, decorationRadius * 2, 185, 80); ctx.graphics.DrawArc(pen, -decorationRadius, -decorationRadius, decorationRadius * 2, decorationRadius * 2, 275, 80); } } #endregion #region GasGiant if (ctx.styles.worldDetails.HasFlag(WorldDetails.GasGiant)) { if (world.GasGiants > 0) { decorationRadius += 0.1f; const float symbolRadius = 0.05f; solidBrush.Color = ctx.styles.worlds.textHighlightColor; ; ctx.graphics.DrawEllipse(solidBrush, decorationRadius - symbolRadius, 0.0f - symbolRadius, symbolRadius * 2, symbolRadius * 2); } } #endregion #region UWP if (renderUWP) { string uwp = world.UWP; solidBrush.Color = ctx.styles.worlds.textColor; using (RenderUtil.SaveState(ctx.graphics)) { XMatrix uwpMatrix = new XMatrix(); uwpMatrix.TranslatePrepend(decorationRadius, 0.0f); uwpMatrix.ScalePrepend(ctx.styles.worlds.textStyle.Scale.Width, ctx.styles.worlds.textStyle.Scale.Height); uwpMatrix.Multiply(uwpMatrix, XMatrixOrder.Prepend); ctx.graphics.DrawString(uwp, ctx.styles.hexNumber.Font, solidBrush, ctx.styles.StarportPosition.X, -ctx.styles.StarportPosition.Y, RenderUtil.StringFormatCenterLeft); } } #endregion #region Name if (renderName) { string name = world.Name; if (isHiPop) name = name.ToUpperInvariant(); using (RenderUtil.SaveState(ctx.graphics)) { Color textColor = (isCapital && ctx.styles.worldDetails.HasFlag(WorldDetails.Highlight)) ? ctx.styles.worlds.textHighlightColor : ctx.styles.worlds.textColor; if (ctx.styles.worlds.textStyle.Uppercase) name = name.ToUpper(); decorationRadius += 0.1f; XMatrix imageMatrix = new XMatrix(); imageMatrix.TranslatePrepend(decorationRadius, 0.0f); imageMatrix.ScalePrepend(ctx.styles.worlds.textStyle.Scale.Width, ctx.styles.worlds.textStyle.Scale.Height); imageMatrix.TranslatePrepend(ctx.graphics.MeasureString(name, ctx.styles.worlds.Font).Width / 2, 0.0f); // Left align ctx.graphics.MultiplyTransform(imageMatrix, XMatrixOrder.Prepend); DrawWorldLabel(ctx, ctx.styles.worlds.textBackgroundStyle, solidBrush, textColor, ctx.styles.worlds.textStyle.Translation, ctx.styles.worlds.Font, name); } } #endregion } } } }
/// <summary> /// Saves a graphics container with the current state of this XGraphics and /// opens and uses a new graphics container. /// </summary> public XGraphicsContainer BeginContainer(XRect dstrect, XRect srcrect, XGraphicsUnit unit) { // TODO: unit if (unit != XGraphicsUnit.Point) throw new ArgumentException("The current implementation supports XGraphicsUnit.Point only.", "unit"); XGraphicsContainer xContainer = null; #if GDI if (this.targetContext == XGraphicTargetContext.GDI) xContainer = new XGraphicsContainer(this.gfx.Save()); #endif #if WPF if (this.targetContext == XGraphicTargetContext.WPF) xContainer = new XGraphicsContainer(); #endif InternalGraphicsState iState = new InternalGraphicsState(this, xContainer); iState.Transform = this.transform; this.gsStack.Push(iState); if (this.renderer != null) this.renderer.BeginContainer(xContainer, dstrect, srcrect, unit); XMatrix matrix = new XMatrix(); //XMatrix.Identity; #if true double scaleX = dstrect.Width / srcrect.Width; double scaleY = dstrect.Height / srcrect.Height; matrix.TranslatePrepend(-srcrect.X, -srcrect.Y); matrix.ScalePrepend(scaleX, scaleY); matrix.TranslatePrepend(dstrect.X / scaleX, dstrect.Y / scaleY); #else matrix.Translate(-dstrect.X, -dstrect.Y); matrix.Scale(dstrect.Width / srcrect.Width, dstrect.Height / srcrect.Height); matrix.Translate(srcrect.X, srcrect.Y); #endif AddTransform(matrix, XMatrixOrder.Prepend); return xContainer; }
public void Draw(XGraphics graphics, RectangleF rect, MapOptions options, XPen pen) { if (graphics == null) throw new ArgumentNullException("graphics"); RectangleF bounds = TransformedBounds; //graphics.DrawRectangle( new XPen(XColors.Yellow, 1), bounds.X, bounds.Y, bounds.Width, bounds.Height ); if (bounds.IntersectsWith(rect)) { XGraphicsPath path = Path; using (RenderUtil.SaveState(graphics)) { XMatrix matrix = new XMatrix(); matrix.ScalePrepend(ScaleX, ScaleY); matrix.TranslatePrepend(-OriginX, -OriginY); graphics.MultiplyTransform(matrix, XMatrixOrder.Prepend); graphics.DrawPath(pen, path); } } }
/// <summary> /// Applies the specified translation operation to the transformation matrix of this object /// in the specified order. /// </summary> public void TranslateTransform(double dx, double dy, XMatrixOrder order) { //XMatrix matrix = this.transform; //matrix.Translate(dx, dy, order); //Transform = matrix; XMatrix matrix = new XMatrix(); //XMatrix.Identity; matrix.TranslatePrepend(dx, dy); AddTransform(matrix, order); }
public void DrawName(XGraphics graphics, RectangleF rect, MapOptions options, XFont font, XBrush textBrush, LabelStyle labelStyle) { if (graphics == null) throw new ArgumentNullException("graphics"); RectangleF bounds = TransformedBounds; if (bounds.IntersectsWith(rect)) { if (Name != null) { string str = Name; if (labelStyle.Uppercase) str = str.ToUpperInvariant(); PointF pos = NamePosition;// PointF( bounds.Left + bounds.Width / 2, bounds.Top + bounds.Height / 2 ); using (RenderUtil.SaveState(graphics)) { XMatrix matrix = new XMatrix(); matrix.TranslatePrepend(pos.X, pos.Y); matrix.ScalePrepend(1.0f / Astrometrics.ParsecScaleX, 1.0f / Astrometrics.ParsecScaleY); matrix.RotatePrepend(-labelStyle.Rotation); // Rotate it graphics.MultiplyTransform(matrix, XMatrixOrder.Prepend); XSize size = graphics.MeasureString(str, font); graphics.TranslateTransform(-size.Width / 2, -size.Height / 2); // Center the text RectangleF textBounds = new RectangleF(0, 0, (float)size.Width, (float)size.Height * 2); // *2 or it gets cut off at high sizes XTextFormatter tf = new XTextFormatter(graphics); tf.Alignment = XParagraphAlignment.Center; tf.DrawString(str, font, textBrush, textBounds); } } } }
/// <summary> /// Draws the vertical Y axis. /// </summary> internal override void Draw() { AxisRendererInfo yari = ((ChartRendererInfo)this.rendererParms.RendererInfo).yAxisRendererInfo; double yMin = yari.MinimumScale; double yMax = yari.MaximumScale; double yMajorTick = yari.MajorTick; double yMinorTick = yari.MinorTick; XMatrix matrix = new XMatrix(); //XMatrix.Identity; matrix.TranslatePrepend(-yMin, -yari.Y); matrix.Scale(yari.InnerRect.Width / (yMax - yMin), 1, XMatrixOrder.Append); matrix.Translate(yari.X, yari.Y, XMatrixOrder.Append); // Draw axis. // First draw tick marks, second draw axis. double majorTickMarkStart = 0, majorTickMarkEnd = 0, minorTickMarkStart = 0, minorTickMarkEnd = 0; GetTickMarkPos(yari, ref majorTickMarkStart, ref majorTickMarkEnd, ref minorTickMarkStart, ref minorTickMarkEnd); XGraphics gfx = this.rendererParms.Graphics; LineFormatRenderer lineFormatRenderer = new LineFormatRenderer(gfx, yari.LineFormat); XPoint[] points = new XPoint[2]; if (yari.MinorTickMark != TickMarkType.None) { for (double y = yMin + yMinorTick; y < yMax; y += yMinorTick) { points[0].X = y; points[0].Y = minorTickMarkStart; points[1].X = y; points[1].Y = minorTickMarkEnd; matrix.TransformPoints(points); lineFormatRenderer.DrawLine(points[0], points[1]); } } XStringFormat xsf = new XStringFormat(); xsf.LineAlignment = XLineAlignment.Near; int countTickLabels = (int)((yMax - yMin) / yMajorTick) + 1; for (int i = 0; i < countTickLabels; ++i) { double y = yMin + yMajorTick * i; string str = y.ToString(yari.TickLabelsFormat); XSize labelSize = gfx.MeasureString(str, yari.TickLabelsFont); if (yari.MajorTickMark != TickMarkType.None) { labelSize.Height += 1.5f * yari.MajorTickMarkWidth; points[0].X = y; points[0].Y = majorTickMarkStart; points[1].X = y; points[1].Y = majorTickMarkEnd; matrix.TransformPoints(points); lineFormatRenderer.DrawLine(points[0], points[1]); } XPoint[] layoutText = new XPoint[1]; layoutText[0].X = y; layoutText[0].Y = yari.Y + 1.5 * yari.MajorTickMarkWidth; matrix.TransformPoints(layoutText); layoutText[0].X -= labelSize.Width / 2; // Center text vertically. gfx.DrawString(str, yari.TickLabelsFont, yari.TickLabelsBrush, layoutText[0], xsf); } if (yari.LineFormat != null) { points[0].X = yMin; points[0].Y = yari.Y; points[1].X = yMax; points[1].Y = yari.Y; matrix.TransformPoints(points); if (yari.MajorTickMark != TickMarkType.None) { // yMax is at the upper side of the axis points[0].X -= yari.LineFormat.Width / 2; points[1].X += yari.LineFormat.Width / 2; } lineFormatRenderer.DrawLine(points[0], points[1]); } // Draw axis title if (yari.axisTitleRendererInfo != null) { RendererParameters parms = new RendererParameters(); parms.Graphics = gfx; parms.RendererInfo = yari; XRect rcTitle = yari.Rect; rcTitle.Height = yari.axisTitleRendererInfo.Height; rcTitle.Y += yari.Rect.Height - rcTitle.Height; yari.axisTitleRendererInfo.Rect = rcTitle; AxisTitleRenderer atr = new AxisTitleRenderer(parms); atr.Draw(); } }
public static void DrawLabel(XGraphics g, string text, PointF labelPos, XFont font, XBrush brush, LabelStyle labelStyle) { using (RenderUtil.SaveState(g)) { XTextFormatter tf = new XTextFormatter(g); tf.Alignment = XParagraphAlignment.Center; XMatrix matrix = new XMatrix(); matrix.TranslatePrepend(labelPos.X, labelPos.Y); matrix.ScalePrepend(1.0f / Astrometrics.ParsecScaleX, 1.0f / Astrometrics.ParsecScaleY); if (labelStyle.Uppercase) text = text.ToUpper(); if (labelStyle.Wrap) text = text.Replace(' ', '\n'); matrix.TranslatePrepend(labelStyle.Translation.X, labelStyle.Translation.Y); matrix.RotatePrepend(labelStyle.Rotation); matrix.ScalePrepend(labelStyle.Scale.Width, labelStyle.Scale.Height); g.MultiplyTransform(matrix, XMatrixOrder.Prepend); XSize size = g.MeasureString(text, font); size.Width *= 2; // prevent cut-off e.g. when rotated XRect bounds = new XRect(-size.Width / 2, -size.Height / 2, size.Width, size.Height); tf.DrawString(text, font, brush, bounds); } }