Example #1
0
        void ISerializeExplicit.ReadData(IDataReader reader)
        {
            int version;

            try { reader.ReadValue("version", out version); }
            catch (Exception) { version = Serialize_Version_Unknown; }

            string formatId;

            if (version == Serialize_Version_FormatId)
            {
                reader.ReadValue("formatId", out formatId);
            }
            else if (version == Serialize_Version_LayerPng)
            {
                formatId = ImageCodec.FormatPng;
            }
            else
            {
                throw new NotSupportedException(string.Format(
                                                    "Unknown PixelData serialization version '{0}'. Can't load image data.",
                                                    version));
            }

            IImageCodec codec = ImageCodec.GetRead(formatId);

            if (codec == null)
            {
                throw new NotSupportedException(string.Format(
                                                    "Unable to retrieve image codec for format '{0}'. Can't load image data.",
                                                    formatId));
            }

            byte[] dataBlock;
            reader.ReadValue("pixelData", out dataBlock);
            using (MemoryStream stream = new MemoryStream(dataBlock))
            {
                PixelData pixelData = codec.Read(stream);
                this.data   = pixelData.data;
                this.width  = pixelData.width;
                this.height = pixelData.height;
                pixelData   = null;
            }
        }
Example #2
0
        /// <summary>
        /// Renders a text to the specified target Image.
        /// </summary>
        /// <param name="text"></param>
        /// <param name="target"></param>
        public void RenderToBitmap(string text, PixelData target, float x = 0.0f, float y = 0.0f, PixelData 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;
                    Vector2 iconOffset = icon.offset;
                    Rect iconUvRect = icon.uvRect;
                    Vector2 dataCoord = iconUvRect.Pos * new Vector2(icons.Width, icons.Height);
                    Vector2 dataSize = iconUvRect.Size * new Vector2(icons.Width, icons.Height);

                    PixelData 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 + iconOffset.X),
                        MathF.RoundToInt(y + state.CurrentElemOffset.Y + state.LineBaseLine - iconSize.Y + iconOffset.Y),
                        iconLayer.Width,
                        iconLayer.Height,
                        0,
                        0,
                        state.Color);

                }
            }
        }
Example #3
0
        /// <summary>
        /// Generates pixel and atlas data for a single <see cref="Tileset"/> visual layer.
        /// </summary>
        /// <param name="renderInput"></param>
        /// <param name="sourceData"></param>
        /// <param name="geometry"></param>
        /// <param name="tileData"></param>
        /// <returns></returns>
        private LayerPixelData GenerateLayerPixelData(TilesetRenderInput renderInput, PixelData sourceData, LayerGeometry geometry, RawList<TileInfo> tileData)
        {
            // Create a buffer for writing target pixel data
            LayerPixelData target;
            target.PixelData = new PixelData(geometry.TargetTextureSize.X, geometry.TargetTextureSize.Y);
            target.Atlas = new List<Rect>();

            // Iterate over tiles and move each tile from source to target
            Point2 targetTilePos = new Point2(0, 0);
            for (int tileIndex = 0; tileIndex < geometry.SourceTileCount; tileIndex++)
            {
                // Initialize a new tile info when necessary
                if (tileIndex >= tileData.Count)
                {
                    tileData.Count++;
                    tileData.Data[tileIndex].IsVisuallyEmpty = true;
                }

                // Determine where on the source buffer the tile is located
                Point2 sourceTilePos = new Point2(
                    geometry.SourceTileAdvance.X * (tileIndex % geometry.SourceTilesPerRow),
                    geometry.SourceTileAdvance.Y * (tileIndex / geometry.SourceTilesPerRow));

                // Draw the source tile onto the target buffer, including its spacing / border
                Point2 targetContentPos = new Point2(
                    targetTilePos.X + renderInput.TargetTileMargin,
                    targetTilePos.Y + renderInput.TargetTileMargin);
                sourceData.DrawOnto(target.PixelData,
                    BlendMode.Solid,
                    targetContentPos.X,
                    targetContentPos.Y,
                    renderInput.SourceTileSize.X,
                    renderInput.SourceTileSize.Y,
                    sourceTilePos.X,
                    sourceTilePos.Y);

                // Fill up the target spacing area with similar pixels
                if (renderInput.TargetTileMargin > 0)
                {
                    FillTileSpacing(target.PixelData, renderInput.TargetTileMargin, targetContentPos, renderInput.SourceTileSize);
                }

                // Update whether the tile is considered visually empty
                if (tileData.Data[tileIndex].IsVisuallyEmpty)
                {
                    bool isLayerVisuallyEmpty = IsCompletelyTransparent(
                        sourceData,
                        sourceTilePos,
                        renderInput.SourceTileSize);
                    if (!isLayerVisuallyEmpty)
                        tileData.Data[tileIndex].IsVisuallyEmpty = false;
                }

                // Add an entry to the generated atlas
                Rect atlasRect = new Rect(
                    targetTilePos.X + renderInput.TargetTileMargin,
                    targetTilePos.Y + renderInput.TargetTileMargin,
                    geometry.TargetTileAdvance.X - renderInput.TargetTileMargin * 2,
                    geometry.TargetTileAdvance.Y - renderInput.TargetTileMargin * 2);
                target.Atlas.Add(atlasRect);

                // Advance the target tile position
                targetTilePos.X += geometry.TargetTileAdvance.X;
                if (targetTilePos.X + geometry.TargetTileAdvance.X > target.PixelData.Width)
                {
                    targetTilePos.X = 0;
                    targetTilePos.Y += geometry.TargetTileAdvance.Y;
                }
            }

            return target;
        }
