diff --git a/Source/Core/IO/FileImageReader.cs b/Source/Core/IO/FileImageReader.cs index 180a15bf0cafae4caa20ba673e8a8397910d5fce..138e178a5d691920f70839b12c27cc619bf35365 100755 --- a/Source/Core/IO/FileImageReader.cs +++ b/Source/Core/IO/FileImageReader.cs @@ -34,7 +34,177 @@ namespace CodeImp.DoomBuilder.IO { offsetx = int.MinValue; offsety = int.MinValue; - throw new NotImplementedException(); + + using (BinaryReader reader = new BinaryReader(stream)) + { + int manufacturer = reader.ReadByte(); // 10=ZSoft + int version = reader.ReadByte(); + // 0=PC Paintbrush v2.5 + // 2=PC Paintbrush v2.8 w palette information + // 3=PC Paintbrush v2.8 w/o palette information + // 4=PC Paintbrush/Windows + // 5=PC Paintbrush v3.0+ + int encoding = reader.ReadByte(); // 1 = RLE, none other known + int bitsPerComponent = reader.ReadByte(); + int leftMargin = reader.ReadUInt16(); + int topMargin = reader.ReadUInt16(); + int rightMargin = reader.ReadUInt16(); + int bottomMargin = reader.ReadUInt16(); + int dpiX = reader.ReadUInt16(); + int dpiY = reader.ReadUInt16(); + byte[] egaPalette = reader.ReadBytes(48); // 16 RGB triplets + reader.ReadByte(); // reserved + int numColorPlanes = reader.ReadByte(); + int planePitch = reader.ReadUInt16(); // always even + int paletteInfo = reader.ReadUInt16(); // 1=color/bw palette, 2=grayscale image + int width = reader.ReadUInt16(); + int height = reader.ReadUInt16(); + reader.ReadBytes(54); // reserved + + int vgaPaletteID = 0; + byte[] vgaPalette = null; + + int srcpitch = numColorPlanes * planePitch; + byte[] scanlines = new byte[srcpitch * height]; + + int pos = 0; + while (pos < scanlines.Length) + { + byte value = reader.ReadByte(); + if ((value & 0xc0) == 0xc0) // two last bits + { + byte length = (byte)(value & 0x3f); + value = reader.ReadByte(); + while (length > 0) + { + scanlines[pos++] = value; + length--; + } + } + else + { + scanlines[pos++] = value; + } + } + + byte[] imageData = new byte[width * height * 4]; + int destpitch = width * 4; + + if (bitsPerComponent == 4 && numColorPlanes == 1 && paletteInfo == 1) // 16 colors from a palette + { + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + int srcshift = ((x & 1) << 2); + int srcoffset = (x >> 1) + y * srcpitch; + int palentry = (scanlines[srcoffset] >> srcshift) & 15; + int offset = x + y * destpitch; + imageData[offset + 2] = egaPalette[palentry * 3]; + imageData[offset + 1] = egaPalette[palentry * 3 + 1]; + imageData[offset + 0] = egaPalette[palentry * 3 + 2]; + imageData[offset + 3] = 255; + } + } + } + else if (bitsPerComponent == 4 && numColorPlanes == 4 && paletteInfo == 2) // 4096 colors with 16 levels of transparency + { + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + int srcshift = ((x & 1) << 2); + int srcoffset = (x >> 1) + y * srcpitch; + int red = (scanlines[srcoffset] >> srcshift) & 15; + int green = (scanlines[srcoffset + planePitch] >> srcshift) & 15; + int blue = (scanlines[srcoffset + planePitch * 2] >> srcshift) & 15; + int alpha = (scanlines[srcoffset + planePitch * 3] >> srcshift) & 15; + + int offset = x + y * destpitch; + imageData[offset + 2] = (byte)((red * 255 + 7) / 15); + imageData[offset + 1] = (byte)((green * 255 + 7) / 15); + imageData[offset + 0] = (byte)((blue * 255 + 7) / 15); + imageData[offset + 3] = (byte)((alpha * 255 + 7) / 15); + } + } + } + else if (bitsPerComponent == 8 && numColorPlanes == 1 && paletteInfo == 1) // 256 colors from a palette + { + if (version == 5) + { + vgaPaletteID = reader.ReadByte(); // 0x0c + vgaPalette = reader.ReadBytes(768); // 256 RGB triplets + } + else + { + throw new InvalidDataException("No vga palette in pcx"); + } + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + int palentry = scanlines[x + y * srcpitch]; + int offset = x + y * destpitch; + imageData[offset + 2] = vgaPalette[palentry * 3]; + imageData[offset + 1] = vgaPalette[palentry * 3 + 1]; + imageData[offset + 0] = vgaPalette[palentry * 3 + 2]; + imageData[offset + 3] = 255; + } + } + } + else if (bitsPerComponent == 8 && numColorPlanes == 1 && paletteInfo == 2) // 256 shades of gray + { + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + byte gray = scanlines[x + y * srcpitch]; + int offset = x + y * destpitch; + imageData[offset + 2] = gray; + imageData[offset + 1] = gray; + imageData[offset + 0] = gray; + imageData[offset + 3] = 255; + } + } + } + else if (bitsPerComponent == 8 && numColorPlanes == 3) // 24 true color + { + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + int srcoffset = x + y * srcpitch; + int offset = x + y * destpitch; + imageData[offset + 2] = scanlines[srcoffset]; + imageData[offset + 1] = scanlines[srcoffset + planePitch]; + imageData[offset + 0] = scanlines[srcoffset + planePitch * 2]; + imageData[offset + 3] = 255; + } + } + } + else if (bitsPerComponent == 8 && numColorPlanes == 4) // 24 true color with alpha channel + { + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + int srcoffset = x + y * srcpitch; + int offset = x + y * destpitch; + imageData[offset + 2] = scanlines[srcoffset]; + imageData[offset + 1] = scanlines[srcoffset + planePitch]; + imageData[offset + 0] = scanlines[srcoffset + planePitch * 2]; + imageData[offset + 3] = scanlines[srcoffset + planePitch * 3]; + } + } + } + else + { + throw new InvalidDataException(string.Format("Unsupported pcx subformat (bits={0}, planes={1})", bitsPerComponent, numColorPlanes)); + } + + return new Bitmap(width, height, destpitch, PixelFormat.Format32bppArgb, Marshal.UnsafeAddrOfPinnedArrayElement(imageData, 0)); + } } }