png.Decode and gif.Decode hand back the source colors untouched, so a wide gamut image came out oversaturated where a jpeg, read through imaging, was converted. Real imagor passes the profile on to the client instead.
The service is thumbnails and its released variables say so; the new ones said THUMBNAIL. None of them has shipped yet, so they are renamed rather than aliased. The input file size gains the shared OC_THUMBNAILS_ name, because webdav and the generator have to agree on it.
An animated png carried every frame through imaging.Decode, which only the gif path guarded: a 465 KB file with 200 full-canvas frames allocated 226 MB, now 10 MB. png.Decode reads IDAT and ignores an animation's fdAT chunks.
imagor's own defaults are looser, so a difference could pass as agreement. With the frame limit set it truncates the animation where the built-in refuses the file, which the override now records.
The source ceiling had to stand in for the box a request may ask for, two limits with different reasons under one name. webdav decides the preview size and never asks for more than its largest resolution; this is the net for everything else.
Only the source was bounded, while fill and stretch upscale, so a tiny image asked for at 7680 allocated accordingly. The pixel budget for animations and webdav's ladder above this are a follow-up.
A cropping resize now carries imagor's smart segment, which is how the libvips
build and a real imagor keep the region that carries the image, the way main
cropped before the generator moved out. A build without libvips answers the
same request with a centre crop, and the parity suite records that.
Both builds and imagor now crop the same region, a gif is bounded by its
declared frames, the posted body by THUMBNAILS_MAX_INPUT_IMAGE_FILE_SIZE and an
image extracted from a container by the configured input size. The s3 cache
reads a miss where minio reports it.
The push endpoint's HTTP behaviour is asserted in the parity suite now, where
imagor answers the same request, so 41 duplicated service specs are gone and
the 32 that remain are what a non-default config or a helper decides.
The aspect-ignored header blamed a processor for a decision the free axis can make too. fit-in does not letterbox, it answers smaller than the box. The s3 cache checks bucket, region and credentials, not the endpoint. A mapping onto a type the generator cannot write is refused at startup instead of quietly becoming a JPEG. And a processor name nobody knows resolves like none, which three comments and a readme line still denied.
The four writable formats were a whitelist in the filter parser and a second list of content types in the encode step; they are one map now, so a format nobody writes stays a 400 and one without an encoder in this build stays a 406. The fit-in shape existed twice since the gif loop started rounding like vips, it lives in box.go now. And the push routes are back inline: mountPushRoutes only existed so a test mux could not drift from them, and the specs go through the real NewService instead.
With previews off a thumbnail path answered 405, because the REPORT patterns match the same paths and the GET routes were left out; the routes stay now and the shared parse step answers 404, which is what the setting promises. The deprecated cache directory is read again: its default no longer sits in the config struct, where it made the fallback unreachable. A processor nobody knows is treated like none, in the parser too. A comma-joined resolutions entry in yaml keeps working. Plus the comments and the readme line that described something else than the code does.
The thumbnails service kept a single map of supported mime types that nothing in that service used any more, only webdav read it, and the comment still claimed reva reads it there. It is webdav's announcement, so DefaultSourceTypes lives next to SourceTypes now, the leftover package is gone, and no webdav file imports the thumbnails service any longer.
A processor that needs the whole box is not a cropping one, so targetsExactBox says what it decides. An output format without an encoder answers 406 like every other thing this build cannot write. The configured defaults are no longer seeded twice, the constructors merge them anyway, like they do for the source types. And webp needs no build tag: both builds read it, the pure Go one answers the JPEG the mapping asks for, which the parity suite pins.
The dimension guard and the input-format detection used Go's decoder registry in both builds, so with libvips they were silently skipped for everything Go cannot read, and a webp source came back as a JPEG where imagor keeps it a webp. Each build now reads the header with the library that will decode the image, the guard runs on that, and the animated gif path is shared instead of living in both encoders. The hook names stay per build, the function variables are typed per build, so the any and its seven type assertions are gone.
The push endpoint takes an empty filters segment, ignores an image key behind the box and computes a side given as 0 from the other one, all measured against both imagors and pinned in the parity suite. Plus the review leftovers: no em dashes, the request test on ginkgo, comments that contradicted the code, one stat for Head and generate, one thumbnail handler, the gif fit-in rounding of vips, and the new env names last so they win.
The same image goes to the built-in generator and to both imagor images,
and status, dimensions, format, frames and alpha are compared. A generator
that answers differently is held to that answer by a recorded override, so
the test fails once the divergence disappears. It runs once per resize
implementation, because make test builds the pure Go path while the images
ship vips.
The gif loop is shared by both builds, the executor takes a request
struct, the route table halves, the aspect flag is a pointer instead of
two bools, and the cache's budget is named after what it is.
The upload part has to be typed, a malformed request is a 400 while a
source it cannot read is a 406, an animated gif flattens when the output
is still, a still source can be written as a gif, libvips writes webp, and
fit-in rounds the way vips and imagor round it.
The imaging build decodes the full pixel buffer from the header-declared
dimensions before the existing MaxInputWidth/MaxInputHeight guard runs, so a
tiny crafted file whose header declares huge dimensions forces a multi-GB
allocation and can OOM the worker. Read the header with DecodeConfig and reject
oversized sources before the decode allocates, in both the imaging and vips
builds, and thread the limit through the audio cover-art and geogebra decoders
that decode a second attacker-controlled image.
Extend the list by these resolutions:
"500x280", "280x500", "1000x560", "560x1000", "512x2048"
This allows for a more granular thumbnail generation for requested previews
between 128 and 1080 px.
The libvips generator passed 0 as the target height to vips_thumbnail,
so the value was rejected and dropped.
As a result, a preview of a 4000x5000 portrait image requested with e.g.
x=500&y=500 returned a ...x1920 image instead of a ...x1080 one, which
would be the next correct size in the pre-defined resolutions list. This
was due to the missing height, so it used the width (=1920) to determine
the longest side.
This also aligns it with the non-libvips behavior.