Skip to content

Commit

Permalink
write icc files to tmp only when necessary (with tests) (#456)
Browse files Browse the repository at this point in the history
* write icc files to tmp only when necessary

* wrremove temporaryDirectory if have

* Using characters that are not possible in a file path

* Adjust tests for new default ICC profile mechanism

* Remove unused function initializeICCProfiles

Closes: #435

---------

Co-authored-by: Code1Super <[email protected]>
  • Loading branch information
rhafer and code1super authored Jan 8, 2025
1 parent 859c51d commit f70e077
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 27 deletions.
6 changes: 3 additions & 3 deletions vips/govips.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,6 @@ func Startup(config *Config) {
panic(fmt.Sprintf("Failed to start vips code=%v", err))
}

initializeICCProfiles()

running = true

if config != nil {
Expand Down Expand Up @@ -185,7 +183,9 @@ func Shutdown() {
return
}

os.RemoveAll(temporaryDirectory)
if temporaryDirectory != "" {
os.RemoveAll(temporaryDirectory)
}

C.vips_shutdown()
disableLogging()
Expand Down
89 changes: 70 additions & 19 deletions vips/icc_profiles.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package vips

import (
"fmt"
"os"
"path/filepath"
"sync"
)

var (
Expand Down Expand Up @@ -644,32 +644,83 @@ var (
0x00, 0x20, 0x63, 0xcf, 0x8f, 0xf0, 0x65, 0x87, 0x4e, 0xf6, 0x00, 0x00,
}

temporaryDirectory = temporaryDirectoryOrPanic()
SRGBV2MicroICCProfilePath = filepath.Join(temporaryDirectory, "srgb_v2_micro.icc")
SGrayV2MicroICCProfilePath = filepath.Join(temporaryDirectory, "sgray_v2_micro.icc")
SRGBIEC6196621ICCProfilePath = filepath.Join(temporaryDirectory, "srgb_iec61966_2_1.icc")
GenericGrayGamma22ICCProfilePath = filepath.Join(temporaryDirectory, "generic_gray_gamma_2_2.icc")
sRGBV2MicroICCProfilePathToken = "\x00srgb_v2_micro.icc"
sGrayV2MicroICCProfilePathToken = "\x00sgray_v2_micro.icc"
sRGBIEC6196621ICCProfilePathToken = "\x00srgb_iec61966_2_1.icc"
genericGrayGamma22ICCProfilePathToken = "\x00generic_gray_gamma_2_2.icc"

temporaryDirectory = ""
SRGBV2MicroICCProfilePath = sRGBV2MicroICCProfilePathToken
SGrayV2MicroICCProfilePath = sGrayV2MicroICCProfilePathToken
SRGBIEC6196621ICCProfilePath = sRGBIEC6196621ICCProfilePathToken
GenericGrayGamma22ICCProfilePath = genericGrayGamma22ICCProfilePathToken
)

func initializeICCProfiles() {
storeIccProfile(SRGBV2MicroICCProfilePath, sRGBV2MicroICCProfile)
storeIccProfile(SGrayV2MicroICCProfilePath, sGrayV2MicroICCProfile)
storeIccProfile(SRGBIEC6196621ICCProfilePath, sRGBIEC6196621ICCProfile)
storeIccProfile(GenericGrayGamma22ICCProfilePath, genericGrayGamma22ICCProfile)
// Back support
func ensureLoadICCPath(name *string) (err error) {
if len(*name) > 0 && (*name)[0] == 0 {
switch *name {
case sRGBV2MicroICCProfilePathToken:
*name, err = GetSRGBV2MicroICCProfilePath()
return
case sGrayV2MicroICCProfilePathToken:
*name, err = GetSGrayV2MicroICCProfilePath()
return
case sRGBIEC6196621ICCProfilePathToken:
*name, err = GetSRGBIEC6196621ICCProfilePath()
return
case genericGrayGamma22ICCProfilePathToken:
*name, err = GetGenericGrayGamma22ICCProfilePath()
return
}
}
return
}

func storeIccProfile(path string, data []byte) {
err := os.WriteFile(path, data, 0600)
func getTemporaryDirectory() (string, error) {
if temporaryDirectory != "" {
return temporaryDirectory, nil
}
var err error
temporaryDirectory, err = os.MkdirTemp("", "govips-")
if err != nil {
panic(fmt.Sprintf("Couldn't store temporary file for ICC profile in '%v': %v", path, err.Error()))
return "", err
}
return temporaryDirectory, nil
}

func temporaryDirectoryOrPanic() string {
temporaryDirectory, err := os.MkdirTemp("", "govips-")
if err != nil {
panic(fmt.Sprintf("Couldn't create temporary directory: %v", err.Error()))
var lockIcc sync.Mutex

func GetSRGBV2MicroICCProfilePath() (string, error) {
return getOrLoad(&SRGBV2MicroICCProfilePath, "srgb_v2_micro.icc", sRGBV2MicroICCProfile)
}

func GetSGrayV2MicroICCProfilePath() (string, error) {
return getOrLoad(&SGrayV2MicroICCProfilePath, "sgray_v2_micro.icc", sGrayV2MicroICCProfile)
}

func GetSRGBIEC6196621ICCProfilePath() (string, error) {
return getOrLoad(&SRGBIEC6196621ICCProfilePath, "srgb_iec61966_2_1.icc", sRGBIEC6196621ICCProfile)
}

func GetGenericGrayGamma22ICCProfilePath() (string, error) {
return getOrLoad(&GenericGrayGamma22ICCProfilePath, "generic_gray_gamma_2_2.icc", genericGrayGamma22ICCProfile)
}

func getOrLoad(pathFile *string, name string, fileBytes []byte) (string, error) {
lockIcc.Lock()
defer lockIcc.Unlock()
if len(*pathFile) > 0 && (*pathFile)[0] != 0 {
return *pathFile, nil
}

if _, err := getTemporaryDirectory(); err != nil {
return "", err
}

return temporaryDirectory
*pathFile = filepath.Join(temporaryDirectory, name)
if err := os.WriteFile(*pathFile, fileBytes, 0600); err != nil {
return "", err
}
return *pathFile, nil
}
31 changes: 26 additions & 5 deletions vips/icc_profiles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,33 @@ import (
)

func Test_ICCProfileInitialisation(t *testing.T) {
initializeICCProfiles()
nonDefaultProfile := "non-default"
err := ensureLoadICCPath(&nonDefaultProfile)
require.NoError(t, err)

err = ensureLoadICCPath(&sRGBV2MicroICCProfilePathToken)
require.NoError(t, err)
path, err := GetSRGBV2MicroICCProfilePath()
require.NoError(t, err)
assertIccProfile(t, sRGBV2MicroICCProfile, path)

err = ensureLoadICCPath(&sGrayV2MicroICCProfilePathToken)
require.NoError(t, err)
path, err = GetSGrayV2MicroICCProfilePath()
require.NoError(t, err)
assertIccProfile(t, sGrayV2MicroICCProfile, path)

err = ensureLoadICCPath(&sRGBIEC6196621ICCProfilePathToken)
require.NoError(t, err)
path, err = GetSRGBIEC6196621ICCProfilePath()
require.NoError(t, err)
assertIccProfile(t, sRGBIEC6196621ICCProfile, path)

assertIccProfile(t, sRGBV2MicroICCProfile, SRGBV2MicroICCProfilePath)
assertIccProfile(t, sGrayV2MicroICCProfile, SGrayV2MicroICCProfilePath)
assertIccProfile(t, sRGBIEC6196621ICCProfile, SRGBIEC6196621ICCProfilePath)
assertIccProfile(t, genericGrayGamma22ICCProfile, GenericGrayGamma22ICCProfilePath)
err = ensureLoadICCPath(&genericGrayGamma22ICCProfilePathToken)
require.NoError(t, err)
path, err = GetGenericGrayGamma22ICCProfilePath()
require.NoError(t, err)
assertIccProfile(t, genericGrayGamma22ICCProfile, path)
}

func assertIccProfile(t *testing.T, expectedProfile []byte, path string) {
Expand Down
11 changes: 11 additions & 0 deletions vips/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -1402,6 +1402,13 @@ func (r *ImageRef) RemoveICCProfile() error {
// TransformICCProfileWithFallback transforms from the embedded ICC profile of the image to the ICC profile at the given path.
// The fallback ICC profile is used if the image does not have an embedded ICC profile.
func (r *ImageRef) TransformICCProfileWithFallback(targetProfilePath, fallbackProfilePath string) error {
if err := ensureLoadICCPath(&targetProfilePath); err != nil {
return err
}
if err := ensureLoadICCPath(&fallbackProfilePath); err != nil {
return err
}

depth := 16
if r.BandFormat() == BandFormatUchar || r.BandFormat() == BandFormatChar || r.BandFormat() == BandFormatNotSet {
depth = 8
Expand Down Expand Up @@ -1437,6 +1444,10 @@ func (r *ImageRef) OptimizeICCProfile() error {
r.optimizedIccProfile = SGrayV2MicroICCProfilePath
}

if err := ensureLoadICCPath(&r.optimizedIccProfile); err != nil {
return err
}

embedded := r.HasICCProfile() && (inputProfile == "")

depth := 16
Expand Down

0 comments on commit f70e077

Please sign in to comment.