├── 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 |
8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /test/uploadfile-video.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | FileUpload 5 | 6 | 7 |
8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /test/uploadfile-audio.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | FileUpload 5 | 6 | 7 |
8 | 9 | 10 |
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 |
8 | 9 | 10 | 11 |
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 | [![Build Status](https://travis-ci.org/satori/go.uuid.png?branch=master)](https://travis-ci.org/satori/go.uuid) 4 | [![GoDoc](http://godoc.org/github.com/satori/go.uuid?status.png)](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 | ![srcImage](http://disintegration.github.io/imaging/in_lena_bw_512.png) 55 | 56 | Filter | Resize result 57 | ---|--- 58 | `imaging.NearestNeighbor` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_nearest.png) 59 | `imaging.Box` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_box.png) 60 | `imaging.Linear` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_linear.png) 61 | `imaging.MitchellNetravali` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_mitchell.png) 62 | `imaging.CatmullRom` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_catrom.png) 63 | `imaging.Gaussian` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_gaussian.png) 64 | `imaging.Lanczos` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_lanczos.png) 65 | 66 | **Resize functions comparison** 67 | 68 | Original image: 69 | 70 | ![srcImage](http://disintegration.github.io/imaging/in.jpg) 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 | ![dstImage](http://disintegration.github.io/imaging/out-comp-resize.jpg) 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 | ![dstImage](http://disintegration.github.io/imaging/out-comp-fit.jpg) 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 | ![dstImage](http://disintegration.github.io/imaging/out-comp-fit.jpg) 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 | ![dstImage](http://disintegration.github.io/imaging/out-comp-fill.jpg) 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 | ![srcImage](http://disintegration.github.io/imaging/in_lena_bw_128.png) | ![dstImage](http://disintegration.github.io/imaging/out_blur_0.5.png) | ![dstImage](http://disintegration.github.io/imaging/out_blur_1.5.png) 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 | ![srcImage](http://disintegration.github.io/imaging/in_lena_bw_128.png) | ![dstImage](http://disintegration.github.io/imaging/out_sharpen_0.5.png) | ![dstImage](http://disintegration.github.io/imaging/out_sharpen_1.5.png) 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 | ![srcImage](http://disintegration.github.io/imaging/in_lena_bw_128.png) | ![dstImage](http://disintegration.github.io/imaging/out_gamma_0.75.png) | ![dstImage](http://disintegration.github.io/imaging/out_gamma_1.25.png) 130 | 131 | ### Contrast adjustment 132 | ```go 133 | dstImage := imaging.AdjustContrast(srcImage, 20) 134 | ``` 135 | 136 | Original image | Contrast = 20 | Contrast = -20 137 | ---|---|--- 138 | ![srcImage](http://disintegration.github.io/imaging/in_lena_bw_128.png) | ![dstImage](http://disintegration.github.io/imaging/out_contrast_p20.png) | ![dstImage](http://disintegration.github.io/imaging/out_contrast_m20.png) 139 | 140 | ### Brightness adjustment 141 | ```go 142 | dstImage := imaging.AdjustBrightness(srcImage, 20) 143 | ``` 144 | 145 | Original image | Brightness = 20 | Brightness = -20 146 | ---|---|--- 147 | ![srcImage](http://disintegration.github.io/imaging/in_lena_bw_128.png) | ![dstImage](http://disintegration.github.io/imaging/out_brightness_p20.png) | ![dstImage](http://disintegration.github.io/imaging/out_brightness_m20.png) 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 | --------------------------------------------------------------------------------