Example #4
0
        /// <summary>
        /// Determines the overall geometry of a single <see cref="Tileset"/> visual layer. This involves
        /// tile boundaries in source and target data, as well as texture sizes and similar.
        /// </summary>
        /// <param name="renderInput"></param>
        /// <param name="layerData"></param>
        /// <returns></returns>
        private LayerGeometry CalculateLayerGeometry(TilesetRenderInput renderInput, PixelData layerData)
        {
            LayerGeometry geometry;

            // What's the space requirement for each tile?
            geometry.SourceTileAdvance = renderInput.SourceTileAdvance;
            geometry.TargetTileAdvance = renderInput.TargetTileAdvance;

            // How many tiles will we have?
            Point2 tileCount = renderInput.GetSourceTileCount(layerData.Width, layerData.Height);
            geometry.SourceTilesPerRow = tileCount.X;
            geometry.SourceTilesPerColumn = tileCount.Y;
            geometry.SourceTileCount = geometry.SourceTilesPerRow * geometry.SourceTilesPerColumn;
            geometry.TargetTileCount = geometry.SourceTileCount; // ToDo: Account for expanded AutoTiles

            // What's the optimal texture size to include them all?
            int minTilesPerLine = MathF.Max(1, (int)MathF.Sqrt(geometry.TargetTileCount));
            geometry.TargetTextureSize.X = MathF.NextPowerOfTwo(geometry.TargetTileAdvance.X * minTilesPerLine);

            int actualTilesPerLine = geometry.TargetTextureSize.X / geometry.TargetTileAdvance.X;
            int requiredLineCount = 1 + (geometry.TargetTileCount / actualTilesPerLine);
            geometry.TargetTextureSize.Y = MathF.NextPowerOfTwo(geometry.TargetTileAdvance.Y * requiredLineCount);

            return geometry;
        }
Example #5
0
 /// <summary>
 /// Determines whether the specified pixel data block is completely transparent in
 /// the specified area.
 /// </summary>
 /// <param name="data"></param>
 /// <param name="pos"></param>
 /// <param name="size"></param>
 /// <returns></returns>
 private static bool IsCompletelyTransparent(PixelData data, Point2 pos, Point2 size)
 {
     for (int y = pos.Y; y < pos.Y + size.Y; y++)
     {
         for (int x = pos.X; x < pos.X + size.X; x++)
         {
             if (data[x, y].A > 0)
                 return false;
         }
     }
     return true;
 }
