rasterisation
scan line conversion
(siggraph.org)。
straight lines
,
circles and ellipses
和
convex
concave
多边形。
然而,这是一个已经解决了很多次的问题。虽然OP当然可以实现必要的基本体(直线、椭圆、三角形、多边形),但有一种更简单的方法。
我建议OP实现一个简单的
NetPBM format
P5(二进制灰度pixmap)格式的读取器,以及
netpbm tools
(来自
netpbm
Netpbm home page
对于其他系统),将任何图像转换为易于读取的PGM(P5)文件,其中每个像素对应于OP矩阵中的一个元素。
这样,可以使用Inkscape等工具使用矢量图形绘制系统,将其光栅化为任意大小(例如导出为PNG图像),使用netpbm工具转换为PGM(P5)格式(
pngtopnm
anytopnm
ppmtopgm
),并读取文件。事实上,在POSIX中。1系统(除了windows之外,几乎任何地方都可以使用)
popen("anytopnm path-to-file | pnmtopng", "r")
(或稍微复杂一点的两个-
fork()
或者,可以考虑使用ImageMagick库来读取任何格式的pixmap图像(JPEG、GIF、PNG等)。
就个人而言,无论是作为开发人员还是用户(尽管请注意,我显然是非Windows用户;十多年来没有使用过Microsoft产品),我更喜欢netpbm方法。比如说这个节目
mysim
,将使用例如。
/usr/lib/mysim/read-image
shell脚本(或Windows中的程序,可能是Mac;或者,如果定义了,则是由
MYSIM_READ_IMAGE
这样,如果用户需要对输入文件进行特殊处理,他们可以简单地复制现有脚本,修改它以满足自己的需要,并将其安装在自己的主目录下(或者全局安装,甚至替换现有的,如果所有用户都使用它的话)。
该程序可以使用
popen()
分叉()
+
execv()
以输入文件名作为命令行参数执行脚本,并在父进程中读取输出以构造初始矩阵。
与图像库方法相比,我更喜欢这种方法,原因有很多。首先,它更加模块化,允许用户覆盖图像读取机制,并在必要时对其进行操作。(根据我的经验,这种覆盖并不经常需要,但当它们是时,它们是非常有用的,并且绝对是值得的。)第二,图像处理(在许多情况下相当复杂)是在一个单独的过程中完成的,这意味着读取和解密图像所需的所有内存(用于代码和数据)在完全读取图像时被释放。第三,这种方法遵循
Unix philosophy
和
KISS principle
,它们在指导开发强大而有用的工具方面有着良好的记录。
下面是一个示例程序,它将二进制PBM、PGM或PPM文件(分别为NetPBM P4、P5和P6格式)从标准输入读取到矩阵结构中,并用
0
1
PBM (P4)
,
PGM (P5)
和
PPM (P6)
格式)。维基百科文章
NetPBM formats
不
评论。(如果注释可能在最终标题值之后,则不可能知道
#
Creative Commons CC0
许可证这意味着您可以在任何地方以任何方式完全自由地使用下面的代码,即使是在商业项目中,但也不能保证:如果代码断裂,或损坏了什么东西,或让你的头发着火,那么您可以保留所有代码,只怪自己。
这就是说,它只是经过轻微测试,因此如果您发现其中存在错误,请在评论中告诉我,以便我可以验证和修复。
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
typedef struct {
int rows;
int cols;
long rowstride;
long colstride;
unsigned char *data;
} matrix;
#define MATRIX_INIT { 0, 0, 0, 0, NULL }
#define PNM_PBM 4
#define PNM_PGM 5
#define PNM_PPM 6
#define PNM_EOF -1
#define PNM_INVALID -2
#define PNM_OVERFLOW -3
static int pnm_type(FILE *in)
{
if (getc(in) != 'P')
return PNM_INVALID;
switch (getc(in)) {
case '4': return PNM_PBM;
case '5': return PNM_PGM;
case '6': return PNM_PPM;
default: return PNM_INVALID;
}
}
static int pnm_value(FILE *in)
{
int c;
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);
}
if (c != EOF)
ungetc(c, in);
return value;
}
return PNM_INVALID;
}
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;
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 (to) {
to->rows = 0L;
to->cols = 0L;
to->rowstride = 0L;
to->colstride = 0L;
to->data = NULL;
}
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);
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);
else
putchar(0);
return EXIT_SUCCESS;
}
0
或者
在矩阵中。)如果需要将其反转为PBM图像,请使用
!(byte & NUMBER)
(val >= limit)
b
在
fopen()
标志),否则可能会损坏输入。
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