Example #1
0
 /// <summary>
 /// Adjusts the color to match the specified int-Argb color.
 /// </summary>
 /// <param name="argb"></param>
 public void SetIntArgb(int argb)
 {
     this.SetRgba(ColorRgba.FromIntArgb(argb));
 }
Example #2
0
        /// <summary>
        /// Adds a parameterized set of vertices to the drawing devices rendering schedule.
        /// </summary>
        /// <typeparam name="T">The type of vertex data to add.</typeparam>
        /// <param name="material">The <see cref="Duality.Drawing.BatchInfo"/> to use for rendering the vertices.</param>
        /// <param name="vertexMode">The vertices drawing mode.</param>
        /// <param name="vertexBuffer">
        /// A vertex data buffer that stores the vertices to add. Ownership of the buffer
        /// remains at the callsite, while the <see cref="IDrawDevice"/> copies the required
        /// data into internal storage.
        /// </param>
        /// <param name="vertexCount">The number of vertices to add, from the beginning of the buffer.</param>
        public void AddVertices <T>(BatchInfo material, VertexMode vertexMode, T[] vertexBuffer, int vertexCount) where T : struct, IVertexData
        {
            if (vertexCount == 0)
            {
                return;
            }
            if (vertexBuffer == null || vertexBuffer.Length == 0)
            {
                return;
            }
            if (vertexCount > vertexBuffer.Length)
            {
                vertexCount = vertexBuffer.Length;
            }
            if (material == null)
            {
                material = Material.Checkerboard.Res.Info;
            }

            // In picking mode, override incoming vertices material and vertex colors
            // to generate a lookup texture by which we can retrieve each pixels object.
            if (this.pickingIndex != 0)
            {
                ColorRgba clr = new ColorRgba((this.pickingIndex << 8) | 0xFF);
                for (int i = 0; i < vertexCount; ++i)
                {
                    vertexBuffer[i].Color = clr;
                }

                material           = this.RentMaterial(material);
                material.Technique = DrawTechnique.Picking;
                material.MainColor = ColorRgba.White;
            }
            else if (material.Technique == null || !material.Technique.IsAvailable)
            {
                material           = this.RentMaterial(material);
                material.Technique = DrawTechnique.Solid;
            }

            // Move the added vertices to an internal shared buffer
            VertexSlice <T> slice = this.drawVertices.Rent <T>(vertexCount);

            Array.Copy(vertexBuffer, 0, slice.Data, slice.Offset, slice.Length);

            // Aggregate all info we have about our incoming vertices
            VertexDrawItem drawItem = new VertexDrawItem
            {
                Offset      = (ushort)slice.Offset,
                Count       = (ushort)slice.Length,
                BufferIndex = (byte)(this.drawVertices.GetBatchCount <T>() - 1),
                TypeIndex   = (byte)VertexDeclaration.Get <T>().TypeIndex,
                Mode        = vertexMode,
                Material    = material
            };

            // Determine whether we need depth sorting and calculate a reference depth
            bool sortByDepth = (this.projection == ProjectionMode.Screen) || material.Technique.Res.NeedsZSort;
            RawList <SortItem> sortBuffer = sortByDepth ? this.sortBufferBlended : this.sortBufferSolid;
            SortItem           sortItem   = new SortItem();

            if (sortByDepth)
            {
                sortItem.SortDepth = this.CalcZSortIndex <T>(vertexBuffer, vertexCount);
            }

            // Determine whether we can batch the new vertex item with the previous one
            int prevDrawIndex = -1;

            if (vertexMode.IsBatchableMode() && sortBuffer.Count > 0)
            {
                // Since we require a complete material match to append items, we can
                // assume that the previous items sortByDepth value is the same as ours.
                SortItem prevSortItem = sortBuffer.Data[sortBuffer.Count - 1];

                // Compare the previously added item with the new one. If everything
                // except the vertices themselves is the same, we can append them directly.
                if (this.drawBuffer.Data[prevSortItem.DrawItemIndex].CanAppend(ref drawItem) &&
                    (!sortByDepth || sortItem.CanShareDepth(prevSortItem.SortDepth)))
                {
                    prevDrawIndex = prevSortItem.DrawItemIndex;
                }
            }

            // Append the new item directly to the previous one, or add it as a new one
            if (prevDrawIndex != -1)
            {
                if (sortByDepth)
                {
                    sortBuffer.Data[sortBuffer.Count - 1].MergeDepth(
                        sortItem.SortDepth,
                        this.drawBuffer.Data[prevDrawIndex].Count,
                        drawItem.Count);
                }
                this.drawBuffer.Data[prevDrawIndex].Count += drawItem.Count;
            }
            else
            {
                sortItem.DrawItemIndex = this.drawBuffer.Count;
                sortBuffer.Add(sortItem);
                this.drawBuffer.Add(drawItem);
            }

            ++this.numRawBatches;
        }
