hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

smartcrop.go (2938B)

    1 // Copyright 2019 The Hugo Authors. All rights reserved.
    2 //
    3 // Licensed under the Apache License, Version 2.0 (the "License");
    4 // you may not use this file except in compliance with the License.
    5 // You may obtain a copy of the License at
    6 // http://www.apache.org/licenses/LICENSE-2.0
    7 //
    8 // Unless required by applicable law or agreed to in writing, software
    9 // distributed under the License is distributed on an "AS IS" BASIS,
   10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   11 // See the License for the specific language governing permissions and
   12 // limitations under the License.
   13 
   14 package images
   15 
   16 import (
   17 	"image"
   18 	"math"
   19 
   20 	"github.com/disintegration/gift"
   21 
   22 	"github.com/muesli/smartcrop"
   23 )
   24 
   25 const (
   26 	// Do not change.
   27 	smartCropIdentifier = "smart"
   28 
   29 	// This is just a increment, starting on 1. If Smart Crop improves its cropping, we
   30 	// need a way to trigger a re-generation of the crops in the wild, so increment this.
   31 	smartCropVersionNumber = 1
   32 )
   33 
   34 func (p *ImageProcessor) newSmartCropAnalyzer(filter gift.Resampling) smartcrop.Analyzer {
   35 	return smartcrop.NewAnalyzer(imagingResizer{p: p, filter: filter})
   36 }
   37 
   38 // Needed by smartcrop
   39 type imagingResizer struct {
   40 	p      *ImageProcessor
   41 	filter gift.Resampling
   42 }
   43 
   44 func (r imagingResizer) Resize(img image.Image, width, height uint) image.Image {
   45 	// See https://github.com/gohugoio/hugo/issues/7955#issuecomment-861710681
   46 	scaleX, scaleY := calcFactorsNfnt(width, height, float64(img.Bounds().Dx()), float64(img.Bounds().Dy()))
   47 	if width == 0 {
   48 		width = uint(math.Ceil(float64(img.Bounds().Dx()) / scaleX))
   49 	}
   50 	if height == 0 {
   51 		height = uint(math.Ceil(float64(img.Bounds().Dy()) / scaleY))
   52 	}
   53 	result, _ := r.p.Filter(img, gift.Resize(int(width), int(height), r.filter))
   54 	return result
   55 }
   56 
   57 func (p *ImageProcessor) smartCrop(img image.Image, width, height int, filter gift.Resampling) (image.Rectangle, error) {
   58 	if width <= 0 || height <= 0 {
   59 		return image.Rectangle{}, nil
   60 	}
   61 
   62 	srcBounds := img.Bounds()
   63 	srcW := srcBounds.Dx()
   64 	srcH := srcBounds.Dy()
   65 
   66 	if srcW <= 0 || srcH <= 0 {
   67 		return image.Rectangle{}, nil
   68 	}
   69 
   70 	if srcW == width && srcH == height {
   71 		return srcBounds, nil
   72 	}
   73 
   74 	smart := p.newSmartCropAnalyzer(filter)
   75 
   76 	rect, err := smart.FindBestCrop(img, width, height)
   77 	if err != nil {
   78 		return image.Rectangle{}, err
   79 	}
   80 
   81 	return img.Bounds().Intersect(rect), nil
   82 }
   83 
   84 // Calculates scaling factors using old and new image dimensions.
   85 // Code borrowed from https://github.com/nfnt/resize/blob/83c6a9932646f83e3267f353373d47347b6036b2/resize.go#L593
   86 func calcFactorsNfnt(width, height uint, oldWidth, oldHeight float64) (scaleX, scaleY float64) {
   87 	if width == 0 {
   88 		if height == 0 {
   89 			scaleX = 1.0
   90 			scaleY = 1.0
   91 		} else {
   92 			scaleY = oldHeight / float64(height)
   93 			scaleX = scaleY
   94 		}
   95 	} else {
   96 		scaleX = oldWidth / float64(width)
   97 		if height == 0 {
   98 			scaleY = scaleX
   99 		} else {
  100 			scaleY = oldHeight / float64(height)
  101 		}
  102 	}
  103 	return
  104 }