scale_on_load.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. package processing
  2. import (
  3. "math"
  4. "github.com/imgproxy/imgproxy/v3/config"
  5. "github.com/imgproxy/imgproxy/v3/imagedata"
  6. "github.com/imgproxy/imgproxy/v3/imagetype"
  7. "github.com/imgproxy/imgproxy/v3/imath"
  8. "github.com/imgproxy/imgproxy/v3/options"
  9. "github.com/imgproxy/imgproxy/v3/vips"
  10. )
  11. func canScaleOnLoad(pctx *pipelineContext, imgdata *imagedata.ImageData, scale float64) bool {
  12. if imgdata == nil || pctx.trimmed || scale == 1 {
  13. return false
  14. }
  15. if imgdata.Type == imagetype.SVG {
  16. return true
  17. }
  18. if config.DisableShrinkOnLoad || scale >= 1 {
  19. return false
  20. }
  21. return imgdata.Type == imagetype.JPEG ||
  22. imgdata.Type == imagetype.WEBP ||
  23. imgdata.Type == imagetype.HEIC ||
  24. imgdata.Type == imagetype.AVIF
  25. }
  26. func calcJpegShink(scale float64, imgtype imagetype.Type) int {
  27. shrink := int(1.0 / scale)
  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. prescale := math.Max(pctx.wscale, pctx.hscale)
  40. if !canScaleOnLoad(pctx, imgdata, prescale) {
  41. return nil
  42. }
  43. var newWidth, newHeight int
  44. if imgdata.Type.SupportsThumbnail() {
  45. thumbnail := new(vips.Image)
  46. defer thumbnail.Clear()
  47. if err := thumbnail.LoadThumbnail(imgdata); err != nil {
  48. return err
  49. }
  50. angle, flip := 0, false
  51. newWidth, newHeight, angle, flip = extractMeta(thumbnail, po.Rotate, po.AutoRotate)
  52. if newWidth >= pctx.srcWidth || float64(newWidth)/float64(pctx.srcWidth) < prescale {
  53. return nil
  54. }
  55. img.Swap(thumbnail)
  56. pctx.angle = angle
  57. pctx.flip = flip
  58. } else {
  59. jpegShrink := calcJpegShink(prescale, pctx.imgtype)
  60. if pctx.imgtype == imagetype.JPEG && jpegShrink == 1 {
  61. return nil
  62. }
  63. if err := img.Load(imgdata, jpegShrink, prescale, 1); err != nil {
  64. return err
  65. }
  66. newWidth, newHeight, _, _ = extractMeta(img, po.Rotate, po.AutoRotate)
  67. }
  68. // Update scales after scale-on-load
  69. wpreshrink := float64(pctx.srcWidth) / float64(newWidth)
  70. hpreshrink := float64(pctx.srcHeight) / float64(newHeight)
  71. pctx.wscale = wpreshrink * pctx.wscale
  72. if newWidth == imath.Scale(newWidth, pctx.wscale) {
  73. pctx.wscale = 1.0
  74. }
  75. pctx.hscale = hpreshrink * pctx.hscale
  76. if newHeight == imath.Scale(newHeight, pctx.hscale) {
  77. pctx.hscale = 1.0
  78. }
  79. if pctx.cropWidth > 0 {
  80. pctx.cropWidth = imath.Max(1, imath.Shrink(pctx.cropWidth, wpreshrink))
  81. }
  82. if pctx.cropHeight > 0 {
  83. pctx.cropHeight = imath.Max(1, imath.Shrink(pctx.cropHeight, hpreshrink))
  84. }
  85. if pctx.cropGravity.Type != options.GravityFocusPoint {
  86. pctx.cropGravity.X /= wpreshrink
  87. pctx.cropGravity.Y /= hpreshrink
  88. }
  89. return nil
  90. }