Creating 2x filters to overlay text and then a logo over an image.
The text filter is fine but the logo is displaying way lower quality than the original.
I have tried different images, jpg versions, different bit-depth of png, even changed the order of filters: text → overlay, then overlay–>text but the output is the same low quality logo.
Output:
Original Logo image:

Repo: https://github.com/jemmsuk/camino/
File with the filter code: https://github.com/jemmsuk/camino/blob/main/layouts/_default/index.pinterest-test5.xml
Output file: https://github.com/jemmsuk/camino/blob/main/public/pinterest-test5.xml
Currently these filters are only applied to the featured_image in pinterest-test5.xml,
The XML output purpose is for an RSS feed that will be connected to Pinterest for automatic uploads.
hugo env
hugo v0.126.1-3d40aba512931031921463dafc172c0d124437b8+extended windows/amd64 BuildDate=2024-05-15T10:42:34Z VendorInfo=gohugoio
GOOS=“windows”
GOARCH=“amd64”
GOVERSION=“go1.22.2”
github.com/sass/libsass=“3.6.5”
github.com/webmproject/libwebp=“v1.3.2”
This is trivial to reproduce:
git clone --single-branch -b hugo-forum-topic-50034 https://github.com/jmooring/hugo-testing hugo-forum-topic-50034
cd hugo-forum-topic-50034
hugo server
Zooming into the result:

This construct places a PNG on a PNG:
{{ with resources.Get "images/original.jpg" }}
{{ with .Process "png" }}
{{ with . | images.Filter $filter }}
<img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}" alt="">
{{ end }}
{{ end }}
{{ end }}
The quality is good, but the image size is terrible (800% larger).
Please log a bug. I’m not convinced this is a bug. I think quality of the overlay on the composite image is just related to the encoding of the composite image. This is (I think) proven by the PNG-to-PNG (lossless) encoding example above.
The best I have come up with is:
{{ with resources.Get "images/original.jpg" }}
{{ with .Process "png" }}
{{ with . | images.Filter $filter }}
{{ with .Process "jpg q95" }}
<img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}" alt="">
{{ end }}
{{ end }}
{{ end }}
{{ end }}
This doubles the size of the original image (cost), but the logo looks pretty good (benefit).
Thank you for quick response.
the quality of the logo overlay is very good with all other tests, except that particular tall background png. The original png background but resized even works well.
with reproduced repo image converted to png

original background image resized png

background image original size png
different png background
different webp background image
Can you create a minimal example repo so that I can test/reproduce?
Using your code above on the specific ‘problem’ png outputs:
Actually, all that I need is the original (problem) image. Don’t upload here because Discourse does some magic that modifies the original file. Feel free to email to the address on my GitHub profile.
OK, thanks. I invited you to this repo https://github.com/jemmsuk/50034-images which contains input and output versions of all the images i am currently processing.
Various image types included.
It appears that only some of the PNG are affected.
OK thanks. Please point to the image in your example.
Using exiftool to analyze all of the PNG input files:
- When Color Type is RGB the output is good
- When Color Type is Palette the output is bad
The list
File Color Type
------------ ----------
1_input.png Palette
2_input.png Palette
3_input.png Palette
4_input.png Palette
5_input.png Palette
6_input.png Palette
7_input.png Palette
8_input.png RGB
9_input.png Palette
12_input.png Palette
13_input.png Palette
15_input.png RGB
16_input.png RGB
17_input.png RGB
18_input.png RGB
19_input.png RGB
20_input.png RGB
21_input.png Palette
22_input.png RGB
23_input.png RGB
24_input.png RGB
26_input.png RGB
27_input.png RGB
28_input.png Palette
29_input.png RGB
30_input.png Palette
31_input.png Palette
32_input.png Palette
33_input.png Palette
34_input.png Palette
35_input.png Palette
36_input.png Palette
37_input.png Palette
38_input.png Palette
ImageMagick’s identify reports the “good” images to be color type 3 (true color), and the “bad” images to be color type 2 (indexed). This is consistent with that reported by exiftool.
I’m not surprised by the result of overlaying a 32 bit true color image on top of 24 bit indexed image. The final (composite) image is still indexed using the same palette as the base image, so the colors in the logo will change based on the base image. It can’t add more colors to the palette.
So let’s convert the 24 bit indexed image to a 24 bit true color image before applying the overlay:
{{ with resources.Get "images/1_input.png" }}
{{ with .Process "jpg" }}
{{ with . | images.Filter $filter }}
<img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}" alt="">
{{ end }}
{{ end }}
{{ end }}
You can use the above with all image types. I’ve update the test site:
git clone --single-branch -b hugo-forum-topic-50034 https://github.com/jmooring/hugo-testing hugo-forum-topic-50034
cd hugo-forum-topic-50034
hugo server
Either that or use something like ImageMagick to batch convert the indexed images to true color images.
Thank you, I don’t care what format the resulting image is so converting to jpg works well as a precaution. Here it is using your {{ with .Process “jpg” }} suggestion above. I’m happy with it.
I’m glad it’s working for you. Although I’m not surprised by the problem, it shouldn’t be happening:
https://github.com/gohugoio/hugo/issues/12543
Thank you for logging as a bug. As an aside, during this exercise while consolidating my template code i discovered that I can reduce the number of steps in the process. Surprisingly (for me) is that 2x overlay filters can be applied in one step. I also love the fact that all the parameters for the filters can be placed into hugo.toml i.e.
<!-- Use dimensions from config, crop to fill & convert to jpg-->
{{- $fillSpec := printf "jpg %dx%d $imageCropMethod q%d %s" $imageWidth $imageHeight $imageQuality $imageResampleMethod -}}
{{- $filledImage := $featuredImage.Fill $fillSpec -}}
<!-- Apply the text & logo overlay filters -->
{{- with $filledImage | images.Filter $logofilter $textfilter -}}