simd-blend-modes 1.0.2
SIMD-accelerated blend modes
License: MIT
Requires Python: >=3.9
Keywords: image, processing

# SIMD Blend Modes

This project reimplements the blend modes from [`blend_modes`](https://github.com/flrs/blend_modes) with C kernels and SIMD
(SSE4.2/AVX2) acceleration. It supports uint8 and float32 NumPy inputs in the range 0..255
and returns output dtype/channel count matching the background image. Missing alpha channels
are treated as fully opaque (255). Opacity defaults to 1.0.

This is mostly intended to be a mostly drop-in replacement, but with a more permissive 
API that allows you to go faster if you don't need FP32 arrays or the information of an
Alpha channel for some layers.

## Build and Install

### General

```bash
pip install simd-blend-modes
```

### Development

```bash
pip install -r requirements-dev.txt
pip install -e .
```

## Usage

```python
import numpy as np
import simd_blend_modes as sbm

background = np.zeros((512, 512, 4), dtype=np.uint8)
foreground = np.zeros((512, 512, 4), dtype=np.uint8)

out = sbm.screen(background, foreground, 0.5)
```

Inputs:

- Dtypes: `np.uint8` or `np.float32` only.
- Value range: 0..255 for both dtypes.
  - This expects float32 inputs to be cast from uint8, not normalized as well.
- Shapes: `H x W x C` with `C` = 3 (RGB) or 4 (RGBA).
- Output: dtype and channel count match the background image.
- Alpha: if a source is RGB (3 channels), alpha is treated as 255 (fully opaque).
- Opacity: the third argument is optional; defaults to `1.0`.

Supported blend modes:

- [`normal`](https://en.wikipedia.org/wiki/Blend_modes#Normal_blend_mode)
- [`soft_light`](https://en.wikipedia.org/wiki/Blend_modes#Soft_Light)
- [`lighten_only`](https://en.wikipedia.org/wiki/Blend_modes#Lighten_Only)
- [`screen`](https://en.wikipedia.org/wiki/Blend_modes#Screen)
- [`dodge`](https://en.wikipedia.org/wiki/Blend_modes#Dodge_and_burn)
- [`addition`](https://en.wikipedia.org/wiki/Blend_modes#Addition)
- [`darken_only`](https://en.wikipedia.org/wiki/Blend_modes#Darken_Only)
- [`multiply`](https://en.wikipedia.org/wiki/Blend_modes#Multiply)
- [`hard_light`](https://en.wikipedia.org/wiki/Blend_modes#Hard_Light)
- [`difference`](https://en.wikipedia.org/wiki/Blend_modes#Difference)
- [`subtract`](https://en.wikipedia.org/wiki/Blend_modes#Subtract)
- `grain_extract` (known from GIMP)
- `grain_merge` (known from GIMP)
- [`divide`](https://en.wikipedia.org/wiki/Blend_modes#Divide)
- [`overlay`](https://en.wikipedia.org/wiki/Blend_modes#Overlay)

You can force a kernel by passing a string (or `KernelKind` value):

```python
out = sbm.screen(background, foreground, 0.5, "avx2")
```

## Tests

Correctness and performance:

```bash
python3 -m unittest discover tests/
```

Performance:

```bash
python3 -m unittest tests.test_performance
```

The performance test prints a markdown table of per-kernel speedups vs the NumPy reference
for common square sizes and screen resolutions.

## ARM

ARM isn't properly supported as I do not have a new enough ARM CPU to test on. 
Nor do I wish to use a cloud VM to test it. So, if you want ARM support, open a PR.
It should build and be faster, but there's no SIMD support there (yet).

ARM builds run in scalar-only mode (x86 SIMD is compile-time gated). To test ARM under Docker,
enable emulation and then build with the ARM platform. 

If you don't already have buildx/binfmt configured, run:

```bash
docker run --privileged --rm tonistiigi/binfmt --install arm64
```

Then build or run the ARM container:

```bash
docker compose up --build
```

This is incredibly slow. I wouldn't actually do this, but it's here.

## Notes

- SIMD kernels are selected at runtime: AVX2 → SSE4.2 → scalar.
- ARM builds are supported in scalar-only mode; x86 SIMD is compile-time gated. CI does not emit
  ARM artifacts.
- Reference tests adapted from the original project live in `tests/reference_blend_modes_tests.py`
  and are skipped unless the `blend_modes` package and test assets are available.
- The SIMD paths currently assume contiguous arrays (the input validation enforces this).

## Performance 

<!--
The performance test prints large tables. If your terminal buffer is limited, you can write the
output into this README instead by setting `WRITE_RESULTS_TO_README = True` in
`tests/test_performance.py`. When enabled, it replaces the block between the markers below.
-->

<!-- PERF_RESULTS_START -->
| Mode          | Kernel | Ref (s)  | Kernel (s) | Speedup | Percent Change |
| ------------- | ------ | -------- | ---------- | ------- | -------------- |
| normal        | scalar | 0.152080 | 0.032742   | 4.64x   | -78.47%        |
| normal        | sse42  | 0.152080 | 0.010798   | 14.08x  | -92.90%        |
| normal        | avx2   | 0.152080 | 0.010636   | 14.30x  | -93.01%        |
| soft_light    | scalar | 0.209721 | 0.038664   | 5.42x   | -81.56%        |
| soft_light    | sse42  | 0.209721 | 0.013059   | 16.06x  | -93.77%        |
| soft_light    | avx2   | 0.209721 | 0.011835   | 17.72x  | -94.36%        |
| lighten_only  | scalar | 0.153868 | 0.041726   | 3.69x   | -72.88%        |
| lighten_only  | sse42  | 0.153868 | 0.011720   | 13.13x  | -92.38%        |
| lighten_only  | avx2   | 0.153868 | 0.011296   | 13.62x  | -92.66%        |
| screen        | scalar | 0.162643 | 0.036807   | 4.42x   | -77.37%        |
| screen        | sse42  | 0.162643 | 0.012259   | 13.27x  | -92.46%        |
| screen        | avx2   | 0.162643 | 0.011528   | 14.11x  | -92.91%        |
| dodge         | scalar | 0.163841 | 0.039055   | 4.20x   | -76.16%        |
| dodge         | sse42  | 0.163841 | 0.013628   | 12.02x  | -91.68%        |
| dodge         | avx2   | 0.163841 | 0.011869   | 13.80x  | -92.76%        |
| addition      | scalar | 0.157343 | 0.059510   | 2.64x   | -62.18%        |
| addition      | sse42  | 0.157343 | 0.012699   | 12.39x  | -91.93%        |
| addition      | avx2   | 0.157343 | 0.011721   | 13.42x  | -92.55%        |
| darken_only   | scalar | 0.153869 | 0.041986   | 3.66x   | -72.71%        |
| darken_only   | sse42  | 0.153869 | 0.011764   | 13.08x  | -92.35%        |
| darken_only   | avx2   | 0.153869 | 0.011305   | 13.61x  | -92.65%        |
| multiply      | scalar | 0.157435 | 0.036593   | 4.30x   | -76.76%        |
| multiply      | sse42  | 0.157435 | 0.011845   | 13.29x  | -92.48%        |
| multiply      | avx2   | 0.157435 | 0.011343   | 13.88x  | -92.80%        |
| hard_light    | scalar | 0.231979 | 0.073631   | 3.15x   | -68.26%        |
| hard_light    | sse42  | 0.231979 | 0.013737   | 16.89x  | -94.08%        |
| hard_light    | avx2   | 0.231979 | 0.011871   | 19.54x  | -94.88%        |
| difference    | scalar | 0.213577 | 0.036500   | 5.85x   | -82.91%        |
| difference    | sse42  | 0.213577 | 0.011911   | 17.93x  | -94.42%        |
| difference    | avx2   | 0.213577 | 0.011371   | 18.78x  | -94.68%        |
| subtract      | scalar | 0.156726 | 0.037817   | 4.14x   | -75.87%        |
| subtract      | sse42  | 0.156726 | 0.013245   | 11.83x  | -91.55%        |
| subtract      | avx2   | 0.156726 | 0.011774   | 13.31x  | -92.49%        |
| grain_extract | scalar | 0.161499 | 0.048936   | 3.30x   | -69.70%        |
| grain_extract | sse42  | 0.161499 | 0.012698   | 12.72x  | -92.14%        |
| grain_extract | avx2   | 0.161499 | 0.011656   | 13.86x  | -92.78%        |
| grain_merge   | scalar | 0.161065 | 0.048878   | 3.30x   | -69.65%        |
| grain_merge   | sse42  | 0.161065 | 0.012660   | 12.72x  | -92.14%        |
| grain_merge   | avx2   | 0.161065 | 0.011710   | 13.75x  | -92.73%        |
| divide        | scalar | 0.164504 | 0.037938   | 4.34x   | -76.94%        |
| divide        | sse42  | 0.164504 | 0.013081   | 12.58x  | -92.05%        |
| divide        | avx2   | 0.164504 | 0.011762   | 13.99x  | -92.85%        |
| overlay       | scalar | 0.215788 | 0.070267   | 3.07x   | -67.44%        |
| overlay       | sse42  | 0.215788 | 0.013140   | 16.42x  | -93.91%        |
| overlay       | avx2   | 0.215788 | 0.011764   | 18.34x  | -94.55%        |

<details>
<summary>Per-kernel, size, and type results</summary>

| Case      | Input   | Channels | Opacity | Mode          | Kernel | Ref (s)  | Kernel (s) | Speedup | Percent Change |
| --------- | ------- | -------- | ------- | ------------- | ------ | -------- | ---------- | ------- | -------------- |
| 256x256   | uint8   | 3        | 0.50    | normal        | scalar | 0.006370 | 0.001590   | 4.01x   | -75.03%        |
| 256x256   | uint8   | 3        | 0.50    | normal        | sse42  | 0.006370 | 0.000699   | 9.11x   | -89.02%        |
| 256x256   | uint8   | 3        | 0.50    | normal        | avx2   | 0.006370 | 0.000705   | 9.03x   | -88.93%        |
| 256x256   | uint8   | 3        | 0.50    | soft_light    | scalar | 0.008471 | 0.001787   | 4.74x   | -78.90%        |
| 256x256   | uint8   | 3        | 0.50    | soft_light    | sse42  | 0.008471 | 0.000875   | 9.69x   | -89.68%        |
| 256x256   | uint8   | 3        | 0.50    | soft_light    | avx2   | 0.008471 | 0.000801   | 10.58x  | -90.54%        |
| 256x256   | uint8   | 3        | 0.50    | lighten_only  | scalar | 0.007139 | 0.001952   | 3.66x   | -72.66%        |
| 256x256   | uint8   | 3        | 0.50    | lighten_only  | sse42  | 0.007139 | 0.000798   | 8.94x   | -88.82%        |
| 256x256   | uint8   | 3        | 0.50    | lighten_only  | avx2   | 0.007139 | 0.000770   | 9.27x   | -89.21%        |
| 256x256   | uint8   | 3        | 0.50    | screen        | scalar | 0.007168 | 0.001715   | 4.18x   | -76.07%        |
| 256x256   | uint8   | 3        | 0.50    | screen        | sse42  | 0.007168 | 0.000794   | 9.03x   | -88.92%        |
| 256x256   | uint8   | 3        | 0.50    | screen        | avx2   | 0.007168 | 0.000765   | 9.37x   | -89.33%        |
| 256x256   | uint8   | 3        | 0.50    | dodge         | scalar | 0.007354 | 0.001816   | 4.05x   | -75.30%        |
| 256x256   | uint8   | 3        | 0.50    | dodge         | sse42  | 0.007354 | 0.000878   | 8.37x   | -88.06%        |
| 256x256   | uint8   | 3        | 0.50    | dodge         | avx2   | 0.007354 | 0.000796   | 9.24x   | -89.18%        |
| 256x256   | uint8   | 3        | 0.50    | addition      | scalar | 0.007341 | 0.002490   | 2.95x   | -66.09%        |
| 256x256   | uint8   | 3        | 0.50    | addition      | sse42  | 0.007341 | 0.000791   | 9.28x   | -89.22%        |
| 256x256   | uint8   | 3        | 0.50    | addition      | avx2   | 0.007341 | 0.000747   | 9.82x   | -89.82%        |
| 256x256   | uint8   | 3        | 0.50    | darken_only   | scalar | 0.007225 | 0.001920   | 3.76x   | -73.43%        |
| 256x256   | uint8   | 3        | 0.50    | darken_only   | sse42  | 0.007225 | 0.000796   | 9.08x   | -88.98%        |
| 256x256   | uint8   | 3        | 0.50    | darken_only   | avx2   | 0.007225 | 0.000775   | 9.32x   | -89.28%        |
| 256x256   | uint8   | 3        | 0.50    | multiply      | scalar | 0.006988 | 0.001737   | 4.02x   | -75.15%        |
| 256x256   | uint8   | 3        | 0.50    | multiply      | sse42  | 0.006988 | 0.000808   | 8.65x   | -88.44%        |
| 256x256   | uint8   | 3        | 0.50    | multiply      | avx2   | 0.006988 | 0.000782   | 8.94x   | -88.81%        |
| 256x256   | uint8   | 3        | 0.50    | hard_light    | scalar | 0.008761 | 0.002959   | 2.96x   | -66.23%        |
| 256x256   | uint8   | 3        | 0.50    | hard_light    | sse42  | 0.008761 | 0.000934   | 9.38x   | -89.34%        |
| 256x256   | uint8   | 3        | 0.50    | hard_light    | avx2   | 0.008761 | 0.000796   | 11.01x  | -90.92%        |
| 256x256   | uint8   | 3        | 0.50    | difference    | scalar | 0.008801 | 0.001744   | 5.05x   | -80.18%        |
| 256x256   | uint8   | 3        | 0.50    | difference    | sse42  | 0.008801 | 0.000801   | 10.99x  | -90.90%        |
| 256x256   | uint8   | 3        | 0.50    | difference    | avx2   | 0.008801 | 0.000774   | 11.37x  | -91.20%        |
| 256x256   | uint8   | 3        | 0.50    | subtract      | scalar | 0.007266 | 0.001603   | 4.53x   | -77.94%        |
| 256x256   | uint8   | 3        | 0.50    | subtract      | sse42  | 0.007266 | 0.000875   | 8.30x   | -87.95%        |
| 256x256   | uint8   | 3        | 0.50    | subtract      | avx2   | 0.007266 | 0.000815   | 8.92x   | -88.78%        |
| 256x256   | uint8   | 3        | 0.50    | grain_extract | scalar | 0.007047 | 0.002080   | 3.39x   | -70.48%        |
| 256x256   | uint8   | 3        | 0.50    | grain_extract | sse42  | 0.007047 | 0.000840   | 8.39x   | -88.07%        |
| 256x256   | uint8   | 3        | 0.50    | grain_extract | avx2   | 0.007047 | 0.000781   | 9.02x   | -88.91%        |
| 256x256   | uint8   | 3        | 0.50    | grain_merge   | scalar | 0.007489 | 0.002105   | 3.56x   | -71.89%        |
| 256x256   | uint8   | 3        | 0.50    | grain_merge   | sse42  | 0.007489 | 0.000869   | 8.62x   | -88.40%        |
| 256x256   | uint8   | 3        | 0.50    | grain_merge   | avx2   | 0.007489 | 0.000859   | 8.72x   | -88.53%        |
| 256x256   | uint8   | 3        | 0.50    | divide        | scalar | 0.007437 | 0.001764   | 4.22x   | -76.29%        |
| 256x256   | uint8   | 3        | 0.50    | divide        | sse42  | 0.007437 | 0.000866   | 8.58x   | -88.35%        |
| 256x256   | uint8   | 3        | 0.50    | divide        | avx2   | 0.007437 | 0.000810   | 9.19x   | -89.11%        |
| 256x256   | uint8   | 3        | 0.50    | overlay       | scalar | 0.008993 | 0.002876   | 3.13x   | -68.02%        |
| 256x256   | uint8   | 3        | 0.50    | overlay       | sse42  | 0.008993 | 0.000865   | 10.40x  | -90.38%        |
| 256x256   | uint8   | 3        | 0.50    | overlay       | avx2   | 0.008993 | 0.000806   | 11.16x  | -91.04%        |
| 256x256   | uint8   | 4        | 0.50    | normal        | scalar | 0.003095 | 0.001316   | 2.35x   | -57.46%        |
| 256x256   | uint8   | 4        | 0.50    | normal        | sse42  | 0.003095 | 0.000178   | 17.40x  | -94.25%        |
| 256x256   | uint8   | 4        | 0.50    | normal        | avx2   | 0.003095 | 0.000162   | 19.15x  | -94.78%        |
| 256x256   | uint8   | 4        | 0.50    | soft_light    | scalar | 0.006785 | 0.001655   | 4.10x   | -75.61%        |
| 256x256   | uint8   | 4        | 0.50    | soft_light    | sse42  | 0.006785 | 0.000222   | 30.59x  | -96.73%        |
| 256x256   | uint8   | 4        | 0.50    | soft_light    | avx2   | 0.006785 | 0.000197   | 34.46x  | -97.10%        |
| 256x256   | uint8   | 4        | 0.50    | lighten_only  | scalar | 0.005477 | 0.001777   | 3.08x   | -67.55%        |
| 256x256   | uint8   | 4        | 0.50    | lighten_only  | sse42  | 0.005477 | 0.000188   | 29.08x  | -96.56%        |
| 256x256   | uint8   | 4        | 0.50    | lighten_only  | avx2   | 0.005477 | 0.000194   | 28.28x  | -96.46%        |
| 256x256   | uint8   | 4        | 0.50    | screen        | scalar | 0.005569 | 0.001538   | 3.62x   | -72.37%        |
| 256x256   | uint8   | 4        | 0.50    | screen        | sse42  | 0.005569 | 0.000208   | 26.73x  | -96.26%        |
| 256x256   | uint8   | 4        | 0.50    | screen        | avx2   | 0.005569 | 0.000194   | 28.64x  | -96.51%        |
| 256x256   | uint8   | 4        | 0.50    | dodge         | scalar | 0.005804 | 0.001616   | 3.59x   | -72.16%        |
| 256x256   | uint8   | 4        | 0.50    | dodge         | sse42  | 0.005804 | 0.000233   | 24.87x  | -95.98%        |
| 256x256   | uint8   | 4        | 0.50    | dodge         | avx2   | 0.005804 | 0.000194   | 29.93x  | -96.66%        |
| 256x256   | uint8   | 4        | 0.50    | addition      | scalar | 0.005651 | 0.001928   | 2.93x   | -65.89%        |
| 256x256   | uint8   | 4        | 0.50    | addition      | sse42  | 0.005651 | 0.000260   | 21.75x  | -95.40%        |
| 256x256   | uint8   | 4        | 0.50    | addition      | avx2   | 0.005651 | 0.000200   | 28.26x  | -96.46%        |
| 256x256   | uint8   | 4        | 0.50    | darken_only   | scalar | 0.005410 | 0.001722   | 3.14x   | -68.18%        |
| 256x256   | uint8   | 4        | 0.50    | darken_only   | sse42  | 0.005410 | 0.000193   | 28.09x  | -96.44%        |
| 256x256   | uint8   | 4        | 0.50    | darken_only   | avx2   | 0.005410 | 0.000189   | 28.55x  | -96.50%        |
| 256x256   | uint8   | 4        | 0.50    | multiply      | scalar | 0.005434 | 0.001546   | 3.51x   | -71.55%        |
| 256x256   | uint8   | 4        | 0.50    | multiply      | sse42  | 0.005434 | 0.000196   | 27.79x  | -96.40%        |
| 256x256   | uint8   | 4        | 0.50    | multiply      | avx2   | 0.005434 | 0.000186   | 29.16x  | -96.57%        |
| 256x256   | uint8   | 4        | 0.50    | hard_light    | scalar | 0.007276 | 0.002551   | 2.85x   | -64.94%        |
| 256x256   | uint8   | 4        | 0.50    | hard_light    | sse42  | 0.007276 | 0.000236   | 30.83x  | -96.76%        |
| 256x256   | uint8   | 4        | 0.50    | hard_light    | avx2   | 0.007276 | 0.000196   | 37.19x  | -97.31%        |
| 256x256   | uint8   | 4        | 0.50    | difference    | scalar | 0.007216 | 0.001544   | 4.67x   | -78.61%        |
| 256x256   | uint8   | 4        | 0.50    | difference    | sse42  | 0.007216 | 0.000193   | 37.42x  | -97.33%        |
| 256x256   | uint8   | 4        | 0.50    | difference    | avx2   | 0.007216 | 0.000192   | 37.66x  | -97.34%        |
| 256x256   | uint8   | 4        | 0.50    | subtract      | scalar | 0.005564 | 0.001470   | 3.79x   | -73.59%        |
| 256x256   | uint8   | 4        | 0.50    | subtract      | sse42  | 0.005564 | 0.000269   | 20.70x  | -95.17%        |
| 256x256   | uint8   | 4        | 0.50    | subtract      | avx2   | 0.005564 | 0.000198   | 28.15x  | -96.45%        |
| 256x256   | uint8   | 4        | 0.50    | grain_extract | scalar | 0.005604 | 0.001861   | 3.01x   | -66.79%        |
| 256x256   | uint8   | 4        | 0.50    | grain_extract | sse42  | 0.005604 | 0.000217   | 25.78x  | -96.12%        |
| 256x256   | uint8   | 4        | 0.50    | grain_extract | avx2   | 0.005604 | 0.000195   | 28.75x  | -96.52%        |
| 256x256   | uint8   | 4        | 0.50    | grain_merge   | scalar | 0.005473 | 0.001873   | 2.92x   | -65.78%        |
| 256x256   | uint8   | 4        | 0.50    | grain_merge   | sse42  | 0.005473 | 0.000213   | 25.68x  | -96.11%        |
| 256x256   | uint8   | 4        | 0.50    | grain_merge   | avx2   | 0.005473 | 0.000192   | 28.52x  | -96.49%        |
| 256x256   | uint8   | 4        | 0.50    | divide        | scalar | 0.005750 | 0.001582   | 3.63x   | -72.48%        |
| 256x256   | uint8   | 4        | 0.50    | divide        | sse42  | 0.005750 | 0.000216   | 26.59x  | -96.24%        |
| 256x256   | uint8   | 4        | 0.50    | divide        | avx2   | 0.005750 | 0.000191   | 30.11x  | -96.68%        |
| 256x256   | uint8   | 4        | 0.50    | overlay       | scalar | 0.006926 | 0.002479   | 2.79x   | -64.21%        |
| 256x256   | uint8   | 4        | 0.50    | overlay       | sse42  | 0.006926 | 0.000243   | 28.54x  | -96.50%        |
| 256x256   | uint8   | 4        | 0.50    | overlay       | avx2   | 0.006926 | 0.000194   | 35.76x  | -97.20%        |
| 256x256   | float32 | 3        | 0.50    | normal        | scalar | 0.005580 | 0.000497   | 11.22x  | -91.09%        |
| 256x256   | float32 | 3        | 0.50    | normal        | sse42  | 0.005580 | 0.000219   | 25.49x  | -96.08%        |
| 256x256   | float32 | 3        | 0.50    | normal        | avx2   | 0.005580 | 0.000141   | 39.54x  | -97.47%        |
| 256x256   | float32 | 3        | 0.50    | soft_light    | scalar | 0.008864 | 0.000618   | 14.36x  | -93.03%        |
| 256x256   | float32 | 3        | 0.50    | soft_light    | sse42  | 0.008864 | 0.000258   | 34.33x  | -97.09%        |
| 256x256   | float32 | 3        | 0.50    | soft_light    | avx2   | 0.008864 | 0.000194   | 45.67x  | -97.81%        |
| 256x256   | float32 | 3        | 0.50    | lighten_only  | scalar | 0.007150 | 0.000737   | 9.70x   | -89.69%        |
| 256x256   | float32 | 3        | 0.50    | lighten_only  | sse42  | 0.007150 | 0.000217   | 32.98x  | -96.97%        |
| 256x256   | float32 | 3        | 0.50    | lighten_only  | avx2   | 0.007150 | 0.000178   | 40.21x  | -97.51%        |
| 256x256   | float32 | 3        | 0.50    | screen        | scalar | 0.007325 | 0.000551   | 13.28x  | -92.47%        |
| 256x256   | float32 | 3        | 0.50    | screen        | sse42  | 0.007325 | 0.000240   | 30.48x  | -96.72%        |
| 256x256   | float32 | 3        | 0.50    | screen        | avx2   | 0.007325 | 0.000184   | 39.83x  | -97.49%        |
| 256x256   | float32 | 3        | 0.50    | dodge         | scalar | 0.007305 | 0.000631   | 11.59x  | -91.37%        |
| 256x256   | float32 | 3        | 0.50    | dodge         | sse42  | 0.007305 | 0.000275   | 26.53x  | -96.23%        |
| 256x256   | float32 | 3        | 0.50    | dodge         | avx2   | 0.007305 | 0.000211   | 34.63x  | -97.11%        |
| 256x256   | float32 | 3        | 0.50    | addition      | scalar | 0.007376 | 0.001557   | 4.74x   | -78.89%        |
| 256x256   | float32 | 3        | 0.50    | addition      | sse42  | 0.007376 | 0.000233   | 31.68x  | -96.84%        |
| 256x256   | float32 | 3        | 0.50    | addition      | avx2   | 0.007376 | 0.000184   | 40.09x  | -97.51%        |
| 256x256   | float32 | 3        | 0.50    | darken_only   | scalar | 0.007243 | 0.000734   | 9.86x   | -89.86%        |
| 256x256   | float32 | 3        | 0.50    | darken_only   | sse42  | 0.007243 | 0.000215   | 33.69x  | -97.03%        |
| 256x256   | float32 | 3        | 0.50    | darken_only   | avx2   | 0.007243 | 0.000177   | 40.90x  | -97.56%        |
| 256x256   | float32 | 3        | 0.50    | multiply      | scalar | 0.007388 | 0.000540   | 13.68x  | -92.69%        |
| 256x256   | float32 | 3        | 0.50    | multiply      | sse42  | 0.007388 | 0.000220   | 33.53x  | -97.02%        |
| 256x256   | float32 | 3        | 0.50    | multiply      | avx2   | 0.007388 | 0.000179   | 41.38x  | -97.58%        |
| 256x256   | float32 | 3        | 0.50    | hard_light    | scalar | 0.009079 | 0.001760   | 5.16x   | -80.62%        |
| 256x256   | float32 | 3        | 0.50    | hard_light    | sse42  | 0.009079 | 0.000279   | 32.53x  | -96.93%        |
| 256x256   | float32 | 3        | 0.50    | hard_light    | avx2   | 0.009079 | 0.000185   | 49.19x  | -97.97%        |
| 256x256   | float32 | 3        | 0.50    | difference    | scalar | 0.009052 | 0.000547   | 16.53x  | -93.95%        |
| 256x256   | float32 | 3        | 0.50    | difference    | sse42  | 0.009052 | 0.000227   | 39.82x  | -97.49%        |
| 256x256   | float32 | 3        | 0.50    | difference    | avx2   | 0.009052 | 0.000183   | 49.41x  | -97.98%        |
| 256x256   | float32 | 3        | 0.50    | subtract      | scalar | 0.007449 | 0.000692   | 10.77x  | -90.71%        |
| 256x256   | float32 | 3        | 0.50    | subtract      | sse42  | 0.007449 | 0.000241   | 30.94x  | -96.77%        |
| 256x256   | float32 | 3        | 0.50    | subtract      | avx2   | 0.007449 | 0.000185   | 40.31x  | -97.52%        |
| 256x256   | float32 | 3        | 0.50    | grain_extract | scalar | 0.007552 | 0.001002   | 7.54x   | -86.73%        |
| 256x256   | float32 | 3        | 0.50    | grain_extract | sse42  | 0.007552 | 0.000232   | 32.52x  | -96.92%        |
| 256x256   | float32 | 3        | 0.50    | grain_extract | avx2   | 0.007552 | 0.000179   | 42.08x  | -97.62%        |
| 256x256   | float32 | 3        | 0.50    | grain_merge   | scalar | 0.007136 | 0.001000   | 7.14x   | -85.99%        |
| 256x256   | float32 | 3        | 0.50    | grain_merge   | sse42  | 0.007136 | 0.000266   | 26.79x  | -96.27%        |
| 256x256   | float32 | 3        | 0.50    | grain_merge   | avx2   | 0.007136 | 0.000182   | 39.16x  | -97.45%        |
| 256x256   | float32 | 3        | 0.50    | divide        | scalar | 0.007395 | 0.000607   | 12.18x  | -91.79%        |
| 256x256   | float32 | 3        | 0.50    | divide        | sse42  | 0.007395 | 0.000263   | 28.17x  | -96.45%        |
| 256x256   | float32 | 3        | 0.50    | divide        | avx2   | 0.007395 | 0.000181   | 40.89x  | -97.55%        |
| 256x256   | float32 | 3        | 0.50    | overlay       | scalar | 0.008732 | 0.001631   | 5.35x   | -81.32%        |
| 256x256   | float32 | 3        | 0.50    | overlay       | sse42  | 0.008732 | 0.000254   | 34.38x  | -97.09%        |
| 256x256   | float32 | 3        | 0.50    | overlay       | avx2   | 0.008732 | 0.000183   | 47.81x  | -97.91%        |
| 256x256   | float32 | 4        | 0.50    | normal        | scalar | 0.004371 | 0.000616   | 7.10x   | -85.91%        |
| 256x256   | float32 | 4        | 0.50    | normal        | sse42  | 0.004371 | 0.000143   | 30.52x  | -96.72%        |
| 256x256   | float32 | 4        | 0.50    | normal        | avx2   | 0.004371 | 0.000153   | 28.49x  | -96.49%        |
| 256x256   | float32 | 4        | 0.50    | soft_light    | scalar | 0.006858 | 0.000720   | 9.52x   | -89.50%        |
| 256x256   | float32 | 4        | 0.50    | soft_light    | sse42  | 0.006858 | 0.000182   | 37.74x  | -97.35%        |
| 256x256   | float32 | 4        | 0.50    | soft_light    | avx2   | 0.006858 | 0.000182   | 37.69x  | -97.35%        |
| 256x256   | float32 | 4        | 0.50    | lighten_only  | scalar | 0.005419 | 0.000769   | 7.05x   | -85.82%        |
| 256x256   | float32 | 4        | 0.50    | lighten_only  | sse42  | 0.005419 | 0.000165   | 32.90x  | -96.96%        |
| 256x256   | float32 | 4        | 0.50    | lighten_only  | avx2   | 0.005419 | 0.000181   | 29.90x  | -96.66%        |
| 256x256   | float32 | 4        | 0.50    | screen        | scalar | 0.005604 | 0.000672   | 8.34x   | -88.01%        |
| 256x256   | float32 | 4        | 0.50    | screen        | sse42  | 0.005604 | 0.000174   | 32.25x  | -96.90%        |
| 256x256   | float32 | 4        | 0.50    | screen        | avx2   | 0.005604 | 0.000179   | 31.30x  | -96.80%        |
| 256x256   | float32 | 4        | 0.50    | dodge         | scalar | 0.005572 | 0.000751   | 7.42x   | -86.52%        |
| 256x256   | float32 | 4        | 0.50    | dodge         | sse42  | 0.005572 | 0.000216   | 25.76x  | -96.12%        |
| 256x256   | float32 | 4        | 0.50    | dodge         | avx2   | 0.005572 | 0.000189   | 29.55x  | -96.62%        |
| 256x256   | float32 | 4        | 0.50    | addition      | scalar | 0.005738 | 0.001335   | 4.30x   | -76.73%        |
| 256x256   | float32 | 4        | 0.50    | addition      | sse42  | 0.005738 | 0.000186   | 30.80x  | -96.75%        |
| 256x256   | float32 | 4        | 0.50    | addition      | avx2   | 0.005738 | 0.000185   | 31.02x  | -96.78%        |
| 256x256   | float32 | 4        | 0.50    | darken_only   | scalar | 0.005302 | 0.000759   | 6.99x   | -85.69%        |
| 256x256   | float32 | 4        | 0.50    | darken_only   | sse42  | 0.005302 | 0.000159   | 33.44x  | -97.01%        |
| 256x256   | float32 | 4        | 0.50    | darken_only   | avx2   | 0.005302 | 0.000176   | 30.16x  | -96.68%        |
| 256x256   | float32 | 4        | 0.50    | multiply      | scalar | 0.005762 | 0.000639   | 9.02x   | -88.91%        |
| 256x256   | float32 | 4        | 0.50    | multiply      | sse42  | 0.005762 | 0.000167   | 34.59x  | -97.11%        |
| 256x256   | float32 | 4        | 0.50    | multiply      | avx2   | 0.005762 | 0.000179   | 32.26x  | -96.90%        |
| 256x256   | float32 | 4        | 0.50    | hard_light    | scalar | 0.007241 | 0.001865   | 3.88x   | -74.24%        |
| 256x256   | float32 | 4        | 0.50    | hard_light    | sse42  | 0.007241 | 0.000222   | 32.58x  | -96.93%        |
| 256x256   | float32 | 4        | 0.50    | hard_light    | avx2   | 0.007241 | 0.000183   | 39.61x  | -97.48%        |
| 256x256   | float32 | 4        | 0.50    | difference    | scalar | 0.007348 | 0.000647   | 11.36x  | -91.20%        |
| 256x256   | float32 | 4        | 0.50    | difference    | sse42  | 0.007348 | 0.000173   | 42.36x  | -97.64%        |
| 256x256   | float32 | 4        | 0.50    | difference    | avx2   | 0.007348 | 0.000183   | 40.04x  | -97.50%        |
| 256x256   | float32 | 4        | 0.50    | subtract      | scalar | 0.005691 | 0.000862   | 6.60x   | -84.85%        |
| 256x256   | float32 | 4        | 0.50    | subtract      | sse42  | 0.005691 | 0.000191   | 29.83x  | -96.65%        |
| 256x256   | float32 | 4        | 0.50    | subtract      | avx2   | 0.005691 | 0.000184   | 31.01x  | -96.77%        |
| 256x256   | float32 | 4        | 0.50    | grain_extract | scalar | 0.005481 | 0.001052   | 5.21x   | -80.81%        |
| 256x256   | float32 | 4        | 0.50    | grain_extract | sse42  | 0.005481 | 0.000170   | 32.19x  | -96.89%        |
| 256x256   | float32 | 4        | 0.50    | grain_extract | avx2   | 0.005481 | 0.000182   | 30.09x  | -96.68%        |
| 256x256   | float32 | 4        | 0.50    | grain_merge   | scalar | 0.005445 | 0.001059   | 5.14x   | -80.56%        |
| 256x256   | float32 | 4        | 0.50    | grain_merge   | sse42  | 0.005445 | 0.000178   | 30.52x  | -96.72%        |
| 256x256   | float32 | 4        | 0.50    | grain_merge   | avx2   | 0.005445 | 0.000181   | 30.04x  | -96.67%        |
| 256x256   | float32 | 4        | 0.50    | divide        | scalar | 0.005570 | 0.000705   | 7.90x   | -87.34%        |
| 256x256   | float32 | 4        | 0.50    | divide        | sse42  | 0.005570 | 0.000179   | 31.09x  | -96.78%        |
| 256x256   | float32 | 4        | 0.50    | divide        | avx2   | 0.005570 | 0.000180   | 30.95x  | -96.77%        |
| 256x256   | float32 | 4        | 0.50    | overlay       | scalar | 0.007038 | 0.001723   | 4.08x   | -75.52%        |
| 256x256   | float32 | 4        | 0.50    | overlay       | sse42  | 0.007038 | 0.000183   | 38.42x  | -97.40%        |
| 256x256   | float32 | 4        | 0.50    | overlay       | avx2   | 0.007038 | 0.000177   | 39.77x  | -97.49%        |
| 512x512   | uint8   | 3        | 0.50    | normal        | scalar | 0.032664 | 0.006359   | 5.14x   | -80.53%        |
| 512x512   | uint8   | 3        | 0.50    | normal        | sse42  | 0.032664 | 0.002753   | 11.86x  | -91.57%        |
| 512x512   | uint8   | 3        | 0.50    | normal        | avx2   | 0.032664 | 0.002755   | 11.86x  | -91.56%        |
| 512x512   | uint8   | 3        | 0.00    | normal        | scalar | 0.032962 | 0.002511   | 13.12x  | -92.38%        |
| 512x512   | uint8   | 3        | 0.00    | normal        | sse42  | 0.032962 | 0.002717   | 12.13x  | -91.76%        |
| 512x512   | uint8   | 3        | 0.00    | normal        | avx2   | 0.032962 | 0.002575   | 12.80x  | -92.19%        |
| 512x512   | uint8   | 3        | 1.00    | normal        | scalar | 0.032701 | 0.002452   | 13.34x  | -92.50%        |
| 512x512   | uint8   | 3        | 1.00    | normal        | sse42  | 0.032701 | 0.002444   | 13.38x  | -92.53%        |
| 512x512   | uint8   | 3        | 1.00    | normal        | avx2   | 0.032701 | 0.002456   | 13.32x  | -92.49%        |
| 512x512   | uint8   | 3        | 0.50    | soft_light    | scalar | 0.043186 | 0.007168   | 6.03x   | -83.40%        |
| 512x512   | uint8   | 3        | 0.50    | soft_light    | sse42  | 0.043186 | 0.003435   | 12.57x  | -92.05%        |
| 512x512   | uint8   | 3        | 0.50    | soft_light    | avx2   | 0.043186 | 0.003369   | 12.82x  | -92.20%        |
| 512x512   | uint8   | 3        | 0.00    | soft_light    | scalar | 0.042857 | 0.002442   | 17.55x  | -94.30%        |
| 512x512   | uint8   | 3        | 0.00    | soft_light    | sse42  | 0.042857 | 0.002439   | 17.57x  | -94.31%        |
| 512x512   | uint8   | 3        | 0.00    | soft_light    | avx2   | 0.042857 | 0.002448   | 17.51x  | -94.29%        |
| 512x512   | uint8   | 3        | 1.00    | soft_light    | scalar | 0.042902 | 0.007092   | 6.05x   | -83.47%        |
| 512x512   | uint8   | 3        | 1.00    | soft_light    | sse42  | 0.042902 | 0.003376   | 12.71x  | -92.13%        |
| 512x512   | uint8   | 3        | 1.00    | soft_light    | avx2   | 0.042902 | 0.003155   | 13.60x  | -92.65%        |
| 512x512   | uint8   | 3        | 0.50    | lighten_only  | scalar | 0.036846 | 0.007739   | 4.76x   | -79.00%        |
| 512x512   | uint8   | 3        | 0.50    | lighten_only  | sse42  | 0.036846 | 0.003063   | 12.03x  | -91.69%        |
| 512x512   | uint8   | 3        | 0.50    | lighten_only  | avx2   | 0.036846 | 0.003028   | 12.17x  | -91.78%        |
| 512x512   | uint8   | 3        | 0.00    | lighten_only  | scalar | 0.036623 | 0.002479   | 14.77x  | -93.23%        |
| 512x512   | uint8   | 3        | 0.00    | lighten_only  | sse42  | 0.036623 | 0.002485   | 14.74x  | -93.21%        |
| 512x512   | uint8   | 3        | 0.00    | lighten_only  | avx2   | 0.036623 | 0.002544   | 14.40x  | -93.05%        |
| 512x512   | uint8   | 3        | 1.00    | lighten_only  | scalar | 0.036539 | 0.007693   | 4.75x   | -78.95%        |
| 512x512   | uint8   | 3        | 1.00    | lighten_only  | sse42  | 0.036539 | 0.003188   | 11.46x  | -91.27%        |
| 512x512   | uint8   | 3        | 1.00    | lighten_only  | avx2   | 0.036539 | 0.002953   | 12.38x  | -91.92%        |
| 512x512   | uint8   | 3        | 0.50    | screen        | scalar | 0.037287 | 0.007076   | 5.27x   | -81.02%        |
| 512x512   | uint8   | 3        | 0.50    | screen        | sse42  | 0.037287 | 0.003285   | 11.35x  | -91.19%        |
| 512x512   | uint8   | 3        | 0.50    | screen        | avx2   | 0.037287 | 0.003154   | 11.82x  | -91.54%        |
| 512x512   | uint8   | 3        | 0.00    | screen        | scalar | 0.038598 | 0.002465   | 15.66x  | -93.61%        |
| 512x512   | uint8   | 3        | 0.00    | screen        | sse42  | 0.038598 | 0.002453   | 15.73x  | -93.64%        |
| 512x512   | uint8   | 3        | 0.00    | screen        | avx2   | 0.038598 | 0.002452   | 15.74x  | -93.65%        |
| 512x512   | uint8   | 3        | 1.00    | screen        | scalar | 0.038291 | 0.006831   | 5.61x   | -82.16%        |
| 512x512   | uint8   | 3        | 1.00    | screen        | sse42  | 0.038291 | 0.003159   | 12.12x  | -91.75%        |
| 512x512   | uint8   | 3        | 1.00    | screen        | avx2   | 0.038291 | 0.003032   | 12.63x  | -92.08%        |
| 512x512   | uint8   | 3        | 0.50    | dodge         | scalar | 0.037752 | 0.007065   | 5.34x   | -81.29%        |
| 512x512   | uint8   | 3        | 0.50    | dodge         | sse42  | 0.037752 | 0.003489   | 10.82x  | -90.76%        |
| 512x512   | uint8   | 3        | 0.50    | dodge         | avx2   | 0.037752 | 0.003187   | 11.85x  | -91.56%        |
| 512x512   | uint8   | 3        | 0.00    | dodge         | scalar | 0.037179 | 0.002459   | 15.12x  | -93.39%        |
| 512x512   | uint8   | 3        | 0.00    | dodge         | sse42  | 0.037179 | 0.002452   | 15.16x  | -93.40%        |
| 512x512   | uint8   | 3        | 0.00    | dodge         | avx2   | 0.037179 | 0.002465   | 15.08x  | -93.37%        |
| 512x512   | uint8   | 3        | 1.00    | dodge         | scalar | 0.037046 | 0.007060   | 5.25x   | -80.94%        |
| 512x512   | uint8   | 3        | 1.00    | dodge         | sse42  | 0.037046 | 0.003464   | 10.69x  | -90.65%        |
| 512x512   | uint8   | 3        | 1.00    | dodge         | avx2   | 0.037046 | 0.003211   | 11.54x  | -91.33%        |
| 512x512   | uint8   | 3        | 0.50    | addition      | scalar | 0.037243 | 0.010142   | 3.67x   | -72.77%        |
| 512x512   | uint8   | 3        | 0.50    | addition      | sse42  | 0.037243 | 0.003262   | 11.42x  | -91.24%        |
| 512x512   | uint8   | 3        | 0.50    | addition      | avx2   | 0.037243 | 0.003050   | 12.21x  | -91.81%        |
| 512x512   | uint8   | 3        | 0.00    | addition      | scalar | 0.036870 | 0.002529   | 14.58x  | -93.14%        |
| 512x512   | uint8   | 3        | 0.00    | addition      | sse42  | 0.036870 | 0.002482   | 14.86x  | -93.27%        |
| 512x512   | uint8   | 3        | 0.00    | addition      | avx2   | 0.036870 | 0.002518   | 14.64x  | -93.17%        |
| 512x512   | uint8   | 3        | 1.00    | addition      | scalar | 0.036626 | 0.013275   | 2.76x   | -63.76%        |
| 512x512   | uint8   | 3        | 1.00    | addition      | sse42  | 0.036626 | 0.003178   | 11.52x  | -91.32%        |
| 512x512   | uint8   | 3        | 1.00    | addition      | avx2   | 0.036626 | 0.003021   | 12.12x  | -91.75%        |
| 512x512   | uint8   | 3        | 0.50    | darken_only   | scalar | 0.036627 | 0.007679   | 4.77x   | -79.04%        |
| 512x512   | uint8   | 3        | 0.50    | darken_only   | sse42  | 0.036627 | 0.003123   | 11.73x  | -91.47%        |
| 512x512   | uint8   | 3        | 0.50    | darken_only   | avx2   | 0.036627 | 0.003037   | 12.06x  | -91.71%        |
| 512x512   | uint8   | 3        | 0.00    | darken_only   | scalar | 0.037025 | 0.002500   | 14.81x  | -93.25%        |
| 512x512   | uint8   | 3        | 0.00    | darken_only   | sse42  | 0.037025 | 0.002501   | 14.81x  | -93.25%        |
| 512x512   | uint8   | 3        | 0.00    | darken_only   | avx2   | 0.037025 | 0.002535   | 14.60x  | -93.15%        |
| 512x512   | uint8   | 3        | 1.00    | darken_only   | scalar | 0.037378 | 0.007924   | 4.72x   | -78.80%        |
| 512x512   | uint8   | 3        | 1.00    | darken_only   | sse42  | 0.037378 | 0.003105   | 12.04x  | -91.69%        |
| 512x512   | uint8   | 3        | 1.00    | darken_only   | avx2   | 0.037378 | 0.003077   | 12.15x  | -91.77%        |
| 512x512   | uint8   | 3        | 0.50    | multiply      | scalar | 0.037533 | 0.006937   | 5.41x   | -81.52%        |
| 512x512   | uint8   | 3        | 0.50    | multiply      | sse42  | 0.037533 | 0.003240   | 11.58x  | -91.37%        |
| 512x512   | uint8   | 3        | 0.50    | multiply      | avx2   | 0.037533 | 0.003026   | 12.40x  | -91.94%        |
| 512x512   | uint8   | 3        | 0.00    | multiply      | scalar | 0.036780 | 0.002619   | 14.04x  | -92.88%        |
| 512x512   | uint8   | 3        | 0.00    | multiply      | sse42  | 0.036780 | 0.002651   | 13.87x  | -92.79%        |
| 512x512   | uint8   | 3        | 0.00    | multiply      | avx2   | 0.036780 | 0.002491   | 14.76x  | -93.23%        |
| 512x512   | uint8   | 3        | 1.00    | multiply      | scalar | 0.036960 | 0.007474   | 4.95x   | -79.78%        |
| 512x512   | uint8   | 3        | 1.00    | multiply      | sse42  | 0.036960 | 0.003158   | 11.70x  | -91.46%        |
| 512x512   | uint8   | 3        | 1.00    | multiply      | avx2   | 0.036960 | 0.003044   | 12.14x  | -91.76%        |
| 512x512   | uint8   | 3        | 0.50    | hard_light    | scalar | 0.046398 | 0.011862   | 3.91x   | -74.44%        |
| 512x512   | uint8   | 3        | 0.50    | hard_light    | sse42  | 0.046398 | 0.003543   | 13.10x  | -92.36%        |
| 512x512   | uint8   | 3        | 0.50    | hard_light    | avx2   | 0.046398 | 0.003261   | 14.23x  | -92.97%        |
| 512x512   | uint8   | 3        | 0.00    | hard_light    | scalar | 0.046417 | 0.002519   | 18.43x  | -94.57%        |
| 512x512   | uint8   | 3        | 0.00    | hard_light    | sse42  | 0.046417 | 0.002527   | 18.37x  | -94.55%        |
| 512x512   | uint8   | 3        | 0.00    | hard_light    | avx2   | 0.046417 | 0.002500   | 18.57x  | -94.62%        |
| 512x512   | uint8   | 3        | 1.00    | hard_light    | scalar | 0.045839 | 0.011953   | 3.84x   | -73.92%        |
| 512x512   | uint8   | 3        | 1.00    | hard_light    | sse42  | 0.045839 | 0.003688   | 12.43x  | -91.96%        |
| 512x512   | uint8   | 3        | 1.00    | hard_light    | avx2   | 0.045839 | 0.003292   | 13.92x  | -92.82%        |
| 512x512   | uint8   | 3        | 0.50    | difference    | scalar | 0.044237 | 0.006964   | 6.35x   | -84.26%        |
| 512x512   |