精华内容
参与话题
问答
  • Linux编程介绍

    千次阅读 2012-10-15 09:50:40
    1.什么是  在windows平台和linux平台下都大量存在着。 ... 本质上来说是一种可执行代码的二进制形式,可以被操作系统载入内存... linux下的有两种:静态和共享(动态)。  二者的不同点在于代

    1.什么是库

      在windows平台和linux平台下都大量存在着库。

      本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。

      由于windows和linux的本质不同,因此二者库的二进制是不兼容的。

      本文仅限于介绍linux下的库。

    2.库的种类

      linux下的库有两种:静态库和共享库(动态库)。

      二者的不同点在于代码被载入的时刻不同。

      静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。

      共享库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。

    3.库存在的意义

      库是别人写好的现有的,成熟的,可以复用的代码,你可以使用但要记得遵守许可协议

      现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。

      共享库的好处是,不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。

    4.在linux下库文件是如何产生的

      静态库的后缀是.a,它的产生分两步

      Step 1.由源文件编译生成一堆.o,每个.o里都包含这个编译单元的符号表

      Step 2.ar命令将很多.o转换成.a,成文静态库

      动态库的后缀是.so,它由gcc加特定参数编译产生。

      例如:

      $ gcc -fPIC -c *.c $ gcc -shared -Wl,-soname, libfoo.so.1 -o libfoo.so.1.0 *.

    5.库文件是如何命名的,有没有什么规范

      在linux下,库文件一般放在/usr/lib /lib下,

      静态库的名字一般为libxxxx.a,其中xxxx是该lib的名称

      动态库的名字一般为libxxxx.so.major.minor,xxxx是该lib的名称,major是主版本号, minor是副版本号

    6.如何知道一个可执行程序依赖哪些库

      ldd命令可以查看一个可执行程序依赖的共享库,

      例如# ldd /bin/lnlibc.so.6

      => /lib/libc.so.6 (0×40021000)/lib/ld-linux.so.2

      => /lib/ld- linux.so.2 (0×40000000)

      可以看到ln命令依赖于libc库和ld-linux库

    7.可执行程序在执行的时候如何定位共享库文件

      当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径

      此时就需要系统动态载入器(dynamic linker/loader)

      对于elf格式的可执行程序,是由ld-linux.so*来完成的,它先后搜索elf文件的 DT_RPATH段—环境变量LD_LIBRARY_PATH—/etc/ld.so.cache文件列表—/lib/,/usr/lib目录找到库文件后将其载入内存

    8.在新安装一个库之后如何让系统能够找到他

      如果安装在/lib或者/usr/lib下,那么ld默认能够找到,无需其他操作。

      如果安装在其他目录,需要将其添加到/etc/ld.so.cache文件中,步骤如下

      1.编辑/etc/ld.so.conf文件,加入库文件所在目录的路径

      2.运行ldconfig,该命令会重建/etc/ld.so.cache文件


     

      我们通常把一些公用函数制作成函数库,供其它程序使用。函数库分为静态库和动态库两种。静态库在程序编译时会被连接到目标代码中,程序运行时将 不再需要该静态库。动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。本文主要通过举例来说明 在Linux中如何创建静态库和动态库,以及使用它们。在创建函数库前,我们先来准备举例用的源程序,并将函数库的源程序编译成.o文件。

    第1步:编辑得到举例的程序--hello.h、hello.c和main.c;

      hello.h(见程序1)为该函数库的头文件。

      hello.c(见程序2)是函数库的源程序,其中包含公用函数hello,该函数将在屏幕上输出"Hello XXX!"。

      main.c(见程序3)为测试库文件的主程序,在主程序中调用了公用函数hello。

      程序1: hello.h

      #ifndef HELLO_H

      #define HELLO_H

      void hello(const char *name);

      #endif //HELLO_H

      程序2: hello.c

      #include

      void hello(const char *name)

      {

      printf("Hello %s!\n", name);

      }

      程序3: main.c

      #include "hello.h"

      int main()

      {

      hello("everyone");

      return 0;

      }


    第2步:将hello.c编译成.o文件;

      无论静态库,还是动态库,都是由.o文件创建的。因此,我们必须将源程序hello.c通过gcc先编译成.o文件。

      在系统提示符下键入以下命令得到hello.o文件。

      # gcc -c hello.c

      #

      (注1:本文不介绍各命令使用和其参数功能,若希望详细了解它们,请参考其他文档。)

      (注2:首字符"#"是系统提示符,不需要键入,下文相同。)

      我们运行ls命令看看是否生存了hello.o文件。

      # ls

      hello.c hello.h hello.o main.c

      #

      (注3:首字符不是"#"为系统运行结果,下文相同。)

      在ls命令结果中,我们看到了hello.o文件,本步操作完成。

      下面我们先来看看如何创建静态库,以及使用它。

    第3步:由.o文件创建静态库;

      静态库文件名的命名规范是以lib为前缀,紧接着跟静态库名,扩展名为.a。例如:我们将创建的静态库名为myhello,则静态库文件名就是libmyhello.a。在创建和使用静态库时,需要注意这点。创建静态库用ar命令。

      在系统提示符下键入以下命令将创建静态库文件libmyhello.a。

      # ar cr libmyhello.a hello.o

      #

      我们同样运行ls命令查看结果:

      # ls

      hello.c hello.h hello.o libmyhello.a main.c

      #

      ls命令结果中有libmyhello.a。

     第4步:在程序中使用静态库;

      静态库制作完了,如何使用它内部的函数呢?只需要在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用gcc命令生成目标文件 时指明静态库名,gcc将会从静态库中将公用函数连接到目标文件中。注意,gcc会在静态库名前加上前缀lib,然后追加扩展名.a得到的静态库文件名来 查找静态库文件。

      在程序3:main.c中,我们包含了静态库的头文件hello.h,然后在主程序main中直接调用公用函数hello。下面先生成目标程序hello,然后运行hello程序看看结果如何。

      # gcc -o hello main.c -L. -lmyhello

      # ./hello

      Hello everyone!

      #

      我们删除静态库文件试试公用函数hello是否真的连接到目标文件 hello中了。

      # rm libmyhello.a

      rm: remove regular file `libmyhello.a'? y

      # ./hello

      Hello everyone!

      #

      程序照常运行,静态库中的公用函数已经连接到目标文件中了。

      我们继续看看如何在Linux中创建动态库。我们还是从.o文件开始。

    第5步:由.o文件创建动态库文件;

      动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀lib,但其文件扩展名为.so。例如:我们将创建的动态库名为myhello,则动态库文件名就是libmyhello.so。用gcc来创建动态库。

      在系统提示符下键入以下命令得到动态库文件libmyhello.so。

      # gcc -shared -fPCI -o libmyhello.so hello.o

      #

      我们照样使用ls命令看看动态库文件是否生成。

      # ls

      hello.c hello.h hello.o libmyhello.so main.c

      #

    第6步:在程序中使用动态库;

      在程序中使用动态库和使用静态库完全一样,也是在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明动态库名进行编译。我们先运行gcc命令生成目标文件,再运行它看看结果。

      # gcc -o hello main.c -L. -lmyhello

      # ./hello

      ./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory

      #

      哦!出错了。快看看错误提示,原来是找不到动态库文件libmyhello.so。程序在运行时,会在/usr/lib和/lib等目录中查找 需要的动态库文件。若找到,则载入动态库,否则将提示类似上述错误而终止程序运行。我们将文件libmyhello.so复制到目录/usr/lib中, 再试试。

      # mv libmyhello.so /usr/lib

      # ./hello

      ./hello: error while loading shared libraries: /usr/lib/libhello.so: cannot restore segment prot after reloc: Permission denied

      由于SELinux引起,

      # chcon -t texrel_shlib_t /usr/lib/libhello.so

      # ./hello

      Hello everyone!

      #

      成功了。这也进一步说明了动态库在程序运行时是需要的。

      我们回过头看看,发现使用静态库和使用动态库编译成目标程序使用的gcc命令完全一样,那当静态库和动态库同名时,gcc命令会使用哪个库文件呢?抱着对问题必究到底的心情,来试试看。

      先删除 除.c和.h外的 所有文件,恢复成我们刚刚编辑完举例程序状态。

      # rm -f hello hello.o /usr/lib/libmyhello.so

      # ls

      hello.c hello.h main.c

      #

      在来创建静态库文件libmyhello.a和动态库文件libmyhello.so。

      # gcc -c hello.c

      # ar cr libmyhello.a hello.o

      # gcc -shared -fPCI -o libmyhello.so hello.o

      # ls

      hello.c hello.h hello.o libmyhello.a libmyhello.so main.c

      #

      通过上述最后一条ls命令,可以发现静态库文件libmyhello.a和动态库文件libmyhello.so都已经生成,并都在当前目录中。然后,我们运行gcc命令来使用函数库myhello生成目标文件hello,并运行程序 hello。

      # gcc -o hello main.c -L. -lmyhello

      # ./hello

      ./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory

      #

      从程序hello运行的结果中很容易知道,当静态库和动态库同名时, gcc命令将优先使用动态库。


      基本概念

      库有动态与静态两种,动态通常用.so为后缀,静态用.a为后缀。

      例如:libhello.so libhello.a 为了在同一系统中使用不同版本的库,可以在库文件名后加上版本号为后缀,例如: libhello.so.1.0,由于程序连接默认以.so为文件后缀名。所以为了使用这些库,通常使用建立符号连接的方式。

      ln -s libhello.so.1.0 libhello.so.1

      ln -s libhello.so.1 libhello.so

      1、使用库

      当要使用静态的程序库时,连接器会找出程序所需的函数,然后将它们拷贝到执行文件,由于这种拷贝是完整的,所以一旦连接成功,静态程序库也就不 再需要了。然 而,对动态库而言,就不是这样。动态库会在执行程序内留下一个标记指明当程序执行时,首先必须载入这个库。由于动态库节省空间,linux下进行连接的缺 省操作是首先连接动态库,也就是说,如果同时存在静态和动态库,不特别指定的话,将与动态库相连接。 现在假设有一个叫hello的程序开发包,它提供一个静态库libhello.a 一个动态库libhello.so,一个头文件hello.h,头文件中提供sayhello()这个函数 /* hello.h */ void sayhello(); 另外还有一些说明文档。

      这一个典型的程序开发包结构 与动态库连接 linux默认的就是与动态库连接,下面这段程序testlib.c使用hello库中的sayhello()函数

      /*testlib.c*/

      #include

      #include

      int main()

      {

      sayhello();

      return 0;

      }

     使用如下命令进行编译 $gcc -c testlib.c -o testlib.o

      用如下命令连接: $gcc testlib.o -lhello -o testlib

      连接时要注意,假设libhello.o 和libhello.a都在缺省的库搜索路径下/usr/lib下,如果在其它位置要加上-L参数 与与静态库连接麻烦一些,主要是参数问题。还是上面的例子:

      $gcc testlib.o -o testlib -WI,-Bstatic -lhello

      注:这个特别的"-WI,-Bstatic"参数,实际上是传给了连接器ld。指示它与静态库连接,如果系统中只有静态库当然就不需要这个参数 了。 如果要和多个库相连接,而每个库的连接方式不一样,比如上面的程序既要和libhello进行静态连接,又要和libbye进行动态连接,其命令应为:

      $gcc testlib.o -o testlib -WI,-Bstatic -lhello -WI,-Bdynamic -lbye

      2、动态库的路径问题 为了让执行程序顺利找到动态库,有三种方法:

      (1)把库拷贝到/usr/lib和/lib目录下。

      (2)在LD_LIBRARY_PATH环境变量中加上库所在路径。

      例如动态库libhello.so在/home/ting/lib目录下,以bash为例,使用命令:

      $export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ting/lib

      (3) 修改/etc/ld.so.conf文件,把库所在的路径加到文件末尾,并执行ldconfig刷新。这样,加入的目录下的所有库文件都可见。

      3、查看库中的符号

      有时候可能需要查看一个库中到底有哪些函数,nm命令可以打印出库中的涉及到的所有符号。库既可以是静态的也可以是动态的。nm列出的符号有很多,常见的有三种:

      一种是在库中被调用,但并没有在库中定义(表明需要其他库支持),用U表示;

      一种是库中定义的函数,用T表示,这是最常见的;

      另外一种是所谓的“弱 态”符号,它们虽然在库中被定义,但是可能被其他库中的同名符号覆盖,用W表示。

      例如,假设开发者希望知道上文提到的hello库中是否定义了 printf():

      $nm libhello.so |grep printf U

      其中printf U表示符号printf被引用,但是并没有在函数内定义,由此可以推断,要正常使用hello库,必须有其它库支持,再使用ldd命令查看hello依赖于哪些库:

      $ldd hello libc.so.6=>/lib/libc.so.6(0x400la000) /lib/ld-linux.so.2=>/lib/ld-linux.so.2 (0x40000000)

      从上面的结果可以继续查看printf最终在哪里被定义,有兴趣可以go on

      4、生成库

      第一步要把源代码编绎成目标代码。

      以下面的代码为例,生成上面用到的hello库:

      /* hello.c */

      #include

      void sayhello()

      {

      printf("hello,world ");

      }

      用gcc编绎该文件,在编绎时可以使用任何全法的编绎参数,例如-g加入调试代码等: gcc -c hello.c -o hello.o

      (1)连接成静态库 连接成静态库使用ar命令,其实ar是archive的意思

      $ar cqs libhello.a hello.o

      (2)连接成动态库 生成动态库用gcc来完成,由于可能存在多个版本,因此通常指定版本号:

      $gcc -shared -Wl,-soname,libhello.so.1 -o libhello.so.1.0 hello.o

      另外再建立两个符号连接:

      $ln -s libhello.so.1.0 libhello.so.1

      $ln -s libhello.so.1 libhello.so

      这样一个libhello的动态连接库就生成了。最重要的是传gcc -shared 参数使其生成是动态库而不是普通执行程序。 -Wl 表示后面的参数也就是-soname,libhello.so.1直接传给连接器ld进行处理。实际上,每一个库都有一个soname,当连接器发现它正 在查找的程序库中有这样一个名称,连接器便会将soname嵌入连结中的二进制文件内,而不是它正在运行的实际文件名,在程序执行期间,程序会查找拥有 soname名字的文件,而不是库的文件名,换句话说,soname是库的区分标志。 这样做的目的主要是允许系统中多个版本的库文件共存,习惯上在命名库文件的时候通常与soname相同 libxxxx.so.major.minor 其中,xxxx是库的名字,major是主版本号,minor 是次版本号

    展开全文
  • linux下libpng的安装以及使用

    万次阅读 2018-08-31 19:49:03
    前几天手里的智能锁项目 , 收到产品的建议(命令)说 , 就是人脸识别成功的时候 , 不要只显示摄像头捕捉到的图像 , 要弄个酷炫一点的背景, 背景里图片中间有个圆圈, 人脸就放到圆圈里也就是类似这样。...

    前几天手里的智能锁项目 , 收到产品的建议(命令)说 , 就是人脸识别成功的时候 , 不要只显示摄像头捕捉到的图像 , 要弄个酷炫一点的背景, 背景里图片中间有个圆圈, 人脸就放到圆圈里也就是类似这样。。

    当然, 这是我思考了好几个小时的结果, 开始想不明白要怎么实现,其实想通了也很简单, 三个步骤

    A 把背景图像的RGB读出来 out_buf

    B 把摄像头采集到的图像读出来(分辨率和背景图像一样) , in_buf

    C 如果out_buf当前像素点是黑色不变, 否则填充in_buf到out_buf

    有了思路了 ,  就要付诸行动  , 花了好几个小时来了解了一下PNG图片的格式 , 又找到一个资料比较全的库 , 就各种找资料 , 把png的读写跑了一遍 ,  特意记录一下

    关于PNG文件格式这里不讨论 , 有兴趣的朋友直接百度"PNG文件格式"会得到自己想要的答案


    一: 安装PNG库文件

    因为PNG文件需要用到zlib进行编解码,  所以用到libpng的同时也需要zlib加密解密

    本文使用的是libpng版本和zlib版本如下:

    下载地址:

    https://sourceforge.net/projects/libpng/files/

    下载完之后如下命令顺序执行:

    libpng:
    #解压
    tar -xzvf libpng-1.6.26.tar.gz && cd libpng-1.6.26
    #安装
    ./configure
    make check
    sudo make install
    sudo ldconfig
    
    zlib:
    #解压:
    tar -xf zlib-1.2.11.tar.xz && cd zlib-1.2.11
    #安装
    ./configure 
    sudo make install
    sudo make
    

    安装成功如下图 :

    运行pkg-config libpng16 zlib --libs --cflags得到正确的头文件和链接库的位置

    二 : 使用libpng

    libpng有一套自己的流程如下:

    1:初始化png_structp结构体 (用于和打开的文件指针绑定)

    2:初始化png_infop结构体 (此结构体包含各种图片信息)

    3:设置错误返回点 (如果是Libpng内部出现错误, 则会跳到你设置的错误返回点处执行)

    4:绑定文件IO流到png_structp结构体 (打开的文件和png_structp结构体实例进行绑定)

    5:写入需要生成PNG图像的信息, 信息内容包括尺寸, 位深度,颜色类型,是否交错, 换行等等,按照格式逐个填写就行了

    6:写入实际的RGB数据到绑定好的png_structp结构体

    7:写入文件尾部信息

    8:清理工作 (释放申请的内存, 注销png_structp结构体)

    代码如下 :

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <png.h>
    
    
    #define PNG_BYTES_TO_CHECK	8
    #define HAVE_ALPHA			1
    #define NOT_HAVE_ALPHA		0
    
    typedef struct _pic_data pic_data;
    struct _pic_data {
    	int width, height; 	//长宽
    	int bit_depth; 	   	//位深度
    	int alpha_flag;		//是否有透明通道
    	unsigned char *rgba;//实际rgb数据
    };
    
    int check_is_png(FILE **fp, const char *filename) //检查是否png文件
    {
    	char checkheader[PNG_BYTES_TO_CHECK]; //查询是否png头
    	*fp = fopen(filename, "rb");
    	if (*fp == NULL) {
    		printf("open failed ...1\n");
    		return 0;
    	}
    	if (fread(checkheader, 1, PNG_BYTES_TO_CHECK, *fp) != PNG_BYTES_TO_CHECK) //读取png文件长度错误直接退出
    		return 0;
    	return png_sig_cmp(checkheader, 0, PNG_BYTES_TO_CHECK); //0正确, 非0错误
    }
    
    int decode_png(const char *filename, pic_data *out) //取出png文件中的rgb数据
    {
    	png_structp png_ptr; //png文件句柄
    	png_infop	info_ptr;//png图像信息句柄
    	int ret;
    	FILE *fp;
    	if (check_is_png(&fp, filename) != 0) {
    		printf("file is not png ...\n");
    		return -1;
    	}
    	printf("launcher[%s] ...\n", PNG_LIBPNG_VER_STRING); //打印当前libpng版本号
    
    	//1: 初始化libpng的数据结构 :png_ptr, info_ptr
    	png_ptr  = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 
    	info_ptr = png_create_info_struct(png_ptr);
    
    	//2: 设置错误的返回点
    	setjmp(png_jmpbuf(png_ptr));
    	rewind(fp); //等价fseek(fp, 0, SEEK_SET);
    
    	//3: 把png结构体和文件流io进行绑定 
    	png_init_io(png_ptr, fp);
    	//4:读取png文件信息以及强转转换成RGBA:8888数据格式
    	png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0); //读取文件信息
    	int channels, color_type; 
    	channels 	= png_get_channels(png_ptr, info_ptr); //通道数量
    	color_type 	= png_get_color_type(png_ptr, info_ptr);//颜色类型
    	out->bit_depth = png_get_bit_depth(png_ptr, info_ptr);//位深度	
    	out->width 	 = png_get_image_width(png_ptr, info_ptr);//宽
    	out->height  = png_get_image_height(png_ptr, info_ptr);//高
    	
    	//if(color_type == PNG_COLOR_TYPE_PALETTE)
    	//	png_set_palette_to_rgb(png_ptr);//要求转换索引颜色到RGB
    	//if(color_type == PNG_COLOR_TYPE_GRAY && out->bit_depth < 8)
    	//	png_set_expand_gray_1_2_4_to_8(png_ptr);//要求位深度强制8bit
    	//if(out->bit_depth == 16)
    	//	png_set_strip_16(png_ptr);//要求位深度强制8bit
    	//if(png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS))
    	//	png_set_tRNS_to_alpha(png_ptr);
    	//if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
    	//	png_set_gray_to_rgb(png_ptr);//灰度必须转换成RG
    	printf("channels = %d color_type = %d bit_depth = %d width = %d height = %d ...\n",
    			channels, color_type, out->bit_depth, out->width, out->height);
    
    	int i, j, k;
    	int size, pos = 0;
    	int temp;
    	
    	//5: 读取实际的rgb数据
    	png_bytepp row_pointers; //实际存储rgb数据的buf
    	row_pointers = png_get_rows(png_ptr, info_ptr); //也可以分别每一行获取png_get_rowbytes();
    	size = out->width * out->height; //申请内存先计算空间
    	if (channels == 4 || color_type == PNG_COLOR_TYPE_RGB_ALPHA) { //判断是24位还是32位
    		out->alpha_flag = HAVE_ALPHA; //记录是否有透明通道
    		size *= (sizeof(unsigned char) * 4); //size = out->width * out->height * channel
    		out->rgba = (png_bytep)malloc(size);
    		if (NULL == out->rgba) {
    			printf("malloc rgba faile ...\n");
    			png_destroy_read_struct(&png_ptr, &info_ptr, 0);
    			fclose(fp);
    			return -1;
    		}
    		//从row_pointers里读出实际的rgb数据出来
    		temp = channels - 1;
    		for (i = 0; i < out->height; i++) 
    			for (j = 0; j < out->width * 4; j += 4) 
    				for (k = temp; k >= 0; k--)
    					out->rgba[pos++] = row_pointers[i][j + k];
    	} else if (channels == 3 || color_type == PNG_COLOR_TYPE_RGB) { //判断颜色深度是24位还是32位
    		out->alpha_flag = NOT_HAVE_ALPHA;
    		size *= (sizeof(unsigned char) * 3);
    		out->rgba = (png_bytep)malloc(size);
    		if (NULL == out->rgba) {
    			printf("malloc rgba faile ...\n");
    			png_destroy_read_struct(&png_ptr, &info_ptr, 0);
    			fclose(fp);
    			return -1;
    		}
    		//从row_pointers里读出实际的rgb数据
    		temp = (3 * out->width);
    		for (i = 0; i < out->height; i ++) {
    			for (j = 0; j < temp; j += 3) {
    				out->rgba[pos++] = row_pointers[i][j+2];
    				out->rgba[pos++] = row_pointers[i][j+1];
    				out->rgba[pos++] = row_pointers[i][j+0];
    			}
    		}
    	} else return -1; 
    	//6:销毁内存
    	png_destroy_read_struct(&png_ptr, &info_ptr, 0);
    	fclose(fp);
    	//此时, 我们的out->rgba里面已经存储有实际的rgb数据了
    	//处理完成以后free(out->rgba)
    	return 0;
    }
    
    int RotationRight90(unsigned char * src, int srcW, int srcH, int channel) //顺时针旋转90度
    {
    	unsigned char * tempSrc = NULL; //临时的buf用来记录原始的图像(未旋转之前的图像)
    	int mSize = srcW * srcH * sizeof(char) * channel;
    	int i = 0;
    	int j = 0;
    	int k = 0;
    	int l = 3;
    	int desW = 0;
    	int desH = 0;
    
    	desW = srcH;
    	desH = srcW;
    
    	tempSrc = (unsigned char *)malloc(sizeof(char) * srcW * srcH * channel);
    	memcpy(tempSrc, src, mSize); //拷贝原始图像至tempbuf
    	for(i = 0; i < desH; i ++)
    	{
    		for(j = 0; j < desW; j ++)
    		{
    			for(k = 0; k < channel; k ++)
    			{
    				src[(i * desW + j) * channel + k] = tempSrc[((srcH - 1 - j) * srcW + i) * channel + k]; //替换像素
    			}
    		}
    	}
    	free(tempSrc);
    	return 0;
    }
    
    int write_png_file(const char *filename , pic_data *out) //生成一个新的png图像
    {
    	png_structp png_ptr;
    	png_infop 	info_ptr;
    	png_byte color_type;
    	png_bytep * row_pointers;
    	FILE *fp = fopen(filename, "wb");
    	if (NULL == fp) {
    		printf("open failed ...2\n");
    		return -1;
    	}
    	//1: 初始化libpng结构体  
    	png_ptr	= png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
    	if (!png_ptr) {
    		printf("png_create_write_struct failed ...\n");
    		return -1;
    	}
    	//2: 初始化png_infop结构体 , 
    	//此结构体包含了图像的各种信息如尺寸,像素位深, 颜色类型等等
    	info_ptr = png_create_info_struct(png_ptr);
    	if (!info_ptr) {
    		printf("png_create_info_struct failed ...\n");
    		return -1;
    	}
    	//3: 设置错误返回点
    	if (setjmp(png_jmpbuf(png_ptr))) {
    		printf("error during init_io ...\n");
    		return -1;
    	}
    	//4:绑定文件IO到Png结构体
    	png_init_io(png_ptr, fp);
    	if (setjmp(png_jmpbuf(png_ptr))) {
    		printf("error during init_io ...\n");
    		return -1;
    	}
    	if (out->alpha_flag == HAVE_ALPHA) color_type = PNG_COLOR_TYPE_RGB_ALPHA;
    	else color_type = PNG_COLOR_TYPE_RGB;
    	//5:设置以及写入头部信息到Png文件
    	png_set_IHDR(png_ptr, info_ptr, out->width, out->height, out->bit_depth,
    	color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
    	png_write_info(png_ptr, info_ptr);
    	if (setjmp(png_jmpbuf(png_ptr))) {
    		printf("error during init_io ...\n");
    		return -1;
    	}
    	int channels, temp;
    	int i, j, pos = 0;
    	if (out->alpha_flag == HAVE_ALPHA) {
    		channels = 4;
    		temp = (4 * out->width);
    		printf("have alpha ...\n");
    	} else {
    		channels = 3;
    		temp = (3 * out->width);
    		printf("not have alpha ...\n");
    	}
    	// 顺时针旋转90度 , 旋转完了一定要把width 和height调换 不然得到的图像是花的  旋转三次就是逆时针旋转一次
    	//RotationRight90(out->rgba, out->width, out->height, channels);
    	//RotationRight90(out->rgba, out->height, out->width, channels);
    	//RotationRight90(out->rgba, out->width, out->height, channels);
    	row_pointers = (png_bytep*)malloc(out->height * sizeof(png_bytep));
    	for (i = 0; i < out->height; i++) {
    		row_pointers[i] = (png_bytep)malloc(temp* sizeof(unsigned char));
    		for (j = 0; j < temp; j += channels) {
    			if (channels == 4) {
    				row_pointers[i][j+3] = out->rgba[pos++];
    				row_pointers[i][j+2] = out->rgba[pos++];
    				row_pointers[i][j+1] = out->rgba[pos++];
    				row_pointers[i][j+0] = out->rgba[pos++];
    			} else {
    				row_pointers[i][j+2] = out->rgba[pos++];
    				row_pointers[i][j+1] = out->rgba[pos++];
    				row_pointers[i][j+0] = out->rgba[pos++];
    			}
    		}
    	}
    	//6: 写入rgb数据到Png文件
    	png_write_image(png_ptr, (png_bytepp)row_pointers);
    	if (setjmp(png_jmpbuf(png_ptr))) {
    		printf("error during init_io ...\n");
    		return -1;
    	}
    	//7: 写入尾部信息
    	png_write_end(png_ptr, NULL);
    	//8:释放内存 ,销毁png结构体
    	for (i = 0; i < out->height; i ++)
    		free(row_pointers[i]);
    	free(row_pointers);
    	png_destroy_write_struct(&png_ptr, &info_ptr);
    	fclose(fp);
    	return 0;
    }
    
    int main(int argc, char **argv)
    {
    	pic_data out;
    	if (argc == 3) {
    		decode_png(argv[1], &out);
    		write_png_file(argv[2], &out);
    		free(out.rgba);
    	} else {
    		puts("please input two file, \nargv[1]:source.png argv[2]:dest.png");	
    	}
    	return 0;
    }
    

    编译需要链接已经安装好的libpng 和 zlib库

    gcc test.c `pkg-config libpng16 zlib --libs --cflags` 

    功能就是简单得读取PNG格式图片, 再生成(写入)一个新的一模一样的图片 ,  代码中把图片旋转注释了 , 亲测也是可用的。

    最后送上一张随便百度的PNG格式图片, 还有一张就是项目的图片(无视本人)

     

    展开全文
  • linux动态及动态的版本管理

    千次阅读 2019-03-14 12:31:09
    Linux下动态有三个名字:real name、so name、link name。 real name:共享本身的名字,命名格式为libname.so.x.y.z,lib是前缀,name是共享的名字,so是后缀,x是主版本,y是次版本号,z是发布版本号。 主...

    Linux下动态库有三个名字:real name、so name、link name。

    • real name:共享库本身的名字,命名格式为libname.so.x.y.z,lib是前缀,name是共享库的名字,so是后缀,x是主版本,y是次版本号,z是发布版本号。

             主版本号、次版本号和发布版本号的意义如下:

             主版本号:表示库的重大升级,比如改变了原有的接口。不同主版本号的库是不兼容的,要保证向下兼容就不能删除旧版本的动态库。

              次版本号:表示库的增量升级,比如增加了新的接口,但原有接口保持不变。高的次版本号的库向下兼容低的次版本号的动态库,原有的版本库是可以不保留的。

    发布版本号:表示指示改正了一些错误或者是性能优化等,接口没有增加,也没有改变,因此也是向下兼容的。

    • so name:是通过ldconfig命令,来创建的指向真正的动态库(real name)的链接,so name的命名规则是,real name去掉次版本号和发布版本号,格式为libname.so.x。
    • link name:是so name的软链接,命名规则是,real name去掉主版本号、次版本号和发布版本号,格式为libname.so,编译时,通过参数-lname来链接。

    下面通过实际的例子来说明动态库的编译和链接:

    首先,建立hello.c和hello.h两个文件,然后将他们编译成动态链接库。

    hello.c文件:

    #include <stdio.h>
    
    void hello (void)
    {
        printf("Hello, library world.\n");
    }
    

    hello.h文件:

    #ifndef __HELLO_H__
    #define __HELLO_H__
    
    void hello (void);
    
    #endif

    执行如下命令,编译生成共享库

    $gcc hello.c -fPIC -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0.0

    编译完成后,只生成了libhello.so.0.0.0,并未生成libhello.so.0,若要生成libhello.so.0需要做如下操作:

    在/etc/ld.so.conf文件中指定库的路径,在文件中添加:libhello.so.0.0.0这个库所在的绝对路径。

    执行如下命令生成libhello.so.0:

    $sudo ldconfig

    下面需要为libhello.so.0建立一个软链接,方便编译应用程序是链接,执行如下命令:

    $ln -s libhello.so.0 libhello.so

    然后,编写应用程序来调用动态库。

    main.c文件:

    #include "hello.h"
    
    int main(void)
    {
        hello();
        
        return 0;
    }

    执行下面的命令编译生成main的可执行文件:

    $gcc main.c -L. -lhello -o main

    注:没有重大升级(主版本号未更改),编译新版本的动态库后,执行$sudo ldconfig就可以进行动态库的升级。如果进行重大升级(更改主版本号),编译新版本的动态库后,执行$sudo ldconfig,生成新的so name文件,再执行$ln -s libhello.so.1 libhello.so(主版本号升级为1).如果想重新恢复为旧版本的动态库,只需要执行$ln -s libhello.so.0 libhello.so

    展开全文
  • Linux中的

    2018-08-16 12:59:10
    1.是什么? 是一种可执行代码的二进制形式,可以被操作系统载入内存执行。就是将源代码转化为二进制格式的源代码,相当于进行了加密,别人可以使用,但是看不到中的内容。 2.如何使用? 用户需要同时具有...

    1.库是什么?

    库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。就是将源代码转化为二进制格式的源代码,相当于进行了加密,别人可以使用库,但是看不到库中的内容。

    2.如何使用?

    用户需要同时具有头文件和库。

    头文件(相当于说明书的作用,能够知道这个库能干嘛)

    制作出来的库(具体的实现,存放.c、.cpp)

    3.静态库的制作和使用

    (1)命名规则

    静态库文件名的命名方式是“libxxx.a”,库名前加”lib”,后缀用”.a”,“xxx”为静态库名。

    (2)制作步骤

    原材料:源代码.c 或者 .cpp

    将.c文件生成.o,gcc a.c b.c -c

    将.o 打包
        ar rcs 静态库的名字   原材料
        ar rcs libtest.a a.o b.o

    (3)库的使用

    gcc test.c -I ./include -L./lib -lmycalc -o app

    -I(大写i):指定头文件的路径

    -L: 指定库的路径

    -l(小写L):指定库的名字(去掉lib和.a)

    -o:指定生成的最终应用程序的名字

    示例:

    在include目录下存放的是加减乘除的头文件head.h

    #ifndef __HEAD_H_
    #define __HEAD_H_
    int add(int a, int b);
    int sub(int a, int b);
    int mul(int a, int b);
    int div(int a, int b);
    #endif
    

    在src目录下存放的是加减乘除的源代码

    将.c文件生成.o文件

    因为头文佳head.h和.c文件不在同一级目录,所以需要通过-I指出

    然后将将.o 打包,ar rcs 静态库的名字   原材料

    把制作好的库放到目录lib中,不放也行,其实只要有这个库就行,在这里,移到上层目录中的lib中

    然后将include、lib发给用户就可以了

    (4)库的使用

    用户拿到上面的两个目录,就可以借助库实现自己的需求

    包含头文件

    #include <stdio.h>
    #include "head.h"
    
    int main(void)
    {
        int sum = add(2, 24);
        printf("sum = %d\n", sum);
        return 0;
    }
    

    4.动态库的制作和使用

    (1)命名规则

    动态库的命名方式与静态库类似,前缀相同,为“lib”,后缀变为“.so”。所以为“libmytime.so” 

    (2)制作步骤

    将源文件生成.o,需要加一个参数fpic
    gcc a.c b.c -c -fpic(fPIC)

    打包,gcc -shared a.o b.o -o libxxx.so

    把制作好的库放到目录lib中,不放也行,其实只要有这个库就行,在这里,移到上层目录中的lib中

    (3)库的使用

    gcc main.c -I ./  -L ./ -l test -o app

    此时会提示No such file or directory,需要进行环境变量的设置

    动态库无法加载:

    使用环境变量
    临时设置:
    在终端进行:
    export LD_LIBRARY_PATH=动态库的路径:$LD_LIBRARY_PATH

    永久设置:
    用户级别:
    ~/.bashrc,配置完成之后,重启终端或者source ~/.bashrc
    系统级别:
    /etc/profile,配置完成之后,重启计算机或者source /etc/profile

    5.静态库、动态库优缺点

    静态库:

    优点:

    1.静态库被打包到应用程序中加载速度快
    2.发布程序无需提供静态库,因为已经在app中,移植方便

    缺点

    1.链接时完整地拷贝至可执行文件中,被多次使用就有多份冗余拷贝。
    2.更新、部署、发布麻烦。

    动态库:

    优点

    1.链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序可以共用,节省内存。
    2.程序升级简单,因为app里面没有库的源代码,升级之后只要库的名字不变,函数名以及参数不变,只是实现做了优化,就能加载成功。

    缺点:

    加载速度比静态库慢
    发布程序需要提供依赖的动态库

    6.两个动态库函数重名问题

    应用程序a(a.c),动态库liba.so(liba.h, liba.c),libb.so,均实现了func()

    gcc -la -lb a.c

    则调用的是liba.so中的函数实现

    gcc -lb -la a.c

    则调用的是libb.so中的函数实现

    展开全文
  • linux中的动态链接

    千次阅读 2019-04-03 12:38:41
    在使用动态链接的时候并不会把动态的代码全部copy到生成的程序中,而是在程序运行的时候再去加载对应的代码,所以使用同一个动态的程序可以共用一份代码,而且当你对动态进行升级的时候,也不用去修改使用它...
  • Linux中的(Library)

    千次阅读 2018-08-25 16:28:06
    一、文件 是一组预先编译好的函数的集合,这些函数都...在Linux中,标准的文件一般存放在/lib和usr/lib的目录中。c语言编译器gcc(更确切的说是链接程序)需要知道要搜索哪些文件,因为默认情况下,它只搜...
  • linux文件

    千次阅读 2019-05-09 16:41:38
    一、什么是文件? 本质上来说,文件就是可执行代码的... Linux操作系统支持的函数分为静态和动态,动态又称共享 Linux系统有几个重要的目录存放相应的函数,如/lib、/usr/lib 二、静态和动...
  • Linux库文件详解

    千次阅读 2018-06-07 07:03:42
    &nbsp; 源自: https://www.cnblogs.com/yangg518/p/5842651.html 转自: http://www.cppblog.com/deane/articles/165216.html ...http://www.pchou.info/linux/2016/07/...
  • Linux库文件使用与编译

    千次阅读 2018-10-11 17:41:53
    Linux库文件 静态库 在Linux中,以.a为后缀,如libtest.a 直接拷贝函数到二进制映像文件 只需要运行二进制文件,可以直接运行 共享库 在Linux操作系统中,以libxxx.so.x.x为格式命名 可执行文件在开始运行时将...
  • linux 库文件 拷贝

    千次阅读 2012-11-16 11:13:05
    库文件从一台机器拷贝到另一台机器上,就如同拷贝一个可执行程序,或者一个文本文件是一样的,没多大区别。    A机器上,没办法编译安装某个可执行程序,或者库程序。可以在B机器上,编译安装好,然后将这些...
  • linux库文件的安装于使用

    千次阅读 2014-11-14 11:53:15
    在学习某一函数的时候,我们经常做的第一件事, 就是把该函数的源码下载回来进行编译和安装(一般是这三步:./configure -> make -> make install),然后写一些调用库函数的小用例,看其能否正常运行以及运行后的...
  • 查看linux库文件32位还是64位

    万次阅读 2014-09-25 09:46:11
    objdump -a *.a objdump -a *.so
  • 概述Linux库文件含义

    2010-09-12 01:45:00
    秘籍:概述Linux库文件含义  经过长时间学习linux库文件的概念,今天小编也给大家搜集了关于库的概念以及作用,希望大家看后会有很多收获。 1.什么是linux库  在windows平台和linux平台下都...
  • 1、库文件的搜索方式 库文件在连接(静态库和共享库)和运行(仅限于使用共享库的程序)时被使用,其搜索路径是在系统中进行设置的。一般 Linux 系统把 /lib 和 /usr/lib 两个目录作为默认的库搜索路径,所以使用这...
  • Linux系统中添加库文件路径的方法

    万次阅读 2017-02-23 16:44:13
    库文件在链接(静态库和共享库)和运行(仅限于使用共享库的程序)时被使用,其搜索路径是在系统中进行设置的。一般 Linux 系统把 /lib 和 /usr/lib 两个目录作为默认的库搜索路径,所以使用这两个目录中的库时不...
  • linux库文件后缀名称

    千次阅读 2008-05-22 12:04:00
    windows静态库文件就是.lib文件,动态库文件就是.dll文件。内容一样,都是将函数封装在一起编译后供自己或他人调用。好处在于编译后的库文件看不到源代码,可保密;同时不会因为不小心修改了函数而出问题,便于维护...
  • linux查看文件依赖的

    千次阅读 2014-02-13 18:44:38
    一般使用ldd命令查看文件所依赖的(.so) ldd file 工具链还提供了另外2个命令可以使用 xx-linux-readelf -a "your binary" | grep "Shared" xx-linux-objdump -x "your binary" | grep "NEEDED" 曲线方式 ...
  • linux下查找库文件所在位置

    千次阅读 2019-04-18 14:52:36
    ldconfig -p | grep libcrypto
  • linux 库文件 生成与使用

    万次阅读 2008-11-17 19:08:00
    1、linux库文件分为静态库和动态库两种。静态库习惯以.a 结尾,而动态库习惯以.so(shared object)结尾。而且必须以lib开头。2、静态库的原则是“以空间换时间”,增加程序体积,减少运行时间;生成:在编译时候,先生...
  • Linux 源码安装及库文件的一些经验

    千次阅读 2018-01-12 23:14:45
     从源码包安装软件最重要的就是仔细阅读README INSTALL等说明文件,它会告诉你怎样才能成功安装。  通常从源码包安装软件的步骤是:  1、tar jxvf gtk+-2.4.13.tar.bz2 解开源码包  2、cd gtk+-2.4.13/ 进入...
  • Java Web中调用.so Linux库

    千次阅读 2017-06-08 09:57:03
    在Java Web项目中我们很少看到用到外部的共享,周天在玩Android Studio的时候研究了一下,配合网上的教程,移植到了Java Web下,仅供参考
  • linux库文件编写入门

    万次阅读 2007-02-16 10:16:00
    linux库文件的编写作者: laomai地址: http://blog.csdn.net/laomai本文主要参考了如下资料⑴hcj写的"Linux静态/动态链接库的创建和使用"地址 http://fanqiang.chinaunix.net/system/linux/2006-05-08/4126.shtml⑵雨...
  • Linux libc 库文件更新

    千次阅读 2018-12-22 12:38:06
    觉得busybox 里面用的一些libc 库函数不支持Linux的一些内核特性,需要更新,怎么办? 首先就是要查看版本,然后去下载代码过来看,如果有必要自己改一下,更新一下版本值了IDE,或者想办法避开。 readelf -a /bin...
  • libevent简介 libevent下载 libevent安装 简单shili
  • 1. 头文件1)系统标准头文件位置: /usr/include下,以及安装的头文件位置:/usr/local/include/如 #include&lt;linux/can.h&gt; 对应 /usr/include/linux/can.h #include&lt;stdio.h&gt; 对应 /...
  • Linux/C】如何编译和使用库文件

    千次阅读 2016-06-21 20:55:50
    1. 库文件分类库文件可以分为两类,静态库和动态库。 链接静态库的程序特点:1. 程序较大;2.不容易升级;3.容易部署 链接动态库的程序特点:1. 程序较小;2.容易升级;3.不容易部署2. 存档文件(静态库)存档文件...
  • linux库文件头文件查找顺序

    千次阅读 2012-05-14 20:57:54
    Include的header文件,连结数据库,系统定义,总共有下列来源指定gcc去那找。 当初在编译时指定的(在~gcc/gcc/collect2.c:locatelib() 写在specs内的 后来用-D -I -L指定的 gcc环境变量设定(编译的时候) ld.so的...
  • linux共享命名规则

    千次阅读 2010-07-07 21:21:00
    linux有一套规则来命名系统中的每一个共享, 它规定共享文件命名规则必须如下: libname.so.x.y.z 最前面使用前缀"lib", 中间是的名字,后缀是".so", 最后面跟着的是三个数字组成的版本好。...
  • linux动态命名规则

    千次阅读 2018-11-02 13:28:38
    说道“动态版本兼容”,很多人头脑中首先蹦出的就是“Dll Hell”。啊,这曾经让人头疼的难题。时至今日,这个难题已经很好地解决了。   在进一步讨论之前来思考一个问题:Linux下为什么没有让人头痛的“DllHell”...
  • linux下安装完一些第三方,需要链接的时候,一般安装的都在/usr/lib或者/usr/lib64下面,一般是以.so文件存在的。 以下是一些常出现的问题,首先是如何链接到这些,如链接pcre的时候和头文件一般如下...

空空如也

1 2 3 4 5 ... 20
收藏数 828,358
精华内容 331,343
关键字:

linux库

linux 订阅