Example #1
0
        private Texture FindMatch(Pixmap baseRes)
        {
            if (baseRes == null)
            {
                return null;
            }
            else if (baseRes.IsDefaultContent)
            {
                var defaultContent = ContentProvider.GetDefaultContent<Resource>();
                return defaultContent.Res().OfType<Texture>().FirstOrDefault(r => r.BasePixmap == baseRes);
            }
            else
            {
                // First try a direct approach
                string fileExt = Resource.GetFileExtByType<Texture>();
                string targetPath = baseRes.FullName + fileExt;
                Texture match = ContentProvider.RequestContent<Texture>(targetPath).Res;
                if (match != null) return match;

                // If that fails, search for other matches
                string targetName = baseRes.Name + fileExt;
                List<string> resFilePaths = Resource.GetResourceFiles();
                var resNameMatch = resFilePaths.Where(p => Path.GetFileName(p) == targetName);
                var resQuery = resNameMatch.Concat(resFilePaths).Distinct();
                foreach (string resFile in resQuery)
                {
                    if (!resFile.EndsWith(fileExt)) continue;
                    match = ContentProvider.RequestContent<Texture>(resFile).Res;
                    if (match != null && match.BasePixmap == baseRes) return match;
                }

                // Give up.
                return null;
            }
        }
Example #2
0
 public void ImportFile(string srcFile, string targetName, string targetDir)
 {
     string[] output = this.GetOutputFiles(srcFile, targetName, targetDir);
     PixelData pixelData = LoadPixelData(srcFile);
     Pixmap res = new Pixmap(pixelData);
     res.SourcePath = srcFile;
     res.Save(output[0]);
 }
		protected override void OnGetValue()
		{
			base.OnGetValue();

			Pixmap lastValue = this.value;
			Pixmap[] values = this.GetValue().Cast<Pixmap>().ToArray();

			this.value = values.NotNull().FirstOrDefault() as Pixmap;
			this.ResetPreviewImage();

			if (this.value != lastValue)
				this.Invalidate();
			else
				this.Invalidate(this.rectHeader);
		}
		private void ActionPixmapCreateTexture(Pixmap pixmap)
		{
			Texture.CreateFromPixmap(pixmap);
		}
Example #5
0
 public void ImportFile(string srcFile, string targetName, string targetDir)
 {
     string[] output = this.GetOutputFiles(srcFile, targetName, targetDir);
     Pixmap res = new Pixmap(srcFile);
     res.Save(output[0]);
 }
Example #6
0
        /// <summary>
        /// Loads the specified <see cref="Duality.Resources.Pixmap">Pixmaps</see> pixel data.
        /// </summary>
        /// <param name="basePixmap">The <see cref="Duality.Resources.Pixmap"/> that is used as pixel data source.</param>
        /// <param name="sizeMode">Specifies behaviour in case the source data has non-power-of-two dimensions.</param>
        public void LoadData(ContentRef <Pixmap> basePixmap, SizeMode sizeMode)
        {
            DualityApp.GuardSingleThreadState();
            if (this.glTexId == 0)
            {
                this.glTexId = GL.GenTexture();
            }
            this.needsReload = false;
            this.basePixmap  = basePixmap;
            this.texSizeMode = sizeMode;

            int lastTexId;

            GL.GetInteger(GetPName.TextureBinding2D, out lastTexId);
            GL.BindTexture(TextureTarget.Texture2D, this.glTexId);

            if (!this.basePixmap.IsExplicitNull)
            {
                Pixmap.Layer pixelData     = null;
                Pixmap       basePixmapRes = this.basePixmap.IsAvailable ? this.basePixmap.Res : null;
                if (basePixmapRes != null)
                {
                    pixelData  = basePixmapRes.MainLayer;
                    this.atlas = basePixmapRes.Atlas != null?basePixmapRes.Atlas.ToArray() : null;

                    this.animCols = basePixmapRes.AnimCols;
                    this.animRows = basePixmapRes.AnimRows;
                }

                if (pixelData == null)
                {
                    pixelData = Pixmap.Checkerboard.Res.MainLayer;
                }

                this.AdjustSize(pixelData.Width, pixelData.Height);
                this.SetupOpenGLRes();
                if (this.texSizeMode != SizeMode.NonPowerOfTwo &&
                    (this.pxWidth != this.texWidth || this.pxHeight != this.texHeight))
                {
                    if (this.texSizeMode == SizeMode.Enlarge)
                    {
                        Pixmap.Layer oldData = pixelData;
                        pixelData = oldData.CloneResize(this.texWidth, this.texHeight);
                        // Fill border pixels manually - that's cheaper than ColorTransparentPixels here.
                        oldData.DrawOnto(pixelData, BlendMode.Solid, this.pxWidth, 0, 1, this.pxHeight, this.pxWidth - 1, 0);
                        oldData.DrawOnto(pixelData, BlendMode.Solid, 0, this.pxHeight, this.pxWidth, 1, 0, this.pxHeight - 1);
                    }
                    else
                    {
                        pixelData = pixelData.CloneRescale(this.texWidth, this.texHeight, Pixmap.FilterMethod.Linear);
                    }
                }

                // Load pixel data to video memory
                GL.TexImage2D(TextureTarget.Texture2D, 0,
                              this.pixelformat, pixelData.Width, pixelData.Height, 0,
                              GLPixelFormat.Rgba, PixelType.UnsignedByte,
                              pixelData.Data);

                // Adjust atlas to represent UV coordinates
                if (this.atlas != null)
                {
                    Vector2 scale;
                    scale.X = this.uvRatio.X / this.pxWidth;
                    scale.Y = this.uvRatio.Y / this.pxHeight;
                    for (int i = 0; i < this.atlas.Length; i++)
                    {
                        this.atlas[i].X *= scale.X;
                        this.atlas[i].W *= scale.X;
                        this.atlas[i].Y *= scale.Y;
                        this.atlas[i].H *= scale.Y;
                    }
                }
            }
            else
            {
                this.atlas    = null;
                this.animCols = 0;
                this.animRows = 0;
                this.AdjustSize(this.size.X, this.size.Y);
                this.SetupOpenGLRes();
            }

            GL.BindTexture(TextureTarget.Texture2D, lastTexId);
        }
