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