public AccessibleBitmap GetAccessibleBitmap() { AccessibleBitmap aBitmap = new AccessibleBitmap(width, height, pixelBytes); aBitmap.SetRawPixelBytes(byteArray); return(aBitmap); }
public AccessibleBitmapBytewise(AccessibleBitmap aBitmap) { width = aBitmap.width; height = aBitmap.height; pixelBytes = aBitmap.pixelBytes; byteArray = aBitmap.GetRawPixelBytes(); }
//Decompress byte array into aBitmap with help of width, length and bitdepth public static AccessibleBitmap Decompress(byte[] source, int width, int height, int pixelBytes) { //Create a BitStream from the input bytes becouse the decompress functions need this as input BitStreamFIFO bitStream = new BitStreamFIFO(source); //Create an empty bitmap with the correct dimensions, where the decompressed pixels will be written to AccessibleBitmap aBitmap = new AccessibleBitmap(width, height, pixelBytes); //Decompress the BitStream to a queue of integers for the lengths Queue <int> pixels = new Queue <int>(VaryingIntArrayCompressor.Decompress(ref bitStream)); //Decompress the BitStream to a queue of integers for the pixel values Queue <int> runs = new Queue <int>(VaryingIntArrayCompressor.Decompress(ref bitStream)); //Initialize int pixelsToGo = runs.Dequeue() + 1; //The amount of pixels that should be written before the next run starts byte[] currentPixel = new byte[pixelBytes]; //The pixel value of the current run: initialize with the correct amount of channels //Loop trough all channels in the pixel of the current length for (int i = 0; i < pixelBytes; i++) { //Take a byte from the queue of pixel values to put in the current channel currentPixel[i] = (byte)pixels.Dequeue(); } //Loop trough all lines of pixels for (int y = 0; y < height; y++) { //Loop trough all pixels in this line for (int x = 0; x < width; x++) { //Set the bit of the current pixel to the value of the current run aBitmap.SetPixel(x, y, currentPixel); //Decrease the length of the current run pixelsToGo--; //If the end of the run has been reached if (pixelsToGo == 0 && (x * y != (height - 1) * (width - 1))) { //Read the new run length from the BitStream pixelsToGo = runs.Dequeue() + 1; //Loop trough all channels in the pixel of the current length for (int i = 0; i < pixelBytes; i++) { //Take a byte from the queue of pixel values to put in the current channel currentPixel[i] = (byte)pixels.Dequeue(); } } } } //Return the image as aBitmap return(aBitmap); }
//Decompress byte array into aBitmap with help of width, length and bitdepth public static AccessibleBitmap Decompress(byte[] source, int width, int height, int pixelBytes) { //Create aBitmap from raw data AccessibleBitmap aBitmap = new AccessibleBitmap(width, height, pixelBytes); aBitmap.SetRawPixelBytes(source); //Return the created bitmap return(aBitmap); }
public AccessibleBitmap GetAccessibleBitmap() { byte[] tmpBytes = new byte[width * height * pixelBytes]; new BitArray(boolArray).CopyTo(tmpBytes, 0); AccessibleBitmap aBitmap = new AccessibleBitmap(width, height, pixelBytes); aBitmap.SetRawPixelBytes(tmpBytes); return(aBitmap); }
//Decompress byte array into aBitmap with help of width, length and bitdepth public static AccessibleBitmap Decompress(byte[] source, int width, int height, int pixelBytes) { BitStreamFIFO layerBits = new BitStreamFIFO(source); int[] byteArray = VaryingIntArrayCompressor.Decompress(ref layerBits); //Create aBitmap from pixel data AccessibleBitmap aBitmap = new AccessibleBitmap(width, height, pixelBytes); aBitmap.SetRawPixelBytes(Array.ConvertAll(byteArray, Convert.ToByte)); return(aBitmap); }
//Compress aBitmap into byte array public static byte[] Compress(AccessibleBitmap source) { //Initialize List <int> distances = new List <int>(); //A list containing all the lenghts of same pixels List <int> pixels = new List <int>(); //A list containing all the pixels that correspond to the lengths in 'distances' list int tempDistance = -1; //The length of one run of bits with the same value, while it is not saved yet: -1 because it will be increased before the first check byte[] lastPixel = source.GetPixel(0, 0); //The pixel of the last checked pixel, to compare with the current pixel: set value to the value of the first pixel so the first check will succeed //Loop trough all lines of pixels for (int y = 0; y < source.height; y++) { //Loop trough all pixels in this line for (int x = 0; x < source.width; x++) { //Take value of the current pixel byte[] currentPixel = source.GetPixel(x, y); //If the value of the bit of this pixel matches the value of the bit of the previous pixel if (currentPixel.SequenceEqual(lastPixel)) { //Values are the same, so increase current run tempDistance++; } else { //Values are not the same, so save the run distances.Add(tempDistance); pixels.AddRange(Array.ConvertAll(lastPixel, b => (int)b)); //Set the bit value for the next comparison to the bit value of this pixel lastPixel = currentPixel; //Reset the run length for the new run tempDistance = 0; } } } //Save the last run becouse this never happens in the loop distances.Add(tempDistance); pixels.AddRange(Array.ConvertAll(lastPixel, b => (int)b)); //Compress the array of run lengths using different techniques BitStreamFIFO pixelStream = VaryingIntArrayCompressor.Compress(pixels.ToArray()); //Compress the array of run lengths using different techniques BitStreamFIFO lengthStream = VaryingIntArrayCompressor.Compress(distances.ToArray()); //Combine the compressed data of the runs with the compressed data of the pixel values, then return the BitStream return(BitStreamFIFO.Merge(pixelStream, lengthStream).ToByteArray()); }
// This function is used to compress the image using the LZW algorithm public static byte[] Compress(AccessibleBitmap source) { // Add first 255 standard values to LZWDictionary in LZWCompressor.cs string[] LZWDictionary = new string[256]; for (int i = 0; i < 256; i++) { LZWDictionary[i] = ((char)i).ToString(); } List <string> dictionary = new List <string>(LZWDictionary); // Clone dictionary of all bytes Queue <byte> bytes = new Queue <byte>(source.GetRawPixelBytes()); // Get all bytes from the source image BitStreamFIFO bs = new BitStreamFIFO(); // Create bitstream for output int maxDictSize = (int)Math.Pow(2, maxBitCount); // Get maximum dictionary size string encodingString = ((char)bytes.Dequeue()).ToString(); // Create string to add encoding to while (bytes.Count > 0) { // Clear dict if full if (dictionary.Count >= maxDictSize) { dictionary = new List <string>(LZWDictionary); } char b = (char)bytes.Dequeue(); if (dictionary.Contains(encodingString + b)) { encodingString += b; } else { bs.Write(dictionary.FindIndex(x => x.StartsWith(encodingString)), maxBitCount); dictionary.Add(encodingString + b); encodingString = b.ToString(); } } // Write remaining byte to bitstream bs.Write(dictionary.FindIndex(x => x.StartsWith(encodingString)), maxBitCount); // Return the bitstream as byte array return(bs.ToByteArray()); }
public static AccessibleBitmap Decompress(byte[] source, int width, int height, int pixelBytes) { BitStreamFIFO bs = new BitStreamFIFO(source); // Convert the image into a bitstream bool verticallyCompressed = bs.ReadBool(); // Store if image was vertically compressed or not int maxBitCount = bs.ReadByte(); // Get the highest bitcount value AccessibleBitmap bmp = new AccessibleBitmap(width, height, pixelBytes); // Create new bitmap to write all pixels to // Ints to keep track of coords int x = 0; int y = 0; // Loop while there are still bits to read while (bs.Length > maxBitCount) { int counterValue = bs.ReadInt(maxBitCount); // Get the counter value of the next pixel value byte[] pixel = bs.ReadByteArray(pixelBytes); // Get the pixel value for (int i = 0; i < counterValue; i++) { bmp.SetPixel(x, y, pixel); if (verticallyCompressed) { y++; if (y >= height) { x++; y = 0; } }else { x++; if (x >= width) { y++; x = 0; } } } } // Return the bitmap return bmp; }
//Compress aBitmap into byte array public static byte[] Compress(AccessibleBitmap source) { return(VaryingIntArrayCompressor.Compress(Array.ConvertAll(source.GetRawPixelBytes(), Convert.ToInt32)).ToByteArray()); }
public static byte[] CompressVertical(AccessibleBitmap source) { byte[] lastpixel = null; // Create variable to store the last pixel int colorCounter = 1; // Create counter for current color BitStreamFIFO bs = new BitStreamFIFO(); // Create new bitstream for all the bits int maxCount = 0; // Create variable to store the max bitcount Queue<PixelArray> output = new Queue<PixelArray>(); // Create list to store all the pixelvalues in // Write one bit to the bitstream, so the decompressor knows to decompress vertically bs.Write(true); // Iterate through every vertical row for (int x = 0; x < source.width; x++) { // Iterate through every pixel in the vertical row for (int y = 0; y < source.height; y++) { // Check if the variable lastpixel is empty if (lastpixel == null) { // If lastpixel is empty, set last pixel to the first pixel lastpixel = source.GetPixel(x, y); } else { // If lastpixel isn't empty, compare last pixel with new pixel if (lastpixel.SequenceEqual(source.GetPixel(x, y))) { // Pixels matched, so increase the counter value colorCounter++; } else { // If the pixels don't match, add the counter with the last pixel to the output queue output.Enqueue(new PixelArray(colorCounter, lastpixel)); // Check if the new countervalue is higher then the last one, if so set maxBitCount to that if (colorCounter > maxCount) maxCount = colorCounter; // Reset the colorCounter and set the last pixel to the new pixel colorCounter = 1; lastpixel = source.GetPixel(x, y); } } } } // Add the remaining pixel(s) to the bitstream output.Enqueue(new PixelArray(colorCounter, lastpixel)); // Check if the new countervalue is higher then the last one, if so set maxBitCount to that if (colorCounter > maxCount) maxCount = colorCounter; // Write the maxCount to the bitstream bs.Write((byte)Math.Ceiling(Math.Log(maxCount, 2))); // Add all the pixels from the queue to the bitstream while (output.Count > 0) { PixelArray pixel = output.Dequeue(); bs.Write(pixel.Count, (int)Math.Ceiling(Math.Log(maxCount, 2))); bs.Write(pixel.Pixel); } // Return the bitsream as a byte[] return bs.ToByteArray(); }
//Compress aBitmap into byte array public static byte[] Compress(AccessibleBitmap source) { //Loop trough all layers of bytes, where possible at the same time AccessibleBitmapBytewise aBitmap = new AccessibleBitmapBytewise(source); byte[][] byteLayers = new byte[source.pixelBytes][]; Parallel.For(0, source.pixelBytes, (z, state) => //for(int z = 0; z < source.pixelBytes; z++) { //Compress image using all different compression techniques, where possible at the same time byte[][] compressionTechniques = new byte[5][]; Parallel.For(0, compressionTechniques.Length, (i, state2) => { switch (i) { //Uncompressed (only used if no compression technique is smaller) case 0: compressionTechniques[i] = UncompressedBitmapCompressorBytewise.Compress(aBitmap, z); break; //Split color channel in its bit channels and apply compression over them case 1: compressionTechniques[i] = BitLayerVaryingCompressor.Compress(aBitmap, z); break; //Compress color channel as an integer array using several techniques case 2: compressionTechniques[i] = ByteArrayCompressorBytewise.Compress(aBitmap, z); break; //Run length compression: save the length of a sequence of pixels with the same color instead of saving them seperately case 3: compressionTechniques[i] = RunLengthEncodingCompressorBytewise.Compress(aBitmap, z); break; //Run length compression vertical: run length compression, but scan the pixels horizontally, becouse with some images this yields better results case 4: compressionTechniques[i] = RunLengthEncodingCompressorVerticalBytewise.Compress(aBitmap, z); break; //To add a compression technique, add a new case like the existing ones and increase the length of new byte[??][] } }); //Choose the smallest compression type //Initialize int smallestID = 0; //The ID of the smallest compression type int smallestSize = int.MaxValue; //The size ofthe smallest compression type: int.MaxValue is assigned to make sure that the first compression to be checked will be smaaller than this value //Loop trough all saved compression techniques for (int i = 0; i < compressionTechniques.Length; i++) { //If the current technique is smaller than the smallest technique which has been checked if (compressionTechniques[i].Length < smallestSize) { //Mark this technique as smallest smallestSize = compressionTechniques[i].Length; smallestID = i; } } //Merge the number of the compression type of this layer with corresponding byte array byteLayers[z] = new byte[compressionTechniques[smallestID].Length + 1]; byteLayers[z][0] = (byte)smallestID; //This byte indicates which technique the decompressor should use, and should be before the image data Array.Copy(compressionTechniques[smallestID], 0, byteLayers[z], 1, compressionTechniques[smallestID].Length); }); //Combine all byte layers by looping trough all of them and adding them after each other List <byte> output = new List <byte>(); foreach (byte[] b in byteLayers) { output.AddRange(b); } //Return the data of all the color channels combined return(output.ToArray()); }
//Compress aBitmap into byte array public static byte[] Compress(AccessibleBitmap source) { //Return raw aBitmap return(source.GetRawPixelBytes()); }
//Decodes a byte array containing a compressed APIF image to a C# Bitmap image public Bitmap Decode(byte[] bytes) { //Check if the file version matches the version of the this decoder if (bytes[0] != version) { throw new Exception("Version not matching"); } //Start timer for decoding time encodingStart = DateTime.Now.TimeOfDay; //Read the header info to the correct variables int pixelBytes = bytes[1]; //The amount of color channels int width = bytes[2] * 256 + bytes[3]; //The image width int height = bytes[4] * 256 + bytes[5]; //The image heigth compressionType = bytes[6]; //The compression type //Store the image data apart from the header byte[] image = new byte[bytes.Length - 7]; Array.Copy(bytes, 7, image, 0, image.Length); //Initialize AccessibleBitmap outputBitmap = null; //The final AccessibleBitmap object //Choose the right decoding type from the header info switch (compressionType) { //Uncompressed bitmap case 0: outputBitmap = UncompressedBitmapCompressor.Decompress(image, width, height, pixelBytes); break; //Individual compressed color channels merged together case 1: outputBitmap = ByteLayerVaryingCompression.Decompress(image, width, height, pixelBytes); break; //Run length encoding case 2: outputBitmap = RunLengthEncodingCompressor.Decompress(image, width, height, pixelBytes); break; //Run length encoding vertical case 3: outputBitmap = RunLengthEncodingCompressorVertical.Decompress(image, width, height, pixelBytes); break; //LZW compression case 4: outputBitmap = LZWCompressor.Decompress(image, width, height, pixelBytes); break; //To add a decompression type add a new case like the existing ones //Unknown compression type: error default: throw new Exception("Unexisting compression type"); } //Stop timer encodingStop = DateTime.Now.TimeOfDay; SetStatus("Finished"); //Calculate compression rate in bytes per pixel compressionRate = (double)bytes.Length / (outputBitmap.width * outputBitmap.height); //Return the output image as bitmap format return(outputBitmap.GetBitmap()); }
//Encodes a C# Bitmap image to a byte array containing this image compressed as APIF image public byte[] Encode(Bitmap bitmap) { //Start timer for compression time encodingStart = DateTime.Now.TimeOfDay; //Creates a AccessibleBitmap class for the input bitmap: this class makes reading pixels easier and faster AccessibleBitmap aBitmap = new AccessibleBitmap(bitmap); //Compress image using all different compression techniques, where possible at the same time byte[][] compressionTechniques = new byte[4][]; Parallel.For(0, compressionTechniques.Length, (i, state) => { switch (i) { //Uncompressed (only used if no compression technique is smaller) case 0: compressionTechniques[i] = UncompressedBitmapCompressor.Compress(aBitmap); break; //Split colors in their channels and apply compression over them case 1: compressionTechniques[i] = ByteLayerVaryingCompression.Compress(aBitmap); break; //Run length compression: save the length of a sequence of pixels with the same color instead of saving them seperately case 2: compressionTechniques[i] = RunLengthEncodingCompressor.Compress(aBitmap); break; //Run length compression vertical: run length compression, but scan the pixels horizontally, becouse with some images this yields better results case 3: compressionTechniques[i] = RunLengthEncodingCompressorVertical.Compress(aBitmap); break; //LZW compression: save sequences of pixels as a single value, without the need of adding a dictionary\ // REMOVED BECAUSE VERY SLOW AND NOT PROPERLY WORKING //case 4: //compressionTechniques[i] = LZWCompressor.Compress(aBitmap); //break; //To add a compression technique, add a new case like the existing ones and increase the length of new byte[??][] } }); //Choose the smallest compression type //Initialize int smallestID = 0; int smallestSize = int.MaxValue; //Loop trough all saved compression techniques for (int i = 0; i < compressionTechniques.Length; i++) { //If the current technique is smaller than the smallest technique which has been checked if (compressionTechniques[i].Length < smallestSize) { //Mark this technique as smallest smallestSize = compressionTechniques[i].Length; smallestID = i; } } //Set the output byte array to the output of the smallest compression technique byte[] image = compressionTechniques[smallestID]; compressionType = smallestID; //Build the file header containing information for the decoder byte[] header = new byte[7]; header[0] = (byte)version; //This byte indicates the version of the compressor, to handle possible changes in the future header[1] = (byte)aBitmap.pixelBytes; //This byte indicates the amount of color channels in the image header[2] = (byte)(aBitmap.width >> 8); //These 2 bytes together indicate the width of the image header[3] = (byte)aBitmap.width; //The reason for using 2 bytes instead of 1, is that 1 byte can store a width of 0-255, while 2 bytes can store 0-65535 header[4] = (byte)(aBitmap.height >> 8); //These 2 bytes together indicate the heigth of the image header[5] = (byte)aBitmap.height; //The reason for using 2 bytes instead of 1, is that 1 byte can store a width of 0-255, while 2 bytes can store 0-65535 header[6] = (byte)compressionType; //This contains the number of the compression technique used //Merge the header with the image data to form the fimal file data byte[] fileBytes = new byte[header.Length + image.Length]; Array.Copy(header, fileBytes, header.Length); Array.Copy(image, 0, fileBytes, header.Length, image.Length); //Stop timer encodingStop = DateTime.Now.TimeOfDay; compressionRate = (double)fileBytes.Length / (aBitmap.width * aBitmap.height); //Return final file as byte array return(fileBytes); }