Example #7
0
        /// <summary>
        /// Renders a text to the specified target <see cref="Duality.Resources.Pixmap"/> <see cref="Duality.Resources.Pixmap.Layer"/>.
        /// </summary>
        /// <param name="text"></param>
        /// <param name="target"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="clr"></param>
        public void RenderToBitmap(string text, Pixmap.Layer target, float x, float y, ColorRgba clr)
        {
            Pixmap.Layer pixelData = this.pixelData.MainLayer;

            float curOffset = 0.0f;
            GlyphData glyphData;
            Rect uvRect;
            float glyphXOff;
            float glyphXAdv;
            for (int i = 0; i < text.Length; i++)
            {
                this.ProcessTextAdv(text, i, out glyphData, out uvRect, out glyphXAdv, out glyphXOff);
                Vector2 dataCoord = uvRect.Pos * new Vector2(this.pixelData.Width, this.pixelData.Height) / this.texture.UVRatio;

                pixelData.DrawOnto(target,
                    BlendMode.Alpha,
                    MathF.RoundToInt(x + curOffset + glyphXOff),
                    MathF.RoundToInt(y),
                    glyphData.width,
                    glyphData.height,
                    MathF.RoundToInt(dataCoord.X),
                    MathF.RoundToInt(dataCoord.Y),
                    clr);

                curOffset += glyphXAdv;
            }
        }
Example #8
0
        /// <summary>
        /// Applies a new set of rendered glyphs to the <see cref="Font"/>, adjusts its typeface metadata and clears out the <see cref="GlyphsDirty"/> flag.
        /// This method is used by the editor to update a Font after adjusting its properties.
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="atlas"></param>
        /// <param name="glyphs"></param>
        /// <param name="metrics"></param>
        public void SetGlyphData(PixelData bitmap, Rect[] atlas, GlyphData[] glyphs, FontMetrics metrics)
        {
            this.ReleaseResources();

            this.glyphs = glyphs;
            this.GenerateCharLookup();

            this.pixelData = new Pixmap(bitmap);
            this.pixelData.Atlas = atlas.ToList();

            this.metrics = metrics;

            // Copy metrics data into local fields.
            // Remove this on the next major version step.
            this.size = metrics.Size;
            this.height = metrics.Height;
            this.ascent = metrics.Ascent;
            this.bodyAscent = metrics.BodyAscent;
            this.descent = metrics.Descent;
            this.baseLine = metrics.BaseLine;
            this.monospace = metrics.Monospace;

            this.maxGlyphWidth = 0;
            for (int i = 0; i < this.glyphs.Length; i++)
            {
                this.maxGlyphWidth = Math.Max(this.maxGlyphWidth, this.glyphs[i].Width);
            }

            this.UpdateKerningData();
            this.GenerateTexture();
            this.GenerateMaterial();
        }
Example #9
0
		private Pixmap.Layer CreateDiffImage(Pixmap.Layer first, Pixmap.Layer second)
		{
			if (first == second) return new Pixmap.Layer(first.Width, first.Height);
			if (first.Width != second.Width) return new Pixmap.Layer(1, 1);
			if (first.Height != second.Height) return new Pixmap.Layer(1, 1);

			Pixmap.Layer diff = new Pixmap.Layer(first.Width, first.Height);
			ColorRgba[] firstData = first.Data;
			ColorRgba[] secondData = second.Data;
			ColorRgba[] diffData = diff.Data;
			for (int i = 0; i < firstData.Length; i++)
			{
				diffData[i].R = (byte)MathF.Abs(firstData[i].R - secondData[i].R);
				diffData[i].G = (byte)MathF.Abs(firstData[i].G - secondData[i].G);
				diffData[i].B = (byte)MathF.Abs(firstData[i].B - secondData[i].B);
				diffData[i].A = (byte)MathF.Abs(firstData[i].A - secondData[i].A);
			}

			return diff;
		}
