之前写的LIBPNG库学习小结介绍了怎么样自定义LIBPNG库的write、read、flush函数,而不使用LIBPNG库提供的默认函数。

上一篇讲述的都是在单线程的情况下,今天将程序升级,放在多线程下面跑,发现了几个问题:
首先说明一下:上一篇中是用struct保存的数据结构,而这次需要将数据封装在类中,因此程序有点小变动。以下是类的部分定义:

private:
png_infop m_pInfo;
png_structp m_pPng;
char *m_pImage;

其中m_pImage就是上一篇tData中的data。

上一篇中tData是全局定义的,在多线程的情况下会发生争抢资源的情况,可查看上一篇中的代码,在void PNGAPI png_own_write_data(png_structp png_ptr, png_bytep data, png_size_t length)函数中需要一个变量来存储每次写入的位置,不能每次都从第0个位置开始写吧?会争抢的资源就是这个记录位置的变量。这该怎么解决呢?

1-首先想到的办法就是将所有数据成员和函数成员作为类的私有成员访问即可解决问题,可是编译的时候却提示png_own_write_data()和png_own_flush()函数出错,具体错误信息就不写了,此路不通。

2.-加锁可以解决这个问题,但是加锁效率很低,怎么办呢?有没更好的办法?

想到的解决办法:

1-在png_struct中找一个闲置的字段来存储这个变量,可是我看完了png_struct的定义,也不确定那个变量是没有用的,虽然有很多字段的值从始至终都是0,但是我还是不敢贸然使用,因为不知道什么时候这个值就被使用了。

2-还是在png_struct中找一个闲置的字段,不过这次不是使用某个int或long型的变量了,而是使用函数指针,没错,就是   函数指针,因为任何指针在系统中都是一个内存地址(4字节),可以当做一个int类型来使用。

下面就详细的介绍一下这种方法:

1-首先锁定的是output_flush_fn,在上一篇中也说了,默认情况下

PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED

这个宏是没有被定义的,因此output_flush_fn()这个函数就一直不会执行,所以output_flush_fn这个函数指针是一直没有使用的,但是若使用自己编译的LIBPNG库,而恰好定义了上面的那个宏,那么这样程序就会发生意想不到的错误。因此此方法可以用,但是要慎重。

2-根据read和write的互斥性,我们可以交错的使用write_data_fn和read_data_fn这两个函数指针来保存这个偏移地址。因为我们一般不会在读的时候进行写,也不会在写的时候进行读操作(要是有这种情况,你还是老老实实的使用output_flush_fn吧,这种情况不适合你),所以我们在写的时候使用read_data_fn来保存写偏移地址,在读的时候使用write_data_fn来保存读偏移地址,具体请见代码:

void PNGAPI pngOwnReadData( png_structp pPng, png_bytep data, png_size_t length )
{
png_bytep src = (png_bytep)pPng->io_ptr;
//强制类型转换,获得偏移地址
int offSet = (int)pPng->write_data_fn;
memcpy( data, src + offSet, length );
offSet += length;
//将偏移地址强转成png_rw_ptr类型,该类型是png库定义的函数指针类型
pPng->write_data_fn = (png_rw_ptr)offSet;
}

而在读取的主函数里面必须有这样的代码:

void readPNG(…… )
{
png_voidp io_ptr = (void *)image;
//将write_data_fn初始化为0
m_pPng->write_data_fn = (png_rw_ptr);
png_set_read_fn( m_pPng, io_ptr, pngOwnReadData );
png_read_png( m_pPng, m_pInfo, PNG_TRANSFORM_EXPAND, NULL );
}

同样的写函数也具有同样的结构:

void PNGAPI pngOwnWriteData(png_structp pPng, png_bytep data, png_size_t length)
{
……
png_bytep src = (png_bytep)pPng->io_ptr;
int offSet = ( int )pPng->read_data_fn;
memcpy( src + offSet, data, length );
offSet += length;
pPng->read_data_fn = (png_rw_ptr)offSet;
……
}
char* writePNG(…… )
{
……
png_voidp io_ptr = (void *)m_pImage;
m_pPng->read_data_fn = (png_rw_ptr);
png_set_IHDR( m_pPng, m_pInfo, width, height, , PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT );
png_set_rows( m_pPng, m_pInfo, rgb );
png_set_write_fn( m_pPng, io_ptr, pngOwnWriteData, NULL );
png_write_png( m_pPng, m_pInfo, PNG_TRANSFORM_EXPAND, NULL );
//在对象销毁前获取长度信息
length = (int)m_pPng->read_data_fn;
png_destroy_write_struct( &m_pPng, &m_pInfo );
……
}

好,到此为止我们就实现了即保证了多线程的安全性,又保证了程序的效率,搞定,收工……

