scale_on_load.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. package processing
  2. import (
  3. "math"
  4. log "github.com/sirupsen/logrus"
  5. "github.com/imgproxy/imgproxy/v3/config"
  6. "github.com/imgproxy/imgproxy/v3/imagedata"
  7. "github.com/imgproxy/imgproxy/v3/imagetype"
  8. "github.com/imgproxy/imgproxy/v3/imath"
  9. "github.com/imgproxy/imgproxy/v3/options"
  10. "github.com/imgproxy/imgproxy/v3/vips"
  11. )
  12. func canScaleOnLoad(pctx *pipelineContext, imgdata imagedata.ImageData, scale float64) bool {
  13. if imgdata == nil || pctx.trimmed || scale == 1 {
  14. return false
  15. }
  16. if imgdata.Format().IsVector() {
  17. return true
  18. }
  19. if config.DisableShrinkOnLoad || scale >= 1 {
  20. return false
  21. }
  22. return imgdata.Format() == imagetype.JPEG ||
  23. imgdata.Format() == imagetype.WEBP ||
  24. imgdata.Format() == imagetype.HEIC ||
  25. imgdata.Format() == imagetype.AVIF
  26. }
  27. func calcJpegShink(shrink float64) int {
  28. switch {
  29. case shrink >= 8:
  30. return 8
  31. case shrink >= 4:
  32. return 4
  33. case shrink >= 2:
  34. return 2
  35. }
  36. return 1
  37. }
  38. func scaleOnLoad(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptions, imgdata imagedata.ImageData) error {
  39. wshrink := float64(pctx.srcWidth) / float64(imath.Scale(pctx.srcWidth, pctx.wscale))
  40. hshrink := float64(pctx.srcHeight) / float64(imath.Scale(pctx.srcHeight, pctx.hscale))
  41. preshrink := math.Min(wshrink, hshrink)
  42. prescale := 1.0 / preshrink
  43. if imgdata != nil && imgdata.Format().IsVector() {
  44. // For vector images, apply the vector base scale
  45. prescale *= pctx.vectorBaseScale
  46. }
  47. if !canScaleOnLoad(pctx, imgdata, prescale) {
  48. return nil
  49. }
  50. var newWidth, newHeight int
  51. if imgdata.Format().SupportsThumbnail() {
  52. thumbnail := new(vips.Image)
  53. defer thumbnail.Clear()
  54. if err := thumbnail.LoadThumbnail(imgdata); err != nil {
  55. log.Debugf("Can't load thumbnail: %s", err)
  56. return nil
  57. }
  58. angle, flip := 0, false
  59. newWidth, newHeight, angle, flip = extractMeta(thumbnail, po.Rotate, po.AutoRotate)
  60. if newWidth >= pctx.srcWidth || float64(newWidth)/float64(pctx.srcWidth) < prescale {
  61. return nil
  62. }
  63. img.Swap(thumbnail)
  64. pctx.angle = angle
  65. pctx.flip = flip
  66. } else {
  67. jpegShrink := calcJpegShink(preshrink)
  68. if pctx.imgtype == imagetype.JPEG && jpegShrink == 1 {
  69. return nil
  70. }
  71. if err := img.Load(imgdata, jpegShrink, prescale, 1); err != nil {
  72. return err
  73. }
  74. newWidth, newHeight, _, _ = extractMeta(img, po.Rotate, po.AutoRotate)
  75. }
  76. // Update scales after scale-on-load
  77. wpreshrink := float64(pctx.srcWidth) / float64(newWidth)
  78. hpreshrink := float64(pctx.srcHeight) / float64(newHeight)
  79. pctx.wscale = wpreshrink * pctx.wscale
  80. if newWidth == imath.Scale(newWidth, pctx.wscale) {
  81. pctx.wscale = 1.0
  82. }
  83. pctx.hscale = hpreshrink * pctx.hscale
  84. if newHeight == imath.Scale(newHeight, pctx.hscale) {
  85. pctx.hscale = 1.0
  86. }
  87. // We should crop before scaling, but we scaled the image on load,
  88. // so we need to adjust crop options
  89. if pctx.cropWidth > 0 {
  90. pctx.cropWidth = max(1, imath.Shrink(pctx.cropWidth, wpreshrink))
  91. }
  92. if pctx.cropHeight > 0 {
  93. pctx.cropHeight = max(1, imath.Shrink(pctx.cropHeight, hpreshrink))
  94. }
  95. if pctx.cropGravity.Type != options.GravityFocusPoint {
  96. // Adjust only when crop gravity offsets are absolute
  97. if math.Abs(pctx.cropGravity.X) >= 1.0 {
  98. // Round offsets to prevent turning absolute offsets to relative (ex: 1.0 => 0.5)
  99. pctx.cropGravity.X = math.RoundToEven(pctx.cropGravity.X / wpreshrink)
  100. }
  101. if math.Abs(pctx.cropGravity.Y) >= 1.0 {
  102. pctx.cropGravity.Y = math.RoundToEven(pctx.cropGravity.Y / hpreshrink)
  103. }
  104. }
  105. return nil
  106. }