private NetpbmImage <TPixelComponent> CorrectOrderBA <TPixelComponent>(NetpbmImage <TPixelComponent> image) { Contract.Requires(image.Header.Components.Count == 2); Contract.Requires(image.Header.Components.Contains(Component.Black)); Contract.Requires(image.Header.Components.Contains(Component.Alpha)); return(CorrectOrder(new[] { Component.Black, Component.Alpha }, image)); }
private NetpbmImage <TPixelComponent> CorrectOrderRGB <TPixelComponent>(NetpbmImage <TPixelComponent> image) { Contract.Requires(image.Header.Components.Count == 3); Contract.Requires(image.Header.Components.Contains(Component.Red)); Contract.Requires(image.Header.Components.Contains(Component.Green)); Contract.Requires(image.Header.Components.Contains(Component.Blue)); return(CorrectOrder(new[] { Component.Red, Component.Green, Component.Blue }, image)); }
private NetpbmImage <TPixelComponent> CorrectOrderCMYK <TPixelComponent>(NetpbmImage <TPixelComponent> image) { Contract.Requires(image.Header.Components.Count == 4); Contract.Requires(image.Header.Components.Contains(Component.Cyan)); Contract.Requires(image.Header.Components.Contains(Component.Magenta)); Contract.Requires(image.Header.Components.Contains(Component.Yellow)); Contract.Requires(image.Header.Components.Contains(Component.Black)); return(CorrectOrder(new[] { Component.Cyan, Component.Magenta, Component.Yellow, Component.Black }, image)); }
/// <summary> /// Encodes up to eight bitmap values into a byte. The values are stored from the most significant bit downward, /// i.e. the leftmost value is stored in the most significant bit. /// </summary> /// <returns>The bitmap pixels encoded into a byte.</returns> /// <param name="image">The image being encoded.</param> /// <param name="pixels">The bitmap pixels to encode into a byte.</param> /// <typeparam name="TPixelComponent">The type of the pixel component.</typeparam> private byte EncodeBitmapValuesIntoByte <TPixelComponent>(NetpbmImage <TPixelComponent> image, IList <TPixelComponent> pixels) { Contract.Assert(pixels.Count > 0 && pixels.Count < 9); byte theByte = 0; // earliest value = most significant bit for (int i = 0; i < 8; ++i) { if (!image.IsComponentValueZero(pixels[i])) { theByte |= (byte)(1 << (7 - i)); } } return(theByte); }
/// <summary> /// Returns a new image which is the given image with the components of the given type inverted and changed to /// another type. /// </summary> /// <typeparam name="TPixelComponent">The pixel component type being used.</typeparam> /// <param name="image">The image whose component to invert.</param> /// <param name="from">The component whose values to invert.</param> /// <param name="to">The new component type of the component being inverted.</param> /// <returns>A copy of <paramref name="image"/> where all components of type <paramref name="from"/> are /// inverted and changed to type <paramref name="to"/>.</returns> private NetpbmImage <TPixelComponent> InvertComponent <TPixelComponent>(NetpbmImage <TPixelComponent> image, Component from, Component to) { Contract.Requires(image.Header.Components.Contains(from)); var componentIndices = new HashSet <int>(Enumerable.Range(0, image.Header.Components.Count).Where(i => image.Header.Components[i] == from)); var newRows = image.NativeRows.Select(oldRow => InvertRowComponent(oldRow, image, componentIndices, from, to)); var newComponents = image.Header.Components.Select(c => c == from ? to : c); var header = new NetpbmHeader <TPixelComponent>( image.Header.ImageType, image.Header.Width, image.Header.Height, image.Header.BytesPerPixelComponent, newComponents, image.Header.HighestComponentValue ); return(image.NewImageOfSameType(header, newRows)); }
private IEnumerable <TPixelComponent> InvertRowComponent <TPixelComponent>(IEnumerable <TPixelComponent> row, NetpbmImage <TPixelComponent> image, HashSet <int> componentIndices, Component from, Component to) { var componentCount = image.Header.Components.Count; var oldRowPixels = row.Batch(componentCount); foreach (var oldPixel in oldRowPixels) { var oldPixelArray = oldPixel.ToArray(); for (int c = 0; c < componentCount; ++c) { if (componentIndices.Contains(c)) { yield return(image.InvertPixelValue(oldPixelArray[c])); } else { yield return(oldPixelArray[c]); } } } }
private NetpbmImage <TPixelComponent> CorrectOrder <TPixelComponent>(IList <Component> requestedOrder, NetpbmImage <TPixelComponent> image) { Contract.Requires(requestedOrder.Count == image.Header.Components.Count); Contract.Requires(ListsContainTheSameElements(requestedOrder, image.Header.Components)); if (requestedOrder.SequenceEqual(image.Header.Components)) { // short-circuit return(image.NewImageOfSameType(image.Header, image.NativeRows)); } var newToOldOrder = new int[requestedOrder.Count]; var currentOrder = new List <int>(image.Header.Components.Select(c => (int)c)); for (int i = 0; i < requestedOrder.Count; ++i) { var index = currentOrder.IndexOf((int)requestedOrder[i]); Debug.Assert(index != -1); newToOldOrder[i] = index; currentOrder[index] = -1; } Debug.Assert(currentOrder.All(o => o == -1)); var newRows = image.NativeRows.Select(originalRow => TransposeComponentOrder(originalRow, image.Header.Width, newToOldOrder)); var newHeader = new NetpbmHeader <TPixelComponent>( image.Header.ImageType, image.Header.Width, image.Header.Height, image.Header.BytesPerPixelComponent, requestedOrder, image.Header.HighestComponentValue ); return(image.NewImageOfSameType(newHeader, newRows)); }
/// <summary> /// Writes a row of image data into the stream in the given format. /// </summary> /// <param name="image">The image whose row is being encoded (for formatting purposes).</param> /// <param name="row">The row to write.</param> /// <param name="stream">The stream to write into.</param> /// <param name="type">The format according to which to write.</param> public void WriteImageRow <TPixelComponent>(NetpbmImage <TPixelComponent> image, IEnumerable <TPixelComponent> row, Stream stream, ImageType type) { using (var writer = new NetpbmBinaryWriter(stream, new UTF8Encoding(false, true), leaveOpen: true)) { bool isPlain; switch (type) { case ImageType.PBM: case ImageType.PGM: case ImageType.PPM: case ImageType.PAM: case ImageType.BigPAM: isPlain = false; break; case ImageType.PlainPBM: case ImageType.PlainPGM: case ImageType.PlainPPM: isPlain = true; break; default: throw new ArgumentOutOfRangeException("type", type, "unknown image type"); } if (isPlain) { // output row as decimal numbers // add a newline after the row to make things nicer foreach (var value in row) { writer.WriteUnprefixed(value.ToString()); writer.Write(' '); } writer.Write('\n'); } else if (type == ImageType.PBM) { // special case: bit-packed format! var values = new List <TPixelComponent>(8); foreach (var value in row) { values.Add(value); if (values.Count == 8) { byte theByte = EncodeBitmapValuesIntoByte(image, values); writer.Write(theByte); values.Clear(); } } if (values.Count != 0) { byte theByte = EncodeBitmapValuesIntoByte(image, values); writer.Write(theByte); } } else { // just the big endian bytes foreach (var value in row) { writer.Write(image.ComponentToBigEndianBytes(value).ToArray()); } } } }
/// <summary> /// Writes the image data into the stream in the given format. /// </summary> /// <param name="image">The image whose data to write.</param> /// <param name="stream">The stream into which to write the data.</param> /// <param name="type">The type of Netpbm image according to which to encode the data.</param> /// <typeparam name="TPixelComponent">The type of pixel component values.</typeparam> public void WriteImageData <TPixelComponent>(NetpbmImage <TPixelComponent> image, Stream stream, ImageType type) { // check if the format is supported var supportedTypes = SupportedTypesForImage(image); if (!supportedTypes.Contains(type)) { throw new ArgumentOutOfRangeException("type", type, "the image cannot be encoded into this type"); } using (var writer = new NetpbmBinaryWriter(stream, new UTF8Encoding(false, true), leaveOpen: true)) { bool isPlain; switch (type) { case ImageType.PBM: case ImageType.PGM: case ImageType.PPM: case ImageType.PAM: case ImageType.BigPAM: isPlain = false; break; case ImageType.PlainPBM: case ImageType.PlainPGM: case ImageType.PlainPPM: isPlain = true; break; default: throw new ArgumentOutOfRangeException("type", type, "unknown image type"); } if (isPlain) { // output in row-major order as decimal numbers // add newlines after each row to make things nicer foreach (var row in image.NativeRows) { foreach (var value in row) { writer.WriteUnprefixed(value.ToString()); writer.Write(' '); } writer.Write('\n'); } } else if (type == ImageType.PBM) { // special case: bit-packed format! var values = new List <TPixelComponent>(8); foreach (var row in image.NativeRows) { foreach (var value in row) { values.Add(value); if (values.Count == 8) { byte theByte = EncodeBitmapValuesIntoByte(image, values); writer.Write(theByte); values.Clear(); } } if (values.Count != 0) { byte theByte = EncodeBitmapValuesIntoByte(image, values); writer.Write(theByte); } } } else { // just the big endian bytes foreach (var row in image.NativeRows) { foreach (var value in row) { writer.Write(image.ComponentToBigEndianBytes(value).ToArray()); } } } } }
/// <summary> /// Returns the set of Netpbm image types into which the given image can be encoded. /// </summary> /// <returns>The Netpbm image types the given image can be encoded into.</returns> /// <param name="img">The image to be encoded.</param> /// <typeparam name="TPixelComponent">The pixel component type of the image.</typeparam> public ISet <ImageType> SupportedTypesForImage <TPixelComponent>(NetpbmImage <TPixelComponent> img) { return(SupportedTypesForHeader(img.Header)); }
/// <summary> /// Writes the image into the stream in the given format. /// </summary> /// <param name="image">The image to write.</param> /// <param name="stream">The stream into which to write the image.</param> /// <param name="type">The type of Netpbm image into which to encode the image.</param> /// <typeparam name="TPixelComponent">The type of pixel component values.</typeparam> public void WriteImage <TPixelComponent>(NetpbmImage <TPixelComponent> image, Stream stream, ImageType type) { WriteImageHeader(image.Header, stream, type); WriteImageData(image, stream, type); }
/// <summary> /// Returns a new image which is the given image converted to a canonical form. /// </summary> /// <typeparam name="TPixelComponent">The pixel component type being used.</typeparam> /// <param name="image">The image whose canonical form to return.</param> /// <param name="grayscaleConversion">Whether to perform grayscale conversion and what kind.</param> /// <returns>The new image, in canonical form.</returns> public NetpbmImage <TPixelComponent> Canonicalize <TPixelComponent>(NetpbmImage <TPixelComponent> image, GrayscaleConversion grayscaleConversion = GrayscaleConversion.None) { var ret = image.NewImageOfSameType(image.Header, image.NativeRows); // perform grayscale conversion if necessary if (grayscaleConversion == GrayscaleConversion.BlackToWhite && image.Header.Components.Contains(Component.Black)) { ret = InvertComponent(ret, Component.Black, Component.White); } else if (grayscaleConversion == GrayscaleConversion.WhiteToBlack && image.Header.Components.Contains(Component.White)) { ret = InvertComponent(ret, Component.White, Component.Black); } // canonicalize order if ( ret.Header.Components.Count == 3 && ret.Header.Components.Contains(Component.Red) && ret.Header.Components.Contains(Component.Green) && ret.Header.Components.Contains(Component.Blue) ) { ret = CorrectOrderRGB(ret); } else if ( ret.Header.Components.Count == 4 && ret.Header.Components.Contains(Component.Red) && ret.Header.Components.Contains(Component.Green) && ret.Header.Components.Contains(Component.Blue) && ret.Header.Components.Contains(Component.Alpha) ) { ret = CorrectOrderRGBA(ret); } else if ( ret.Header.Components.Count == 2 && ret.Header.Components.Contains(Component.White) && ret.Header.Components.Contains(Component.Alpha) ) { ret = CorrectOrderWA(ret); } else if ( ret.Header.Components.Count == 2 && ret.Header.Components.Contains(Component.Black) && ret.Header.Components.Contains(Component.Alpha) ) { ret = CorrectOrderBA(ret); } else if ( ret.Header.Components.Count == 4 && ret.Header.Components.Contains(Component.Cyan) && ret.Header.Components.Contains(Component.Magenta) && ret.Header.Components.Contains(Component.Yellow) && ret.Header.Components.Contains(Component.Black) ) { ret = CorrectOrderCMYK(ret); } else if ( ret.Header.Components.Count == 5 && ret.Header.Components.Contains(Component.Cyan) && ret.Header.Components.Contains(Component.Magenta) && ret.Header.Components.Contains(Component.Yellow) && ret.Header.Components.Contains(Component.Black) && ret.Header.Components.Contains(Component.Alpha) ) { ret = CorrectOrderCMYKA(ret); } return(ret); }