Example #10
0
 private static bool IsMatch(Pixmap source, Texture target)
 {
     return target.BasePixmap == source;
 }
        private static void DrawTileHighlights(Canvas canvas, ICmpTilemapRenderer renderer, Point2 origin, IReadOnlyGrid<bool> highlight, ColorRgba fillTint, ColorRgba outlineTint, TileHighlightMode mode, List<Vector2[]> outlineCache = null)
        {
            if (highlight.Width == 0 || highlight.Height == 0) return;

            // Generate strippled line texture if not available yet
            if (strippledLineTex == null)
            {
                PixelData pixels = new PixelData(8, 1);
                for (int i = 0; i < pixels.Width / 2; i++)
                    pixels[i, 0] = ColorRgba.White;
                for (int i = pixels.Width / 2; i < pixels.Width; i++)
                    pixels[i, 0] = ColorRgba.TransparentWhite;

                using (Pixmap pixmap = new Pixmap(pixels))
                {
                    strippledLineTex = new Texture(pixmap,
                        TextureSizeMode.Default,
                        TextureMagFilter.Nearest,
                        TextureMinFilter.Nearest,
                        TextureWrapMode.Repeat,
                        TextureWrapMode.Repeat,
                        TexturePixelFormat.Rgba);
                }
            }

            BatchInfo defaultMaterial = new BatchInfo(DrawTechnique.Alpha, canvas.State.Material.MainColor);
            BatchInfo strippleMaterial = new BatchInfo(DrawTechnique.Alpha, canvas.State.Material.MainColor, strippledLineTex);
            bool uncertain = (mode & TileHighlightMode.Uncertain) != 0;
            bool selection = (mode & TileHighlightMode.Selection) != 0;

            Component component = renderer as Component;
            Transform transform = component.GameObj.Transform;
            Tilemap tilemap = renderer.ActiveTilemap;
            Tileset tileset = tilemap != null ? tilemap.Tileset.Res : null;
            Vector2 tileSize = tileset != null ? tileset.TileSize : Tileset.DefaultTileSize;
            Rect localRect = renderer.LocalTilemapRect;

            // Determine the object's local coordinate system (rotated, scaled) in world space
            Vector2 worldAxisX = Vector2.UnitX;
            Vector2 worldAxisY = Vector2.UnitY;
            MathF.TransformCoord(ref worldAxisX.X, ref worldAxisX.Y, transform.Angle, transform.Scale);
            MathF.TransformCoord(ref worldAxisY.X, ref worldAxisY.Y, transform.Angle, transform.Scale);

            Vector2 localOriginPos = tileSize * origin;
            Vector2 worldOriginPos = localOriginPos.X * worldAxisX + localOriginPos.Y * worldAxisY;

            canvas.PushState();
            {
                // Configure the canvas so our shapes are properly rotated and scaled
                canvas.State.TransformHandle = -localRect.TopLeft;
                canvas.State.TransformAngle = transform.Angle;
                canvas.State.TransformScale = new Vector2(transform.Scale);

                // Fill all highlighted tiles that are currently visible
                {
                    canvas.State.SetMaterial(defaultMaterial);
                    canvas.State.ColorTint = fillTint * ColorRgba.White.WithAlpha(selection ? 0.2f : 0.375f);

                    // Determine tile visibility
                    Vector2 worldTilemapOriginPos = localRect.TopLeft;
                    MathF.TransformCoord(ref worldTilemapOriginPos.X, ref worldTilemapOriginPos.Y, transform.Angle, transform.Scale);
                    TilemapCulling.TileInput cullingIn = new TilemapCulling.TileInput
                    {
                        // Remember: All these transform values are in world space
                        TilemapPos = transform.Pos + new Vector3(worldTilemapOriginPos) + new Vector3(worldOriginPos),
                        TilemapScale = transform.Scale,
                        TilemapAngle = transform.Angle,
                        TileCount = new Point2(highlight.Width, highlight.Height),
                        TileSize = tileSize
                    };
                    TilemapCulling.TileOutput cullingOut = TilemapCulling.GetVisibleTileRect(canvas.DrawDevice, cullingIn);
                    int renderedTileCount = cullingOut.VisibleTileCount.X * cullingOut.VisibleTileCount.Y;

                    // Draw all visible highlighted tiles
                    {
                        Point2 tileGridPos = cullingOut.VisibleTileStart;
                        Vector2 renderStartPos = worldOriginPos + tileGridPos.X * tileSize.X * worldAxisX + tileGridPos.Y * tileSize.Y * worldAxisY;;
                        Vector2 renderPos = renderStartPos;
                        Vector2 tileXStep = worldAxisX * tileSize.X;
                        Vector2 tileYStep = worldAxisY * tileSize.Y;
                        int lineMergeCount = 0;
                        int totalRects = 0;
                        for (int tileIndex = 0; tileIndex < renderedTileCount; tileIndex++)
                        {
                            bool current = highlight[tileGridPos.X, tileGridPos.Y];
                            if (current)
                            {
                                // Try to merge consecutive rects in the same line to reduce drawcalls / CPU load
                                bool hasNext = (tileGridPos.X + 1 < highlight.Width) && ((tileGridPos.X + 1 - cullingOut.VisibleTileStart.X) < cullingOut.VisibleTileCount.X);
                                bool next = hasNext ? highlight[tileGridPos.X + 1, tileGridPos.Y] : false;
                                if (next)
                                {
                                    lineMergeCount++;
                                }
                                else
                                {
                                    totalRects++;
                                    canvas.FillRect(
                                        transform.Pos.X + renderPos.X - lineMergeCount * tileXStep.X,
                                        transform.Pos.Y + renderPos.Y - lineMergeCount * tileXStep.Y,
                                        transform.Pos.Z,
                                        tileSize.X * (1 + lineMergeCount),
                                        tileSize.Y);
                                    lineMergeCount = 0;
                                }
                            }

                            tileGridPos.X++;
                            renderPos += tileXStep;
                            if ((tileGridPos.X - cullingOut.VisibleTileStart.X) >= cullingOut.VisibleTileCount.X)
                            {
                                tileGridPos.X = cullingOut.VisibleTileStart.X;
                                tileGridPos.Y++;
                                renderPos = renderStartPos;
                                renderPos += tileYStep * (tileGridPos.Y - cullingOut.VisibleTileStart.Y);
                            }
                        }
                    }
                }

                // Draw highlight area outlines, unless flagged as uncertain
                if (!uncertain)
                {
                    // Determine the outlines of individual highlighted tile patches
                    if (outlineCache == null) outlineCache = new List<Vector2[]>();
                    if (outlineCache.Count == 0)
                    {
                        GetTileAreaOutlines(highlight, tileSize, ref outlineCache);
                    }

                    // Draw outlines around all highlighted tile patches
                    canvas.State.SetMaterial(selection ? strippleMaterial : defaultMaterial);
                    canvas.State.ColorTint = outlineTint;
                    foreach (Vector2[] outline in outlineCache)
                    {
                        // For strippled-line display, determine total length of outline
                        if (selection)
                        {
                            float totalLength = 0.0f;
                            for (int i = 1; i < outline.Length; i++)
                            {
                                totalLength += (outline[i - 1] - outline[i]).Length;
                            }
                            canvas.State.TextureCoordinateRect = new Rect(totalLength / strippledLineTex.PixelWidth, 1.0f);
                        }

                        // Draw the outline
                        canvas.DrawPolygon(
                            outline,
                            transform.Pos.X + worldOriginPos.X,
                            transform.Pos.Y + worldOriginPos.Y,
                            transform.Pos.Z);
                    }
                }

                // If this is an uncertain highlight, i.e. not actually reflecting the represented action,
                // draw a gizmo to indicate this for the user.
                if (uncertain)
                {
                    Vector2 highlightSize = new Vector2(highlight.Width * tileSize.X, highlight.Height * tileSize.Y);
                    Vector2 highlightCenter = highlightSize * 0.5f;

                    Vector3 circlePos = transform.Pos + new Vector3(worldOriginPos + worldAxisX * highlightCenter + worldAxisY * highlightCenter);
                    float circleRadius = MathF.Min(tileSize.X, tileSize.Y) * 0.2f;

                    canvas.State.SetMaterial(defaultMaterial);
                    canvas.State.ColorTint = outlineTint;
                    canvas.FillCircle(
                        circlePos.X,
                        circlePos.Y,
                        circlePos.Z,
                        circleRadius);
                }
            }
            canvas.PopState();
        }
Example #12
0
        public FontRasterizer(FontFamily fontFamily, float emSize, FontStyle style, string extendedSet, bool antialiasing, bool monospace, FontRenderMode renderMode)
        {
            fontData = this.RenderGlyphs(
                fontFamily,
                emSize,
                style,
                !string.IsNullOrEmpty(extendedSet) ? new FontCharSet(extendedSet) : null,
                renderMode,
                antialiasing,
                monospace);

            FontGlyphData[] glyphs = fontData.Glyphs;
            if (glyphs == null)
            {
                this.charLookup = new int[0];
                return;
            }

            int maxCharVal = 0;

            for (int i = 0; i < glyphs.Length; i++)
            {
                maxCharVal = Math.Max(maxCharVal, (int)glyphs[i].Glyph);
            }

            this.charLookup = new int[maxCharVal + 1];
            for (int i = 0; i < glyphs.Length; i++)
            {
                this.charLookup[(int)glyphs[i].Glyph] = i;
            }


            this.pixmap       = new Pixmap(fontData.Bitmap);
            this.pixmap.Atlas = fontData.Atlas.ToList();

            bool isPixelGridAligned =
                renderMode == FontRenderMode.MonochromeBitmap || renderMode == FontRenderMode.GrayscaleBitmap || renderMode == FontRenderMode.ClearType;

            this.texture = new Texture(this.pixmap,
                                       TextureSizeMode.Enlarge,
                                       isPixelGridAligned ? TextureMagFilter.Nearest : TextureMagFilter.Linear,
                                       isPixelGridAligned ? TextureMinFilter.Nearest : TextureMinFilter.LinearMipmapLinear);

            // Select DrawTechnique to use
            ContentRef <DrawTechnique> technique;

            if (renderMode == FontRenderMode.ClearType)
            {
                technique = DrawTechnique.Alpha;
            }
            else if (renderMode == FontRenderMode.MonochromeBitmap)
            {
                technique = DrawTechnique.Mask;
            }
            else if (renderMode == FontRenderMode.GrayscaleBitmap)
            {
                technique = DrawTechnique.Alpha;
            }
            else if (renderMode == FontRenderMode.SmoothBitmap)
            {
                technique = DrawTechnique.Alpha;
            }
            else
            {
                technique = DrawTechnique.SharpAlpha;
            }

            // Create and configure internal BatchInfo
            BatchInfo matInfo = new BatchInfo(technique, this.texture);

            if (technique == DrawTechnique.SharpAlpha)
            {
                matInfo.SetValue("smoothness", this.fontData.Metrics.Size * 4.0f);
            }
            this.material = new Material(matInfo);

            this.kerningLookup = new FontKerningLookup(this.fontData.KerningPairs);
        }
