Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3835797
  • 博文数量: 146
  • 博客积分: 3918
  • 博客等级: 少校
  • 技术积分: 8584
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-17 13:52
个人简介

个人微薄: weibo.com/manuscola

文章分类

全部博文(146)

文章存档

2016年(3)

2015年(2)

2014年(5)

2013年(42)

2012年(31)

2011年(58)

2010年(5)

分类: C/C++

2011-12-22 22:24:45

    我知道C语言的能力很强,但是对于图形图像类的C语言实现很是好奇,觉得很难。我读书的时候,用C语言画过BMP格式的图像,因为没有任何的压缩,所以图像的数据量比较大,基本上就是RGB 字节流。今天看了Banu blog中的博文 Drawing circles,想起了以前用C语言进行BMP图像的处理。
     写这篇博客,是给自己枯燥的学习带来一些乐趣,C语言也可以干些比较有有趣的事情。代码基本上是照搬Banu,只是,文章画的图像是灰度图像,不够靓丽,我将代码改成了生成彩色图像。不管怎么说,光荣属于Banu,我只是仰望前辈的学习者。

    生成一个文件,后缀为ppm,Linux下的GIMP自然可以解析这种格式。ppm格式和BMP很像,基本属于傻瓜型图像格式,每个像素就是RGB三个字节表示,当然为了声明文件类型,会有文件头。看下WIKI中的介绍。    
Magic NumberTypeEncoding
P1Portable bitmapASCII
P2Portable graymapASCII
P3Portable pixmapASCII
P4Portable bitmapBinary
P5Portable graymapBinary
P6Portable pixmapBinary

P3 
# The P3 means colors are in ASCII, then 3 columns and 2 rows, 
# then 255 for max color, then RGB triplets
 3 2
 255
 255 0 0       0 255 0        0 0 255 
255 255 0      255 255 255    0 0 0

我们看到了上面虚框中的文件内容,画出来就是下面这个彩色图像。当然,下面的图用一个比较大的正方形来表示一个像素点(255,0,0)表示红色,所以代表第一个像素点的正方形是红色的,依次类推。

OK,下面看下代码:
  1. static void
  2. image_save (const Image *image,
  3.             const char *filename)
  4. {
  5.   FILE *out;

  6.   out = fopen (filename, "wb");
  7.   if (!out)
  8.     return;

  9.   fprintf (out, "P6\n");
  10.   fprintf (out, "%zu %zu\n", image->width, image->height);
  11.   fprintf (out, "255\n");

  12.   fwrite (image->data, 1, image->width * image->height*3, out);

  13.   fclose (out);
  14. }

P6告诉文件解析程序(如GIMP)我是PPM格式的文件(Portable pixmap)。
第二行告诉文件的长和高。
下面是完整的代码,再次强调,荣耀和版权都属于Banu,我只是仰望Banu的学习者
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <math.h>

  5. typedef struct {
  6.   size_t width;
  7.   size_t height;
  8.   unsigned char *data;
  9. } Image;

  10. static Image *
  11. image_new (size_t width,
  12.            size_t height)
  13. {
  14.   Image *image;

  15.   image = malloc (sizeof *image);
  16.   image->width = width;
  17.   image->height = height;
  18.   image->data = malloc (width * height*3);

  19.   return image;
  20. }

  21. static void
  22. image_free (Image *image)
  23. {
  24.   free (image->data);
  25.   free (image);
  26. }

  27. static void
  28. image_fill (Image *image,
  29.             unsigned char value)
  30. {
  31.   memset (image->data, value, image->width * image->height*3);
  32. }

  33. /**
  34.  * image_set_pixel:
  35.  *
  36.  * Sets a pixel passed in signed (x, y) coordinates, where (0,0) is at
  37.  * the center of the image.
  38.  **/
  39. static void
  40. image_set_pixel (Image *image,
  41.                  ssize_t x,
  42.                  ssize_t y,
  43.                  unsigned char Rvalue,
  44.                  unsigned char Gvalue,
  45.                  unsigned char Bvalue)
  46. {
  47.   size_t tx, ty;
  48.   unsigned char *p;

  49.   tx = (image->width / 2) + x;
  50.   ty = (image->height / 2) + y;

  51.   p = image->data + (ty * image->width*3) + tx*3;

  52.   *p = Rvalue;
  53.     *(p+1) = Gvalue;
  54.     *(p+2) = Bvalue;
  55. }

  56. static void
  57. image_save (const Image *image,
  58.             const char *filename)
  59. {
  60.   FILE *out;

  61.   out = fopen (filename, "wb");
  62.   if (!out)
  63.     return;

  64.   fprintf (out, "P6\n");
  65.   fprintf (out, "%zu %zu\n", image->width, image->height);
  66.   fprintf (out, "255\n");

  67.   fwrite (image->data, 1, image->width * image->height*3, out);

  68.   fclose (out);
  69. }

  70. static void
  71. draw_circle (Image *image,
  72.              int radius,
  73.              unsigned char Rvalue,
  74.                          unsigned char Gvalue,
  75.                          unsigned char Bvalue)
  76. {
  77.   int x, y;

  78.   for (y = -radius; y <= radius; y++)
  79.     for (x = -radius; x <= radius; x++)
  80.       if ((x * x) + (y * y) <= (radius * radius))
  81.         image_set_pixel (image, x, y, Rvalue,Gvalue,Bvalue);
  82. }

  83. int
  84. main (int argc, char *argv[])
  85. {
  86.   Image *image;

  87.   image = image_new (600, 600);

  88.   image_fill (image, 0xff);
  89.   draw_circle (image, 200, 0x00,0x00,0xff);
  90.   image_save (image, "circle.ppm");

  91.   image_free (image);

  92.   return 0;
  93. }
就可以在本地查看一个蓝色的圆图像。下图格式是jpg格式的,我用QQ截图工具生成的。
由于CU不支持ppm格式的图像,所以只能传jpg了。


参考文献:
1 Banu 博文 Drawing Circles
2 Wiki

阅读(23531) | 评论(4) | 转发(5) |
给主人留下些什么吧!~~

Debroon2019-11-12 23:33:26

博主,可不可以发一下 Banu 的博客链接???

gjf05_052012-06-02 10:02:12

太神奇了,原来在当前目录下生成了一个circle.ppm的图片呀。

gjf05_052012-06-01 19:12:04

在fedora下gcc 通过,但是执行./文件名 的时候什么都没有显示。

gjf05_052012-05-31 18:00:51

学习。