libpng 库的源码包中有个 example.c ,里面包含PNG文件读/写的示例代码,参考示例代码和注释(虽然是英文的),可以了解大致的用法。

以下是读取PNG图片的图像数据的代码,使用前还需要按自己的需求补充剩余代码。

  1. #include <png.h>
  2. #define PNG_BYTES_TO_CHECK 4
  3. int load_png_image( const char *filepath, /* 其它参数 */ )
  4. {
  5. FILE *fp;
  6. png_structp png_ptr;
  7. png_infop info_ptr;
  8. png_bytep* row_pointers;
  9. char buf[PNG_BYTES_TO_CHECK];
  10. int w, h, x, y, temp, color_type;
  11. fp = fopen( filepath, "rb" );
  12. if( fp == NULL ) {
  13. return /* 返回值 */;
  14. }
  15. png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 );
  16. info_ptr = png_create_info_struct( png_ptr );
  17. setjmp( png_jmpbuf(png_ptr) );
  18. /* 读取PNG_BYTES_TO_CHECK个字节的数据 */
  19. temp = fread( buf, 1, PNG_BYTES_TO_CHECK, fp );
  20. /* 若读到的数据并没有PNG_BYTES_TO_CHECK个字节 */
  21. if( temp < PNG_BYTES_TO_CHECK ) {
  22. fclose(fp);
  23. png_destroy_read_struct( &png_ptr, &info_ptr, 0);
  24. return /* 返回值 */;
  25. }
  26. /* 检测数据是否为PNG的签名 */
  27. temp = png_sig_cmp( (png_bytep)buf, (png_size_t)0, PNG_BYTES_TO_CHECK );
  28. /* 如果不是PNG的签名,则说明该文件不是PNG文件 */
  29. if( temp != 0 ) {
  30. fclose(fp);
  31. png_destroy_read_struct( &png_ptr, &info_ptr, 0);
  32. return /* 返回值 */;
  33. }
  34. /* 复位文件指针 */
  35. rewind( fp );
  36. /* 开始读文件 */
  37. png_init_io( png_ptr, fp );
  38. /* 读取PNG图片信息 */
  39. png_read_png( png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0 );
  40. /* 获取图像的色彩类型 */
  41. color_type = png_get_color_type( png_ptr, info_ptr );
  42. /* 获取图像的宽高 */
  43. w = png_get_image_width( png_ptr, info_ptr );
  44. h = png_get_image_height( png_ptr, info_ptr );
  45. /* 获取图像的所有行像素数据,row_pointers里边就是rgba数据 */
  46. row_pointers = png_get_rows( png_ptr, info_ptr );
  47. /* 根据不同的色彩类型进行相应处理 */
  48. switch( color_type ) {
  49. case PNG_COLOR_TYPE_RGB_ALPHA:
  50. for( y=0; y<h; ++y ) {
  51. for( x=0; x<w*4; ) {
  52. /* 以下是RGBA数据,需要自己补充代码,保存RGBA数据 */
  53. /* 目标内存 */ = row_pointers[y][x++]; // red
  54. /* 目标内存 */ = row_pointers[y][x++]; // green
  55. /* 目标内存 */ = row_pointers[y][x++]; // blue
  56. /* 目标内存 */ = row_pointers[y][x++]; // alpha
  57. }
  58. }
  59. break;
  60. case PNG_COLOR_TYPE_RGB:
  61. for( y=0; y<h; ++y ) {
  62. for( x=0; x<w*3; ) {
  63. /* 目标内存 */ = row_pointers[y][x++]; // red
  64. /* 目标内存 */ = row_pointers[y][x++]; // green
  65. /* 目标内存 */ = row_pointers[y][x++]; // blue
  66. }
  67. }
  68. break;
  69. /* 其它色彩类型的图像就不读了 */
  70. default:
  71. fclose(fp);
  72. png_destroy_read_struct( &png_ptr, &info_ptr, 0);
  73. return /* 返回值 */;
  74. }
  75. png_destroy_read_struct( &png_ptr, &info_ptr, 0);
  76. return 0;
  77. }

