質問

I have this class that worked for 8 bpp bitmaps to pcx, but I am having trouble making it work for 1 bpp.

The image starts off like this:

A.bmp

But the resulting PCX is black after line 27, as seen in IrfanView:

AOut.pcx in IrfanView

Can anyone help me spot the glaring error?

Usage by the way is Pcx.SavePCX("AOut.pcx", new Bitmap("A.bmp"));

using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;

namespace Imaging
{
    public sealed class Pcx
    {
        private static void WriteWord(Stream stream, int data)
        {
            stream.WriteByte((byte) (data & 0xFF));
            stream.WriteByte((byte) ((data >> 8) & 0xFF));
        }

        public static void SavePCX(Stream pcxStream, Bitmap bmp)
        {
            if (bmp.PixelFormat != PixelFormat.Format1bppIndexed)
            {
                throw new Exception("Can only PCX bitmaps that are 1bpp indexed");
            }

            var data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
            try
            {
                {
                    //header
                    pcxStream.WriteByte(10); //    char manufacturer;
                    pcxStream.WriteByte(5); //char version;
                    pcxStream.WriteByte(1); //char encoding;
                    pcxStream.WriteByte(1); // char bpp;
                    pcxStream.WriteByte(0);
                    pcxStream.WriteByte(0); //char xmin[2];
                    pcxStream.WriteByte(0);
                    pcxStream.WriteByte(0); //char ymin[2];
                    WriteWord(pcxStream, bmp.Width - 1); // char xmax[2];
                    WriteWord(pcxStream, bmp.Height - 1); //char ymax[2];

                    WriteWord(pcxStream, 72); //word(pcx->hdpi, 72);
                    WriteWord(pcxStream, 72); // word(pcx->vdpi, 72);
                    for (var i = 0; i < 16*3; i++) //4bpp palette
                    {
                        pcxStream.WriteByte(0);
                    }
                    pcxStream.WriteByte(0); // pcx->res = 0;
                    pcxStream.WriteByte(1); // pcx->nplanes = 1;
                    WriteWord(pcxStream, bmp.Width); // word(pcx->bytesperline, width / 2);
                    WriteWord(pcxStream, 0); //word(pcx->palletteinfo, 0);
                    WriteWord(pcxStream, 0); //word(pcx->hscrn, 0);
                    WriteWord(pcxStream, 0); //word(pcx->vscrn, 0);

                    for (var i = 0; i < 54; i++) //memset(pcx->filler, 0, 54);
                    {
                        pcxStream.WriteByte(0);
                    }
                } //end of header

                {
                    //read all bytes to an array
                    var baseLine = data.Scan0;
                    // Declare an array to hold the bytes of the bitmap.
                    var byteLength = bmp.Width*bmp.Height;
                    var bytes = new byte[byteLength];

                    // Copy the RGB values into the array.
                    for (var y = 0; y < data.Height; y++)
                    {
                        var lineOffset = y*data.Stride;
                        Debug.WriteLine("Y={0}, Offset={1}", y, lineOffset);
                        for (var x = 0; x < data.Width; x++)
                        {
                            bytes[y*bmp.Width + x] = Marshal.ReadByte(baseLine, lineOffset + x);
                        }
                    }

                    var baseIdx = 0;
                    var end = byteLength;
                    var run = 0;
                    var ldata = -1;
                    byte ld;

                    while (baseIdx < end)
                    {
                        //if it matches, increase the run by 1 up to max of 63
                        if ((bytes[baseIdx] == ldata) && (run < 63)) run++;
                        else
                        {
                            //write data
                            if (run != 0) //not first run
                            {
                                ld = (byte) ldata;
                                if ((run > 1) || (ld >= 0xC0)) pcxStream.WriteByte((byte) (0xC0 | run));
                                pcxStream.WriteByte(ld);
                            }
                            run = 1;
                        }
                        ldata = bytes[baseIdx];
                        baseIdx++;
                    }
                    ld = (byte) ((ldata >> 4) | (ldata << 4));
                    if ((run > 1) || (ld >= 0xC0)) pcxStream.WriteByte((byte) (0xC0 | run));
                    pcxStream.WriteByte(ld);
                }
            }
            finally
            {
                bmp.UnlockBits(data);
            }
        }

