summaryrefslogtreecommitdiffstats
path: root/resource
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2018-02-19 16:34:49 +0100
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2018-02-19 20:15:58 +0100
commit799c654b0d39ec869c2da24d41de3636eb7157f0 (patch)
treebfa501c44fe3aa91f1ae4e827f7625ba54ba79da /resource
parentfaa3159e5e09e2a95cbef4dca5fcb9c8e2b41bf4 (diff)
resource: Preserve color palette for PNG images
This commit will force a reprocessing of PNG images with new names, so it is adviced to run a `hugo --gc` to remove stale files. Fixes #4416
Diffstat (limited to 'resource')
-rw-r--r--resource/image.go69
-rw-r--r--resource/image_test.go20
-rw-r--r--resource/resource.go10
-rw-r--r--resource/testdata/gohugoio.pngbin0 -> 73886 bytes
4 files changed, 79 insertions, 20 deletions
diff --git a/resource/image.go b/resource/image.go
index 208a0e9fb..2529413cc 100644
--- a/resource/image.go
+++ b/resource/image.go
@@ -30,6 +30,7 @@ import (
// Importing image codecs for image.DecodeConfig
"image"
+ "image/draw"
_ "image/gif"
"image/jpeg"
_ "image/png"
@@ -65,15 +66,27 @@ const (
defaultResampleFilter = "box"
)
-var imageFormats = map[string]imaging.Format{
- ".jpg": imaging.JPEG,
- ".jpeg": imaging.JPEG,
- ".png": imaging.PNG,
- ".tif": imaging.TIFF,
- ".tiff": imaging.TIFF,
- ".bmp": imaging.BMP,
- ".gif": imaging.GIF,
-}
+var (
+ imageFormats = map[string]imaging.Format{
+ ".jpg": imaging.JPEG,
+ ".jpeg": imaging.JPEG,
+ ".png": imaging.PNG,
+ ".tif": imaging.TIFF,
+ ".tiff": imaging.TIFF,
+ ".bmp": imaging.BMP,
+ ".gif": imaging.GIF,
+ }
+
+ // Add or increment if changes to an image format's processing requires
+ // re-generation.
+ imageFormatsVersions = map[imaging.Format]int{
+ imaging.PNG: 1, // 1: Add proper palette handling
+ }
+
+ // Increment to mark all processed images as stale. Only use when absolutely needed.
+ // See the finer grained smartCropVersionNumber and imageFormatsVersions.
+ mainImageVersionNumber = 0
+)
var anchorPositions = map[string]imaging.Anchor{
strings.ToLower("Center"): imaging.Center,
@@ -117,6 +130,8 @@ type Image struct {
imaging *Imaging
+ format imaging.Format
+
hash string
*genericResource
@@ -137,6 +152,7 @@ func (i *Image) WithNewBase(base string) Resource {
return &Image{
imaging: i.imaging,
hash: i.hash,
+ format: i.format,
genericResource: i.genericResource.WithNewBase(base).(*genericResource)}
}
@@ -246,6 +262,15 @@ func (i *Image) doWithImageConfig(action, spec string, f func(src image.Image, c
return ci, &os.PathError{Op: errOp, Path: errPath, Err: err}
}
+ if i.format == imaging.PNG {
+ // Apply the colour palette from the source
+ if paletted, ok := src.(*image.Paletted); ok {
+ tmp := image.NewPaletted(converted.Bounds(), paletted.Palette)
+ draw.Src.Draw(tmp, tmp.Bounds(), converted, converted.Bounds().Min)
+ converted = tmp
+ }
+ }
+
b := converted.Bounds()
ci.config = image.Config{Width: b.Max.X, Height: b.Max.Y}
ci.configLoaded = true
@@ -255,7 +280,7 @@ func (i *Image) doWithImageConfig(action, spec string, f func(src image.Image, c
}
-func (i imageConfig) key() string {
+func (i imageConfig) key(format imaging.Format) string {
k := strconv.Itoa(i.Width) + "x" + strconv.Itoa(i.Height)
if i.Action != "" {
k += "_" + i.Action
@@ -277,6 +302,14 @@ func (i imageConfig) key() string {
k += "_" + anchor
}
+ if v, ok := imageFormatsVersions[format]; ok {
+ k += "_" + strconv.Itoa(v)
+ }
+
+ if mainImageVersionNumber > 0 {
+ k += "_" + strconv.Itoa(mainImageVersionNumber)
+ }
+
return k
}
@@ -410,7 +443,8 @@ func (i *Image) decodeSource() (image.Image, error) {
return nil, err
}
defer file.Close()
- return imaging.Decode(file)
+ img, _, err := image.Decode(file)
+ return img, err
}
func (i *Image) copyToDestination(src string) error {
@@ -464,12 +498,6 @@ func (i *Image) copyToDestination(src string) error {
}
func (i *Image) encodeToDestinations(img image.Image, conf imageConfig, resourceCacheFilename, filename string) error {
- ext := strings.ToLower(helpers.Ext(filename))
-
- imgFormat, ok := imageFormats[ext]
- if !ok {
- return imaging.ErrUnsupportedFormat
- }
target := filepath.Join(i.absPublishDir, filename)
@@ -509,7 +537,7 @@ func (i *Image) encodeToDestinations(img image.Image, conf imageConfig, resource
w = file1
}
- switch imgFormat {
+ switch i.format {
case imaging.JPEG:
var rgba *image.RGBA
@@ -530,7 +558,7 @@ func (i *Image) encodeToDestinations(img image.Image, conf imageConfig, resource
return jpeg.Encode(w, img, &jpeg.Options{Quality: quality})
}
default:
- return imaging.Encode(w, img, imgFormat)
+ return imaging.Encode(w, img, i.format)
}
}
@@ -541,6 +569,7 @@ func (i *Image) clone() *Image {
return &Image{
imaging: i.imaging,
hash: i.hash,
+ format: i.format,
genericResource: &g}
}
@@ -555,7 +584,7 @@ func (i *Image) filenameFromConfig(conf imageConfig) string {
// Do not change for no good reason.
const md5Threshold = 100
- key := conf.key()
+ key := conf.key(i.format)
// It is useful to have the key in clear text, but when nesting transforms, it
// can easily be too long to read, and maybe even too long
diff --git a/resource/image_test.go b/resource/image_test.go
index e981a208f..1a937d56b 100644
--- a/resource/image_test.go
+++ b/resource/image_test.go
@@ -19,6 +19,8 @@ import (
"strconv"
"testing"
+ "github.com/disintegration/imaging"
+
"sync"
"github.com/stretchr/testify/require"
@@ -258,6 +260,24 @@ func TestImageWithMetadata(t *testing.T) {
}
+func TestImageResize8BitPNG(t *testing.T) {
+
+ assert := require.New(t)
+
+ image := fetchImage(assert, "gohugoio.png")
+
+ assert.Equal(imaging.PNG, image.format)
+ assert.Equal("/a/gohugoio.png", image.RelPermalink())
+ assert.Equal("image", image.ResourceType())
+
+ resized, err := image.Resize("800x")
+ assert.NoError(err)
+ assert.Equal(imaging.PNG, resized.format)
+ assert.Equal("/a/gohugoio_hu0e1b9e4a4be4d6f86c7b37b9ccce3fbc_73886_800x0_resize_linear_1.png", resized.RelPermalink())
+ assert.Equal(800, resized.Width())
+
+}
+
func BenchmarkResizeParallel(b *testing.B) {
assert := require.New(b)
img := fetchSunset(assert)
diff --git a/resource/resource.go b/resource/resource.go
index da62db65c..66fda4c1b 100644
--- a/resource/resource.go
+++ b/resource/resource.go
@@ -23,6 +23,8 @@ import (
"strings"
"sync"
+ "github.com/disintegration/imaging"
+
"github.com/spf13/cast"
"github.com/gobwas/glob"
@@ -297,8 +299,16 @@ func (r *Spec) newResource(
return nil, err
}
+ ext := strings.ToLower(helpers.Ext(absSourceFilename))
+
+ imgFormat, ok := imageFormats[ext]
+ if !ok {
+ return nil, imaging.ErrUnsupportedFormat
+ }
+
return &Image{
hash: hash,
+ format: imgFormat,
imaging: r.imaging,
genericResource: gr}, nil
}
diff --git a/resource/testdata/gohugoio.png b/resource/testdata/gohugoio.png
new file mode 100644
index 000000000..0591db959
--- /dev/null
+++ b/resource/testdata/gohugoio.png
Binary files differ