以下是生成png图片文件的代码,也就是照搬了 example.c 里的 write_png() 的代码,稍微翻译了主要的注释。

  1. /* 写入 png 文件 */
  2. void write_png(char *file_name /* , ... 其他图像信息相关的参数 ... */)
  3. {
  4. FILE *fp;
  5. png_structp png_ptr;
  6. png_infop info_ptr;
  7. png_colorp palette;
  8. /* 打开需要写入的文件 */
  9. fp = fopen(file_name, "wb");
  10. if (fp == NULL)
  11. return (ERROR);
  12. /* 创建并初始化 png_struct 及其所需的错误处理函数,如果你想使用默
  13. * 认的 stderr 和 longjump() 方法,你可以将最后三个参数设为 NULL,
  14. * 在使用动态链接库的情况下,我们也会检测函数库版本是否与在编译时
  15. * 使用的版本是否兼容。(必要)
  16. */
  17. png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
  18. png_voidp user_error_ptr, user_error_fn, user_warning_fn);
  19. if (png_ptr == NULL)
  20. {
  21. fclose(fp);
  22. return (ERROR);
  23. }
  24. /* 分配内存并初始化图像信息数据。(必要)*/
  25. info_ptr = png_create_info_struct(png_ptr);
  26. if (info_ptr == NULL)
  27. {
  28. fclose(fp);
  29. png_destroy_write_struct(&png_ptr,  NULL);
  30. return (ERROR);
  31. }
  32. /* 设置错误处理。如果你在调用 png_create_write_struct() 时没
  33. * 有设置错误处理函数,那么这段代码是必须写的。*/
  34. if (setjmp(png_jmpbuf(png_ptr)))
  35. {
  36. /* 如果程序跑到这里了,那么写入文件时出现了问题 */
  37. fclose(fp);
  38. png_destroy_write_struct(&png_ptr, &info_ptr);
  39. return (ERROR);
  40. }
  41. /* 下面的 I/O 初始化函数有一个是必需的 */
  42. #ifdef streams /* I/O 初始化方法 1 */
  43. /* 设置输出控制,如果你使用的是 C 的标准 I/O 流 */
  44. png_init_io(png_ptr, fp);
  45. #else no_streams /* I/O 初始化方法 2 */
  46. /* 如果你是要替换写入函数,而不想调用 png_init_io(),那么需要指定三个参数:
  47. * I/O相关的指针,假设为 user_io_ptr
  48. * 自定义的写入函数,假设为 user_write_fn
  49. * 自定义的I/O刷新函数,假设为 user_IO_flush_function
  50. */
  51. png_set_write_fn(png_ptr, (void *)user_io_ptr, user_write_fn,
  52. user_IO_flush_function);
  53. /* 你需要在某个地方构造一个可用 user_io_ptr 给回调函数使用 */
  54. #endif no_streams /* 只能选择一种初始化方式 */
  55. #ifdef hilevel
  56. /* 这是一种简单的做法,前提是你已经已经有了全部图像信息。
  57. * 你可以使用 | 运算符合并多个 PNG_TRANSFORM 标志到这个
  58. * png_transforms 整型变量中 */
  59. png_write_png(png_ptr, info_ptr, png_transforms, NULL);
  60. #else
  61. /* 这是一种复杂的做法 */
  62. /* (必需)在这里设置图像的信息,宽度、高度的上限是 2^31。
  63. * bit_depth 取值必需是 1、2、4、8 或者 16, 但是可用的值也依赖于 color_type。
  64. * color_type 可选值有: PNG_COLOR_TYPE_GRAY、PNG_COLOR_TYPE_GRAY_ALPHA、
  65. * PNG_COLOR_TYPE_PALETTE、PNG_COLOR_TYPE_RGB、PNG_COLOR_TYPE_RGB_ALPHA。
  66. * interlace 可以是 PNG_INTERLACE_NONE 或 PNG_INTERLACE_ADAM7,
  67. * 而 compression_type 和 filter_type 目前必需是 PNG_COMPRESSION_TYPE_BASE
  68. * 和 and PNG_FILTER_TYPE_BASE。
  69. */
  70. png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_???,
  71. PNG_INTERLACE_????, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
  72. /* 如果要调色板的话,在这里设置调色板,对于索引图像,这个是必需的 */
  73. palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH
  74. * (sizeof (png_color)));
  75. /* ... 设置调色板的颜色集 ... */
  76. png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH);
  77. /* 在销毁 png 结构前的这段期间,你不能释放调色板,因为 png_set_PLTE
  78. * 只是在你分配的调色板上建立引用,并未另外建立副本 */
  79. /* 标注位(sBIT)块 */
  80. png_color_8 sig_bit;
  81. /* 如果我们处理的是灰度图像,则这样 */
  82. sig_bit.gray = true_bit_depth;
  83. /* 否则,我们处理的是彩色图像 */
  84. sig_bit.red = true_red_bit_depth;
  85. sig_bit.green = true_green_bit_depth;
  86. sig_bit.blue = true_blue_bit_depth;
  87. /* 如果这个图像有 alpha 通道 */
  88. sig_bit.alpha = true_alpha_bit_depth;
  89. png_set_sBIT(png_ptr, info_ptr, &sig_bit);
  90. /* (可选)如果你怀疑图像伽马值的正确性 */
  91. png_set_gAMA(png_ptr, info_ptr, gamma);
  92. /* (可选)写注释文本到图像里 */
  93. {
  94. png_text text_ptr[3];
  95. char key0[]="Title";
  96. char text0[]="Mona Lisa";
  97. text_ptr[0].key = key0;
  98. text_ptr[0].text = text0;
  99. text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE;
  100. text_ptr[0].itxt_length = 0;
  101. text_ptr[0].lang = NULL;
  102. text_ptr[0].lang_key = NULL;
  103. char key1[]="Author";
  104. char text1[]="Leonardo DaVinci";
  105. text_ptr[1].key = key1;
  106. text_ptr[1].text = text1;
  107. text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE;
  108. text_ptr[1].itxt_length = 0;
  109. text_ptr[1].lang = NULL;
  110. text_ptr[1].lang_key = NULL;
  111. char key2[]="Description";
  112. char text2[]="<long text>";
  113. text_ptr[2].key = key2;
  114. text_ptr[2].text = text2;
  115. text_ptr[2].compression = PNG_TEXT_COMPRESSION_zTXt;
  116. text_ptr[2].itxt_length = 0;
  117. text_ptr[2].lang = NULL;
  118. text_ptr[2].lang_key = NULL;
  119. png_set_text(write_ptr, write_info_ptr, text_ptr, 3);
  120. }
  121. /* Other optional chunks like cHRM, bKGD, tRNS, tIME, oFFs, pHYs */
  122. /* Note that if sRGB is present the gAMA and cHRM chunks must be ignored
  123. * on read and, if your application chooses to write them, they must
  124. * be written in accordance with the sRGB profile
  125. */
  126. /* 写入文件头部信息(必需) */
  127. png_write_info(png_ptr, info_ptr);
  128. /* If you want, you can write the info in two steps, in case you need to
  129. * write your private chunk ahead of PLTE:
  130. *
  131. *   png_write_info_before_PLTE(write_ptr, write_info_ptr);
  132. *   write_my_chunk();
  133. *   png_write_info(png_ptr, info_ptr);
  134. *
  135. * However, given the level of known- and unknown-chunk support in 1.2.0
  136. * and up, this should no longer be necessary.
  137. */
  138. /* Once we write out the header, the compression type on the text
  139. * chunk gets changed to PNG_TEXT_COMPRESSION_NONE_WR or
  140. * PNG_TEXT_COMPRESSION_zTXt_WR, so it doesn't get written out again
  141. * at the end.
  142. */
  143. /* Set up the transformations you want.  Note that these are
  144. * all optional.  Only call them if you want them.
  145. */
  146. /* Invert monochrome pixels */
  147. png_set_invert_mono(png_ptr);
  148. /* Shift the pixels up to a legal bit depth and fill in
  149. * as appropriate to correctly scale the image.
  150. */
  151. png_set_shift(png_ptr, &sig_bit);
  152. /* Pack pixels into bytes */
  153. png_set_packing(png_ptr);
  154. /* Swap location of alpha bytes from ARGB to RGBA */
  155. png_set_swap_alpha(png_ptr);
  156. /* Get rid of filler (OR ALPHA) bytes, pack XRGB/RGBX/ARGB/RGBA into
  157. * RGB (4 channels -> 3 channels). The second parameter is not used.
  158. */
  159. png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
  160. /* Flip BGR pixels to RGB */
  161. png_set_bgr(png_ptr);
  162. /* Swap bytes of 16-bit files to most significant byte first */
  163. png_set_swap(png_ptr);
  164. /* Swap bits of 1, 2, 4 bit packed pixel formats */
  165. png_set_packswap(png_ptr);
  166. /* 启用交错处理,如果你没有使用 png_write_image() */
  167. if (interlacing != 0)
  168. number_passes = png_set_interlace_handling(png_ptr);
  169. else
  170. number_passes = 1;
  171. /* 这是最简单的图像写入方法。(你或许有不同的内存布局,因此你需要选择一个最适合你的方法)
  172. * 如果你自己不是逐行写入,则需要使用第一种方法。
  173. */
  174. png_uint_32 k, height, width;
  175. /* 在这个示例代码中,"image" 是一个一维的字节数组(每个元素占一个字节空间) */
  176. png_byte image[height*width*bytes_per_pixel];
  177. png_bytep row_pointers[height];
  178. if (height > PNG_UINT_32_MAX/(sizeof (png_bytep)))
  179. png_error (png_ptr, "Image is too tall to process in memory");
  180. /* 将这些像素行指针指向你的 "image" 字节数组中对应的位置,即:指向每行像素的起始处 */
  181. for (k = 0; k < height; k++)
  182. row_pointers[k] = image + k*width*bytes_per_pixel;
  183. /* 必需在下面的输出方式中选择一个 */
  184. #ifdef entire /* 一次调用就将整个图像写进文件 */
  185. png_write_image(png_ptr, row_pointers);
  186. /* 其他的写出方式:交错写出 */
  187. #else no_entire /* 用一个或多个扫描线写出图像数据 */
  188. /* 扫描次数为 1 的是非交错的图像,其它的则是交错图像。*/
  189. for (pass = 0; pass < number_passes; pass++)
  190. {
  191. /* 一次性写几行 */
  192. png_write_rows(png_ptr, &row_pointers[first_row], number_of_rows);
  193. /* 如果你一次性只写一行像素,可以用下面的代码 */
  194. for (y = 0; y < height; y++)
  195. png_write_rows(png_ptr, &row_pointers[y], 1);
  196. }
  197. #endif no_entire /*只能选择一种输出方式 */
  198. /* You can write optional chunks like tEXt, zTXt, and tIME at the end
  199. * as well.  Shouldn't be necessary in 1.2.0 and up as all the public
  200. * chunks are supported and you can use png_set_unknown_chunks() to
  201. * register unknown chunks into the info structure to be written out.
  202. */
  203. /* 必需调用这个函数完成写入文件其余部分 */
  204. png_write_end(png_ptr, info_ptr);
  205. #endif hilevel
  206. /* If you png_malloced a palette, free it here (don't free info_ptr->palette,
  207. * as recommended in versions 1.0.5m and earlier of this example; if
  208. * libpng mallocs info_ptr->palette, libpng will free it).  If you
  209. * allocated it with malloc() instead of png_malloc(), use free() instead
  210. * of png_free().
  211. */
  212. png_free(png_ptr, palette);
  213. palette = NULL;
  214. /* Similarly, if you png_malloced any data that you passed in with
  215. * png_set_something(), such as a hist or trans array, free it here,
  216. * when you can be sure that libpng is through with it.
  217. */
  218. png_free(png_ptr, trans);
  219. trans = NULL;
  220. /* Whenever you use png_free() it is a good idea to set the pointer to
  221. * NULL in case your application inadvertently tries to png_free() it
  222. * again.  When png_free() sees a NULL it returns without action, thus
  223. * avoiding the double-free security problem.
  224. */
  225. /* 写完后清理并释放已分配的内存 */
  226. png_destroy_write_struct(&png_ptr, &info_ptr);
  227. /* 关闭文件 */
  228. fclose(fp);
  229. /* That's it */
  230. return (OK);
  231. }