Example #6
0
        /// <summary>
        /// Fills up the specified spacing around a tile's pixel data with colors that are
        /// similar to the existing edge colors in order to prevent filtering artifacts
        /// when rendering them as a texture atlas.
        /// </summary>
        /// <param name="targetData"></param>
        /// <param name="targetTileSpacing"></param>
        /// <param name="targetContentPos"></param>
        /// <param name="targetTileSize"></param>
        private static void FillTileSpacing(PixelData targetData, int targetTileSpacing, Point2 targetContentPos, Point2 targetTileSize)
        {
            ColorRgba[] rawData = targetData.Data;
            int width = targetData.Width;
            int baseIndex;
            int offsetIndex;

            // Top
            for (int offset = 1; offset <= targetTileSpacing; offset++)
            {
                baseIndex = targetContentPos.Y * width + targetContentPos.X;
                offsetIndex = (targetContentPos.Y - offset) * width + targetContentPos.X;
                for (int i = 0; i < targetTileSize.X; i++)
                {
                    rawData[offsetIndex + i] = rawData[baseIndex + i];
                }
            }

            // Bottom
            for (int offset = 1; offset <= targetTileSpacing; offset++)
            {
                baseIndex = (targetContentPos.Y + targetTileSize.Y - 1) * width + targetContentPos.X;
                offsetIndex = (targetContentPos.Y + targetTileSize.Y - 1 + offset) * width + targetContentPos.X;
                for (int i = 0; i < targetTileSize.X; i++)
                {
                    rawData[offsetIndex + i] = rawData[baseIndex + i];
                }
            }

            // Left
            for (int offset = 1; offset <= targetTileSpacing; offset++)
            {
                baseIndex = targetContentPos.Y * width + targetContentPos.X;
                offsetIndex = targetContentPos.Y * width + targetContentPos.X - offset;
                for (int i = 0; i < targetTileSize.X; i++)
                {
                    rawData[offsetIndex + i * width] = rawData[baseIndex + i * width];
                }
            }

            // Right
            for (int offset = 1; offset <= targetTileSpacing; offset++)
            {
                baseIndex = targetContentPos.Y * width + targetContentPos.X + targetTileSize.X - 1;
                offsetIndex = targetContentPos.Y * width + targetContentPos.X + targetTileSize.X - 1 + offset;
                for (int i = 0; i < targetTileSize.X; i++)
                {
                    rawData[offsetIndex + i * width] = rawData[baseIndex + i * width];
                }
            }

            // Top Left Corner
            baseIndex = targetContentPos.Y * width + targetContentPos.X;
            for (int offsetY = 1; offsetY <= targetTileSpacing; offsetY++)
            {
                for (int offsetX = 1; offsetX <= targetTileSpacing; offsetX++)
                {
                    offsetIndex = baseIndex - offsetX - offsetY * width;
                    rawData[offsetIndex] = rawData[baseIndex];
                }
            }

            // Top Right Corner
            baseIndex = targetContentPos.Y * width + targetContentPos.X + targetTileSize.X - 1;
            for (int offsetY = 1; offsetY <= targetTileSpacing; offsetY++)
            {
                for (int offsetX = 1; offsetX <= targetTileSpacing; offsetX++)
                {
                    offsetIndex = baseIndex + offsetX - offsetY * width;
                    rawData[offsetIndex] = rawData[baseIndex];
                }
            }

            // Bottom Left Corner
            baseIndex = (targetContentPos.Y + targetTileSize.Y - 1) * width + targetContentPos.X;
            for (int offsetY = 1; offsetY <= targetTileSpacing; offsetY++)
            {
                for (int offsetX = 1; offsetX <= targetTileSpacing; offsetX++)
                {
                    offsetIndex = baseIndex - offsetX + offsetY * width;
                    rawData[offsetIndex] = rawData[baseIndex];
                }
            }

            // Bottom Right Corner
            baseIndex = (targetContentPos.Y + targetTileSize.Y - 1) * width + targetContentPos.X + targetTileSize.X - 1;
            for (int offsetY = 1; offsetY <= targetTileSpacing; offsetY++)
            {
                for (int offsetX = 1; offsetX <= targetTileSpacing; offsetX++)
                {
                    offsetIndex = baseIndex + offsetX + offsetY * width;
                    rawData[offsetIndex] = rawData[baseIndex];
                }
            }
        }