Example #3
0
        private ColorRgba[] InternalRescale(int w, int h, ImageScaleFilter filter)
        {
            if (this.width == w && this.height == h)
            {
                return(null);
            }

            ColorRgba[] tempDestData = new ColorRgba[w * h];
            if (filter == ImageScaleFilter.Nearest)
            {
                // Don't use Parallel.For here, the overhead is too big and the compiler
                // does a great job optimizing this piece of code without, so don't get in the way.
                for (int i = 0; i < tempDestData.Length; i++)
                {
                    int y = i / w;
                    int x = i - (y * w);

                    int xTmp = (x * this.width) / w;
                    int yTmp = (y * this.height) / h;
                    int nTmp = xTmp + (yTmp * this.width);
                    tempDestData[i] = this.data[nTmp];
                }
            }
            else if (filter == ImageScaleFilter.Linear)
            {
                Parallel.ForEach(Partitioner.Create(0, tempDestData.Length), range =>
                {
                    for (int i = range.Item1; i < range.Item2; i++)
                    {
                        int y = i / w;
                        int x = i - (y * w);

                        float xRatio = ((float)(x * this.width) / (float)w) + 0.5f;
                        float yRatio = ((float)(y * this.height) / (float)h) + 0.5f;
                        int xTmp     = (int)xRatio;
                        int yTmp     = (int)yRatio;
                        xRatio      -= xTmp;
                        yRatio      -= yTmp;

                        int xTmp2 = xTmp + 1;
                        int yTmp2 = yTmp + 1;
                        xTmp      = xTmp < this.width ? xTmp : this.width - 1;
                        yTmp      = (yTmp < this.height ? yTmp : this.height - 1) * this.width;
                        xTmp2     = xTmp2 < this.width ? xTmp2 : this.width - 1;
                        yTmp2     = (yTmp2 < this.height ? yTmp2 : this.height - 1) * this.width;

                        int nTmp0 = xTmp + yTmp;
                        int nTmp1 = xTmp2 + yTmp;
                        int nTmp2 = xTmp + yTmp2;
                        int nTmp3 = xTmp2 + yTmp2;

                        tempDestData[i].R =
                            (byte)
                            (
                                ((float)this.data[nTmp0].R * (1.0f - xRatio) * (1.0f - yRatio)) +
                                ((float)this.data[nTmp1].R * xRatio * (1.0f - yRatio)) +
                                ((float)this.data[nTmp2].R * yRatio * (1.0f - xRatio)) +
                                ((float)this.data[nTmp3].R * xRatio * yRatio)
                            );
                        tempDestData[i].G =
                            (byte)
                            (
                                ((float)this.data[nTmp0].G * (1.0f - xRatio) * (1.0f - yRatio)) +
                                ((float)this.data[nTmp1].G * xRatio * (1.0f - yRatio)) +
                                ((float)this.data[nTmp2].G * yRatio * (1.0f - xRatio)) +
                                ((float)this.data[nTmp3].G * xRatio * yRatio)
                            );
                        tempDestData[i].B =
                            (byte)
                            (
                                ((float)this.data[nTmp0].B * (1.0f - xRatio) * (1.0f - yRatio)) +
                                ((float)this.data[nTmp1].B * xRatio * (1.0f - yRatio)) +
                                ((float)this.data[nTmp2].B * yRatio * (1.0f - xRatio)) +
                                ((float)this.data[nTmp3].B * xRatio * yRatio)
                            );
                        tempDestData[i].A =
                            (byte)
                            (
                                ((float)this.data[nTmp0].A * (1.0f - xRatio) * (1.0f - yRatio)) +
                                ((float)this.data[nTmp1].A * xRatio * (1.0f - yRatio)) +
                                ((float)this.data[nTmp2].A * yRatio * (1.0f - xRatio)) +
                                ((float)this.data[nTmp3].A * xRatio * yRatio)
                            );
                    }
                });
            }

            return(tempDestData);
        }
Example #4
0
 /// <summary>
 /// Adjusts the color to match the specified int-Rgba color.
 /// </summary>
 /// <param name="rgba"></param>
 public void SetIntRgba(int rgba)
 {
     this.SetRgba(ColorRgba.FromIntRgba(rgba));
 }
