1
0
mirror of https://github.com/imgproxy/imgproxy.git synced 2025-02-12 11:46:10 +02:00

GIF output support

This commit is contained in:
DarthSim 2018-11-08 16:34:21 +06:00
parent fcdc4580a5
commit 2c0b538eb5
16 changed files with 535 additions and 123 deletions

View File

@ -5,24 +5,83 @@ ENV GOPATH /go
ENV PATH /usr/local/go/bin:$PATH
ADD . /go/src/github.com/DarthSim/imgproxy
WORKDIR /go/src/github.com/DarthSim/imgproxy
# Install dependencies
RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \
&& apk --no-cache upgrade \
&& apk add --no-cache --virtual .build-deps go gcc musl-dev fftw-dev vips-dev \
&& cd /go/src/github.com/DarthSim/imgproxy \
&& CGO_LDFLAGS_ALLOW="-s|-w" go build -v -o /usr/local/bin/imgproxy \
&& apk del --purge .build-deps \
&& rm -rf /var/cache/apk*
&& apk add --no-cache curl ca-certificates go gcc g++ make musl-dev fftw-dev glib-dev expat-dev \
libjpeg-turbo-dev libpng-dev libwebp-dev giflib-dev libexif-dev lcms2-dev
# Build ImageMagick
RUN cd /root \
&& mkdir ImageMagick \
&& curl -Ls https://imagemagick.org/download/ImageMagick.tar.gz | tar -xz -C ImageMagick --strip-components 1 \
&& cd ImageMagick \
&& ./configure \
--enable-silent-rules \
--disable-static \
--disable-openmp \
--disable-deprecated \
--disable-docs \
--with-threads \
--without-magick-plus-plus \
--without-utilities \
--without-perl \
--without-bzlib \
--without-dps \
--without-freetype \
--without-jbig \
--without-jpeg \
--without-lcms \
--without-lzma \
--without-png \
--without-tiff \
--without-wmf \
--without-xml \
--without-webp \
&& make install-strip
# Build libvips
RUN cd /root \
&& export VIPS_VERSION=$(curl -s "https://api.github.com/repos/libvips/libvips/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/') \
&& echo "Vips version: $VIPS_VERSION" \
&& curl -Ls https://github.com/libvips/libvips/releases/download/v$VIPS_VERSION/vips-$VIPS_VERSION.tar.gz | tar -xz \
&& cd vips-$VIPS_VERSION \
&& ./configure \
--disable-magickload \
--without-python \
--without-tiff \
--without-orc \
--without-OpenEXR \
--enable-debug=no \
--disable-static \
--enable-silent-rules \
&& make install-strip
# Build imgproxy
RUN cd /go/src/github.com/DarthSim/imgproxy \
&& CGO_LDFLAGS_ALLOW="-s|-w" go build -v -o /usr/local/bin/imgproxy
# Copy compiled libs here to copy them to the final image
RUN cd /root \
&& mkdir libs \
&& ldd /usr/local/bin/imgproxy | grep /usr/local/lib/ | awk '{print $3}' | xargs -I '{}' cp '{}' libs/
# ==================================================================================================
# Final image
FROM alpine:edge
LABEL maintainer="Sergey Alexandrovich <darthsim@gmail.com>"
RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \
&& apk --no-cache upgrade \
&& apk add --no-cache ca-certificates bash vips \
&& apk add --no-cache bash ca-certificates fftw glib expat libjpeg-turbo libpng \
libwebp giflib libexif lcms2 \
&& rm -rf /var/cache/apk*
COPY --from=0 /usr/local/bin/imgproxy /usr/local/bin
COPY --from=0 /usr/local/bin/imgproxy /usr/local/bin/
COPY --from=0 /root/libs/* /usr/local/lib/
CMD ["imgproxy"]

9
Gopkg.lock generated
View File

@ -274,6 +274,14 @@
pruneopts = "UT"
revision = "9dcd33a902f40452422c2367fefcb95b54f9f8f8"
[[projects]]
branch = "master"
digest = "1:b521f10a2d8fa85c04a8ef4e62f2d1e14d303599a55d64dabf9f5a02f84d35eb"
name = "golang.org/x/sync"
packages = ["errgroup"]
pruneopts = "UT"
revision = "42b317875d0fa942474b76e1b46a6060d720ae6e"
[[projects]]
branch = "master"
digest = "1:fbacfb57e3d052810813bca2d48c22fbde916ecfc4a2bac08df30ed6e9e59759"
@ -408,6 +416,7 @@
"github.com/stretchr/testify/suite",
"golang.org/x/image/webp",
"golang.org/x/net/netutil",
"golang.org/x/sync/errgroup",
"google.golang.org/api/option",
]
solver-name = "gps-cdcl"

View File

@ -79,7 +79,7 @@ Massive processing of remote images is a potentially dangerous thing, security-w
9. [Serving files from Google Cloud Storage](./docs/serving_files_from_google_cloud_storage.md)
10. [New Relic](./docs/new_relic.md)
11. [Prometheus](./docs/prometheus.md)
12. [Source image formats support](./docs/source_image_formats_support.md)
12. [Image formats support](./docs/image_formats_support.md)
13. [About processing pipeline](./docs/about_processing_pipeline.md)
14. [Health check](./docs/healthcheck.md)

View File

@ -271,7 +271,9 @@ When using encoded source URL, you can specify the [extension](#extension) after
#### Extension
Extension specifies the format of the resulting image. At the moment, imgproxy supports only `jpg`, `png` and `webp`, them being the most popular and useful image formats on the Web.
Extension specifies the format of the resulting image. At the moment, imgproxy supports only `jpg`, `png`, `webp`, and `gif`, them being the most popular and useful image formats on the Web.
**Note:** Read about GIF support [here](./image_formats_support.md#gif-support).
The extension part can be omitted. In this case, if the format is not defined by processing options, imgproxy will use `jpg` by default. You can also [enable WebP support detection](./configuration.md#webp-support-detection) to use it as default resulting format when possible.

View File

@ -87,7 +87,9 @@ When using encoded source URL, you can specify the [extension](#extension) after
#### Extension
Extension specifies the format of the resulting image. At the moment, imgproxy supports only `jpg`, `png` and `webp`, them being the most popular and useful image formats on the Web.
Extension specifies the format of the resulting image. At the moment, imgproxy supports only `jpg`, `png`, `webp`, and `gif`, them being the most popular and useful image formats on the Web.
**Note:** Read about GIF support [here](./image_formats_support.md#gif-support).
The extension part can be omitted. In this case, imgproxy will use `jpg` by default. You also can [enable WebP support detection](./configuration.md#webp-support-detection) to use it as default resulting format when possible.

View File

@ -0,0 +1,12 @@
# Image formats support
At the moment, imgproxy supports only the most popular Web image formats:
* PNG;
* JPEG;
* WebP;
* GIF.
## GIF support
imgproxy supports GIF output only when using libvips 8.7.0+ compiled with ImageMagick support. Official imgproxy Docker image supports GIF out of the box.

View File

@ -1,8 +0,0 @@
# Source image formats support
At the moment, imgproxy supports only the most popular Web image formats:
* PNG;
* JPEG;
* GIF;
* WebP.

View File

@ -63,23 +63,33 @@ func initDownloading() {
}
}
func checkDimensions(width, height int) error {
if width > conf.MaxSrcDimension || height > conf.MaxSrcDimension {
return errSourceDimensionsTooBig
}
if width*height > conf.MaxSrcResolution {
return errSourceResolutionTooBig
}
return nil
}
func checkTypeAndDimensions(r io.Reader) (imageType, error) {
imgconf, imgtypeStr, err := image.DecodeConfig(r)
imgtype, imgtypeOk := imageTypes[imgtypeStr]
if err != nil {
return imageTypeUnknown, err
}
if imgconf.Width > conf.MaxSrcDimension || imgconf.Height > conf.MaxSrcDimension {
return imageTypeUnknown, errSourceDimensionsTooBig
}
if imgconf.Width*imgconf.Height > conf.MaxSrcResolution {
return imageTypeUnknown, errSourceResolutionTooBig
}
imgtype, imgtypeOk := imageTypes[imgtypeStr]
if !imgtypeOk || !vipsTypeSupportLoad[imgtype] {
return imageTypeUnknown, errSourceImageTypeNotSupported
}
if err = checkDimensions(imgconf.Width, imgconf.Height); err != nil {
return imageTypeUnknown, err
}
return imgtype, nil
}

View File

@ -15,6 +15,8 @@ import (
"os"
"runtime"
"unsafe"
"golang.org/x/sync/errgroup"
)
var (
@ -81,6 +83,9 @@ func initVips() {
if int(C.vips_type_find_save_go(C.int(imageTypeWEBP))) != 0 {
vipsTypeSupportSave[imageTypeWEBP] = true
}
if int(C.vips_type_find_save_go(C.int(imageTypeGIF))) != 0 {
vipsTypeSupportSave[imageTypeGIF] = true
}
if conf.JpegProgressive {
cConf.JpegProgressive = C.int(1)
@ -216,35 +221,10 @@ func calcCrop(width, height int, po *processingOptions) (left, top int) {
return
}
func processImage(ctx context.Context) ([]byte, error) {
if newRelicEnabled {
newRelicCancel := startNewRelicSegment(ctx, "Processing image")
defer newRelicCancel()
}
func transformImage(ctx context.Context, img **C.struct__VipsImage, data []byte, po *processingOptions, imgtype imageType) error {
var err error
if prometheusEnabled {
defer startPrometheusDuration(prometheusProcessingDuration)()
}
defer C.vips_cleanup()
data := getImageData(ctx).Bytes()
po := getProcessingOptions(ctx)
imgtype := getImageType(ctx)
if po.Gravity.Type == gravitySmart && !vipsSupportSmartcrop {
return nil, errSmartCropNotSupported
}
img, err := vipsLoadImage(data, imgtype, 1)
if err != nil {
return nil, err
}
defer C.clear_image(&img)
checkTimeout(ctx)
imgWidth, imgHeight, angle, flip := extractMeta(img)
imgWidth, imgHeight, angle, flip := extractMeta(*img)
// Ensure we won't crop out of bounds
if !po.Enlarge || po.Resize == resizeCrop {
@ -257,20 +237,20 @@ func processImage(ctx context.Context) ([]byte, error) {
}
}
hasAlpha := vipsImageHasAlpha(img)
hasAlpha := vipsImageHasAlpha(*img)
if needToScale(imgWidth, imgHeight, po) {
scale := calcScale(imgWidth, imgHeight, po)
// Do some shrink-on-load
if scale < 1.0 {
if scale < 1.0 && data != nil {
if shrink := calcShink(scale, imgtype); shrink != 1 {
scale = scale * float64(shrink)
if tmp, e := vipsLoadImage(data, imgtype, shrink); e == nil {
C.swap_and_clear(&img, tmp)
if tmp, err := vipsLoadImage(data, imgtype, shrink, false); err == nil {
C.swap_and_clear(img, tmp)
} else {
return nil, e
return err
}
}
}
@ -279,50 +259,46 @@ func processImage(ctx context.Context) ([]byte, error) {
var bandFormat C.VipsBandFormat
if hasAlpha {
if bandFormat, err = vipsPremultiply(&img); err != nil {
return nil, err
if bandFormat, err = vipsPremultiply(img); err != nil {
return err
}
premultiplied = true
}
if err = vipsResize(&img, scale); err != nil {
return nil, err
if err = vipsResize(img, scale); err != nil {
return err
}
// Update actual image size after resize
imgWidth, imgHeight, _, _ = extractMeta(img)
imgWidth, imgHeight, _, _ = extractMeta(*img)
if premultiplied {
if err = vipsUnpremultiply(&img, bandFormat); err != nil {
return nil, err
if err = vipsUnpremultiply(img, bandFormat); err != nil {
return err
}
}
}
if err = vipsImportColourProfile(&img); err != nil {
return nil, err
}
if err = vipsFixColourspace(&img); err != nil {
return nil, err
if err = vipsImportColourProfile(img); err != nil {
return err
}
checkTimeout(ctx)
if angle != C.VIPS_ANGLE_D0 || flip {
if err = vipsImageCopyMemory(&img); err != nil {
return nil, err
if err = vipsImageCopyMemory(img); err != nil {
return err
}
if angle != C.VIPS_ANGLE_D0 {
if err = vipsRotate(&img, angle); err != nil {
return nil, err
if err = vipsRotate(img, angle); err != nil {
return err
}
}
if flip {
if err = vipsFlip(&img); err != nil {
return nil, err
if err = vipsFlip(img); err != nil {
return err
}
}
}
@ -339,21 +315,21 @@ func processImage(ctx context.Context) ([]byte, error) {
if po.Width < imgWidth || po.Height < imgHeight {
if po.Gravity.Type == gravitySmart {
if err = vipsImageCopyMemory(&img); err != nil {
return nil, err
if err = vipsImageCopyMemory(img); err != nil {
return err
}
if err = vipsSmartCrop(&img, po.Width, po.Height); err != nil {
return nil, err
if err = vipsSmartCrop(img, po.Width, po.Height); err != nil {
return err
}
// Applying additional modifications after smart crop causes SIGSEGV on Alpine
// so we have to copy memory after it
if err = vipsImageCopyMemory(&img); err != nil {
return nil, err
if err = vipsImageCopyMemory(img); err != nil {
return err
}
} else {
left, top := calcCrop(imgWidth, imgHeight, po)
if err = vipsCrop(&img, left, top, po.Width, po.Height); err != nil {
return nil, err
if err = vipsCrop(img, left, top, po.Width, po.Height); err != nil {
return err
}
}
@ -361,29 +337,153 @@ func processImage(ctx context.Context) ([]byte, error) {
}
if hasAlpha && po.Flatten {
if err = vipsFlatten(&img, po.Background); err != nil {
return nil, err
if err = vipsFlatten(img, po.Background); err != nil {
return err
}
}
if po.Blur > 0 {
if err = vipsBlur(&img, po.Blur); err != nil {
return nil, err
if err = vipsBlur(img, po.Blur); err != nil {
return err
}
}
if po.Sharpen > 0 {
if err = vipsSharpen(&img, po.Sharpen); err != nil {
return nil, err
if err = vipsSharpen(img, po.Sharpen); err != nil {
return err
}
}
checkTimeout(ctx)
if po.Watermark.Enabled {
if err = vipsApplyWatermark(&img, &po.Watermark); err != nil {
if err = vipsApplyWatermark(img, &po.Watermark); err != nil {
return err
}
}
if err = vipsFixColourspace(img); err != nil {
return err
}
return nil
}
func transformGif(ctx context.Context, img **C.struct__VipsImage, po *processingOptions) error {
imgWidth := int((*img).Xsize)
imgHeight := int((*img).Ysize)
// Double check dimensions because gif may have many frames
if err := checkDimensions(imgWidth, imgHeight); err != nil {
return err
}
frameHeight, err := vipsGetInt(*img, "page-height")
if err != nil {
return err
}
delay, err := vipsGetInt(*img, "gif-delay")
if err != nil {
return err
}
loop, err := vipsGetInt(*img, "gif-loop")
if err != nil {
return err
}
framesCount := imgHeight / frameHeight
frames := make([]*C.struct__VipsImage, framesCount)
defer func() {
for _, frame := range frames {
C.clear_image(&frame)
}
}()
var errg errgroup.Group
for i := 0; i < framesCount; i++ {
ind := i
errg.Go(func() error {
var frame *C.struct__VipsImage
if err := vipsExtract(*img, &frame, 0, ind*frameHeight, imgWidth, frameHeight); err != nil {
return err
}
if err := transformImage(ctx, &frame, nil, po, imageTypeGIF); err != nil {
return err
}
frames[ind] = frame
return nil
})
}
if err := errg.Wait(); err != nil {
return err
}
checkTimeout(ctx)
if err := vipsArrayjoin(frames, img); err != nil {
return err
}
vipsSetInt(*img, "page-height", int(frames[0].Ysize))
vipsSetInt(*img, "gif-delay", delay)
vipsSetInt(*img, "gif-loop", loop)
return nil
}
func processImage(ctx context.Context) ([]byte, error) {
if newRelicEnabled {
newRelicCancel := startNewRelicSegment(ctx, "Processing image")
defer newRelicCancel()
}
if prometheusEnabled {
defer startPrometheusDuration(prometheusProcessingDuration)()
}
po := getProcessingOptions(ctx)
defer C.vips_cleanup()
data := getImageData(ctx).Bytes()
imgtype := getImageType(ctx)
if po.Gravity.Type == gravitySmart && !vipsSupportSmartcrop {
return nil, errSmartCropNotSupported
}
img, err := vipsLoadImage(data, imgtype, 1, po.Format == imageTypeGIF)
if err != nil {
return nil, err
}
defer C.clear_image(&img)
if imgtype == imageTypeGIF && po.Format == imageTypeGIF {
if err := transformGif(ctx, &img, po); err != nil {
return nil, err
}
} else {
if err := transformImage(ctx, &img, data, po, imgtype); err != nil {
return nil, err
}
}
checkTimeout(ctx)
if po.Format == imageTypeGIF {
if err := vipsCastUchar(&img); err != nil {
return nil, err
}
checkTimeout(ctx)
}
return vipsSaveImage(img, po.Format, po.Quality)
@ -401,8 +501,9 @@ func vipsPrepareWatermark() error {
return nil
}
if C.vips_load_buffer(unsafe.Pointer(&data[0]), C.size_t(len(data)), C.int(imgtype), 1, &watermark) != 0 {
return vipsError()
watermark, err = vipsLoadImage(data, imgtype, 1, false)
if err != nil {
return err
}
var tmp *C.struct__VipsImage
@ -447,11 +548,30 @@ func vipsPrepareWatermark() error {
return nil
}
func vipsLoadImage(data []byte, imgtype imageType, shrink int) (*C.struct__VipsImage, error) {
func vipsLoadImage(data []byte, imgtype imageType, shrink int, allPages bool) (*C.struct__VipsImage, error) {
var img *C.struct__VipsImage
if C.vips_load_buffer(unsafe.Pointer(&data[0]), C.size_t(len(data)), C.int(imgtype), C.int(shrink), &img) != 0 {
err := C.int(0)
pages := C.int(1)
if allPages {
pages = -1
}
switch imgtype {
case imageTypeJPEG:
err = C.vips_jpegload_go(unsafe.Pointer(&data[0]), C.size_t(len(data)), C.int(shrink), &img)
case imageTypePNG:
err = C.vips_pngload_go(unsafe.Pointer(&data[0]), C.size_t(len(data)), &img)
case imageTypeWEBP:
err = C.vips_webpload_go(unsafe.Pointer(&data[0]), C.size_t(len(data)), C.int(shrink), &img)
case imageTypeGIF:
err = C.vips_gifload_go(unsafe.Pointer(&data[0]), C.size_t(len(data)), pages, &img)
}
if err != 0 {
return nil, vipsError()
}
return img, nil
}
@ -470,6 +590,8 @@ func vipsSaveImage(img *C.struct__VipsImage, imgtype imageType, quality int) ([]
err = C.vips_pngsave_go(img, &ptr, &imgsize, cConf.PngInterlaced)
case imageTypeWEBP:
err = C.vips_webpsave_go(img, &ptr, &imgsize, 1, C.int(quality))
case imageTypeGIF:
err = C.vips_gifsave_go(img, &ptr, &imgsize)
}
if err != 0 {
return nil, vipsError()
@ -478,10 +600,33 @@ func vipsSaveImage(img *C.struct__VipsImage, imgtype imageType, quality int) ([]
return C.GoBytes(ptr, C.int(imgsize)), nil
}
func vipsArrayjoin(in []*C.struct__VipsImage, out **C.struct__VipsImage) error {
var tmp *C.struct__VipsImage
if C.vips_arrayjoin_go(&in[0], &tmp, C.int(len(in))) != 0 {
return vipsError()
}
C.swap_and_clear(out, tmp)
return nil
}
func vipsImageHasAlpha(img *C.struct__VipsImage) bool {
return C.vips_image_hasalpha_go(img) > 0
}
func vipsGetInt(img *C.struct__VipsImage, name string) (int, error) {
var i C.int
if C.vips_image_get_int(img, C.CString(name), &i) != 0 {
return 0, vipsError()
}
return int(i), nil
}
func vipsSetInt(img *C.struct__VipsImage, name string, value int) {
C.vips_image_set_int(img, C.CString(name), C.int(value))
}
func vipsPremultiply(img **C.struct__VipsImage) (C.VipsBandFormat, error) {
var tmp *C.struct__VipsImage
@ -511,6 +656,19 @@ func vipsUnpremultiply(img **C.struct__VipsImage, format C.VipsBandFormat) error
return nil
}
func vipsCastUchar(img **C.struct__VipsImage) error {
var tmp *C.struct__VipsImage
if C.vips_image_get_format(*img) != C.VIPS_FORMAT_UCHAR {
if C.vips_cast_go(*img, &tmp, C.VIPS_FORMAT_UCHAR) != 0 {
return vipsError()
}
C.swap_and_clear(img, tmp)
}
return nil
}
func vipsResize(img **C.struct__VipsImage, scale float64) error {
var tmp *C.struct__VipsImage
@ -555,6 +713,13 @@ func vipsCrop(img **C.struct__VipsImage, left, top, width, height int) error {
return nil
}
func vipsExtract(in *C.struct__VipsImage, out **C.struct__VipsImage, left, top, width, height int) error {
if C.vips_extract_area_go(in, out, C.int(left), C.int(top), C.int(width), C.int(height)) != 0 {
return vipsError()
}
return nil
}
func vipsSmartCrop(img **C.struct__VipsImage, width, height int) error {
var tmp *C.struct__VipsImage
@ -770,8 +935,10 @@ func vipsApplyWatermark(img **C.struct__VipsImage, opts *watermarkOptions) error
}
C.swap_and_clear(&wm, tmp)
if C.vips_image_guess_interpretation(*img) != C.vips_image_guess_interpretation(wm) {
if C.vips_colourspace_go(wm, &tmp, C.vips_image_guess_interpretation(*img)) != 0 {
imgInterpolation := C.vips_image_guess_interpretation(*img)
if imgInterpolation != C.vips_image_guess_interpretation(wm) {
if C.vips_colourspace_go(wm, &tmp, imgInterpolation) != 0 {
return vipsError()
}
C.swap_and_clear(&wm, tmp)
@ -784,6 +951,8 @@ func vipsApplyWatermark(img **C.struct__VipsImage, opts *watermarkOptions) error
C.swap_and_clear(&wmAlpha, tmp)
}
imgFormat := C.vips_image_get_format(*img)
var imgAlpha *C.struct__VipsImage
defer C.clear_image(&imgAlpha)
@ -812,6 +981,13 @@ func vipsApplyWatermark(img **C.struct__VipsImage, opts *watermarkOptions) error
C.swap_and_clear(img, tmp)
}
if imgFormat != C.vips_image_get_format(*img) {
if C.vips_cast_go(*img, &tmp, imgFormat) != 0 {
return vipsError()
}
C.swap_and_clear(img, tmp)
}
return nil
}

View File

@ -24,12 +24,14 @@ var (
imageTypeJPEG: "image/jpeg",
imageTypePNG: "image/png",
imageTypeWEBP: "image/webp",
imageTypeGIF: "image/gif",
}
contentDispositions = map[imageType]string{
imageTypeJPEG: "inline; filename=\"image.jpg\"",
imageTypePNG: "inline; filename=\"image.png\"",
imageTypeWEBP: "inline; filename=\"image.webp\"",
imageTypeGIF: "inline; filename=\"image.gif\"",
}
authHeaderMust []byte

3
vendor/golang.org/x/sync/AUTHORS generated vendored Normal file
View File

@ -0,0 +1,3 @@
# This source code refers to The Go Authors for copyright purposes.
# The master list of authors is in the main Go distribution,
# visible at http://tip.golang.org/AUTHORS.

3
vendor/golang.org/x/sync/CONTRIBUTORS generated vendored Normal file
View File

@ -0,0 +1,3 @@
# This source code was written by the Go contributors.
# The master list of contributors is in the main Go distribution,
# visible at http://tip.golang.org/CONTRIBUTORS.

27
vendor/golang.org/x/sync/LICENSE generated vendored Normal file
View File

@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

22
vendor/golang.org/x/sync/PATENTS generated vendored Normal file
View File

@ -0,0 +1,22 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.

66
vendor/golang.org/x/sync/errgroup/errgroup.go generated vendored Normal file
View File

@ -0,0 +1,66 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package errgroup provides synchronization, error propagation, and Context
// cancelation for groups of goroutines working on subtasks of a common task.
package errgroup
import (
"context"
"sync"
)
// A Group is a collection of goroutines working on subtasks that are part of
// the same overall task.
//
// A zero Group is valid and does not cancel on error.
type Group struct {
cancel func()
wg sync.WaitGroup
errOnce sync.Once
err error
}
// WithContext returns a new Group and an associated Context derived from ctx.
//
// The derived Context is canceled the first time a function passed to Go
// returns a non-nil error or the first time Wait returns, whichever occurs
// first.
func WithContext(ctx context.Context) (*Group, context.Context) {
ctx, cancel := context.WithCancel(ctx)
return &Group{cancel: cancel}, ctx
}
// Wait blocks until all function calls from the Go method have returned, then
// returns the first non-nil error (if any) from them.
func (g *Group) Wait() error {
g.wg.Wait()
if g.cancel != nil {
g.cancel()
}
return g.err
}
// Go calls the given function in a new goroutine.
//
// The first call to return a non-nil error cancels the group; its error will be
// returned by Wait.
func (g *Group) Go(f func() error) {
g.wg.Add(1)
go func() {
defer g.wg.Done()
if err := f(); err != nil {
g.errOnce.Do(func() {
g.err = err
if g.cancel != nil {
g.cancel()
}
})
}
}()
}

75
vips.h
View File

@ -10,7 +10,10 @@
(VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 5))
#define VIPS_SUPPORT_GIF \
VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 3)
(VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 3))
#define VIPS_SUPPORT_MAGICK \
(VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 7))
#define EXIF_ORIENTATION "exif-ifd0-Orientation"
@ -38,16 +41,16 @@ swap_and_clear(VipsImage **in, VipsImage *out) {
int
vips_type_find_load_go(int imgtype) {
if (imgtype == JPEG) {
return vips_type_find("VipsOperation", "jpegload");
return vips_type_find("VipsOperation", "jpegload_buffer");
}
if (imgtype == PNG) {
return vips_type_find("VipsOperation", "pngload");
return vips_type_find("VipsOperation", "pngload_buffer");
}
if (imgtype == WEBP) {
return vips_type_find("VipsOperation", "webpload");
return vips_type_find("VipsOperation", "webpload_buffer");
}
if (imgtype == GIF) {
return vips_type_find("VipsOperation", "gifload");
return vips_type_find("VipsOperation", "gifload_buffer");
}
return 0;
}
@ -63,30 +66,40 @@ vips_type_find_save_go(int imgtype) {
if (imgtype == WEBP) {
return vips_type_find("VipsOperation", "webpsave_buffer");
}
if (imgtype == GIF) {
return vips_type_find("VipsOperation", "magicksave_buffer");
}
return 0;
}
int
vips_load_buffer(void *buf, size_t len, int imgtype, int shrink, VipsImage **out) {
switch (imgtype) {
case JPEG:
if (shrink > 1) {
return vips_jpegload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, "shrink", shrink, NULL);
}
return vips_jpegload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL);
case PNG:
return vips_pngload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL);
case WEBP:
if (shrink > 1) {
return vips_webpload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, "shrink", shrink, NULL);
}
return vips_webpload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL);
#if VIPS_SUPPORT_GIF
case GIF:
return vips_gifload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL);
#endif
vips_jpegload_go(void *buf, size_t len, int shrink, VipsImage **out) {
if (shrink > 1) {
return vips_jpegload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, "shrink", shrink, NULL);
}
return 1;
return vips_jpegload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL);
}
int
vips_pngload_go(void *buf, size_t len, VipsImage **out) {
return vips_pngload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL);
}
int
vips_webpload_go(void *buf, size_t len, int shrink, VipsImage **out) {
if (shrink > 1) {
return vips_webpload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, "shrink", shrink, NULL);
}
return vips_webpload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL);
}
int
vips_gifload_go(void *buf, size_t len, int pages, VipsImage **out) {
#if VIPS_SUPPORT_GIF
return vips_gifload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, "n", pages, NULL);
#else
return 1;
#endif
}
int
@ -238,6 +251,11 @@ vips_ifthenelse_go(VipsImage *cond, VipsImage *in1, VipsImage *in2, VipsImage **
return vips_ifthenelse(cond, in1, in2, out, "blend", TRUE, NULL);
}
int
vips_arrayjoin_go(VipsImage **in, VipsImage **out, int n) {
return vips_arrayjoin(in, out, n, "across", 1, NULL);
}
int
vips_jpegsave_go(VipsImage *in, void **buf, size_t *len, int strip, int quality, int interlace) {
return vips_jpegsave_buffer(in, buf, len, "strip", strip, "Q", quality, "optimize_coding", TRUE, "interlace", interlace, NULL);
@ -253,6 +271,15 @@ vips_webpsave_go(VipsImage *in, void **buf, size_t *len, int strip, int quality)
return vips_webpsave_buffer(in, buf, len, "strip", strip, "Q", quality, NULL);
}
int
vips_gifsave_go(VipsImage *in, void **buf, size_t *len) {
#if VIPS_SUPPORT_MAGICK
return vips_magicksave_buffer(in, buf, len, "format", "gif", NULL);
#else
return 1;
#endif
}
void
vips_cleanup() {
vips_thread_shutdown();