Example #1
0
        /// <summary>
        /// Gets the IHDR chunk of the parent image and throws an exception if it is not present.
        /// </summary>
        /// <returns>The IHDR chunk of the parent image.</returns>
        /// <exception cref="ChunkNotFoundException" />
        protected IHDRChunk GetAssertIHDRChunk()
        {
            IHDRChunk chunk = _image.Chunks["IHDR"] as IHDRChunk;

            if (chunk == null)
            {
                throw new ChunkNotFoundException("IHDR chunk not found.");
            }

            return(chunk);
        }
Example #2
0
        public tRNSChunk(byte[] data, PNGImage image)
            : base(data, image)
        {
            IHDRChunk hdr = this.GetAssertIHDRChunk();

            switch (hdr.ColorType)
            {
            case ColorType.Grayscale:
                this.AssertDataLength(data, 2);
                _grayValue = Utils.BytesToUShort(data, 4, Utils.Endianness.Big);
                break;

            case ColorType.GrayscaleWithAlpha:
                throw new InvalidChunkDataException("The tRNS chunk cannot appear for the grayscale with alpha color type.");

            case ColorType.Truecolor:
                this.AssertDataLength(data, 6);
                _redValue   = Utils.BytesToUShort(data, 4, Utils.Endianness.Big);
                _greenValue = Utils.BytesToUShort(data, 6, Utils.Endianness.Big);
                _blueValue  = Utils.BytesToUShort(data, 8, Utils.Endianness.Big);
                break;

            case ColorType.TruecolorWithAlpha:
                throw new InvalidChunkDataException("The tRNS chunk cannot appear for the truecolor with alpha color type.");

            case ColorType.IndexedColor:
                if (data.Length - 4 > this.GetAssertPLTEChunk().Entries.Length)
                {
                    throw new InvalidChunkLengthException("tRNS chunk must contain the same amount of entries as the palette chunk or less.");
                }

                _paletteAlpha = new byte[data.Length - 4];

                for (int i = 0; i < data.Length - 4; i++)
                {
                    _paletteAlpha[i] = data[i + 4];
                }

                break;

            default:
                throw new InvalidChunkDataException("Invalid color type.");
            }
        }
Example #3
0
        protected override void WriteChunkData(MemoryStream ms)
        {
            IHDRChunk    hdr = this.GetAssertIHDRChunk();
            BinaryWriter bw  = new BinaryWriter(ms);

            switch (hdr.ColorType)
            {
            case ColorType.Grayscale:
                bw.Write(Utils.UShortToBytes(_grayValue, Utils.Endianness.Big));
                break;

            case ColorType.GrayscaleWithAlpha:
                throw new InvalidChunkDataException("The tRNS chunk cannot appear for the grayscale with alpha color type.");

            case ColorType.Truecolor:
                bw.Write(Utils.UShortToBytes(_redValue, Utils.Endianness.Big));
                bw.Write(Utils.UShortToBytes(_greenValue, Utils.Endianness.Big));
                bw.Write(Utils.UShortToBytes(_blueValue, Utils.Endianness.Big));
                break;

            case ColorType.TruecolorWithAlpha:
                throw new InvalidChunkDataException("The tRNS chunk cannot appear for the truecolor with alpha color type.");

            case ColorType.IndexedColor:
                if (_paletteAlpha.Length > this.GetAssertPLTEChunk().Entries.Length)
                {
                    throw new InvalidChunkLengthException("tRNS chunk must contain the same amount of entries as the palette chunk or less.");
                }

                bw.Write(_paletteAlpha);

                break;

            default:
                throw new InvalidChunkDataException("Invalid color type.");
            }
        }
