github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/app/imaging/decode.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package imaging
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"image"
    10  	_ "image/gif"
    11  	_ "image/jpeg"
    12  	_ "image/png"
    13  	"io"
    14  	"sync"
    15  
    16  	_ "github.com/oov/psd"
    17  	_ "golang.org/x/image/bmp"
    18  	_ "golang.org/x/image/tiff"
    19  )
    20  
    21  // DecoderOptions holds configuration options for an image decoder.
    22  type DecoderOptions struct {
    23  	// The level of concurrency for the decoder. This defines a limit on the
    24  	// number of concurrently running encoding goroutines.
    25  	ConcurrencyLevel int
    26  }
    27  
    28  func (o *DecoderOptions) validate() error {
    29  	if o.ConcurrencyLevel < 0 {
    30  		return errors.New("ConcurrencyLevel must be non-negative")
    31  	}
    32  	return nil
    33  }
    34  
    35  // Decoder holds the necessary state to decode images.
    36  // This is safe to be used from multiple goroutines.
    37  type Decoder struct {
    38  	sem  chan struct{}
    39  	opts DecoderOptions
    40  }
    41  
    42  // NewDecoder creates and returns a new image decoder with the given options.
    43  func NewDecoder(opts DecoderOptions) (*Decoder, error) {
    44  	var d Decoder
    45  	if err := opts.validate(); err != nil {
    46  		return nil, fmt.Errorf("imaging: error validating decoder options: %w", err)
    47  	}
    48  	if opts.ConcurrencyLevel > 0 {
    49  		d.sem = make(chan struct{}, opts.ConcurrencyLevel)
    50  	}
    51  	d.opts = opts
    52  	return &d, nil
    53  }
    54  
    55  // Decode decodes the given encoded data and returns the decoded image.
    56  func (d *Decoder) Decode(rd io.Reader) (img image.Image, format string, err error) {
    57  	if d.opts.ConcurrencyLevel != 0 {
    58  		d.sem <- struct{}{}
    59  		defer func() { <-d.sem }()
    60  	}
    61  
    62  	img, format, err = image.Decode(rd)
    63  	if err != nil {
    64  		return nil, "", fmt.Errorf("imaging: failed to decode image: %w", err)
    65  	}
    66  
    67  	return img, format, nil
    68  }
    69  
    70  // DecodeMemBounded works similarly to Decode but also returns a release function that
    71  // must be called when access to the raw image is not needed anymore.
    72  // This sets the raw image data pointer to nil in an attempt to help the GC to re-use the underlying data as soon as possible.
    73  func (d *Decoder) DecodeMemBounded(rd io.Reader) (img image.Image, format string, releaseFunc func(), err error) {
    74  	if d.opts.ConcurrencyLevel != 0 {
    75  		d.sem <- struct{}{}
    76  		defer func() {
    77  			if err != nil {
    78  				<-d.sem
    79  			}
    80  		}()
    81  	}
    82  
    83  	img, format, err = image.Decode(rd)
    84  	if err != nil {
    85  		return nil, "", nil, fmt.Errorf("imaging: failed to decode image: %w", err)
    86  	}
    87  
    88  	var once sync.Once
    89  	releaseFunc = func() {
    90  		if d.opts.ConcurrencyLevel == 0 {
    91  			return
    92  		}
    93  		once.Do(func() {
    94  			if img != nil {
    95  				releaseImageData(img)
    96  			}
    97  			<-d.sem
    98  		})
    99  	}
   100  
   101  	return img, format, releaseFunc, nil
   102  }
   103  
   104  // DecodeConfig returns the image config for the given data.
   105  func (d *Decoder) DecodeConfig(rd io.Reader) (image.Config, string, error) {
   106  	img, format, err := image.DecodeConfig(rd)
   107  	if err != nil {
   108  		return image.Config{}, "", fmt.Errorf("imaging: failed to decode image config: %w", err)
   109  	}
   110  	return img, format, nil
   111  }
   112  
   113  // GetDimensions returns the dimensions for the given encoded image data.
   114  func GetDimensions(imageData io.Reader) (int, int, error) {
   115  	cfg, _, err := image.DecodeConfig(imageData)
   116  	if seeker, ok := imageData.(io.ReadSeeker); ok {
   117  		defer seeker.Seek(0, 0)
   118  	}
   119  	return cfg.Width, cfg.Height, err
   120  }
   121  
   122  // This is only needed to try and simplify GC work.
   123  func releaseImageData(img image.Image) {
   124  	switch raw := img.(type) {
   125  	case *image.Alpha:
   126  		raw.Pix = nil
   127  	case *image.Alpha16:
   128  		raw.Pix = nil
   129  	case *image.Gray:
   130  		raw.Pix = nil
   131  	case *image.Gray16:
   132  		raw.Pix = nil
   133  	case *image.NRGBA:
   134  		raw.Pix = nil
   135  	case *image.NRGBA64:
   136  		raw.Pix = nil
   137  	case *image.Paletted:
   138  		raw.Pix = nil
   139  	case *image.RGBA:
   140  		raw.Pix = nil
   141  	case *image.RGBA64:
   142  		raw.Pix = nil
   143  	default:
   144  		return
   145  	}
   146  }