mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-11 16:24:11 +01:00
Thumbs: Enhance embedding of ICC profiles based on InteropIndex #5178
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# PhotoPrism® Repository Guidelines
|
||||
|
||||
**Last Updated:** November 21, 2025
|
||||
**Last Updated:** November 23, 2025
|
||||
|
||||
## Purpose
|
||||
|
||||
@@ -212,6 +212,7 @@ Note: Across our public documentation, official images, and in production, the c
|
||||
## Code Style & Lint
|
||||
|
||||
- Go: run `make fmt-go swag-fmt` to reformat the backend code + Swagger annotations (see `Makefile` for additional targets)
|
||||
- Run `make lint-go` (golangci-lint) after Go changes; prefer `golangci-lint run ./internal/<pkg>/...` for focused edits.
|
||||
- Doc comments for packages and exported identifiers must be complete sentences that begin with the name of the thing being described and end with a period.
|
||||
- All newly added functions, including unexported helpers, must have a concise doc comment that explains their behavior.
|
||||
- For short examples inside comments, indent code rather than using backticks; godoc treats indented blocks as preformatted.
|
||||
|
||||
@@ -1,54 +1,122 @@
|
||||
================================================================================
|
||||
================================================================================
|
||||
|
||||
Files: compatibleWithAdobeRGB1998.icc
|
||||
Source: Debian icc-profiles-free
|
||||
https://salsa.debian.org/debian/icc-profiles-free/-/tree/a7a3c11b8a6d3bc2937447183b87dc89de9d2388/icc-profiles-openicc/default_profiles/base
|
||||
Copyright: Kai-Uwe Behrmann <www.behrmann.name>
|
||||
Marti Maria <www.littlecms.com>
|
||||
Photogamut <www.photogamut.org>
|
||||
Graeme Gill <www.argyllcms.com>
|
||||
ColorSolutions <www.basICColor.com>
|
||||
License: Zlib
|
||||
Third-Party ICC Profiles for PhotoPrism
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
The following 3rd-party ICC profiles may be used by or distributed with
|
||||
PhotoPrism. Any information relevant to third-party vendors listed below are
|
||||
collected using common, reasonable means.
|
||||
|
||||
Date generated: 2025-11-23
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Files: a98.icc (compatibleWithAdobeRGB1998.icc)
|
||||
Source: OpenICC "compatibleWithAdobeRGB1998.icc" via Debian icc-profiles-free
|
||||
URL: https://salsa.debian.org/debian/icc-profiles-free/-/blob/a7a3c11b8a6d3bc2937447183b87dc89de9d2388/icc-profiles-openicc/default_profiles/base/compatibleWithAdobeRGB1998.icc
|
||||
License: zlib/libpng
|
||||
Checksum (md5): 826a1e13374e3dc34f9872f31ec028c8
|
||||
|
||||
The zlib/libpng License
|
||||
|
||||
Copyright (c) Graeme Gill <graeme@argyllcms.com>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
License: Zlib
|
||||
The zlib/libpng License
|
||||
.
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
.
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
.
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
.
|
||||
NO WARRANTY
|
||||
.
|
||||
BECAUSE THE DATA IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE DATA, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE DATA "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE DATA IS WITH YOU. SHOULD THE
|
||||
DATA PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
.
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE DATA AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE DATA (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE DATA TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
The provided ICC Profiles in the package are called DATA in the following
|
||||
statement:
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
BECAUSE THE DATA IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE DATA, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE DATA "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE DATA IS WITH YOU. SHOULD THE
|
||||
DATA PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE DATA AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE DATA (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE DATA TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Files: adobecompat-v2.icc, adobecompat-v4.icc, applecompat-v2.icc, applecompat-v4.icc, cgats001compat-v2-micro.icc, colormatchcompat-v2.icc, colormatchcompat-v4.icc, dci-p3-v4.icc, displayp3-v2-magic.icc, displayp3-v2-micro.icc, displayp3-v4.icc, displayp3compat-v2-magic.icc, displayp3compat-v2-micro.icc, displayp3compat-v4.icc, prophoto-v2-magic.icc, prophoto-v2-micro.icc, prophoto-v4.icc, rec2020-g24-v4.icc, rec2020-v2-magic.icc, rec2020-v2-micro.icc, rec2020-v4.icc, rec2020compat-v2-magic.icc, rec2020compat-v2-micro.icc, rec2020compat-v4.icc, rec601ntsc-v2-magic.icc, rec601ntsc-v2-micro.icc, rec601ntsc-v4.icc, rec601pal-v2-magic.icc, rec601pal-v2-micro.icc, rec601pal-v4.icc, rec709-v2-magic.icc, rec709-v2-micro.icc, rec709-v4.icc, scrgb-v2.icc, sgrey-v2-magic.icc, sgrey-v2-micro.icc, sgrey-v2-nano.icc, sgrey-v4.icc, srgb-v2-magic.icc, srgb-v2-micro.icc, srgb-v2-nano.icc, srgb-v4.icc, widegamutcompat-v2.icc, widegamutcompat-v4.icc
|
||||
Source: Compact-ICC-Profiles via Debian icc-profiles-free
|
||||
URL: https://salsa.debian.org/debian/icc-profiles-free/-/tree/a7a3c11b8a6d3bc2937447183b87dc89de9d2388/Compact-ICC-Profiles/profiles
|
||||
License: CC0-1.0 (Public Domain Dedication)
|
||||
Checksums (md5):
|
||||
adobecompat-v2.icc 08220aa4b4e4259ec3c446a35197d89b
|
||||
adobecompat-v4.icc fbf912760a8d14e496ff389c29c3132d
|
||||
applecompat-v2.icc 21453c734d9364abceacc7ab837019ec
|
||||
applecompat-v4.icc fc399558e27a0d53748820cff2a98a2b
|
||||
cgats001compat-v2-micro.icc 56a85233ee08fa7527875be36cf426d6
|
||||
colormatchcompat-v2.icc 9f2a755b4b3069f46f4eaef11e24926d
|
||||
colormatchcompat-v4.icc 9e119efb0abaa31e955f8a30eeabc58c
|
||||
dci-p3-v4.icc bdedf9e7ad0b93ed8f85f8c3ebdc4223
|
||||
displayp3-v2-magic.icc 6748fcfd56d38770a02c023bbd6d0529
|
||||
displayp3-v2-micro.icc 2615293123ddc4366af3da39455c3d7a
|
||||
displayp3-v4.icc 32dc35d6a113b86cbc31bd1281e3baed
|
||||
displayp3compat-v2-magic.icc 05fb82a702e27438ecec47a2f120cdcd
|
||||
displayp3compat-v2-micro.icc 2ef6c295ac5d05760c1a4cf1668951a3
|
||||
displayp3compat-v4.icc c34c90451326b183916f05b3ae41d920
|
||||
prophoto-v2-magic.icc 15a31d407cf35662fbe4513f6204bfdf
|
||||
prophoto-v2-micro.icc 445c1a3f3f1a20aab68e76838d3ed334
|
||||
prophoto-v4.icc 8f4523255234753cd2d3111d8f09b184
|
||||
rec2020-g24-v4.icc 3c7afbfff612d10a10775c167d36b51e
|
||||
rec2020-v2-magic.icc 3b5846fb69faa53ebdfd29061f17b0e3
|
||||
rec2020-v2-micro.icc 13d1e96875c35f7d5ebaf0f6a3d16357
|
||||
rec2020-v4.icc 297c644758a979abe62349ca1ff416d5
|
||||
rec2020compat-v2-magic.icc 9aa85534e81c275bc0627badf157580a
|
||||
rec2020compat-v2-micro.icc fa39fc95daece7dcaff18e7265082368
|
||||
rec2020compat-v4.icc 66839229639bb55bc443b490fe302364
|
||||
rec601ntsc-v2-magic.icc 53ee12707ac87a8e3b3452af4a325e29
|
||||
rec601ntsc-v2-micro.icc af05afec2146917a67445ac6cb5ca61d
|
||||
rec601ntsc-v4.icc cc57dd6fa3d6f08e43e0f70b5376c00a
|
||||
rec601pal-v2-magic.icc f706fd528cedcd1c88dac50073112f94
|
||||
rec601pal-v2-micro.icc d193e01949eb5347b0d16d2b3ccdabcf
|
||||
rec601pal-v4.icc dd7cd61d6ee14a521c9fb5afa2803e8e
|
||||
rec709-v2-magic.icc 47f09046656a2f0d66117a9c1b15e137
|
||||
rec709-v2-micro.icc f91edc9f3ff1390c842bd9e8759688b0
|
||||
rec709-v4.icc 0339e2a70940aefd9237311889d065e6
|
||||
scrgb-v2.icc a841263101bdf48fb9b81486f5451f2d
|
||||
sgrey-v2-magic.icc ef6221686b517e4665480639202dacd5
|
||||
sgrey-v2-micro.icc ca08451dba57ca1e910330cda37515ad
|
||||
sgrey-v2-nano.icc 57d72e3f6437d65c618ebcdb1f6fa1bd
|
||||
sgrey-v4.icc b93b1e31e75243ea08fbdab9e82f7cbe
|
||||
srgb-v2-magic.icc 5967f401f9a54913a283942710cef93c
|
||||
srgb-v2-micro.icc e8de3a5b44d70610306b0a20225701d1
|
||||
srgb-v2-nano.icc e060a57b7a057f7f0bdb859b68db60e9
|
||||
srgb-v4.icc 3c6a277ddee033ad090ba22b8323dcaf
|
||||
widegamutcompat-v2.icc 63c0165987ee94c8c7f2c68749e0418d
|
||||
widegamutcompat-v4.icc 4ccb168ae2f7daa51d3cef7df6fa6f16
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
BIN
assets/profiles/icc/adobecompat-v2.icc
Normal file
BIN
assets/profiles/icc/adobecompat-v2.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/adobecompat-v4.icc
Normal file
BIN
assets/profiles/icc/adobecompat-v4.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/applecompat-v2.icc
Normal file
BIN
assets/profiles/icc/applecompat-v2.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/applecompat-v4.icc
Normal file
BIN
assets/profiles/icc/applecompat-v4.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/cgats001compat-v2-micro.icc
Normal file
BIN
assets/profiles/icc/cgats001compat-v2-micro.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/colormatchcompat-v2.icc
Normal file
BIN
assets/profiles/icc/colormatchcompat-v2.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/colormatchcompat-v4.icc
Normal file
BIN
assets/profiles/icc/colormatchcompat-v4.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/dci-p3-v4.icc
Normal file
BIN
assets/profiles/icc/dci-p3-v4.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/displayp3-v2-magic.icc
Normal file
BIN
assets/profiles/icc/displayp3-v2-magic.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/displayp3-v2-micro.icc
Normal file
BIN
assets/profiles/icc/displayp3-v2-micro.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/displayp3-v4.icc
Normal file
BIN
assets/profiles/icc/displayp3-v4.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/displayp3compat-v2-magic.icc
Normal file
BIN
assets/profiles/icc/displayp3compat-v2-magic.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/displayp3compat-v2-micro.icc
Normal file
BIN
assets/profiles/icc/displayp3compat-v2-micro.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/displayp3compat-v4.icc
Normal file
BIN
assets/profiles/icc/displayp3compat-v4.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/prophoto-v2-magic.icc
Normal file
BIN
assets/profiles/icc/prophoto-v2-magic.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/prophoto-v2-micro.icc
Normal file
BIN
assets/profiles/icc/prophoto-v2-micro.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/prophoto-v4.icc
Normal file
BIN
assets/profiles/icc/prophoto-v4.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/rec2020-g24-v4.icc
Normal file
BIN
assets/profiles/icc/rec2020-g24-v4.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/rec2020-v2-magic.icc
Normal file
BIN
assets/profiles/icc/rec2020-v2-magic.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/rec2020-v2-micro.icc
Normal file
BIN
assets/profiles/icc/rec2020-v2-micro.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/rec2020-v4.icc
Normal file
BIN
assets/profiles/icc/rec2020-v4.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/rec2020compat-v2-magic.icc
Normal file
BIN
assets/profiles/icc/rec2020compat-v2-magic.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/rec2020compat-v2-micro.icc
Normal file
BIN
assets/profiles/icc/rec2020compat-v2-micro.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/rec2020compat-v4.icc
Normal file
BIN
assets/profiles/icc/rec2020compat-v4.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/rec601ntsc-v2-magic.icc
Normal file
BIN
assets/profiles/icc/rec601ntsc-v2-magic.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/rec601ntsc-v2-micro.icc
Normal file
BIN
assets/profiles/icc/rec601ntsc-v2-micro.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/rec601ntsc-v4.icc
Normal file
BIN
assets/profiles/icc/rec601ntsc-v4.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/rec601pal-v2-magic.icc
Normal file
BIN
assets/profiles/icc/rec601pal-v2-magic.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/rec601pal-v2-micro.icc
Normal file
BIN
assets/profiles/icc/rec601pal-v2-micro.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/rec601pal-v4.icc
Normal file
BIN
assets/profiles/icc/rec601pal-v4.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/rec709-v2-magic.icc
Normal file
BIN
assets/profiles/icc/rec709-v2-magic.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/rec709-v2-micro.icc
Normal file
BIN
assets/profiles/icc/rec709-v2-micro.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/rec709-v4.icc
Normal file
BIN
assets/profiles/icc/rec709-v4.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/scrgb-v2.icc
Normal file
BIN
assets/profiles/icc/scrgb-v2.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/sgrey-v2-magic.icc
Normal file
BIN
assets/profiles/icc/sgrey-v2-magic.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/sgrey-v2-micro.icc
Normal file
BIN
assets/profiles/icc/sgrey-v2-micro.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/sgrey-v2-nano.icc
Normal file
BIN
assets/profiles/icc/sgrey-v2-nano.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/sgrey-v4.icc
Normal file
BIN
assets/profiles/icc/sgrey-v4.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/srgb-v2-magic.icc
Normal file
BIN
assets/profiles/icc/srgb-v2-magic.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/srgb-v2-micro.icc
Normal file
BIN
assets/profiles/icc/srgb-v2-micro.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/srgb-v2-nano.icc
Normal file
BIN
assets/profiles/icc/srgb-v2-nano.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/srgb-v4.icc
Normal file
BIN
assets/profiles/icc/srgb-v4.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/widegamutcompat-v2.icc
Normal file
BIN
assets/profiles/icc/widegamutcompat-v2.icc
Normal file
Binary file not shown.
BIN
assets/profiles/icc/widegamutcompat-v4.icc
Normal file
BIN
assets/profiles/icc/widegamutcompat-v4.icc
Normal file
Binary file not shown.
65
internal/thumb/README.md
Normal file
65
internal/thumb/README.md
Normal file
@@ -0,0 +1,65 @@
|
||||
## PhotoPrism — Thumbnails Package
|
||||
|
||||
**Last Updated:** November 23, 2025
|
||||
|
||||
### Overview
|
||||
|
||||
`internal/thumb` builds thumbnails with libvips, handling resize/crop options, color management, metadata stripping, and format export (JPEG/PNG). It is used by PhotoPrism’s workers and CLI to generate cached thumbs consistently.
|
||||
|
||||
### Context & Constraints
|
||||
|
||||
- Uses libvips via govips; initialization is centralized in `VipsInit`.
|
||||
- Works on files or in-memory buffers; writes outputs with `fs.ModeFile`.
|
||||
- ICC handling: if a JPEG lacks an embedded profile but sets EXIF `InteroperabilityIndex` (`R03`/Adobe RGB, `R98`/sRGB, `THM`/thumbnail), we embed an Adobe-compatible profile; otherwise we leave color untouched.
|
||||
- Metadata is removed from outputs to keep thumbs small.
|
||||
|
||||
### Goals
|
||||
|
||||
- Produce consistent thumbnails for all configured sizes and resample modes.
|
||||
- Preserve color fidelity when cameras signal color space through EXIF interop tags.
|
||||
- Keep error paths non-fatal: invalid sizes, missing files, or absent profiles should return errors (not panics).
|
||||
|
||||
### Non-Goals
|
||||
|
||||
- Serving or caching thumbnails (handled elsewhere).
|
||||
- Full ICC workflow management; only minimal embedding for interop-index cases.
|
||||
|
||||
### Package Layout (Code Map)
|
||||
|
||||
- `vips.go` — main `Vips` entry: load, resize/crop, strip metadata, export.
|
||||
- `vips_icc.go` — EXIF InteroperabilityIndex handling and ICC embedding.
|
||||
- `icc.go` — lists bundled ICC filenames (`IccProfiles`) and `GetIccProfile` helper.
|
||||
- `resample.go`, `sizes.go` — resample options and predefined sizes.
|
||||
- `thumb.go` and helpers — naming, caching, file info.
|
||||
- Tests live alongside sources (`*_test.go`, fixtures under `testdata/`).
|
||||
|
||||
### ICC & Interop Handling
|
||||
|
||||
- EXIF `InteroperabilityIndex` codes we honor (per EXIF TagNames and regex.info):
|
||||
- `R03` → Adobe RGB (1998) compatible (`a98.icc`, etc.)
|
||||
- `R98` → sRGB (assumed default; no embed)
|
||||
- `THM` → Thumbnail (treated as sRGB; no embed)
|
||||
- If an ICC profile already exists, we skip embedding.
|
||||
- Test Files:
|
||||
- `testdata/interop_index.jpg` — R03 interop tag, no ICC (expects Adobe profile embed).
|
||||
- `testdata/interop_index_srgb_icc.jpg` — R03 tag with embedded ICC (must remain unchanged).
|
||||
- `testdata/interop_index_r98.jpg` — R98 interop tag, no ICC (should stay sRGB without embedding).
|
||||
- `testdata/interop_index_thm.jpg` — THM interop tag, no ICC (thumbnail; should remain unchanged).
|
||||
- References:
|
||||
- [EXIF TagNames (InteroperabilityIndex)](https://unpkg.com/exiftool-vendored.pl@10.50.0/bin/html/TagNames/EXIF.html)
|
||||
- [Digital-Image Color Spaces: Recommendations and Links](https://regex.info/blog/photo-tech/color-spaces-page7)
|
||||
|
||||
### Tests
|
||||
|
||||
- Fast scoped: `go test ./internal/thumb -run 'Icc|Vips' -count=1`
|
||||
- Full: `go test ./internal/thumb -count=1`
|
||||
|
||||
### Lint & Formatting
|
||||
|
||||
- Format: `make fmt-go`
|
||||
- Lint: `make lint-go` or `golangci-lint run ./internal/thumb/...`
|
||||
|
||||
### Notes
|
||||
|
||||
- When adding ICC files, place them in `assets/profiles/icc/` and append to `IccProfiles`.
|
||||
- Comments for exported identifiers must start with the identifier name (Go style).
|
||||
@@ -1,21 +1,189 @@
|
||||
package thumb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
/*
|
||||
Possible TODO: move this into a shared pkg/ so non-thumb
|
||||
consumers can also use it. However, it looks fiddly to hook that
|
||||
up to `assets`, so I'm punting on that for now.
|
||||
*/
|
||||
// Standard ICC profiles located in "assets/profiles/icc".
|
||||
const (
|
||||
// IccAdobeRGBCompat is compatible with Adobe RGB (1998).
|
||||
IccAdobeRGBCompat = "a98.icc"
|
||||
|
||||
func MustGetAdobeRGB1998Path() string {
|
||||
p := path.Join(IccProfilesPath, "adobe_rgb_compat.icc")
|
||||
_, err := os.Stat(p)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return p
|
||||
// IccAdobeRGBCompatV2 is A98C (Adobe RGB 1998 compatible, ICC v2).
|
||||
IccAdobeRGBCompatV2 = "adobecompat-v2.icc"
|
||||
// IccAdobeRGBCompatV4 is A98C (Adobe RGB 1998 compatible, ICC v4).
|
||||
IccAdobeRGBCompatV4 = "adobecompat-v4.icc"
|
||||
|
||||
// IccAppleCompatV2 is APLC (Apple Color Matching compatible, ICC v2).
|
||||
IccAppleCompatV2 = "applecompat-v2.icc"
|
||||
// IccAppleCompatV4 is APLC (Apple Color Matching compatible, ICC v4).
|
||||
IccAppleCompatV4 = "applecompat-v4.icc"
|
||||
|
||||
// IccCgats001CompatV2Micro is uCMY (CGATS.001 compatible CMY, compact).
|
||||
IccCgats001CompatV2Micro = "cgats001compat-v2-micro.icc"
|
||||
|
||||
// IccColorMatchCompatV2 is ACMC (ColorMatch RGB compatible, ICC v2).
|
||||
IccColorMatchCompatV2 = "colormatchcompat-v2.icc"
|
||||
// IccColorMatchCompatV4 is ACMC (ColorMatch RGB compatible, ICC v4).
|
||||
IccColorMatchCompatV4 = "colormatchcompat-v4.icc"
|
||||
|
||||
// IccDciP3V4 is TP3 (DCI‑P3).
|
||||
IccDciP3V4 = "dci-p3-v4.icc"
|
||||
|
||||
// IccDisplayP3V2Magic is sP3 (Display P3, ICC v2 magic).
|
||||
IccDisplayP3V2Magic = "displayp3-v2-magic.icc"
|
||||
// IccDisplayP3V2Micro is uP3 (Display P3, micro).
|
||||
IccDisplayP3V2Micro = "displayp3-v2-micro.icc"
|
||||
// IccDisplayP3V4 is sP3 (Display P3, ICC v4).
|
||||
IccDisplayP3V4 = "displayp3-v4.icc"
|
||||
|
||||
// IccDisplayP3CompatV2Magic is sP3C (Display P3 compatible, ICC v2 magic).
|
||||
IccDisplayP3CompatV2Magic = "displayp3compat-v2-magic.icc"
|
||||
// IccDisplayP3CompatV2Micro is uP3C (Display P3 compatible, micro).
|
||||
IccDisplayP3CompatV2Micro = "displayp3compat-v2-micro.icc"
|
||||
// IccDisplayP3CompatV4 is sP3C (Display P3 compatible, ICC v4).
|
||||
IccDisplayP3CompatV4 = "displayp3compat-v4.icc"
|
||||
|
||||
// IccProPhotoV2Magic is uROM (ProPhoto RGB compact).
|
||||
IccProPhotoV2Magic = "prophoto-v2-magic.icc"
|
||||
// IccProPhotoV2Micro is uROM (ProPhoto RGB micro).
|
||||
IccProPhotoV2Micro = "prophoto-v2-micro.icc"
|
||||
// IccProPhotoV4 is ROMM (ProPhoto/ROMM RGB, ICC v4).
|
||||
IccProPhotoV4 = "prophoto-v4.icc"
|
||||
|
||||
// IccRec2020Gamma24V4 is 2024 (Rec.2020 gamma 2.4, ICC v4).
|
||||
IccRec2020Gamma24V4 = "rec2020-g24-v4.icc"
|
||||
// IccRec2020V2Magic is 2020 (Rec.2020, ICC v2 magic).
|
||||
IccRec2020V2Magic = "rec2020-v2-magic.icc"
|
||||
// IccRec2020V2Micro is u202 (Rec.2020 micro).
|
||||
IccRec2020V2Micro = "rec2020-v2-micro.icc"
|
||||
// IccRec2020V4 is 2020 (Rec.2020, ICC v4).
|
||||
IccRec2020V4 = "rec2020-v4.icc"
|
||||
|
||||
// IccRec2020CompatV2Magic is 202C (Rec.2020 compatible, ICC v2 magic).
|
||||
IccRec2020CompatV2Magic = "rec2020compat-v2-magic.icc"
|
||||
// IccRec2020CompatV2Micro is u20C (Rec.2020 compatible, micro).
|
||||
IccRec2020CompatV2Micro = "rec2020compat-v2-micro.icc"
|
||||
// IccRec2020CompatV4 is 202C (Rec.2020 compatible, ICC v4).
|
||||
IccRec2020CompatV4 = "rec2020compat-v4.icc"
|
||||
|
||||
// IccRec601NtscV2Magic is R601 (Rec.601 NTSC, ICC v2 magic).
|
||||
IccRec601NtscV2Magic = "rec601ntsc-v2-magic.icc"
|
||||
// IccRec601NtscV2Micro is u601 (Rec.601 NTSC, micro).
|
||||
IccRec601NtscV2Micro = "rec601ntsc-v2-micro.icc"
|
||||
// IccRec601NtscV4 is R601 (Rec.601 NTSC, ICC v4).
|
||||
IccRec601NtscV4 = "rec601ntsc-v4.icc"
|
||||
|
||||
// IccRec601PalV2Magic is 601P (Rec.601 PAL, ICC v2 magic).
|
||||
IccRec601PalV2Magic = "rec601pal-v2-magic.icc"
|
||||
// IccRec601PalV2Micro is u60P (Rec.601 PAL, micro).
|
||||
IccRec601PalV2Micro = "rec601pal-v2-micro.icc"
|
||||
// IccRec601PalV4 is 601P (Rec.601 PAL, ICC v4).
|
||||
IccRec601PalV4 = "rec601pal-v4.icc"
|
||||
|
||||
// IccRec709V2Magic is R709 (Rec.709, ICC v2 magic).
|
||||
IccRec709V2Magic = "rec709-v2-magic.icc"
|
||||
// IccRec709V2Micro is u709 (Rec.709, micro).
|
||||
IccRec709V2Micro = "rec709-v2-micro.icc"
|
||||
// IccRec709V4 is R709 (Rec.709, ICC v4).
|
||||
IccRec709V4 = "rec709-v4.icc"
|
||||
|
||||
// IccScRgbV2 is cRGB (scRGB, ICC v2).
|
||||
IccScRgbV2 = "scrgb-v2.icc"
|
||||
|
||||
// IccSGreyV2Magic is sGry (Display P3 compatible gray, ICC v2 magic).
|
||||
IccSGreyV2Magic = "sgrey-v2-magic.icc"
|
||||
// IccSGreyV2Micro is uGry (Display P3 compatible gray, micro).
|
||||
IccSGreyV2Micro = "sgrey-v2-micro.icc"
|
||||
// IccSGreyV2Nano is nGry (Display P3 compatible gray, nano).
|
||||
IccSGreyV2Nano = "sgrey-v2-nano.icc"
|
||||
// IccSGreyV4 is sGry (Display P3 compatible gray, ICC v4).
|
||||
IccSGreyV4 = "sgrey-v4.icc"
|
||||
|
||||
// IccSRgbV2Magic is sRGB (standard sRGB, ICC v2 magic).
|
||||
IccSRgbV2Magic = "srgb-v2-magic.icc"
|
||||
// IccSRgbV2Micro is uRGB (sRGB micro).
|
||||
IccSRgbV2Micro = "srgb-v2-micro.icc"
|
||||
// IccSRgbV2Nano is nRGB (sRGB nano).
|
||||
IccSRgbV2Nano = "srgb-v2-nano.icc"
|
||||
// IccSRgbV4 is sRGB (standard sRGB, ICC v4).
|
||||
IccSRgbV4 = "srgb-v4.icc"
|
||||
|
||||
// IccWideGamutCompatV2 is AWGC (Adobe Wide Gamut compatible, ICC v2).
|
||||
IccWideGamutCompatV2 = "widegamutcompat-v2.icc"
|
||||
// IccWideGamutCompatV4 is AWGC (Adobe Wide Gamut compatible, ICC v4).
|
||||
IccWideGamutCompatV4 = "widegamutcompat-v4.icc"
|
||||
)
|
||||
|
||||
// IccProfiles lists all bundled ICC profile filenames in one place so tests and
|
||||
// callers can iterate or validate the full set shipped in assets/profiles/icc.
|
||||
var IccProfiles = []string{
|
||||
IccAdobeRGBCompat,
|
||||
IccAdobeRGBCompatV2,
|
||||
IccAdobeRGBCompatV4,
|
||||
IccAppleCompatV2,
|
||||
IccAppleCompatV4,
|
||||
IccCgats001CompatV2Micro,
|
||||
IccColorMatchCompatV2,
|
||||
IccColorMatchCompatV4,
|
||||
IccDciP3V4,
|
||||
IccDisplayP3V2Magic,
|
||||
IccDisplayP3V2Micro,
|
||||
IccDisplayP3V4,
|
||||
IccDisplayP3CompatV2Magic,
|
||||
IccDisplayP3CompatV2Micro,
|
||||
IccDisplayP3CompatV4,
|
||||
IccProPhotoV2Magic,
|
||||
IccProPhotoV2Micro,
|
||||
IccProPhotoV4,
|
||||
IccRec2020Gamma24V4,
|
||||
IccRec2020V2Magic,
|
||||
IccRec2020V2Micro,
|
||||
IccRec2020V4,
|
||||
IccRec2020CompatV2Magic,
|
||||
IccRec2020CompatV2Micro,
|
||||
IccRec2020CompatV4,
|
||||
IccRec601NtscV2Magic,
|
||||
IccRec601NtscV2Micro,
|
||||
IccRec601NtscV4,
|
||||
IccRec601PalV2Magic,
|
||||
IccRec601PalV2Micro,
|
||||
IccRec601PalV4,
|
||||
IccRec709V2Magic,
|
||||
IccRec709V2Micro,
|
||||
IccRec709V4,
|
||||
IccScRgbV2,
|
||||
IccSGreyV2Magic,
|
||||
IccSGreyV2Micro,
|
||||
IccSGreyV2Nano,
|
||||
IccSGreyV4,
|
||||
IccSRgbV2Magic,
|
||||
IccSRgbV2Micro,
|
||||
IccSRgbV2Nano,
|
||||
IccSRgbV4,
|
||||
IccWideGamutCompatV2,
|
||||
IccWideGamutCompatV4,
|
||||
}
|
||||
|
||||
// GetIccProfile returns the absolute path to the first requested ICC profile
|
||||
// that is present in assets/profiles/icc. It validates existence so callers
|
||||
// can embed profiles without risking a panic or missing file error.
|
||||
func GetIccProfile(profiles ...string) (string, error) {
|
||||
if len(profiles) == 0 {
|
||||
return "", errors.New("no icc profiles specified")
|
||||
}
|
||||
|
||||
// Find first ICC profile file that exists.
|
||||
for _, p := range profiles {
|
||||
filePath := filepath.Join(IccProfilesPath, p)
|
||||
if info, err := os.Stat(filePath); err == nil && !info.IsDir() {
|
||||
return filePath, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("no matching icc profiles found (%s)", strings.Join(profiles, ", "))
|
||||
}
|
||||
|
||||
55
internal/thumb/icc_test.go
Normal file
55
internal/thumb/icc_test.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package thumb
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
)
|
||||
|
||||
func TestGetIccProfile(t *testing.T) {
|
||||
t.Run("Exists", func(t *testing.T) {
|
||||
profilePath, err := GetIccProfile(IccAdobeRGBCompat)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, fs.FileExists(profilePath))
|
||||
})
|
||||
t.Run("Missing", func(t *testing.T) {
|
||||
originalPath := IccProfilesPath
|
||||
missingDir := t.TempDir()
|
||||
|
||||
IccProfilesPath = missingDir
|
||||
|
||||
t.Cleanup(func() {
|
||||
IccProfilesPath = originalPath
|
||||
})
|
||||
|
||||
profilePath, err := GetIccProfile(IccAdobeRGBCompat)
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Empty(t, profilePath)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIccProfiles(t *testing.T) {
|
||||
for _, profile := range IccProfiles {
|
||||
t.Run(profile, func(t *testing.T) {
|
||||
path, err := GetIccProfile(profile)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.True(t, fs.FileExists(path))
|
||||
|
||||
//nolint:gosec // test-only: path is constrained to known profile files in assets
|
||||
data, err := os.ReadFile(path)
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(data) < 40 {
|
||||
t.Fatalf("profile %s too small to contain ICC header", profile)
|
||||
}
|
||||
|
||||
assert.Equal(t, []byte{'a', 'c', 's', 'p'}, data[36:40], "profile %s must contain ICC signature", profile)
|
||||
})
|
||||
}
|
||||
}
|
||||
BIN
internal/thumb/testdata/interop_index_r98.jpg
vendored
Normal file
BIN
internal/thumb/testdata/interop_index_r98.jpg
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 528 KiB |
BIN
internal/thumb/testdata/interop_index_srgb_icc.jpg
vendored
Normal file
BIN
internal/thumb/testdata/interop_index_srgb_icc.jpg
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
BIN
internal/thumb/testdata/interop_index_thm.jpg
vendored
Normal file
BIN
internal/thumb/testdata/interop_index_thm.jpg
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 528 KiB |
@@ -75,6 +75,8 @@ func Vips(imageName string, imageBuffer []byte, hash, thumbPath string, width, h
|
||||
case ResampleFillCenter, ResampleResize:
|
||||
crop = vips.InterestingCentre
|
||||
size = vips.SizeBoth
|
||||
default:
|
||||
// Use defaults.
|
||||
}
|
||||
|
||||
if err = vipsSetIccProfileForInteropIndex(img, clean.Log(filepath.Base(imageName))); err != nil {
|
||||
@@ -161,47 +163,3 @@ func VipsJpegExportParams(width, height int) *vips.JpegExportParams {
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
func vipsSetIccProfileForInteropIndex(img *vips.ImageRef, logName string) error {
|
||||
|
||||
// Many cameras will define a JPEG's colour space by setting the InteroperabilityIndex
|
||||
// tag instead of embedding an inline ICC profile.
|
||||
// We detect this and embed explicit icc profiles for thumbs of such images, for the benefit of Vips
|
||||
// and web browsers, none of which pay any attention to the InteropIndex tag.
|
||||
iiFull := img.GetString("exif-ifd4-InteroperabilityIndex")
|
||||
if iiFull == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// according to my reading, I think [:4] should be e.g. "R98\x00".
|
||||
// However, vips always returns [:4] = "R98 ", e.g. space instead of null.
|
||||
// I'm pulling [:3] instead to paper over this - the exif spec says "4 bytes
|
||||
// incl null terminator" so I think this is safe.
|
||||
ii := iiFull[:3]
|
||||
log.Tracef("interopindex: %s read exif and got interopindex %s, %s", logName, ii, iiFull)
|
||||
|
||||
if img.HasICCProfile() {
|
||||
log.Debugf("interopindex: %s has both an interop index tag and an embedded ICC profile. ignoring.", logName)
|
||||
return nil
|
||||
}
|
||||
|
||||
fallbackProfile := ""
|
||||
switch ii {
|
||||
case "R03":
|
||||
// adobe rgb
|
||||
fallbackProfile = MustGetAdobeRGB1998Path()
|
||||
case "R98":
|
||||
// srgb
|
||||
// we could logically embed an srgb profile in the image here, but
|
||||
// there's no value in doing so; everything assumes srgb anyway.
|
||||
case "THM":
|
||||
// a thumbnail file. I can't find a ref on what colour space
|
||||
// this is, so I'm assuming without evidence that they are also srgb.
|
||||
default:
|
||||
log.Debugf("interopindex: %s has unknown interop index %s", logName, ii)
|
||||
}
|
||||
if fallbackProfile == "" {
|
||||
return nil
|
||||
}
|
||||
return img.TransformICCProfileWithFallback(fallbackProfile, fallbackProfile) // icc profile gets embedded here
|
||||
}
|
||||
|
||||
78
internal/thumb/vips_icc.go
Normal file
78
internal/thumb/vips_icc.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package thumb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/davidbyttow/govips/v2/vips"
|
||||
)
|
||||
|
||||
// InteroperabilityIndex EXIF codes used by some cameras to hint at the color space.
|
||||
// Sources: EXIF TagNames (R03=Adobe RGB, R98=sRGB, THM=thumbnail)
|
||||
// https://unpkg.com/exiftool-vendored.pl@10.50.0/bin/html/TagNames/EXIF.html
|
||||
// Additional context: https://regex.info/blog/photo-tech/color-spaces-page7
|
||||
const (
|
||||
// InteropIndexAdobeRGB is the EXIF code for Adobe RGB (1998) ("R03").
|
||||
InteropIndexAdobeRGB = "R03"
|
||||
// InteropIndexSRGB is the EXIF code for sRGB ("R98").
|
||||
InteropIndexSRGB = "R98"
|
||||
// InteropIndexThumb marks a thumbnail image; treated as sRGB ("THM").
|
||||
InteropIndexThumb = "THM"
|
||||
)
|
||||
|
||||
// vipsSetIccProfileForInteropIndex embeds an ICC profile when a JPEG declares
|
||||
// its color space via the EXIF InteroperabilityIndex tag (e.g., "R03"/Adobe RGB)
|
||||
// but lacks an embedded profile. If an ICC profile is already present, it
|
||||
// leaves the image untouched.
|
||||
func vipsSetIccProfileForInteropIndex(img *vips.ImageRef, logName string) (err error) {
|
||||
// Some cameras signal color space via EXIF InteroperabilityIndex instead of
|
||||
// embedding an ICC profile. Browsers and libvips ignore this tag, so we
|
||||
// inject a matching ICC profile to produce correct thumbnails.
|
||||
iiFull := img.GetString("exif-ifd4-InteroperabilityIndex")
|
||||
|
||||
if iiFull == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// EXIF InteroperabilityIndex is 4 bytes including null; libvips returns
|
||||
// a string with a trailing space. Using the first three bytes covers the
|
||||
// meaningful code (e.g., "R03", "R98").
|
||||
if len(iiFull) < 3 {
|
||||
log.Debugf("interopindex: %s has unexpected interop index %q", logName, iiFull)
|
||||
return nil
|
||||
}
|
||||
|
||||
ii := iiFull[:3]
|
||||
log.Tracef("interopindex: %s read exif and got interopindex %s, %s", logName, ii, iiFull)
|
||||
|
||||
if img.HasICCProfile() {
|
||||
log.Debugf("interopindex: %s already has an embedded ICC profile; skipping fallback.", logName)
|
||||
return nil
|
||||
}
|
||||
|
||||
profilePath := ""
|
||||
|
||||
switch ii {
|
||||
case InteropIndexAdobeRGB:
|
||||
// Use Adobe RGB 1998 compatible profile.
|
||||
profilePath, err = GetIccProfile(IccAdobeRGBCompat, IccAdobeRGBCompatV2, IccAdobeRGBCompatV4)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("interopindex %s: %w", ii, err)
|
||||
}
|
||||
case InteropIndexSRGB:
|
||||
// sRGB: browsers and libvips assume sRGB by default, so no embed needed.
|
||||
case InteropIndexThumb:
|
||||
// Thumbnail file; specification unclear—treat as sRGB and do nothing.
|
||||
default:
|
||||
log.Debugf("interopindex: %s has unknown interop index %s", logName, ii)
|
||||
}
|
||||
|
||||
if profilePath == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Embed ICC profile. govips expects both an input (fallback) and output
|
||||
// profile; using the same path injects the chosen profile when none is
|
||||
// embedded and keeps colors consistent otherwise.
|
||||
return img.TransformICCProfileWithFallback(profilePath, profilePath)
|
||||
}
|
||||
75
internal/thumb/vips_icc_test.go
Normal file
75
internal/thumb/vips_icc_test.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package thumb
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/davidbyttow/govips/v2/vips"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestVipsSetIccProfileForInteropIndex(t *testing.T) {
|
||||
t.Run("PreservesExistingProfile", func(t *testing.T) {
|
||||
VipsInit()
|
||||
|
||||
img, err := vips.LoadImageFromFile("testdata/interop_index_srgb_icc.jpg", VipsImportParams())
|
||||
require.NoError(t, err)
|
||||
|
||||
iiFull := img.GetString("exif-ifd4-InteroperabilityIndex")
|
||||
require.NotEmpty(t, iiFull)
|
||||
require.True(t, img.HasICCProfile())
|
||||
|
||||
originalProfile := img.GetICCProfile()
|
||||
require.NotEmpty(t, originalProfile)
|
||||
|
||||
err = vipsSetIccProfileForInteropIndex(img, "interop_index_srgb_icc.jpg")
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, img.HasICCProfile())
|
||||
assert.Equal(t, originalProfile, img.GetICCProfile())
|
||||
})
|
||||
t.Run("EmbedsAdobeProfileWhenMissing", func(t *testing.T) {
|
||||
VipsInit()
|
||||
|
||||
img, err := vips.LoadImageFromFile("testdata/interop_index.jpg", VipsImportParams())
|
||||
require.NoError(t, err)
|
||||
require.False(t, img.HasICCProfile(), "fixture should have no embedded ICC profile")
|
||||
|
||||
err = vipsSetIccProfileForInteropIndex(img, "interop_index.jpg")
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, img.HasICCProfile(), "Adobe ICC profile should be embedded based on InteropIndex R03")
|
||||
assert.NotEmpty(t, img.GetICCProfile())
|
||||
})
|
||||
t.Run("NoInteropIndexNoop", func(t *testing.T) {
|
||||
VipsInit()
|
||||
|
||||
img, err := vips.LoadImageFromFile("testdata/example.jpg", VipsImportParams())
|
||||
require.NoError(t, err)
|
||||
|
||||
hasICCBefore := img.HasICCProfile()
|
||||
err = vipsSetIccProfileForInteropIndex(img, "example.jpg")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, hasICCBefore, img.HasICCProfile())
|
||||
})
|
||||
t.Run("InteropIndexSRGB_NoEmbed", func(t *testing.T) {
|
||||
VipsInit()
|
||||
|
||||
img, err := vips.LoadImageFromFile("testdata/interop_index_r98.jpg", VipsImportParams())
|
||||
require.NoError(t, err)
|
||||
require.False(t, img.HasICCProfile(), "fixture should have no embedded ICC profile")
|
||||
|
||||
err = vipsSetIccProfileForInteropIndex(img, "interop_index_r98.jpg")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, img.HasICCProfile(), "sRGB interop index should remain without embedded ICC")
|
||||
})
|
||||
t.Run("InteropIndexThumb_NoEmbed", func(t *testing.T) {
|
||||
VipsInit()
|
||||
|
||||
img, err := vips.LoadImageFromFile("testdata/interop_index_thm.jpg", VipsImportParams())
|
||||
require.NoError(t, err)
|
||||
require.False(t, img.HasICCProfile(), "fixture should have no embedded ICC profile")
|
||||
|
||||
err = vipsSetIccProfileForInteropIndex(img, "interop_index_thm.jpg")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, img.HasICCProfile(), "THM interop index should remain without embedded ICC")
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user