在m x n矩阵中绘制矩形,圆形或任意多边形 [英] Draw rectangles, circles or arbitrary polygons in a m x n matrix

查看:133
本文介绍了在m x n矩阵中绘制矩形,圆形或任意多边形的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想模拟二维物体周围的流动.因此,我用C语言编写了一个程序,该程序使用Navier-Stokes方程来描述流体的运动.现在,我要提出的要点不仅仅是在仿真域中放置一个矩形.要绘制这样的矩形,我只需要做类似的事情:

for(int i=start_x; i<end_x; i++)
    for(int j=start_y; j<end_y; j++)
        M[i][j] = 1; // barrier cell = 1

这样做,我得到一个漂亮的矩形.没有惊喜.但是,如果我想模拟围绕圆形,十字形,三角形,机翼轮廓或任何其他任意多边形的流动,那将是一种方法呢?有没有简单的方法可以在尺寸为m x n的矩阵M中绘制此类2D对象?


我刚刚找到了一种简单的方法来绘制几乎任何想要的形状. @Nominal Animal的答案启发了我找到这种解决方案.我只是使用.png文件,并使用命令convert picture.png picture.pgm将其转换为.pgm文件(使用Linux).在我的代码中,我只需要多几行即可:

FILE *pgmFile;
pgmFile = fopen("picture.pgm", "r");
for(int i=0; i<1024; i++){
    for(int j=0; j<1024; j++){
        int d = fgetc(pgmFile);
        if(d < 255){
            M[i][j] = 1; // barrier cell = 1
        }
    }
}
fclose(pgmFile);

在这里,我使用1024 x 1024像素的图片.如果像素的值小于255(不是白色),则比我将M[i][j]的像素设置为1.这是我使用Stack Overflow徽标得到的结果(助焊剂来自左侧):

速度图,Re = 20000(雷诺数)

解决方案

当前的问题归结为栅格化(维基百科);特别是扫描行转换(siggraph.org)

siggraph.org文章包含有关如何绘制直线圆形和椭圆形 NetPBM格式阅读器.格式和 netpbm工具(来自Linux发行版和BSD变体中的netpbm包;请参见 Netpbm主页(用于其他系统)将任何图像转换为易于阅读的PGM(P5)文件,其中每个像素对应于OP矩阵中的一个元素.

这样,一个人可以使用例如Inkscape使用矢量图形绘制系统,对其进行任何尺寸的栅格化(例如通过导出为PNG图像),使用netpbm工具(pngtopnmanytopnm,然后按ppmtopgm)转换为PGM(P5)格式,并读取文件.确实,在POSIX.1系统中(除了在Windows中几乎所有地方),人们都可以使用popen("anytopnm path-to-file | pnmtopng", "r")(或更复杂的2-fork()管道解决方案)来读取任何PGM(P5)格式的像素图图像.

或者,可以考虑使用例如ImageMagick库可读取几乎所有格式的像素图图像(JPEG,GIF,PNG等).


就个人而言,无论是作为开发人员还是用户(尽管请注意,我显然不是Windows用户;十年来没有使用Microsoft产品),我还是更喜欢netpbm方法.程序mysim将使用例如/usr/lib/mysim/read-image shell脚本(或Windows中的程序,可能是macs;或者,如果定义,则由MYSIM_READ_IMAGE环境变量定义的脚本或程序),以读取命令行上指定的图像,并以PGM(P5)发出格式.主程序只需读取辅助程序的输出即可.

这样,如果用户需要对输入文件进行特殊处理,则可以轻松复制现有脚本,对其进行修改以适合自己的需求,然后将其安装在自己的主目录下(或全局安装),甚至替换现有的主目录. ,如果所有用户仍在使用它).

程序可以使用popen()fork() + execv()来执行脚本,并将输入文件名作为命令行参数,并在父进程中读取输出以构造初始矩阵. /p>

