Images with overlays

Create a composite of two images using Hugo’s overlay image filter, typically for watermarking or “frame within a frame” applications.

Overview

The overlay image filter is simple, placing one image on top of another at the given coordinates, relative to top left corner of the base image. The two files must be image resources: page, global, or remote.

{{ with $bi := resources.Get "images/base.jpg" }}
  {{ with $oi := resources.Get "images/overlay.jpg" }}
    {{ with $bi.Filter (images.Overlay $oi 67 42) }}
      <img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}" alt="">
    {{ end }}
  {{ end }}
{{ end }}

The example above places overlay.jpg on top of base.jpg. The top left corner of the overlay image is offset from the top left corner of the base image by 67 pixels to the right (the x offset), and 42 pixels down (the y offset).

Practical usage

More often than not, when applying an overlay, we don’t know the dimensions of the base image, and sometimes we don’t know the dimensions of the overlay image either.

Let’s say you want to place a watermark in the bottom right corner of an image, padded with 50 pixels. To calculate the x and y offsets you need to know the dimensions of the base image, the dimensions of the overlay image, and the desired padding.

Diagram of base and overlay image

After doing the math you determine that the x offset should be 450, and the y offset should be 250. That was easy, but what if:

  • You need to place the overlay in the center, or in the upper right corner?
  • The dimensions of the base image are variable?
  • The dimensions of the overlay image are variable?
  • You need the overlay sized proportionally to the base image?
  • You need the padding to be proportional to the base image?
  • You need to render the composite image in different formats and sizes?

The easiest way to handle this is with a shortcode.

Picture shortcode

This shortcode is a wrapper for the picture partial. You must install both.

The picture shortcode renders an HTML picture element, in multiple formats and sizes, with an optional overlay. It resolves internal destinations by looking for a matching:

  1. Page resource (an image in the current page bundle)
  2. Section resource (an image in the current section)
  3. Global resource (an image in the assets directory)

It skips the section resource lookup if the current page is a leaf bundle, and captures external destinations as resources for local hosting. The build will fail if this shortcode is unable to resolve a destination.

You must place global resources in the assets directory. If you have placed your resources in the static directory, and you are unable or unwilling to move them, you must mount the static directory to the assets directory by including both of these entries in your site configuration:

[[module.mounts]]
source = 'assets'
target = 'assets'

[[module.mounts]]
source = 'static'
target = 'assets'

Add this CSS to your site to enable responsive image behavior:

img {
  height: auto;
  max-width: 100%;
}

Add this CSS to your site to remove small gaps between adjacent img and picture elements:

img, picture {
  font-size: 0;
}

Examples

Watermark

Add a watermark to any image. To change the watermark location, set the overlayAnchor and adjust overlayPadding as needed. The overlayPadding and overlayWidth are a percentage of the base image width.