LIBPNG使用小结(二)的更多相关文章

  1. gulp使用小结(二)

    接上篇文章接Gulp使用小结(一) 内容如下: 首先,偶在gulp-demos上已经提交了个较通用的栗子...俺琢磨半天,原准备分阶段搞些 Gulp 套路,但是写完介个栗子之后,觉得已经能覆盖绝大多数 ...

  2. Vue学习小结(二)

    接上一批,小结(二). 三.导航内容(含左侧导航及顶部面包屑导航) 其实导航条主要根据element-ui的教程进行编写,官网:http://element-ui.cn/#/zh-CN/compone ...

  3. python --- 字符编码学习小结(二)

    距离上一篇的python --- 字符编码学习小结(一)已经过去2年了,2年的时间里,确实也遇到了各种各样的字符编码问题,也能解决,但是每次都是把所有的方法都试一遍,然后终于正常.这种方法显然是不科学 ...

  4. Element Vue 开箱即用框架如何使用-测试开发【提测平台】阶段小结(二)

    微信搜索[大奇测试开],关注这个坚持分享测试开发干货的家伙. 上一篇总结了后端服务接口的开发,这篇我们主要来总结下前后端分离开发中的前端部分,主要是开箱即用的框架介绍和之前章节组件的梳理和部分的扩展内 ...

  5. Spring知识点小结(二)

    一.配置非自定义的Bean(数据源DataSource模型) DBCP数据源:        导入dbcp的jar包:dbcp+pool+connector                代码实现:  ...

  6. 动态规划小结 - 二维动态规划 - 时间复杂度 O(n*n)的棋盘型,题 [LeetCode] Minimum Path Sum,Unique Paths II,Edit Distance

    引言 二维动态规划中最常见的是棋盘型二维动态规划. 即 func(i, j) 往往只和 func(i-1, j-1), func(i-1, j) 以及 func(i, j-1) 有关 这种情况下,时间 ...

  7. Hibernate知识点小结(二)

    一.持久化对象和标识符    1.持久化类        配置完关系后,操作的实体对应的类,成为持久化类 (Customer) 2.持久化类标识符(oid:object id)        3.持久 ...

  8. Struts2知识点小结(二)

    一.结果视图的配置    <result name="success">/success.jsp</result>        1.局部结果视图      ...

  9. Java并发包中常用类小结(二)

    6.ThredPoolExecutor ThredPoolExecutor是基于命令模式下的一个典型的线程池的实现,主要通过一些策略实现一个典型的线程池,目前已知的策略有ThreadPoolExecu ...

随机推荐

  1. Eclipse反编译插件: Jodeclipse与JadClipse

    Eclipse反编译插件: Jodeclipse与JadClipse Jodeclipse 是Jode的Eclipse插件,JadClipse是Jad的Eclipse插件,它们都是非常好的反编译插件. ...

  2. js图片实现延迟加载

    <script type="text/javascript"> function delayload(option){ //读取参数 //图片未加载时显示的图片 var ...

  3. java操作pdf添加页眉条码添加水印图片

    添加条码页眉以及图片水印 1. 引入jar包     1. itext-4.2.1.jar     2. itext-asian-5.2.0.jar     3. jbarcode-0.2.8.jar ...

  4. 集群搭建:主机宽带拨号上网,虚拟机使用桥接模式,该如何ping通外网

    首先介绍一下看这篇文章需要的基础.需要了解虚拟机的 虚拟机的三种网络模式,有Linux基础知识,这些都是前提.首先介绍一下我的环境:主机:win7虚拟机:VMware Workstation 10虚拟 ...

  5. HW2.10

    import javax.swing.JOptionPane; public class Solution { public static void main(String[] args) { Str ...

  6. ASP.NET--ListBox初始化时设置多个选中项

    public void SetSelectedListItem(ListBox lst, List<DBServerIPBind> source) { ; i < source.Co ...

  7. php5.5 yum源

    PHP官网在下载页面中只有源代码下载,当然使用编译安装也是可以的,但是使用二进制包安装会非常快速.虽然PHP官网没有提供二进制安装包,但是它推荐了两个第三方的YUM源(CentOS中默认的YUM安装的 ...

  8. ZOJ2834--Maximize Game Time(树形DP)

    写之前觉得很恶心,写完了觉得挺好玩了,1A,棒棒哒~ 题解全在代码注释中了,想清楚思路一路写下了果然没怎么卡 ^_^ /**************************************** ...

  9. 一步一步学android控件(之十六)—— CheckBox

    根据使用场景不同,有时候使用系统默认的CheckBox样式就可以了,但是有时候就需要自定义CheckBox的样式.今天主要学习如何自定义CheckBox样式.在CheckBox状态改变时有时需要做一些 ...

  10. JavaScript的递归之更多例子

    更多例子 第二个递归的例子是求两个自然数的最大公约数(有没有回到令人怀念的中学时代).下面的程序用的是经典的辗转相除法. //greatest common divisor //假定a.b都是正整数 ...