Example #4
0
        public Color GetEffectiveBackgroundColor()
        {
            IHDRChunk hdr = this.GetAssertIHDRChunk();

            switch (hdr.ColorType)
            {
            case ColorType.Grayscale:
            case ColorType.GrayscaleWithAlpha:
                if (hdr.BitDepth == 1)
                {
                    return(Utils.MakeGray((_backgroundColor.Gray & 0x1) * 255));
                }
                else if (hdr.BitDepth == 2)
                {
                    return(Utils.MakeGray((_backgroundColor.Gray & 0x3) * 255 / 3));
                }
                else if (hdr.BitDepth == 4)
                {
                    return(Utils.MakeGray((_backgroundColor.Gray & 0xf) * 255 / 15));
                }
                else if (hdr.BitDepth == 8)
                {
                    return(Utils.MakeGray(_backgroundColor.Gray & 0xff));
                }
                else if (hdr.BitDepth == 16)
                {
                    return(Utils.MakeGray(_backgroundColor.Gray * 255 / 65535));
                }
                else
                {
                    throw new InvalidChunkDataException("Invalid bit depth.");
                }

            case ColorType.Truecolor:
            case ColorType.TruecolorWithAlpha:
                if (hdr.BitDepth == 8)
                {
                    return(Color.FromArgb(
                               _backgroundColor.Red & 0xff,
                               _backgroundColor.Green & 0xff,
                               _backgroundColor.Blue & 0xff));
                }
                else if (hdr.BitDepth == 16)
                {
                    return(Color.FromArgb(
                               _backgroundColor.Red * 255 / 65535,
                               _backgroundColor.Green * 255 / 65535,
                               _backgroundColor.Blue * 255 / 65535));
                }
                else
                {
                    throw new InvalidChunkDataException("Invalid bit depth.");
                }

            case ColorType.IndexedColor:
                PLTEChunk plte = this.GetAssertPLTEChunk();

                return(plte.Entries[_backgroundColor.Index]);

            default:
                throw new InvalidChunkDataException("Invalid color type.");
            }
        }
Example #5
0
        /// <summary>
        /// Checks the image's chunks for conformance to the PNG specification.
        /// </summary>
        public void Verify()
        {
            // verify the critical chunks
            // IHDR and IEND
            if (_chunks["IHDR"] == null)
            {
                throw new InvalidImageException("Critical chunk IHDR was not found.");
            }
            if (_chunks[0].Type != "IHDR")
            {
                throw new InvalidImageException("IHDR chunk must appear first.");
            }

            if (_chunks["IEND"] == null)
            {
                throw new InvalidImageException("Critical chunk IEND was not found.");
            }
            if (_chunks[_chunks.Count - 1].Type != "IEND")
            {
                throw new InvalidImageException("IEND chunk must appear last.");
            }

            // PLTE
            IHDRChunk hdr = _chunks["IHDR"] as IHDRChunk;

            if (hdr.ColorType == ColorType.IndexedColor)
            {
                if (_chunks["PLTE"] == null)
                {
                    throw new InvalidImageException(
                              "Critical chunk PLTE required for color type 3 (indexed color) was not found.");
                }
            }

            if (hdr.ColorType == ColorType.Grayscale || hdr.ColorType == ColorType.GrayscaleWithAlpha)
            {
                if (_chunks["PLTE"] != null)
                {
                    throw new InvalidImageException(
                              "PLTE chunk cannot appear for color types 0 and 4 (grayscale and grayscale with alpha).");
                }
            }

            // IDAT
            if (_chunks["IDAT"] == null)
            {
                throw new InvalidImageException("Critical chunk IDAT was not found.");
            }

            // verify chunk counts
            Dictionary <Type, int> counts = new Dictionary <Type, int>();

            foreach (Chunk c in _chunks)
            {
                if (!counts.ContainsKey(c.GetType()))
                {
                    counts.Add(c.GetType(), 1);
                }
                else
                {
                    counts[c.GetType()]++;
                }
            }

            foreach (Type t in counts.Keys)
            {
                if (_classes.ContainsValue(t))
                {
                    ChunkAttribute a = t.GetCustomAttributes(typeof(ChunkAttribute), true)[0] as ChunkAttribute;

                    if (a.AllowMultiple == false && counts[t] > 1)
                    {
                        throw new InvalidImageException(
                                  string.Format("Multiple instances ({0}) of {1} chunk are not allowed.", counts[t], a.Type));
                    }
                }
            }

            // verify ancillary chunks
            if (_chunks["iCCP"] != null && _chunks["sRGB"] != null)
            {
                throw new InvalidImageException("iCCP chunk and sRGB chunk cannot be both present.");
            }

            // verify chunk ordering
            int plteLoc, idatLoc;

            plteLoc = _chunks.IndexOf("PLTE");
            idatLoc = _chunks.IndexOf("IDAT");

            // verify that IDAT chunks are consecutive
            bool idatEnded = false;

            for (int i = idatLoc; i < _chunks.Count; i++)
            {
                if (_chunks[i].Type == "IDAT" && idatEnded)
                {
                    throw new InvalidImageException("IDAT chunks must be consecutive.");
                }
                else if (_chunks[i].Type != "IDAT" && !idatEnded)
                {
                    idatEnded = true;
                }
            }

            // PLTE must be before IDAT
            if (plteLoc >= idatLoc)
            {
                throw new InvalidImageException("PLTE chunk must be before first IDAT chunk.");
            }

            // verify other chunks' ordering
            string[] beforePLTEbeforeIDAT = new string[] { "cHRM", "gAMA", "iCCP", "sBIT", "sRGB" };
            string[] afterPLTEbeforeIDAT  = new string[] { "bKGD", "hIST", "tRNS" };
            string[] beforeIDAT           = new string[] { "pHYs", "sPLT" };

            for (int i = 0; i < _chunks.Count; i++)
            {
                if (Utils.ArrayContains <string>(beforePLTEbeforeIDAT, _chunks[i].Type))
                {
                    if (((plteLoc == -1) ? false : (i >= plteLoc)) || i >= idatLoc)
                    {
                        throw new InvalidImageException(
                                  string.Format("{0} chunk must be before PLTE chunk and first IDAT chunk.", _chunks[i].Type));
                    }
                }

                if (Utils.ArrayContains <string>(afterPLTEbeforeIDAT, _chunks[i].Type))
                {
                    if (((plteLoc == -1) ? false : (i <= plteLoc)) || i >= idatLoc)
                    {
                        throw new InvalidImageException(
                                  string.Format("{0} must be after PLTE chunk and before first IDAT chunk.", _chunks[i].Type));
                    }
                }

                if (Utils.ArrayContains <string>(beforeIDAT, _chunks[i].Type))
                {
                    if (i >= idatLoc)
                    {
                        throw new InvalidImageException(
                                  string.Format("{0} must be before first IDAT chunk.", _chunks[i].Type));
                    }
                }
            }
        }
