63da850e3b
The reference port recomputed the inverse DCT cosine tables once per output pixel — for a 32x24 decode that's ~43k redundant cos() calls. This change lifts the tables out of the inner loop and caches them across decodes keyed by (size, componentCount) so subsequent placeholders at the same target size skip cosine evaluation entirely. Additional wins: - AC coefficients unpack into pre-sized DoubleArrays (no ArrayList<Double> boxing, no post-hoc copy) - truncated hashes are rejected up-front via a single length check instead of mid-stream - LPQA -> sRGB uses an inline branch clamp instead of a min/max/round/coerceIn chain Tests: 12 unit tests cover determinism across repeated decodes, cache clearing, alpha preservation, aspect-ratio preservation both directions, average-color drift bounds, truncated input rejection, and round-tripping base64 vs. raw bytes. All green. Bench: new ThumbHashBenchmark exercises opaque decode, alpha decode, warm- and cold-cache decode, aspect-ratio probe, and the full decodeKeepAspectRatio pipeline so the perf delta is visible on device.