prawnpdf/prawn — pull request #600 by cheba (2013-12-15 19:46:35+00:00)
Title: Alternative PNG alpha-channel extraction

According to PNG spec filtering happens on a per-byte basis and uses corresponding bytes from other pixels. This means that we don't have to actually unfilter all the data. Instead it's possible to split pixel data into planes and reuse filters.

A few points to note:

* Filters are reused from original image
* Resulting opaque PNG uses the same filtered data
* Resulting mask PNG image uses the same filteres data
* Both resulting images are valid PNG images
* All the unfiltering math is skipped which results in great boosts

## Benchmarks

Benchmark is done by creating a document and adding a PNG image, repeated 100 times.

Mem is recorder by watching Activity Monitor. Values are not exact and provided just to illustrate approximate scale.

### RBX

#### Original

```
user system total real
opaque: 2.724633 0.541629 3.266262 ( 1.698649)
transparent: 340.380027 9.747552 350.127579 (326.535695)
```

Mem: 225-227 MB

#### Modified

```
user system total real
opaque: 2.675154 0.526945 3.202099 ( 1.706802)
transparent: 77.626282 0.724561 78.350843 ( 72.843238)
```

Mem: 95-97 MB

#### Change

Execution time: **4.47x faster**
Memory: **57% less**

### MRI

#### Original

```
user system total real
opaque: 1.900000 0.190000 2.090000 ( 2.206105)
transparent: 339.740000 6.330000 346.070000 (349.369761)
```

Mem: 110-130 -> 150-180 MB (Leak?)

Notes: Mem fluctuates widely. Median steadily rises. Mem leak?

#### Modified

```
user system total real
opaque: 1.780000 0.170000 1.950000 ( 1.964678)
transparent: 50.380000 0.350000 50.730000 ( 51.129041)
```
Mem: 36-38 MB

#### Change
Execution time: **6.82x faster**
Memory: **62% - 79% less**

### JRuby

#### Original

```
user system total real
opaque: 5.390000 0.270000 5.660000 ( 2.817000)
transparent: 564.830000 5.690000 570.520000 (474.866000)
```

Mem: 287.4 -> 298, 293 -> 312.2 MB (Leak?)

#### Modified

```
user system total real
opaque: 5.190000 0.210000 5.400000 ( 2.497000)
transparent: 3559.290000 10.180000 3569.470000 (3614.186000)
```

Mem: 197.9 -> 211, 185.2 -> 187.2 MB (Leak?)

```
user system total real
opaque: 5.440000 0.240000 5.680000 ( 2.985000)
transparent: 3553.670000 12.020000 3565.690000 (3668.508000)
```

Mem: 224, 223.1 MB

Notes: Mem steadily rises, no fluctuation. Sometimes it drops.

#### Change
Execution time: **6.25x slower**
Memory: **25%—34% less**

***

PNG-related specs are broken. That is expected because "unfiltered" data is different with this implementation.

Also note that this code is *extremely* slow on JRuby. I'm looking into it.