From 03880bd534685b6bcc431b3bed4f50efcf9bd62e Mon Sep 17 00:00:00 2001 From: esimov Date: Thu, 19 Apr 2018 14:26:10 +0300 Subject: [PATCH] Add vendor dependencies --- Gopkg.lock | 15 + Gopkg.toml | 34 ++ grayscale.go | 21 + output.png | Bin 10132 -> 0 bytes test.jpg | Bin 9732 -> 0 bytes vendor/github.com/nfnt/resize/.travis.yml | 7 + vendor/github.com/nfnt/resize/LICENSE | 13 + vendor/github.com/nfnt/resize/README.md | 151 +++++ vendor/github.com/nfnt/resize/converter.go | 438 +++++++++++++++ vendor/github.com/nfnt/resize/filters.go | 143 +++++ vendor/github.com/nfnt/resize/nearest.go | 318 +++++++++++ vendor/github.com/nfnt/resize/resize.go | 620 +++++++++++++++++++++ vendor/github.com/nfnt/resize/thumbnail.go | 55 ++ vendor/github.com/nfnt/resize/ycc.go | 387 +++++++++++++ 14 files changed, 2202 insertions(+) create mode 100644 Gopkg.lock create mode 100644 Gopkg.toml create mode 100644 grayscale.go delete mode 100644 output.png delete mode 100644 test.jpg create mode 100644 vendor/github.com/nfnt/resize/.travis.yml create mode 100644 vendor/github.com/nfnt/resize/LICENSE create mode 100644 vendor/github.com/nfnt/resize/README.md create mode 100644 vendor/github.com/nfnt/resize/converter.go create mode 100644 vendor/github.com/nfnt/resize/filters.go create mode 100644 vendor/github.com/nfnt/resize/nearest.go create mode 100644 vendor/github.com/nfnt/resize/resize.go create mode 100644 vendor/github.com/nfnt/resize/thumbnail.go create mode 100644 vendor/github.com/nfnt/resize/ycc.go diff --git a/Gopkg.lock b/Gopkg.lock new file mode 100644 index 0000000..46628a1 --- /dev/null +++ b/Gopkg.lock @@ -0,0 +1,15 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + branch = "master" + name = "github.com/nfnt/resize" + packages = ["."] + revision = "83c6a9932646f83e3267f353373d47347b6036b2" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "5726c315ea88a21cdd12e3a0963b997eea3baeda3a2d70472cab1a06db39dd70" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml new file mode 100644 index 0000000..7d0d6d5 --- /dev/null +++ b/Gopkg.toml @@ -0,0 +1,34 @@ +# Gopkg.toml example +# +# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" +# +# [prune] +# non-go = false +# go-tests = true +# unused-packages = true + + +[[constraint]] + branch = "master" + name = "github.com/nfnt/resize" + +[prune] + go-tests = true + unused-packages = true diff --git a/grayscale.go b/grayscale.go new file mode 100644 index 0000000..a20f69d --- /dev/null +++ b/grayscale.go @@ -0,0 +1,21 @@ +package main + +import ( + "image" + "image/color" +) + +// Grayscale converts the image to grayscale mode. +func Grayscale(src *image.NRGBA) *image.NRGBA { + dx, dy := src.Bounds().Max.X, src.Bounds().Max.Y + dst := image.NewNRGBA(src.Bounds()) + for x := 0; x < dx; x++ { + for y := 0; y < dy; y++ { + r, g, b, _ := src.At(x, y).RGBA() + lum := float32(r)*0.299 + float32(g)*0.587 + float32(b)*0.114 + pixel := color.Gray{Y: uint8(lum / 256)} + dst.Set(x, y, pixel) + } + } + return dst +} diff --git a/output.png b/output.png deleted file mode 100644 index 0adae0aba323ed76650be926fda81451d1b2bff5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10132 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 K#%A~5F8>cxg&}wV diff --git a/vendor/github.com/nfnt/resize/.travis.yml b/vendor/github.com/nfnt/resize/.travis.yml new file mode 100644 index 0000000..5ff08e7 --- /dev/null +++ b/vendor/github.com/nfnt/resize/.travis.yml @@ -0,0 +1,7 @@ +language: go + +go: + - "1.x" + - "1.1" + - "1.4" + - "1.10" diff --git a/vendor/github.com/nfnt/resize/LICENSE b/vendor/github.com/nfnt/resize/LICENSE new file mode 100644 index 0000000..7836cad --- /dev/null +++ b/vendor/github.com/nfnt/resize/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2012, Jan Schlicht + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. diff --git a/vendor/github.com/nfnt/resize/README.md b/vendor/github.com/nfnt/resize/README.md new file mode 100644 index 0000000..372777d --- /dev/null +++ b/vendor/github.com/nfnt/resize/README.md @@ -0,0 +1,151 @@ +# This package is no longer being updated! Please look for alternatives if that bothers you. + +Resize +====== + +Image resizing for the [Go programming language](http://golang.org) with common interpolation methods. + +[![Build Status](https://travis-ci.org/nfnt/resize.svg)](https://travis-ci.org/nfnt/resize) + +Installation +------------ + +```bash +$ go get github.com/nfnt/resize +``` + +It's that easy! + +Usage +----- + +This package needs at least Go 1.1. Import package with + +```go +import "github.com/nfnt/resize" +``` + +The resize package provides 2 functions: + +* `resize.Resize` creates a scaled image with new dimensions (`width`, `height`) using the interpolation function `interp`. + If either `width` or `height` is set to 0, it will be set to an aspect ratio preserving value. +* `resize.Thumbnail` downscales an image preserving its aspect ratio to the maximum dimensions (`maxWidth`, `maxHeight`). + It will return the original image if original sizes are smaller than the provided dimensions. + +```go +resize.Resize(width, height uint, img image.Image, interp resize.InterpolationFunction) image.Image +resize.Thumbnail(maxWidth, maxHeight uint, img image.Image, interp resize.InterpolationFunction) image.Image +``` + +The provided interpolation functions are (from fast to slow execution time) + +- `NearestNeighbor`: [Nearest-neighbor interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation) +- `Bilinear`: [Bilinear interpolation](http://en.wikipedia.org/wiki/Bilinear_interpolation) +- `Bicubic`: [Bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation) +- `MitchellNetravali`: [Mitchell-Netravali interpolation](http://dl.acm.org/citation.cfm?id=378514) +- `Lanczos2`: [Lanczos resampling](http://en.wikipedia.org/wiki/Lanczos_resampling) with a=2 +- `Lanczos3`: [Lanczos resampling](http://en.wikipedia.org/wiki/Lanczos_resampling) with a=3 + +Which of these methods gives the best results depends on your use case. + +Sample usage: + +```go +package main + +import ( + "github.com/nfnt/resize" + "image/jpeg" + "log" + "os" +) + +func main() { + // open "test.jpg" + file, err := os.Open("test.jpg") + if err != nil { + log.Fatal(err) + } + + // decode jpeg into image.Image + img, err := jpeg.Decode(file) + if err != nil { + log.Fatal(err) + } + file.Close() + + // resize to width 1000 using Lanczos resampling + // and preserve aspect ratio + m := resize.Resize(1000, 0, img, resize.Lanczos3) + + out, err := os.Create("test_resized.jpg") + if err != nil { + log.Fatal(err) + } + defer out.Close() + + // write new image to file + jpeg.Encode(out, m, nil) +} +``` + +Caveats +------- + +* Optimized access routines are used for `image.RGBA`, `image.NRGBA`, `image.RGBA64`, `image.NRGBA64`, `image.YCbCr`, `image.Gray`, and `image.Gray16` types. All other image types are accessed in a generic way that will result in slow processing speed. +* JPEG images are stored in `image.YCbCr`. This image format stores data in a way that will decrease processing speed. A resize may be up to 2 times slower than with `image.RGBA`. + + +Downsizing Samples +------- + +Downsizing is not as simple as it might look like. Images have to be filtered before they are scaled down, otherwise aliasing might occur. +Filtering is highly subjective: Applying too much will blur the whole image, too little will make aliasing become apparent. +Resize tries to provide sane defaults that should suffice in most cases. + +### Artificial sample + +Original image +![Rings](http://nfnt.github.com/img/rings_lg_orig.png) + + + + + + + + + + + + + + +

