Commit graph

19 commits

Author SHA1 Message Date
Nico Weber
9875ce0c78 LibPDF: Reorder loops in SampledFunction::evaluate()
Previously, we'd loop over the index of the output coordinate,
for example for a CMYK->RGB function, we'd loop over RGB. For
every output index, we'd then sample the function at the CMYK
input point.

Now, we sample at CMYK once and return a span for all outputs,
since they're stored in contiguous memory. And we then loop
over the outputs only to do weighting and mapping to the target
range at the end.

Reduces the runtime of

      (cd Tests/LibPDF; \
          ../../Build/lagom/bin/BenchmarkPDF --benchmark_repetitions 5)

from 235.6±2.3ms to 103.2±3.3ms on my system, and makes
SampledFunction::evaluate() more similar to lerp_nd() in TagTypes.h.
2024-02-13 19:45:19 +01:00
Nico Weber
9fc47345ce LibGfx+LibPDF: Make sample() functions take ReadonlySpan<>
...instead of Vector<>.

No behavior (or performance) change.
2024-02-06 08:44:53 +01:00
Nico Weber
f562c470e2 LibGfx+LibPDF: Simpler and faster N-D linear sampling
Previously, if we wanted to to e.g. do linear interpolation in 2-D,
we'd get a sample point like (1.3, 4.4), then get 4 samples around
it at (1, 4), (2, 4), (1, 5), (2, 5), then reduce the 4 samples
to 2 samples by computing the combined samples
`0.3 * f(1, 4) + 0.7 * f(2, 4)` and `0.3 * f(1, 5) + 0.8 * f(2, 5)`,
and then 1-D linearly blending between these two samples with the
factor 0.4. In the end we'd multiply the first value by 0.3 * 0.4,
the second by 0.7 * 0.4, the third by 0.3 * 0.6, and the third by
0.7 * 0.6, and then sum them all up.

This requires computing and storing 2**N samples, followed by
another 2**N iterations to combine the 2**N sampls to a single value.
(N is in practice either 4 or 3, so 2**N isn't super huge.)

Instead, for every sample we can directly compute the product of
weights and sum them up directly. This lets us omit the second loop
and storing 2**N values, in exchange for doing an additional O(n)
work to compute the product.

Takes

    Build/lagom/bin/image --no-output --invert-cmyk \
        --assign-color-profile \
            Build/lagom/Root/res/icc/Adobe/CMYK/USWebCoatedSWOP.icc \
        --convert-to-color-profile serenity-sRGB.icc \
        cmyk.jpg

form 3.42s to 3.08s on my machine, almost 10% faster (and less code).

Here cmyk.jpg is a 2253x3080 cmyk jpeg, and USWebCoatedSWOP.icc is an
mft2 profile with input tables with 256 samples and a 9x9x9x9 CLUT.

The LibPDF change is covered by TEST_CASE(sampled) in LibPDF.cpp,
and the LibGfx change is basically the same change as the one in
LibPDF (where the test results don't change) and the output
subjectively looks identical. So hopefully this causes indeed no
behavior change :^)
2024-02-04 21:49:23 +01:00
Nico Weber
f157cd50a1 LibPDF: Use mix() in SampledFunction::evaluate()
No behavior change.
2024-01-04 21:12:23 +01:00
Nico Weber
1c88b82dfc LibPDF: Do less work in SampledFunction::evaluate()'s inner loop
Instead of recomputing the left index and the float amount in that
interval for each coordinate all the time, do it once when we
preprocess the input coordinates.

One line less, faster, and arguably easier to read.

No behavior change.
2023-12-02 22:26:13 +01:00
Nico Weber
54883b7d41 LibPDF: Remove get_bounds lambda in SampledFunction::evaluate()
Using `min()` to guarantee the left index is never == `size() - 1`,
even for an interpolation value of 1.0, is less code, and arguably
easier to understand as well.

No behavior change.
2023-12-02 22:26:13 +01:00
Nico Weber
d9fd72007e LibPDF: Add a spec comment to SampledFunction::sample() 2023-12-02 22:26:13 +01:00
Nico Weber
f4a847894f LibPDF: Make SampledFunction::evaluate() work for n-dimensional input
I didn't find example code for this and the AI assistant did very
poorly on this as well. So I had to write it all by myself!

It can be much more efficient I think, but I think the overall
shape is maybe roughly fine.
2023-11-12 07:55:04 +01:00
Nico Weber
a9ef65e64a LibPDF: For multi-output SampledFunctions, fix output colors
For N outputs, the outputs aren't stored in N independent planes.
Instead, N output values are stored right next to each other in
the stream data.
2023-11-11 08:55:37 +01:00
Nico Weber
ec739460e0 LibPDF: Add test for SampledFunction and fix bugs found by it
* SampledFunction now keeps the StreamObject it gets data from alive
  (doesn't matter too much in practice, but does matter in the test,
  where nothing else keeps the stream alive).

* If a sample is an integer, we would previously sample that value
  twice and then divide by zero when interpolating. Make sure to
  sample 1 unit apart.
2023-11-11 08:55:37 +01:00
Nico Weber
323ba7404c LibPDF: Implement SampledFunction::evaluate() for some sampled functions
Things now work for functions that are all of:
* linear
* 1-D input
* 8 bits per sample
2023-11-10 15:03:30 +00:00
Nico Weber
fd1876441a LibPDF: Implement SampledFunction::create() 2023-11-10 15:03:30 +00:00
Nico Weber
cd9f4655ec LibPDF: Tweak implementation of postscript roll op
Since positive offsets roll to the right, it makes more sense
to do the big reverse first. Gets rid of an awkward minus sign.

No behavior change.
2023-11-10 14:45:38 +01:00
Nico Weber
b23ed86889 LibPDF: Implement StitchingFunction::evaluate() 2023-11-10 14:45:16 +01:00
Nico Weber
ba34ddeb21 LibPDF: Implement StitchingFunction creation 2023-11-10 14:45:16 +01:00
Nico Weber
80eec1e16b LibPDF: Implement PostScriptCalculatorFunction
Includes a tokenizer and interpreter for the subset of PostScript
supported in PDF type 4 functions.
2023-11-09 16:06:25 +01:00
Nico Weber
bbd86ee4f3 LibPDF: Implement ExponentialInterpolationFunction 2023-11-06 10:01:05 +01:00
Nico Weber
1aed465efe LibPDF: Implement Fuction::create() 2023-11-06 10:01:05 +01:00
Nico Weber
9204252d02 LibPDF: Add scaffolding for function objects
See PDF 1.7 Spec, "3.9 Functions".
2023-11-06 10:01:05 +01:00