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; } }
/* 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; } }
/* 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; } }