{{< picture 
  alt="Bryce Canyon National Park"
  overlay="images/logo.png"
  overlayPadding=0.02
  overlayWidth=0.25
  src="images/bryce-canyon-national-park.jpg"
>}}
Bryce Canyon National Park
<picture>
  <source type="image/webp" srcset="/articles/images-with-overlays/images/bryce-canyon-national-park_hu2bf68451a1bc8c7a1c22035ccb943492_1561757_4bf3b5d6e10a1c4e1644282174e454df.webp 720w,/articles/images-with-overlays/images/bryce-canyon-national-park_hu2bf68451a1bc8c7a1c22035ccb943492_1561757_76f3ee2e104b561d6b4a998c7efa3514.webp 1080w,/articles/images-with-overlays/images/bryce-canyon-national-park_hu2bf68451a1bc8c7a1c22035ccb943492_1561757_d931eeabf8510329542d9a742cb83d3a.webp 1440w,/articles/images-with-overlays/images/bryce-canyon-national-park_hu2bf68451a1bc8c7a1c22035ccb943492_1561757_57b1c2dff267ea186562b9356111dc5c.webp 1920w" sizes="100vw">
  <source type="image/jpeg" srcset="/articles/images-with-overlays/images/bryce-canyon-national-park_hu2bf68451a1bc8c7a1c22035ccb943492_1561757_56aacb85c07bd7889613761adc1760b1.jpg 720w,/articles/images-with-overlays/images/bryce-canyon-national-park_hu2bf68451a1bc8c7a1c22035ccb943492_1561757_aac280aaaabd3dbc55f7f77ec0519a78.jpg 1080w,/articles/images-with-overlays/images/bryce-canyon-national-park_hu2bf68451a1bc8c7a1c22035ccb943492_1561757_07fd32bd4d5c04c9ecdb4fde4cef4e55.jpg 1440w,/articles/images-with-overlays/images/bryce-canyon-national-park_hu2bf68451a1bc8c7a1c22035ccb943492_1561757_97dafd992f77366a10689afb488eaff0.jpg 1920w" sizes="100vw">
  <img alt="Bryce Canyon National Park" loading="lazy" src="/articles/images-with-overlays/images/bryce-canyon-national-park_hu2bf68451a1bc8c7a1c22035ccb943492_1561757_56aacb85c07bd7889613761adc1760b1.jpg">
</picture>

Frame within a frame

Place one image inside another. The size of the overlay image is irrelevant; you can adjust its relative size with overlayWidth, a percentage of the base image width.

{{< picture 
  alt="Bryce Canyon National Park"
  overlay="images/zion-national-park.jpg"
  overlayAnchor="bottomleft"
  overlayPadding=0.06
  overlayWidth=0.45
  src="images/bryce-canyon-national-park.jpg"
>}}
Bryce Canyon National Park
<picture>
  <source type="image/webp" srcset="/articles/images-with-overlays/images/bryce-canyon-national-park_hu2bf68451a1bc8c7a1c22035ccb943492_1561757_edefeb502dc35d4989def20718219e07.webp 720w,/articles/images-with-overlays/images/bryce-canyon-national-park_hu2bf68451a1bc8c7a1c22035ccb943492_1561757_5efeb018cbf2d17c9a56506364227537.webp 1080w,/articles/images-with-overlays/images/bryce-canyon-national-park_hu2bf68451a1bc8c7a1c22035ccb943492_1561757_b6fa23eb5821a5f01b8357a5de9d24ac.webp 1440w,/articles/images-with-overlays/images/bryce-canyon-national-park_hu2bf68451a1bc8c7a1c22035ccb943492_1561757_7a163cfd28a1848309510e716e39e565.webp 1920w" sizes="100vw">
  <source type="image/jpeg" srcset="/articles/images-with-overlays/images/bryce-canyon-national-park_hu2bf68451a1bc8c7a1c22035ccb943492_1561757_fc491ccfb206e58434ee5dd28687ae04.jpg 720w,/articles/images-with-overlays/images/bryce-canyon-national-park_hu2bf68451a1bc8c7a1c22035ccb943492_1561757_9d3ec42c923e2e0bdf51fef3fdbef51d.jpg 1080w,/articles/images-with-overlays/images/bryce-canyon-national-park_hu2bf68451a1bc8c7a1c22035ccb943492_1561757_2a3adab9d552f7b79c2f48ac89a56cae.jpg 1440w,/articles/images-with-overlays/images/bryce-canyon-national-park_hu2bf68451a1bc8c7a1c22035ccb943492_1561757_cbe61cc6feab3b52665bb9d70facf579.jpg 1920w" sizes="100vw">
  <img alt="Bryce Canyon National Park" loading="lazy" src="/articles/images-with-overlays/images/bryce-canyon-national-park_hu2bf68451a1bc8c7a1c22035ccb943492_1561757_fc491ccfb206e58434ee5dd28687ae04.jpg">
</picture>

Remote resources

You are not limited to page or global resources. Use a remote resource for the base image, or the overlay image, or both.

