Exemplo n.º 1
0
        private void CopyIndexedMemoryBitmap(int bits/*, ref bool hasAlpha*/, ImageDataBitmap dest)
        {
            int firstMaskColor = -1, lastMaskColor = -1;
            bool segmentedColorMask = false;

            int bytesColorPaletteOffset = ((ImagePrivateDataBitmap)Image.Data).ColorPaletteOffset; // GDI+ always returns Windows bitmaps: sizeof BITMAPFILEHEADER + sizeof BITMAPINFOHEADER

            int bytesFileOffset = ((ImagePrivateDataBitmap)Image.Data).Offset;
            uint paletteColors = Image.Information.ColorsUsed;
            int width = (int)Image.Information.Width;
            int height = (int)Image.Information.Height;

            MonochromeMask mask = new MonochromeMask(width, height);

            bool isGray = bits == 8 && (paletteColors == 256 || paletteColors == 0);
            int isBitonal = 0; // 0: false; >0: true; <0: true (inverted)
            byte[] paletteData = new byte[3 * paletteColors];
            for (int color = 0; color < paletteColors; ++color)
            {
                paletteData[3 * color] = Data[bytesColorPaletteOffset + 4 * color + 2];
                paletteData[3 * color + 1] = Data[bytesColorPaletteOffset + 4 * color + 1];
                paletteData[3 * color + 2] = Data[bytesColorPaletteOffset + 4 * color + 0];
                if (isGray)
                    isGray = paletteData[3 * color] == paletteData[3 * color + 1] &&
                      paletteData[3 * color] == paletteData[3 * color + 2];

                if (Data[bytesColorPaletteOffset + 4 * color + 3] < 128)
                {
                    // We treat this as transparency:
                    if (firstMaskColor == -1)
                        firstMaskColor = color;
                    if (lastMaskColor == -1 || lastMaskColor == color - 1)
                        lastMaskColor = color;
                    if (lastMaskColor != color)
                        segmentedColorMask = true;
                }
                //else
                //{
                //  // We treat this as opacity:
                //}
            }

            if (bits == 1)
            {
                if (paletteColors == 0)
                    isBitonal = 1;
                if (paletteColors == 2)
                {
                    if (paletteData[0] == 0 &&
                      paletteData[1] == 0 &&
                      paletteData[2] == 0 &&
                      paletteData[3] == 255 &&
                      paletteData[4] == 255 &&
                      paletteData[5] == 255)
                        isBitonal = 1; // Black on white
                    if (paletteData[5] == 0 &&
                      paletteData[4] == 0 &&
                      paletteData[3] == 0 &&
                      paletteData[2] == 255 &&
                      paletteData[1] == 255 &&
                      paletteData[0] == 255)
                        isBitonal = -1; // White on black
                }
            }

            // NYI: (no sample found where this was required) 
            // if (segmentedColorMask = true)
            // { ... }

            bool isFaxEncoding = false;
            byte[] imageData = new byte[((width * bits + 7) / 8) * height];
            byte[] imageDataFax = null;
            int k = 0;


            if (bits == 1 && dest._document.Options.EnableCcittCompressionForBilevelImages)
            {
                // TODO: flag/option?
                // We try Group 3 1D and Group 4 (2D) encoding here and keep the smaller byte array.
                //byte[] temp = new byte[imageData.Length];
                //int ccittSize = DoFaxEncoding(ref temp, imageBits, (uint)bytesFileOffset, (uint)width, (uint)height);

                // It seems that Group 3 2D encoding never beats both other encodings, therefore we don't call it here.
                //byte[] temp2D = new byte[imageData.Length];
                //uint dpiY = (uint)image.VerticalResolution;
                //uint kTmp = 0;
                //int ccittSize2D = DoFaxEncoding2D((uint)bytesFileOffset, ref temp2D, imageBits, (uint)width, (uint)height, dpiY, out kTmp);
                //k = (int) kTmp;

                byte[] tempG4 = new byte[imageData.Length];
                int ccittSizeG4 = PdfImage.DoFaxEncodingGroup4(ref tempG4, Data, (uint)bytesFileOffset, (uint)width, (uint)height);

                isFaxEncoding = /*ccittSize > 0 ||*/ ccittSizeG4 > 0;
                if (isFaxEncoding)
                {
                    //if (ccittSize == 0)
                    //  ccittSize = 0x7fffffff;
                    if (ccittSizeG4 == 0)
                        ccittSizeG4 = 0x7fffffff;
                    //if (ccittSize <= ccittSizeG4)
                    //{
                    //  Array.Resize(ref temp, ccittSize);
                    //  imageDataFax = temp;
                    //  k = 0;
                    //}
                    //else
                    {
                        Array.Resize(ref tempG4, ccittSizeG4);
                        imageDataFax = tempG4;
                        k = -1;
                    }
                }
            }

            //if (!isFaxEncoding)
            {
                int bytesOffsetRead = 0;
                if (bits == 8 || bits == 4 || bits == 1)
                {
                    int bytesPerLine = (width * bits + 7) / 8;
                    for (int y = 0; y < height; ++y)
                    {
                        mask.StartLine(y);
                        int bytesOffsetWrite = (height - 1 - y) * ((width * bits + 7) / 8);
                        for (int x = 0; x < bytesPerLine; ++x)
                        {
                            if (isGray)
                            {
                                // Lookup the gray value from the palette:
                                imageData[bytesOffsetWrite] = paletteData[3 * Data[bytesFileOffset + bytesOffsetRead]];
                            }
                            else
                            {
                                // Store the palette index.
                                imageData[bytesOffsetWrite] = Data[bytesFileOffset + bytesOffsetRead];
                            }
                            if (firstMaskColor != -1)
                            {
                                int n = Data[bytesFileOffset + bytesOffsetRead];
                                if (bits == 8)
                                {
                                    // TODO???: segmentedColorMask == true => bad mask NYI
                                    mask.AddPel((n >= firstMaskColor) && (n <= lastMaskColor));
                                }
                                else if (bits == 4)
                                {
                                    // TODO???: segmentedColorMask == true => bad mask NYI
                                    int n1 = (n & 0xf0) / 16;
                                    int n2 = (n & 0x0f);
                                    mask.AddPel((n1 >= firstMaskColor) && (n1 <= lastMaskColor));
                                    mask.AddPel((n2 >= firstMaskColor) && (n2 <= lastMaskColor));
                                }
                                else if (bits == 1)
                                {
                                    // TODO???: segmentedColorMask == true => bad mask NYI
                                    for (int bit = 1; bit <= 8; ++bit)
                                    {
                                        int n1 = (n & 0x80) / 128;
                                        mask.AddPel((n1 >= firstMaskColor) && (n1 <= lastMaskColor));
                                        n *= 2;
                                    }
                                }
                            }
                            bytesOffsetRead += 1;
                            bytesOffsetWrite += 1;
                        }
                        bytesOffsetRead = 4 * ((bytesOffsetRead + 3) / 4); // Align to 32 bit boundary
                    }
                }
                else
                {
                    throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format #3");
                }
            }

            dest.Data = imageData;
            dest.Length = imageData.Length;

            if (imageDataFax != null)
            {
                dest.DataFax = imageDataFax;
                dest.LengthFax = imageDataFax.Length;
            }

            dest.IsGray = isGray;
            dest.K = k;
            dest.IsBitonal = isBitonal;

            dest.PaletteData = paletteData;
            dest.PaletteDataLength = paletteData.Length;
            dest.SegmentedColorMask = segmentedColorMask;

            //if (alphaMask != null)
            //{
            //    dest.AlphaMask = alphaMask;
            //    dest.AlphaMaskLength = alphaMask.Length;
            //}

            if (mask != null && firstMaskColor != -1)
            {
                dest.BitmapMask = mask.MaskData;
                dest.BitmapMaskLength = mask.MaskData.Length;
            }

        }