Example #7
0
        /// <summary>
        /// Performs a drawing operation from this Layer to a target layer.
        /// </summary>
        /// <param name="target"></param>
        /// <param name="blend"></param>
        /// <param name="destX"></param>
        /// <param name="destY"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="srcX"></param>
        /// <param name="srcY"></param>
        public void DrawOnto(PixelData target, BlendMode blend, int destX, int destY, int width = -1, int height = -1, int srcX = 0, int srcY = 0)
        {
            if (width == -1)
            {
                width = this.width;
            }
            if (height == -1)
            {
                height = this.height;
            }

            int beginX = MathF.Max(0, -destX, -srcX);
            int beginY = MathF.Max(0, -destY, -srcY);
            int endX   = MathF.Min(width, this.width, target.width - destX, this.width - srcX);
            int endY   = MathF.Min(height, this.height, target.height - destY, this.height - srcY);

            if (endX - beginX < 1)
            {
                return;
            }
            if (endY - beginY < 1)
            {
                return;
            }

            Parallel.ForEach(Partitioner.Create(beginX, endX), range =>
            {
                for (int i = range.Item1; i < range.Item2; i++)
                {
                    for (int j = beginY; j < endY; j++)
                    {
                        int sourceN = srcX + i + this.width * (srcY + j);
                        int targetN = destX + i + target.width * (destY + j);

                        if (blend == BlendMode.Solid)
                        {
                            target.data[targetN] = this.data[sourceN];
                        }
                        else if (blend == BlendMode.Mask)
                        {
                            // ToDo: it was >= 0? really?
                            if (this.data[sourceN].A > 0)
                            {
                                target.data[targetN] = this.data[sourceN];
                            }
                        }
                        else if (blend == BlendMode.Add)
                        {
                            ColorRgba targetColor  = target.data[targetN];
                            float alphaTemp        = (float)this.data[sourceN].A / 255.0f;
                            target.data[targetN].R = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(targetColor.R + this.data[sourceN].R * alphaTemp)));
                            target.data[targetN].G = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(targetColor.G + this.data[sourceN].G * alphaTemp)));
                            target.data[targetN].B = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(targetColor.B + this.data[sourceN].B * alphaTemp)));
                            target.data[targetN].A = (byte)Math.Min(255, Math.Max(0, (int)targetColor.A + (int)this.data[sourceN].A));
                        }
                        else if (blend == BlendMode.Alpha)
                        {
                            ColorRgba targetColor  = target.data[targetN];
                            float alphaTemp        = (float)this.data[sourceN].A / 255.0f;
                            target.data[targetN].R = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(targetColor.R * (1.0f - alphaTemp) + this.data[sourceN].R * alphaTemp)));
                            target.data[targetN].G = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(targetColor.G * (1.0f - alphaTemp) + this.data[sourceN].G * alphaTemp)));
                            target.data[targetN].B = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(targetColor.B * (1.0f - alphaTemp) + this.data[sourceN].B * alphaTemp)));
                            target.data[targetN].A = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(targetColor.A * (1.0f - alphaTemp) + this.data[sourceN].A)));
                        }
                        else if (blend == BlendMode.AlphaPre)
                        {
                            ColorRgba targetColor  = target.data[targetN];
                            float alphaTemp        = (float)this.data[sourceN].A / 255.0f;
                            target.data[targetN].R = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(targetColor.R * (1.0f - alphaTemp) + this.data[sourceN].R)));
                            target.data[targetN].G = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(targetColor.G * (1.0f - alphaTemp) + this.data[sourceN].G)));
                            target.data[targetN].B = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(targetColor.B * (1.0f - alphaTemp) + this.data[sourceN].B)));
                            target.data[targetN].A = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(targetColor.A * (1.0f - alphaTemp) + this.data[sourceN].A)));
                        }
                        else if (blend == BlendMode.Multiply)
                        {
                            ColorRgba targetColor  = target.data[targetN];
                            float clrTempR         = (float)targetColor.R / 255.0f;
                            float clrTempG         = (float)targetColor.G / 255.0f;
                            float clrTempB         = (float)targetColor.B / 255.0f;
                            float clrTempA         = (float)targetColor.A / 255.0f;
                            target.data[targetN].R = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(this.data[sourceN].R * clrTempR)));
                            target.data[targetN].G = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(this.data[sourceN].G * clrTempG)));
                            target.data[targetN].B = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(this.data[sourceN].B * clrTempB)));
                            target.data[targetN].A = (byte)Math.Min(255, Math.Max(0, (int)targetColor.A + (int)this.data[sourceN].A));
                        }
                        else if (blend == BlendMode.Light)
                        {
                            ColorRgba targetColor  = target.data[targetN];
                            float clrTempR         = (float)targetColor.R / 255.0f;
                            float clrTempG         = (float)targetColor.G / 255.0f;
                            float clrTempB         = (float)targetColor.B / 255.0f;
                            float clrTempA         = (float)targetColor.A / 255.0f;
                            target.data[targetN].R = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(this.data[sourceN].R * clrTempR + targetColor.R)));
                            target.data[targetN].G = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(this.data[sourceN].G * clrTempG + targetColor.G)));
                            target.data[targetN].B = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(this.data[sourceN].B * clrTempB + targetColor.B)));
                            target.data[targetN].A = (byte)Math.Min(255, Math.Max(0, (int)targetColor.A + (int)this.data[sourceN].A));
                        }
                        else if (blend == BlendMode.Invert)
                        {
                            ColorRgba targetColor  = target.data[targetN];
                            float clrTempR         = (float)targetColor.R / 255.0f;
                            float clrTempG         = (float)targetColor.G / 255.0f;
                            float clrTempB         = (float)targetColor.B / 255.0f;
                            float clrTempA         = (float)targetColor.A / 255.0f;
                            float clrTempR2        = (float)this.data[sourceN].R / 255.0f;
                            float clrTempG2        = (float)this.data[sourceN].G / 255.0f;
                            float clrTempB2        = (float)this.data[sourceN].B / 255.0f;
                            float clrTempA2        = (float)this.data[sourceN].A / 255.0f;
                            target.data[targetN].R = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(this.data[sourceN].R * (1.0f - clrTempR) + targetColor.R * (1.0f - clrTempR2))));
                            target.data[targetN].G = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(this.data[sourceN].G * (1.0f - clrTempG) + targetColor.G * (1.0f - clrTempG2))));
                            target.data[targetN].B = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(this.data[sourceN].B * (1.0f - clrTempB) + targetColor.B * (1.0f - clrTempB2))));
                            target.data[targetN].A = (byte)Math.Min(255, Math.Max(0, (int)(targetColor.A + this.data[sourceN].A)));
                        }
                    }
                }
            });
        }