        public static void SavePCX(string fileName, Bitmap bbp1Bmp)
        {
            using (var fstest = new FileStream(fileName, FileMode.Create, FileAccess.Write))
            {
                SavePCX(fstest, bbp1Bmp);
            }
        }
    }
}
役に立ちましたか?

解決

Solved it by using data.Stride for the bytes per line, and reading in the bytes from the 1bpp using stride as the width, not width.

public static void SavePCX(Stream pcxStream, Bitmap bmp)
{
    if (bmp.PixelFormat != PixelFormat.Format1bppIndexed)
    {
        throw new Exception("Can only PCX bitmaps that are 1bpp indexed");
    }

    var data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
    try
    {
        {
            //header
            pcxStream.WriteByte(10); //    char manufacturer;
            pcxStream.WriteByte(5); //char version;
            pcxStream.WriteByte(1); //char encoding;
            pcxStream.WriteByte(1); // char bpp;
            pcxStream.WriteByte(0);
            pcxStream.WriteByte(0); //char xmin[2];
            pcxStream.WriteByte(0);
            pcxStream.WriteByte(0); //char ymin[2];
            WriteWord(pcxStream, bmp.Width - 1); // char xmax[2];
            WriteWord(pcxStream, bmp.Height - 1); //char ymax[2];

            WriteWord(pcxStream, 72); //word(pcx->hdpi, 72);
            WriteWord(pcxStream, 72); // word(pcx->vdpi, 72);
            for (var i = 0; i < 16*3; i++) //4bpp palette
            {
                pcxStream.WriteByte(0);
            }
            pcxStream.WriteByte(0); // pcx->res = 0;
            pcxStream.WriteByte(1); // pcx->nplanes = 1;
            WriteWord(pcxStream, data.Stride); // word(pcx->bytesperline, width / 2);
            WriteWord(pcxStream, 0); //word(pcx->palletteinfo, 0);
            WriteWord(pcxStream, 0); //word(pcx->hscrn, 0);
            WriteWord(pcxStream, 0); //word(pcx->vscrn, 0);

            for (var i = 0; i < 54; i++) //memset(pcx->filler, 0, 54);
            {
                pcxStream.WriteByte(0);
            }
        } //end of header

        {
            //read all bytes to an array
            var baseLine = data.Scan0;
            // Declare an array to hold the bytes of the bitmap.
            var byteLength = data.Stride*data.Height;
            var bytes = new byte[byteLength];

            // Copy the RGB values into the array.
            for (var y = 0; y < data.Height; y++)
            {
                var lineOffset = y*data.Stride;
                Debug.WriteLine("Y={0}, Offset={1}", y, lineOffset);
                for (var x = 0; x < data.Stride; x++)
                {
                    bytes[y*data.Stride + x] = Marshal.ReadByte(baseLine, lineOffset + x);
                }
            }

            var baseIdx = 0;
            var end = byteLength;
            var run = 0;
            var ldata = -1;
            byte ld;

            while (baseIdx < end)
            {
                //if it matches, increase the run by 1 up to max of 63
                if ((bytes[baseIdx] == ldata) && (run < 63)) run++;
                else
                {
                    //write data
                    if (run != 0) //not first run
                    {
                        ld = (byte) ldata;
                        if ((run > 1) || (ld >= 0xC0)) pcxStream.WriteByte((byte) (0xC0 | run));
                        pcxStream.WriteByte(ld);
                    }
                    run = 1;
                }
                ldata = bytes[baseIdx];
                baseIdx++;
            }
            ld = (byte) ((ldata >> 4) | (ldata << 4));
            if ((run > 1) || (ld >= 0xC0)) pcxStream.WriteByte((byte) (0xC0 | run));
            pcxStream.WriteByte(ld);
        }
    }
    finally
    {
        bmp.UnlockBits(data);
    }
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top