Example #6
0
        /// <summary>
        /// Saves the PNG image to a stream.
        /// </summary>
        /// <param name="s">The stream to write to.</param>
        public void Save(Stream s)
        {
            IHDRChunk hdr    = _image.Chunks["IHDR"] as IHDRChunk;
            int       width  = (int)hdr.Width;
            int       height = (int)hdr.Height;

            _scanlineLength = Utils.IntCeilDiv(Utils.GetBitsPerPixel(hdr.ColorType, hdr.BitDepth) * width, 8) + 1;
            _bpp            = Utils.GetBytesPerPixel(hdr.ColorType, hdr.BitDepth); // bytes per pixel

            byte bitDepth = hdr.BitDepth;

            MemoryStream data   = new MemoryStream();
            BinaryWriter bwdata = new BinaryWriter(data);

            byte[] scanline = null;

            for (int line = 0; line < height; line++)
            {
                byte[] prevLine = scanline;

                scanline = new byte[_scanlineLength - 1];

                switch (hdr.ColorType)
                {
                case ColorType.Grayscale:
                    if (bitDepth == 1)
                    {
                        for (int i = 0; i < Utils.IntCeilDiv(width, 8); i++)
                        {
                            byte b = 0;

                            for (int j = 0; j < 8; j++)
                            {
                                if (i * 8 + j < hdr.Width)
                                {
                                    Color c     = _bitmap.GetPixel(i * 8 + j, line);
                                    byte  value = (byte)((c.R + c.G + c.B) / 3);

                                    b |= (byte)(((value / 255) >> (7 - j)) & 0x1);
                                }
                            }

                            scanline[i] = b;
                        }
                    }
                    else if (bitDepth == 2)
                    {
                        for (int i = 0; i < Utils.IntCeilDiv(width, 4); i++)
                        {
                            byte b = 0;

                            for (int j = 0; j < 4; j++)
                            {
                                if (i * 4 + j < hdr.Width)
                                {
                                    Color c     = _bitmap.GetPixel(i * 4 + j, line);
                                    byte  value = (byte)((c.R + c.G + c.B) / 3);

                                    b |= (byte)(((value * 3 / 255) >> ((3 - j) * 2)) & 0x3);
                                }
                            }

                            scanline[i] = b;
                        }
                    }
                    else if (bitDepth == 4)
                    {
                        for (int i = 0; i < Utils.IntCeilDiv(width, 2); i++)
                        {
                            byte b = 0;

                            Color c     = _bitmap.GetPixel(i * 2, line);
                            byte  value = (byte)((c.R + c.G + c.B) / 3);

                            b |= (byte)((value * 15 / 255) << 4);

                            if (i * 2 + 1 < width)
                            {
                                c     = _bitmap.GetPixel(i * 2 + 1, line);
                                value = (byte)((c.R + c.G + c.B) / 3);

                                b |= (byte)(value * 15 / 255);
                            }

                            scanline[i] = b;
                        }
                    }
                    else if (bitDepth == 8)
                    {
                        for (int i = 0; i < width; i++)
                        {
                            Color c = _bitmap.GetPixel(i, line);

                            scanline[i] = (byte)((c.R + c.G + c.B) / 3);
                        }
                    }
                    else if (bitDepth == 16)
                    {
                        for (int i = 0; i < width; i++)
                        {
                            Color c     = _bitmap.GetPixel(i, line);
                            byte  value = (byte)((c.R + c.G + c.B) / 3);

                            scanline[i * 2]     = (byte)((value * 65535 / 255) >> 8);
                            scanline[i * 2 + 1] = (byte)((value * 65535 / 255) & 0xff);
                        }
                    }

                    break;

                case ColorType.GrayscaleWithAlpha:
                    if (bitDepth == 8)
                    {
                        for (int i = 0; i < width; i++)
                        {
                            Color c = _bitmap.GetPixel(i, line);

                            scanline[i * 2]     = (byte)((c.R + c.G + c.B) / 3);
                            scanline[i * 2 + 1] = c.A;
                        }
                    }
                    else if (bitDepth == 16)
                    {
                        for (int i = 0; i < width; i++)
                        {
                            Color c     = _bitmap.GetPixel(i, line);
                            byte  value = (byte)((c.R + c.G + c.B) / 3);

                            scanline[i * 4]     = (byte)((value * 65535 / 255) >> 8);
                            scanline[i * 4 + 1] = (byte)((value * 65535 / 255) & 0xff);
                            scanline[i * 4 + 2] = (byte)((c.A * 65535 / 255) >> 8);
                            scanline[i * 4 + 3] = (byte)((c.A * 65535 / 255) & 0xff);
                        }
                    }

                    break;

                case ColorType.IndexedColor:


                    break;

                case ColorType.Truecolor:
                    if (bitDepth == 8)
                    {
                        for (int i = 0; i < width; i++)
                        {
                            Color c = _bitmap.GetPixel(i, line);

                            scanline[i * 3]     = c.R;
                            scanline[i * 3 + 1] = c.G;
                            scanline[i * 3 + 2] = c.B;
                        }
                    }
                    else if (bitDepth == 16)
                    {
                        for (int i = 0; i < width; i++)
                        {
                            Color c = _bitmap.GetPixel(i, line);

                            scanline[i * 6]     = (byte)((c.R * 65535 / 255) >> 8);
                            scanline[i * 6 + 1] = (byte)((c.R * 65535 / 255) & 0xff);
                            scanline[i * 6 + 2] = (byte)((c.G * 65535 / 255) >> 8);
                            scanline[i * 6 + 3] = (byte)((c.G * 65535 / 255) & 0xff);
                            scanline[i * 6 + 4] = (byte)((c.B * 65535 / 255) >> 8);
                            scanline[i * 6 + 5] = (byte)((c.B * 65535 / 255) & 0xff);
                        }
                    }

                    break;

                case ColorType.TruecolorWithAlpha:
                    if (bitDepth == 8)
                    {
                        for (int i = 0; i < width; i++)
                        {
                            Color c = _bitmap.GetPixel(i, line);

                            scanline[i * 4]     = c.R;
                            scanline[i * 4 + 1] = c.G;
                            scanline[i * 4 + 2] = c.B;
                            scanline[i * 4 + 3] = c.A;
                        }
                    }
                    else if (bitDepth == 16)
                    {
                        for (int i = 0; i < width; i++)
                        {
                            Color c = _bitmap.GetPixel(i, line);

                            scanline[i * 8]     = (byte)((c.R * 65535 / 255) >> 8);
                            scanline[i * 8 + 1] = (byte)((c.R * 65535 / 255) & 0xff);
                            scanline[i * 8 + 2] = (byte)((c.G * 65535 / 255) >> 8);
                            scanline[i * 8 + 3] = (byte)((c.G * 65535 / 255) & 0xff);
                            scanline[i * 8 + 4] = (byte)((c.B * 65535 / 255) >> 8);
                            scanline[i * 8 + 5] = (byte)((c.B * 65535 / 255) & 0xff);
                            scanline[i * 8 + 6] = (byte)((c.A * 65535 / 255) >> 8);
                            scanline[i * 8 + 7] = (byte)((c.A * 65535 / 255) & 0xff);
                        }
                    }

                    break;

                default:
                    throw new Exception("Invalid color type.");
                }

                byte[] filtered = this.Filter(scanline, line, this.GetBestFilterMethod(scanline, line, prevLine), prevLine);

                bwdata.Write(filtered);
            }

            this.SaveChunks(data.ToArray());
            _image.Write(s, true);

            bwdata.Close();
        }
