Skip to content
This repository was archived by the owner on Oct 8, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,14 @@ For Gaussian blur, the radius used is this value * the image width. This allows
you to use a blur parameter (from 0-1) which will apply the same proportion of
blurring to each image size.

##### auto_orient

If set to true, the image processor will respect EXIF rotation data. A common
case are photos taken with a camera (eg: iPhone, digital camera) in landscape
mode. The built-in gyroscope will embed rotation data in the image via EXIF.

Disabled by default.

### Routes

The `routes` block is a mapping of route patterns to route configuration values.
Expand Down
2 changes: 2 additions & 0 deletions halfshell/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ type ProcessorConfig struct {
DefaultImageWidth uint64
MaxImageDimensions ImageDimensions
MaxBlurRadiusPercentage float64
AutoOrient bool

// DEPRECATED
MaintainAspectRatio bool
Expand Down Expand Up @@ -228,6 +229,7 @@ func (c *configParser) parseProcessorConfig(processorName string) *ProcessorConf
DefaultImageWidth: c.uintForKeypath("processors.%s.default_image_width", processorName),
MaxImageDimensions: maxDimensions,
MaxBlurRadiusPercentage: c.floatForKeypath("processors.%s.max_blur_radius_percentage", processorName),
AutoOrient: c.boolForKeypath("processors.%s.auto_orient", processorName),

// DEPRECATED
MaintainAspectRatio: c.boolForKeypath("processors.%s.maintain_aspect_ratio", processorName),
Expand Down
53 changes: 52 additions & 1 deletion halfshell/image_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,15 @@ func (ip *imageProcessor) ProcessImage(img *Image, req *ImageProcessorOptions) e
req.Dimensions.Height = uint(ip.Config.DefaultImageHeight)
}

err := ip.resize(img, req)
var err error

err = ip.orient(img, req)
if err != nil {
ip.Logger.Errorf("Error orienting image: %s", err)
return err
}

err = ip.resize(img, req)
if err != nil {
ip.Logger.Errorf("Error resizing image: %s", err)
return err
Expand All @@ -83,6 +91,49 @@ func (ip *imageProcessor) ProcessImage(img *Image, req *ImageProcessorOptions) e
return nil
}

func (ip *imageProcessor) orient(img *Image, req *ImageProcessorOptions) error {
if !ip.Config.AutoOrient {
return nil
}

orientation := img.Wand.GetImageOrientation()

switch orientation {
case imagick.ORIENTATION_UNDEFINED:
case imagick.ORIENTATION_TOP_LEFT:
return nil
}

transparent := imagick.NewPixelWand()
defer transparent.Destroy()
transparent.SetColor("none")

var err error

switch orientation {
case imagick.ORIENTATION_TOP_RIGHT:
err = img.Wand.FlopImage()
case imagick.ORIENTATION_BOTTOM_RIGHT:
err = img.Wand.RotateImage(transparent, 180)
case imagick.ORIENTATION_BOTTOM_LEFT:
err = img.Wand.FlipImage()
case imagick.ORIENTATION_LEFT_TOP:
err = img.Wand.TransposeImage()
case imagick.ORIENTATION_RIGHT_TOP:
err = img.Wand.RotateImage(transparent, 90)
case imagick.ORIENTATION_RIGHT_BOTTOM:
err = img.Wand.TransverseImage()
case imagick.ORIENTATION_LEFT_BOTTOM:
err = img.Wand.RotateImage(transparent, 270)
}

if err != nil {
return err
}

return img.Wand.SetImageOrientation(imagick.ORIENTATION_TOP_LEFT)
}

func (ip *imageProcessor) resize(img *Image, req *ImageProcessorOptions) error {
scaleMode := req.ScaleMode
if scaleMode == 0 {
Expand Down