Exemplo n.º 2
0
    /* BITMAPINFOHEADER struct and byte offsets:
        typedef struct tagBITMAPINFOHEADER{
          DWORD  biSize;           // 14
          LONG   biWidth;          // 18
          LONG   biHeight;         // 22
          WORD   biPlanes;         // 26
          WORD   biBitCount;       // 28
          DWORD  biCompression;    // 30
          DWORD  biSizeImage;      // 34
          LONG   biXPelsPerMeter;  // 38
          LONG   biYPelsPerMeter;  // 42
          DWORD  biClrUsed;        // 46
          DWORD  biClrImportant;   // 50
        } BITMAPINFOHEADER, *PBITMAPINFOHEADER; 
    */

    private void ReadIndexedMemoryBitmap(int bits/*, ref bool hasAlpha*/)
    {
#if DEBUG_
      image.image.Save("$$$.bmp", ImageFormat.Bmp);
#endif
      int pdfVersion = Owner.Version;
      int firstMaskColor = -1, lastMaskColor = -1;
      bool segmentedColorMask = false;

      MemoryStream memory = new MemoryStream();
#if GDI
      image.gdiImage.Save(memory, ImageFormat.Bmp);
#endif
#if WPF
#if !SILVERLIGHT
      // WPFTHHO: StL: keine Ahnung ob das so stimmt.
      BmpBitmapEncoder encoder = new BmpBitmapEncoder();
      encoder.Frames.Add(BitmapFrame.Create(image.wpfImage));
      encoder.Save(memory);
#else
      // AGHACK
#endif
#endif
      int streamLength = (int)memory.Length;
      Debug.Assert(streamLength > 0, "Bitmap image encoding failed.");
      if (streamLength > 0)
      {
        byte[] imageBits = new byte[streamLength];
        memory.Seek(0, SeekOrigin.Begin);
        memory.Read(imageBits, 0, streamLength);
        memory.Close();

        int height = image.PixelHeight;
        int width = image.PixelWidth;

        if (ReadWord(imageBits, 0) != 0x4d42 || // "BM"
          ReadDWord(imageBits, 2) != streamLength ||
          ReadDWord(imageBits, 14) != 40 || // sizeof BITMAPINFOHEADER
#if WPF
          // TODOWPF: bug with height and width??? With which files???
          ReadDWord(imageBits, 18) != width ||
          ReadDWord(imageBits, 22) != height)
#else
          ReadDWord(imageBits, 18) != width ||
          ReadDWord(imageBits, 22) != height)
#endif
        {
          throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format");
        }
#if WPF
        // TODOWPF: bug with height and width
        width = ReadDWord(imageBits, 18);
        height = ReadDWord(imageBits, 22);
#endif
        int fileBits = ReadWord(imageBits, 28);
        if (fileBits != bits)
        {
          if (fileBits == 1 || fileBits == 4 || fileBits == 8)
            bits = fileBits;
        }

        if (ReadWord(imageBits, 26) != 1 ||
            ReadWord(imageBits, 28) != bits ||
            ReadDWord(imageBits, 30) != 0)
        {
          throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format #2");
        }

        int bytesFileOffset = ReadDWord(imageBits, 10);
        int bytesColorPaletteOffset = 0x36; // GDI+ always returns Windows bitmaps: sizeof BITMAPFILEHEADER + sizeof BITMAPINFOHEADER
        int paletteColors = ReadDWord(imageBits, 46);
        if ((bytesFileOffset - bytesColorPaletteOffset) / 4 != paletteColors)
        {
          throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format #3");
        }

        MonochromeMask mask = new MonochromeMask(width, height);

        bool isGray = bits == 8 && (paletteColors == 256 || paletteColors == 0);
        int isBitonal = 0; // 0: false; >0: true; <0: true (inverted)
        byte[] paletteData = new byte[3 * paletteColors];
        for (int color = 0; color < paletteColors; ++color)
        {
          paletteData[3 * color] = imageBits[bytesColorPaletteOffset + 4 * color + 2];
          paletteData[3 * color + 1] = imageBits[bytesColorPaletteOffset + 4 * color + 1];
          paletteData[3 * color + 2] = imageBits[bytesColorPaletteOffset + 4 * color + 0];
          if (isGray)
            isGray = paletteData[3 * color] == paletteData[3 * color + 1] &&
              paletteData[3 * color] == paletteData[3 * color + 2];

          if (imageBits[bytesColorPaletteOffset + 4 * color + 3] < 128)
          {
            // We treat this as transparency:
            if (firstMaskColor == -1)
              firstMaskColor = color;
            if (lastMaskColor == -1 || lastMaskColor == color - 1)
              lastMaskColor = color;
            if (lastMaskColor != color)
              segmentedColorMask = true;
          }
          //else
          //{
          //  // We treat this as opacity:
          //}
        }

        if (bits == 1)
        {
          if (paletteColors == 0)
            isBitonal = 1;
          if (paletteColors == 2)
          {
            if (paletteData[0] == 0 &&
              paletteData[1] == 0 &&
              paletteData[2] == 0 &&
              paletteData[3] == 255 &&
              paletteData[4] == 255 &&
              paletteData[5] == 255)
              isBitonal = 1; // Black on white
            if (paletteData[5] == 0 &&
              paletteData[4] == 0 &&
              paletteData[3] == 0 &&
              paletteData[2] == 255 &&
              paletteData[1] == 255 &&
              paletteData[0] == 255)
              isBitonal = -1; // White on black
          }
        }

        // NYI: (no sample found where this was required) 
        // if (segmentedColorMask = true)
        // { ... }

        FlateDecode fd = new FlateDecode();
        PdfDictionary colorPalette = null;
        if (isBitonal == 0 && !isGray)
        {
          colorPalette = new PdfDictionary(this.document);
          byte[] packedPaletteData = paletteData.Length >= 48 ? fd.Encode(paletteData) : null; // don't compress small palettes
          if (packedPaletteData != null && packedPaletteData.Length + 20 < paletteData.Length) // +20: compensate for the overhead (estimated value)
          {
            // Create compressed color palette:
            colorPalette.CreateStream(packedPaletteData);
            colorPalette.Elements[Keys.Length] = new PdfInteger(packedPaletteData.Length);
            colorPalette.Elements[Keys.Filter] = new PdfName("/FlateDecode");
          }
          else
          {
            // Create uncompressed color palette:
            colorPalette.CreateStream(paletteData);
            colorPalette.Elements[Keys.Length] = new PdfInteger(paletteData.Length);
          }
          Owner.irefTable.Add(colorPalette);
        }

        bool isFaxEncoding = false;
        byte[] imageData = new byte[((width * bits + 7) / 8) * height];
        byte[] imageDataFax = null;
        int k = 0;


        if (bits == 1)
        {
          // TODO: flag/option?
          // We try Group 3 1D and Group 4 (2D) encoding here and keep the smaller byte array.
          //byte[] temp = new byte[imageData.Length];
          //int ccittSize = DoFaxEncoding(ref temp, imageBits, (uint)bytesFileOffset, (uint)width, (uint)height);

          // It seems that Group 3 2D encoding never beats both other encodings, therefore we don't call it here.
          //byte[] temp2D = new byte[imageData.Length];
          //uint dpiY = (uint)image.VerticalResolution;
          //uint kTmp = 0;
          //int ccittSize2D = DoFaxEncoding2D((uint)bytesFileOffset, ref temp2D, imageBits, (uint)width, (uint)height, dpiY, out kTmp);
          //k = (int) kTmp;

          byte[] tempG4 = new byte[imageData.Length];
          int ccittSizeG4 = DoFaxEncodingGroup4(ref tempG4, imageBits, (uint)bytesFileOffset, (uint)width, (uint)height);

          isFaxEncoding = /*ccittSize > 0 ||*/ ccittSizeG4 > 0;
          if (isFaxEncoding)
          {
            //if (ccittSize == 0)
            //  ccittSize = 0x7fffffff;
            if (ccittSizeG4 == 0)
              ccittSizeG4 = 0x7fffffff;
            //if (ccittSize <= ccittSizeG4)
            //{
            //  Array.Resize(ref temp, ccittSize);
            //  imageDataFax = temp;
            //  k = 0;
            //}
            //else
            {
              Array.Resize(ref tempG4, ccittSizeG4);
              imageDataFax = tempG4;
              k = -1;
            }
          }
        }

        //if (!isFaxEncoding)
        {
          int bytesOffsetRead = 0;
          if (bits == 8 || bits == 4 || bits == 1)
          {
            int bytesPerLine = (width * bits + 7) / 8;
            for (int y = 0; y < height; ++y)
            {
              mask.StartLine(y);
              int bytesOffsetWrite = (height - 1 - y) * ((width * bits + 7) / 8);
              for (int x = 0; x < bytesPerLine; ++x)
              {
                if (isGray)
                {
                  // Lookup the gray value from the palette:
                  imageData[bytesOffsetWrite] = paletteData[3 * imageBits[bytesFileOffset + bytesOffsetRead]];
                }
                else
                {
                  // Store the palette index:
                  imageData[bytesOffsetWrite] = imageBits[bytesFileOffset + bytesOffsetRead];
                }
                if (firstMaskColor != -1)
                {
                  int n = imageBits[bytesFileOffset + bytesOffsetRead];
                  if (bits == 8)
                  {
                    // TODO???: segmentedColorMask == true => bad mask NYI
                    mask.AddPel((n >= firstMaskColor) && (n <= lastMaskColor));
                  }
                  else if (bits == 4)
                  {
                    // TODO???: segmentedColorMask == true => bad mask NYI
                    int n1 = (n & 0xf0) / 16;
                    int n2 = (n & 0x0f);
                    mask.AddPel((n1 >= firstMaskColor) && (n1 <= lastMaskColor));
                    mask.AddPel((n2 >= firstMaskColor) && (n2 <= lastMaskColor));
                  }
                  else if (bits == 1)
                  {
                    // TODO???: segmentedColorMask == true => bad mask NYI
                    for (int bit = 1; bit <= 8; ++bit)
                    {
                      int n1 = (n & 0x80) / 128;
                      mask.AddPel((n1 >= firstMaskColor) && (n1 <= lastMaskColor));
                      n *= 2;
                    }
                  }
                }
                bytesOffsetRead += 1;
                bytesOffsetWrite += 1;
              }
              bytesOffsetRead = 4 * ((bytesOffsetRead + 3) / 4); // Align to 32 bit boundary
            }
          }
          else
          {
            throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format #3");
          }
        }

        if (firstMaskColor != -1 &&
          lastMaskColor != -1)
        {
          // Color mask requires Reader 4.0 or higher:
          if (!segmentedColorMask && pdfVersion >= 13)
          {
            PdfArray array = new PdfArray(document);
            array.Elements.Add(new PdfInteger(firstMaskColor));
            array.Elements.Add(new PdfInteger(lastMaskColor));
            Elements[Keys.Mask] = array;
          }
          else
          {
            // Monochrome mask
            byte[] maskDataCompressed = fd.Encode(mask.MaskData);
            PdfDictionary pdfMask = new PdfDictionary(document);
            pdfMask.Elements.SetName(Keys.Type, "/XObject");
            pdfMask.Elements.SetName(Keys.Subtype, "/Image");

            this.Owner.irefTable.Add(pdfMask);
            pdfMask.Stream = new PdfStream(maskDataCompressed, pdfMask);
            pdfMask.Elements[Keys.Length] = new PdfInteger(maskDataCompressed.Length);
            pdfMask.Elements[Keys.Filter] = new PdfName("/FlateDecode");
            pdfMask.Elements[Keys.Width] = new PdfInteger(width);
            pdfMask.Elements[Keys.Height] = new PdfInteger(height);
            pdfMask.Elements[Keys.BitsPerComponent] = new PdfInteger(1);
            pdfMask.Elements[Keys.ImageMask] = new PdfBoolean(true);
            Elements[Keys.Mask] = pdfMask.Reference;
          }
        }

        byte[] imageDataCompressed = fd.Encode(imageData);
        byte[] imageDataFaxCompressed = isFaxEncoding ? fd.Encode(imageDataFax) : null;

        bool usesCcittEncoding = false;
        if (isFaxEncoding &&
          (imageDataFax.Length < imageDataCompressed.Length ||
          imageDataFaxCompressed.Length < imageDataCompressed.Length))
        {
          // /CCITTFaxDecode creates the smaller file (with or without /FlateDecode):
          usesCcittEncoding = true;

          if (imageDataFax.Length < imageDataCompressed.Length)
          {
            Stream = new PdfStream(imageDataFax, this);
            Elements[Keys.Length] = new PdfInteger(imageDataFax.Length);
            Elements[Keys.Filter] = new PdfName("/CCITTFaxDecode");
            //PdfArray array2 = new PdfArray(this.document);
            PdfDictionary dictionary = new PdfDictionary();
            if (k != 0)
              dictionary.Elements.Add("/K", new PdfInteger(k));
            if (isBitonal < 0)
              dictionary.Elements.Add("/BlackIs1", new PdfBoolean(true));
            dictionary.Elements.Add("/EndOfBlock", new PdfBoolean(false));
            dictionary.Elements.Add("/Columns", new PdfInteger(width));
            dictionary.Elements.Add("/Rows", new PdfInteger(height));
            //array2.Elements.Add(dictionary);
            Elements[Keys.DecodeParms] = dictionary; // array2;
          }
          else
          {
            Stream = new PdfStream(imageDataFaxCompressed, this);
            Elements[Keys.Length] = new PdfInteger(imageDataFaxCompressed.Length);
            PdfArray arrayFilters = new PdfArray(document);
            arrayFilters.Elements.Add(new PdfName("/FlateDecode"));
            arrayFilters.Elements.Add(new PdfName("/CCITTFaxDecode"));
            Elements[Keys.Filter] = arrayFilters;
            PdfArray arrayDecodeParms = new PdfArray(document);

            PdfDictionary dictFlateDecodeParms = new PdfDictionary();
            //dictFlateDecodeParms.Elements.Add("/Columns", new PdfInteger(1));

            PdfDictionary dictCcittFaxDecodeParms = new PdfDictionary();
            if (k != 0)
              dictCcittFaxDecodeParms.Elements.Add("/K", new PdfInteger(k));
            if (isBitonal < 0)
              dictCcittFaxDecodeParms.Elements.Add("/BlackIs1", new PdfBoolean(true));
            dictCcittFaxDecodeParms.Elements.Add("/EndOfBlock", new PdfBoolean(false));
            dictCcittFaxDecodeParms.Elements.Add("/Columns", new PdfInteger(width));
            dictCcittFaxDecodeParms.Elements.Add("/Rows", new PdfInteger(height));

            arrayDecodeParms.Elements.Add(dictFlateDecodeParms); // How to add the "null object"?
            arrayDecodeParms.Elements.Add(dictCcittFaxDecodeParms);
            Elements[Keys.DecodeParms] = arrayDecodeParms;
          }
        }
        else
        {
          // /FlateDecode creates the smaller file (or no monochrome bitmap):
          Stream = new PdfStream(imageDataCompressed, this);
          Elements[Keys.Length] = new PdfInteger(imageDataCompressed.Length);
          Elements[Keys.Filter] = new PdfName("/FlateDecode");
        }

        Elements[Keys.Width] = new PdfInteger(width);
        Elements[Keys.Height] = new PdfInteger(height);
        Elements[Keys.BitsPerComponent] = new PdfInteger(bits);
        // TODO: CMYK

        // CCITT encoding: we need color palette for isBitonal == 0
        // FlateDecode: we need color palette for isBitonal <= 0 unless we have grayscales
        if ((usesCcittEncoding && isBitonal == 0) ||
          (!usesCcittEncoding && isBitonal <= 0  && !isGray))
        {
          PdfArray arrayColorSpace = new PdfArray(document);
          arrayColorSpace.Elements.Add(new PdfName("/Indexed"));
          arrayColorSpace.Elements.Add(new PdfName("/DeviceRGB"));
          arrayColorSpace.Elements.Add(new PdfInteger(paletteColors - 1));
          // ReSharper disable PossibleNullReferenceException
          arrayColorSpace.Elements.Add(colorPalette.Reference);
          // ReSharper restore PossibleNullReferenceException
          Elements[Keys.ColorSpace] = arrayColorSpace;
        }
        else
        {
          Elements[Keys.ColorSpace] = new PdfName("/DeviceGray");
        }
        if (image.Interpolate)
          Elements[Keys.Interpolate] = PdfBoolean.True;
      }
    }