{{< picture 
  alt="Kittens"
  overlay="https://www.veriphor.com/shared/images/a.jpg"
  overlayAnchor="topright"
  overlayPadding=0.025
  overlayWidth=0.50
  src="https://www.veriphor.com/shared/images/d.jpg"
  title="Kittens!"
  width=480
>}}
Kittens
<picture>
  <source type="image/webp" srcset="/d_12109794374918945428_huf45280b676e172f3c7d8bb9a803361e6_0_c76db4d97ac154c516e372450c34091f.webp 480w,/d_12109794374918945428_huf45280b676e172f3c7d8bb9a803361e6_0_00e6b5a0287a2b36adb5df3ef885f971.webp 600w" sizes="480px">
  <source type="image/jpeg" srcset="/d_12109794374918945428_huf45280b676e172f3c7d8bb9a803361e6_0_71bebf66a09dfaee9c28016e4dc41298.jpg 480w,/d_12109794374918945428_huf45280b676e172f3c7d8bb9a803361e6_0_af1df0571a7594081f9735b58dc705e9.jpg 600w" sizes="480px">
  <img alt="Kittens" loading="lazy" src="/d_12109794374918945428_huf45280b676e172f3c7d8bb9a803361e6_0_71bebf66a09dfaee9c28016e4dc41298.jpg" title="Kittens!">
</picture>

No overlay

Use the picture shortcode without an overlay. Like the previous examples, the rendered image is available in multiple formats and sizes.

{{< picture src="images/zion-national-park.jpg" width=480 >}}
<picture>
  <source type="image/webp" srcset="/articles/images-with-overlays/images/zion-national-park_hu7c935ba4ce6706aba5fb06d3a753722b_1506922_480x0_resize_q75_h2_catmullrom.webp 480w,/articles/images-with-overlays/images/zion-national-park_hu7c935ba4ce6706aba5fb06d3a753722b_1506922_960x0_resize_q75_h2_catmullrom.webp 960w,/articles/images-with-overlays/images/zion-national-park_hu7c935ba4ce6706aba5fb06d3a753722b_1506922_1440x0_resize_q75_h2_catmullrom.webp 1440w,/articles/images-with-overlays/images/zion-national-park_hu7c935ba4ce6706aba5fb06d3a753722b_1506922_1920x0_resize_q75_h2_catmullrom.webp 1920w" sizes="480px">
  <source type="image/jpeg" srcset="/articles/images-with-overlays/images/zion-national-park_hu7c935ba4ce6706aba5fb06d3a753722b_1506922_480x0_resize_q75_catmullrom.jpg 480w,/articles/images-with-overlays/images/zion-national-park_hu7c935ba4ce6706aba5fb06d3a753722b_1506922_960x0_resize_q75_catmullrom.jpg 960w,/articles/images-with-overlays/images/zion-national-park_hu7c935ba4ce6706aba5fb06d3a753722b_1506922_1440x0_resize_q75_catmullrom.jpg 1440w,/articles/images-with-overlays/images/zion-national-park_hu7c935ba4ce6706aba5fb06d3a753722b_1506922_1920x0_resize_q75_catmullrom.jpg 1920w" sizes="480px">
  <img alt="" loading="lazy" src="/articles/images-with-overlays/images/zion-national-park_hu7c935ba4ce6706aba5fb06d3a753722b_1506922_480x0_resize_q75_catmullrom.jpg">
</picture>

Arguments

The src argument is required; others are optional.

alt
(string) The img element’s alt attribute.
class
(string) The img element’s class attribute.
formats
(string) A comma- or space-delimited list of image formats, ordered by precedence, to use when creating images for the srcset attribute of each source element.
loading
(string) The img element’s loading attribute. Default is lazy.
overlay
(string) The path to the overlay image: a page resource, a global resource, or a remote resource.
overlayAnchor
(string) The anchor point of the overlay image. May be one of TopLeft, Top, TopRight, Left, Center, Right, BottomLeft, Bottom, or BottomRight. The value is case-insensitive. Default is BottomRight.
overlayPadding
(float) The padding around the overlay image, as a percentage of the base image width, between 0 and 1 (inclusive). Default is 0.00.
overlayWidth
(float) The width of the overlay image, as a percentage of the base image width, between 0 and 1. Default is 0.20.
src
(string) The path to the base image: a page resource, a global resource, or a remote resource.
title
(string) The img element’s title attribute.
width
(int) The display width of the image, in pixels, falling back to 100% of the viewport width.

