1

我正在与仪器通信(远程控制它),
我需要做的一件事是绘制仪器屏幕。

为了获得屏幕,我发出一个命令,仪器
回复一个代表屏幕的字节数组。

以下是仪器手册关于将响应转换为实际屏幕的内容:

该命令检索用于显示的帧缓冲区数据。
它的大小为 19200 字节,每像素 2 位,每字节 4 像素,排列为
320x240 字符。
数据以 RLE 编码形式发送。
要将此数据转换为 BMP 以在 Windows 中使用,需要将其
转换为 4BPP。另请注意,BMP 文件相对于该数据是上下颠倒
的,即顶部显示行是 BMP 中的最后一行。

我设法解包数据,但现在我被困在如何
从解包的字节数组实际转到位图上。

我在这方面的背景几乎为零,我的搜索
也没有透露太多。

我正在寻找可以用来帮助我
理解如何完成这项工作的方向和/或文章。

任何代码甚至伪代码也会有所帮助。:-)

所以,总结一下:

如何将大小为 19200 字节的字节数组(
每个字节代表 4 个像素(每像素 2 位))
转换为排列为 320x240 个字符的位图。

提前致谢。

4

3 回答 3

2

要做这样的事情,你需要这样的例程:

Bitmap ConvertToBitmap(byte[] data, int width, int height)
{
    Bitmap bm = new Bitmap(width, height, PixelFormat.Format24bppRgb);
    for (int y=0; y < height; y++) {
        for (int x=0; x < width; x++) {
            int value = ReadPixelValue(data, x, y, width);
            Color c = ConvertValToColor(value);
            bm.SetPixel(x, y, c);
        }
    }
    return bm;
}

从这里开始,您需要 ReadPixelValue 和 ConvertValToColor。

static int ReadPixelValue(byte[] data, int x, int y, width)
{
    int pixelsPerByte = 4;
    // added the % pixelsPerByte to deal with width not being a multiple of pixelsPerByte,
    // which won't happen in your case, but will in the general case
    int bytesPerLine = width / pixelsPerByte + (width % pixelsPerByte != 0 ? 1 : 0);
    int index = y * bytesPerLine + (x / pixelsPerByte);
    byte b = data[index];

    int pixelIndex = (x % pixelsPerByte) * 2;
    // if every 4 pixels are reversed, try this:
    // int pixelIndex = 8 - (x % pixelsPerByte) * 2;
    return ((int b) >> pixelIndex) & 0x3;        
}

基本上,我从每个字节中提取每组两位并将其作为 int 返回。

至于转换为颜色,这取决于您如何制作返回的 4 个值的头部或尾部。您很可能可以执行以下操作:

static Color[] _colors = new Color[] { Color.Black, Color.Red, Color.Blue, Color.White };
static Color ConvertValToColor(int val)
{
    if (val < 0 || val > _colors.Length)
        throw new ArgumentOutOfRangeException("val");
    return _colors[val];
}
于 2010-06-03T20:02:52.577 回答
1

如果每个像素有两个位,则每个像素都有 4 种不同的可能颜色。可能颜色已编入索引或只是硬编码(即 0 表示黑色,1 表示白色等)。

不知道这是否有很大帮助(我不知道您使用的是什么位图对象,但也许它具有每个通道 1 个字节的常规 RGB 或 ARGB 方案),但在伪动作脚本中,我认为您应该做这样的事情。

//  80 -> 320 / 4
for(var x:int = 0; x < 80; x++) {
    for(var y:int = 0; y < 240; y++) {
        var byteVal:int = readByte();

        var px_1:int = (byteVal >> 6) & 0x03;
        var px_2:int = (byteVal >> 4) & 0x03;
        var px_3:int = (byteVal >> 2) & 0x03;
        var px_4:int = (byteVal) & 0x03;

        //  map your pixel value to ARGB 
        px_1 = getPixelValue(px_1);
        px_2 = getPixelValue(px_2);
        px_3 = getPixelValue(px_3);
        px_4 = getPixelValue(px_4);     
        //  assuming setPixel(x,y,pixelValue)
        setPixel((x * 4), y, px_1);
        setPixel((x * 4) + 1, y, px_2);
        setPixel((x * 4) + 2, y, px_3);
        setPixel((x * 4) + 3, y, px_4);


    }
}

function getPixelValue(idx:int):uint {
    //   just an example...
    switch(idx) {
        case 0:     return 0xff000000;  //  black
        case 1:     return 0xffffffff;  //  white
        case 2:     return 0xffff0000;  //  red
        case 3:     return 0xff0000ff;  //  blue
    }
}

上面的代码,我只想说,只是给你一个想法(希望如此!)并且基于一些假设,比如这四个像素如何存储在一个字节中。

希望这是有道理的。

于 2010-06-03T20:01:44.903 回答
0

我不知道这是否有帮助,我将其用于从罕见的旧硬件获得的数据:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            byte[] imageBytes = new byte[19201];
            //(Fill it with the data from the unit before doing the rest).
            Bitmap bmp_workarea = new Bitmap(320, 240, System.Drawing.Imaging.PixelFormat.Format4bppIndexed);
            Image newImage = Image.FromStream(new MemoryStream(imageBytes));
            using (Graphics gr = Graphics.FromImage(bmp_workarea))
            {
                gr.DrawImage(newImage, new Rectangle(0, 0, bmp_workarea.Width, bmp_workarea.Height));
            }
            //now you can use newImage, for example picturebox1.image=newimage

        }
    }
}
于 2010-06-03T20:11:13.053 回答