Exemplo n.º 3
0
    /* BITMAPINFOHEADER struct and byte offsets:
        typedef struct tagBITMAPINFOHEADER{
          DWORD  biSize;           // 14
          LONG   biWidth;          // 18
          LONG   biHeight;         // 22
          WORD   biPlanes;         // 26
          WORD   biBitCount;       // 28
          DWORD  biCompression;    // 30
          DWORD  biSizeImage;      // 34
          LONG   biXPelsPerMeter;  // 38
          LONG   biYPelsPerMeter;  // 42
          DWORD  biClrUsed;        // 46
          DWORD  biClrImportant;   // 50
        } BITMAPINFOHEADER, *PBITMAPINFOHEADER; 
    */

    private void ReadIndexedMemoryBitmap(int bits, ref bool hasAlpha)
    {
#if DEBUG_
      image.image.Save("$$$.bmp", ImageFormat.Bmp);
#endif
      int pdfVersion = this.Owner.Version;
      int firstMaskColor = -1, lastMaskColor = -1;
      bool segmentedColorMask = false;

      MemoryStream memory = new MemoryStream();
#if GDI
      image.gdiImage.Save(memory, ImageFormat.Bmp);
#endif
#if WPF
      // WPFTHHO: StL: keine Ahnung ob das so stimmt.
      BmpBitmapEncoder encoder = new BmpBitmapEncoder();
      encoder.Frames.Add(BitmapFrame.Create(this.image.wpfImage));
      encoder.Save(memory);
#endif
      int streamLength = (int)memory.Length;
      Debug.Assert(streamLength > 0, "Bitmap image encoding failed.");
      if (streamLength > 0)
      {
        byte[] imageBits = new byte[streamLength];
        memory.Seek(0, SeekOrigin.Begin);
        memory.Read(imageBits, 0, streamLength);
        memory.Close();

        int height = this.image.PixelHeight;
        int width = this.image.PixelWidth;

        if (ReadWord(imageBits, 0) != 0x4d42 || // "BM"
          ReadDWord(imageBits, 2) != streamLength ||
          ReadDWord(imageBits, 14) != 40 || // sizeof BITMAPINFOHEADER
#if WPF
          // TODOWPF: bug with height and width
          false)
#else
          ReadDWord(imageBits, 18) != width ||
          ReadDWord(imageBits, 22) != height)
#endif
        {
          throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format");
        }
#if WPF
        // TODOWPF: bug with height and width
        width = ReadDWord(imageBits, 18);
        height = ReadDWord(imageBits, 22);
#endif
        if (ReadWord(imageBits, 26) != 1 ||
            ReadWord(imageBits, 28) != bits ||
            ReadDWord(imageBits, 30) != 0)
        {
          throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format #2");
        }

        int bytesFileOffset = ReadDWord(imageBits, 10);
        int bytesColorPaletteOffset = 0x36; // GDI+ always returns Windows bitmaps: sizeof BITMAPFILEHEADER + sizeof BITMAPINFOHEADER
        int paletteColors = ReadDWord(imageBits, 46);
        if ((bytesFileOffset - bytesColorPaletteOffset) / 4 != paletteColors)
        {
          throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format #3");
        }

        MonochromeMask mask = new MonochromeMask(width, height);

        byte[] paletteData = new byte[3 * paletteColors];
        for (int color = 0; color < paletteColors; ++color)
        {
          paletteData[3 * color] = imageBits[bytesColorPaletteOffset + 4 * color + 2];
          paletteData[3 * color + 1] = imageBits[bytesColorPaletteOffset + 4 * color + 1];
          paletteData[3 * color + 2] = imageBits[bytesColorPaletteOffset + 4 * color + 0];
          if (imageBits[bytesColorPaletteOffset + 4 * color + 3] < 128)
          {
            // We treat this as transparency:
            if (firstMaskColor == -1)
              firstMaskColor = color;
            if (lastMaskColor == -1 || lastMaskColor == color - 1)
              lastMaskColor = color;
            if (lastMaskColor != color)
              segmentedColorMask = true;
          }
          else
          {
            // We treat this as opacity:
          }
        }

        // NYI: (no sample found where this was required) 
        // if (segmentedColorMask = true)
        // { ... }

        FlateDecode fd = new FlateDecode();
        PdfDictionary colorPalette = new PdfDictionary(this.document);
        // TODO: decide at run-time if compression makes sense
#if false
        // Create uncompressed color palette:
        colorPalette.CreateStream(paletteData);
        colorPalette.Elements[Keys.Length] = new PdfInteger(paletteData.Length);
#else
        // Create compressed color palette:
        byte[] packedPaletteData = fd.Encode(paletteData);
        colorPalette.CreateStream(packedPaletteData);
        colorPalette.Elements[Keys.Length] = new PdfInteger(packedPaletteData.Length);
        colorPalette.Elements[Keys.Filter] = new PdfName("/FlateDecode");
#endif
        this.Owner.irefTable.Add(colorPalette);

        byte[] imageData = new byte[1 * width * height];

        int bytesOffsetRead = 0;
        if (bits == 8 || bits == 4 || bits == 1)
        {
          int bytesPerLine = (width * bits + 7) / 8;
          for (int y = 0; y < height; ++y)
          {
            mask.StartLine(y);
            int bytesOffsetWrite = (height - 1 - y) * ((width * bits + 7) / 8);
            for (int x = 0; x < bytesPerLine; ++x)
            {
              imageData[bytesOffsetWrite] = imageBits[bytesFileOffset + bytesOffsetRead];
              if (firstMaskColor != -1)
              {
                int n = imageBits[bytesFileOffset + bytesOffsetRead];
                if (bits == 8)
                {
                  // TODO???: segmentedColorMask == true => falsche Maske NYI
                  mask.AddPel((n >= firstMaskColor) && (n <= lastMaskColor));
                }
                else if (bits == 4)
                {
                  // TODO???: segmentedColorMask == true => falsche Maske NYI
                  int n1 = (n & 0xf0) / 16;
                  int n2 = (n & 0x0f);
                  mask.AddPel((n1 >= firstMaskColor) && (n1 <= lastMaskColor));
                  mask.AddPel((n2 >= firstMaskColor) && (n2 <= lastMaskColor));
                }
                else if (bits == 1)
                {
                  // TODO???: segmentedColorMask == true => bad mask NYI
                  for (int bit = 1; bit <= 8; ++bit)
                  {
                    int n1 = (n & 0x80) / 128;
                    mask.AddPel((n1 >= firstMaskColor) && (n1 <= lastMaskColor));
                    n *= 2;
                  }
                }
              }
              bytesOffsetRead += 1;
              bytesOffsetWrite += 1;
            }
            bytesOffsetRead = 4 * ((bytesOffsetRead + 3) / 4); // Align to 32 bit boundary
          }
        }
        else
        {
          throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format #3");
        }

        if (firstMaskColor != -1 &&
          lastMaskColor != -1)
        {
          // Color mask requires Reader 4.0 or higher:
          if (!segmentedColorMask && pdfVersion >= 13)
          {
            PdfArray array = new PdfArray(this.document);
            array.Elements.Add(new PdfInteger(firstMaskColor));
            array.Elements.Add(new PdfInteger(lastMaskColor));
            Elements[Keys.Mask] = array;
          }
          else
          {
            // Monochrome mask
            byte[] maskDataCompressed = fd.Encode(mask.MaskData);
            PdfDictionary pdfMask = new PdfDictionary(this.document);
            pdfMask.Elements.SetName(Keys.Type, "/XObject");
            pdfMask.Elements.SetName(Keys.Subtype, "/Image");

            this.Owner.irefTable.Add(pdfMask);
            pdfMask.Stream = new PdfStream(maskDataCompressed, pdfMask);
            pdfMask.Elements[Keys.Length] = new PdfInteger(maskDataCompressed.Length);
            pdfMask.Elements[Keys.Filter] = new PdfName("/FlateDecode");
            pdfMask.Elements[Keys.Width] = new PdfInteger(width);
            pdfMask.Elements[Keys.Height] = new PdfInteger(height);
            pdfMask.Elements[Keys.BitsPerComponent] = new PdfInteger(1);
            pdfMask.Elements[Keys.ImageMask] = new PdfBoolean(true);
            Elements[Keys.Mask] = pdfMask.Reference;
          }
        }

        byte[] imageDataCompressed = fd.Encode(imageData);

        Stream = new PdfStream(imageDataCompressed, this);
        Elements[Keys.Length] = new PdfInteger(imageDataCompressed.Length);
        Elements[Keys.Filter] = new PdfName("/FlateDecode");
        Elements[Keys.Width] = new PdfInteger(width);
        Elements[Keys.Height] = new PdfInteger(height);
        Elements[Keys.BitsPerComponent] = new PdfInteger(bits);
        PdfArray array2 = new PdfArray(this.document);
        array2.Elements.Add(new PdfName("/Indexed"));
        // TODO: CMYK
        array2.Elements.Add(new PdfName("/DeviceRGB"));
        array2.Elements.Add(new PdfInteger(paletteColors - 1));
        array2.Elements.Add(colorPalette.Reference);
        Elements[Keys.ColorSpace] = array2;
      }
    }