出于多种原因,我更喜欢这种方法而不是图像库方法.首先,它更具模块化,允许用户覆盖图像读取机制,并在必要时进行操作. (以我的经验,这种覆盖并不是经常需要的,但是当它们被使用时,它们非常有用,并且绝对值得.)其次,图像处理(在许多情况下是相当复杂的)是在单独的过程中完成的,这意味着在完全读取图像后,将释放读取和解密图像所需的所有内存(用于代码和数据).第三,这种方法遵循 Unix哲学 PBM( P4) PGM(P5)和<分别分别是href ="http://gittup.org/cgi-bin/man/man2html?5+ppm" rel ="nofollow noreferrer"> PPM(P6)格式). Wikipedia上有关 NetPBM格式的文章当前显示带有无效注释的示例(在标题和数据之间) . NetPBM手册页指出,最终的标头值后跟一个空格字符,并且不是为注释. (如果注释可能跟随最终的标头值,则无法知道二进制数据中的#(二进制0x23 = 35)是开始注释还是实际数据值.)

这是在公共领域中明确声明的,或者等效地是根据知识共享CC0 许可的

一个>许可证.这意味着即使在商业项目中,您也可以随意随意使用下面的代码,即使在商业项目中,也不能保证:如果它破坏,破坏某些东西或着火了,您就可以全力以赴.碎片,只能怪自己.

也就是说,它只是经过了轻微的测试,因此,如果您发现其中的错误,请在评论中告知我,以便我进行验证和修复.

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

/* Matrix to read data into */
typedef struct {
    int            rows;
    int            cols;
    long           rowstride;
    long           colstride;
    unsigned char *data;        /* data[row*rowstride + col*colstride] */
} matrix;
#define MATRIX_INIT { 0, 0, 0, 0, NULL }

/* NetPBM (binary) formats supported */
#define PNM_PBM  4
#define PNM_PGM  5
#define PNM_PPM  6

/* Error codes from pnm_*() functions */
#define PNM_EOF       -1
#define PNM_INVALID   -2
#define PNM_OVERFLOW  -3


/* This helper function returns the NetPBM file identifier;
   PNM_PBM, PNM_PGM, PNM_PPM, or PNM_INVALID if unsupported.
*/
static int pnm_type(FILE *in)
{
    /* First character must be 'P'. */
    if (getc(in) != 'P')
        return PNM_INVALID;

    /* Second character determines the type. */
    switch (getc(in)) {
    case '4': return PNM_PBM;
    case '5': return PNM_PGM;
    case '6': return PNM_PPM;
    default:  return PNM_INVALID;
    }
}

/* This helper function reads a number from a NetPBM header,
   correctly handling comments. Since all numbers in NetPBM
   headers are nonnegative, this function returns negative
   when an error occurs:
    -1: Premature end of input
    -2: Value is too large (int overflow)
    -3: Invalid input (not a NetPBM format file)
*/
static int pnm_value(FILE *in)
{
    int  c;

    /* Skip leading whitespace and comments. */
    c = getc(in);
    while (c == '\t' || c == '\n' || c == '\v' ||
           c == '\f' || c == '\r' || c == ' ' || c == '#')
        if (c == '#') {
            while (c != EOF && c != '\n')
                c = getc(in);
        } else
            c = getc(in);

    if (c == EOF)
        return PNM_EOF;

    if (c >= '0' && c <= '9') {
        int value = 0;

        while (c >= '0' && c <= '9') {
            const int oldvalue = value;
            value = 10*value + (c - '0');
            if ((int)(value / 10) != oldvalue)
                return PNM_OVERFLOW;
            c = getc(in);
        }

        /* Do not consume the separator. */
        if (c != EOF)
            ungetc(c, in);

        /* Success. */
        return value;
    }

    return PNM_INVALID;
}

/* This helper function consumes the single newline
   following the final value in the header.
   Returns 0 if success, PNM_INVALID otherwise.
*/
static int pnm_newline(FILE *in)
{
    int c;

    c = getc(in);
    if (c == '\r')
        c = getc(in);
    if (c == '\n')
        return 0;

    return PNM_INVALID;
}

static void pnm_matrix_free(matrix *to)
{
    if (to) {
        free(to->data);
        to->rows = 0;
        to->cols = 0;
        to->rowstride = 0;
        to->colstride = 0;
        to->data = NULL;
    }
}

static int pnm_matrix_init(matrix *to, int rows, int cols)
{
    size_t  cells, bytes;

    if (rows < 1 || cols < 1)
        return PNM_INVALID;

    cells = (size_t)rows * (size_t)cols;
    if ((size_t)(cells / (size_t)rows) != (size_t)cols ||
        (size_t)(cells / (size_t)cols) != (size_t)rows)
        return PNM_OVERFLOW;

    bytes = cells * sizeof to->data[0];
    if ((size_t)(bytes / sizeof to->data[0]) != cells)
        return PNM_OVERFLOW;

    to->data = malloc(bytes);
    if (!to->data)
        return PNM_OVERFLOW;

    to->rows = rows;
    to->cols = cols;

    /* Default to a row-major data order. */
    to->colstride = 1L;
    to->rowstride = cols;

    return 0;
}

static int pnm_p4_matrix(FILE *in, matrix *to)
{
    int rows, cols, result, r, c, byte = 0;

    cols = pnm_value(in);
    if (cols < 1)
        return PNM_INVALID;

    rows = pnm_value(in);
    if (rows < 1)
        return PNM_INVALID;

    if (pnm_newline(in))
        return PNM_INVALID;

    result = pnm_matrix_init(to, rows, cols);
    if (result)
        return result;

    for (r = 0; r < rows; r++) {
        const long ri = r * to->rowstride;
        for (c = 0; c < cols; c++) {
            const long i = ri + c * to->colstride;

            switch (c & 7) {
            case 0:
                byte = getc(in);
                if (byte == EOF) {
                    pnm_matrix_free(to);
                    return PNM_INVALID;
                }
                to->data[i] = !!(byte & 128);
                break;
            case 1:
                to->data[i] = !!(byte & 64);
                break;
            case 2:
                to->data[i] = !!(byte & 32);
                break;
            case 3:
                to->data[i] = !!(byte & 16);
                break;
            case 4:
                to->data[i] = !!(byte & 8);
                break;
            case 5:
                to->data[i] = !!(byte & 4);
                break;
            case 6:
                to->data[i] = !!(byte & 2);
                break;
            case 7:
                to->data[i] = !!(byte & 1);
                break;
            }
        }
    }

    return 0;
}

static int pnm_p5_matrix(FILE *in, matrix *to)
{
    int rows, cols, max, r, c, result;

    cols = pnm_value(in);
    if (cols < 1)
        return PNM_INVALID;

    rows = pnm_value(in);
    if (rows < 1)
        return PNM_INVALID;

    max = pnm_value(in);
    if (max < 1 || max > 65535)
        return PNM_INVALID;

    if (pnm_newline(in))
        return PNM_INVALID;

    result = pnm_matrix_init(to, rows, cols);
    if (result)
        return result; 

    if (max < 256) {
        const int limit = (max + 1) / 2;
        int val;
        for (r = 0; r < rows; r++) {
            const long ri = r * to->rowstride;
            for (c = 0; c < cols; c++) {
                const long i = ri + c * to->colstride;

                val = getc(in);
                if (val == EOF) {
                    pnm_matrix_free(to);
                    return PNM_INVALID;
                }

                to->data[i] = (val < limit);
            }
        }
    } else {
        const int limit = (max + 1) / 2;
        int val, low;
        for (r = 0; r < rows; r++) {
            const long ri = r * to->rowstride;
            for (c = 0; c < cols; c++) {
                const long i = ri + c * to->colstride;

                val = getc(in);
                low = getc(in);
                if (val == EOF || low == EOF) {
                    pnm_matrix_free(to);
                    return PNM_INVALID;
                }
                val = 256*val + low;

                to->data[i] = (val < limit);
            }
        }
    }

    return 0;
}

static int pnm_p6_matrix(FILE *in, matrix *to)
{
    int rows, cols, max, r, c, result;

    cols = pnm_value(in);
    if (cols < 1)
        return PNM_INVALID;

    rows = pnm_value(in);
    if (rows < 1)
        return PNM_INVALID;

    max = pnm_value(in);
    if (max < 1 || max > 65535)
        return PNM_INVALID;

    if (pnm_newline(in))
        return PNM_INVALID;

    result = pnm_matrix_init(to, rows, cols);
    if (result)
        return result;

    if (max < 256) {
        const int limit = 128 * max;
        int       val, rval, gval, bval;

        for (r = 0; r < rows; r++) {
            const long ri = r * to->rowstride;
            for (c = 0; c < cols; c++) {
                const long i = ri + c * to->colstride;

                rval = getc(in);
                gval = getc(in);
                bval = getc(in);
                if (rval == EOF || gval == EOF || bval == EOF) {
                    pnm_matrix_free(to);
                    return PNM_INVALID;
                }

                val =  54 * rval
                    + 183 * gval
                    +  19 * bval;

                to->data[i] = (val < limit);
            }
        }
    } else {
        const int limit = 128 * max;
        int       val, r rlo, g glo, b blo;

        for (r = 0; r < rows; r++) {
            const long ri = r * to->rowstride;
            for (c = 0; c < cols; c++) {
                const long i = ri + c * to->colstride;

                rhi = getc(in);
                rlo = getc(in);
                ghi = getc(in);
                glo = getc(in);
                bhi = getc(in);
                blo = getc(in);
                if (rhi == EOF || rlo == EOF ||
                    ghi == EOF || glo == EOF ||
                    bhi == EOF || blo == EOF) {
                    pnm_matrix_free(to);
                    return PNM_INVALID;
                }

                val =  54 * (rhi*256 + rlo)
                    + 183 * (ghi*256 + glo)
                    +  19 * (bhi*256 + blo);

                to->data[i] = (val < limit);
            }
        }
    }

    return 0;
}

int pnm_matrix(FILE *in, matrix *to)
{
    /* If the matrix is specified, initialize it. */
    if (to) {
        to->rows = 0L;
        to->cols = 0L;
        to->rowstride = 0L;
        to->colstride = 0L;
        to->data = NULL;
    }

    /* Sanity checks on parameters. */
    if (!to || !in || ferror(in))
        return PNM_INVALID;

    switch (pnm_type(in)) {
    case PNM_PBM: return pnm_p4_matrix(in, to);
    case PNM_PGM: return pnm_p5_matrix(in, to);
    case PNM_PPM: return pnm_p6_matrix(in, to);
    default:      return PNM_INVALID;
    }
}

int main(void)
{
    int r, c;
    matrix m = MATRIX_INIT;

    if (pnm_matrix(stdin, &m)) {
        fprintf(stderr, "Cannot parse standard input.\n");
        return EXIT_FAILURE;
    }

    fprintf(stderr, "Read %d rows, %d columns, from standard input.\n", m.rows, m.cols);

    /* For ease of debugging, we output the matrix as a PGM file. */
    printf("P5\n%d %d\n255\n", m.cols, m.rows);
    for (r = 0; r < m.rows; r++)
        for (c = 0; c < m.cols; c++)
            if (m.data[r * m.rowstride + c * m.colstride] == 0)
                putchar(255); /* White */
            else
                putchar(0);   /* Black */

    return EXIT_SUCCESS;
}

请注意,对于OP打算如何使用矩阵,我没有验证位/灰度/颜色转换是否正确. (即,白色"还是浅色应在矩阵中产生01.)如果需要将其反转以用于PBM图像,请使用!(byte & NUMBER).如果您需要将其反转为PGM或PPM图像,请使用(val >= limit).

该程序应为有效的C语言(甚至是C89),并且可以在任何体系结构上进行编译.在Windows等愚蠢的体系结构上,您可能必须以二进制模式"(在fopen()标志中包含b)打开/重新打开标准输入,否则它们可能会破坏输入.

在Linux上,我使用

编译并测试了程序(example.c)

gcc -Wall -O2 example.c -o example
./example < inputfile.pbm > result-pbm.pgm
./example < inputfile.pgm > result-pgm.pgm
./example < inputfile.ppm > result-ppm.pgm

I want to simulate the flow around objects in two dimensions. Therefore I wrote a program in C which uses the Navier-Stokes equations to describe the motion of fluids. Now I came to the point where I actually want more than just placing a rectangle in the simulation domain. To draw such a rectangle I just do something like:

for(int i=start_x; i<end_x; i++)
    for(int j=start_y; j<end_y; j++)
        M[i][j] = 1; // barrier cell = 1

Doing this I get a nice rectangle. No surprise. But what would be an approach if I want to simulate the flow around a circle, a cross, a triangle, a wing profile or any other arbitrary polygon? Is there an easy way to draw such 2D objects in a matrix M of size m x n?


I just found an easy way to draw almost any shape I want. The answer of @Nominal Animal inspired me to find this solution. I just use a .png file and convert it to a .pgm file using the command convert picture.png picture.pgm (using Linux). In my code I only need a few more lines:

FILE *pgmFile;
pgmFile = fopen("picture.pgm", "r");
for(int i=0; i<1024; i++){
    for(int j=0; j<1024; j++){
        int d = fgetc(pgmFile);
        if(d < 255){
            M[i][j] = 1; // barrier cell = 1
        }
    }
}
fclose(pgmFile);

Here I use a picture of 1024 x 1024 pixels. If the value of the pixel is smaller than 255 (not white) than I set the pixel of M[i][j] to 1. Here is a result I made with the Stack Overflow logo (flux is coming from the left):

Velocity plot, Re = 20000 (Reynolds number)

解决方案

The problem at hand boils down to rasterisation (Wikipedia); and to scan line conversion (siggraph.org) in particular.

The siggraph.org article contains detailed explanations on how to draw straight lines, circles and ellipses, and convex and concave polygons.

However, this is a problem that has already been solved a large number of times. While OP could certainly implement the necessary primitives (lines, ellipses, triangles, polygons), there is a much simpler approach.

I suggest that OP implements a simple NetPBM format reader for P5 (binary grayscale pixmap) format, and the netpbm tools (from netpbm package in Linux distributions and BSD variants; see the Netpbm home page for other systems) to convert any image to an easy-to-read PGM (P5) file, where each pixel corresponds to one element in OP's matrix.

That way, one can use e.g. Inkscape to draw the system using vector graphics, rasterize it at any size (by e.g. exporting as PNG image), convert to PGM (P5) format using the netpbm tools (pngtopnm or anytopnm, followed by ppmtopgm), and read the file. Indeed, in POSIX.1 systems (just about everywhere except in windows), one can use popen("anytopnm path-to-file | pnmtopng", "r") (or slightly more complicated two-fork() piped solution) to read any pixmap image in PGM (P5) format.

Alternatively, one could consider using e.g. the ImageMagick library to read just about any format pixmap images (JPEG, GIF, PNG etc.).


Personally, both as a developer and user (although note that I'm explicitly a non-Windows user; haven't used Microsoft products in over a decade), I would prefer the netpbm approach. The program, say mysim, would use e.g. /usr/lib/mysim/read-image shell script (or program in Windows, perhaps macs; or, if defined, the script or program defined by the MYSIM_READ_IMAGE environment variable), to read an image specified on the command line, emitting it in PGM (P5) format. The main program would simply read the output of the helper.

This way, if a user needs special handling for input files, they can trivially copy the existing script, modify it to suit their own needs, and install somewhere under their own home directory (or globally, or even replace the existing one, if it is used by all users anyway).

The program can use either popen() or fork()+execv() to execute the script, with the input filename as a command-line parameter, and read the output in the parent process to construct the initial matrix.

I prefer this approach over the image library approach for a number of reasons. First, it is more modular, allowing the user to override the image reading mechanism and manipulate it if necessary. (In my experience, such overrides are not very often needed, but when they are, they are extremely useful, and definitely overall worth it.) Second, the image processing (which in many cases is quite complex) is done in a separate process, which means all memory (for code and data) needed to read and decipher the image are released when the image has been fully read. Third, this approach follows the Unix philosophy and the KISS principle, which have a proven track record of guiding the development of robust and useful tools.


Here is an example program that reads a binary PBM, PGM, or PPM file (NetPBM P4, P5, and P6 formats, respectively) from the standard input, into a matrix structure, filling the matrix with 0 or 1 (based on the colors or grayscale values read from the image). For ease of testing, the program outputs the matrix to standard output in PGM (P5) format.

The program follows the format specification in the NetPBM manual pages (for PBM (P4), PGM (P5), and PPM (P6) formats, respectively). The Wikipedia article on NetPBM formats currently show examples with invalid comments (between the header and the data). The NetPBM manual pages state that the final header value is followed by a single whitespace character, and not a comment. (If a comment may follow the final header value, it is impossible to know whether a # (binary 0x23 = 35) in the binary data starts a comment, or is an actual data value.)

This is explicitly in the public domain, or, equivalently, licensed under the Creative Commons CC0 license. This means you are completely free to use the below code any way and anywhere you like, even in commercial projects, but that there are no guarantees: if it breaks, or breaks something, or sets your hair on fire, you get to keep all the pieces and only blame yourself.

That said, it is only lightly tested, so if you find a bug in it, let me know in the comments so I can verify and fix.

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

/* Matrix to read data into */
typedef struct {
    int            rows;
    int            cols;
    long           rowstride;
    long           colstride;
    unsigned char *data;        /* data[row*rowstride + col*colstride] */
} matrix;
#define MATRIX_INIT { 0, 0, 0, 0, NULL }

/* NetPBM (binary) formats supported */
#define PNM_PBM  4
#define PNM_PGM  5
#define PNM_PPM  6

/* Error codes from pnm_*() functions */
#define PNM_EOF       -1
#define PNM_INVALID   -2
#define PNM_OVERFLOW  -3


/* This helper function returns the NetPBM file identifier;
   PNM_PBM, PNM_PGM, PNM_PPM, or PNM_INVALID if unsupported.
*/
static int pnm_type(FILE *in)
{
    /* First character must be 'P'. */
    if (getc(in) != 'P')
        return PNM_INVALID;

    /* Second character determines the type. */
    switch (getc(in)) {
    case '4': return PNM_PBM;
    case '5': return PNM_PGM;
    case '6': return PNM_PPM;
    default:  return PNM_INVALID;
    }
}

/* This helper function reads a number from a NetPBM header,
   correctly handling comments. Since all numbers in NetPBM
   headers are nonnegative, this function returns negative
   when an error occurs:
    -1: Premature end of input
    -2: Value is too large (int overflow)
    -3: Invalid input (not a NetPBM format file)
*/
static int pnm_value(FILE *in)
{
    int  c;

    /* Skip leading whitespace and comments. */
    c = getc(in);
    while (c == '\t' || c == '\n' || c == '\v' ||
           c == '\f' || c == '\r' || c == ' ' || c == '#')
        if (c == '#') {
            while (c != EOF && c != '\n')
                c = getc(in);
        } else
            c = getc(in);

    if (c == EOF)
        return PNM_EOF;

    if (c >= '0' && c <= '9') {
        int value = 0;

        while (c >= '0' && c <= '9') {
            const int oldvalue = value;
            value = 10*value + (c - '0');
            if ((int)(value / 10) != oldvalue)
                return PNM_OVERFLOW;
            c = getc(in);
        }

        /* Do not consume the separator. */
        if (c != EOF)
            ungetc(c, in);

        /* Success. */
        return value;
    }

    return PNM_INVALID;
}

/* This helper function consumes the single newline
   following the final value in the header.
   Returns 0 if success, PNM_INVALID otherwise.
*/
static int pnm_newline(FILE *in)
{
    int c;

    c = getc(in);
    if (c == '\r')
        c = getc(in);
    if (c == '\n')
        return 0;

    return PNM_INVALID;
}

static void pnm_matrix_free(matrix *to)
{
    if (to) {
        free(to->data);
        to->rows = 0;
        to->cols = 0;
        to->rowstride = 0;
        to->colstride = 0;
        to->data = NULL;
    }
}

static int pnm_matrix_init(matrix *to, int rows, int cols)
{
    size_t  cells, bytes;

    if (rows < 1 || cols < 1)
        return PNM_INVALID;

    cells = (size_t)rows * (size_t)cols;
    if ((size_t)(cells / (size_t)rows) != (size_t)cols ||
        (size_t)(cells / (size_t)cols) != (size_t)rows)
        return PNM_OVERFLOW;

    bytes = cells * sizeof to->data[0];
    if ((size_t)(bytes / sizeof to->data[0]) != cells)
        return PNM_OVERFLOW;

    to->data = malloc(bytes);
    if (!to->data)
        return PNM_OVERFLOW;

    to->rows = rows;
    to->cols = cols;

    /* Default to a row-major data order. */
    to->colstride = 1L;
    to->rowstride = cols;

    return 0;
}

static int pnm_p4_matrix(FILE *in, matrix *to)
{
    int rows, cols, result, r, c, byte = 0;

    cols = pnm_value(in);
    if (cols < 1)
        return PNM_INVALID;

    rows = pnm_value(in);
    if (rows < 1)
        return PNM_INVALID;

    if (pnm_newline(in))
        return PNM_INVALID;

    result = pnm_matrix_init(to, rows, cols);
    if (result)
        return result;

    for (r = 0; r < rows; r++) {
        const long ri = r * to->rowstride;
        for (c = 0; c < cols; c++) {
            const long i = ri + c * to->colstride;

            switch (c & 7) {
            case 0:
                byte = getc(in);
                if (byte == EOF) {
                    pnm_matrix_free(to);
                    return PNM_INVALID;
                }
                to->data[i] = !!(byte & 128);
                break;
            case 1:
                to->data[i] = !!(byte & 64);
                break;
            case 2:
                to->data[i] = !!(byte & 32);
                break;
            case 3:
                to->data[i] = !!(byte & 16);
                break;
            case 4:
                to->data[i] = !!(byte & 8);
                break;
            case 5:
                to->data[i] = !!(byte & 4);
                break;
            case 6:
                to->data[i] = !!(byte & 2);
                break;
            case 7:
                to->data[i] = !!(byte & 1);
                break;
            }
        }
    }

    return 0;
}

static int pnm_p5_matrix(FILE *in, matrix *to)
{
    int rows, cols, max, r, c, result;

    cols = pnm_value(in);
    if (cols < 1)
        return PNM_INVALID;

    rows = pnm_value(in);
    if (rows < 1)
        return PNM_INVALID;

    max = pnm_value(in);
    if (max < 1 || max > 65535)
        return PNM_INVALID;

    if (pnm_newline(in))
        return PNM_INVALID;

    result = pnm_matrix_init(to, rows, cols);
    if (result)
        return result; 

    if (max < 256) {
        const int limit = (max + 1) / 2;
        int val;
        for (r = 0; r < rows; r++) {
            const long ri = r * to->rowstride;
            for (c = 0; c < cols; c++) {
                const long i = ri + c * to->colstride;

                val = getc(in);
                if (val == EOF) {
                    pnm_matrix_free(to);
                    return PNM_INVALID;
                }

                to->data[i] = (val < limit);
            }
        }
    } else {
        const int limit = (max + 1) / 2;
        int val, low;
        for (r = 0; r < rows; r++) {
            const long ri = r * to->rowstride;
            for (c = 0; c < cols; c++) {
                const long i = ri + c * to->colstride;

                val = getc(in);
                low = getc(in);
                if (val == EOF || low == EOF) {
                    pnm_matrix_free(to);
                    return PNM_INVALID;
                }
                val = 256*val + low;

                to->data[i] = (val < limit);
            }
        }
    }

    return 0;
}

static int pnm_p6_matrix(FILE *in, matrix *to)
{
    int rows, cols, max, r, c, result;

    cols = pnm_value(in);
    if (cols < 1)
        return PNM_INVALID;

    rows = pnm_value(in);
    if (rows < 1)
        return PNM_INVALID;

    max = pnm_value(in);
    if (max < 1 || max > 65535)
        return PNM_INVALID;

    if (pnm_newline(in))
        return PNM_INVALID;

    result = pnm_matrix_init(to, rows, cols);
    if (result)
        return result;

    if (max < 256) {
        const int limit = 128 * max;
        int       val, rval, gval, bval;

        for (r = 0; r < rows; r++) {
            const long ri = r * to->rowstride;
            for (c = 0; c < cols; c++) {
                const long i = ri + c * to->colstride;

                rval = getc(in);
                gval = getc(in);
                bval = getc(in);
                if (rval == EOF || gval == EOF || bval == EOF) {
                    pnm_matrix_free(to);
                    return PNM_INVALID;
                }

                val =  54 * rval
                    + 183 * gval
                    +  19 * bval;

                to->data[i] = (val < limit);
            }
        }
    } else {
        const int limit = 128 * max;
        int       val, rhi, rlo, ghi, glo, bhi, blo;

        for (r = 0; r < rows; r++) {
            const long ri = r * to->rowstride;
            for (c = 0; c < cols; c++) {
                const long i = ri + c * to->colstride;

                rhi = getc(in);
                rlo = getc(in);
                ghi = getc(in);
                glo = getc(in);
                bhi = getc(in);
                blo = getc(in);
                if (rhi == EOF || rlo == EOF ||
                    ghi == EOF || glo == EOF ||
                    bhi == EOF || blo == EOF) {
                    pnm_matrix_free(to);
                    return PNM_INVALID;
                }

                val =  54 * (rhi*256 + rlo)
                    + 183 * (ghi*256 + glo)
                    +  19 * (bhi*256 + blo);

                to->data[i] = (val < limit);
            }
        }
    }

    return 0;
}

int pnm_matrix(FILE *in, matrix *to)
{
    /* If the matrix is specified, initialize it. */
    if (to) {
        to->rows = 0L;
        to->cols = 0L;
        to->rowstride = 0L;
        to->colstride = 0L;
        to->data = NULL;
    }

    /* Sanity checks on parameters. */
    if (!to || !in || ferror(in))
        return PNM_INVALID;

    switch (pnm_type(in)) {
    case PNM_PBM: return pnm_p4_matrix(in, to);
    case PNM_PGM: return pnm_p5_matrix(in, to);
    case PNM_PPM: return pnm_p6_matrix(in, to);
    default:      return PNM_INVALID;
    }
}

int main(void)
{
    int r, c;
    matrix m = MATRIX_INIT;

    if (pnm_matrix(stdin, &m)) {
        fprintf(stderr, "Cannot parse standard input.\n");
        return EXIT_FAILURE;
    }

    fprintf(stderr, "Read %d rows, %d columns, from standard input.\n", m.rows, m.cols);

    /* For ease of debugging, we output the matrix as a PGM file. */
    printf("P5\n%d %d\n255\n", m.cols, m.rows);
    for (r = 0; r < m.rows; r++)
        for (c = 0; c < m.cols; c++)
            if (m.data[r * m.rowstride + c * m.colstride] == 0)
                putchar(255); /* White */
            else
                putchar(0);   /* Black */

    return EXIT_SUCCESS;
}

Note that I did not verify whether the bit/grayscale/color conversion is the right way with respect to how OP intends to use the matrix. (That is, whether "white" or light colours should yield a 0 or a 1 in the matrix.) If you need to invert it for PBM images, use !(byte & NUMBER) instead. If you need to invert it for PGM or PPM images, use (val >= limit) instead.

The program should be valid C (even down to C89), and compile on any architecture. On silly architectures like Windows, you might have to open/reopen the standard input in "binary mode" (include b in the fopen() flags), as they otherwise may mangle the input.

On Linux, I compiled and tested the program (example.c) with

gcc -Wall -O2 example.c -o example
./example < inputfile.pbm > result-pbm.pgm
./example < inputfile.pgm > result-pgm.pgm
./example < inputfile.ppm > result-ppm.pgm

这篇关于在m x n矩阵中绘制矩形,圆形或任意多边形的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