Example #5
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 #6
0
        /// <summary>
        /// Sets the color of all transparent pixels based on the non-transparent color values next to them.
        /// This does not affect any alpha values but prepares the Layer for correct filtering once uploaded
        /// to <see cref="Duality.Resources.Texture"/>.
        /// </summary>
        public void ColorTransparentPixels()
        {
            ColorRgba[] dataCopy = new ColorRgba[this.data.Length];
            Array.Copy(this.data, dataCopy, this.data.Length);

            Parallel.ForEach(Partitioner.Create(0, this.data.Length), range =>
            {
                Point2 pos     = new Point2();
                int[]   nPos   = new int[8];
                bool[]  nOk    = new bool[8];
                int[]   mixClr = new int[4];

                for (int i = range.Item1; i < range.Item2; i++)
                {
                    if (dataCopy[i].A != 0)
                    {
                        continue;
                    }

                    pos.Y = i / this.width;
                    pos.X = i - (pos.Y * this.width);

                    mixClr[0] = 0;
                    mixClr[1] = 0;
                    mixClr[2] = 0;
                    mixClr[3] = 0;

                    nPos[0] = i - this.width;
                    nPos[1] = i + this.width;
                    nPos[2] = i - 1;
                    nPos[3] = i + 1;
                    nPos[4] = i - this.width - 1;
                    nPos[5] = i + this.width - 1;
                    nPos[6] = i - this.width + 1;
                    nPos[7] = i + this.width + 1;

                    nOk[0] = pos.Y > 0;
                    nOk[1] = pos.Y < this.height - 1;
                    nOk[2] = pos.X > 0;
                    nOk[3] = pos.X < this.width - 1;
                    nOk[4] = nOk[2] && nOk[0];
                    nOk[5] = nOk[2] && nOk[1];
                    nOk[6] = nOk[3] && nOk[0];
                    nOk[7] = nOk[3] && nOk[1];

                    int nMult = 2;
                    for (int j = 0; j < 8; j++)
                    {
                        if (!nOk[j])
                        {
                            continue;
                        }
                        if (dataCopy[nPos[j]].A == 0)
                        {
                            continue;
                        }

                        mixClr[0] += dataCopy[nPos[j]].R * nMult;
                        mixClr[1] += dataCopy[nPos[j]].G * nMult;
                        mixClr[2] += dataCopy[nPos[j]].B * nMult;
                        mixClr[3] += nMult;

                        if (j > 3)
                        {
                            nMult = 1;
                        }
                    }

                    if (mixClr[3] > 0)
                    {
                        this.data[i].R = (byte)Math.Round((float)mixClr[0] / (float)mixClr[3]);
                        this.data[i].G = (byte)Math.Round((float)mixClr[1] / (float)mixClr[3]);
                        this.data[i].B = (byte)Math.Round((float)mixClr[2] / (float)mixClr[3]);
                    }
                }
            });
        }
Example #7
0
 /// <summary>
 /// Creates a new color-only BatchInfo.
 /// </summary>
 /// <param name="technique">The <see cref="Duality.Resources.DrawTechnique"/> to use.</param>
 /// <param name="mainColor">The <see cref="MainColor"/> to use.</param>
 public BatchInfo(ContentRef <DrawTechnique> technique, ColorRgba mainColor) : this(technique)
 {
     this.MainColor = mainColor;
 }
Example #8
0
 public VertexC1P3T2(Vector3 pos, Vector2 uv, ColorRgba clr)
 {
     this.Pos      = pos;
     this.TexCoord = uv;
     this.Color    = clr;
 }
Example #9
0
        public void AddVertices <T>(BatchInfo material, VertexMode vertexMode, T[] vertexBuffer, int vertexCount) where T : struct, IVertexData
        {
            if (vertexCount == 0)
            {
                return;
            }
            if (vertexBuffer == null || vertexBuffer.Length == 0)
            {
                return;
            }
            if (vertexCount > vertexBuffer.Length)
            {
                vertexCount = vertexBuffer.Length;
            }
            if (material == null)
            {
                material = Material.Checkerboard.Res.InfoDirect;
            }

            if (this.pickingIndex != 0)
            {
                ColorRgba clr = new ColorRgba((this.pickingIndex << 8) | 0xFF);
                for (int i = 0; i < vertexCount; ++i)
                {
                    vertexBuffer[i].Color = clr;
                }

                material           = new BatchInfo(material);
                material.Technique = DrawTechnique.Picking;
                if (material.Textures == null)
                {
                    material.MainTexture = Texture.White;
                }
            }
            else if (material.Technique == null || !material.Technique.IsAvailable)
            {
                material           = new BatchInfo(material);
                material.Technique = DrawTechnique.Solid;
            }
            else if (material.Technique.Res.NeedsPreprocess)
            {
                material = new BatchInfo(material);
                material.Technique.Res.PreprocessBatch <T>(this, material, ref vertexMode, ref vertexBuffer, ref vertexCount);
                if (vertexCount == 0)
                {
                    return;
                }
                if (vertexBuffer == null || vertexBuffer.Length == 0)
                {
                    return;
                }
                if (vertexCount > vertexBuffer.Length)
                {
                    vertexCount = vertexBuffer.Length;
                }
                if (material.Technique == null || !material.Technique.IsAvailable)
                {
                    material.Technique = DrawTechnique.Solid;
                }
            }

            // When rendering without depth writing, use z sorting everywhere - there's no real depth buffering!
            bool zSort = !this.DepthWrite || material.Technique.Res.NeedsZSort;
            List <IDrawBatch> buffer     = zSort ? this.drawBufferZSort : this.drawBuffer;
            float             zSortIndex = zSort ? DrawBatch <T> .CalcZSortIndex(vertexBuffer, vertexCount) : 0.0f;

            if (buffer.Count > 0 && buffer[buffer.Count - 1].CanAppendJIT <T>(
                    zSort ? 1.0f / this.zSortAccuracy : 0.0f,
                    zSortIndex,
                    material,
                    vertexMode))
            {
                buffer[buffer.Count - 1].AppendJIT(vertexBuffer, vertexCount);
            }
            else
            {
                buffer.Add(new DrawBatch <T>(material, vertexMode, vertexBuffer, vertexCount, zSortIndex));
            }
            ++this.numRawBatches;
        }