Nearest-Neighbor

Bilinear

Bicubic

Mitchell-Netravali

Lanczos2

Lanczos3
+ +### Real-Life sample + +Original image +![Original](http://nfnt.github.com/img/IMG_3694_720.jpg) + + + + + + + + + + + + + + +

Nearest-Neighbor

Bilinear

Bicubic

Mitchell-Netravali

Lanczos2

Lanczos3
+ + +License +------- + +Copyright (c) 2012 Jan Schlicht +Resize is released under a MIT style license. diff --git a/vendor/github.com/nfnt/resize/converter.go b/vendor/github.com/nfnt/resize/converter.go new file mode 100644 index 0000000..f9c520d --- /dev/null +++ b/vendor/github.com/nfnt/resize/converter.go @@ -0,0 +1,438 @@ +/* +Copyright (c) 2012, Jan Schlicht + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +*/ + +package resize + +import "image" + +// Keep value in [0,255] range. +func clampUint8(in int32) uint8 { + // casting a negative int to an uint will result in an overflown + // large uint. this behavior will be exploited here and in other functions + // to achieve a higher performance. + if uint32(in) < 256 { + return uint8(in) + } + if in > 255 { + return 255 + } + return 0 +} + +// Keep value in [0,65535] range. +func clampUint16(in int64) uint16 { + if uint64(in) < 65536 { + return uint16(in) + } + if in > 65535 { + return 65535 + } + return 0 +} + +func resizeGeneric(in image.Image, out *image.RGBA64, scale float64, coeffs []int32, offset []int, filterLength int) { + newBounds := out.Bounds() + maxX := in.Bounds().Dx() - 1 + + for x := newBounds.Min.X; x < newBounds.Max.X; x++ { + for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { + var rgba [4]int64 + var sum int64 + start := offset[y] + ci := y * filterLength + for i := 0; i < filterLength; i++ { + coeff := coeffs[ci+i] + if coeff != 0 { + xi := start + i + switch { + case xi < 0: + xi = 0 + case xi >= maxX: + xi = maxX + } + + r, g, b, a := in.At(xi+in.Bounds().Min.X, x+in.Bounds().Min.Y).RGBA() + + rgba[0] += int64(coeff) * int64(r) + rgba[1] += int64(coeff) * int64(g) + rgba[2] += int64(coeff) * int64(b) + rgba[3] += int64(coeff) * int64(a) + sum += int64(coeff) + } + } + + offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8 + + value := clampUint16(rgba[0] / sum) + out.Pix[offset+0] = uint8(value >> 8) + out.Pix[offset+1] = uint8(value) + value = clampUint16(rgba[1] / sum) + out.Pix[offset+2] = uint8(value >> 8) + out.Pix[offset+3] = uint8(value) + value = clampUint16(rgba[2] / sum) + out.Pix[offset+4] = uint8(value >> 8) + out.Pix[offset+5] = uint8(value) + value = clampUint16(rgba[3] / sum) + out.Pix[offset+6] = uint8(value >> 8) + out.Pix[offset+7] = uint8(value) + } + } +} + +func resizeRGBA(in *image.RGBA, out *image.RGBA, scale float64, coeffs []int16, offset []int, filterLength int) { + newBounds := out.Bounds() + maxX := in.Bounds().Dx() - 1 + + for x := newBounds.Min.X; x < newBounds.Max.X; x++ { + row := in.Pix[x*in.Stride:] + for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { + var rgba [4]int32 + var sum int32 + start := offset[y] + ci := y * filterLength + for i := 0; i < filterLength; i++ { + coeff := coeffs[ci+i] + if coeff != 0 { + xi := start + i + switch { + case uint(xi) < uint(maxX): + xi *= 4 + case xi >= maxX: + xi = 4 * maxX + default: + xi = 0 + } + + rgba[0] += int32(coeff) * int32(row[xi+0]) + rgba[1] += int32(coeff) * int32(row[xi+1]) + rgba[2] += int32(coeff) * int32(row[xi+2]) + rgba[3] += int32(coeff) * int32(row[xi+3]) + sum += int32(coeff) + } + } + + xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*4 + + out.Pix[xo+0] = clampUint8(rgba[0] / sum) + out.Pix[xo+1] = clampUint8(rgba[1] / sum) + out.Pix[xo+2] = clampUint8(rgba[2] / sum) + out.Pix[xo+3] = clampUint8(rgba[3] / sum) + } + } +} + +func resizeNRGBA(in *image.NRGBA, out *image.RGBA, scale float64, coeffs []int16, offset []int, filterLength int) { + newBounds := out.Bounds() + maxX := in.Bounds().Dx() - 1 + + for x := newBounds.Min.X; x < newBounds.Max.X; x++ { + row := in.Pix[x*in.Stride:] + for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { + var rgba [4]int32 + var sum int32 + start := offset[y] + ci := y * filterLength + for i := 0; i < filterLength; i++ { + coeff := coeffs[ci+i] + if coeff != 0 { + xi := start + i + switch { + case uint(xi) < uint(maxX): + xi *= 4 + case xi >= maxX: + xi = 4 * maxX + default: + xi = 0 + } + + // Forward alpha-premultiplication + a := int32(row[xi+3]) + r := int32(row[xi+0]) * a + r /= 0xff + g := int32(row[xi+1]) * a + g /= 0xff + b := int32(row[xi+2]) * a + b /= 0xff + + rgba[0] += int32(coeff) * r + rgba[1] += int32(coeff) * g + rgba[2] += int32(coeff) * b + rgba[3] += int32(coeff) * a + sum += int32(coeff) + } + } + + xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*4 + + out.Pix[xo+0] = clampUint8(rgba[0] / sum) + out.Pix[xo+1] = clampUint8(rgba[1] / sum) + out.Pix[xo+2] = clampUint8(rgba[2] / sum) + out.Pix[xo+3] = clampUint8(rgba[3] / sum) + } + } +} + +func resizeRGBA64(in *image.RGBA64, out *image.RGBA64, scale float64, coeffs []int32, offset []int, filterLength int) { + newBounds := out.Bounds() + maxX := in.Bounds().Dx() - 1 + + for x := newBounds.Min.X; x < newBounds.Max.X; x++ { + row := in.Pix[x*in.Stride:] + for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { + var rgba [4]int64 + var sum int64 + start := offset[y] + ci := y * filterLength + for i := 0; i < filterLength; i++ { + coeff := coeffs[ci+i] + if coeff != 0 { + xi := start + i + switch { + case uint(xi) < uint(maxX): + xi *= 8 + case xi >= maxX: + xi = 8 * maxX + default: + xi = 0 + } + + rgba[0] += int64(coeff) * (int64(row[xi+0])<<8 | int64(row[xi+1])) + rgba[1] += int64(coeff) * (int64(row[xi+2])<<8 | int64(row[xi+3])) + rgba[2] += int64(coeff) * (int64(row[xi+4])<<8 | int64(row[xi+5])) + rgba[3] += int64(coeff) * (int64(row[xi+6])<<8 | int64(row[xi+7])) + sum += int64(coeff) + } + } + + xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8 + + value := clampUint16(rgba[0] / sum) + out.Pix[xo+0] = uint8(value >> 8) + out.Pix[xo+1] = uint8(value) + value = clampUint16(rgba[1] / sum) + out.Pix[xo+2] = uint8(value >> 8) + out.Pix[xo+3] = uint8(value) + value = clampUint16(rgba[2] / sum) + out.Pix[xo+4] = uint8(value >> 8) + out.Pix[xo+5] = uint8(value) + value = clampUint16(rgba[3] / sum) + out.Pix[xo+6] = uint8(value >> 8) + out.Pix[xo+7] = uint8(value) + } + } +} + +func resizeNRGBA64(in *image.NRGBA64, out *image.RGBA64, scale float64, coeffs []int32, offset []int, filterLength int) { + newBounds := out.Bounds() + maxX := in.Bounds().Dx() - 1 + + for x := newBounds.Min.X; x < newBounds.Max.X; x++ { + row := in.Pix[x*in.Stride:] + for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { + var rgba [4]int64 + var sum int64 + start := offset[y] + ci := y * filterLength + for i := 0; i < filterLength; i++ { + coeff := coeffs[ci+i] + if coeff != 0 { + xi := start + i + switch { + case uint(xi) < uint(maxX): + xi *= 8 + case xi >= maxX: + xi = 8 * maxX + default: + xi = 0 + } + + // Forward alpha-premultiplication + a := int64(uint16(row[xi+6])<<8 | uint16(row[xi+7])) + r := int64(uint16(row[xi+0])<<8|uint16(row[xi+1])) * a + r /= 0xffff + g := int64(uint16(row[xi+2])<<8|uint16(row[xi+3])) * a + g /= 0xffff + b := int64(uint16(row[xi+4])<<8|uint16(row[xi+5])) * a + b /= 0xffff + + rgba[0] += int64(coeff) * r + rgba[1] += int64(coeff) * g + rgba[2] += int64(coeff) * b + rgba[3] += int64(coeff) * a + sum += int64(coeff) + } + } + + xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8 + + value := clampUint16(rgba[0] / sum) + out.Pix[xo+0] = uint8(value >> 8) + out.Pix[xo+1] = uint8(value) + value = clampUint16(rgba[1] / sum) + out.Pix[xo+2] = uint8(value >> 8) + out.Pix[xo+3] = uint8(value) + value = clampUint16(rgba[2] / sum) + out.Pix[xo+4] = uint8(value >> 8) + out.Pix[xo+5] = uint8(value) + value = clampUint16(rgba[3] / sum) + out.Pix[xo+6] = uint8(value >> 8) + out.Pix[xo+7] = uint8(value) + } + } +} + +func resizeGray(in *image.Gray, out *image.Gray, scale float64, coeffs []int16, offset []int, filterLength int) { + newBounds := out.Bounds() + maxX := in.Bounds().Dx() - 1 + + for x := newBounds.Min.X; x < newBounds.Max.X; x++ { + row := in.Pix[(x-newBounds.Min.X)*in.Stride:] + for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { + var gray int32 + var sum int32 + start := offset[y] + ci := y * filterLength + for i := 0; i < filterLength; i++ { + coeff := coeffs[ci+i] + if coeff != 0 { + xi := start + i + switch { + case xi < 0: + xi = 0 + case xi >= maxX: + xi = maxX + } + gray += int32(coeff) * int32(row[xi]) + sum += int32(coeff) + } + } + + offset := (y-newBounds.Min.Y)*out.Stride + (x - newBounds.Min.X) + out.Pix[offset] = clampUint8(gray / sum) + } + } +} + +func resizeGray16(in *image.Gray16, out *image.Gray16, scale float64, coeffs []int32, offset []int, filterLength int) { + newBounds := out.Bounds() + maxX := in.Bounds().Dx() - 1 + + for x := newBounds.Min.X; x < newBounds.Max.X; x++ { + row := in.Pix[x*in.Stride:] + for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { + var gray int64 + var sum int64 + start := offset[y] + ci := y * filterLength + for i := 0; i < filterLength; i++ { + coeff := coeffs[ci+i] + if coeff != 0 { + xi := start + i + switch { + case uint(xi) < uint(maxX): + xi *= 2 + case xi >= maxX: + xi = 2 * maxX + default: + xi = 0 + } + gray += int64(coeff) * int64(uint16(row[xi+0])<<8|uint16(row[xi+1])) + sum += int64(coeff) + } + } + + offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*2 + value := clampUint16(gray / sum) + out.Pix[offset+0] = uint8(value >> 8) + out.Pix[offset+1] = uint8(value) + } + } +} + +func resizeYCbCr(in *ycc, out *ycc, scale float64, coeffs []int16, offset []int, filterLength int) { + newBounds := out.Bounds() + maxX := in.Bounds().Dx() - 1 + + for x := newBounds.Min.X; x < newBounds.Max.X; x++ { + row := in.Pix[x*in.Stride:] + for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { + var p [3]int32 + var sum int32 + start := offset[y] + ci := y * filterLength + for i := 0; i < filterLength; i++ { + coeff := coeffs[ci+i] + if coeff != 0 { + xi := start + i + switch { + case uint(xi) < uint(maxX): + xi *= 3 + case xi >= maxX: + xi = 3 * maxX + default: + xi = 0 + } + p[0] += int32(coeff) * int32(row[xi+0]) + p[1] += int32(coeff) * int32(row[xi+1]) + p[2] += int32(coeff) * int32(row[xi+2]) + sum += int32(coeff) + } + } + + xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*3 + out.Pix[xo+0] = clampUint8(p[0] / sum) + out.Pix[xo+1] = clampUint8(p[1] / sum) + out.Pix[xo+2] = clampUint8(p[2] / sum) + } + } +} + +func nearestYCbCr(in *ycc, out *ycc, scale float64, coeffs []bool, offset []int, filterLength int) { + newBounds := out.Bounds() + maxX := in.Bounds().Dx() - 1 + + for x := newBounds.Min.X; x < newBounds.Max.X; x++ { + row := in.Pix[x*in.Stride:] + for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { + var p [3]float32 + var sum float32 + start := offset[y] + ci := y * filterLength + for i := 0; i < filterLength; i++ { + if coeffs[ci+i] { + xi := start + i + switch { + case uint(xi) < uint(maxX): + xi *= 3 + case xi >= maxX: + xi = 3 * maxX + default: + xi = 0 + } + p[0] += float32(row[xi+0]) + p[1] += float32(row[xi+1]) + p[2] += float32(row[xi+2]) + sum++ + } + } + + xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*3 + out.Pix[xo+0] = floatToUint8(p[0] / sum) + out.Pix[xo+1] = floatToUint8(p[1] / sum) + out.Pix[xo+2] = floatToUint8(p[2] / sum) + } + } +} diff --git a/vendor/github.com/nfnt/resize/filters.go b/vendor/github.com/nfnt/resize/filters.go new file mode 100644 index 0000000..4ce04e3 --- /dev/null +++ b/vendor/github.com/nfnt/resize/filters.go @@ -0,0 +1,143 @@ +/* +Copyright (c) 2012, Jan Schlicht + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +*/ + +package resize + +import ( + "math" +) + +func nearest(in float64) float64 { + if in >= -0.5 && in < 0.5 { + return 1 + } + return 0 +} + +func linear(in float64) float64 { + in = math.Abs(in) + if in <= 1 { + return 1 - in + } + return 0 +} + +func cubic(in float64) float64 { + in = math.Abs(in) + if in <= 1 { + return in*in*(1.5*in-2.5) + 1.0 + } + if in <= 2 { + return in*(in*(2.5-0.5*in)-4.0) + 2.0 + } + return 0 +} + +func mitchellnetravali(in float64) float64 { + in = math.Abs(in) + if in <= 1 { + return (7.0*in*in*in - 12.0*in*in + 5.33333333333) * 0.16666666666 + } + if in <= 2 { + return (-2.33333333333*in*in*in + 12.0*in*in - 20.0*in + 10.6666666667) * 0.16666666666 + } + return 0 +} + +func sinc(x float64) float64 { + x = math.Abs(x) * math.Pi + if x >= 1.220703e-4 { + return math.Sin(x) / x + } + return 1 +} + +func lanczos2(in float64) float64 { + if in > -2 && in < 2 { + return sinc(in) * sinc(in*0.5) + } + return 0 +} + +func lanczos3(in float64) float64 { + if in > -3 && in < 3 { + return sinc(in) * sinc(in*0.3333333333333333) + } + return 0 +} + +// range [-256,256] +func createWeights8(dy, filterLength int, blur, scale float64, kernel func(float64) float64) ([]int16, []int, int) { + filterLength = filterLength * int(math.Max(math.Ceil(blur*scale), 1)) + filterFactor := math.Min(1./(blur*scale), 1) + + coeffs := make([]int16, dy*filterLength) + start := make([]int, dy) + for y := 0; y < dy; y++ { + interpX := scale*(float64(y)+0.5) - 0.5 + start[y] = int(interpX) - filterLength/2 + 1 + interpX -= float64(start[y]) + for i := 0; i < filterLength; i++ { + in := (interpX - float64(i)) * filterFactor + coeffs[y*filterLength+i] = int16(kernel(in) * 256) + } + } + + return coeffs, start, filterLength +} + +// range [-65536,65536] +func createWeights16(dy, filterLength int, blur, scale float64, kernel func(float64) float64) ([]int32, []int, int) { + filterLength = filterLength * int(math.Max(math.Ceil(blur*scale), 1)) + filterFactor := math.Min(1./(blur*scale), 1) + + coeffs := make([]int32, dy*filterLength) + start := make([]int, dy) + for y := 0; y < dy; y++ { + interpX := scale*(float64(y)+0.5) - 0.5 + start[y] = int(interpX) - filterLength/2 + 1 + interpX -= float64(start[y]) + for i := 0; i < filterLength; i++ { + in := (interpX - float64(i)) * filterFactor + coeffs[y*filterLength+i] = int32(kernel(in) * 65536) + } + } + + return coeffs, start, filterLength +} + +func createWeightsNearest(dy, filterLength int, blur, scale float64) ([]bool, []int, int) { + filterLength = filterLength * int(math.Max(math.Ceil(blur*scale), 1)) + filterFactor := math.Min(1./(blur*scale), 1) + + coeffs := make([]bool, dy*filterLength) + start := make([]int, dy) + for y := 0; y < dy; y++ { + interpX := scale*(float64(y)+0.5) - 0.5 + start[y] = int(interpX) - filterLength/2 + 1 + interpX -= float64(start[y]) + for i := 0; i < filterLength; i++ { + in := (interpX - float64(i)) * filterFactor + if in >= -0.5 && in < 0.5 { + coeffs[y*filterLength+i] = true + } else { + coeffs[y*filterLength+i] = false + } + } + } + + return coeffs, start, filterLength +} diff --git a/vendor/github.com/nfnt/resize/nearest.go b/vendor/github.com/nfnt/resize/nearest.go new file mode 100644 index 0000000..888039d --- /dev/null +++ b/vendor/github.com/nfnt/resize/nearest.go @@ -0,0 +1,318 @@ +/* +Copyright (c) 2014, Charlie Vieth + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +*/ + +package resize + +import "image" + +func floatToUint8(x float32) uint8 { + // Nearest-neighbor values are always + // positive no need to check lower-bound. + if x > 0xfe { + return 0xff + } + return uint8(x) +} + +func floatToUint16(x float32) uint16 { + if x > 0xfffe { + return 0xffff + } + return uint16(x) +} + +func nearestGeneric(in image.Image, out *image.RGBA64, scale float64, coeffs []bool, offset []int, filterLength int) { + newBounds := out.Bounds() + maxX := in.Bounds().Dx() - 1 + + for x := newBounds.Min.X; x < newBounds.Max.X; x++ { + for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { + var rgba [4]float32 + var sum float32 + start := offset[y] + ci := y * filterLength + for i := 0; i < filterLength; i++ { + if coeffs[ci+i] { + xi := start + i + switch { + case xi < 0: + xi = 0 + case xi >= maxX: + xi = maxX + } + r, g, b, a := in.At(xi+in.Bounds().Min.X, x+in.Bounds().Min.Y).RGBA() + rgba[0] += float32(r) + rgba[1] += float32(g) + rgba[2] += float32(b) + rgba[3] += float32(a) + sum++ + } + } + + offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8 + value := floatToUint16(rgba[0] / sum) + out.Pix[offset+0] = uint8(value >> 8) + out.Pix[offset+1] = uint8(value) + value = floatToUint16(rgba[1] / sum) + out.Pix[offset+2] = uint8(value >> 8) + out.Pix[offset+3] = uint8(value) + value = floatToUint16(rgba[2] / sum) + out.Pix[offset+4] = uint8(value >> 8) + out.Pix[offset+5] = uint8(value) + value = floatToUint16(rgba[3] / sum) + out.Pix[offset+6] = uint8(value >> 8) + out.Pix[offset+7] = uint8(value) + } + } +} + +func nearestRGBA(in *image.RGBA, out *image.RGBA, scale float64, coeffs []bool, offset []int, filterLength int) { + newBounds := out.Bounds() + maxX := in.Bounds().Dx() - 1 + + for x := newBounds.Min.X; x < newBounds.Max.X; x++ { + row := in.Pix[x*in.Stride:] + for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { + var rgba [4]float32 + var sum float32 + start := offset[y] + ci := y * filterLength + for i := 0; i < filterLength; i++ { + if coeffs[ci+i] { + xi := start + i + switch { + case uint(xi) < uint(maxX): + xi *= 4 + case xi >= maxX: + xi = 4 * maxX + default: + xi = 0 + } + rgba[0] += float32(row[xi+0]) + rgba[1] += float32(row[xi+1]) + rgba[2] += float32(row[xi+2]) + rgba[3] += float32(row[xi+3]) + sum++ + } + } + + xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*4 + out.Pix[xo+0] = floatToUint8(rgba[0] / sum) + out.Pix[xo+1] = floatToUint8(rgba[1] / sum) + out.Pix[xo+2] = floatToUint8(rgba[2] / sum) + out.Pix[xo+3] = floatToUint8(rgba[3] / sum) + } + } +} + +func nearestNRGBA(in *image.NRGBA, out *image.NRGBA, scale float64, coeffs []bool, offset []int, filterLength int) { + newBounds := out.Bounds() + maxX := in.Bounds().Dx() - 1 + + for x := newBounds.Min.X; x < newBounds.Max.X; x++ { + row := in.Pix[x*in.Stride:] + for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { + var rgba [4]float32 + var sum float32 + start := offset[y] + ci := y * filterLength + for i := 0; i < filterLength; i++ { + if coeffs[ci+i] { + xi := start + i + switch { + case uint(xi) < uint(maxX): + xi *= 4 + case xi >= maxX: + xi = 4 * maxX + default: + xi = 0 + } + rgba[0] += float32(row[xi+0]) + rgba[1] += float32(row[xi+1]) + rgba[2] += float32(row[xi+2]) + rgba[3] += float32(row[xi+3]) + sum++ + } + } + + xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*4 + out.Pix[xo+0] = floatToUint8(rgba[0] / sum) + out.Pix[xo+1] = floatToUint8(rgba[1] / sum) + out.Pix[xo+2] = floatToUint8(rgba[2] / sum) + out.Pix[xo+3] = floatToUint8(rgba[3] / sum) + } + } +} + +func nearestRGBA64(in *image.RGBA64, out *image.RGBA64, scale float64, coeffs []bool, offset []int, filterLength int) { + newBounds := out.Bounds() + maxX := in.Bounds().Dx() - 1 + + for x := newBounds.Min.X; x < newBounds.Max.X; x++ { + row := in.Pix[x*in.Stride:] + for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { + var rgba [4]float32 + var sum float32 + start := offset[y] + ci := y * filterLength + for i := 0; i < filterLength; i++ { + if coeffs[ci+i] { + xi := start + i + switch { + case uint(xi) < uint(maxX): + xi *= 8 + case xi >= maxX: + xi = 8 * maxX + default: + xi = 0 + } + rgba[0] += float32(uint16(row[xi+0])<<8 | uint16(row[xi+1])) + rgba[1] += float32(uint16(row[xi+2])<<8 | uint16(row[xi+3])) + rgba[2] += float32(uint16(row[xi+4])<<8 | uint16(row[xi+5])) + rgba[3] += float32(uint16(row[xi+6])<<8 | uint16(row[xi+7])) + sum++ + } + } + + xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8 + value := floatToUint16(rgba[0] / sum) + out.Pix[xo+0] = uint8(value >> 8) + out.Pix[xo+1] = uint8(value) + value = floatToUint16(rgba[1] / sum) + out.Pix[xo+2] = uint8(value >> 8) + out.Pix[xo+3] = uint8(value) + value = floatToUint16(rgba[2] / sum) + out.Pix[xo+4] = uint8(value >> 8) + out.Pix[xo+5] = uint8(value) + value = floatToUint16(rgba[3] / sum) + out.Pix[xo+6] = uint8(value >> 8) + out.Pix[xo+7] = uint8(value) + } + } +} + +func nearestNRGBA64(in *image.NRGBA64, out *image.NRGBA64, scale float64, coeffs []bool, offset []int, filterLength int) { + newBounds := out.Bounds() + maxX := in.Bounds().Dx() - 1 + + for x := newBounds.Min.X; x < newBounds.Max.X; x++ { + row := in.Pix[x*in.Stride:] + for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { + var rgba [4]float32 + var sum float32 + start := offset[y] + ci := y * filterLength + for i := 0; i < filterLength; i++ { + if coeffs[ci+i] { + xi := start + i + switch { + case uint(xi) < uint(maxX): + xi *= 8 + case xi >= maxX: + xi = 8 * maxX + default: + xi = 0 + } + rgba[0] += float32(uint16(row[xi+0])<<8 | uint16(row[xi+1])) + rgba[1] += float32(uint16(row[xi+2])<<8 | uint16(row[xi+3])) + rgba[2] += float32(uint16(row[xi+4])<<8 | uint16(row[xi+5])) + rgba[3] += float32(uint16(row[xi+6])<<8 | uint16(row[xi+7])) + sum++ + } + } + + xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8 + value := floatToUint16(rgba[0] / sum) + out.Pix[xo+0] = uint8(value >> 8) + out.Pix[xo+1] = uint8(value) + value = floatToUint16(rgba[1] / sum) + out.Pix[xo+2] = uint8(value >> 8) + out.Pix[xo+3] = uint8(value) + value = floatToUint16(rgba[2] / sum) + out.Pix[xo+4] = uint8(value >> 8) + out.Pix[xo+5] = uint8(value) + value = floatToUint16(rgba[3] / sum) + out.Pix[xo+6] = uint8(value >> 8) + out.Pix[xo+7] = uint8(value) + } + } +} + +func nearestGray(in *image.Gray, out *image.Gray, scale float64, coeffs []bool, offset []int, filterLength int) { + newBounds := out.Bounds() + maxX := in.Bounds().Dx() - 1 + + for x := newBounds.Min.X; x < newBounds.Max.X; x++ { + row := in.Pix[x*in.Stride:] + for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { + var gray float32 + var sum float32 + start := offset[y] + ci := y * filterLength + for i := 0; i < filterLength; i++ { + if coeffs[ci+i] { + xi := start + i + switch { + case xi < 0: + xi = 0 + case xi >= maxX: + xi = maxX + } + gray += float32(row[xi]) + sum++ + } + } + + offset := (y-newBounds.Min.Y)*out.Stride + (x - newBounds.Min.X) + out.Pix[offset] = floatToUint8(gray / sum) + } + } +} + +func nearestGray16(in *image.Gray16, out *image.Gray16, scale float64, coeffs []bool, offset []int, filterLength int) { + newBounds := out.Bounds() + maxX := in.Bounds().Dx() - 1 + + for x := newBounds.Min.X; x < newBounds.Max.X; x++ { + row := in.Pix[x*in.Stride:] + for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { + var gray float32 + var sum float32 + start := offset[y] + ci := y * filterLength + for i := 0; i < filterLength; i++ { + if coeffs[ci+i] { + xi := start + i + switch { + case uint(xi) < uint(maxX): + xi *= 2 + case xi >= maxX: + xi = 2 * maxX + default: + xi = 0 + } + gray += float32(uint16(row[xi+0])<<8 | uint16(row[xi+1])) + sum++ + } + } + + offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*2 + value := floatToUint16(gray / sum) + out.Pix[offset+0] = uint8(value >> 8) + out.Pix[offset+1] = uint8(value) + } + } +} diff --git a/vendor/github.com/nfnt/resize/resize.go b/vendor/github.com/nfnt/resize/resize.go new file mode 100644 index 0000000..0d7fbf6 --- /dev/null +++ b/vendor/github.com/nfnt/resize/resize.go @@ -0,0 +1,620 @@ +/* +Copyright (c) 2012, Jan Schlicht + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +*/ + +// Package resize implements various image resizing methods. +// +// The package works with the Image interface described in the image package. +// Various interpolation methods are provided and multiple processors may be +// utilized in the computations. +// +// Example: +// imgResized := resize.Resize(1000, 0, imgOld, resize.MitchellNetravali) +package resize + +import ( + "image" + "runtime" + "sync" +) + +// An InterpolationFunction provides the parameters that describe an +// interpolation kernel. It returns the number of samples to take +// and the kernel function to use for sampling. +type InterpolationFunction int + +// InterpolationFunction constants +const ( + // Nearest-neighbor interpolation + NearestNeighbor InterpolationFunction = iota + // Bilinear interpolation + Bilinear + // Bicubic interpolation (with cubic hermite spline) + Bicubic + // Mitchell-Netravali interpolation + MitchellNetravali + // Lanczos interpolation (a=2) + Lanczos2 + // Lanczos interpolation (a=3) + Lanczos3 +) + +// kernal, returns an InterpolationFunctions taps and kernel. +func (i InterpolationFunction) kernel() (int, func(float64) float64) { + switch i { + case Bilinear: + return 2, linear + case Bicubic: + return 4, cubic + case MitchellNetravali: + return 4, mitchellnetravali + case Lanczos2: + return 4, lanczos2 + case Lanczos3: + return 6, lanczos3 + default: + // Default to NearestNeighbor. + return 2, nearest + } +} + +// values <1 will sharpen the image +var blur = 1.0 + +// Resize scales an image to new width and height using the interpolation function interp. +// A new image with the given dimensions will be returned. +// If one of the parameters width or height is set to 0, its size will be calculated so that +// the aspect ratio is that of the originating image. +// The resizing algorithm uses channels for parallel computation. +// If the input image has width or height of 0, it is returned unchanged. +func Resize(width, height uint, img image.Image, interp InterpolationFunction) image.Image { + scaleX, scaleY := calcFactors(width, height, float64(img.Bounds().Dx()), float64(img.Bounds().Dy())) + if width == 0 { + width = uint(0.7 + float64(img.Bounds().Dx())/scaleX) + } + if height == 0 { + height = uint(0.7 + float64(img.Bounds().Dy())/scaleY) + } + + // Trivial case: return input image + if int(width) == img.Bounds().Dx() && int(height) == img.Bounds().Dy() { + return img + } + + // Input image has no pixels + if img.Bounds().Dx() <= 0 || img.Bounds().Dy() <= 0 { + return img + } + + if interp == NearestNeighbor { + return resizeNearest(width, height, scaleX, scaleY, img, interp) + } + + taps, kernel := interp.kernel() + cpus := runtime.GOMAXPROCS(0) + wg := sync.WaitGroup{} + + // Generic access to image.Image is slow in tight loops. + // The optimal access has to be determined from the concrete image type. + switch input := img.(type) { + case *image.RGBA: + // 8-bit precision + temp := image.NewRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width))) + result := image.NewRGBA(image.Rect(0, 0, int(width), int(height))) + + // horizontal filter, results in transposed temporary image + coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(temp, i, cpus).(*image.RGBA) + go func() { + defer wg.Done() + resizeRGBA(input, slice, scaleX, coeffs, offset, filterLength) + }() + } + wg.Wait() + + // horizontal filter on transposed image, result is not transposed + coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(result, i, cpus).(*image.RGBA) + go func() { + defer wg.Done() + resizeRGBA(temp, slice, scaleY, coeffs, offset, filterLength) + }() + } + wg.Wait() + return result + case *image.NRGBA: + // 8-bit precision + temp := image.NewRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width))) + result := image.NewRGBA(image.Rect(0, 0, int(width), int(height))) + + // horizontal filter, results in transposed temporary image + coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(temp, i, cpus).(*image.RGBA) + go func() { + defer wg.Done() + resizeNRGBA(input, slice, scaleX, coeffs, offset, filterLength) + }() + } + wg.Wait() + + // horizontal filter on transposed image, result is not transposed + coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(result, i, cpus).(*image.RGBA) + go func() { + defer wg.Done() + resizeRGBA(temp, slice, scaleY, coeffs, offset, filterLength) + }() + } + wg.Wait() + return result + + case *image.YCbCr: + // 8-bit precision + // accessing the YCbCr arrays in a tight loop is slow. + // converting the image to ycc increases performance by 2x. + temp := newYCC(image.Rect(0, 0, input.Bounds().Dy(), int(width)), input.SubsampleRatio) + result := newYCC(image.Rect(0, 0, int(width), int(height)), image.YCbCrSubsampleRatio444) + + coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel) + in := imageYCbCrToYCC(input) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(temp, i, cpus).(*ycc) + go func() { + defer wg.Done() + resizeYCbCr(in, slice, scaleX, coeffs, offset, filterLength) + }() + } + wg.Wait() + + coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(result, i, cpus).(*ycc) + go func() { + defer wg.Done() + resizeYCbCr(temp, slice, scaleY, coeffs, offset, filterLength) + }() + } + wg.Wait() + return result.YCbCr() + case *image.RGBA64: + // 16-bit precision + temp := image.NewRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width))) + result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height))) + + // horizontal filter, results in transposed temporary image + coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(temp, i, cpus).(*image.RGBA64) + go func() { + defer wg.Done() + resizeRGBA64(input, slice, scaleX, coeffs, offset, filterLength) + }() + } + wg.Wait() + + // horizontal filter on transposed image, result is not transposed + coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(result, i, cpus).(*image.RGBA64) + go func() { + defer wg.Done() + resizeRGBA64(temp, slice, scaleY, coeffs, offset, filterLength) + }() + } + wg.Wait() + return result + case *image.NRGBA64: + // 16-bit precision + temp := image.NewRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width))) + result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height))) + + // horizontal filter, results in transposed temporary image + coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(temp, i, cpus).(*image.RGBA64) + go func() { + defer wg.Done() + resizeNRGBA64(input, slice, scaleX, coeffs, offset, filterLength) + }() + } + wg.Wait() + + // horizontal filter on transposed image, result is not transposed + coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(result, i, cpus).(*image.RGBA64) + go func() { + defer wg.Done() + resizeRGBA64(temp, slice, scaleY, coeffs, offset, filterLength) + }() + } + wg.Wait() + return result + case *image.Gray: + // 8-bit precision + temp := image.NewGray(image.Rect(0, 0, input.Bounds().Dy(), int(width))) + result := image.NewGray(image.Rect(0, 0, int(width), int(height))) + + // horizontal filter, results in transposed temporary image + coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(temp, i, cpus).(*image.Gray) + go func() { + defer wg.Done() + resizeGray(input, slice, scaleX, coeffs, offset, filterLength) + }() + } + wg.Wait() + + // horizontal filter on transposed image, result is not transposed + coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(result, i, cpus).(*image.Gray) + go func() { + defer wg.Done() + resizeGray(temp, slice, scaleY, coeffs, offset, filterLength) + }() + } + wg.Wait() + return result + case *image.Gray16: + // 16-bit precision + temp := image.NewGray16(image.Rect(0, 0, input.Bounds().Dy(), int(width))) + result := image.NewGray16(image.Rect(0, 0, int(width), int(height))) + + // horizontal filter, results in transposed temporary image + coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(temp, i, cpus).(*image.Gray16) + go func() { + defer wg.Done() + resizeGray16(input, slice, scaleX, coeffs, offset, filterLength) + }() + } + wg.Wait() + + // horizontal filter on transposed image, result is not transposed + coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(result, i, cpus).(*image.Gray16) + go func() { + defer wg.Done() + resizeGray16(temp, slice, scaleY, coeffs, offset, filterLength) + }() + } + wg.Wait() + return result + default: + // 16-bit precision + temp := image.NewRGBA64(image.Rect(0, 0, img.Bounds().Dy(), int(width))) + result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height))) + + // horizontal filter, results in transposed temporary image + coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(temp, i, cpus).(*image.RGBA64) + go func() { + defer wg.Done() + resizeGeneric(img, slice, scaleX, coeffs, offset, filterLength) + }() + } + wg.Wait() + + // horizontal filter on transposed image, result is not transposed + coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(result, i, cpus).(*image.RGBA64) + go func() { + defer wg.Done() + resizeRGBA64(temp, slice, scaleY, coeffs, offset, filterLength) + }() + } + wg.Wait() + return result + } +} + +func resizeNearest(width, height uint, scaleX, scaleY float64, img image.Image, interp InterpolationFunction) image.Image { + taps, _ := interp.kernel() + cpus := runtime.GOMAXPROCS(0) + wg := sync.WaitGroup{} + + switch input := img.(type) { + case *image.RGBA: + // 8-bit precision + temp := image.NewRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width))) + result := image.NewRGBA(image.Rect(0, 0, int(width), int(height))) + + // horizontal filter, results in transposed temporary image + coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(temp, i, cpus).(*image.RGBA) + go func() { + defer wg.Done() + nearestRGBA(input, slice, scaleX, coeffs, offset, filterLength) + }() + } + wg.Wait() + + // horizontal filter on transposed image, result is not transposed + coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(result, i, cpus).(*image.RGBA) + go func() { + defer wg.Done() + nearestRGBA(temp, slice, scaleY, coeffs, offset, filterLength) + }() + } + wg.Wait() + return result + case *image.NRGBA: + // 8-bit precision + temp := image.NewNRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width))) + result := image.NewNRGBA(image.Rect(0, 0, int(width), int(height))) + + // horizontal filter, results in transposed temporary image + coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(temp, i, cpus).(*image.NRGBA) + go func() { + defer wg.Done() + nearestNRGBA(input, slice, scaleX, coeffs, offset, filterLength) + }() + } + wg.Wait() + + // horizontal filter on transposed image, result is not transposed + coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(result, i, cpus).(*image.NRGBA) + go func() { + defer wg.Done() + nearestNRGBA(temp, slice, scaleY, coeffs, offset, filterLength) + }() + } + wg.Wait() + return result + case *image.YCbCr: + // 8-bit precision + // accessing the YCbCr arrays in a tight loop is slow. + // converting the image to ycc increases performance by 2x. + temp := newYCC(image.Rect(0, 0, input.Bounds().Dy(), int(width)), input.SubsampleRatio) + result := newYCC(image.Rect(0, 0, int(width), int(height)), image.YCbCrSubsampleRatio444) + + coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX) + in := imageYCbCrToYCC(input) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(temp, i, cpus).(*ycc) + go func() { + defer wg.Done() + nearestYCbCr(in, slice, scaleX, coeffs, offset, filterLength) + }() + } + wg.Wait() + + coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(result, i, cpus).(*ycc) + go func() { + defer wg.Done() + nearestYCbCr(temp, slice, scaleY, coeffs, offset, filterLength) + }() + } + wg.Wait() + return result.YCbCr() + case *image.RGBA64: + // 16-bit precision + temp := image.NewRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width))) + result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height))) + + // horizontal filter, results in transposed temporary image + coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(temp, i, cpus).(*image.RGBA64) + go func() { + defer wg.Done() + nearestRGBA64(input, slice, scaleX, coeffs, offset, filterLength) + }() + } + wg.Wait() + + // horizontal filter on transposed image, result is not transposed + coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(result, i, cpus).(*image.RGBA64) + go func() { + defer wg.Done() + nearestRGBA64(temp, slice, scaleY, coeffs, offset, filterLength) + }() + } + wg.Wait() + return result + case *image.NRGBA64: + // 16-bit precision + temp := image.NewNRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width))) + result := image.NewNRGBA64(image.Rect(0, 0, int(width), int(height))) + + // horizontal filter, results in transposed temporary image + coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(temp, i, cpus).(*image.NRGBA64) + go func() { + defer wg.Done() + nearestNRGBA64(input, slice, scaleX, coeffs, offset, filterLength) + }() + } + wg.Wait() + + // horizontal filter on transposed image, result is not transposed + coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(result, i, cpus).(*image.NRGBA64) + go func() { + defer wg.Done() + nearestNRGBA64(temp, slice, scaleY, coeffs, offset, filterLength) + }() + } + wg.Wait() + return result + case *image.Gray: + // 8-bit precision + temp := image.NewGray(image.Rect(0, 0, input.Bounds().Dy(), int(width))) + result := image.NewGray(image.Rect(0, 0, int(width), int(height))) + + // horizontal filter, results in transposed temporary image + coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(temp, i, cpus).(*image.Gray) + go func() { + defer wg.Done() + nearestGray(input, slice, scaleX, coeffs, offset, filterLength) + }() + } + wg.Wait() + + // horizontal filter on transposed image, result is not transposed + coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(result, i, cpus).(*image.Gray) + go func() { + defer wg.Done() + nearestGray(temp, slice, scaleY, coeffs, offset, filterLength) + }() + } + wg.Wait() + return result + case *image.Gray16: + // 16-bit precision + temp := image.NewGray16(image.Rect(0, 0, input.Bounds().Dy(), int(width))) + result := image.NewGray16(image.Rect(0, 0, int(width), int(height))) + + // horizontal filter, results in transposed temporary image + coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(temp, i, cpus).(*image.Gray16) + go func() { + defer wg.Done() + nearestGray16(input, slice, scaleX, coeffs, offset, filterLength) + }() + } + wg.Wait() + + // horizontal filter on transposed image, result is not transposed + coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(result, i, cpus).(*image.Gray16) + go func() { + defer wg.Done() + nearestGray16(temp, slice, scaleY, coeffs, offset, filterLength) + }() + } + wg.Wait() + return result + default: + // 16-bit precision + temp := image.NewRGBA64(image.Rect(0, 0, img.Bounds().Dy(), int(width))) + result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height))) + + // horizontal filter, results in transposed temporary image + coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(temp, i, cpus).(*image.RGBA64) + go func() { + defer wg.Done() + nearestGeneric(img, slice, scaleX, coeffs, offset, filterLength) + }() + } + wg.Wait() + + // horizontal filter on transposed image, result is not transposed + coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY) + wg.Add(cpus) + for i := 0; i < cpus; i++ { + slice := makeSlice(result, i, cpus).(*image.RGBA64) + go func() { + defer wg.Done() + nearestRGBA64(temp, slice, scaleY, coeffs, offset, filterLength) + }() + } + wg.Wait() + return result + } + +} + +// Calculates scaling factors using old and new image dimensions. +func calcFactors(width, height uint, oldWidth, oldHeight float64) (scaleX, scaleY float64) { + if width == 0 { + if height == 0 { + scaleX = 1.0 + scaleY = 1.0 + } else { + scaleY = oldHeight / float64(height) + scaleX = scaleY + } + } else { + scaleX = oldWidth / float64(width) + if height == 0 { + scaleY = scaleX + } else { + scaleY = oldHeight / float64(height) + } + } + return +} + +type imageWithSubImage interface { + image.Image + SubImage(image.Rectangle) image.Image +} + +func makeSlice(img imageWithSubImage, i, n int) image.Image { + return img.SubImage(image.Rect(img.Bounds().Min.X, img.Bounds().Min.Y+i*img.Bounds().Dy()/n, img.Bounds().Max.X, img.Bounds().Min.Y+(i+1)*img.Bounds().Dy()/n)) +} diff --git a/vendor/github.com/nfnt/resize/thumbnail.go b/vendor/github.com/nfnt/resize/thumbnail.go new file mode 100644 index 0000000..9efc246 --- /dev/null +++ b/vendor/github.com/nfnt/resize/thumbnail.go @@ -0,0 +1,55 @@ +/* +Copyright (c) 2012, Jan Schlicht + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +*/ + +package resize + +import ( + "image" +) + +// Thumbnail will downscale provided image to max width and height preserving +// original aspect ratio and using the interpolation function interp. +// It will return original image, without processing it, if original sizes +// are already smaller than provided constraints. +func Thumbnail(maxWidth, maxHeight uint, img image.Image, interp InterpolationFunction) image.Image { + origBounds := img.Bounds() + origWidth := uint(origBounds.Dx()) + origHeight := uint(origBounds.Dy()) + newWidth, newHeight := origWidth, origHeight + + // Return original image if it have same or smaller size as constraints + if maxWidth >= origWidth && maxHeight >= origHeight { + return img + } + + // Preserve aspect ratio + if origWidth > maxWidth { + newHeight = uint(origHeight * maxWidth / origWidth) + if newHeight < 1 { + newHeight = 1 + } + newWidth = maxWidth + } + + if newHeight > maxHeight { + newWidth = uint(newWidth * maxHeight / newHeight) + if newWidth < 1 { + newWidth = 1 + } + newHeight = maxHeight + } + return Resize(newWidth, newHeight, img, interp) +} diff --git a/vendor/github.com/nfnt/resize/ycc.go b/vendor/github.com/nfnt/resize/ycc.go new file mode 100644 index 0000000..143e4d0 --- /dev/null +++ b/vendor/github.com/nfnt/resize/ycc.go @@ -0,0 +1,387 @@ +/* +Copyright (c) 2014, Charlie Vieth + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +*/ + +package resize + +import ( + "image" + "image/color" +) + +// ycc is an in memory YCbCr image. The Y, Cb and Cr samples are held in a +// single slice to increase resizing performance. +type ycc struct { + // Pix holds the image's pixels, in Y, Cb, Cr order. The pixel at + // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*3]. + Pix []uint8 + // Stride is the Pix stride (in bytes) between vertically adjacent pixels. + Stride int + // Rect is the image's bounds. + Rect image.Rectangle + // SubsampleRatio is the subsample ratio of the original YCbCr image. + SubsampleRatio image.YCbCrSubsampleRatio +} + +// PixOffset returns the index of the first element of Pix that corresponds to +// the pixel at (x, y). +func (p *ycc) PixOffset(x, y int) int { + return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*3 +} + +func (p *ycc) Bounds() image.Rectangle { + return p.Rect +} + +func (p *ycc) ColorModel() color.Model { + return color.YCbCrModel +} + +func (p *ycc) At(x, y int) color.Color { + if !(image.Point{x, y}.In(p.Rect)) { + return color.YCbCr{} + } + i := p.PixOffset(x, y) + return color.YCbCr{ + p.Pix[i+0], + p.Pix[i+1], + p.Pix[i+2], + } +} + +func (p *ycc) Opaque() bool { + return true +} + +// SubImage returns an image representing the portion of the image p visible +// through r. The returned value shares pixels with the original image. +func (p *ycc) SubImage(r image.Rectangle) image.Image { + r = r.Intersect(p.Rect) + if r.Empty() { + return &ycc{SubsampleRatio: p.SubsampleRatio} + } + i := p.PixOffset(r.Min.X, r.Min.Y) + return &ycc{ + Pix: p.Pix[i:], + Stride: p.Stride, + Rect: r, + SubsampleRatio: p.SubsampleRatio, + } +} + +// newYCC returns a new ycc with the given bounds and subsample ratio. +func newYCC(r image.Rectangle, s image.YCbCrSubsampleRatio) *ycc { + w, h := r.Dx(), r.Dy() + buf := make([]uint8, 3*w*h) + return &ycc{Pix: buf, Stride: 3 * w, Rect: r, SubsampleRatio: s} +} + +// Copy of image.YCbCrSubsampleRatio constants - this allows us to support +// older versions of Go where these constants are not defined (i.e. Go 1.4) +const ( + ycbcrSubsampleRatio444 image.YCbCrSubsampleRatio = iota + ycbcrSubsampleRatio422 + ycbcrSubsampleRatio420 + ycbcrSubsampleRatio440 + ycbcrSubsampleRatio411 + ycbcrSubsampleRatio410 +) + +// YCbCr converts ycc to a YCbCr image with the same subsample ratio +// as the YCbCr image that ycc was generated from. +func (p *ycc) YCbCr() *image.YCbCr { + ycbcr := image.NewYCbCr(p.Rect, p.SubsampleRatio) + switch ycbcr.SubsampleRatio { + case ycbcrSubsampleRatio422: + return p.ycbcr422(ycbcr) + case ycbcrSubsampleRatio420: + return p.ycbcr420(ycbcr) + case ycbcrSubsampleRatio440: + return p.ycbcr440(ycbcr) + case ycbcrSubsampleRatio444: + return p.ycbcr444(ycbcr) + case ycbcrSubsampleRatio411: + return p.ycbcr411(ycbcr) + case ycbcrSubsampleRatio410: + return p.ycbcr410(ycbcr) + } + return ycbcr +} + +// imageYCbCrToYCC converts a YCbCr image to a ycc image for resizing. +func imageYCbCrToYCC(in *image.YCbCr) *ycc { + w, h := in.Rect.Dx(), in.Rect.Dy() + p := ycc{ + Pix: make([]uint8, 3*w*h), + Stride: 3 * w, + Rect: image.Rect(0, 0, w, h), + SubsampleRatio: in.SubsampleRatio, + } + switch in.SubsampleRatio { + case ycbcrSubsampleRatio422: + return convertToYCC422(in, &p) + case ycbcrSubsampleRatio420: + return convertToYCC420(in, &p) + case ycbcrSubsampleRatio440: + return convertToYCC440(in, &p) + case ycbcrSubsampleRatio444: + return convertToYCC444(in, &p) + case ycbcrSubsampleRatio411: + return convertToYCC411(in, &p) + case ycbcrSubsampleRatio410: + return convertToYCC410(in, &p) + } + return &p +} + +func (p *ycc) ycbcr422(ycbcr *image.YCbCr) *image.YCbCr { + var off int + Pix := p.Pix + Y := ycbcr.Y + Cb := ycbcr.Cb + Cr := ycbcr.Cr + for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ { + yy := y * ycbcr.YStride + cy := y * ycbcr.CStride + for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ { + ci := cy + x/2 + Y[yy+x] = Pix[off+0] + Cb[ci] = Pix[off+1] + Cr[ci] = Pix[off+2] + off += 3 + } + } + return ycbcr +} + +func (p *ycc) ycbcr420(ycbcr *image.YCbCr) *image.YCbCr { + var off int + Pix := p.Pix + Y := ycbcr.Y + Cb := ycbcr.Cb + Cr := ycbcr.Cr + for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ { + yy := y * ycbcr.YStride + cy := (y / 2) * ycbcr.CStride + for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ { + ci := cy + x/2 + Y[yy+x] = Pix[off+0] + Cb[ci] = Pix[off+1] + Cr[ci] = Pix[off+2] + off += 3 + } + } + return ycbcr +} + +func (p *ycc) ycbcr440(ycbcr *image.YCbCr) *image.YCbCr { + var off int + Pix := p.Pix + Y := ycbcr.Y + Cb := ycbcr.Cb + Cr := ycbcr.Cr + for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ { + yy := y * ycbcr.YStride + cy := (y / 2) * ycbcr.CStride + for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ { + ci := cy + x + Y[yy+x] = Pix[off+0] + Cb[ci] = Pix[off+1] + Cr[ci] = Pix[off+2] + off += 3 + } + } + return ycbcr +} + +func (p *ycc) ycbcr444(ycbcr *image.YCbCr) *image.YCbCr { + var off int + Pix := p.Pix + Y := ycbcr.Y + Cb := ycbcr.Cb + Cr := ycbcr.Cr + for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ { + yy := y * ycbcr.YStride + cy := y * ycbcr.CStride + for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ { + ci := cy + x + Y[yy+x] = Pix[off+0] + Cb[ci] = Pix[off+1] + Cr[ci] = Pix[off+2] + off += 3 + } + } + return ycbcr +} + +func (p *ycc) ycbcr411(ycbcr *image.YCbCr) *image.YCbCr { + var off int + Pix := p.Pix + Y := ycbcr.Y + Cb := ycbcr.Cb + Cr := ycbcr.Cr + for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ { + yy := y * ycbcr.YStride + cy := y * ycbcr.CStride + for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ { + ci := cy + x/4 + Y[yy+x] = Pix[off+0] + Cb[ci] = Pix[off+1] + Cr[ci] = Pix[off+2] + off += 3 + } + } + return ycbcr +} + +func (p *ycc) ycbcr410(ycbcr *image.YCbCr) *image.YCbCr { + var off int + Pix := p.Pix + Y := ycbcr.Y + Cb := ycbcr.Cb + Cr := ycbcr.Cr + for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ { + yy := y * ycbcr.YStride + cy := (y / 2) * ycbcr.CStride + for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ { + ci := cy + x/4 + Y[yy+x] = Pix[off+0] + Cb[ci] = Pix[off+1] + Cr[ci] = Pix[off+2] + off += 3 + } + } + return ycbcr +} + +func convertToYCC422(in *image.YCbCr, p *ycc) *ycc { + var off int + Pix := p.Pix + Y := in.Y + Cb := in.Cb + Cr := in.Cr + for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ { + yy := y * in.YStride + cy := y * in.CStride + for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ { + ci := cy + x/2 + Pix[off+0] = Y[yy+x] + Pix[off+1] = Cb[ci] + Pix[off+2] = Cr[ci] + off += 3 + } + } + return p +} + +func convertToYCC420(in *image.YCbCr, p *ycc) *ycc { + var off int + Pix := p.Pix + Y := in.Y + Cb := in.Cb + Cr := in.Cr + for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ { + yy := y * in.YStride + cy := (y / 2) * in.CStride + for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ { + ci := cy + x/2 + Pix[off+0] = Y[yy+x] + Pix[off+1] = Cb[ci] + Pix[off+2] = Cr[ci] + off += 3 + } + } + return p +} + +func convertToYCC440(in *image.YCbCr, p *ycc) *ycc { + var off int + Pix := p.Pix + Y := in.Y + Cb := in.Cb + Cr := in.Cr + for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ { + yy := y * in.YStride + cy := (y / 2) * in.CStride + for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ { + ci := cy + x + Pix[off+0] = Y[yy+x] + Pix[off+1] = Cb[ci] + Pix[off+2] = Cr[ci] + off += 3 + } + } + return p +} + +func convertToYCC444(in *image.YCbCr, p *ycc) *ycc { + var off int + Pix := p.Pix + Y := in.Y + Cb := in.Cb + Cr := in.Cr + for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ { + yy := y * in.YStride + cy := y * in.CStride + for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ { + ci := cy + x + Pix[off+0] = Y[yy+x] + Pix[off+1] = Cb[ci] + Pix[off+2] = Cr[ci] + off += 3 + } + } + return p +} + +func convertToYCC411(in *image.YCbCr, p *ycc) *ycc { + var off int + Pix := p.Pix + Y := in.Y + Cb := in.Cb + Cr := in.Cr + for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ { + yy := y * in.YStride + cy := y * in.CStride + for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ { + ci := cy + x/4 + Pix[off+0] = Y[yy+x] + Pix[off+1] = Cb[ci] + Pix[off+2] = Cr[ci] + off += 3 + } + } + return p +} + +func convertToYCC410(in *image.YCbCr, p *ycc) *ycc { + var off int + Pix := p.Pix + Y := in.Y + Cb := in.Cb + Cr := in.Cr + for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ { + yy := y * in.YStride + cy := (y / 2) * in.CStride + for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ { + ci := cy + x/4 + Pix[off+0] = Y[yy+x] + Pix[off+1] = Cb[ci] + Pix[off+2] = Cr[ci] + off += 3 + } + } + return p +}