├── Godeps
├── _workspace
│ ├── .gitignore
│ └── src
│ │ ├── github.com
│ │ ├── satori
│ │ │ └── go.uuid
│ │ │ │ ├── .travis.yml
│ │ │ │ ├── LICENSE
│ │ │ │ ├── README.md
│ │ │ │ └── uuid.go
│ │ └── disintegration
│ │ │ └── imaging
│ │ │ ├── LICENSE
│ │ │ ├── utils.go
│ │ │ ├── effects.go
│ │ │ ├── transform.go
│ │ │ ├── tools.go
│ │ │ ├── adjust.go
│ │ │ ├── README.md
│ │ │ ├── helpers.go
│ │ │ └── resize.go
│ │ └── golang.org
│ │ └── x
│ │ └── image
│ │ ├── PATENTS
│ │ ├── tiff
│ │ ├── compress.go
│ │ ├── buffer.go
│ │ ├── consts.go
│ │ ├── lzw
│ │ │ └── reader.go
│ │ ├── writer.go
│ │ └── reader.go
│ │ ├── LICENSE
│ │ └── bmp
│ │ ├── writer.go
│ │ └── reader.go
├── Readme
└── Godeps.json
├── index.html
├── test
├── uploadfile-file.html
├── uploadfile-video.html
├── uploadfile-audio.html
├── index.html
└── uploadfile-image.html
├── Dockerfile
├── model
├── response.go
├── resImage.go
├── resAudio.go
├── resFile.go
└── resVideo.go
├── .gitignore
├── README.md
├── main.go
└── handle
├── fileUpload.go
└── fileDownload.go
/Godeps/_workspace/.gitignore:
--------------------------------------------------------------------------------
1 | /pkg
2 | /bin
3 |
--------------------------------------------------------------------------------
/Godeps/Readme:
--------------------------------------------------------------------------------
1 | This directory tree is generated automatically by godep.
2 |
3 | Please do not edit.
4 |
5 | See https://github.com/tools/godep for more information.
6 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | File Server
6 |
7 |
8 | Golang File Server
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/satori/go.uuid/.travis.yml:
--------------------------------------------------------------------------------
1 | language: go
2 | go:
3 | - 1.0
4 | - 1.1
5 | - 1.2
6 | - 1.3
7 | - 1.4
8 | - 1.5
9 | sudo: false
10 | notifications:
11 | email: false
12 |
--------------------------------------------------------------------------------
/test/uploadfile-file.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | FileUpload
5 |
6 |
7 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/test/uploadfile-video.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | FileUpload
5 |
6 |
7 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/test/uploadfile-audio.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | FileUpload
5 |
6 |
7 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UploadFile Test
6 |
7 |
8 |
9 | uploadFile
10 | uploadAudio
11 | uploadImage
12 | uploadVideo
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # GolangFileServer
2 | #
3 | # VERSION 1.0
4 |
5 | FROM oceanwu/godepffmpeg:latest
6 |
7 | MAINTAINER oceanwu
8 |
9 | # build & run golangfieserver
10 | ENV kpdir /go/src/github.com/mind-stack-cn/golang-fileserver
11 |
12 | RUN mkdir -p ${kpdir}
13 |
14 | ADD . ${kpdir}/
15 |
16 | WORKDIR ${kpdir}
17 |
18 | RUN godep restore && godep go build -v
19 |
20 | EXPOSE 8088
21 |
22 | ENTRYPOINT ["./golang-fileserver"]
23 |
24 |
--------------------------------------------------------------------------------
/test/uploadfile-image.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | FileUpload
5 |
6 |
7 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Godeps/Godeps.json:
--------------------------------------------------------------------------------
1 | {
2 | "ImportPath": "github.com/mind-stack-cn/golang-fileserver",
3 | "GoVersion": "go1.6",
4 | "Deps": [
5 | {
6 | "ImportPath": "github.com/disintegration/imaging",
7 | "Rev": "546cb3c5137b3f1232e123a26aa033aade6b3066"
8 | },
9 | {
10 | "ImportPath": "github.com/satori/go.uuid",
11 | "Rev": "d41af8bb6a7704f00bc3b7cba9355ae6a5a80048"
12 | },
13 | {
14 | "ImportPath": "golang.org/x/image/bmp",
15 | "Rev": "baddd3465a05d84a6d8d3507547a91cb188c81ea"
16 | },
17 | {
18 | "ImportPath": "golang.org/x/image/tiff",
19 | "Rev": "baddd3465a05d84a6d8d3507547a91cb188c81ea"
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/model/response.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 JiaoHu. All rights reserved.
3 | * JiaoHu PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
4 | *
5 | * Created by tony on 15/12/16.
6 | */
7 | package model
8 |
9 | type Response struct {
10 | Header Header `json:"header"`
11 | Data interface{} `json:"data"`
12 | }
13 |
14 | const (
15 | ServerSuccessCode = 1000
16 | ServerSuccessDesc = "success"
17 |
18 | FileTypeFile = "file"
19 | FileTypeImage = "image"
20 | FileTypeAudio = "audio"
21 | FileTypeVideo = "video"
22 | )
23 |
24 | type Header struct {
25 | Code int `json:"code"`
26 | Description string `json:"description"`
27 | }
28 |
29 |
--------------------------------------------------------------------------------
/model/resImage.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 JiaoHu. All rights reserved.
3 | * JiaoHu PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
4 | *
5 | * Created by tony on 15/12/18.
6 | */
7 | package model
8 | import "github.com/disintegration/imaging"
9 |
10 | type ResImage struct {
11 | ResFile
12 | Width int `json:"width"`
13 | Height int `json:"height"`
14 | }
15 |
16 | func (f *ResImage) AddAttribute(){
17 | f.ResFile.AddAttribute()
18 | // Image, Width & Height
19 | width, height, err := GetImageSize(f.AbsoluteFilePath)
20 | if err == nil {
21 | f.Width = width
22 | f.Height = height
23 | }
24 | }
25 |
26 | // Use imaging library to Get Image Size
27 | func GetImageSize(name string) (int, int, error) {
28 | img, err := imaging.Open(name)
29 | if err == nil {
30 | srcBounds := img.Bounds()
31 | return srcBounds.Max.X, srcBounds.Max.Y, nil
32 | }else {
33 | return 0, 0, err
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/model/resAudio.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 JiaoHu. All rights reserved.
3 | * JiaoHu PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
4 | *
5 | * Created by tony on 15/12/18.
6 | */
7 | package model
8 | import (
9 | "strings"
10 | "os/exec"
11 | "strconv"
12 | )
13 |
14 | type ResAudio struct {
15 | ResFile
16 | Duration float64 `json:"duration"`
17 | }
18 |
19 | func (f *ResAudio) AddAttribute(){
20 | f.ResFile.AddAttribute()
21 | // Get File Duration
22 | duration, err := GetMediaDuration(f.AbsoluteFilePath)
23 | if err == nil {
24 | f.Duration = duration
25 | }
26 | }
27 |
28 | // Use Command Line "ffprobe" to Get media duration
29 | func GetMediaDuration(filePath string) (float64, error) {
30 | out, err := exec.Command("sh", "-c", "ffprobe -i " + filePath + " -show_entries format=duration -v quiet -of csv=\"p=0\"").Output()
31 | if err == nil {
32 | durationStr := strings.Trim(string(out), "\n")
33 | duration, _ := strconv.ParseFloat(durationStr, 64)
34 | return duration, nil
35 | }else {
36 | return 0, err
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/satori/go.uuid/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (C) 2013-2015 by Maxim Bublis
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/disintegration/imaging/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2012-2014 Grigory Dryapak
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/model/resFile.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 JiaoHu. All rights reserved.
3 | * JiaoHu PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
4 | *
5 | * Created by tony on 15/12/18.
6 | */
7 | package model
8 | import (
9 | "os"
10 | )
11 |
12 | type ResFileWrapper interface {
13 | // Add File Attribute
14 | AddAttribute()
15 | }
16 |
17 | type ResFile struct {
18 | AbsoluteFilePath string `json:"-"`
19 | Uri string `json:"uri"`
20 | Size int64 `json:"size"`
21 | FileType string `json:"fileType"`
22 | }
23 |
24 | func (f *ResFile) AddAttribute() {
25 | file, err := os.Open(f.AbsoluteFilePath)
26 | if err != nil {
27 | return
28 | }
29 | defer file.Close()
30 | if stat, err := file.Stat(); err == nil {
31 | f.Size = stat.Size()
32 | }
33 | }
34 |
35 |
36 | // Construct ResFile
37 | func ResFileFromFileName(absoluteFilePath string, uri string, fileType string) interface{} {
38 | var res ResFileWrapper
39 | resFile := ResFile{AbsoluteFilePath:absoluteFilePath, Uri:uri, FileType:fileType}
40 |
41 | switch fileType {
42 | case FileTypeImage:
43 | res = &ResImage{ResFile:resFile}
44 | case FileTypeAudio:
45 | res = &ResAudio{ResFile:resFile}
46 | case FileTypeVideo:
47 | res = &ResVideo{ResAudio:ResAudio{ResFile:resFile}}
48 | default:
49 | res = &resFile
50 | }
51 | res.AddAttribute()
52 | return res
53 | }
54 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/golang.org/x/image/PATENTS:
--------------------------------------------------------------------------------
1 | Additional IP Rights Grant (Patents)
2 |
3 | "This implementation" means the copyrightable works distributed by
4 | Google as part of the Go project.
5 |
6 | Google hereby grants to You a perpetual, worldwide, non-exclusive,
7 | no-charge, royalty-free, irrevocable (except as stated in this section)
8 | patent license to make, have made, use, offer to sell, sell, import,
9 | transfer and otherwise run, modify and propagate the contents of this
10 | implementation of Go, where such license applies only to those patent
11 | claims, both currently owned or controlled by Google and acquired in
12 | the future, licensable by Google that are necessarily infringed by this
13 | implementation of Go. This grant does not include claims that would be
14 | infringed only as a consequence of further modification of this
15 | implementation. If you or your agent or exclusive licensee institute or
16 | order or agree to the institution of patent litigation against any
17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging
18 | that this implementation of Go or any code incorporated within this
19 | implementation of Go constitutes direct or contributory patent
20 | infringement, or inducement of patent infringement, then any patent
21 | rights granted to you under this License for this implementation of Go
22 | shall terminate as of the date such litigation is filed.
23 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/golang.org/x/image/tiff/compress.go:
--------------------------------------------------------------------------------
1 | // Copyright 2011 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package tiff
6 |
7 | import (
8 | "bufio"
9 | "io"
10 | )
11 |
12 | type byteReader interface {
13 | io.Reader
14 | io.ByteReader
15 | }
16 |
17 | // unpackBits decodes the PackBits-compressed data in src and returns the
18 | // uncompressed data.
19 | //
20 | // The PackBits compression format is described in section 9 (p. 42)
21 | // of the TIFF spec.
22 | func unpackBits(r io.Reader) ([]byte, error) {
23 | buf := make([]byte, 128)
24 | dst := make([]byte, 0, 1024)
25 | br, ok := r.(byteReader)
26 | if !ok {
27 | br = bufio.NewReader(r)
28 | }
29 |
30 | for {
31 | b, err := br.ReadByte()
32 | if err != nil {
33 | if err == io.EOF {
34 | return dst, nil
35 | }
36 | return nil, err
37 | }
38 | code := int(int8(b))
39 | switch {
40 | case code >= 0:
41 | n, err := io.ReadFull(br, buf[:code+1])
42 | if err != nil {
43 | return nil, err
44 | }
45 | dst = append(dst, buf[:n]...)
46 | case code == -128:
47 | // No-op.
48 | default:
49 | if b, err = br.ReadByte(); err != nil {
50 | return nil, err
51 | }
52 | for j := 0; j < 1-code; j++ {
53 | buf[j] = b
54 | }
55 | dst = append(dst, buf[:1-code]...)
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/model/resVideo.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 JiaoHu. All rights reserved.
3 | * JiaoHu PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
4 | *
5 | * Created by tony on 15/12/18.
6 | */
7 | package model
8 | import (
9 | "os/exec"
10 | "strings"
11 | "path"
12 | "log"
13 | "fmt"
14 | "bytes"
15 | )
16 |
17 | const thumbExt = ".jpg"
18 |
19 | type ResVideo struct {
20 | ResAudio
21 | Thumbnail interface{} `json:"thumbnail"`
22 | }
23 |
24 | func (f *ResVideo) AddAttribute() {
25 | // add audio attribute
26 | f.ResAudio.AddAttribute()
27 |
28 | // Thumbnail path
29 | thumbNailPath := strings.TrimSuffix(f.AbsoluteFilePath, path.Ext(f.AbsoluteFilePath)) + thumbExt
30 | thumbNailUri := strings.TrimSuffix(f.Uri, path.Ext(f.Uri)) + thumbExt
31 | // Generate default thumbnail
32 | if err := GetVideoThumbnail(f.AbsoluteFilePath, thumbNailPath); err == nil {
33 | f.Thumbnail = ResFileFromFileName(thumbNailPath, thumbNailUri, FileTypeImage)
34 | }else{
35 | log.Print("GetVideoThumbnail Error: ", err.Error())
36 | }
37 | }
38 |
39 | // Use Command Line "ffmpeg" to Get media duration
40 | func GetVideoThumbnail(filePath string, thumbNailPath string) error {
41 | cmd:= exec.Command("/bin/sh", "-c", "ffmpeg -i " + filePath + " -ss 00:00:00.000 -vframes 1 -y " + thumbNailPath)
42 | var out bytes.Buffer
43 | var stderr bytes.Buffer
44 | cmd.Stdout = &out
45 | cmd.Stderr = &stderr
46 | err := cmd.Run()
47 | if err != nil {
48 | fmt.Println(fmt.Sprint(err) + ": " + stderr.String())
49 | return err
50 | }else{
51 | fmt.Println("Result: " + out.String())
52 | return nil
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/golang.org/x/image/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2009 The Go Authors. All rights reserved.
2 |
3 | Redistribution and use in source and binary forms, with or without
4 | modification, are permitted provided that the following conditions are
5 | met:
6 |
7 | * Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | * Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following disclaimer
11 | in the documentation and/or other materials provided with the
12 | distribution.
13 | * Neither the name of Google Inc. nor the names of its
14 | contributors may be used to endorse or promote products derived from
15 | this software without specific prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/intellij,go
3 |
4 | ### Intellij ###
5 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
6 |
7 | *.iml
8 |
9 | ## Directory-based project format:
10 | .idea/
11 | # if you remove the above rule, at least ignore the following:
12 |
13 | # User-specific stuff:
14 | # .idea/workspace.xml
15 | # .idea/tasks.xml
16 | # .idea/dictionaries
17 | # .idea/shelf
18 |
19 | # Sensitive or high-churn files:
20 | # .idea/dataSources.ids
21 | # .idea/dataSources.xml
22 | # .idea/sqlDataSources.xml
23 | # .idea/dynamic.xml
24 | # .idea/uiDesigner.xml
25 |
26 | # Gradle:
27 | # .idea/gradle.xml
28 | # .idea/libraries
29 |
30 | # Mongo Explorer plugin:
31 | # .idea/mongoSettings.xml
32 |
33 | ## File-based project format:
34 | *.ipr
35 | *.iws
36 |
37 | ## Plugin-specific files:
38 |
39 | # IntelliJ
40 | /out/
41 |
42 | # mpeltonen/sbt-idea plugin
43 | .idea_modules/
44 |
45 | # JIRA plugin
46 | atlassian-ide-plugin.xml
47 |
48 | # Crashlytics plugin (for Android Studio and IntelliJ)
49 | com_crashlytics_export_strings.xml
50 | crashlytics.properties
51 | crashlytics-build.properties
52 | fabric.properties
53 |
54 |
55 | ### Go ###
56 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
57 | *.o
58 | *.a
59 | *.so
60 |
61 | # Folders
62 | _obj
63 | _test
64 |
65 | # Architecture specific extensions/prefixes
66 | *.[568vq]
67 | [568vq].out
68 |
69 | *.cgo1.go
70 | *.cgo2.c
71 | _cgo_defun.c
72 | _cgo_gotypes.go
73 | _cgo_export.*
74 |
75 | _testmain.go
76 |
77 | *.exe
78 | *.test
79 | *.prof
80 |
81 | sql.log
82 | *.tmp
83 | kanPingserver
84 |
85 | # temp data
86 | /data
87 | /data/*
88 |
89 | golang-fileserver
90 |
91 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/disintegration/imaging/utils.go:
--------------------------------------------------------------------------------
1 | package imaging
2 |
3 | import (
4 | "math"
5 | "runtime"
6 | "sync"
7 | "sync/atomic"
8 | )
9 |
10 | var parallelizationEnabled = true
11 |
12 | // if GOMAXPROCS = 1: no goroutines used
13 | // if GOMAXPROCS > 1: spawn N=GOMAXPROCS workers in separate goroutines
14 | func parallel(dataSize int, fn func(partStart, partEnd int)) {
15 | numGoroutines := 1
16 | partSize := dataSize
17 |
18 | if parallelizationEnabled {
19 | numProcs := runtime.GOMAXPROCS(0)
20 | if numProcs > 1 {
21 | numGoroutines = numProcs
22 | partSize = dataSize / (numGoroutines * 10)
23 | if partSize < 1 {
24 | partSize = 1
25 | }
26 | }
27 | }
28 |
29 | if numGoroutines == 1 {
30 | fn(0, dataSize)
31 | } else {
32 | var wg sync.WaitGroup
33 | wg.Add(numGoroutines)
34 | idx := uint64(0)
35 |
36 | for p := 0; p < numGoroutines; p++ {
37 | go func() {
38 | defer wg.Done()
39 | for {
40 | partStart := int(atomic.AddUint64(&idx, uint64(partSize))) - partSize
41 | if partStart >= dataSize {
42 | break
43 | }
44 | partEnd := partStart + partSize
45 | if partEnd > dataSize {
46 | partEnd = dataSize
47 | }
48 | fn(partStart, partEnd)
49 | }
50 | }()
51 | }
52 |
53 | wg.Wait()
54 | }
55 | }
56 |
57 | func absint(i int) int {
58 | if i < 0 {
59 | return -i
60 | }
61 | return i
62 | }
63 |
64 | // clamp & round float64 to uint8 (0..255)
65 | func clamp(v float64) uint8 {
66 | return uint8(math.Min(math.Max(v, 0.0), 255.0) + 0.5)
67 | }
68 |
69 | // clamp int32 to uint8 (0..255)
70 | func clampint32(v int32) uint8 {
71 | if v < 0 {
72 | return 0
73 | } else if v > 255 {
74 | return 255
75 | }
76 | return uint8(v)
77 | }
78 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/golang.org/x/image/tiff/buffer.go:
--------------------------------------------------------------------------------
1 | // Copyright 2011 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package tiff
6 |
7 | import "io"
8 |
9 | // buffer buffers an io.Reader to satisfy io.ReaderAt.
10 | type buffer struct {
11 | r io.Reader
12 | buf []byte
13 | }
14 |
15 | // fill reads data from b.r until the buffer contains at least end bytes.
16 | func (b *buffer) fill(end int) error {
17 | m := len(b.buf)
18 | if end > m {
19 | if end > cap(b.buf) {
20 | newcap := 1024
21 | for newcap < end {
22 | newcap *= 2
23 | }
24 | newbuf := make([]byte, end, newcap)
25 | copy(newbuf, b.buf)
26 | b.buf = newbuf
27 | } else {
28 | b.buf = b.buf[:end]
29 | }
30 | if n, err := io.ReadFull(b.r, b.buf[m:end]); err != nil {
31 | end = m + n
32 | b.buf = b.buf[:end]
33 | return err
34 | }
35 | }
36 | return nil
37 | }
38 |
39 | func (b *buffer) ReadAt(p []byte, off int64) (int, error) {
40 | o := int(off)
41 | end := o + len(p)
42 | if int64(end) != off+int64(len(p)) {
43 | return 0, io.ErrUnexpectedEOF
44 | }
45 |
46 | err := b.fill(end)
47 | return copy(p, b.buf[o:end]), err
48 | }
49 |
50 | // Slice returns a slice of the underlying buffer. The slice contains
51 | // n bytes starting at offset off.
52 | func (b *buffer) Slice(off, n int) ([]byte, error) {
53 | end := off + n
54 | if err := b.fill(end); err != nil {
55 | return nil, err
56 | }
57 | return b.buf[off:end], nil
58 | }
59 |
60 | // newReaderAt converts an io.Reader into an io.ReaderAt.
61 | func newReaderAt(r io.Reader) io.ReaderAt {
62 | if ra, ok := r.(io.ReaderAt); ok {
63 | return ra
64 | }
65 | return &buffer{
66 | r: r,
67 | buf: make([]byte, 0, 1024),
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/satori/go.uuid/README.md:
--------------------------------------------------------------------------------
1 | # UUID package for Go language
2 |
3 | [](https://travis-ci.org/satori/go.uuid)
4 | [](http://godoc.org/github.com/satori/go.uuid)
5 |
6 | This package provides pure Go implementation of Universally Unique Identifier (UUID). Supported both creation and parsing of UUIDs.
7 |
8 | With 100% test coverage and benchmarks out of box.
9 |
10 | Supported versions:
11 | * Version 1, based on timestamp and MAC address (RFC 4122)
12 | * Version 2, based on timestamp, MAC address and POSIX UID/GID (DCE 1.1)
13 | * Version 3, based on MD5 hashing (RFC 4122)
14 | * Version 4, based on random numbers (RFC 4122)
15 | * Version 5, based on SHA-1 hashing (RFC 4122)
16 |
17 | ## Installation
18 |
19 | Use the `go` command:
20 |
21 | $ go get github.com/satori/go.uuid
22 |
23 | ## Requirements
24 |
25 | UUID package requires any stable version of Go Programming Language.
26 |
27 | It is tested against following versions of Go: 1.0-1.5
28 |
29 | ## Example
30 |
31 | ```go
32 | package main
33 |
34 | import (
35 | "fmt"
36 | "github.com/satori/go.uuid"
37 | )
38 |
39 | func main() {
40 | // Creating UUID Version 4
41 | u1 := uuid.NewV4()
42 | fmt.Printf("UUIDv4: %s\n", u1)
43 |
44 | // Parsing UUID from string input
45 | u2, err := uuid.FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
46 | if err != nil {
47 | fmt.Printf("Something gone wrong: %s", err)
48 | }
49 | fmt.Printf("Successfully parsed: %s", u2)
50 | }
51 | ```
52 |
53 | ## Documentation
54 |
55 | [Documentation](http://godoc.org/github.com/satori/go.uuid) is hosted at GoDoc project.
56 |
57 | ## Links
58 | * [RFC 4122](http://tools.ietf.org/html/rfc4122)
59 | * [DCE 1.1: Authentication and Security Services](http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01)
60 |
61 | ## Copyright
62 |
63 | Copyright (C) 2013-2015 by Maxim Bublis .
64 |
65 | UUID package released under MIT License.
66 | See [LICENSE](https://github.com/satori/go.uuid/blob/master/LICENSE) for details.
67 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GoLangFileServer
2 |
3 | ### build
4 | docker build -t oceanwu/golang-fileserver .
5 |
6 | ### Usage
7 | docker run -d -p 8088:8088 -v $(pwd)/data:/go/src/github.com/mind-stack-cn/golang-fileserver/data oceanwu/golang-fileserver
8 |
9 | ## test page
10 | http://127.0.0.1:8088/test
11 |
12 | ## upload File
13 | ```
14 | POST http://127.0.0.1:8088/?fileType=image
15 |
16 | fileType=file|image|audio|video
17 | ```
18 |
19 | ## Sample Return Json
20 | ```json
21 | Image
22 | {
23 | "header": {
24 | "code": 1000,
25 | "description": "success"
26 | },
27 | "data": [
28 | {
29 | "uri": "/828c8279/62ea/4829/bb16/0ad7bee0e5c6.png",
30 | "size": 141913,
31 | "fileType": "image",
32 | "width": 848,
33 | "height": 878
34 | },
35 | {
36 | "uri": "/97f9882f/81cf/4ad4/9bcc/b80bf2d4205f.png",
37 | "size": 171262,
38 | "fileType": "image",
39 | "width": 784,
40 | "height": 818
41 | }
42 | ]
43 | }
44 |
45 | Audio
46 | {
47 | "header": {
48 | "code": 1000,
49 | "description": "success"
50 | },
51 | "data": [
52 | {
53 | "uri": "/0efc42f4/7d76/4771/9717/49ad46ac1f6a.mp3",
54 | "size": 1673125,
55 | "fileType": "audio",
56 | "duration": 192.339592
57 | }
58 | ]
59 | }
60 |
61 | Video
62 | {
63 | "header": {
64 | "code": 1000,
65 | "description": "success"
66 | },
67 | "data": [
68 | {
69 | "uri": "/d6165916/2724/4822/ba83/689e6984583c.mp4",
70 | "size": 424407,
71 | "fileType": "video",
72 | "duration": 10.024,
73 | "thumbnail": {
74 | "uri": "/d6165916/2724/4822/ba83/689e6984583c.jpg",
75 | "size": 18109,
76 | "fileType": "image",
77 | "width": 640,
78 | "height": 360
79 | }
80 | }
81 | ]
82 | }
83 |
84 | common File
85 | {
86 | "header": {
87 | "code": 1000,
88 | "description": "success"
89 | },
90 | "data": [
91 | {
92 | "uri": "/8cd1f889/fd43/49ba/8016/40f366327bc4.word",
93 | "size": 45,
94 | "fileType": "file"
95 | }
96 | ]
97 | }
98 | ```
99 |
100 | ## Image Thumbnail
101 | ```
102 | Query Parameter : width && height
103 | example: http://127.0.0.1:8088/828c8279/62ea/4829/bb16/0ad7bee0e5c6.png?width=800&height=800
104 | ```
105 |
106 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 JiaoHu. All rights reserved.
3 | * JiaoHu PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
4 | *
5 | * Created by tony on 15/12/17.
6 | */
7 | package main
8 |
9 | import (
10 | "flag"
11 | "fmt"
12 | "io/ioutil"
13 | "log"
14 | "net/http"
15 | _ "net/http/pprof"
16 | "os"
17 | "path/filepath"
18 | "strings"
19 | "github.com/mind-stack-cn/golang-fileserver/handle"
20 | )
21 |
22 | var (
23 | dir string
24 | port string
25 | logging bool
26 | debug bool
27 | )
28 |
29 |
30 | const VERSION = "1.0"
31 |
32 | func main() {
33 | //fmt.Println(len(os.Args), os.Args)
34 | if len(os.Args) > 1 && os.Args[1] == "-v" {
35 | fmt.Println("Version " + VERSION)
36 | os.Exit(0)
37 | }
38 |
39 | flag.StringVar(&dir, "dir", ".", "Specify a directory to server files from.")
40 | flag.StringVar(&port, "port", ":8088", "Port to bind the file server")
41 | flag.BoolVar(&logging, "log", true, "Enable Log (true/false)")
42 | flag.BoolVar(&debug, "debug", true, "Make external assets expire every request")
43 | flag.Parse()
44 |
45 | if logging == false {
46 | log.SetOutput(ioutil.Discard)
47 | }
48 | // If no path is passed to app, normalize to path formath
49 | if dir == "." {
50 | dir, _ = filepath.Abs(dir)
51 | dir += "/data/"
52 | }
53 |
54 | if _, err := os.Stat(dir); err != nil {
55 | log.Printf("Directory %s not exist, Create it", dir)
56 | errPath := os.MkdirAll(dir, 0777)
57 | if errPath != nil {
58 | log.Fatalf("Directory %s not exist, Create it Fail", dir)
59 | return
60 | }
61 | }
62 |
63 | // normalize dir, ending with... /
64 | if strings.HasSuffix(dir, "/") == false {
65 | dir = dir + "/"
66 | }
67 |
68 | mux := http.NewServeMux()
69 |
70 | mux.Handle("/", addDefaultHeaders(http.HandlerFunc(handleReq)))
71 |
72 | log.Printf("Listening on port %s .....\n", port)
73 | if debug {
74 | log.Print("Serving data dir in debug mode.. .\n")
75 | }
76 | http.ListenAndServe(port, mux)
77 | }
78 |
79 | func handleReq(w http.ResponseWriter, r *http.Request) {
80 | if r.Method == "POST" {
81 | handle.FileUpload(dir, w, r)
82 | return
83 | }
84 |
85 | log.Print("Request: ", r.RequestURI)
86 | // See bug #9. For some reason, don't arrive index.html, when asked it..
87 | if r.URL.Path != "/" && r.URL.Path != "/test/" && strings.HasSuffix(r.URL.Path, "/") && r.FormValue("get_file") != "true" {
88 | log.Printf("Index dir %s", r.URL.Path)
89 | http.Error(w, "BadRequest", http.StatusBadRequest)
90 | } else {
91 | handle.FileDownload(dir, w, r)
92 | }
93 | }
94 |
95 | func addDefaultHeaders(fn http.HandlerFunc) http.HandlerFunc {
96 | return func(w http.ResponseWriter, r *http.Request) {
97 | w.Header().Set("Access-Control-Allow-Origin", "*")
98 | fn(w, r)
99 | }
100 | }
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/handle/fileUpload.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 JiaoHu. All rights reserved.
3 | * JiaoHu PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
4 | *
5 | * Created by tony on 15/12/17.
6 | */
7 | package handle
8 |
9 | import (
10 | "net/http"
11 | "fmt"
12 | "io"
13 | "strings"
14 | "path/filepath"
15 | "os"
16 | "log"
17 | "github.com/mind-stack-cn/golang-fileserver/model"
18 | "encoding/json"
19 | "github.com/satori/go.uuid"
20 | )
21 |
22 | // generate new file path and create folder in workDir
23 | // return absoluteFilePath, relatedFilePath, error
24 | func GenerateNewFilePath(workDir string, extension string) (string, string, error) {
25 | // UUID
26 | randomUUId := uuid.NewV4()
27 | // Split
28 | paSplit := strings.Split(randomUUId.String(), "-")
29 | // File Path
30 | relatedFileDir := paSplit[0] + "/" + paSplit[1] + "/" + paSplit[2] + "/" + paSplit[3] + "/"
31 | // File Name
32 | fileName := paSplit[4] + extension
33 |
34 | // Create File Dir if not
35 | var fileDir string
36 | if workDir != "." {
37 | fileDir = workDir + relatedFileDir
38 | }
39 | // Make dir
40 | err := os.MkdirAll(fileDir, 0777)
41 |
42 | return fileDir + fileName, "/" + relatedFileDir + fileName, err
43 | }
44 |
45 | // Handler Upload File Request
46 | // Save It, Return saved file info
47 | func FileUpload(dir string, w http.ResponseWriter, r *http.Request) {
48 | reader, err := r.MultipartReader()
49 | if err != nil {
50 | fmt.Print(err)
51 | http.Error(w, err.Error(), http.StatusInternalServerError)
52 | return
53 | }
54 |
55 | var fileArray []interface{}
56 |
57 | for {
58 | part, err := reader.NextPart()
59 | if err == io.EOF {
60 | break
61 | }
62 |
63 |
64 | absoluteFilePath, relatedFilePath, errPath := GenerateNewFilePath(dir, filepath.Ext(part.FileName()))
65 | if errPath != nil {
66 | http.Error(w, err.Error(), http.StatusInternalServerError)
67 | return
68 | }
69 |
70 | // Create File
71 | dst, err := os.Create(absoluteFilePath)
72 | defer dst.Close()
73 | if err != nil {
74 | http.Error(w, err.Error(), http.StatusInternalServerError)
75 | return
76 | }
77 | if _, err := io.Copy(dst, part); err != nil {
78 | http.Error(w, err.Error(), http.StatusInternalServerError)
79 | return
80 | }
81 |
82 | log.Print("receive file successfully! fileName:" + part.FileName())
83 |
84 | // append resFile to response Array
85 | fileArray = append(fileArray, model.ResFileFromFileName(dst.Name(), relatedFilePath, r.URL.Query().Get("fileType")))
86 | if len(fileArray) >= 10 {
87 | // 最多上传10个文件
88 | break;
89 | }
90 | }
91 |
92 | res := model.Response{Header:model.Header{Code:model.ServerSuccessCode, Description:model.ServerSuccessDesc}, Data:fileArray}
93 |
94 | // Generate Json
95 | jsonByte, err := json.Marshal(res)
96 | if err != nil {
97 | http.Error(w, err.Error(), http.StatusInternalServerError)
98 | return
99 | }
100 |
101 | // Write Response
102 | fmt.Fprint(w, string(jsonByte))
103 | return
104 | }
105 |
--------------------------------------------------------------------------------
/handle/fileDownload.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 JiaoHu. All rights reserved.
3 | * JiaoHu PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
4 | *
5 | * Created by tony on 15/12/17.
6 | */
7 | package handle
8 |
9 | import (
10 | "log"
11 | "github.com/disintegration/imaging"
12 | "os"
13 | "strings"
14 | "path"
15 | "strconv"
16 | "regexp"
17 | "net/http"
18 | "path/filepath"
19 | )
20 |
21 | // Handler File Download Request
22 | // Crop for Image if Query Parameter contains width or height
23 | func FileDownload(dir string, w http.ResponseWriter, r *http.Request) {
24 | var forTest bool
25 | urlPath := r.URL.Path
26 | if urlPath == "/" {
27 | urlPath = urlPath + "/index.html"
28 | // Redirect test dir
29 | dir, _ = filepath.Abs(".")
30 | dir = dir + "/"
31 | forTest = true
32 | } else if strings.HasPrefix(urlPath, "/test") {
33 | if strings.Compare(urlPath, "/test") == 0 {
34 | urlPath = urlPath + "/index.html"
35 | }
36 | if strings.Compare(urlPath, "/test/") == 0 {
37 | urlPath = urlPath + "index.html"
38 | }
39 | // Redirect test dir
40 | dir, _ = filepath.Abs(".")
41 | dir = dir + "/"
42 | forTest = true
43 | }
44 |
45 | fileAbsPath := path.Clean(dir + urlPath)
46 |
47 | if !forTest {
48 | r.Header.Del("If-Modified-Since")
49 | width, height := CheckThumbParameters(r.URL.Query().Get("width"), r.URL.Query().Get("height"))
50 | fileAbsPath = GenerateThumbIfNeed(fileAbsPath, width, height)
51 | }
52 |
53 | log.Printf("downloading file %s", path.Clean(fileAbsPath))
54 | http.ServeFile(w, r, fileAbsPath)
55 | }
56 |
57 | // Check If Query Parameter contains Thumb parameter
58 | // return width,height in Query Parameters, otherwise return -1,-1
59 | func CheckThumbParameters(width string, height string) (int, int) {
60 | matched, err := regexp.MatchString("^[1-9][0-9]*$", width)
61 | if (err == nil) && matched {
62 | matched, err := regexp.MatchString("^[1-9][0-9]*$", height)
63 | if (err == nil) && matched {
64 | iWidth, _ := strconv.Atoi(width)
65 | iHeight, _ := strconv.Atoi(height)
66 | return iWidth, iHeight
67 | }
68 | }
69 | return -1, -1
70 | }
71 |
72 | // Generate Thumbnail if not exist
73 | func GenerateThumbIfNeed(fileAbsPath string, width int, height int) string {
74 | if (width == -1) || (height == -1) {
75 | return fileAbsPath
76 | }
77 |
78 | extension := path.Ext(fileAbsPath)
79 | name := strings.TrimSuffix(fileAbsPath, extension)
80 | thumbNailPath := name + "_" + strconv.Itoa(width) + "_" + strconv.Itoa(height) + extension
81 | // Generate Thumbnail If not exist
82 | if _, err := os.Stat(thumbNailPath); os.IsNotExist(err) {
83 | // path/to/whatever exists
84 | log.Print("generate thumbnail:" + fileAbsPath)
85 | img, err := imaging.Open(fileAbsPath)
86 | if err == nil {
87 | dstImage := imaging.Thumbnail(img, width, height, imaging.CatmullRom)
88 |
89 | // save the combined image to file
90 | err := imaging.Save(dstImage, thumbNailPath)
91 | if err == nil {
92 | return thumbNailPath
93 | } else {
94 | log.Print("imaging.Save error:")
95 | }
96 | } else {
97 | log.Print("imaging.Open error:")
98 | }
99 | } else {
100 | log.Print("thubnail has already exist")
101 | return thumbNailPath
102 | }
103 | return fileAbsPath
104 | }
105 |
106 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/golang.org/x/image/tiff/consts.go:
--------------------------------------------------------------------------------
1 | // Copyright 2011 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package tiff
6 |
7 | // A tiff image file contains one or more images. The metadata
8 | // of each image is contained in an Image File Directory (IFD),
9 | // which contains entries of 12 bytes each and is described
10 | // on page 14-16 of the specification. An IFD entry consists of
11 | //
12 | // - a tag, which describes the signification of the entry,
13 | // - the data type and length of the entry,
14 | // - the data itself or a pointer to it if it is more than 4 bytes.
15 | //
16 | // The presence of a length means that each IFD is effectively an array.
17 |
18 | const (
19 | leHeader = "II\x2A\x00" // Header for little-endian files.
20 | beHeader = "MM\x00\x2A" // Header for big-endian files.
21 |
22 | ifdLen = 12 // Length of an IFD entry in bytes.
23 | )
24 |
25 | // Data types (p. 14-16 of the spec).
26 | const (
27 | dtByte = 1
28 | dtASCII = 2
29 | dtShort = 3
30 | dtLong = 4
31 | dtRational = 5
32 | )
33 |
34 | // The length of one instance of each data type in bytes.
35 | var lengths = [...]uint32{0, 1, 1, 2, 4, 8}
36 |
37 | // Tags (see p. 28-41 of the spec).
38 | const (
39 | tImageWidth = 256
40 | tImageLength = 257
41 | tBitsPerSample = 258
42 | tCompression = 259
43 | tPhotometricInterpretation = 262
44 |
45 | tStripOffsets = 273
46 | tSamplesPerPixel = 277
47 | tRowsPerStrip = 278
48 | tStripByteCounts = 279
49 |
50 | tTileWidth = 322
51 | tTileLength = 323
52 | tTileOffsets = 324
53 | tTileByteCounts = 325
54 |
55 | tXResolution = 282
56 | tYResolution = 283
57 | tResolutionUnit = 296
58 |
59 | tPredictor = 317
60 | tColorMap = 320
61 | tExtraSamples = 338
62 | tSampleFormat = 339
63 | )
64 |
65 | // Compression types (defined in various places in the spec and supplements).
66 | const (
67 | cNone = 1
68 | cCCITT = 2
69 | cG3 = 3 // Group 3 Fax.
70 | cG4 = 4 // Group 4 Fax.
71 | cLZW = 5
72 | cJPEGOld = 6 // Superseded by cJPEG.
73 | cJPEG = 7
74 | cDeflate = 8 // zlib compression.
75 | cPackBits = 32773
76 | cDeflateOld = 32946 // Superseded by cDeflate.
77 | )
78 |
79 | // Photometric interpretation values (see p. 37 of the spec).
80 | const (
81 | pWhiteIsZero = 0
82 | pBlackIsZero = 1
83 | pRGB = 2
84 | pPaletted = 3
85 | pTransMask = 4 // transparency mask
86 | pCMYK = 5
87 | pYCbCr = 6
88 | pCIELab = 8
89 | )
90 |
91 | // Values for the tPredictor tag (page 64-65 of the spec).
92 | const (
93 | prNone = 1
94 | prHorizontal = 2
95 | )
96 |
97 | // Values for the tResolutionUnit tag (page 18).
98 | const (
99 | resNone = 1
100 | resPerInch = 2 // Dots per inch.
101 | resPerCM = 3 // Dots per centimeter.
102 | )
103 |
104 | // imageMode represents the mode of the image.
105 | type imageMode int
106 |
107 | const (
108 | mBilevel imageMode = iota
109 | mPaletted
110 | mGray
111 | mGrayInvert
112 | mRGB
113 | mRGBA
114 | mNRGBA
115 | )
116 |
117 | // CompressionType describes the type of compression used in Options.
118 | type CompressionType int
119 |
120 | const (
121 | Uncompressed CompressionType = iota
122 | Deflate
123 | )
124 |
125 | // specValue returns the compression type constant from the TIFF spec that
126 | // is equivalent to c.
127 | func (c CompressionType) specValue() uint32 {
128 | switch c {
129 | case Deflate:
130 | return cDeflate
131 | }
132 | return cNone
133 | }
134 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/golang.org/x/image/bmp/writer.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package bmp
6 |
7 | import (
8 | "encoding/binary"
9 | "errors"
10 | "image"
11 | "io"
12 | )
13 |
14 | type header struct {
15 | sigBM [2]byte
16 | fileSize uint32
17 | resverved [2]uint16
18 | pixOffset uint32
19 | dibHeaderSize uint32
20 | width uint32
21 | height uint32
22 | colorPlane uint16
23 | bpp uint16
24 | compression uint32
25 | imageSize uint32
26 | xPixelsPerMeter uint32
27 | yPixelsPerMeter uint32
28 | colorUse uint32
29 | colorImportant uint32
30 | }
31 |
32 | func encodePaletted(w io.Writer, pix []uint8, dx, dy, stride, step int) error {
33 | var padding []byte
34 | if dx < step {
35 | padding = make([]byte, step-dx)
36 | }
37 | for y := dy - 1; y >= 0; y-- {
38 | min := y*stride + 0
39 | max := y*stride + dx
40 | if _, err := w.Write(pix[min:max]); err != nil {
41 | return err
42 | }
43 | if padding != nil {
44 | if _, err := w.Write(padding); err != nil {
45 | return err
46 | }
47 | }
48 | }
49 | return nil
50 | }
51 |
52 | func encodeRGBA(w io.Writer, pix []uint8, dx, dy, stride, step int) error {
53 | buf := make([]byte, step)
54 | for y := dy - 1; y >= 0; y-- {
55 | min := y*stride + 0
56 | max := y*stride + dx*4
57 | off := 0
58 | for i := min; i < max; i += 4 {
59 | buf[off+2] = pix[i+0]
60 | buf[off+1] = pix[i+1]
61 | buf[off+0] = pix[i+2]
62 | off += 3
63 | }
64 | if _, err := w.Write(buf); err != nil {
65 | return err
66 | }
67 | }
68 | return nil
69 | }
70 |
71 | func encode(w io.Writer, m image.Image, step int) error {
72 | b := m.Bounds()
73 | buf := make([]byte, step)
74 | for y := b.Max.Y - 1; y >= b.Min.Y; y-- {
75 | off := 0
76 | for x := b.Min.X; x < b.Max.X; x++ {
77 | r, g, b, _ := m.At(x, y).RGBA()
78 | buf[off+2] = byte(r >> 8)
79 | buf[off+1] = byte(g >> 8)
80 | buf[off+0] = byte(b >> 8)
81 | off += 3
82 | }
83 | if _, err := w.Write(buf); err != nil {
84 | return err
85 | }
86 | }
87 | return nil
88 | }
89 |
90 | // Encode writes the image m to w in BMP format.
91 | func Encode(w io.Writer, m image.Image) error {
92 | d := m.Bounds().Size()
93 | if d.X < 0 || d.Y < 0 {
94 | return errors.New("bmp: negative bounds")
95 | }
96 | h := &header{
97 | sigBM: [2]byte{'B', 'M'},
98 | fileSize: 14 + 40,
99 | pixOffset: 14 + 40,
100 | dibHeaderSize: 40,
101 | width: uint32(d.X),
102 | height: uint32(d.Y),
103 | colorPlane: 1,
104 | }
105 |
106 | var step int
107 | var palette []byte
108 | switch m := m.(type) {
109 | case *image.Gray:
110 | step = (d.X + 3) &^ 3
111 | palette = make([]byte, 1024)
112 | for i := 0; i < 256; i++ {
113 | palette[i*4+0] = uint8(i)
114 | palette[i*4+1] = uint8(i)
115 | palette[i*4+2] = uint8(i)
116 | palette[i*4+3] = 0xFF
117 | }
118 | h.imageSize = uint32(d.Y * step)
119 | h.fileSize += uint32(len(palette)) + h.imageSize
120 | h.pixOffset += uint32(len(palette))
121 | h.bpp = 8
122 |
123 | case *image.Paletted:
124 | step = (d.X + 3) &^ 3
125 | palette = make([]byte, 1024)
126 | for i := 0; i < len(m.Palette) && i < 256; i++ {
127 | r, g, b, _ := m.Palette[i].RGBA()
128 | palette[i*4+0] = uint8(b >> 8)
129 | palette[i*4+1] = uint8(g >> 8)
130 | palette[i*4+2] = uint8(r >> 8)
131 | palette[i*4+3] = 0xFF
132 | }
133 | h.imageSize = uint32(d.Y * step)
134 | h.fileSize += uint32(len(palette)) + h.imageSize
135 | h.pixOffset += uint32(len(palette))
136 | h.bpp = 8
137 | default:
138 | step = (3*d.X + 3) &^ 3
139 | h.imageSize = uint32(d.Y * step)
140 | h.fileSize += h.imageSize
141 | h.bpp = 24
142 | }
143 |
144 | if err := binary.Write(w, binary.LittleEndian, h); err != nil {
145 | return err
146 | }
147 | if palette != nil {
148 | if err := binary.Write(w, binary.LittleEndian, palette); err != nil {
149 | return err
150 | }
151 | }
152 |
153 | if d.X == 0 || d.Y == 0 {
154 | return nil
155 | }
156 |
157 | switch m := m.(type) {
158 | case *image.Gray:
159 | return encodePaletted(w, m.Pix, d.X, d.Y, m.Stride, step)
160 | case *image.Paletted:
161 | return encodePaletted(w, m.Pix, d.X, d.Y, m.Stride, step)
162 | case *image.RGBA:
163 | return encodeRGBA(w, m.Pix, d.X, d.Y, m.Stride, step)
164 | }
165 | return encode(w, m, step)
166 | }
167 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/disintegration/imaging/effects.go:
--------------------------------------------------------------------------------
1 | package imaging
2 |
3 | import (
4 | "image"
5 | "math"
6 | )
7 |
8 | func gaussianBlurKernel(x, sigma float64) float64 {
9 | return math.Exp(-(x*x)/(2*sigma*sigma)) / (sigma * math.Sqrt(2*math.Pi))
10 | }
11 |
12 | // Blur produces a blurred version of the image using a Gaussian function.
13 | // Sigma parameter must be positive and indicates how much the image will be blurred.
14 | //
15 | // Usage example:
16 | //
17 | // dstImage := imaging.Blur(srcImage, 3.5)
18 | //
19 | func Blur(img image.Image, sigma float64) *image.NRGBA {
20 | if sigma <= 0 {
21 | // sigma parameter must be positive!
22 | return Clone(img)
23 | }
24 |
25 | src := toNRGBA(img)
26 | radius := int(math.Ceil(sigma * 3.0))
27 | kernel := make([]float64, radius+1)
28 |
29 | for i := 0; i <= radius; i++ {
30 | kernel[i] = gaussianBlurKernel(float64(i), sigma)
31 | }
32 |
33 | var dst *image.NRGBA
34 | dst = blurHorizontal(src, kernel)
35 | dst = blurVertical(dst, kernel)
36 |
37 | return dst
38 | }
39 |
40 | func blurHorizontal(src *image.NRGBA, kernel []float64) *image.NRGBA {
41 | radius := len(kernel) - 1
42 | width := src.Bounds().Max.X
43 | height := src.Bounds().Max.Y
44 |
45 | dst := image.NewNRGBA(image.Rect(0, 0, width, height))
46 |
47 | parallel(width, func(partStart, partEnd int) {
48 | for x := partStart; x < partEnd; x++ {
49 | start := x - radius
50 | if start < 0 {
51 | start = 0
52 | }
53 |
54 | end := x + radius
55 | if end > width-1 {
56 | end = width - 1
57 | }
58 |
59 | weightSum := 0.0
60 | for ix := start; ix <= end; ix++ {
61 | weightSum += kernel[absint(x-ix)]
62 | }
63 |
64 | for y := 0; y < height; y++ {
65 |
66 | r, g, b, a := 0.0, 0.0, 0.0, 0.0
67 | for ix := start; ix <= end; ix++ {
68 | weight := kernel[absint(x-ix)]
69 | i := y*src.Stride + ix*4
70 | r += float64(src.Pix[i+0]) * weight
71 | g += float64(src.Pix[i+1]) * weight
72 | b += float64(src.Pix[i+2]) * weight
73 | a += float64(src.Pix[i+3]) * weight
74 | }
75 |
76 | r = math.Min(math.Max(r/weightSum, 0.0), 255.0)
77 | g = math.Min(math.Max(g/weightSum, 0.0), 255.0)
78 | b = math.Min(math.Max(b/weightSum, 0.0), 255.0)
79 | a = math.Min(math.Max(a/weightSum, 0.0), 255.0)
80 |
81 | j := y*dst.Stride + x*4
82 | dst.Pix[j+0] = uint8(r + 0.5)
83 | dst.Pix[j+1] = uint8(g + 0.5)
84 | dst.Pix[j+2] = uint8(b + 0.5)
85 | dst.Pix[j+3] = uint8(a + 0.5)
86 |
87 | }
88 | }
89 | })
90 |
91 | return dst
92 | }
93 |
94 | func blurVertical(src *image.NRGBA, kernel []float64) *image.NRGBA {
95 | radius := len(kernel) - 1
96 | width := src.Bounds().Max.X
97 | height := src.Bounds().Max.Y
98 |
99 | dst := image.NewNRGBA(image.Rect(0, 0, width, height))
100 |
101 | parallel(height, func(partStart, partEnd int) {
102 | for y := partStart; y < partEnd; y++ {
103 | start := y - radius
104 | if start < 0 {
105 | start = 0
106 | }
107 |
108 | end := y + radius
109 | if end > height-1 {
110 | end = height - 1
111 | }
112 |
113 | weightSum := 0.0
114 | for iy := start; iy <= end; iy++ {
115 | weightSum += kernel[absint(y-iy)]
116 | }
117 |
118 | for x := 0; x < width; x++ {
119 |
120 | r, g, b, a := 0.0, 0.0, 0.0, 0.0
121 | for iy := start; iy <= end; iy++ {
122 | weight := kernel[absint(y-iy)]
123 | i := iy*src.Stride + x*4
124 | r += float64(src.Pix[i+0]) * weight
125 | g += float64(src.Pix[i+1]) * weight
126 | b += float64(src.Pix[i+2]) * weight
127 | a += float64(src.Pix[i+3]) * weight
128 | }
129 |
130 | r = math.Min(math.Max(r/weightSum, 0.0), 255.0)
131 | g = math.Min(math.Max(g/weightSum, 0.0), 255.0)
132 | b = math.Min(math.Max(b/weightSum, 0.0), 255.0)
133 | a = math.Min(math.Max(a/weightSum, 0.0), 255.0)
134 |
135 | j := y*dst.Stride + x*4
136 | dst.Pix[j+0] = uint8(r + 0.5)
137 | dst.Pix[j+1] = uint8(g + 0.5)
138 | dst.Pix[j+2] = uint8(b + 0.5)
139 | dst.Pix[j+3] = uint8(a + 0.5)
140 |
141 | }
142 | }
143 | })
144 |
145 | return dst
146 | }
147 |
148 | // Sharpen produces a sharpened version of the image.
149 | // Sigma parameter must be positive and indicates how much the image will be sharpened.
150 | //
151 | // Usage example:
152 | //
153 | // dstImage := imaging.Sharpen(srcImage, 3.5)
154 | //
155 | func Sharpen(img image.Image, sigma float64) *image.NRGBA {
156 | if sigma <= 0 {
157 | // sigma parameter must be positive!
158 | return Clone(img)
159 | }
160 |
161 | src := toNRGBA(img)
162 | blurred := Blur(img, sigma)
163 |
164 | width := src.Bounds().Max.X
165 | height := src.Bounds().Max.Y
166 | dst := image.NewNRGBA(image.Rect(0, 0, width, height))
167 |
168 | parallel(height, func(partStart, partEnd int) {
169 | for y := partStart; y < partEnd; y++ {
170 | for x := 0; x < width; x++ {
171 | i := y*src.Stride + x*4
172 | for j := 0; j < 4; j++ {
173 | k := i + j
174 | val := int(src.Pix[k]) + (int(src.Pix[k]) - int(blurred.Pix[k]))
175 | if val < 0 {
176 | val = 0
177 | } else if val > 255 {
178 | val = 255
179 | }
180 | dst.Pix[k] = uint8(val)
181 | }
182 | }
183 | }
184 | })
185 |
186 | return dst
187 | }
188 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/disintegration/imaging/transform.go:
--------------------------------------------------------------------------------
1 | package imaging
2 |
3 | import (
4 | "image"
5 | )
6 |
7 | // Rotate90 rotates the image 90 degrees counterclockwise and returns the transformed image.
8 | func Rotate90(img image.Image) *image.NRGBA {
9 | src := toNRGBA(img)
10 | srcW := src.Bounds().Max.X
11 | srcH := src.Bounds().Max.Y
12 | dstW := srcH
13 | dstH := srcW
14 | dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
15 |
16 | parallel(dstH, func(partStart, partEnd int) {
17 |
18 | for dstY := partStart; dstY < partEnd; dstY++ {
19 | for dstX := 0; dstX < dstW; dstX++ {
20 | srcX := dstH - dstY - 1
21 | srcY := dstX
22 |
23 | srcOff := srcY*src.Stride + srcX*4
24 | dstOff := dstY*dst.Stride + dstX*4
25 |
26 | copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
27 | }
28 | }
29 |
30 | })
31 |
32 | return dst
33 | }
34 |
35 | // Rotate180 rotates the image 180 degrees counterclockwise and returns the transformed image.
36 | func Rotate180(img image.Image) *image.NRGBA {
37 | src := toNRGBA(img)
38 | srcW := src.Bounds().Max.X
39 | srcH := src.Bounds().Max.Y
40 | dstW := srcW
41 | dstH := srcH
42 | dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
43 |
44 | parallel(dstH, func(partStart, partEnd int) {
45 |
46 | for dstY := partStart; dstY < partEnd; dstY++ {
47 | for dstX := 0; dstX < dstW; dstX++ {
48 | srcX := dstW - dstX - 1
49 | srcY := dstH - dstY - 1
50 |
51 | srcOff := srcY*src.Stride + srcX*4
52 | dstOff := dstY*dst.Stride + dstX*4
53 |
54 | copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
55 | }
56 | }
57 |
58 | })
59 |
60 | return dst
61 | }
62 |
63 | // Rotate270 rotates the image 270 degrees counterclockwise and returns the transformed image.
64 | func Rotate270(img image.Image) *image.NRGBA {
65 | src := toNRGBA(img)
66 | srcW := src.Bounds().Max.X
67 | srcH := src.Bounds().Max.Y
68 | dstW := srcH
69 | dstH := srcW
70 | dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
71 |
72 | parallel(dstH, func(partStart, partEnd int) {
73 |
74 | for dstY := partStart; dstY < partEnd; dstY++ {
75 | for dstX := 0; dstX < dstW; dstX++ {
76 | srcX := dstY
77 | srcY := dstW - dstX - 1
78 |
79 | srcOff := srcY*src.Stride + srcX*4
80 | dstOff := dstY*dst.Stride + dstX*4
81 |
82 | copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
83 | }
84 | }
85 |
86 | })
87 |
88 | return dst
89 | }
90 |
91 | // FlipH flips the image horizontally (from left to right) and returns the transformed image.
92 | func FlipH(img image.Image) *image.NRGBA {
93 | src := toNRGBA(img)
94 | srcW := src.Bounds().Max.X
95 | srcH := src.Bounds().Max.Y
96 | dstW := srcW
97 | dstH := srcH
98 | dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
99 |
100 | parallel(dstH, func(partStart, partEnd int) {
101 |
102 | for dstY := partStart; dstY < partEnd; dstY++ {
103 | for dstX := 0; dstX < dstW; dstX++ {
104 | srcX := dstW - dstX - 1
105 | srcY := dstY
106 |
107 | srcOff := srcY*src.Stride + srcX*4
108 | dstOff := dstY*dst.Stride + dstX*4
109 |
110 | copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
111 | }
112 | }
113 |
114 | })
115 |
116 | return dst
117 | }
118 |
119 | // FlipV flips the image vertically (from top to bottom) and returns the transformed image.
120 | func FlipV(img image.Image) *image.NRGBA {
121 | src := toNRGBA(img)
122 | srcW := src.Bounds().Max.X
123 | srcH := src.Bounds().Max.Y
124 | dstW := srcW
125 | dstH := srcH
126 | dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
127 |
128 | parallel(dstH, func(partStart, partEnd int) {
129 |
130 | for dstY := partStart; dstY < partEnd; dstY++ {
131 | for dstX := 0; dstX < dstW; dstX++ {
132 | srcX := dstX
133 | srcY := dstH - dstY - 1
134 |
135 | srcOff := srcY*src.Stride + srcX*4
136 | dstOff := dstY*dst.Stride + dstX*4
137 |
138 | copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
139 | }
140 | }
141 |
142 | })
143 |
144 | return dst
145 | }
146 |
147 | // Transpose flips the image horizontally and rotates 90 degrees counter-clockwise.
148 | func Transpose(img image.Image) *image.NRGBA {
149 | src := toNRGBA(img)
150 | srcW := src.Bounds().Max.X
151 | srcH := src.Bounds().Max.Y
152 | dstW := srcH
153 | dstH := srcW
154 | dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
155 |
156 | parallel(dstH, func(partStart, partEnd int) {
157 |
158 | for dstY := partStart; dstY < partEnd; dstY++ {
159 | for dstX := 0; dstX < dstW; dstX++ {
160 | srcX := dstY
161 | srcY := dstX
162 |
163 | srcOff := srcY*src.Stride + srcX*4
164 | dstOff := dstY*dst.Stride + dstX*4
165 |
166 | copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
167 | }
168 | }
169 |
170 | })
171 |
172 | return dst
173 | }
174 |
175 | // Transverse flips the image vertically and rotates 90 degrees counter-clockwise.
176 | func Transverse(img image.Image) *image.NRGBA {
177 | src := toNRGBA(img)
178 | srcW := src.Bounds().Max.X
179 | srcH := src.Bounds().Max.Y
180 | dstW := srcH
181 | dstH := srcW
182 | dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
183 |
184 | parallel(dstH, func(partStart, partEnd int) {
185 |
186 | for dstY := partStart; dstY < partEnd; dstY++ {
187 | for dstX := 0; dstX < dstW; dstX++ {
188 | srcX := dstH - dstY - 1
189 | srcY := dstW - dstX - 1
190 |
191 | srcOff := srcY*src.Stride + srcX*4
192 | dstOff := dstY*dst.Stride + dstX*4
193 |
194 | copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
195 | }
196 | }
197 |
198 | })
199 |
200 | return dst
201 | }
202 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/disintegration/imaging/tools.go:
--------------------------------------------------------------------------------
1 | package imaging
2 |
3 | import (
4 | "image"
5 | "math"
6 | )
7 |
8 | // Anchor is the anchor point for image alignment.
9 | type Anchor int
10 |
11 | const (
12 | Center Anchor = iota
13 | TopLeft
14 | Top
15 | TopRight
16 | Left
17 | Right
18 | BottomLeft
19 | Bottom
20 | BottomRight
21 | )
22 |
23 | func anchorPt(b image.Rectangle, w, h int, anchor Anchor) image.Point {
24 | var x, y int
25 | switch anchor {
26 | case TopLeft:
27 | x = b.Min.X
28 | y = b.Min.Y
29 | case Top:
30 | x = b.Min.X + (b.Dx()-w)/2
31 | y = b.Min.Y
32 | case TopRight:
33 | x = b.Max.X - w
34 | y = b.Min.Y
35 | case Left:
36 | x = b.Min.X
37 | y = b.Min.Y + (b.Dy()-h)/2
38 | case Right:
39 | x = b.Max.X - w
40 | y = b.Min.Y + (b.Dy()-h)/2
41 | case BottomLeft:
42 | x = b.Min.X
43 | y = b.Max.Y - h
44 | case Bottom:
45 | x = b.Min.X + (b.Dx()-w)/2
46 | y = b.Max.Y - h
47 | case BottomRight:
48 | x = b.Max.X - w
49 | y = b.Max.Y - h
50 | default:
51 | x = b.Min.X + (b.Dx()-w)/2
52 | y = b.Min.Y + (b.Dy()-h)/2
53 | }
54 | return image.Pt(x, y)
55 | }
56 |
57 | // Crop cuts out a rectangular region with the specified bounds
58 | // from the image and returns the cropped image.
59 | func Crop(img image.Image, rect image.Rectangle) *image.NRGBA {
60 | src := toNRGBA(img)
61 | srcRect := rect.Sub(img.Bounds().Min)
62 | sub := src.SubImage(srcRect)
63 | return Clone(sub) // New image Bounds().Min point will be (0, 0)
64 | }
65 |
66 | // CropAnchor cuts out a rectangular region with the specified size
67 | // from the image using the specified anchor point and returns the cropped image.
68 | func CropAnchor(img image.Image, width, height int, anchor Anchor) *image.NRGBA {
69 | srcBounds := img.Bounds()
70 | pt := anchorPt(srcBounds, width, height, anchor)
71 | r := image.Rect(0, 0, width, height).Add(pt)
72 | b := srcBounds.Intersect(r)
73 | return Crop(img, b)
74 | }
75 |
76 | // CropCenter cuts out a rectangular region with the specified size
77 | // from the center of the image and returns the cropped image.
78 | func CropCenter(img image.Image, width, height int) *image.NRGBA {
79 | return CropAnchor(img, width, height, Center)
80 | }
81 |
82 | // Paste pastes the img image to the background image at the specified position and returns the combined image.
83 | func Paste(background, img image.Image, pos image.Point) *image.NRGBA {
84 | src := toNRGBA(img)
85 | dst := Clone(background) // cloned image bounds start at (0, 0)
86 | startPt := pos.Sub(background.Bounds().Min) // so we should translate start point
87 | endPt := startPt.Add(src.Bounds().Size())
88 | pasteBounds := image.Rectangle{startPt, endPt}
89 |
90 | if dst.Bounds().Overlaps(pasteBounds) {
91 | intersectBounds := dst.Bounds().Intersect(pasteBounds)
92 |
93 | rowSize := intersectBounds.Dx() * 4
94 | numRows := intersectBounds.Dy()
95 |
96 | srcStartX := intersectBounds.Min.X - pasteBounds.Min.X
97 | srcStartY := intersectBounds.Min.Y - pasteBounds.Min.Y
98 |
99 | i0 := dst.PixOffset(intersectBounds.Min.X, intersectBounds.Min.Y)
100 | j0 := src.PixOffset(srcStartX, srcStartY)
101 |
102 | di := dst.Stride
103 | dj := src.Stride
104 |
105 | for row := 0; row < numRows; row++ {
106 | copy(dst.Pix[i0:i0+rowSize], src.Pix[j0:j0+rowSize])
107 | i0 += di
108 | j0 += dj
109 | }
110 | }
111 |
112 | return dst
113 | }
114 |
115 | // PasteCenter pastes the img image to the center of the background image and returns the combined image.
116 | func PasteCenter(background, img image.Image) *image.NRGBA {
117 | bgBounds := background.Bounds()
118 | bgW := bgBounds.Dx()
119 | bgH := bgBounds.Dy()
120 | bgMinX := bgBounds.Min.X
121 | bgMinY := bgBounds.Min.Y
122 |
123 | centerX := bgMinX + bgW/2
124 | centerY := bgMinY + bgH/2
125 |
126 | x0 := centerX - img.Bounds().Dx()/2
127 | y0 := centerY - img.Bounds().Dy()/2
128 |
129 | return Paste(background, img, image.Pt(x0, y0))
130 | }
131 |
132 | // Overlay draws the img image over the background image at given position
133 | // and returns the combined image. Opacity parameter is the opacity of the img
134 | // image layer, used to compose the images, it must be from 0.0 to 1.0.
135 | //
136 | // Usage examples:
137 | //
138 | // // draw the sprite over the background at position (50, 50)
139 | // dstImage := imaging.Overlay(backgroundImage, spriteImage, image.Pt(50, 50), 1.0)
140 | //
141 | // // blend two opaque images of the same size
142 | // dstImage := imaging.Overlay(imageOne, imageTwo, image.Pt(0, 0), 0.5)
143 | //
144 | func Overlay(background, img image.Image, pos image.Point, opacity float64) *image.NRGBA {
145 | opacity = math.Min(math.Max(opacity, 0.0), 1.0) // check: 0.0 <= opacity <= 1.0
146 |
147 | src := toNRGBA(img)
148 | dst := Clone(background) // cloned image bounds start at (0, 0)
149 | startPt := pos.Sub(background.Bounds().Min) // so we should translate start point
150 | endPt := startPt.Add(src.Bounds().Size())
151 | pasteBounds := image.Rectangle{startPt, endPt}
152 |
153 | if dst.Bounds().Overlaps(pasteBounds) {
154 | intersectBounds := dst.Bounds().Intersect(pasteBounds)
155 |
156 | for y := intersectBounds.Min.Y; y < intersectBounds.Max.Y; y++ {
157 | for x := intersectBounds.Min.X; x < intersectBounds.Max.X; x++ {
158 | i := y*dst.Stride + x*4
159 |
160 | srcX := x - pasteBounds.Min.X
161 | srcY := y - pasteBounds.Min.Y
162 | j := srcY*src.Stride + srcX*4
163 |
164 | a1 := float64(dst.Pix[i+3])
165 | a2 := float64(src.Pix[j+3])
166 |
167 | coef2 := opacity * a2 / 255.0
168 | coef1 := (1 - coef2) * a1 / 255.0
169 | coefSum := coef1 + coef2
170 | coef1 /= coefSum
171 | coef2 /= coefSum
172 |
173 | dst.Pix[i+0] = uint8(float64(dst.Pix[i+0])*coef1 + float64(src.Pix[j+0])*coef2)
174 | dst.Pix[i+1] = uint8(float64(dst.Pix[i+1])*coef1 + float64(src.Pix[j+1])*coef2)
175 | dst.Pix[i+2] = uint8(float64(dst.Pix[i+2])*coef1 + float64(src.Pix[j+2])*coef2)
176 | dst.Pix[i+3] = uint8(math.Min(a1+a2*opacity*(255.0-a1)/255.0, 255.0))
177 | }
178 | }
179 | }
180 |
181 | return dst
182 | }
183 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/disintegration/imaging/adjust.go:
--------------------------------------------------------------------------------
1 | package imaging
2 |
3 | import (
4 | "image"
5 | "image/color"
6 | "math"
7 | )
8 |
9 | // AdjustFunc applies the fn function to each pixel of the img image and returns the adjusted image.
10 | //
11 | // Example:
12 | //
13 | // dstImage = imaging.AdjustFunc(
14 | // srcImage,
15 | // func(c color.NRGBA) color.NRGBA {
16 | // // shift the red channel by 16
17 | // r := int(c.R) + 16
18 | // if r > 255 {
19 | // r = 255
20 | // }
21 | // return color.NRGBA{uint8(r), c.G, c.B, c.A}
22 | // }
23 | // )
24 | //
25 | func AdjustFunc(img image.Image, fn func(c color.NRGBA) color.NRGBA) *image.NRGBA {
26 | src := toNRGBA(img)
27 | width := src.Bounds().Max.X
28 | height := src.Bounds().Max.Y
29 | dst := image.NewNRGBA(image.Rect(0, 0, width, height))
30 |
31 | parallel(height, func(partStart, partEnd int) {
32 | for y := partStart; y < partEnd; y++ {
33 | for x := 0; x < width; x++ {
34 | i := y*src.Stride + x*4
35 | j := y*dst.Stride + x*4
36 |
37 | r := src.Pix[i+0]
38 | g := src.Pix[i+1]
39 | b := src.Pix[i+2]
40 | a := src.Pix[i+3]
41 |
42 | c := fn(color.NRGBA{r, g, b, a})
43 |
44 | dst.Pix[j+0] = c.R
45 | dst.Pix[j+1] = c.G
46 | dst.Pix[j+2] = c.B
47 | dst.Pix[j+3] = c.A
48 | }
49 | }
50 | })
51 |
52 | return dst
53 | }
54 |
55 | // AdjustGamma performs a gamma correction on the image and returns the adjusted image.
56 | // Gamma parameter must be positive. Gamma = 1.0 gives the original image.
57 | // Gamma less than 1.0 darkens the image and gamma greater than 1.0 lightens it.
58 | //
59 | // Example:
60 | //
61 | // dstImage = imaging.AdjustGamma(srcImage, 0.7)
62 | //
63 | func AdjustGamma(img image.Image, gamma float64) *image.NRGBA {
64 | e := 1.0 / math.Max(gamma, 0.0001)
65 | lut := make([]uint8, 256)
66 |
67 | for i := 0; i < 256; i++ {
68 | lut[i] = clamp(math.Pow(float64(i)/255.0, e) * 255.0)
69 | }
70 |
71 | fn := func(c color.NRGBA) color.NRGBA {
72 | return color.NRGBA{lut[c.R], lut[c.G], lut[c.B], c.A}
73 | }
74 |
75 | return AdjustFunc(img, fn)
76 | }
77 |
78 | func sigmoid(a, b, x float64) float64 {
79 | return 1 / (1 + math.Exp(b*(a-x)))
80 | }
81 |
82 | // AdjustSigmoid changes the contrast of the image using a sigmoidal function and returns the adjusted image.
83 | // It's a non-linear contrast change useful for photo adjustments as it preserves highlight and shadow detail.
84 | // The midpoint parameter is the midpoint of contrast that must be between 0 and 1, typically 0.5.
85 | // The factor parameter indicates how much to increase or decrease the contrast, typically in range (-10, 10).
86 | // If the factor parameter is positive the image contrast is increased otherwise the contrast is decreased.
87 | //
88 | // Examples:
89 | //
90 | // dstImage = imaging.AdjustSigmoid(srcImage, 0.5, 3.0) // increase the contrast
91 | // dstImage = imaging.AdjustSigmoid(srcImage, 0.5, -3.0) // decrease the contrast
92 | //
93 | func AdjustSigmoid(img image.Image, midpoint, factor float64) *image.NRGBA {
94 | if factor == 0 {
95 | return Clone(img)
96 | }
97 |
98 | lut := make([]uint8, 256)
99 | a := math.Min(math.Max(midpoint, 0.0), 1.0)
100 | b := math.Abs(factor)
101 | sig0 := sigmoid(a, b, 0)
102 | sig1 := sigmoid(a, b, 1)
103 | e := 1.0e-6
104 |
105 | if factor > 0 {
106 | for i := 0; i < 256; i++ {
107 | x := float64(i) / 255.0
108 | sigX := sigmoid(a, b, x)
109 | f := (sigX - sig0) / (sig1 - sig0)
110 | lut[i] = clamp(f * 255.0)
111 | }
112 | } else {
113 | for i := 0; i < 256; i++ {
114 | x := float64(i) / 255.0
115 | arg := math.Min(math.Max((sig1-sig0)*x+sig0, e), 1.0-e)
116 | f := a - math.Log(1.0/arg-1.0)/b
117 | lut[i] = clamp(f * 255.0)
118 | }
119 | }
120 |
121 | fn := func(c color.NRGBA) color.NRGBA {
122 | return color.NRGBA{lut[c.R], lut[c.G], lut[c.B], c.A}
123 | }
124 |
125 | return AdjustFunc(img, fn)
126 | }
127 |
128 | // AdjustContrast changes the contrast of the image using the percentage parameter and returns the adjusted image.
129 | // The percentage must be in range (-100, 100). The percentage = 0 gives the original image.
130 | // The percentage = -100 gives solid grey image.
131 | //
132 | // Examples:
133 | //
134 | // dstImage = imaging.AdjustContrast(srcImage, -10) // decrease image contrast by 10%
135 | // dstImage = imaging.AdjustContrast(srcImage, 20) // increase image contrast by 20%
136 | //
137 | func AdjustContrast(img image.Image, percentage float64) *image.NRGBA {
138 | percentage = math.Min(math.Max(percentage, -100.0), 100.0)
139 | lut := make([]uint8, 256)
140 |
141 | v := (100.0 + percentage) / 100.0
142 | for i := 0; i < 256; i++ {
143 | if 0 <= v && v <= 1 {
144 | lut[i] = clamp((0.5 + (float64(i)/255.0-0.5)*v) * 255.0)
145 | } else if 1 < v && v < 2 {
146 | lut[i] = clamp((0.5 + (float64(i)/255.0-0.5)*(1/(2.0-v))) * 255.0)
147 | } else {
148 | lut[i] = uint8(float64(i)/255.0+0.5) * 255
149 | }
150 | }
151 |
152 | fn := func(c color.NRGBA) color.NRGBA {
153 | return color.NRGBA{lut[c.R], lut[c.G], lut[c.B], c.A}
154 | }
155 |
156 | return AdjustFunc(img, fn)
157 | }
158 |
159 | // AdjustBrightness changes the brightness of the image using the percentage parameter and returns the adjusted image.
160 | // The percentage must be in range (-100, 100). The percentage = 0 gives the original image.
161 | // The percentage = -100 gives solid black image. The percentage = 100 gives solid white image.
162 | //
163 | // Examples:
164 | //
165 | // dstImage = imaging.AdjustBrightness(srcImage, -15) // decrease image brightness by 15%
166 | // dstImage = imaging.AdjustBrightness(srcImage, 10) // increase image brightness by 10%
167 | //
168 | func AdjustBrightness(img image.Image, percentage float64) *image.NRGBA {
169 | percentage = math.Min(math.Max(percentage, -100.0), 100.0)
170 | lut := make([]uint8, 256)
171 |
172 | shift := 255.0 * percentage / 100.0
173 | for i := 0; i < 256; i++ {
174 | lut[i] = clamp(float64(i) + shift)
175 | }
176 |
177 | fn := func(c color.NRGBA) color.NRGBA {
178 | return color.NRGBA{lut[c.R], lut[c.G], lut[c.B], c.A}
179 | }
180 |
181 | return AdjustFunc(img, fn)
182 | }
183 |
184 | // Grayscale produces grayscale version of the image.
185 | func Grayscale(img image.Image) *image.NRGBA {
186 | fn := func(c color.NRGBA) color.NRGBA {
187 | f := 0.299*float64(c.R) + 0.587*float64(c.G) + 0.114*float64(c.B)
188 | y := uint8(f + 0.5)
189 | return color.NRGBA{y, y, y, c.A}
190 | }
191 | return AdjustFunc(img, fn)
192 | }
193 |
194 | // Invert produces inverted (negated) version of the image.
195 | func Invert(img image.Image) *image.NRGBA {
196 | fn := func(c color.NRGBA) color.NRGBA {
197 | return color.NRGBA{255 - c.R, 255 - c.G, 255 - c.B, c.A}
198 | }
199 | return AdjustFunc(img, fn)
200 | }
201 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/golang.org/x/image/bmp/reader.go:
--------------------------------------------------------------------------------
1 | // Copyright 2011 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // Package bmp implements a BMP image decoder and encoder.
6 | //
7 | // The BMP specification is at http://www.digicamsoft.com/bmp/bmp.html.
8 | package bmp
9 |
10 | import (
11 | "errors"
12 | "image"
13 | "image/color"
14 | "io"
15 | )
16 |
17 | // ErrUnsupported means that the input BMP image uses a valid but unsupported
18 | // feature.
19 | var ErrUnsupported = errors.New("bmp: unsupported BMP image")
20 |
21 | func readUint16(b []byte) uint16 {
22 | return uint16(b[0]) | uint16(b[1])<<8
23 | }
24 |
25 | func readUint32(b []byte) uint32 {
26 | return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
27 | }
28 |
29 | // decodePaletted reads an 8 bit-per-pixel BMP image from r.
30 | // If topDown is false, the image rows will be read bottom-up.
31 | func decodePaletted(r io.Reader, c image.Config, topDown bool) (image.Image, error) {
32 | paletted := image.NewPaletted(image.Rect(0, 0, c.Width, c.Height), c.ColorModel.(color.Palette))
33 | if c.Width == 0 || c.Height == 0 {
34 | return paletted, nil
35 | }
36 | var tmp [4]byte
37 | y0, y1, yDelta := c.Height-1, -1, -1
38 | if topDown {
39 | y0, y1, yDelta = 0, c.Height, +1
40 | }
41 | for y := y0; y != y1; y += yDelta {
42 | p := paletted.Pix[y*paletted.Stride : y*paletted.Stride+c.Width]
43 | if _, err := io.ReadFull(r, p); err != nil {
44 | return nil, err
45 | }
46 | // Each row is 4-byte aligned.
47 | if c.Width%4 != 0 {
48 | _, err := io.ReadFull(r, tmp[:4-c.Width%4])
49 | if err != nil {
50 | return nil, err
51 | }
52 | }
53 | }
54 | return paletted, nil
55 | }
56 |
57 | // decodeRGB reads a 24 bit-per-pixel BMP image from r.
58 | // If topDown is false, the image rows will be read bottom-up.
59 | func decodeRGB(r io.Reader, c image.Config, topDown bool) (image.Image, error) {
60 | rgba := image.NewRGBA(image.Rect(0, 0, c.Width, c.Height))
61 | if c.Width == 0 || c.Height == 0 {
62 | return rgba, nil
63 | }
64 | // There are 3 bytes per pixel, and each row is 4-byte aligned.
65 | b := make([]byte, (3*c.Width+3)&^3)
66 | y0, y1, yDelta := c.Height-1, -1, -1
67 | if topDown {
68 | y0, y1, yDelta = 0, c.Height, +1
69 | }
70 | for y := y0; y != y1; y += yDelta {
71 | if _, err := io.ReadFull(r, b); err != nil {
72 | return nil, err
73 | }
74 | p := rgba.Pix[y*rgba.Stride : y*rgba.Stride+c.Width*4]
75 | for i, j := 0, 0; i < len(p); i, j = i+4, j+3 {
76 | // BMP images are stored in BGR order rather than RGB order.
77 | p[i+0] = b[j+2]
78 | p[i+1] = b[j+1]
79 | p[i+2] = b[j+0]
80 | p[i+3] = 0xFF
81 | }
82 | }
83 | return rgba, nil
84 | }
85 |
86 | // decodeNRGBA reads a 32 bit-per-pixel BMP image from r.
87 | // If topDown is false, the image rows will be read bottom-up.
88 | func decodeNRGBA(r io.Reader, c image.Config, topDown bool) (image.Image, error) {
89 | rgba := image.NewNRGBA(image.Rect(0, 0, c.Width, c.Height))
90 | if c.Width == 0 || c.Height == 0 {
91 | return rgba, nil
92 | }
93 | y0, y1, yDelta := c.Height-1, -1, -1
94 | if topDown {
95 | y0, y1, yDelta = 0, c.Height, +1
96 | }
97 | for y := y0; y != y1; y += yDelta {
98 | p := rgba.Pix[y*rgba.Stride : y*rgba.Stride+c.Width*4]
99 | if _, err := io.ReadFull(r, p); err != nil {
100 | return nil, err
101 | }
102 | for i := 0; i < len(p); i += 4 {
103 | // BMP images are stored in BGRA order rather than RGBA order.
104 | p[i+0], p[i+2] = p[i+2], p[i+0]
105 | }
106 | }
107 | return rgba, nil
108 | }
109 |
110 | // Decode reads a BMP image from r and returns it as an image.Image.
111 | // Limitation: The file must be 8, 24 or 32 bits per pixel.
112 | func Decode(r io.Reader) (image.Image, error) {
113 | c, bpp, topDown, err := decodeConfig(r)
114 | if err != nil {
115 | return nil, err
116 | }
117 | switch bpp {
118 | case 8:
119 | return decodePaletted(r, c, topDown)
120 | case 24:
121 | return decodeRGB(r, c, topDown)
122 | case 32:
123 | return decodeNRGBA(r, c, topDown)
124 | }
125 | panic("unreachable")
126 | }
127 |
128 | // DecodeConfig returns the color model and dimensions of a BMP image without
129 | // decoding the entire image.
130 | // Limitation: The file must be 8, 24 or 32 bits per pixel.
131 | func DecodeConfig(r io.Reader) (image.Config, error) {
132 | config, _, _, err := decodeConfig(r)
133 | return config, err
134 | }
135 |
136 | func decodeConfig(r io.Reader) (config image.Config, bitsPerPixel int, topDown bool, err error) {
137 | // We only support those BMP images that are a BITMAPFILEHEADER
138 | // immediately followed by a BITMAPINFOHEADER.
139 | const (
140 | fileHeaderLen = 14
141 | infoHeaderLen = 40
142 | )
143 | var b [1024]byte
144 | if _, err := io.ReadFull(r, b[:fileHeaderLen+infoHeaderLen]); err != nil {
145 | return image.Config{}, 0, false, err
146 | }
147 | if string(b[:2]) != "BM" {
148 | return image.Config{}, 0, false, errors.New("bmp: invalid format")
149 | }
150 | offset := readUint32(b[10:14])
151 | if readUint32(b[14:18]) != infoHeaderLen {
152 | return image.Config{}, 0, false, ErrUnsupported
153 | }
154 | width := int(int32(readUint32(b[18:22])))
155 | height := int(int32(readUint32(b[22:26])))
156 | if height < 0 {
157 | height, topDown = -height, true
158 | }
159 | if width < 0 || height < 0 {
160 | return image.Config{}, 0, false, ErrUnsupported
161 | }
162 | // We only support 1 plane, 8 or 24 bits per pixel and no compression.
163 | planes, bpp, compression := readUint16(b[26:28]), readUint16(b[28:30]), readUint32(b[30:34])
164 | if planes != 1 || compression != 0 {
165 | return image.Config{}, 0, false, ErrUnsupported
166 | }
167 | switch bpp {
168 | case 8:
169 | if offset != fileHeaderLen+infoHeaderLen+256*4 {
170 | return image.Config{}, 0, false, ErrUnsupported
171 | }
172 | _, err = io.ReadFull(r, b[:256*4])
173 | if err != nil {
174 | return image.Config{}, 0, false, err
175 | }
176 | pcm := make(color.Palette, 256)
177 | for i := range pcm {
178 | // BMP images are stored in BGR order rather than RGB order.
179 | // Every 4th byte is padding.
180 | pcm[i] = color.RGBA{b[4*i+2], b[4*i+1], b[4*i+0], 0xFF}
181 | }
182 | return image.Config{ColorModel: pcm, Width: width, Height: height}, 8, topDown, nil
183 | case 24:
184 | if offset != fileHeaderLen+infoHeaderLen {
185 | return image.Config{}, 0, false, ErrUnsupported
186 | }
187 | return image.Config{ColorModel: color.RGBAModel, Width: width, Height: height}, 24, topDown, nil
188 | case 32:
189 | if offset != fileHeaderLen+infoHeaderLen {
190 | return image.Config{}, 0, false, ErrUnsupported
191 | }
192 | return image.Config{ColorModel: color.RGBAModel, Width: width, Height: height}, 32, topDown, nil
193 | }
194 | return image.Config{}, 0, false, ErrUnsupported
195 | }
196 |
197 | func init() {
198 | image.RegisterFormat("bmp", "BM????\x00\x00\x00\x00", Decode, DecodeConfig)
199 | }
200 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/disintegration/imaging/README.md:
--------------------------------------------------------------------------------
1 | # Imaging
2 |
3 | Package imaging provides basic image manipulation functions (resize, rotate, flip, crop, etc.).
4 | This package is based on the standard Go image package and works best along with it.
5 |
6 | Image manipulation functions provided by the package take any image type
7 | that implements `image.Image` interface as an input, and return a new image of
8 | `*image.NRGBA` type (32bit RGBA colors, not premultiplied by alpha).
9 |
10 | ## Installation
11 |
12 | Imaging requires Go version 1.2 or greater.
13 |
14 | go get -u github.com/disintegration/imaging
15 |
16 | ## Documentation
17 |
18 | http://godoc.org/github.com/disintegration/imaging
19 |
20 | ## Usage examples
21 |
22 | A few usage examples can be found below. See the documentation for the full list of supported functions.
23 |
24 | ### Image resizing
25 | ```go
26 | // resize srcImage to size = 128x128px using the Lanczos filter
27 | dstImage128 := imaging.Resize(srcImage, 128, 128, imaging.Lanczos)
28 |
29 | // resize srcImage to width = 800px preserving the aspect ratio
30 | dstImage800 := imaging.Resize(srcImage, 800, 0, imaging.Lanczos)
31 |
32 | // scale down srcImage to fit the 800x600px bounding box
33 | dstImageFit := imaging.Fit(srcImage, 800, 600, imaging.Lanczos)
34 |
35 | // resize and crop the srcImage to fill the 100x100px area
36 | dstImageFill := imaging.Fill(srcImage, 100, 100, imaging.Center, imaging.Lanczos)
37 | ```
38 |
39 | Imaging supports image resizing using various resampling filters. The most notable ones:
40 | - `NearestNeighbor` - Fastest resampling filter, no antialiasing.
41 | - `Box` - Simple and fast averaging filter appropriate for downscaling. When upscaling it's similar to NearestNeighbor.
42 | - `Linear` - Bilinear filter, smooth and reasonably fast.
43 | - `MitchellNetravali` - А smooth bicubic filter.
44 | - `CatmullRom` - A sharp bicubic filter.
45 | - `Gaussian` - Blurring filter that uses gaussian function, useful for noise removal.
46 | - `Lanczos` - High-quality resampling filter for photographic images yielding sharp results, but it's slower than cubic filters.
47 |
48 | The full list of supported filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali, CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine. Custom filters can be created using ResampleFilter struct.
49 |
50 | **Resampling filters comparison**
51 |
52 | Original image. Will be resized from 512x512px to 128x128px.
53 |
54 | 
55 |
56 | Filter | Resize result
57 | ---|---
58 | `imaging.NearestNeighbor` | 
59 | `imaging.Box` | 
60 | `imaging.Linear` | 
61 | `imaging.MitchellNetravali` | 
62 | `imaging.CatmullRom` | 
63 | `imaging.Gaussian` | 
64 | `imaging.Lanczos` | 
65 |
66 | **Resize functions comparison**
67 |
68 | Original image:
69 |
70 | 
71 |
72 | Resize the image to width=100px and height=100px:
73 |
74 | ```go
75 | dstImage := imaging.Resize(srcImage, 100, 100, imaging.Lanczos)
76 | ```
77 | 
78 |
79 | Resize the image to width=100px preserving the aspect ratio:
80 |
81 | ```go
82 | dstImage := imaging.Resize(srcImage, 100, 0, imaging.Lanczos)
83 | ```
84 | 
85 |
86 | Resize the image to fit the 100x100px boundng box preserving the aspect ratio:
87 |
88 | ```go
89 | dstImage := imaging.Fit(srcImage, 100, 100, imaging.Lanczos)
90 | ```
91 | 
92 |
93 | Resize and crop the image with a center anchor point to fill the 100x100px area:
94 |
95 | ```go
96 | dstImage := imaging.Fill(srcImage, 100, 100, imaging.Center, imaging.Lanczos)
97 | ```
98 | 
99 |
100 | ### Gaussian Blur
101 | ```go
102 | dstImage := imaging.Blur(srcImage, 0.5)
103 | ```
104 |
105 | Sigma parameter allows to control the strength of the blurring effect.
106 |
107 | Original image | Sigma = 0.5 | Sigma = 1.5
108 | ---|---|---
109 |  |  | 
110 |
111 | ### Sharpening
112 | ```go
113 | dstImage := imaging.Sharpen(srcImage, 0.5)
114 | ```
115 |
116 | Uses gaussian function internally. Sigma parameter allows to control the strength of the sharpening effect.
117 |
118 | Original image | Sigma = 0.5 | Sigma = 1.5
119 | ---|---|---
120 |  |  | 
121 |
122 | ### Gamma correction
123 | ```go
124 | dstImage := imaging.AdjustGamma(srcImage, 0.75)
125 | ```
126 |
127 | Original image | Gamma = 0.75 | Gamma = 1.25
128 | ---|---|---
129 |  |  | 
130 |
131 | ### Contrast adjustment
132 | ```go
133 | dstImage := imaging.AdjustContrast(srcImage, 20)
134 | ```
135 |
136 | Original image | Contrast = 20 | Contrast = -20
137 | ---|---|---
138 |  |  | 
139 |
140 | ### Brightness adjustment
141 | ```go
142 | dstImage := imaging.AdjustBrightness(srcImage, 20)
143 | ```
144 |
145 | Original image | Brightness = 20 | Brightness = -20
146 | ---|---|---
147 |  |  | 
148 |
149 |
150 | ### Complete code example
151 | Here is the code example that loads several images, makes thumbnails of them
152 | and combines them together side-by-side.
153 |
154 | ```go
155 | package main
156 |
157 | import (
158 | "image"
159 | "image/color"
160 |
161 | "github.com/disintegration/imaging"
162 | )
163 |
164 | func main() {
165 |
166 | // input files
167 | files := []string{"01.jpg", "02.jpg", "03.jpg"}
168 |
169 | // load images and make 100x100 thumbnails of them
170 | var thumbnails []image.Image
171 | for _, file := range files {
172 | img, err := imaging.Open(file)
173 | if err != nil {
174 | panic(err)
175 | }
176 | thumb := imaging.Thumbnail(img, 100, 100, imaging.CatmullRom)
177 | thumbnails = append(thumbnails, thumb)
178 | }
179 |
180 | // create a new blank image
181 | dst := imaging.New(100*len(thumbnails), 100, color.NRGBA{0, 0, 0, 0})
182 |
183 | // paste thumbnails into the new image side by side
184 | for i, thumb := range thumbnails {
185 | dst = imaging.Paste(dst, thumb, image.Pt(i*100, 0))
186 | }
187 |
188 | // save the combined image to file
189 | err := imaging.Save(dst, "dst.jpg")
190 | if err != nil {
191 | panic(err)
192 | }
193 | }
194 | ```
195 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/golang.org/x/image/tiff/lzw/reader.go:
--------------------------------------------------------------------------------
1 | // Copyright 2011 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // Package lzw implements the Lempel-Ziv-Welch compressed data format,
6 | // described in T. A. Welch, ``A Technique for High-Performance Data
7 | // Compression'', Computer, 17(6) (June 1984), pp 8-19.
8 | //
9 | // In particular, it implements LZW as used by the TIFF file format, including
10 | // an "off by one" algorithmic difference when compared to standard LZW.
11 | package lzw
12 |
13 | /*
14 | This file was branched from src/pkg/compress/lzw/reader.go in the
15 | standard library. Differences from the original are marked with "NOTE".
16 |
17 | The tif_lzw.c file in the libtiff C library has this comment:
18 |
19 | ----
20 | The 5.0 spec describes a different algorithm than Aldus
21 | implements. Specifically, Aldus does code length transitions
22 | one code earlier than should be done (for real LZW).
23 | Earlier versions of this library implemented the correct
24 | LZW algorithm, but emitted codes in a bit order opposite
25 | to the TIFF spec. Thus, to maintain compatibility w/ Aldus
26 | we interpret MSB-LSB ordered codes to be images written w/
27 | old versions of this library, but otherwise adhere to the
28 | Aldus "off by one" algorithm.
29 | ----
30 |
31 | The Go code doesn't read (invalid) TIFF files written by old versions of
32 | libtiff, but the LZW algorithm in this package still differs from the one in
33 | Go's standard package library to accomodate this "off by one" in valid TIFFs.
34 | */
35 |
36 | import (
37 | "bufio"
38 | "errors"
39 | "fmt"
40 | "io"
41 | )
42 |
43 | // Order specifies the bit ordering in an LZW data stream.
44 | type Order int
45 |
46 | const (
47 | // LSB means Least Significant Bits first, as used in the GIF file format.
48 | LSB Order = iota
49 | // MSB means Most Significant Bits first, as used in the TIFF and PDF
50 | // file formats.
51 | MSB
52 | )
53 |
54 | const (
55 | maxWidth = 12
56 | decoderInvalidCode = 0xffff
57 | flushBuffer = 1 << maxWidth
58 | )
59 |
60 | // decoder is the state from which the readXxx method converts a byte
61 | // stream into a code stream.
62 | type decoder struct {
63 | r io.ByteReader
64 | bits uint32
65 | nBits uint
66 | width uint
67 | read func(*decoder) (uint16, error) // readLSB or readMSB
68 | litWidth int // width in bits of literal codes
69 | err error
70 |
71 | // The first 1<= 1<>= d.width
111 | d.nBits -= d.width
112 | return code, nil
113 | }
114 |
115 | // readMSB returns the next code for "Most Significant Bits first" data.
116 | func (d *decoder) readMSB() (uint16, error) {
117 | for d.nBits < d.width {
118 | x, err := d.r.ReadByte()
119 | if err != nil {
120 | return 0, err
121 | }
122 | d.bits |= uint32(x) << (24 - d.nBits)
123 | d.nBits += 8
124 | }
125 | code := uint16(d.bits >> (32 - d.width))
126 | d.bits <<= d.width
127 | d.nBits -= d.width
128 | return code, nil
129 | }
130 |
131 | func (d *decoder) Read(b []byte) (int, error) {
132 | for {
133 | if len(d.toRead) > 0 {
134 | n := copy(b, d.toRead)
135 | d.toRead = d.toRead[n:]
136 | return n, nil
137 | }
138 | if d.err != nil {
139 | return 0, d.err
140 | }
141 | d.decode()
142 | }
143 | }
144 |
145 | // decode decompresses bytes from r and leaves them in d.toRead.
146 | // read specifies how to decode bytes into codes.
147 | // litWidth is the width in bits of literal codes.
148 | func (d *decoder) decode() {
149 | // Loop over the code stream, converting codes into decompressed bytes.
150 | for {
151 | code, err := d.read(d)
152 | if err != nil {
153 | if err == io.EOF {
154 | err = io.ErrUnexpectedEOF
155 | }
156 | d.err = err
157 | d.flush()
158 | return
159 | }
160 | switch {
161 | case code < d.clear:
162 | // We have a literal code.
163 | d.output[d.o] = uint8(code)
164 | d.o++
165 | if d.last != decoderInvalidCode {
166 | // Save what the hi code expands to.
167 | d.suffix[d.hi] = uint8(code)
168 | d.prefix[d.hi] = d.last
169 | }
170 | case code == d.clear:
171 | d.width = 1 + uint(d.litWidth)
172 | d.hi = d.eof
173 | d.overflow = 1 << d.width
174 | d.last = decoderInvalidCode
175 | continue
176 | case code == d.eof:
177 | d.flush()
178 | d.err = io.EOF
179 | return
180 | case code <= d.hi:
181 | c, i := code, len(d.output)-1
182 | if code == d.hi {
183 | // code == hi is a special case which expands to the last expansion
184 | // followed by the head of the last expansion. To find the head, we walk
185 | // the prefix chain until we find a literal code.
186 | c = d.last
187 | for c >= d.clear {
188 | c = d.prefix[c]
189 | }
190 | d.output[i] = uint8(c)
191 | i--
192 | c = d.last
193 | }
194 | // Copy the suffix chain into output and then write that to w.
195 | for c >= d.clear {
196 | d.output[i] = d.suffix[c]
197 | i--
198 | c = d.prefix[c]
199 | }
200 | d.output[i] = uint8(c)
201 | d.o += copy(d.output[d.o:], d.output[i:])
202 | if d.last != decoderInvalidCode {
203 | // Save what the hi code expands to.
204 | d.suffix[d.hi] = uint8(c)
205 | d.prefix[d.hi] = d.last
206 | }
207 | default:
208 | d.err = errors.New("lzw: invalid code")
209 | d.flush()
210 | return
211 | }
212 | d.last, d.hi = code, d.hi+1
213 | if d.hi+1 >= d.overflow { // NOTE: the "+1" is where TIFF's LZW differs from the standard algorithm.
214 | if d.width == maxWidth {
215 | d.last = decoderInvalidCode
216 | } else {
217 | d.width++
218 | d.overflow <<= 1
219 | }
220 | }
221 | if d.o >= flushBuffer {
222 | d.flush()
223 | return
224 | }
225 | }
226 | }
227 |
228 | func (d *decoder) flush() {
229 | d.toRead = d.output[:d.o]
230 | d.o = 0
231 | }
232 |
233 | var errClosed = errors.New("lzw: reader/writer is closed")
234 |
235 | func (d *decoder) Close() error {
236 | d.err = errClosed // in case any Reads come along
237 | return nil
238 | }
239 |
240 | // NewReader creates a new io.ReadCloser.
241 | // Reads from the returned io.ReadCloser read and decompress data from r.
242 | // If r does not also implement io.ByteReader,
243 | // the decompressor may read more data than necessary from r.
244 | // It is the caller's responsibility to call Close on the ReadCloser when
245 | // finished reading.
246 | // The number of bits to use for literal codes, litWidth, must be in the
247 | // range [2,8] and is typically 8. It must equal the litWidth
248 | // used during compression.
249 | func NewReader(r io.Reader, order Order, litWidth int) io.ReadCloser {
250 | d := new(decoder)
251 | switch order {
252 | case LSB:
253 | d.read = (*decoder).readLSB
254 | case MSB:
255 | d.read = (*decoder).readMSB
256 | default:
257 | d.err = errors.New("lzw: unknown order")
258 | return d
259 | }
260 | if litWidth < 2 || 8 < litWidth {
261 | d.err = fmt.Errorf("lzw: litWidth %d out of range", litWidth)
262 | return d
263 | }
264 | if br, ok := r.(io.ByteReader); ok {
265 | d.r = br
266 | } else {
267 | d.r = bufio.NewReader(r)
268 | }
269 | d.litWidth = litWidth
270 | d.width = 1 + uint(litWidth)
271 | d.clear = uint16(1) << uint(litWidth)
272 | d.eof, d.hi = d.clear+1, d.clear+1
273 | d.overflow = uint16(1) << d.width
274 | d.last = decoderInvalidCode
275 |
276 | return d
277 | }
278 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/disintegration/imaging/helpers.go:
--------------------------------------------------------------------------------
1 | /*
2 | Package imaging provides basic image manipulation functions (resize, rotate, flip, crop, etc.).
3 | This package is based on the standard Go image package and works best along with it.
4 |
5 | Image manipulation functions provided by the package take any image type
6 | that implements `image.Image` interface as an input, and return a new image of
7 | `*image.NRGBA` type (32bit RGBA colors, not premultiplied by alpha).
8 | */
9 | package imaging
10 |
11 | import (
12 | "errors"
13 | "image"
14 | "image/color"
15 | "image/gif"
16 | "image/jpeg"
17 | "image/png"
18 | "io"
19 | "os"
20 | "path/filepath"
21 | "strings"
22 |
23 | "golang.org/x/image/bmp"
24 | "golang.org/x/image/tiff"
25 | )
26 |
27 | type Format int
28 |
29 | const (
30 | JPEG Format = iota
31 | PNG
32 | GIF
33 | TIFF
34 | BMP
35 | )
36 |
37 | func (f Format) String() string {
38 | switch f {
39 | case JPEG:
40 | return "JPEG"
41 | case PNG:
42 | return "PNG"
43 | case GIF:
44 | return "GIF"
45 | case TIFF:
46 | return "TIFF"
47 | case BMP:
48 | return "BMP"
49 | default:
50 | return "Unsupported"
51 | }
52 | }
53 |
54 | var (
55 | ErrUnsupportedFormat = errors.New("imaging: unsupported image format")
56 | )
57 |
58 | // Decode reads an image from r.
59 | func Decode(r io.Reader) (image.Image, error) {
60 | img, _, err := image.Decode(r)
61 | if err != nil {
62 | return nil, err
63 | }
64 | return toNRGBA(img), nil
65 | }
66 |
67 | // Open loads an image from file
68 | func Open(filename string) (image.Image, error) {
69 | file, err := os.Open(filename)
70 | if err != nil {
71 | return nil, err
72 | }
73 | defer file.Close()
74 | img, err := Decode(file)
75 | return img, err
76 | }
77 |
78 | // Encode writes the image img to w in the specified format (JPEG, PNG, GIF, TIFF or BMP).
79 | func Encode(w io.Writer, img image.Image, format Format) error {
80 | var err error
81 | switch format {
82 | case JPEG:
83 | var rgba *image.RGBA
84 | if nrgba, ok := img.(*image.NRGBA); ok {
85 | if nrgba.Opaque() {
86 | rgba = &image.RGBA{
87 | Pix: nrgba.Pix,
88 | Stride: nrgba.Stride,
89 | Rect: nrgba.Rect,
90 | }
91 | }
92 | }
93 | if rgba != nil {
94 | err = jpeg.Encode(w, rgba, &jpeg.Options{Quality: 95})
95 | } else {
96 | err = jpeg.Encode(w, img, &jpeg.Options{Quality: 95})
97 | }
98 |
99 | case PNG:
100 | err = png.Encode(w, img)
101 | case GIF:
102 | err = gif.Encode(w, img, &gif.Options{NumColors: 256})
103 | case TIFF:
104 | err = tiff.Encode(w, img, &tiff.Options{Compression: tiff.Deflate, Predictor: true})
105 | case BMP:
106 | err = bmp.Encode(w, img)
107 | default:
108 | err = ErrUnsupportedFormat
109 | }
110 | return err
111 | }
112 |
113 | // Save saves the image to file with the specified filename.
114 | // The format is determined from the filename extension: "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported.
115 | func Save(img image.Image, filename string) (err error) {
116 | formats := map[string]Format{
117 | ".jpg": JPEG,
118 | ".jpeg": JPEG,
119 | ".png": PNG,
120 | ".tif": TIFF,
121 | ".tiff": TIFF,
122 | ".bmp": BMP,
123 | ".gif": GIF,
124 | }
125 |
126 | ext := strings.ToLower(filepath.Ext(filename))
127 | f, ok := formats[ext]
128 | if !ok {
129 | return ErrUnsupportedFormat
130 | }
131 |
132 | file, err := os.Create(filename)
133 | if err != nil {
134 | return err
135 | }
136 | defer file.Close()
137 |
138 | return Encode(file, img, f)
139 | }
140 |
141 | // New creates a new image with the specified width and height, and fills it with the specified color.
142 | func New(width, height int, fillColor color.Color) *image.NRGBA {
143 | if width <= 0 || height <= 0 {
144 | return &image.NRGBA{}
145 | }
146 |
147 | dst := image.NewNRGBA(image.Rect(0, 0, width, height))
148 | c := color.NRGBAModel.Convert(fillColor).(color.NRGBA)
149 |
150 | if c.R == 0 && c.G == 0 && c.B == 0 && c.A == 0 {
151 | return dst
152 | }
153 |
154 | cs := []uint8{c.R, c.G, c.B, c.A}
155 |
156 | // fill the first row
157 | for x := 0; x < width; x++ {
158 | copy(dst.Pix[x*4:(x+1)*4], cs)
159 | }
160 | // copy the first row to other rows
161 | for y := 1; y < height; y++ {
162 | copy(dst.Pix[y*dst.Stride:y*dst.Stride+width*4], dst.Pix[0:width*4])
163 | }
164 |
165 | return dst
166 | }
167 |
168 | // Clone returns a copy of the given image.
169 | func Clone(img image.Image) *image.NRGBA {
170 | srcBounds := img.Bounds()
171 | srcMinX := srcBounds.Min.X
172 | srcMinY := srcBounds.Min.Y
173 |
174 | dstBounds := srcBounds.Sub(srcBounds.Min)
175 | dstW := dstBounds.Dx()
176 | dstH := dstBounds.Dy()
177 | dst := image.NewNRGBA(dstBounds)
178 |
179 | switch src := img.(type) {
180 |
181 | case *image.NRGBA:
182 | rowSize := srcBounds.Dx() * 4
183 | parallel(dstH, func(partStart, partEnd int) {
184 | for dstY := partStart; dstY < partEnd; dstY++ {
185 | di := dst.PixOffset(0, dstY)
186 | si := src.PixOffset(srcMinX, srcMinY+dstY)
187 | copy(dst.Pix[di:di+rowSize], src.Pix[si:si+rowSize])
188 | }
189 | })
190 |
191 | case *image.NRGBA64:
192 | parallel(dstH, func(partStart, partEnd int) {
193 | for dstY := partStart; dstY < partEnd; dstY++ {
194 | di := dst.PixOffset(0, dstY)
195 | si := src.PixOffset(srcMinX, srcMinY+dstY)
196 | for dstX := 0; dstX < dstW; dstX++ {
197 |
198 | dst.Pix[di+0] = src.Pix[si+0]
199 | dst.Pix[di+1] = src.Pix[si+2]
200 | dst.Pix[di+2] = src.Pix[si+4]
201 | dst.Pix[di+3] = src.Pix[si+6]
202 |
203 | di += 4
204 | si += 8
205 |
206 | }
207 | }
208 | })
209 |
210 | case *image.RGBA:
211 | parallel(dstH, func(partStart, partEnd int) {
212 | for dstY := partStart; dstY < partEnd; dstY++ {
213 | di := dst.PixOffset(0, dstY)
214 | si := src.PixOffset(srcMinX, srcMinY+dstY)
215 | for dstX := 0; dstX < dstW; dstX++ {
216 |
217 | a := src.Pix[si+3]
218 | dst.Pix[di+3] = a
219 | switch a {
220 | case 0:
221 | dst.Pix[di+0] = 0
222 | dst.Pix[di+1] = 0
223 | dst.Pix[di+2] = 0
224 | case 0xff:
225 | dst.Pix[di+0] = src.Pix[si+0]
226 | dst.Pix[di+1] = src.Pix[si+1]
227 | dst.Pix[di+2] = src.Pix[si+2]
228 | default:
229 | var tmp uint16
230 | tmp = uint16(src.Pix[si+0]) * 0xff / uint16(a)
231 | dst.Pix[di+0] = uint8(tmp)
232 | tmp = uint16(src.Pix[si+1]) * 0xff / uint16(a)
233 | dst.Pix[di+1] = uint8(tmp)
234 | tmp = uint16(src.Pix[si+2]) * 0xff / uint16(a)
235 | dst.Pix[di+2] = uint8(tmp)
236 | }
237 |
238 | di += 4
239 | si += 4
240 |
241 | }
242 | }
243 | })
244 |
245 | case *image.RGBA64:
246 | parallel(dstH, func(partStart, partEnd int) {
247 | for dstY := partStart; dstY < partEnd; dstY++ {
248 | di := dst.PixOffset(0, dstY)
249 | si := src.PixOffset(srcMinX, srcMinY+dstY)
250 | for dstX := 0; dstX < dstW; dstX++ {
251 |
252 | a := src.Pix[si+6]
253 | dst.Pix[di+3] = a
254 | switch a {
255 | case 0:
256 | dst.Pix[di+0] = 0
257 | dst.Pix[di+1] = 0
258 | dst.Pix[di+2] = 0
259 | case 0xff:
260 | dst.Pix[di+0] = src.Pix[si+0]
261 | dst.Pix[di+1] = src.Pix[si+2]
262 | dst.Pix[di+2] = src.Pix[si+4]
263 | default:
264 | var tmp uint16
265 | tmp = uint16(src.Pix[si+0]) * 0xff / uint16(a)
266 | dst.Pix[di+0] = uint8(tmp)
267 | tmp = uint16(src.Pix[si+2]) * 0xff / uint16(a)
268 | dst.Pix[di+1] = uint8(tmp)
269 | tmp = uint16(src.Pix[si+4]) * 0xff / uint16(a)
270 | dst.Pix[di+2] = uint8(tmp)
271 | }
272 |
273 | di += 4
274 | si += 8
275 |
276 | }
277 | }
278 | })
279 |
280 | case *image.Gray:
281 | parallel(dstH, func(partStart, partEnd int) {
282 | for dstY := partStart; dstY < partEnd; dstY++ {
283 | di := dst.PixOffset(0, dstY)
284 | si := src.PixOffset(srcMinX, srcMinY+dstY)
285 | for dstX := 0; dstX < dstW; dstX++ {
286 |
287 | c := src.Pix[si]
288 | dst.Pix[di+0] = c
289 | dst.Pix[di+1] = c
290 | dst.Pix[di+2] = c
291 | dst.Pix[di+3] = 0xff
292 |
293 | di += 4
294 | si += 1
295 |
296 | }
297 | }
298 | })
299 |
300 | case *image.Gray16:
301 | parallel(dstH, func(partStart, partEnd int) {
302 | for dstY := partStart; dstY < partEnd; dstY++ {
303 | di := dst.PixOffset(0, dstY)
304 | si := src.PixOffset(srcMinX, srcMinY+dstY)
305 | for dstX := 0; dstX < dstW; dstX++ {
306 |
307 | c := src.Pix[si]
308 | dst.Pix[di+0] = c
309 | dst.Pix[di+1] = c
310 | dst.Pix[di+2] = c
311 | dst.Pix[di+3] = 0xff
312 |
313 | di += 4
314 | si += 2
315 |
316 | }
317 | }
318 | })
319 |
320 | case *image.YCbCr:
321 | parallel(dstH, func(partStart, partEnd int) {
322 | for dstY := partStart; dstY < partEnd; dstY++ {
323 | di := dst.PixOffset(0, dstY)
324 | for dstX := 0; dstX < dstW; dstX++ {
325 |
326 | srcX := srcMinX + dstX
327 | srcY := srcMinY + dstY
328 | siy := src.YOffset(srcX, srcY)
329 | sic := src.COffset(srcX, srcY)
330 | r, g, b := color.YCbCrToRGB(src.Y[siy], src.Cb[sic], src.Cr[sic])
331 | dst.Pix[di+0] = r
332 | dst.Pix[di+1] = g
333 | dst.Pix[di+2] = b
334 | dst.Pix[di+3] = 0xff
335 |
336 | di += 4
337 |
338 | }
339 | }
340 | })
341 |
342 | case *image.Paletted:
343 | plen := len(src.Palette)
344 | pnew := make([]color.NRGBA, plen)
345 | for i := 0; i < plen; i++ {
346 | pnew[i] = color.NRGBAModel.Convert(src.Palette[i]).(color.NRGBA)
347 | }
348 |
349 | parallel(dstH, func(partStart, partEnd int) {
350 | for dstY := partStart; dstY < partEnd; dstY++ {
351 | di := dst.PixOffset(0, dstY)
352 | si := src.PixOffset(srcMinX, srcMinY+dstY)
353 | for dstX := 0; dstX < dstW; dstX++ {
354 |
355 | c := pnew[src.Pix[si]]
356 | dst.Pix[di+0] = c.R
357 | dst.Pix[di+1] = c.G
358 | dst.Pix[di+2] = c.B
359 | dst.Pix[di+3] = c.A
360 |
361 | di += 4
362 | si += 1
363 |
364 | }
365 | }
366 | })
367 |
368 | default:
369 | parallel(dstH, func(partStart, partEnd int) {
370 | for dstY := partStart; dstY < partEnd; dstY++ {
371 | di := dst.PixOffset(0, dstY)
372 | for dstX := 0; dstX < dstW; dstX++ {
373 |
374 | c := color.NRGBAModel.Convert(img.At(srcMinX+dstX, srcMinY+dstY)).(color.NRGBA)
375 | dst.Pix[di+0] = c.R
376 | dst.Pix[di+1] = c.G
377 | dst.Pix[di+2] = c.B
378 | dst.Pix[di+3] = c.A
379 |
380 | di += 4
381 |
382 | }
383 | }
384 | })
385 |
386 | }
387 |
388 | return dst
389 | }
390 |
391 | // This function used internally to convert any image type to NRGBA if needed.
392 | func toNRGBA(img image.Image) *image.NRGBA {
393 | srcBounds := img.Bounds()
394 | if srcBounds.Min.X == 0 && srcBounds.Min.Y == 0 {
395 | if src0, ok := img.(*image.NRGBA); ok {
396 | return src0
397 | }
398 | }
399 | return Clone(img)
400 | }
401 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/satori/go.uuid/uuid.go:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013-2015 by Maxim Bublis
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining
4 | // a copy of this software and associated documentation files (the
5 | // "Software"), to deal in the Software without restriction, including
6 | // without limitation the rights to use, copy, modify, merge, publish,
7 | // distribute, sublicense, and/or sell copies of the Software, and to
8 | // permit persons to whom the Software is furnished to do so, subject to
9 | // the following conditions:
10 | //
11 | // The above copyright notice and this permission notice shall be
12 | // included in all copies or substantial portions of the Software.
13 | //
14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
22 | // Package uuid provides implementation of Universally Unique Identifier (UUID).
23 | // Supported versions are 1, 3, 4 and 5 (as specified in RFC 4122) and
24 | // version 2 (as specified in DCE 1.1).
25 | package uuid
26 |
27 | import (
28 | "bytes"
29 | "crypto/md5"
30 | "crypto/rand"
31 | "crypto/sha1"
32 | "database/sql/driver"
33 | "encoding/binary"
34 | "encoding/hex"
35 | "fmt"
36 | "hash"
37 | "net"
38 | "os"
39 | "sync"
40 | "time"
41 | )
42 |
43 | // UUID layout variants.
44 | const (
45 | VariantNCS = iota
46 | VariantRFC4122
47 | VariantMicrosoft
48 | VariantFuture
49 | )
50 |
51 | // UUID DCE domains.
52 | const (
53 | DomainPerson = iota
54 | DomainGroup
55 | DomainOrg
56 | )
57 |
58 | // Difference in 100-nanosecond intervals between
59 | // UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970).
60 | const epochStart = 122192928000000000
61 |
62 | // Used in string method conversion
63 | const dash byte = '-'
64 |
65 | // UUID v1/v2 storage.
66 | var (
67 | storageMutex sync.Mutex
68 | storageOnce sync.Once
69 | epochFunc = unixTimeFunc
70 | clockSequence uint16
71 | lastTime uint64
72 | hardwareAddr [6]byte
73 | posixUID = uint32(os.Getuid())
74 | posixGID = uint32(os.Getgid())
75 | )
76 |
77 | // String parse helpers.
78 | var (
79 | urnPrefix = []byte("urn:uuid:")
80 | byteGroups = []int{8, 4, 4, 4, 12}
81 | )
82 |
83 | func initClockSequence() {
84 | buf := make([]byte, 2)
85 | safeRandom(buf)
86 | clockSequence = binary.BigEndian.Uint16(buf)
87 | }
88 |
89 | func initHardwareAddr() {
90 | interfaces, err := net.Interfaces()
91 | if err == nil {
92 | for _, iface := range interfaces {
93 | if len(iface.HardwareAddr) >= 6 {
94 | copy(hardwareAddr[:], iface.HardwareAddr)
95 | return
96 | }
97 | }
98 | }
99 |
100 | // Initialize hardwareAddr randomly in case
101 | // of real network interfaces absence
102 | safeRandom(hardwareAddr[:])
103 |
104 | // Set multicast bit as recommended in RFC 4122
105 | hardwareAddr[0] |= 0x01
106 | }
107 |
108 | func initStorage() {
109 | initClockSequence()
110 | initHardwareAddr()
111 | }
112 |
113 | func safeRandom(dest []byte) {
114 | if _, err := rand.Read(dest); err != nil {
115 | panic(err)
116 | }
117 | }
118 |
119 | // Returns difference in 100-nanosecond intervals between
120 | // UUID epoch (October 15, 1582) and current time.
121 | // This is default epoch calculation function.
122 | func unixTimeFunc() uint64 {
123 | return epochStart + uint64(time.Now().UnixNano()/100)
124 | }
125 |
126 | // UUID representation compliant with specification
127 | // described in RFC 4122.
128 | type UUID [16]byte
129 |
130 | // The nil UUID is special form of UUID that is specified to have all
131 | // 128 bits set to zero.
132 | var Nil = UUID{}
133 |
134 | // Predefined namespace UUIDs.
135 | var (
136 | NamespaceDNS, _ = FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
137 | NamespaceURL, _ = FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8")
138 | NamespaceOID, _ = FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8")
139 | NamespaceX500, _ = FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
140 | )
141 |
142 | // And returns result of binary AND of two UUIDs.
143 | func And(u1 UUID, u2 UUID) UUID {
144 | u := UUID{}
145 | for i := 0; i < 16; i++ {
146 | u[i] = u1[i] & u2[i]
147 | }
148 | return u
149 | }
150 |
151 | // Or returns result of binary OR of two UUIDs.
152 | func Or(u1 UUID, u2 UUID) UUID {
153 | u := UUID{}
154 | for i := 0; i < 16; i++ {
155 | u[i] = u1[i] | u2[i]
156 | }
157 | return u
158 | }
159 |
160 | // Equal returns true if u1 and u2 equals, otherwise returns false.
161 | func Equal(u1 UUID, u2 UUID) bool {
162 | return bytes.Equal(u1[:], u2[:])
163 | }
164 |
165 | // Version returns algorithm version used to generate UUID.
166 | func (u UUID) Version() uint {
167 | return uint(u[6] >> 4)
168 | }
169 |
170 | // Variant returns UUID layout variant.
171 | func (u UUID) Variant() uint {
172 | switch {
173 | case (u[8] & 0x80) == 0x00:
174 | return VariantNCS
175 | case (u[8]&0xc0)|0x80 == 0x80:
176 | return VariantRFC4122
177 | case (u[8]&0xe0)|0xc0 == 0xc0:
178 | return VariantMicrosoft
179 | }
180 | return VariantFuture
181 | }
182 |
183 | // Bytes returns bytes slice representation of UUID.
184 | func (u UUID) Bytes() []byte {
185 | return u[:]
186 | }
187 |
188 | // Returns canonical string representation of UUID:
189 | // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
190 | func (u UUID) String() string {
191 | buf := make([]byte, 36)
192 |
193 | hex.Encode(buf[0:8], u[0:4])
194 | buf[8] = dash
195 | hex.Encode(buf[9:13], u[4:6])
196 | buf[13] = dash
197 | hex.Encode(buf[14:18], u[6:8])
198 | buf[18] = dash
199 | hex.Encode(buf[19:23], u[8:10])
200 | buf[23] = dash
201 | hex.Encode(buf[24:], u[10:])
202 |
203 | return string(buf)
204 | }
205 |
206 | // SetVersion sets version bits.
207 | func (u *UUID) SetVersion(v byte) {
208 | u[6] = (u[6] & 0x0f) | (v << 4)
209 | }
210 |
211 | // SetVariant sets variant bits as described in RFC 4122.
212 | func (u *UUID) SetVariant() {
213 | u[8] = (u[8] & 0xbf) | 0x80
214 | }
215 |
216 | // MarshalText implements the encoding.TextMarshaler interface.
217 | // The encoding is the same as returned by String.
218 | func (u UUID) MarshalText() (text []byte, err error) {
219 | text = []byte(u.String())
220 | return
221 | }
222 |
223 | // UnmarshalText implements the encoding.TextUnmarshaler interface.
224 | // Following formats are supported:
225 | // "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
226 | // "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}",
227 | // "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8"
228 | func (u *UUID) UnmarshalText(text []byte) (err error) {
229 | if len(text) < 32 {
230 | err = fmt.Errorf("uuid: invalid UUID string: %s", text)
231 | return
232 | }
233 |
234 | if bytes.Equal(text[:9], urnPrefix) {
235 | text = text[9:]
236 | } else if text[0] == '{' {
237 | text = text[1:]
238 | }
239 |
240 | b := u[:]
241 |
242 | for _, byteGroup := range byteGroups {
243 | if text[0] == '-' {
244 | text = text[1:]
245 | }
246 |
247 | _, err = hex.Decode(b[:byteGroup/2], text[:byteGroup])
248 |
249 | if err != nil {
250 | return
251 | }
252 |
253 | text = text[byteGroup:]
254 | b = b[byteGroup/2:]
255 | }
256 |
257 | return
258 | }
259 |
260 | // MarshalBinary implements the encoding.BinaryMarshaler interface.
261 | func (u UUID) MarshalBinary() (data []byte, err error) {
262 | data = u.Bytes()
263 | return
264 | }
265 |
266 | // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
267 | // It will return error if the slice isn't 16 bytes long.
268 | func (u *UUID) UnmarshalBinary(data []byte) (err error) {
269 | if len(data) != 16 {
270 | err = fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data))
271 | return
272 | }
273 | copy(u[:], data)
274 |
275 | return
276 | }
277 |
278 | // Value implements the driver.Valuer interface.
279 | func (u UUID) Value() (driver.Value, error) {
280 | return u.String(), nil
281 | }
282 |
283 | // Scan implements the sql.Scanner interface.
284 | // A 16-byte slice is handled by UnmarshalBinary, while
285 | // a longer byte slice or a string is handled by UnmarshalText.
286 | func (u *UUID) Scan(src interface{}) error {
287 | switch src := src.(type) {
288 | case []byte:
289 | if len(src) == 16 {
290 | return u.UnmarshalBinary(src)
291 | }
292 | return u.UnmarshalText(src)
293 |
294 | case string:
295 | return u.UnmarshalText([]byte(src))
296 | }
297 |
298 | return fmt.Errorf("uuid: cannot convert %T to UUID", src)
299 | }
300 |
301 | // FromBytes returns UUID converted from raw byte slice input.
302 | // It will return error if the slice isn't 16 bytes long.
303 | func FromBytes(input []byte) (u UUID, err error) {
304 | err = u.UnmarshalBinary(input)
305 | return
306 | }
307 |
308 | // FromBytesOrNil returns UUID converted from raw byte slice input.
309 | // Same behavior as FromBytes, but returns a Nil UUID on error.
310 | func FromBytesOrNil(input []byte) UUID {
311 | uuid, err := FromBytes(input)
312 | if err != nil {
313 | return Nil
314 | }
315 | return uuid
316 | }
317 |
318 | // FromString returns UUID parsed from string input.
319 | // Input is expected in a form accepted by UnmarshalText.
320 | func FromString(input string) (u UUID, err error) {
321 | err = u.UnmarshalText([]byte(input))
322 | return
323 | }
324 |
325 | // FromStringOrNil returns UUID parsed from string input.
326 | // Same behavior as FromString, but returns a Nil UUID on error.
327 | func FromStringOrNil(input string) UUID {
328 | uuid, err := FromString(input)
329 | if err != nil {
330 | return Nil
331 | }
332 | return uuid
333 | }
334 |
335 | // Returns UUID v1/v2 storage state.
336 | // Returns epoch timestamp, clock sequence, and hardware address.
337 | func getStorage() (uint64, uint16, []byte) {
338 | storageOnce.Do(initStorage)
339 |
340 | storageMutex.Lock()
341 | defer storageMutex.Unlock()
342 |
343 | timeNow := epochFunc()
344 | // Clock changed backwards since last UUID generation.
345 | // Should increase clock sequence.
346 | if timeNow <= lastTime {
347 | clockSequence++
348 | }
349 | lastTime = timeNow
350 |
351 | return timeNow, clockSequence, hardwareAddr[:]
352 | }
353 |
354 | // NewV1 returns UUID based on current timestamp and MAC address.
355 | func NewV1() UUID {
356 | u := UUID{}
357 |
358 | timeNow, clockSeq, hardwareAddr := getStorage()
359 |
360 | binary.BigEndian.PutUint32(u[0:], uint32(timeNow))
361 | binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32))
362 | binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48))
363 | binary.BigEndian.PutUint16(u[8:], clockSeq)
364 |
365 | copy(u[10:], hardwareAddr)
366 |
367 | u.SetVersion(1)
368 | u.SetVariant()
369 |
370 | return u
371 | }
372 |
373 | // NewV2 returns DCE Security UUID based on POSIX UID/GID.
374 | func NewV2(domain byte) UUID {
375 | u := UUID{}
376 |
377 | timeNow, clockSeq, hardwareAddr := getStorage()
378 |
379 | switch domain {
380 | case DomainPerson:
381 | binary.BigEndian.PutUint32(u[0:], posixUID)
382 | case DomainGroup:
383 | binary.BigEndian.PutUint32(u[0:], posixGID)
384 | }
385 |
386 | binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32))
387 | binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48))
388 | binary.BigEndian.PutUint16(u[8:], clockSeq)
389 | u[9] = domain
390 |
391 | copy(u[10:], hardwareAddr)
392 |
393 | u.SetVersion(2)
394 | u.SetVariant()
395 |
396 | return u
397 | }
398 |
399 | // NewV3 returns UUID based on MD5 hash of namespace UUID and name.
400 | func NewV3(ns UUID, name string) UUID {
401 | u := newFromHash(md5.New(), ns, name)
402 | u.SetVersion(3)
403 | u.SetVariant()
404 |
405 | return u
406 | }
407 |
408 | // NewV4 returns random generated UUID.
409 | func NewV4() UUID {
410 | u := UUID{}
411 | safeRandom(u[:])
412 | u.SetVersion(4)
413 | u.SetVariant()
414 |
415 | return u
416 | }
417 |
418 | // NewV5 returns UUID based on SHA-1 hash of namespace UUID and name.
419 | func NewV5(ns UUID, name string) UUID {
420 | u := newFromHash(sha1.New(), ns, name)
421 | u.SetVersion(5)
422 | u.SetVariant()
423 |
424 | return u
425 | }
426 |
427 | // Returns UUID based on hashing of namespace UUID and name.
428 | func newFromHash(h hash.Hash, ns UUID, name string) UUID {
429 | u := UUID{}
430 | h.Write(ns[:])
431 | h.Write([]byte(name))
432 | copy(u[:], h.Sum(nil))
433 |
434 | return u
435 | }
436 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/golang.org/x/image/tiff/writer.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package tiff
6 |
7 | import (
8 | "bytes"
9 | "compress/zlib"
10 | "encoding/binary"
11 | "image"
12 | "io"
13 | "sort"
14 | )
15 |
16 | // The TIFF format allows to choose the order of the different elements freely.
17 | // The basic structure of a TIFF file written by this package is:
18 | //
19 | // 1. Header (8 bytes).
20 | // 2. Image data.
21 | // 3. Image File Directory (IFD).
22 | // 4. "Pointer area" for larger entries in the IFD.
23 |
24 | // We only write little-endian TIFF files.
25 | var enc = binary.LittleEndian
26 |
27 | // An ifdEntry is a single entry in an Image File Directory.
28 | // A value of type dtRational is composed of two 32-bit values,
29 | // thus data contains two uints (numerator and denominator) for a single number.
30 | type ifdEntry struct {
31 | tag int
32 | datatype int
33 | data []uint32
34 | }
35 |
36 | func (e ifdEntry) putData(p []byte) {
37 | for _, d := range e.data {
38 | switch e.datatype {
39 | case dtByte, dtASCII:
40 | p[0] = byte(d)
41 | p = p[1:]
42 | case dtShort:
43 | enc.PutUint16(p, uint16(d))
44 | p = p[2:]
45 | case dtLong, dtRational:
46 | enc.PutUint32(p, uint32(d))
47 | p = p[4:]
48 | }
49 | }
50 | }
51 |
52 | type byTag []ifdEntry
53 |
54 | func (d byTag) Len() int { return len(d) }
55 | func (d byTag) Less(i, j int) bool { return d[i].tag < d[j].tag }
56 | func (d byTag) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
57 |
58 | func encodeGray(w io.Writer, pix []uint8, dx, dy, stride int, predictor bool) error {
59 | if !predictor {
60 | return writePix(w, pix, dy, dx, stride)
61 | }
62 | buf := make([]byte, dx)
63 | for y := 0; y < dy; y++ {
64 | min := y*stride + 0
65 | max := y*stride + dx
66 | off := 0
67 | var v0 uint8
68 | for i := min; i < max; i++ {
69 | v1 := pix[i]
70 | buf[off] = v1 - v0
71 | v0 = v1
72 | off++
73 | }
74 | if _, err := w.Write(buf); err != nil {
75 | return err
76 | }
77 | }
78 | return nil
79 | }
80 |
81 | func encodeGray16(w io.Writer, pix []uint8, dx, dy, stride int, predictor bool) error {
82 | buf := make([]byte, dx*2)
83 | for y := 0; y < dy; y++ {
84 | min := y*stride + 0
85 | max := y*stride + dx*2
86 | off := 0
87 | var v0 uint16
88 | for i := min; i < max; i += 2 {
89 | // An image.Gray16's Pix is in big-endian order.
90 | v1 := uint16(pix[i])<<8 | uint16(pix[i+1])
91 | if predictor {
92 | v0, v1 = v1, v1-v0
93 | }
94 | // We only write little-endian TIFF files.
95 | buf[off+0] = byte(v1)
96 | buf[off+1] = byte(v1 >> 8)
97 | off += 2
98 | }
99 | if _, err := w.Write(buf); err != nil {
100 | return err
101 | }
102 | }
103 | return nil
104 | }
105 |
106 | func encodeRGBA(w io.Writer, pix []uint8, dx, dy, stride int, predictor bool) error {
107 | if !predictor {
108 | return writePix(w, pix, dy, dx*4, stride)
109 | }
110 | buf := make([]byte, dx*4)
111 | for y := 0; y < dy; y++ {
112 | min := y*stride + 0
113 | max := y*stride + dx*4
114 | off := 0
115 | var r0, g0, b0, a0 uint8
116 | for i := min; i < max; i += 4 {
117 | r1, g1, b1, a1 := pix[i+0], pix[i+1], pix[i+2], pix[i+3]
118 | buf[off+0] = r1 - r0
119 | buf[off+1] = g1 - g0
120 | buf[off+2] = b1 - b0
121 | buf[off+3] = a1 - a0
122 | off += 4
123 | r0, g0, b0, a0 = r1, g1, b1, a1
124 | }
125 | if _, err := w.Write(buf); err != nil {
126 | return err
127 | }
128 | }
129 | return nil
130 | }
131 |
132 | func encodeRGBA64(w io.Writer, pix []uint8, dx, dy, stride int, predictor bool) error {
133 | buf := make([]byte, dx*8)
134 | for y := 0; y < dy; y++ {
135 | min := y*stride + 0
136 | max := y*stride + dx*8
137 | off := 0
138 | var r0, g0, b0, a0 uint16
139 | for i := min; i < max; i += 8 {
140 | // An image.RGBA64's Pix is in big-endian order.
141 | r1 := uint16(pix[i+0])<<8 | uint16(pix[i+1])
142 | g1 := uint16(pix[i+2])<<8 | uint16(pix[i+3])
143 | b1 := uint16(pix[i+4])<<8 | uint16(pix[i+5])
144 | a1 := uint16(pix[i+6])<<8 | uint16(pix[i+7])
145 | if predictor {
146 | r0, r1 = r1, r1-r0
147 | g0, g1 = g1, g1-g0
148 | b0, b1 = b1, b1-b0
149 | a0, a1 = a1, a1-a0
150 | }
151 | // We only write little-endian TIFF files.
152 | buf[off+0] = byte(r1)
153 | buf[off+1] = byte(r1 >> 8)
154 | buf[off+2] = byte(g1)
155 | buf[off+3] = byte(g1 >> 8)
156 | buf[off+4] = byte(b1)
157 | buf[off+5] = byte(b1 >> 8)
158 | buf[off+6] = byte(a1)
159 | buf[off+7] = byte(a1 >> 8)
160 | off += 8
161 | }
162 | if _, err := w.Write(buf); err != nil {
163 | return err
164 | }
165 | }
166 | return nil
167 | }
168 |
169 | func encode(w io.Writer, m image.Image, predictor bool) error {
170 | bounds := m.Bounds()
171 | buf := make([]byte, 4*bounds.Dx())
172 | for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
173 | off := 0
174 | if predictor {
175 | var r0, g0, b0, a0 uint8
176 | for x := bounds.Min.X; x < bounds.Max.X; x++ {
177 | r, g, b, a := m.At(x, y).RGBA()
178 | r1 := uint8(r >> 8)
179 | g1 := uint8(g >> 8)
180 | b1 := uint8(b >> 8)
181 | a1 := uint8(a >> 8)
182 | buf[off+0] = r1 - r0
183 | buf[off+1] = g1 - g0
184 | buf[off+2] = b1 - b0
185 | buf[off+3] = a1 - a0
186 | off += 4
187 | r0, g0, b0, a0 = r1, g1, b1, a1
188 | }
189 | } else {
190 | for x := bounds.Min.X; x < bounds.Max.X; x++ {
191 | r, g, b, a := m.At(x, y).RGBA()
192 | buf[off+0] = uint8(r >> 8)
193 | buf[off+1] = uint8(g >> 8)
194 | buf[off+2] = uint8(b >> 8)
195 | buf[off+3] = uint8(a >> 8)
196 | off += 4
197 | }
198 | }
199 | if _, err := w.Write(buf); err != nil {
200 | return err
201 | }
202 | }
203 | return nil
204 | }
205 |
206 | // writePix writes the internal byte array of an image to w. It is less general
207 | // but much faster then encode. writePix is used when pix directly
208 | // corresponds to one of the TIFF image types.
209 | func writePix(w io.Writer, pix []byte, nrows, length, stride int) error {
210 | if length == stride {
211 | _, err := w.Write(pix[:nrows*length])
212 | return err
213 | }
214 | for ; nrows > 0; nrows-- {
215 | if _, err := w.Write(pix[:length]); err != nil {
216 | return err
217 | }
218 | pix = pix[stride:]
219 | }
220 | return nil
221 | }
222 |
223 | func writeIFD(w io.Writer, ifdOffset int, d []ifdEntry) error {
224 | var buf [ifdLen]byte
225 | // Make space for "pointer area" containing IFD entry data
226 | // longer than 4 bytes.
227 | parea := make([]byte, 1024)
228 | pstart := ifdOffset + ifdLen*len(d) + 6
229 | var o int // Current offset in parea.
230 |
231 | // The IFD has to be written with the tags in ascending order.
232 | sort.Sort(byTag(d))
233 |
234 | // Write the number of entries in this IFD.
235 | if err := binary.Write(w, enc, uint16(len(d))); err != nil {
236 | return err
237 | }
238 | for _, ent := range d {
239 | enc.PutUint16(buf[0:2], uint16(ent.tag))
240 | enc.PutUint16(buf[2:4], uint16(ent.datatype))
241 | count := uint32(len(ent.data))
242 | if ent.datatype == dtRational {
243 | count /= 2
244 | }
245 | enc.PutUint32(buf[4:8], count)
246 | datalen := int(count * lengths[ent.datatype])
247 | if datalen <= 4 {
248 | ent.putData(buf[8:12])
249 | } else {
250 | if (o + datalen) > len(parea) {
251 | newlen := len(parea) + 1024
252 | for (o + datalen) > newlen {
253 | newlen += 1024
254 | }
255 | newarea := make([]byte, newlen)
256 | copy(newarea, parea)
257 | parea = newarea
258 | }
259 | ent.putData(parea[o : o+datalen])
260 | enc.PutUint32(buf[8:12], uint32(pstart+o))
261 | o += datalen
262 | }
263 | if _, err := w.Write(buf[:]); err != nil {
264 | return err
265 | }
266 | }
267 | // The IFD ends with the offset of the next IFD in the file,
268 | // or zero if it is the last one (page 14).
269 | if err := binary.Write(w, enc, uint32(0)); err != nil {
270 | return err
271 | }
272 | _, err := w.Write(parea[:o])
273 | return err
274 | }
275 |
276 | // Options are the encoding parameters.
277 | type Options struct {
278 | // Compression is the type of compression used.
279 | Compression CompressionType
280 | // Predictor determines whether a differencing predictor is used;
281 | // if true, instead of each pixel's color, the color difference to the
282 | // preceding one is saved. This improves the compression for certain
283 | // types of images and compressors. For example, it works well for
284 | // photos with Deflate compression.
285 | Predictor bool
286 | }
287 |
288 | // Encode writes the image m to w. opt determines the options used for
289 | // encoding, such as the compression type. If opt is nil, an uncompressed
290 | // image is written.
291 | func Encode(w io.Writer, m image.Image, opt *Options) error {
292 | d := m.Bounds().Size()
293 |
294 | compression := uint32(cNone)
295 | predictor := false
296 | if opt != nil {
297 | compression = opt.Compression.specValue()
298 | // The predictor field is only used with LZW. See page 64 of the spec.
299 | predictor = opt.Predictor && compression == cLZW
300 | }
301 |
302 | _, err := io.WriteString(w, leHeader)
303 | if err != nil {
304 | return err
305 | }
306 |
307 | // Compressed data is written into a buffer first, so that we
308 | // know the compressed size.
309 | var buf bytes.Buffer
310 | // dst holds the destination for the pixel data of the image --
311 | // either w or a writer to buf.
312 | var dst io.Writer
313 | // imageLen is the length of the pixel data in bytes.
314 | // The offset of the IFD is imageLen + 8 header bytes.
315 | var imageLen int
316 |
317 | switch compression {
318 | case cNone:
319 | dst = w
320 | // Write IFD offset before outputting pixel data.
321 | switch m.(type) {
322 | case *image.Paletted:
323 | imageLen = d.X * d.Y * 1
324 | case *image.Gray:
325 | imageLen = d.X * d.Y * 1
326 | case *image.Gray16:
327 | imageLen = d.X * d.Y * 2
328 | case *image.RGBA64:
329 | imageLen = d.X * d.Y * 8
330 | case *image.NRGBA64:
331 | imageLen = d.X * d.Y * 8
332 | default:
333 | imageLen = d.X * d.Y * 4
334 | }
335 | err = binary.Write(w, enc, uint32(imageLen+8))
336 | if err != nil {
337 | return err
338 | }
339 | case cDeflate:
340 | dst = zlib.NewWriter(&buf)
341 | }
342 |
343 | pr := uint32(prNone)
344 | photometricInterpretation := uint32(pRGB)
345 | samplesPerPixel := uint32(4)
346 | bitsPerSample := []uint32{8, 8, 8, 8}
347 | extraSamples := uint32(0)
348 | colorMap := []uint32{}
349 |
350 | if predictor {
351 | pr = prHorizontal
352 | }
353 | switch m := m.(type) {
354 | case *image.Paletted:
355 | photometricInterpretation = pPaletted
356 | samplesPerPixel = 1
357 | bitsPerSample = []uint32{8}
358 | colorMap = make([]uint32, 256*3)
359 | for i := 0; i < 256 && i < len(m.Palette); i++ {
360 | r, g, b, _ := m.Palette[i].RGBA()
361 | colorMap[i+0*256] = uint32(r)
362 | colorMap[i+1*256] = uint32(g)
363 | colorMap[i+2*256] = uint32(b)
364 | }
365 | err = encodeGray(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
366 | case *image.Gray:
367 | photometricInterpretation = pBlackIsZero
368 | samplesPerPixel = 1
369 | bitsPerSample = []uint32{8}
370 | err = encodeGray(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
371 | case *image.Gray16:
372 | photometricInterpretation = pBlackIsZero
373 | samplesPerPixel = 1
374 | bitsPerSample = []uint32{16}
375 | err = encodeGray16(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
376 | case *image.NRGBA:
377 | extraSamples = 2 // Unassociated alpha.
378 | err = encodeRGBA(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
379 | case *image.NRGBA64:
380 | extraSamples = 2 // Unassociated alpha.
381 | bitsPerSample = []uint32{16, 16, 16, 16}
382 | err = encodeRGBA64(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
383 | case *image.RGBA:
384 | extraSamples = 1 // Associated alpha.
385 | err = encodeRGBA(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
386 | case *image.RGBA64:
387 | extraSamples = 1 // Associated alpha.
388 | bitsPerSample = []uint32{16, 16, 16, 16}
389 | err = encodeRGBA64(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
390 | default:
391 | extraSamples = 1 // Associated alpha.
392 | err = encode(dst, m, predictor)
393 | }
394 | if err != nil {
395 | return err
396 | }
397 |
398 | if compression != cNone {
399 | if err = dst.(io.Closer).Close(); err != nil {
400 | return err
401 | }
402 | imageLen = buf.Len()
403 | if err = binary.Write(w, enc, uint32(imageLen+8)); err != nil {
404 | return err
405 | }
406 | if _, err = buf.WriteTo(w); err != nil {
407 | return err
408 | }
409 | }
410 |
411 | ifd := []ifdEntry{
412 | {tImageWidth, dtShort, []uint32{uint32(d.X)}},
413 | {tImageLength, dtShort, []uint32{uint32(d.Y)}},
414 | {tBitsPerSample, dtShort, bitsPerSample},
415 | {tCompression, dtShort, []uint32{compression}},
416 | {tPhotometricInterpretation, dtShort, []uint32{photometricInterpretation}},
417 | {tStripOffsets, dtLong, []uint32{8}},
418 | {tSamplesPerPixel, dtShort, []uint32{samplesPerPixel}},
419 | {tRowsPerStrip, dtShort, []uint32{uint32(d.Y)}},
420 | {tStripByteCounts, dtLong, []uint32{uint32(imageLen)}},
421 | // There is currently no support for storing the image
422 | // resolution, so give a bogus value of 72x72 dpi.
423 | {tXResolution, dtRational, []uint32{72, 1}},
424 | {tYResolution, dtRational, []uint32{72, 1}},
425 | {tResolutionUnit, dtShort, []uint32{resPerInch}},
426 | }
427 | if pr != prNone {
428 | ifd = append(ifd, ifdEntry{tPredictor, dtShort, []uint32{pr}})
429 | }
430 | if len(colorMap) != 0 {
431 | ifd = append(ifd, ifdEntry{tColorMap, dtShort, colorMap})
432 | }
433 | if extraSamples > 0 {
434 | ifd = append(ifd, ifdEntry{tExtraSamples, dtShort, []uint32{extraSamples}})
435 | }
436 |
437 | return writeIFD(w, imageLen+8, ifd)
438 | }
439 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/github.com/disintegration/imaging/resize.go:
--------------------------------------------------------------------------------
1 | package imaging
2 |
3 | import (
4 | "image"
5 | "math"
6 | )
7 |
8 | type iwpair struct {
9 | i int
10 | w int32
11 | }
12 |
13 | type pweights struct {
14 | iwpairs []iwpair
15 | wsum int32
16 | }
17 |
18 | func precomputeWeights(dstSize, srcSize int, filter ResampleFilter) []pweights {
19 | du := float64(srcSize) / float64(dstSize)
20 | scale := du
21 | if scale < 1.0 {
22 | scale = 1.0
23 | }
24 | ru := math.Ceil(scale * filter.Support)
25 |
26 | out := make([]pweights, dstSize)
27 |
28 | for v := 0; v < dstSize; v++ {
29 | fu := (float64(v)+0.5)*du - 0.5
30 |
31 | startu := int(math.Ceil(fu - ru))
32 | if startu < 0 {
33 | startu = 0
34 | }
35 | endu := int(math.Floor(fu + ru))
36 | if endu > srcSize-1 {
37 | endu = srcSize - 1
38 | }
39 |
40 | wsum := int32(0)
41 | for u := startu; u <= endu; u++ {
42 | w := int32(0xff * filter.Kernel((float64(u)-fu)/scale))
43 | if w != 0 {
44 | wsum += w
45 | out[v].iwpairs = append(out[v].iwpairs, iwpair{u, w})
46 | }
47 | }
48 | out[v].wsum = wsum
49 | }
50 |
51 | return out
52 | }
53 |
54 | // Resize resizes the image to the specified width and height using the specified resampling
55 | // filter and returns the transformed image. If one of width or height is 0, the image aspect
56 | // ratio is preserved.
57 | //
58 | // Supported resample filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali,
59 | // CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine.
60 | //
61 | // Usage example:
62 | //
63 | // dstImage := imaging.Resize(srcImage, 800, 600, imaging.Lanczos)
64 | //
65 | func Resize(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA {
66 | dstW, dstH := width, height
67 |
68 | if dstW < 0 || dstH < 0 {
69 | return &image.NRGBA{}
70 | }
71 | if dstW == 0 && dstH == 0 {
72 | return &image.NRGBA{}
73 | }
74 |
75 | src := toNRGBA(img)
76 |
77 | srcW := src.Bounds().Max.X
78 | srcH := src.Bounds().Max.Y
79 |
80 | if srcW <= 0 || srcH <= 0 {
81 | return &image.NRGBA{}
82 | }
83 |
84 | // if new width or height is 0 then preserve aspect ratio, minimum 1px
85 | if dstW == 0 {
86 | tmpW := float64(dstH) * float64(srcW) / float64(srcH)
87 | dstW = int(math.Max(1.0, math.Floor(tmpW+0.5)))
88 | }
89 | if dstH == 0 {
90 | tmpH := float64(dstW) * float64(srcH) / float64(srcW)
91 | dstH = int(math.Max(1.0, math.Floor(tmpH+0.5)))
92 | }
93 |
94 | var dst *image.NRGBA
95 |
96 | if filter.Support <= 0.0 {
97 | // nearest-neighbor special case
98 | dst = resizeNearest(src, dstW, dstH)
99 |
100 | } else {
101 | // two-pass resize
102 | if srcW != dstW {
103 | dst = resizeHorizontal(src, dstW, filter)
104 | } else {
105 | dst = src
106 | }
107 |
108 | if srcH != dstH {
109 | dst = resizeVertical(dst, dstH, filter)
110 | }
111 | }
112 |
113 | return dst
114 | }
115 |
116 | func resizeHorizontal(src *image.NRGBA, width int, filter ResampleFilter) *image.NRGBA {
117 | srcBounds := src.Bounds()
118 | srcW := srcBounds.Max.X
119 | srcH := srcBounds.Max.Y
120 |
121 | dstW := width
122 | dstH := srcH
123 |
124 | dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
125 |
126 | weights := precomputeWeights(dstW, srcW, filter)
127 |
128 | parallel(dstH, func(partStart, partEnd int) {
129 | for dstY := partStart; dstY < partEnd; dstY++ {
130 | for dstX := 0; dstX < dstW; dstX++ {
131 | var c [4]int32
132 | for _, iw := range weights[dstX].iwpairs {
133 | i := dstY*src.Stride + iw.i*4
134 | c[0] += int32(src.Pix[i+0]) * iw.w
135 | c[1] += int32(src.Pix[i+1]) * iw.w
136 | c[2] += int32(src.Pix[i+2]) * iw.w
137 | c[3] += int32(src.Pix[i+3]) * iw.w
138 | }
139 | j := dstY*dst.Stride + dstX*4
140 | sum := weights[dstX].wsum
141 | dst.Pix[j+0] = clampint32(int32(float32(c[0])/float32(sum) + 0.5))
142 | dst.Pix[j+1] = clampint32(int32(float32(c[1])/float32(sum) + 0.5))
143 | dst.Pix[j+2] = clampint32(int32(float32(c[2])/float32(sum) + 0.5))
144 | dst.Pix[j+3] = clampint32(int32(float32(c[3])/float32(sum) + 0.5))
145 | }
146 | }
147 | })
148 |
149 | return dst
150 | }
151 |
152 | func resizeVertical(src *image.NRGBA, height int, filter ResampleFilter) *image.NRGBA {
153 | srcBounds := src.Bounds()
154 | srcW := srcBounds.Max.X
155 | srcH := srcBounds.Max.Y
156 |
157 | dstW := srcW
158 | dstH := height
159 |
160 | dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
161 |
162 | weights := precomputeWeights(dstH, srcH, filter)
163 |
164 | parallel(dstW, func(partStart, partEnd int) {
165 |
166 | for dstX := partStart; dstX < partEnd; dstX++ {
167 | for dstY := 0; dstY < dstH; dstY++ {
168 | var c [4]int32
169 | for _, iw := range weights[dstY].iwpairs {
170 | i := iw.i*src.Stride + dstX*4
171 | c[0] += int32(src.Pix[i+0]) * iw.w
172 | c[1] += int32(src.Pix[i+1]) * iw.w
173 | c[2] += int32(src.Pix[i+2]) * iw.w
174 | c[3] += int32(src.Pix[i+3]) * iw.w
175 | }
176 | j := dstY*dst.Stride + dstX*4
177 | sum := weights[dstY].wsum
178 | dst.Pix[j+0] = clampint32(int32(float32(c[0])/float32(sum) + 0.5))
179 | dst.Pix[j+1] = clampint32(int32(float32(c[1])/float32(sum) + 0.5))
180 | dst.Pix[j+2] = clampint32(int32(float32(c[2])/float32(sum) + 0.5))
181 | dst.Pix[j+3] = clampint32(int32(float32(c[3])/float32(sum) + 0.5))
182 | }
183 | }
184 |
185 | })
186 |
187 | return dst
188 | }
189 |
190 | // fast nearest-neighbor resize, no filtering
191 | func resizeNearest(src *image.NRGBA, width, height int) *image.NRGBA {
192 | dstW, dstH := width, height
193 |
194 | srcBounds := src.Bounds()
195 | srcW := srcBounds.Max.X
196 | srcH := srcBounds.Max.Y
197 |
198 | dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
199 |
200 | dx := float64(srcW) / float64(dstW)
201 | dy := float64(srcH) / float64(dstH)
202 |
203 | parallel(dstH, func(partStart, partEnd int) {
204 |
205 | for dstY := partStart; dstY < partEnd; dstY++ {
206 | fy := (float64(dstY)+0.5)*dy - 0.5
207 |
208 | for dstX := 0; dstX < dstW; dstX++ {
209 | fx := (float64(dstX)+0.5)*dx - 0.5
210 |
211 | srcX := int(math.Min(math.Max(math.Floor(fx+0.5), 0.0), float64(srcW)))
212 | srcY := int(math.Min(math.Max(math.Floor(fy+0.5), 0.0), float64(srcH)))
213 |
214 | srcOff := srcY*src.Stride + srcX*4
215 | dstOff := dstY*dst.Stride + dstX*4
216 |
217 | copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
218 | }
219 | }
220 |
221 | })
222 |
223 | return dst
224 | }
225 |
226 | // Fit scales down the image using the specified resample filter to fit the specified
227 | // maximum width and height and returns the transformed image.
228 | //
229 | // Supported resample filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali,
230 | // CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine.
231 | //
232 | // Usage example:
233 | //
234 | // dstImage := imaging.Fit(srcImage, 800, 600, imaging.Lanczos)
235 | //
236 | func Fit(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA {
237 | maxW, maxH := width, height
238 |
239 | if maxW <= 0 || maxH <= 0 {
240 | return &image.NRGBA{}
241 | }
242 |
243 | srcBounds := img.Bounds()
244 | srcW := srcBounds.Dx()
245 | srcH := srcBounds.Dy()
246 |
247 | if srcW <= 0 || srcH <= 0 {
248 | return &image.NRGBA{}
249 | }
250 |
251 | if srcW <= maxW && srcH <= maxH {
252 | return Clone(img)
253 | }
254 |
255 | srcAspectRatio := float64(srcW) / float64(srcH)
256 | maxAspectRatio := float64(maxW) / float64(maxH)
257 |
258 | var newW, newH int
259 | if srcAspectRatio > maxAspectRatio {
260 | newW = maxW
261 | newH = int(float64(newW) / srcAspectRatio)
262 | } else {
263 | newH = maxH
264 | newW = int(float64(newH) * srcAspectRatio)
265 | }
266 |
267 | return Resize(img, newW, newH, filter)
268 | }
269 |
270 | // Fill scales the image to the smallest possible size that will cover the specified dimensions,
271 | // crops the resized image to the specified dimensions using the given anchor point and returns
272 | // the transformed image.
273 | //
274 | // Supported resample filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali,
275 | // CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine.
276 | //
277 | // Usage example:
278 | //
279 | // dstImage := imaging.Fill(srcImage, 800, 600, imaging.Center, imaging.Lanczos)
280 | //
281 | func Fill(img image.Image, width, height int, anchor Anchor, filter ResampleFilter) *image.NRGBA {
282 | minW, minH := width, height
283 |
284 | if minW <= 0 || minH <= 0 {
285 | return &image.NRGBA{}
286 | }
287 |
288 | srcBounds := img.Bounds()
289 | srcW := srcBounds.Dx()
290 | srcH := srcBounds.Dy()
291 |
292 | if srcW <= 0 || srcH <= 0 {
293 | return &image.NRGBA{}
294 | }
295 |
296 | if srcW == minW && srcH == minH {
297 | return Clone(img)
298 | }
299 |
300 | srcAspectRatio := float64(srcW) / float64(srcH)
301 | minAspectRatio := float64(minW) / float64(minH)
302 |
303 | var tmp *image.NRGBA
304 | if srcAspectRatio < minAspectRatio {
305 | tmp = Resize(img, minW, 0, filter)
306 | } else {
307 | tmp = Resize(img, 0, minH, filter)
308 | }
309 |
310 | return CropAnchor(tmp, minW, minH, anchor)
311 | }
312 |
313 | // Thumbnail scales the image up or down using the specified resample filter, crops it
314 | // to the specified width and hight and returns the transformed image.
315 | //
316 | // Supported resample filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali,
317 | // CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine.
318 | //
319 | // Usage example:
320 | //
321 | // dstImage := imaging.Thumbnail(srcImage, 100, 100, imaging.Lanczos)
322 | //
323 | func Thumbnail(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA {
324 | return Fill(img, width, height, Center, filter)
325 | }
326 |
327 | // Resample filter struct. It can be used to make custom filters.
328 | //
329 | // Supported resample filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali,
330 | // CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine.
331 | //
332 | // General filter recommendations:
333 | //
334 | // - Lanczos
335 | // Probably the best resampling filter for photographic images yielding sharp results,
336 | // but it's slower than cubic filters (see below).
337 | //
338 | // - CatmullRom
339 | // A sharp cubic filter. It's a good filter for both upscaling and downscaling if sharp results are needed.
340 | //
341 | // - MitchellNetravali
342 | // A high quality cubic filter that produces smoother results with less ringing than CatmullRom.
343 | //
344 | // - BSpline
345 | // A good filter if a very smooth output is needed.
346 | //
347 | // - Linear
348 | // Bilinear interpolation filter, produces reasonably good, smooth output. It's faster than cubic filters.
349 | //
350 | // - Box
351 | // Simple and fast resampling filter appropriate for downscaling.
352 | // When upscaling it's similar to NearestNeighbor.
353 | //
354 | // - NearestNeighbor
355 | // Fastest resample filter, no antialiasing at all. Rarely used.
356 | //
357 | type ResampleFilter struct {
358 | Support float64
359 | Kernel func(float64) float64
360 | }
361 |
362 | // Nearest-neighbor filter, no anti-aliasing.
363 | var NearestNeighbor ResampleFilter
364 |
365 | // Box filter (averaging pixels).
366 | var Box ResampleFilter
367 |
368 | // Linear filter.
369 | var Linear ResampleFilter
370 |
371 | // Hermite cubic spline filter (BC-spline; B=0; C=0).
372 | var Hermite ResampleFilter
373 |
374 | // Mitchell-Netravali cubic filter (BC-spline; B=1/3; C=1/3).
375 | var MitchellNetravali ResampleFilter
376 |
377 | // Catmull-Rom - sharp cubic filter (BC-spline; B=0; C=0.5).
378 | var CatmullRom ResampleFilter
379 |
380 | // Cubic B-spline - smooth cubic filter (BC-spline; B=1; C=0).
381 | var BSpline ResampleFilter
382 |
383 | // Gaussian Blurring Filter.
384 | var Gaussian ResampleFilter
385 |
386 | // Bartlett-windowed sinc filter (3 lobes).
387 | var Bartlett ResampleFilter
388 |
389 | // Lanczos filter (3 lobes).
390 | var Lanczos ResampleFilter
391 |
392 | // Hann-windowed sinc filter (3 lobes).
393 | var Hann ResampleFilter
394 |
395 | // Hamming-windowed sinc filter (3 lobes).
396 | var Hamming ResampleFilter
397 |
398 | // Blackman-windowed sinc filter (3 lobes).
399 | var Blackman ResampleFilter
400 |
401 | // Welch-windowed sinc filter (parabolic window, 3 lobes).
402 | var Welch ResampleFilter
403 |
404 | // Cosine-windowed sinc filter (3 lobes).
405 | var Cosine ResampleFilter
406 |
407 | func bcspline(x, b, c float64) float64 {
408 | x = math.Abs(x)
409 | if x < 1.0 {
410 | return ((12-9*b-6*c)*x*x*x + (-18+12*b+6*c)*x*x + (6 - 2*b)) / 6
411 | }
412 | if x < 2.0 {
413 | return ((-b-6*c)*x*x*x + (6*b+30*c)*x*x + (-12*b-48*c)*x + (8*b + 24*c)) / 6
414 | }
415 | return 0
416 | }
417 |
418 | func sinc(x float64) float64 {
419 | if x == 0 {
420 | return 1
421 | }
422 | return math.Sin(math.Pi*x) / (math.Pi * x)
423 | }
424 |
425 | func init() {
426 | NearestNeighbor = ResampleFilter{
427 | Support: 0.0, // special case - not applying the filter
428 | }
429 |
430 | Box = ResampleFilter{
431 | Support: 0.5,
432 | Kernel: func(x float64) float64 {
433 | x = math.Abs(x)
434 | if x <= 0.5 {
435 | return 1.0
436 | }
437 | return 0
438 | },
439 | }
440 |
441 | Linear = ResampleFilter{
442 | Support: 1.0,
443 | Kernel: func(x float64) float64 {
444 | x = math.Abs(x)
445 | if x < 1.0 {
446 | return 1.0 - x
447 | }
448 | return 0
449 | },
450 | }
451 |
452 | Hermite = ResampleFilter{
453 | Support: 1.0,
454 | Kernel: func(x float64) float64 {
455 | x = math.Abs(x)
456 | if x < 1.0 {
457 | return bcspline(x, 0.0, 0.0)
458 | }
459 | return 0
460 | },
461 | }
462 |
463 | MitchellNetravali = ResampleFilter{
464 | Support: 2.0,
465 | Kernel: func(x float64) float64 {
466 | x = math.Abs(x)
467 | if x < 2.0 {
468 | return bcspline(x, 1.0/3.0, 1.0/3.0)
469 | }
470 | return 0
471 | },
472 | }
473 |
474 | CatmullRom = ResampleFilter{
475 | Support: 2.0,
476 | Kernel: func(x float64) float64 {
477 | x = math.Abs(x)
478 | if x < 2.0 {
479 | return bcspline(x, 0.0, 0.5)
480 | }
481 | return 0
482 | },
483 | }
484 |
485 | BSpline = ResampleFilter{
486 | Support: 2.0,
487 | Kernel: func(x float64) float64 {
488 | x = math.Abs(x)
489 | if x < 2.0 {
490 | return bcspline(x, 1.0, 0.0)
491 | }
492 | return 0
493 | },
494 | }
495 |
496 | Gaussian = ResampleFilter{
497 | Support: 2.0,
498 | Kernel: func(x float64) float64 {
499 | x = math.Abs(x)
500 | if x < 2.0 {
501 | return math.Exp(-2 * x * x)
502 | }
503 | return 0
504 | },
505 | }
506 |
507 | Bartlett = ResampleFilter{
508 | Support: 3.0,
509 | Kernel: func(x float64) float64 {
510 | x = math.Abs(x)
511 | if x < 3.0 {
512 | return sinc(x) * (3.0 - x) / 3.0
513 | }
514 | return 0
515 | },
516 | }
517 |
518 | Lanczos = ResampleFilter{
519 | Support: 3.0,
520 | Kernel: func(x float64) float64 {
521 | x = math.Abs(x)
522 | if x < 3.0 {
523 | return sinc(x) * sinc(x/3.0)
524 | }
525 | return 0
526 | },
527 | }
528 |
529 | Hann = ResampleFilter{
530 | Support: 3.0,
531 | Kernel: func(x float64) float64 {
532 | x = math.Abs(x)
533 | if x < 3.0 {
534 | return sinc(x) * (0.5 + 0.5*math.Cos(math.Pi*x/3.0))
535 | }
536 | return 0
537 | },
538 | }
539 |
540 | Hamming = ResampleFilter{
541 | Support: 3.0,
542 | Kernel: func(x float64) float64 {
543 | x = math.Abs(x)
544 | if x < 3.0 {
545 | return sinc(x) * (0.54 + 0.46*math.Cos(math.Pi*x/3.0))
546 | }
547 | return 0
548 | },
549 | }
550 |
551 | Blackman = ResampleFilter{
552 | Support: 3.0,
553 | Kernel: func(x float64) float64 {
554 | x = math.Abs(x)
555 | if x < 3.0 {
556 | return sinc(x) * (0.42 - 0.5*math.Cos(math.Pi*x/3.0+math.Pi) + 0.08*math.Cos(2.0*math.Pi*x/3.0))
557 | }
558 | return 0
559 | },
560 | }
561 |
562 | Welch = ResampleFilter{
563 | Support: 3.0,
564 | Kernel: func(x float64) float64 {
565 | x = math.Abs(x)
566 | if x < 3.0 {
567 | return sinc(x) * (1.0 - (x * x / 9.0))
568 | }
569 | return 0
570 | },
571 | }
572 |
573 | Cosine = ResampleFilter{
574 | Support: 3.0,
575 | Kernel: func(x float64) float64 {
576 | x = math.Abs(x)
577 | if x < 3.0 {
578 | return sinc(x) * math.Cos((math.Pi/2.0)*(x/3.0))
579 | }
580 | return 0
581 | },
582 | }
583 | }
584 |
--------------------------------------------------------------------------------
/Godeps/_workspace/src/golang.org/x/image/tiff/reader.go:
--------------------------------------------------------------------------------
1 | // Copyright 2011 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // Package tiff implements a TIFF image decoder and encoder.
6 | //
7 | // The TIFF specification is at http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf
8 | package tiff
9 |
10 | import (
11 | "compress/zlib"
12 | "encoding/binary"
13 | "fmt"
14 | "image"
15 | "image/color"
16 | "io"
17 | "io/ioutil"
18 | "math"
19 |
20 | "golang.org/x/image/tiff/lzw"
21 | )
22 |
23 | // A FormatError reports that the input is not a valid TIFF image.
24 | type FormatError string
25 |
26 | func (e FormatError) Error() string {
27 | return "tiff: invalid format: " + string(e)
28 | }
29 |
30 | // An UnsupportedError reports that the input uses a valid but
31 | // unimplemented feature.
32 | type UnsupportedError string
33 |
34 | func (e UnsupportedError) Error() string {
35 | return "tiff: unsupported feature: " + string(e)
36 | }
37 |
38 | // An InternalError reports that an internal error was encountered.
39 | type InternalError string
40 |
41 | func (e InternalError) Error() string {
42 | return "tiff: internal error: " + string(e)
43 | }
44 |
45 | var errNoPixels = FormatError("not enough pixel data")
46 |
47 | type decoder struct {
48 | r io.ReaderAt
49 | byteOrder binary.ByteOrder
50 | config image.Config
51 | mode imageMode
52 | bpp uint
53 | features map[int][]uint
54 | palette []color.Color
55 |
56 | buf []byte
57 | off int // Current offset in buf.
58 | v uint32 // Buffer value for reading with arbitrary bit depths.
59 | nbits uint // Remaining number of bits in v.
60 | }
61 |
62 | // firstVal returns the first uint of the features entry with the given tag,
63 | // or 0 if the tag does not exist.
64 | func (d *decoder) firstVal(tag int) uint {
65 | f := d.features[tag]
66 | if len(f) == 0 {
67 | return 0
68 | }
69 | return f[0]
70 | }
71 |
72 | // ifdUint decodes the IFD entry in p, which must be of the Byte, Short
73 | // or Long type, and returns the decoded uint values.
74 | func (d *decoder) ifdUint(p []byte) (u []uint, err error) {
75 | var raw []byte
76 | if len(p) < ifdLen {
77 | return nil, FormatError("bad IFD entry")
78 | }
79 |
80 | datatype := d.byteOrder.Uint16(p[2:4])
81 | if dt := int(datatype); dt <= 0 || dt >= len(lengths) {
82 | return nil, UnsupportedError("IFD entry datatype")
83 | }
84 |
85 | count := d.byteOrder.Uint32(p[4:8])
86 | if count > math.MaxInt32/lengths[datatype] {
87 | return nil, FormatError("IFD data too large")
88 | }
89 | if datalen := lengths[datatype] * count; datalen > 4 {
90 | // The IFD contains a pointer to the real value.
91 | raw = make([]byte, datalen)
92 | _, err = d.r.ReadAt(raw, int64(d.byteOrder.Uint32(p[8:12])))
93 | } else {
94 | raw = p[8 : 8+datalen]
95 | }
96 | if err != nil {
97 | return nil, err
98 | }
99 |
100 | u = make([]uint, count)
101 | switch datatype {
102 | case dtByte:
103 | for i := uint32(0); i < count; i++ {
104 | u[i] = uint(raw[i])
105 | }
106 | case dtShort:
107 | for i := uint32(0); i < count; i++ {
108 | u[i] = uint(d.byteOrder.Uint16(raw[2*i : 2*(i+1)]))
109 | }
110 | case dtLong:
111 | for i := uint32(0); i < count; i++ {
112 | u[i] = uint(d.byteOrder.Uint32(raw[4*i : 4*(i+1)]))
113 | }
114 | default:
115 | return nil, UnsupportedError("data type")
116 | }
117 | return u, nil
118 | }
119 |
120 | // parseIFD decides whether the the IFD entry in p is "interesting" and
121 | // stows away the data in the decoder.
122 | func (d *decoder) parseIFD(p []byte) error {
123 | tag := d.byteOrder.Uint16(p[0:2])
124 | switch tag {
125 | case tBitsPerSample,
126 | tExtraSamples,
127 | tPhotometricInterpretation,
128 | tCompression,
129 | tPredictor,
130 | tStripOffsets,
131 | tStripByteCounts,
132 | tRowsPerStrip,
133 | tTileWidth,
134 | tTileLength,
135 | tTileOffsets,
136 | tTileByteCounts,
137 | tImageLength,
138 | tImageWidth:
139 | val, err := d.ifdUint(p)
140 | if err != nil {
141 | return err
142 | }
143 | d.features[int(tag)] = val
144 | case tColorMap:
145 | val, err := d.ifdUint(p)
146 | if err != nil {
147 | return err
148 | }
149 | numcolors := len(val) / 3
150 | if len(val)%3 != 0 || numcolors <= 0 || numcolors > 256 {
151 | return FormatError("bad ColorMap length")
152 | }
153 | d.palette = make([]color.Color, numcolors)
154 | for i := 0; i < numcolors; i++ {
155 | d.palette[i] = color.RGBA64{
156 | uint16(val[i]),
157 | uint16(val[i+numcolors]),
158 | uint16(val[i+2*numcolors]),
159 | 0xffff,
160 | }
161 | }
162 | case tSampleFormat:
163 | // Page 27 of the spec: If the SampleFormat is present and
164 | // the value is not 1 [= unsigned integer data], a Baseline
165 | // TIFF reader that cannot handle the SampleFormat value
166 | // must terminate the import process gracefully.
167 | val, err := d.ifdUint(p)
168 | if err != nil {
169 | return err
170 | }
171 | for _, v := range val {
172 | if v != 1 {
173 | return UnsupportedError("sample format")
174 | }
175 | }
176 | }
177 | return nil
178 | }
179 |
180 | // readBits reads n bits from the internal buffer starting at the current offset.
181 | func (d *decoder) readBits(n uint) (v uint32, ok bool) {
182 | for d.nbits < n {
183 | d.v <<= 8
184 | if d.off >= len(d.buf) {
185 | return 0, false
186 | }
187 | d.v |= uint32(d.buf[d.off])
188 | d.off++
189 | d.nbits += 8
190 | }
191 | d.nbits -= n
192 | rv := d.v >> d.nbits
193 | d.v &^= rv << d.nbits
194 | return rv, true
195 | }
196 |
197 | // flushBits discards the unread bits in the buffer used by readBits.
198 | // It is used at the end of a line.
199 | func (d *decoder) flushBits() {
200 | d.v = 0
201 | d.nbits = 0
202 | }
203 |
204 | // minInt returns the smaller of x or y.
205 | func minInt(a, b int) int {
206 | if a <= b {
207 | return a
208 | }
209 | return b
210 | }
211 |
212 | // decode decodes the raw data of an image.
213 | // It reads from d.buf and writes the strip or tile into dst.
214 | func (d *decoder) decode(dst image.Image, xmin, ymin, xmax, ymax int) error {
215 | d.off = 0
216 |
217 | // Apply horizontal predictor if necessary.
218 | // In this case, p contains the color difference to the preceding pixel.
219 | // See page 64-65 of the spec.
220 | if d.firstVal(tPredictor) == prHorizontal {
221 | switch d.bpp {
222 | case 16:
223 | var off int
224 | n := 2 * len(d.features[tBitsPerSample]) // bytes per sample times samples per pixel
225 | for y := ymin; y < ymax; y++ {
226 | off += n
227 | for x := 0; x < (xmax-xmin-1)*n; x += 2 {
228 | if off+2 > len(d.buf) {
229 | return errNoPixels
230 | }
231 | v0 := d.byteOrder.Uint16(d.buf[off-n : off-n+2])
232 | v1 := d.byteOrder.Uint16(d.buf[off : off+2])
233 | d.byteOrder.PutUint16(d.buf[off:off+2], v1+v0)
234 | off += 2
235 | }
236 | }
237 | case 8:
238 | var off int
239 | n := 1 * len(d.features[tBitsPerSample]) // bytes per sample times samples per pixel
240 | for y := ymin; y < ymax; y++ {
241 | off += n
242 | for x := 0; x < (xmax-xmin-1)*n; x++ {
243 | if off >= len(d.buf) {
244 | return errNoPixels
245 | }
246 | d.buf[off] += d.buf[off-n]
247 | off++
248 | }
249 | }
250 | case 1:
251 | return UnsupportedError("horizontal predictor with 1 BitsPerSample")
252 | }
253 | }
254 |
255 | rMaxX := minInt(xmax, dst.Bounds().Max.X)
256 | rMaxY := minInt(ymax, dst.Bounds().Max.Y)
257 | switch d.mode {
258 | case mGray, mGrayInvert:
259 | if d.bpp == 16 {
260 | img := dst.(*image.Gray16)
261 | for y := ymin; y < rMaxY; y++ {
262 | for x := xmin; x < rMaxX; x++ {
263 | if d.off+2 > len(d.buf) {
264 | return errNoPixels
265 | }
266 | v := d.byteOrder.Uint16(d.buf[d.off : d.off+2])
267 | d.off += 2
268 | if d.mode == mGrayInvert {
269 | v = 0xffff - v
270 | }
271 | img.SetGray16(x, y, color.Gray16{v})
272 | }
273 | }
274 | } else {
275 | img := dst.(*image.Gray)
276 | max := uint32((1 << d.bpp) - 1)
277 | for y := ymin; y < rMaxY; y++ {
278 | for x := xmin; x < rMaxX; x++ {
279 | v, ok := d.readBits(d.bpp)
280 | if !ok {
281 | return errNoPixels
282 | }
283 | v = v * 0xff / max
284 | if d.mode == mGrayInvert {
285 | v = 0xff - v
286 | }
287 | img.SetGray(x, y, color.Gray{uint8(v)})
288 | }
289 | d.flushBits()
290 | }
291 | }
292 | case mPaletted:
293 | img := dst.(*image.Paletted)
294 | for y := ymin; y < rMaxY; y++ {
295 | for x := xmin; x < rMaxX; x++ {
296 | v, ok := d.readBits(d.bpp)
297 | if !ok {
298 | return errNoPixels
299 | }
300 | img.SetColorIndex(x, y, uint8(v))
301 | }
302 | d.flushBits()
303 | }
304 | case mRGB:
305 | if d.bpp == 16 {
306 | img := dst.(*image.RGBA64)
307 | for y := ymin; y < rMaxY; y++ {
308 | for x := xmin; x < rMaxX; x++ {
309 | if d.off+6 > len(d.buf) {
310 | return errNoPixels
311 | }
312 | r := d.byteOrder.Uint16(d.buf[d.off+0 : d.off+2])
313 | g := d.byteOrder.Uint16(d.buf[d.off+2 : d.off+4])
314 | b := d.byteOrder.Uint16(d.buf[d.off+4 : d.off+6])
315 | d.off += 6
316 | img.SetRGBA64(x, y, color.RGBA64{r, g, b, 0xffff})
317 | }
318 | }
319 | } else {
320 | img := dst.(*image.RGBA)
321 | for y := ymin; y < rMaxY; y++ {
322 | min := img.PixOffset(xmin, y)
323 | max := img.PixOffset(rMaxX, y)
324 | off := (y - ymin) * (xmax - xmin) * 3
325 | for i := min; i < max; i += 4 {
326 | if off+3 > len(d.buf) {
327 | return errNoPixels
328 | }
329 | img.Pix[i+0] = d.buf[off+0]
330 | img.Pix[i+1] = d.buf[off+1]
331 | img.Pix[i+2] = d.buf[off+2]
332 | img.Pix[i+3] = 0xff
333 | off += 3
334 | }
335 | }
336 | }
337 | case mNRGBA:
338 | if d.bpp == 16 {
339 | img := dst.(*image.NRGBA64)
340 | for y := ymin; y < rMaxY; y++ {
341 | for x := xmin; x < rMaxX; x++ {
342 | if d.off+8 > len(d.buf) {
343 | return errNoPixels
344 | }
345 | r := d.byteOrder.Uint16(d.buf[d.off+0 : d.off+2])
346 | g := d.byteOrder.Uint16(d.buf[d.off+2 : d.off+4])
347 | b := d.byteOrder.Uint16(d.buf[d.off+4 : d.off+6])
348 | a := d.byteOrder.Uint16(d.buf[d.off+6 : d.off+8])
349 | d.off += 8
350 | img.SetNRGBA64(x, y, color.NRGBA64{r, g, b, a})
351 | }
352 | }
353 | } else {
354 | img := dst.(*image.NRGBA)
355 | for y := ymin; y < rMaxY; y++ {
356 | min := img.PixOffset(xmin, y)
357 | max := img.PixOffset(rMaxX, y)
358 | i0, i1 := (y-ymin)*(xmax-xmin)*4, (y-ymin+1)*(xmax-xmin)*4
359 | if i1 > len(d.buf) {
360 | return errNoPixels
361 | }
362 | copy(img.Pix[min:max], d.buf[i0:i1])
363 | }
364 | }
365 | case mRGBA:
366 | if d.bpp == 16 {
367 | img := dst.(*image.RGBA64)
368 | for y := ymin; y < rMaxY; y++ {
369 | for x := xmin; x < rMaxX; x++ {
370 | if d.off+8 > len(d.buf) {
371 | return errNoPixels
372 | }
373 | r := d.byteOrder.Uint16(d.buf[d.off+0 : d.off+2])
374 | g := d.byteOrder.Uint16(d.buf[d.off+2 : d.off+4])
375 | b := d.byteOrder.Uint16(d.buf[d.off+4 : d.off+6])
376 | a := d.byteOrder.Uint16(d.buf[d.off+6 : d.off+8])
377 | d.off += 8
378 | img.SetRGBA64(x, y, color.RGBA64{r, g, b, a})
379 | }
380 | }
381 | } else {
382 | img := dst.(*image.RGBA)
383 | for y := ymin; y < rMaxY; y++ {
384 | min := img.PixOffset(xmin, y)
385 | max := img.PixOffset(rMaxX, y)
386 | i0, i1 := (y-ymin)*(xmax-xmin)*4, (y-ymin+1)*(xmax-xmin)*4
387 | if i1 > len(d.buf) {
388 | return errNoPixels
389 | }
390 | copy(img.Pix[min:max], d.buf[i0:i1])
391 | }
392 | }
393 | }
394 |
395 | return nil
396 | }
397 |
398 | func newDecoder(r io.Reader) (*decoder, error) {
399 | d := &decoder{
400 | r: newReaderAt(r),
401 | features: make(map[int][]uint),
402 | }
403 |
404 | p := make([]byte, 8)
405 | if _, err := d.r.ReadAt(p, 0); err != nil {
406 | return nil, err
407 | }
408 | switch string(p[0:4]) {
409 | case leHeader:
410 | d.byteOrder = binary.LittleEndian
411 | case beHeader:
412 | d.byteOrder = binary.BigEndian
413 | default:
414 | return nil, FormatError("malformed header")
415 | }
416 |
417 | ifdOffset := int64(d.byteOrder.Uint32(p[4:8]))
418 |
419 | // The first two bytes contain the number of entries (12 bytes each).
420 | if _, err := d.r.ReadAt(p[0:2], ifdOffset); err != nil {
421 | return nil, err
422 | }
423 | numItems := int(d.byteOrder.Uint16(p[0:2]))
424 |
425 | // All IFD entries are read in one chunk.
426 | p = make([]byte, ifdLen*numItems)
427 | if _, err := d.r.ReadAt(p, ifdOffset+2); err != nil {
428 | return nil, err
429 | }
430 |
431 | for i := 0; i < len(p); i += ifdLen {
432 | if err := d.parseIFD(p[i : i+ifdLen]); err != nil {
433 | return nil, err
434 | }
435 | }
436 |
437 | d.config.Width = int(d.firstVal(tImageWidth))
438 | d.config.Height = int(d.firstVal(tImageLength))
439 |
440 | if _, ok := d.features[tBitsPerSample]; !ok {
441 | return nil, FormatError("BitsPerSample tag missing")
442 | }
443 | d.bpp = d.firstVal(tBitsPerSample)
444 | switch d.bpp {
445 | case 0:
446 | return nil, FormatError("BitsPerSample must not be 0")
447 | case 1, 8, 16:
448 | // Nothing to do, these are accepted by this implementation.
449 | default:
450 | return nil, UnsupportedError(fmt.Sprintf("BitsPerSample of %v", d.bpp))
451 | }
452 |
453 | // Determine the image mode.
454 | switch d.firstVal(tPhotometricInterpretation) {
455 | case pRGB:
456 | if d.bpp == 16 {
457 | for _, b := range d.features[tBitsPerSample] {
458 | if b != 16 {
459 | return nil, FormatError("wrong number of samples for 16bit RGB")
460 | }
461 | }
462 | } else {
463 | for _, b := range d.features[tBitsPerSample] {
464 | if b != 8 {
465 | return nil, FormatError("wrong number of samples for 8bit RGB")
466 | }
467 | }
468 | }
469 | // RGB images normally have 3 samples per pixel.
470 | // If there are more, ExtraSamples (p. 31-32 of the spec)
471 | // gives their meaning (usually an alpha channel).
472 | //
473 | // This implementation does not support extra samples
474 | // of an unspecified type.
475 | switch len(d.features[tBitsPerSample]) {
476 | case 3:
477 | d.mode = mRGB
478 | if d.bpp == 16 {
479 | d.config.ColorModel = color.RGBA64Model
480 | } else {
481 | d.config.ColorModel = color.RGBAModel
482 | }
483 | case 4:
484 | switch d.firstVal(tExtraSamples) {
485 | case 1:
486 | d.mode = mRGBA
487 | if d.bpp == 16 {
488 | d.config.ColorModel = color.RGBA64Model
489 | } else {
490 | d.config.ColorModel = color.RGBAModel
491 | }
492 | case 2:
493 | d.mode = mNRGBA
494 | if d.bpp == 16 {
495 | d.config.ColorModel = color.NRGBA64Model
496 | } else {
497 | d.config.ColorModel = color.NRGBAModel
498 | }
499 | default:
500 | return nil, FormatError("wrong number of samples for RGB")
501 | }
502 | default:
503 | return nil, FormatError("wrong number of samples for RGB")
504 | }
505 | case pPaletted:
506 | d.mode = mPaletted
507 | d.config.ColorModel = color.Palette(d.palette)
508 | case pWhiteIsZero:
509 | d.mode = mGrayInvert
510 | if d.bpp == 16 {
511 | d.config.ColorModel = color.Gray16Model
512 | } else {
513 | d.config.ColorModel = color.GrayModel
514 | }
515 | case pBlackIsZero:
516 | d.mode = mGray
517 | if d.bpp == 16 {
518 | d.config.ColorModel = color.Gray16Model
519 | } else {
520 | d.config.ColorModel = color.GrayModel
521 | }
522 | default:
523 | return nil, UnsupportedError("color model")
524 | }
525 |
526 | return d, nil
527 | }
528 |
529 | // DecodeConfig returns the color model and dimensions of a TIFF image without
530 | // decoding the entire image.
531 | func DecodeConfig(r io.Reader) (image.Config, error) {
532 | d, err := newDecoder(r)
533 | if err != nil {
534 | return image.Config{}, err
535 | }
536 | return d.config, nil
537 | }
538 |
539 | // Decode reads a TIFF image from r and returns it as an image.Image.
540 | // The type of Image returned depends on the contents of the TIFF.
541 | func Decode(r io.Reader) (img image.Image, err error) {
542 | d, err := newDecoder(r)
543 | if err != nil {
544 | return
545 | }
546 |
547 | blockPadding := false
548 | blockWidth := d.config.Width
549 | blockHeight := d.config.Height
550 | blocksAcross := 1
551 | blocksDown := 1
552 |
553 | if d.config.Width == 0 {
554 | blocksAcross = 0
555 | }
556 | if d.config.Height == 0 {
557 | blocksDown = 0
558 | }
559 |
560 | var blockOffsets, blockCounts []uint
561 |
562 | if int(d.firstVal(tTileWidth)) != 0 {
563 | blockPadding = true
564 |
565 | blockWidth = int(d.firstVal(tTileWidth))
566 | blockHeight = int(d.firstVal(tTileLength))
567 |
568 | if blockWidth != 0 {
569 | blocksAcross = (d.config.Width + blockWidth - 1) / blockWidth
570 | }
571 | if blockHeight != 0 {
572 | blocksDown = (d.config.Height + blockHeight - 1) / blockHeight
573 | }
574 |
575 | blockCounts = d.features[tTileByteCounts]
576 | blockOffsets = d.features[tTileOffsets]
577 |
578 | } else {
579 | if int(d.firstVal(tRowsPerStrip)) != 0 {
580 | blockHeight = int(d.firstVal(tRowsPerStrip))
581 | }
582 |
583 | if blockHeight != 0 {
584 | blocksDown = (d.config.Height + blockHeight - 1) / blockHeight
585 | }
586 |
587 | blockOffsets = d.features[tStripOffsets]
588 | blockCounts = d.features[tStripByteCounts]
589 | }
590 |
591 | // Check if we have the right number of strips/tiles, offsets and counts.
592 | if n := blocksAcross * blocksDown; len(blockOffsets) < n || len(blockCounts) < n {
593 | return nil, FormatError("inconsistent header")
594 | }
595 |
596 | imgRect := image.Rect(0, 0, d.config.Width, d.config.Height)
597 | switch d.mode {
598 | case mGray, mGrayInvert:
599 | if d.bpp == 16 {
600 | img = image.NewGray16(imgRect)
601 | } else {
602 | img = image.NewGray(imgRect)
603 | }
604 | case mPaletted:
605 | img = image.NewPaletted(imgRect, d.palette)
606 | case mNRGBA:
607 | if d.bpp == 16 {
608 | img = image.NewNRGBA64(imgRect)
609 | } else {
610 | img = image.NewNRGBA(imgRect)
611 | }
612 | case mRGB, mRGBA:
613 | if d.bpp == 16 {
614 | img = image.NewRGBA64(imgRect)
615 | } else {
616 | img = image.NewRGBA(imgRect)
617 | }
618 | }
619 |
620 | for i := 0; i < blocksAcross; i++ {
621 | blkW := blockWidth
622 | if !blockPadding && i == blocksAcross-1 && d.config.Width%blockWidth != 0 {
623 | blkW = d.config.Width % blockWidth
624 | }
625 | for j := 0; j < blocksDown; j++ {
626 | blkH := blockHeight
627 | if !blockPadding && j == blocksDown-1 && d.config.Height%blockHeight != 0 {
628 | blkH = d.config.Height % blockHeight
629 | }
630 | offset := int64(blockOffsets[j*blocksAcross+i])
631 | n := int64(blockCounts[j*blocksAcross+i])
632 | switch d.firstVal(tCompression) {
633 |
634 | // According to the spec, Compression does not have a default value,
635 | // but some tools interpret a missing Compression value as none so we do
636 | // the same.
637 | case cNone, 0:
638 | if b, ok := d.r.(*buffer); ok {
639 | d.buf, err = b.Slice(int(offset), int(n))
640 | } else {
641 | d.buf = make([]byte, n)
642 | _, err = d.r.ReadAt(d.buf, offset)
643 | }
644 | case cLZW:
645 | r := lzw.NewReader(io.NewSectionReader(d.r, offset, n), lzw.MSB, 8)
646 | d.buf, err = ioutil.ReadAll(r)
647 | r.Close()
648 | case cDeflate, cDeflateOld:
649 | var r io.ReadCloser
650 | r, err = zlib.NewReader(io.NewSectionReader(d.r, offset, n))
651 | if err != nil {
652 | return nil, err
653 | }
654 | d.buf, err = ioutil.ReadAll(r)
655 | r.Close()
656 | case cPackBits:
657 | d.buf, err = unpackBits(io.NewSectionReader(d.r, offset, n))
658 | default:
659 | err = UnsupportedError(fmt.Sprintf("compression value %d", d.firstVal(tCompression)))
660 | }
661 | if err != nil {
662 | return nil, err
663 | }
664 |
665 | xmin := i * blockWidth
666 | ymin := j * blockHeight
667 | xmax := xmin + blkW
668 | ymax := ymin + blkH
669 | err = d.decode(img, xmin, ymin, xmax, ymax)
670 | if err != nil {
671 | return nil, err
672 | }
673 | }
674 | }
675 | return
676 | }
677 |
678 | func init() {
679 | image.RegisterFormat("tiff", leHeader, Decode, DecodeConfig)
680 | image.RegisterFormat("tiff", beHeader, Decode, DecodeConfig)
681 | }
682 |
--------------------------------------------------------------------------------