删掉那些可选的代码,write_png() 的主要代码也就这些:

    1. /* 写入 png 文件 */
    2. void write_png(char *file_name /* , ... 其他图像信息相关的参数 ... */)
    3. {
    4. FILE *fp;
    5. png_structp png_ptr;
    6. png_infop info_ptr;
    7. png_colorp palette;
    8. /* 打开需要写入的文件 */
    9. fp = fopen(file_name, "wb");
    10. if (fp == NULL)
    11. return (ERROR);
    12. /* 创建并初始化 png_struct 及其所需的错误处理函数,如果你想使用默
    13. * 认的 stderr 和 longjump() 方法,你可以将最后三个参数设为 NULL,
    14. * 在使用动态链接库的情况下,我们也会检测函数库版本是否与在编译时
    15. * 使用的版本是否兼容。(必要)
    16. */
    17. png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    18. if (png_ptr == NULL)
    19. {
    20. fclose(fp);
    21. return (ERROR);
    22. }
    23. /* 分配内存并初始化图像信息数据。(必要)*/
    24. info_ptr = png_create_info_struct(png_ptr);
    25. if (info_ptr == NULL)
    26. {
    27. fclose(fp);
    28. png_destroy_write_struct(&png_ptr,  NULL);
    29. return (ERROR);
    30. }
    31. /* 设置错误处理。如果你在调用 png_create_write_struct() 时没
    32. * 有设置错误处理函数,那么这段代码是必须写的。*/
    33. if (setjmp(png_jmpbuf(png_ptr)))
    34. {
    35. /* 如果程序跑到这里了,那么写入文件时出现了问题 */
    36. fclose(fp);
    37. png_destroy_write_struct(&png_ptr, &info_ptr);
    38. return (ERROR);
    39. }
    40. /* 设置输出控制,如果你使用的是 C 的标准 I/O 流 */
    41. png_init_io(png_ptr, fp);
    42. /* 这是一种复杂的做法 */
    43. /* (必需)在这里设置图像的信息,宽度、高度的上限是 2^31。
    44. * bit_depth 取值必需是 1、2、4、8 或者 16, 但是可用的值也依赖于 color_type。
    45. * color_type 可选值有: PNG_COLOR_TYPE_GRAY、PNG_COLOR_TYPE_GRAY_ALPHA、
    46. * PNG_COLOR_TYPE_PALETTE、PNG_COLOR_TYPE_RGB、PNG_COLOR_TYPE_RGB_ALPHA。
    47. * interlace 可以是 PNG_INTERLACE_NONE 或 PNG_INTERLACE_ADAM7,
    48. * 而 compression_type 和 filter_type 目前必需是 PNG_COMPRESSION_TYPE_BASE
    49. * 和 and PNG_FILTER_TYPE_BASE。
    50. */
    51. png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_???,
    52. PNG_INTERLACE_????, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
    53. /* 写入文件头部信息(必需) */
    54. png_write_info(png_ptr, info_ptr);
    55. png_uint_32 k, height, width;
    56. /* 在这个示例代码中,"image" 是一个一维的字节数组(每个元素占一个字节空间) */
    57. png_byte image[height*width*bytes_per_pixel];
    58. png_bytep row_pointers[height];
    59. if (height > PNG_UINT_32_MAX/(sizeof (png_bytep)))
    60. png_error (png_ptr, "Image is too tall to process in memory");
    61. /* 将这些像素行指针指向你的 "image" 字节数组中对应的位置,即:指向每行像素的起始处 */
    62. for (k = 0; k < height; k++)
    63. row_pointers[k] = image + k*width*bytes_per_pixel;
    64. /* 一次调用就将整个图像写进文件 */
    65. png_write_image(png_ptr, row_pointers);
    66. /* 必需调用这个函数完成写入文件其余部分 */
    67. png_write_end(png_ptr, info_ptr);
    68. /* 写完后清理并释放已分配的内存 */
    69. png_destroy_write_struct(&png_ptr, &info_ptr);
    70. /* 关闭文件 */
    71. fclose(fp);
    72. /* That's it */
    73. return (OK);
    74. }