Source code

This shortcode is a wrapper for the picture partial. You must install both.

layouts/shortcodes/picture.html
{{- /* Last modified: 2023-09-04T20:30:30-07:00 */}}

{{- /*
Copyright 2023 Veriphor LLC

Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy of
the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License.
*/}}

{{- /*
Renders an HTML picture element, in multiple formats and sizes, with an optional overlay.

It resolves internal destinations by looking for a matching:

  1. Page resource (an image in the current page bundle)
  2. Section resource (an image in the current section)
  3. Global resource (an image in the assets directory)

It skips the section resource lookup if the current page is a leaf bundle, and
captures external destinations as resources for local hosting. The build will
fail if this shortcode is unable to resolve a destination.

You must place global resources in the assets directory. If you have placed
your resources in the static directory, and you are unable or unwilling to move
them, you must mount the static directory to the assets directory by including
both of these entries in your site configuration:

  [[module.mounts]]
  source = 'assets'
  target = 'assets'

  [[module.mounts]]
  source = 'static'
  target = 'assets'

Add this CSS to your site to enable responsive image behavior:

  img {
    height: auto;
    max-width: 100%;
  }

Add this CSS to your site to remove small gaps between adjacent elements:

  img, picture {
    font-size: 0;
  }

This shortcode is a wrapper for, and requires, the picture partial:
<https://www.veriphor.com/articles/images-with-overlays/#partial-source-code>

@context {string} Inner The content between the opening and closing shortcode tags.
@context {string} InnerDeindent The content between the opening and closing shortcode tags with indentation removed.
@context {string} Name The file name of the shortcode template, excluding the extension.
@context {int} Ordinal The zero-based ordinal of the shortcode on the page, or within its parent shortcode.
@context {page} Page A reference to the page containing the shortcode.
@context {map} Params The parameters specified in the opening shortcode tag.
@context {hugolib.ShortcodeWithPage} Parent The context of the parent shortcode.
@context {text.Position} Position The position of the shortcode within the page content.

@method {any} Get Returns the parameter value for the given key (for named parameters) or position (for positional parameters).
@mathod {bool} IsNamedParams Returns true if the shortcode is called with named instead of positional parameters.
@method {maps.Scratch) Scratch Returns a writable Scratch to store and manipulate data.

@param {string} [alt] The img element's alt attribute.
@param {string} [class] The img element's class attribute.
@param {string slice} [formats=webp,jpeg] A comma- or space-delimited list of image formats, ordered by precedence, to use when creating images for the srcset attribute of each source element.
@param {string} [loading=lazy] The img element's loading attribute.
@param {string} [overlay] The path to the overlay image: a page resource, a global resource, or a remote resource.
@param {string} [overlayAnchor=BottomRight] The anchor point of the overlay image. May be one of TopLeft, Top, TopRight, Left, Center, Right, BottomLeft, Bottom, or BottomRight. The value is case-insensitive.
@param {float} [overlayPadding=0.00] The padding around the overlay image, as a percentage of the base image width, between 0 and 1 (inclusive).
@param {float} [overlayWidth=0.20] The width of the overlay image, as a percentage of the base image width, between 0 and 1.
@param {string} src The path to the base image: a page resource, a global resource, or a remote resource.
@param {string} [title] The img element's title attribute.
@param {int} [width] The display width of the image, in pixels, falling back to 100% of the viewport width.

@returns {template.HTML}

@example (required args only)

  {{< picture src="images/zion-national-park.jpg" >}}

@example (all args)

  {{< picture
    alt="Bryce Canyon National Park"
    class="foo"
    formats="webp,jpeg"
    loading=""eager"
    overlay="images/logo.png"
    overlayAnchor="bottomright"
    overlayPadding=0.02
    overlayWidth=0.25
    src="images/bryce-canyon-national-park.jpg"
    title="A beautiful day in Bryce Canyon National Park"
    width=768
  >}}

*/}}

