目录

  • 一、解码显示png图片
    • 1、思路分析
    • 2、libpng移植
    • 3、zlib移植
    • 4、参考源码包自带的资料
    • 5、学习了解示例代码
    • 6、完整移植好的代码

一、解码显示png图片

1、思路分析

(1)png更像是jpg而不像是bmp

(2)png和jpg都是压缩格式的图片,都是二进制文件,不同之处是压缩和解压缩的算法不同。

(3)通过libjpeg来编解码jpg图片,那么同样有一个libpng用来编解码png图片。

(4)工作思路和顺序:找到并移植并部署libpng,然后查readme和其他文档示例代码
等来使用libpng提供的API来对png图片进行解码,并将解码出来的数据丢到framebuffer中去显示。

2、libpng移植

(1)下载源码包:
官方网址:http://www.libpng.org/pub/png/libpng.html

链接:https://pan.baidu.com/s/1Z7sC2X4B78vYy_ob4PGICQ
提取码:puqn
–来自百度网盘超级会员V5的分享

本篇文章使用libpng-1.6.37.tar.gz

(2)解压、配置、修改Makefile、编译、部署

./configure --host=arm-linux --enable-shared --enable-static --prefix=/opt/libdecode

(3)配置出错,报错信息:configure: error: zlib not installed
分析问题是因为libpng依赖于zlib库,所以要先移植zlib库才可以。

(4)移植了zlib后再过来配置,还是报错,原因是因为没有导出相关环境变量,所以libpng在配置的时候找不到刚才移植的zlib库的库文件和头文件。

(5)解决方案就是使用epport临时性的导出,

#命令行执行,导出zlib所在的路径,使得编译时可以找到zlib
export LDFLAGS="-L/opt/libdecode/lib"
export CFLAGS="-I/opt/libdecode/include"
export CPPFLAGS="-I/opt/libdecode/include"

(6)导出后再次配置就过了,然后编译和安装

(7)make && make install

(8)部署生成的动态链接库:

