// Checks whether a bitmap contains entirely the specified RGB value. public static bool IsRgbEntirely(Color expectedRgb, Bitmap bitmap) { using (var bitmapData = new PixelAccessor(bitmap, ImageLockMode.ReadOnly)) { for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { System.Drawing.Color color = bitmapData[x, y]; if (color.A == 0) continue; if ((color.R != expectedRgb.R) || (color.G != expectedRgb.G) || (color.B != expectedRgb.B)) { return false; } } } } return true; }
public override void PixBltBGRb(PixelAccessor<BGRb> accessor, int x, int y) { MemoryStream ms = new MemoryStream(); // 2. Run length encode the image to a memory stream NewTOAPIA.Imaging.TargaRunLengthCodec rlc = new NewTOAPIA.Imaging.TargaRunLengthCodec(); rlc.Encode(accessor, ms); // 3. Get the bytes from the stream byte[] imageBytes = ms.GetBuffer(); int dataLength = (int)imageBytes.Length; // 4. Allocate a buffer chunk to accomodate the bytes, plus some more BufferChunk chunk = new BufferChunk(dataLength + 128); // 5. Put the command, destination, and data size into the buffer first chunk += (int)UICommands.PixBltRLE; ChunkUtils.Pack(chunk, x, y); ChunkUtils.Pack(chunk, accessor.Width, accessor.Height); chunk += dataLength; // 6. Put the image bytes into the chunk chunk += imageBytes; // 6. Finally, send the packet SendCommand(chunk); }
// Checks whether an area of a bitmap contains entirely the specified alpha value. public static bool IsAlphaEntirely(byte expectedAlpha, Bitmap bitmap, System.Drawing.Rectangle? region = null) { using (var bitmapData = new PixelAccessor(bitmap, ImageLockMode.ReadOnly, region)) { for (int y = 0; y < bitmapData.Region.Height; y++) { for (int x = 0; x < bitmapData.Region.Width; x++) { byte alpha = bitmapData[x, y].A; if (alpha != expectedAlpha) return false; } } } return true; }
// Copies a rectangular area from one bitmap to another. public static void CopyRect(Bitmap source, System.Drawing.Rectangle sourceRegion, Bitmap output, System.Drawing.Rectangle outputRegion) { if (sourceRegion.Width != outputRegion.Width || sourceRegion.Height != outputRegion.Height) { throw new ArgumentException(); } using (var sourceData = new PixelAccessor(source, ImageLockMode.ReadOnly, sourceRegion)) using (var outputData = new PixelAccessor(output, ImageLockMode.WriteOnly, outputRegion)) { for (int y = 0; y < sourceRegion.Height; y++) { for (int x = 0; x < sourceRegion.Width; x++) { outputData[x, y] = sourceData[x, y]; } } } }
/// <inheritdoc/> protected override void OnApply(ImageBase <TColor, TPacked> source, Rectangle sourceRectangle) { int kernelYHeight = this.KernelY.Length; int kernelYWidth = this.KernelY[0].Length; int kernelXHeight = this.KernelX.Length; int kernelXWidth = this.KernelX[0].Length; int radiusY = kernelYHeight >> 1; int radiusX = kernelXWidth >> 1; int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; int maxY = endY - 1; int maxX = endX - 1; TColor[] target = new TColor[source.Width * source.Height]; using (PixelAccessor <TColor, TPacked> sourcePixels = source.Lock()) using (PixelAccessor <TColor, TPacked> targetPixels = target.Lock <TColor, TPacked>(source.Width, source.Height)) { Parallel.For( startY, endY, this.ParallelOptions, y => { for (int x = startX; x < endX; x++) { float rX = 0; float gX = 0; float bX = 0; float rY = 0; float gY = 0; float bY = 0; // Apply each matrix multiplier to the color components for each pixel. for (int fy = 0; fy < kernelYHeight; fy++) { int fyr = fy - radiusY; int offsetY = y + fyr; offsetY = offsetY.Clamp(0, maxY); for (int fx = 0; fx < kernelXWidth; fx++) { int fxr = fx - radiusX; int offsetX = x + fxr; offsetX = offsetX.Clamp(0, maxX); Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); float r = currentColor.X; float g = currentColor.Y; float b = currentColor.Z; if (fy < kernelXHeight) { rX += this.KernelX[fy][fx] * r; gX += this.KernelX[fy][fx] * g; bX += this.KernelX[fy][fx] * b; } if (fx < kernelYWidth) { rY += this.KernelY[fy][fx] * r; gY += this.KernelY[fy][fx] * g; bY += this.KernelY[fy][fx] * b; } } } float red = (float)Math.Sqrt((rX * rX) + (rY * rY)); float green = (float)Math.Sqrt((gX * gX) + (gY * gY)); float blue = (float)Math.Sqrt((bX * bX) + (bY * bY)); TColor packed = default(TColor); packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W)); targetPixels[x, y] = packed; } }); } source.SetPixels(source.Width, source.Height, target); }
/// <inheritdoc/> protected override void OnApply(ImageBase <TColor, TPacked> source, Rectangle sourceRectangle) { using (IPenApplicator <TColor, TPacked> applicator = this.pen.CreateApplicator(this.region)) { var rect = RectangleF.Ceiling(applicator.RequiredRegion); int polyStartY = rect.Y - PaddingFactor; int polyEndY = rect.Bottom + PaddingFactor; int startX = rect.X - PaddingFactor; int endX = rect.Right + PaddingFactor; int minX = Math.Max(sourceRectangle.Left, startX); int maxX = Math.Min(sourceRectangle.Right, endX); int minY = Math.Max(sourceRectangle.Top, polyStartY); int maxY = Math.Min(sourceRectangle.Bottom, polyEndY); // Align start/end positions. minX = Math.Max(0, minX); maxX = Math.Min(source.Width, maxX); minY = Math.Max(0, minY); maxY = Math.Min(source.Height, maxY); // Reset offset if necessary. if (minX > 0) { startX = 0; } if (minY > 0) { polyStartY = 0; } using (PixelAccessor <TColor, TPacked> sourcePixels = source.Lock()) { Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - polyStartY; var currentPoint = default(Vector2); for (int x = minX; x < maxX; x++) { int offsetX = x - startX; currentPoint.X = offsetX; currentPoint.Y = offsetY; var dist = Closest(currentPoint); var color = applicator.GetColor(dist); var opacity = this.Opacity(color.DistanceFromElement); if (opacity > Epsilon) { int offsetColorX = x - minX; Vector4 backgroundVector = sourcePixels[offsetX, offsetY].ToVector4(); Vector4 sourceVector = color.Color.ToVector4(); var finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); finalColor.W = backgroundVector.W; TColor packed = default(TColor); packed.PackFromVector4(finalColor); sourcePixels[offsetX, offsetY] = packed; } } }); } } }
void SendVideoFrame(PixelAccessor<BGRb> aFrame) { BufferChunk chunk = EncodeBGRb(aFrame, 0, 0); fChannel.Send(chunk); }
// Converts a bitmap to premultiplied alpha format. public static void PremultiplyAlphaClearType(Bitmap bitmap, bool srgb) { using (var bitmapData = new PixelAccessor(bitmap, ImageLockMode.ReadWrite)) { for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { DrawingColor color = bitmapData[x, y]; int a; if (srgb) { var colorLinear = new Color4(new SiliconStudio.Core.Mathematics.Color(color.R, color.G, color.B)).ToLinear(); var alphaLinear = (colorLinear.R + colorLinear.G + colorLinear.B) / 3.0f; a = MathUtil.Clamp((int)Math.Round(alphaLinear * 255), 0, 255); } else { a = (color.R + color.G + color.B) / 3; } int r = color.R; int g = color.G; int b = color.B; bitmapData[x, y] = DrawingColor.FromArgb(a, r, g, b); } } } }
/// <summary> /// Decodes the raw interlaced pixel data row by row /// <see href="https://github.com/juehv/DentalImageViewer/blob/8a1a4424b15d6cc453b5de3f273daf3ff5e3a90d/DentalImageViewer/lib/jiu-0.14.3/net/sourceforge/jiu/codecs/PNGCodec.java"/> /// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <param name="compressedStream">The compressed pixel data stream.</param> /// <param name="pixels">The image pixel accessor.</param> private void DecodeInterlacedPixelData <TPixel>(Stream compressedStream, PixelAccessor <TPixel> pixels) where TPixel : struct, IPixel <TPixel> { while (true) { int numColumns = this.ComputeColumnsAdam7(this.pass); if (numColumns == 0) { this.pass++; // This pass contains no data; skip to next pass continue; } int bytesPerInterlaceScanline = this.CalculateScanlineLength(numColumns) + 1; while (this.currentRow < this.header.Height) { int bytesRead = compressedStream.Read(this.scanline, this.currentRowBytesRead, bytesPerInterlaceScanline - this.currentRowBytesRead); this.currentRowBytesRead += bytesRead; if (this.currentRowBytesRead < bytesPerInterlaceScanline) { return; } this.currentRowBytesRead = 0; FilterType filterType = (FilterType)this.scanline[0]; switch (filterType) { case FilterType.None: NoneFilter.Decode(this.scanline); break; case FilterType.Sub: SubFilter.Decode(this.scanline, bytesPerInterlaceScanline, this.bytesPerPixel); break; case FilterType.Up: UpFilter.Decode(this.scanline, this.previousScanline, bytesPerInterlaceScanline); break; case FilterType.Average: AverageFilter.Decode(this.scanline, this.previousScanline, bytesPerInterlaceScanline, this.bytesPerPixel); break; case FilterType.Paeth: PaethFilter.Decode(this.scanline, this.previousScanline, bytesPerInterlaceScanline, this.bytesPerPixel); break; default: throw new ImageFormatException("Unknown filter type."); } this.ProcessInterlacedDefilteredScanline(this.scanline, this.currentRow, pixels, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]); Swap(ref this.scanline, ref this.previousScanline); this.currentRow += Adam7RowIncrement[this.pass]; } this.pass++; if (this.pass < 7) { this.currentRow = Adam7FirstRow[this.pass]; } else { break; } } }
public void SendVideoFrame(PixelAccessor<BGRb> aFrame) { BufferChunk chunk = EncodeBGRb(aFrame, 0, 0); Session.SendVideoFrame(chunk); }
// Copies a single pixel within a bitmap, preserving RGB but forcing alpha to zero. static void CopyBorderPixel(PixelAccessor bitmapData, int sourceX, int sourceY, int destX, int destY) { System.Drawing.Color color = bitmapData[sourceX, sourceY]; bitmapData[destX, destY] = System.Drawing.Color.FromArgb(0, color); }
/// <inheritdoc /> public BrushApplicator <TColor> CreateApplicator(PixelAccessor <TColor> sourcePixels, RectangleF region) { return(new SolidBrushApplicator(sourcePixels, this.color)); }
/// <inheritdoc/> protected override void OnApply(ImageBase <TPixel> source, Rectangle sourceRectangle) { using (PixelAccessor <TPixel> sourcePixels = source.Lock()) using (PenApplicator <TPixel> applicator = this.Pen.CreateApplicator(sourcePixels, this.Path.Bounds)) { Rectangle rect = RectangleF.Ceiling(applicator.RequiredRegion); int polyStartY = rect.Y - PaddingFactor; int polyEndY = rect.Bottom + PaddingFactor; int startX = rect.X - PaddingFactor; int endX = rect.Right + PaddingFactor; int minX = Math.Max(sourceRectangle.Left, startX); int maxX = Math.Min(sourceRectangle.Right, endX); int minY = Math.Max(sourceRectangle.Top, polyStartY); int maxY = Math.Min(sourceRectangle.Bottom, polyEndY); // Align start/end positions. minX = Math.Max(0, minX); maxX = Math.Min(source.Width, maxX); minY = Math.Max(0, minY); maxY = Math.Min(source.Height, maxY); // Reset offset if necessary. if (minX > 0) { startX = 0; } if (minY > 0) { polyStartY = 0; } Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - polyStartY; for (int x = minX; x < maxX; x++) { // TODO add find intersections code to skip and scan large regions of this. int offsetX = x - startX; PointInfo info = this.Path.GetPointInfo(offsetX, offsetY); ColoredPointInfo <TPixel> color = applicator.GetColor(offsetX, offsetY, info); float opacity = this.Opacity(color.DistanceFromElement); if (opacity > Constants.Epsilon) { Vector4 backgroundVector = sourcePixels[offsetX, offsetY].ToVector4(); Vector4 sourceVector = color.Color.ToVector4(); Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); TPixel packed = default(TPixel); packed.PackFromVector4(finalColor); sourcePixels[offsetX, offsetY] = packed; } } }); } }
/// <inheritdoc /> public BrushApplicator <TColor> CreateApplicator(PixelAccessor <TColor> sourcePixels, RectangleF region) { return(new ImageBrushApplicator(this.image, region)); }
/// <summary> /// Processes the interlaced de-filtered scanline filling the image pixel data /// </summary> /// <typeparam name="TColor">The pixel format.</typeparam> /// <param name="defilteredScanline">The de-filtered scanline</param> /// <param name="row">The current image row.</param> /// <param name="pixels">The image pixels</param> /// <param name="pixelOffset">The column start index. Always 0 for none interlaced images.</param> /// <param name="increment">The column increment. Always 1 for none interlaced images.</param> private void ProcessInterlacedDefilteredScanline <TColor>(byte[] defilteredScanline, int row, PixelAccessor <TColor> pixels, int pixelOffset = 0, int increment = 1) where TColor : struct, IPackedPixel, IEquatable <TColor> { TColor color = default(TColor); switch (this.PngColorType) { case PngColorType.Grayscale: int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1); byte[] newScanline1 = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth); for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o++) { byte intensity = (byte)(newScanline1[o] * factor); color.PackFromBytes(intensity, intensity, intensity, 255); pixels[x, row] = color; } break; case PngColorType.GrayscaleWithAlpha: for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel) { byte intensity = defilteredScanline[o]; byte alpha = defilteredScanline[o + this.bytesPerSample]; color.PackFromBytes(intensity, intensity, intensity, alpha); pixels[x, row] = color; } break; case PngColorType.Palette: byte[] newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth); if (this.paletteAlpha != null && this.paletteAlpha.Length > 0) { // If the alpha palette is not null and has one or more entries, this means, that the image contains an alpha // channel and we should try to read it. for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o++) { int index = newScanline[o]; int offset = index * 3; byte a = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255; if (a > 0) { byte r = this.palette[offset]; byte g = this.palette[offset + 1]; byte b = this.palette[offset + 2]; color.PackFromBytes(r, g, b, a); } else { color.PackFromBytes(0, 0, 0, 0); } pixels[x, row] = color; } } else { for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o++) { int index = newScanline[o]; int offset = index * 3; byte r = this.palette[offset]; byte g = this.palette[offset + 1]; byte b = this.palette[offset + 2]; color.PackFromBytes(r, g, b, 255); pixels[x, row] = color; } } break; case PngColorType.Rgb: for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel) { byte r = defilteredScanline[o]; byte g = defilteredScanline[o + this.bytesPerSample]; byte b = defilteredScanline[o + (2 * this.bytesPerSample)]; color.PackFromBytes(r, g, b, 255); pixels[x, row] = color; } break; case PngColorType.RgbWithAlpha: for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel) { byte r = defilteredScanline[o]; byte g = defilteredScanline[o + this.bytesPerSample]; byte b = defilteredScanline[o + (2 * this.bytesPerSample)]; byte a = defilteredScanline[o + (3 * this.bytesPerSample)]; color.PackFromBytes(r, g, b, a); pixels[x, row] = color; } break; } }
/// <summary> /// Decodes the raw interlaced pixel data row by row /// <see href="https://github.com/juehv/DentalImageViewer/blob/8a1a4424b15d6cc453b5de3f273daf3ff5e3a90d/DentalImageViewer/lib/jiu-0.14.3/net/sourceforge/jiu/codecs/PNGCodec.java"/> /// </summary> /// <typeparam name="TColor">The pixel format.</typeparam> /// <param name="compressedStream">The compressed pixel data stream.</param> /// <param name="pixels">The image pixel accessor.</param> private void DecodeInterlacedPixelData <TColor>(Stream compressedStream, PixelAccessor <TColor> pixels) where TColor : struct, IPackedPixel, IEquatable <TColor> { byte[] previousScanline = ArrayPool <byte> .Shared.Rent(this.bytesPerScanline); byte[] scanline = ArrayPool <byte> .Shared.Rent(this.bytesPerScanline); try { for (int pass = 0; pass < 7; pass++) { // Zero out the scanlines, because the bytes that are rented from the arraypool may not be zero. Array.Clear(scanline, 0, this.bytesPerScanline); Array.Clear(previousScanline, 0, this.bytesPerScanline); int y = Adam7FirstRow[pass]; int numColumns = this.ComputeColumnsAdam7(pass); if (numColumns == 0) { // This pass contains no data; skip to next pass continue; } int bytesPerInterlaceScanline = this.CalculateScanlineLength(numColumns) + 1; while (y < this.header.Height) { compressedStream.Read(scanline, 0, bytesPerInterlaceScanline); FilterType filterType = (FilterType)scanline[0]; switch (filterType) { case FilterType.None: NoneFilter.Decode(scanline); break; case FilterType.Sub: SubFilter.Decode(scanline, bytesPerInterlaceScanline, this.bytesPerPixel); break; case FilterType.Up: UpFilter.Decode(scanline, previousScanline, bytesPerInterlaceScanline); break; case FilterType.Average: AverageFilter.Decode(scanline, previousScanline, bytesPerInterlaceScanline, this.bytesPerPixel); break; case FilterType.Paeth: PaethFilter.Decode(scanline, previousScanline, bytesPerInterlaceScanline, this.bytesPerPixel); break; default: throw new ImageFormatException("Unknown filter type."); } this.ProcessInterlacedDefilteredScanline(scanline, y, pixels, Adam7FirstColumn[pass], Adam7ColumnIncrement[pass]); Swap(ref scanline, ref previousScanline); y += Adam7RowIncrement[pass]; } } } finally { ArrayPool <byte> .Shared.Return(previousScanline); ArrayPool <byte> .Shared.Return(scanline); } }
/// <summary> /// Decodes the raw pixel data row by row /// </summary> /// <typeparam name="TColor">The pixel format.</typeparam> /// <param name="compressedStream">The compressed pixel data stream.</param> /// <param name="pixels">The image pixel accessor.</param> private void DecodePixelData <TColor>(Stream compressedStream, PixelAccessor <TColor> pixels) where TColor : struct, IPackedPixel, IEquatable <TColor> { byte[] previousScanline = ArrayPool <byte> .Shared.Rent(this.bytesPerScanline); byte[] scanline = ArrayPool <byte> .Shared.Rent(this.bytesPerScanline); // Zero out the scanlines, because the bytes that are rented from the arraypool may not be zero. Array.Clear(scanline, 0, this.bytesPerScanline); Array.Clear(previousScanline, 0, this.bytesPerScanline); try { for (int y = 0; y < this.header.Height; y++) { compressedStream.Read(scanline, 0, this.bytesPerScanline); FilterType filterType = (FilterType)scanline[0]; switch (filterType) { case FilterType.None: NoneFilter.Decode(scanline); break; case FilterType.Sub: SubFilter.Decode(scanline, this.bytesPerScanline, this.bytesPerPixel); break; case FilterType.Up: UpFilter.Decode(scanline, previousScanline, this.bytesPerScanline); break; case FilterType.Average: AverageFilter.Decode(scanline, previousScanline, this.bytesPerScanline, this.bytesPerPixel); break; case FilterType.Paeth: PaethFilter.Decode(scanline, previousScanline, this.bytesPerScanline, this.bytesPerPixel); break; default: throw new ImageFormatException("Unknown filter type."); } this.ProcessDefilteredScanline(scanline, y, pixels); Swap(ref scanline, ref previousScanline); } } finally { ArrayPool <byte> .Shared.Return(previousScanline); ArrayPool <byte> .Shared.Return(scanline); } }
/// <summary> /// Decodes the stream to the image. /// </summary> /// <typeparam name="TColor">The pixel format.</typeparam> /// <param name="image">The image to decode to.</param> /// <param name="stream">The stream containing image data. </param> /// <exception cref="ImageFormatException"> /// Thrown if the stream does not contain and end chunk. /// </exception> /// <exception cref="System.ArgumentOutOfRangeException"> /// Thrown if the image is larger than the maximum allowable size. /// </exception> public void Decode <TColor>(Image <TColor> image, Stream stream) where TColor : struct, IPackedPixel, IEquatable <TColor> { Image <TColor> currentImage = image; this.currentStream = stream; this.currentStream.Skip(8); bool isEndChunkReached = false; using (MemoryStream dataStream = new MemoryStream()) { PngChunk currentChunk; while ((currentChunk = this.ReadChunk()) != null) { if (isEndChunkReached) { throw new ImageFormatException("Image does not end with end chunk."); } try { switch (currentChunk.Type) { case PngChunkTypes.Header: this.ReadHeaderChunk(currentChunk.Data); this.ValidateHeader(); break; case PngChunkTypes.Physical: this.ReadPhysicalChunk(currentImage, currentChunk.Data); break; case PngChunkTypes.Data: dataStream.Write(currentChunk.Data, 0, currentChunk.Length); break; case PngChunkTypes.Palette: byte[] pal = new byte[currentChunk.Length]; Buffer.BlockCopy(currentChunk.Data, 0, pal, 0, currentChunk.Length); this.palette = pal; image.MetaData.Quality = pal.Length / 3; break; case PngChunkTypes.PaletteAlpha: byte[] alpha = new byte[currentChunk.Length]; Buffer.BlockCopy(currentChunk.Data, 0, alpha, 0, currentChunk.Length); this.paletteAlpha = alpha; break; case PngChunkTypes.Text: this.ReadTextChunk(currentImage, currentChunk.Data, currentChunk.Length); break; case PngChunkTypes.End: isEndChunkReached = true; break; } } finally { // Data is rented in ReadChunkData() ArrayPool <byte> .Shared.Return(currentChunk.Data); } } if (this.header.Width > image.MaxWidth || this.header.Height > image.MaxHeight) { throw new ArgumentOutOfRangeException($"The input png '{this.header.Width}x{this.header.Height}' is bigger than the max allowed size '{image.MaxWidth}x{image.MaxHeight}'"); } image.InitPixels(this.header.Width, this.header.Height); using (PixelAccessor <TColor> pixels = image.Lock()) { this.ReadScanlines(dataStream, pixels); } } }
// Converts the alpha channel into a greyscale premultiplied texture. public static void ConvertAlphaToGrey(Bitmap bitmap) { using (var bitmapData = new PixelAccessor(bitmap, ImageLockMode.ReadWrite)) { for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { System.Drawing.Color color = bitmapData[x, y]; bitmapData[x, y] = System.Drawing.Color.FromArgb(color.A, color.A, color.A, color.A); } } } }
/// <inheritdoc/> protected override void OnApply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; int size = this.Value; int offset = this.Value / 2; // Align start/end positions. int minX = Math.Max(0, startX); int maxX = Math.Min(source.Width, endX); int minY = Math.Max(0, startY); int maxY = Math.Min(source.Height, endY); // Reset offset if necessary. if (minX > 0) { startX = 0; } if (minY > 0) { startY = 0; } // Get the range on the y-plane to choose from. IEnumerable<int> range = EnumerableExtensions.SteppedRange(minY, i => i < maxY, size); TColor[] target = new TColor[source.Width * source.Height]; using (PixelAccessor<TColor, TPacked> sourcePixels = source.Lock()) using (PixelAccessor<TColor, TPacked> targetPixels = target.Lock<TColor, TPacked>(source.Width, source.Height)) { Parallel.ForEach( range, this.ParallelOptions, y => { int offsetY = y - startY; int offsetPy = offset; for (int x = minX; x < maxX; x += size) { int offsetX = x - startX; int offsetPx = offset; // Make sure that the offset is within the boundary of the image. while (offsetY + offsetPy >= maxY) { offsetPy--; } while (x + offsetPx >= maxX) { offsetPx--; } // Get the pixel color in the centre of the soon to be pixelated area. // ReSharper disable AccessToDisposedClosure TColor pixel = sourcePixels[offsetX + offsetPx, offsetY + offsetPy]; // For each pixel in the pixelate size, set it to the centre color. for (int l = offsetY; l < offsetY + size && l < maxY; l++) { for (int k = offsetX; k < offsetX + size && k < maxX; k++) { targetPixels[k, l] = pixel; } } } }); source.SetPixels(source.Width, source.Height, target); } }
// Copies a single pixel within a bitmap, preserving RGB but forcing alpha to zero. static void CopyBorderPixel(PixelAccessor bitmapData, int sourceX, int sourceY, int destX, int destY) { DrawingColor color = bitmapData[sourceX, sourceY]; bitmapData[destX, destY] = DrawingColor.FromArgb(0, color); }
public virtual void ReceiveChunk(BufferChunk aRecord) { // First read out the record type int recordType = aRecord.NextInt32(); // Then deserialize the rest from there switch (recordType) { //case (int)UICommands.PixBlt: // { // // Get the X, Y // int x = aRecord.NextInt32(); // int y = aRecord.NextInt32(); // // get the length of the image bytes buffer // int dataSize = aRecord.NextInt32(); // byte[] imageBytes = (byte[])aRecord; // // Create a memory stream from the imageBytes // MemoryStream ms = new MemoryStream(imageBytes, false); // // Create a pixelArray from the stream // Bitmap bm = (Bitmap)Bitmap.FromStream(ms); // PixelArray<BGRAb> pixMap = PixelBufferHelper.CreatePixelArrayFromBitmap(bm); // // And finally, call the PixBlt function // PixBltBGRAb(pixMap, x, y); // } // break; case (int)UICommands.PixBltRLE: { // Get the X, Y int x = aRecord.NextInt32(); int y = aRecord.NextInt32(); int width = aRecord.NextInt32(); int height = aRecord.NextInt32(); // get the length of the image bytes buffer int dataSize = aRecord.NextInt32(); byte[] imageBytes = (byte[])aRecord; // Create a memory stream from the imageBytes MemoryStream ms = new MemoryStream(imageBytes, false); // Create a pixelArray from the stream if ((width != fPixMap.Width) || (height != fPixMap.Height)) fPixMap = new GDIDIBSection(width, height); TargaRunLengthCodec rlc = new TargaRunLengthCodec(); PixelAccessor<BGRb> accessor = new PixelAccessor<BGRb>(fPixMap.Width, fPixMap.Height, fPixMap.Orientation, fPixMap.Pixels, fPixMap.BytesPerRow); rlc.Decode(ms, accessor); // And finally, call the local PixBlt function PixBltPixelBuffer24(fPixMap, x, y); } break; case (int)UICommands.PixBltLuminance: { // Get the X, Y int x = aRecord.NextInt32(); int y = aRecord.NextInt32(); int width = aRecord.NextInt32(); int height = aRecord.NextInt32(); // get the length of the image bytes buffer int dataSize = aRecord.NextInt32(); byte[] imageBytes = (byte[])aRecord; // Create a memory stream from the imageBytes MemoryStream ms = new MemoryStream(imageBytes, false); // Create a pixelArray from the stream if ((width != fGrayImage.Width) || (height != fGrayImage.Height)) fGrayImage = new PixelArray<Lumb>(width, height); TargaLuminanceRLE rlc = new TargaLuminanceRLE(); rlc.Decode(ms, fGrayImage); // And finally, call the local PixBlt function PixBltLumb(fGrayImage, x, y); } break; default: //if (CommandReceived != null) // CommandReceived(aRecord); break; } }
/// <inheritdoc /> public BrushApplicator <TPixel> CreateApplicator(PixelAccessor <TPixel> sourcePixels, RectangleF region) { return(new PatternBrushApplicator(sourcePixels, this.pattern, this.patternVector)); }
/// <summary> /// Initializes a new instance of the <see cref="SolidBrushApplicator"/> class. /// </summary> /// <param name="color">The color.</param> /// <param name="sourcePixels">The sourcePixels.</param> public SolidBrushApplicator(PixelAccessor <TColor> sourcePixels, TColor color) : base(sourcePixels) { this.color = color; this.colorVector = color.ToVector4(); }
/// <summary> /// Initializes the image and various buffers needed for processing /// </summary> /// <typeparam name="TPixel">The type the pixels will be</typeparam> /// <param name="metadata">The metadata information for the image</param> /// <param name="image">The image that we will populate</param> /// <param name="pixels">The pixel accessor</param> private void InitializeImage <TPixel>(ImageMetaData metadata, out Image <TPixel> image, out PixelAccessor <TPixel> pixels) where TPixel : struct, IPixel <TPixel> { if (this.header.Width > Image <TPixel> .MaxWidth || this.header.Height > Image <TPixel> .MaxHeight) { throw new ArgumentOutOfRangeException($"The input png '{this.header.Width}x{this.header.Height}' is bigger than the max allowed size '{Image<TPixel>.MaxWidth}x{Image<TPixel>.MaxHeight}'"); } image = new Image <TPixel>(this.configuration, this.header.Width, this.header.Height, metadata); pixels = image.Lock(); this.bytesPerPixel = this.CalculateBytesPerPixel(); this.bytesPerScanline = this.CalculateScanlineLength(this.header.Width) + 1; this.bytesPerSample = 1; if (this.header.BitDepth >= 8) { this.bytesPerSample = this.header.BitDepth / 8; } this.previousScanline = ArrayPool <byte> .Shared.Rent(this.bytesPerScanline); this.scanline = ArrayPool <byte> .Shared.Rent(this.bytesPerScanline); // Zero out the scanlines, because the bytes that are rented from the arraypool may not be zero. Array.Clear(this.scanline, 0, this.bytesPerScanline); Array.Clear(this.previousScanline, 0, this.bytesPerScanline); }
/// <inheritdoc/> protected override void OnApply(ImageBase <TColor, TPacked> source, Rectangle sourceRectangle) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; int radius = this.BrushSize >> 1; int levels = this.Levels; // Align start/end positions. int minX = Math.Max(0, startX); int maxX = Math.Min(source.Width, endX); int minY = Math.Max(0, startY); int maxY = Math.Min(source.Height, endY); // Reset offset if necessary. if (minX > 0) { startX = 0; } TColor[] target = new TColor[source.Width * source.Height]; using (PixelAccessor <TColor, TPacked> sourcePixels = source.Lock()) using (PixelAccessor <TColor, TPacked> targetPixels = target.Lock <TColor, TPacked>(source.Width, source.Height)) { Parallel.For( minY, maxY, this.ParallelOptions, y => { for (int x = startX; x < endX; x++) { int maxIntensity = 0; int maxIndex = 0; int[] intensityBin = new int[levels]; float[] redBin = new float[levels]; float[] blueBin = new float[levels]; float[] greenBin = new float[levels]; for (int fy = 0; fy <= radius; fy++) { int fyr = fy - radius; int offsetY = y + fyr; // Skip the current row if (offsetY < minY) { continue; } // Outwith the current bounds so break. if (offsetY >= maxY) { break; } for (int fx = 0; fx <= radius; fx++) { int fxr = fx - radius; int offsetX = x + fxr; // Skip the column if (offsetX < 0) { continue; } if (offsetX < maxX) { // ReSharper disable once AccessToDisposedClosure Vector4 color = sourcePixels[offsetX, offsetY].ToVector4(); float sourceRed = color.X; float sourceBlue = color.Z; float sourceGreen = color.Y; int currentIntensity = (int)Math.Round((sourceBlue + sourceGreen + sourceRed) / 3.0 * (levels - 1)); intensityBin[currentIntensity] += 1; blueBin[currentIntensity] += sourceBlue; greenBin[currentIntensity] += sourceGreen; redBin[currentIntensity] += sourceRed; if (intensityBin[currentIntensity] > maxIntensity) { maxIntensity = intensityBin[currentIntensity]; maxIndex = currentIntensity; } } } float red = Math.Abs(redBin[maxIndex] / maxIntensity); float green = Math.Abs(greenBin[maxIndex] / maxIntensity); float blue = Math.Abs(blueBin[maxIndex] / maxIntensity); TColor packed = default(TColor); packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W)); targetPixels[x, y] = packed; } } }); } source.SetPixels(source.Width, source.Height, target); }
/// <inheritdoc/> protected override void OnApply(ImageBase <TPixel> source, Rectangle sourceRectangle) { int kernelYHeight = this.KernelY.Height; int kernelYWidth = this.KernelY.Width; int kernelXHeight = this.KernelX.Height; int kernelXWidth = this.KernelX.Width; int radiusY = kernelYHeight >> 1; int radiusX = kernelXWidth >> 1; int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; int maxY = endY - 1; int maxX = endX - 1; using (var targetPixels = new PixelAccessor <TPixel>(source.Width, source.Height)) { source.CopyTo(targetPixels); Parallel.For( startY, endY, this.ParallelOptions, y => { Span <TPixel> sourceRow = source.GetRowSpan(y); Span <TPixel> targetRow = targetPixels.GetRowSpan(y); for (int x = startX; x < endX; x++) { float rX = 0; float gX = 0; float bX = 0; float rY = 0; float gY = 0; float bY = 0; // Apply each matrix multiplier to the color components for each pixel. for (int fy = 0; fy < kernelYHeight; fy++) { int fyr = fy - radiusY; int offsetY = y + fyr; offsetY = offsetY.Clamp(0, maxY); Span <TPixel> sourceOffsetRow = source.GetRowSpan(offsetY); for (int fx = 0; fx < kernelXWidth; fx++) { int fxr = fx - radiusX; int offsetX = x + fxr; offsetX = offsetX.Clamp(0, maxX); var currentColor = sourceOffsetRow[offsetX].ToVector4(); if (fy < kernelXHeight) { Vector4 kx = this.KernelX[fy, fx] * currentColor; rX += kx.X; gX += kx.Y; bX += kx.Z; } if (fx < kernelYWidth) { Vector4 ky = this.KernelY[fy, fx] * currentColor; rY += ky.X; gY += ky.Y; bY += ky.Z; } } } float red = MathF.Sqrt((rX * rX) + (rY * rY)); float green = MathF.Sqrt((gX * gX) + (gY * gY)); float blue = MathF.Sqrt((bX * bX) + (bY * bY)); ref TPixel pixel = ref targetRow[x]; pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W)); } }); source.SwapPixelsBuffers(targetPixels); } }
/// <summary> /// Initializes a new instance of the <see cref="PatternBrushApplicator" /> class. /// </summary> /// <param name="sourcePixels">The sourcePixels.</param> /// <param name="pattern">The pattern.</param> /// <param name="patternVector">The patternVector.</param> public PatternBrushApplicator(PixelAccessor <TPixel> sourcePixels, Fast2DArray <TPixel> pattern, Fast2DArray <Vector4> patternVector) : base(sourcePixels) { this.pattern = pattern; this.patternVector = patternVector; }
/// <inheritdoc /> public BrushApplicator <TPixel> CreateApplicator(PixelAccessor <TPixel> sourcePixels, RectangleF region, GraphicsOptions options) { return(new PatternBrushApplicator(sourcePixels, this.pattern, this.patternVector, options)); }
// Converts greyscale luminosity to alpha data. public static void ConvertGreyToAlpha(Bitmap bitmap, Rectangle region) { using (var bitmapData = new PixelAccessor(bitmap, ImageLockMode.ReadWrite, region)) { for (int y = 0; y < region.Height; y++) { for (int x = 0; x < region.Width; x++) { var color = bitmapData[x, y]; // Average the red, green and blue values to compute brightness. var alpha = (color.R + color.G + color.B) / 3; bitmapData[x, y] = DrawingColor.FromArgb(alpha, 255, 255, 255); } } } }
/// <inheritdoc /> public BrushApplicator <TColor> CreateApplicator(PixelAccessor <TColor> sourcePixels, RectangleF region) { return(new RecolorBrushApplicator(sourcePixels, this.SourceColor, this.TargetColor, this.Threshold)); }
// Converts a bitmap to premultiplied alpha format. public static void PremultiplyAlpha(Bitmap bitmap, bool srgb) { using (var bitmapData = new PixelAccessor(bitmap, ImageLockMode.ReadWrite)) { for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { DrawingColor color = bitmapData[x, y]; int a = color.A; int r; int g; int b; if (srgb) { var colorLinear = new Color4(new SiliconStudio.Core.Mathematics.Color(color.R, color.G, color.B)).ToLinear(); colorLinear *= color.A / 255.0f; var colorSRgb = (SiliconStudio.Core.Mathematics.Color)colorLinear.ToSRgb(); r = colorSRgb.R; g = colorSRgb.G; b = colorSRgb.B; } else { r = color.R * a / 255; g = color.G * a / 255; b = color.B * a / 255; } bitmapData[x, y] = DrawingColor.FromArgb(a, r, g, b); } } } }
public void Dither <TColor>(PixelAccessor <TColor> pixels, TColor source, TColor transformed, int x, int y, int width, int height) where TColor : struct, IPixel <TColor> { this.Dither(pixels, source, transformed, x, y, width, height, true); }
BufferChunk EncodeBGRb(PixelAccessor<BGRb> accessor, int x, int y) { fImageStream.SetLength(0); // 2. Run length encode the image to a memory stream NewTOAPIA.Imaging.TargaRunLengthCodec rlc = new NewTOAPIA.Imaging.TargaRunLengthCodec(); rlc.Encode(accessor, fImageStream); // 3. Get the bytes from the stream byte[] imageBytes = fImageStream.GetBuffer(); int dataLength = (int)imageBytes.Length; // 4. Allocate a buffer chunk to accomodate the bytes, plus some more BufferChunk chunk = new BufferChunk(dataLength + 128); // 5. Put the command, destination, and data size into the buffer first chunk += (int)UICommands.PixBltRLE; chunk += (int)x; chunk += (int)y; chunk += (int)accessor.Width; chunk += (int)accessor.Height; chunk += dataLength; // 6. Put the image bytes into the chunk chunk += imageBytes; // 7. Finally, return the packet return chunk; }
/// <inheritdoc /> protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration) { DenseMatrix <float>[] kernels = { this.North, this.NorthWest, this.West, this.SouthWest, this.South, this.SouthEast, this.East, this.NorthEast }; int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; // Align start/end positions. int minX = Math.Max(0, startX); int maxX = Math.Min(source.Width, endX); int minY = Math.Max(0, startY); int maxY = Math.Min(source.Height, endY); // we need a clean copy for each pass to start from using (ImageFrame <TPixel> cleanCopy = source.Clone()) { new ConvolutionProcessor <TPixel>(kernels[0]).Apply(source, sourceRectangle, configuration); if (kernels.Length == 1) { return; } int shiftY = startY; int shiftX = startX; // Reset offset if necessary. if (minX > 0) { shiftX = 0; } if (minY > 0) { shiftY = 0; } // Additional runs. // ReSharper disable once ForCanBeConvertedToForeach for (int i = 1; i < kernels.Length; i++) { using (ImageFrame <TPixel> pass = cleanCopy.Clone()) { new ConvolutionProcessor <TPixel>(kernels[i]).Apply(pass, sourceRectangle, configuration); using (PixelAccessor <TPixel> passPixels = pass.Lock()) using (PixelAccessor <TPixel> targetPixels = source.Lock()) { Parallel.For( minY, maxY, configuration.ParallelOptions, y => { int offsetY = y - shiftY; for (int x = minX; x < maxX; x++) { int offsetX = x - shiftX; // Grab the max components of the two pixels TPixel packed = default(TPixel); packed.PackFromVector4(Vector4.Max(passPixels[offsetX, offsetY].ToVector4(), targetPixels[offsetX, offsetY].ToVector4())); targetPixels[offsetX, offsetY] = packed; } }); } } } } }
/// <inheritdoc/> protected override void OnApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration) { if (this.BrushSize <= 0 || this.BrushSize > source.Height || this.BrushSize > source.Width) { throw new ArgumentOutOfRangeException(nameof(this.BrushSize)); } int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; int maxY = endY - 1; int maxX = endX - 1; int radius = this.BrushSize >> 1; int levels = this.Levels; using (var targetPixels = new PixelAccessor <TPixel>(source.Width, source.Height)) { source.CopyTo(targetPixels); Parallel.For( startY, maxY, configuration.ParallelOptions, y => { Span <TPixel> sourceRow = source.GetPixelRowSpan(y); Span <TPixel> targetRow = targetPixels.GetRowSpan(y); for (int x = startX; x < endX; x++) { int maxIntensity = 0; int maxIndex = 0; int[] intensityBin = new int[levels]; float[] redBin = new float[levels]; float[] blueBin = new float[levels]; float[] greenBin = new float[levels]; for (int fy = 0; fy <= radius; fy++) { int fyr = fy - radius; int offsetY = y + fyr; offsetY = offsetY.Clamp(0, maxY); Span <TPixel> sourceOffsetRow = source.GetPixelRowSpan(offsetY); for (int fx = 0; fx <= radius; fx++) { int fxr = fx - radius; int offsetX = x + fxr; offsetX = offsetX.Clamp(0, maxX); var vector = sourceOffsetRow[offsetX].ToVector4(); float sourceRed = vector.X; float sourceBlue = vector.Z; float sourceGreen = vector.Y; int currentIntensity = (int)Math.Round((sourceBlue + sourceGreen + sourceRed) / 3F * (levels - 1)); intensityBin[currentIntensity] += 1; blueBin[currentIntensity] += sourceBlue; greenBin[currentIntensity] += sourceGreen; redBin[currentIntensity] += sourceRed; if (intensityBin[currentIntensity] > maxIntensity) { maxIntensity = intensityBin[currentIntensity]; maxIndex = currentIntensity; } } float red = Math.Abs(redBin[maxIndex] / maxIntensity); float green = Math.Abs(greenBin[maxIndex] / maxIntensity); float blue = Math.Abs(blueBin[maxIndex] / maxIntensity); ref TPixel pixel = ref targetRow[x]; pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W)); } } }); source.SwapPixelsBuffers(targetPixels); } }
/// <summary> /// Encode writes the image to the jpeg baseline format with the given options. /// </summary> /// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <param name="image">The image to write from.</param> /// <param name="stream">The stream to write to.</param> /// <param name="quality">The quality.</param> /// <param name="sample">The subsampling mode.</param> public void Encode <TColor, TPacked>(Image <TColor, TPacked> image, Stream stream, int quality, JpegSubsample sample) where TColor : struct, IPackedPixel <TPacked> where TPacked : struct { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); ushort max = JpegConstants.MaxLength; if (image.Width >= max || image.Height >= max) { throw new ImageFormatException($"Image is too large to encode at {image.Width}x{image.Height}."); } this.outputStream = stream; this.subsample = sample; for (int i = 0; i < QuantizationTableCount; i++) { this.quant[i] = new byte[Block.BlockSize]; } if (quality < 1) { quality = 1; } if (quality > 100) { quality = 100; } // Convert from a quality rating to a scaling factor. int scale; if (quality < 50) { scale = 5000 / quality; } else { scale = 200 - (quality * 2); } // Initialize the quantization tables. for (int i = 0; i < QuantizationTableCount; i++) { for (int j = 0; j < Block.BlockSize; j++) { int x = this.unscaledQuant[i, j]; x = ((x * scale) + 50) / 100; if (x < 1) { x = 1; } if (x > 255) { x = 255; } this.quant[i][j] = (byte)x; } } // Compute number of components based on input image type. int componentCount = 3; // Write the Start Of Image marker. this.WriteApplicationHeader((short)image.HorizontalResolution, (short)image.VerticalResolution); this.WriteProfiles(image); // Write the quantization tables. this.WriteDefineQuantizationTables(); // Write the image dimensions. this.WriteStartOfFrame(image.Width, image.Height, componentCount); // Write the Huffman tables. this.WriteDefineHuffmanTables(componentCount); // Write the image data. using (PixelAccessor <TColor, TPacked> pixels = image.Lock()) { this.WriteStartOfScan(pixels); } // Write the End Of Image marker. this.buffer[0] = JpegConstants.Markers.XFF; this.buffer[1] = JpegConstants.Markers.EOI; stream.Write(this.buffer, 0, 2); stream.Flush(); }
// Converts a bitmap to premultiplied alpha format. public static void PremultiplyAlphaClearType(Bitmap bitmap) { using (var bitmapData = new PixelAccessor(bitmap, ImageLockMode.ReadWrite)) { for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { Color color = bitmapData[x, y]; int a = (color.R + color.G + color.B) / 3; int r = color.R; int g = color.G; int b = color.B; bitmapData[x, y] = Color.FromArgb(a, r, g, b); } } } }
/// <inheritdoc/> protected override void OnApply(ImageBase <TPixel> source, Rectangle sourceRectangle) { Region region = this.Region; Rectangle rect = region.Bounds; // Align start/end positions. int minX = Math.Max(0, rect.Left); int maxX = Math.Min(source.Width, rect.Right); int minY = Math.Max(0, rect.Top); int maxY = Math.Min(source.Height, rect.Bottom); if (minX >= maxX) { return; // no effect inside image; } if (minY >= maxY) { return; // no effect inside image; } ArrayPool <float> arrayPool = ArrayPool <float> .Shared; int maxIntersections = region.MaxIntersections; float subpixelCount = 4; if (this.Options.Antialias) { subpixelCount = this.Options.AntialiasSubpixelDepth; if (subpixelCount < 4) { subpixelCount = 4; } } using (PixelAccessor <TPixel> sourcePixels = source.Lock()) using (BrushApplicator <TPixel> applicator = this.Brush.CreateApplicator(sourcePixels, rect, this.Options)) { float[] buffer = arrayPool.Rent(maxIntersections); int scanlineWidth = maxX - minX; using (Buffer <float> scanline = new Buffer <float>(scanlineWidth)) { try { bool scanlineDirty = true; for (int y = minY; y < maxY; y++) { if (scanlineDirty) { // clear the buffer for (int x = 0; x < scanlineWidth; x++) { scanline[x] = 0; } scanlineDirty = false; } float subpixelFraction = 1f / subpixelCount; float subpixelFractionPoint = subpixelFraction / subpixelCount; for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction) { int pointsFound = region.Scan(subPixel, buffer, maxIntersections, 0); if (pointsFound == 0) { // nothing on this line skip continue; } QuickSort(buffer, pointsFound); for (int point = 0; point < pointsFound; point += 2) { // points will be paired up float scanStart = buffer[point] - minX; float scanEnd = buffer[point + 1] - minX; int startX = (int)MathF.Floor(scanStart); int endX = (int)MathF.Floor(scanEnd); if (startX >= 0 && startX < scanline.Length) { for (float x = scanStart; x < startX + 1; x += subpixelFraction) { scanline[startX] += subpixelFractionPoint; scanlineDirty = true; } } if (endX >= 0 && endX < scanline.Length) { for (float x = endX; x < scanEnd; x += subpixelFraction) { scanline[endX] += subpixelFractionPoint; scanlineDirty = true; } } int nextX = startX + 1; endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge if (nextX >= 0) { for (int x = nextX; x < endX; x++) { scanline[x] += subpixelFraction; scanlineDirty = true; } } } } if (scanlineDirty) { if (!this.Options.Antialias) { for (int x = 0; x < scanlineWidth; x++) { if (scanline[x] > 0.5) { scanline[x] = 1; } else { scanline[x] = 0; } } } applicator.Apply(scanline, minX, y); } } } finally { arrayPool.Return(buffer); } } } }
// Converts a bitmap to premultiplied alpha format. public static void PremultiplyAlpha(Bitmap bitmap) { using (var bitmapData = new PixelAccessor(bitmap, ImageLockMode.ReadWrite)) { for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { System.Drawing.Color color = bitmapData[x, y]; int a = color.A; int r = color.R * a / 255; int g = color.G * a / 255; int b = color.B * a / 255; bitmapData[x, y] = System.Drawing.Color.FromArgb(a, r, g, b); } } } }
/// <inheritdoc /> protected override void OnApply(ImageBase <TColor, TPacked> source, Rectangle sourceRectangle) { float[][][] kernels = { this.North, this.NorthWest, this.West, this.SouthWest, this.South, this.SouthEast, this.East, this.NorthEast }; int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; // Align start/end positions. int minX = Math.Max(0, startX); int maxX = Math.Min(source.Width, endX); int minY = Math.Max(0, startY); int maxY = Math.Min(source.Height, endY); // First run. ImageBase <TColor, TPacked> target = new Image <TColor, TPacked>(source.Width, source.Height); target.ClonePixels(source.Width, source.Height, source.Pixels); new ConvolutionProcessor <TColor, TPacked>(kernels[0]).Apply(target, sourceRectangle); if (kernels.Length == 1) { return; } int shiftY = startY; int shiftX = startX; // Reset offset if necessary. if (minX > 0) { shiftX = 0; } if (minY > 0) { shiftY = 0; } // Additional runs. // ReSharper disable once ForCanBeConvertedToForeach for (int i = 1; i < kernels.Length; i++) { // Create a clone for each pass and copy the offset pixels across. ImageBase <TColor, TPacked> pass = new Image <TColor, TPacked>(source.Width, source.Height); pass.ClonePixels(source.Width, source.Height, source.Pixels); new ConvolutionProcessor <TColor, TPacked>(kernels[i]).Apply(pass, sourceRectangle); using (PixelAccessor <TColor, TPacked> passPixels = pass.Lock()) using (PixelAccessor <TColor, TPacked> targetPixels = target.Lock()) { Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - shiftY; for (int x = minX; x < maxX; x++) { int offsetX = x - shiftX; // Grab the max components of the two pixels TColor packed = default(TColor); packed.PackFromVector4(Vector4.Max(passPixels[offsetX, offsetY].ToVector4(), targetPixels[offsetX, offsetY].ToVector4())); targetPixels[offsetX, offsetY] = packed; } }); } } source.SetPixels(source.Width, source.Height, target.Pixels); }
// To avoid filtering artifacts when scaling or rotating fonts that do not use premultiplied alpha, // make sure the one pixel border around each glyph contains the same RGB values as the edge of the // glyph itself, but with zero alpha. This processing is an elaborate no-op when using premultiplied // alpha, because the premultiply conversion will change the RGB of all such zero alpha pixels to black. public static void PadBorderPixels(Bitmap bitmap, System.Drawing.Rectangle region) { using (var bitmapData = new PixelAccessor(bitmap, ImageLockMode.ReadWrite)) { // Pad the top and bottom. for (int x = region.Left; x < region.Right; x++) { CopyBorderPixel(bitmapData, x, region.Top, x, region.Top - 1); CopyBorderPixel(bitmapData, x, region.Bottom - 1, x, region.Bottom); } // Pad the left and right. for (int y = region.Top; y < region.Bottom; y++) { CopyBorderPixel(bitmapData, region.Left, y, region.Left - 1, y); CopyBorderPixel(bitmapData, region.Right - 1, y, region.Right, y); } // Pad the four corners. CopyBorderPixel(bitmapData, region.Left, region.Top, region.Left - 1, region.Top - 1); CopyBorderPixel(bitmapData, region.Right - 1, region.Top, region.Right, region.Top - 1); CopyBorderPixel(bitmapData, region.Left, region.Bottom - 1, region.Left - 1, region.Bottom); CopyBorderPixel(bitmapData, region.Right - 1, region.Bottom - 1, region.Right, region.Bottom); } }
/// <summary> /// Encode writes the image to the jpeg baseline format with the given options. /// </summary> /// <typeparam name="TColor">The pixel format.</typeparam> /// <param name="image">The image to write from.</param> /// <param name="stream">The stream to write to.</param> /// <param name="quality">The quality.</param> /// <param name="sample">The subsampling mode.</param> public void Encode <TColor>(Image <TColor> image, Stream stream, int quality, JpegSubsample sample) where TColor : struct, IPackedPixel, IEquatable <TColor> { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); ushort max = JpegConstants.MaxLength; if (image.Width >= max || image.Height >= max) { throw new ImageFormatException($"Image is too large to encode at {image.Width}x{image.Height}."); } this.outputStream = stream; this.subsample = sample; if (quality < 1) { quality = 1; } if (quality > 100) { quality = 100; } // Convert from a quality rating to a scaling factor. int scale; if (quality < 50) { scale = 5000 / quality; } else { scale = 200 - (quality * 2); } // Initialize the quantization tables. InitQuantizationTable(0, scale, ref this.luminanceQuantTable); InitQuantizationTable(1, scale, ref this.chrominanceQuantTable); // Compute number of components based on input image type. int componentCount = 3; // Write the Start Of Image marker. this.WriteApplicationHeader((short)image.MetaData.HorizontalResolution, (short)image.MetaData.VerticalResolution); this.WriteProfiles(image); // Write the quantization tables. this.WriteDefineQuantizationTables(); // Write the image dimensions. this.WriteStartOfFrame(image.Width, image.Height, componentCount); // Write the Huffman tables. this.WriteDefineHuffmanTables(componentCount); // Write the image data. using (PixelAccessor <TColor> pixels = image.Lock()) { this.WriteStartOfScan(pixels); } // Write the End Of Image marker. this.buffer[0] = JpegConstants.Markers.XFF; this.buffer[1] = JpegConstants.Markers.EOI; stream.Write(this.buffer, 0, 2); stream.Flush(); }
/// <summary> /// Encodes the image with subsampling. The Cb and Cr components are each subsampled /// at a factor of 2 both horizontally and vertically. /// </summary> /// <typeparam name="TColor">The pixel format.</typeparam> /// <param name="pixels">The pixel accessor providing access to the image pixels.</param> private void Encode420 <TColor>(PixelAccessor <TColor> pixels) where TColor : struct, IPackedPixel, IEquatable <TColor> { // TODO: Need a JpegScanEncoder<TColor> class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) Block8x8F b = default(Block8x8F); BlockQuad cb = default(BlockQuad); BlockQuad cr = default(BlockQuad); Block8x8F *cbPtr = (Block8x8F *)cb.Data; Block8x8F *crPtr = (Block8x8F *)cr.Data; Block8x8F temp1 = default(Block8x8F); Block8x8F temp2 = default(Block8x8F); Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable; Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable; UnzigData unzig = UnzigData.Create(); // ReSharper disable once InconsistentNaming int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; using (PixelArea <TColor> rgbBytes = new PixelArea <TColor>(8, 8, ComponentOrder.Xyz)) { for (int y = 0; y < pixels.Height; y += 16) { for (int x = 0; x < pixels.Width; x += 16) { for (int i = 0; i < 4; i++) { int xOff = (i & 1) * 8; int yOff = (i & 2) * 4; ToYCbCr(pixels, x + xOff, y + yOff, &b, cbPtr + i, crPtr + i, rgbBytes); prevDCY = this.WriteBlock( QuantIndex.Luminance, prevDCY, &b, &temp1, &temp2, &onStackLuminanceQuantTable, unzig.Data); } Block8x8F.Scale16X16To8X8(&b, cbPtr); prevDCCb = this.WriteBlock( QuantIndex.Chrominance, prevDCCb, &b, &temp1, &temp2, &onStackChrominanceQuantTable, unzig.Data); Block8x8F.Scale16X16To8X8(&b, crPtr); prevDCCr = this.WriteBlock( QuantIndex.Chrominance, prevDCCr, &b, &temp1, &temp2, &onStackChrominanceQuantTable, unzig.Data); } } } }
/// <inheritdoc/> protected override void OnApply(ImageBase <TColor> source, Rectangle sourceRectangle) { // Jump out, we'll deal with that later. if (source.Width == this.Width && source.Height == this.Height && sourceRectangle == this.ResizeRectangle) { return; } int width = this.Width; int height = this.Height; int sourceX = sourceRectangle.X; int sourceY = sourceRectangle.Y; int startY = this.ResizeRectangle.Y; int endY = this.ResizeRectangle.Bottom; int startX = this.ResizeRectangle.X; int endX = this.ResizeRectangle.Right; int minX = Math.Max(0, startX); int maxX = Math.Min(width, endX); int minY = Math.Max(0, startY); int maxY = Math.Min(height, endY); if (this.Sampler is NearestNeighborResampler) { // Scaling factors float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; using (PixelAccessor <TColor> targetPixels = new PixelAccessor <TColor>(width, height)) { using (PixelAccessor <TColor> sourcePixels = source.Lock()) { Parallel.For( minY, maxY, this.ParallelOptions, y => { // Y coordinates of source points int originY = (int)(((y - startY) * heightFactor) + sourceY); for (int x = minX; x < maxX; x++) { // X coordinates of source points targetPixels[x, y] = sourcePixels[(int)(((x - startX) * widthFactor) + sourceX), originY]; } }); } // Break out now. source.SwapPixelsBuffers(targetPixels); return; } } // Interpolate the image using the calculated weights. // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm // First process the columns. Since we are not using multiple threads startY and endY // are the upper and lower bounds of the source rectangle. using (PixelAccessor <TColor> targetPixels = new PixelAccessor <TColor>(width, height)) { using (PixelAccessor <TColor> sourcePixels = source.Lock()) using (PixelAccessor <TColor> firstPassPixels = new PixelAccessor <TColor>(width, source.Height)) { Parallel.For( 0, sourceRectangle.Bottom, this.ParallelOptions, y => { for (int x = minX; x < maxX; x++) { // Ensure offsets are normalised for cropping and padding. Weight[] horizontalValues = this.HorizontalWeights[x - startX].Values; // Destination color components Vector4 destination = Vector4.Zero; for (int i = 0; i < horizontalValues.Length; i++) { Weight xw = horizontalValues[i]; destination += sourcePixels[xw.Index + sourceX, y].ToVector4().Expand() * xw.Value; } TColor d = default(TColor); d.PackFromVector4(destination.Compress()); firstPassPixels[x, y] = d; } }); // Now process the rows. Parallel.For( minY, maxY, this.ParallelOptions, y => { // Ensure offsets are normalised for cropping and padding. Weight[] verticalValues = this.VerticalWeights[y - startY].Values; for (int x = 0; x < width; x++) { // Destination color components Vector4 destination = Vector4.Zero; for (int i = 0; i < verticalValues.Length; i++) { Weight yw = verticalValues[i]; destination += firstPassPixels[x, yw.Index + sourceY].ToVector4().Expand() * yw.Value; } TColor d = default(TColor); d.PackFromVector4(destination.Compress()); targetPixels[x, y] = d; } }); } source.SwapPixelsBuffers(targetPixels); } }
// Converts greyscale luminosity to alpha data. public static void ConvertGreyToAlpha(Bitmap bitmap) { using (var bitmapData = new PixelAccessor(bitmap, ImageLockMode.ReadWrite)) { for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { System.Drawing.Color color = bitmapData[x, y]; // Average the red, green and blue values to compute brightness. int alpha = (color.R + color.G + color.B) / 3; bitmapData[x, y] = System.Drawing.Color.FromArgb(alpha, 255, 255, 255); } } } }
/// <summary> /// Reads the frames colors, mapping indices to colors. /// </summary> /// <param name="indices">The indexed pixels.</param> /// <param name="colorTable">The color table containing the available colors.</param> /// <param name="colorTableLength">The color table length.</param> /// <param name="descriptor">The <see cref="GifImageDescriptor"/></param> private unsafe void ReadFrameColors(byte[] indices, byte[] colorTable, int colorTableLength, GifImageDescriptor descriptor) { int imageWidth = this.logicalScreenDescriptor.Width; int imageHeight = this.logicalScreenDescriptor.Height; ImageFrame <TColor> previousFrame = null; ImageFrame <TColor> currentFrame = null; ImageBase <TColor> image; if (this.previousFrame == null) { this.decodedImage.MetaData.Quality = colorTableLength / 3; // This initializes the image to become fully transparent because the alpha channel is zero. this.decodedImage.InitPixels(imageWidth, imageHeight); this.SetFrameDelay(this.decodedImage.MetaData); image = this.decodedImage; } else { if (this.graphicsControlExtension != null && this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious) { previousFrame = this.previousFrame; } currentFrame = this.previousFrame.Clone(); this.SetFrameDelay(currentFrame.MetaData); image = currentFrame; this.RestoreToBackground(image); this.decodedImage.Frames.Add(currentFrame); } int i = 0; int interlacePass = 0; // The interlace pass int interlaceIncrement = 8; // The interlacing line increment int interlaceY = 0; // The current interlaced line using (PixelAccessor <TColor> pixelAccessor = image.Lock()) { for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++) { // Check if this image is interlaced. int writeY; // the target y offset to write to if (descriptor.InterlaceFlag) { // If so then we read lines at predetermined offsets. // When an entire image height worth of offset lines has been read we consider this a pass. // With each pass the number of offset lines changes and the starting line changes. if (interlaceY >= descriptor.Height) { interlacePass++; switch (interlacePass) { case 1: interlaceY = 4; break; case 2: interlaceY = 2; interlaceIncrement = 4; break; case 3: interlaceY = 1; interlaceIncrement = 2; break; } } writeY = interlaceY + descriptor.Top; interlaceY += interlaceIncrement; } else { writeY = y; } for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++) { int index = indices[i]; if (this.graphicsControlExtension == null || this.graphicsControlExtension.TransparencyFlag == false || this.graphicsControlExtension.TransparencyIndex != index) { int indexOffset = index * 3; TColor pixel = default(TColor); pixel.PackFromBytes(colorTable[indexOffset], colorTable[indexOffset + 1], colorTable[indexOffset + 2], 255); pixelAccessor[x, writeY] = pixel; } i++; } } } if (previousFrame != null) { this.previousFrame = previousFrame; return; } this.previousFrame = currentFrame == null?this.decodedImage.ToFrame() : currentFrame; if (this.graphicsControlExtension != null && this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground) { this.restoreArea = new Rectangle(descriptor.Left, descriptor.Top, descriptor.Width, descriptor.Height); } }
public virtual void PixBltBGRb(PixelAccessor<BGRb> pixBuff, int x, int y) { if (null != PixBltBGRbHandler) PixBltBGRbHandler(pixBuff, x, y); }
// Drawing against a transparent black background blends // the 'alpha' pixels against black, leaving a black outline. // Interpolate between black and white // based on it's intensity to covert this 'outline' to // it's grayscale equivalent. public static void ConvertToGrey( Bitmap bitmap) { var transBlack = Color.Transparent; using (var bitmapData = new PixelAccessor(bitmap, ImageLockMode.ReadWrite)) { for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { System.Drawing.Color color = bitmapData[x, y]; if (color.ColorsEqual(transBlack)) continue; var alpha = color.A / (255.0f); var col = Color.Lerp(Color.Transparent, Color.White, alpha); color = System.Drawing.Color.FromArgb(color.A, col.R, col.G, col.B); bitmapData [x, y] = color; } } } }