{{- /* Create slices from comma or space separated values. */}}
{{- $formats := slice }}
{{- with .Get "formats" }}
  {{- range partial "inline/split.html" . }}
    {{- $formats = $formats | append . }}
  {{- end }}
{{- end }}

{{- /* Build the context to send to the picture partial. */}}
{{- $ctx := merge .Params
  (dict "page" .Page)
  (dict "formats" $formats)
}}

{{- /* Call the picture partial. */}}
{{- partial "picture.html" $ctx -}}

{{- /* Returns a slice of strings, splitting s by a comma or whitespace. */}}
{{- define "partials/inline/split.html" }}
  {{- $s := trim . " " }}
  {{- $s = replace $s " " "," }}
  {{- $s := replaceRE `,{2,}` "," $s }}
  {{- return split $s "," }}
{{- end -}}

Picture partial

Call the picture partial from any template, including a shortcode as shown above.

Arguments

The src and page arguments are required.

alt
(string) The img element’s alt attribute.
class
(string) The img element’s class attribute.
formats
(string slice) A slice of image formats, ordered by precedence, to use when creating images for the srcset attribute of each source element.
loading
(string) The img element’s loading attribute. Default is lazy.
overlay
(string) The path to the overlay image: a page resource, a global resource, or a remote resource.
overlayAnchor
(string) The anchor point of the overlay image. May be one of TopLeft, Top, TopRight, Left, Center, Right, BottomLeft, Bottom, or BottomRight. The value is case-insensitive. Default is BottomRight.
overlayPadding
(float) The padding around the overlay image, as a percentage of the base image width, between 0 and 1 (inclusive). Default is 0.00.
overlayWidth
(float) The width of the overlay image, as a percentage of the base image width, between 0 and 1. Default is 0.20.
page
(page) The current page.
src
(string) The path to the base image: a page resource, a global resource, or a remote resource.
title
(string) The img element’s title attribute.
width
(int) The display width of the image, in pixels, falling back to 100% of the viewport width.

Source code

layouts/partials/picture.html
{{- /* Last modified: 2023-09-05T11:48:34-07:00 */}}

{{- /*
Copyright 2023 Veriphor LLC

Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy of
the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License.
*/}}