LIBPNG的更多相关文章

  1. pngcrush caught libpng error: Not a PNG file..

    今日真机测试遇到这样的奇葩问题: While reading XXX/XXX.png pngcrush caught libpng error: Not a PNG file.. 替换几次图片都没解决 ...

  2. libpng使用

    自己的实现 unsigned int component(png_const_bytep row, png_uint_32 x, unsigned int c, unsigned int bit_de ...

  3. iOS真机运行 Xcode报错(libpng error: CgBI: unhandled critical chunk)问题已解决;

    Cocos2d-x加载图片资源出现libpng error: CgBI: unhandled critical chunk Xcode7.3 设置Remove Text Metadata From P ...

  4. libpng安装与配置(Win7+VS2010)

    一.下载 libpng:http://libmng.com/pub/png/libpng.html zlib:http://www.zlib.net/ IDE:VS2010 二.编译 将下载的两个zi ...

  5. Visual Studio 2015 下 编译 libpng

    libpng https://github.com/glennrp/libpng zlib https://github.com/madler/zlib/releases https://github ...

  6. Win7 64位 VS2015环境编译Libpng

    第3次编译Libpng依然想不起任何东西,为了不浪费第4次的时间... http://libmng.com/pub/png/libpng.html http://www.zlib.net/ 解压两个压 ...

  7. 【Xamarin报错】libpng warning : iCCP: Not recognizing known sRGB profile that has been edited

    报错: Xamarin Android 编译时发生以下错误: libpng warning : iCCP: Not recognizing known sRGB profile that has be ...

  8. VC++编译libpng

    目录 第1章简介    1 第2章 Visual C++6.0    2 2.1 打开项目    2 2.2 编译宏    3 2.2.1 小结    5 第3章 Visual C++2010     ...

  9. Windows下zlib库和libPng库的编译和使用

    关于zlib库和libpng是干嘛的,我就不说了,度娘和谷歌都能告诉你.这里主要记录下windows下如何利用vs2010编译和使用这两个库. 一.zlib库的编译 首先要下载这个库,这个谷歌和百度也 ...

  10. While reading xxx.png pngcrush caught libpng error: Not a PNG file..

    While reading /XXX/XXX/XXX/img1.png pngcrush caught libpng error: Not a PNG filCould not find file: ...

随机推荐

  1. linux进程、管道和重定向

    1.shell先后使用fork和exec系统调用来执行一个外部命令. 2.在linux系统中,有三个文件会被内核自动打开,分别是stdin.stdout.stderr. 3.进程的属性相关命令: 查看 ...

  2. 转:Jmeter常见问题 (转载) http://www.51testing.com/?uid-128005-action-viewspace-itemid-84094

    说明:这些问答是从网上转载的,自己修改了其中的一些内容,如果大家兴趣,可以将大家在使用Jmeter的时候碰到的问题写下来,我们一起补充到这个问答里面,共同努力完善jmeter的资料. 1.  JMet ...

  3. 让shell 变得容易理解

    1.重建你的语义模型(简单语义模型)2.变量,参数和方法命名3.测试用例4.足够的组块

  4. Brain Network (medium)

    Brain Network (medium) Further research on zombie thought processes yielded interesting results. As ...

  5. poj 1837 Balance 动态规划 (经典好题,很锻炼思维)

    题目大意:给你一个天平,并给出m个刻度,n个砝码,刻度的绝对值代表距离平衡点的位置,并给出每个砝码的重量.达到平衡状态的方法有几种. 题目思路:首先我们先要明确dp数组的作用,dp[i][j]中,i为 ...

  6. app每个页面都有一个相同的浮层控件 实现思路

    可以创建一个window,设置其windowLevel为alert;

  7. vim的复制粘贴小结(转)

    原文地址:http://lsong17.spaces.live.com/blog/cns!556C21919D77FB59!603.entry 内容: 用vim这么久 了,始终也不知道怎么在vim中使 ...

  8. python 打印类的属性、方法

    打印变量db的类(class):[root@fuel ~]# pythonPython 2.6.6 (r266:84292, Jan 22 2014, 09:42:36)[GCC 4.4.7 2012 ...

  9. 自动获取访客QQ

    http://jerring.cn/bizqq/index.html http://www.oicqzone.com/qqjiqiao/2015072322139.html

  10. localStorage、sessionStorages 使用

    html5中的Web Storage包括了两种存储方式:sessionStorage和localStorage.sessionStorage用于本地存储一个会话(session)中的数据,这些数据只有 ...