24 The Go image package go图片包:图片包的基本原理
The Go image package go图片包:图片包的基本原理
21 September 2011
Introduction
The image and image/color packages define a number of types: color.Color
and color.Model
describe colors, image.Point
and image.Rectangle
describe basic 2-D geometry, and image.Image
brings the two concepts together to represent a rectangular grid of colors. A separate article covers image composition with the image/draw package.
Colors and Color Models
Color is an interface that defines the minimal method set of any type that can be considered a color: one that can be converted to red, green, blue and alpha values. The conversion may be lossy, such as converting from CMYK or YCbCr color spaces.
type Color interface {
// RGBA returns the alpha-premultiplied red, green, blue and alpha values
// for the color. Each value ranges within [0, 0xFFFF], but is represented
// by a uint32 so that multiplying by a blend factor up to 0xFFFF will not
// overflow.
RGBA() (r, g, b, a uint32)
}
There are three important subtleties about the return values. First, the red, green and blue are alpha-premultiplied: a fully saturated red that is also 25% transparent is represented by RGBA returning a 75% r. Second, the channels have a 16-bit effective range: 100% red is represented by RGBA returning an r of 65535, not 255, so that converting from CMYK or YCbCr is not as lossy. Third, the type returned is uint32
, even though the maximum value is 65535, to guarantee that multiplying two values together won't overflow. Such multiplications occur when blending two colors according to an alpha mask from a third color, in the style of Porter and Duff's classic algebra:
dstr, dstg, dstb, dsta := dst.RGBA()
srcr, srcg, srcb, srca := src.RGBA()
_, _, _, m := mask.RGBA()
const M = 1<<16 - 1
// The resultant red value is a blend of dstr and srcr, and ranges in [0, M].
// The calculation for green, blue and alpha is similar.
dstr = (dstr*(M-m) + srcr*m) / M
The last line of that code snippet would have been more complicated if we worked with non-alpha-premultiplied colors, which is why Color
uses alpha-premultiplied values.
The image/color package also defines a number of concrete types that implement the Color
interface. For example, RGBA
is a struct that represents the classic "8 bits per channel" color.
type RGBA struct {
R, G, B, A uint8
}
Note that the R
field of an RGBA
is an 8-bit alpha-premultiplied color in the range [0, 255]. RGBA
satisfies the Color
interface by multiplying that value by 0x101 to generate a 16-bit alpha-premultiplied color in the range [0, 65535]. Similarly, the NRGBA
struct type represents an 8-bit non-alpha-premultiplied color, as used by the PNG image format. When manipulating an NRGBA
's fields directly, the values are non-alpha-premultiplied, but when calling the RGBA
method, the return values are alpha-premultiplied.
A Model
is simply something that can convert `Color`s to other `Color`s, possibly lossily. For example, the GrayModel
can convert any Color
to a desaturated Gray
. A Palette
can convert any Color
to one from a limited palette.
type Model interface {
Convert(c Color) Color
} type Palette []Color
Points and Rectangles
A Point
is an (x, y) co-ordinate on the integer grid, with axes increasing right and down. It is neither a pixel nor a grid square. A Point
has no intrinsic width, height or color, but the visualizations below use a small colored square.
type Point struct {
X, Y int
}
p := image.Point{2, 1}
A Rectangle
is an axis-aligned rectangle on the integer grid, defined by its top-left and bottom-right Point
. A Rectangle
also has no intrinsic color, but the visualizations below outline rectangles with a thin colored line, and call out their Min
and Max
`Point`s.
type Rectangle struct {
Min, Max Point
}
For convenience, image.Rect(x0, y0, x1, y1)
is equivalent to image.Rectangle{image.Point{x0, y0}, image.Point{x1, y1}}
, but is much easier to type.
A Rectangle
is inclusive at the top-left and exclusive at the bottom-right. For a Point p
and a Rectangle r
, p.In(r)
if and only if r.Min.X <= p.X && p.X < r.Max.X
, and similarly for Y
. This is analogous to how a slice s[i0:i1]
is inclusive at the low end and exclusive at the high end. (Unlike arrays and slices, a Rectangle
often has a non-zero origin.)
r := image.Rect(2, 1, 5, 5)
// Dx and Dy return a rectangle's width and height.
fmt.Println(r.Dx(), r.Dy(), image.Pt(0, 0).In(r)) // prints 3 4 false
Adding a Point
to a Rectangle
translates the Rectangle
. Points and Rectangles are not restricted to be in the bottom-right quadrant.
r := image.Rect(2, 1, 5, 5).Add(image.Pt(-4, -2))
fmt.Println(r.Dx(), r.Dy(), image.Pt(0, 0).In(r)) // prints 3 4 true
Intersecting two Rectangles yields another Rectangle, which may be empty.
r := image.Rect(0, 0, 4, 3).Intersect(image.Rect(2, 2, 5, 5))
// Size returns a rectangle's width and height, as a Point.
fmt.Printf("%#v\n", r.Size()) // prints image.Point{X:2, Y:1}
Points and Rectangles are passed and returned by value. A function that takes a Rectangle
argument will be as efficient as a function that takes two Point
arguments, or four int
arguments.
Images
An Image maps every grid square in a Rectangle
to a Color
from a Model
. "The pixel at (x, y)" refers to the color of the grid square defined by the points (x, y), (x+1, y), (x+1, y+1) and (x, y+1).
type Image interface {
// ColorModel returns the Image's color model.
ColorModel() color.Model
// Bounds returns the domain for which At can return non-zero color.
// The bounds do not necessarily contain the point (0, 0).
Bounds() Rectangle
// At returns the color of the pixel at (x, y).
// At(Bounds().Min.X, Bounds().Min.Y) returns the upper-left pixel of the grid.
// At(Bounds().Max.X-1, Bounds().Max.Y-1) returns the lower-right one.
At(x, y int) color.Color
}
A common mistake is assuming that an Image
's bounds start at (0, 0). For example, an animated GIF contains a sequence of Images, and each Image
after the first typically only holds pixel data for the area that changed, and that area doesn't necessarily start at (0, 0). The correct way to iterate over an Image
m's pixels looks like:
b := m.Bounds()
for y := b.Min.Y; y < b.Max.Y; y++ {
for x := b.Min.X; x < b.Max.X; x++ {
doStuffWith(m.At(x, y))
}
}
Image
implementations do not have to be based on an in-memory slice of pixel data. For example, a Uniform
is an Image
of enormous bounds and uniform color, whose in-memory representation is simply that color.
type Uniform struct {
C color.Color
}
Typically, though, programs will want an image based on a slice. Struct types like RGBA
and Gray
(which other packages refer to as image.RGBA
and image.Gray
) hold slices of pixel data and implement the Image
interface.
type RGBA struct {
// Pix holds the image's pixels, in R, G, B, A order. The pixel at
// (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*4].
Pix []uint8
// Stride is the Pix stride (in bytes) between vertically adjacent pixels.
Stride int
// Rect is the image's bounds.
Rect Rectangle
}
These types also provide a Set(x, y int, c color.Color)
method that allows modifying the image one pixel at a time.
m := image.NewRGBA(image.Rect(0, 0, 640, 480))
m.Set(5, 5, color.RGBA{255, 0, 0, 255})
If you're reading or writing a lot of pixel data, it can be more efficient, but more complicated, to access these struct type's Pix
field directly.
The slice-based Image
implementations also provide a SubImage
method, which returns an Image
backed by the same array. Modifying the pixels of a sub-image will affect the pixels of the original image, analogous to how modifying the contents of a sub-slice s[i0:i1]
will affect the contents of the original slice s
.
m0 := image.NewRGBA(image.Rect(0, 0, 8, 5))
m1 := m0.SubImage(image.Rect(1, 2, 5, 5)).(*image.RGBA)
fmt.Println(m0.Bounds().Dx(), m1.Bounds().Dx()) // prints 8, 4
fmt.Println(m0.Stride == m1.Stride) // prints true
For low-level code that works on an image's Pix
field, be aware that ranging over Pix
can affect pixels outside an image's bounds. In the example above, the pixels covered by m1.Pix
are shaded in blue. Higher-level code, such as the At
and Set
methods or the image/draw package, will clip their operations to the image's bounds.
Image Formats
The standard package library supports a number of common image formats, such as GIF, JPEG and PNG. If you know the format of a source image file, you can decode from an io.Reader
directly.
import (
"image/jpeg"
"image/png"
"io"
) // convertJPEGToPNG converts from JPEG to PNG.
func convertJPEGToPNG(w io.Writer, r io.Reader) error {
img, err := jpeg.Decode(r)
if err != nil {
return err
}
return png.Encode(w, img)
}
If you have image data of unknown format, the image.Decode
function can detect the format. The set of recognized formats is constructed at run time and is not limited to those in the standard package library. An image format package typically registers its format in an init function, and the main package will "underscore import" such a package solely for the side effect of format registration.
import (
"image"
"image/png"
"io" _ "code.google.com/p/vp8-go/webp"
_ "image/jpeg"
) // convertToPNG converts from any recognized format to PNG.
func convertToPNG(w io.Writer, r io.Reader) error {
img, _, err := image.Decode(r)
if err != nil {
return err
}
return png.Encode(w, img)
}
By Nigel Tao
Related articles
- HTTP/2 Server Push
- Introducing HTTP Tracing
- Generating code
- Introducing the Go Race Detector
- Go maps in action
- go fmt your code
- Organizing Go code
- Debugging Go programs with the GNU Debugger
- The Go image/draw package
- The Laws of Reflection
- Error handling and Go
- "First Class Functions in Go"
- Profiling Go Programs
- Spotlight on external Go libraries
- A GIF decoder: an exercise in Go interfaces
- Introducing Gofix
- Godoc: documenting Go code
- Gobs of data
- C? Go? Cgo!
- JSON and Go
- Go Slices: usage and internals
- Go Concurrency Patterns: Timing out, moving on
- Defer, Panic, and Recover
- Share Memory By Communicating
- JSON-RPC: a tale of interfaces
24 The Go image package go图片包:图片包的基本原理的更多相关文章
- Java开发桌面程序学习(七)——ImageView设置图片以及jar包读取fxml文件
ImageView设置图片 JavaFx的ImageView,设置图片不能直接通过属性设置,只能通过代码来设置 ImageView设置图片 首先,我们让fxml对应的那个controller的java ...
- [js高手之路]深入浅出webpack教程系列9-打包图片(file-loader)用法
我们还是接着上文继续,本文我们要讲的是图片资源的打包,图片在静态排版中,经常出现的两个位置是css通过background引入背景,还有一种就是在html模板文件中用img标签引入的方式,如果要在we ...
- 解决 mac 10.14.4 无法 sublime text 3207 安装 Package Control,以及安装第三方包报错 `Package Control There are no packages available for installation`
下载最新的 sublime text 3207,无法安装 Package Control. 根据官方提示,手动安装 Package Control. 手动安装 Package Control 后,无法 ...
- Package.json中dependencies依赖包中^符号和~符号前缀的区别
刚git了webpack的包发现package.json里面dependencies依赖包的版本号前面的符号有两种,一种是~,一种是^,如下图标记: 然后搜了下在stackoverflow上找到一个比 ...
- eclipse批量修改package、import中的包名
问题:想把以前开发的包,用到新项目中,怎么操作呢? 解决方案: 把文件夹复制到新项目包中,同时需要更改很多package.import中的包名第一步:打开一个java文件,选中要替换的字段: 第二步: ...
- 下载了包在node_modules中,但没有在package.json中保存该包信息。
发现安装了包,但没有在package.json中保存该包信息,而且没有创建package-lock.json. 经过测试,发现是使用cnpm的原因,使用npm安装不会出现这样的问题,(与cnpm版本无 ...
- 利用POI获取Excel中图片和图片位置
利用POI获取Excel中图片和图片位置(支持excel2003or2007多sheet) 转自:http://blog.csdn.net/delongcpp/article/details/8833 ...
- [转]java nio解决半包 粘包问题
java nio解决半包 粘包问题 NIO socket是非阻塞的通讯模式,与IO阻塞式的通讯不同点在于NIO的数据要通过channel放到一个缓存池ByteBuffer中,然后再从这个缓存池中读出数 ...
- 关于war包 jar包 ear包 及打包方法
关于war包 jar包 ear包 及打包方法 war包:是做好一个web应用后,通常是网站打成包部署到容器中 jar包:通常是开发的时候要引用的通用类,打成包便于存放管理. ear包:企业级应用 通常 ...
- linux包之包管理命令rpm-yum
背景 YUM(Yellow dog Updater, Modified)为多个Linux发行版的前端软件包管理器,例如 Redhat RHEL, CentOS & Fedora. YUM通过调 ...
随机推荐
- AGC019
质量果然挺高的. A 贪心. ll Q,H,S,D,N; int main() { cin>>Q>>H>>S>>D>>N; H=min(H, ...
- Generate Parentheses - LeetCode
目录 题目链接 注意点 解法 小结 题目链接 Generate Parentheses - LeetCode 注意点 解法 解法一:递归.当left>right的时候返回(为了防止出现 )( ) ...
- React事件传递参数
<button onClick={(ev) => {this.handleClick(ev,arg1,arg2,...)}} 用箭头函数,注意第一个参数一定要是事件参数.
- bzoj 3190 赛车 半平面交
直接写的裸的半平面交,已经有点背不过模板了... 这题卡精度,要用long double ,esp设1e-20... #include<iostream> #include<cstd ...
- PyPt5 浏览器实例
title: PyPt5 浏览器实例 date: 2018-02-02 13:40:03 tags: Python PyQt5 便携浏览器 categries: Python --- 导入包 pyQt ...
- javascript实现div的显示和隐藏
http://www.cnblogs.com/oec2003/archive/2007/05/05/736492.html <html> <head> <meta htt ...
- 清除.svn文件(windows & linux)
如何清除文件夹中的.svn信息 1:来由 当需要在某个svn版本控制下添加某个包时, 常常是在另一个版本控制下sync过来, 但这是这个包是在别的版本控制下, 每个目录下都有版本控制文件.svn, 如 ...
- array_unshift() 函数用于向数组插入新元素。新数组的值将被插入到数组的开头。
<?php $a=array("a"=>"red","b"=>"green"); array_unsh ...
- Maven项目导出jar包配置
<!-- 第一种打包方式 (maven-jar-plugin), 将依赖包和配置文件放到jar包外 --> <build> <sourceDirectory>src ...
- js事件的捕获和冒泡阶段
讨论的主要是两个事件模型:IE事件模型与DOM事件模型 IE内核浏览器的事件模型是冒泡型事件(没有捕获事件过程),事件句柄的触发顺序是从ChildNode到ParentNode. <div id ...