/// <inheritdoc/> protected override void Apply(ImageBase <T, TP> target, ImageBase <T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { // Jump out, we'll deal with that later. if (source.Bounds == target.Bounds) { return; } int targetY = this.cropRectangle.Y; int targetBottom = this.cropRectangle.Bottom; int startX = this.cropRectangle.X; int endX = this.cropRectangle.Right; int minY = Math.Max(targetY, startY); int maxY = Math.Min(targetBottom, endY); using (IPixelAccessor <T, TP> sourcePixels = source.Lock()) using (IPixelAccessor <T, TP> targetPixels = target.Lock()) { Parallel.For( minY, maxY, this.ParallelOptions, y => { for (int x = startX; x < endX; x++) { targetPixels[x - startX, y - targetY] = sourcePixels[x, y]; } this.OnRowProcessed(); }); } }
/// <summary> /// Swaps the image at the Y-axis, which goes vertically through the middle /// at half of the width of the image. /// </summary> /// <param name="target">Target image to apply the process to.</param> private void FlipY(ImageBase <T, TP> target) { int width = target.Width; int height = target.Height; int halfWidth = (int)Math.Ceiling(width * .5F); Image <T, TP> temp = new Image <T, TP>(width, height); temp.ClonePixels(width, height, target.Pixels); using (IPixelAccessor <T, TP> targetPixels = target.Lock()) using (IPixelAccessor <T, TP> tempPixels = temp.Lock()) { Parallel.For( 0, height, this.ParallelOptions, y => { for (int x = 0; x < halfWidth; x++) { int newX = width - x - 1; targetPixels[x, y] = tempPixels[newX, y]; targetPixels[newX, y] = tempPixels[x, y]; } this.OnRowProcessed(); }); } }
/// <summary> /// Collects the true color pixel data. /// </summary> /// <typeparam name="T">The pixel format.</typeparam> /// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam> /// <param name="image">The image to encode.</param> private void CollectColorBytes <T, TP>(ImageBase <T, TP> image) where T : IPackedVector <TP> where TP : struct { // Copy the pixels across from the image. this.pixelData = new byte[this.width * this.height * this.bytesPerPixel]; int stride = this.width * this.bytesPerPixel; using (IPixelAccessor <T, TP> pixels = image.Lock()) { Parallel.For( 0, this.height, Bootstrapper.Instance.ParallelOptions, y => { // Color data is stored in r -> g -> b -> a order for (int x = 0; x < this.width; x++) { int dataOffset = (y * stride) + (x * this.bytesPerPixel); byte[] source = pixels[x, y].ToBytes(); for (int i = 0; i < this.bytesPerPixel; i++) { this.pixelData[dataOffset + i] = source[i]; } } }); } }
/// <summary> /// Rotates the image 90 degrees clockwise at the centre point. /// </summary> /// <param name="target">The target image.</param> /// <param name="source">The source image.</param> private void Rotate90(ImageBase <T, TP> target, ImageBase <T, TP> source) { int width = source.Width; int height = source.Height; Image <T, TP> temp = new Image <T, TP>(height, width); using (IPixelAccessor <T, TP> sourcePixels = source.Lock()) using (IPixelAccessor <T, TP> tempPixels = temp.Lock()) { Parallel.For( 0, height, this.ParallelOptions, y => { for (int x = 0; x < width; x++) { int newX = height - y - 1; tempPixels[newX, x] = sourcePixels[x, y]; } this.OnRowProcessed(); }); } target.SetPixels(height, width, temp.Pixels); }
/// <inheritdoc/> protected override void Apply(ImageBase <T, TP> target, ImageBase <T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { if (OptimizedApply(target, source)) { return; } int height = target.Height; int width = target.Width; Matrix3x2 matrix = GetCenteredMatrix(target, source, this.processMatrix); using (IPixelAccessor <T, TP> sourcePixels = source.Lock()) using (IPixelAccessor <T, TP> targetPixels = target.Lock()) { Parallel.For( 0, height, this.ParallelOptions, y => { for (int x = 0; x < width; x++) { Point transformedPoint = Point.Rotate(new Point(x, y), matrix); if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y)) { targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y]; } } this.OnRowProcessed(); }); } }
/// <summary> /// Builds a 3-D color histogram of <c>counts, r/g/b, c^2</c>. /// </summary> /// <param name="pixels">The pixel accessor.</param> private void Build3DHistogram(IPixelAccessor <T, TP> pixels) { for (int y = 0; y < pixels.Height; y++) { for (int x = 0; x < pixels.Width; x++) { // Colors are expected in r->g->b->a format byte[] color = pixels[x, y].ToBytes(); byte r = color[0]; byte g = color[1]; byte b = color[2]; byte a = color[3]; int inr = r >> (8 - IndexBits); int ing = g >> (8 - IndexBits); int inb = b >> (8 - IndexBits); int ina = a >> (8 - IndexAlphaBits); int ind = GetPaletteIndex(inr + 1, ing + 1, inb + 1, ina + 1); this.vwt[ind]++; this.vmr[ind] += r; this.vmg[ind] += g; this.vmb[ind] += b; this.vma[ind] += a; this.m2[ind] += (r * r) + (g * g) + (b * b) + (a * a); } } }
/// <summary> /// Rotates the image 180 degrees clockwise at the centre point. /// </summary> /// <param name="target">The target image.</param> /// <param name="source">The source image.</param> private void Rotate180(ImageBase <T, TP> target, ImageBase <T, TP> source) { int width = source.Width; int height = source.Height; using (IPixelAccessor <T, TP> sourcePixels = source.Lock()) using (IPixelAccessor <T, TP> targetPixels = target.Lock()) { Parallel.For( 0, height, this.ParallelOptions, y => { for (int x = 0; x < width; x++) { int newX = width - x - 1; int newY = height - y - 1; targetPixels[newX, newY] = sourcePixels[x, y]; } this.OnRowProcessed(); }); } }
/// <inheritdoc/> protected override void Apply(ImageBase <T, TP> target, ImageBase <T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { int startX = targetRectangle.X; int endX = targetRectangle.Right; int sourceX = sourceRectangle.X; int sourceY = sourceRectangle.Y; using (IPixelAccessor <T, TP> sourcePixels = source.Lock()) using (IPixelAccessor <T, TP> targetPixels = target.Lock()) { Parallel.For( startY, endY, this.ParallelOptions, y => { for (int x = startX; x < endX; x++) { targetPixels[x, y] = sourcePixels[x + sourceX, y + sourceY]; } this.OnRowProcessed(); }); } }
private void Build3DHistogram(IPixelAccessor pixels) { for (int y = 0; y < pixels.Height; y++) { for (int x = 0; x < pixels.Width; x++) { byte[] color = pixels[x, y].ToBytes(); byte r = color[0]; byte g = color[1]; byte b = color[2]; byte a = color[3]; int inr = r >> (8 - IndexBits); int ing = g >> (8 - IndexBits); int inb = b >> (8 - IndexBits); int ina = a >> (8 - IndexAlphaBits); int ind = GetPaletteIndex(inr + 1, ing + 1, inb + 1, ina + 1); vwt[ind]++; vmr[ind] += r; vmg[ind] += g; vmb[ind] += b; vma[ind] += a; m2[ind] += (r * r) + (g * g) + (b * b) + (a * a); } } }
/// <inheritdoc/> public virtual QuantizedImage <T, TP> Quantize(ImageBase <T, TP> image, int maxColors) { Guard.NotNull(image, nameof(image)); // Get the size of the source image int height = image.Height; int width = image.Width; byte[] quantizedPixels = new byte[width * height]; List <T> palette; using (IPixelAccessor <T, TP> pixels = image.Lock()) { // Call the FirstPass function if not a single pass algorithm. // For something like an Octree quantizer, this will run through // all image pixels, build a data structure, and create a palette. if (!this.singlePass) { this.FirstPass(pixels, width, height); } // Get the palette palette = this.GetPalette(); this.SecondPass(pixels, quantizedPixels, width, height); } return(new QuantizedImage <T, TP>(width, height, palette.ToArray(), quantizedPixels, this.TransparentIndex)); }
private void Encode420(IPixelAccessor pixels) { Block b = new Block(); Block[] cb = new Block[4]; Block[] cr = new Block[4]; int prevDcy = 0, prevDcCb = 0, prevDcCr = 0; for (int i = 0; i < 4; i++){ cb[i] = new Block(); } for (int i = 0; i < 4; i++){ cr[i] = new Block(); } 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, cb[i], cr[i]); prevDcy = WriteBlock(b, QuantIndex.Luminance, prevDcy); } Scale16X16_8X8(b, cb); prevDcCb = WriteBlock(b, QuantIndex.Chrominance, prevDcCb); Scale16X16_8X8(b, cr); prevDcCr = WriteBlock(b, QuantIndex.Chrominance, prevDcCr); } } }
/// <inheritdoc/> protected override void Apply(ImageBase <T, TP> target, ImageBase <T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { 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); // Reset offset if necessary. if (minX > 0) { startX = 0; } if (minY > 0) { startY = 0; } Vector4 backgroundColor = this.Value.ToVector4(); using (IPixelAccessor <T, TP> sourcePixels = source.Lock()) using (IPixelAccessor <T, TP> targetPixels = target.Lock()) { Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - startY; for (int x = minX; x < maxX; x++) { int offsetX = x - startX; Vector4 color = sourcePixels[offsetX, offsetY].ToVector4(); float a = color.W; if (a < 1 && a > 0) { color = Vector4.Lerp(color, backgroundColor, .5F); } if (Math.Abs(a) < Epsilon) { color = backgroundColor; } T packed = default(T); packed.PackFromVector4(color); targetPixels[offsetX, offsetY] = packed; } this.OnRowProcessed(); }); } }
public void Encode(ImageBase image, Stream stream, int quality, JpegSubsample sample) { if (image == null || stream == null){ throw new ArgumentNullException(); } ushort max = JpegConstants.MaxLength; if (image.Width >= max || image.Height >= max){ throw new Exception($"Image is too large to encode at {image.Width}x{image.Height}."); } outputStream = stream; subsample = sample; for (int i = 0; i < theHuffmanSpec.Length; i++){ theHuffmanLut[i] = new HuffmanLut(theHuffmanSpec[i]); } for (int i = 0; i < nQuantIndex; i++){ quant[i] = new byte[Block.blockSize]; } if (quality < 1){ quality = 1; } if (quality > 100){ quality = 100; } int scale; if (quality < 50){ scale = 5000/quality; } else{ scale = 200 - quality*2; } for (int i = 0; i < nQuantIndex; i++){ for (int j = 0; j < Block.blockSize; j++){ int x = unscaledQuant[i, j]; x = (x*scale + 50)/100; if (x < 1){ x = 1; } if (x > 255){ x = 255; } quant[i][j] = (byte) x; } } int componentCount = 3; double densityX = ((Image2) image).HorizontalResolution; double densityY = ((Image2) image).VerticalResolution; WriteApplicationHeader((short) densityX, (short) densityY); WriteDqt(); WriteSof0(image.Width, image.Height, componentCount); WriteDht(componentCount); using (IPixelAccessor pixels = image.Lock()){ WriteSos(pixels); } buffer[0] = 0xff; buffer[1] = 0xd9; stream.Write(buffer, 0, 2); stream.Flush(); }
protected virtual void FirstPass(IPixelAccessor source, int width, int height) { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { InitialQuantizePixel(source[x, y]); } } }
protected virtual void SecondPass(IPixelAccessor source, byte[] output, int width, int height) { Parallel.For(0, source.Height, Bootstrapper.instance.ParallelOptions, y => { for (int x = 0; x < source.Width; x++) { Color2 sourcePixel = source[x, y]; output[y * source.Width + x] = QuantizePixel(sourcePixel); } }); }
/// <summary> /// Applies the process to the specified portion of the specified <see cref="ImageBase{T,TP}"/> at the specified location /// and with the specified size. /// </summary> /// <param name="target">Target image to apply the process to.</param> /// <param name="source">The source image. Cannot be null.</param> /// <param name="sourceRectangle"> /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw. /// </param> /// <param name="startY">The index of the row within the source image to start processing.</param> /// <param name="endY">The index of the row within the source image to end processing.</param> /// <param name="kernel">The kernel operator.</param> private void ApplyConvolution(ImageBase <T, TP> target, ImageBase <T, TP> source, Rectangle sourceRectangle, int startY, int endY, float[,] kernel) { int kernelHeight = kernel.GetLength(0); int kernelWidth = kernel.GetLength(1); int radiusY = kernelHeight >> 1; int radiusX = kernelWidth >> 1; int sourceBottom = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; int maxY = sourceBottom - 1; int maxX = endX - 1; using (IPixelAccessor <T, TP> sourcePixels = source.Lock()) using (IPixelAccessor <T, TP> targetPixels = target.Lock()) { Parallel.For( startY, endY, this.ParallelOptions, y => { for (int x = startX; x < endX; x++) { Vector4 destination = new Vector4(); // Apply each matrix multiplier to the color components for each pixel. for (int fy = 0; fy < kernelHeight; fy++) { int fyr = fy - radiusY; int offsetY = y + fyr; offsetY = offsetY.Clamp(0, maxY); for (int fx = 0; fx < kernelWidth; fx++) { int fxr = fx - radiusX; int offsetX = x + fxr; offsetX = offsetX.Clamp(0, maxX); Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); destination += kernel[fy, fx] * currentColor; } } T packed = default(T); packed.PackFromVector4(destination); targetPixels[x, y] = packed; } this.OnRowProcessed(); }); } }
/// <inheritdoc/> protected override void Apply(ImageBase <T, TP> target, ImageBase <T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { int startX = sourceRectangle.X; int endX = sourceRectangle.Right; T glowColor = this.GlowColor; Vector2 centre = Rectangle.Center(sourceRectangle).ToVector2(); float maxDistance = this.Radius > 0 ? Math.Min(this.Radius, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; Ellipse ellipse = new Ellipse(new Point(centre), maxDistance, maxDistance); // 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; } using (IPixelAccessor <T, TP> sourcePixels = source.Lock()) using (IPixelAccessor <T, TP> targetPixels = target.Lock()) { Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - startY; for (int x = minX; x < maxX; x++) { int offsetX = x - startX; if (ellipse.Contains(offsetX, offsetY)) { // TODO: Premultiply? float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY)); Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4(); T packed = default(T); packed.PackFromVector4(Vector4.Lerp(glowColor.ToVector4(), sourceColor, distance / maxDistance)); targetPixels[offsetX, offsetY] = packed; } } this.OnRowProcessed(); }); } }
/// <inheritdoc/> protected override void Apply(ImageBase <T, TP> target, ImageBase <T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { float brightness = this.Value / 100F; 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); // Reset offset if necessary. if (minX > 0) { startX = 0; } if (minY > 0) { startY = 0; } using (IPixelAccessor <T, TP> sourcePixels = source.Lock()) using (IPixelAccessor <T, TP> targetPixels = target.Lock()) { Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - startY; for (int x = minX; x < maxX; x++) { int offsetX = x - startX; // TODO: Check this with other formats. Vector4 vector = sourcePixels[offsetX, offsetY].ToVector4().Expand(); Vector3 transformed = new Vector3(vector.X, vector.Y, vector.Z) + new Vector3(brightness); vector = new Vector4(transformed, vector.W); T packed = default(T); packed.PackFromVector4(vector.Compress()); targetPixels[offsetX, offsetY] = packed; } this.OnRowProcessed(); }); } }
private QuantizedImage GenerateResult(IPixelAccessor imagePixels, int colorCount, Box[] cube) { List <Color2> pallette = new List <Color2>(); byte[] pixels = new byte[imagePixels.Width * imagePixels.Height]; int transparentIndex = -1; int width = imagePixels.Width; int height = imagePixels.Height; for (int k = 0; k < colorCount; k++) { Mark(cube[k], (byte)k); double weight = Volume(cube[k], vwt); if (Math.Abs(weight) > Epsilon) { byte r = (byte)(Volume(cube[k], vmr) / weight); byte g = (byte)(Volume(cube[k], vmg) / weight); byte b = (byte)(Volume(cube[k], vmb) / weight); byte a = (byte)(Volume(cube[k], vma) / weight); Color2 color = Color2.FromArgb(a, r, g, b); if (color.Equals(default(Color2))) { transparentIndex = k; } pallette.Add(color); } else { pallette.Add(default(Color2)); transparentIndex = k; } } Parallel.For(0, height, Bootstrapper.instance.ParallelOptions, y => { for (int x = 0; x < width; x++) { // Expected order r->g->b->a byte[] color = imagePixels[x, y].ToBytes(); int r = color[0] >> (8 - IndexBits); int g = color[1] >> (8 - IndexBits); int b = color[2] >> (8 - IndexBits); int a = color[3] >> (8 - IndexAlphaBits); if (transparentIndex > -1 && color[3] <= Threshold) { pixels[(y * width) + x] = (byte)transparentIndex; continue; } int ind = GetPaletteIndex(r + 1, g + 1, b + 1, a + 1); pixels[(y * width) + x] = tag[ind]; } }); return(new QuantizedImage(width, height, pallette.ToArray(), pixels, transparentIndex)); }
/// <inheritdoc/> protected override void Apply(ImageBase <T, TP> target, ImageBase <T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { float contrast = (100F + this.Value) / 100F; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; Vector4 contrastVector = new Vector4(contrast, contrast, contrast, 1); Vector4 shiftVector = new Vector4(.5F, .5F, .5F, 1); // 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; } using (IPixelAccessor <T, TP> sourcePixels = source.Lock()) using (IPixelAccessor <T, TP> targetPixels = target.Lock()) { Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - startY; for (int x = minX; x < maxX; x++) { int offsetX = x - startX; Vector4 vector = sourcePixels[offsetX, offsetY].ToVector4().Expand(); vector -= shiftVector; vector *= contrastVector; vector += shiftVector; T packed = default(T); packed.PackFromVector4(vector.Compress()); targetPixels[offsetX, offsetY] = packed; } this.OnRowProcessed(); }); } }
/// <summary> /// Execute the first pass through the pixels in the image /// </summary> /// <param name="source">The source data</param> /// <param name="width">The width in pixels of the image.</param> /// <param name="height">The height in pixels of the image.</param> protected virtual void FirstPass(IPixelAccessor <T, TP> source, int width, int height) { // Loop through each row for (int y = 0; y < height; y++) { // And loop through each column for (int x = 0; x < width; x++) { // Now I have the pixel, call the FirstPassQuantize function... this.InitialQuantizePixel(source[x, y]); } } }
public void Encode(ImageBase image, Stream stream) { if (image == null || stream == null) { throw new ArgumentNullException(); } stream.Write(new byte[] { 0x89, // Set the high bit. 0x50, // P 0x4E, // N 0x47, // G 0x0D, // Line ending CRLF 0x0A, // Line ending CRLF 0x1A, // EOF 0x0A // LF }, 0, 8); int quality = Quality > 0 ? Quality : image.Quality; Quality = quality > 0 ? NumUtils.Clamp(quality, 1, int.MaxValue) : int.MaxValue; bitDepth = Quality <= 256 ? (byte)NumUtils.Clamp(GifEncoderCore.GetBitsNeededForColorDepth(Quality), 1, 8) : (byte)8; if (bitDepth == 3) { bitDepth = 4; } else if (bitDepth >= 5 || bitDepth <= 7) { bitDepth = 8; } PngHeader header = new PngHeader { Width = image.Width, Height = image.Height, ColorType = (byte)(Quality <= 256 ? 3 : 6), BitDepth = bitDepth, FilterMethod = 0, // None CompressionMethod = 0, InterlaceMethod = 0 }; WriteHeaderChunk(stream, header); QuantizedImage quantized = WritePaletteChunk(stream, header, image); WritePhysicalChunk(stream, image); WriteGammaChunk(stream); using (IPixelAccessor pixels = image.Lock()){ WriteDataChunks(stream, pixels, quantized); } WriteEndChunk(stream); stream.Flush(); }
/// <inheritdoc/> protected override void Apply(ImageBase <T, TP> target, ImageBase <T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { int startX = sourceRectangle.X; int endX = sourceRectangle.Right; T vignetteColor = this.VignetteColor; Vector2 centre = Rectangle.Center(sourceRectangle).ToVector2(); float rX = this.RadiusX > 0 ? Math.Min(this.RadiusX, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; float rY = this.RadiusY > 0 ? Math.Min(this.RadiusY, sourceRectangle.Height * .5F) : sourceRectangle.Height * .5F; float maxDistance = (float)Math.Sqrt((rX * rX) + (rY * rY)); // 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; } using (IPixelAccessor <T, TP> sourcePixels = source.Lock()) using (IPixelAccessor <T, TP> targetPixels = target.Lock()) { Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - startY; for (int x = minX; x < maxX; x++) { int offsetX = x - startX; float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY)); Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4(); T packed = default(T); packed.PackFromVector4(Vector4.Lerp(vignetteColor.ToVector4(), sourceColor, 1 - (.9F * (distance / maxDistance)))); targetPixels[offsetX, offsetY] = packed; } this.OnRowProcessed(); }); } }
private void WriteSos(IPixelAccessor pixels) { // TODO: We should allow grayscale writing. outputStream.Write(sosHeaderYCbCr, 0, sosHeaderYCbCr.Length); switch (subsample){ case JpegSubsample.Ratio444: Encode444(pixels); break; case JpegSubsample.Ratio420: Encode420(pixels); break; } Emit(0x7f, 7); }
private static void ToYCbCr(IPixelAccessor pixels, int x, int y, Block yBlock, Block cbBlock, Block crBlock) { int xmax = pixels.Width - 1; int ymax = pixels.Height - 1; for (int j = 0; j < 8; j++){ for (int i = 0; i < 8; i++){ byte[] pixel = pixels[Math.Min(x + i, xmax), Math.Min(y + j, ymax)].ToBytes(); YCbCr2 color = Color2.FromArgb(pixel[3], pixel[0], pixel[1], pixel[2]); int index = 8*j + i; yBlock[index] = (int) color.Y; cbBlock[index] = (int) color.Cb; crBlock[index] = (int) color.Cr; } } }
private void Write32bit(EndianBinaryWriter writer, IPixelAccessor pixels, int amount) { for (int y = pixels.Height - 1; y >= 0; y--){ for (int x = 0; x < pixels.Width; x++){ // Convert back to b-> g-> r-> a order. byte[] bytes = pixels[x, y].ToBytes(); writer.Write(new[]{bytes[2], bytes[1], bytes[0], bytes[3]}); } // Pad for (int i = 0; i < amount; i++){ writer.Write((byte) 0); } } }
private void Encode444(IPixelAccessor pixels) { Block b = new Block(); Block cb = new Block(); Block cr = new Block(); int prevDcy = 0, prevDcCb = 0, prevDcCr = 0; for (int y = 0; y < pixels.Height; y += 8){ for (int x = 0; x < pixels.Width; x += 8){ ToYCbCr(pixels, x, y, b, cb, cr); prevDcy = WriteBlock(b, QuantIndex.Luminance, prevDcy); prevDcCb = WriteBlock(cb, QuantIndex.Chrominance, prevDcCb); prevDcCr = WriteBlock(cr, QuantIndex.Chrominance, prevDcCr); } } }
/// <inheritdoc/> protected override void Apply(ImageBase <T, TP> target, ImageBase <T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { int startX = sourceRectangle.X; int endX = sourceRectangle.Right; Vector3 inverseVector = Vector3.One; // 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; } using (IPixelAccessor <T, TP> sourcePixels = source.Lock()) using (IPixelAccessor <T, TP> targetPixels = target.Lock()) { Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - startY; for (int x = minX; x < maxX; x++) { int offsetX = x - startX; Vector4 color = sourcePixels[offsetX, offsetY].ToVector4(); Vector3 vector = inverseVector - new Vector3(color.X, color.Y, color.Z); T packed = default(T); packed.PackFromVector4(new Vector4(vector, color.W)); targetPixels[offsetX, offsetY] = packed; } this.OnRowProcessed(); }); } }
/// <inheritdoc/> protected override void Apply(ImageBase <T, TP> target, ImageBase <T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { float threshold = this.Value; T upper = this.UpperColor; T lower = this.LowerColor; 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); // Reset offset if necessary. if (minX > 0) { startX = 0; } if (minY > 0) { startY = 0; } using (IPixelAccessor <T, TP> sourcePixels = source.Lock()) using (IPixelAccessor <T, TP> targetPixels = target.Lock()) { Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - startY; for (int x = minX; x < maxX; x++) { int offsetX = x - startX; T color = sourcePixels[offsetX, offsetY]; // Any channel will do since it's Grayscale. targetPixels[offsetX, offsetY] = color.ToVector4().X >= threshold ? upper : lower; } this.OnRowProcessed(); }); } }
/// <inheritdoc/> protected override void Apply(ImageBase <T, TP> target, ImageBase <T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { float alpha = this.Value / 100F; 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); // Reset offset if necessary. if (minX > 0) { startX = 0; } if (minY > 0) { startY = 0; } Vector4 alphaVector = new Vector4(1, 1, 1, alpha); using (IPixelAccessor <T, TP> sourcePixels = source.Lock()) using (IPixelAccessor <T, TP> targetPixels = target.Lock()) { Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - startY; for (int x = minX; x < maxX; x++) { int offsetX = x - startX; T packed = default(T); packed.PackFromVector4(sourcePixels[offsetX, offsetY].ToVector4() * alphaVector); targetPixels[offsetX, offsetY] = packed; } this.OnRowProcessed(); }); } }
public QuantizedImage Quantize(ImageBase image, int maxColors) { if (image == null) { throw new ArgumentNullException(); } int colorCount = NumUtils.Clamp(maxColors, 1, 256); Clear(); using (IPixelAccessor imagePixels = image.Lock()){ Build3DHistogram(imagePixels); Get3DMoments(); Box[] cube; BuildCube(out cube, ref colorCount); return(GenerateResult(imagePixels, colorCount, cube)); } }
private void WriteSos(IPixelAccessor pixels) { // TODO: We should allow grayscale writing. outputStream.Write(sosHeaderYCbCr, 0, sosHeaderYCbCr.Length); switch (subsample){ case JpegSubsample.Ratio444: Encode444(pixels); break; case JpegSubsample.Ratio420: Encode420(pixels); break; } Emit(0x7f, 7); }
private void Encode444(IPixelAccessor pixels) { Block b = new Block(); Block cb = new Block(); Block cr = new Block(); int prevDcy = 0, prevDcCb = 0, prevDcCr = 0; for (int y = 0; y < pixels.Height; y += 8){ for (int x = 0; x < pixels.Width; x += 8){ ToYCbCr(pixels, x, y, b, cb, cr); prevDcy = WriteBlock(b, QuantIndex.Luminance, prevDcy); prevDcCb = WriteBlock(cb, QuantIndex.Chrominance, prevDcCb); prevDcCr = WriteBlock(cr, QuantIndex.Chrominance, prevDcCr); } } }
private void Encode420(IPixelAccessor pixels) { Block b = new Block(); Block[] cb = new Block[4]; Block[] cr = new Block[4]; int prevDcy = 0, prevDcCb = 0, prevDcCr = 0; for (int i = 0; i < 4; i++){ cb[i] = new Block(); } for (int i = 0; i < 4; i++){ cr[i] = new Block(); } 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, cb[i], cr[i]); prevDcy = WriteBlock(b, QuantIndex.Luminance, prevDcy); } Scale16X16_8X8(b, cb); prevDcCb = WriteBlock(b, QuantIndex.Chrominance, prevDcCb); Scale16X16_8X8(b, cr); prevDcCr = WriteBlock(b, QuantIndex.Chrominance, prevDcCr); } } }
private static void ToYCbCr(IPixelAccessor pixels, int x, int y, Block yBlock, Block cbBlock, Block crBlock) { int xmax = pixels.Width - 1; int ymax = pixels.Height - 1; for (int j = 0; j < 8; j++){ for (int i = 0; i < 8; i++){ byte[] pixel = pixels[Math.Min(x + i, xmax), Math.Min(y + j, ymax)].ToBytes(); YCbCr2 color = Color2.FromArgb(pixel[3], pixel[0], pixel[1], pixel[2]); int index = 8*j + i; yBlock[index] = (int) color.Y; cbBlock[index] = (int) color.Cb; crBlock[index] = (int) color.Cr; } } }
private void Build3DHistogram(IPixelAccessor pixels) { for (int y = 0; y < pixels.Height; y++){ for (int x = 0; x < pixels.Width; x++){ byte[] color = pixels[x, y].ToBytes(); byte r = color[0]; byte g = color[1]; byte b = color[2]; byte a = color[3]; int inr = r >> (8 - IndexBits); int ing = g >> (8 - IndexBits); int inb = b >> (8 - IndexBits); int ina = a >> (8 - IndexAlphaBits); int ind = GetPaletteIndex(inr + 1, ing + 1, inb + 1, ina + 1); vwt[ind]++; vmr[ind] += r; vmg[ind] += g; vmb[ind] += b; vma[ind] += a; m2[ind] += (r*r) + (g*g) + (b*b) + (a*a); } } }
private QuantizedImage GenerateResult(IPixelAccessor imagePixels, int colorCount, Box[] cube) { List<Color2> pallette = new List<Color2>(); byte[] pixels = new byte[imagePixels.Width*imagePixels.Height]; int transparentIndex = -1; int width = imagePixels.Width; int height = imagePixels.Height; for (int k = 0; k < colorCount; k++){ Mark(cube[k], (byte) k); double weight = Volume(cube[k], vwt); if (Math.Abs(weight) > Epsilon){ byte r = (byte) (Volume(cube[k], vmr)/weight); byte g = (byte) (Volume(cube[k], vmg)/weight); byte b = (byte) (Volume(cube[k], vmb)/weight); byte a = (byte) (Volume(cube[k], vma)/weight); Color2 color = Color2.FromArgb(a,r, g, b); if (color.Equals(default(Color2))){ transparentIndex = k; } pallette.Add(color); } else{ pallette.Add(default(Color2)); transparentIndex = k; } } Parallel.For(0, height, Bootstrapper.instance.ParallelOptions, y =>{ for (int x = 0; x < width; x++){ // Expected order r->g->b->a byte[] color = imagePixels[x, y].ToBytes(); int r = color[0] >> (8 - IndexBits); int g = color[1] >> (8 - IndexBits); int b = color[2] >> (8 - IndexBits); int a = color[3] >> (8 - IndexAlphaBits); if (transparentIndex > -1 && color[3] <= Threshold){ pixels[(y*width) + x] = (byte) transparentIndex; continue; } int ind = GetPaletteIndex(r + 1, g + 1, b + 1, a + 1); pixels[(y*width) + x] = tag[ind]; } }); return new QuantizedImage(width, height, pallette.ToArray(), pixels, transparentIndex); }