{{- /*
Renders an HTML picture element, in multiple formats and sizes, with an optional overlay.

It resolves internal destinations by looking for a matching:

  1. Page resource (an image in the current page bundle)
  2. Section resource (an image in the current section)
  3. Global resource (an image in the assets directory)

It skips the section resource lookup if the current page is a leaf bundle, and
captures external destinations as resources for local hosting. The build will
fail if this partial is unable to resolve a destination.

You must place global resources in the assets directory. If you have placed
your resources in the static directory, and you are unable or unwilling to move
them, you must mount the static directory to the assets directory by including
both of these entries in your site configuration:

  [[module.mounts]]
  source = 'assets'
  target = 'assets'

  [[module.mounts]]
  source = 'static'
  target = 'assets'

Add this CSS to your site to enable responsive image behavior:

  img {
    height: auto;
    max-width: 100%;
  }

Add this CSS to your site to remove small gaps between adjacent elements:

  img, picture {
    font-size: 0;
  }

@context {string} [alt] The img element's alt attribute.
@context {string} [class] The img element's class attribute.
@context {string slice} [formats=webp,jpeg] A slice of image formats, ordered by precedence, to use when creating images for the srcset attribute of each source element.
@context {string} [loading=lazy] The img element's loading attribute.
@context {string} [overlay] The path to the overlay image: a page resource, a global resource, or a remote resource.
@context {string} [overlayAnchor=BottomRight] The anchor point of the overlay image. May be one of TopLeft, Top, TopRight, Left, Center, Right, BottomLeft, Bottom, or BottomRight. The value is case-insensitive.
@context {float} [overlayPadding=0.00] The padding around the overlay image, as a percentage of the base image width, between 0 and 1 (inclusive).
@context {float} [overlayWidth=0.20] The width of the overlay image, as a percentage of the base image width, between 0 and 1.
@context {string} src The path to the base image: a page resource, a global resource, or a remote resource.
@context {string} [title] The img element's title attribute.
@contect {int} [width] The display width of the image, in pixels, falling back to 100% of the viewport width.
@context {page} page The current page.

@returns {template.HTML}

@example (required args only)

  {{- partial "picture.html" (dict "page" . "src" "images/zion.jpg") }}

@example (all args)

  {{- $opts := dict
    "alt" "Bryce Canyon National Park"
    "class" "foo"
    "formats" (slice "webp" "jpeg")
    "loading" "eager"
    "overlay" "images/logo.png"
    "overlayAnchor" "bottomright"
    "overlayPadding" 0.02
    "overlayWidth" 0.25
    "page" .
    "src" "images/bryce-canyon-national-park.jpg"
    "title" "A beautiful day in Bryce Canyon National Park"
    "title" "A beautiful day in Bryce Canyon National Park"
    "width" 768
  }}
  {{- partial "picture.html" $opts }}

*/}}

{{- /* Initialize. */}}
{{- $partialName := "picture" }}

{{- /* Verify minimum required version. */}}
{{- $minHugoVersion := "0.118.0" }}
{{- if lt hugo.Version $minHugoVersion }}
  {{- errorf "The %q partial requires Hugo v%s or later." $partialName $minHugoVersion }}
{{- end }}

{{- /* Validate page arg. */}}
{{- if not .page }}
  {{- errorf "The %q partial requires a page argument." $partialName }}
{{- end }}

{{- /* Determine content path for warning and error messages. */}}
{{- $contentPath := "" }}
{{- with .page.File }}
  {{- $contentPath = .Path }}
{{- else }}
  {{- $contentPath = .Path }}
{{- end }}

{{- /* Set defaults and get args. */}}
{{- $alt := or .alt "" }}
{{- $class := or .class "" }}
{{- $formats := or .formats (slice "webp" "jpeg") }}
{{- $loading := or .loading "lazy" }}
{{- $overlay := or .overlay "" }}
{{- $overlayAnchor := or .overlayAnchor "bottomright" | lower }}
{{- $overlayPadding := or (float .overlayPadding) 0.00 }}
{{- $overlayWidth := or (float .overlayWidth) 0.20 }}
{{- $src := or .src "" }}
{{- $title := or .title "" }}
{{- $width := or (int .width) 0 }}
{{- $fallbackFormat := "jpeg" }}
{{- $stdWidths := slice 720 1080 1440 2160 }}

{{- /* Validate args. */}}
{{- $validFormats := slice "gif" "jpg" "jpeg" "png" "webp"}}
{{- if reflect.IsSlice $formats }}
  {{- $formats = apply $formats "strings.ToLower" "." }}
  {{- range $formats }}
    {{- if not (in $validFormats .) }}
      {{- errorf "The formats argument passed to the %q partial is invalid. Valid formats are %s. See %s" $partialName (delimit $validFormats ", " ", and ") $contentPath }}
    {{- end }}
  {{- end }}
{{- else }}
  {{- errorf "The formats argument passed to the %q partial is not a slice. See %s" $partialName $contentPath }}
{{- end }}