Example #8
0
		/// <summary>
		/// Extracts a rectangular region of this Layer. If the extracted region is bigger than the original Layer,
		/// all new space is filled with a background color.
		/// </summary>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <param name="w"></param>
		/// <param name="h"></param>
		/// <param name="backColor"></param>
		public PixelData CloneSubImage(int x, int y, int w, int h, ColorRgba backColor)
		{
			PixelData tempLayer = new PixelData(w, h, backColor);
			this.DrawOnto(tempLayer, BlendMode.Solid, -x, -y);
			return tempLayer;
		}
Example #9
0
		/// <summary>
		/// Creates a new Pixmap from the specified <see cref="Duality.Drawing.PixelData"/>.
		/// </summary>
		/// <param name="image"></param>
		public Pixmap(PixelData image)
		{
			this.MainLayer = image;
		}
Example #10
0
		/// <summary>
		/// Renders a text to the specified target <see cref="Duality.Drawing.PixelData"/>.
		/// </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, PixelData target, float x, float y, ColorRgba clr)
		{
			if (this.pixelData == null)
				return;

			PixelData bitmap = 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;
				
				bitmap.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 #11
0
		/// <summary>
		/// Renders a text to the specified target <see cref="Duality.Resources.Pixmap"/> <see cref="Duality.Drawing.PixelData"/>.
		/// </summary>
		/// <param name="text"></param>
		/// <param name="target"></param>
		/// <param name="x"></param>
		/// <param name="y"></param>
		public void RenderToBitmap(string text, PixelData target, float x = 0.0f, float y = 0.0f)
		{
			this.RenderToBitmap(text, target, x, y, ColorRgba.White);
		}
Example #12
0
		/// <summary>
		/// Retrieves the rasterized <see cref="Duality.Drawing.PixelData"/> for a single glyph.
		/// </summary>
		/// <param name="glyph">The glyph of which to retrieve the Bitmap.</param>
		/// <returns>The Bitmap that has been retrieved, or null if the glyph is not supported.</returns>
		public PixelData GetGlyphBitmap(char glyph)
		{
			Rect targetRect;
			int charIndex = (int)glyph > this.charLookup.Length ? 0 : this.charLookup[(int)glyph];
			this.pixelData.LookupAtlas(charIndex, out targetRect);
			PixelData subImg = new PixelData(
				MathF.RoundToInt(targetRect.W), 
				MathF.RoundToInt(targetRect.H));
			this.pixelData.MainLayer.DrawOnto(subImg, BlendMode.Solid, 
				-MathF.RoundToInt(targetRect.X), 
				-MathF.RoundToInt(targetRect.Y));
			return subImg;
		}
Example #13
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 #14
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 #15
0
		/// <summary>
		/// Performs a drawing operation from this Layer to a target layer.
		/// </summary>
		/// <param name="target"></param>
		/// <param name="blend"></param>
		/// <param name="destX"></param>
		/// <param name="destY"></param>
		/// <param name="width"></param>
		/// <param name="height"></param>
		/// <param name="srcX"></param>
		/// <param name="srcY"></param>
		/// <param name="colorTint"></param>
		public void DrawOnto(PixelData target, BlendMode blend, int destX, int destY, int width, int height, int srcX, int srcY, ColorRgba colorTint)
		{
			if (colorTint == ColorRgba.White)
			{
				this.DrawOnto(target, blend, destX, destY, width, height, srcX, srcY);
				return;
			}
			if (width == -1) width = this.width;
			if (height == -1) height = this.height;

			int beginX = MathF.Max(0, -destX, -srcX);
			int beginY = MathF.Max(0, -destY, -srcY);
			int endX = MathF.Min(width, this.width, target.width - destX, this.width - srcX);
			int endY = MathF.Min(height, this.height, target.height - destY, this.height - srcY);
			if (endX - beginX < 1) return;
			if (endY - beginY < 1) return;

			ColorRgba clrSource;
			ColorRgba clrTarget;
			Parallel.ForEach(Partitioner.Create(beginX, endX), range =>
			{
				for (int i = range.Item1; i < range.Item2; i++)
				{
					for (int j = beginY; j < endY; j++)
					{
						int sourceN = srcX + i + this.width * (srcY + j);
						int targetN = destX + i + target.width * (destY + j);

						clrSource = this.data[sourceN] * colorTint;

						if (blend == BlendMode.Solid)
						{
							target.data[targetN] = clrSource;
						}
						else if (blend == BlendMode.Mask)
						{
							if (clrSource.A >= 0) target.data[targetN] = this.data[sourceN];
						}
						else if (blend == BlendMode.Add)
						{
							clrTarget	= target.data[targetN];
							float alphaTemp = (float)clrSource.A / 255.0f;
							target.data[targetN].R = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrTarget.R + clrSource.R * alphaTemp)));
							target.data[targetN].G = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrTarget.G + clrSource.G * alphaTemp)));
							target.data[targetN].B = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrTarget.B + clrSource.B * alphaTemp)));
							target.data[targetN].A = (byte)Math.Min(255, Math.Max(0, (int)clrTarget.A + (int)clrSource.A));
						}
						else if (blend == BlendMode.Alpha)
						{
							clrTarget	= target.data[targetN];
							float alphaTemp = (float)clrSource.A / 255.0f;
							target.data[targetN].R = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrTarget.R * (1.0f - alphaTemp) + clrSource.R * alphaTemp)));
							target.data[targetN].G = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrTarget.G * (1.0f - alphaTemp) + clrSource.G * alphaTemp)));
							target.data[targetN].B = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrTarget.B * (1.0f - alphaTemp) + clrSource.B * alphaTemp)));
							target.data[targetN].A = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrTarget.A * (1.0f - alphaTemp) + clrSource.A)));
						}
						else if (blend == BlendMode.AlphaPre)
						{
							clrTarget	= target.data[targetN];
							float alphaTemp = (float)clrSource.A / 255.0f;
							target.data[targetN].R = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrTarget.R * (1.0f - alphaTemp) + clrSource.R)));
							target.data[targetN].G = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrTarget.G * (1.0f - alphaTemp) + clrSource.G)));
							target.data[targetN].B = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrTarget.B * (1.0f - alphaTemp) + clrSource.B)));
							target.data[targetN].A = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrTarget.A * (1.0f - alphaTemp) + clrSource.A)));
						}
						else if (blend == BlendMode.Multiply)
						{
							clrTarget	= target.data[targetN];
							float clrTempR = (float)clrTarget.R / 255.0f;
							float clrTempG = (float)clrTarget.G / 255.0f;
							float clrTempB = (float)clrTarget.B / 255.0f;
							float clrTempA = (float)clrTarget.A / 255.0f;
							target.data[targetN].R = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrSource.R * clrTempR)));
							target.data[targetN].G = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrSource.G * clrTempG)));
							target.data[targetN].B = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrSource.B * clrTempB)));
							target.data[targetN].A = (byte)Math.Min(255, Math.Max(0, (int)clrTarget.A + (int)clrSource.A));
						}
						else if (blend == BlendMode.Light)
						{
							clrTarget	= target.data[targetN];
							float clrTempR = (float)clrTarget.R / 255.0f;
							float clrTempG = (float)clrTarget.G / 255.0f;
							float clrTempB = (float)clrTarget.B / 255.0f;
							float clrTempA = (float)clrTarget.A / 255.0f;
							target.data[targetN].R = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrSource.R * clrTempR + clrTarget.R)));
							target.data[targetN].G = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrSource.G * clrTempG + clrTarget.G)));
							target.data[targetN].B = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrSource.B * clrTempB + clrTarget.B)));
							target.data[targetN].A = (byte)Math.Min(255, Math.Max(0, (int)clrTarget.A + (int)clrSource.A));
						}
						else if (blend == BlendMode.Invert)
						{
							clrTarget	= target.data[targetN];
							float clrTempR = (float)clrTarget.R / 255.0f;
							float clrTempG = (float)clrTarget.G / 255.0f;
							float clrTempB = (float)clrTarget.B / 255.0f;
							float clrTempA = (float)clrTarget.A / 255.0f;
							float clrTempR2 = (float)clrSource.R / 255.0f;
							float clrTempG2 = (float)clrSource.G / 255.0f;
							float clrTempB2 = (float)clrSource.B / 255.0f;
							float clrTempA2 = (float)clrSource.A / 255.0f;
							target.data[targetN].R = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrSource.R * (1.0f - clrTempR) + clrTarget.R * (1.0f - clrTempR2))));
							target.data[targetN].G = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrSource.G * (1.0f - clrTempG) + clrTarget.G * (1.0f - clrTempG2))));
							target.data[targetN].B = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrSource.B * (1.0f - clrTempB) + clrTarget.B * (1.0f - clrTempB2))));
							target.data[targetN].A = (byte)Math.Min(255, Math.Max(0, (int)(clrTarget.A + clrSource.A)));
						}
					}
				}
			});
		}