Example #13
0
		/// <summary>
		/// Renders a text to the specified target Image.
		/// </summary>
		/// <param name="text"></param>
		/// <param name="target"></param>
		public void RenderToBitmap(string text, Pixmap.Layer target, float x = 0.0f, float y = 0.0f, Pixmap.Layer icons = null)
		{
			// Rendering
			int fontNum = this.fonts != null ? this.fonts.Length : 0;
			RenderState state = new RenderState(this);
			Element elem;
			while ((elem = state.NextElement()) != null)
			{
				if (elem is TextElement && state.Font != null)
				{
					TextElement textElem = elem as TextElement;
					state.Font.RenderToBitmap(
						state.CurrentElemText, 
						target, 
						x + state.CurrentElemOffset.X, 
						y + state.CurrentElemOffset.Y + state.LineBaseLine - state.Font.BaseLine, 
						state.Color);
				}
				else if (elem is IconElement)
				{
					IconElement iconElem = elem as IconElement;
					Icon icon = iconElem.IconIndex >= 0 && iconElem.IconIndex < this.icons.Length ? this.icons[iconElem.IconIndex] : new Icon();
					Vector2 iconSize = icon.size;
					Rect iconUvRect = icon.uvRect;
					Vector2 dataCoord = iconUvRect.Pos * new Vector2(icons.Width, icons.Height);
					Vector2 dataSize = iconUvRect.Size * new Vector2(icons.Width, icons.Height);
					
					Pixmap.Layer iconLayer = icons.CloneSubImage(
						MathF.RoundToInt(dataCoord.X), 
						MathF.RoundToInt(dataCoord.Y),
						MathF.RoundToInt(dataSize.X), 
						MathF.RoundToInt(dataSize.Y));
					iconLayer.Rescale(
						MathF.RoundToInt(iconSize.X), 
						MathF.RoundToInt(iconSize.Y));
					iconLayer.DrawOnto(target,
						BlendMode.Alpha,
						MathF.RoundToInt(x + state.CurrentElemOffset.X), 
						MathF.RoundToInt(y + state.CurrentElemOffset.Y + state.LineBaseLine - iconSize.Y), 
						iconLayer.Width, 
						iconLayer.Height,
						0, 
						0,
						state.Color);

				}
			}
		}
Example #14
0
		private void ReleaseResources()
		{
			if (this.material != null) this.material.Dispose();
			if (this.texture != null) this.texture.Dispose();
			if (this.pixelData != null) this.pixelData.Dispose();

			this.material = null;
			this.texture = null;
			this.pixelData = null;

			this.glyphsDirty = true;
		}
Example #15
0
		/// <summary>
		/// Applies a new set of rendered glyphs to the <see cref="Font"/>, adjusts its typeface metadata and clears out the <see cref="GlyphsDirty"/> flag.
		/// This method is used by the editor to update a Font after adjusting its properties.
		/// </summary>
		/// <param name="bitmap"></param>
		/// <param name="atlas"></param>
		/// <param name="glyphs"></param>
		/// <param name="height"></param>
		/// <param name="ascent"></param>
		/// <param name="bodyAscent"></param>
		/// <param name="descent"></param>
		/// <param name="baseLine"></param>
		public void SetGlyphData(PixelData bitmap, Rect[] atlas, GlyphData[] glyphs, int height, int ascent, int bodyAscent, int descent, int baseLine)
		{
			this.ReleaseResources();

			this.glyphs = glyphs;
			this.GenerateCharLookup();

			this.pixelData = new Pixmap(bitmap);
			this.pixelData.Atlas = atlas.ToList();
			this.height = height;
			this.ascent = ascent;
			this.bodyAscent = bodyAscent;
			this.descent = descent;
			this.baseLine = baseLine;
			for (int i = 0; i < this.glyphs.Length; i++)
			{
				this.maxGlyphWidth = Math.Max(this.maxGlyphWidth, this.glyphs[i].Width);
			}

			this.UpdateKerningData();
			this.GenerateTexMat();

			this.glyphsDirty = false;
		}