{{- $validOverlayAnchors := slice "topleft" "top" "topright" "left" "center" "right" "bottomleft" "bottom" "bottomright" }}
{{- if not (in $validOverlayAnchors $overlayAnchor) }}
  {{- errorf "The overlayAnchor argument passed to the %q partial is invalid. Valid anchor positions are %s. See %s" $partialName (delimit $validOverlayAnchors ", " ", and ") $contentPath }}
{{- end }}

{{- if not (and (ge $overlayPadding 0) (le $overlayPadding 1)) }}
  {{- errorf "The overlayPadding argument passed to the %q partial is invalid. The value must be between 0 and 1 (inclusive). See %s" $partialName $contentPath }}
{{- end }}

{{- if not (and (gt $overlayWidth 0) (lt $overlayWidth 1)) }}
  {{- errorf "The overlayWidth argument passed to the %q partial is invalid. The value must be between 0 and 1. See %s" $partialName $contentPath }}
{{- end }}

{{- if not $src }}
  {{- errorf "The %q partial requires an image path, relative to the assets directory. See %s" $partialName $contentPath }}
{{- end }}

{{- /* Capture base image (bi) as a resource. */}}
{{- $bi := "" }}
{{- $ctx := dict
  "page" .page
  "path" $src
  "partialName" $partialName
  "contentPath" $contentPath
}}
{{- with partial "inline/capture-resource.html" $ctx }}
  {{- $bi = . }}
{{- end }}

{{- /* Capture overlay image (oi) as a resource, then resize. */}}
{{- $oi := "" }}
{{- with $overlay }}
  {{- $ctx := dict
    "page" $.page
    "path" $overlay
    "partialName" $partialName
    "contentPath" $contentPath
  }}
  {{- with partial "inline/capture-resource.html" $ctx }}
    {{- with .Resize (printf "%dx" (mul $overlayWidth $bi.Width | int)) }}
      {{- $oi = . }}
    {{- end }}
  {{- end }}
{{- end }}

{{- /* Create composite image (ci) from base image (bi) and overlay image (oi). */}}
{{- $ci := $bi }}
{{- if $oi }}

  {{- /* Calulate x an y overlay offets where p is padding. */}}
  {{- $p := mul $overlayPadding $bi.Width | int }}

  {{- $xl := $p }}
  {{- $xc := sub (div $bi.Width 2) (div $oi.Width 2) | int }}
  {{- $xr := sub (sub $bi.Width $oi.Width) $p | int }}
  {{- $yt := $p }}
  {{- $yc := sub (div $bi.Height 2) (div $oi.Height 2) | int }}
  {{- $yb := sub (sub $bi.Height $oi.Height) $p | int }}

  {{- $x := 0 }}
  {{- $y := 0 }}

  {{- with $overlayAnchor }}
    {{- if eq . "bottomright" }}
      {{- $x = $xr}}
      {{- $y = $yb }}
    {{- else if eq . "bottomleft" }}
      {{- $x = $xl }}
      {{- $y = $yb }}
    {{- else if eq . "topright" }}
      {{- $x = $xr }}
      {{- $y = $yt }}
    {{- else if eq . "center" }}
      {{- $x = $xc }}
      {{- $y = $yc }}
    {{- else if eq . "topleft" }}
      {{- $x = $xl }}
      {{- $y = $yt }}
    {{- else if eq . "top" }}
      {{- $x = $xc }}
      {{- $y = $yt }}
    {{- else if eq . "left" }}
      {{- $x = $xl }}
      {{- $y = $yc }}
    {{- else if eq . "right" }}
      {{- $x = $xr }}
      {{- $y = $yc }}
    {{- else if eq . "bottom" }}
      {{- $x = $xc }}
      {{- $y = $yb }}
    {{- end }}
  {{- end }}

  {{- /* Apply the overlay. */}}
  {{- if $oi }}
    {{- $ci = $bi.Filter (images.Overlay $oi $x $y )}}
  {{- end }}

{{- end }}