Example #7
0
        /// <summary>
        /// Reads the image pixels and stores them in Bitmap.
        /// </summary>
        /// <param name="data">The image bytes.</param>
        private void Read(byte[] data)
        {
            // optimized for speed (it's very slow already)

            IHDRChunk hdr      = _image.Chunks["IHDR"] as IHDRChunk;
            int       width    = (int)hdr.Width;
            int       height   = (int)hdr.Height;
            byte      bitDepth = hdr.BitDepth;

            _bitmap         = CreateBitmap(hdr.ColorType, hdr.BitDepth, width, height);
            _scanlineLength = Utils.IntCeilDiv(Utils.GetBitsPerPixel(hdr.ColorType, hdr.BitDepth) * width, 8) + 1;
            _bpp            = Utils.GetBytesPerPixel(hdr.ColorType, hdr.BitDepth); // bytes per pixel

            byte[] decoded = null;

            // palette
            PLTEChunk palette = _image.Chunks["PLTE"] as PLTEChunk;

            #region tRNS Chunk

            tRNSChunk trns     = _image.Chunks["tRNS"] as tRNSChunk;
            int       tGray1   = -1;
            int       tGray2   = -1;
            int       tGray4   = -1;
            int       tGray8   = -1;
            int       tGray16  = -1;
            Color     tRgb8    = Color.FromArgb(0, 0, 0, 0);
            Color     tRgb16   = Color.FromArgb(0, 0, 0, 0);
            int[]     tPalette = null;

            if (trns != null)
            {
                tGray1  = trns.Gray & 0x1;
                tGray2  = trns.Gray & 0x3;
                tGray4  = trns.Gray & 0xf;
                tGray8  = trns.Gray & 0xff;
                tGray16 = trns.Gray;
                tRgb8   = Color.FromArgb(trns.Red & 0xff, trns.Green & 0xff, trns.Blue & 0xff);
                tRgb16  = Color.FromArgb(trns.Red * 255 / 65535, trns.Green * 255 / 65535, trns.Blue * 255 / 65535);

                if (palette != null)
                {
                    tPalette = new int[palette.Entries.Length];

                    for (int i = 0; i < tPalette.Length; i++)
                    {
                        if (i < trns.PaletteAlpha.Length)
                        {
                            tPalette[i] = trns.PaletteAlpha[i];
                        }
                        else
                        {
                            tPalette[i] = 255;
                        }
                    }
                }
            }
            else
            {
                if (palette != null)
                {
                    tPalette = new int[palette.Entries.Length];

                    for (int i = 0; i < tPalette.Length; i++)
                    {
                        tPalette[i] = 255;
                    }
                }
            }

            #endregion

            if (hdr.InterlaceMethod == InterlaceMethod.Adam7)
            {
                #region Adam7

                for (int pass = 0; pass < _adam7.Length; pass++)
                {
                }

                #endregion
            }
            else
            {
                #region Normal

                for (int line = 0; line < data.Length / _scanlineLength; line++)
                {
                    decoded = this.Defilter(data, line, decoded);

                    switch (hdr.ColorType)
                    {
                    case ColorType.Grayscale:
                        if (bitDepth == 1)
                        {
                            for (int i = 0; i < Utils.IntCeilDiv(width, 8); i++)
                            {
                                int[] pixels = new int[8];

                                for (int j = 0; j < 8; j++)
                                {
                                    pixels[j] = (decoded[i] >> (7 - j)) & 0x1;
                                }

                                for (int j = 0; j < 8; j++)
                                {
                                    if (i * 8 + j < hdr.Width)
                                    {
                                        _bitmap.SetPixel(i * 8 + j, line,
                                                         Utils.MakeGray(pixels[j] == tGray1 ? 0 : 255, pixels[j] * 255));
                                    }
                                }
                            }
                        }
                        else if (bitDepth == 2)
                        {
                            for (int i = 0; i < Utils.IntCeilDiv(width, 4); i++)
                            {
                                int[] pixels = new int[4];

                                for (int j = 0; j < 4; j++)
                                {
                                    pixels[j] = (decoded[i] >> ((3 - j) * 2)) & 0x3;
                                }

                                for (int j = 0; j < 4; j++)
                                {
                                    if (i * 4 + j < hdr.Width)
                                    {
                                        _bitmap.SetPixel(i * 4 + j, line,
                                                         Utils.MakeGray(pixels[j] == tGray2 ? 0 : 255, pixels[j] * 255 / 3));
                                    }
                                }
                            }
                        }
                        else if (bitDepth == 4)
                        {
                            for (int i = 0; i < Utils.IntCeilDiv(width, 2); i++)
                            {
                                int pixel1 = decoded[i] >> 4;     // upper four bits
                                int pixel2 = decoded[i] & 0xf;    // lower two bits

                                _bitmap.SetPixel(i * 2, line, Utils.MakeGray(
                                                     pixel1 == tGray4 ? 0 : 255, pixel1 * 255 / 15));

                                if (i * 2 + 1 < hdr.Width)
                                {
                                    _bitmap.SetPixel(i * 2 + 1, line, Utils.MakeGray(
                                                         pixel2 == tGray4 ? 0 : 255, pixel2 * 255 / 15));
                                }
                            }
                        }
                        else if (bitDepth == 8)
                        {
                            for (int i = 0; i < hdr.Width; i++)
                            {
                                _bitmap.SetPixel(i, line,
                                                 Utils.MakeGray(decoded[i] == tGray8 ? 0 : 255, decoded[i]));
                            }
                        }
                        else if (bitDepth == 16)
                        {
                            for (int i = 0; i < hdr.Width; i++)
                            {
                                int value = Utils.BytesToUShort(decoded, i * 2, Utils.Endianness.Big);

                                _bitmap.SetPixel(i, line, Utils.MakeGray(
                                                     value == tGray16 ? 0 : 255, value * 255 / 65535));
                            }
                        }

                        break;

                    case ColorType.GrayscaleWithAlpha:
                        if (bitDepth == 8)
                        {
                            for (int i = 0; i < hdr.Width; i++)
                            {
                                _bitmap.SetPixel(i, line, Color.FromArgb(
                                                     decoded[i * 2 + 1],
                                                     decoded[i * 2], decoded[i * 2], decoded[i * 2]
                                                     ));
                            }
                        }
                        else if (bitDepth == 16)
                        {
                            for (int i = 0; i < hdr.Width; i++)
                            {
                                _bitmap.SetPixel(i, line, Color.FromArgb(
                                                     Utils.BytesToUShort(decoded, i * 4 + 2, Utils.Endianness.Big) * 255 / 65535,
                                                     Utils.BytesToUShort(decoded, i * 4, Utils.Endianness.Big) * 255 / 65535,
                                                     Utils.BytesToUShort(decoded, i * 4, Utils.Endianness.Big) * 255 / 65535,
                                                     Utils.BytesToUShort(decoded, i * 4, Utils.Endianness.Big) * 255 / 65535
                                                     ));
                            }
                        }

                        break;

                    case ColorType.IndexedColor:
                        if (bitDepth == 1)
                        {
                            for (int i = 0; i < Utils.IntCeilDiv(width, 8); i++)
                            {
                                int[] pixels = new int[8];

                                for (int j = 0; j < 8; j++)
                                {
                                    pixels[j] = (decoded[i] >> (7 - j)) & 0x1;
                                }

                                for (int j = 0; j < 8; j++)
                                {
                                    if (i * 8 + j < hdr.Width)
                                    {
                                        _bitmap.SetPixel(i * 8 + j, line,
                                                         Color.FromArgb(tPalette[pixels[j]], palette.Entries[pixels[j]]));
                                    }
                                }
                            }
                        }
                        else if (bitDepth == 2)
                        {
                            for (int i = 0; i < Utils.IntCeilDiv(width, 4); i++)
                            {
                                int[] pixels = new int[4];

                                for (int j = 0; j < 4; j++)
                                {
                                    pixels[j] = (decoded[i] >> ((3 - j) * 2)) & 0x3;
                                }

                                for (int j = 0; j < 4; j++)
                                {
                                    if (i * 4 + j < hdr.Width)
                                    {
                                        _bitmap.SetPixel(i * 4 + j, line,
                                                         Color.FromArgb(tPalette[pixels[j]], palette.Entries[pixels[j]]));
                                    }
                                }
                            }
                        }
                        else if (bitDepth == 4)
                        {
                            for (int i = 0; i < Utils.IntCeilDiv(width, 2); i++)
                            {
                                int pixel1 = decoded[i] >> 4;     // upper four bits
                                int pixel2 = decoded[i] & 0xf;    // lower two bits

                                _bitmap.SetPixel(i * 2, line,
                                                 Color.FromArgb(tPalette[pixel1], palette.Entries[pixel1]));

                                if (i * 2 + 1 < hdr.Width)
                                {
                                    _bitmap.SetPixel(i * 2 + 1, line,
                                                     Color.FromArgb(tPalette[pixel2], palette.Entries[pixel2]));
                                }
                            }
                        }
                        else if (bitDepth == 8)
                        {
                            for (int i = 0; i < hdr.Width; i++)
                            {
                                _bitmap.SetPixel(i, line,
                                                 Color.FromArgb(tPalette[decoded[i]], palette.Entries[decoded[i]]));
                            }
                        }

                        break;

                    case ColorType.Truecolor:
                        if (bitDepth == 8)
                        {
                            for (int i = 0; i < hdr.Width; i++)
                            {
                                Color c = Color.FromArgb(
                                    decoded[i * 3],
                                    decoded[i * 3 + 1],
                                    decoded[i * 3 + 2]
                                    );

                                _bitmap.SetPixel(i, line, Color.FromArgb(
                                                     Utils.ColorsEqual(c, tRgb8) ? 0 : 255, c));
                            }
                        }
                        else if (bitDepth == 16)
                        {
                            // .NET doesn't support 16-bit bit depths
                            for (int i = 0; i < hdr.Width; i++)
                            {
                                Color c = Color.FromArgb(
                                    Utils.BytesToUShort(decoded, i * 6, Utils.Endianness.Big) * 255 / 65535,
                                    Utils.BytesToUShort(decoded, i * 6 + 2, Utils.Endianness.Big) * 255 / 65535,
                                    Utils.BytesToUShort(decoded, i * 6 + 4, Utils.Endianness.Big) * 255 / 65535
                                    );

                                _bitmap.SetPixel(i, line, Color.FromArgb(
                                                     Utils.ColorsEqual(c, tRgb16) ? 0 : 255, c));
                            }
                        }

                        break;

                    case ColorType.TruecolorWithAlpha:
                        if (bitDepth == 8)
                        {
                            for (int i = 0; i < hdr.Width; i++)
                            {
                                _bitmap.SetPixel(i, line, Color.FromArgb(
                                                     decoded[i * 4 + 3],
                                                     decoded[i * 4],
                                                     decoded[i * 4 + 1],
                                                     decoded[i * 4 + 2]));
                            }
                        }
                        else if (bitDepth == 16)
                        {
                            for (int i = 0; i < hdr.Width; i++)
                            {
                                _bitmap.SetPixel(i, line, Color.FromArgb(
                                                     Utils.BytesToUShort(decoded, i * 8 + 6, Utils.Endianness.Big) * 255 / 65535,
                                                     Utils.BytesToUShort(decoded, i * 8, Utils.Endianness.Big) * 255 / 65535,
                                                     Utils.BytesToUShort(decoded, i * 8 + 2, Utils.Endianness.Big) * 255 / 65535,
                                                     Utils.BytesToUShort(decoded, i * 8 + 4, Utils.Endianness.Big) * 255 / 65535
                                                     ));
                            }
                        }

                        break;

                    default:
                        throw new Exception("Invalid color type.");
                    }
                }

                #endregion
            }
        }