/// <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)); }
/// <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; }
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); }
/// <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)); }
/// <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))); } } } }); }
/// <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]); } } }); }
/// <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; }
public VertexC1P3T2(Vector3 pos, Vector2 uv, ColorRgba clr) { this.Pos = pos; this.TexCoord = uv; this.Color = clr; }
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; }