#修改该项目的主Makefile
LDFLAGS := -ljpeg -lz -lpng -L/opt/libdecode/lib  #指定编译器链接库(根据实际项目手动修改)
#将动态链接库放置到开发板的根文件系统的/usr/lib目录下
#动态链接库所在的目录:/opt/libdecode/lib/
#开发板根文件系统在Ubuntu中的目录:/home/rootfs
cp /opt/libdecode/lib/*so* /home/rootfs/usr/lib/ -rf

3、zlib移植

(1)下载:http://www.zlib.net/,并解压,本篇文章使用zlib-1.2.8.tar.gz

(2)命令行执行:export CC=arm-linux-gcc
进行配置:./configure -shared --prefix=/opt/libdecode

(3)make && make install

(4)make install后/opt/libdecode目录下的lib和include目录下就有了zlib的静态库动态库和头文件了,然后再回去继续libpng的移植。

4、参考源码包自带的资料

(1)README
(2)libpng-manual.txt
(3)example.c 和 pngtest.c

  对于例程中所给的函数想知道在哪libpng的那个文件,可在ubuntu中通过grep命令进行搜索,也可以在windows中通过sourceinsight等软件进行查找!

5、学习了解示例代码

//example.c
#include <png.h>/* The png_jmpbuf() macro, used in error handling, became available in* libpng version 1.0.6.  If you want to be able to run your code with older* versions of libpng, you must define the macro yourself (but only if it* is not already defined by libpng!).*/#ifndef png_jmpbuf
#  define png_jmpbuf(png_ptr) ((png_ptr)->png_jmpbuf)
#endif/* Check to see if a file is a PNG file using png_sig_cmp().  png_sig_cmp()* returns zero if the image is a PNG and nonzero if it isn't a PNG.** The function check_if_png() shown here, but not used, returns nonzero (true)* if the file can be opened and is a PNG, 0 (false) otherwise.** If this call is successful, and you are going to keep the file open,* you should call png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); once* you have created the png_ptr, so that libpng knows your application* has read that many bytes from the start of the file.  Make sure you* don't call png_set_sig_bytes() with more than 8 bytes read or give it* an incorrect number of bytes read, or you will either have read too* many bytes (your fault), or you are telling libpng to read the wrong* number of magic bytes (also your fault).** Many applications already read the first 2 or 4 bytes from the start* of the image to determine the file type, so it would be easiest just* to pass the bytes to png_sig_cmp() or even skip that if you know* you have a PNG file, and call png_set_sig_bytes().*/
#define PNG_BYTES_TO_CHECK 4
int check_if_png(char *file_name, FILE **fp)
{char buf[PNG_BYTES_TO_CHECK];/* Open the prospective PNG file. */if ((*fp = fopen(file_name, "rb")) == NULL)return 0;/* Read in some of the signature bytes */if (fread(buf, 1, PNG_BYTES_TO_CHECK, *fp) != PNG_BYTES_TO_CHECK)return 0;/* Compare the first PNG_BYTES_TO_CHECK bytes of the signature.Return nonzero (true) if they match */return(!png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK));
}/* Read a PNG file.  You may want to return an error code if the read* fails (depending upon the failure).  There are two "prototypes" given* here - one where we are given the filename, and we need to open the* file, and the other where we are given an open file (possibly with* some or all of the magic bytes read - see comments above).*/
#ifdef open_file /* prototype 1 */
void read_png(char *file_name)  /* We need to open the file */
{png_structp png_ptr;png_infop info_ptr;unsigned int sig_read = 0;png_uint_32 width, height;int bit_depth, color_type, interlace_type;FILE *fp;if ((fp = fopen(file_name, "rb")) == NULL)return (ERROR);#else no_open_file /* prototype 2 */
void read_png(FILE *fp, unsigned int sig_read)  /* File is already open */
{png_structp png_ptr;png_infop info_ptr;png_uint_32 width, height;int bit_depth, color_type, interlace_type;
#endif no_open_file /* Only use one prototype! *//* Create and initialize the png_struct with the desired error handler* functions.  If you want to use the default stderr and longjump method,* you can supply NULL for the last three parameters.  We also supply the* the compiler header file version, so that we know if the application* was compiled with a compatible version of the library.  REQUIRED*/png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,png_voidp user_error_ptr, user_error_fn, user_warning_fn);if (png_ptr == NULL){fclose(fp);return (ERROR);}/* Allocate/initialize the memory for image information.  REQUIRED. */info_ptr = png_create_info_struct(png_ptr);if (info_ptr == NULL){fclose(fp);png_destroy_read_struct(&png_ptr, NULL, NULL);return (ERROR);}/* Set error handling if you are using the setjmp/longjmp method (this is* the normal method of doing things with libpng).  REQUIRED unless you* set up your own error handlers in the png_create_read_struct() earlier.*/if (setjmp(png_jmpbuf(png_ptr))){/* Free all of the memory associated with the png_ptr and info_ptr */png_destroy_read_struct(&png_ptr, &info_ptr, NULL);fclose(fp);/* If we get here, we had a problem reading the file */return (ERROR);}/* One of the following I/O initialization methods is REQUIRED */
#ifdef streams /* PNG file I/O method 1 *//* Set up the input control if you are using standard C streams */png_init_io(png_ptr, fp);#else no_streams /* PNG file I/O method 2 *//* If you are using replacement read functions, instead of calling* png_init_io() here you would call:*/png_set_read_fn(png_ptr, (void *)user_io_ptr, user_read_fn);/* where user_io_ptr is a structure you want available to the callbacks */
#endif no_streams /* Use only one I/O method! *//* If we have already read some of the signature */png_set_sig_bytes(png_ptr, sig_read);#ifdef hilevel/** If you have enough memory to read in the entire image at once,* and you need to specify only transforms that can be controlled* with one of the PNG_TRANSFORM_* bits (this presently excludes* quantizing, filling, setting background, and doing gamma* adjustment), then you can read the entire image (including* pixels) into the info structure with this call:*/png_read_png(png_ptr, info_ptr, png_transforms, NULL);#else/* OK, you're doing it the hard way, with the lower-level functions *//* The call to png_read_info() gives us all of the information from the* PNG file before the first IDAT (image data chunk).  REQUIRED*/png_read_info(png_ptr, info_ptr);png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,&interlace_type, NULL, NULL);/* Set up the data transformations you want.  Note that these are all* optional.  Only call them if you want/need them.  Many of the* transformations only work on specific types of images, and many* are mutually exclusive.*//* Tell libpng to strip 16 bit/color files down to 8 bits/color.* Use accurate scaling if it's available, otherwise just chop off the* low byte.*/
#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTEDpng_set_scale_16(png_ptr);
#elsepng_set_strip_16(png_ptr);
#endif/* Strip alpha bytes from the input data without combining with the* background (not recommended).*/png_set_strip_alpha(png_ptr);/* Extract multiple pixels with bit depths of 1, 2, and 4 from a single* byte into separate bytes (useful for paletted and grayscale images).*/png_set_packing(png_ptr);/* Change the order of packed pixels to least significant bit first* (not useful if you are using png_set_packing). */png_set_packswap(png_ptr);/* Expand paletted colors into true RGB triplets */if (color_type == PNG_COLOR_TYPE_PALETTE)png_set_palette_to_rgb(png_ptr);/* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)png_set_expand_gray_1_2_4_to_8(png_ptr);/* Expand paletted or RGB images with transparency to full alpha channels* so the data will be available as RGBA quartets.*/if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))png_set_tRNS_to_alpha(png_ptr);/* Set the background color to draw transparent and alpha images over.* It is possible to set the red, green, and blue components directly* for paletted images instead of supplying a palette index.  Note that* even if the PNG file supplies a background, you are not required to* use it - you should use the (solid) application background if it has one.*/png_color_16 my_background, *image_background;if (png_get_bKGD(png_ptr, info_ptr, &image_background))png_set_background(png_ptr, image_background,PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);elsepng_set_background(png_ptr, &my_background,PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);/* Some suggestions as to how to get a screen gamma value** Note that screen gamma is the display_exponent, which includes* the CRT_exponent and any correction for viewing conditions*/if (/* We have a user-defined screen gamma value */){screen_gamma = user-defined screen_gamma;}/* This is one way that applications share the same screen gamma value */else if ((gamma_str = getenv("SCREEN_GAMMA")) != NULL){screen_gamma = atof(gamma_str);}/* If we don't have another value */else{screen_gamma = PNG_DEFAULT_sRGB;  /* A good guess for a PC monitorin a dimly lit room */screen_gamma = PNG_GAMMA_MAC_18 or 1.0; /* Good guesses for Mac systems */}/* Tell libpng to handle the gamma conversion for you.  The final call* is a good guess for PC generated images, but it should be configurable* by the user at run time by the user.  It is strongly suggested that* your application support gamma correction.*/int intent;if (png_get_sRGB(png_ptr, info_ptr, &intent))png_set_gamma(png_ptr, screen_gamma, PNG_DEFAULT_sRGB);else{double image_gamma;if (png_get_gAMA(png_ptr, info_ptr, &image_gamma))png_set_gamma(png_ptr, screen_gamma, image_gamma);elsepng_set_gamma(png_ptr, screen_gamma, 0.45455);}#ifdef PNG_READ_QUANTIZE_SUPPORTED/* Quantize RGB files down to 8 bit palette or reduce palettes* to the number of colors available on your screen.*/if (color_type & PNG_COLOR_MASK_COLOR){int num_palette;png_colorp palette;/* This reduces the image to the application supplied palette */if (/* We have our own palette */){/* An array of colors to which the image should be quantized */png_color std_color_cube[MAX_SCREEN_COLORS];png_set_quantize(png_ptr, std_color_cube, MAX_SCREEN_COLORS,MAX_SCREEN_COLORS, NULL, 0);}/* This reduces the image to the palette supplied in the file */else if (png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)){png_uint_16p histogram = NULL;png_get_hIST(png_ptr, info_ptr, &histogram);png_set_quantize(png_ptr, palette, num_palette,max_screen_colors, histogram, 0);}}
#endif /* PNG_READ_QUANTIZE_SUPPORTED *//* Invert monochrome files to have 0 as white and 1 as black */png_set_invert_mono(png_ptr);/* If you want to shift the pixel values from the range [0,255] or* [0,65535] to the original [0,7] or [0,31], or whatever range the* colors were originally in:*/if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT)){png_color_8p sig_bit_p;png_get_sBIT(png_ptr, info_ptr, &sig_bit_p);png_set_shift(png_ptr, sig_bit_p);}/* Flip the RGB pixels to BGR (or RGBA to BGRA) */if (color_type & PNG_COLOR_MASK_COLOR)png_set_bgr(png_ptr);/* Swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */png_set_swap_alpha(png_ptr);/* Swap bytes of 16 bit files to least significant byte first */png_set_swap(png_ptr);/* Add filler (or alpha) byte (before/after each RGB triplet) */png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);#ifdef PNG_READ_INTERLACING_SUPPORTED/* Turn on interlace handling.  REQUIRED if you are not using* png_read_image().  To see how to handle interlacing passes,* see the png_read_row() method below:*/number_passes = png_set_interlace_handling(png_ptr);
#elsenumber_passes = 1;
#endif /* PNG_READ_INTERLACING_SUPPORTED *//* Optional call to gamma correct and add the background to the palette* and update info structure.  REQUIRED if you are expecting libpng to* update the palette for you (ie you selected such a transform above).*/png_read_update_info(png_ptr, info_ptr);/* Allocate the memory to hold the image using the fields of info_ptr. *//* The easiest way to read the image: */png_bytep row_pointers[height];/* Clear the pointer array */for (row = 0; row < height; row++)row_pointers[row] = NULL;for (row = 0; row < height; row++)row_pointers[row] = png_malloc(png_ptr, png_get_rowbytes(png_ptr,info_ptr));/* Now it's time to read the image.  One of these methods is REQUIRED */
#ifdef entire /* Read the entire image in one go */png_read_image(png_ptr, row_pointers);#else no_entire /* Read the image one or more scanlines at a time *//* The other way to read images - deal with interlacing: */for (pass = 0; pass < number_passes; pass++){#ifdef single /* Read the image a single row at a time */for (y = 0; y < height; y++){png_read_rows(png_ptr, &row_pointers[y], NULL, 1);}#else no_single /* Read the image several rows at a time */for (y = 0; y < height; y += number_of_rows){#ifdef sparkle /* Read the image using the "sparkle" effect. */png_read_rows(png_ptr, &row_pointers[y], NULL,number_of_rows);
#else no_sparkle /* Read the image using the "rectangle" effect */png_read_rows(png_ptr, NULL, &row_pointers[y],number_of_rows);
#endif no_sparkle /* Use only one of these two methods */}/* If you want to display the image after every pass, do so here */
#endif no_single /* Use only one of these two methods */}
#endif no_entire /* Use only one of these two methods *//* Read rest of file, and get additional chunks in info_ptr - REQUIRED */png_read_end(png_ptr, info_ptr);
#endif hilevel/* At this point you have read the entire image *//* Clean up after the read, and free any memory allocated - REQUIRED */png_destroy_read_struct(&png_ptr, &info_ptr, NULL);/* Close the file */fclose(fp);/* That's it */return (OK);
}

6、完整移植好的代码

创建fb_png.c,将其放在display目录下,修改该目录的子Makefile:

/************************************************************
FileName: fb_png.cAuthor:Mr.Zhang      Version:1.0      Date:2021/11/10Description: This file is used to parse the data of PNG images and load them into FB.
***********************************************************/
#include <stdio.h>
#include <string.h>
#include <config.h>
#include <fb.h>
#include <png.h>
#include <pngstruct.h>//需要去源码包中得到该文件
#include <pnginfo.h>//同上,这几个头文件顺序很重要#define PNG_BYTES_TO_CHECK 8//参数列表:path:要解析的图片的文件名
//函数功能  :判断一个图片文件是否为一个合法的png文件
//返回值     :若是则返回0, 不是返回非0
static int is_png(const char *path)
{char buf[PNG_BYTES_TO_CHECK];FILE *fp = NULL;/* Open the prospective PNG file. */if ((fp = fopen(path, "rb")) == NULL)return -1;/* Read in some of the signature bytes */if (fread(buf, 1, PNG_BYTES_TO_CHECK, fp) != PNG_BYTES_TO_CHECK)return -1;/* Compare the first PNG_BYTES_TO_CHECK bytes of the signature.Return nonzero (true) if they match */return(png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK));
}//参数列表:pPic:描述图片相关信息的结构体指针
//函数功能  :解析一张png格式的图片并进行显示
//返回值     :若失败则返回-1,若成功解码则返回0static int png_analyze(pic_info *pPic)
{FILE *fp = NULL;png_structp png_ptr;png_infop info_ptr;int color_type;png_bytep *row_pointers;unsigned long len = 0;int pos = 0;int i = 0, j = 0;//第一步:打开文件if ((fp = fopen(pPic->pathname, "rb")) == NULL){fprintf(stderr, "fopen %s error.\n", pPic->pathname);return -1;}//第二步:相关数据结构实例化png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);if (png_ptr == NULL){fclose(fp);return -1;}/* Allocate/initialize the memory for image information.  REQUIRED. */info_ptr = png_create_info_struct(png_ptr);if (info_ptr == NULL){fclose(fp);png_destroy_read_struct(&png_ptr, NULL, NULL);return -1;}//第三步:设置错误处理函数if (setjmp(png_jmpbuf(png_ptr))){/* Free all of the memory associated with the png_ptr and info_ptr */png_destroy_read_struct(&png_ptr, &info_ptr, NULL);fclose(fp);/* If we get here, we had a problem reading the file */return -1;}//第四步:将要解码的png图片的文件指针和png解码器绑定起来png_init_io(png_ptr, fp);//第五步:读取png图片的信息png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_ALPHA, NULL);color_type = info_ptr->color_type;DBG("color_type = %d.\n", color_type);pPic->width = info_ptr->width;pPic->height = info_ptr->height;pPic->bpp = info_ptr->pixel_depth;DBG("width = %u,height = %u, bpp = %u", pPic->width, pPic->height, pPic->bpp);// 第六步: 读取真正的图像信息row_pointers = png_get_rows(png_ptr,info_ptr);// 只处理RGB24位真彩色图片,其他格式的图片不管//图像数据移动到我们自己的buf中if(color_type == PNG_COLOR_TYPE_RGB){for(i=0; i<pPic->height; i++){for(j=0; j<3*pPic->width; j+=3){pPic->pData[pos++] = row_pointers[i][j+0];      pPic->pData[pos++] = row_pointers[i][j+1];       pPic->pData[pos++] = row_pointers[i][j+2];       }}}//第七步:进行清理工作png_destroy_read_struct(&png_ptr, &info_ptr, NULL);//第八步:关闭打开的png文件fclose(fp);return 0;
}//封装一个对外使用的jpg显示函数,本函数对外只需要一个jpg图片的
//pathname即可,那些复杂的显示数据处理过程在显示模块内部处理,
//正确显示图片返回0,错误返回-1
int display_png(const char*pathname)
{int ret = -1;pic_info png;//第一步:检测给的图片是不是png图片ret = is_png(pathname);if (ret != 0){  return -1;}//第二步:解析jpg图片png.pathname = pathname;png.pData = rgb_buf;png_analyze(&png);//第三步:显示该图片lcd_display_jpeg_png(0, 0, &png);return 0;
}
void lcd_display_jpeg_png(int x0, int y0, pic_info *pPic)
{const unsigned char *pData = pPic->pData;//指针指向图像数据数组unsigned int x, y, color, p = 0;if ((pPic->bpp != 32) && (pPic->bpp != 24)){fprintf(stderr, "BPP %d is not support.\n", pPic->bpp);}for(y = y0; y < (pPic->height+y0); y++){if(y > HEIGHT)break;for(x = x0; x < (pPic->width+x0); x++){if(x > WIDTH){p += 3;continue;}  color = ((pData[p+2] << 0)|(pData[p+1] << 8)|(pData[p+0] << 16));//得到图片一个像素点的颜色*(pfb + y * WIDTH + x) = color;//将图片像素点的颜色填充到LCD屏幕的某个像素点上p += 3;}}DBG("lcd_display_picture ending.\n");}

注:本资料大部分由朱老师物联网大讲堂课程笔记整理而来并且引用了部分他人博客的内容,如有侵权,联系删除!水平有限,如有错误,欢迎各位在评论区交流。

嵌入式Linux小项目之图片编解码播放器(6)相关推荐

  1. 嵌入式Linux小项目之图片编解码播放器学习导读

      首先欢迎大家阅读本篇文章,在这里我将会为大家简要介绍一下图片编解码播放器系列文章的学习路线.   该小项目共有七篇文章,分别为<嵌入式Linux小项目之图片编解码播放器(1-7)>,这 ...

  2. 嵌入式Linux小项目之图片编解码播放器(1)

    目录 前言 一.项目展示与整体规划 二.环境搭建和基础确认 三.开始动手写代码 四.framebuffer基本操作代码 五.图片显示原理和实践 前言 首先非常感谢大家来阅读我的文章,在这里特别感谢朱老 ...

  3. 嵌入式Linux小项目之图片编解码播放器(5)

    目录 一.jpg图片的显示原理分析 1.认识jpg图片 2.jpg图片如何显示 3.如何解码jpg图片 二.libjpeg介绍及开源库的使用方法 1.libjpeg介绍 2.libjpeg版本及下载资 ...

  4. linux下和嵌入式linux下通过udp接收来自vlc播放器的视频并转发播放

    1.最近需要在linux下基于udp写一个应用程序,实现接收vlc播放器发送的视频流,并将接收到的视频流转发到另一个vlc客户端播放.并将这个应用程序交叉编译到ARM上执行测试通过.主机端ip地址:1 ...

  5. 小项目:网易云音乐播放器

    <!DOCTYPE html> <html>     <head>         <meta charset="utf-8" /> ...

  6. linux 蓝牙 手机遥控器,嵌入式Android小项目之万能手机遥控器详解

    原标题:嵌入式Android小项目之万能手机遥控器详解 在很久很久以前,手机是有红外功能的,后来随着蓝牙技术的成熟,红外逐渐被蓝牙取代,不再是标配了. 红外本身还是有些优点,比如操作简便,成本低.要想 ...

  7. 新一代图片编解码技术在淘宝的应用及落地

    本文回顾淘宝图片发展的历史,阐述了新一代图像编解码格式AVIF在淘宝业务场景中的应用及落地方案,节省流量,为用户提供更好的看图体验. 背景 淘宝图片空间下行链路承载着集团图片的访问请求,包括手淘.飞猪 ...

  8. 嵌入式 linux 启动脚本 编写,[9构建嵌入式linux系统项目-启动脚本的编写.ppt

    [9构建嵌入式linux系统项目-启动脚本的编写 启动脚本 教学回顾 shell语法 管道.重定向 变量 结构性语句 教学内容 启动脚本的写法 教学要求 熟悉掌握启动脚本的写法 shell 函数 在s ...

  9. C语言到嵌入式Linux开发项目指导

    C语言到嵌入式Linux开发项目指导 第一阶段C语言 1.常量与变量,数据类型,数据类型转换,数据输入与输出: 2.C语言运算符,C语言操作符,C语言表达式,表达式优先级: 3.C语言流程控制,分支, ...

最新文章

  1. 大数据druid查询不支持分页_Klin、Druid、ClickHouse核心技术对比
  2. Tinyshop前后台操作基础教程讲解
  3. get clone 出现 fatal: the remote end hung up unexpectedly5 MiB | 892.00 KiB/s 报错信息
  4. 11.乘最多水的容器
  5. PyTorch 1.0 中文官方教程:使用 PyTorch 进行图像风格转换
  6. 别把可视化不当事,看完大屏模板,Excel和PPT直言比不过
  7. 18. jQuery - 尺寸
  8. 结构体变量偏移量及大小计算
  9. 基于matlab的高等数学实验,《基于MATLAB高等数学实验》出版发行
  10. Microsoft Visual Studio 2012 旗舰版 镜像 ISO 官方下载地址 旗舰版 序列号 SN VS2012_ULT_chs.iso
  11. VLAN详解系列:(6)VLAN间路由详解
  12. python写自动化测试脚本常见报错_Python Appium自动化测试 MonkeyScript
  13. mysql 文本 挖掘_GitHub - myseve/dianping_textmining: 大众点评评论文本挖掘,包括点评数据爬取、数据清洗入库、数据分析、评论情感分析等的完整挖掘项目...
  14. SpringBoot+EasyPOI word模板导出,含多张图片
  15. 【数据结构】二叉树遍历
  16. Androidstudio配置git及连接远程库全过程
  17. HTML Hover 的巧用。
  18. WireShark流量分析(中国菜刀,webshell)
  19. 四篇早期人体姿态的论文
  20. ArcGIS Runtime SDK for Android100.9语音路径导航

热门文章

  1. 关于透明度混合blend
  2. 【日期】根据日期求星期
  3. checkv的基本使用
  4. Dell Inspiron 15R - QQ语音时麦克风没有声音的设置办法
  5. 刷题打卡一刷完成 总结
  6. Java 老矣,尚能饭否?
  7. “交通·未来”第17期:深度出行感知,从出行需求预测到出行目的地预测
  8. 大三,请问现在自学Java还来得及吗?
  9. C语言基础知识讲解(入门)
  10. flex布局(弹性盒子三)