Example #16
0
        /// <summary>
        /// Performs a drawing operation from this Layer to a target layer.
        /// </summary>
        /// <param name="target"></param>
        /// <param name="blend"></param>
        /// <param name="destX"></param>
        /// <param name="destY"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="srcX"></param>
        /// <param name="srcY"></param>
        /// <param name="colorTint"></param>
        public void DrawOnto(PixelData target, BlendMode blend, int destX, int destY, int width, int height, int srcX, int srcY, ColorRgba colorTint)
        {
            if (colorTint == ColorRgba.White)
            {
                this.DrawOnto(target, blend, destX, destY, width, height, srcX, srcY);
                return;
            }
            if (width == -1)
            {
                width = this.width;
            }
            if (height == -1)
            {
                height = this.height;
            }

            int beginX = MathF.Max(0, -destX, -srcX);
            int beginY = MathF.Max(0, -destY, -srcY);
            int endX   = MathF.Min(width, this.width, target.width - destX, this.width - srcX);
            int endY   = MathF.Min(height, this.height, target.height - destY, this.height - srcY);

            if (endX - beginX < 1)
            {
                return;
            }
            if (endY - beginY < 1)
            {
                return;
            }

            ColorRgba clrSource;
            ColorRgba clrTarget;

            Parallel.ForEach(Partitioner.Create(beginX, endX), range =>
            {
                for (int i = range.Item1; i < range.Item2; i++)
                {
                    for (int j = beginY; j < endY; j++)
                    {
                        int sourceN = srcX + i + this.width * (srcY + j);
                        int targetN = destX + i + target.width * (destY + j);

                        clrSource = this.data[sourceN] * colorTint;

                        if (blend == BlendMode.Solid)
                        {
                            target.data[targetN] = clrSource;
                        }
                        else if (blend == BlendMode.Mask)
                        {
                            if (clrSource.A > 0)
                            {
                                target.data[targetN] = this.data[sourceN];
                            }
                        }
                        else if (blend == BlendMode.Add)
                        {
                            clrTarget              = target.data[targetN];
                            float alphaTemp        = (float)clrSource.A / 255.0f;
                            target.data[targetN].R = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrTarget.R + clrSource.R * alphaTemp)));
                            target.data[targetN].G = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrTarget.G + clrSource.G * alphaTemp)));
                            target.data[targetN].B = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrTarget.B + clrSource.B * alphaTemp)));
                            target.data[targetN].A = (byte)Math.Min(255, Math.Max(0, (int)clrTarget.A + (int)clrSource.A));
                        }
                        else if (blend == BlendMode.Alpha)
                        {
                            clrTarget              = target.data[targetN];
                            float alphaTemp        = (float)clrSource.A / 255.0f;
                            target.data[targetN].R = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrTarget.R * (1.0f - alphaTemp) + clrSource.R * alphaTemp)));
                            target.data[targetN].G = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrTarget.G * (1.0f - alphaTemp) + clrSource.G * alphaTemp)));
                            target.data[targetN].B = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrTarget.B * (1.0f - alphaTemp) + clrSource.B * alphaTemp)));
                            target.data[targetN].A = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrTarget.A * (1.0f - alphaTemp) + clrSource.A)));
                        }
                        else if (blend == BlendMode.AlphaPre)
                        {
                            clrTarget              = target.data[targetN];
                            float alphaTemp        = (float)clrSource.A / 255.0f;
                            target.data[targetN].R = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrTarget.R * (1.0f - alphaTemp) + clrSource.R)));
                            target.data[targetN].G = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrTarget.G * (1.0f - alphaTemp) + clrSource.G)));
                            target.data[targetN].B = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrTarget.B * (1.0f - alphaTemp) + clrSource.B)));
                            target.data[targetN].A = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrTarget.A * (1.0f - alphaTemp) + clrSource.A)));
                        }
                        else if (blend == BlendMode.Multiply)
                        {
                            clrTarget              = target.data[targetN];
                            float clrTempR         = (float)clrTarget.R / 255.0f;
                            float clrTempG         = (float)clrTarget.G / 255.0f;
                            float clrTempB         = (float)clrTarget.B / 255.0f;
                            float clrTempA         = (float)clrTarget.A / 255.0f;
                            target.data[targetN].R = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrSource.R * clrTempR)));
                            target.data[targetN].G = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrSource.G * clrTempG)));
                            target.data[targetN].B = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrSource.B * clrTempB)));
                            target.data[targetN].A = (byte)Math.Min(255, Math.Max(0, (int)clrTarget.A + (int)clrSource.A));
                        }
                        else if (blend == BlendMode.Light)
                        {
                            clrTarget              = target.data[targetN];
                            float clrTempR         = (float)clrTarget.R / 255.0f;
                            float clrTempG         = (float)clrTarget.G / 255.0f;
                            float clrTempB         = (float)clrTarget.B / 255.0f;
                            float clrTempA         = (float)clrTarget.A / 255.0f;
                            target.data[targetN].R = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrSource.R * clrTempR + clrTarget.R)));
                            target.data[targetN].G = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrSource.G * clrTempG + clrTarget.G)));
                            target.data[targetN].B = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrSource.B * clrTempB + clrTarget.B)));
                            target.data[targetN].A = (byte)Math.Min(255, Math.Max(0, (int)clrTarget.A + (int)clrSource.A));
                        }
                        else if (blend == BlendMode.Invert)
                        {
                            clrTarget              = target.data[targetN];
                            float clrTempR         = (float)clrTarget.R / 255.0f;
                            float clrTempG         = (float)clrTarget.G / 255.0f;
                            float clrTempB         = (float)clrTarget.B / 255.0f;
                            float clrTempA         = (float)clrTarget.A / 255.0f;
                            float clrTempR2        = (float)clrSource.R / 255.0f;
                            float clrTempG2        = (float)clrSource.G / 255.0f;
                            float clrTempB2        = (float)clrSource.B / 255.0f;
                            float clrTempA2        = (float)clrSource.A / 255.0f;
                            target.data[targetN].R = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrSource.R * (1.0f - clrTempR) + clrTarget.R * (1.0f - clrTempR2))));
                            target.data[targetN].G = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrSource.G * (1.0f - clrTempG) + clrTarget.G * (1.0f - clrTempG2))));
                            target.data[targetN].B = (byte)Math.Min(255, Math.Max(0, (int)Math.Round(clrSource.B * (1.0f - clrTempB) + clrTarget.B * (1.0f - clrTempB2))));
                            target.data[targetN].A = (byte)Math.Min(255, Math.Max(0, (int)(clrTarget.A + clrSource.A)));
                        }
                    }
                }
            });
        }
Example #17
0
		/// <summary>
		/// Extracts a rectangular region of this Layer. If the extracted region is bigger than the original Layer,
		/// all new space is filled with a background color.
		/// </summary>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <param name="w"></param>
		/// <param name="h"></param>
		/// <param name="backColor"></param>
		public PixelData CloneSubImage(int x, int y, int w, int h, ColorRgba backColor)
		{
			PixelData tempLayer = new PixelData(w, h, backColor);
			this.DrawOnto(tempLayer, BlendMode.Solid, -x, -y);
			return tempLayer;
		}
Example #18
0
		/// <summary>
		/// Extracts a rectangular region of this Layer. If the extracted region is bigger than the original Layer,
		/// all new space is filled with a background color.
		/// </summary>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <param name="w"></param>
		/// <param name="h"></param>
		/// <param name="backColor"></param>
		public void SubImage(int x, int y, int w, int h, ColorRgba backColor)
		{
			PixelData tempLayer = new PixelData(w, h, backColor);
			this.DrawOnto(tempLayer, BlendMode.Solid, -x, -y);
			this.width = tempLayer.width;
			this.height = tempLayer.height;
			this.data = tempLayer.data;
		}