Beispiel #1
0
func (ih *ImageHandler) scaleImage(fileRef blob.Ref) (*formatAndImage, error) {
	fr, err := schema.NewFileReader(ih.storageSeekFetcher(), fileRef)
	if err != nil {
		return nil, err
	}
	defer fr.Close()

	var buf bytes.Buffer
	scaleImageGateSlurp.Start()
	n, err := io.Copy(&buf, fr)
	scaleImageGateSlurp.Done()
	imageBytesFetchedVar.Add(n)

	if err != nil {
		return nil, fmt.Errorf("image resize: error reading image %s: %v", fileRef, err)
	}

	scaleImageGateResize.Start()
	defer scaleImageGateResize.Done()

	i, imConfig, err := images.Decode(bytes.NewReader(buf.Bytes()),
		&images.DecodeOpts{MaxWidth: ih.MaxWidth, MaxHeight: ih.MaxHeight})
	if err != nil {
		return nil, err
	}
	b := i.Bounds()
	format := imConfig.Format

	useBytesUnchanged := !imConfig.Modified &&
		format != "cr2" // always recompress CR2 files

	isSquare := b.Dx() == b.Dy()
	if ih.Square && !isSquare {
		useBytesUnchanged = false
		i = squareImage(i)
		b = i.Bounds()
	}

	if !useBytesUnchanged {
		// Encode as a new image
		buf.Reset()
		switch format {
		case "png":
			err = png.Encode(&buf, i)
		case "cr":
			// Recompress CR2 files as JPEG
			format = "jpeg"
			fallthrough
		default:
			err = jpeg.Encode(&buf, i, &jpeg.Options{
				Quality: 90,
			})
		}
		if err != nil {
			return nil, err
		}
	}
	return &formatAndImage{format: format, image: buf.Bytes()}, nil
}
Beispiel #2
0
func (ih *ImageHandler) scaleImage(buf *bytes.Buffer, file blob.Ref) (format string, err error) {
	fr, err := schema.NewFileReader(ih.storageSeekFetcher(), file)
	if err != nil {
		return format, err
	}
	defer fr.Close()

	_, err = io.Copy(buf, fr)
	if err != nil {
		return format, fmt.Errorf("image resize: error reading image %s: %v", file, err)
	}
	i, imConfig, err := images.Decode(bytes.NewReader(buf.Bytes()),
		&images.DecodeOpts{MaxWidth: ih.MaxWidth, MaxHeight: ih.MaxHeight})
	if err != nil {
		return format, err
	}
	b := i.Bounds()
	format = imConfig.Format

	useBytesUnchanged := !imConfig.Modified &&
		format != "cr2" // always recompress CR2 files

	isSquare := b.Dx() == b.Dy()
	if ih.Square && !isSquare {
		useBytesUnchanged = false
		i = squareImage(i)
		b = i.Bounds()
	}

	if !useBytesUnchanged {
		// Encode as a new image
		buf.Reset()
		switch format {
		case "png":
			err = png.Encode(buf, i)
		case "cr":
			// Recompress CR2 files as JPEG
			format = "jpeg"
			fallthrough
		default:
			err = jpeg.Encode(buf, i, nil)
		}
		if err != nil {
			return format, err
		}
	}
	return format, nil
}
Beispiel #3
0
func (ih *ImageHandler) scaleImage(buf *bytes.Buffer, file *blobref.BlobRef) (format string, err error) {
	mw, mh := ih.MaxWidth, ih.MaxHeight

	fr, err := schema.NewFileReader(ih.storageSeekFetcher(), file)
	if err != nil {
		return format, err
	}

	_, err = io.Copy(buf, fr)
	if err != nil {
		return format, fmt.Errorf("image resize: error reading image %s: %v", file, err)
	}
	i, format, err := images.Decode(bytes.NewReader(buf.Bytes()), nil)
	if err != nil {
		return format, err
	}
	b := i.Bounds()

	// TODO(mpl): sort the useBytesUnchanged story out,
	// so that a rotation/flip is not being ignored
	// when there was no rescaling required.
	useBytesUnchanged := true

	isSquare := b.Dx() == b.Dy()
	if ih.Square && !isSquare {
		useBytesUnchanged = false
		i = squareImage(i)
		b = i.Bounds()
	}

	// only do downscaling, otherwise just serve the original image
	if mw < b.Dx() || mh < b.Dy() {
		useBytesUnchanged = false

		const huge = 2400
		// If it's gigantic, it's more efficient to downsample first
		// and then resize; resizing will smooth out the roughness.
		// (trusting the moustachio guys on that one).
		if b.Dx() > huge || b.Dy() > huge {
			w, h := mw*2, mh*2
			if b.Dx() > b.Dy() {
				w = b.Dx() * h / b.Dy()
			} else {
				h = b.Dy() * w / b.Dx()
			}
			i = resize.Resample(i, i.Bounds(), w, h)
			b = i.Bounds()
		}
		// conserve proportions. use the smallest of the two as the decisive one.
		if mw > mh {
			mw = b.Dx() * mh / b.Dy()
		} else {
			mh = b.Dy() * mw / b.Dx()
		}
	}

	if !useBytesUnchanged {
		i = resize.Resize(i, b, mw, mh)
		// Encode as a new image
		buf.Reset()
		switch format {
		case "jpeg":
			err = jpeg.Encode(buf, i, nil)
		default:
			err = png.Encode(buf, i)
		}
		if err != nil {
			return format, err
		}
	}
	return format, nil
}
Beispiel #4
0
func (ih *ImageHandler) scaleImage(fileRef blob.Ref) (*formatAndImage, error) {
	fr, err := schema.NewFileReader(ih.Fetcher, fileRef)
	if err != nil {
		return nil, err
	}
	defer fr.Close()

	sr := types.NewStatsReader(imageBytesFetchedVar, fr)
	sr, conf, err := imageConfigFromReader(sr)
	if err != nil {
		return nil, err
	}

	// TODO(wathiede): build a size table keyed by conf.ColorModel for
	// common color models for a more exact size estimate.

	// This value is an estimate of the memory required to decode an image.
	// PNGs range from 1-64 bits per pixel (not all of which are supported by
	// the Go standard parser). JPEGs encoded in YCbCr 4:4:4 are 3 byte/pixel.
	// For all other JPEGs this is an overestimate.  For GIFs it is 3x larger
	// than needed.  How accurate this estimate is depends on the mix of
	// images being resized concurrently.
	ramSize := int64(conf.Width) * int64(conf.Height) * 3

	if err = ih.resizeSem.Acquire(ramSize); err != nil {
		return nil, err
	}
	defer ih.resizeSem.Release(ramSize)

	i, imConfig, err := images.Decode(sr, &images.DecodeOpts{
		MaxWidth:  ih.MaxWidth,
		MaxHeight: ih.MaxHeight,
	})
	if err != nil {
		return nil, err
	}
	b := i.Bounds()
	format := imConfig.Format

	isSquare := b.Dx() == b.Dy()
	if ih.Square && !isSquare {
		i = squareImage(i)
		b = i.Bounds()
	}

	// Encode as a new image
	var buf bytes.Buffer
	switch format {
	case "png":
		err = png.Encode(&buf, i)
	case "cr2":
		// Recompress CR2 files as JPEG
		format = "jpeg"
		fallthrough
	default:
		err = jpeg.Encode(&buf, i, &jpeg.Options{
			Quality: 90,
		})
	}
	if err != nil {
		return nil, err
	}

	return &formatAndImage{format: format, image: buf.Bytes()}, nil
}