Example #16
0
		private void GenerateResources()
		{
			if (this.mat != null || this.texture != null || this.pixelData != null)
				this.ReleaseResources();

			int cols;
			int rows;
			cols = rows = (int)Math.Ceiling(Math.Sqrt(SupportedChars.Length));

			Pixmap.Layer pixelLayer = new Pixmap.Layer(MathF.RoundToInt(cols * this.internalFont.Size), MathF.RoundToInt(rows * (this.internalFont.Height + 1)));
			Pixmap.Layer glyphTemp;
			Pixmap.Layer glyphTempTypo;
			Bitmap bm;
			Bitmap measureBm = new Bitmap(1, 1);
			Rect[] atlas = new Rect[SupportedChars.Length];
			using (Graphics measureGraphics = Graphics.FromImage(measureBm))
			{
				Brush fntBrush = new SolidBrush(Color.Black);

				StringFormat formatDef = StringFormat.GenericDefault;
				formatDef.LineAlignment = StringAlignment.Near;
				formatDef.FormatFlags = 0;
				StringFormat formatTypo = StringFormat.GenericTypographic;
				formatTypo.LineAlignment = StringAlignment.Near;

				int x = 0;
				int y = 0;
				for (int i = 0; i < SupportedChars.Length; ++i)
				{
					string str = SupportedChars[i].ToString(CultureInfo.InvariantCulture);
					bool isSpace = str == " ";
					SizeF charSize = measureGraphics.MeasureString(str, this.internalFont, pixelLayer.Width, formatDef);

					// Render a single glyph
					bm = new Bitmap((int)Math.Ceiling(Math.Max(1, charSize.Width)), this.internalFont.Height + 1);
					using (Graphics glyphGraphics = Graphics.FromImage(bm))
					{
						glyphGraphics.Clear(Color.Transparent);
						glyphGraphics.TextRenderingHint = (System.Drawing.Text.TextRenderingHint)this.hint;
						glyphGraphics.DrawString(str, this.internalFont, fntBrush, new RectangleF(0, 0, bm.Width, bm.Height), formatDef);
					}
					glyphTemp = new Pixmap.Layer(bm);
					
					if (!isSpace)
					{
						Rectangle glyphTempBounds = glyphTemp.OpaqueBounds();
						glyphTemp.SubImage(glyphTempBounds.X, 0, glyphTempBounds.Width, glyphTemp.Height);
						if (BodyAscentRef.Contains(SupportedChars[i]))
							this.bodyAscent += glyphTempBounds.Height;
						
						// Render a single glyph in typographic mode
						bm = new Bitmap((int)Math.Ceiling(Math.Max(1, charSize.Width)), this.internalFont.Height + 1);
						using (Graphics glyphGraphics = Graphics.FromImage(bm))
						{
							glyphGraphics.Clear(Color.Transparent);
							glyphGraphics.TextRenderingHint = (System.Drawing.Text.TextRenderingHint)this.hint;
							glyphGraphics.DrawString(str, this.internalFont, fntBrush, new RectangleF(0, 0, bm.Width, bm.Height), formatTypo);
						}
						glyphTempTypo = new Pixmap.Layer(bm);
						glyphTempTypo.Crop(true, false);
					}
					else
						glyphTempTypo = glyphTemp;

					// Update xy values if it doesn't fit anymore
					if (x + glyphTemp.Width + 2 > pixelLayer.Width)
					{
						x = 0;
						y += (this.internalFont.Height + 1) + 2;
					}
					
					// Memorize atlas coordinates & glyph data
					this.maxGlyphWidth = Math.Max(this.maxGlyphWidth, glyphTemp.Width);
					this.glyphs[i].width = glyphTemp.Width;
					this.glyphs[i].height = glyphTemp.Height;
					this.glyphs[i].offsetX = glyphTemp.Width - glyphTempTypo.Width;
					if (isSpace)
					{
						this.glyphs[i].width /= 2;
						this.glyphs[i].offsetX /= 2;
					}
					atlas[i].X = x;
					atlas[i].Y = y;
					atlas[i].W = glyphTemp.Width;
					atlas[i].H = (this.internalFont.Height + 1);

					// Draw it onto the font surface
					glyphTemp.DrawOnto(pixelLayer, BlendMode.Solid, x, y);

					x += glyphTemp.Width + 2;
				}
			}

			// Adjust colors based on alpha value
			//for (int i = 0; i < pixelLayer.Data.Length; i++)
			System.Threading.Tasks.Parallel.For(0, pixelLayer.Data.Length, i =>
			{
				float factor = pixelLayer.Data[i].A / 255.0f;
				float invFactor = 1.0f - factor;
				pixelLayer.Data[i].R = (byte)Math.Min(255.0f, Math.Max(0.0f, this.bgColor.R * invFactor + this.color.R * factor));
				pixelLayer.Data[i].G = (byte)Math.Min(255.0f, Math.Max(0.0f, this.bgColor.G * invFactor + this.color.G * factor));
				pixelLayer.Data[i].B = (byte)Math.Min(255.0f, Math.Max(0.0f, this.bgColor.B * invFactor + this.color.B * factor));
				pixelLayer.Data[i].A = (byte)Math.Min(255.0f, Math.Max(0.0f, this.bgColor.A * invFactor + this.color.A * factor));
			});

			this.height = this.internalFont.Height;
			this.ascent = (int)Math.Round(this.internalFont.FontFamily.GetCellAscent(this.internalFont.Style) * this.internalFont.Size / this.internalFont.FontFamily.GetEmHeight(this.internalFont.Style));
			this.bodyAscent /= BodyAscentRef.Length;
			this.descent = (int)Math.Round(this.internalFont.FontFamily.GetCellDescent(this.internalFont.Style) * this.internalFont.GetHeight() / this.internalFont.FontFamily.GetLineSpacing(this.internalFont.Style));
			this.baseLine = (int)Math.Round(this.internalFont.FontFamily.GetCellAscent(this.internalFont.Style) * this.internalFont.GetHeight() / this.internalFont.FontFamily.GetLineSpacing(this.internalFont.Style));

			bool useNearest = this.hint == RenderHint.Monochrome || !this.filtering;

			this.pixelData = new Pixmap(pixelLayer);
			this.pixelData.Atlas = new List<Rect>(atlas);
			this.texture = new Texture(this.pixelData, 
				Texture.SizeMode.Enlarge, 
				useNearest ? OpenTK.Graphics.OpenGL.TextureMagFilter.Nearest : OpenTK.Graphics.OpenGL.TextureMagFilter.Linear,
				useNearest ? OpenTK.Graphics.OpenGL.TextureMinFilter.Nearest : OpenTK.Graphics.OpenGL.TextureMinFilter.LinearMipmapLinear);

			this.mat = new Material(this.hint == RenderHint.Monochrome ? DrawTechnique.Mask : DrawTechnique.Alpha, ColorRgba.White, this.texture);
		}
		private void ActionPixmapOpenRes(Pixmap pixmap)
		{
			if (pixmap == null) return;
			FileImportProvider.OpenSourceFile(pixmap, ".png", pixmap.SavePixelData);
		}
Example #18
0
 private static bool IsMatch(Pixmap source, Tileset target)
 {
     return
         target.RenderConfig != null &&
         target.RenderConfig.Any(config => config.SourceData == source);
 }
Example #19
0
        /// <summary>
        /// Loads the specified <see cref="Duality.Resources.Pixmap">Pixmaps</see> pixel data.
        /// </summary>
        /// <param name="basePixmap">The <see cref="Duality.Resources.Pixmap"/> that is used as pixel data source.</param>
        /// <param name="sizeMode">Specifies behaviour in case the source data has non-power-of-two dimensions.</param>
        public void LoadData(ContentRef <Pixmap> basePixmap, TextureSizeMode sizeMode)
        {
            if (this.nativeTex == null)
            {
                this.nativeTex = DualityApp.GraphicsBackend.CreateTexture();
            }
            this.needsReload = false;
            this.basePixmap  = basePixmap;
            this.texSizeMode = sizeMode;

            if (!this.basePixmap.IsExplicitNull)
            {
                PixelData pixelData     = null;
                Pixmap    basePixmapRes = this.basePixmap.IsAvailable ? this.basePixmap.Res : null;
                if (basePixmapRes != null)
                {
                    pixelData  = basePixmapRes.MainLayer;
                    this.atlas = basePixmapRes.Atlas != null?basePixmapRes.Atlas.ToArray() : null;
                }

                if (pixelData == null)
                {
                    pixelData = Pixmap.Checkerboard.Res.MainLayer;
                }

                this.AdjustSize(pixelData.Width, pixelData.Height);
                this.SetupNativeRes();
                if (this.texSizeMode != TextureSizeMode.NonPowerOfTwo &&
                    (this.pxWidth != this.texWidth || this.pxHeight != this.texHeight))
                {
                    if (this.texSizeMode == TextureSizeMode.Enlarge)
                    {
                        PixelData oldData = pixelData;
                        pixelData = oldData.CloneResize(this.texWidth, this.texHeight);
                        // Fill border pixels manually - that's cheaper than ColorTransparentPixels here.
                        oldData.DrawOnto(pixelData, BlendMode.Solid, this.pxWidth, 0, 1, this.pxHeight, this.pxWidth - 1, 0);
                        oldData.DrawOnto(pixelData, BlendMode.Solid, 0, this.pxHeight, this.pxWidth, 1, 0, this.pxHeight - 1);
                    }
                    else
                    {
                        pixelData = pixelData.CloneRescale(this.texWidth, this.texHeight, ImageScaleFilter.Linear);
                    }
                }

                // Load pixel data to video memory
                this.nativeTex.LoadData(
                    this.pixelformat,
                    pixelData.Width, pixelData.Height,
                    pixelData.Data,
                    ColorDataLayout.Rgba,
                    ColorDataElementType.Byte);

                // Adjust atlas to represent UV coordinates
                if (this.atlas != null)
                {
                    Vector2 scale;
                    scale.X = this.uvRatio.X / this.pxWidth;
                    scale.Y = this.uvRatio.Y / this.pxHeight;
                    for (int i = 0; i < this.atlas.Length; i++)
                    {
                        this.atlas[i].X *= scale.X;
                        this.atlas[i].W *= scale.X;
                        this.atlas[i].Y *= scale.Y;
                        this.atlas[i].H *= scale.Y;
                    }
                }
            }
            else
            {
                this.atlas = null;
                this.AdjustSize(this.size.X, this.size.Y);
                this.SetupNativeRes();
            }
        }
Example #20
0
        private void GenerateResources()
        {
            if (this.mat != null || this.texture != null || this.pixelData != null)
                this.ReleaseResources();

            TextRenderingHint textRenderingHint;
            if (this.renderMode == RenderMode.MonochromeBitmap)
                textRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit;
            else
                textRenderingHint = TextRenderingHint.AntiAliasGridFit;

            int cols;
            int rows;
            cols = rows = (int)Math.Ceiling(Math.Sqrt(SupportedChars.Length));

            Pixmap.Layer pixelLayer = new Pixmap.Layer(MathF.RoundToInt(cols * this.internalFont.Size * 1.2f), MathF.RoundToInt(rows * this.internalFont.Height * 1.2f));
            Pixmap.Layer glyphTemp;
            Pixmap.Layer glyphTempTypo;
            Bitmap bm;
            Bitmap measureBm = new Bitmap(1, 1);
            Rect[] atlas = new Rect[SupportedChars.Length];
            using (Graphics measureGraphics = Graphics.FromImage(measureBm))
            {
                Brush fntBrush = new SolidBrush(Color.Black);

                StringFormat formatDef = StringFormat.GenericDefault;
                formatDef.LineAlignment = StringAlignment.Near;
                formatDef.FormatFlags = 0;
                StringFormat formatTypo = StringFormat.GenericTypographic;
                formatTypo.LineAlignment = StringAlignment.Near;

                int x = 1;
                int y = 1;
                for (int i = 0; i < SupportedChars.Length; ++i)
                {
                    string str = SupportedChars[i].ToString(CultureInfo.InvariantCulture);
                    bool isSpace = str == " ";
                    SizeF charSize = measureGraphics.MeasureString(str, this.internalFont, pixelLayer.Width, formatDef);

                    // Rasterize a single glyph for rendering
                    bm = new Bitmap((int)Math.Ceiling(Math.Max(1, charSize.Width)), this.internalFont.Height + 1);
                    using (Graphics glyphGraphics = Graphics.FromImage(bm))
                    {
                        glyphGraphics.Clear(Color.Transparent);
                        glyphGraphics.TextRenderingHint = textRenderingHint;
                        glyphGraphics.DrawString(str, this.internalFont, fntBrush, new RectangleF(0, 0, bm.Width, bm.Height), formatDef);
                    }
                    glyphTemp = new Pixmap.Layer(bm);

                    // Rasterize a single glyph in typographic mode for metric analysis
                    if (!isSpace)
                    {
                        Rectangle glyphTempBounds = glyphTemp.OpaqueBounds();
                        glyphTemp.SubImage(glyphTempBounds.X, 0, glyphTempBounds.Width, glyphTemp.Height);
                        if (BodyAscentRef.Contains(SupportedChars[i]))
                            this.bodyAscent += glyphTempBounds.Height;

                        bm = new Bitmap((int)Math.Ceiling(Math.Max(1, charSize.Width)), this.internalFont.Height + 1);
                        using (Graphics glyphGraphics = Graphics.FromImage(bm))
                        {
                            glyphGraphics.Clear(Color.Transparent);
                            glyphGraphics.TextRenderingHint = textRenderingHint;
                            glyphGraphics.DrawString(str, this.internalFont, fntBrush, new RectangleF(0, 0, bm.Width, bm.Height), formatTypo);
                        }
                        glyphTempTypo = new Pixmap.Layer(bm);
                        glyphTempTypo.Crop(true, false);
                    }
                    else
                    {
                        glyphTempTypo = glyphTemp;
                    }

                    // Update xy values if it doesn't fit anymore
                    if (x + glyphTemp.Width + 2 > pixelLayer.Width)
                    {
                        x = 1;
                        y += this.internalFont.Height + MathF.Clamp((int)MathF.Ceiling(this.internalFont.Height * 0.1875f), 3, 10);
                    }

                    // Memorize atlas coordinates & glyph data
                    this.maxGlyphWidth = Math.Max(this.maxGlyphWidth, glyphTemp.Width);
                    this.glyphs[i].width = glyphTemp.Width;
                    this.glyphs[i].height = glyphTemp.Height;
                    this.glyphs[i].offsetX = glyphTemp.Width - glyphTempTypo.Width;
                    if (isSpace)
                    {
                        this.glyphs[i].width /= 2;
                        this.glyphs[i].offsetX /= 2;
                    }
                    atlas[i].X = x;
                    atlas[i].Y = y;
                    atlas[i].W = glyphTemp.Width;
                    atlas[i].H = (this.internalFont.Height + 1);

                    // Draw it onto the font surface
                    glyphTemp.DrawOnto(pixelLayer, BlendMode.Solid, x, y);

                    x += glyphTemp.Width + MathF.Clamp((int)MathF.Ceiling(this.internalFont.Height * 0.125f), 2, 10);
                }
            }

            // White out texture except alpha channel.
            for (int i = 0; i < pixelLayer.Data.Length; i++)
            {
                pixelLayer.Data[i].R = 255;
                pixelLayer.Data[i].G = 255;
                pixelLayer.Data[i].B = 255;
            }

            // Determine Font properties
            this.height = this.internalFont.Height;
            this.ascent = (int)Math.Round(this.internalFont.FontFamily.GetCellAscent(this.internalFont.Style) * this.internalFont.Size / this.internalFont.FontFamily.GetEmHeight(this.internalFont.Style));
            this.bodyAscent /= BodyAscentRef.Length;
            this.descent = (int)Math.Round(this.internalFont.FontFamily.GetCellDescent(this.internalFont.Style) * this.internalFont.GetHeight() / this.internalFont.FontFamily.GetLineSpacing(this.internalFont.Style));
            this.baseLine = (int)Math.Round(this.internalFont.FontFamily.GetCellAscent(this.internalFont.Style) * this.internalFont.GetHeight() / this.internalFont.FontFamily.GetLineSpacing(this.internalFont.Style));

            // Create internal Pixmap and Texture Resources
            this.pixelData = new Pixmap(pixelLayer);
            this.pixelData.Atlas = new List<Rect>(atlas);
            this.texture = new Texture(this.pixelData,
                Texture.SizeMode.Enlarge,
                this.IsPixelGridAligned ? TextureMagFilter.Nearest : TextureMagFilter.Linear,
                this.IsPixelGridAligned ? TextureMinFilter.Nearest : TextureMinFilter.LinearMipmapLinear);

            // Select DrawTechnique to use
            ContentRef<DrawTechnique> technique;
            if (this.renderMode == RenderMode.MonochromeBitmap)
                technique = DrawTechnique.Mask;
            else if (this.renderMode == RenderMode.GrayscaleBitmap)
                technique = DrawTechnique.Alpha;
            else if (this.renderMode == RenderMode.SmoothBitmap)
                technique = DrawTechnique.Alpha;
            else
                technique = DrawTechnique.SharpAlpha;

            // Create and configure internal BatchInfo
            BatchInfo matInfo = new BatchInfo(technique, ColorRgba.White, this.texture);
            if (technique == DrawTechnique.SharpAlpha)
            {
                matInfo.SetUniform("smoothness", this.size * 3.0f);
            }
            this.mat = new Material(matInfo);
        }
Example #21
0
        private void ReleaseResources()
        {
            if (this.mat != null) this.mat.Dispose();
            if (this.texture != null) this.texture.Dispose();
            if (this.pixelData != null) this.pixelData.Dispose();

            this.mat = null;
            this.texture = null;
            this.pixelData = null;

            this.needsReload = true;
        }
Example #22
0
		private bool AreImagesEqual(Pixmap.Layer first, Pixmap.Layer second)
		{
			if (first == second) return true;
			if (first.Width != second.Width) return false;
			if (first.Height != second.Height) return false;

			ColorRgba[] firstData = first.Data;
			ColorRgba[] secondData = second.Data;
			float error = 0;
			float maxError = firstData.Length; // (1/255) off per pixel is probably okay.
			for (int i = 0; i < firstData.Length; i++)
			{
				error += MathF.Abs(firstData[i].R * (firstData[i].A / 255.0f) - secondData[i].R * (secondData[i].A / 255.0f));
				error += MathF.Abs(firstData[i].G * (firstData[i].A / 255.0f) - secondData[i].G * (secondData[i].A / 255.0f));
				error += MathF.Abs(firstData[i].B * (firstData[i].A / 255.0f) - secondData[i].B * (secondData[i].A / 255.0f));
				error += MathF.Abs(firstData[i].A - secondData[i].A);
				if (error >= maxError) return false;
			}

			return true;
		}
Example #23
0
 /// <summary>
 /// Renders a text to the specified target <see cref="Duality.Resources.Pixmap"/> <see cref="Duality.Resources.Pixmap.Layer"/>.
 /// </summary>
 /// <param name="text"></param>
 /// <param name="target"></param>
 /// <param name="x"></param>
 /// <param name="y"></param>
 public void RenderToBitmap(string text, Pixmap.Layer target, float x = 0.0f, float y = 0.0f)
 {
     this.RenderToBitmap(text, target, x, y, ColorRgba.White);
 }
Example #24
0
        /// <summary>
        /// Compiles a <see cref="Tileset"/> using its specified source data, in order to
        /// generate optimized target data for rendering and collision detection.
        /// </summary>
        public TilesetCompilerOutput Compile(TilesetCompilerInput input)
        {
            TilesetCompilerOutput output = input.ExistingOutput;
            output.TileData = output.TileData ?? new RawList<TileInfo>(input.TileInput.Count);
            output.RenderData = output.RenderData ?? new List<Texture>();
            output.AutoTileData = output.AutoTileData ?? new List<TilesetAutoTileInfo>();

            // Clear existing data, but keep the sufficiently big data structures
            output.TileData.Clear();
            output.RenderData.Clear();
            output.AutoTileData.Clear();

            // Determine how many source tiles we have
            int sourceTileCount = int.MaxValue;
            for (int renderInputIndex = 0; renderInputIndex < input.RenderConfig.Count; renderInputIndex++)
            {
                TilesetRenderInput renderInput = input.RenderConfig[renderInputIndex] ?? DefaultRenderInput;
                PixelData sourceLayerData = (renderInput.SourceData.Res ?? Pixmap.Checkerboard.Res).MainLayer;
                LayerGeometry layerGeometry = this.CalculateLayerGeometry(renderInput, sourceLayerData);
                sourceTileCount = Math.Min(sourceTileCount, layerGeometry.SourceTileCount);
            }
            if (input.RenderConfig.Count == 0) sourceTileCount = 0;

            // Transform AutoTile data
            for (int autoTileIndex = 0; autoTileIndex < input.AutoTileConfig.Count; autoTileIndex++)
            {
                TilesetAutoTileInput autoTileInput = input.AutoTileConfig[autoTileIndex];
                TilesetAutoTileInfo autoTileInfo = this.TransformAutoTileData(
                    autoTileIndex,
                    autoTileInput,
                    output.TileData,
                    sourceTileCount);
                output.AutoTileData.Add(autoTileInfo);
            }

            // Initialize all tiles to being visually empty. They will be subtractively updated
            // during output pixel data generation in the next step.
            {
                int tileDataCount = output.TileData.Count;
                TileInfo[] tileData = output.TileData.Data;
                for (int i = 0; i < tileDataCount; i++)
                {
                    tileData[i].IsVisuallyEmpty = true;
                }
            }

            // Generate output pixel data
            for (int renderInputIndex = 0; renderInputIndex < input.RenderConfig.Count; renderInputIndex++)
            {
                TilesetRenderInput renderInput = input.RenderConfig[renderInputIndex] ?? DefaultRenderInput;
                PixelData sourceLayerData = (renderInput.SourceData.Res ?? Pixmap.Checkerboard.Res).MainLayer;

                // Determine overal geometry values for this layer, such as tile bounds and texture sizes
                LayerGeometry layerGeometry = this.CalculateLayerGeometry(renderInput, sourceLayerData);

                // Generate pixel data and atlas values for this layer's texture
                LayerPixelData targetLayerData = this.GenerateLayerPixelData(
                    renderInput,
                    sourceLayerData,
                    layerGeometry,
                    output.TileData);

                // Create the texture to be used for this rendering input
                using (Pixmap targetPixmap = new Pixmap(targetLayerData.PixelData))
                {
                    targetPixmap.Atlas = targetLayerData.Atlas;
                    Texture targetTexture = new Texture(
                        targetPixmap, TextureSizeMode.Enlarge,
                        renderInput.TargetMagFilter, renderInput.TargetMinFilter,
                        TextureWrapMode.Clamp, TextureWrapMode.Clamp,
                        renderInput.TargetFormat);

                    output.RenderData.Add(targetTexture);
                }
            }

            // Generate additional per-tile data
            this.TransformTileData(input.TileInput, output.TileData, output.RenderData);

            // Apply global tileset stats
            output.TileCount = sourceTileCount;

            return output;
        }
Example #25
0
        private void LoadOrCreatePixelData(int cols, int rows, TextRenderingHint textRenderingHint)
        {
            Pixmap.Layer pixelLayer = new Pixmap.Layer(MathF.RoundToInt(cols*this.internalFont.Size*1.2f), MathF.RoundToInt(rows*this.internalFont.Height*1.2f));
            Pixmap.Layer glyphTemp;
            Pixmap.Layer glyphTempTypo;
            Bitmap bm;
            Bitmap measureBm = new Bitmap(1, 1);
            Rect[] atlas = new Rect[SupportedChars.Length];
            using (Graphics measureGraphics = Graphics.FromImage(measureBm))
            {
                Brush fntBrush = new SolidBrush(Color.Black);

                StringFormat formatDef = StringFormat.GenericDefault;
                formatDef.LineAlignment = StringAlignment.Near;
                formatDef.FormatFlags = 0;
                StringFormat formatTypo = StringFormat.GenericTypographic;
                formatTypo.LineAlignment = StringAlignment.Near;

                int x = 1;
                int y = 1;
                for (int i = 0; i < SupportedChars.Length; ++i)
                {
                    string str = SupportedChars[i].ToString(CultureInfo.InvariantCulture);
                    bool isSpace = str == " ";
                    SizeF charSize = measureGraphics.MeasureString(str, this.internalFont, pixelLayer.Width, formatDef);

                    // Rasterize a single glyph for rendering
                    bm = new Bitmap((int)Math.Ceiling(Math.Max(1, charSize.Width)), this.internalFont.Height + 1);
                    using (Graphics glyphGraphics = Graphics.FromImage(bm))
                    {
                        glyphGraphics.Clear(Color.Transparent);
                        glyphGraphics.TextRenderingHint = textRenderingHint;
                        glyphGraphics.DrawString(str, this.internalFont, fntBrush, new RectangleF(0, 0, bm.Width, bm.Height), formatDef);
                    }
                    glyphTemp = new Pixmap.Layer(bm);

                    // Rasterize a single glyph in typographic mode for metric analysis
                    if (!isSpace)
                    {
                        Rectangle glyphTempBounds = glyphTemp.OpaqueBounds();
                        glyphTemp.SubImage(glyphTempBounds.X, 0, glyphTempBounds.Width, glyphTemp.Height);
                        if (BodyAscentRef.Contains(SupportedChars[i]))
                            this.bodyAscent += glyphTempBounds.Height;

                        bm = new Bitmap((int) Math.Ceiling(Math.Max(1, charSize.Width)), this.internalFont.Height + 1);
                        using (Graphics glyphGraphics = Graphics.FromImage(bm))
                        {
                            glyphGraphics.Clear(Color.Transparent);
                            glyphGraphics.TextRenderingHint = textRenderingHint;
                            glyphGraphics.DrawString(str, this.internalFont, fntBrush, new RectangleF(0, 0, bm.Width, bm.Height), formatTypo);
                        }
                        glyphTempTypo = new Pixmap.Layer(bm);
                        glyphTempTypo.Crop(true, false);
                    }
                    else
                    {
                        glyphTempTypo = glyphTemp;
                    }

                    // Update xy values if it doesn't fit anymore
                    if (x + glyphTemp.Width + 2 > pixelLayer.Width)
                    {
                        x = 1;
                        y += this.internalFont.Height + MathF.Clamp((int) MathF.Ceiling(this.internalFont.Height*0.1875f), 3, 10);
                    }

                    // Memorize atlas coordinates & glyph data
                    this.maxGlyphWidth = Math.Max(this.maxGlyphWidth, glyphTemp.Width);
                    this.glyphs[i].width = glyphTemp.Width;
                    this.glyphs[i].height = glyphTemp.Height;
                    this.glyphs[i].offsetX = glyphTemp.Width - glyphTempTypo.Width;
                    if (isSpace)
                    {
                        this.glyphs[i].width /= 2;
                        this.glyphs[i].offsetX /= 2;
                    }
                    atlas[i].X = x;
                    atlas[i].Y = y;
                    atlas[i].W = glyphTemp.Width;
                    atlas[i].H = (this.internalFont.Height + 1);

                    // Draw it onto the font surface
                    glyphTemp.DrawOnto(pixelLayer, BlendMode.Solid, x, y);

                    x += glyphTemp.Width + MathF.Clamp((int) MathF.Ceiling(this.internalFont.Height*0.125f), 2, 10);

                    if(glyphTempTypo != glyphTemp)
                        glyphTempTypo.Dispose();

                    glyphTemp.Dispose();
                }
            }

            // White out texture except alpha channel.
            for (int i = 0; i < pixelLayer.Data.Length; i++)
            {
                pixelLayer.Data[i].R = 255;
                pixelLayer.Data[i].G = 255;
                pixelLayer.Data[i].B = 255;
            }

            this.pixelData = new Pixmap(pixelLayer) {Atlas = new List<Rect>(atlas)};
        }