{{- /* Determine widths for srcset generation. */}}
{{- $widths := slice }}
{{- if $width }}
  {{- /* The width was specified; generate 1x, 2x, 3x, and 4x images. */}}
  {{- $widths = slice $ci.Width }}
  {{- range seq 4 }}
    {{- with mul . $width }}
      {{- if and (le . $ci.Width) (le . (math.Max $stdWidths)) }}
        {{- /* Do not enlarge, and do not exceed maximum of $stdWidths. */}}
        {{- $widths = $widths | append . }}
      {{- end }}
    {{- end }}
  {{- end }}
{{- else }}
  {{- /* The width was not speficied, will be using $stdWidths with 100vw. */}}
  {{- $stdWidths = $stdWidths | append $ci.Width | sort }}
  {{- range $stdWidths }}
    {{- /* Do not enlarge. */}}
    {{- if (le . $ci.Width) }}
      {{- $widths = $widths | append . }}
    {{- end }}
  {{- end }}
{{- end }}
{{- $widths = $widths | uniq | sort}}

{{- /* Create fallback image (fi) with the smallest of widths. */}}
{{- $fi := $ci.Resize (printf "%dx %s" (math.Min $widths | int) $fallbackFormat) }}

{{- /* Create the image map. */}}
{{- $im := dict  }}
{{- range $format := $formats }}
  {{- $sizes := slice }}
  {{- range sort $widths }}
    {{- $sizes = $sizes | append ($ci.Resize (printf "%dx %s" . $format)) }}
  {{- end }}
  {{- $im = merge $im (dict $format $sizes) }}
{{- end }}

{{- /* Render. */}}
<picture>
  {{- range $formats }}
    {{- with index $im . }}
      {{- $sizes := "100vw" }}
      {{- with $width }}
        {{- $sizes = printf "%dpx" . }}
      {{- end }}
      <source type="{{ (index . 0).MediaType.Type }}" srcset="
        {{- range $k, $_ := . }}
          {{- if $k }},{{- end }}
          {{- printf `%s %dw` .RelPermalink .Width }}
        {{- end }}"
        sizes="{{ $sizes }}">
    {{- end }}
  {{- end }}
  {{- $attrs := dict
    "src" $fi.RelPermalink
    "alt" $alt
    "class" $class
    "title" $title
    "loading" $loading
  }}
  <img
    {{- range $k, $v := $attrs }}
      {{- if or $v (eq $k "alt") }}
        {{- printf " %s=%q" $k $v | safeHTMLAttr }}
      {{- end }}
    {{- end -}}
  >
</picture>

{{- define "partials/inline/capture-resource.html" }}
  {{- /* Parse destination. */}}
  {{- $u := urls.Parse .path }}

  {{- /* Set common message. */}}
  {{- $msg := printf "The %q partial was unable to get %q in %s" .partialName $u.String .contentPath }}

  {{- /* Get image resource. */}}
  {{- $r := "" }}
  {{- if $u.IsAbs }}
    {{- with resources.GetRemote $u.String }}
      {{- with .Err }}
        {{- errorf "%s. See %s" . $.contentPath }}
      {{- else }}
        {{- /* Destination is a remote resource. */}}
        {{- $r = . }}
      {{- end }}
    {{- else }}
      {{- errorf $msg }}
    {{- end }}
  {{- else }}
    {{- with .page.Resources.Get (strings.TrimPrefix "./" $u.Path) }}
      {{- /* Destination is a page resource. */}}
      {{- $r = . }}
    {{- else }}
      {{- with (and (ne .page.BundleType "leaf") (.page.CurrentSection.Resources.Get (strings.TrimPrefix "./" $u.Path)) ) }}
        {{- /* Destination is a section resource, and current page is not a leaf bundle. */}}
        {{- $r = . }}
      {{- else }}
        {{- with resources.Get $u.Path }}
          {{- /* Destination is a global resource. */}}
          {{- $r = . }}
        {{- else }}
          {{- errorf $msg }}
        {{- end }}
      {{- end }}
    {{- end }}
  {{- end }}
  {{- return $r }}
{{- end -}}
Last modified: