chore(v2): vendor dependencies for offline/China builds
go mod vendor pins onnxruntime_go v1.12.1, Gio and the rest into v2/vendor so go run/build work without hitting proxy.golang.org (blocked/slow in China). Verified: CGO_ENABLED=1 go build -mod=vendor ./internal/spike and GOOS=windows go build -mod=vendor ./internal/ui both pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+27
@@ -0,0 +1,27 @@
|
||||
Copyright 2009 The Go Authors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google LLC nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+343
@@ -0,0 +1,343 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package iconvg
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"math"
|
||||
)
|
||||
|
||||
// buffer holds an encoded IconVG graphic.
|
||||
//
|
||||
// The decodeXxx methods return the decoded value and an integer n, the number
|
||||
// of bytes that value was encoded in. They return n == 0 if an error occurred.
|
||||
//
|
||||
// The encodeXxx methods append to the buffer, modifying the slice in place.
|
||||
type buffer []byte
|
||||
|
||||
func (b buffer) decodeNatural() (u uint32, n int) {
|
||||
if len(b) < 1 {
|
||||
return 0, 0
|
||||
}
|
||||
x := b[0]
|
||||
if x&0x01 == 0 {
|
||||
return uint32(x) >> 1, 1
|
||||
}
|
||||
if x&0x02 == 0 {
|
||||
if len(b) >= 2 {
|
||||
y := uint16(b[0]) | uint16(b[1])<<8
|
||||
return uint32(y) >> 2, 2
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
if len(b) >= 4 {
|
||||
y := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
return y >> 2, 4
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// decodeNaturalFFV1 is like decodeNatural but for File Format Version 1. See
|
||||
// https://github.com/google/iconvg/issues/33
|
||||
func (b buffer) decodeNaturalFFV1() (u uint32, n int) {
|
||||
if len(b) < 1 {
|
||||
return 0, 0
|
||||
}
|
||||
x := b[0]
|
||||
if x&0x01 != 0 {
|
||||
return uint32(x) >> 1, 1
|
||||
}
|
||||
if x&0x02 != 0 {
|
||||
if len(b) >= 2 {
|
||||
y := uint16(b[0]) | uint16(b[1])<<8
|
||||
return uint32(y) >> 2, 2
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
if len(b) >= 4 {
|
||||
y := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
return y >> 2, 4
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
func (b buffer) decodeReal() (f float32, n int) {
|
||||
switch u, n := b.decodeNatural(); n {
|
||||
case 0:
|
||||
return 0, n
|
||||
case 1:
|
||||
return float32(u), n
|
||||
case 2:
|
||||
return float32(u), n
|
||||
default:
|
||||
return math.Float32frombits(u << 2), n
|
||||
}
|
||||
}
|
||||
|
||||
func (b buffer) decodeCoordinate() (f float32, n int) {
|
||||
switch u, n := b.decodeNatural(); n {
|
||||
case 0:
|
||||
return 0, n
|
||||
case 1:
|
||||
return float32(int32(u) - 64), n
|
||||
case 2:
|
||||
return float32(int32(u)-64*128) / 64, n
|
||||
default:
|
||||
return math.Float32frombits(u << 2), n
|
||||
}
|
||||
}
|
||||
|
||||
func (b buffer) decodeZeroToOne() (f float32, n int) {
|
||||
switch u, n := b.decodeNatural(); n {
|
||||
case 0:
|
||||
return 0, n
|
||||
case 1:
|
||||
return float32(u) / 120, n
|
||||
case 2:
|
||||
return float32(u) / 15120, n
|
||||
default:
|
||||
return math.Float32frombits(u << 2), n
|
||||
}
|
||||
}
|
||||
|
||||
func (b buffer) decodeColor1() (c Color, n int) {
|
||||
if len(b) < 1 {
|
||||
return Color{}, 0
|
||||
}
|
||||
return decodeColor1(b[0]), 1
|
||||
}
|
||||
|
||||
func (b buffer) decodeColor2() (c Color, n int) {
|
||||
if len(b) < 2 {
|
||||
return Color{}, 0
|
||||
}
|
||||
return RGBAColor(color.RGBA{
|
||||
R: 0x11 * (b[0] >> 4),
|
||||
G: 0x11 * (b[0] & 0x0f),
|
||||
B: 0x11 * (b[1] >> 4),
|
||||
A: 0x11 * (b[1] & 0x0f),
|
||||
}), 2
|
||||
}
|
||||
|
||||
func (b buffer) decodeColor3Direct() (c Color, n int) {
|
||||
if len(b) < 3 {
|
||||
return Color{}, 0
|
||||
}
|
||||
return RGBAColor(color.RGBA{
|
||||
R: b[0],
|
||||
G: b[1],
|
||||
B: b[2],
|
||||
A: 0xff,
|
||||
}), 3
|
||||
}
|
||||
|
||||
func (b buffer) decodeColor4() (c Color, n int) {
|
||||
if len(b) < 4 {
|
||||
return Color{}, 0
|
||||
}
|
||||
return RGBAColor(color.RGBA{
|
||||
R: b[0],
|
||||
G: b[1],
|
||||
B: b[2],
|
||||
A: b[3],
|
||||
}), 4
|
||||
}
|
||||
|
||||
func (b buffer) decodeColor3Indirect() (c Color, n int) {
|
||||
if len(b) < 3 {
|
||||
return Color{}, 0
|
||||
}
|
||||
return BlendColor(b[0], b[1], b[2]), 3
|
||||
}
|
||||
|
||||
func (b *buffer) encodeNatural(u uint32) {
|
||||
if u < 1<<7 {
|
||||
u = (u << 1)
|
||||
*b = append(*b, uint8(u))
|
||||
return
|
||||
}
|
||||
if u < 1<<14 {
|
||||
u = (u << 2) | 1
|
||||
*b = append(*b, uint8(u), uint8(u>>8))
|
||||
return
|
||||
}
|
||||
u = (u << 2) | 3
|
||||
*b = append(*b, uint8(u), uint8(u>>8), uint8(u>>16), uint8(u>>24))
|
||||
}
|
||||
|
||||
// encodeNaturalFFV1 is like encodeNatural but for File Format Version 1. See
|
||||
// https://github.com/google/iconvg/issues/33
|
||||
func (b *buffer) encodeNaturalFFV1(u uint32) {
|
||||
if u < 1<<7 {
|
||||
u = (u << 1) | 0x01
|
||||
*b = append(*b, uint8(u))
|
||||
return
|
||||
}
|
||||
if u < 1<<14 {
|
||||
u = (u << 2) | 0x02
|
||||
*b = append(*b, uint8(u), uint8(u>>8))
|
||||
return
|
||||
}
|
||||
u = (u << 2)
|
||||
*b = append(*b, uint8(u), uint8(u>>8), uint8(u>>16), uint8(u>>24))
|
||||
}
|
||||
|
||||
func (b *buffer) encodeReal(f float32) int {
|
||||
if u := uint32(f); float32(u) == f && u < 1<<14 {
|
||||
if u < 1<<7 {
|
||||
u = (u << 1)
|
||||
*b = append(*b, uint8(u))
|
||||
return 1
|
||||
}
|
||||
u = (u << 2) | 1
|
||||
*b = append(*b, uint8(u), uint8(u>>8))
|
||||
return 2
|
||||
}
|
||||
b.encode4ByteReal(f)
|
||||
return 4
|
||||
}
|
||||
|
||||
func (b *buffer) encode4ByteReal(f float32) {
|
||||
u := math.Float32bits(f)
|
||||
|
||||
// Round the fractional bits (the low 23 bits) to the nearest multiple of
|
||||
// 4, being careful not to overflow into the upper bits.
|
||||
v := u & 0x007fffff
|
||||
if v < 0x007ffffe {
|
||||
v += 2
|
||||
}
|
||||
u = (u & 0xff800000) | v
|
||||
|
||||
// A 4 byte encoding has the low two bits set.
|
||||
u |= 0x03
|
||||
*b = append(*b, uint8(u), uint8(u>>8), uint8(u>>16), uint8(u>>24))
|
||||
}
|
||||
|
||||
// encode4ByteRealFFV1 is like encode4ByteReal but for File Format Version 1.
|
||||
// See https://github.com/google/iconvg/issues/33
|
||||
func (b *buffer) encode4ByteRealFFV1(f float32) {
|
||||
u := math.Float32bits(f)
|
||||
|
||||
// Round the fractional bits (the low 23 bits) to the nearest multiple of
|
||||
// 4, being careful not to overflow into the upper bits.
|
||||
v := u & 0x007fffff
|
||||
if v < 0x007ffffe {
|
||||
v += 2
|
||||
}
|
||||
u = (u & 0xff800000) | v
|
||||
|
||||
// A 4 byte encoding has the low two bits unset.
|
||||
u &= 0xfffffffc
|
||||
*b = append(*b, uint8(u), uint8(u>>8), uint8(u>>16), uint8(u>>24))
|
||||
}
|
||||
|
||||
func (b *buffer) encodeCoordinate(f float32) int {
|
||||
if i := int32(f); -64 <= i && i < +64 && float32(i) == f {
|
||||
u := uint32(i + 64)
|
||||
u = (u << 1)
|
||||
*b = append(*b, uint8(u))
|
||||
return 1
|
||||
}
|
||||
if i := int32(f * 64); -128*64 <= i && i < +128*64 && float32(i) == f*64 {
|
||||
u := uint32(i + 128*64)
|
||||
u = (u << 2) | 1
|
||||
*b = append(*b, uint8(u), uint8(u>>8))
|
||||
return 2
|
||||
}
|
||||
b.encode4ByteReal(f)
|
||||
return 4
|
||||
}
|
||||
|
||||
// encodeCoordinateFFV1 is like encodeCoordinate but for File Format Version 1.
|
||||
// See https://github.com/google/iconvg/issues/33
|
||||
func (b *buffer) encodeCoordinateFFV1(f float32) int {
|
||||
if i := int32(f); -64 <= i && i < +64 && float32(i) == f {
|
||||
u := uint32(i + 64)
|
||||
u = (u << 1) | 0x01
|
||||
*b = append(*b, uint8(u))
|
||||
return 1
|
||||
}
|
||||
if i := int32(f * 64); -128*64 <= i && i < +128*64 && float32(i) == f*64 {
|
||||
u := uint32(i + 128*64)
|
||||
u = (u << 2) | 0x02
|
||||
*b = append(*b, uint8(u), uint8(u>>8))
|
||||
return 2
|
||||
}
|
||||
b.encode4ByteRealFFV1(f)
|
||||
return 4
|
||||
}
|
||||
|
||||
func (b *buffer) encodeCoordinatePairFFV1(f [2]float32) int {
|
||||
n0 := b.encodeCoordinateFFV1(f[0])
|
||||
n1 := b.encodeCoordinateFFV1(f[1])
|
||||
return n0 + n1
|
||||
}
|
||||
|
||||
func (b *buffer) encodeAngle(f float32) int {
|
||||
// Normalize f to the range [0, 1).
|
||||
g := float64(f)
|
||||
g -= math.Floor(g)
|
||||
return b.encodeZeroToOne(float32(g))
|
||||
}
|
||||
|
||||
func (b *buffer) encodeZeroToOne(f float32) int {
|
||||
if u := uint32(f * 15120); float32(u) == f*15120 && u < 15120 {
|
||||
if u%126 == 0 {
|
||||
u = ((u / 126) << 1)
|
||||
*b = append(*b, uint8(u))
|
||||
return 1
|
||||
}
|
||||
u = (u << 2) | 1
|
||||
*b = append(*b, uint8(u), uint8(u>>8))
|
||||
return 2
|
||||
}
|
||||
b.encode4ByteReal(f)
|
||||
return 4
|
||||
}
|
||||
|
||||
func (b *buffer) encodeColor1(c Color) {
|
||||
if x, ok := encodeColor1(c); ok {
|
||||
*b = append(*b, x)
|
||||
return
|
||||
}
|
||||
// Default to opaque black.
|
||||
*b = append(*b, 0x00)
|
||||
}
|
||||
|
||||
func (b *buffer) encodeColor2(c Color) {
|
||||
if x, ok := encodeColor2(c); ok {
|
||||
*b = append(*b, x[0], x[1])
|
||||
return
|
||||
}
|
||||
// Default to opaque black.
|
||||
*b = append(*b, 0x00, 0x0f)
|
||||
}
|
||||
|
||||
func (b *buffer) encodeColor3Direct(c Color) {
|
||||
if x, ok := encodeColor3Direct(c); ok {
|
||||
*b = append(*b, x[0], x[1], x[2])
|
||||
return
|
||||
}
|
||||
// Default to opaque black.
|
||||
*b = append(*b, 0x00, 0x00, 0x00)
|
||||
}
|
||||
|
||||
func (b *buffer) encodeColor4(c Color) {
|
||||
if x, ok := encodeColor4(c); ok {
|
||||
*b = append(*b, x[0], x[1], x[2], x[3])
|
||||
return
|
||||
}
|
||||
// Default to opaque black.
|
||||
*b = append(*b, 0x00, 0x00, 0x00, 0xff)
|
||||
}
|
||||
|
||||
func (b *buffer) encodeColor3Indirect(c Color) {
|
||||
if x, ok := encodeColor3Indirect(c); ok {
|
||||
*b = append(*b, x[0], x[1], x[2])
|
||||
return
|
||||
}
|
||||
// Default to opaque black.
|
||||
*b = append(*b, 0x00, 0x00, 0x00)
|
||||
}
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package iconvg
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
)
|
||||
|
||||
func validAlphaPremulColor(c color.RGBA) bool {
|
||||
return c.R <= c.A && c.G <= c.A && c.B <= c.A
|
||||
}
|
||||
|
||||
// ColorType distinguishes types of Colors.
|
||||
type ColorType uint8
|
||||
|
||||
const (
|
||||
// ColorTypeRGBA is a direct RGBA color.
|
||||
ColorTypeRGBA ColorType = iota
|
||||
|
||||
// ColorTypePaletteIndex is an indirect color, indexing the custom palette.
|
||||
ColorTypePaletteIndex
|
||||
|
||||
// ColorTypeCReg is an indirect color, indexing the CREG color registers.
|
||||
ColorTypeCReg
|
||||
|
||||
// ColorTypeBlend is an indirect color, blending two other colors.
|
||||
ColorTypeBlend
|
||||
)
|
||||
|
||||
// Color is an IconVG color, whose RGBA values can depend on context. Some
|
||||
// Colors are direct RGBA values. Other Colors are indirect, referring to an
|
||||
// index of the custom palette, a color register of the decoder virtual
|
||||
// machine, or a blend of two other Colors.
|
||||
//
|
||||
// See the "Colors" section in the package documentation for details.
|
||||
type Color struct {
|
||||
typ ColorType
|
||||
data color.RGBA
|
||||
}
|
||||
|
||||
func (c Color) rgba() color.RGBA { return c.data }
|
||||
func (c Color) paletteIndex() uint8 { return c.data.R }
|
||||
func (c Color) cReg() uint8 { return c.data.R }
|
||||
func (c Color) blend() (t, c0, c1 uint8) { return c.data.R, c.data.G, c.data.B }
|
||||
|
||||
// Resolve resolves the Color's RGBA value, given its context: the custom
|
||||
// palette and the color registers of the decoder virtual machine.
|
||||
func (c Color) Resolve(pal *Palette, cReg *[64]color.RGBA) color.RGBA {
|
||||
switch c.typ {
|
||||
case ColorTypeRGBA:
|
||||
return c.rgba()
|
||||
case ColorTypePaletteIndex:
|
||||
return pal[c.paletteIndex()&0x3f]
|
||||
case ColorTypeCReg:
|
||||
return cReg[c.cReg()&0x3f]
|
||||
}
|
||||
t, c0, c1 := c.blend()
|
||||
p, q := uint32(255-t), uint32(t)
|
||||
rgba0 := decodeColor1(c0).Resolve(pal, cReg)
|
||||
rgba1 := decodeColor1(c1).Resolve(pal, cReg)
|
||||
return color.RGBA{
|
||||
uint8(((p * uint32(rgba0.R)) + q*uint32(rgba1.R) + 128) / 255),
|
||||
uint8(((p * uint32(rgba0.G)) + q*uint32(rgba1.G) + 128) / 255),
|
||||
uint8(((p * uint32(rgba0.B)) + q*uint32(rgba1.B) + 128) / 255),
|
||||
uint8(((p * uint32(rgba0.A)) + q*uint32(rgba1.A) + 128) / 255),
|
||||
}
|
||||
}
|
||||
|
||||
// RGBAColor returns a direct Color.
|
||||
func RGBAColor(c color.RGBA) Color { return Color{ColorTypeRGBA, c} }
|
||||
|
||||
// PaletteIndexColor returns an indirect Color referring to an index of the
|
||||
// custom palette.
|
||||
func PaletteIndexColor(i uint8) Color { return Color{ColorTypePaletteIndex, color.RGBA{R: i & 0x3f}} }
|
||||
|
||||
// CRegColor returns an indirect Color referring to a color register of the
|
||||
// decoder virtual machine.
|
||||
func CRegColor(i uint8) Color { return Color{ColorTypeCReg, color.RGBA{R: i & 0x3f}} }
|
||||
|
||||
// BlendColor returns an indirect Color that blends two other Colors. Those two
|
||||
// other Colors must both be encodable as a 1 byte color.
|
||||
//
|
||||
// To blend a Color that is not encodable as a 1 byte color, first load that
|
||||
// Color into a CREG color register, then call CRegColor to produce a Color
|
||||
// that is encodable as a 1 byte color. See testdata/favicon.ivg for an
|
||||
// example.
|
||||
//
|
||||
// See the "Colors" section in the package documentation for details.
|
||||
func BlendColor(t, c0, c1 uint8) Color { return Color{ColorTypeBlend, color.RGBA{R: t, G: c0, B: c1}} }
|
||||
|
||||
func decodeColor1(x byte) Color {
|
||||
if x >= 0x80 {
|
||||
if x >= 0xc0 {
|
||||
return CRegColor(x)
|
||||
} else {
|
||||
return PaletteIndexColor(x)
|
||||
}
|
||||
}
|
||||
if x >= 125 {
|
||||
switch x - 125 {
|
||||
case 0:
|
||||
return RGBAColor(color.RGBA{0xc0, 0xc0, 0xc0, 0xc0})
|
||||
case 1:
|
||||
return RGBAColor(color.RGBA{0x80, 0x80, 0x80, 0x80})
|
||||
case 2:
|
||||
return RGBAColor(color.RGBA{0x00, 0x00, 0x00, 0x00})
|
||||
}
|
||||
}
|
||||
blue := dc1Table[x%5]
|
||||
x = x / 5
|
||||
green := dc1Table[x%5]
|
||||
x = x / 5
|
||||
red := dc1Table[x]
|
||||
return RGBAColor(color.RGBA{red, green, blue, 0xff})
|
||||
}
|
||||
|
||||
var dc1Table = [5]byte{0x00, 0x40, 0x80, 0xc0, 0xff}
|
||||
|
||||
func is1(u uint8) bool { return u&0x3f == 0 || u == 0xff }
|
||||
|
||||
func encodeColor1(c Color) (x byte, ok bool) {
|
||||
switch c.typ {
|
||||
case ColorTypeRGBA:
|
||||
if c.data.A != 0xff {
|
||||
switch c.data {
|
||||
case color.RGBA{0x00, 0x00, 0x00, 0x00}:
|
||||
return 127, true
|
||||
case color.RGBA{0x80, 0x80, 0x80, 0x80}:
|
||||
return 126, true
|
||||
case color.RGBA{0xc0, 0xc0, 0xc0, 0xc0}:
|
||||
return 125, true
|
||||
}
|
||||
} else if is1(c.data.R) && is1(c.data.G) && is1(c.data.B) && is1(c.data.A) {
|
||||
r := c.data.R / 0x3f
|
||||
g := c.data.G / 0x3f
|
||||
b := c.data.B / 0x3f
|
||||
return 25*r + 5*g + b, true
|
||||
}
|
||||
case ColorTypePaletteIndex:
|
||||
return c.data.R | 0x80, true
|
||||
case ColorTypeCReg:
|
||||
return c.data.R | 0xc0, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func is2(u uint8) bool { return u%0x11 == 0 }
|
||||
|
||||
func encodeColor2(c Color) (x [2]byte, ok bool) {
|
||||
if c.typ == ColorTypeRGBA && is2(c.data.R) && is2(c.data.G) && is2(c.data.B) && is2(c.data.A) {
|
||||
return [2]byte{
|
||||
(c.data.R/0x11)<<4 | (c.data.G / 0x11),
|
||||
(c.data.B/0x11)<<4 | (c.data.A / 0x11),
|
||||
}, true
|
||||
}
|
||||
return [2]byte{}, false
|
||||
}
|
||||
|
||||
func encodeColor3Direct(c Color) (x [3]byte, ok bool) {
|
||||
if c.typ == ColorTypeRGBA && c.data.A == 0xff {
|
||||
return [3]byte{c.data.R, c.data.G, c.data.B}, true
|
||||
}
|
||||
return [3]byte{}, false
|
||||
}
|
||||
|
||||
func encodeColor4(c Color) (x [4]byte, ok bool) {
|
||||
if c.typ == ColorTypeRGBA {
|
||||
return [4]byte{c.data.R, c.data.G, c.data.B, c.data.A}, true
|
||||
}
|
||||
return [4]byte{}, false
|
||||
}
|
||||
|
||||
func encodeColor3Indirect(c Color) (x [3]byte, ok bool) {
|
||||
if c.typ == ColorTypeBlend {
|
||||
return [3]byte{c.data.R, c.data.G, c.data.B}, true
|
||||
}
|
||||
return [3]byte{}, false
|
||||
}
|
||||
+699
@@ -0,0 +1,699 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package iconvg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"image/color"
|
||||
)
|
||||
|
||||
var (
|
||||
errInconsistentMetadataChunkLength = errors.New("iconvg: inconsistent metadata chunk length")
|
||||
errInvalidColor = errors.New("iconvg: invalid color")
|
||||
errInvalidMagicIdentifier = errors.New("iconvg: invalid magic identifier")
|
||||
errInvalidMetadataChunkLength = errors.New("iconvg: invalid metadata chunk length")
|
||||
errInvalidMetadataIdentifier = errors.New("iconvg: invalid metadata identifier")
|
||||
errInvalidNumber = errors.New("iconvg: invalid number")
|
||||
errInvalidNumberOfMetadataChunks = errors.New("iconvg: invalid number of metadata chunks")
|
||||
errInvalidSuggestedPalette = errors.New("iconvg: invalid suggested palette")
|
||||
errInvalidViewBox = errors.New("iconvg: invalid view box")
|
||||
errUnsupportedDrawingOpcode = errors.New("iconvg: unsupported drawing opcode")
|
||||
errUnsupportedMetadataIdentifier = errors.New("iconvg: unsupported metadata identifier")
|
||||
errUnsupportedStylingOpcode = errors.New("iconvg: unsupported styling opcode")
|
||||
errUnsupportedUpgrade = errors.New("iconvg: unsupported upgrade")
|
||||
)
|
||||
|
||||
var midDescriptions = [...]string{
|
||||
midViewBox: "viewBox",
|
||||
midSuggestedPalette: "suggested palette",
|
||||
}
|
||||
|
||||
// Destination handles the actions decoded from an IconVG graphic's opcodes.
|
||||
//
|
||||
// When passed to Decode, the first method called (if any) will be Reset. No
|
||||
// methods will be called at all if an error is encountered in the encoded form
|
||||
// before the metadata is fully decoded.
|
||||
type Destination interface {
|
||||
Reset(m Metadata)
|
||||
|
||||
SetCSel(cSel uint8)
|
||||
SetNSel(nSel uint8)
|
||||
SetCReg(adj uint8, incr bool, c Color)
|
||||
SetNReg(adj uint8, incr bool, f float32)
|
||||
SetLOD(lod0, lod1 float32)
|
||||
|
||||
StartPath(adj uint8, x, y float32)
|
||||
ClosePathEndPath()
|
||||
ClosePathAbsMoveTo(x, y float32)
|
||||
ClosePathRelMoveTo(x, y float32)
|
||||
|
||||
AbsHLineTo(x float32)
|
||||
RelHLineTo(x float32)
|
||||
AbsVLineTo(y float32)
|
||||
RelVLineTo(y float32)
|
||||
AbsLineTo(x, y float32)
|
||||
RelLineTo(x, y float32)
|
||||
AbsSmoothQuadTo(x, y float32)
|
||||
RelSmoothQuadTo(x, y float32)
|
||||
AbsQuadTo(x1, y1, x, y float32)
|
||||
RelQuadTo(x1, y1, x, y float32)
|
||||
AbsSmoothCubeTo(x2, y2, x, y float32)
|
||||
RelSmoothCubeTo(x2, y2, x, y float32)
|
||||
AbsCubeTo(x1, y1, x2, y2, x, y float32)
|
||||
RelCubeTo(x1, y1, x2, y2, x, y float32)
|
||||
AbsArcTo(rx, ry, xAxisRotation float32, largeArc, sweep bool, x, y float32)
|
||||
RelArcTo(rx, ry, xAxisRotation float32, largeArc, sweep bool, x, y float32)
|
||||
}
|
||||
|
||||
type printer func(b []byte, format string, args ...interface{})
|
||||
|
||||
// DecodeOptions are the optional parameters to the Decode function.
|
||||
type DecodeOptions struct {
|
||||
// Palette is an optional 64 color palette. If one isn't provided, the
|
||||
// IconVG graphic's suggested palette will be used.
|
||||
Palette *Palette
|
||||
}
|
||||
|
||||
// DecodeMetadata decodes only the metadata in an IconVG graphic.
|
||||
func DecodeMetadata(src []byte) (m Metadata, err error) {
|
||||
m.ViewBox = DefaultViewBox
|
||||
m.Palette = DefaultPalette
|
||||
if err = decode(nil, nil, &m, true, src, nil); err != nil {
|
||||
return Metadata{}, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Decode decodes an IconVG graphic.
|
||||
func Decode(dst Destination, src []byte, opts *DecodeOptions) error {
|
||||
m := Metadata{
|
||||
ViewBox: DefaultViewBox,
|
||||
Palette: DefaultPalette,
|
||||
}
|
||||
if opts != nil && opts.Palette != nil {
|
||||
m.Palette = *opts.Palette
|
||||
}
|
||||
return decode(dst, nil, &m, false, src, opts)
|
||||
}
|
||||
|
||||
func decode(dst Destination, p printer, m *Metadata, metadataOnly bool, src buffer, opts *DecodeOptions) (err error) {
|
||||
if !bytes.HasPrefix(src, magicBytes) {
|
||||
// TODO: detect FFV 1 (File Format Version 1), as opposed to the FFV 0
|
||||
// that this package implements, and delegate to a FFV 1 decoder.
|
||||
return errInvalidMagicIdentifier
|
||||
}
|
||||
if p != nil {
|
||||
p(src[:len(magic)], "IconVG Magic identifier\n")
|
||||
}
|
||||
src = src[len(magic):]
|
||||
|
||||
nMetadataChunks, n := src.decodeNatural()
|
||||
if n == 0 {
|
||||
return errInvalidNumberOfMetadataChunks
|
||||
}
|
||||
if p != nil {
|
||||
p(src[:n], "Number of metadata chunks: %d\n", nMetadataChunks)
|
||||
}
|
||||
src = src[n:]
|
||||
|
||||
for ; nMetadataChunks > 0; nMetadataChunks-- {
|
||||
src, err = decodeMetadataChunk(p, m, src, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if metadataOnly {
|
||||
return nil
|
||||
}
|
||||
if dst != nil {
|
||||
dst.Reset(*m)
|
||||
}
|
||||
|
||||
mf := modeFunc(decodeStyling)
|
||||
for len(src) > 0 {
|
||||
mf, src, err = mf(dst, p, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeMetadataChunk(p printer, m *Metadata, src buffer, opts *DecodeOptions) (src1 buffer, err error) {
|
||||
length, n := src.decodeNatural()
|
||||
if n == 0 {
|
||||
return nil, errInvalidMetadataChunkLength
|
||||
}
|
||||
if p != nil {
|
||||
p(src[:n], "Metadata chunk length: %d\n", length)
|
||||
}
|
||||
src = src[n:]
|
||||
lenSrcWant := int64(len(src)) - int64(length)
|
||||
|
||||
mid, n := src.decodeNatural()
|
||||
if n == 0 {
|
||||
return nil, errInvalidMetadataIdentifier
|
||||
}
|
||||
if mid >= uint32(len(midDescriptions)) {
|
||||
return nil, errUnsupportedMetadataIdentifier
|
||||
}
|
||||
if p != nil {
|
||||
p(src[:n], "Metadata Identifier: %d (%s)\n", mid, midDescriptions[mid])
|
||||
}
|
||||
src = src[n:]
|
||||
|
||||
switch mid {
|
||||
case midViewBox:
|
||||
if m.ViewBox.Min[0], src, err = decodeNumber(p, src, buffer.decodeCoordinate); err != nil {
|
||||
return nil, errInvalidViewBox
|
||||
}
|
||||
if m.ViewBox.Min[1], src, err = decodeNumber(p, src, buffer.decodeCoordinate); err != nil {
|
||||
return nil, errInvalidViewBox
|
||||
}
|
||||
if m.ViewBox.Max[0], src, err = decodeNumber(p, src, buffer.decodeCoordinate); err != nil {
|
||||
return nil, errInvalidViewBox
|
||||
}
|
||||
if m.ViewBox.Max[1], src, err = decodeNumber(p, src, buffer.decodeCoordinate); err != nil {
|
||||
return nil, errInvalidViewBox
|
||||
}
|
||||
if m.ViewBox.Min[0] > m.ViewBox.Max[0] || m.ViewBox.Min[1] > m.ViewBox.Max[1] ||
|
||||
isNaNOrInfinity(m.ViewBox.Min[0]) || isNaNOrInfinity(m.ViewBox.Min[1]) ||
|
||||
isNaNOrInfinity(m.ViewBox.Max[0]) || isNaNOrInfinity(m.ViewBox.Max[1]) {
|
||||
return nil, errInvalidViewBox
|
||||
}
|
||||
|
||||
case midSuggestedPalette:
|
||||
if len(src) == 0 {
|
||||
return nil, errInvalidSuggestedPalette
|
||||
}
|
||||
length, format := 1+int(src[0]&0x3f), src[0]>>6
|
||||
decode := buffer.decodeColor4
|
||||
switch format {
|
||||
case 0:
|
||||
decode = buffer.decodeColor1
|
||||
case 1:
|
||||
decode = buffer.decodeColor2
|
||||
case 2:
|
||||
decode = buffer.decodeColor3Direct
|
||||
}
|
||||
if p != nil {
|
||||
p(src[:1], " %d palette colors, %d bytes per color\n", length, 1+format)
|
||||
}
|
||||
src = src[1:]
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
c, n := decode(src)
|
||||
if n == 0 {
|
||||
return nil, errInvalidSuggestedPalette
|
||||
}
|
||||
rgba := c.rgba()
|
||||
if c.typ != ColorTypeRGBA || !validAlphaPremulColor(rgba) {
|
||||
rgba = color.RGBA{0x00, 0x00, 0x00, 0xff}
|
||||
}
|
||||
if p != nil {
|
||||
p(src[:n], " RGBA %02x%02x%02x%02x\n", rgba.R, rgba.G, rgba.B, rgba.A)
|
||||
}
|
||||
src = src[n:]
|
||||
if opts == nil || opts.Palette == nil {
|
||||
m.Palette[i] = rgba
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, errUnsupportedMetadataIdentifier
|
||||
}
|
||||
|
||||
if int64(len(src)) != lenSrcWant {
|
||||
return nil, errInconsistentMetadataChunkLength
|
||||
}
|
||||
return src, nil
|
||||
}
|
||||
|
||||
// modeFunc is the decoding mode: whether we are decoding styling or drawing
|
||||
// opcodes.
|
||||
//
|
||||
// It is a function type. The decoding loop calls this function to decode and
|
||||
// execute the next opcode from the src buffer, returning the subsequent mode
|
||||
// and the remaining source bytes.
|
||||
type modeFunc func(dst Destination, p printer, src buffer) (modeFunc, buffer, error)
|
||||
|
||||
func decodeStyling(dst Destination, p printer, src buffer) (modeFunc, buffer, error) {
|
||||
switch opcode := src[0]; {
|
||||
case opcode < 0x80:
|
||||
if opcode < 0x40 {
|
||||
opcode &= 0x3f
|
||||
if p != nil {
|
||||
p(src[:1], "Set CSEL = %d\n", opcode)
|
||||
}
|
||||
src = src[1:]
|
||||
if dst != nil {
|
||||
dst.SetCSel(opcode)
|
||||
}
|
||||
} else {
|
||||
opcode &= 0x3f
|
||||
if p != nil {
|
||||
p(src[:1], "Set NSEL = %d\n", opcode)
|
||||
}
|
||||
src = src[1:]
|
||||
if dst != nil {
|
||||
dst.SetNSel(opcode)
|
||||
}
|
||||
}
|
||||
return decodeStyling, src, nil
|
||||
case opcode < 0xa8:
|
||||
return decodeSetCReg(dst, p, src, opcode)
|
||||
case opcode < 0xc0:
|
||||
return decodeSetNReg(dst, p, src, opcode)
|
||||
case opcode < 0xc7:
|
||||
return decodeStartPath(dst, p, src, opcode)
|
||||
case opcode == 0xc7:
|
||||
return decodeSetLOD(dst, p, src)
|
||||
}
|
||||
return nil, nil, errUnsupportedStylingOpcode
|
||||
}
|
||||
|
||||
func decodeSetCReg(dst Destination, p printer, src buffer, opcode byte) (modeFunc, buffer, error) {
|
||||
nBytes, directness, adj := 0, "", opcode&0x07
|
||||
var decode func(buffer) (Color, int)
|
||||
incr := adj == 7
|
||||
if incr {
|
||||
adj = 0
|
||||
}
|
||||
|
||||
switch (opcode - 0x80) >> 3 {
|
||||
case 0:
|
||||
nBytes, directness, decode = 1, "", buffer.decodeColor1
|
||||
case 1:
|
||||
nBytes, directness, decode = 2, "", buffer.decodeColor2
|
||||
case 2:
|
||||
nBytes, directness, decode = 3, " (direct)", buffer.decodeColor3Direct
|
||||
case 3:
|
||||
nBytes, directness, decode = 4, "", buffer.decodeColor4
|
||||
case 4:
|
||||
nBytes, directness, decode = 3, " (indirect)", buffer.decodeColor3Indirect
|
||||
}
|
||||
if p != nil {
|
||||
if incr {
|
||||
p(src[:1], "Set CREG[CSEL-0] to a %d byte%s color; CSEL++\n", nBytes, directness)
|
||||
} else {
|
||||
p(src[:1], "Set CREG[CSEL-%d] to a %d byte%s color\n", adj, nBytes, directness)
|
||||
}
|
||||
}
|
||||
src = src[1:]
|
||||
|
||||
c, n := decode(src)
|
||||
if n == 0 {
|
||||
return nil, nil, errInvalidColor
|
||||
}
|
||||
|
||||
if p != nil {
|
||||
printColor(src[:n], p, c, "")
|
||||
}
|
||||
src = src[n:]
|
||||
|
||||
if dst != nil {
|
||||
dst.SetCReg(adj, incr, c)
|
||||
}
|
||||
|
||||
return decodeStyling, src, nil
|
||||
}
|
||||
|
||||
func printColor(src []byte, p printer, c Color, prefix string) {
|
||||
switch c.typ {
|
||||
case ColorTypeRGBA:
|
||||
if rgba := c.rgba(); validAlphaPremulColor(rgba) {
|
||||
p(src, " %sRGBA %02x%02x%02x%02x\n", prefix, rgba.R, rgba.G, rgba.B, rgba.A)
|
||||
} else if rgba.A == 0 && rgba.B&0x80 != 0 {
|
||||
p(src, " %sgradient (NSTOPS=%d, CBASE=%d, NBASE=%d, %s, %s)\n",
|
||||
prefix,
|
||||
rgba.R&0x3f,
|
||||
rgba.G&0x3f,
|
||||
rgba.B&0x3f,
|
||||
gradientShapeNames[(rgba.B>>6)&0x01],
|
||||
gradientSpreadNames[rgba.G>>6],
|
||||
)
|
||||
} else {
|
||||
p(src, " %snonsensical color\n", prefix)
|
||||
}
|
||||
case ColorTypePaletteIndex:
|
||||
p(src, " %scustomPalette[%d]\n", prefix, c.paletteIndex())
|
||||
case ColorTypeCReg:
|
||||
p(src, " %sCREG[%d]\n", prefix, c.cReg())
|
||||
case ColorTypeBlend:
|
||||
t, c0, c1 := c.blend()
|
||||
p(src[:1], " blend %d:%d c0:c1\n", 0xff-t, t)
|
||||
printColor(src[1:2], p, decodeColor1(c0), " c0: ")
|
||||
printColor(src[2:3], p, decodeColor1(c1), " c1: ")
|
||||
}
|
||||
}
|
||||
|
||||
func decodeSetNReg(dst Destination, p printer, src buffer, opcode byte) (modeFunc, buffer, error) {
|
||||
decode, typ, adj := buffer.decodeZeroToOne, "zero-to-one", opcode&0x07
|
||||
incr := adj == 7
|
||||
if incr {
|
||||
adj = 0
|
||||
}
|
||||
|
||||
switch (opcode - 0xa8) >> 3 {
|
||||
case 0:
|
||||
decode, typ = buffer.decodeReal, "real"
|
||||
case 1:
|
||||
decode, typ = buffer.decodeCoordinate, "coordinate"
|
||||
}
|
||||
if p != nil {
|
||||
if incr {
|
||||
p(src[:1], "Set NREG[NSEL-0] to a %s number; NSEL++\n", typ)
|
||||
} else {
|
||||
p(src[:1], "Set NREG[NSEL-%d] to a %s number\n", adj, typ)
|
||||
}
|
||||
}
|
||||
src = src[1:]
|
||||
|
||||
f, n := decode(src)
|
||||
if n == 0 {
|
||||
return nil, nil, errInvalidNumber
|
||||
}
|
||||
if p != nil {
|
||||
p(src[:n], " %g\n", f)
|
||||
}
|
||||
src = src[n:]
|
||||
|
||||
if dst != nil {
|
||||
dst.SetNReg(adj, incr, f)
|
||||
}
|
||||
|
||||
return decodeStyling, src, nil
|
||||
}
|
||||
|
||||
func decodeStartPath(dst Destination, p printer, src buffer, opcode byte) (modeFunc, buffer, error) {
|
||||
adj := opcode & 0x07
|
||||
if p != nil {
|
||||
p(src[:1], "Start path, filled with CREG[CSEL-%d]; M (absolute moveTo)\n", adj)
|
||||
}
|
||||
src = src[1:]
|
||||
|
||||
x, src, err := decodeNumber(p, src, buffer.decodeCoordinate)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
y, src, err := decodeNumber(p, src, buffer.decodeCoordinate)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if dst != nil {
|
||||
dst.StartPath(adj, x, y)
|
||||
}
|
||||
|
||||
return decodeDrawing, src, nil
|
||||
}
|
||||
|
||||
func decodeSetLOD(dst Destination, p printer, src buffer) (modeFunc, buffer, error) {
|
||||
if p != nil {
|
||||
p(src[:1], "Set LOD\n")
|
||||
}
|
||||
src = src[1:]
|
||||
|
||||
lod0, src, err := decodeNumber(p, src, buffer.decodeReal)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
lod1, src, err := decodeNumber(p, src, buffer.decodeReal)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if dst != nil {
|
||||
dst.SetLOD(lod0, lod1)
|
||||
}
|
||||
return decodeStyling, src, nil
|
||||
}
|
||||
|
||||
func decodeDrawing(dst Destination, p printer, src buffer) (mf modeFunc, src1 buffer, err error) {
|
||||
var coords [6]float32
|
||||
|
||||
switch opcode := src[0]; {
|
||||
case opcode < 0xe0:
|
||||
op, nCoords, nReps := "", 0, 1+int(opcode&0x0f)
|
||||
switch opcode >> 4 {
|
||||
case 0x00, 0x01:
|
||||
op = "L (absolute lineTo)"
|
||||
nCoords = 2
|
||||
nReps = 1 + int(opcode&0x1f)
|
||||
case 0x02, 0x03:
|
||||
op = "l (relative lineTo)"
|
||||
nCoords = 2
|
||||
nReps = 1 + int(opcode&0x1f)
|
||||
case 0x04:
|
||||
op = "T (absolute smooth quadTo)"
|
||||
nCoords = 2
|
||||
case 0x05:
|
||||
op = "t (relative smooth quadTo)"
|
||||
nCoords = 2
|
||||
case 0x06:
|
||||
op = "Q (absolute quadTo)"
|
||||
nCoords = 4
|
||||
case 0x07:
|
||||
op = "q (relative quadTo)"
|
||||
nCoords = 4
|
||||
case 0x08:
|
||||
op = "S (absolute smooth cubeTo)"
|
||||
nCoords = 4
|
||||
case 0x09:
|
||||
op = "s (relative smooth cubeTo)"
|
||||
nCoords = 4
|
||||
case 0x0a:
|
||||
op = "C (absolute cubeTo)"
|
||||
nCoords = 6
|
||||
case 0x0b:
|
||||
op = "c (relative cubeTo)"
|
||||
nCoords = 6
|
||||
case 0x0c:
|
||||
op = "A (absolute arcTo)"
|
||||
nCoords = 0
|
||||
case 0x0d:
|
||||
op = "a (relative arcTo)"
|
||||
nCoords = 0
|
||||
}
|
||||
|
||||
if p != nil {
|
||||
p(src[:1], "%s, %d reps\n", op, nReps)
|
||||
}
|
||||
src = src[1:]
|
||||
|
||||
for i := 0; i < nReps; i++ {
|
||||
if p != nil && i != 0 {
|
||||
p(nil, "%s, implicit\n", op)
|
||||
}
|
||||
var largeArc, sweep bool
|
||||
if op[0] != 'A' && op[0] != 'a' {
|
||||
src, err = decodeCoordinates(coords[:nCoords], p, src)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
} else {
|
||||
// We have an absolute or relative arcTo.
|
||||
src, err = decodeCoordinates(coords[:2], p, src)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
coords[2], src, err = decodeAngle(p, src)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
largeArc, sweep, src, err = decodeArcToFlags(p, src)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
src, err = decodeCoordinates(coords[4:6], p, src)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if dst == nil {
|
||||
continue
|
||||
}
|
||||
switch op[0] {
|
||||
case 'L':
|
||||
dst.AbsLineTo(coords[0], coords[1])
|
||||
case 'l':
|
||||
dst.RelLineTo(coords[0], coords[1])
|
||||
case 'T':
|
||||
dst.AbsSmoothQuadTo(coords[0], coords[1])
|
||||
case 't':
|
||||
dst.RelSmoothQuadTo(coords[0], coords[1])
|
||||
case 'Q':
|
||||
dst.AbsQuadTo(coords[0], coords[1], coords[2], coords[3])
|
||||
case 'q':
|
||||
dst.RelQuadTo(coords[0], coords[1], coords[2], coords[3])
|
||||
case 'S':
|
||||
dst.AbsSmoothCubeTo(coords[0], coords[1], coords[2], coords[3])
|
||||
case 's':
|
||||
dst.RelSmoothCubeTo(coords[0], coords[1], coords[2], coords[3])
|
||||
case 'C':
|
||||
dst.AbsCubeTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5])
|
||||
case 'c':
|
||||
dst.RelCubeTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5])
|
||||
case 'A':
|
||||
dst.AbsArcTo(coords[0], coords[1], coords[2], largeArc, sweep, coords[4], coords[5])
|
||||
case 'a':
|
||||
dst.RelArcTo(coords[0], coords[1], coords[2], largeArc, sweep, coords[4], coords[5])
|
||||
}
|
||||
}
|
||||
|
||||
case opcode == 0xe1:
|
||||
if p != nil {
|
||||
p(src[:1], "z (closePath); end path\n")
|
||||
}
|
||||
src = src[1:]
|
||||
if dst != nil {
|
||||
dst.ClosePathEndPath()
|
||||
}
|
||||
return decodeStyling, src, nil
|
||||
|
||||
case opcode == 0xe2:
|
||||
if p != nil {
|
||||
p(src[:1], "z (closePath); M (absolute moveTo)\n")
|
||||
}
|
||||
src = src[1:]
|
||||
src, err = decodeCoordinates(coords[:2], p, src)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if dst != nil {
|
||||
dst.ClosePathAbsMoveTo(coords[0], coords[1])
|
||||
}
|
||||
|
||||
case opcode == 0xe3:
|
||||
if p != nil {
|
||||
p(src[:1], "z (closePath); m (relative moveTo)\n")
|
||||
}
|
||||
src = src[1:]
|
||||
src, err = decodeCoordinates(coords[:2], p, src)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if dst != nil {
|
||||
dst.ClosePathRelMoveTo(coords[0], coords[1])
|
||||
}
|
||||
|
||||
case opcode == 0xe6:
|
||||
if p != nil {
|
||||
p(src[:1], "H (absolute horizontal lineTo)\n")
|
||||
}
|
||||
src = src[1:]
|
||||
src, err = decodeCoordinates(coords[:1], p, src)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if dst != nil {
|
||||
dst.AbsHLineTo(coords[0])
|
||||
}
|
||||
|
||||
case opcode == 0xe7:
|
||||
if p != nil {
|
||||
p(src[:1], "h (relative horizontal lineTo)\n")
|
||||
}
|
||||
src = src[1:]
|
||||
src, err = decodeCoordinates(coords[:1], p, src)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if dst != nil {
|
||||
dst.RelHLineTo(coords[0])
|
||||
}
|
||||
|
||||
case opcode == 0xe8:
|
||||
if p != nil {
|
||||
p(src[:1], "V (absolute vertical lineTo)\n")
|
||||
}
|
||||
src = src[1:]
|
||||
src, err = decodeCoordinates(coords[:1], p, src)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if dst != nil {
|
||||
dst.AbsVLineTo(coords[0])
|
||||
}
|
||||
|
||||
case opcode == 0xe9:
|
||||
if p != nil {
|
||||
p(src[:1], "v (relative vertical lineTo)\n")
|
||||
}
|
||||
src = src[1:]
|
||||
src, err = decodeCoordinates(coords[:1], p, src)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if dst != nil {
|
||||
dst.RelVLineTo(coords[0])
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, nil, errUnsupportedDrawingOpcode
|
||||
}
|
||||
return decodeDrawing, src, nil
|
||||
}
|
||||
|
||||
type decodeNumberFunc func(buffer) (float32, int)
|
||||
|
||||
func decodeNumber(p printer, src buffer, dnf decodeNumberFunc) (float32, buffer, error) {
|
||||
x, n := dnf(src)
|
||||
if n == 0 {
|
||||
return 0, nil, errInvalidNumber
|
||||
}
|
||||
if p != nil {
|
||||
p(src[:n], " %+g\n", x)
|
||||
}
|
||||
return x, src[n:], nil
|
||||
}
|
||||
|
||||
func decodeCoordinates(coords []float32, p printer, src buffer) (src1 buffer, err error) {
|
||||
for i := range coords {
|
||||
coords[i], src, err = decodeNumber(p, src, buffer.decodeCoordinate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return src, nil
|
||||
}
|
||||
|
||||
func decodeCoordinatePairs(coords [][2]float32, p printer, src buffer) (src1 buffer, err error) {
|
||||
for i := range coords {
|
||||
coords[i][0], src, err = decodeNumber(p, src, buffer.decodeCoordinate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
coords[i][1], src, err = decodeNumber(p, src, buffer.decodeCoordinate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return src, nil
|
||||
}
|
||||
|
||||
func decodeAngle(p printer, src buffer) (float32, buffer, error) {
|
||||
x, n := src.decodeZeroToOne()
|
||||
if n == 0 {
|
||||
return 0, nil, errInvalidNumber
|
||||
}
|
||||
if p != nil {
|
||||
p(src[:n], " %v × 360 degrees (%v degrees)\n", x, x*360)
|
||||
}
|
||||
return x, src[n:], nil
|
||||
}
|
||||
|
||||
func decodeArcToFlags(p printer, src buffer) (bool, bool, buffer, error) {
|
||||
x, n := src.decodeNatural()
|
||||
if n == 0 {
|
||||
return false, false, nil, errInvalidNumber
|
||||
}
|
||||
if p != nil {
|
||||
p(src[:n], " %#x (largeArc=%d, sweep=%d)\n", x, (x>>0)&0x01, (x>>1)&0x01)
|
||||
}
|
||||
return (x>>0)&0x01 != 0, (x>>1)&0x01 != 0, src[n:], nil
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package iconvg implements a compact, binary format for simple vector graphics:
|
||||
icons, logos, glyphs and emoji.
|
||||
|
||||
WARNING: THIS FORMAT IS EXPERIMENTAL AND SUBJECT TO INCOMPATIBLE CHANGES.
|
||||
|
||||
A longer overview is at
|
||||
https://github.com/google/iconvg
|
||||
|
||||
The file format is specified at
|
||||
https://github.com/google/iconvg/blob/main/spec/iconvg-spec.md
|
||||
|
||||
This package's encoder emits byte-identical output for the same input,
|
||||
independent of the platform (and specifically its floating-point hardware).
|
||||
*/
|
||||
package iconvg
|
||||
|
||||
// TODO: shapes (circles, rects) and strokes? Or can we assume that authoring
|
||||
// tools will convert shapes and strokes to paths?
|
||||
|
||||
// TODO: mark somehow that a graphic (such as a back arrow) should be flipped
|
||||
// horizontally or its paths otherwise varied when presented in a Right-To-Left
|
||||
// context, such as among Arabic and Hebrew text? Or should that be the
|
||||
// responsibility of higher layers, selecting different IconVG graphics based
|
||||
// on context, the way they would select different PNG graphics.
|
||||
|
||||
// TODO: hinting?
|
||||
+605
@@ -0,0 +1,605 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package iconvg
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"image/color"
|
||||
"math"
|
||||
|
||||
"golang.org/x/image/math/f32"
|
||||
)
|
||||
|
||||
var (
|
||||
errCSELUsedAsBothGradientAndStop = errors.New("iconvg: CSEL used as both gradient and stop")
|
||||
errDrawingOpsUsedInStylingMode = errors.New("iconvg: drawing ops used in styling mode")
|
||||
errInvalidSelectorAdjustment = errors.New("iconvg: invalid selector adjustment")
|
||||
errInvalidIncrementingAdjustment = errors.New("iconvg: invalid incrementing adjustment")
|
||||
errStylingOpsUsedInDrawingMode = errors.New("iconvg: styling ops used in drawing mode")
|
||||
errTooManyGradientStops = errors.New("iconvg: too many gradient stops")
|
||||
)
|
||||
|
||||
type mode uint8
|
||||
|
||||
const (
|
||||
modeInitial mode = iota
|
||||
modeStyling
|
||||
modeDrawing
|
||||
)
|
||||
|
||||
// Encoder is an IconVG encoder.
|
||||
//
|
||||
// The zero value is usable. Calling Reset, which is optional, sets the
|
||||
// Metadata for the subsequent encoded form. If Reset is not called before
|
||||
// other Encoder methods, the default metadata is implied.
|
||||
//
|
||||
// It aims to emit byte-identical Bytes output for the same input, independent
|
||||
// of the platform (and specifically its floating-point hardware).
|
||||
type Encoder struct {
|
||||
// HighResolutionCoordinates is whether the encoder should encode
|
||||
// coordinate numbers for subsequent paths at the best possible resolution
|
||||
// afforded by the underlying graphic format.
|
||||
//
|
||||
// By default (false), the encoder quantizes coordinates to 1/64th of a
|
||||
// unit if possible (the default graphic size is 64 by 64 units, so
|
||||
// 1/4096th of the default width or height). Each such coordinate can
|
||||
// therefore be encoded in either 1 or 2 bytes. If true, some coordinates
|
||||
// will be encoded in 4 bytes, giving greater accuracy but larger file
|
||||
// sizes. On the Material Design icon set, the 950 or so icons take up
|
||||
// around 40% more bytes (172K vs 123K) at high resolution.
|
||||
//
|
||||
// See the package documentation for more details on the coordinate number
|
||||
// encoding format.
|
||||
HighResolutionCoordinates bool
|
||||
|
||||
// highResolutionCoordinates is a local copy, copied during StartPath, to
|
||||
// avoid having to specify the semantics of modifying the exported field
|
||||
// while drawing.
|
||||
highResolutionCoordinates bool
|
||||
|
||||
buf buffer
|
||||
altBuf buffer
|
||||
metadata Metadata
|
||||
err error
|
||||
|
||||
lod0 float32
|
||||
lod1 float32
|
||||
cSel uint8
|
||||
nSel uint8
|
||||
|
||||
mode mode
|
||||
drawOp byte
|
||||
drawArgs []float32
|
||||
|
||||
scratch [12]byte
|
||||
}
|
||||
|
||||
// Bytes returns the encoded form.
|
||||
func (e *Encoder) Bytes() ([]byte, error) {
|
||||
if e.err != nil {
|
||||
return nil, e.err
|
||||
}
|
||||
if e.mode == modeInitial {
|
||||
e.appendDefaultMetadata()
|
||||
}
|
||||
return []byte(e.buf), nil
|
||||
}
|
||||
|
||||
// Reset resets the Encoder for the given Metadata.
|
||||
//
|
||||
// This includes setting e.HighResolutionCoordinates to false.
|
||||
func (e *Encoder) Reset(m Metadata) {
|
||||
*e = Encoder{
|
||||
buf: append(e.buf[:0], magic...),
|
||||
metadata: m,
|
||||
mode: modeStyling,
|
||||
lod1: positiveInfinity,
|
||||
}
|
||||
|
||||
nMetadataChunks := 0
|
||||
mcViewBox := m.ViewBox != DefaultViewBox
|
||||
if mcViewBox {
|
||||
nMetadataChunks++
|
||||
}
|
||||
mcSuggestedPalette := m.Palette != DefaultPalette
|
||||
if mcSuggestedPalette {
|
||||
nMetadataChunks++
|
||||
}
|
||||
e.buf.encodeNatural(uint32(nMetadataChunks))
|
||||
|
||||
if mcViewBox {
|
||||
e.altBuf = e.altBuf[:0]
|
||||
e.altBuf.encodeNatural(midViewBox)
|
||||
e.altBuf.encodeCoordinate(m.ViewBox.Min[0])
|
||||
e.altBuf.encodeCoordinate(m.ViewBox.Min[1])
|
||||
e.altBuf.encodeCoordinate(m.ViewBox.Max[0])
|
||||
e.altBuf.encodeCoordinate(m.ViewBox.Max[1])
|
||||
|
||||
e.buf.encodeNatural(uint32(len(e.altBuf)))
|
||||
e.buf = append(e.buf, e.altBuf...)
|
||||
}
|
||||
|
||||
if mcSuggestedPalette {
|
||||
n := 63
|
||||
for ; n >= 0 && m.Palette[n] == (color.RGBA{0x00, 0x00, 0x00, 0xff}); n-- {
|
||||
}
|
||||
|
||||
// Find the shortest encoding that can represent all of m.Palette's n+1
|
||||
// explicit colors.
|
||||
enc1, enc2, enc3 := true, true, true
|
||||
for _, c := range m.Palette[:n+1] {
|
||||
if enc1 && (!is1(c.R) || !is1(c.G) || !is1(c.B) || !is1(c.A)) {
|
||||
enc1 = false
|
||||
}
|
||||
if enc2 && (!is2(c.R) || !is2(c.G) || !is2(c.B) || !is2(c.A)) {
|
||||
enc2 = false
|
||||
}
|
||||
if enc3 && (c.A != 0xff) {
|
||||
enc3 = false
|
||||
}
|
||||
}
|
||||
|
||||
e.altBuf = e.altBuf[:0]
|
||||
e.altBuf.encodeNatural(midSuggestedPalette)
|
||||
if enc1 {
|
||||
e.altBuf = append(e.altBuf, byte(n)|0x00)
|
||||
for _, c := range m.Palette[:n+1] {
|
||||
x, _ := encodeColor1(RGBAColor(c))
|
||||
e.altBuf = append(e.altBuf, x)
|
||||
}
|
||||
} else if enc2 {
|
||||
e.altBuf = append(e.altBuf, byte(n)|0x40)
|
||||
for _, c := range m.Palette[:n+1] {
|
||||
x, _ := encodeColor2(RGBAColor(c))
|
||||
e.altBuf = append(e.altBuf, x[0], x[1])
|
||||
}
|
||||
} else if enc3 {
|
||||
e.altBuf = append(e.altBuf, byte(n)|0x80)
|
||||
for _, c := range m.Palette[:n+1] {
|
||||
e.altBuf = append(e.altBuf, c.R, c.G, c.B)
|
||||
}
|
||||
} else {
|
||||
e.altBuf = append(e.altBuf, byte(n)|0xc0)
|
||||
for _, c := range m.Palette[:n+1] {
|
||||
e.altBuf = append(e.altBuf, c.R, c.G, c.B, c.A)
|
||||
}
|
||||
}
|
||||
|
||||
e.buf.encodeNatural(uint32(len(e.altBuf)))
|
||||
e.buf = append(e.buf, e.altBuf...)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Encoder) appendDefaultMetadata() {
|
||||
e.buf = append(e.buf[:0], magic...)
|
||||
e.buf = append(e.buf, 0x00) // There are zero metadata chunks.
|
||||
e.mode = modeStyling
|
||||
}
|
||||
|
||||
func (e *Encoder) CSel() uint8 {
|
||||
if e.mode == modeInitial {
|
||||
e.appendDefaultMetadata()
|
||||
}
|
||||
return e.cSel
|
||||
}
|
||||
|
||||
func (e *Encoder) NSel() uint8 {
|
||||
if e.mode == modeInitial {
|
||||
e.appendDefaultMetadata()
|
||||
}
|
||||
return e.nSel
|
||||
}
|
||||
|
||||
func (e *Encoder) LOD() (lod0, lod1 float32) {
|
||||
if e.mode == modeInitial {
|
||||
e.appendDefaultMetadata()
|
||||
}
|
||||
return e.lod0, e.lod1
|
||||
}
|
||||
|
||||
func (e *Encoder) checkModeStyling() {
|
||||
if e.mode == modeStyling {
|
||||
return
|
||||
}
|
||||
if e.mode == modeInitial {
|
||||
e.appendDefaultMetadata()
|
||||
return
|
||||
}
|
||||
e.err = errStylingOpsUsedInDrawingMode
|
||||
}
|
||||
|
||||
func (e *Encoder) SetCSel(cSel uint8) {
|
||||
e.checkModeStyling()
|
||||
if e.err != nil {
|
||||
return
|
||||
}
|
||||
e.cSel = cSel & 0x3f
|
||||
e.buf = append(e.buf, e.cSel)
|
||||
}
|
||||
|
||||
func (e *Encoder) SetNSel(nSel uint8) {
|
||||
e.checkModeStyling()
|
||||
if e.err != nil {
|
||||
return
|
||||
}
|
||||
e.nSel = nSel & 0x3f
|
||||
e.buf = append(e.buf, e.nSel|0x40)
|
||||
}
|
||||
|
||||
func (e *Encoder) SetCReg(adj uint8, incr bool, c Color) {
|
||||
e.checkModeStyling()
|
||||
if e.err != nil {
|
||||
return
|
||||
}
|
||||
if adj > 6 {
|
||||
e.err = errInvalidSelectorAdjustment
|
||||
return
|
||||
}
|
||||
if incr {
|
||||
if adj != 0 {
|
||||
e.err = errInvalidIncrementingAdjustment
|
||||
}
|
||||
adj = 7
|
||||
}
|
||||
|
||||
if x, ok := encodeColor1(c); ok {
|
||||
e.buf = append(e.buf, adj|0x80, x)
|
||||
return
|
||||
}
|
||||
if x, ok := encodeColor2(c); ok {
|
||||
e.buf = append(e.buf, adj|0x88, x[0], x[1])
|
||||
return
|
||||
}
|
||||
if x, ok := encodeColor3Direct(c); ok {
|
||||
e.buf = append(e.buf, adj|0x90, x[0], x[1], x[2])
|
||||
return
|
||||
}
|
||||
if x, ok := encodeColor4(c); ok {
|
||||
e.buf = append(e.buf, adj|0x98, x[0], x[1], x[2], x[3])
|
||||
return
|
||||
}
|
||||
if x, ok := encodeColor3Indirect(c); ok {
|
||||
e.buf = append(e.buf, adj|0xa0, x[0], x[1], x[2])
|
||||
return
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func (e *Encoder) SetNReg(adj uint8, incr bool, f float32) {
|
||||
e.checkModeStyling()
|
||||
if e.err != nil {
|
||||
return
|
||||
}
|
||||
if adj > 6 {
|
||||
e.err = errInvalidSelectorAdjustment
|
||||
return
|
||||
}
|
||||
if incr {
|
||||
if adj != 0 {
|
||||
e.err = errInvalidIncrementingAdjustment
|
||||
}
|
||||
adj = 7
|
||||
}
|
||||
|
||||
// Try three different encodings and pick the shortest.
|
||||
b := buffer(e.scratch[0:0])
|
||||
opcode, iBest, nBest := uint8(0xa8), 0, b.encodeReal(f)
|
||||
|
||||
b = buffer(e.scratch[4:4])
|
||||
if n := b.encodeCoordinate(f); n < nBest {
|
||||
opcode, iBest, nBest = 0xb0, 4, n
|
||||
}
|
||||
|
||||
b = buffer(e.scratch[8:8])
|
||||
if n := b.encodeZeroToOne(f); n < nBest {
|
||||
opcode, iBest, nBest = 0xb8, 8, n
|
||||
}
|
||||
|
||||
e.buf = append(e.buf, adj|opcode)
|
||||
e.buf = append(e.buf, e.scratch[iBest:iBest+nBest]...)
|
||||
}
|
||||
|
||||
func (e *Encoder) SetLOD(lod0, lod1 float32) {
|
||||
e.checkModeStyling()
|
||||
if e.err != nil {
|
||||
return
|
||||
}
|
||||
e.lod0 = lod0
|
||||
e.lod1 = lod1
|
||||
e.buf = append(e.buf, 0xc7)
|
||||
e.buf.encodeReal(lod0)
|
||||
e.buf.encodeReal(lod1)
|
||||
}
|
||||
|
||||
// SetGradient sets CREG[CSEL] to encode the gradient whose colors defined by
|
||||
// spread and stops. Its geometry is either linear or radial, depending on the
|
||||
// radial argument, and the given affine transformation matrix maps from
|
||||
// graphic coordinate space defined by the metadata's viewBox (e.g. from (-32,
|
||||
// -32) to (+32, +32)) to gradient coordinate space. Gradient coordinate space
|
||||
// is where a linear gradient ranges from x=0 to x=1, and a radial gradient has
|
||||
// center (0, 0) and radius 1.
|
||||
//
|
||||
// The colors of the n stops are encoded at CREG[cBase+0], CREG[cBase+1], ...,
|
||||
// CREG[cBase+n-1]. Similarly, the offsets of the n stops are encoded at
|
||||
// NREG[nBase+0], NREG[nBase+1], ..., NREG[nBase+n-1]. Additional parameters
|
||||
// are stored at NREG[nBase-4], NREG[nBase-3], NREG[nBase-2] and NREG[nBase-1].
|
||||
//
|
||||
// The CSEL and NSEL selector registers maintain the same values after the
|
||||
// method returns as they had when the method was called.
|
||||
//
|
||||
// See the package documentation for more details on the gradient encoding
|
||||
// format and the derivation of common transformation matrices.
|
||||
func (e *Encoder) SetGradient(cBase, nBase uint8, radial bool, transform f32.Aff3, spread GradientSpread, stops []GradientStop) {
|
||||
e.checkModeStyling()
|
||||
if e.err != nil {
|
||||
return
|
||||
}
|
||||
if len(stops) > 64-len(transform) {
|
||||
e.err = errTooManyGradientStops
|
||||
return
|
||||
}
|
||||
if x, y := e.cSel, e.cSel+64; (cBase <= x && x < cBase+uint8(len(stops))) ||
|
||||
(cBase <= y && y < cBase+uint8(len(stops))) {
|
||||
e.err = errCSELUsedAsBothGradientAndStop
|
||||
return
|
||||
}
|
||||
|
||||
oldCSel := e.cSel
|
||||
oldNSel := e.nSel
|
||||
cBase &= 0x3f
|
||||
nBase &= 0x3f
|
||||
bFlags := uint8(0x80)
|
||||
if radial {
|
||||
bFlags = 0xc0
|
||||
}
|
||||
e.SetCReg(0, false, RGBAColor(color.RGBA{
|
||||
R: uint8(len(stops)),
|
||||
G: cBase | uint8(spread<<6),
|
||||
B: nBase | bFlags,
|
||||
A: 0x00,
|
||||
}))
|
||||
e.SetCSel(cBase)
|
||||
e.SetNSel(nBase)
|
||||
for i, v := range transform {
|
||||
e.SetNReg(uint8(len(transform)-i), false, v)
|
||||
}
|
||||
for _, s := range stops {
|
||||
r, g, b, a := s.Color.RGBA()
|
||||
e.SetCReg(0, true, RGBAColor(color.RGBA{
|
||||
R: uint8(r >> 8),
|
||||
G: uint8(g >> 8),
|
||||
B: uint8(b >> 8),
|
||||
A: uint8(a >> 8),
|
||||
}))
|
||||
e.SetNReg(0, true, s.Offset)
|
||||
}
|
||||
e.SetCSel(oldCSel)
|
||||
e.SetNSel(oldNSel)
|
||||
}
|
||||
|
||||
// SetLinearGradient is like SetGradient with radial=false except that the
|
||||
// transformation matrix is implicitly defined by two boundary points (x1, y1)
|
||||
// and (x2, y2).
|
||||
func (e *Encoder) SetLinearGradient(cBase, nBase uint8, x1, y1, x2, y2 float32, spread GradientSpread, stops []GradientStop) {
|
||||
// See the package documentation's appendix for a derivation of the
|
||||
// transformation matrix.
|
||||
dx, dy := x2-x1, y2-y1
|
||||
d := dx*dx + dy*dy
|
||||
ma := dx / d
|
||||
mb := dy / d
|
||||
e.SetGradient(cBase, nBase, false, f32.Aff3{
|
||||
ma, mb, -ma*x1 - mb*y1,
|
||||
0, 0, 0,
|
||||
}, spread, stops)
|
||||
}
|
||||
|
||||
// SetCircularGradient is like SetGradient with radial=true except that the
|
||||
// transformation matrix is implicitly defined by a center (cx, cy) and a
|
||||
// radius vector (rx, ry) such that (cx+rx, cy+ry) is on the circle.
|
||||
func (e *Encoder) SetCircularGradient(cBase, nBase uint8, cx, cy, rx, ry float32, spread GradientSpread, stops []GradientStop) {
|
||||
// See the package documentation's appendix for a derivation of the
|
||||
// transformation matrix.
|
||||
invR := float32(1 / math.Sqrt(float64(rx*rx+ry*ry)))
|
||||
e.SetGradient(cBase, nBase, true, f32.Aff3{
|
||||
invR, 0, -cx * invR,
|
||||
0, invR, -cy * invR,
|
||||
}, spread, stops)
|
||||
}
|
||||
|
||||
// SetEllipticalGradient is like SetGradient with radial=true except that the
|
||||
// transformation matrix is implicitly defined by a center (cx, cy) and two
|
||||
// axis vectors (rx, ry) and (sx, sy) such that (cx+rx, cy+ry) and (cx+sx,
|
||||
// cy+sy) are on the ellipse.
|
||||
func (e *Encoder) SetEllipticalGradient(cBase, nBase uint8, cx, cy, rx, ry, sx, sy float32, spread GradientSpread, stops []GradientStop) {
|
||||
// Explicitly disable FMA in the floating-point calculations below
|
||||
// to get consistent results on all platforms, and in turn produce
|
||||
// a byte-identical encoding.
|
||||
// See https://golang.org/ref/spec#Floating_point_operators and issue 43219.
|
||||
|
||||
// See the package documentation's appendix for a derivation of the
|
||||
// transformation matrix.
|
||||
invRSSR := 1 / (float32(rx*sy) - float32(sx*ry))
|
||||
|
||||
ma := +sy * invRSSR
|
||||
mb := -sx * invRSSR
|
||||
mc := -float32(ma*cx) - float32(mb*cy)
|
||||
md := -ry * invRSSR
|
||||
me := +rx * invRSSR
|
||||
mf := -float32(md*cx) - float32(me*cy)
|
||||
|
||||
e.SetGradient(cBase, nBase, true, f32.Aff3{
|
||||
ma, mb, mc,
|
||||
md, me, mf,
|
||||
}, spread, stops)
|
||||
}
|
||||
|
||||
func (e *Encoder) StartPath(adj uint8, x, y float32) {
|
||||
e.checkModeStyling()
|
||||
if e.err != nil {
|
||||
return
|
||||
}
|
||||
if adj > 6 {
|
||||
e.err = errInvalidSelectorAdjustment
|
||||
return
|
||||
}
|
||||
e.highResolutionCoordinates = e.HighResolutionCoordinates
|
||||
e.buf = append(e.buf, uint8(0xc0+adj))
|
||||
e.buf.encodeCoordinate(quantize(x, e.highResolutionCoordinates))
|
||||
e.buf.encodeCoordinate(quantize(y, e.highResolutionCoordinates))
|
||||
e.mode = modeDrawing
|
||||
}
|
||||
|
||||
func (e *Encoder) AbsHLineTo(x float32) { e.draw('H', x, 0, 0, 0, 0, 0) }
|
||||
func (e *Encoder) RelHLineTo(x float32) { e.draw('h', x, 0, 0, 0, 0, 0) }
|
||||
func (e *Encoder) AbsVLineTo(y float32) { e.draw('V', y, 0, 0, 0, 0, 0) }
|
||||
func (e *Encoder) RelVLineTo(y float32) { e.draw('v', y, 0, 0, 0, 0, 0) }
|
||||
func (e *Encoder) AbsLineTo(x, y float32) { e.draw('L', x, y, 0, 0, 0, 0) }
|
||||
func (e *Encoder) RelLineTo(x, y float32) { e.draw('l', x, y, 0, 0, 0, 0) }
|
||||
func (e *Encoder) AbsSmoothQuadTo(x, y float32) { e.draw('T', x, y, 0, 0, 0, 0) }
|
||||
func (e *Encoder) RelSmoothQuadTo(x, y float32) { e.draw('t', x, y, 0, 0, 0, 0) }
|
||||
func (e *Encoder) AbsQuadTo(x1, y1, x, y float32) { e.draw('Q', x1, y1, x, y, 0, 0) }
|
||||
func (e *Encoder) RelQuadTo(x1, y1, x, y float32) { e.draw('q', x1, y1, x, y, 0, 0) }
|
||||
func (e *Encoder) AbsSmoothCubeTo(x2, y2, x, y float32) { e.draw('S', x2, y2, x, y, 0, 0) }
|
||||
func (e *Encoder) RelSmoothCubeTo(x2, y2, x, y float32) { e.draw('s', x2, y2, x, y, 0, 0) }
|
||||
func (e *Encoder) AbsCubeTo(x1, y1, x2, y2, x, y float32) { e.draw('C', x1, y1, x2, y2, x, y) }
|
||||
func (e *Encoder) RelCubeTo(x1, y1, x2, y2, x, y float32) { e.draw('c', x1, y1, x2, y2, x, y) }
|
||||
func (e *Encoder) ClosePathEndPath() { e.draw('Z', 0, 0, 0, 0, 0, 0) }
|
||||
func (e *Encoder) ClosePathAbsMoveTo(x, y float32) { e.draw('Y', x, y, 0, 0, 0, 0) }
|
||||
func (e *Encoder) ClosePathRelMoveTo(x, y float32) { e.draw('y', x, y, 0, 0, 0, 0) }
|
||||
|
||||
func (e *Encoder) AbsArcTo(rx, ry, xAxisRotation float32, largeArc, sweep bool, x, y float32) {
|
||||
e.arcTo('A', rx, ry, xAxisRotation, largeArc, sweep, x, y)
|
||||
}
|
||||
|
||||
func (e *Encoder) RelArcTo(rx, ry, xAxisRotation float32, largeArc, sweep bool, x, y float32) {
|
||||
e.arcTo('a', rx, ry, xAxisRotation, largeArc, sweep, x, y)
|
||||
}
|
||||
|
||||
func (e *Encoder) arcTo(drawOp byte, rx, ry, xAxisRotation float32, largeArc, sweep bool, x, y float32) {
|
||||
flags := uint32(0)
|
||||
if largeArc {
|
||||
flags |= 0x01
|
||||
}
|
||||
if sweep {
|
||||
flags |= 0x02
|
||||
}
|
||||
e.draw(drawOp, rx, ry, xAxisRotation, float32(flags), x, y)
|
||||
}
|
||||
|
||||
func (e *Encoder) draw(drawOp byte, arg0, arg1, arg2, arg3, arg4, arg5 float32) {
|
||||
if e.err != nil {
|
||||
return
|
||||
}
|
||||
if e.mode != modeDrawing {
|
||||
e.err = errDrawingOpsUsedInStylingMode
|
||||
return
|
||||
}
|
||||
if e.drawOp != drawOp {
|
||||
e.flushDrawOps()
|
||||
}
|
||||
e.drawOp = drawOp
|
||||
switch drawOps[drawOp].nArgs {
|
||||
case 0:
|
||||
// No-op.
|
||||
case 1:
|
||||
e.drawArgs = append(e.drawArgs, arg0)
|
||||
case 2:
|
||||
e.drawArgs = append(e.drawArgs, arg0, arg1)
|
||||
case 4:
|
||||
e.drawArgs = append(e.drawArgs, arg0, arg1, arg2, arg3)
|
||||
case 6:
|
||||
e.drawArgs = append(e.drawArgs, arg0, arg1, arg2, arg3, arg4, arg5)
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
switch drawOp {
|
||||
case 'Z':
|
||||
e.mode = modeStyling
|
||||
fallthrough
|
||||
case 'Y', 'y':
|
||||
e.flushDrawOps()
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Encoder) flushDrawOps() {
|
||||
if e.drawOp == 0x00 {
|
||||
return
|
||||
}
|
||||
|
||||
if op := drawOps[e.drawOp]; op.nArgs == 0 {
|
||||
e.buf = append(e.buf, op.opcodeBase)
|
||||
} else {
|
||||
n := len(e.drawArgs) / int(op.nArgs)
|
||||
for i := 0; n > 0; {
|
||||
m := n
|
||||
if m > int(op.maxRepCount) {
|
||||
m = int(op.maxRepCount)
|
||||
}
|
||||
e.buf = append(e.buf, op.opcodeBase+uint8(m)-1)
|
||||
|
||||
switch e.drawOp {
|
||||
default:
|
||||
for j := m * int(op.nArgs); j > 0; j-- {
|
||||
e.buf.encodeCoordinate(quantize(e.drawArgs[i], e.highResolutionCoordinates))
|
||||
i++
|
||||
}
|
||||
case 'A', 'a':
|
||||
for j := m; j > 0; j-- {
|
||||
e.buf.encodeCoordinate(quantize(e.drawArgs[i+0], e.highResolutionCoordinates))
|
||||
e.buf.encodeCoordinate(quantize(e.drawArgs[i+1], e.highResolutionCoordinates))
|
||||
e.buf.encodeAngle(e.drawArgs[i+2])
|
||||
e.buf.encodeNatural(uint32(e.drawArgs[i+3]))
|
||||
e.buf.encodeCoordinate(quantize(e.drawArgs[i+4], e.highResolutionCoordinates))
|
||||
e.buf.encodeCoordinate(quantize(e.drawArgs[i+5], e.highResolutionCoordinates))
|
||||
i += 6
|
||||
}
|
||||
}
|
||||
|
||||
n -= m
|
||||
}
|
||||
}
|
||||
|
||||
e.drawOp = 0x00
|
||||
e.drawArgs = e.drawArgs[:0]
|
||||
}
|
||||
|
||||
func quantize(coord float32, highResolutionCoordinates bool) float32 {
|
||||
if !highResolutionCoordinates && (-128 <= coord && coord < 128) {
|
||||
x := math.Floor(float64(coord*64 + 0.5))
|
||||
return float32(x) / 64
|
||||
}
|
||||
return coord
|
||||
}
|
||||
|
||||
var drawOps = [256]struct {
|
||||
opcodeBase byte
|
||||
maxRepCount uint8
|
||||
nArgs uint8
|
||||
}{
|
||||
'L': {0x00, 32, 2},
|
||||
'l': {0x20, 32, 2},
|
||||
'T': {0x40, 16, 2},
|
||||
't': {0x50, 16, 2},
|
||||
'Q': {0x60, 16, 4},
|
||||
'q': {0x70, 16, 4},
|
||||
'S': {0x80, 16, 4},
|
||||
's': {0x90, 16, 4},
|
||||
'C': {0xa0, 16, 6},
|
||||
'c': {0xb0, 16, 6},
|
||||
'A': {0xc0, 16, 6},
|
||||
'a': {0xd0, 16, 6},
|
||||
|
||||
// Z means close path and then end path.
|
||||
'Z': {0xe1, 1, 0},
|
||||
// Y/y means close path and then open a new path (with a MoveTo/moveTo).
|
||||
'Y': {0xe2, 1, 2},
|
||||
'y': {0xe3, 1, 2},
|
||||
|
||||
'H': {0xe6, 1, 1},
|
||||
'h': {0xe7, 1, 1},
|
||||
'V': {0xe8, 1, 1},
|
||||
'v': {0xe9, 1, 1},
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package iconvg
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"math"
|
||||
|
||||
"golang.org/x/image/math/f32"
|
||||
)
|
||||
|
||||
const magic = "\x89IVG"
|
||||
|
||||
var magicBytes = []byte(magic)
|
||||
|
||||
var (
|
||||
negativeInfinity = math.Float32frombits(0xff800000)
|
||||
positiveInfinity = math.Float32frombits(0x7f800000)
|
||||
)
|
||||
|
||||
func isNaNOrInfinity(f float32) bool {
|
||||
return math.Float32bits(f)&0x7f800000 == 0x7f800000
|
||||
}
|
||||
|
||||
const (
|
||||
// File Format Version 0.
|
||||
midViewBox = 0
|
||||
midSuggestedPalette = 1
|
||||
|
||||
// File Format Version 1.
|
||||
ffv1MIDViewBox = 8
|
||||
ffv1MIDSuggestedPalette = 16
|
||||
)
|
||||
|
||||
var gradientShapeNames = [2]string{
|
||||
"linear",
|
||||
"radial",
|
||||
}
|
||||
|
||||
var gradientSpreadNames = [4]string{
|
||||
"none",
|
||||
"pad",
|
||||
"reflect",
|
||||
"repeat",
|
||||
}
|
||||
|
||||
// GradientSpread is how to spread a gradient past its nominal bounds (from
|
||||
// offset being 0.0 to offset being 1.0).
|
||||
type GradientSpread uint8
|
||||
|
||||
const (
|
||||
GradientSpreadNone GradientSpread = 0
|
||||
GradientSpreadPad GradientSpread = 1
|
||||
GradientSpreadReflect GradientSpread = 2
|
||||
GradientSpreadRepeat GradientSpread = 3
|
||||
)
|
||||
|
||||
// GradientStop is a color/offset gradient stop.
|
||||
type GradientStop struct {
|
||||
Offset float32
|
||||
Color color.Color
|
||||
}
|
||||
|
||||
// Rectangle is defined by its minimum and maximum coordinates.
|
||||
type Rectangle struct {
|
||||
Min, Max f32.Vec2
|
||||
}
|
||||
|
||||
// AspectRatio returns the Rectangle's aspect ratio. An IconVG graphic is
|
||||
// scalable; these dimensions do not necessarily map 1:1 to pixels.
|
||||
func (r *Rectangle) AspectRatio() (dx, dy float32) {
|
||||
return r.Max[0] - r.Min[0], r.Max[1] - r.Min[1]
|
||||
}
|
||||
|
||||
// Palette is an IconVG palette.
|
||||
type Palette [64]color.RGBA
|
||||
|
||||
// Metadata is an IconVG's metadata.
|
||||
type Metadata struct {
|
||||
ViewBox Rectangle
|
||||
|
||||
// Palette is a 64 color palette. When encoding, it is the suggested
|
||||
// palette to place within the IconVG graphic. When decoding, it is either
|
||||
// the optional palette passed to Decode, or if no optional palette was
|
||||
// given, the suggested palette within the IconVG graphic.
|
||||
Palette Palette
|
||||
}
|
||||
|
||||
// DefaultViewBox is the default ViewBox. Its values should not be modified.
|
||||
var DefaultViewBox = Rectangle{
|
||||
Min: f32.Vec2{-32, -32},
|
||||
Max: f32.Vec2{+32, +32},
|
||||
}
|
||||
|
||||
// DefaultPalette is the default Palette. Its values should not be modified.
|
||||
var DefaultPalette = Palette{
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
color.RGBA{0x00, 0x00, 0x00, 0xff},
|
||||
}
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package gradient provides linear and radial gradient images.
|
||||
package gradient
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"math"
|
||||
|
||||
"golang.org/x/image/math/f64"
|
||||
)
|
||||
|
||||
// TODO: gamma correction / non-linear color interpolation?
|
||||
|
||||
// TODO: move this out of an internal directory, either under
|
||||
// golang.org/x/image or under the standard library's image, so that
|
||||
// golang.org/x/image/{draw,vector} and possibly image/draw can type switch on
|
||||
// the gradient.Gradient type and provide fast path code.
|
||||
//
|
||||
// Doing so requires coming up with a stable API that we'd be happy to support
|
||||
// in the long term. This would probably include an easier way to create
|
||||
// linear, circular and elliptical gradients, without having to explicitly
|
||||
// calculate the f64.Aff3 matrix.
|
||||
|
||||
// Shape is the gradient shape.
|
||||
type Shape uint8
|
||||
|
||||
const (
|
||||
ShapeLinear Shape = iota
|
||||
ShapeRadial
|
||||
)
|
||||
|
||||
// Spread is the gradient spread, or how to spread a gradient past its nominal
|
||||
// bounds (from offset being 0.0 to offset being 1.0).
|
||||
type Spread uint8
|
||||
|
||||
const (
|
||||
// SpreadNone means that offsets outside of the [0, 1] range map to
|
||||
// transparent black.
|
||||
SpreadNone Spread = iota
|
||||
// SpreadPad means that offsets below 0 and above 1 map to the colors that
|
||||
// 0 and 1 would map to.
|
||||
SpreadPad
|
||||
// SpreadReflect means that the offset mapping is reflected start-to-end,
|
||||
// end-to-start, start-to-end, etc.
|
||||
SpreadReflect
|
||||
// SpreadRepeat means that the offset mapping is repeated start-to-end,
|
||||
// start-to-end, start-to-end, etc.
|
||||
SpreadRepeat
|
||||
)
|
||||
|
||||
// Clamp clamps x to the range [0, 1]. If x is outside that range, it is
|
||||
// converted to a value in that range according to s's semantics. It returns -1
|
||||
// if s is SpreadNone and x is outside the range [0, 1].
|
||||
func (s Spread) Clamp(x float64) float64 {
|
||||
if x >= 0 {
|
||||
if x <= 1 {
|
||||
return x
|
||||
}
|
||||
switch s {
|
||||
case SpreadPad:
|
||||
return 1
|
||||
case SpreadReflect:
|
||||
if int(x)&1 == 0 {
|
||||
return x - math.Floor(x)
|
||||
}
|
||||
return math.Ceil(x) - x
|
||||
case SpreadRepeat:
|
||||
return x - math.Floor(x)
|
||||
}
|
||||
return -1
|
||||
}
|
||||
switch s {
|
||||
case SpreadPad:
|
||||
return 0
|
||||
case SpreadReflect:
|
||||
x = -x
|
||||
if int(x)&1 == 0 {
|
||||
return x - math.Floor(x)
|
||||
}
|
||||
return math.Ceil(x) - x
|
||||
case SpreadRepeat:
|
||||
return x - math.Floor(x)
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Stop is an offset and color.
|
||||
type Stop struct {
|
||||
Offset float64
|
||||
RGBA64 color.RGBA64
|
||||
}
|
||||
|
||||
// Range is the range between two stops.
|
||||
type Range struct {
|
||||
Offset0 float64
|
||||
Offset1 float64
|
||||
Width float64
|
||||
R0 float64
|
||||
R1 float64
|
||||
G0 float64
|
||||
G1 float64
|
||||
B0 float64
|
||||
B1 float64
|
||||
A0 float64
|
||||
A1 float64
|
||||
}
|
||||
|
||||
// MakeRange returns the range between two stops.
|
||||
func MakeRange(s0, s1 Stop) Range {
|
||||
return Range{
|
||||
Offset0: s0.Offset,
|
||||
Offset1: s1.Offset,
|
||||
Width: s1.Offset - s0.Offset,
|
||||
R0: float64(s0.RGBA64.R),
|
||||
R1: float64(s1.RGBA64.R),
|
||||
G0: float64(s0.RGBA64.G),
|
||||
G1: float64(s1.RGBA64.G),
|
||||
B0: float64(s0.RGBA64.B),
|
||||
B1: float64(s1.RGBA64.B),
|
||||
A0: float64(s0.RGBA64.A),
|
||||
A1: float64(s1.RGBA64.A),
|
||||
}
|
||||
}
|
||||
|
||||
// AppendRanges appends to a the ranges defined by a's implicit final stop (if
|
||||
// any exist) and stops.
|
||||
func AppendRanges(a []Range, stops []Stop) []Range {
|
||||
if len(stops) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(a) != 0 {
|
||||
z := a[len(a)-1]
|
||||
a = append(a, MakeRange(Stop{
|
||||
Offset: z.Offset1,
|
||||
RGBA64: color.RGBA64{
|
||||
R: uint16(z.R1),
|
||||
G: uint16(z.G1),
|
||||
B: uint16(z.B1),
|
||||
A: uint16(z.A1),
|
||||
},
|
||||
}, stops[0]))
|
||||
}
|
||||
for i := 0; i < len(stops)-1; i++ {
|
||||
a = append(a, MakeRange(stops[i], stops[i+1]))
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Gradient is a very large image.Image (the same size as an image.Uniform)
|
||||
// whose colors form a gradient.
|
||||
type Gradient struct {
|
||||
Shape Shape
|
||||
Spread Spread
|
||||
|
||||
// Pix2Grad transforms coordinates from pixel space (the arguments to the
|
||||
// Image.At method) to gradient space. Gradient space is where a linear
|
||||
// gradient ranges from x == 0 to x == 1, and a radial gradient has center
|
||||
// (0, 0) and radius 1.
|
||||
//
|
||||
// This is an affine transform, so it can represent elliptical gradients in
|
||||
// pixel space, including non-axis-aligned ellipses.
|
||||
//
|
||||
// For a linear gradient, the bottom row is ignored.
|
||||
Pix2Grad f64.Aff3
|
||||
|
||||
Ranges []Range
|
||||
|
||||
// First and Last are the first and last stop's colors.
|
||||
First, Last color.RGBA64
|
||||
}
|
||||
|
||||
// Init initializes g to a gradient whose geometry is defined by shape and
|
||||
// pix2Grad and whose colors are defined by spread and stops.
|
||||
func (g *Gradient) Init(shape Shape, spread Spread, pix2Grad f64.Aff3, stops []Stop) {
|
||||
g.Shape = shape
|
||||
g.Spread = spread
|
||||
g.Pix2Grad = pix2Grad
|
||||
g.Ranges = AppendRanges(g.Ranges[:0], stops)
|
||||
if len(stops) == 0 {
|
||||
g.First = color.RGBA64{}
|
||||
g.Last = color.RGBA64{}
|
||||
} else {
|
||||
g.First = stops[0].RGBA64
|
||||
g.Last = stops[len(stops)-1].RGBA64
|
||||
}
|
||||
}
|
||||
|
||||
// ColorModel satisfies the image.Image interface.
|
||||
func (g *Gradient) ColorModel() color.Model {
|
||||
return color.RGBA64Model
|
||||
}
|
||||
|
||||
// Bounds satisfies the image.Image interface.
|
||||
func (g *Gradient) Bounds() image.Rectangle {
|
||||
return image.Rectangle{
|
||||
Min: image.Point{-1e9, -1e9},
|
||||
Max: image.Point{+1e9, +1e9},
|
||||
}
|
||||
}
|
||||
|
||||
// At satisfies the image.Image interface.
|
||||
func (g *Gradient) At(x, y int) color.Color {
|
||||
if len(g.Ranges) == 0 {
|
||||
return color.RGBA64{}
|
||||
}
|
||||
|
||||
px := float64(x) + 0.5
|
||||
py := float64(y) + 0.5
|
||||
|
||||
offset := 0.0
|
||||
if g.Shape == ShapeLinear {
|
||||
offset = g.Spread.Clamp(g.Pix2Grad[0]*px + g.Pix2Grad[1]*py + g.Pix2Grad[2])
|
||||
} else {
|
||||
gx := g.Pix2Grad[0]*px + g.Pix2Grad[1]*py + g.Pix2Grad[2]
|
||||
gy := g.Pix2Grad[3]*px + g.Pix2Grad[4]*py + g.Pix2Grad[5]
|
||||
offset = g.Spread.Clamp(math.Sqrt(gx*gx + gy*gy))
|
||||
}
|
||||
if !(offset >= 0) {
|
||||
return color.RGBA64{}
|
||||
}
|
||||
|
||||
if offset < g.Ranges[0].Offset0 {
|
||||
return g.First
|
||||
}
|
||||
for _, r := range g.Ranges {
|
||||
if r.Offset0 <= offset && offset <= r.Offset1 {
|
||||
t := (offset - r.Offset0) / r.Width
|
||||
s := 1 - t
|
||||
return color.RGBA64{
|
||||
uint16(s*r.R0 + t*r.R1),
|
||||
uint16(s*r.G0 + t*r.G1),
|
||||
uint16(s*r.B0 + t*r.B1),
|
||||
uint16(s*r.A0 + t*r.A1),
|
||||
}
|
||||
}
|
||||
}
|
||||
return g.Last
|
||||
}
|
||||
+595
@@ -0,0 +1,595 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package iconvg
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"math"
|
||||
|
||||
"golang.org/x/exp/shiny/iconvg/internal/gradient"
|
||||
"golang.org/x/image/math/f64"
|
||||
"golang.org/x/image/vector"
|
||||
)
|
||||
|
||||
const (
|
||||
smoothTypeNone = iota
|
||||
smoothTypeQuad
|
||||
smoothTypeCube
|
||||
)
|
||||
|
||||
// Rasterizer is a Destination that draws an IconVG graphic onto a raster
|
||||
// image.
|
||||
//
|
||||
// The zero value is usable, in that it has no raster image to draw onto, so
|
||||
// that calling Decode with this Destination is a no-op (other than checking
|
||||
// the encoded form for errors in the byte code). Call SetDstImage to change
|
||||
// the raster image, before calling Decode or between calls to Decode.
|
||||
type Rasterizer struct {
|
||||
z vector.Rasterizer
|
||||
|
||||
dst draw.Image
|
||||
r image.Rectangle
|
||||
drawOp draw.Op
|
||||
|
||||
// scale and bias transforms the metadata.ViewBox rectangle to the (0, 0) -
|
||||
// (r.Dx(), r.Dy()) rectangle.
|
||||
scaleX float32
|
||||
biasX float32
|
||||
scaleY float32
|
||||
biasY float32
|
||||
|
||||
metadata Metadata
|
||||
|
||||
lod0 float32
|
||||
lod1 float32
|
||||
cSel uint8
|
||||
nSel uint8
|
||||
|
||||
disabled bool
|
||||
|
||||
firstStartPath bool
|
||||
prevSmoothType uint8
|
||||
prevSmoothPointX float32
|
||||
prevSmoothPointY float32
|
||||
|
||||
fill image.Image
|
||||
flatColor color.RGBA
|
||||
flatImage image.Uniform
|
||||
gradient gradient.Gradient
|
||||
|
||||
cReg [64]color.RGBA
|
||||
nReg [64]float32
|
||||
stops [64]gradient.Stop
|
||||
}
|
||||
|
||||
// SetDstImage sets the Rasterizer to draw onto a destination image, given by
|
||||
// dst and r, with the given compositing operator.
|
||||
//
|
||||
// The IconVG graphic (which does not have a fixed size in pixels) will be
|
||||
// scaled in the X and Y dimensions to fit the rectangle r. The scaling factors
|
||||
// may differ in the two dimensions.
|
||||
func (z *Rasterizer) SetDstImage(dst draw.Image, r image.Rectangle, drawOp draw.Op) {
|
||||
z.dst = dst
|
||||
if r.Empty() {
|
||||
r = image.Rectangle{}
|
||||
}
|
||||
z.r = r
|
||||
z.drawOp = drawOp
|
||||
z.recalcTransform()
|
||||
}
|
||||
|
||||
// Reset resets the Rasterizer for the given Metadata.
|
||||
func (z *Rasterizer) Reset(m Metadata) {
|
||||
z.metadata = m
|
||||
z.lod0 = 0
|
||||
z.lod1 = positiveInfinity
|
||||
z.cSel = 0
|
||||
z.nSel = 0
|
||||
z.firstStartPath = true
|
||||
z.prevSmoothType = smoothTypeNone
|
||||
z.prevSmoothPointX = 0
|
||||
z.prevSmoothPointY = 0
|
||||
z.cReg = m.Palette
|
||||
z.nReg = [64]float32{}
|
||||
z.recalcTransform()
|
||||
}
|
||||
|
||||
func (z *Rasterizer) recalcTransform() {
|
||||
z.scaleX = float32(z.r.Dx()) / (z.metadata.ViewBox.Max[0] - z.metadata.ViewBox.Min[0])
|
||||
z.biasX = -z.metadata.ViewBox.Min[0]
|
||||
z.scaleY = float32(z.r.Dy()) / (z.metadata.ViewBox.Max[1] - z.metadata.ViewBox.Min[1])
|
||||
z.biasY = -z.metadata.ViewBox.Min[1]
|
||||
}
|
||||
|
||||
func (z *Rasterizer) SetCSel(cSel uint8) { z.cSel = cSel & 0x3f }
|
||||
func (z *Rasterizer) SetNSel(nSel uint8) { z.nSel = nSel & 0x3f }
|
||||
|
||||
func (z *Rasterizer) SetCReg(adj uint8, incr bool, c Color) {
|
||||
z.cReg[(z.cSel-adj)&0x3f] = c.Resolve(&z.metadata.Palette, &z.cReg)
|
||||
if incr {
|
||||
z.cSel++
|
||||
}
|
||||
}
|
||||
|
||||
func (z *Rasterizer) SetNReg(adj uint8, incr bool, f float32) {
|
||||
z.nReg[(z.nSel-adj)&0x3f] = f
|
||||
if incr {
|
||||
z.nSel++
|
||||
}
|
||||
}
|
||||
|
||||
func (z *Rasterizer) SetLOD(lod0, lod1 float32) {
|
||||
z.lod0, z.lod1 = lod0, lod1
|
||||
}
|
||||
|
||||
func (z *Rasterizer) unabsX(x float32) float32 { return x/z.scaleX - z.biasX }
|
||||
func (z *Rasterizer) unabsY(y float32) float32 { return y/z.scaleY - z.biasY }
|
||||
|
||||
func (z *Rasterizer) absX(x float32) float32 { return z.scaleX * (x + z.biasX) }
|
||||
func (z *Rasterizer) absY(y float32) float32 { return z.scaleY * (y + z.biasY) }
|
||||
func (z *Rasterizer) relX(x float32) float32 { return z.scaleX * x }
|
||||
func (z *Rasterizer) relY(y float32) float32 { return z.scaleY * y }
|
||||
|
||||
func (z *Rasterizer) absVec2(x, y float32) (zx, zy float32) {
|
||||
return z.absX(x), z.absY(y)
|
||||
}
|
||||
|
||||
func (z *Rasterizer) relVec2(x, y float32) (zx, zy float32) {
|
||||
px, py := z.z.Pen()
|
||||
return px + z.relX(x), py + z.relY(y)
|
||||
}
|
||||
|
||||
// implicitSmoothPoint returns the implicit control point for smooth-quadratic
|
||||
// and smooth-cubic Bézier curves.
|
||||
//
|
||||
// https://www.w3.org/TR/SVG/paths.html#PathDataCurveCommands says, "The first
|
||||
// control point is assumed to be the reflection of the second control point on
|
||||
// the previous command relative to the current point. (If there is no previous
|
||||
// command or if the previous command was not [a quadratic or cubic command],
|
||||
// assume the first control point is coincident with the current point.)"
|
||||
func (z *Rasterizer) implicitSmoothPoint(thisSmoothType uint8) (zx, zy float32) {
|
||||
px, py := z.z.Pen()
|
||||
if z.prevSmoothType != thisSmoothType {
|
||||
return px, py
|
||||
}
|
||||
return 2*px - z.prevSmoothPointX, 2*py - z.prevSmoothPointY
|
||||
}
|
||||
|
||||
func (z *Rasterizer) initGradient(rgba color.RGBA) (ok bool) {
|
||||
nStops := int(rgba.R & 0x3f)
|
||||
cBase := int(rgba.G & 0x3f)
|
||||
nBase := int(rgba.B & 0x3f)
|
||||
prevN := negativeInfinity
|
||||
for i := 0; i < nStops; i++ {
|
||||
c := z.cReg[(cBase+i)&0x3f]
|
||||
if !validAlphaPremulColor(c) {
|
||||
return false
|
||||
}
|
||||
n := z.nReg[(nBase+i)&0x3f]
|
||||
if !(0 <= n && n <= 1) || !(n > prevN) {
|
||||
return false
|
||||
}
|
||||
prevN = n
|
||||
z.stops[i] = gradient.Stop{
|
||||
Offset: float64(n),
|
||||
RGBA64: color.RGBA64{
|
||||
R: uint16(c.R) * 0x101,
|
||||
G: uint16(c.G) * 0x101,
|
||||
B: uint16(c.B) * 0x101,
|
||||
A: uint16(c.A) * 0x101,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// The affine transformation matrix in the IconVG graphic, stored in 6
|
||||
// contiguous NREG registers, goes from graphic coordinate space (i.e. the
|
||||
// metadata viewBox) to the gradient coordinate space. We need it to start
|
||||
// in pixel space, not graphic coordinate space.
|
||||
|
||||
invZSX := 1 / float64(z.scaleX)
|
||||
invZSY := 1 / float64(z.scaleY)
|
||||
zBX := float64(z.biasX)
|
||||
zBY := float64(z.biasY)
|
||||
|
||||
a := float64(z.nReg[(nBase-6)&0x3f])
|
||||
b := float64(z.nReg[(nBase-5)&0x3f])
|
||||
c := float64(z.nReg[(nBase-4)&0x3f])
|
||||
d := float64(z.nReg[(nBase-3)&0x3f])
|
||||
e := float64(z.nReg[(nBase-2)&0x3f])
|
||||
f := float64(z.nReg[(nBase-1)&0x3f])
|
||||
|
||||
pix2Grad := f64.Aff3{
|
||||
a * invZSX,
|
||||
b * invZSY,
|
||||
c - a*zBX - b*zBY,
|
||||
d * invZSX,
|
||||
e * invZSY,
|
||||
f - d*zBX - e*zBY,
|
||||
}
|
||||
|
||||
shape := gradient.ShapeLinear
|
||||
if (rgba.B>>6)&0x01 != 0 {
|
||||
shape = gradient.ShapeRadial
|
||||
}
|
||||
z.gradient.Init(
|
||||
shape,
|
||||
gradient.Spread(rgba.G>>6),
|
||||
pix2Grad,
|
||||
z.stops[:nStops],
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (z *Rasterizer) StartPath(adj uint8, x, y float32) {
|
||||
z.flatColor = z.cReg[(z.cSel-adj)&0x3f]
|
||||
if validAlphaPremulColor(z.flatColor) {
|
||||
z.flatImage.C = &z.flatColor
|
||||
z.fill = &z.flatImage
|
||||
z.disabled = z.flatColor.A == 0
|
||||
} else if z.flatColor.A == 0x00 && z.flatColor.B&0x80 != 0 {
|
||||
z.fill = &z.gradient
|
||||
z.disabled = !z.initGradient(z.flatColor)
|
||||
} else {
|
||||
z.fill = nil
|
||||
z.disabled = true
|
||||
}
|
||||
|
||||
width, height := z.r.Dx(), z.r.Dy()
|
||||
h := float32(height)
|
||||
z.disabled = z.disabled || !(z.lod0 <= h && h < z.lod1)
|
||||
if z.disabled {
|
||||
return
|
||||
}
|
||||
|
||||
z.z.Reset(width, height)
|
||||
if z.firstStartPath {
|
||||
z.firstStartPath = false
|
||||
z.z.DrawOp = z.drawOp
|
||||
}
|
||||
z.prevSmoothType = smoothTypeNone
|
||||
z.z.MoveTo(z.absVec2(x, y))
|
||||
}
|
||||
|
||||
func (z *Rasterizer) ClosePathEndPath() {
|
||||
if z.disabled {
|
||||
return
|
||||
}
|
||||
z.z.ClosePath()
|
||||
if z.dst == nil {
|
||||
return
|
||||
}
|
||||
z.z.Draw(z.dst, z.r, z.fill, image.Point{})
|
||||
}
|
||||
|
||||
func (z *Rasterizer) ClosePathAbsMoveTo(x, y float32) {
|
||||
if z.disabled {
|
||||
return
|
||||
}
|
||||
z.prevSmoothType = smoothTypeNone
|
||||
z.z.ClosePath()
|
||||
z.z.MoveTo(z.absVec2(x, y))
|
||||
}
|
||||
|
||||
func (z *Rasterizer) ClosePathRelMoveTo(x, y float32) {
|
||||
if z.disabled {
|
||||
return
|
||||
}
|
||||
z.prevSmoothType = smoothTypeNone
|
||||
z.z.ClosePath()
|
||||
z.z.MoveTo(z.relVec2(x, y))
|
||||
}
|
||||
|
||||
func (z *Rasterizer) AbsHLineTo(x float32) {
|
||||
if z.disabled {
|
||||
return
|
||||
}
|
||||
_, py := z.z.Pen()
|
||||
z.prevSmoothType = smoothTypeNone
|
||||
z.z.LineTo(z.absX(x), py)
|
||||
}
|
||||
|
||||
func (z *Rasterizer) RelHLineTo(x float32) {
|
||||
if z.disabled {
|
||||
return
|
||||
}
|
||||
px, py := z.z.Pen()
|
||||
z.prevSmoothType = smoothTypeNone
|
||||
z.z.LineTo(px+z.relX(x), py)
|
||||
}
|
||||
|
||||
func (z *Rasterizer) AbsVLineTo(y float32) {
|
||||
if z.disabled {
|
||||
return
|
||||
}
|
||||
px, _ := z.z.Pen()
|
||||
z.prevSmoothType = smoothTypeNone
|
||||
z.z.LineTo(px, z.absY(y))
|
||||
}
|
||||
|
||||
func (z *Rasterizer) RelVLineTo(y float32) {
|
||||
if z.disabled {
|
||||
return
|
||||
}
|
||||
px, py := z.z.Pen()
|
||||
z.prevSmoothType = smoothTypeNone
|
||||
z.z.LineTo(px, py+z.relY(y))
|
||||
}
|
||||
|
||||
func (z *Rasterizer) AbsLineTo(x, y float32) {
|
||||
if z.disabled {
|
||||
return
|
||||
}
|
||||
z.prevSmoothType = smoothTypeNone
|
||||
z.z.LineTo(z.absVec2(x, y))
|
||||
}
|
||||
|
||||
func (z *Rasterizer) RelLineTo(x, y float32) {
|
||||
if z.disabled {
|
||||
return
|
||||
}
|
||||
z.prevSmoothType = smoothTypeNone
|
||||
z.z.LineTo(z.relVec2(x, y))
|
||||
}
|
||||
|
||||
func (z *Rasterizer) AbsSmoothQuadTo(x, y float32) {
|
||||
if z.disabled {
|
||||
return
|
||||
}
|
||||
x1, y1 := z.implicitSmoothPoint(smoothTypeQuad)
|
||||
x, y = z.absVec2(x, y)
|
||||
z.prevSmoothType = smoothTypeQuad
|
||||
z.prevSmoothPointX, z.prevSmoothPointY = x1, y1
|
||||
z.z.QuadTo(x1, y1, x, y)
|
||||
}
|
||||
|
||||
func (z *Rasterizer) RelSmoothQuadTo(x, y float32) {
|
||||
if z.disabled {
|
||||
return
|
||||
}
|
||||
x1, y1 := z.implicitSmoothPoint(smoothTypeQuad)
|
||||
x, y = z.relVec2(x, y)
|
||||
z.prevSmoothType = smoothTypeQuad
|
||||
z.prevSmoothPointX, z.prevSmoothPointY = x1, y1
|
||||
z.z.QuadTo(x1, y1, x, y)
|
||||
}
|
||||
|
||||
func (z *Rasterizer) AbsQuadTo(x1, y1, x, y float32) {
|
||||
if z.disabled {
|
||||
return
|
||||
}
|
||||
x1, y1 = z.absVec2(x1, y1)
|
||||
x, y = z.absVec2(x, y)
|
||||
z.prevSmoothType = smoothTypeQuad
|
||||
z.prevSmoothPointX, z.prevSmoothPointY = x1, y1
|
||||
z.z.QuadTo(x1, y1, x, y)
|
||||
}
|
||||
|
||||
func (z *Rasterizer) RelQuadTo(x1, y1, x, y float32) {
|
||||
if z.disabled {
|
||||
return
|
||||
}
|
||||
x1, y1 = z.relVec2(x1, y1)
|
||||
x, y = z.relVec2(x, y)
|
||||
z.prevSmoothType = smoothTypeQuad
|
||||
z.prevSmoothPointX, z.prevSmoothPointY = x1, y1
|
||||
z.z.QuadTo(x1, y1, x, y)
|
||||
}
|
||||
|
||||
func (z *Rasterizer) AbsSmoothCubeTo(x2, y2, x, y float32) {
|
||||
if z.disabled {
|
||||
return
|
||||
}
|
||||
x1, y1 := z.implicitSmoothPoint(smoothTypeCube)
|
||||
x2, y2 = z.absVec2(x2, y2)
|
||||
x, y = z.absVec2(x, y)
|
||||
z.prevSmoothType = smoothTypeCube
|
||||
z.prevSmoothPointX, z.prevSmoothPointY = x2, y2
|
||||
z.z.CubeTo(x1, y1, x2, y2, x, y)
|
||||
}
|
||||
|
||||
func (z *Rasterizer) RelSmoothCubeTo(x2, y2, x, y float32) {
|
||||
if z.disabled {
|
||||
return
|
||||
}
|
||||
x1, y1 := z.implicitSmoothPoint(smoothTypeCube)
|
||||
x2, y2 = z.relVec2(x2, y2)
|
||||
x, y = z.relVec2(x, y)
|
||||
z.prevSmoothType = smoothTypeCube
|
||||
z.prevSmoothPointX, z.prevSmoothPointY = x2, y2
|
||||
z.z.CubeTo(x1, y1, x2, y2, x, y)
|
||||
}
|
||||
|
||||
func (z *Rasterizer) AbsCubeTo(x1, y1, x2, y2, x, y float32) {
|
||||
if z.disabled {
|
||||
return
|
||||
}
|
||||
x1, y1 = z.absVec2(x1, y1)
|
||||
x2, y2 = z.absVec2(x2, y2)
|
||||
x, y = z.absVec2(x, y)
|
||||
z.prevSmoothType = smoothTypeCube
|
||||
z.prevSmoothPointX, z.prevSmoothPointY = x2, y2
|
||||
z.z.CubeTo(x1, y1, x2, y2, x, y)
|
||||
}
|
||||
|
||||
func (z *Rasterizer) RelCubeTo(x1, y1, x2, y2, x, y float32) {
|
||||
if z.disabled {
|
||||
return
|
||||
}
|
||||
x1, y1 = z.relVec2(x1, y1)
|
||||
x2, y2 = z.relVec2(x2, y2)
|
||||
x, y = z.relVec2(x, y)
|
||||
z.prevSmoothType = smoothTypeCube
|
||||
z.prevSmoothPointX, z.prevSmoothPointY = x2, y2
|
||||
z.z.CubeTo(x1, y1, x2, y2, x, y)
|
||||
}
|
||||
|
||||
func (z *Rasterizer) AbsArcTo(rx, ry, xAxisRotation float32, largeArc, sweep bool, x, y float32) {
|
||||
if z.disabled {
|
||||
return
|
||||
}
|
||||
z.prevSmoothType = smoothTypeNone
|
||||
|
||||
// We follow the "Conversion from endpoint to center parameterization"
|
||||
// algorithm as per
|
||||
// https://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
|
||||
|
||||
// There seems to be a bug in the spec's "implementation notes".
|
||||
//
|
||||
// Actual implementations, such as
|
||||
// - https://git.gnome.org/browse/librsvg/tree/rsvg-path.c
|
||||
// - http://svn.apache.org/repos/asf/xmlgraphics/batik/branches/svg11/sources/org/apache/batik/ext/awt/geom/ExtendedGeneralPath.java
|
||||
// - https://java.net/projects/svgsalamander/sources/svn/content/trunk/svg-core/src/main/java/com/kitfox/svg/pathcmd/Arc.java
|
||||
// - https://github.com/millermedeiros/SVGParser/blob/master/com/millermedeiros/geom/SVGArc.as
|
||||
// do something slightly different (marked with a †).
|
||||
|
||||
// (†) The Abs isn't part of the spec. Neither is checking that Rx and Ry
|
||||
// are non-zero (and non-NaN).
|
||||
Rx := math.Abs(float64(rx))
|
||||
Ry := math.Abs(float64(ry))
|
||||
if !(Rx > 0 && Ry > 0) {
|
||||
z.z.LineTo(x, y)
|
||||
return
|
||||
}
|
||||
|
||||
// We work in IconVG coordinates (e.g. from -32 to +32 by default), rather
|
||||
// than destination image coordinates (e.g. the width of the dst image),
|
||||
// since the rx and ry radii also need to be scaled, but their scaling
|
||||
// factors can be different, and aren't trivial to calculate due to
|
||||
// xAxisRotation.
|
||||
//
|
||||
// We convert back to destination image coordinates via absX and absY calls
|
||||
// later, during arcSegmentTo.
|
||||
penX, penY := z.z.Pen()
|
||||
x1 := float64(z.unabsX(penX))
|
||||
y1 := float64(z.unabsY(penY))
|
||||
x2 := float64(x)
|
||||
y2 := float64(y)
|
||||
|
||||
phi := 2 * math.Pi * float64(xAxisRotation)
|
||||
|
||||
// Step 1: Compute (x1′, y1′)
|
||||
halfDx := (x1 - x2) / 2
|
||||
halfDy := (y1 - y2) / 2
|
||||
cosPhi := math.Cos(phi)
|
||||
sinPhi := math.Sin(phi)
|
||||
x1Prime := +cosPhi*halfDx + sinPhi*halfDy
|
||||
y1Prime := -sinPhi*halfDx + cosPhi*halfDy
|
||||
|
||||
// Step 2: Compute (cx′, cy′)
|
||||
rxSq := Rx * Rx
|
||||
rySq := Ry * Ry
|
||||
x1PrimeSq := x1Prime * x1Prime
|
||||
y1PrimeSq := y1Prime * y1Prime
|
||||
|
||||
// (†) Check that the radii are large enough.
|
||||
radiiCheck := x1PrimeSq/rxSq + y1PrimeSq/rySq
|
||||
if radiiCheck > 1 {
|
||||
c := math.Sqrt(radiiCheck)
|
||||
Rx *= c
|
||||
Ry *= c
|
||||
rxSq = Rx * Rx
|
||||
rySq = Ry * Ry
|
||||
}
|
||||
|
||||
denom := rxSq*y1PrimeSq + rySq*x1PrimeSq
|
||||
step2 := 0.0
|
||||
if a := rxSq*rySq/denom - 1; a > 0 {
|
||||
step2 = math.Sqrt(a)
|
||||
}
|
||||
if largeArc == sweep {
|
||||
step2 = -step2
|
||||
}
|
||||
cxPrime := +step2 * Rx * y1Prime / Ry
|
||||
cyPrime := -step2 * Ry * x1Prime / Rx
|
||||
|
||||
// Step 3: Compute (cx, cy) from (cx′, cy′)
|
||||
cx := +cosPhi*cxPrime - sinPhi*cyPrime + (x1+x2)/2
|
||||
cy := +sinPhi*cxPrime + cosPhi*cyPrime + (y1+y2)/2
|
||||
|
||||
// Step 4: Compute θ1 and Δθ
|
||||
ax := (+x1Prime - cxPrime) / Rx
|
||||
ay := (+y1Prime - cyPrime) / Ry
|
||||
bx := (-x1Prime - cxPrime) / Rx
|
||||
by := (-y1Prime - cyPrime) / Ry
|
||||
theta1 := angle(1, 0, ax, ay)
|
||||
deltaTheta := angle(ax, ay, bx, by)
|
||||
if sweep {
|
||||
if deltaTheta < 0 {
|
||||
deltaTheta += 2 * math.Pi
|
||||
}
|
||||
} else {
|
||||
if deltaTheta > 0 {
|
||||
deltaTheta -= 2 * math.Pi
|
||||
}
|
||||
}
|
||||
|
||||
// This ends the
|
||||
// https://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
|
||||
// algorithm. What follows below is specific to this implementation.
|
||||
|
||||
// We approximate an arc by one or more cubic Bézier curves.
|
||||
n := int(math.Ceil(math.Abs(deltaTheta) / (math.Pi/2 + 0.001)))
|
||||
for i := 0; i < n; i++ {
|
||||
z.arcSegmentTo(cx, cy,
|
||||
theta1+deltaTheta*float64(i+0)/float64(n),
|
||||
theta1+deltaTheta*float64(i+1)/float64(n),
|
||||
Rx, Ry, cosPhi, sinPhi,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// arcSegmentTo approximates an arc by a cubic Bézier curve. The mathematical
|
||||
// formulae for the control points are the same as that used by librsvg.
|
||||
func (z *Rasterizer) arcSegmentTo(cx, cy, theta1, theta2, rx, ry, cosPhi, sinPhi float64) {
|
||||
halfDeltaTheta := (theta2 - theta1) * 0.5
|
||||
q := math.Sin(halfDeltaTheta * 0.5)
|
||||
t := (8 * q * q) / (3 * math.Sin(halfDeltaTheta))
|
||||
cos1 := math.Cos(theta1)
|
||||
sin1 := math.Sin(theta1)
|
||||
cos2 := math.Cos(theta2)
|
||||
sin2 := math.Sin(theta2)
|
||||
x1 := rx * (+cos1 - t*sin1)
|
||||
y1 := ry * (+sin1 + t*cos1)
|
||||
x2 := rx * (+cos2 + t*sin2)
|
||||
y2 := ry * (+sin2 - t*cos2)
|
||||
x3 := rx * (+cos2)
|
||||
y3 := ry * (+sin2)
|
||||
z.z.CubeTo(
|
||||
z.absX(float32(cx+cosPhi*x1-sinPhi*y1)),
|
||||
z.absY(float32(cy+sinPhi*x1+cosPhi*y1)),
|
||||
z.absX(float32(cx+cosPhi*x2-sinPhi*y2)),
|
||||
z.absY(float32(cy+sinPhi*x2+cosPhi*y2)),
|
||||
z.absX(float32(cx+cosPhi*x3-sinPhi*y3)),
|
||||
z.absY(float32(cy+sinPhi*x3+cosPhi*y3)),
|
||||
)
|
||||
}
|
||||
|
||||
func (z *Rasterizer) RelArcTo(rx, ry, xAxisRotation float32, largeArc, sweep bool, x, y float32) {
|
||||
ax, ay := z.relVec2(x, y)
|
||||
z.AbsArcTo(rx, ry, xAxisRotation, largeArc, sweep, z.unabsX(ax), z.unabsY(ay))
|
||||
}
|
||||
|
||||
// angle returns the angle between the u and v vectors.
|
||||
func angle(ux, uy, vx, vy float64) float64 {
|
||||
uNorm := math.Sqrt(ux*ux + uy*uy)
|
||||
vNorm := math.Sqrt(vx*vx + vy*vy)
|
||||
norm := uNorm * vNorm
|
||||
cos := (ux*vx + uy*vy) / norm
|
||||
ret := 0.0
|
||||
if cos <= -1 {
|
||||
ret = math.Pi
|
||||
} else if cos >= +1 {
|
||||
ret = 0
|
||||
} else {
|
||||
ret = math.Acos(cos)
|
||||
}
|
||||
if ux*vy < uy*vx {
|
||||
return -ret
|
||||
}
|
||||
return +ret
|
||||
}
|
||||
+1100
File diff suppressed because it is too large
Load Diff
+202
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+10969
File diff suppressed because it is too large
Load Diff
+12
@@ -0,0 +1,12 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:generate go run gen.go -mdicons=/path/to/the/material-design-icons
|
||||
|
||||
// Package icons contains the Material Design icon set, in the IconVG vector
|
||||
// graphic format.
|
||||
//
|
||||
// See https://design.google.com/icons/ and
|
||||
// https://godoc.org/golang.org/x/exp/shiny/iconvg
|
||||
package icons
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
Copyright 2009 The Go Authors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google LLC nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
||||
+795
@@ -0,0 +1,795 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:generate go run gen.go
|
||||
|
||||
// Package ccitt implements a CCITT (fax) image decoder.
|
||||
package ccitt
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"image"
|
||||
"io"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
var (
|
||||
errIncompleteCode = errors.New("ccitt: incomplete code")
|
||||
errInvalidBounds = errors.New("ccitt: invalid bounds")
|
||||
errInvalidCode = errors.New("ccitt: invalid code")
|
||||
errInvalidMode = errors.New("ccitt: invalid mode")
|
||||
errInvalidOffset = errors.New("ccitt: invalid offset")
|
||||
errMissingEOL = errors.New("ccitt: missing End-of-Line")
|
||||
errRunLengthOverflowsWidth = errors.New("ccitt: run length overflows width")
|
||||
errRunLengthTooLong = errors.New("ccitt: run length too long")
|
||||
errUnsupportedMode = errors.New("ccitt: unsupported mode")
|
||||
errUnsupportedSubFormat = errors.New("ccitt: unsupported sub-format")
|
||||
errUnsupportedWidth = errors.New("ccitt: unsupported width")
|
||||
)
|
||||
|
||||
// Order specifies the bit ordering in a CCITT data stream.
|
||||
type Order uint32
|
||||
|
||||
const (
|
||||
// LSB means Least Significant Bits first.
|
||||
LSB Order = iota
|
||||
// MSB means Most Significant Bits first.
|
||||
MSB
|
||||
)
|
||||
|
||||
// SubFormat represents that the CCITT format consists of a number of
|
||||
// sub-formats. Decoding or encoding a CCITT data stream requires knowing the
|
||||
// sub-format context. It is not represented in the data stream per se.
|
||||
type SubFormat uint32
|
||||
|
||||
const (
|
||||
Group3 SubFormat = iota
|
||||
Group4
|
||||
)
|
||||
|
||||
// AutoDetectHeight is passed as the height argument to NewReader to indicate
|
||||
// that the image height (the number of rows) is not known in advance.
|
||||
const AutoDetectHeight = -1
|
||||
|
||||
// Options are optional parameters.
|
||||
type Options struct {
|
||||
// Align means that some variable-bit-width codes are byte-aligned.
|
||||
Align bool
|
||||
// Invert means that black is the 1 bit or 0xFF byte, and white is 0.
|
||||
Invert bool
|
||||
}
|
||||
|
||||
// maxWidth is the maximum (inclusive) supported width. This is a limitation of
|
||||
// this implementation, to guard against integer overflow, and not anything
|
||||
// inherent to the CCITT format.
|
||||
const maxWidth = 1 << 20
|
||||
|
||||
func invertBytes(b []byte) {
|
||||
for i, c := range b {
|
||||
b[i] = ^c
|
||||
}
|
||||
}
|
||||
|
||||
func reverseBitsWithinBytes(b []byte) {
|
||||
for i, c := range b {
|
||||
b[i] = bits.Reverse8(c)
|
||||
}
|
||||
}
|
||||
|
||||
// highBits writes to dst (1 bit per pixel, most significant bit first) the
|
||||
// high (0x80) bits from src (1 byte per pixel). It returns the number of bytes
|
||||
// written and read such that dst[:d] is the packed form of src[:s].
|
||||
//
|
||||
// For example, if src starts with the 8 bytes [0x7D, 0x7E, 0x7F, 0x80, 0x81,
|
||||
// 0x82, 0x00, 0xFF] then 0x1D will be written to dst[0].
|
||||
//
|
||||
// If src has (8 * len(dst)) or more bytes then only len(dst) bytes are
|
||||
// written, (8 * len(dst)) bytes are read, and invert is ignored.
|
||||
//
|
||||
// Otherwise, if len(src) is not a multiple of 8 then the final byte written to
|
||||
// dst is padded with 1 bits (if invert is true) or 0 bits. If inverted, the 1s
|
||||
// are typically temporary, e.g. they will be flipped back to 0s by an
|
||||
// invertBytes call in the highBits caller, reader.Read.
|
||||
func highBits(dst []byte, src []byte, invert bool) (d int, s int) {
|
||||
// Pack as many complete groups of 8 src bytes as we can.
|
||||
n := len(src) / 8
|
||||
if n > len(dst) {
|
||||
n = len(dst)
|
||||
}
|
||||
dstN := dst[:n]
|
||||
for i := range dstN {
|
||||
src8 := src[i*8 : i*8+8]
|
||||
dstN[i] = ((src8[0] & 0x80) >> 0) |
|
||||
((src8[1] & 0x80) >> 1) |
|
||||
((src8[2] & 0x80) >> 2) |
|
||||
((src8[3] & 0x80) >> 3) |
|
||||
((src8[4] & 0x80) >> 4) |
|
||||
((src8[5] & 0x80) >> 5) |
|
||||
((src8[6] & 0x80) >> 6) |
|
||||
((src8[7] & 0x80) >> 7)
|
||||
}
|
||||
d, s = n, 8*n
|
||||
dst, src = dst[d:], src[s:]
|
||||
|
||||
// Pack up to 7 remaining src bytes, if there's room in dst.
|
||||
if (len(dst) > 0) && (len(src) > 0) {
|
||||
dstByte := byte(0)
|
||||
if invert {
|
||||
dstByte = 0xFF >> uint(len(src))
|
||||
}
|
||||
for n, srcByte := range src {
|
||||
dstByte |= (srcByte & 0x80) >> uint(n)
|
||||
}
|
||||
dst[0] = dstByte
|
||||
d, s = d+1, s+len(src)
|
||||
}
|
||||
return d, s
|
||||
}
|
||||
|
||||
type bitReader struct {
|
||||
r io.Reader
|
||||
|
||||
// readErr is the error returned from the most recent r.Read call. As the
|
||||
// io.Reader documentation says, when r.Read returns (n, err), "always
|
||||
// process the n > 0 bytes returned before considering the error err".
|
||||
readErr error
|
||||
|
||||
// order is whether to process r's bytes LSB first or MSB first.
|
||||
order Order
|
||||
|
||||
// The high nBits bits of the bits field hold upcoming bits in MSB order.
|
||||
bits uint64
|
||||
nBits uint32
|
||||
|
||||
// bytes[br:bw] holds bytes read from r but not yet loaded into bits.
|
||||
br uint32
|
||||
bw uint32
|
||||
bytes [1024]uint8
|
||||
}
|
||||
|
||||
func (b *bitReader) alignToByteBoundary() {
|
||||
n := b.nBits & 7
|
||||
b.bits <<= n
|
||||
b.nBits -= n
|
||||
}
|
||||
|
||||
// nextBitMaxNBits is the maximum possible value of bitReader.nBits after a
|
||||
// bitReader.nextBit call, provided that bitReader.nBits was not more than this
|
||||
// value before that call.
|
||||
//
|
||||
// Note that the decode function can unread bits, which can temporarily set the
|
||||
// bitReader.nBits value above nextBitMaxNBits.
|
||||
const nextBitMaxNBits = 31
|
||||
|
||||
func (b *bitReader) nextBit() (uint64, error) {
|
||||
for {
|
||||
if b.nBits > 0 {
|
||||
bit := b.bits >> 63
|
||||
b.bits <<= 1
|
||||
b.nBits--
|
||||
return bit, nil
|
||||
}
|
||||
|
||||
if available := b.bw - b.br; available >= 4 {
|
||||
// Read 32 bits, even though b.bits is a uint64, since the decode
|
||||
// function may need to unread up to maxCodeLength bits, putting
|
||||
// them back in the remaining (64 - 32) bits. TestMaxCodeLength
|
||||
// checks that the generated maxCodeLength constant fits.
|
||||
//
|
||||
// If changing the Uint32 call, also change nextBitMaxNBits.
|
||||
b.bits = uint64(binary.BigEndian.Uint32(b.bytes[b.br:])) << 32
|
||||
b.br += 4
|
||||
b.nBits = 32
|
||||
continue
|
||||
} else if available > 0 {
|
||||
b.bits = uint64(b.bytes[b.br]) << (7 * 8)
|
||||
b.br++
|
||||
b.nBits = 8
|
||||
continue
|
||||
}
|
||||
|
||||
if b.readErr != nil {
|
||||
return 0, b.readErr
|
||||
}
|
||||
|
||||
n, err := b.r.Read(b.bytes[:])
|
||||
b.br = 0
|
||||
b.bw = uint32(n)
|
||||
b.readErr = err
|
||||
|
||||
if b.order != MSB {
|
||||
reverseBitsWithinBytes(b.bytes[:b.bw])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func decode(b *bitReader, decodeTable [][2]int16) (uint32, error) {
|
||||
nBitsRead, bitsRead, state := uint32(0), uint64(0), int32(1)
|
||||
for {
|
||||
bit, err := b.nextBit()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = errIncompleteCode
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
bitsRead |= bit << (63 - nBitsRead)
|
||||
nBitsRead++
|
||||
|
||||
// The "&1" is redundant, but can eliminate a bounds check.
|
||||
state = int32(decodeTable[state][bit&1])
|
||||
if state < 0 {
|
||||
return uint32(^state), nil
|
||||
} else if state == 0 {
|
||||
// Unread the bits we've read, then return errInvalidCode.
|
||||
b.bits = (b.bits >> nBitsRead) | bitsRead
|
||||
b.nBits += nBitsRead
|
||||
return 0, errInvalidCode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// decodeEOL decodes the 12-bit EOL code 0000_0000_0001.
|
||||
func decodeEOL(b *bitReader) error {
|
||||
nBitsRead, bitsRead := uint32(0), uint64(0)
|
||||
for {
|
||||
bit, err := b.nextBit()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = errMissingEOL
|
||||
}
|
||||
return err
|
||||
}
|
||||
bitsRead |= bit << (63 - nBitsRead)
|
||||
nBitsRead++
|
||||
|
||||
if nBitsRead < 12 {
|
||||
if bit&1 == 0 {
|
||||
continue
|
||||
}
|
||||
} else if bit&1 != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unread the bits we've read, then return errMissingEOL.
|
||||
b.bits = (b.bits >> nBitsRead) | bitsRead
|
||||
b.nBits += nBitsRead
|
||||
return errMissingEOL
|
||||
}
|
||||
}
|
||||
|
||||
type reader struct {
|
||||
br bitReader
|
||||
subFormat SubFormat
|
||||
|
||||
// width is the image width in pixels.
|
||||
width int
|
||||
|
||||
// rowsRemaining starts at the image height in pixels, when the reader is
|
||||
// driven through the io.Reader interface, and decrements to zero as rows
|
||||
// are decoded. Alternatively, it may be negative if the image height is
|
||||
// not known in advance at the time of the NewReader call.
|
||||
//
|
||||
// When driven through DecodeIntoGray, this field is unused.
|
||||
rowsRemaining int
|
||||
|
||||
// curr and prev hold the current and previous rows. Each element is either
|
||||
// 0x00 (black) or 0xFF (white).
|
||||
//
|
||||
// prev may be nil, when processing the first row.
|
||||
curr []byte
|
||||
prev []byte
|
||||
|
||||
// ri is the read index. curr[:ri] are those bytes of curr that have been
|
||||
// passed along via the Read method.
|
||||
//
|
||||
// When the reader is driven through DecodeIntoGray, instead of through the
|
||||
// io.Reader interface, this field is unused.
|
||||
ri int
|
||||
|
||||
// wi is the write index. curr[:wi] are those bytes of curr that have
|
||||
// already been decoded via the decodeRow method.
|
||||
//
|
||||
// What this implementation calls wi is roughly equivalent to what the spec
|
||||
// calls the a0 index.
|
||||
wi int
|
||||
|
||||
// These fields are copied from the *Options (which may be nil).
|
||||
align bool
|
||||
invert bool
|
||||
|
||||
// atStartOfRow is whether we have just started the row. Some parts of the
|
||||
// spec say to treat this situation as if "wi = -1".
|
||||
atStartOfRow bool
|
||||
|
||||
// penColorIsWhite is whether the next run is black or white.
|
||||
penColorIsWhite bool
|
||||
|
||||
// seenStartOfImage is whether we've called the startDecode method.
|
||||
seenStartOfImage bool
|
||||
|
||||
// truncated is whether the input is missing the final 6 consecutive EOL's
|
||||
// (for Group3) or 2 consecutive EOL's (for Group4). Omitting that trailer
|
||||
// (but otherwise padding to a byte boundary, with either all 0 bits or all
|
||||
// 1 bits) is invalid according to the spec, but happens in practice when
|
||||
// exporting from Adobe Acrobat to TIFF + CCITT. This package silently
|
||||
// ignores the format error for CCITT input that has been truncated in that
|
||||
// fashion, returning the full decoded image.
|
||||
//
|
||||
// Detecting trailer truncation (just after the final row of pixels)
|
||||
// requires knowing which row is the final row, and therefore does not
|
||||
// trigger if the image height is not known in advance.
|
||||
truncated bool
|
||||
|
||||
// readErr is a sticky error for the Read method.
|
||||
readErr error
|
||||
}
|
||||
|
||||
func (z *reader) Read(p []byte) (int, error) {
|
||||
if z.readErr != nil {
|
||||
return 0, z.readErr
|
||||
}
|
||||
originalP := p
|
||||
|
||||
for len(p) > 0 {
|
||||
// Allocate buffers (and decode any start-of-image codes), if
|
||||
// processing the first or second row.
|
||||
if z.curr == nil {
|
||||
if !z.seenStartOfImage {
|
||||
if z.readErr = z.startDecode(); z.readErr != nil {
|
||||
break
|
||||
}
|
||||
z.atStartOfRow = true
|
||||
}
|
||||
z.curr = make([]byte, z.width)
|
||||
}
|
||||
|
||||
// Decode the next row, if necessary.
|
||||
if z.atStartOfRow {
|
||||
if z.rowsRemaining < 0 {
|
||||
// We do not know the image height in advance. See if the next
|
||||
// code is an EOL. If it is, it is consumed. If it isn't, the
|
||||
// bitReader shouldn't advance along the bit stream, and we
|
||||
// simply decode another row of pixel data.
|
||||
//
|
||||
// For the Group4 subFormat, we may need to align to a byte
|
||||
// boundary. For the Group3 subFormat, the previous z.decodeRow
|
||||
// call (or z.startDecode call) has already consumed one of the
|
||||
// 6 consecutive EOL's. The next EOL is actually the second of
|
||||
// 6, in the middle, and we shouldn't align at that point.
|
||||
if z.align && (z.subFormat == Group4) {
|
||||
z.br.alignToByteBoundary()
|
||||
}
|
||||
|
||||
if err := z.decodeEOL(); err == errMissingEOL {
|
||||
// No-op. It's another row of pixel data.
|
||||
} else if err != nil {
|
||||
z.readErr = err
|
||||
break
|
||||
} else {
|
||||
if z.readErr = z.finishDecode(true); z.readErr != nil {
|
||||
break
|
||||
}
|
||||
z.readErr = io.EOF
|
||||
break
|
||||
}
|
||||
|
||||
} else if z.rowsRemaining == 0 {
|
||||
// We do know the image height in advance, and we have already
|
||||
// decoded exactly that many rows.
|
||||
if z.readErr = z.finishDecode(false); z.readErr != nil {
|
||||
break
|
||||
}
|
||||
z.readErr = io.EOF
|
||||
break
|
||||
|
||||
} else {
|
||||
z.rowsRemaining--
|
||||
}
|
||||
|
||||
if z.readErr = z.decodeRow(z.rowsRemaining == 0); z.readErr != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Pack from z.curr (1 byte per pixel) to p (1 bit per pixel).
|
||||
packD, packS := highBits(p, z.curr[z.ri:], z.invert)
|
||||
p = p[packD:]
|
||||
z.ri += packS
|
||||
|
||||
// Prepare to decode the next row, if necessary.
|
||||
if z.ri == len(z.curr) {
|
||||
z.ri, z.curr, z.prev = 0, z.prev, z.curr
|
||||
z.atStartOfRow = true
|
||||
}
|
||||
}
|
||||
|
||||
n := len(originalP) - len(p)
|
||||
if z.invert {
|
||||
invertBytes(originalP[:n])
|
||||
}
|
||||
return n, z.readErr
|
||||
}
|
||||
|
||||
func (z *reader) penColor() byte {
|
||||
if z.penColorIsWhite {
|
||||
return 0xFF
|
||||
}
|
||||
return 0x00
|
||||
}
|
||||
|
||||
func (z *reader) startDecode() error {
|
||||
switch z.subFormat {
|
||||
case Group3:
|
||||
if err := z.decodeEOL(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case Group4:
|
||||
// No-op.
|
||||
|
||||
default:
|
||||
return errUnsupportedSubFormat
|
||||
}
|
||||
|
||||
z.seenStartOfImage = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (z *reader) finishDecode(alreadySeenEOL bool) error {
|
||||
numberOfEOLs := 0
|
||||
switch z.subFormat {
|
||||
case Group3:
|
||||
if z.truncated {
|
||||
return nil
|
||||
}
|
||||
// The stream ends with a RTC (Return To Control) of 6 consecutive
|
||||
// EOL's, but we should have already just seen an EOL, either in
|
||||
// z.startDecode (for a zero-height image) or in z.decodeRow.
|
||||
numberOfEOLs = 5
|
||||
|
||||
case Group4:
|
||||
autoDetectHeight := z.rowsRemaining < 0
|
||||
if autoDetectHeight {
|
||||
// Aligning to a byte boundary was already handled by reader.Read.
|
||||
} else if z.align {
|
||||
z.br.alignToByteBoundary()
|
||||
}
|
||||
// The stream ends with two EOL's. If the first one is missing, and we
|
||||
// had an explicit image height, we just assume that the trailing two
|
||||
// EOL's were truncated and return a nil error.
|
||||
if err := z.decodeEOL(); err != nil {
|
||||
if (err == errMissingEOL) && !autoDetectHeight {
|
||||
z.truncated = true
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
numberOfEOLs = 1
|
||||
|
||||
default:
|
||||
return errUnsupportedSubFormat
|
||||
}
|
||||
|
||||
if alreadySeenEOL {
|
||||
numberOfEOLs--
|
||||
}
|
||||
for ; numberOfEOLs > 0; numberOfEOLs-- {
|
||||
if err := z.decodeEOL(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (z *reader) decodeEOL() error {
|
||||
return decodeEOL(&z.br)
|
||||
}
|
||||
|
||||
func (z *reader) decodeRow(finalRow bool) error {
|
||||
z.wi = 0
|
||||
z.atStartOfRow = true
|
||||
z.penColorIsWhite = true
|
||||
|
||||
if z.align {
|
||||
z.br.alignToByteBoundary()
|
||||
}
|
||||
|
||||
switch z.subFormat {
|
||||
case Group3:
|
||||
for ; z.wi < len(z.curr); z.atStartOfRow = false {
|
||||
if err := z.decodeRun(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err := z.decodeEOL()
|
||||
if finalRow && (err == errMissingEOL) {
|
||||
z.truncated = true
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
|
||||
case Group4:
|
||||
for ; z.wi < len(z.curr); z.atStartOfRow = false {
|
||||
mode, err := decode(&z.br, modeDecodeTable[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rm := readerMode{}
|
||||
if mode < uint32(len(readerModes)) {
|
||||
rm = readerModes[mode]
|
||||
}
|
||||
if rm.function == nil {
|
||||
return errInvalidMode
|
||||
}
|
||||
if err := rm.function(z, rm.arg); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return errUnsupportedSubFormat
|
||||
}
|
||||
|
||||
func (z *reader) decodeRun() error {
|
||||
table := blackDecodeTable[:]
|
||||
if z.penColorIsWhite {
|
||||
table = whiteDecodeTable[:]
|
||||
}
|
||||
|
||||
total := 0
|
||||
for {
|
||||
n, err := decode(&z.br, table)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n > maxWidth {
|
||||
panic("unreachable")
|
||||
}
|
||||
total += int(n)
|
||||
if total > maxWidth {
|
||||
return errRunLengthTooLong
|
||||
}
|
||||
// Anything 0x3F or below is a terminal code.
|
||||
if n <= 0x3F {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if total > (len(z.curr) - z.wi) {
|
||||
return errRunLengthOverflowsWidth
|
||||
}
|
||||
dst := z.curr[z.wi : z.wi+total]
|
||||
penColor := z.penColor()
|
||||
for i := range dst {
|
||||
dst[i] = penColor
|
||||
}
|
||||
z.wi += total
|
||||
z.penColorIsWhite = !z.penColorIsWhite
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// The various modes' semantics are based on determining a row of pixels'
|
||||
// "changing elements": those pixels whose color differs from the one on its
|
||||
// immediate left.
|
||||
//
|
||||
// The row above the first row is implicitly all white. Similarly, the column
|
||||
// to the left of the first column is implicitly all white.
|
||||
//
|
||||
// For example, here's Figure 1 in "ITU-T Recommendation T.6", where the
|
||||
// current and previous rows contain black (B) and white (w) pixels. The a?
|
||||
// indexes point into curr, the b? indexes point into prev.
|
||||
//
|
||||
// b1 b2
|
||||
// v v
|
||||
// prev: BBBBBwwwwwBBBwwwww
|
||||
// curr: BBBwwwwwBBBBBBwwww
|
||||
// ^ ^ ^
|
||||
// a0 a1 a2
|
||||
//
|
||||
// a0 is the "reference element" or current decoder position, roughly
|
||||
// equivalent to what this implementation calls reader.wi.
|
||||
//
|
||||
// a1 is the next changing element to the right of a0, on the "coding line"
|
||||
// (the current row).
|
||||
//
|
||||
// a2 is the next changing element to the right of a1, again on curr.
|
||||
//
|
||||
// b1 is the first changing element on the "reference line" (the previous row)
|
||||
// to the right of a0 and of opposite color to a0.
|
||||
//
|
||||
// b2 is the next changing element to the right of b1, again on prev.
|
||||
//
|
||||
// The various modes calculate a1 (and a2, for modeH):
|
||||
// - modePass calculates that a1 is at or to the right of b2.
|
||||
// - modeH calculates a1 and a2 without considering b1 or b2.
|
||||
// - modeV* calculates a1 to be b1 plus an adjustment (between -3 and +3).
|
||||
|
||||
const (
|
||||
findB1 = false
|
||||
findB2 = true
|
||||
)
|
||||
|
||||
// findB finds either the b1 or b2 value.
|
||||
func (z *reader) findB(whichB bool) int {
|
||||
// The initial row is a special case. The previous row is implicitly all
|
||||
// white, so that there are no changing pixel elements. We return b1 or b2
|
||||
// to be at the end of the row.
|
||||
if len(z.prev) != len(z.curr) {
|
||||
return len(z.curr)
|
||||
}
|
||||
|
||||
i := z.wi
|
||||
|
||||
if z.atStartOfRow {
|
||||
// a0 is implicitly at -1, on a white pixel. b1 is the first black
|
||||
// pixel in the previous row. b2 is the first white pixel after that.
|
||||
for ; (i < len(z.prev)) && (z.prev[i] == 0xFF); i++ {
|
||||
}
|
||||
if whichB == findB2 {
|
||||
for ; (i < len(z.prev)) && (z.prev[i] == 0x00); i++ {
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// As per figure 1 above, assume that the current pen color is white.
|
||||
// First, walk past every contiguous black pixel in prev, starting at a0.
|
||||
oppositeColor := ^z.penColor()
|
||||
for ; (i < len(z.prev)) && (z.prev[i] == oppositeColor); i++ {
|
||||
}
|
||||
|
||||
// Then walk past every contiguous white pixel.
|
||||
penColor := ^oppositeColor
|
||||
for ; (i < len(z.prev)) && (z.prev[i] == penColor); i++ {
|
||||
}
|
||||
|
||||
// We're now at a black pixel (or at the end of the row). That's b1.
|
||||
if whichB == findB2 {
|
||||
// If we're looking for b2, walk past every contiguous black pixel
|
||||
// again.
|
||||
oppositeColor := ^penColor
|
||||
for ; (i < len(z.prev)) && (z.prev[i] == oppositeColor); i++ {
|
||||
}
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
type readerMode struct {
|
||||
function func(z *reader, arg int) error
|
||||
arg int
|
||||
}
|
||||
|
||||
var readerModes = [...]readerMode{
|
||||
modePass: {function: readerModePass},
|
||||
modeH: {function: readerModeH},
|
||||
modeV0: {function: readerModeV, arg: +0},
|
||||
modeVR1: {function: readerModeV, arg: +1},
|
||||
modeVR2: {function: readerModeV, arg: +2},
|
||||
modeVR3: {function: readerModeV, arg: +3},
|
||||
modeVL1: {function: readerModeV, arg: -1},
|
||||
modeVL2: {function: readerModeV, arg: -2},
|
||||
modeVL3: {function: readerModeV, arg: -3},
|
||||
modeExt: {function: readerModeExt},
|
||||
}
|
||||
|
||||
func readerModePass(z *reader, arg int) error {
|
||||
b2 := z.findB(findB2)
|
||||
if (b2 < z.wi) || (len(z.curr) < b2) {
|
||||
return errInvalidOffset
|
||||
}
|
||||
dst := z.curr[z.wi:b2]
|
||||
penColor := z.penColor()
|
||||
for i := range dst {
|
||||
dst[i] = penColor
|
||||
}
|
||||
z.wi = b2
|
||||
return nil
|
||||
}
|
||||
|
||||
func readerModeH(z *reader, arg int) error {
|
||||
// The first iteration finds a1. The second finds a2.
|
||||
for i := 0; i < 2; i++ {
|
||||
if err := z.decodeRun(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readerModeV(z *reader, arg int) error {
|
||||
a1 := z.findB(findB1) + arg
|
||||
if (a1 < z.wi) || (len(z.curr) < a1) {
|
||||
return errInvalidOffset
|
||||
}
|
||||
dst := z.curr[z.wi:a1]
|
||||
penColor := z.penColor()
|
||||
for i := range dst {
|
||||
dst[i] = penColor
|
||||
}
|
||||
z.wi = a1
|
||||
z.penColorIsWhite = !z.penColorIsWhite
|
||||
return nil
|
||||
}
|
||||
|
||||
func readerModeExt(z *reader, arg int) error {
|
||||
return errUnsupportedMode
|
||||
}
|
||||
|
||||
// DecodeIntoGray decodes the CCITT-formatted data in r into dst.
|
||||
//
|
||||
// It returns an error if dst's width and height don't match the implied width
|
||||
// and height of CCITT-formatted data.
|
||||
func DecodeIntoGray(dst *image.Gray, r io.Reader, order Order, sf SubFormat, opts *Options) error {
|
||||
bounds := dst.Bounds()
|
||||
if (bounds.Dx() < 0) || (bounds.Dy() < 0) {
|
||||
return errInvalidBounds
|
||||
}
|
||||
if bounds.Dx() > maxWidth {
|
||||
return errUnsupportedWidth
|
||||
}
|
||||
|
||||
z := reader{
|
||||
br: bitReader{r: r, order: order},
|
||||
subFormat: sf,
|
||||
align: (opts != nil) && opts.Align,
|
||||
invert: (opts != nil) && opts.Invert,
|
||||
width: bounds.Dx(),
|
||||
}
|
||||
if err := z.startDecode(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
width := bounds.Dx()
|
||||
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||
p := (y - bounds.Min.Y) * dst.Stride
|
||||
z.curr = dst.Pix[p : p+width]
|
||||
if err := z.decodeRow(y+1 == bounds.Max.Y); err != nil {
|
||||
return err
|
||||
}
|
||||
z.curr, z.prev = nil, z.curr
|
||||
}
|
||||
|
||||
if err := z.finishDecode(false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if z.invert {
|
||||
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||
p := (y - bounds.Min.Y) * dst.Stride
|
||||
invertBytes(dst.Pix[p : p+width])
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewReader returns an io.Reader that decodes the CCITT-formatted data in r.
|
||||
// The resultant byte stream is one bit per pixel (MSB first), with 1 meaning
|
||||
// white and 0 meaning black. Each row in the result is byte-aligned.
|
||||
//
|
||||
// A negative height, such as passing AutoDetectHeight, means that the image
|
||||
// height is not known in advance. A negative width is invalid.
|
||||
func NewReader(r io.Reader, order Order, sf SubFormat, width int, height int, opts *Options) io.Reader {
|
||||
readErr := error(nil)
|
||||
if width < 0 {
|
||||
readErr = errInvalidBounds
|
||||
} else if width > maxWidth {
|
||||
readErr = errUnsupportedWidth
|
||||
}
|
||||
|
||||
return &reader{
|
||||
br: bitReader{r: r, order: order},
|
||||
subFormat: sf,
|
||||
align: (opts != nil) && opts.Align,
|
||||
invert: (opts != nil) && opts.Invert,
|
||||
width: width,
|
||||
rowsRemaining: height,
|
||||
readErr: readErr,
|
||||
}
|
||||
}
|
||||
+972
@@ -0,0 +1,972 @@
|
||||
// generated by "go run gen.go". DO NOT EDIT.
|
||||
|
||||
package ccitt
|
||||
|
||||
// Each decodeTable is represented by an array of [2]int16's: a binary tree.
|
||||
// Each array element (other than element 0, which means invalid) is a branch
|
||||
// node in that tree. The root node is always element 1 (the second element).
|
||||
//
|
||||
// To walk the tree, look at the next bit in the bit stream, using it to select
|
||||
// the first or second element of the [2]int16. If that int16 is 0, we have an
|
||||
// invalid code. If it is positive, go to that branch node. If it is negative,
|
||||
// then we have a leaf node, whose value is the bitwise complement (the ^
|
||||
// operator) of that int16.
|
||||
//
|
||||
// Comments above each decodeTable also show the same structure visually. The
|
||||
// "b123" lines show the 123'rd branch node. The "=XXXXX" lines show an invalid
|
||||
// code. The "=v1234" lines show a leaf node with value 1234. When reading the
|
||||
// bit stream, a 0 or 1 bit means to go up or down, as you move left to right.
|
||||
//
|
||||
// For example, in modeDecodeTable, branch node b005 is three steps up from the
|
||||
// root node, meaning that we have already seen "000". If the next bit is "0"
|
||||
// then we move to branch node b006. Otherwise, the next bit is "1", and we
|
||||
// move to the leaf node v0000 (also known as the modePass constant). Indeed,
|
||||
// the bits that encode modePass are "0001".
|
||||
//
|
||||
// Tables 1, 2 and 3 come from the "ITU-T Recommendation T.6: FACSIMILE CODING
|
||||
// SCHEMES AND CODING CONTROL FUNCTIONS FOR GROUP 4 FACSIMILE APPARATUS"
|
||||
// specification:
|
||||
//
|
||||
// https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-T.6-198811-I!!PDF-E&type=items
|
||||
|
||||
// modeDecodeTable represents Table 1 and the End-of-Line code.
|
||||
//
|
||||
// +=XXXXX
|
||||
// b009 +-+
|
||||
// | +=v0009
|
||||
// b007 +-+
|
||||
// | | +=v0008
|
||||
// b010 | +-+
|
||||
// | +=v0005
|
||||
// b006 +-+
|
||||
// | | +=v0007
|
||||
// b008 | +-+
|
||||
// | +=v0004
|
||||
// b005 +-+
|
||||
// | +=v0000
|
||||
// b003 +-+
|
||||
// | +=v0001
|
||||
// b002 +-+
|
||||
// | | +=v0006
|
||||
// b004 | +-+
|
||||
// | +=v0003
|
||||
// b001 +-+
|
||||
// +=v0002
|
||||
var modeDecodeTable = [...][2]int16{
|
||||
0: {0, 0},
|
||||
1: {2, ^2},
|
||||
2: {3, 4},
|
||||
3: {5, ^1},
|
||||
4: {^6, ^3},
|
||||
5: {6, ^0},
|
||||
6: {7, 8},
|
||||
7: {9, 10},
|
||||
8: {^7, ^4},
|
||||
9: {0, ^9},
|
||||
10: {^8, ^5},
|
||||
}
|
||||
|
||||
// whiteDecodeTable represents Tables 2 and 3 for a white run.
|
||||
//
|
||||
// +=XXXXX
|
||||
// b059 +-+
|
||||
// | | +=v1792
|
||||
// b096 | | +-+
|
||||
// | | | | +=v1984
|
||||
// b100 | | | +-+
|
||||
// | | | +=v2048
|
||||
// b094 | | +-+
|
||||
// | | | | +=v2112
|
||||
// b101 | | | | +-+
|
||||
// | | | | | +=v2176
|
||||
// b097 | | | +-+
|
||||
// | | | | +=v2240
|
||||
// b102 | | | +-+
|
||||
// | | | +=v2304
|
||||
// b085 | +-+
|
||||
// | | +=v1856
|
||||
// b098 | | +-+
|
||||
// | | | +=v1920
|
||||
// b095 | +-+
|
||||
// | | +=v2368
|
||||
// b103 | | +-+
|
||||
// | | | +=v2432
|
||||
// b099 | +-+
|
||||
// | | +=v2496
|
||||
// b104 | +-+
|
||||
// | +=v2560
|
||||
// b040 +-+
|
||||
// | | +=v0029
|
||||
// b060 | +-+
|
||||
// | +=v0030
|
||||
// b026 +-+
|
||||
// | | +=v0045
|
||||
// b061 | | +-+
|
||||
// | | | +=v0046
|
||||
// b041 | +-+
|
||||
// | +=v0022
|
||||
// b016 +-+
|
||||
// | | +=v0023
|
||||
// b042 | | +-+
|
||||
// | | | | +=v0047
|
||||
// b062 | | | +-+
|
||||
// | | | +=v0048
|
||||
// b027 | +-+
|
||||
// | +=v0013
|
||||
// b008 +-+
|
||||
// | | +=v0020
|
||||
// b043 | | +-+
|
||||
// | | | | +=v0033
|
||||
// b063 | | | +-+
|
||||
// | | | +=v0034
|
||||
// b028 | | +-+
|
||||
// | | | | +=v0035
|
||||
// b064 | | | | +-+
|
||||
// | | | | | +=v0036
|
||||
// b044 | | | +-+
|
||||
// | | | | +=v0037
|
||||
// b065 | | | +-+
|
||||
// | | | +=v0038
|
||||
// b017 | +-+
|
||||
// | | +=v0019
|
||||
// b045 | | +-+
|
||||
// | | | | +=v0031
|
||||
// b066 | | | +-+
|
||||
// | | | +=v0032
|
||||
// b029 | +-+
|
||||
// | +=v0001
|
||||
// b004 +-+
|
||||
// | | +=v0012
|
||||
// b030 | | +-+
|
||||
// | | | | +=v0053
|
||||
// b067 | | | | +-+
|
||||
// | | | | | +=v0054
|
||||
// b046 | | | +-+
|
||||
// | | | +=v0026
|
||||
// b018 | | +-+
|
||||
// | | | | +=v0039
|
||||
// b068 | | | | +-+
|
||||
// | | | | | +=v0040
|
||||
// b047 | | | | +-+
|
||||
// | | | | | | +=v0041
|
||||
// b069 | | | | | +-+
|
||||
// | | | | | +=v0042
|
||||
// b031 | | | +-+
|
||||
// | | | | +=v0043
|
||||
// b070 | | | | +-+
|
||||
// | | | | | +=v0044
|
||||
// b048 | | | +-+
|
||||
// | | | +=v0021
|
||||
// b009 | +-+
|
||||
// | | +=v0028
|
||||
// b049 | | +-+
|
||||
// | | | | +=v0061
|
||||
// b071 | | | +-+
|
||||
// | | | +=v0062
|
||||
// b032 | | +-+
|
||||
// | | | | +=v0063
|
||||
// b072 | | | | +-+
|
||||
// | | | | | +=v0000
|
||||
// b050 | | | +-+
|
||||
// | | | | +=v0320
|
||||
// b073 | | | +-+
|
||||
// | | | +=v0384
|
||||
// b019 | +-+
|
||||
// | +=v0010
|
||||
// b002 +-+
|
||||
// | | +=v0011
|
||||
// b020 | | +-+
|
||||
// | | | | +=v0027
|
||||
// b051 | | | | +-+
|
||||
// | | | | | | +=v0059
|
||||
// b074 | | | | | +-+
|
||||
// | | | | | +=v0060
|
||||
// b033 | | | +-+
|
||||
// | | | | +=v1472
|
||||
// b086 | | | | +-+
|
||||
// | | | | | +=v1536
|
||||
// b075 | | | | +-+
|
||||
// | | | | | | +=v1600
|
||||
// b087 | | | | | +-+
|
||||
// | | | | | +=v1728
|
||||
// b052 | | | +-+
|
||||
// | | | +=v0018
|
||||
// b010 | | +-+
|
||||
// | | | | +=v0024
|
||||
// b053 | | | | +-+
|
||||
// | | | | | | +=v0049
|
||||
// b076 | | | | | +-+
|
||||
// | | | | | +=v0050
|
||||
// b034 | | | | +-+
|
||||
// | | | | | | +=v0051
|
||||
// b077 | | | | | | +-+
|
||||
// | | | | | | | +=v0052
|
||||
// b054 | | | | | +-+
|
||||
// | | | | | +=v0025
|
||||
// b021 | | | +-+
|
||||
// | | | | +=v0055
|
||||
// b078 | | | | +-+
|
||||
// | | | | | +=v0056
|
||||
// b055 | | | | +-+
|
||||
// | | | | | | +=v0057
|
||||
// b079 | | | | | +-+
|
||||
// | | | | | +=v0058
|
||||
// b035 | | | +-+
|
||||
// | | | +=v0192
|
||||
// b005 | +-+
|
||||
// | | +=v1664
|
||||
// b036 | | +-+
|
||||
// | | | | +=v0448
|
||||
// b080 | | | | +-+
|
||||
// | | | | | +=v0512
|
||||
// b056 | | | +-+
|
||||
// | | | | +=v0704
|
||||
// b088 | | | | +-+
|
||||
// | | | | | +=v0768
|
||||
// b081 | | | +-+
|
||||
// | | | +=v0640
|
||||
// b022 | | +-+
|
||||
// | | | | +=v0576
|
||||
// b082 | | | | +-+
|
||||
// | | | | | | +=v0832
|
||||
// b089 | | | | | +-+
|
||||
// | | | | | +=v0896
|
||||
// b057 | | | | +-+
|
||||
// | | | | | | +=v0960
|
||||
// b090 | | | | | | +-+
|
||||
// | | | | | | | +=v1024
|
||||
// b083 | | | | | +-+
|
||||
// | | | | | | +=v1088
|
||||
// b091 | | | | | +-+
|
||||
// | | | | | +=v1152
|
||||
// b037 | | | +-+
|
||||
// | | | | +=v1216
|
||||
// b092 | | | | +-+
|
||||
// | | | | | +=v1280
|
||||
// b084 | | | | +-+
|
||||
// | | | | | | +=v1344
|
||||
// b093 | | | | | +-+
|
||||
// | | | | | +=v1408
|
||||
// b058 | | | +-+
|
||||
// | | | +=v0256
|
||||
// b011 | +-+
|
||||
// | +=v0002
|
||||
// b001 +-+
|
||||
// | +=v0003
|
||||
// b012 | +-+
|
||||
// | | | +=v0128
|
||||
// b023 | | +-+
|
||||
// | | +=v0008
|
||||
// b006 | +-+
|
||||
// | | | +=v0009
|
||||
// b024 | | | +-+
|
||||
// | | | | | +=v0016
|
||||
// b038 | | | | +-+
|
||||
// | | | | +=v0017
|
||||
// b013 | | +-+
|
||||
// | | +=v0004
|
||||
// b003 +-+
|
||||
// | +=v0005
|
||||
// b014 | +-+
|
||||
// | | | +=v0014
|
||||
// b039 | | | +-+
|
||||
// | | | | +=v0015
|
||||
// b025 | | +-+
|
||||
// | | +=v0064
|
||||
// b007 +-+
|
||||
// | +=v0006
|
||||
// b015 +-+
|
||||
// +=v0007
|
||||
var whiteDecodeTable = [...][2]int16{
|
||||
0: {0, 0},
|
||||
1: {2, 3},
|
||||
2: {4, 5},
|
||||
3: {6, 7},
|
||||
4: {8, 9},
|
||||
5: {10, 11},
|
||||
6: {12, 13},
|
||||
7: {14, 15},
|
||||
8: {16, 17},
|
||||
9: {18, 19},
|
||||
10: {20, 21},
|
||||
11: {22, ^2},
|
||||
12: {^3, 23},
|
||||
13: {24, ^4},
|
||||
14: {^5, 25},
|
||||
15: {^6, ^7},
|
||||
16: {26, 27},
|
||||
17: {28, 29},
|
||||
18: {30, 31},
|
||||
19: {32, ^10},
|
||||
20: {^11, 33},
|
||||
21: {34, 35},
|
||||
22: {36, 37},
|
||||
23: {^128, ^8},
|
||||
24: {^9, 38},
|
||||
25: {39, ^64},
|
||||
26: {40, 41},
|
||||
27: {42, ^13},
|
||||
28: {43, 44},
|
||||
29: {45, ^1},
|
||||
30: {^12, 46},
|
||||
31: {47, 48},
|
||||
32: {49, 50},
|
||||
33: {51, 52},
|
||||
34: {53, 54},
|
||||
35: {55, ^192},
|
||||
36: {^1664, 56},
|
||||
37: {57, 58},
|
||||
38: {^16, ^17},
|
||||
39: {^14, ^15},
|
||||
40: {59, 60},
|
||||
41: {61, ^22},
|
||||
42: {^23, 62},
|
||||
43: {^20, 63},
|
||||
44: {64, 65},
|
||||
45: {^19, 66},
|
||||
46: {67, ^26},
|
||||
47: {68, 69},
|
||||
48: {70, ^21},
|
||||
49: {^28, 71},
|
||||
50: {72, 73},
|
||||
51: {^27, 74},
|
||||
52: {75, ^18},
|
||||
53: {^24, 76},
|
||||
54: {77, ^25},
|
||||
55: {78, 79},
|
||||
56: {80, 81},
|
||||
57: {82, 83},
|
||||
58: {84, ^256},
|
||||
59: {0, 85},
|
||||
60: {^29, ^30},
|
||||
61: {^45, ^46},
|
||||
62: {^47, ^48},
|
||||
63: {^33, ^34},
|
||||
64: {^35, ^36},
|
||||
65: {^37, ^38},
|
||||
66: {^31, ^32},
|
||||
67: {^53, ^54},
|
||||
68: {^39, ^40},
|
||||
69: {^41, ^42},
|
||||
70: {^43, ^44},
|
||||
71: {^61, ^62},
|
||||
72: {^63, ^0},
|
||||
73: {^320, ^384},
|
||||
74: {^59, ^60},
|
||||
75: {86, 87},
|
||||
76: {^49, ^50},
|
||||
77: {^51, ^52},
|
||||
78: {^55, ^56},
|
||||
79: {^57, ^58},
|
||||
80: {^448, ^512},
|
||||
81: {88, ^640},
|
||||
82: {^576, 89},
|
||||
83: {90, 91},
|
||||
84: {92, 93},
|
||||
85: {94, 95},
|
||||
86: {^1472, ^1536},
|
||||
87: {^1600, ^1728},
|
||||
88: {^704, ^768},
|
||||
89: {^832, ^896},
|
||||
90: {^960, ^1024},
|
||||
91: {^1088, ^1152},
|
||||
92: {^1216, ^1280},
|
||||
93: {^1344, ^1408},
|
||||
94: {96, 97},
|
||||
95: {98, 99},
|
||||
96: {^1792, 100},
|
||||
97: {101, 102},
|
||||
98: {^1856, ^1920},
|
||||
99: {103, 104},
|
||||
100: {^1984, ^2048},
|
||||
101: {^2112, ^2176},
|
||||
102: {^2240, ^2304},
|
||||
103: {^2368, ^2432},
|
||||
104: {^2496, ^2560},
|
||||
}
|
||||
|
||||
// blackDecodeTable represents Tables 2 and 3 for a black run.
|
||||
//
|
||||
// +=XXXXX
|
||||
// b017 +-+
|
||||
// | | +=v1792
|
||||
// b042 | | +-+
|
||||
// | | | | +=v1984
|
||||
// b063 | | | +-+
|
||||
// | | | +=v2048
|
||||
// b029 | | +-+
|
||||
// | | | | +=v2112
|
||||
// b064 | | | | +-+
|
||||
// | | | | | +=v2176
|
||||
// b043 | | | +-+
|
||||
// | | | | +=v2240
|
||||
// b065 | | | +-+
|
||||
// | | | +=v2304
|
||||
// b022 | +-+
|
||||
// | | +=v1856
|
||||
// b044 | | +-+
|
||||
// | | | +=v1920
|
||||
// b030 | +-+
|
||||
// | | +=v2368
|
||||
// b066 | | +-+
|
||||
// | | | +=v2432
|
||||
// b045 | +-+
|
||||
// | | +=v2496
|
||||
// b067 | +-+
|
||||
// | +=v2560
|
||||
// b013 +-+
|
||||
// | | +=v0018
|
||||
// b031 | | +-+
|
||||
// | | | | +=v0052
|
||||
// b068 | | | | +-+
|
||||
// | | | | | | +=v0640
|
||||
// b095 | | | | | +-+
|
||||
// | | | | | +=v0704
|
||||
// b046 | | | +-+
|
||||
// | | | | +=v0768
|
||||
// b096 | | | | +-+
|
||||
// | | | | | +=v0832
|
||||
// b069 | | | +-+
|
||||
// | | | +=v0055
|
||||
// b023 | | +-+
|
||||
// | | | | +=v0056
|
||||
// b070 | | | | +-+
|
||||
// | | | | | | +=v1280
|
||||
// b097 | | | | | +-+
|
||||
// | | | | | +=v1344
|
||||
// b047 | | | | +-+
|
||||
// | | | | | | +=v1408
|
||||
// b098 | | | | | | +-+
|
||||
// | | | | | | | +=v1472
|
||||
// b071 | | | | | +-+
|
||||
// | | | | | +=v0059
|
||||
// b032 | | | +-+
|
||||
// | | | | +=v0060
|
||||
// b072 | | | | +-+
|
||||
// | | | | | | +=v1536
|
||||
// b099 | | | | | +-+
|
||||
// | | | | | +=v1600
|
||||
// b048 | | | +-+
|
||||
// | | | +=v0024
|
||||
// b018 | +-+
|
||||
// | | +=v0025
|
||||
// b049 | | +-+
|
||||
// | | | | +=v1664
|
||||
// b100 | | | | +-+
|
||||
// | | | | | +=v1728
|
||||
// b073 | | | +-+
|
||||
// | | | +=v0320
|
||||
// b033 | | +-+
|
||||
// | | | | +=v0384
|
||||
// b074 | | | | +-+
|
||||
// | | | | | +=v0448
|
||||
// b050 | | | +-+
|
||||
// | | | | +=v0512
|
||||
// b101 | | | | +-+
|
||||
// | | | | | +=v0576
|
||||
// b075 | | | +-+
|
||||
// | | | +=v0053
|
||||
// b024 | +-+
|
||||
// | | +=v0054
|
||||
// b076 | | +-+
|
||||
// | | | | +=v0896
|
||||
// b102 | | | +-+
|
||||
// | | | +=v0960
|
||||
// b051 | | +-+
|
||||
// | | | | +=v1024
|
||||
// b103 | | | | +-+
|
||||
// | | | | | +=v1088
|
||||
// b077 | | | +-+
|
||||
// | | | | +=v1152
|
||||
// b104 | | | +-+
|
||||
// | | | +=v1216
|
||||
// b034 | +-+
|
||||
// | +=v0064
|
||||
// b010 +-+
|
||||
// | | +=v0013
|
||||
// b019 | | +-+
|
||||
// | | | | +=v0023
|
||||
// b052 | | | | +-+
|
||||
// | | | | | | +=v0050
|
||||
// b078 | | | | | +-+
|
||||
// | | | | | +=v0051
|
||||
// b035 | | | | +-+
|
||||
// | | | | | | +=v0044
|
||||
// b079 | | | | | | +-+
|
||||
// | | | | | | | +=v0045
|
||||
// b053 | | | | | +-+
|
||||
// | | | | | | +=v0046
|
||||
// b080 | | | | | +-+
|
||||
// | | | | | +=v0047
|
||||
// b025 | | | +-+
|
||||
// | | | | +=v0057
|
||||
// b081 | | | | +-+
|
||||
// | | | | | +=v0058
|
||||
// b054 | | | | +-+
|
||||
// | | | | | | +=v0061
|
||||
// b082 | | | | | +-+
|
||||
// | | | | | +=v0256
|
||||
// b036 | | | +-+
|
||||
// | | | +=v0016
|
||||
// b014 | +-+
|
||||
// | | +=v0017
|
||||
// b037 | | +-+
|
||||
// | | | | +=v0048
|
||||
// b083 | | | | +-+
|
||||
// | | | | | +=v0049
|
||||
// b055 | | | +-+
|
||||
// | | | | +=v0062
|
||||
// b084 | | | +-+
|
||||
// | | | +=v0063
|
||||
// b026 | | +-+
|
||||
// | | | | +=v0030
|
||||
// b085 | | | | +-+
|
||||
// | | | | | +=v0031
|
||||
// b056 | | | | +-+
|
||||
// | | | | | | +=v0032
|
||||
// b086 | | | | | +-+
|
||||
// | | | | | +=v0033
|
||||
// b038 | | | +-+
|
||||
// | | | | +=v0040
|
||||
// b087 | | | | +-+
|
||||
// | | | | | +=v0041
|
||||
// b057 | | | +-+
|
||||
// | | | +=v0022
|
||||
// b020 | +-+
|
||||
// | +=v0014
|
||||
// b008 +-+
|
||||
// | | +=v0010
|
||||
// b015 | | +-+
|
||||
// | | | +=v0011
|
||||
// b011 | +-+
|
||||
// | | +=v0015
|
||||
// b027 | | +-+
|
||||
// | | | | +=v0128
|
||||
// b088 | | | | +-+
|
||||
// | | | | | +=v0192
|
||||
// b058 | | | | +-+
|
||||
// | | | | | | +=v0026
|
||||
// b089 | | | | | +-+
|
||||
// | | | | | +=v0027
|
||||
// b039 | | | +-+
|
||||
// | | | | +=v0028
|
||||
// b090 | | | | +-+
|
||||
// | | | | | +=v0029
|
||||
// b059 | | | +-+
|
||||
// | | | +=v0019
|
||||
// b021 | | +-+
|
||||
// | | | | +=v0020
|
||||
// b060 | | | | +-+
|
||||
// | | | | | | +=v0034
|
||||
// b091 | | | | | +-+
|
||||
// | | | | | +=v0035
|
||||
// b040 | | | | +-+
|
||||
// | | | | | | +=v0036
|
||||
// b092 | | | | | | +-+
|
||||
// | | | | | | | +=v0037
|
||||
// b061 | | | | | +-+
|
||||
// | | | | | | +=v0038
|
||||
// b093 | | | | | +-+
|
||||
// | | | | | +=v0039
|
||||
// b028 | | | +-+
|
||||
// | | | | +=v0021
|
||||
// b062 | | | | +-+
|
||||
// | | | | | | +=v0042
|
||||
// b094 | | | | | +-+
|
||||
// | | | | | +=v0043
|
||||
// b041 | | | +-+
|
||||
// | | | +=v0000
|
||||
// b016 | +-+
|
||||
// | +=v0012
|
||||
// b006 +-+
|
||||
// | | +=v0009
|
||||
// b012 | | +-+
|
||||
// | | | +=v0008
|
||||
// b009 | +-+
|
||||
// | +=v0007
|
||||
// b004 +-+
|
||||
// | | +=v0006
|
||||
// b007 | +-+
|
||||
// | +=v0005
|
||||
// b002 +-+
|
||||
// | | +=v0001
|
||||
// b005 | +-+
|
||||
// | +=v0004
|
||||
// b001 +-+
|
||||
// | +=v0003
|
||||
// b003 +-+
|
||||
// +=v0002
|
||||
var blackDecodeTable = [...][2]int16{
|
||||
0: {0, 0},
|
||||
1: {2, 3},
|
||||
2: {4, 5},
|
||||
3: {^3, ^2},
|
||||
4: {6, 7},
|
||||
5: {^1, ^4},
|
||||
6: {8, 9},
|
||||
7: {^6, ^5},
|
||||
8: {10, 11},
|
||||
9: {12, ^7},
|
||||
10: {13, 14},
|
||||
11: {15, 16},
|
||||
12: {^9, ^8},
|
||||
13: {17, 18},
|
||||
14: {19, 20},
|
||||
15: {^10, ^11},
|
||||
16: {21, ^12},
|
||||
17: {0, 22},
|
||||
18: {23, 24},
|
||||
19: {^13, 25},
|
||||
20: {26, ^14},
|
||||
21: {27, 28},
|
||||
22: {29, 30},
|
||||
23: {31, 32},
|
||||
24: {33, 34},
|
||||
25: {35, 36},
|
||||
26: {37, 38},
|
||||
27: {^15, 39},
|
||||
28: {40, 41},
|
||||
29: {42, 43},
|
||||
30: {44, 45},
|
||||
31: {^18, 46},
|
||||
32: {47, 48},
|
||||
33: {49, 50},
|
||||
34: {51, ^64},
|
||||
35: {52, 53},
|
||||
36: {54, ^16},
|
||||
37: {^17, 55},
|
||||
38: {56, 57},
|
||||
39: {58, 59},
|
||||
40: {60, 61},
|
||||
41: {62, ^0},
|
||||
42: {^1792, 63},
|
||||
43: {64, 65},
|
||||
44: {^1856, ^1920},
|
||||
45: {66, 67},
|
||||
46: {68, 69},
|
||||
47: {70, 71},
|
||||
48: {72, ^24},
|
||||
49: {^25, 73},
|
||||
50: {74, 75},
|
||||
51: {76, 77},
|
||||
52: {^23, 78},
|
||||
53: {79, 80},
|
||||
54: {81, 82},
|
||||
55: {83, 84},
|
||||
56: {85, 86},
|
||||
57: {87, ^22},
|
||||
58: {88, 89},
|
||||
59: {90, ^19},
|
||||
60: {^20, 91},
|
||||
61: {92, 93},
|
||||
62: {^21, 94},
|
||||
63: {^1984, ^2048},
|
||||
64: {^2112, ^2176},
|
||||
65: {^2240, ^2304},
|
||||
66: {^2368, ^2432},
|
||||
67: {^2496, ^2560},
|
||||
68: {^52, 95},
|
||||
69: {96, ^55},
|
||||
70: {^56, 97},
|
||||
71: {98, ^59},
|
||||
72: {^60, 99},
|
||||
73: {100, ^320},
|
||||
74: {^384, ^448},
|
||||
75: {101, ^53},
|
||||
76: {^54, 102},
|
||||
77: {103, 104},
|
||||
78: {^50, ^51},
|
||||
79: {^44, ^45},
|
||||
80: {^46, ^47},
|
||||
81: {^57, ^58},
|
||||
82: {^61, ^256},
|
||||
83: {^48, ^49},
|
||||
84: {^62, ^63},
|
||||
85: {^30, ^31},
|
||||
86: {^32, ^33},
|
||||
87: {^40, ^41},
|
||||
88: {^128, ^192},
|
||||
89: {^26, ^27},
|
||||
90: {^28, ^29},
|
||||
91: {^34, ^35},
|
||||
92: {^36, ^37},
|
||||
93: {^38, ^39},
|
||||
94: {^42, ^43},
|
||||
95: {^640, ^704},
|
||||
96: {^768, ^832},
|
||||
97: {^1280, ^1344},
|
||||
98: {^1408, ^1472},
|
||||
99: {^1536, ^1600},
|
||||
100: {^1664, ^1728},
|
||||
101: {^512, ^576},
|
||||
102: {^896, ^960},
|
||||
103: {^1024, ^1088},
|
||||
104: {^1152, ^1216},
|
||||
}
|
||||
|
||||
const maxCodeLength = 13
|
||||
|
||||
// Each encodeTable is represented by an array of bitStrings.
|
||||
|
||||
// bitString is a pair of uint32 values representing a bit code.
|
||||
// The nBits low bits of bits make up the actual bit code.
|
||||
// Eg. bitString{0x0004, 8} represents the bitcode "00000100".
|
||||
type bitString struct {
|
||||
bits uint32
|
||||
nBits uint32
|
||||
}
|
||||
|
||||
// modeEncodeTable represents Table 1 and the End-of-Line code.
|
||||
var modeEncodeTable = [...]bitString{
|
||||
0: {0x0001, 4}, // "0001"
|
||||
1: {0x0001, 3}, // "001"
|
||||
2: {0x0001, 1}, // "1"
|
||||
3: {0x0003, 3}, // "011"
|
||||
4: {0x0003, 6}, // "000011"
|
||||
5: {0x0003, 7}, // "0000011"
|
||||
6: {0x0002, 3}, // "010"
|
||||
7: {0x0002, 6}, // "000010"
|
||||
8: {0x0002, 7}, // "0000010"
|
||||
9: {0x0001, 7}, // "0000001"
|
||||
}
|
||||
|
||||
// whiteEncodeTable2 represents Table 2 for a white run.
|
||||
var whiteEncodeTable2 = [...]bitString{
|
||||
0: {0x0035, 8}, // "00110101"
|
||||
1: {0x0007, 6}, // "000111"
|
||||
2: {0x0007, 4}, // "0111"
|
||||
3: {0x0008, 4}, // "1000"
|
||||
4: {0x000b, 4}, // "1011"
|
||||
5: {0x000c, 4}, // "1100"
|
||||
6: {0x000e, 4}, // "1110"
|
||||
7: {0x000f, 4}, // "1111"
|
||||
8: {0x0013, 5}, // "10011"
|
||||
9: {0x0014, 5}, // "10100"
|
||||
10: {0x0007, 5}, // "00111"
|
||||
11: {0x0008, 5}, // "01000"
|
||||
12: {0x0008, 6}, // "001000"
|
||||
13: {0x0003, 6}, // "000011"
|
||||
14: {0x0034, 6}, // "110100"
|
||||
15: {0x0035, 6}, // "110101"
|
||||
16: {0x002a, 6}, // "101010"
|
||||
17: {0x002b, 6}, // "101011"
|
||||
18: {0x0027, 7}, // "0100111"
|
||||
19: {0x000c, 7}, // "0001100"
|
||||
20: {0x0008, 7}, // "0001000"
|
||||
21: {0x0017, 7}, // "0010111"
|
||||
22: {0x0003, 7}, // "0000011"
|
||||
23: {0x0004, 7}, // "0000100"
|
||||
24: {0x0028, 7}, // "0101000"
|
||||
25: {0x002b, 7}, // "0101011"
|
||||
26: {0x0013, 7}, // "0010011"
|
||||
27: {0x0024, 7}, // "0100100"
|
||||
28: {0x0018, 7}, // "0011000"
|
||||
29: {0x0002, 8}, // "00000010"
|
||||
30: {0x0003, 8}, // "00000011"
|
||||
31: {0x001a, 8}, // "00011010"
|
||||
32: {0x001b, 8}, // "00011011"
|
||||
33: {0x0012, 8}, // "00010010"
|
||||
34: {0x0013, 8}, // "00010011"
|
||||
35: {0x0014, 8}, // "00010100"
|
||||
36: {0x0015, 8}, // "00010101"
|
||||
37: {0x0016, 8}, // "00010110"
|
||||
38: {0x0017, 8}, // "00010111"
|
||||
39: {0x0028, 8}, // "00101000"
|
||||
40: {0x0029, 8}, // "00101001"
|
||||
41: {0x002a, 8}, // "00101010"
|
||||
42: {0x002b, 8}, // "00101011"
|
||||
43: {0x002c, 8}, // "00101100"
|
||||
44: {0x002d, 8}, // "00101101"
|
||||
45: {0x0004, 8}, // "00000100"
|
||||
46: {0x0005, 8}, // "00000101"
|
||||
47: {0x000a, 8}, // "00001010"
|
||||
48: {0x000b, 8}, // "00001011"
|
||||
49: {0x0052, 8}, // "01010010"
|
||||
50: {0x0053, 8}, // "01010011"
|
||||
51: {0x0054, 8}, // "01010100"
|
||||
52: {0x0055, 8}, // "01010101"
|
||||
53: {0x0024, 8}, // "00100100"
|
||||
54: {0x0025, 8}, // "00100101"
|
||||
55: {0x0058, 8}, // "01011000"
|
||||
56: {0x0059, 8}, // "01011001"
|
||||
57: {0x005a, 8}, // "01011010"
|
||||
58: {0x005b, 8}, // "01011011"
|
||||
59: {0x004a, 8}, // "01001010"
|
||||
60: {0x004b, 8}, // "01001011"
|
||||
61: {0x0032, 8}, // "00110010"
|
||||
62: {0x0033, 8}, // "00110011"
|
||||
63: {0x0034, 8}, // "00110100"
|
||||
}
|
||||
|
||||
// whiteEncodeTable3 represents Table 3 for a white run.
|
||||
var whiteEncodeTable3 = [...]bitString{
|
||||
0: {0x001b, 5}, // "11011"
|
||||
1: {0x0012, 5}, // "10010"
|
||||
2: {0x0017, 6}, // "010111"
|
||||
3: {0x0037, 7}, // "0110111"
|
||||
4: {0x0036, 8}, // "00110110"
|
||||
5: {0x0037, 8}, // "00110111"
|
||||
6: {0x0064, 8}, // "01100100"
|
||||
7: {0x0065, 8}, // "01100101"
|
||||
8: {0x0068, 8}, // "01101000"
|
||||
9: {0x0067, 8}, // "01100111"
|
||||
10: {0x00cc, 9}, // "011001100"
|
||||
11: {0x00cd, 9}, // "011001101"
|
||||
12: {0x00d2, 9}, // "011010010"
|
||||
13: {0x00d3, 9}, // "011010011"
|
||||
14: {0x00d4, 9}, // "011010100"
|
||||
15: {0x00d5, 9}, // "011010101"
|
||||
16: {0x00d6, 9}, // "011010110"
|
||||
17: {0x00d7, 9}, // "011010111"
|
||||
18: {0x00d8, 9}, // "011011000"
|
||||
19: {0x00d9, 9}, // "011011001"
|
||||
20: {0x00da, 9}, // "011011010"
|
||||
21: {0x00db, 9}, // "011011011"
|
||||
22: {0x0098, 9}, // "010011000"
|
||||
23: {0x0099, 9}, // "010011001"
|
||||
24: {0x009a, 9}, // "010011010"
|
||||
25: {0x0018, 6}, // "011000"
|
||||
26: {0x009b, 9}, // "010011011"
|
||||
27: {0x0008, 11}, // "00000001000"
|
||||
28: {0x000c, 11}, // "00000001100"
|
||||
29: {0x000d, 11}, // "00000001101"
|
||||
30: {0x0012, 12}, // "000000010010"
|
||||
31: {0x0013, 12}, // "000000010011"
|
||||
32: {0x0014, 12}, // "000000010100"
|
||||
33: {0x0015, 12}, // "000000010101"
|
||||
34: {0x0016, 12}, // "000000010110"
|
||||
35: {0x0017, 12}, // "000000010111"
|
||||
36: {0x001c, 12}, // "000000011100"
|
||||
37: {0x001d, 12}, // "000000011101"
|
||||
38: {0x001e, 12}, // "000000011110"
|
||||
39: {0x001f, 12}, // "000000011111"
|
||||
}
|
||||
|
||||
// blackEncodeTable2 represents Table 2 for a black run.
|
||||
var blackEncodeTable2 = [...]bitString{
|
||||
0: {0x0037, 10}, // "0000110111"
|
||||
1: {0x0002, 3}, // "010"
|
||||
2: {0x0003, 2}, // "11"
|
||||
3: {0x0002, 2}, // "10"
|
||||
4: {0x0003, 3}, // "011"
|
||||
5: {0x0003, 4}, // "0011"
|
||||
6: {0x0002, 4}, // "0010"
|
||||
7: {0x0003, 5}, // "00011"
|
||||
8: {0x0005, 6}, // "000101"
|
||||
9: {0x0004, 6}, // "000100"
|
||||
10: {0x0004, 7}, // "0000100"
|
||||
11: {0x0005, 7}, // "0000101"
|
||||
12: {0x0007, 7}, // "0000111"
|
||||
13: {0x0004, 8}, // "00000100"
|
||||
14: {0x0007, 8}, // "00000111"
|
||||
15: {0x0018, 9}, // "000011000"
|
||||
16: {0x0017, 10}, // "0000010111"
|
||||
17: {0x0018, 10}, // "0000011000"
|
||||
18: {0x0008, 10}, // "0000001000"
|
||||
19: {0x0067, 11}, // "00001100111"
|
||||
20: {0x0068, 11}, // "00001101000"
|
||||
21: {0x006c, 11}, // "00001101100"
|
||||
22: {0x0037, 11}, // "00000110111"
|
||||
23: {0x0028, 11}, // "00000101000"
|
||||
24: {0x0017, 11}, // "00000010111"
|
||||
25: {0x0018, 11}, // "00000011000"
|
||||
26: {0x00ca, 12}, // "000011001010"
|
||||
27: {0x00cb, 12}, // "000011001011"
|
||||
28: {0x00cc, 12}, // "000011001100"
|
||||
29: {0x00cd, 12}, // "000011001101"
|
||||
30: {0x0068, 12}, // "000001101000"
|
||||
31: {0x0069, 12}, // "000001101001"
|
||||
32: {0x006a, 12}, // "000001101010"
|
||||
33: {0x006b, 12}, // "000001101011"
|
||||
34: {0x00d2, 12}, // "000011010010"
|
||||
35: {0x00d3, 12}, // "000011010011"
|
||||
36: {0x00d4, 12}, // "000011010100"
|
||||
37: {0x00d5, 12}, // "000011010101"
|
||||
38: {0x00d6, 12}, // "000011010110"
|
||||
39: {0x00d7, 12}, // "000011010111"
|
||||
40: {0x006c, 12}, // "000001101100"
|
||||
41: {0x006d, 12}, // "000001101101"
|
||||
42: {0x00da, 12}, // "000011011010"
|
||||
43: {0x00db, 12}, // "000011011011"
|
||||
44: {0x0054, 12}, // "000001010100"
|
||||
45: {0x0055, 12}, // "000001010101"
|
||||
46: {0x0056, 12}, // "000001010110"
|
||||
47: {0x0057, 12}, // "000001010111"
|
||||
48: {0x0064, 12}, // "000001100100"
|
||||
49: {0x0065, 12}, // "000001100101"
|
||||
50: {0x0052, 12}, // "000001010010"
|
||||
51: {0x0053, 12}, // "000001010011"
|
||||
52: {0x0024, 12}, // "000000100100"
|
||||
53: {0x0037, 12}, // "000000110111"
|
||||
54: {0x0038, 12}, // "000000111000"
|
||||
55: {0x0027, 12}, // "000000100111"
|
||||
56: {0x0028, 12}, // "000000101000"
|
||||
57: {0x0058, 12}, // "000001011000"
|
||||
58: {0x0059, 12}, // "000001011001"
|
||||
59: {0x002b, 12}, // "000000101011"
|
||||
60: {0x002c, 12}, // "000000101100"
|
||||
61: {0x005a, 12}, // "000001011010"
|
||||
62: {0x0066, 12}, // "000001100110"
|
||||
63: {0x0067, 12}, // "000001100111"
|
||||
}
|
||||
|
||||
// blackEncodeTable3 represents Table 3 for a black run.
|
||||
var blackEncodeTable3 = [...]bitString{
|
||||
0: {0x000f, 10}, // "0000001111"
|
||||
1: {0x00c8, 12}, // "000011001000"
|
||||
2: {0x00c9, 12}, // "000011001001"
|
||||
3: {0x005b, 12}, // "000001011011"
|
||||
4: {0x0033, 12}, // "000000110011"
|
||||
5: {0x0034, 12}, // "000000110100"
|
||||
6: {0x0035, 12}, // "000000110101"
|
||||
7: {0x006c, 13}, // "0000001101100"
|
||||
8: {0x006d, 13}, // "0000001101101"
|
||||
9: {0x004a, 13}, // "0000001001010"
|
||||
10: {0x004b, 13}, // "0000001001011"
|
||||
11: {0x004c, 13}, // "0000001001100"
|
||||
12: {0x004d, 13}, // "0000001001101"
|
||||
13: {0x0072, 13}, // "0000001110010"
|
||||
14: {0x0073, 13}, // "0000001110011"
|
||||
15: {0x0074, 13}, // "0000001110100"
|
||||
16: {0x0075, 13}, // "0000001110101"
|
||||
17: {0x0076, 13}, // "0000001110110"
|
||||
18: {0x0077, 13}, // "0000001110111"
|
||||
19: {0x0052, 13}, // "0000001010010"
|
||||
20: {0x0053, 13}, // "0000001010011"
|
||||
21: {0x0054, 13}, // "0000001010100"
|
||||
22: {0x0055, 13}, // "0000001010101"
|
||||
23: {0x005a, 13}, // "0000001011010"
|
||||
24: {0x005b, 13}, // "0000001011011"
|
||||
25: {0x0064, 13}, // "0000001100100"
|
||||
26: {0x0065, 13}, // "0000001100101"
|
||||
27: {0x0008, 11}, // "00000001000"
|
||||
28: {0x000c, 11}, // "00000001100"
|
||||
29: {0x000d, 11}, // "00000001101"
|
||||
30: {0x0012, 12}, // "000000010010"
|
||||
31: {0x0013, 12}, // "000000010011"
|
||||
32: {0x0014, 12}, // "000000010100"
|
||||
33: {0x0015, 12}, // "000000010101"
|
||||
34: {0x0016, 12}, // "000000010110"
|
||||
35: {0x0017, 12}, // "000000010111"
|
||||
36: {0x001c, 12}, // "000000011100"
|
||||
37: {0x001d, 12}, // "000000011101"
|
||||
38: {0x001e, 12}, // "000000011110"
|
||||
39: {0x001f, 12}, // "000000011111"
|
||||
}
|
||||
|
||||
// COPY PASTE table.go BEGIN
|
||||
|
||||
const (
|
||||
modePass = iota // Pass
|
||||
modeH // Horizontal
|
||||
modeV0 // Vertical-0
|
||||
modeVR1 // Vertical-Right-1
|
||||
modeVR2 // Vertical-Right-2
|
||||
modeVR3 // Vertical-Right-3
|
||||
modeVL1 // Vertical-Left-1
|
||||
modeVL2 // Vertical-Left-2
|
||||
modeVL3 // Vertical-Left-3
|
||||
modeExt // Extension
|
||||
)
|
||||
|
||||
// COPY PASTE table.go END
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ccitt
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
type bitWriter struct {
|
||||
w io.Writer
|
||||
|
||||
// order is whether to process w's bytes LSB first or MSB first.
|
||||
order Order
|
||||
|
||||
// The high nBits bits of the bits field hold encoded bits to be written to w.
|
||||
bits uint64
|
||||
nBits uint32
|
||||
|
||||
// bytes[:bw] holds encoded bytes not yet written to w.
|
||||
// Overflow protection is ensured by using a multiple of 8 as bytes length.
|
||||
bw uint32
|
||||
bytes [1024]uint8
|
||||
}
|
||||
|
||||
// flushBits copies 64 bits from b.bits to b.bytes. If b.bytes is then full, it
|
||||
// is written to b.w.
|
||||
func (b *bitWriter) flushBits() error {
|
||||
binary.BigEndian.PutUint64(b.bytes[b.bw:], b.bits)
|
||||
b.bits = 0
|
||||
b.nBits = 0
|
||||
b.bw += 8
|
||||
if b.bw < uint32(len(b.bytes)) {
|
||||
return nil
|
||||
}
|
||||
b.bw = 0
|
||||
if b.order != MSB {
|
||||
reverseBitsWithinBytes(b.bytes[:])
|
||||
}
|
||||
_, err := b.w.Write(b.bytes[:])
|
||||
return err
|
||||
}
|
||||
|
||||
// close finalizes a bitcode stream by writing any
|
||||
// pending bits to bitWriter's underlying io.Writer.
|
||||
func (b *bitWriter) close() error {
|
||||
// Write any encoded bits to bytes.
|
||||
if b.nBits > 0 {
|
||||
binary.BigEndian.PutUint64(b.bytes[b.bw:], b.bits)
|
||||
b.bw += (b.nBits + 7) >> 3
|
||||
}
|
||||
|
||||
if b.order != MSB {
|
||||
reverseBitsWithinBytes(b.bytes[:b.bw])
|
||||
}
|
||||
|
||||
// Write b.bw bytes to b.w.
|
||||
_, err := b.w.Write(b.bytes[:b.bw])
|
||||
return err
|
||||
}
|
||||
|
||||
// alignToByteBoundary rounds b.nBits up to a multiple of 8.
|
||||
// If all 64 bits are used, flush them to bitWriter's bytes.
|
||||
func (b *bitWriter) alignToByteBoundary() error {
|
||||
if b.nBits = (b.nBits + 7) &^ 7; b.nBits == 64 {
|
||||
return b.flushBits()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeCode writes a variable length bitcode to b's underlying io.Writer.
|
||||
func (b *bitWriter) writeCode(bs bitString) error {
|
||||
bits := bs.bits
|
||||
nBits := bs.nBits
|
||||
if 64-b.nBits >= nBits {
|
||||
// b.bits has sufficient room for storing nBits bits.
|
||||
b.bits |= uint64(bits) << (64 - nBits - b.nBits)
|
||||
b.nBits += nBits
|
||||
if b.nBits == 64 {
|
||||
return b.flushBits()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Number of leading bits that fill b.bits.
|
||||
i := 64 - b.nBits
|
||||
|
||||
// Fill b.bits then flush and write remaining bits.
|
||||
b.bits |= uint64(bits) >> (nBits - i)
|
||||
b.nBits = 64
|
||||
|
||||
if err := b.flushBits(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nBits -= i
|
||||
b.bits = uint64(bits) << (64 - nBits)
|
||||
b.nBits = nBits
|
||||
return nil
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:generate go run gen.go
|
||||
|
||||
// Package basicfont provides fixed-size font faces.
|
||||
package basicfont // import "golang.org/x/image/font/basicfont"
|
||||
|
||||
import (
|
||||
"image"
|
||||
|
||||
"golang.org/x/image/font"
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
// Range maps a contiguous range of runes to vertically adjacent sub-images of
|
||||
// a Face's Mask image. The rune range is inclusive on the low end and
|
||||
// exclusive on the high end.
|
||||
//
|
||||
// If Low <= r && r < High, then the rune r is mapped to the sub-image of
|
||||
// Face.Mask whose bounds are image.Rect(0, y*h, Face.Width, (y+1)*h),
|
||||
// where y = (int(r-Low) + Offset) and h = (Face.Ascent + Face.Descent).
|
||||
type Range struct {
|
||||
Low, High rune
|
||||
Offset int
|
||||
}
|
||||
|
||||
// Face7x13 is a Face derived from the public domain X11 misc-fixed font files.
|
||||
//
|
||||
// At the moment, it holds the printable characters in ASCII starting with
|
||||
// space, and the Unicode replacement character U+FFFD.
|
||||
//
|
||||
// Its data is entirely self-contained and does not require loading from
|
||||
// separate files.
|
||||
var Face7x13 = &Face{
|
||||
Advance: 7,
|
||||
Width: 6,
|
||||
Height: 13,
|
||||
Ascent: 11,
|
||||
Descent: 2,
|
||||
Mask: mask7x13,
|
||||
Ranges: []Range{
|
||||
{'\u0020', '\u007f', 0},
|
||||
{'\ufffd', '\ufffe', 95},
|
||||
},
|
||||
}
|
||||
|
||||
// Face is a basic font face whose glyphs all have the same metrics.
|
||||
//
|
||||
// It is safe to use concurrently.
|
||||
type Face struct {
|
||||
// Advance is the glyph advance, in pixels.
|
||||
Advance int
|
||||
// Width is the glyph width, in pixels.
|
||||
Width int
|
||||
// Height is the inter-line height, in pixels.
|
||||
Height int
|
||||
// Ascent is the glyph ascent, in pixels.
|
||||
Ascent int
|
||||
// Descent is the glyph descent, in pixels.
|
||||
Descent int
|
||||
// Left is the left side bearing, in pixels. A positive value means that
|
||||
// all of a glyph is to the right of the dot.
|
||||
Left int
|
||||
|
||||
// Mask contains all of the glyph masks. Its width is typically the Face's
|
||||
// Width, and its height a multiple of the Face's Height.
|
||||
Mask image.Image
|
||||
// Ranges map runes to sub-images of Mask. The rune ranges must not
|
||||
// overlap, and must be in increasing rune order.
|
||||
Ranges []Range
|
||||
}
|
||||
|
||||
func (f *Face) Close() error { return nil }
|
||||
func (f *Face) Kern(r0, r1 rune) fixed.Int26_6 { return 0 }
|
||||
|
||||
func (f *Face) Metrics() font.Metrics {
|
||||
return font.Metrics{
|
||||
Height: fixed.I(f.Height),
|
||||
Ascent: fixed.I(f.Ascent),
|
||||
Descent: fixed.I(f.Descent),
|
||||
XHeight: fixed.I(f.Ascent),
|
||||
CapHeight: fixed.I(f.Ascent),
|
||||
CaretSlope: image.Point{X: 0, Y: 1},
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Face) Glyph(dot fixed.Point26_6, r rune) (
|
||||
dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) {
|
||||
|
||||
if found, rng := f.find(r); rng != nil {
|
||||
maskp.Y = (int(found-rng.Low) + rng.Offset) * (f.Ascent + f.Descent)
|
||||
x := int(dot.X+32)>>6 + f.Left
|
||||
y := int(dot.Y+32) >> 6
|
||||
dr = image.Rectangle{
|
||||
Min: image.Point{
|
||||
X: x,
|
||||
Y: y - f.Ascent,
|
||||
},
|
||||
Max: image.Point{
|
||||
X: x + f.Width,
|
||||
Y: y + f.Descent,
|
||||
},
|
||||
}
|
||||
|
||||
return dr, f.Mask, maskp, fixed.I(f.Advance), r == found
|
||||
}
|
||||
return image.Rectangle{}, nil, image.Point{}, 0, false
|
||||
}
|
||||
|
||||
func (f *Face) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
|
||||
if found, rng := f.find(r); rng != nil {
|
||||
return fixed.R(0, -f.Ascent, f.Width, +f.Descent), fixed.I(f.Advance), r == found
|
||||
}
|
||||
return fixed.Rectangle26_6{}, 0, false
|
||||
}
|
||||
|
||||
func (f *Face) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
|
||||
if found, rng := f.find(r); rng != nil {
|
||||
return fixed.I(f.Advance), r == found
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func (f *Face) find(r rune) (rune, *Range) {
|
||||
for {
|
||||
for i, rng := range f.Ranges {
|
||||
if (rng.Low <= r) && (r < rng.High) {
|
||||
return r, &f.Ranges[i]
|
||||
}
|
||||
}
|
||||
if r == '\ufffd' {
|
||||
return 0, nil
|
||||
}
|
||||
r = '\ufffd'
|
||||
}
|
||||
}
|
||||
+1456
File diff suppressed because it is too large
Load Diff
+354
@@ -0,0 +1,354 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package font defines an interface for font faces, for drawing text on an
|
||||
// image.
|
||||
//
|
||||
// Other packages provide font face implementations. For example, a truetype
|
||||
// package would provide one based on .ttf font files.
|
||||
package font // import "golang.org/x/image/font"
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/draw"
|
||||
"io"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
// TODO: who is responsible for caches (glyph images, glyph indices, kerns)?
|
||||
// The Drawer or the Face?
|
||||
|
||||
// Face is a font face. Its glyphs are often derived from a font file, such as
|
||||
// "Comic_Sans_MS.ttf", but a face has a specific size, style, weight and
|
||||
// hinting. For example, the 12pt and 18pt versions of Comic Sans are two
|
||||
// different faces, even if derived from the same font file.
|
||||
//
|
||||
// A Face is not safe for concurrent use by multiple goroutines, as its methods
|
||||
// may re-use implementation-specific caches and mask image buffers.
|
||||
//
|
||||
// To create a Face, look to other packages that implement specific font file
|
||||
// formats.
|
||||
type Face interface {
|
||||
io.Closer
|
||||
|
||||
// Glyph returns the draw.DrawMask parameters (dr, mask, maskp) to draw r's
|
||||
// glyph at the sub-pixel destination location dot, and that glyph's
|
||||
// advance width.
|
||||
//
|
||||
// It returns !ok if the face does not contain a glyph for r. This includes
|
||||
// returning !ok for a fallback glyph (such as substituting a U+FFFD glyph
|
||||
// or OpenType's .notdef glyph), in which case the other return values may
|
||||
// still be non-zero.
|
||||
//
|
||||
// The contents of the mask image returned by one Glyph call may change
|
||||
// after the next Glyph call. Callers that want to cache the mask must make
|
||||
// a copy.
|
||||
Glyph(dot fixed.Point26_6, r rune) (
|
||||
dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool)
|
||||
|
||||
// GlyphBounds returns the bounding box of r's glyph, drawn at a dot equal
|
||||
// to the origin, and that glyph's advance width.
|
||||
//
|
||||
// It returns !ok if the face does not contain a glyph for r. This includes
|
||||
// returning !ok for a fallback glyph (such as substituting a U+FFFD glyph
|
||||
// or OpenType's .notdef glyph), in which case the other return values may
|
||||
// still be non-zero.
|
||||
//
|
||||
// The glyph's ascent and descent are equal to -bounds.Min.Y and
|
||||
// +bounds.Max.Y. The glyph's left-side and right-side bearings are equal
|
||||
// to bounds.Min.X and advance-bounds.Max.X. A visual depiction of what
|
||||
// these metrics are is at
|
||||
// https://developer.apple.com/library/archive/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyphterms_2x.png
|
||||
GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool)
|
||||
|
||||
// GlyphAdvance returns the advance width of r's glyph.
|
||||
//
|
||||
// It returns !ok if the face does not contain a glyph for r. This includes
|
||||
// returning !ok for a fallback glyph (such as substituting a U+FFFD glyph
|
||||
// or OpenType's .notdef glyph), in which case the other return values may
|
||||
// still be non-zero.
|
||||
GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool)
|
||||
|
||||
// Kern returns the horizontal adjustment for the kerning pair (r0, r1). A
|
||||
// positive kern means to move the glyphs further apart.
|
||||
Kern(r0, r1 rune) fixed.Int26_6
|
||||
|
||||
// Metrics returns the metrics for this Face.
|
||||
Metrics() Metrics
|
||||
|
||||
// TODO: ColoredGlyph for various emoji?
|
||||
// TODO: Ligatures? Shaping?
|
||||
}
|
||||
|
||||
// Metrics holds the metrics for a Face. A visual depiction is at
|
||||
// https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyph_metrics_2x.png
|
||||
type Metrics struct {
|
||||
// Height is the recommended amount of vertical space between two lines of
|
||||
// text.
|
||||
Height fixed.Int26_6
|
||||
|
||||
// Ascent is the distance from the top of a line to its baseline.
|
||||
Ascent fixed.Int26_6
|
||||
|
||||
// Descent is the distance from the bottom of a line to its baseline. The
|
||||
// value is typically positive, even though a descender goes below the
|
||||
// baseline.
|
||||
Descent fixed.Int26_6
|
||||
|
||||
// XHeight is the distance from the top of non-ascending lowercase letters
|
||||
// to the baseline.
|
||||
XHeight fixed.Int26_6
|
||||
|
||||
// CapHeight is the distance from the top of uppercase letters to the
|
||||
// baseline.
|
||||
CapHeight fixed.Int26_6
|
||||
|
||||
// CaretSlope is the slope of a caret as a vector with the Y axis pointing up.
|
||||
// The slope {0, 1} is the vertical caret.
|
||||
CaretSlope image.Point
|
||||
}
|
||||
|
||||
// Drawer draws text on a destination image.
|
||||
//
|
||||
// A Drawer is not safe for concurrent use by multiple goroutines, since its
|
||||
// Face is not.
|
||||
type Drawer struct {
|
||||
// Dst is the destination image.
|
||||
Dst draw.Image
|
||||
// Src is the source image.
|
||||
Src image.Image
|
||||
// Face provides the glyph mask images.
|
||||
Face Face
|
||||
// Dot is the baseline location to draw the next glyph. The majority of the
|
||||
// affected pixels will be above and to the right of the dot, but some may
|
||||
// be below or to the left. For example, drawing a 'j' in an italic face
|
||||
// may affect pixels below and to the left of the dot.
|
||||
Dot fixed.Point26_6
|
||||
|
||||
// TODO: Clip image.Image?
|
||||
// TODO: SrcP image.Point for Src images other than *image.Uniform? How
|
||||
// does it get updated during DrawString?
|
||||
}
|
||||
|
||||
// TODO: should DrawString return the last rune drawn, so the next DrawString
|
||||
// call can kern beforehand? Or should that be the responsibility of the caller
|
||||
// if they really want to do that, since they have to explicitly shift d.Dot
|
||||
// anyway? What if ligatures span more than two runes? What if grapheme
|
||||
// clusters span multiple runes?
|
||||
//
|
||||
// TODO: do we assume that the input is in any particular Unicode Normalization
|
||||
// Form?
|
||||
//
|
||||
// TODO: have DrawRunes(s []rune)? DrawRuneReader(io.RuneReader)?? If we take
|
||||
// io.RuneReader, we can't assume that we can rewind the stream.
|
||||
//
|
||||
// TODO: how does this work with line breaking: drawing text up until a
|
||||
// vertical line? Should DrawString return the number of runes drawn?
|
||||
|
||||
// DrawBytes draws s at the dot and advances the dot's location.
|
||||
//
|
||||
// It is equivalent to DrawString(string(s)) but may be more efficient.
|
||||
func (d *Drawer) DrawBytes(s []byte) {
|
||||
prevC := rune(-1)
|
||||
for len(s) > 0 {
|
||||
c, size := utf8.DecodeRune(s)
|
||||
s = s[size:]
|
||||
if prevC >= 0 {
|
||||
d.Dot.X += d.Face.Kern(prevC, c)
|
||||
}
|
||||
dr, mask, maskp, advance, _ := d.Face.Glyph(d.Dot, c)
|
||||
if !dr.Empty() {
|
||||
draw.DrawMask(d.Dst, dr, d.Src, image.Point{}, mask, maskp, draw.Over)
|
||||
}
|
||||
d.Dot.X += advance
|
||||
prevC = c
|
||||
}
|
||||
}
|
||||
|
||||
// DrawString draws s at the dot and advances the dot's location.
|
||||
func (d *Drawer) DrawString(s string) {
|
||||
prevC := rune(-1)
|
||||
for _, c := range s {
|
||||
if prevC >= 0 {
|
||||
d.Dot.X += d.Face.Kern(prevC, c)
|
||||
}
|
||||
dr, mask, maskp, advance, _ := d.Face.Glyph(d.Dot, c)
|
||||
if !dr.Empty() {
|
||||
draw.DrawMask(d.Dst, dr, d.Src, image.Point{}, mask, maskp, draw.Over)
|
||||
}
|
||||
d.Dot.X += advance
|
||||
prevC = c
|
||||
}
|
||||
}
|
||||
|
||||
// BoundBytes returns the bounding box of s, drawn at the drawer dot, as well as
|
||||
// the advance.
|
||||
//
|
||||
// It is equivalent to BoundBytes(string(s)) but may be more efficient.
|
||||
func (d *Drawer) BoundBytes(s []byte) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) {
|
||||
bounds, advance = BoundBytes(d.Face, s)
|
||||
bounds.Min = bounds.Min.Add(d.Dot)
|
||||
bounds.Max = bounds.Max.Add(d.Dot)
|
||||
return
|
||||
}
|
||||
|
||||
// BoundString returns the bounding box of s, drawn at the drawer dot, as well
|
||||
// as the advance.
|
||||
func (d *Drawer) BoundString(s string) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) {
|
||||
bounds, advance = BoundString(d.Face, s)
|
||||
bounds.Min = bounds.Min.Add(d.Dot)
|
||||
bounds.Max = bounds.Max.Add(d.Dot)
|
||||
return
|
||||
}
|
||||
|
||||
// MeasureBytes returns how far dot would advance by drawing s.
|
||||
//
|
||||
// It is equivalent to MeasureString(string(s)) but may be more efficient.
|
||||
func (d *Drawer) MeasureBytes(s []byte) (advance fixed.Int26_6) {
|
||||
return MeasureBytes(d.Face, s)
|
||||
}
|
||||
|
||||
// MeasureString returns how far dot would advance by drawing s.
|
||||
func (d *Drawer) MeasureString(s string) (advance fixed.Int26_6) {
|
||||
return MeasureString(d.Face, s)
|
||||
}
|
||||
|
||||
// BoundBytes returns the bounding box of s with f, drawn at a dot equal to the
|
||||
// origin, as well as the advance.
|
||||
//
|
||||
// It is equivalent to BoundString(string(s)) but may be more efficient.
|
||||
func BoundBytes(f Face, s []byte) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) {
|
||||
prevC := rune(-1)
|
||||
for len(s) > 0 {
|
||||
c, size := utf8.DecodeRune(s)
|
||||
s = s[size:]
|
||||
if prevC >= 0 {
|
||||
advance += f.Kern(prevC, c)
|
||||
}
|
||||
b, a, _ := f.GlyphBounds(c)
|
||||
if !b.Empty() {
|
||||
b.Min.X += advance
|
||||
b.Max.X += advance
|
||||
bounds = bounds.Union(b)
|
||||
}
|
||||
advance += a
|
||||
prevC = c
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// BoundString returns the bounding box of s with f, drawn at a dot equal to the
|
||||
// origin, as well as the advance.
|
||||
func BoundString(f Face, s string) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) {
|
||||
prevC := rune(-1)
|
||||
for _, c := range s {
|
||||
if prevC >= 0 {
|
||||
advance += f.Kern(prevC, c)
|
||||
}
|
||||
b, a, _ := f.GlyphBounds(c)
|
||||
if !b.Empty() {
|
||||
b.Min.X += advance
|
||||
b.Max.X += advance
|
||||
bounds = bounds.Union(b)
|
||||
}
|
||||
advance += a
|
||||
prevC = c
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MeasureBytes returns how far dot would advance by drawing s with f.
|
||||
//
|
||||
// It is equivalent to MeasureString(string(s)) but may be more efficient.
|
||||
func MeasureBytes(f Face, s []byte) (advance fixed.Int26_6) {
|
||||
prevC := rune(-1)
|
||||
for len(s) > 0 {
|
||||
c, size := utf8.DecodeRune(s)
|
||||
s = s[size:]
|
||||
if prevC >= 0 {
|
||||
advance += f.Kern(prevC, c)
|
||||
}
|
||||
a, _ := f.GlyphAdvance(c)
|
||||
advance += a
|
||||
prevC = c
|
||||
}
|
||||
return advance
|
||||
}
|
||||
|
||||
// MeasureString returns how far dot would advance by drawing s with f.
|
||||
func MeasureString(f Face, s string) (advance fixed.Int26_6) {
|
||||
prevC := rune(-1)
|
||||
for _, c := range s {
|
||||
if prevC >= 0 {
|
||||
advance += f.Kern(prevC, c)
|
||||
}
|
||||
a, _ := f.GlyphAdvance(c)
|
||||
advance += a
|
||||
prevC = c
|
||||
}
|
||||
return advance
|
||||
}
|
||||
|
||||
// Hinting selects how to quantize a vector font's glyph nodes.
|
||||
//
|
||||
// Not all fonts support hinting.
|
||||
type Hinting int
|
||||
|
||||
const (
|
||||
HintingNone Hinting = iota
|
||||
HintingVertical
|
||||
HintingFull
|
||||
)
|
||||
|
||||
// Stretch selects a normal, condensed, or expanded face.
|
||||
//
|
||||
// Not all fonts support stretches.
|
||||
type Stretch int
|
||||
|
||||
const (
|
||||
StretchUltraCondensed Stretch = -4
|
||||
StretchExtraCondensed Stretch = -3
|
||||
StretchCondensed Stretch = -2
|
||||
StretchSemiCondensed Stretch = -1
|
||||
StretchNormal Stretch = +0
|
||||
StretchSemiExpanded Stretch = +1
|
||||
StretchExpanded Stretch = +2
|
||||
StretchExtraExpanded Stretch = +3
|
||||
StretchUltraExpanded Stretch = +4
|
||||
)
|
||||
|
||||
// Style selects a normal, italic, or oblique face.
|
||||
//
|
||||
// Not all fonts support styles.
|
||||
type Style int
|
||||
|
||||
const (
|
||||
StyleNormal Style = iota
|
||||
StyleItalic
|
||||
StyleOblique
|
||||
)
|
||||
|
||||
// Weight selects a normal, light or bold face.
|
||||
//
|
||||
// Not all fonts support weights.
|
||||
//
|
||||
// The named Weight constants (e.g. WeightBold) correspond to CSS' common
|
||||
// weight names (e.g. "Bold"), but the numerical values differ, so that in Go,
|
||||
// the zero value means to use a normal weight. For the CSS names and values,
|
||||
// see https://developer.mozilla.org/en/docs/Web/CSS/font-weight
|
||||
type Weight int
|
||||
|
||||
const (
|
||||
WeightThin Weight = -3 // CSS font-weight value 100.
|
||||
WeightExtraLight Weight = -2 // CSS font-weight value 200.
|
||||
WeightLight Weight = -1 // CSS font-weight value 300.
|
||||
WeightNormal Weight = +0 // CSS font-weight value 400.
|
||||
WeightMedium Weight = +1 // CSS font-weight value 500.
|
||||
WeightSemiBold Weight = +2 // CSS font-weight value 600.
|
||||
WeightBold Weight = +3 // CSS font-weight value 700.
|
||||
WeightExtraBold Weight = +4 // CSS font-weight value 800.
|
||||
WeightBlack Weight = +5 // CSS font-weight value 900.
|
||||
)
|
||||
+9496
File diff suppressed because it is too large
Load Diff
+9979
File diff suppressed because it is too large
Load Diff
+9834
File diff suppressed because it is too large
Load Diff
+9835
File diff suppressed because it is too large
Load Diff
+10309
File diff suppressed because it is too large
Load Diff
+10839
File diff suppressed because it is too large
Load Diff
+11196
File diff suppressed because it is too large
Load Diff
+11772
File diff suppressed because it is too large
Load Diff
+11450
File diff suppressed because it is too large
Load Diff
+9303
File diff suppressed because it is too large
Load Diff
+8828
File diff suppressed because it is too large
Load Diff
+9287
File diff suppressed because it is too large
Load Diff
+37
@@ -0,0 +1,37 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package f32 implements float32 vector and matrix types.
|
||||
package f32 // import "golang.org/x/image/math/f32"
|
||||
|
||||
// Vec2 is a 2-element vector.
|
||||
type Vec2 [2]float32
|
||||
|
||||
// Vec3 is a 3-element vector.
|
||||
type Vec3 [3]float32
|
||||
|
||||
// Vec4 is a 4-element vector.
|
||||
type Vec4 [4]float32
|
||||
|
||||
// Mat3 is a 3x3 matrix in row major order.
|
||||
//
|
||||
// m[3*r + c] is the element in the r'th row and c'th column.
|
||||
type Mat3 [9]float32
|
||||
|
||||
// Mat4 is a 4x4 matrix in row major order.
|
||||
//
|
||||
// m[4*r + c] is the element in the r'th row and c'th column.
|
||||
type Mat4 [16]float32
|
||||
|
||||
// Aff3 is a 3x3 affine transformation matrix in row major order, where the
|
||||
// bottom row is implicitly [0 0 1].
|
||||
//
|
||||
// m[3*r + c] is the element in the r'th row and c'th column.
|
||||
type Aff3 [6]float32
|
||||
|
||||
// Aff4 is a 4x4 affine transformation matrix in row major order, where the
|
||||
// bottom row is implicitly [0 0 0 1].
|
||||
//
|
||||
// m[4*r + c] is the element in the r'th row and c'th column.
|
||||
type Aff4 [12]float32
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package f64 implements float64 vector and matrix types.
|
||||
package f64 // import "golang.org/x/image/math/f64"
|
||||
|
||||
// Vec2 is a 2-element vector.
|
||||
type Vec2 [2]float64
|
||||
|
||||
// Vec3 is a 3-element vector.
|
||||
type Vec3 [3]float64
|
||||
|
||||
// Vec4 is a 4-element vector.
|
||||
type Vec4 [4]float64
|
||||
|
||||
// Mat3 is a 3x3 matrix in row major order.
|
||||
//
|
||||
// m[3*r + c] is the element in the r'th row and c'th column.
|
||||
type Mat3 [9]float64
|
||||
|
||||
// Mat4 is a 4x4 matrix in row major order.
|
||||
//
|
||||
// m[4*r + c] is the element in the r'th row and c'th column.
|
||||
type Mat4 [16]float64
|
||||
|
||||
// Aff3 is a 3x3 affine transformation matrix in row major order, where the
|
||||
// bottom row is implicitly [0 0 1].
|
||||
//
|
||||
// m[3*r + c] is the element in the r'th row and c'th column.
|
||||
type Aff3 [6]float64
|
||||
|
||||
// Aff4 is a 4x4 affine transformation matrix in row major order, where the
|
||||
// bottom row is implicitly [0 0 0 1].
|
||||
//
|
||||
// m[4*r + c] is the element in the r'th row and c'th column.
|
||||
type Aff4 [12]float64
|
||||
+410
@@ -0,0 +1,410 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package fixed implements fixed-point integer types.
|
||||
package fixed // import "golang.org/x/image/math/fixed"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// TODO: implement fmt.Formatter for %f and %g.
|
||||
|
||||
// I returns the integer value i as an Int26_6.
|
||||
//
|
||||
// For example, passing the integer value 2 yields Int26_6(128).
|
||||
func I(i int) Int26_6 {
|
||||
return Int26_6(i << 6)
|
||||
}
|
||||
|
||||
// Int26_6 is a signed 26.6 fixed-point number.
|
||||
//
|
||||
// The integer part ranges from -33554432 to 33554431, inclusive. The
|
||||
// fractional part has 6 bits of precision.
|
||||
//
|
||||
// For example, the number one-and-a-quarter is Int26_6(1<<6 + 1<<4).
|
||||
type Int26_6 int32
|
||||
|
||||
// String returns a human-readable representation of a 26.6 fixed-point number.
|
||||
//
|
||||
// For example, the number one-and-a-quarter becomes "1:16".
|
||||
func (x Int26_6) String() string {
|
||||
const shift, mask = 6, 1<<6 - 1
|
||||
if x >= 0 {
|
||||
return fmt.Sprintf("%d:%02d", int32(x>>shift), int32(x&mask))
|
||||
}
|
||||
x = -x
|
||||
if x >= 0 {
|
||||
return fmt.Sprintf("-%d:%02d", int32(x>>shift), int32(x&mask))
|
||||
}
|
||||
return "-33554432:00" // The minimum value is -(1<<25).
|
||||
}
|
||||
|
||||
// Floor returns the greatest integer value less than or equal to x.
|
||||
//
|
||||
// Its return type is int, not Int26_6.
|
||||
func (x Int26_6) Floor() int { return int((x + 0x00) >> 6) }
|
||||
|
||||
// Round returns the nearest integer value to x. Ties are rounded up.
|
||||
//
|
||||
// Its return type is int, not Int26_6.
|
||||
func (x Int26_6) Round() int { return int((x + 0x20) >> 6) }
|
||||
|
||||
// Ceil returns the least integer value greater than or equal to x.
|
||||
//
|
||||
// Its return type is int, not Int26_6.
|
||||
func (x Int26_6) Ceil() int { return int((x + 0x3f) >> 6) }
|
||||
|
||||
// Mul returns x*y in 26.6 fixed-point arithmetic.
|
||||
func (x Int26_6) Mul(y Int26_6) Int26_6 {
|
||||
return Int26_6((int64(x)*int64(y) + 1<<5) >> 6)
|
||||
}
|
||||
|
||||
// Int52_12 is a signed 52.12 fixed-point number.
|
||||
//
|
||||
// The integer part ranges from -2251799813685248 to 2251799813685247,
|
||||
// inclusive. The fractional part has 12 bits of precision.
|
||||
//
|
||||
// For example, the number one-and-a-quarter is Int52_12(1<<12 + 1<<10).
|
||||
type Int52_12 int64
|
||||
|
||||
// String returns a human-readable representation of a 52.12 fixed-point
|
||||
// number.
|
||||
//
|
||||
// For example, the number one-and-a-quarter becomes "1:1024".
|
||||
func (x Int52_12) String() string {
|
||||
const shift, mask = 12, 1<<12 - 1
|
||||
if x >= 0 {
|
||||
return fmt.Sprintf("%d:%04d", int64(x>>shift), int64(x&mask))
|
||||
}
|
||||
x = -x
|
||||
if x >= 0 {
|
||||
return fmt.Sprintf("-%d:%04d", int64(x>>shift), int64(x&mask))
|
||||
}
|
||||
return "-2251799813685248:0000" // The minimum value is -(1<<51).
|
||||
}
|
||||
|
||||
// Floor returns the greatest integer value less than or equal to x.
|
||||
//
|
||||
// Its return type is int, not Int52_12.
|
||||
func (x Int52_12) Floor() int { return int((x + 0x000) >> 12) }
|
||||
|
||||
// Round returns the nearest integer value to x. Ties are rounded up.
|
||||
//
|
||||
// Its return type is int, not Int52_12.
|
||||
func (x Int52_12) Round() int { return int((x + 0x800) >> 12) }
|
||||
|
||||
// Ceil returns the least integer value greater than or equal to x.
|
||||
//
|
||||
// Its return type is int, not Int52_12.
|
||||
func (x Int52_12) Ceil() int { return int((x + 0xfff) >> 12) }
|
||||
|
||||
// Mul returns x*y in 52.12 fixed-point arithmetic.
|
||||
func (x Int52_12) Mul(y Int52_12) Int52_12 {
|
||||
const M, N = 52, 12
|
||||
lo, hi := muli64(int64(x), int64(y))
|
||||
ret := Int52_12(hi<<M | lo>>N)
|
||||
ret += Int52_12((lo >> (N - 1)) & 1) // Round to nearest, instead of rounding down.
|
||||
return ret
|
||||
}
|
||||
|
||||
// muli64 multiplies two int64 values, returning the 128-bit signed integer
|
||||
// result as two uint64 values.
|
||||
//
|
||||
// This implementation is similar to $GOROOT/src/runtime/softfloat64.go's mullu
|
||||
// function, which is in turn adapted from Hacker's Delight.
|
||||
func muli64(u, v int64) (lo, hi uint64) {
|
||||
const (
|
||||
s = 32
|
||||
mask = 1<<s - 1
|
||||
)
|
||||
|
||||
u1 := uint64(u >> s)
|
||||
u0 := uint64(u & mask)
|
||||
v1 := uint64(v >> s)
|
||||
v0 := uint64(v & mask)
|
||||
|
||||
w0 := u0 * v0
|
||||
t := u1*v0 + w0>>s
|
||||
w1 := t & mask
|
||||
w2 := uint64(int64(t) >> s)
|
||||
w1 += u0 * v1
|
||||
return uint64(u) * uint64(v), u1*v1 + w2 + uint64(int64(w1)>>s)
|
||||
}
|
||||
|
||||
// P returns the integer values x and y as a Point26_6.
|
||||
//
|
||||
// For example, passing the integer values (2, -3) yields Point26_6{128, -192}.
|
||||
func P(x, y int) Point26_6 {
|
||||
return Point26_6{Int26_6(x << 6), Int26_6(y << 6)}
|
||||
}
|
||||
|
||||
// Point26_6 is a 26.6 fixed-point coordinate pair.
|
||||
//
|
||||
// It is analogous to the image.Point type in the standard library.
|
||||
type Point26_6 struct {
|
||||
X, Y Int26_6
|
||||
}
|
||||
|
||||
// Add returns the vector p+q.
|
||||
func (p Point26_6) Add(q Point26_6) Point26_6 {
|
||||
return Point26_6{p.X + q.X, p.Y + q.Y}
|
||||
}
|
||||
|
||||
// Sub returns the vector p-q.
|
||||
func (p Point26_6) Sub(q Point26_6) Point26_6 {
|
||||
return Point26_6{p.X - q.X, p.Y - q.Y}
|
||||
}
|
||||
|
||||
// Mul returns the vector p*k.
|
||||
func (p Point26_6) Mul(k Int26_6) Point26_6 {
|
||||
return Point26_6{p.X * k / 64, p.Y * k / 64}
|
||||
}
|
||||
|
||||
// Div returns the vector p/k.
|
||||
func (p Point26_6) Div(k Int26_6) Point26_6 {
|
||||
return Point26_6{p.X * 64 / k, p.Y * 64 / k}
|
||||
}
|
||||
|
||||
// In returns whether p is in r.
|
||||
func (p Point26_6) In(r Rectangle26_6) bool {
|
||||
return r.Min.X <= p.X && p.X < r.Max.X && r.Min.Y <= p.Y && p.Y < r.Max.Y
|
||||
}
|
||||
|
||||
// Point52_12 is a 52.12 fixed-point coordinate pair.
|
||||
//
|
||||
// It is analogous to the image.Point type in the standard library.
|
||||
type Point52_12 struct {
|
||||
X, Y Int52_12
|
||||
}
|
||||
|
||||
// Add returns the vector p+q.
|
||||
func (p Point52_12) Add(q Point52_12) Point52_12 {
|
||||
return Point52_12{p.X + q.X, p.Y + q.Y}
|
||||
}
|
||||
|
||||
// Sub returns the vector p-q.
|
||||
func (p Point52_12) Sub(q Point52_12) Point52_12 {
|
||||
return Point52_12{p.X - q.X, p.Y - q.Y}
|
||||
}
|
||||
|
||||
// Mul returns the vector p*k.
|
||||
func (p Point52_12) Mul(k Int52_12) Point52_12 {
|
||||
return Point52_12{p.X * k / 4096, p.Y * k / 4096}
|
||||
}
|
||||
|
||||
// Div returns the vector p/k.
|
||||
func (p Point52_12) Div(k Int52_12) Point52_12 {
|
||||
return Point52_12{p.X * 4096 / k, p.Y * 4096 / k}
|
||||
}
|
||||
|
||||
// In returns whether p is in r.
|
||||
func (p Point52_12) In(r Rectangle52_12) bool {
|
||||
return r.Min.X <= p.X && p.X < r.Max.X && r.Min.Y <= p.Y && p.Y < r.Max.Y
|
||||
}
|
||||
|
||||
// R returns the integer values minX, minY, maxX, maxY as a Rectangle26_6.
|
||||
//
|
||||
// For example, passing the integer values (0, 1, 2, 3) yields
|
||||
// Rectangle26_6{Point26_6{0, 64}, Point26_6{128, 192}}.
|
||||
//
|
||||
// Like the image.Rect function in the standard library, the returned rectangle
|
||||
// has minimum and maximum coordinates swapped if necessary so that it is
|
||||
// well-formed.
|
||||
func R(minX, minY, maxX, maxY int) Rectangle26_6 {
|
||||
if minX > maxX {
|
||||
minX, maxX = maxX, minX
|
||||
}
|
||||
if minY > maxY {
|
||||
minY, maxY = maxY, minY
|
||||
}
|
||||
return Rectangle26_6{
|
||||
Point26_6{
|
||||
Int26_6(minX << 6),
|
||||
Int26_6(minY << 6),
|
||||
},
|
||||
Point26_6{
|
||||
Int26_6(maxX << 6),
|
||||
Int26_6(maxY << 6),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Rectangle26_6 is a 26.6 fixed-point coordinate rectangle. The Min bound is
|
||||
// inclusive and the Max bound is exclusive. It is well-formed if Min.X <=
|
||||
// Max.X and likewise for Y.
|
||||
//
|
||||
// It is analogous to the image.Rectangle type in the standard library.
|
||||
type Rectangle26_6 struct {
|
||||
Min, Max Point26_6
|
||||
}
|
||||
|
||||
// Add returns the rectangle r translated by p.
|
||||
func (r Rectangle26_6) Add(p Point26_6) Rectangle26_6 {
|
||||
return Rectangle26_6{
|
||||
Point26_6{r.Min.X + p.X, r.Min.Y + p.Y},
|
||||
Point26_6{r.Max.X + p.X, r.Max.Y + p.Y},
|
||||
}
|
||||
}
|
||||
|
||||
// Sub returns the rectangle r translated by -p.
|
||||
func (r Rectangle26_6) Sub(p Point26_6) Rectangle26_6 {
|
||||
return Rectangle26_6{
|
||||
Point26_6{r.Min.X - p.X, r.Min.Y - p.Y},
|
||||
Point26_6{r.Max.X - p.X, r.Max.Y - p.Y},
|
||||
}
|
||||
}
|
||||
|
||||
// Intersect returns the largest rectangle contained by both r and s. If the
|
||||
// two rectangles do not overlap then the zero rectangle will be returned.
|
||||
func (r Rectangle26_6) Intersect(s Rectangle26_6) Rectangle26_6 {
|
||||
if r.Min.X < s.Min.X {
|
||||
r.Min.X = s.Min.X
|
||||
}
|
||||
if r.Min.Y < s.Min.Y {
|
||||
r.Min.Y = s.Min.Y
|
||||
}
|
||||
if r.Max.X > s.Max.X {
|
||||
r.Max.X = s.Max.X
|
||||
}
|
||||
if r.Max.Y > s.Max.Y {
|
||||
r.Max.Y = s.Max.Y
|
||||
}
|
||||
// Letting r0 and s0 be the values of r and s at the time that the method
|
||||
// is called, this next line is equivalent to:
|
||||
//
|
||||
// if max(r0.Min.X, s0.Min.X) >= min(r0.Max.X, s0.Max.X) || likewiseForY { etc }
|
||||
if r.Empty() {
|
||||
return Rectangle26_6{}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Union returns the smallest rectangle that contains both r and s.
|
||||
func (r Rectangle26_6) Union(s Rectangle26_6) Rectangle26_6 {
|
||||
if r.Empty() {
|
||||
return s
|
||||
}
|
||||
if s.Empty() {
|
||||
return r
|
||||
}
|
||||
if r.Min.X > s.Min.X {
|
||||
r.Min.X = s.Min.X
|
||||
}
|
||||
if r.Min.Y > s.Min.Y {
|
||||
r.Min.Y = s.Min.Y
|
||||
}
|
||||
if r.Max.X < s.Max.X {
|
||||
r.Max.X = s.Max.X
|
||||
}
|
||||
if r.Max.Y < s.Max.Y {
|
||||
r.Max.Y = s.Max.Y
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Empty returns whether the rectangle contains no points.
|
||||
func (r Rectangle26_6) Empty() bool {
|
||||
return r.Min.X >= r.Max.X || r.Min.Y >= r.Max.Y
|
||||
}
|
||||
|
||||
// In returns whether every point in r is in s.
|
||||
func (r Rectangle26_6) In(s Rectangle26_6) bool {
|
||||
if r.Empty() {
|
||||
return true
|
||||
}
|
||||
// Note that r.Max is an exclusive bound for r, so that r.In(s)
|
||||
// does not require that r.Max.In(s).
|
||||
return s.Min.X <= r.Min.X && r.Max.X <= s.Max.X &&
|
||||
s.Min.Y <= r.Min.Y && r.Max.Y <= s.Max.Y
|
||||
}
|
||||
|
||||
// Rectangle52_12 is a 52.12 fixed-point coordinate rectangle. The Min bound is
|
||||
// inclusive and the Max bound is exclusive. It is well-formed if Min.X <=
|
||||
// Max.X and likewise for Y.
|
||||
//
|
||||
// It is analogous to the image.Rectangle type in the standard library.
|
||||
type Rectangle52_12 struct {
|
||||
Min, Max Point52_12
|
||||
}
|
||||
|
||||
// Add returns the rectangle r translated by p.
|
||||
func (r Rectangle52_12) Add(p Point52_12) Rectangle52_12 {
|
||||
return Rectangle52_12{
|
||||
Point52_12{r.Min.X + p.X, r.Min.Y + p.Y},
|
||||
Point52_12{r.Max.X + p.X, r.Max.Y + p.Y},
|
||||
}
|
||||
}
|
||||
|
||||
// Sub returns the rectangle r translated by -p.
|
||||
func (r Rectangle52_12) Sub(p Point52_12) Rectangle52_12 {
|
||||
return Rectangle52_12{
|
||||
Point52_12{r.Min.X - p.X, r.Min.Y - p.Y},
|
||||
Point52_12{r.Max.X - p.X, r.Max.Y - p.Y},
|
||||
}
|
||||
}
|
||||
|
||||
// Intersect returns the largest rectangle contained by both r and s. If the
|
||||
// two rectangles do not overlap then the zero rectangle will be returned.
|
||||
func (r Rectangle52_12) Intersect(s Rectangle52_12) Rectangle52_12 {
|
||||
if r.Min.X < s.Min.X {
|
||||
r.Min.X = s.Min.X
|
||||
}
|
||||
if r.Min.Y < s.Min.Y {
|
||||
r.Min.Y = s.Min.Y
|
||||
}
|
||||
if r.Max.X > s.Max.X {
|
||||
r.Max.X = s.Max.X
|
||||
}
|
||||
if r.Max.Y > s.Max.Y {
|
||||
r.Max.Y = s.Max.Y
|
||||
}
|
||||
// Letting r0 and s0 be the values of r and s at the time that the method
|
||||
// is called, this next line is equivalent to:
|
||||
//
|
||||
// if max(r0.Min.X, s0.Min.X) >= min(r0.Max.X, s0.Max.X) || likewiseForY { etc }
|
||||
if r.Empty() {
|
||||
return Rectangle52_12{}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Union returns the smallest rectangle that contains both r and s.
|
||||
func (r Rectangle52_12) Union(s Rectangle52_12) Rectangle52_12 {
|
||||
if r.Empty() {
|
||||
return s
|
||||
}
|
||||
if s.Empty() {
|
||||
return r
|
||||
}
|
||||
if r.Min.X > s.Min.X {
|
||||
r.Min.X = s.Min.X
|
||||
}
|
||||
if r.Min.Y > s.Min.Y {
|
||||
r.Min.Y = s.Min.Y
|
||||
}
|
||||
if r.Max.X < s.Max.X {
|
||||
r.Max.X = s.Max.X
|
||||
}
|
||||
if r.Max.Y < s.Max.Y {
|
||||
r.Max.Y = s.Max.Y
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Empty returns whether the rectangle contains no points.
|
||||
func (r Rectangle52_12) Empty() bool {
|
||||
return r.Min.X >= r.Max.X || r.Min.Y >= r.Max.Y
|
||||
}
|
||||
|
||||
// In returns whether every point in r is in s.
|
||||
func (r Rectangle52_12) In(s Rectangle52_12) bool {
|
||||
if r.Empty() {
|
||||
return true
|
||||
}
|
||||
// Note that r.Max is an exclusive bound for r, so that r.In(s)
|
||||
// does not require that r.Max.In(s).
|
||||
return s.Min.X <= r.Min.X && r.Max.X <= s.Max.X &&
|
||||
s.Min.Y <= r.Min.Y && r.Max.Y <= s.Max.Y
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tiff
|
||||
|
||||
import "io"
|
||||
|
||||
// buffer buffers an io.Reader to satisfy io.ReaderAt.
|
||||
type buffer struct {
|
||||
r io.Reader
|
||||
buf []byte
|
||||
}
|
||||
|
||||
// fill reads data from b.r until the buffer contains at least end bytes.
|
||||
func (b *buffer) fill(end int) error {
|
||||
m := len(b.buf)
|
||||
if end > m {
|
||||
if end > cap(b.buf) {
|
||||
newcap := 1024
|
||||
for newcap < end {
|
||||
newcap *= 2
|
||||
}
|
||||
newbuf := make([]byte, end, newcap)
|
||||
copy(newbuf, b.buf)
|
||||
b.buf = newbuf
|
||||
} else {
|
||||
b.buf = b.buf[:end]
|
||||
}
|
||||
if n, err := io.ReadFull(b.r, b.buf[m:end]); err != nil {
|
||||
end = m + n
|
||||
b.buf = b.buf[:end]
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *buffer) ReadAt(p []byte, off int64) (int, error) {
|
||||
o := int(off)
|
||||
end := o + len(p)
|
||||
if int64(end) != off+int64(len(p)) {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
err := b.fill(end)
|
||||
return copy(p, b.buf[o:end]), err
|
||||
}
|
||||
|
||||
// Slice returns a slice of the underlying buffer. The slice contains
|
||||
// n bytes starting at offset off.
|
||||
func (b *buffer) Slice(off, n int) ([]byte, error) {
|
||||
end := off + n
|
||||
if err := b.fill(end); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b.buf[off:end], nil
|
||||
}
|
||||
|
||||
// newReaderAt converts an io.Reader into an io.ReaderAt.
|
||||
func newReaderAt(r io.Reader) io.ReaderAt {
|
||||
if ra, ok := r.(io.ReaderAt); ok {
|
||||
return ra
|
||||
}
|
||||
return &buffer{
|
||||
r: r,
|
||||
buf: make([]byte, 0, 1024),
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tiff
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
)
|
||||
|
||||
type byteReader interface {
|
||||
io.Reader
|
||||
io.ByteReader
|
||||
}
|
||||
|
||||
// unpackBits decodes the PackBits-compressed data in src and returns the
|
||||
// uncompressed data.
|
||||
//
|
||||
// The PackBits compression format is described in section 9 (p. 42)
|
||||
// of the TIFF spec.
|
||||
func unpackBits(r io.Reader) ([]byte, error) {
|
||||
buf := make([]byte, 128)
|
||||
dst := make([]byte, 0, 1024)
|
||||
br, ok := r.(byteReader)
|
||||
if !ok {
|
||||
br = bufio.NewReader(r)
|
||||
}
|
||||
|
||||
for {
|
||||
b, err := br.ReadByte()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return dst, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
code := int(int8(b))
|
||||
switch {
|
||||
case code >= 0:
|
||||
n, err := io.ReadFull(br, buf[:code+1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dst = append(dst, buf[:n]...)
|
||||
case code == -128:
|
||||
// No-op.
|
||||
default:
|
||||
if b, err = br.ReadByte(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for j := 0; j < 1-code; j++ {
|
||||
buf[j] = b
|
||||
}
|
||||
dst = append(dst, buf[:1-code]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tiff
|
||||
|
||||
// A tiff image file contains one or more images. The metadata
|
||||
// of each image is contained in an Image File Directory (IFD),
|
||||
// which contains entries of 12 bytes each and is described
|
||||
// on page 14-16 of the specification. An IFD entry consists of
|
||||
//
|
||||
// - a tag, which describes the signification of the entry,
|
||||
// - the data type and length of the entry,
|
||||
// - the data itself or a pointer to it if it is more than 4 bytes.
|
||||
//
|
||||
// The presence of a length means that each IFD is effectively an array.
|
||||
|
||||
const (
|
||||
leHeader = "II\x2A\x00" // Header for little-endian files.
|
||||
beHeader = "MM\x00\x2A" // Header for big-endian files.
|
||||
|
||||
ifdLen = 12 // Length of an IFD entry in bytes.
|
||||
)
|
||||
|
||||
// Data types (p. 14-16 of the spec).
|
||||
const (
|
||||
dtByte = 1
|
||||
dtASCII = 2
|
||||
dtShort = 3
|
||||
dtLong = 4
|
||||
dtRational = 5
|
||||
)
|
||||
|
||||
// The length of one instance of each data type in bytes.
|
||||
var lengths = [...]uint32{0, 1, 1, 2, 4, 8}
|
||||
|
||||
// Tags (see p. 28-41 of the spec).
|
||||
const (
|
||||
tImageWidth = 256
|
||||
tImageLength = 257
|
||||
tBitsPerSample = 258
|
||||
tCompression = 259
|
||||
tPhotometricInterpretation = 262
|
||||
|
||||
tFillOrder = 266
|
||||
|
||||
tStripOffsets = 273
|
||||
tSamplesPerPixel = 277
|
||||
tRowsPerStrip = 278
|
||||
tStripByteCounts = 279
|
||||
|
||||
tT4Options = 292 // CCITT Group 3 options, a set of 32 flag bits.
|
||||
tT6Options = 293 // CCITT Group 4 options, a set of 32 flag bits.
|
||||
|
||||
tTileWidth = 322
|
||||
tTileLength = 323
|
||||
tTileOffsets = 324
|
||||
tTileByteCounts = 325
|
||||
|
||||
tXResolution = 282
|
||||
tYResolution = 283
|
||||
tResolutionUnit = 296
|
||||
|
||||
tPredictor = 317
|
||||
tColorMap = 320
|
||||
tExtraSamples = 338
|
||||
tSampleFormat = 339
|
||||
)
|
||||
|
||||
// Compression types (defined in various places in the spec and supplements).
|
||||
const (
|
||||
cNone = 1
|
||||
cCCITT = 2
|
||||
cG3 = 3 // Group 3 Fax.
|
||||
cG4 = 4 // Group 4 Fax.
|
||||
cLZW = 5
|
||||
cJPEGOld = 6 // Superseded by cJPEG.
|
||||
cJPEG = 7
|
||||
cDeflate = 8 // zlib compression.
|
||||
cPackBits = 32773
|
||||
cDeflateOld = 32946 // Superseded by cDeflate.
|
||||
)
|
||||
|
||||
// Photometric interpretation values (see p. 37 of the spec).
|
||||
const (
|
||||
pWhiteIsZero = 0
|
||||
pBlackIsZero = 1
|
||||
pRGB = 2
|
||||
pPaletted = 3
|
||||
pTransMask = 4 // transparency mask
|
||||
pCMYK = 5
|
||||
pYCbCr = 6
|
||||
pCIELab = 8
|
||||
)
|
||||
|
||||
// Values for the tPredictor tag (page 64-65 of the spec).
|
||||
const (
|
||||
prNone = 1
|
||||
prHorizontal = 2
|
||||
)
|
||||
|
||||
// Values for the tResolutionUnit tag (page 18).
|
||||
const (
|
||||
resNone = 1
|
||||
resPerInch = 2 // Dots per inch.
|
||||
resPerCM = 3 // Dots per centimeter.
|
||||
)
|
||||
|
||||
// imageMode represents the mode of the image.
|
||||
type imageMode int
|
||||
|
||||
const (
|
||||
mBilevel imageMode = iota
|
||||
mPaletted
|
||||
mGray
|
||||
mGrayInvert
|
||||
mRGB
|
||||
mRGBA
|
||||
mNRGBA
|
||||
mCMYK
|
||||
)
|
||||
|
||||
// CompressionType describes the type of compression used in Options.
|
||||
type CompressionType int
|
||||
|
||||
// Constants for supported compression types.
|
||||
const (
|
||||
Uncompressed CompressionType = iota
|
||||
Deflate
|
||||
LZW
|
||||
CCITTGroup3
|
||||
CCITTGroup4
|
||||
)
|
||||
|
||||
// specValue returns the compression type constant from the TIFF spec that
|
||||
// is equivalent to c.
|
||||
func (c CompressionType) specValue() uint32 {
|
||||
switch c {
|
||||
case LZW:
|
||||
return cLZW
|
||||
case Deflate:
|
||||
return cDeflate
|
||||
case CCITTGroup3:
|
||||
return cG3
|
||||
case CCITTGroup4:
|
||||
return cG4
|
||||
}
|
||||
return cNone
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gofuzz
|
||||
|
||||
package tiff
|
||||
|
||||
import "bytes"
|
||||
|
||||
func Fuzz(data []byte) int {
|
||||
cfg, err := DecodeConfig(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
if cfg.Width*cfg.Height > 1e6 {
|
||||
return 0
|
||||
}
|
||||
img, err := Decode(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
var w bytes.Buffer
|
||||
err = Encode(&w, img, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return 1
|
||||
}
|
||||
+272
@@ -0,0 +1,272 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package lzw implements the Lempel-Ziv-Welch compressed data format,
|
||||
// described in T. A. Welch, “A Technique for High-Performance Data
|
||||
// Compression”, Computer, 17(6) (June 1984), pp 8-19.
|
||||
//
|
||||
// In particular, it implements LZW as used by the TIFF file format, including
|
||||
// an "off by one" algorithmic difference when compared to standard LZW.
|
||||
package lzw // import "golang.org/x/image/tiff/lzw"
|
||||
|
||||
/*
|
||||
This file was branched from src/pkg/compress/lzw/reader.go in the
|
||||
standard library. Differences from the original are marked with "NOTE".
|
||||
|
||||
The tif_lzw.c file in the libtiff C library has this comment:
|
||||
|
||||
----
|
||||
The 5.0 spec describes a different algorithm than Aldus
|
||||
implements. Specifically, Aldus does code length transitions
|
||||
one code earlier than should be done (for real LZW).
|
||||
Earlier versions of this library implemented the correct
|
||||
LZW algorithm, but emitted codes in a bit order opposite
|
||||
to the TIFF spec. Thus, to maintain compatibility w/ Aldus
|
||||
we interpret MSB-LSB ordered codes to be images written w/
|
||||
old versions of this library, but otherwise adhere to the
|
||||
Aldus "off by one" algorithm.
|
||||
----
|
||||
|
||||
The Go code doesn't read (invalid) TIFF files written by old versions of
|
||||
libtiff, but the LZW algorithm in this package still differs from the one in
|
||||
Go's standard package library to accommodate this "off by one" in valid TIFFs.
|
||||
*/
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Order specifies the bit ordering in an LZW data stream.
|
||||
type Order int
|
||||
|
||||
const (
|
||||
// LSB means Least Significant Bits first, as used in the GIF file format.
|
||||
LSB Order = iota
|
||||
// MSB means Most Significant Bits first, as used in the TIFF and PDF
|
||||
// file formats.
|
||||
MSB
|
||||
)
|
||||
|
||||
const (
|
||||
maxWidth = 12
|
||||
decoderInvalidCode = 0xffff
|
||||
flushBuffer = 1 << maxWidth
|
||||
)
|
||||
|
||||
// decoder is the state from which the readXxx method converts a byte
|
||||
// stream into a code stream.
|
||||
type decoder struct {
|
||||
r io.ByteReader
|
||||
bits uint32
|
||||
nBits uint
|
||||
width uint
|
||||
read func(*decoder) (uint16, error) // readLSB or readMSB
|
||||
litWidth int // width in bits of literal codes
|
||||
err error
|
||||
|
||||
// The first 1<<litWidth codes are literal codes.
|
||||
// The next two codes mean clear and EOF.
|
||||
// Other valid codes are in the range [lo, hi] where lo := clear + 2,
|
||||
// with the upper bound incrementing on each code seen.
|
||||
// overflow is the code at which hi overflows the code width. NOTE: TIFF's LZW is "off by one".
|
||||
// last is the most recently seen code, or decoderInvalidCode.
|
||||
clear, eof, hi, overflow, last uint16
|
||||
|
||||
// Each code c in [lo, hi] expands to two or more bytes. For c != hi:
|
||||
// suffix[c] is the last of these bytes.
|
||||
// prefix[c] is the code for all but the last byte.
|
||||
// This code can either be a literal code or another code in [lo, c).
|
||||
// The c == hi case is a special case.
|
||||
suffix [1 << maxWidth]uint8
|
||||
prefix [1 << maxWidth]uint16
|
||||
|
||||
// output is the temporary output buffer.
|
||||
// Literal codes are accumulated from the start of the buffer.
|
||||
// Non-literal codes decode to a sequence of suffixes that are first
|
||||
// written right-to-left from the end of the buffer before being copied
|
||||
// to the start of the buffer.
|
||||
// It is flushed when it contains >= 1<<maxWidth bytes,
|
||||
// so that there is always room to decode an entire code.
|
||||
output [2 * 1 << maxWidth]byte
|
||||
o int // write index into output
|
||||
toRead []byte // bytes to return from Read
|
||||
}
|
||||
|
||||
// readLSB returns the next code for "Least Significant Bits first" data.
|
||||
func (d *decoder) readLSB() (uint16, error) {
|
||||
for d.nBits < d.width {
|
||||
x, err := d.r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
d.bits |= uint32(x) << d.nBits
|
||||
d.nBits += 8
|
||||
}
|
||||
code := uint16(d.bits & (1<<d.width - 1))
|
||||
d.bits >>= d.width
|
||||
d.nBits -= d.width
|
||||
return code, nil
|
||||
}
|
||||
|
||||
// readMSB returns the next code for "Most Significant Bits first" data.
|
||||
func (d *decoder) readMSB() (uint16, error) {
|
||||
for d.nBits < d.width {
|
||||
x, err := d.r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
d.bits |= uint32(x) << (24 - d.nBits)
|
||||
d.nBits += 8
|
||||
}
|
||||
code := uint16(d.bits >> (32 - d.width))
|
||||
d.bits <<= d.width
|
||||
d.nBits -= d.width
|
||||
return code, nil
|
||||
}
|
||||
|
||||
func (d *decoder) Read(b []byte) (int, error) {
|
||||
for {
|
||||
if len(d.toRead) > 0 {
|
||||
n := copy(b, d.toRead)
|
||||
d.toRead = d.toRead[n:]
|
||||
return n, nil
|
||||
}
|
||||
if d.err != nil {
|
||||
return 0, d.err
|
||||
}
|
||||
d.decode()
|
||||
}
|
||||
}
|
||||
|
||||
// decode decompresses bytes from r and leaves them in d.toRead.
|
||||
// read specifies how to decode bytes into codes.
|
||||
// litWidth is the width in bits of literal codes.
|
||||
func (d *decoder) decode() {
|
||||
// Loop over the code stream, converting codes into decompressed bytes.
|
||||
loop:
|
||||
for {
|
||||
code, err := d.read(d)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
d.err = err
|
||||
break
|
||||
}
|
||||
switch {
|
||||
case code < d.clear:
|
||||
// We have a literal code.
|
||||
d.output[d.o] = uint8(code)
|
||||
d.o++
|
||||
if d.last != decoderInvalidCode {
|
||||
// Save what the hi code expands to.
|
||||
d.suffix[d.hi] = uint8(code)
|
||||
d.prefix[d.hi] = d.last
|
||||
}
|
||||
case code == d.clear:
|
||||
d.width = 1 + uint(d.litWidth)
|
||||
d.hi = d.eof
|
||||
d.overflow = 1 << d.width
|
||||
d.last = decoderInvalidCode
|
||||
continue
|
||||
case code == d.eof:
|
||||
d.err = io.EOF
|
||||
break loop
|
||||
case code <= d.hi:
|
||||
c, i := code, len(d.output)-1
|
||||
if code == d.hi && d.last != decoderInvalidCode {
|
||||
// code == hi is a special case which expands to the last expansion
|
||||
// followed by the head of the last expansion. To find the head, we walk
|
||||
// the prefix chain until we find a literal code.
|
||||
c = d.last
|
||||
for c >= d.clear {
|
||||
c = d.prefix[c]
|
||||
}
|
||||
d.output[i] = uint8(c)
|
||||
i--
|
||||
c = d.last
|
||||
}
|
||||
// Copy the suffix chain into output and then write that to w.
|
||||
for c >= d.clear {
|
||||
d.output[i] = d.suffix[c]
|
||||
i--
|
||||
c = d.prefix[c]
|
||||
}
|
||||
d.output[i] = uint8(c)
|
||||
d.o += copy(d.output[d.o:], d.output[i:])
|
||||
if d.last != decoderInvalidCode {
|
||||
// Save what the hi code expands to.
|
||||
d.suffix[d.hi] = uint8(c)
|
||||
d.prefix[d.hi] = d.last
|
||||
}
|
||||
default:
|
||||
d.err = errors.New("lzw: invalid code")
|
||||
break loop
|
||||
}
|
||||
d.last, d.hi = code, d.hi+1
|
||||
if d.hi+1 >= d.overflow { // NOTE: the "+1" is where TIFF's LZW differs from the standard algorithm.
|
||||
if d.width == maxWidth {
|
||||
d.last = decoderInvalidCode
|
||||
} else {
|
||||
d.width++
|
||||
d.overflow <<= 1
|
||||
}
|
||||
}
|
||||
if d.o >= flushBuffer {
|
||||
break
|
||||
}
|
||||
}
|
||||
// Flush pending output.
|
||||
d.toRead = d.output[:d.o]
|
||||
d.o = 0
|
||||
}
|
||||
|
||||
var errClosed = errors.New("lzw: reader/writer is closed")
|
||||
|
||||
func (d *decoder) Close() error {
|
||||
d.err = errClosed // in case any Reads come along
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewReader creates a new io.ReadCloser.
|
||||
// Reads from the returned io.ReadCloser read and decompress data from r.
|
||||
// If r does not also implement io.ByteReader,
|
||||
// the decompressor may read more data than necessary from r.
|
||||
// It is the caller's responsibility to call Close on the ReadCloser when
|
||||
// finished reading.
|
||||
// The number of bits to use for literal codes, litWidth, must be in the
|
||||
// range [2,8] and is typically 8. It must equal the litWidth
|
||||
// used during compression.
|
||||
func NewReader(r io.Reader, order Order, litWidth int) io.ReadCloser {
|
||||
d := new(decoder)
|
||||
switch order {
|
||||
case LSB:
|
||||
d.read = (*decoder).readLSB
|
||||
case MSB:
|
||||
d.read = (*decoder).readMSB
|
||||
default:
|
||||
d.err = errors.New("lzw: unknown order")
|
||||
return d
|
||||
}
|
||||
if litWidth < 2 || 8 < litWidth {
|
||||
d.err = fmt.Errorf("lzw: litWidth %d out of range", litWidth)
|
||||
return d
|
||||
}
|
||||
if br, ok := r.(io.ByteReader); ok {
|
||||
d.r = br
|
||||
} else {
|
||||
d.r = bufio.NewReader(r)
|
||||
}
|
||||
d.litWidth = litWidth
|
||||
d.width = 1 + uint(litWidth)
|
||||
d.clear = uint16(1) << uint(litWidth)
|
||||
d.eof, d.hi = d.clear+1, d.clear+1
|
||||
d.overflow = uint16(1) << d.width
|
||||
d.last = decoderInvalidCode
|
||||
|
||||
return d
|
||||
}
|
||||
+785
@@ -0,0 +1,785 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package tiff implements a TIFF image decoder and encoder.
|
||||
//
|
||||
// The TIFF specification is at http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf
|
||||
package tiff // import "golang.org/x/image/tiff"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"io"
|
||||
"math"
|
||||
|
||||
"golang.org/x/image/ccitt"
|
||||
"golang.org/x/image/tiff/lzw"
|
||||
)
|
||||
|
||||
// A FormatError reports that the input is not a valid TIFF image.
|
||||
type FormatError string
|
||||
|
||||
func (e FormatError) Error() string {
|
||||
return "tiff: invalid format: " + string(e)
|
||||
}
|
||||
|
||||
// An UnsupportedError reports that the input uses a valid but
|
||||
// unimplemented feature.
|
||||
type UnsupportedError string
|
||||
|
||||
func (e UnsupportedError) Error() string {
|
||||
return "tiff: unsupported feature: " + string(e)
|
||||
}
|
||||
|
||||
var (
|
||||
errNoPixels = FormatError("not enough pixel data")
|
||||
errInvalidColorIndex = FormatError("invalid color index")
|
||||
)
|
||||
|
||||
const maxChunkSize = 10 << 20 // 10M
|
||||
|
||||
// safeReadAt is a verbatim copy of internal/saferio.ReadDataAt from the
|
||||
// standard library, which is used to read data from a reader using a length
|
||||
// provided by untrusted data, without allocating the entire slice ahead of time
|
||||
// if it is large (>maxChunkSize). This allows us to avoid allocating giant
|
||||
// slices before learning that we can't actually read that much data from the
|
||||
// reader.
|
||||
func safeReadAt(r io.ReaderAt, n uint64, off int64) ([]byte, error) {
|
||||
if int64(n) < 0 || n != uint64(int(n)) {
|
||||
// n is too large to fit in int, so we can't allocate
|
||||
// a buffer large enough. Treat this as a read failure.
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
if n < maxChunkSize {
|
||||
buf := make([]byte, n)
|
||||
_, err := r.ReadAt(buf, off)
|
||||
if err != nil {
|
||||
// io.SectionReader can return EOF for n == 0,
|
||||
// but for our purposes that is a success.
|
||||
if err != io.EOF || n > 0 {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
var buf []byte
|
||||
buf1 := make([]byte, maxChunkSize)
|
||||
for n > 0 {
|
||||
next := n
|
||||
if next > maxChunkSize {
|
||||
next = maxChunkSize
|
||||
}
|
||||
_, err := r.ReadAt(buf1[:next], off)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf = append(buf, buf1[:next]...)
|
||||
n -= next
|
||||
off += int64(next)
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
type decoder struct {
|
||||
r io.ReaderAt
|
||||
byteOrder binary.ByteOrder
|
||||
config image.Config
|
||||
mode imageMode
|
||||
bpp uint
|
||||
features map[int][]uint
|
||||
palette []color.Color
|
||||
|
||||
buf []byte
|
||||
off int // Current offset in buf.
|
||||
v uint32 // Buffer value for reading with arbitrary bit depths.
|
||||
nbits uint // Remaining number of bits in v.
|
||||
}
|
||||
|
||||
// firstVal returns the first uint of the features entry with the given tag,
|
||||
// or 0 if the tag does not exist.
|
||||
func (d *decoder) firstVal(tag int) uint {
|
||||
f := d.features[tag]
|
||||
if len(f) == 0 {
|
||||
return 0
|
||||
}
|
||||
return f[0]
|
||||
}
|
||||
|
||||
// ifdUint decodes the IFD entry in p, which must be of the Byte, Short
|
||||
// or Long type, and returns the decoded uint values.
|
||||
func (d *decoder) ifdUint(p []byte) (u []uint, err error) {
|
||||
var raw []byte
|
||||
if len(p) < ifdLen {
|
||||
return nil, FormatError("bad IFD entry")
|
||||
}
|
||||
|
||||
datatype := d.byteOrder.Uint16(p[2:4])
|
||||
if dt := int(datatype); dt <= 0 || dt >= len(lengths) {
|
||||
return nil, UnsupportedError("IFD entry datatype")
|
||||
}
|
||||
|
||||
count := d.byteOrder.Uint32(p[4:8])
|
||||
if count > math.MaxInt32/lengths[datatype] {
|
||||
return nil, FormatError("IFD data too large")
|
||||
}
|
||||
if datalen := lengths[datatype] * count; datalen > 4 {
|
||||
// The IFD contains a pointer to the real value.
|
||||
raw, err = safeReadAt(d.r, uint64(datalen), int64(d.byteOrder.Uint32(p[8:12])))
|
||||
} else {
|
||||
raw = p[8 : 8+datalen]
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u = make([]uint, count)
|
||||
switch datatype {
|
||||
case dtByte:
|
||||
for i := uint32(0); i < count; i++ {
|
||||
u[i] = uint(raw[i])
|
||||
}
|
||||
case dtShort:
|
||||
for i := uint32(0); i < count; i++ {
|
||||
u[i] = uint(d.byteOrder.Uint16(raw[2*i : 2*(i+1)]))
|
||||
}
|
||||
case dtLong:
|
||||
for i := uint32(0); i < count; i++ {
|
||||
u[i] = uint(d.byteOrder.Uint32(raw[4*i : 4*(i+1)]))
|
||||
}
|
||||
default:
|
||||
return nil, UnsupportedError("data type")
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// parseIFD decides whether the IFD entry in p is "interesting" and
|
||||
// stows away the data in the decoder. It returns the tag number of the
|
||||
// entry and an error, if any.
|
||||
func (d *decoder) parseIFD(p []byte) (int, error) {
|
||||
tag := d.byteOrder.Uint16(p[0:2])
|
||||
switch tag {
|
||||
case tBitsPerSample,
|
||||
tExtraSamples,
|
||||
tPhotometricInterpretation,
|
||||
tCompression,
|
||||
tPredictor,
|
||||
tStripOffsets,
|
||||
tStripByteCounts,
|
||||
tRowsPerStrip,
|
||||
tTileWidth,
|
||||
tTileLength,
|
||||
tTileOffsets,
|
||||
tTileByteCounts,
|
||||
tImageLength,
|
||||
tImageWidth,
|
||||
tFillOrder,
|
||||
tT4Options,
|
||||
tT6Options:
|
||||
val, err := d.ifdUint(p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
d.features[int(tag)] = val
|
||||
case tColorMap:
|
||||
val, err := d.ifdUint(p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
numcolors := len(val) / 3
|
||||
if len(val)%3 != 0 || numcolors <= 0 || numcolors > 256 {
|
||||
return 0, FormatError("bad ColorMap length")
|
||||
}
|
||||
d.palette = make([]color.Color, numcolors)
|
||||
for i := 0; i < numcolors; i++ {
|
||||
d.palette[i] = color.RGBA64{
|
||||
uint16(val[i]),
|
||||
uint16(val[i+numcolors]),
|
||||
uint16(val[i+2*numcolors]),
|
||||
0xffff,
|
||||
}
|
||||
}
|
||||
case tSampleFormat:
|
||||
// Page 27 of the spec: If the SampleFormat is present and
|
||||
// the value is not 1 [= unsigned integer data], a Baseline
|
||||
// TIFF reader that cannot handle the SampleFormat value
|
||||
// must terminate the import process gracefully.
|
||||
val, err := d.ifdUint(p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for _, v := range val {
|
||||
if v != 1 {
|
||||
return 0, UnsupportedError("sample format")
|
||||
}
|
||||
}
|
||||
}
|
||||
return int(tag), nil
|
||||
}
|
||||
|
||||
// readBits reads n bits from the internal buffer starting at the current offset.
|
||||
func (d *decoder) readBits(n uint) (v uint32, ok bool) {
|
||||
for d.nbits < n {
|
||||
d.v <<= 8
|
||||
if d.off >= len(d.buf) {
|
||||
return 0, false
|
||||
}
|
||||
d.v |= uint32(d.buf[d.off])
|
||||
d.off++
|
||||
d.nbits += 8
|
||||
}
|
||||
d.nbits -= n
|
||||
rv := d.v >> d.nbits
|
||||
d.v &^= rv << d.nbits
|
||||
return rv, true
|
||||
}
|
||||
|
||||
// flushBits discards the unread bits in the buffer used by readBits.
|
||||
// It is used at the end of a line.
|
||||
func (d *decoder) flushBits() {
|
||||
d.v = 0
|
||||
d.nbits = 0
|
||||
}
|
||||
|
||||
// minInt returns the smaller of x or y.
|
||||
func minInt(a, b int) int {
|
||||
if a <= b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// decode decodes the raw data of an image.
|
||||
// It reads from d.buf and writes the strip or tile into dst.
|
||||
func (d *decoder) decode(dst image.Image, xmin, ymin, xmax, ymax int) error {
|
||||
d.off = 0
|
||||
|
||||
// Apply horizontal predictor if necessary.
|
||||
// In this case, p contains the color difference to the preceding pixel.
|
||||
// See page 64-65 of the spec.
|
||||
if d.firstVal(tPredictor) == prHorizontal {
|
||||
switch d.bpp {
|
||||
case 16:
|
||||
var off int
|
||||
n := 2 * len(d.features[tBitsPerSample]) // bytes per sample times samples per pixel
|
||||
for y := ymin; y < ymax; y++ {
|
||||
off += n
|
||||
for x := 0; x < (xmax-xmin-1)*n; x += 2 {
|
||||
if off+2 > len(d.buf) {
|
||||
return errNoPixels
|
||||
}
|
||||
v0 := d.byteOrder.Uint16(d.buf[off-n : off-n+2])
|
||||
v1 := d.byteOrder.Uint16(d.buf[off : off+2])
|
||||
d.byteOrder.PutUint16(d.buf[off:off+2], v1+v0)
|
||||
off += 2
|
||||
}
|
||||
}
|
||||
case 8:
|
||||
var off int
|
||||
n := 1 * len(d.features[tBitsPerSample]) // bytes per sample times samples per pixel
|
||||
for y := ymin; y < ymax; y++ {
|
||||
off += n
|
||||
for x := 0; x < (xmax-xmin-1)*n; x++ {
|
||||
if off >= len(d.buf) {
|
||||
return errNoPixels
|
||||
}
|
||||
d.buf[off] += d.buf[off-n]
|
||||
off++
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
return UnsupportedError("horizontal predictor with 1 BitsPerSample")
|
||||
}
|
||||
}
|
||||
|
||||
rMaxX := minInt(xmax, dst.Bounds().Max.X)
|
||||
rMaxY := minInt(ymax, dst.Bounds().Max.Y)
|
||||
switch d.mode {
|
||||
case mGray, mGrayInvert:
|
||||
if d.bpp == 16 {
|
||||
img := dst.(*image.Gray16)
|
||||
for y := ymin; y < rMaxY; y++ {
|
||||
for x := xmin; x < rMaxX; x++ {
|
||||
if d.off+2 > len(d.buf) {
|
||||
return errNoPixels
|
||||
}
|
||||
v := d.byteOrder.Uint16(d.buf[d.off : d.off+2])
|
||||
d.off += 2
|
||||
if d.mode == mGrayInvert {
|
||||
v = 0xffff - v
|
||||
}
|
||||
img.SetGray16(x, y, color.Gray16{v})
|
||||
}
|
||||
if rMaxX == img.Bounds().Max.X {
|
||||
d.off += 2 * (xmax - img.Bounds().Max.X)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
img := dst.(*image.Gray)
|
||||
max := uint32((1 << d.bpp) - 1)
|
||||
for y := ymin; y < rMaxY; y++ {
|
||||
for x := xmin; x < rMaxX; x++ {
|
||||
v, ok := d.readBits(d.bpp)
|
||||
if !ok {
|
||||
return errNoPixels
|
||||
}
|
||||
v = v * 0xff / max
|
||||
if d.mode == mGrayInvert {
|
||||
v = 0xff - v
|
||||
}
|
||||
img.SetGray(x, y, color.Gray{uint8(v)})
|
||||
}
|
||||
d.flushBits()
|
||||
}
|
||||
}
|
||||
case mPaletted:
|
||||
img := dst.(*image.Paletted)
|
||||
pLen := len(d.palette)
|
||||
for y := ymin; y < rMaxY; y++ {
|
||||
for x := xmin; x < rMaxX; x++ {
|
||||
v, ok := d.readBits(d.bpp)
|
||||
if !ok {
|
||||
return errNoPixels
|
||||
}
|
||||
idx := uint8(v)
|
||||
if int(idx) >= pLen {
|
||||
return errInvalidColorIndex
|
||||
}
|
||||
img.SetColorIndex(x, y, idx)
|
||||
}
|
||||
d.flushBits()
|
||||
}
|
||||
case mRGB:
|
||||
if d.bpp == 16 {
|
||||
img := dst.(*image.RGBA64)
|
||||
for y := ymin; y < rMaxY; y++ {
|
||||
for x := xmin; x < rMaxX; x++ {
|
||||
if d.off+6 > len(d.buf) {
|
||||
return errNoPixels
|
||||
}
|
||||
r := d.byteOrder.Uint16(d.buf[d.off+0 : d.off+2])
|
||||
g := d.byteOrder.Uint16(d.buf[d.off+2 : d.off+4])
|
||||
b := d.byteOrder.Uint16(d.buf[d.off+4 : d.off+6])
|
||||
d.off += 6
|
||||
img.SetRGBA64(x, y, color.RGBA64{r, g, b, 0xffff})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
img := dst.(*image.RGBA)
|
||||
for y := ymin; y < rMaxY; y++ {
|
||||
min := img.PixOffset(xmin, y)
|
||||
max := img.PixOffset(rMaxX, y)
|
||||
off := (y - ymin) * (xmax - xmin) * 3
|
||||
for i := min; i < max; i += 4 {
|
||||
if off+3 > len(d.buf) {
|
||||
return errNoPixels
|
||||
}
|
||||
img.Pix[i+0] = d.buf[off+0]
|
||||
img.Pix[i+1] = d.buf[off+1]
|
||||
img.Pix[i+2] = d.buf[off+2]
|
||||
img.Pix[i+3] = 0xff
|
||||
off += 3
|
||||
}
|
||||
}
|
||||
}
|
||||
case mNRGBA:
|
||||
if d.bpp == 16 {
|
||||
img := dst.(*image.NRGBA64)
|
||||
for y := ymin; y < rMaxY; y++ {
|
||||
for x := xmin; x < rMaxX; x++ {
|
||||
if d.off+8 > len(d.buf) {
|
||||
return errNoPixels
|
||||
}
|
||||
r := d.byteOrder.Uint16(d.buf[d.off+0 : d.off+2])
|
||||
g := d.byteOrder.Uint16(d.buf[d.off+2 : d.off+4])
|
||||
b := d.byteOrder.Uint16(d.buf[d.off+4 : d.off+6])
|
||||
a := d.byteOrder.Uint16(d.buf[d.off+6 : d.off+8])
|
||||
d.off += 8
|
||||
img.SetNRGBA64(x, y, color.NRGBA64{r, g, b, a})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
img := dst.(*image.NRGBA)
|
||||
for y := ymin; y < rMaxY; y++ {
|
||||
min := img.PixOffset(xmin, y)
|
||||
max := img.PixOffset(rMaxX, y)
|
||||
i0, i1 := (y-ymin)*(xmax-xmin)*4, (y-ymin+1)*(xmax-xmin)*4
|
||||
if i1 > len(d.buf) {
|
||||
return errNoPixels
|
||||
}
|
||||
copy(img.Pix[min:max], d.buf[i0:i1])
|
||||
}
|
||||
}
|
||||
case mRGBA:
|
||||
if d.bpp == 16 {
|
||||
img := dst.(*image.RGBA64)
|
||||
for y := ymin; y < rMaxY; y++ {
|
||||
for x := xmin; x < rMaxX; x++ {
|
||||
if d.off+8 > len(d.buf) {
|
||||
return errNoPixels
|
||||
}
|
||||
r := d.byteOrder.Uint16(d.buf[d.off+0 : d.off+2])
|
||||
g := d.byteOrder.Uint16(d.buf[d.off+2 : d.off+4])
|
||||
b := d.byteOrder.Uint16(d.buf[d.off+4 : d.off+6])
|
||||
a := d.byteOrder.Uint16(d.buf[d.off+6 : d.off+8])
|
||||
d.off += 8
|
||||
img.SetRGBA64(x, y, color.RGBA64{r, g, b, a})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
img := dst.(*image.RGBA)
|
||||
for y := ymin; y < rMaxY; y++ {
|
||||
min := img.PixOffset(xmin, y)
|
||||
max := img.PixOffset(rMaxX, y)
|
||||
i0, i1 := (y-ymin)*(xmax-xmin)*4, (y-ymin+1)*(xmax-xmin)*4
|
||||
if i1 > len(d.buf) {
|
||||
return errNoPixels
|
||||
}
|
||||
copy(img.Pix[min:max], d.buf[i0:i1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newDecoder(r io.Reader) (*decoder, error) {
|
||||
d := &decoder{
|
||||
r: newReaderAt(r),
|
||||
features: make(map[int][]uint),
|
||||
}
|
||||
|
||||
p := make([]byte, 8)
|
||||
if _, err := d.r.ReadAt(p, 0); err != nil {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
switch string(p[0:4]) {
|
||||
case leHeader:
|
||||
d.byteOrder = binary.LittleEndian
|
||||
case beHeader:
|
||||
d.byteOrder = binary.BigEndian
|
||||
default:
|
||||
return nil, FormatError("malformed header")
|
||||
}
|
||||
|
||||
ifdOffset := int64(d.byteOrder.Uint32(p[4:8]))
|
||||
|
||||
// The first two bytes contain the number of entries (12 bytes each).
|
||||
if _, err := d.r.ReadAt(p[0:2], ifdOffset); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
numItems := int(d.byteOrder.Uint16(p[0:2]))
|
||||
|
||||
// All IFD entries are read in one chunk.
|
||||
var err error
|
||||
p, err = safeReadAt(d.r, uint64(ifdLen*numItems), ifdOffset+2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prevTag := -1
|
||||
for i := 0; i < len(p); i += ifdLen {
|
||||
tag, err := d.parseIFD(p[i : i+ifdLen])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tag <= prevTag {
|
||||
return nil, FormatError("tags are not sorted in ascending order")
|
||||
}
|
||||
prevTag = tag
|
||||
}
|
||||
|
||||
d.config.Width = int(d.firstVal(tImageWidth))
|
||||
d.config.Height = int(d.firstVal(tImageLength))
|
||||
|
||||
if _, ok := d.features[tBitsPerSample]; !ok {
|
||||
// Default is 1 per specification.
|
||||
d.features[tBitsPerSample] = []uint{1}
|
||||
}
|
||||
d.bpp = d.firstVal(tBitsPerSample)
|
||||
switch d.bpp {
|
||||
case 0:
|
||||
return nil, FormatError("BitsPerSample must not be 0")
|
||||
case 1, 8, 16:
|
||||
// Nothing to do, these are accepted by this implementation.
|
||||
default:
|
||||
return nil, UnsupportedError(fmt.Sprintf("BitsPerSample of %v", d.bpp))
|
||||
}
|
||||
|
||||
// Determine the image mode.
|
||||
switch d.firstVal(tPhotometricInterpretation) {
|
||||
case pRGB:
|
||||
if d.bpp == 16 {
|
||||
for _, b := range d.features[tBitsPerSample] {
|
||||
if b != 16 {
|
||||
return nil, FormatError("wrong number of samples for 16bit RGB")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, b := range d.features[tBitsPerSample] {
|
||||
if b != 8 {
|
||||
return nil, FormatError("wrong number of samples for 8bit RGB")
|
||||
}
|
||||
}
|
||||
}
|
||||
// RGB images normally have 3 samples per pixel.
|
||||
// If there are more, ExtraSamples (p. 31-32 of the spec)
|
||||
// gives their meaning (usually an alpha channel).
|
||||
//
|
||||
// This implementation does not support extra samples
|
||||
// of an unspecified type.
|
||||
switch len(d.features[tBitsPerSample]) {
|
||||
case 3:
|
||||
d.mode = mRGB
|
||||
if d.bpp == 16 {
|
||||
d.config.ColorModel = color.RGBA64Model
|
||||
} else {
|
||||
d.config.ColorModel = color.RGBAModel
|
||||
}
|
||||
case 4:
|
||||
switch d.firstVal(tExtraSamples) {
|
||||
case 1:
|
||||
d.mode = mRGBA
|
||||
if d.bpp == 16 {
|
||||
d.config.ColorModel = color.RGBA64Model
|
||||
} else {
|
||||
d.config.ColorModel = color.RGBAModel
|
||||
}
|
||||
case 2:
|
||||
d.mode = mNRGBA
|
||||
if d.bpp == 16 {
|
||||
d.config.ColorModel = color.NRGBA64Model
|
||||
} else {
|
||||
d.config.ColorModel = color.NRGBAModel
|
||||
}
|
||||
default:
|
||||
return nil, FormatError("wrong number of samples for RGB")
|
||||
}
|
||||
default:
|
||||
return nil, FormatError("wrong number of samples for RGB")
|
||||
}
|
||||
case pPaletted:
|
||||
d.mode = mPaletted
|
||||
d.config.ColorModel = color.Palette(d.palette)
|
||||
case pWhiteIsZero:
|
||||
d.mode = mGrayInvert
|
||||
if d.bpp == 16 {
|
||||
d.config.ColorModel = color.Gray16Model
|
||||
} else {
|
||||
d.config.ColorModel = color.GrayModel
|
||||
}
|
||||
case pBlackIsZero:
|
||||
d.mode = mGray
|
||||
if d.bpp == 16 {
|
||||
d.config.ColorModel = color.Gray16Model
|
||||
} else {
|
||||
d.config.ColorModel = color.GrayModel
|
||||
}
|
||||
default:
|
||||
return nil, UnsupportedError("color model")
|
||||
}
|
||||
if d.firstVal(tPhotometricInterpretation) != pRGB {
|
||||
if len(d.features[tBitsPerSample]) != 1 {
|
||||
return nil, UnsupportedError("extra samples")
|
||||
}
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// DecodeConfig returns the color model and dimensions of a TIFF image without
|
||||
// decoding the entire image.
|
||||
func DecodeConfig(r io.Reader) (image.Config, error) {
|
||||
d, err := newDecoder(r)
|
||||
if err != nil {
|
||||
return image.Config{}, err
|
||||
}
|
||||
return d.config, nil
|
||||
}
|
||||
|
||||
func ccittFillOrder(tiffFillOrder uint) ccitt.Order {
|
||||
if tiffFillOrder == 2 {
|
||||
return ccitt.LSB
|
||||
}
|
||||
return ccitt.MSB
|
||||
}
|
||||
|
||||
// Decode reads a TIFF image from r and returns it as an image.Image.
|
||||
// The type of Image returned depends on the contents of the TIFF.
|
||||
func Decode(r io.Reader) (img image.Image, err error) {
|
||||
d, err := newDecoder(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
blockPadding := false
|
||||
blockWidth := d.config.Width
|
||||
blockHeight := d.config.Height
|
||||
blocksAcross := 1
|
||||
blocksDown := 1
|
||||
|
||||
if d.config.Width == 0 {
|
||||
blocksAcross = 0
|
||||
}
|
||||
if d.config.Height == 0 {
|
||||
blocksDown = 0
|
||||
}
|
||||
|
||||
var blockOffsets, blockCounts []uint
|
||||
|
||||
if int(d.firstVal(tTileWidth)) != 0 {
|
||||
blockPadding = true
|
||||
|
||||
blockWidth = int(d.firstVal(tTileWidth))
|
||||
blockHeight = int(d.firstVal(tTileLength))
|
||||
|
||||
// The specification says that tile widths and lengths must be a multiple of 16.
|
||||
// We currently permit invalid sizes, but reject anything too small to limit the
|
||||
// amount of work a malicious input can force us to perform.
|
||||
if blockWidth < 8 || blockHeight < 8 {
|
||||
return nil, FormatError("tile size is too small")
|
||||
}
|
||||
|
||||
if blockWidth != 0 {
|
||||
blocksAcross = (d.config.Width + blockWidth - 1) / blockWidth
|
||||
}
|
||||
if blockHeight != 0 {
|
||||
blocksDown = (d.config.Height + blockHeight - 1) / blockHeight
|
||||
}
|
||||
|
||||
blockCounts = d.features[tTileByteCounts]
|
||||
blockOffsets = d.features[tTileOffsets]
|
||||
|
||||
} else {
|
||||
if int(d.firstVal(tRowsPerStrip)) != 0 {
|
||||
blockHeight = int(d.firstVal(tRowsPerStrip))
|
||||
}
|
||||
|
||||
if blockHeight != 0 {
|
||||
blocksDown = (d.config.Height + blockHeight - 1) / blockHeight
|
||||
}
|
||||
|
||||
blockOffsets = d.features[tStripOffsets]
|
||||
blockCounts = d.features[tStripByteCounts]
|
||||
}
|
||||
|
||||
// Check if we have the right number of strips/tiles, offsets and counts.
|
||||
if n := blocksAcross * blocksDown; len(blockOffsets) < n || len(blockCounts) < n {
|
||||
return nil, FormatError("inconsistent header")
|
||||
}
|
||||
|
||||
imgRect := image.Rect(0, 0, d.config.Width, d.config.Height)
|
||||
switch d.mode {
|
||||
case mGray, mGrayInvert:
|
||||
if d.bpp == 16 {
|
||||
img = image.NewGray16(imgRect)
|
||||
} else {
|
||||
img = image.NewGray(imgRect)
|
||||
}
|
||||
case mPaletted:
|
||||
img = image.NewPaletted(imgRect, d.palette)
|
||||
case mNRGBA:
|
||||
if d.bpp == 16 {
|
||||
img = image.NewNRGBA64(imgRect)
|
||||
} else {
|
||||
img = image.NewNRGBA(imgRect)
|
||||
}
|
||||
case mRGB, mRGBA:
|
||||
if d.bpp == 16 {
|
||||
img = image.NewRGBA64(imgRect)
|
||||
} else {
|
||||
img = image.NewRGBA(imgRect)
|
||||
}
|
||||
}
|
||||
|
||||
if blocksAcross == 0 || blocksDown == 0 {
|
||||
return
|
||||
}
|
||||
// Maximum data per pixel is 8 bytes (RGBA64).
|
||||
blockMaxDataSize := int64(blockWidth) * int64(blockHeight) * 8
|
||||
for i := 0; i < blocksAcross; i++ {
|
||||
blkW := blockWidth
|
||||
if !blockPadding && i == blocksAcross-1 && d.config.Width%blockWidth != 0 {
|
||||
blkW = d.config.Width % blockWidth
|
||||
}
|
||||
for j := 0; j < blocksDown; j++ {
|
||||
blkH := blockHeight
|
||||
if !blockPadding && j == blocksDown-1 && d.config.Height%blockHeight != 0 {
|
||||
blkH = d.config.Height % blockHeight
|
||||
}
|
||||
offset := int64(blockOffsets[j*blocksAcross+i])
|
||||
n := int64(blockCounts[j*blocksAcross+i])
|
||||
switch d.firstVal(tCompression) {
|
||||
|
||||
// According to the spec, Compression does not have a default value,
|
||||
// but some tools interpret a missing Compression value as none so we do
|
||||
// the same.
|
||||
case cNone, 0:
|
||||
if b, ok := d.r.(*buffer); ok {
|
||||
d.buf, err = b.Slice(int(offset), int(n))
|
||||
} else {
|
||||
d.buf, err = safeReadAt(d.r, uint64(n), offset)
|
||||
}
|
||||
case cG3:
|
||||
inv := d.firstVal(tPhotometricInterpretation) == pWhiteIsZero
|
||||
order := ccittFillOrder(d.firstVal(tFillOrder))
|
||||
r := ccitt.NewReader(io.NewSectionReader(d.r, offset, n), order, ccitt.Group3, blkW, blkH, &ccitt.Options{Invert: inv, Align: false})
|
||||
d.buf, err = readBuf(r, d.buf, blockMaxDataSize)
|
||||
case cG4:
|
||||
inv := d.firstVal(tPhotometricInterpretation) == pWhiteIsZero
|
||||
order := ccittFillOrder(d.firstVal(tFillOrder))
|
||||
r := ccitt.NewReader(io.NewSectionReader(d.r, offset, n), order, ccitt.Group4, blkW, blkH, &ccitt.Options{Invert: inv, Align: false})
|
||||
d.buf, err = readBuf(r, d.buf, blockMaxDataSize)
|
||||
case cLZW:
|
||||
r := lzw.NewReader(io.NewSectionReader(d.r, offset, n), lzw.MSB, 8)
|
||||
d.buf, err = readBuf(r, d.buf, blockMaxDataSize)
|
||||
r.Close()
|
||||
case cDeflate, cDeflateOld:
|
||||
var r io.ReadCloser
|
||||
r, err = zlib.NewReader(io.NewSectionReader(d.r, offset, n))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.buf, err = readBuf(r, d.buf, blockMaxDataSize)
|
||||
r.Close()
|
||||
case cPackBits:
|
||||
d.buf, err = unpackBits(io.NewSectionReader(d.r, offset, n))
|
||||
default:
|
||||
err = UnsupportedError(fmt.Sprintf("compression value %d", d.firstVal(tCompression)))
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
xmin := i * blockWidth
|
||||
ymin := j * blockHeight
|
||||
xmax := xmin + blkW
|
||||
ymax := ymin + blkH
|
||||
err = d.decode(img, xmin, ymin, xmax, ymax)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func readBuf(r io.Reader, buf []byte, lim int64) ([]byte, error) {
|
||||
b := bytes.NewBuffer(buf[:0])
|
||||
_, err := b.ReadFrom(io.LimitReader(r, lim))
|
||||
return b.Bytes(), err
|
||||
}
|
||||
|
||||
func init() {
|
||||
image.RegisterFormat("tiff", leHeader, Decode, DecodeConfig)
|
||||
image.RegisterFormat("tiff", beHeader, Decode, DecodeConfig)
|
||||
}
|
||||
+441
@@ -0,0 +1,441 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tiff
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"image"
|
||||
"io"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// The TIFF format allows to choose the order of the different elements freely.
|
||||
// The basic structure of a TIFF file written by this package is:
|
||||
//
|
||||
// 1. Header (8 bytes).
|
||||
// 2. Image data.
|
||||
// 3. Image File Directory (IFD).
|
||||
// 4. "Pointer area" for larger entries in the IFD.
|
||||
|
||||
// We only write little-endian TIFF files.
|
||||
var enc = binary.LittleEndian
|
||||
|
||||
// An ifdEntry is a single entry in an Image File Directory.
|
||||
// A value of type dtRational is composed of two 32-bit values,
|
||||
// thus data contains two uints (numerator and denominator) for a single number.
|
||||
type ifdEntry struct {
|
||||
tag int
|
||||
datatype int
|
||||
data []uint32
|
||||
}
|
||||
|
||||
func (e ifdEntry) putData(p []byte) {
|
||||
for _, d := range e.data {
|
||||
switch e.datatype {
|
||||
case dtByte, dtASCII:
|
||||
p[0] = byte(d)
|
||||
p = p[1:]
|
||||
case dtShort:
|
||||
enc.PutUint16(p, uint16(d))
|
||||
p = p[2:]
|
||||
case dtLong, dtRational:
|
||||
enc.PutUint32(p, uint32(d))
|
||||
p = p[4:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type byTag []ifdEntry
|
||||
|
||||
func (d byTag) Len() int { return len(d) }
|
||||
func (d byTag) Less(i, j int) bool { return d[i].tag < d[j].tag }
|
||||
func (d byTag) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
|
||||
|
||||
func encodeGray(w io.Writer, pix []uint8, dx, dy, stride int, predictor bool) error {
|
||||
if !predictor {
|
||||
return writePix(w, pix, dy, dx, stride)
|
||||
}
|
||||
buf := make([]byte, dx)
|
||||
for y := 0; y < dy; y++ {
|
||||
min := y*stride + 0
|
||||
max := y*stride + dx
|
||||
off := 0
|
||||
var v0 uint8
|
||||
for i := min; i < max; i++ {
|
||||
v1 := pix[i]
|
||||
buf[off] = v1 - v0
|
||||
v0 = v1
|
||||
off++
|
||||
}
|
||||
if _, err := w.Write(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func encodeGray16(w io.Writer, pix []uint8, dx, dy, stride int, predictor bool) error {
|
||||
buf := make([]byte, dx*2)
|
||||
for y := 0; y < dy; y++ {
|
||||
min := y*stride + 0
|
||||
max := y*stride + dx*2
|
||||
off := 0
|
||||
var v0 uint16
|
||||
for i := min; i < max; i += 2 {
|
||||
// An image.Gray16's Pix is in big-endian order.
|
||||
v1 := uint16(pix[i])<<8 | uint16(pix[i+1])
|
||||
if predictor {
|
||||
v0, v1 = v1, v1-v0
|
||||
}
|
||||
// We only write little-endian TIFF files.
|
||||
buf[off+0] = byte(v1)
|
||||
buf[off+1] = byte(v1 >> 8)
|
||||
off += 2
|
||||
}
|
||||
if _, err := w.Write(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func encodeRGBA(w io.Writer, pix []uint8, dx, dy, stride int, predictor bool) error {
|
||||
if !predictor {
|
||||
return writePix(w, pix, dy, dx*4, stride)
|
||||
}
|
||||
buf := make([]byte, dx*4)
|
||||
for y := 0; y < dy; y++ {
|
||||
min := y*stride + 0
|
||||
max := y*stride + dx*4
|
||||
off := 0
|
||||
var r0, g0, b0, a0 uint8
|
||||
for i := min; i < max; i += 4 {
|
||||
r1, g1, b1, a1 := pix[i+0], pix[i+1], pix[i+2], pix[i+3]
|
||||
buf[off+0] = r1 - r0
|
||||
buf[off+1] = g1 - g0
|
||||
buf[off+2] = b1 - b0
|
||||
buf[off+3] = a1 - a0
|
||||
off += 4
|
||||
r0, g0, b0, a0 = r1, g1, b1, a1
|
||||
}
|
||||
if _, err := w.Write(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func encodeRGBA64(w io.Writer, pix []uint8, dx, dy, stride int, predictor bool) error {
|
||||
buf := make([]byte, dx*8)
|
||||
for y := 0; y < dy; y++ {
|
||||
min := y*stride + 0
|
||||
max := y*stride + dx*8
|
||||
off := 0
|
||||
var r0, g0, b0, a0 uint16
|
||||
for i := min; i < max; i += 8 {
|
||||
// An image.RGBA64's Pix is in big-endian order.
|
||||
r1 := uint16(pix[i+0])<<8 | uint16(pix[i+1])
|
||||
g1 := uint16(pix[i+2])<<8 | uint16(pix[i+3])
|
||||
b1 := uint16(pix[i+4])<<8 | uint16(pix[i+5])
|
||||
a1 := uint16(pix[i+6])<<8 | uint16(pix[i+7])
|
||||
if predictor {
|
||||
r0, r1 = r1, r1-r0
|
||||
g0, g1 = g1, g1-g0
|
||||
b0, b1 = b1, b1-b0
|
||||
a0, a1 = a1, a1-a0
|
||||
}
|
||||
// We only write little-endian TIFF files.
|
||||
buf[off+0] = byte(r1)
|
||||
buf[off+1] = byte(r1 >> 8)
|
||||
buf[off+2] = byte(g1)
|
||||
buf[off+3] = byte(g1 >> 8)
|
||||
buf[off+4] = byte(b1)
|
||||
buf[off+5] = byte(b1 >> 8)
|
||||
buf[off+6] = byte(a1)
|
||||
buf[off+7] = byte(a1 >> 8)
|
||||
off += 8
|
||||
}
|
||||
if _, err := w.Write(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func encode(w io.Writer, m image.Image, predictor bool) error {
|
||||
bounds := m.Bounds()
|
||||
buf := make([]byte, 4*bounds.Dx())
|
||||
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||
off := 0
|
||||
if predictor {
|
||||
var r0, g0, b0, a0 uint8
|
||||
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
||||
r, g, b, a := m.At(x, y).RGBA()
|
||||
r1 := uint8(r >> 8)
|
||||
g1 := uint8(g >> 8)
|
||||
b1 := uint8(b >> 8)
|
||||
a1 := uint8(a >> 8)
|
||||
buf[off+0] = r1 - r0
|
||||
buf[off+1] = g1 - g0
|
||||
buf[off+2] = b1 - b0
|
||||
buf[off+3] = a1 - a0
|
||||
off += 4
|
||||
r0, g0, b0, a0 = r1, g1, b1, a1
|
||||
}
|
||||
} else {
|
||||
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
||||
r, g, b, a := m.At(x, y).RGBA()
|
||||
buf[off+0] = uint8(r >> 8)
|
||||
buf[off+1] = uint8(g >> 8)
|
||||
buf[off+2] = uint8(b >> 8)
|
||||
buf[off+3] = uint8(a >> 8)
|
||||
off += 4
|
||||
}
|
||||
}
|
||||
if _, err := w.Write(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// writePix writes the internal byte array of an image to w. It is less general
|
||||
// but much faster then encode. writePix is used when pix directly
|
||||
// corresponds to one of the TIFF image types.
|
||||
func writePix(w io.Writer, pix []byte, nrows, length, stride int) error {
|
||||
if length == stride {
|
||||
_, err := w.Write(pix[:nrows*length])
|
||||
return err
|
||||
}
|
||||
for ; nrows > 0; nrows-- {
|
||||
if _, err := w.Write(pix[:length]); err != nil {
|
||||
return err
|
||||
}
|
||||
pix = pix[stride:]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeIFD(w io.Writer, ifdOffset int, d []ifdEntry) error {
|
||||
var buf [ifdLen]byte
|
||||
// Make space for "pointer area" containing IFD entry data
|
||||
// longer than 4 bytes.
|
||||
parea := make([]byte, 1024)
|
||||
pstart := ifdOffset + ifdLen*len(d) + 6
|
||||
var o int // Current offset in parea.
|
||||
|
||||
// The IFD has to be written with the tags in ascending order.
|
||||
sort.Sort(byTag(d))
|
||||
|
||||
// Write the number of entries in this IFD.
|
||||
if err := binary.Write(w, enc, uint16(len(d))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, ent := range d {
|
||||
enc.PutUint16(buf[0:2], uint16(ent.tag))
|
||||
enc.PutUint16(buf[2:4], uint16(ent.datatype))
|
||||
count := uint32(len(ent.data))
|
||||
if ent.datatype == dtRational {
|
||||
count /= 2
|
||||
}
|
||||
enc.PutUint32(buf[4:8], count)
|
||||
datalen := int(count * lengths[ent.datatype])
|
||||
if datalen <= 4 {
|
||||
ent.putData(buf[8:12])
|
||||
} else {
|
||||
if (o + datalen) > len(parea) {
|
||||
newlen := len(parea) + 1024
|
||||
for (o + datalen) > newlen {
|
||||
newlen += 1024
|
||||
}
|
||||
newarea := make([]byte, newlen)
|
||||
copy(newarea, parea)
|
||||
parea = newarea
|
||||
}
|
||||
ent.putData(parea[o : o+datalen])
|
||||
enc.PutUint32(buf[8:12], uint32(pstart+o))
|
||||
o += datalen
|
||||
}
|
||||
if _, err := w.Write(buf[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// The IFD ends with the offset of the next IFD in the file,
|
||||
// or zero if it is the last one (page 14).
|
||||
if err := binary.Write(w, enc, uint32(0)); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := w.Write(parea[:o])
|
||||
return err
|
||||
}
|
||||
|
||||
// Options are the encoding parameters.
|
||||
type Options struct {
|
||||
// Compression is the type of compression used.
|
||||
Compression CompressionType
|
||||
// Predictor determines whether a differencing predictor is used;
|
||||
// if true, instead of each pixel's color, the color difference to the
|
||||
// preceding one is saved. This improves the compression for certain
|
||||
// types of images and compressors. For example, it works well for
|
||||
// photos with Deflate compression.
|
||||
Predictor bool
|
||||
}
|
||||
|
||||
// Encode writes the image m to w. opt determines the options used for
|
||||
// encoding, such as the compression type. If opt is nil, an uncompressed
|
||||
// image is written.
|
||||
func Encode(w io.Writer, m image.Image, opt *Options) error {
|
||||
d := m.Bounds().Size()
|
||||
|
||||
compression := uint32(cNone)
|
||||
predictor := false
|
||||
if opt != nil {
|
||||
compression = opt.Compression.specValue()
|
||||
// The predictor field is only used with LZW. See page 64 of the spec.
|
||||
predictor = opt.Predictor && compression == cLZW
|
||||
}
|
||||
|
||||
_, err := io.WriteString(w, leHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Compressed data is written into a buffer first, so that we
|
||||
// know the compressed size.
|
||||
var buf bytes.Buffer
|
||||
// dst holds the destination for the pixel data of the image --
|
||||
// either w or a writer to buf.
|
||||
var dst io.Writer
|
||||
// imageLen is the length of the pixel data in bytes.
|
||||
// The offset of the IFD is imageLen + 8 header bytes.
|
||||
var imageLen int
|
||||
|
||||
switch compression {
|
||||
case cNone:
|
||||
dst = w
|
||||
// Write IFD offset before outputting pixel data.
|
||||
switch m.(type) {
|
||||
case *image.Paletted:
|
||||
imageLen = d.X * d.Y * 1
|
||||
case *image.Gray:
|
||||
imageLen = d.X * d.Y * 1
|
||||
case *image.Gray16:
|
||||
imageLen = d.X * d.Y * 2
|
||||
case *image.RGBA64:
|
||||
imageLen = d.X * d.Y * 8
|
||||
case *image.NRGBA64:
|
||||
imageLen = d.X * d.Y * 8
|
||||
default:
|
||||
imageLen = d.X * d.Y * 4
|
||||
}
|
||||
err = binary.Write(w, enc, uint32(imageLen+8))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case cDeflate:
|
||||
dst = zlib.NewWriter(&buf)
|
||||
default:
|
||||
return errors.New("tiff: unsupported compression")
|
||||
}
|
||||
|
||||
pr := uint32(prNone)
|
||||
photometricInterpretation := uint32(pRGB)
|
||||
samplesPerPixel := uint32(4)
|
||||
bitsPerSample := []uint32{8, 8, 8, 8}
|
||||
extraSamples := uint32(0)
|
||||
colorMap := []uint32{}
|
||||
|
||||
if predictor {
|
||||
pr = prHorizontal
|
||||
}
|
||||
switch m := m.(type) {
|
||||
case *image.Paletted:
|
||||
photometricInterpretation = pPaletted
|
||||
samplesPerPixel = 1
|
||||
bitsPerSample = []uint32{8}
|
||||
colorMap = make([]uint32, 256*3)
|
||||
for i := 0; i < 256 && i < len(m.Palette); i++ {
|
||||
r, g, b, _ := m.Palette[i].RGBA()
|
||||
colorMap[i+0*256] = uint32(r)
|
||||
colorMap[i+1*256] = uint32(g)
|
||||
colorMap[i+2*256] = uint32(b)
|
||||
}
|
||||
err = encodeGray(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
|
||||
case *image.Gray:
|
||||
photometricInterpretation = pBlackIsZero
|
||||
samplesPerPixel = 1
|
||||
bitsPerSample = []uint32{8}
|
||||
err = encodeGray(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
|
||||
case *image.Gray16:
|
||||
photometricInterpretation = pBlackIsZero
|
||||
samplesPerPixel = 1
|
||||
bitsPerSample = []uint32{16}
|
||||
err = encodeGray16(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
|
||||
case *image.NRGBA:
|
||||
extraSamples = 2 // Unassociated alpha.
|
||||
err = encodeRGBA(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
|
||||
case *image.NRGBA64:
|
||||
extraSamples = 2 // Unassociated alpha.
|
||||
bitsPerSample = []uint32{16, 16, 16, 16}
|
||||
err = encodeRGBA64(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
|
||||
case *image.RGBA:
|
||||
extraSamples = 1 // Associated alpha.
|
||||
err = encodeRGBA(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
|
||||
case *image.RGBA64:
|
||||
extraSamples = 1 // Associated alpha.
|
||||
bitsPerSample = []uint32{16, 16, 16, 16}
|
||||
err = encodeRGBA64(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
|
||||
default:
|
||||
extraSamples = 1 // Associated alpha.
|
||||
err = encode(dst, m, predictor)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if compression != cNone {
|
||||
if err = dst.(io.Closer).Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
imageLen = buf.Len()
|
||||
if err = binary.Write(w, enc, uint32(imageLen+8)); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = buf.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
ifd := []ifdEntry{
|
||||
{tImageWidth, dtShort, []uint32{uint32(d.X)}},
|
||||
{tImageLength, dtShort, []uint32{uint32(d.Y)}},
|
||||
{tBitsPerSample, dtShort, bitsPerSample},
|
||||
{tCompression, dtShort, []uint32{compression}},
|
||||
{tPhotometricInterpretation, dtShort, []uint32{photometricInterpretation}},
|
||||
{tStripOffsets, dtLong, []uint32{8}},
|
||||
{tSamplesPerPixel, dtShort, []uint32{samplesPerPixel}},
|
||||
{tRowsPerStrip, dtShort, []uint32{uint32(d.Y)}},
|
||||
{tStripByteCounts, dtLong, []uint32{uint32(imageLen)}},
|
||||
// There is currently no support for storing the image
|
||||
// resolution, so give a bogus value of 72x72 dpi.
|
||||
{tXResolution, dtRational, []uint32{72, 1}},
|
||||
{tYResolution, dtRational, []uint32{72, 1}},
|
||||
{tResolutionUnit, dtShort, []uint32{resPerInch}},
|
||||
}
|
||||
if pr != prNone {
|
||||
ifd = append(ifd, ifdEntry{tPredictor, dtShort, []uint32{pr}})
|
||||
}
|
||||
if len(colorMap) != 0 {
|
||||
ifd = append(ifd, ifdEntry{tColorMap, dtShort, colorMap})
|
||||
}
|
||||
if extraSamples > 0 {
|
||||
ifd = append(ifd, ifdEntry{tExtraSamples, dtShort, []uint32{extraSamples}})
|
||||
}
|
||||
|
||||
return writeIFD(w, imageLen+8, ifd)
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !appengine && gc && !noasm
|
||||
|
||||
package vector
|
||||
|
||||
func haveSSE4_1() bool
|
||||
|
||||
var haveAccumulateSIMD = haveSSE4_1()
|
||||
|
||||
//go:noescape
|
||||
func fixedAccumulateOpOverSIMD(dst []uint8, src []uint32)
|
||||
|
||||
//go:noescape
|
||||
func fixedAccumulateOpSrcSIMD(dst []uint8, src []uint32)
|
||||
|
||||
//go:noescape
|
||||
func fixedAccumulateMaskSIMD(buf []uint32)
|
||||
|
||||
//go:noescape
|
||||
func floatingAccumulateOpOverSIMD(dst []uint8, src []float32)
|
||||
|
||||
//go:noescape
|
||||
func floatingAccumulateOpSrcSIMD(dst []uint8, src []float32)
|
||||
|
||||
//go:noescape
|
||||
func floatingAccumulateMaskSIMD(dst []uint32, src []float32)
|
||||
+1028
File diff suppressed because it is too large
Load Diff
+16
@@ -0,0 +1,16 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !amd64 || appengine || !gc || noasm
|
||||
|
||||
package vector
|
||||
|
||||
const haveAccumulateSIMD = false
|
||||
|
||||
func fixedAccumulateOpOverSIMD(dst []uint8, src []uint32) {}
|
||||
func fixedAccumulateOpSrcSIMD(dst []uint8, src []uint32) {}
|
||||
func fixedAccumulateMaskSIMD(buf []uint32) {}
|
||||
func floatingAccumulateOpOverSIMD(dst []uint8, src []float32) {}
|
||||
func floatingAccumulateOpSrcSIMD(dst []uint8, src []float32) {}
|
||||
func floatingAccumulateMaskSIMD(dst []uint32, src []float32) {}
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !noasm
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// fl is short for floating point math. fx is short for fixed point math.
|
||||
|
||||
DATA flAlmost65536<>+0x00(SB)/8, $0x477fffff477fffff
|
||||
DATA flAlmost65536<>+0x08(SB)/8, $0x477fffff477fffff
|
||||
DATA flOne<>+0x00(SB)/8, $0x3f8000003f800000
|
||||
DATA flOne<>+0x08(SB)/8, $0x3f8000003f800000
|
||||
DATA flSignMask<>+0x00(SB)/8, $0x7fffffff7fffffff
|
||||
DATA flSignMask<>+0x08(SB)/8, $0x7fffffff7fffffff
|
||||
|
||||
// scatterAndMulBy0x101 is a PSHUFB mask that brings the low four bytes of an
|
||||
// XMM register to the low byte of that register's four uint32 values. It
|
||||
// duplicates those bytes, effectively multiplying each uint32 by 0x101.
|
||||
//
|
||||
// It transforms a little-endian 16-byte XMM value from
|
||||
// ijkl????????????
|
||||
// to
|
||||
// ii00jj00kk00ll00
|
||||
DATA scatterAndMulBy0x101<>+0x00(SB)/8, $0x8080010180800000
|
||||
DATA scatterAndMulBy0x101<>+0x08(SB)/8, $0x8080030380800202
|
||||
|
||||
// gather is a PSHUFB mask that brings the second-lowest byte of the XMM
|
||||
// register's four uint32 values to the low four bytes of that register.
|
||||
//
|
||||
// It transforms a little-endian 16-byte XMM value from
|
||||
// ?i???j???k???l??
|
||||
// to
|
||||
// ijkl000000000000
|
||||
DATA gather<>+0x00(SB)/8, $0x808080800d090501
|
||||
DATA gather<>+0x08(SB)/8, $0x8080808080808080
|
||||
|
||||
DATA fxAlmost65536<>+0x00(SB)/8, $0x0000ffff0000ffff
|
||||
DATA fxAlmost65536<>+0x08(SB)/8, $0x0000ffff0000ffff
|
||||
DATA inverseFFFF<>+0x00(SB)/8, $0x8000800180008001
|
||||
DATA inverseFFFF<>+0x08(SB)/8, $0x8000800180008001
|
||||
|
||||
GLOBL flAlmost65536<>(SB), (NOPTR+RODATA), $16
|
||||
GLOBL flOne<>(SB), (NOPTR+RODATA), $16
|
||||
GLOBL flSignMask<>(SB), (NOPTR+RODATA), $16
|
||||
GLOBL scatterAndMulBy0x101<>(SB), (NOPTR+RODATA), $16
|
||||
GLOBL gather<>(SB), (NOPTR+RODATA), $16
|
||||
GLOBL fxAlmost65536<>(SB), (NOPTR+RODATA), $16
|
||||
GLOBL inverseFFFF<>(SB), (NOPTR+RODATA), $16
|
||||
|
||||
// func haveSSE4_1() bool
|
||||
TEXT ·haveSSE4_1(SB), NOSPLIT, $0
|
||||
MOVQ $1, AX
|
||||
CPUID
|
||||
SHRQ $19, CX
|
||||
ANDQ $1, CX
|
||||
MOVB CX, ret+0(FP)
|
||||
RET
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// func {{.LongName}}SIMD({{.Args}})
|
||||
//
|
||||
// XMM registers. Variable names are per
|
||||
// https://github.com/google/font-rs/blob/master/src/accumulate.c
|
||||
//
|
||||
// xmm0 scratch
|
||||
// xmm1 x
|
||||
// xmm2 y, z
|
||||
// xmm3 {{.XMM3}}
|
||||
// xmm4 {{.XMM4}}
|
||||
// xmm5 {{.XMM5}}
|
||||
// xmm6 {{.XMM6}}
|
||||
// xmm7 offset
|
||||
// xmm8 {{.XMM8}}
|
||||
// xmm9 {{.XMM9}}
|
||||
// xmm10 {{.XMM10}}
|
||||
TEXT ·{{.LongName}}SIMD(SB), NOSPLIT, ${{.FrameSize}}-{{.ArgsSize}}
|
||||
{{.LoadArgs}}
|
||||
|
||||
// R10 = len(src) &^ 3
|
||||
// R11 = len(src)
|
||||
MOVQ R10, R11
|
||||
ANDQ $-4, R10
|
||||
|
||||
{{.Setup}}
|
||||
|
||||
{{.LoadXMMRegs}}
|
||||
|
||||
// offset := XMM(0x00000000 repeated four times) // Cumulative sum.
|
||||
XORPS X7, X7
|
||||
|
||||
// i := 0
|
||||
MOVQ $0, R9
|
||||
|
||||
{{.ShortName}}Loop4:
|
||||
// for i < (len(src) &^ 3)
|
||||
CMPQ R9, R10
|
||||
JAE {{.ShortName}}Loop1
|
||||
|
||||
// x = XMM(s0, s1, s2, s3)
|
||||
//
|
||||
// Where s0 is src[i+0], s1 is src[i+1], etc.
|
||||
MOVOU (SI), X1
|
||||
|
||||
// scratch = XMM(0, s0, s1, s2)
|
||||
// x += scratch // yields x == XMM(s0, s0+s1, s1+s2, s2+s3)
|
||||
MOVOU X1, X0
|
||||
PSLLO $4, X0
|
||||
{{.Add}} X0, X1
|
||||
|
||||
// scratch = XMM(0, 0, 0, 0)
|
||||
// scratch = XMM(scratch@0, scratch@0, x@0, x@1) // yields scratch == XMM(0, 0, s0, s0+s1)
|
||||
// x += scratch // yields x == XMM(s0, s0+s1, s0+s1+s2, s0+s1+s2+s3)
|
||||
XORPS X0, X0
|
||||
SHUFPS $0x40, X1, X0
|
||||
{{.Add}} X0, X1
|
||||
|
||||
// x += offset
|
||||
{{.Add}} X7, X1
|
||||
|
||||
{{.ClampAndScale}}
|
||||
|
||||
{{.ConvertToInt32}}
|
||||
|
||||
{{.Store4}}
|
||||
|
||||
// offset = XMM(x@3, x@3, x@3, x@3)
|
||||
MOVOU X1, X7
|
||||
SHUFPS $0xff, X1, X7
|
||||
|
||||
// i += 4
|
||||
// dst = dst[4:]
|
||||
// src = src[4:]
|
||||
ADDQ $4, R9
|
||||
ADDQ ${{.DstElemSize4}}, DI
|
||||
ADDQ $16, SI
|
||||
JMP {{.ShortName}}Loop4
|
||||
|
||||
{{.ShortName}}Loop1:
|
||||
// for i < len(src)
|
||||
CMPQ R9, R11
|
||||
JAE {{.ShortName}}End
|
||||
|
||||
// x = src[i] + offset
|
||||
MOVL (SI), X1
|
||||
{{.Add}} X7, X1
|
||||
|
||||
{{.ClampAndScale}}
|
||||
|
||||
{{.ConvertToInt32}}
|
||||
|
||||
{{.Store1}}
|
||||
|
||||
// offset = x
|
||||
MOVOU X1, X7
|
||||
|
||||
// i += 1
|
||||
// dst = dst[1:]
|
||||
// src = src[1:]
|
||||
ADDQ $1, R9
|
||||
ADDQ ${{.DstElemSize1}}, DI
|
||||
ADDQ $4, SI
|
||||
JMP {{.ShortName}}Loop1
|
||||
|
||||
{{.ShortName}}End:
|
||||
RET
|
||||
+330
@@ -0,0 +1,330 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package vector
|
||||
|
||||
// This file contains a fixed point math implementation of the vector
|
||||
// graphics rasterizer.
|
||||
|
||||
const (
|
||||
// ϕ is the number of binary digits after the fixed point.
|
||||
//
|
||||
// For example, if ϕ == 10 (and int1ϕ is based on the int32 type) then we
|
||||
// are using 22.10 fixed point math.
|
||||
//
|
||||
// When changing this number, also change the assembly code (search for ϕ
|
||||
// in the .s files).
|
||||
ϕ = 9
|
||||
|
||||
fxOne int1ϕ = 1 << ϕ
|
||||
fxOneAndAHalf int1ϕ = 1<<ϕ + 1<<(ϕ-1)
|
||||
fxOneMinusIota int1ϕ = 1<<ϕ - 1 // Used for rounding up.
|
||||
)
|
||||
|
||||
// int1ϕ is a signed fixed-point number with 1*ϕ binary digits after the fixed
|
||||
// point.
|
||||
type int1ϕ int32
|
||||
|
||||
// int2ϕ is a signed fixed-point number with 2*ϕ binary digits after the fixed
|
||||
// point.
|
||||
//
|
||||
// The Rasterizer's bufU32 field, nominally of type []uint32 (since that slice
|
||||
// is also used by other code), can be thought of as a []int2ϕ during the
|
||||
// fixedLineTo method. Lines of code that are actually like:
|
||||
//
|
||||
// buf[i] += uint32(etc) // buf has type []uint32.
|
||||
//
|
||||
// can be thought of as
|
||||
//
|
||||
// buf[i] += int2ϕ(etc) // buf has type []int2ϕ.
|
||||
type int2ϕ int32
|
||||
|
||||
func fixedMax(x, y int1ϕ) int1ϕ {
|
||||
if x > y {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
func fixedMin(x, y int1ϕ) int1ϕ {
|
||||
if x < y {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
func fixedFloor(x int1ϕ) int32 { return int32(x >> ϕ) }
|
||||
func fixedCeil(x int1ϕ) int32 { return int32((x + fxOneMinusIota) >> ϕ) }
|
||||
|
||||
func (z *Rasterizer) fixedLineTo(bx, by float32) {
|
||||
ax, ay := z.penX, z.penY
|
||||
z.penX, z.penY = bx, by
|
||||
dir := int1ϕ(1)
|
||||
if ay > by {
|
||||
dir, ax, ay, bx, by = -1, bx, by, ax, ay
|
||||
}
|
||||
// Horizontal line segments yield no change in coverage. Almost horizontal
|
||||
// segments would yield some change, in ideal math, but the computation
|
||||
// further below, involving 1 / (by - ay), is unstable in fixed point math,
|
||||
// so we treat the segment as if it was perfectly horizontal.
|
||||
if by-ay <= 0.000001 {
|
||||
return
|
||||
}
|
||||
dxdy := (bx - ax) / (by - ay)
|
||||
|
||||
ayϕ := int1ϕ(ay * float32(fxOne))
|
||||
byϕ := int1ϕ(by * float32(fxOne))
|
||||
|
||||
x := int1ϕ(ax * float32(fxOne))
|
||||
y := fixedFloor(ayϕ)
|
||||
yMax := fixedCeil(byϕ)
|
||||
if yMax > int32(z.size.Y) {
|
||||
yMax = int32(z.size.Y)
|
||||
}
|
||||
width := int32(z.size.X)
|
||||
|
||||
for ; y < yMax; y++ {
|
||||
dy := fixedMin(int1ϕ(y+1)<<ϕ, byϕ) - fixedMax(int1ϕ(y)<<ϕ, ayϕ)
|
||||
xNext := x + int1ϕ(float32(dy)*dxdy)
|
||||
if y < 0 {
|
||||
x = xNext
|
||||
continue
|
||||
}
|
||||
buf := z.bufU32[y*width:]
|
||||
d := dy * dir // d ranges up to ±1<<(1*ϕ).
|
||||
x0, x1 := x, xNext
|
||||
if x > xNext {
|
||||
x0, x1 = x1, x0
|
||||
}
|
||||
x0i := fixedFloor(x0)
|
||||
x0Floor := int1ϕ(x0i) << ϕ
|
||||
x1i := fixedCeil(x1)
|
||||
x1Ceil := int1ϕ(x1i) << ϕ
|
||||
|
||||
if x1i <= x0i+1 {
|
||||
xmf := (x+xNext)>>1 - x0Floor
|
||||
if i := clamp(x0i+0, width); i < uint(len(buf)) {
|
||||
buf[i] += uint32(d * (fxOne - xmf))
|
||||
}
|
||||
if i := clamp(x0i+1, width); i < uint(len(buf)) {
|
||||
buf[i] += uint32(d * xmf)
|
||||
}
|
||||
} else {
|
||||
oneOverS := x1 - x0
|
||||
twoOverS := 2 * oneOverS
|
||||
x0f := x0 - x0Floor
|
||||
oneMinusX0f := fxOne - x0f
|
||||
oneMinusX0fSquared := oneMinusX0f * oneMinusX0f
|
||||
x1f := x1 - x1Ceil + fxOne
|
||||
x1fSquared := x1f * x1f
|
||||
|
||||
// These next two variables are unused, as rounding errors are
|
||||
// minimized when we delay the division by oneOverS for as long as
|
||||
// possible. These lines of code (and the "In ideal math" comments
|
||||
// below) are commented out instead of deleted in order to aid the
|
||||
// comparison with the floating point version of the rasterizer.
|
||||
//
|
||||
// a0 := ((oneMinusX0f * oneMinusX0f) >> 1) / oneOverS
|
||||
// am := ((x1f * x1f) >> 1) / oneOverS
|
||||
|
||||
if i := clamp(x0i, width); i < uint(len(buf)) {
|
||||
// In ideal math: buf[i] += uint32(d * a0)
|
||||
D := oneMinusX0fSquared // D ranges up to ±1<<(2*ϕ).
|
||||
D *= d // D ranges up to ±1<<(3*ϕ).
|
||||
D /= twoOverS
|
||||
buf[i] += uint32(D)
|
||||
}
|
||||
|
||||
if x1i == x0i+2 {
|
||||
if i := clamp(x0i+1, width); i < uint(len(buf)) {
|
||||
// In ideal math: buf[i] += uint32(d * (fxOne - a0 - am))
|
||||
//
|
||||
// (x1i == x0i+2) and (twoOverS == 2 * (x1 - x0)) implies
|
||||
// that twoOverS ranges up to +1<<(1*ϕ+2).
|
||||
D := twoOverS<<ϕ - oneMinusX0fSquared - x1fSquared // D ranges up to ±1<<(2*ϕ+2).
|
||||
D *= d // D ranges up to ±1<<(3*ϕ+2).
|
||||
D /= twoOverS
|
||||
buf[i] += uint32(D)
|
||||
}
|
||||
} else {
|
||||
// This is commented out for the same reason as a0 and am.
|
||||
//
|
||||
// a1 := ((fxOneAndAHalf - x0f) << ϕ) / oneOverS
|
||||
|
||||
if i := clamp(x0i+1, width); i < uint(len(buf)) {
|
||||
// In ideal math:
|
||||
// buf[i] += uint32(d * (a1 - a0))
|
||||
// or equivalently (but better in non-ideal, integer math,
|
||||
// with respect to rounding errors),
|
||||
// buf[i] += uint32(A * d / twoOverS)
|
||||
// where
|
||||
// A = (a1 - a0) * twoOverS
|
||||
// = a1*twoOverS - a0*twoOverS
|
||||
// Noting that twoOverS/oneOverS equals 2, substituting for
|
||||
// a0 and then a1, given above, yields:
|
||||
// A = a1*twoOverS - oneMinusX0fSquared
|
||||
// = (fxOneAndAHalf-x0f)<<(ϕ+1) - oneMinusX0fSquared
|
||||
// = fxOneAndAHalf<<(ϕ+1) - x0f<<(ϕ+1) - oneMinusX0fSquared
|
||||
//
|
||||
// This is a positive number minus two non-negative
|
||||
// numbers. For an upper bound on A, the positive number is
|
||||
// P = fxOneAndAHalf<<(ϕ+1)
|
||||
// < (2*fxOne)<<(ϕ+1)
|
||||
// = fxOne<<(ϕ+2)
|
||||
// = 1<<(2*ϕ+2)
|
||||
//
|
||||
// For a lower bound on A, the two non-negative numbers are
|
||||
// N = x0f<<(ϕ+1) + oneMinusX0fSquared
|
||||
// ≤ x0f<<(ϕ+1) + fxOne*fxOne
|
||||
// = x0f<<(ϕ+1) + 1<<(2*ϕ)
|
||||
// < x0f<<(ϕ+1) + 1<<(2*ϕ+1)
|
||||
// ≤ fxOne<<(ϕ+1) + 1<<(2*ϕ+1)
|
||||
// = 1<<(2*ϕ+1) + 1<<(2*ϕ+1)
|
||||
// = 1<<(2*ϕ+2)
|
||||
//
|
||||
// Thus, A ranges up to ±1<<(2*ϕ+2). It is possible to
|
||||
// derive a tighter bound, but this bound is sufficient to
|
||||
// reason about overflow.
|
||||
D := (fxOneAndAHalf-x0f)<<(ϕ+1) - oneMinusX0fSquared // D ranges up to ±1<<(2*ϕ+2).
|
||||
D *= d // D ranges up to ±1<<(3*ϕ+2).
|
||||
D /= twoOverS
|
||||
buf[i] += uint32(D)
|
||||
}
|
||||
dTimesS := uint32((d << (2 * ϕ)) / oneOverS)
|
||||
for xi := x0i + 2; xi < x1i-1; xi++ {
|
||||
if i := clamp(xi, width); i < uint(len(buf)) {
|
||||
buf[i] += dTimesS
|
||||
}
|
||||
}
|
||||
|
||||
// This is commented out for the same reason as a0 and am.
|
||||
//
|
||||
// a2 := a1 + (int1ϕ(x1i-x0i-3)<<(2*ϕ))/oneOverS
|
||||
|
||||
if i := clamp(x1i-1, width); i < uint(len(buf)) {
|
||||
// In ideal math:
|
||||
// buf[i] += uint32(d * (fxOne - a2 - am))
|
||||
// or equivalently (but better in non-ideal, integer math,
|
||||
// with respect to rounding errors),
|
||||
// buf[i] += uint32(A * d / twoOverS)
|
||||
// where
|
||||
// A = (fxOne - a2 - am) * twoOverS
|
||||
// = twoOverS<<ϕ - a2*twoOverS - am*twoOverS
|
||||
// Noting that twoOverS/oneOverS equals 2, substituting for
|
||||
// am and then a2, given above, yields:
|
||||
// A = twoOverS<<ϕ - a2*twoOverS - x1f*x1f
|
||||
// = twoOverS<<ϕ - a1*twoOverS - (int1ϕ(x1i-x0i-3)<<(2*ϕ))*2 - x1f*x1f
|
||||
// = twoOverS<<ϕ - a1*twoOverS - int1ϕ(x1i-x0i-3)<<(2*ϕ+1) - x1f*x1f
|
||||
// Substituting for a1, given above, yields:
|
||||
// A = twoOverS<<ϕ - ((fxOneAndAHalf-x0f)<<ϕ)*2 - int1ϕ(x1i-x0i-3)<<(2*ϕ+1) - x1f*x1f
|
||||
// = twoOverS<<ϕ - (fxOneAndAHalf-x0f)<<(ϕ+1) - int1ϕ(x1i-x0i-3)<<(2*ϕ+1) - x1f*x1f
|
||||
// = B<<ϕ - x1f*x1f
|
||||
// where
|
||||
// B = twoOverS - (fxOneAndAHalf-x0f)<<1 - int1ϕ(x1i-x0i-3)<<(ϕ+1)
|
||||
// = (x1-x0)<<1 - (fxOneAndAHalf-x0f)<<1 - int1ϕ(x1i-x0i-3)<<(ϕ+1)
|
||||
//
|
||||
// Re-arranging the defintions given above:
|
||||
// x0Floor := int1ϕ(x0i) << ϕ
|
||||
// x0f := x0 - x0Floor
|
||||
// x1Ceil := int1ϕ(x1i) << ϕ
|
||||
// x1f := x1 - x1Ceil + fxOne
|
||||
// combined with fxOne = 1<<ϕ yields:
|
||||
// x0 = x0f + int1ϕ(x0i)<<ϕ
|
||||
// x1 = x1f + int1ϕ(x1i-1)<<ϕ
|
||||
// so that expanding (x1-x0) yields:
|
||||
// B = (x1f-x0f + int1ϕ(x1i-x0i-1)<<ϕ)<<1 - (fxOneAndAHalf-x0f)<<1 - int1ϕ(x1i-x0i-3)<<(ϕ+1)
|
||||
// = (x1f-x0f)<<1 + int1ϕ(x1i-x0i-1)<<(ϕ+1) - (fxOneAndAHalf-x0f)<<1 - int1ϕ(x1i-x0i-3)<<(ϕ+1)
|
||||
// A large part of the second and fourth terms cancel:
|
||||
// B = (x1f-x0f)<<1 - (fxOneAndAHalf-x0f)<<1 - int1ϕ(-2)<<(ϕ+1)
|
||||
// = (x1f-x0f)<<1 - (fxOneAndAHalf-x0f)<<1 + 1<<(ϕ+2)
|
||||
// = (x1f - fxOneAndAHalf)<<1 + 1<<(ϕ+2)
|
||||
// The first term, (x1f - fxOneAndAHalf)<<1, is a negative
|
||||
// number, bounded below by -fxOneAndAHalf<<1, which is
|
||||
// greater than -fxOne<<2, or -1<<(ϕ+2). Thus, B ranges up
|
||||
// to ±1<<(ϕ+2). One final simplification:
|
||||
// B = x1f<<1 + (1<<(ϕ+2) - fxOneAndAHalf<<1)
|
||||
const C = 1<<(ϕ+2) - fxOneAndAHalf<<1
|
||||
D := x1f<<1 + C // D ranges up to ±1<<(1*ϕ+2).
|
||||
D <<= ϕ // D ranges up to ±1<<(2*ϕ+2).
|
||||
D -= x1fSquared // D ranges up to ±1<<(2*ϕ+3).
|
||||
D *= d // D ranges up to ±1<<(3*ϕ+3).
|
||||
D /= twoOverS
|
||||
buf[i] += uint32(D)
|
||||
}
|
||||
}
|
||||
|
||||
if i := clamp(x1i, width); i < uint(len(buf)) {
|
||||
// In ideal math: buf[i] += uint32(d * am)
|
||||
D := x1fSquared // D ranges up to ±1<<(2*ϕ).
|
||||
D *= d // D ranges up to ±1<<(3*ϕ).
|
||||
D /= twoOverS
|
||||
buf[i] += uint32(D)
|
||||
}
|
||||
}
|
||||
|
||||
x = xNext
|
||||
}
|
||||
}
|
||||
|
||||
func fixedAccumulateOpOver(dst []uint8, src []uint32) {
|
||||
// Sanity check that len(dst) >= len(src).
|
||||
if len(dst) < len(src) {
|
||||
return
|
||||
}
|
||||
|
||||
acc := int2ϕ(0)
|
||||
for i, v := range src {
|
||||
acc += int2ϕ(v)
|
||||
a := acc
|
||||
if a < 0 {
|
||||
a = -a
|
||||
}
|
||||
a >>= 2*ϕ - 16
|
||||
if a > 0xffff {
|
||||
a = 0xffff
|
||||
}
|
||||
// This algorithm comes from the standard library's image/draw package.
|
||||
dstA := uint32(dst[i]) * 0x101
|
||||
maskA := uint32(a)
|
||||
outA := dstA*(0xffff-maskA)/0xffff + maskA
|
||||
dst[i] = uint8(outA >> 8)
|
||||
}
|
||||
}
|
||||
|
||||
func fixedAccumulateOpSrc(dst []uint8, src []uint32) {
|
||||
// Sanity check that len(dst) >= len(src).
|
||||
if len(dst) < len(src) {
|
||||
return
|
||||
}
|
||||
|
||||
acc := int2ϕ(0)
|
||||
for i, v := range src {
|
||||
acc += int2ϕ(v)
|
||||
a := acc
|
||||
if a < 0 {
|
||||
a = -a
|
||||
}
|
||||
a >>= 2*ϕ - 8
|
||||
if a > 0xff {
|
||||
a = 0xff
|
||||
}
|
||||
dst[i] = uint8(a)
|
||||
}
|
||||
}
|
||||
|
||||
func fixedAccumulateMask(buf []uint32) {
|
||||
acc := int2ϕ(0)
|
||||
for i, v := range buf {
|
||||
acc += int2ϕ(v)
|
||||
a := acc
|
||||
if a < 0 {
|
||||
a = -a
|
||||
}
|
||||
a >>= 2*ϕ - 16
|
||||
if a > 0xffff {
|
||||
a = 0xffff
|
||||
}
|
||||
buf[i] = uint32(a)
|
||||
}
|
||||
}
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package vector
|
||||
|
||||
// This file contains a floating point math implementation of the vector
|
||||
// graphics rasterizer.
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
func floatingMax(x, y float32) float32 {
|
||||
if x > y {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
func floatingMin(x, y float32) float32 {
|
||||
if x < y {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
func floatingFloor(x float32) int32 { return int32(math.Floor(float64(x))) }
|
||||
func floatingCeil(x float32) int32 { return int32(math.Ceil(float64(x))) }
|
||||
|
||||
func (z *Rasterizer) floatingLineTo(bx, by float32) {
|
||||
ax, ay := z.penX, z.penY
|
||||
z.penX, z.penY = bx, by
|
||||
dir := float32(1)
|
||||
if ay > by {
|
||||
dir, ax, ay, bx, by = -1, bx, by, ax, ay
|
||||
}
|
||||
// Horizontal line segments yield no change in coverage. Almost horizontal
|
||||
// segments would yield some change, in ideal math, but the computation
|
||||
// further below, involving 1 / (by - ay), is unstable in floating point
|
||||
// math, so we treat the segment as if it was perfectly horizontal.
|
||||
if by-ay <= 0.000001 {
|
||||
return
|
||||
}
|
||||
dxdy := (bx - ax) / (by - ay)
|
||||
|
||||
x := ax
|
||||
y := floatingFloor(ay)
|
||||
yMax := floatingCeil(by)
|
||||
if yMax > int32(z.size.Y) {
|
||||
yMax = int32(z.size.Y)
|
||||
}
|
||||
width := int32(z.size.X)
|
||||
|
||||
for ; y < yMax; y++ {
|
||||
dy := floatingMin(float32(y+1), by) - floatingMax(float32(y), ay)
|
||||
|
||||
// The "float32" in expressions like "float32(foo*bar)" here and below
|
||||
// look redundant, since foo and bar already have type float32, but are
|
||||
// explicit in order to disable the compiler's Fused Multiply Add (FMA)
|
||||
// instruction selection, which can improve performance but can result
|
||||
// in different rounding errors in floating point computations.
|
||||
//
|
||||
// This package aims to have bit-exact identical results across all
|
||||
// GOARCHes, and across pure Go code and assembly, so it disables FMA.
|
||||
//
|
||||
// See the discussion at
|
||||
// https://groups.google.com/d/topic/golang-dev/Sti0bl2xUXQ/discussion
|
||||
xNext := x + float32(dy*dxdy)
|
||||
if y < 0 {
|
||||
x = xNext
|
||||
continue
|
||||
}
|
||||
buf := z.bufF32[y*width:]
|
||||
d := float32(dy * dir)
|
||||
x0, x1 := x, xNext
|
||||
if x > xNext {
|
||||
x0, x1 = x1, x0
|
||||
}
|
||||
x0i := floatingFloor(x0)
|
||||
x0Floor := float32(x0i)
|
||||
x1i := floatingCeil(x1)
|
||||
x1Ceil := float32(x1i)
|
||||
|
||||
if x1i <= x0i+1 {
|
||||
xmf := float32(0.5*(x+xNext)) - x0Floor
|
||||
if i := clamp(x0i+0, width); i < uint(len(buf)) {
|
||||
buf[i] += d - float32(d*xmf)
|
||||
}
|
||||
if i := clamp(x0i+1, width); i < uint(len(buf)) {
|
||||
buf[i] += float32(d * xmf)
|
||||
}
|
||||
} else {
|
||||
s := 1 / (x1 - x0)
|
||||
x0f := x0 - x0Floor
|
||||
oneMinusX0f := 1 - x0f
|
||||
a0 := float32(0.5 * s * oneMinusX0f * oneMinusX0f)
|
||||
x1f := x1 - x1Ceil + 1
|
||||
am := float32(0.5 * s * x1f * x1f)
|
||||
|
||||
if i := clamp(x0i, width); i < uint(len(buf)) {
|
||||
buf[i] += float32(d * a0)
|
||||
}
|
||||
|
||||
if x1i == x0i+2 {
|
||||
if i := clamp(x0i+1, width); i < uint(len(buf)) {
|
||||
buf[i] += float32(d * (1 - a0 - am))
|
||||
}
|
||||
} else {
|
||||
a1 := float32(s * (1.5 - x0f))
|
||||
if i := clamp(x0i+1, width); i < uint(len(buf)) {
|
||||
buf[i] += float32(d * (a1 - a0))
|
||||
}
|
||||
dTimesS := float32(d * s)
|
||||
for xi := x0i + 2; xi < x1i-1; xi++ {
|
||||
if i := clamp(xi, width); i < uint(len(buf)) {
|
||||
buf[i] += dTimesS
|
||||
}
|
||||
}
|
||||
a2 := a1 + float32(s*float32(x1i-x0i-3))
|
||||
if i := clamp(x1i-1, width); i < uint(len(buf)) {
|
||||
buf[i] += float32(d * (1 - a2 - am))
|
||||
}
|
||||
}
|
||||
|
||||
if i := clamp(x1i, width); i < uint(len(buf)) {
|
||||
buf[i] += float32(d * am)
|
||||
}
|
||||
}
|
||||
|
||||
x = xNext
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// almost256 scales a floating point value in the range [0, 1] to a uint8
|
||||
// value in the range [0x00, 0xff].
|
||||
//
|
||||
// 255 is too small. Floating point math accumulates rounding errors, so a
|
||||
// fully covered src value that would in ideal math be float32(1) might be
|
||||
// float32(1-ε), and uint8(255 * (1-ε)) would be 0xfe instead of 0xff. The
|
||||
// uint8 conversion rounds to zero, not to nearest.
|
||||
//
|
||||
// 256 is too big. If we multiplied by 256, below, then a fully covered src
|
||||
// value of float32(1) would translate to uint8(256 * 1), which can be 0x00
|
||||
// instead of the maximal value 0xff.
|
||||
//
|
||||
// math.Float32bits(almost256) is 0x437fffff.
|
||||
almost256 = 255.99998
|
||||
|
||||
// almost65536 scales a floating point value in the range [0, 1] to a
|
||||
// uint16 value in the range [0x0000, 0xffff].
|
||||
//
|
||||
// math.Float32bits(almost65536) is 0x477fffff.
|
||||
almost65536 = almost256 * 256
|
||||
)
|
||||
|
||||
func floatingAccumulateOpOver(dst []uint8, src []float32) {
|
||||
// Sanity check that len(dst) >= len(src).
|
||||
if len(dst) < len(src) {
|
||||
return
|
||||
}
|
||||
|
||||
acc := float32(0)
|
||||
for i, v := range src {
|
||||
acc += v
|
||||
a := acc
|
||||
if a < 0 {
|
||||
a = -a
|
||||
}
|
||||
if a > 1 {
|
||||
a = 1
|
||||
}
|
||||
// This algorithm comes from the standard library's image/draw package.
|
||||
dstA := uint32(dst[i]) * 0x101
|
||||
maskA := uint32(almost65536 * a)
|
||||
outA := dstA*(0xffff-maskA)/0xffff + maskA
|
||||
dst[i] = uint8(outA >> 8)
|
||||
}
|
||||
}
|
||||
|
||||
func floatingAccumulateOpSrc(dst []uint8, src []float32) {
|
||||
// Sanity check that len(dst) >= len(src).
|
||||
if len(dst) < len(src) {
|
||||
return
|
||||
}
|
||||
|
||||
acc := float32(0)
|
||||
for i, v := range src {
|
||||
acc += v
|
||||
a := acc
|
||||
if a < 0 {
|
||||
a = -a
|
||||
}
|
||||
if a > 1 {
|
||||
a = 1
|
||||
}
|
||||
dst[i] = uint8(almost256 * a)
|
||||
}
|
||||
}
|
||||
|
||||
func floatingAccumulateMask(dst []uint32, src []float32) {
|
||||
// Sanity check that len(dst) >= len(src).
|
||||
if len(dst) < len(src) {
|
||||
return
|
||||
}
|
||||
|
||||
acc := float32(0)
|
||||
for i, v := range src {
|
||||
acc += v
|
||||
a := acc
|
||||
if a < 0 {
|
||||
a = -a
|
||||
}
|
||||
if a > 1 {
|
||||
a = 1
|
||||
}
|
||||
dst[i] = uint32(almost65536 * a)
|
||||
}
|
||||
}
|
||||
+472
@@ -0,0 +1,472 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:generate go run gen.go
|
||||
//go:generate asmfmt -w acc_amd64.s
|
||||
|
||||
// asmfmt is https://github.com/klauspost/asmfmt
|
||||
|
||||
// Package vector provides a rasterizer for 2-D vector graphics.
|
||||
package vector // import "golang.org/x/image/vector"
|
||||
|
||||
// The rasterizer's design follows
|
||||
// https://medium.com/@raphlinus/inside-the-fastest-font-renderer-in-the-world-75ae5270c445
|
||||
//
|
||||
// Proof of concept code is in
|
||||
// https://github.com/google/font-go
|
||||
//
|
||||
// See also:
|
||||
// http://nothings.org/gamedev/rasterize/
|
||||
// http://projects.tuxee.net/cl-vectors/section-the-cl-aa-algorithm
|
||||
// https://people.gnome.org/~mathieu/libart/internals.html#INTERNALS-SCANLINE
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"math"
|
||||
)
|
||||
|
||||
// floatingPointMathThreshold is the width or height above which the rasterizer
|
||||
// chooses to used floating point math instead of fixed point math.
|
||||
//
|
||||
// Both implementations of line segmentation rasterization (see raster_fixed.go
|
||||
// and raster_floating.go) implement the same algorithm (in ideal, infinite
|
||||
// precision math) but they perform differently in practice. The fixed point
|
||||
// math version is roughtly 1.25x faster (on GOARCH=amd64) on the benchmarks,
|
||||
// but at sufficiently large scales, the computations will overflow and hence
|
||||
// show rendering artifacts. The floating point math version has more
|
||||
// consistent quality over larger scales, but it is significantly slower.
|
||||
//
|
||||
// This constant determines when to use the faster implementation and when to
|
||||
// use the better quality implementation.
|
||||
//
|
||||
// The rationale for this particular value is that TestRasterizePolygon in
|
||||
// vector_test.go checks the rendering quality of polygon edges at various
|
||||
// angles, inscribed in a circle of diameter 512. It may be that a higher value
|
||||
// would still produce acceptable quality, but 512 seems to work.
|
||||
const floatingPointMathThreshold = 512
|
||||
|
||||
func lerp(t, px, py, qx, qy float32) (x, y float32) {
|
||||
return px + t*(qx-px), py + t*(qy-py)
|
||||
}
|
||||
|
||||
func clamp(i, width int32) uint {
|
||||
if i < 0 {
|
||||
return 0
|
||||
}
|
||||
if i < width {
|
||||
return uint(i)
|
||||
}
|
||||
return uint(width)
|
||||
}
|
||||
|
||||
// NewRasterizer returns a new Rasterizer whose rendered mask image is bounded
|
||||
// by the given width and height.
|
||||
func NewRasterizer(w, h int) *Rasterizer {
|
||||
z := &Rasterizer{}
|
||||
z.Reset(w, h)
|
||||
return z
|
||||
}
|
||||
|
||||
// Raster is a 2-D vector graphics rasterizer.
|
||||
//
|
||||
// The zero value is usable, in that it is a Rasterizer whose rendered mask
|
||||
// image has zero width and zero height. Call Reset to change its bounds.
|
||||
type Rasterizer struct {
|
||||
// bufXxx are buffers of float32 or uint32 values, holding either the
|
||||
// individual or cumulative area values.
|
||||
//
|
||||
// We don't actually need both values at any given time, and to conserve
|
||||
// memory, the integration of the individual to the cumulative could modify
|
||||
// the buffer in place. In other words, we could use a single buffer, say
|
||||
// of type []uint32, and add some math.Float32bits and math.Float32frombits
|
||||
// calls to satisfy the compiler's type checking. As of Go 1.7, though,
|
||||
// there is a performance penalty between:
|
||||
// bufF32[i] += x
|
||||
// and
|
||||
// bufU32[i] = math.Float32bits(x + math.Float32frombits(bufU32[i]))
|
||||
//
|
||||
// See golang.org/issue/17220 for some discussion.
|
||||
bufF32 []float32
|
||||
bufU32 []uint32
|
||||
|
||||
useFloatingPointMath bool
|
||||
|
||||
size image.Point
|
||||
firstX float32
|
||||
firstY float32
|
||||
penX float32
|
||||
penY float32
|
||||
|
||||
// DrawOp is the operator used for the Draw method.
|
||||
//
|
||||
// The zero value is draw.Over.
|
||||
DrawOp draw.Op
|
||||
|
||||
// TODO: an exported field equivalent to the mask point in the
|
||||
// draw.DrawMask function in the stdlib image/draw package?
|
||||
}
|
||||
|
||||
// Reset resets a Rasterizer as if it was just returned by NewRasterizer.
|
||||
//
|
||||
// This includes setting z.DrawOp to draw.Over.
|
||||
func (z *Rasterizer) Reset(w, h int) {
|
||||
z.size = image.Point{w, h}
|
||||
z.firstX = 0
|
||||
z.firstY = 0
|
||||
z.penX = 0
|
||||
z.penY = 0
|
||||
z.DrawOp = draw.Over
|
||||
|
||||
z.setUseFloatingPointMath(w > floatingPointMathThreshold || h > floatingPointMathThreshold)
|
||||
}
|
||||
|
||||
func (z *Rasterizer) setUseFloatingPointMath(b bool) {
|
||||
z.useFloatingPointMath = b
|
||||
|
||||
// Make z.bufF32 or z.bufU32 large enough to hold width * height samples.
|
||||
if z.useFloatingPointMath {
|
||||
if n := z.size.X * z.size.Y; n > cap(z.bufF32) {
|
||||
z.bufF32 = make([]float32, n)
|
||||
} else {
|
||||
z.bufF32 = z.bufF32[:n]
|
||||
for i := range z.bufF32 {
|
||||
z.bufF32[i] = 0
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if n := z.size.X * z.size.Y; n > cap(z.bufU32) {
|
||||
z.bufU32 = make([]uint32, n)
|
||||
} else {
|
||||
z.bufU32 = z.bufU32[:n]
|
||||
for i := range z.bufU32 {
|
||||
z.bufU32[i] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Size returns the width and height passed to NewRasterizer or Reset.
|
||||
func (z *Rasterizer) Size() image.Point {
|
||||
return z.size
|
||||
}
|
||||
|
||||
// Bounds returns the rectangle from (0, 0) to the width and height passed to
|
||||
// NewRasterizer or Reset.
|
||||
func (z *Rasterizer) Bounds() image.Rectangle {
|
||||
return image.Rectangle{Max: z.size}
|
||||
}
|
||||
|
||||
// Pen returns the location of the path-drawing pen: the last argument to the
|
||||
// most recent XxxTo call.
|
||||
func (z *Rasterizer) Pen() (x, y float32) {
|
||||
return z.penX, z.penY
|
||||
}
|
||||
|
||||
// ClosePath closes the current path.
|
||||
func (z *Rasterizer) ClosePath() {
|
||||
z.LineTo(z.firstX, z.firstY)
|
||||
}
|
||||
|
||||
// MoveTo starts a new path and moves the pen to (ax, ay).
|
||||
//
|
||||
// The coordinates are allowed to be out of the Rasterizer's bounds.
|
||||
func (z *Rasterizer) MoveTo(ax, ay float32) {
|
||||
z.firstX = ax
|
||||
z.firstY = ay
|
||||
z.penX = ax
|
||||
z.penY = ay
|
||||
}
|
||||
|
||||
// LineTo adds a line segment, from the pen to (bx, by), and moves the pen to
|
||||
// (bx, by).
|
||||
//
|
||||
// The coordinates are allowed to be out of the Rasterizer's bounds.
|
||||
func (z *Rasterizer) LineTo(bx, by float32) {
|
||||
if z.useFloatingPointMath {
|
||||
z.floatingLineTo(bx, by)
|
||||
} else {
|
||||
z.fixedLineTo(bx, by)
|
||||
}
|
||||
}
|
||||
|
||||
// QuadTo adds a quadratic Bézier segment, from the pen via (bx, by) to (cx,
|
||||
// cy), and moves the pen to (cx, cy).
|
||||
//
|
||||
// The coordinates are allowed to be out of the Rasterizer's bounds.
|
||||
func (z *Rasterizer) QuadTo(bx, by, cx, cy float32) {
|
||||
ax, ay := z.penX, z.penY
|
||||
devsq := devSquared(ax, ay, bx, by, cx, cy)
|
||||
if devsq >= 0.333 {
|
||||
const tol = 3
|
||||
n := 1 + int(math.Sqrt(math.Sqrt(tol*float64(devsq))))
|
||||
t, nInv := float32(0), 1/float32(n)
|
||||
for i := 0; i < n-1; i++ {
|
||||
t += nInv
|
||||
abx, aby := lerp(t, ax, ay, bx, by)
|
||||
bcx, bcy := lerp(t, bx, by, cx, cy)
|
||||
z.LineTo(lerp(t, abx, aby, bcx, bcy))
|
||||
}
|
||||
}
|
||||
z.LineTo(cx, cy)
|
||||
}
|
||||
|
||||
// CubeTo adds a cubic Bézier segment, from the pen via (bx, by) and (cx, cy)
|
||||
// to (dx, dy), and moves the pen to (dx, dy).
|
||||
//
|
||||
// The coordinates are allowed to be out of the Rasterizer's bounds.
|
||||
func (z *Rasterizer) CubeTo(bx, by, cx, cy, dx, dy float32) {
|
||||
ax, ay := z.penX, z.penY
|
||||
devsq := devSquared(ax, ay, bx, by, dx, dy)
|
||||
if devsqAlt := devSquared(ax, ay, cx, cy, dx, dy); devsq < devsqAlt {
|
||||
devsq = devsqAlt
|
||||
}
|
||||
if devsq >= 0.333 {
|
||||
const tol = 3
|
||||
n := 1 + int(math.Sqrt(math.Sqrt(tol*float64(devsq))))
|
||||
t, nInv := float32(0), 1/float32(n)
|
||||
for i := 0; i < n-1; i++ {
|
||||
t += nInv
|
||||
abx, aby := lerp(t, ax, ay, bx, by)
|
||||
bcx, bcy := lerp(t, bx, by, cx, cy)
|
||||
cdx, cdy := lerp(t, cx, cy, dx, dy)
|
||||
abcx, abcy := lerp(t, abx, aby, bcx, bcy)
|
||||
bcdx, bcdy := lerp(t, bcx, bcy, cdx, cdy)
|
||||
z.LineTo(lerp(t, abcx, abcy, bcdx, bcdy))
|
||||
}
|
||||
}
|
||||
z.LineTo(dx, dy)
|
||||
}
|
||||
|
||||
// devSquared returns a measure of how curvy the sequence (ax, ay) to (bx, by)
|
||||
// to (cx, cy) is. It determines how many line segments will approximate a
|
||||
// Bézier curve segment.
|
||||
//
|
||||
// http://lists.nongnu.org/archive/html/freetype-devel/2016-08/msg00080.html
|
||||
// gives the rationale for this evenly spaced heuristic instead of a recursive
|
||||
// de Casteljau approach:
|
||||
//
|
||||
// The reason for the subdivision by n is that I expect the "flatness"
|
||||
// computation to be semi-expensive (it's done once rather than on each
|
||||
// potential subdivision) and also because you'll often get fewer subdivisions.
|
||||
// Taking a circular arc as a simplifying assumption (ie a spherical cow),
|
||||
// where I get n, a recursive approach would get 2^⌈lg n⌉, which, if I haven't
|
||||
// made any horrible mistakes, is expected to be 33% more in the limit.
|
||||
func devSquared(ax, ay, bx, by, cx, cy float32) float32 {
|
||||
devx := ax - 2*bx + cx
|
||||
devy := ay - 2*by + cy
|
||||
return devx*devx + devy*devy
|
||||
}
|
||||
|
||||
// Draw implements the Drawer interface from the standard library's image/draw
|
||||
// package.
|
||||
//
|
||||
// The vector paths previously added via the XxxTo calls become the mask for
|
||||
// drawing src onto dst.
|
||||
func (z *Rasterizer) Draw(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) {
|
||||
// TODO: adjust r and sp (and mp?) if src.Bounds() doesn't contain
|
||||
// r.Add(sp.Sub(r.Min)).
|
||||
|
||||
if src, ok := src.(*image.Uniform); ok {
|
||||
srcR, srcG, srcB, srcA := src.RGBA()
|
||||
switch dst := dst.(type) {
|
||||
case *image.Alpha:
|
||||
// Fast path for glyph rendering.
|
||||
if srcA == 0xffff {
|
||||
if z.DrawOp == draw.Over {
|
||||
z.rasterizeDstAlphaSrcOpaqueOpOver(dst, r)
|
||||
} else {
|
||||
z.rasterizeDstAlphaSrcOpaqueOpSrc(dst, r)
|
||||
}
|
||||
return
|
||||
}
|
||||
case *image.RGBA:
|
||||
if z.DrawOp == draw.Over {
|
||||
z.rasterizeDstRGBASrcUniformOpOver(dst, r, srcR, srcG, srcB, srcA)
|
||||
} else {
|
||||
z.rasterizeDstRGBASrcUniformOpSrc(dst, r, srcR, srcG, srcB, srcA)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if z.DrawOp == draw.Over {
|
||||
z.rasterizeOpOver(dst, r, src, sp)
|
||||
} else {
|
||||
z.rasterizeOpSrc(dst, r, src, sp)
|
||||
}
|
||||
}
|
||||
|
||||
func (z *Rasterizer) accumulateMask() {
|
||||
if z.useFloatingPointMath {
|
||||
if n := z.size.X * z.size.Y; n > cap(z.bufU32) {
|
||||
z.bufU32 = make([]uint32, n)
|
||||
} else {
|
||||
z.bufU32 = z.bufU32[:n]
|
||||
}
|
||||
if haveAccumulateSIMD {
|
||||
floatingAccumulateMaskSIMD(z.bufU32, z.bufF32)
|
||||
} else {
|
||||
floatingAccumulateMask(z.bufU32, z.bufF32)
|
||||
}
|
||||
} else {
|
||||
if haveAccumulateSIMD {
|
||||
fixedAccumulateMaskSIMD(z.bufU32)
|
||||
} else {
|
||||
fixedAccumulateMask(z.bufU32)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (z *Rasterizer) rasterizeDstAlphaSrcOpaqueOpOver(dst *image.Alpha, r image.Rectangle) {
|
||||
// TODO: non-zero vs even-odd winding?
|
||||
if r == dst.Bounds() && r == z.Bounds() {
|
||||
// We bypass the z.accumulateMask step and convert straight from
|
||||
// z.bufF32 or z.bufU32 to dst.Pix.
|
||||
if z.useFloatingPointMath {
|
||||
if haveAccumulateSIMD {
|
||||
floatingAccumulateOpOverSIMD(dst.Pix, z.bufF32)
|
||||
} else {
|
||||
floatingAccumulateOpOver(dst.Pix, z.bufF32)
|
||||
}
|
||||
} else {
|
||||
if haveAccumulateSIMD {
|
||||
fixedAccumulateOpOverSIMD(dst.Pix, z.bufU32)
|
||||
} else {
|
||||
fixedAccumulateOpOver(dst.Pix, z.bufU32)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
z.accumulateMask()
|
||||
pix := dst.Pix[dst.PixOffset(r.Min.X, r.Min.Y):]
|
||||
for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ {
|
||||
for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ {
|
||||
ma := z.bufU32[y*z.size.X+x]
|
||||
i := y*dst.Stride + x
|
||||
|
||||
// This formula is like rasterizeOpOver's, simplified for the
|
||||
// concrete dst type and opaque src assumption.
|
||||
a := 0xffff - ma
|
||||
pix[i] = uint8((uint32(pix[i])*0x101*a/0xffff + ma) >> 8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (z *Rasterizer) rasterizeDstAlphaSrcOpaqueOpSrc(dst *image.Alpha, r image.Rectangle) {
|
||||
// TODO: non-zero vs even-odd winding?
|
||||
if r == dst.Bounds() && r == z.Bounds() {
|
||||
// We bypass the z.accumulateMask step and convert straight from
|
||||
// z.bufF32 or z.bufU32 to dst.Pix.
|
||||
if z.useFloatingPointMath {
|
||||
if haveAccumulateSIMD {
|
||||
floatingAccumulateOpSrcSIMD(dst.Pix, z.bufF32)
|
||||
} else {
|
||||
floatingAccumulateOpSrc(dst.Pix, z.bufF32)
|
||||
}
|
||||
} else {
|
||||
if haveAccumulateSIMD {
|
||||
fixedAccumulateOpSrcSIMD(dst.Pix, z.bufU32)
|
||||
} else {
|
||||
fixedAccumulateOpSrc(dst.Pix, z.bufU32)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
z.accumulateMask()
|
||||
pix := dst.Pix[dst.PixOffset(r.Min.X, r.Min.Y):]
|
||||
for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ {
|
||||
for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ {
|
||||
ma := z.bufU32[y*z.size.X+x]
|
||||
|
||||
// This formula is like rasterizeOpSrc's, simplified for the
|
||||
// concrete dst type and opaque src assumption.
|
||||
pix[y*dst.Stride+x] = uint8(ma >> 8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (z *Rasterizer) rasterizeDstRGBASrcUniformOpOver(dst *image.RGBA, r image.Rectangle, sr, sg, sb, sa uint32) {
|
||||
z.accumulateMask()
|
||||
pix := dst.Pix[dst.PixOffset(r.Min.X, r.Min.Y):]
|
||||
for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ {
|
||||
for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ {
|
||||
ma := z.bufU32[y*z.size.X+x]
|
||||
|
||||
// This formula is like rasterizeOpOver's, simplified for the
|
||||
// concrete dst type and uniform src assumption.
|
||||
a := 0xffff - (sa * ma / 0xffff)
|
||||
i := y*dst.Stride + 4*x
|
||||
pix[i+0] = uint8(((uint32(pix[i+0])*0x101*a + sr*ma) / 0xffff) >> 8)
|
||||
pix[i+1] = uint8(((uint32(pix[i+1])*0x101*a + sg*ma) / 0xffff) >> 8)
|
||||
pix[i+2] = uint8(((uint32(pix[i+2])*0x101*a + sb*ma) / 0xffff) >> 8)
|
||||
pix[i+3] = uint8(((uint32(pix[i+3])*0x101*a + sa*ma) / 0xffff) >> 8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (z *Rasterizer) rasterizeDstRGBASrcUniformOpSrc(dst *image.RGBA, r image.Rectangle, sr, sg, sb, sa uint32) {
|
||||
z.accumulateMask()
|
||||
pix := dst.Pix[dst.PixOffset(r.Min.X, r.Min.Y):]
|
||||
for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ {
|
||||
for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ {
|
||||
ma := z.bufU32[y*z.size.X+x]
|
||||
|
||||
// This formula is like rasterizeOpSrc's, simplified for the
|
||||
// concrete dst type and uniform src assumption.
|
||||
i := y*dst.Stride + 4*x
|
||||
pix[i+0] = uint8((sr * ma / 0xffff) >> 8)
|
||||
pix[i+1] = uint8((sg * ma / 0xffff) >> 8)
|
||||
pix[i+2] = uint8((sb * ma / 0xffff) >> 8)
|
||||
pix[i+3] = uint8((sa * ma / 0xffff) >> 8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (z *Rasterizer) rasterizeOpOver(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) {
|
||||
z.accumulateMask()
|
||||
out := color.RGBA64{}
|
||||
outc := color.Color(&out)
|
||||
for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ {
|
||||
for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ {
|
||||
sr, sg, sb, sa := src.At(sp.X+x, sp.Y+y).RGBA()
|
||||
ma := z.bufU32[y*z.size.X+x]
|
||||
|
||||
// This algorithm comes from the standard library's image/draw
|
||||
// package.
|
||||
dr, dg, db, da := dst.At(r.Min.X+x, r.Min.Y+y).RGBA()
|
||||
a := 0xffff - (sa * ma / 0xffff)
|
||||
out.R = uint16((dr*a + sr*ma) / 0xffff)
|
||||
out.G = uint16((dg*a + sg*ma) / 0xffff)
|
||||
out.B = uint16((db*a + sb*ma) / 0xffff)
|
||||
out.A = uint16((da*a + sa*ma) / 0xffff)
|
||||
|
||||
dst.Set(r.Min.X+x, r.Min.Y+y, outc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (z *Rasterizer) rasterizeOpSrc(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) {
|
||||
z.accumulateMask()
|
||||
out := color.RGBA64{}
|
||||
outc := color.Color(&out)
|
||||
for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ {
|
||||
for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ {
|
||||
sr, sg, sb, sa := src.At(sp.X+x, sp.Y+y).RGBA()
|
||||
ma := z.bufU32[y*z.size.X+x]
|
||||
|
||||
// This algorithm comes from the standard library's image/draw
|
||||
// package.
|
||||
out.R = uint16(sr * ma / 0xffff)
|
||||
out.G = uint16(sg * ma / 0xffff)
|
||||
out.B = uint16(sb * ma / 0xffff)
|
||||
out.A = uint16(sa * ma / 0xffff)
|
||||
|
||||
dst.Set(r.Min.X+x, r.Min.Y+y, outc)
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
Copyright 2009 The Go Authors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google LLC nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.18
|
||||
|
||||
package idna
|
||||
|
||||
// Transitional processing is disabled by default in Go 1.18.
|
||||
// https://golang.org/issue/47510
|
||||
const transitionalLookup = false
|
||||
+769
@@ -0,0 +1,769 @@
|
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.10
|
||||
|
||||
// Package idna implements IDNA2008 using the compatibility processing
|
||||
// defined by UTS (Unicode Technical Standard) #46, which defines a standard to
|
||||
// deal with the transition from IDNA2003.
|
||||
//
|
||||
// IDNA2008 (Internationalized Domain Names for Applications), is defined in RFC
|
||||
// 5890, RFC 5891, RFC 5892, RFC 5893 and RFC 5894.
|
||||
// UTS #46 is defined in https://www.unicode.org/reports/tr46.
|
||||
// See https://unicode.org/cldr/utility/idna.jsp for a visualization of the
|
||||
// differences between these two standards.
|
||||
package idna // import "golang.org/x/net/idna"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/secure/bidirule"
|
||||
"golang.org/x/text/unicode/bidi"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
// NOTE: Unlike common practice in Go APIs, the functions will return a
|
||||
// sanitized domain name in case of errors. Browsers sometimes use a partially
|
||||
// evaluated string as lookup.
|
||||
// TODO: the current error handling is, in my opinion, the least opinionated.
|
||||
// Other strategies are also viable, though:
|
||||
// Option 1) Return an empty string in case of error, but allow the user to
|
||||
// specify explicitly which errors to ignore.
|
||||
// Option 2) Return the partially evaluated string if it is itself a valid
|
||||
// string, otherwise return the empty string in case of error.
|
||||
// Option 3) Option 1 and 2.
|
||||
// Option 4) Always return an empty string for now and implement Option 1 as
|
||||
// needed, and document that the return string may not be empty in case of
|
||||
// error in the future.
|
||||
// I think Option 1 is best, but it is quite opinionated.
|
||||
|
||||
// ToASCII is a wrapper for Punycode.ToASCII.
|
||||
func ToASCII(s string) (string, error) {
|
||||
return Punycode.process(s, true)
|
||||
}
|
||||
|
||||
// ToUnicode is a wrapper for Punycode.ToUnicode.
|
||||
func ToUnicode(s string) (string, error) {
|
||||
return Punycode.process(s, false)
|
||||
}
|
||||
|
||||
// An Option configures a Profile at creation time.
|
||||
type Option func(*options)
|
||||
|
||||
// Transitional sets a Profile to use the Transitional mapping as defined in UTS
|
||||
// #46. This will cause, for example, "ß" to be mapped to "ss". Using the
|
||||
// transitional mapping provides a compromise between IDNA2003 and IDNA2008
|
||||
// compatibility. It is used by some browsers when resolving domain names. This
|
||||
// option is only meaningful if combined with MapForLookup.
|
||||
func Transitional(transitional bool) Option {
|
||||
return func(o *options) { o.transitional = transitional }
|
||||
}
|
||||
|
||||
// VerifyDNSLength sets whether a Profile should fail if any of the IDN parts
|
||||
// are longer than allowed by the RFC.
|
||||
//
|
||||
// This option corresponds to the VerifyDnsLength flag in UTS #46.
|
||||
func VerifyDNSLength(verify bool) Option {
|
||||
return func(o *options) { o.verifyDNSLength = verify }
|
||||
}
|
||||
|
||||
// RemoveLeadingDots removes leading label separators. Leading runes that map to
|
||||
// dots, such as U+3002 IDEOGRAPHIC FULL STOP, are removed as well.
|
||||
func RemoveLeadingDots(remove bool) Option {
|
||||
return func(o *options) { o.removeLeadingDots = remove }
|
||||
}
|
||||
|
||||
// ValidateLabels sets whether to check the mandatory label validation criteria
|
||||
// as defined in Section 5.4 of RFC 5891. This includes testing for correct use
|
||||
// of hyphens ('-'), normalization, validity of runes, and the context rules.
|
||||
// In particular, ValidateLabels also sets the CheckHyphens and CheckJoiners flags
|
||||
// in UTS #46.
|
||||
func ValidateLabels(enable bool) Option {
|
||||
return func(o *options) {
|
||||
// Don't override existing mappings, but set one that at least checks
|
||||
// normalization if it is not set.
|
||||
if o.mapping == nil && enable {
|
||||
o.mapping = normalize
|
||||
}
|
||||
o.trie = trie
|
||||
o.checkJoiners = enable
|
||||
o.checkHyphens = enable
|
||||
if enable {
|
||||
o.fromPuny = validateFromPunycode
|
||||
} else {
|
||||
o.fromPuny = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CheckHyphens sets whether to check for correct use of hyphens ('-') in
|
||||
// labels. Most web browsers do not have this option set, since labels such as
|
||||
// "r3---sn-apo3qvuoxuxbt-j5pe" are in common use.
|
||||
//
|
||||
// This option corresponds to the CheckHyphens flag in UTS #46.
|
||||
func CheckHyphens(enable bool) Option {
|
||||
return func(o *options) { o.checkHyphens = enable }
|
||||
}
|
||||
|
||||
// CheckJoiners sets whether to check the ContextJ rules as defined in Appendix
|
||||
// A of RFC 5892, concerning the use of joiner runes.
|
||||
//
|
||||
// This option corresponds to the CheckJoiners flag in UTS #46.
|
||||
func CheckJoiners(enable bool) Option {
|
||||
return func(o *options) {
|
||||
o.trie = trie
|
||||
o.checkJoiners = enable
|
||||
}
|
||||
}
|
||||
|
||||
// StrictDomainName limits the set of permissible ASCII characters to those
|
||||
// allowed in domain names as defined in RFC 1034 (A-Z, a-z, 0-9 and the
|
||||
// hyphen). This is set by default for MapForLookup and ValidateForRegistration,
|
||||
// but is only useful if ValidateLabels is set.
|
||||
//
|
||||
// This option is useful, for instance, for browsers that allow characters
|
||||
// outside this range, for example a '_' (U+005F LOW LINE). See
|
||||
// http://www.rfc-editor.org/std/std3.txt for more details.
|
||||
//
|
||||
// This option corresponds to the UseSTD3ASCIIRules flag in UTS #46.
|
||||
func StrictDomainName(use bool) Option {
|
||||
return func(o *options) { o.useSTD3Rules = use }
|
||||
}
|
||||
|
||||
// NOTE: the following options pull in tables. The tables should not be linked
|
||||
// in as long as the options are not used.
|
||||
|
||||
// BidiRule enables the Bidi rule as defined in RFC 5893. Any application
|
||||
// that relies on proper validation of labels should include this rule.
|
||||
//
|
||||
// This option corresponds to the CheckBidi flag in UTS #46.
|
||||
func BidiRule() Option {
|
||||
return func(o *options) { o.bidirule = bidirule.ValidString }
|
||||
}
|
||||
|
||||
// ValidateForRegistration sets validation options to verify that a given IDN is
|
||||
// properly formatted for registration as defined by Section 4 of RFC 5891.
|
||||
func ValidateForRegistration() Option {
|
||||
return func(o *options) {
|
||||
o.mapping = validateRegistration
|
||||
StrictDomainName(true)(o)
|
||||
ValidateLabels(true)(o)
|
||||
VerifyDNSLength(true)(o)
|
||||
BidiRule()(o)
|
||||
}
|
||||
}
|
||||
|
||||
// MapForLookup sets validation and mapping options such that a given IDN is
|
||||
// transformed for domain name lookup according to the requirements set out in
|
||||
// Section 5 of RFC 5891. The mappings follow the recommendations of RFC 5894,
|
||||
// RFC 5895 and UTS 46. It does not add the Bidi Rule. Use the BidiRule option
|
||||
// to add this check.
|
||||
//
|
||||
// The mappings include normalization and mapping case, width and other
|
||||
// compatibility mappings.
|
||||
func MapForLookup() Option {
|
||||
return func(o *options) {
|
||||
o.mapping = validateAndMap
|
||||
StrictDomainName(true)(o)
|
||||
ValidateLabels(true)(o)
|
||||
}
|
||||
}
|
||||
|
||||
type options struct {
|
||||
transitional bool
|
||||
useSTD3Rules bool
|
||||
checkHyphens bool
|
||||
checkJoiners bool
|
||||
verifyDNSLength bool
|
||||
removeLeadingDots bool
|
||||
|
||||
trie *idnaTrie
|
||||
|
||||
// fromPuny calls validation rules when converting A-labels to U-labels.
|
||||
fromPuny func(p *Profile, s string) error
|
||||
|
||||
// mapping implements a validation and mapping step as defined in RFC 5895
|
||||
// or UTS 46, tailored to, for example, domain registration or lookup.
|
||||
mapping func(p *Profile, s string) (mapped string, isBidi bool, err error)
|
||||
|
||||
// bidirule, if specified, checks whether s conforms to the Bidi Rule
|
||||
// defined in RFC 5893.
|
||||
bidirule func(s string) bool
|
||||
}
|
||||
|
||||
// A Profile defines the configuration of an IDNA mapper.
|
||||
type Profile struct {
|
||||
options
|
||||
}
|
||||
|
||||
func apply(o *options, opts []Option) {
|
||||
for _, f := range opts {
|
||||
f(o)
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a new Profile.
|
||||
//
|
||||
// With no options, the returned Profile is the most permissive and equals the
|
||||
// Punycode Profile. Options can be passed to further restrict the Profile. The
|
||||
// MapForLookup and ValidateForRegistration options set a collection of options,
|
||||
// for lookup and registration purposes respectively, which can be tailored by
|
||||
// adding more fine-grained options, where later options override earlier
|
||||
// options.
|
||||
func New(o ...Option) *Profile {
|
||||
p := &Profile{}
|
||||
apply(&p.options, o)
|
||||
return p
|
||||
}
|
||||
|
||||
// ToASCII converts a domain or domain label to its ASCII form. For example,
|
||||
// ToASCII("bücher.example.com") is "xn--bcher-kva.example.com", and
|
||||
// ToASCII("golang") is "golang". If an error is encountered it will return
|
||||
// an error and a (partially) processed result.
|
||||
func (p *Profile) ToASCII(s string) (string, error) {
|
||||
return p.process(s, true)
|
||||
}
|
||||
|
||||
// ToUnicode converts a domain or domain label to its Unicode form. For example,
|
||||
// ToUnicode("xn--bcher-kva.example.com") is "bücher.example.com", and
|
||||
// ToUnicode("golang") is "golang". If an error is encountered it will return
|
||||
// an error and a (partially) processed result.
|
||||
func (p *Profile) ToUnicode(s string) (string, error) {
|
||||
pp := *p
|
||||
pp.transitional = false
|
||||
return pp.process(s, false)
|
||||
}
|
||||
|
||||
// String reports a string with a description of the profile for debugging
|
||||
// purposes. The string format may change with different versions.
|
||||
func (p *Profile) String() string {
|
||||
s := ""
|
||||
if p.transitional {
|
||||
s = "Transitional"
|
||||
} else {
|
||||
s = "NonTransitional"
|
||||
}
|
||||
if p.useSTD3Rules {
|
||||
s += ":UseSTD3Rules"
|
||||
}
|
||||
if p.checkHyphens {
|
||||
s += ":CheckHyphens"
|
||||
}
|
||||
if p.checkJoiners {
|
||||
s += ":CheckJoiners"
|
||||
}
|
||||
if p.verifyDNSLength {
|
||||
s += ":VerifyDNSLength"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
var (
|
||||
// Punycode is a Profile that does raw punycode processing with a minimum
|
||||
// of validation.
|
||||
Punycode *Profile = punycode
|
||||
|
||||
// Lookup is the recommended profile for looking up domain names, according
|
||||
// to Section 5 of RFC 5891. The exact configuration of this profile may
|
||||
// change over time.
|
||||
Lookup *Profile = lookup
|
||||
|
||||
// Display is the recommended profile for displaying domain names.
|
||||
// The configuration of this profile may change over time.
|
||||
Display *Profile = display
|
||||
|
||||
// Registration is the recommended profile for checking whether a given
|
||||
// IDN is valid for registration, according to Section 4 of RFC 5891.
|
||||
Registration *Profile = registration
|
||||
|
||||
punycode = &Profile{}
|
||||
lookup = &Profile{options{
|
||||
transitional: transitionalLookup,
|
||||
useSTD3Rules: true,
|
||||
checkHyphens: true,
|
||||
checkJoiners: true,
|
||||
trie: trie,
|
||||
fromPuny: validateFromPunycode,
|
||||
mapping: validateAndMap,
|
||||
bidirule: bidirule.ValidString,
|
||||
}}
|
||||
display = &Profile{options{
|
||||
useSTD3Rules: true,
|
||||
checkHyphens: true,
|
||||
checkJoiners: true,
|
||||
trie: trie,
|
||||
fromPuny: validateFromPunycode,
|
||||
mapping: validateAndMap,
|
||||
bidirule: bidirule.ValidString,
|
||||
}}
|
||||
registration = &Profile{options{
|
||||
useSTD3Rules: true,
|
||||
verifyDNSLength: true,
|
||||
checkHyphens: true,
|
||||
checkJoiners: true,
|
||||
trie: trie,
|
||||
fromPuny: validateFromPunycode,
|
||||
mapping: validateRegistration,
|
||||
bidirule: bidirule.ValidString,
|
||||
}}
|
||||
|
||||
// TODO: profiles
|
||||
// Register: recommended for approving domain names: don't do any mappings
|
||||
// but rather reject on invalid input. Bundle or block deviation characters.
|
||||
)
|
||||
|
||||
type labelError struct{ label, code_ string }
|
||||
|
||||
func (e labelError) code() string { return e.code_ }
|
||||
func (e labelError) Error() string {
|
||||
return fmt.Sprintf("idna: invalid label %q", e.label)
|
||||
}
|
||||
|
||||
type runeError rune
|
||||
|
||||
func (e runeError) code() string { return "P1" }
|
||||
func (e runeError) Error() string {
|
||||
return fmt.Sprintf("idna: disallowed rune %U", e)
|
||||
}
|
||||
|
||||
// process implements the algorithm described in section 4 of UTS #46,
|
||||
// see https://www.unicode.org/reports/tr46.
|
||||
func (p *Profile) process(s string, toASCII bool) (string, error) {
|
||||
var err error
|
||||
var isBidi bool
|
||||
if p.mapping != nil {
|
||||
s, isBidi, err = p.mapping(p, s)
|
||||
}
|
||||
// Remove leading empty labels.
|
||||
if p.removeLeadingDots {
|
||||
for ; len(s) > 0 && s[0] == '.'; s = s[1:] {
|
||||
}
|
||||
}
|
||||
// TODO: allow for a quick check of the tables data.
|
||||
// It seems like we should only create this error on ToASCII, but the
|
||||
// UTS 46 conformance tests suggests we should always check this.
|
||||
if err == nil && p.verifyDNSLength && s == "" {
|
||||
err = &labelError{s, "A4"}
|
||||
}
|
||||
labels := labelIter{orig: s}
|
||||
for ; !labels.done(); labels.next() {
|
||||
label := labels.label()
|
||||
if label == "" {
|
||||
// Empty labels are not okay. The label iterator skips the last
|
||||
// label if it is empty.
|
||||
if err == nil && p.verifyDNSLength {
|
||||
err = &labelError{s, "A4"}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(label, acePrefix) {
|
||||
u, err2 := decode(label[len(acePrefix):])
|
||||
if err2 != nil {
|
||||
if err == nil {
|
||||
err = err2
|
||||
}
|
||||
// Spec says keep the old label.
|
||||
continue
|
||||
}
|
||||
isBidi = isBidi || bidirule.DirectionString(u) != bidi.LeftToRight
|
||||
labels.set(u)
|
||||
if err == nil && p.fromPuny != nil {
|
||||
err = p.fromPuny(p, u)
|
||||
}
|
||||
if err == nil {
|
||||
// This should be called on NonTransitional, according to the
|
||||
// spec, but that currently does not have any effect. Use the
|
||||
// original profile to preserve options.
|
||||
err = p.validateLabel(u)
|
||||
}
|
||||
} else if err == nil {
|
||||
err = p.validateLabel(label)
|
||||
}
|
||||
}
|
||||
if isBidi && p.bidirule != nil && err == nil {
|
||||
for labels.reset(); !labels.done(); labels.next() {
|
||||
if !p.bidirule(labels.label()) {
|
||||
err = &labelError{s, "B"}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if toASCII {
|
||||
for labels.reset(); !labels.done(); labels.next() {
|
||||
label := labels.label()
|
||||
if !ascii(label) {
|
||||
a, err2 := encode(acePrefix, label)
|
||||
if err == nil {
|
||||
err = err2
|
||||
}
|
||||
label = a
|
||||
labels.set(a)
|
||||
}
|
||||
n := len(label)
|
||||
if p.verifyDNSLength && err == nil && (n == 0 || n > 63) {
|
||||
err = &labelError{label, "A4"}
|
||||
}
|
||||
}
|
||||
}
|
||||
s = labels.result()
|
||||
if toASCII && p.verifyDNSLength && err == nil {
|
||||
// Compute the length of the domain name minus the root label and its dot.
|
||||
n := len(s)
|
||||
if n > 0 && s[n-1] == '.' {
|
||||
n--
|
||||
}
|
||||
if len(s) < 1 || n > 253 {
|
||||
err = &labelError{s, "A4"}
|
||||
}
|
||||
}
|
||||
return s, err
|
||||
}
|
||||
|
||||
func normalize(p *Profile, s string) (mapped string, isBidi bool, err error) {
|
||||
// TODO: consider first doing a quick check to see if any of these checks
|
||||
// need to be done. This will make it slower in the general case, but
|
||||
// faster in the common case.
|
||||
mapped = norm.NFC.String(s)
|
||||
isBidi = bidirule.DirectionString(mapped) == bidi.RightToLeft
|
||||
return mapped, isBidi, nil
|
||||
}
|
||||
|
||||
func validateRegistration(p *Profile, s string) (idem string, bidi bool, err error) {
|
||||
// TODO: filter need for normalization in loop below.
|
||||
if !norm.NFC.IsNormalString(s) {
|
||||
return s, false, &labelError{s, "V1"}
|
||||
}
|
||||
for i := 0; i < len(s); {
|
||||
v, sz := trie.lookupString(s[i:])
|
||||
if sz == 0 {
|
||||
return s, bidi, runeError(utf8.RuneError)
|
||||
}
|
||||
bidi = bidi || info(v).isBidi(s[i:])
|
||||
// Copy bytes not copied so far.
|
||||
switch p.simplify(info(v).category()) {
|
||||
// TODO: handle the NV8 defined in the Unicode idna data set to allow
|
||||
// for strict conformance to IDNA2008.
|
||||
case valid, deviation:
|
||||
case disallowed, mapped, unknown, ignored:
|
||||
r, _ := utf8.DecodeRuneInString(s[i:])
|
||||
return s, bidi, runeError(r)
|
||||
}
|
||||
i += sz
|
||||
}
|
||||
return s, bidi, nil
|
||||
}
|
||||
|
||||
func (c info) isBidi(s string) bool {
|
||||
if !c.isMapped() {
|
||||
return c&attributesMask == rtl
|
||||
}
|
||||
// TODO: also store bidi info for mapped data. This is possible, but a bit
|
||||
// cumbersome and not for the common case.
|
||||
p, _ := bidi.LookupString(s)
|
||||
switch p.Class() {
|
||||
case bidi.R, bidi.AL, bidi.AN:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func validateAndMap(p *Profile, s string) (vm string, bidi bool, err error) {
|
||||
var (
|
||||
b []byte
|
||||
k int
|
||||
)
|
||||
// combinedInfoBits contains the or-ed bits of all runes. We use this
|
||||
// to derive the mayNeedNorm bit later. This may trigger normalization
|
||||
// overeagerly, but it will not do so in the common case. The end result
|
||||
// is another 10% saving on BenchmarkProfile for the common case.
|
||||
var combinedInfoBits info
|
||||
for i := 0; i < len(s); {
|
||||
v, sz := trie.lookupString(s[i:])
|
||||
if sz == 0 {
|
||||
b = append(b, s[k:i]...)
|
||||
b = append(b, "\ufffd"...)
|
||||
k = len(s)
|
||||
if err == nil {
|
||||
err = runeError(utf8.RuneError)
|
||||
}
|
||||
break
|
||||
}
|
||||
combinedInfoBits |= info(v)
|
||||
bidi = bidi || info(v).isBidi(s[i:])
|
||||
start := i
|
||||
i += sz
|
||||
// Copy bytes not copied so far.
|
||||
switch p.simplify(info(v).category()) {
|
||||
case valid:
|
||||
continue
|
||||
case disallowed:
|
||||
if err == nil {
|
||||
r, _ := utf8.DecodeRuneInString(s[start:])
|
||||
err = runeError(r)
|
||||
}
|
||||
continue
|
||||
case mapped, deviation:
|
||||
b = append(b, s[k:start]...)
|
||||
b = info(v).appendMapping(b, s[start:i])
|
||||
case ignored:
|
||||
b = append(b, s[k:start]...)
|
||||
// drop the rune
|
||||
case unknown:
|
||||
b = append(b, s[k:start]...)
|
||||
b = append(b, "\ufffd"...)
|
||||
}
|
||||
k = i
|
||||
}
|
||||
if k == 0 {
|
||||
// No changes so far.
|
||||
if combinedInfoBits&mayNeedNorm != 0 {
|
||||
s = norm.NFC.String(s)
|
||||
}
|
||||
} else {
|
||||
b = append(b, s[k:]...)
|
||||
if norm.NFC.QuickSpan(b) != len(b) {
|
||||
b = norm.NFC.Bytes(b)
|
||||
}
|
||||
// TODO: the punycode converters require strings as input.
|
||||
s = string(b)
|
||||
}
|
||||
return s, bidi, err
|
||||
}
|
||||
|
||||
// A labelIter allows iterating over domain name labels.
|
||||
type labelIter struct {
|
||||
orig string
|
||||
slice []string
|
||||
curStart int
|
||||
curEnd int
|
||||
i int
|
||||
}
|
||||
|
||||
func (l *labelIter) reset() {
|
||||
l.curStart = 0
|
||||
l.curEnd = 0
|
||||
l.i = 0
|
||||
}
|
||||
|
||||
func (l *labelIter) done() bool {
|
||||
return l.curStart >= len(l.orig)
|
||||
}
|
||||
|
||||
func (l *labelIter) result() string {
|
||||
if l.slice != nil {
|
||||
return strings.Join(l.slice, ".")
|
||||
}
|
||||
return l.orig
|
||||
}
|
||||
|
||||
func (l *labelIter) label() string {
|
||||
if l.slice != nil {
|
||||
return l.slice[l.i]
|
||||
}
|
||||
p := strings.IndexByte(l.orig[l.curStart:], '.')
|
||||
l.curEnd = l.curStart + p
|
||||
if p == -1 {
|
||||
l.curEnd = len(l.orig)
|
||||
}
|
||||
return l.orig[l.curStart:l.curEnd]
|
||||
}
|
||||
|
||||
// next sets the value to the next label. It skips the last label if it is empty.
|
||||
func (l *labelIter) next() {
|
||||
l.i++
|
||||
if l.slice != nil {
|
||||
if l.i >= len(l.slice) || l.i == len(l.slice)-1 && l.slice[l.i] == "" {
|
||||
l.curStart = len(l.orig)
|
||||
}
|
||||
} else {
|
||||
l.curStart = l.curEnd + 1
|
||||
if l.curStart == len(l.orig)-1 && l.orig[l.curStart] == '.' {
|
||||
l.curStart = len(l.orig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *labelIter) set(s string) {
|
||||
if l.slice == nil {
|
||||
l.slice = strings.Split(l.orig, ".")
|
||||
}
|
||||
l.slice[l.i] = s
|
||||
}
|
||||
|
||||
// acePrefix is the ASCII Compatible Encoding prefix.
|
||||
const acePrefix = "xn--"
|
||||
|
||||
func (p *Profile) simplify(cat category) category {
|
||||
switch cat {
|
||||
case disallowedSTD3Mapped:
|
||||
if p.useSTD3Rules {
|
||||
cat = disallowed
|
||||
} else {
|
||||
cat = mapped
|
||||
}
|
||||
case disallowedSTD3Valid:
|
||||
if p.useSTD3Rules {
|
||||
cat = disallowed
|
||||
} else {
|
||||
cat = valid
|
||||
}
|
||||
case deviation:
|
||||
if !p.transitional {
|
||||
cat = valid
|
||||
}
|
||||
case validNV8, validXV8:
|
||||
// TODO: handle V2008
|
||||
cat = valid
|
||||
}
|
||||
return cat
|
||||
}
|
||||
|
||||
func validateFromPunycode(p *Profile, s string) error {
|
||||
if !norm.NFC.IsNormalString(s) {
|
||||
return &labelError{s, "V1"}
|
||||
}
|
||||
// TODO: detect whether string may have to be normalized in the following
|
||||
// loop.
|
||||
for i := 0; i < len(s); {
|
||||
v, sz := trie.lookupString(s[i:])
|
||||
if sz == 0 {
|
||||
return runeError(utf8.RuneError)
|
||||
}
|
||||
if c := p.simplify(info(v).category()); c != valid && c != deviation {
|
||||
return &labelError{s, "V6"}
|
||||
}
|
||||
i += sz
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
zwnj = "\u200c"
|
||||
zwj = "\u200d"
|
||||
)
|
||||
|
||||
type joinState int8
|
||||
|
||||
const (
|
||||
stateStart joinState = iota
|
||||
stateVirama
|
||||
stateBefore
|
||||
stateBeforeVirama
|
||||
stateAfter
|
||||
stateFAIL
|
||||
)
|
||||
|
||||
var joinStates = [][numJoinTypes]joinState{
|
||||
stateStart: {
|
||||
joiningL: stateBefore,
|
||||
joiningD: stateBefore,
|
||||
joinZWNJ: stateFAIL,
|
||||
joinZWJ: stateFAIL,
|
||||
joinVirama: stateVirama,
|
||||
},
|
||||
stateVirama: {
|
||||
joiningL: stateBefore,
|
||||
joiningD: stateBefore,
|
||||
},
|
||||
stateBefore: {
|
||||
joiningL: stateBefore,
|
||||
joiningD: stateBefore,
|
||||
joiningT: stateBefore,
|
||||
joinZWNJ: stateAfter,
|
||||
joinZWJ: stateFAIL,
|
||||
joinVirama: stateBeforeVirama,
|
||||
},
|
||||
stateBeforeVirama: {
|
||||
joiningL: stateBefore,
|
||||
joiningD: stateBefore,
|
||||
joiningT: stateBefore,
|
||||
},
|
||||
stateAfter: {
|
||||
joiningL: stateFAIL,
|
||||
joiningD: stateBefore,
|
||||
joiningT: stateAfter,
|
||||
joiningR: stateStart,
|
||||
joinZWNJ: stateFAIL,
|
||||
joinZWJ: stateFAIL,
|
||||
joinVirama: stateAfter, // no-op as we can't accept joiners here
|
||||
},
|
||||
stateFAIL: {
|
||||
0: stateFAIL,
|
||||
joiningL: stateFAIL,
|
||||
joiningD: stateFAIL,
|
||||
joiningT: stateFAIL,
|
||||
joiningR: stateFAIL,
|
||||
joinZWNJ: stateFAIL,
|
||||
joinZWJ: stateFAIL,
|
||||
joinVirama: stateFAIL,
|
||||
},
|
||||
}
|
||||
|
||||
// validateLabel validates the criteria from Section 4.1. Item 1, 4, and 6 are
|
||||
// already implicitly satisfied by the overall implementation.
|
||||
func (p *Profile) validateLabel(s string) (err error) {
|
||||
if s == "" {
|
||||
if p.verifyDNSLength {
|
||||
return &labelError{s, "A4"}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if p.checkHyphens {
|
||||
if len(s) > 4 && s[2] == '-' && s[3] == '-' {
|
||||
return &labelError{s, "V2"}
|
||||
}
|
||||
if s[0] == '-' || s[len(s)-1] == '-' {
|
||||
return &labelError{s, "V3"}
|
||||
}
|
||||
}
|
||||
if !p.checkJoiners {
|
||||
return nil
|
||||
}
|
||||
trie := p.trie // p.checkJoiners is only set if trie is set.
|
||||
// TODO: merge the use of this in the trie.
|
||||
v, sz := trie.lookupString(s)
|
||||
x := info(v)
|
||||
if x.isModifier() {
|
||||
return &labelError{s, "V5"}
|
||||
}
|
||||
// Quickly return in the absence of zero-width (non) joiners.
|
||||
if strings.Index(s, zwj) == -1 && strings.Index(s, zwnj) == -1 {
|
||||
return nil
|
||||
}
|
||||
st := stateStart
|
||||
for i := 0; ; {
|
||||
jt := x.joinType()
|
||||
if s[i:i+sz] == zwj {
|
||||
jt = joinZWJ
|
||||
} else if s[i:i+sz] == zwnj {
|
||||
jt = joinZWNJ
|
||||
}
|
||||
st = joinStates[st][jt]
|
||||
if x.isViramaModifier() {
|
||||
st = joinStates[st][joinVirama]
|
||||
}
|
||||
if i += sz; i == len(s) {
|
||||
break
|
||||
}
|
||||
v, sz = trie.lookupString(s[i:])
|
||||
x = info(v)
|
||||
}
|
||||
if st == stateFAIL || st == stateAfter {
|
||||
return &labelError{s, "C"}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ascii(s string) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] >= utf8.RuneSelf {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
+717
@@ -0,0 +1,717 @@
|
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.10
|
||||
|
||||
// Package idna implements IDNA2008 using the compatibility processing
|
||||
// defined by UTS (Unicode Technical Standard) #46, which defines a standard to
|
||||
// deal with the transition from IDNA2003.
|
||||
//
|
||||
// IDNA2008 (Internationalized Domain Names for Applications), is defined in RFC
|
||||
// 5890, RFC 5891, RFC 5892, RFC 5893 and RFC 5894.
|
||||
// UTS #46 is defined in https://www.unicode.org/reports/tr46.
|
||||
// See https://unicode.org/cldr/utility/idna.jsp for a visualization of the
|
||||
// differences between these two standards.
|
||||
package idna // import "golang.org/x/net/idna"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/secure/bidirule"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
// NOTE: Unlike common practice in Go APIs, the functions will return a
|
||||
// sanitized domain name in case of errors. Browsers sometimes use a partially
|
||||
// evaluated string as lookup.
|
||||
// TODO: the current error handling is, in my opinion, the least opinionated.
|
||||
// Other strategies are also viable, though:
|
||||
// Option 1) Return an empty string in case of error, but allow the user to
|
||||
// specify explicitly which errors to ignore.
|
||||
// Option 2) Return the partially evaluated string if it is itself a valid
|
||||
// string, otherwise return the empty string in case of error.
|
||||
// Option 3) Option 1 and 2.
|
||||
// Option 4) Always return an empty string for now and implement Option 1 as
|
||||
// needed, and document that the return string may not be empty in case of
|
||||
// error in the future.
|
||||
// I think Option 1 is best, but it is quite opinionated.
|
||||
|
||||
// ToASCII is a wrapper for Punycode.ToASCII.
|
||||
func ToASCII(s string) (string, error) {
|
||||
return Punycode.process(s, true)
|
||||
}
|
||||
|
||||
// ToUnicode is a wrapper for Punycode.ToUnicode.
|
||||
func ToUnicode(s string) (string, error) {
|
||||
return Punycode.process(s, false)
|
||||
}
|
||||
|
||||
// An Option configures a Profile at creation time.
|
||||
type Option func(*options)
|
||||
|
||||
// Transitional sets a Profile to use the Transitional mapping as defined in UTS
|
||||
// #46. This will cause, for example, "ß" to be mapped to "ss". Using the
|
||||
// transitional mapping provides a compromise between IDNA2003 and IDNA2008
|
||||
// compatibility. It is used by some browsers when resolving domain names. This
|
||||
// option is only meaningful if combined with MapForLookup.
|
||||
func Transitional(transitional bool) Option {
|
||||
return func(o *options) { o.transitional = transitional }
|
||||
}
|
||||
|
||||
// VerifyDNSLength sets whether a Profile should fail if any of the IDN parts
|
||||
// are longer than allowed by the RFC.
|
||||
//
|
||||
// This option corresponds to the VerifyDnsLength flag in UTS #46.
|
||||
func VerifyDNSLength(verify bool) Option {
|
||||
return func(o *options) { o.verifyDNSLength = verify }
|
||||
}
|
||||
|
||||
// RemoveLeadingDots removes leading label separators. Leading runes that map to
|
||||
// dots, such as U+3002 IDEOGRAPHIC FULL STOP, are removed as well.
|
||||
func RemoveLeadingDots(remove bool) Option {
|
||||
return func(o *options) { o.removeLeadingDots = remove }
|
||||
}
|
||||
|
||||
// ValidateLabels sets whether to check the mandatory label validation criteria
|
||||
// as defined in Section 5.4 of RFC 5891. This includes testing for correct use
|
||||
// of hyphens ('-'), normalization, validity of runes, and the context rules.
|
||||
// In particular, ValidateLabels also sets the CheckHyphens and CheckJoiners flags
|
||||
// in UTS #46.
|
||||
func ValidateLabels(enable bool) Option {
|
||||
return func(o *options) {
|
||||
// Don't override existing mappings, but set one that at least checks
|
||||
// normalization if it is not set.
|
||||
if o.mapping == nil && enable {
|
||||
o.mapping = normalize
|
||||
}
|
||||
o.trie = trie
|
||||
o.checkJoiners = enable
|
||||
o.checkHyphens = enable
|
||||
if enable {
|
||||
o.fromPuny = validateFromPunycode
|
||||
} else {
|
||||
o.fromPuny = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CheckHyphens sets whether to check for correct use of hyphens ('-') in
|
||||
// labels. Most web browsers do not have this option set, since labels such as
|
||||
// "r3---sn-apo3qvuoxuxbt-j5pe" are in common use.
|
||||
//
|
||||
// This option corresponds to the CheckHyphens flag in UTS #46.
|
||||
func CheckHyphens(enable bool) Option {
|
||||
return func(o *options) { o.checkHyphens = enable }
|
||||
}
|
||||
|
||||
// CheckJoiners sets whether to check the ContextJ rules as defined in Appendix
|
||||
// A of RFC 5892, concerning the use of joiner runes.
|
||||
//
|
||||
// This option corresponds to the CheckJoiners flag in UTS #46.
|
||||
func CheckJoiners(enable bool) Option {
|
||||
return func(o *options) {
|
||||
o.trie = trie
|
||||
o.checkJoiners = enable
|
||||
}
|
||||
}
|
||||
|
||||
// StrictDomainName limits the set of permissible ASCII characters to those
|
||||
// allowed in domain names as defined in RFC 1034 (A-Z, a-z, 0-9 and the
|
||||
// hyphen). This is set by default for MapForLookup and ValidateForRegistration,
|
||||
// but is only useful if ValidateLabels is set.
|
||||
//
|
||||
// This option is useful, for instance, for browsers that allow characters
|
||||
// outside this range, for example a '_' (U+005F LOW LINE). See
|
||||
// http://www.rfc-editor.org/std/std3.txt for more details.
|
||||
//
|
||||
// This option corresponds to the UseSTD3ASCIIRules flag in UTS #46.
|
||||
func StrictDomainName(use bool) Option {
|
||||
return func(o *options) { o.useSTD3Rules = use }
|
||||
}
|
||||
|
||||
// NOTE: the following options pull in tables. The tables should not be linked
|
||||
// in as long as the options are not used.
|
||||
|
||||
// BidiRule enables the Bidi rule as defined in RFC 5893. Any application
|
||||
// that relies on proper validation of labels should include this rule.
|
||||
//
|
||||
// This option corresponds to the CheckBidi flag in UTS #46.
|
||||
func BidiRule() Option {
|
||||
return func(o *options) { o.bidirule = bidirule.ValidString }
|
||||
}
|
||||
|
||||
// ValidateForRegistration sets validation options to verify that a given IDN is
|
||||
// properly formatted for registration as defined by Section 4 of RFC 5891.
|
||||
func ValidateForRegistration() Option {
|
||||
return func(o *options) {
|
||||
o.mapping = validateRegistration
|
||||
StrictDomainName(true)(o)
|
||||
ValidateLabels(true)(o)
|
||||
VerifyDNSLength(true)(o)
|
||||
BidiRule()(o)
|
||||
}
|
||||
}
|
||||
|
||||
// MapForLookup sets validation and mapping options such that a given IDN is
|
||||
// transformed for domain name lookup according to the requirements set out in
|
||||
// Section 5 of RFC 5891. The mappings follow the recommendations of RFC 5894,
|
||||
// RFC 5895 and UTS 46. It does not add the Bidi Rule. Use the BidiRule option
|
||||
// to add this check.
|
||||
//
|
||||
// The mappings include normalization and mapping case, width and other
|
||||
// compatibility mappings.
|
||||
func MapForLookup() Option {
|
||||
return func(o *options) {
|
||||
o.mapping = validateAndMap
|
||||
StrictDomainName(true)(o)
|
||||
ValidateLabels(true)(o)
|
||||
RemoveLeadingDots(true)(o)
|
||||
}
|
||||
}
|
||||
|
||||
type options struct {
|
||||
transitional bool
|
||||
useSTD3Rules bool
|
||||
checkHyphens bool
|
||||
checkJoiners bool
|
||||
verifyDNSLength bool
|
||||
removeLeadingDots bool
|
||||
|
||||
trie *idnaTrie
|
||||
|
||||
// fromPuny calls validation rules when converting A-labels to U-labels.
|
||||
fromPuny func(p *Profile, s string) error
|
||||
|
||||
// mapping implements a validation and mapping step as defined in RFC 5895
|
||||
// or UTS 46, tailored to, for example, domain registration or lookup.
|
||||
mapping func(p *Profile, s string) (string, error)
|
||||
|
||||
// bidirule, if specified, checks whether s conforms to the Bidi Rule
|
||||
// defined in RFC 5893.
|
||||
bidirule func(s string) bool
|
||||
}
|
||||
|
||||
// A Profile defines the configuration of a IDNA mapper.
|
||||
type Profile struct {
|
||||
options
|
||||
}
|
||||
|
||||
func apply(o *options, opts []Option) {
|
||||
for _, f := range opts {
|
||||
f(o)
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a new Profile.
|
||||
//
|
||||
// With no options, the returned Profile is the most permissive and equals the
|
||||
// Punycode Profile. Options can be passed to further restrict the Profile. The
|
||||
// MapForLookup and ValidateForRegistration options set a collection of options,
|
||||
// for lookup and registration purposes respectively, which can be tailored by
|
||||
// adding more fine-grained options, where later options override earlier
|
||||
// options.
|
||||
func New(o ...Option) *Profile {
|
||||
p := &Profile{}
|
||||
apply(&p.options, o)
|
||||
return p
|
||||
}
|
||||
|
||||
// ToASCII converts a domain or domain label to its ASCII form. For example,
|
||||
// ToASCII("bücher.example.com") is "xn--bcher-kva.example.com", and
|
||||
// ToASCII("golang") is "golang". If an error is encountered it will return
|
||||
// an error and a (partially) processed result.
|
||||
func (p *Profile) ToASCII(s string) (string, error) {
|
||||
return p.process(s, true)
|
||||
}
|
||||
|
||||
// ToUnicode converts a domain or domain label to its Unicode form. For example,
|
||||
// ToUnicode("xn--bcher-kva.example.com") is "bücher.example.com", and
|
||||
// ToUnicode("golang") is "golang". If an error is encountered it will return
|
||||
// an error and a (partially) processed result.
|
||||
func (p *Profile) ToUnicode(s string) (string, error) {
|
||||
pp := *p
|
||||
pp.transitional = false
|
||||
return pp.process(s, false)
|
||||
}
|
||||
|
||||
// String reports a string with a description of the profile for debugging
|
||||
// purposes. The string format may change with different versions.
|
||||
func (p *Profile) String() string {
|
||||
s := ""
|
||||
if p.transitional {
|
||||
s = "Transitional"
|
||||
} else {
|
||||
s = "NonTransitional"
|
||||
}
|
||||
if p.useSTD3Rules {
|
||||
s += ":UseSTD3Rules"
|
||||
}
|
||||
if p.checkHyphens {
|
||||
s += ":CheckHyphens"
|
||||
}
|
||||
if p.checkJoiners {
|
||||
s += ":CheckJoiners"
|
||||
}
|
||||
if p.verifyDNSLength {
|
||||
s += ":VerifyDNSLength"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
var (
|
||||
// Punycode is a Profile that does raw punycode processing with a minimum
|
||||
// of validation.
|
||||
Punycode *Profile = punycode
|
||||
|
||||
// Lookup is the recommended profile for looking up domain names, according
|
||||
// to Section 5 of RFC 5891. The exact configuration of this profile may
|
||||
// change over time.
|
||||
Lookup *Profile = lookup
|
||||
|
||||
// Display is the recommended profile for displaying domain names.
|
||||
// The configuration of this profile may change over time.
|
||||
Display *Profile = display
|
||||
|
||||
// Registration is the recommended profile for checking whether a given
|
||||
// IDN is valid for registration, according to Section 4 of RFC 5891.
|
||||
Registration *Profile = registration
|
||||
|
||||
punycode = &Profile{}
|
||||
lookup = &Profile{options{
|
||||
transitional: true,
|
||||
removeLeadingDots: true,
|
||||
useSTD3Rules: true,
|
||||
checkHyphens: true,
|
||||
checkJoiners: true,
|
||||
trie: trie,
|
||||
fromPuny: validateFromPunycode,
|
||||
mapping: validateAndMap,
|
||||
bidirule: bidirule.ValidString,
|
||||
}}
|
||||
display = &Profile{options{
|
||||
useSTD3Rules: true,
|
||||
removeLeadingDots: true,
|
||||
checkHyphens: true,
|
||||
checkJoiners: true,
|
||||
trie: trie,
|
||||
fromPuny: validateFromPunycode,
|
||||
mapping: validateAndMap,
|
||||
bidirule: bidirule.ValidString,
|
||||
}}
|
||||
registration = &Profile{options{
|
||||
useSTD3Rules: true,
|
||||
verifyDNSLength: true,
|
||||
checkHyphens: true,
|
||||
checkJoiners: true,
|
||||
trie: trie,
|
||||
fromPuny: validateFromPunycode,
|
||||
mapping: validateRegistration,
|
||||
bidirule: bidirule.ValidString,
|
||||
}}
|
||||
|
||||
// TODO: profiles
|
||||
// Register: recommended for approving domain names: don't do any mappings
|
||||
// but rather reject on invalid input. Bundle or block deviation characters.
|
||||
)
|
||||
|
||||
type labelError struct{ label, code_ string }
|
||||
|
||||
func (e labelError) code() string { return e.code_ }
|
||||
func (e labelError) Error() string {
|
||||
return fmt.Sprintf("idna: invalid label %q", e.label)
|
||||
}
|
||||
|
||||
type runeError rune
|
||||
|
||||
func (e runeError) code() string { return "P1" }
|
||||
func (e runeError) Error() string {
|
||||
return fmt.Sprintf("idna: disallowed rune %U", e)
|
||||
}
|
||||
|
||||
// process implements the algorithm described in section 4 of UTS #46,
|
||||
// see https://www.unicode.org/reports/tr46.
|
||||
func (p *Profile) process(s string, toASCII bool) (string, error) {
|
||||
var err error
|
||||
if p.mapping != nil {
|
||||
s, err = p.mapping(p, s)
|
||||
}
|
||||
// Remove leading empty labels.
|
||||
if p.removeLeadingDots {
|
||||
for ; len(s) > 0 && s[0] == '.'; s = s[1:] {
|
||||
}
|
||||
}
|
||||
// It seems like we should only create this error on ToASCII, but the
|
||||
// UTS 46 conformance tests suggests we should always check this.
|
||||
if err == nil && p.verifyDNSLength && s == "" {
|
||||
err = &labelError{s, "A4"}
|
||||
}
|
||||
labels := labelIter{orig: s}
|
||||
for ; !labels.done(); labels.next() {
|
||||
label := labels.label()
|
||||
if label == "" {
|
||||
// Empty labels are not okay. The label iterator skips the last
|
||||
// label if it is empty.
|
||||
if err == nil && p.verifyDNSLength {
|
||||
err = &labelError{s, "A4"}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(label, acePrefix) {
|
||||
u, err2 := decode(label[len(acePrefix):])
|
||||
if err2 != nil {
|
||||
if err == nil {
|
||||
err = err2
|
||||
}
|
||||
// Spec says keep the old label.
|
||||
continue
|
||||
}
|
||||
labels.set(u)
|
||||
if err == nil && p.fromPuny != nil {
|
||||
err = p.fromPuny(p, u)
|
||||
}
|
||||
if err == nil {
|
||||
// This should be called on NonTransitional, according to the
|
||||
// spec, but that currently does not have any effect. Use the
|
||||
// original profile to preserve options.
|
||||
err = p.validateLabel(u)
|
||||
}
|
||||
} else if err == nil {
|
||||
err = p.validateLabel(label)
|
||||
}
|
||||
}
|
||||
if toASCII {
|
||||
for labels.reset(); !labels.done(); labels.next() {
|
||||
label := labels.label()
|
||||
if !ascii(label) {
|
||||
a, err2 := encode(acePrefix, label)
|
||||
if err == nil {
|
||||
err = err2
|
||||
}
|
||||
label = a
|
||||
labels.set(a)
|
||||
}
|
||||
n := len(label)
|
||||
if p.verifyDNSLength && err == nil && (n == 0 || n > 63) {
|
||||
err = &labelError{label, "A4"}
|
||||
}
|
||||
}
|
||||
}
|
||||
s = labels.result()
|
||||
if toASCII && p.verifyDNSLength && err == nil {
|
||||
// Compute the length of the domain name minus the root label and its dot.
|
||||
n := len(s)
|
||||
if n > 0 && s[n-1] == '.' {
|
||||
n--
|
||||
}
|
||||
if len(s) < 1 || n > 253 {
|
||||
err = &labelError{s, "A4"}
|
||||
}
|
||||
}
|
||||
return s, err
|
||||
}
|
||||
|
||||
func normalize(p *Profile, s string) (string, error) {
|
||||
return norm.NFC.String(s), nil
|
||||
}
|
||||
|
||||
func validateRegistration(p *Profile, s string) (string, error) {
|
||||
if !norm.NFC.IsNormalString(s) {
|
||||
return s, &labelError{s, "V1"}
|
||||
}
|
||||
for i := 0; i < len(s); {
|
||||
v, sz := trie.lookupString(s[i:])
|
||||
// Copy bytes not copied so far.
|
||||
switch p.simplify(info(v).category()) {
|
||||
// TODO: handle the NV8 defined in the Unicode idna data set to allow
|
||||
// for strict conformance to IDNA2008.
|
||||
case valid, deviation:
|
||||
case disallowed, mapped, unknown, ignored:
|
||||
r, _ := utf8.DecodeRuneInString(s[i:])
|
||||
return s, runeError(r)
|
||||
}
|
||||
i += sz
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func validateAndMap(p *Profile, s string) (string, error) {
|
||||
var (
|
||||
err error
|
||||
b []byte
|
||||
k int
|
||||
)
|
||||
for i := 0; i < len(s); {
|
||||
v, sz := trie.lookupString(s[i:])
|
||||
start := i
|
||||
i += sz
|
||||
// Copy bytes not copied so far.
|
||||
switch p.simplify(info(v).category()) {
|
||||
case valid:
|
||||
continue
|
||||
case disallowed:
|
||||
if err == nil {
|
||||
r, _ := utf8.DecodeRuneInString(s[start:])
|
||||
err = runeError(r)
|
||||
}
|
||||
continue
|
||||
case mapped, deviation:
|
||||
b = append(b, s[k:start]...)
|
||||
b = info(v).appendMapping(b, s[start:i])
|
||||
case ignored:
|
||||
b = append(b, s[k:start]...)
|
||||
// drop the rune
|
||||
case unknown:
|
||||
b = append(b, s[k:start]...)
|
||||
b = append(b, "\ufffd"...)
|
||||
}
|
||||
k = i
|
||||
}
|
||||
if k == 0 {
|
||||
// No changes so far.
|
||||
s = norm.NFC.String(s)
|
||||
} else {
|
||||
b = append(b, s[k:]...)
|
||||
if norm.NFC.QuickSpan(b) != len(b) {
|
||||
b = norm.NFC.Bytes(b)
|
||||
}
|
||||
// TODO: the punycode converters require strings as input.
|
||||
s = string(b)
|
||||
}
|
||||
return s, err
|
||||
}
|
||||
|
||||
// A labelIter allows iterating over domain name labels.
|
||||
type labelIter struct {
|
||||
orig string
|
||||
slice []string
|
||||
curStart int
|
||||
curEnd int
|
||||
i int
|
||||
}
|
||||
|
||||
func (l *labelIter) reset() {
|
||||
l.curStart = 0
|
||||
l.curEnd = 0
|
||||
l.i = 0
|
||||
}
|
||||
|
||||
func (l *labelIter) done() bool {
|
||||
return l.curStart >= len(l.orig)
|
||||
}
|
||||
|
||||
func (l *labelIter) result() string {
|
||||
if l.slice != nil {
|
||||
return strings.Join(l.slice, ".")
|
||||
}
|
||||
return l.orig
|
||||
}
|
||||
|
||||
func (l *labelIter) label() string {
|
||||
if l.slice != nil {
|
||||
return l.slice[l.i]
|
||||
}
|
||||
p := strings.IndexByte(l.orig[l.curStart:], '.')
|
||||
l.curEnd = l.curStart + p
|
||||
if p == -1 {
|
||||
l.curEnd = len(l.orig)
|
||||
}
|
||||
return l.orig[l.curStart:l.curEnd]
|
||||
}
|
||||
|
||||
// next sets the value to the next label. It skips the last label if it is empty.
|
||||
func (l *labelIter) next() {
|
||||
l.i++
|
||||
if l.slice != nil {
|
||||
if l.i >= len(l.slice) || l.i == len(l.slice)-1 && l.slice[l.i] == "" {
|
||||
l.curStart = len(l.orig)
|
||||
}
|
||||
} else {
|
||||
l.curStart = l.curEnd + 1
|
||||
if l.curStart == len(l.orig)-1 && l.orig[l.curStart] == '.' {
|
||||
l.curStart = len(l.orig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *labelIter) set(s string) {
|
||||
if l.slice == nil {
|
||||
l.slice = strings.Split(l.orig, ".")
|
||||
}
|
||||
l.slice[l.i] = s
|
||||
}
|
||||
|
||||
// acePrefix is the ASCII Compatible Encoding prefix.
|
||||
const acePrefix = "xn--"
|
||||
|
||||
func (p *Profile) simplify(cat category) category {
|
||||
switch cat {
|
||||
case disallowedSTD3Mapped:
|
||||
if p.useSTD3Rules {
|
||||
cat = disallowed
|
||||
} else {
|
||||
cat = mapped
|
||||
}
|
||||
case disallowedSTD3Valid:
|
||||
if p.useSTD3Rules {
|
||||
cat = disallowed
|
||||
} else {
|
||||
cat = valid
|
||||
}
|
||||
case deviation:
|
||||
if !p.transitional {
|
||||
cat = valid
|
||||
}
|
||||
case validNV8, validXV8:
|
||||
// TODO: handle V2008
|
||||
cat = valid
|
||||
}
|
||||
return cat
|
||||
}
|
||||
|
||||
func validateFromPunycode(p *Profile, s string) error {
|
||||
if !norm.NFC.IsNormalString(s) {
|
||||
return &labelError{s, "V1"}
|
||||
}
|
||||
for i := 0; i < len(s); {
|
||||
v, sz := trie.lookupString(s[i:])
|
||||
if c := p.simplify(info(v).category()); c != valid && c != deviation {
|
||||
return &labelError{s, "V6"}
|
||||
}
|
||||
i += sz
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
zwnj = "\u200c"
|
||||
zwj = "\u200d"
|
||||
)
|
||||
|
||||
type joinState int8
|
||||
|
||||
const (
|
||||
stateStart joinState = iota
|
||||
stateVirama
|
||||
stateBefore
|
||||
stateBeforeVirama
|
||||
stateAfter
|
||||
stateFAIL
|
||||
)
|
||||
|
||||
var joinStates = [][numJoinTypes]joinState{
|
||||
stateStart: {
|
||||
joiningL: stateBefore,
|
||||
joiningD: stateBefore,
|
||||
joinZWNJ: stateFAIL,
|
||||
joinZWJ: stateFAIL,
|
||||
joinVirama: stateVirama,
|
||||
},
|
||||
stateVirama: {
|
||||
joiningL: stateBefore,
|
||||
joiningD: stateBefore,
|
||||
},
|
||||
stateBefore: {
|
||||
joiningL: stateBefore,
|
||||
joiningD: stateBefore,
|
||||
joiningT: stateBefore,
|
||||
joinZWNJ: stateAfter,
|
||||
joinZWJ: stateFAIL,
|
||||
joinVirama: stateBeforeVirama,
|
||||
},
|
||||
stateBeforeVirama: {
|
||||
joiningL: stateBefore,
|
||||
joiningD: stateBefore,
|
||||
joiningT: stateBefore,
|
||||
},
|
||||
stateAfter: {
|
||||
joiningL: stateFAIL,
|
||||
joiningD: stateBefore,
|
||||
joiningT: stateAfter,
|
||||
joiningR: stateStart,
|
||||
joinZWNJ: stateFAIL,
|
||||
joinZWJ: stateFAIL,
|
||||
joinVirama: stateAfter, // no-op as we can't accept joiners here
|
||||
},
|
||||
stateFAIL: {
|
||||
0: stateFAIL,
|
||||
joiningL: stateFAIL,
|
||||
joiningD: stateFAIL,
|
||||
joiningT: stateFAIL,
|
||||
joiningR: stateFAIL,
|
||||
joinZWNJ: stateFAIL,
|
||||
joinZWJ: stateFAIL,
|
||||
joinVirama: stateFAIL,
|
||||
},
|
||||
}
|
||||
|
||||
// validateLabel validates the criteria from Section 4.1. Item 1, 4, and 6 are
|
||||
// already implicitly satisfied by the overall implementation.
|
||||
func (p *Profile) validateLabel(s string) error {
|
||||
if s == "" {
|
||||
if p.verifyDNSLength {
|
||||
return &labelError{s, "A4"}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if p.bidirule != nil && !p.bidirule(s) {
|
||||
return &labelError{s, "B"}
|
||||
}
|
||||
if p.checkHyphens {
|
||||
if len(s) > 4 && s[2] == '-' && s[3] == '-' {
|
||||
return &labelError{s, "V2"}
|
||||
}
|
||||
if s[0] == '-' || s[len(s)-1] == '-' {
|
||||
return &labelError{s, "V3"}
|
||||
}
|
||||
}
|
||||
if !p.checkJoiners {
|
||||
return nil
|
||||
}
|
||||
trie := p.trie // p.checkJoiners is only set if trie is set.
|
||||
// TODO: merge the use of this in the trie.
|
||||
v, sz := trie.lookupString(s)
|
||||
x := info(v)
|
||||
if x.isModifier() {
|
||||
return &labelError{s, "V5"}
|
||||
}
|
||||
// Quickly return in the absence of zero-width (non) joiners.
|
||||
if strings.Index(s, zwj) == -1 && strings.Index(s, zwnj) == -1 {
|
||||
return nil
|
||||
}
|
||||
st := stateStart
|
||||
for i := 0; ; {
|
||||
jt := x.joinType()
|
||||
if s[i:i+sz] == zwj {
|
||||
jt = joinZWJ
|
||||
} else if s[i:i+sz] == zwnj {
|
||||
jt = joinZWNJ
|
||||
}
|
||||
st = joinStates[st][jt]
|
||||
if x.isViramaModifier() {
|
||||
st = joinStates[st][joinVirama]
|
||||
}
|
||||
if i += sz; i == len(s) {
|
||||
break
|
||||
}
|
||||
v, sz = trie.lookupString(s[i:])
|
||||
x = info(v)
|
||||
}
|
||||
if st == stateFAIL || st == stateAfter {
|
||||
return &labelError{s, "C"}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ascii(s string) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] >= utf8.RuneSelf {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.18
|
||||
|
||||
package idna
|
||||
|
||||
const transitionalLookup = true
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package idna
|
||||
|
||||
// This file implements the Punycode algorithm from RFC 3492.
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// These parameter values are specified in section 5.
|
||||
//
|
||||
// All computation is done with int32s, so that overflow behavior is identical
|
||||
// regardless of whether int is 32-bit or 64-bit.
|
||||
const (
|
||||
base int32 = 36
|
||||
damp int32 = 700
|
||||
initialBias int32 = 72
|
||||
initialN int32 = 128
|
||||
skew int32 = 38
|
||||
tmax int32 = 26
|
||||
tmin int32 = 1
|
||||
)
|
||||
|
||||
func punyError(s string) error { return &labelError{s, "A3"} }
|
||||
|
||||
// decode decodes a string as specified in section 6.2.
|
||||
func decode(encoded string) (string, error) {
|
||||
if encoded == "" {
|
||||
return "", nil
|
||||
}
|
||||
pos := 1 + strings.LastIndex(encoded, "-")
|
||||
if pos == 1 {
|
||||
return "", punyError(encoded)
|
||||
}
|
||||
if pos == len(encoded) {
|
||||
return encoded[:len(encoded)-1], nil
|
||||
}
|
||||
output := make([]rune, 0, len(encoded))
|
||||
if pos != 0 {
|
||||
for _, r := range encoded[:pos-1] {
|
||||
output = append(output, r)
|
||||
}
|
||||
}
|
||||
i, n, bias := int32(0), initialN, initialBias
|
||||
overflow := false
|
||||
for pos < len(encoded) {
|
||||
oldI, w := i, int32(1)
|
||||
for k := base; ; k += base {
|
||||
if pos == len(encoded) {
|
||||
return "", punyError(encoded)
|
||||
}
|
||||
digit, ok := decodeDigit(encoded[pos])
|
||||
if !ok {
|
||||
return "", punyError(encoded)
|
||||
}
|
||||
pos++
|
||||
i, overflow = madd(i, digit, w)
|
||||
if overflow {
|
||||
return "", punyError(encoded)
|
||||
}
|
||||
t := k - bias
|
||||
if k <= bias {
|
||||
t = tmin
|
||||
} else if k >= bias+tmax {
|
||||
t = tmax
|
||||
}
|
||||
if digit < t {
|
||||
break
|
||||
}
|
||||
w, overflow = madd(0, w, base-t)
|
||||
if overflow {
|
||||
return "", punyError(encoded)
|
||||
}
|
||||
}
|
||||
if len(output) >= 1024 {
|
||||
return "", punyError(encoded)
|
||||
}
|
||||
x := int32(len(output) + 1)
|
||||
bias = adapt(i-oldI, x, oldI == 0)
|
||||
n += i / x
|
||||
i %= x
|
||||
if n < 0 || n > utf8.MaxRune {
|
||||
return "", punyError(encoded)
|
||||
}
|
||||
output = append(output, 0)
|
||||
copy(output[i+1:], output[i:])
|
||||
output[i] = n
|
||||
i++
|
||||
}
|
||||
return string(output), nil
|
||||
}
|
||||
|
||||
// encode encodes a string as specified in section 6.3 and prepends prefix to
|
||||
// the result.
|
||||
//
|
||||
// The "while h < length(input)" line in the specification becomes "for
|
||||
// remaining != 0" in the Go code, because len(s) in Go is in bytes, not runes.
|
||||
func encode(prefix, s string) (string, error) {
|
||||
output := make([]byte, len(prefix), len(prefix)+1+2*len(s))
|
||||
copy(output, prefix)
|
||||
delta, n, bias := int32(0), initialN, initialBias
|
||||
b, remaining := int32(0), int32(0)
|
||||
for _, r := range s {
|
||||
if r < 0x80 {
|
||||
b++
|
||||
output = append(output, byte(r))
|
||||
} else {
|
||||
remaining++
|
||||
}
|
||||
}
|
||||
h := b
|
||||
if b > 0 {
|
||||
output = append(output, '-')
|
||||
}
|
||||
overflow := false
|
||||
for remaining != 0 {
|
||||
m := int32(0x7fffffff)
|
||||
for _, r := range s {
|
||||
if m > r && r >= n {
|
||||
m = r
|
||||
}
|
||||
}
|
||||
delta, overflow = madd(delta, m-n, h+1)
|
||||
if overflow {
|
||||
return "", punyError(s)
|
||||
}
|
||||
n = m
|
||||
for _, r := range s {
|
||||
if r < n {
|
||||
delta++
|
||||
if delta < 0 {
|
||||
return "", punyError(s)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if r > n {
|
||||
continue
|
||||
}
|
||||
q := delta
|
||||
for k := base; ; k += base {
|
||||
t := k - bias
|
||||
if k <= bias {
|
||||
t = tmin
|
||||
} else if k >= bias+tmax {
|
||||
t = tmax
|
||||
}
|
||||
if q < t {
|
||||
break
|
||||
}
|
||||
output = append(output, encodeDigit(t+(q-t)%(base-t)))
|
||||
q = (q - t) / (base - t)
|
||||
}
|
||||
output = append(output, encodeDigit(q))
|
||||
bias = adapt(delta, h+1, h == b)
|
||||
delta = 0
|
||||
h++
|
||||
remaining--
|
||||
}
|
||||
delta++
|
||||
n++
|
||||
}
|
||||
return string(output), nil
|
||||
}
|
||||
|
||||
// madd computes a + (b * c), detecting overflow.
|
||||
func madd(a, b, c int32) (next int32, overflow bool) {
|
||||
p := int64(b) * int64(c)
|
||||
if p > math.MaxInt32-int64(a) {
|
||||
return 0, true
|
||||
}
|
||||
return a + int32(p), false
|
||||
}
|
||||
|
||||
func decodeDigit(x byte) (digit int32, ok bool) {
|
||||
switch {
|
||||
case '0' <= x && x <= '9':
|
||||
return int32(x - ('0' - 26)), true
|
||||
case 'A' <= x && x <= 'Z':
|
||||
return int32(x - 'A'), true
|
||||
case 'a' <= x && x <= 'z':
|
||||
return int32(x - 'a'), true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func encodeDigit(digit int32) byte {
|
||||
switch {
|
||||
case 0 <= digit && digit < 26:
|
||||
return byte(digit + 'a')
|
||||
case 26 <= digit && digit < 36:
|
||||
return byte(digit + ('0' - 26))
|
||||
}
|
||||
panic("idna: internal error in punycode encoding")
|
||||
}
|
||||
|
||||
// adapt is the bias adaptation function specified in section 6.1.
|
||||
func adapt(delta, numPoints int32, firstTime bool) int32 {
|
||||
if firstTime {
|
||||
delta /= damp
|
||||
} else {
|
||||
delta /= 2
|
||||
}
|
||||
delta += delta / numPoints
|
||||
k := int32(0)
|
||||
for delta > ((base-tmin)*tmax)/2 {
|
||||
delta /= base - tmin
|
||||
k += base
|
||||
}
|
||||
return k + (base-tmin+1)*delta/(delta+skew)
|
||||
}
|
||||
+4559
File diff suppressed because it is too large
Load Diff
+4653
File diff suppressed because it is too large
Load Diff
+4733
File diff suppressed because it is too large
Load Diff
+4959
File diff suppressed because it is too large
Load Diff
+5144
File diff suppressed because it is too large
Load Diff
+4486
File diff suppressed because it is too large
Load Diff
+51
@@ -0,0 +1,51 @@
|
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package idna
|
||||
|
||||
// Sparse block handling code.
|
||||
|
||||
type valueRange struct {
|
||||
value uint16 // header: value:stride
|
||||
lo, hi byte // header: lo:n
|
||||
}
|
||||
|
||||
type sparseBlocks struct {
|
||||
values []valueRange
|
||||
offset []uint16
|
||||
}
|
||||
|
||||
var idnaSparse = sparseBlocks{
|
||||
values: idnaSparseValues[:],
|
||||
offset: idnaSparseOffset[:],
|
||||
}
|
||||
|
||||
// Don't use newIdnaTrie to avoid unconditional linking in of the table.
|
||||
var trie = &idnaTrie{}
|
||||
|
||||
// lookup determines the type of block n and looks up the value for b.
|
||||
// For n < t.cutoff, the block is a simple lookup table. Otherwise, the block
|
||||
// is a list of ranges with an accompanying value. Given a matching range r,
|
||||
// the value for b is by r.value + (b - r.lo) * stride.
|
||||
func (t *sparseBlocks) lookup(n uint32, b byte) uint16 {
|
||||
offset := t.offset[n]
|
||||
header := t.values[offset]
|
||||
lo := offset + 1
|
||||
hi := lo + uint16(header.lo)
|
||||
for lo < hi {
|
||||
m := lo + (hi-lo)/2
|
||||
r := t.values[m]
|
||||
if r.lo <= b && b <= r.hi {
|
||||
return r.value + uint16(b-r.lo)*header.value
|
||||
}
|
||||
if b < r.lo {
|
||||
hi = m
|
||||
} else {
|
||||
lo = m + 1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.16
|
||||
|
||||
package idna
|
||||
|
||||
// appendMapping appends the mapping for the respective rune. isMapped must be
|
||||
// true. A mapping is a categorization of a rune as defined in UTS #46.
|
||||
func (c info) appendMapping(b []byte, s string) []byte {
|
||||
index := int(c >> indexShift)
|
||||
if c&xorBit == 0 {
|
||||
s := mappings[index:]
|
||||
return append(b, s[1:s[0]+1]...)
|
||||
}
|
||||
b = append(b, s...)
|
||||
if c&inlineXOR == inlineXOR {
|
||||
// TODO: support and handle two-byte inline masks
|
||||
b[len(b)-1] ^= byte(index)
|
||||
} else {
|
||||
for p := len(b) - int(xorData[index]); p < len(b); p++ {
|
||||
index++
|
||||
b[p] ^= xorData[index]
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.16
|
||||
|
||||
package idna
|
||||
|
||||
// appendMapping appends the mapping for the respective rune. isMapped must be
|
||||
// true. A mapping is a categorization of a rune as defined in UTS #46.
|
||||
func (c info) appendMapping(b []byte, s string) []byte {
|
||||
index := int(c >> indexShift)
|
||||
if c&xorBit == 0 {
|
||||
p := index
|
||||
return append(b, mappings[mappingIndex[p]:mappingIndex[p+1]]...)
|
||||
}
|
||||
b = append(b, s...)
|
||||
if c&inlineXOR == inlineXOR {
|
||||
// TODO: support and handle two-byte inline masks
|
||||
b[len(b)-1] ^= byte(index)
|
||||
} else {
|
||||
for p := len(b) - int(xorData[index]); p < len(b); p++ {
|
||||
index++
|
||||
b[p] ^= xorData[index]
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
package idna
|
||||
|
||||
// This file contains definitions for interpreting the trie value of the idna
|
||||
// trie generated by "go run gen*.go". It is shared by both the generator
|
||||
// program and the resultant package. Sharing is achieved by the generator
|
||||
// copying gen_trieval.go to trieval.go and changing what's above this comment.
|
||||
|
||||
// info holds information from the IDNA mapping table for a single rune. It is
|
||||
// the value returned by a trie lookup. In most cases, all information fits in
|
||||
// a 16-bit value. For mappings, this value may contain an index into a slice
|
||||
// with the mapped string. Such mappings can consist of the actual mapped value
|
||||
// or an XOR pattern to be applied to the bytes of the UTF8 encoding of the
|
||||
// input rune. This technique is used by the cases packages and reduces the
|
||||
// table size significantly.
|
||||
//
|
||||
// The per-rune values have the following format:
|
||||
//
|
||||
// if mapped {
|
||||
// if inlinedXOR {
|
||||
// 15..13 inline XOR marker
|
||||
// 12..11 unused
|
||||
// 10..3 inline XOR mask
|
||||
// } else {
|
||||
// 15..3 index into xor or mapping table
|
||||
// }
|
||||
// } else {
|
||||
// 15..14 unused
|
||||
// 13 mayNeedNorm
|
||||
// 12..11 attributes
|
||||
// 10..8 joining type
|
||||
// 7..3 category type
|
||||
// }
|
||||
// 2 use xor pattern
|
||||
// 1..0 mapped category
|
||||
//
|
||||
// See the definitions below for a more detailed description of the various
|
||||
// bits.
|
||||
type info uint16
|
||||
|
||||
const (
|
||||
catSmallMask = 0x3
|
||||
catBigMask = 0xF8
|
||||
indexShift = 3
|
||||
xorBit = 0x4 // interpret the index as an xor pattern
|
||||
inlineXOR = 0xE000 // These bits are set if the XOR pattern is inlined.
|
||||
|
||||
joinShift = 8
|
||||
joinMask = 0x07
|
||||
|
||||
// Attributes
|
||||
attributesMask = 0x1800
|
||||
viramaModifier = 0x1800
|
||||
modifier = 0x1000
|
||||
rtl = 0x0800
|
||||
|
||||
mayNeedNorm = 0x2000
|
||||
)
|
||||
|
||||
// A category corresponds to a category defined in the IDNA mapping table.
|
||||
type category uint16
|
||||
|
||||
const (
|
||||
unknown category = 0 // not currently defined in unicode.
|
||||
mapped category = 1
|
||||
disallowedSTD3Mapped category = 2
|
||||
deviation category = 3
|
||||
)
|
||||
|
||||
const (
|
||||
valid category = 0x08
|
||||
validNV8 category = 0x18
|
||||
validXV8 category = 0x28
|
||||
disallowed category = 0x40
|
||||
disallowedSTD3Valid category = 0x80
|
||||
ignored category = 0xC0
|
||||
)
|
||||
|
||||
// join types and additional rune information
|
||||
const (
|
||||
joiningL = (iota + 1)
|
||||
joiningD
|
||||
joiningT
|
||||
joiningR
|
||||
|
||||
//the following types are derived during processing
|
||||
joinZWJ
|
||||
joinZWNJ
|
||||
joinVirama
|
||||
numJoinTypes
|
||||
)
|
||||
|
||||
func (c info) isMapped() bool {
|
||||
return c&0x3 != 0
|
||||
}
|
||||
|
||||
func (c info) category() category {
|
||||
small := c & catSmallMask
|
||||
if small != 0 {
|
||||
return category(small)
|
||||
}
|
||||
return category(c & catBigMask)
|
||||
}
|
||||
|
||||
func (c info) joinType() info {
|
||||
if c.isMapped() {
|
||||
return 0
|
||||
}
|
||||
return (c >> joinShift) & joinMask
|
||||
}
|
||||
|
||||
func (c info) isModifier() bool {
|
||||
return c&(modifier|catSmallMask) == modifier
|
||||
}
|
||||
|
||||
func (c info) isViramaModifier() bool {
|
||||
return c&(attributesMask|catSmallMask) == viramaModifier
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
Copyright 2009 The Go Authors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google LLC nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
_obj/
|
||||
unix.test
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
# Building `sys/unix`
|
||||
|
||||
The sys/unix package provides access to the raw system call interface of the
|
||||
underlying operating system. See: https://godoc.org/golang.org/x/sys/unix
|
||||
|
||||
Porting Go to a new architecture/OS combination or adding syscalls, types, or
|
||||
constants to an existing architecture/OS pair requires some manual effort;
|
||||
however, there are tools that automate much of the process.
|
||||
|
||||
## Build Systems
|
||||
|
||||
There are currently two ways we generate the necessary files. We are currently
|
||||
migrating the build system to use containers so the builds are reproducible.
|
||||
This is being done on an OS-by-OS basis. Please update this documentation as
|
||||
components of the build system change.
|
||||
|
||||
### Old Build System (currently for `GOOS != "linux"`)
|
||||
|
||||
The old build system generates the Go files based on the C header files
|
||||
present on your system. This means that files
|
||||
for a given GOOS/GOARCH pair must be generated on a system with that OS and
|
||||
architecture. This also means that the generated code can differ from system
|
||||
to system, based on differences in the header files.
|
||||
|
||||
To avoid this, if you are using the old build system, only generate the Go
|
||||
files on an installation with unmodified header files. It is also important to
|
||||
keep track of which version of the OS the files were generated from (ex.
|
||||
Darwin 14 vs Darwin 15). This makes it easier to track the progress of changes
|
||||
and have each OS upgrade correspond to a single change.
|
||||
|
||||
To build the files for your current OS and architecture, make sure GOOS and
|
||||
GOARCH are set correctly and run `mkall.sh`. This will generate the files for
|
||||
your specific system. Running `mkall.sh -n` shows the commands that will be run.
|
||||
|
||||
Requirements: bash, go
|
||||
|
||||
### New Build System (currently for `GOOS == "linux"`)
|
||||
|
||||
The new build system uses a Docker container to generate the go files directly
|
||||
from source checkouts of the kernel and various system libraries. This means
|
||||
that on any platform that supports Docker, all the files using the new build
|
||||
system can be generated at once, and generated files will not change based on
|
||||
what the person running the scripts has installed on their computer.
|
||||
|
||||
The OS specific files for the new build system are located in the `${GOOS}`
|
||||
directory, and the build is coordinated by the `${GOOS}/mkall.go` program. When
|
||||
the kernel or system library updates, modify the Dockerfile at
|
||||
`${GOOS}/Dockerfile` to checkout the new release of the source.
|
||||
|
||||
To build all the files under the new build system, you must be on an amd64/Linux
|
||||
system and have your GOOS and GOARCH set accordingly. Running `mkall.sh` will
|
||||
then generate all of the files for all of the GOOS/GOARCH pairs in the new build
|
||||
system. Running `mkall.sh -n` shows the commands that will be run.
|
||||
|
||||
Requirements: bash, go, docker
|
||||
|
||||
## Component files
|
||||
|
||||
This section describes the various files used in the code generation process.
|
||||
It also contains instructions on how to modify these files to add a new
|
||||
architecture/OS or to add additional syscalls, types, or constants. Note that
|
||||
if you are using the new build system, the scripts/programs cannot be called normally.
|
||||
They must be called from within the docker container.
|
||||
|
||||
### asm files
|
||||
|
||||
The hand-written assembly file at `asm_${GOOS}_${GOARCH}.s` implements system
|
||||
call dispatch. There are three entry points:
|
||||
```
|
||||
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
|
||||
func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
|
||||
func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
|
||||
```
|
||||
The first and second are the standard ones; they differ only in how many
|
||||
arguments can be passed to the kernel. The third is for low-level use by the
|
||||
ForkExec wrapper. Unlike the first two, it does not call into the scheduler to
|
||||
let it know that a system call is running.
|
||||
|
||||
When porting Go to a new architecture/OS, this file must be implemented for
|
||||
each GOOS/GOARCH pair.
|
||||
|
||||
### mksysnum
|
||||
|
||||
Mksysnum is a Go program located at `${GOOS}/mksysnum.go` (or `mksysnum_${GOOS}.go`
|
||||
for the old system). This program takes in a list of header files containing the
|
||||
syscall number declarations and parses them to produce the corresponding list of
|
||||
Go numeric constants. See `zsysnum_${GOOS}_${GOARCH}.go` for the generated
|
||||
constants.
|
||||
|
||||
Adding new syscall numbers is mostly done by running the build on a sufficiently
|
||||
new installation of the target OS (or updating the source checkouts for the
|
||||
new build system). However, depending on the OS, you may need to update the
|
||||
parsing in mksysnum.
|
||||
|
||||
### mksyscall.go
|
||||
|
||||
The `syscall.go`, `syscall_${GOOS}.go`, `syscall_${GOOS}_${GOARCH}.go` are
|
||||
hand-written Go files which implement system calls (for unix, the specific OS,
|
||||
or the specific OS/Architecture pair respectively) that need special handling
|
||||
and list `//sys` comments giving prototypes for ones that can be generated.
|
||||
|
||||
The mksyscall.go program takes the `//sys` and `//sysnb` comments and converts
|
||||
them into syscalls. This requires the name of the prototype in the comment to
|
||||
match a syscall number in the `zsysnum_${GOOS}_${GOARCH}.go` file. The function
|
||||
prototype can be exported (capitalized) or not.
|
||||
|
||||
Adding a new syscall often just requires adding a new `//sys` function prototype
|
||||
with the desired arguments and a capitalized name so it is exported. However, if
|
||||
you want the interface to the syscall to be different, often one will make an
|
||||
unexported `//sys` prototype, and then write a custom wrapper in
|
||||
`syscall_${GOOS}.go`.
|
||||
|
||||
### types files
|
||||
|
||||
For each OS, there is a hand-written Go file at `${GOOS}/types.go` (or
|
||||
`types_${GOOS}.go` on the old system). This file includes standard C headers and
|
||||
creates Go type aliases to the corresponding C types. The file is then fed
|
||||
through godef to get the Go compatible definitions. Finally, the generated code
|
||||
is fed though mkpost.go to format the code correctly and remove any hidden or
|
||||
private identifiers. This cleaned-up code is written to
|
||||
`ztypes_${GOOS}_${GOARCH}.go`.
|
||||
|
||||
The hardest part about preparing this file is figuring out which headers to
|
||||
include and which symbols need to be `#define`d to get the actual data
|
||||
structures that pass through to the kernel system calls. Some C libraries
|
||||
preset alternate versions for binary compatibility and translate them on the
|
||||
way in and out of system calls, but there is almost always a `#define` that can
|
||||
get the real ones.
|
||||
See `types_darwin.go` and `linux/types.go` for examples.
|
||||
|
||||
To add a new type, add in the necessary include statement at the top of the
|
||||
file (if it is not already there) and add in a type alias line. Note that if
|
||||
your type is significantly different on different architectures, you may need
|
||||
some `#if/#elif` macros in your include statements.
|
||||
|
||||
### mkerrors.sh
|
||||
|
||||
This script is used to generate the system's various constants. This doesn't
|
||||
just include the error numbers and error strings, but also the signal numbers
|
||||
and a wide variety of miscellaneous constants. The constants come from the list
|
||||
of include files in the `includes_${uname}` variable. A regex then picks out
|
||||
the desired `#define` statements, and generates the corresponding Go constants.
|
||||
The error numbers and strings are generated from `#include <errno.h>`, and the
|
||||
signal numbers and strings are generated from `#include <signal.h>`. All of
|
||||
these constants are written to `zerrors_${GOOS}_${GOARCH}.go` via a C program,
|
||||
`_errors.c`, which prints out all the constants.
|
||||
|
||||
To add a constant, add the header that includes it to the appropriate variable.
|
||||
Then, edit the regex (if necessary) to match the desired constant. Avoid making
|
||||
the regex too broad to avoid matching unintended constants.
|
||||
|
||||
### internal/mkmerge
|
||||
|
||||
This program is used to extract duplicate const, func, and type declarations
|
||||
from the generated architecture-specific files listed below, and merge these
|
||||
into a common file for each OS.
|
||||
|
||||
The merge is performed in the following steps:
|
||||
1. Construct the set of common code that is identical in all architecture-specific files.
|
||||
2. Write this common code to the merged file.
|
||||
3. Remove the common code from all architecture-specific files.
|
||||
|
||||
|
||||
## Generated files
|
||||
|
||||
### `zerrors_${GOOS}_${GOARCH}.go`
|
||||
|
||||
A file containing all of the system's generated error numbers, error strings,
|
||||
signal numbers, and constants. Generated by `mkerrors.sh` (see above).
|
||||
|
||||
### `zsyscall_${GOOS}_${GOARCH}.go`
|
||||
|
||||
A file containing all the generated syscalls for a specific GOOS and GOARCH.
|
||||
Generated by `mksyscall.go` (see above).
|
||||
|
||||
### `zsysnum_${GOOS}_${GOARCH}.go`
|
||||
|
||||
A list of numeric constants for all the syscall number of the specific GOOS
|
||||
and GOARCH. Generated by mksysnum (see above).
|
||||
|
||||
### `ztypes_${GOOS}_${GOARCH}.go`
|
||||
|
||||
A file containing Go types for passing into (or returning from) syscalls.
|
||||
Generated by godefs and the types file (see above).
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// CPU affinity functions
|
||||
|
||||
package unix
|
||||
|
||||
import (
|
||||
"math/bits"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const cpuSetSize = _CPU_SETSIZE / _NCPUBITS
|
||||
|
||||
// CPUSet represents a CPU affinity mask.
|
||||
type CPUSet [cpuSetSize]cpuMask
|
||||
|
||||
func schedAffinity(trap uintptr, pid int, set *CPUSet) error {
|
||||
_, _, e := RawSyscall(trap, uintptr(pid), uintptr(unsafe.Sizeof(*set)), uintptr(unsafe.Pointer(set)))
|
||||
if e != 0 {
|
||||
return errnoErr(e)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SchedGetaffinity gets the CPU affinity mask of the thread specified by pid.
|
||||
// If pid is 0 the calling thread is used.
|
||||
func SchedGetaffinity(pid int, set *CPUSet) error {
|
||||
return schedAffinity(SYS_SCHED_GETAFFINITY, pid, set)
|
||||
}
|
||||
|
||||
// SchedSetaffinity sets the CPU affinity mask of the thread specified by pid.
|
||||
// If pid is 0 the calling thread is used.
|
||||
func SchedSetaffinity(pid int, set *CPUSet) error {
|
||||
return schedAffinity(SYS_SCHED_SETAFFINITY, pid, set)
|
||||
}
|
||||
|
||||
// Zero clears the set s, so that it contains no CPUs.
|
||||
func (s *CPUSet) Zero() {
|
||||
clear(s[:])
|
||||
}
|
||||
|
||||
// Fill adds all possible CPU bits to the set s. On Linux, [SchedSetaffinity]
|
||||
// will silently ignore any invalid CPU bits in [CPUSet] so this is an
|
||||
// efficient way of resetting the CPU affinity of a process.
|
||||
func (s *CPUSet) Fill() {
|
||||
for i := range s {
|
||||
s[i] = ^cpuMask(0)
|
||||
}
|
||||
}
|
||||
|
||||
func cpuBitsIndex(cpu int) int {
|
||||
return cpu / _NCPUBITS
|
||||
}
|
||||
|
||||
func cpuBitsMask(cpu int) cpuMask {
|
||||
return cpuMask(1 << (uint(cpu) % _NCPUBITS))
|
||||
}
|
||||
|
||||
// Set adds cpu to the set s.
|
||||
func (s *CPUSet) Set(cpu int) {
|
||||
i := cpuBitsIndex(cpu)
|
||||
if i < len(s) {
|
||||
s[i] |= cpuBitsMask(cpu)
|
||||
}
|
||||
}
|
||||
|
||||
// Clear removes cpu from the set s.
|
||||
func (s *CPUSet) Clear(cpu int) {
|
||||
i := cpuBitsIndex(cpu)
|
||||
if i < len(s) {
|
||||
s[i] &^= cpuBitsMask(cpu)
|
||||
}
|
||||
}
|
||||
|
||||
// IsSet reports whether cpu is in the set s.
|
||||
func (s *CPUSet) IsSet(cpu int) bool {
|
||||
i := cpuBitsIndex(cpu)
|
||||
if i < len(s) {
|
||||
return s[i]&cpuBitsMask(cpu) != 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Count returns the number of CPUs in the set s.
|
||||
func (s *CPUSet) Count() int {
|
||||
c := 0
|
||||
for _, b := range s {
|
||||
c += bits.OnesCount64(uint64(b))
|
||||
}
|
||||
return c
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
|
||||
|
||||
package unix
|
||||
|
||||
import "syscall"
|
||||
|
||||
type Signal = syscall.Signal
|
||||
type Errno = syscall.Errno
|
||||
type SysProcAttr = syscall.SysProcAttr
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System calls for ppc64, AIX are implemented in runtime/syscall_aix.go
|
||||
//
|
||||
|
||||
TEXT ·syscall6(SB),NOSPLIT,$0-88
|
||||
JMP syscall·syscall6(SB)
|
||||
|
||||
TEXT ·rawSyscall6(SB),NOSPLIT,$0-88
|
||||
JMP syscall·rawSyscall6(SB)
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (freebsd || netbsd || openbsd) && gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// System call support for 386 BSD
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||
JMP syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (darwin || dragonfly || freebsd || netbsd || openbsd) && gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// System call support for AMD64 BSD
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||
JMP syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (freebsd || netbsd || openbsd) && gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// System call support for ARM BSD
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||
B syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||
B syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||
B syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
B syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
B syscall·RawSyscall6(SB)
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (darwin || freebsd || netbsd || openbsd) && gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// System call support for ARM64 BSD
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||
JMP syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (darwin || freebsd || netbsd || openbsd) && gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for ppc64, BSD
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||
JMP syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (darwin || freebsd || netbsd || openbsd) && gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// System call support for RISCV64 BSD
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||
JMP syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System calls for 386, Linux
|
||||
//
|
||||
|
||||
// See ../runtime/sys_linux_386.s for the reason why we always use int 0x80
|
||||
// instead of the glibc-specific "CALL 0x10(GS)".
|
||||
#define INVOKE_SYSCALL INT $0x80
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-24
|
||||
CALL runtime·entersyscall(SB)
|
||||
MOVL trap+0(FP), AX // syscall entry
|
||||
MOVL a1+4(FP), BX
|
||||
MOVL a2+8(FP), CX
|
||||
MOVL a3+12(FP), DX
|
||||
MOVL $0, SI
|
||||
MOVL $0, DI
|
||||
INVOKE_SYSCALL
|
||||
MOVL AX, r1+16(FP)
|
||||
MOVL DX, r2+20(FP)
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-24
|
||||
MOVL trap+0(FP), AX // syscall entry
|
||||
MOVL a1+4(FP), BX
|
||||
MOVL a2+8(FP), CX
|
||||
MOVL a3+12(FP), DX
|
||||
MOVL $0, SI
|
||||
MOVL $0, DI
|
||||
INVOKE_SYSCALL
|
||||
MOVL AX, r1+16(FP)
|
||||
MOVL DX, r2+20(FP)
|
||||
RET
|
||||
|
||||
TEXT ·socketcall(SB),NOSPLIT,$0-36
|
||||
JMP syscall·socketcall(SB)
|
||||
|
||||
TEXT ·rawsocketcall(SB),NOSPLIT,$0-36
|
||||
JMP syscall·rawsocketcall(SB)
|
||||
|
||||
TEXT ·seek(SB),NOSPLIT,$0-28
|
||||
JMP syscall·seek(SB)
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System calls for AMD64, Linux
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||
CALL runtime·entersyscall(SB)
|
||||
MOVQ a1+8(FP), DI
|
||||
MOVQ a2+16(FP), SI
|
||||
MOVQ a3+24(FP), DX
|
||||
MOVQ $0, R10
|
||||
MOVQ $0, R8
|
||||
MOVQ $0, R9
|
||||
MOVQ trap+0(FP), AX // syscall entry
|
||||
SYSCALL
|
||||
MOVQ AX, r1+32(FP)
|
||||
MOVQ DX, r2+40(FP)
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||
MOVQ a1+8(FP), DI
|
||||
MOVQ a2+16(FP), SI
|
||||
MOVQ a3+24(FP), DX
|
||||
MOVQ $0, R10
|
||||
MOVQ $0, R8
|
||||
MOVQ $0, R9
|
||||
MOVQ trap+0(FP), AX // syscall entry
|
||||
SYSCALL
|
||||
MOVQ AX, r1+32(FP)
|
||||
MOVQ DX, r2+40(FP)
|
||||
RET
|
||||
|
||||
TEXT ·gettimeofday(SB),NOSPLIT,$0-16
|
||||
JMP syscall·gettimeofday(SB)
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System calls for arm, Linux
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||
B syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||
B syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-24
|
||||
BL runtime·entersyscall(SB)
|
||||
MOVW trap+0(FP), R7
|
||||
MOVW a1+4(FP), R0
|
||||
MOVW a2+8(FP), R1
|
||||
MOVW a3+12(FP), R2
|
||||
MOVW $0, R3
|
||||
MOVW $0, R4
|
||||
MOVW $0, R5
|
||||
SWI $0
|
||||
MOVW R0, r1+16(FP)
|
||||
MOVW $0, R0
|
||||
MOVW R0, r2+20(FP)
|
||||
BL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
B syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
B syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-24
|
||||
MOVW trap+0(FP), R7 // syscall entry
|
||||
MOVW a1+4(FP), R0
|
||||
MOVW a2+8(FP), R1
|
||||
MOVW a3+12(FP), R2
|
||||
SWI $0
|
||||
MOVW R0, r1+16(FP)
|
||||
MOVW $0, R0
|
||||
MOVW R0, r2+20(FP)
|
||||
RET
|
||||
|
||||
TEXT ·seek(SB),NOSPLIT,$0-28
|
||||
B syscall·seek(SB)
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux && arm64 && gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
B syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
B syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||
BL runtime·entersyscall(SB)
|
||||
MOVD a1+8(FP), R0
|
||||
MOVD a2+16(FP), R1
|
||||
MOVD a3+24(FP), R2
|
||||
MOVD $0, R3
|
||||
MOVD $0, R4
|
||||
MOVD $0, R5
|
||||
MOVD trap+0(FP), R8 // syscall entry
|
||||
SVC
|
||||
MOVD R0, r1+32(FP) // r1
|
||||
MOVD R1, r2+40(FP) // r2
|
||||
BL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
B syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
B syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||
MOVD a1+8(FP), R0
|
||||
MOVD a2+16(FP), R1
|
||||
MOVD a3+24(FP), R2
|
||||
MOVD $0, R3
|
||||
MOVD $0, R4
|
||||
MOVD $0, R5
|
||||
MOVD trap+0(FP), R8 // syscall entry
|
||||
SVC
|
||||
MOVD R0, r1+32(FP)
|
||||
MOVD R1, r2+40(FP)
|
||||
RET
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux && loong64 && gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||
JAL runtime·entersyscall(SB)
|
||||
MOVV a1+8(FP), R4
|
||||
MOVV a2+16(FP), R5
|
||||
MOVV a3+24(FP), R6
|
||||
MOVV R0, R7
|
||||
MOVV R0, R8
|
||||
MOVV R0, R9
|
||||
MOVV trap+0(FP), R11 // syscall entry
|
||||
SYSCALL
|
||||
MOVV R4, r1+32(FP)
|
||||
MOVV R0, r2+40(FP) // r2 is not used. Always set to 0
|
||||
JAL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||
MOVV a1+8(FP), R4
|
||||
MOVV a2+16(FP), R5
|
||||
MOVV a3+24(FP), R6
|
||||
MOVV R0, R7
|
||||
MOVV R0, R8
|
||||
MOVV R0, R9
|
||||
MOVV trap+0(FP), R11 // syscall entry
|
||||
SYSCALL
|
||||
MOVV R4, r1+32(FP)
|
||||
MOVV R0, r2+40(FP) // r2 is not used. Always set to 0
|
||||
RET
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux && (mips64 || mips64le) && gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System calls for mips64, Linux
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||
JAL runtime·entersyscall(SB)
|
||||
MOVV a1+8(FP), R4
|
||||
MOVV a2+16(FP), R5
|
||||
MOVV a3+24(FP), R6
|
||||
MOVV R0, R7
|
||||
MOVV R0, R8
|
||||
MOVV R0, R9
|
||||
MOVV trap+0(FP), R2 // syscall entry
|
||||
SYSCALL
|
||||
MOVV R2, r1+32(FP)
|
||||
MOVV R3, r2+40(FP)
|
||||
JAL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||
MOVV a1+8(FP), R4
|
||||
MOVV a2+16(FP), R5
|
||||
MOVV a3+24(FP), R6
|
||||
MOVV R0, R7
|
||||
MOVV R0, R8
|
||||
MOVV R0, R9
|
||||
MOVV trap+0(FP), R2 // syscall entry
|
||||
SYSCALL
|
||||
MOVV R2, r1+32(FP)
|
||||
MOVV R3, r2+40(FP)
|
||||
RET
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux && (mips || mipsle) && gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System calls for mips, Linux
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||
JMP syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-24
|
||||
JAL runtime·entersyscall(SB)
|
||||
MOVW a1+4(FP), R4
|
||||
MOVW a2+8(FP), R5
|
||||
MOVW a3+12(FP), R6
|
||||
MOVW R0, R7
|
||||
MOVW trap+0(FP), R2 // syscall entry
|
||||
SYSCALL
|
||||
MOVW R2, r1+16(FP) // r1
|
||||
MOVW R3, r2+20(FP) // r2
|
||||
JAL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-24
|
||||
MOVW a1+4(FP), R4
|
||||
MOVW a2+8(FP), R5
|
||||
MOVW a3+12(FP), R6
|
||||
MOVW trap+0(FP), R2 // syscall entry
|
||||
SYSCALL
|
||||
MOVW R2, r1+16(FP)
|
||||
MOVW R3, r2+20(FP)
|
||||
RET
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux && (ppc64 || ppc64le) && gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System calls for ppc64, Linux
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||
BL runtime·entersyscall(SB)
|
||||
MOVD a1+8(FP), R3
|
||||
MOVD a2+16(FP), R4
|
||||
MOVD a3+24(FP), R5
|
||||
MOVD R0, R6
|
||||
MOVD R0, R7
|
||||
MOVD R0, R8
|
||||
MOVD trap+0(FP), R9 // syscall entry
|
||||
SYSCALL R9
|
||||
MOVD R3, r1+32(FP)
|
||||
MOVD R4, r2+40(FP)
|
||||
BL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||
MOVD a1+8(FP), R3
|
||||
MOVD a2+16(FP), R4
|
||||
MOVD a3+24(FP), R5
|
||||
MOVD R0, R6
|
||||
MOVD R0, R7
|
||||
MOVD R0, R8
|
||||
MOVD trap+0(FP), R9 // syscall entry
|
||||
SYSCALL R9
|
||||
MOVD R3, r1+32(FP)
|
||||
MOVD R4, r2+40(FP)
|
||||
RET
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build riscv64 && gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System calls for linux/riscv64.
|
||||
//
|
||||
// Where available, just jump to package syscall's implementation of
|
||||
// these functions.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||
CALL runtime·entersyscall(SB)
|
||||
MOV a1+8(FP), A0
|
||||
MOV a2+16(FP), A1
|
||||
MOV a3+24(FP), A2
|
||||
MOV trap+0(FP), A7 // syscall entry
|
||||
ECALL
|
||||
MOV A0, r1+32(FP) // r1
|
||||
MOV A1, r2+40(FP) // r2
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||
MOV a1+8(FP), A0
|
||||
MOV a2+16(FP), A1
|
||||
MOV a3+24(FP), A2
|
||||
MOV trap+0(FP), A7 // syscall entry
|
||||
ECALL
|
||||
MOV A0, r1+32(FP)
|
||||
MOV A1, r2+40(FP)
|
||||
RET
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux && s390x && gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System calls for s390x, Linux
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
BR syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
BR syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||
BL runtime·entersyscall(SB)
|
||||
MOVD a1+8(FP), R2
|
||||
MOVD a2+16(FP), R3
|
||||
MOVD a3+24(FP), R4
|
||||
MOVD $0, R5
|
||||
MOVD $0, R6
|
||||
MOVD $0, R7
|
||||
MOVD trap+0(FP), R1 // syscall entry
|
||||
SYSCALL
|
||||
MOVD R2, r1+32(FP)
|
||||
MOVD R3, r2+40(FP)
|
||||
BL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
BR syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
BR syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||
MOVD a1+8(FP), R2
|
||||
MOVD a2+16(FP), R3
|
||||
MOVD a3+24(FP), R4
|
||||
MOVD $0, R5
|
||||
MOVD $0, R6
|
||||
MOVD $0, R7
|
||||
MOVD trap+0(FP), R1 // syscall entry
|
||||
SYSCALL
|
||||
MOVD R2, r1+32(FP)
|
||||
MOVD R3, r2+40(FP)
|
||||
RET
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for mips64, OpenBSD
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||
JMP syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System calls for amd64, Solaris are implemented in runtime/syscall_solaris.go
|
||||
//
|
||||
|
||||
TEXT ·sysvicall6(SB),NOSPLIT,$0-88
|
||||
JMP syscall·sysvicall6(SB)
|
||||
|
||||
TEXT ·rawSysvicall6(SB),NOSPLIT,$0-88
|
||||
JMP syscall·rawSysvicall6(SB)
|
||||
+382
@@ -0,0 +1,382 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build zos && s390x && gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
#define PSALAA 1208(R0)
|
||||
#define GTAB64(x) 80(x)
|
||||
#define LCA64(x) 88(x)
|
||||
#define SAVSTACK_ASYNC(x) 336(x) // in the LCA
|
||||
#define CAA(x) 8(x)
|
||||
#define CEECAATHDID(x) 976(x) // in the CAA
|
||||
#define EDCHPXV(x) 1016(x) // in the CAA
|
||||
#define GOCB(x) 1104(x) // in the CAA
|
||||
|
||||
// SS_*, where x=SAVSTACK_ASYNC
|
||||
#define SS_LE(x) 0(x)
|
||||
#define SS_GO(x) 8(x)
|
||||
#define SS_ERRNO(x) 16(x)
|
||||
#define SS_ERRNOJR(x) 20(x)
|
||||
|
||||
// Function Descriptor Offsets
|
||||
#define __errno 0x156*16
|
||||
#define __err2ad 0x16C*16
|
||||
|
||||
// Call Instructions
|
||||
#define LE_CALL BYTE $0x0D; BYTE $0x76 // BL R7, R6
|
||||
#define SVC_LOAD BYTE $0x0A; BYTE $0x08 // SVC 08 LOAD
|
||||
#define SVC_DELETE BYTE $0x0A; BYTE $0x09 // SVC 09 DELETE
|
||||
|
||||
DATA zosLibVec<>(SB)/8, $0
|
||||
GLOBL zosLibVec<>(SB), NOPTR, $8
|
||||
|
||||
TEXT ·initZosLibVec(SB), NOSPLIT|NOFRAME, $0-0
|
||||
MOVW PSALAA, R8
|
||||
MOVD LCA64(R8), R8
|
||||
MOVD CAA(R8), R8
|
||||
MOVD EDCHPXV(R8), R8
|
||||
MOVD R8, zosLibVec<>(SB)
|
||||
RET
|
||||
|
||||
TEXT ·GetZosLibVec(SB), NOSPLIT|NOFRAME, $0-0
|
||||
MOVD zosLibVec<>(SB), R8
|
||||
MOVD R8, ret+0(FP)
|
||||
RET
|
||||
|
||||
TEXT ·clearErrno(SB), NOSPLIT, $0-0
|
||||
BL addrerrno<>(SB)
|
||||
MOVD $0, 0(R3)
|
||||
RET
|
||||
|
||||
// Returns the address of errno in R3.
|
||||
TEXT addrerrno<>(SB), NOSPLIT|NOFRAME, $0-0
|
||||
// Get library control area (LCA).
|
||||
MOVW PSALAA, R8
|
||||
MOVD LCA64(R8), R8
|
||||
|
||||
// Get __errno FuncDesc.
|
||||
MOVD CAA(R8), R9
|
||||
MOVD EDCHPXV(R9), R9
|
||||
ADD $(__errno), R9
|
||||
LMG 0(R9), R5, R6
|
||||
|
||||
// Switch to saved LE stack.
|
||||
MOVD SAVSTACK_ASYNC(R8), R9
|
||||
MOVD 0(R9), R4
|
||||
MOVD $0, 0(R9)
|
||||
|
||||
// Call __errno function.
|
||||
LE_CALL
|
||||
NOPH
|
||||
|
||||
// Switch back to Go stack.
|
||||
XOR R0, R0 // Restore R0 to $0.
|
||||
MOVD R4, 0(R9) // Save stack pointer.
|
||||
RET
|
||||
|
||||
// func svcCall(fnptr unsafe.Pointer, argv *unsafe.Pointer, dsa *uint64)
|
||||
TEXT ·svcCall(SB), NOSPLIT, $0
|
||||
BL runtime·save_g(SB) // Save g and stack pointer
|
||||
MOVW PSALAA, R8
|
||||
MOVD LCA64(R8), R8
|
||||
MOVD SAVSTACK_ASYNC(R8), R9
|
||||
MOVD R15, 0(R9)
|
||||
|
||||
MOVD argv+8(FP), R1 // Move function arguments into registers
|
||||
MOVD dsa+16(FP), g
|
||||
MOVD fnptr+0(FP), R15
|
||||
|
||||
BYTE $0x0D // Branch to function
|
||||
BYTE $0xEF
|
||||
|
||||
BL runtime·load_g(SB) // Restore g and stack pointer
|
||||
MOVW PSALAA, R8
|
||||
MOVD LCA64(R8), R8
|
||||
MOVD SAVSTACK_ASYNC(R8), R9
|
||||
MOVD 0(R9), R15
|
||||
|
||||
RET
|
||||
|
||||
// func svcLoad(name *byte) unsafe.Pointer
|
||||
TEXT ·svcLoad(SB), NOSPLIT, $0
|
||||
MOVD R15, R2 // Save go stack pointer
|
||||
MOVD name+0(FP), R0 // Move SVC args into registers
|
||||
MOVD $0x80000000, R1
|
||||
MOVD $0, R15
|
||||
SVC_LOAD
|
||||
MOVW R15, R3 // Save return code from SVC
|
||||
MOVD R2, R15 // Restore go stack pointer
|
||||
CMP R3, $0 // Check SVC return code
|
||||
BNE error
|
||||
|
||||
MOVD $-2, R3 // Reset last bit of entry point to zero
|
||||
AND R0, R3
|
||||
MOVD R3, ret+8(FP) // Return entry point returned by SVC
|
||||
CMP R0, R3 // Check if last bit of entry point was set
|
||||
BNE done
|
||||
|
||||
MOVD R15, R2 // Save go stack pointer
|
||||
MOVD $0, R15 // Move SVC args into registers (entry point still in r0 from SVC 08)
|
||||
SVC_DELETE
|
||||
MOVD R2, R15 // Restore go stack pointer
|
||||
|
||||
error:
|
||||
MOVD $0, ret+8(FP) // Return 0 on failure
|
||||
|
||||
done:
|
||||
XOR R0, R0 // Reset r0 to 0
|
||||
RET
|
||||
|
||||
// func svcUnload(name *byte, fnptr unsafe.Pointer) int64
|
||||
TEXT ·svcUnload(SB), NOSPLIT, $0
|
||||
MOVD R15, R2 // Save go stack pointer
|
||||
MOVD name+0(FP), R0 // Move SVC args into registers
|
||||
MOVD fnptr+8(FP), R15
|
||||
SVC_DELETE
|
||||
XOR R0, R0 // Reset r0 to 0
|
||||
MOVD R15, R1 // Save SVC return code
|
||||
MOVD R2, R15 // Restore go stack pointer
|
||||
MOVD R1, ret+16(FP) // Return SVC return code
|
||||
RET
|
||||
|
||||
// func gettid() uint64
|
||||
TEXT ·gettid(SB), NOSPLIT, $0
|
||||
// Get library control area (LCA).
|
||||
MOVW PSALAA, R8
|
||||
MOVD LCA64(R8), R8
|
||||
|
||||
// Get CEECAATHDID
|
||||
MOVD CAA(R8), R9
|
||||
MOVD CEECAATHDID(R9), R9
|
||||
MOVD R9, ret+0(FP)
|
||||
|
||||
RET
|
||||
|
||||
//
|
||||
// Call LE function, if the return is -1
|
||||
// errno and errno2 is retrieved
|
||||
//
|
||||
TEXT ·CallLeFuncWithErr(SB), NOSPLIT, $0
|
||||
MOVW PSALAA, R8
|
||||
MOVD LCA64(R8), R8
|
||||
MOVD CAA(R8), R9
|
||||
MOVD g, GOCB(R9)
|
||||
|
||||
// Restore LE stack.
|
||||
MOVD SAVSTACK_ASYNC(R8), R9 // R9-> LE stack frame saving address
|
||||
MOVD 0(R9), R4 // R4-> restore previously saved stack frame pointer
|
||||
|
||||
MOVD parms_base+8(FP), R7 // R7 -> argument array
|
||||
MOVD parms_len+16(FP), R8 // R8 number of arguments
|
||||
|
||||
// arg 1 ---> R1
|
||||
CMP R8, $0
|
||||
BEQ docall
|
||||
SUB $1, R8
|
||||
MOVD 0(R7), R1
|
||||
|
||||
// arg 2 ---> R2
|
||||
CMP R8, $0
|
||||
BEQ docall
|
||||
SUB $1, R8
|
||||
ADD $8, R7
|
||||
MOVD 0(R7), R2
|
||||
|
||||
// arg 3 --> R3
|
||||
CMP R8, $0
|
||||
BEQ docall
|
||||
SUB $1, R8
|
||||
ADD $8, R7
|
||||
MOVD 0(R7), R3
|
||||
|
||||
CMP R8, $0
|
||||
BEQ docall
|
||||
MOVD $2176+16, R6 // starting LE stack address-8 to store 4th argument
|
||||
|
||||
repeat:
|
||||
ADD $8, R7
|
||||
MOVD 0(R7), R0 // advance arg pointer by 8 byte
|
||||
ADD $8, R6 // advance LE argument address by 8 byte
|
||||
MOVD R0, (R4)(R6*1) // copy argument from go-slice to le-frame
|
||||
SUB $1, R8
|
||||
CMP R8, $0
|
||||
BNE repeat
|
||||
|
||||
docall:
|
||||
MOVD funcdesc+0(FP), R8 // R8-> function descriptor
|
||||
LMG 0(R8), R5, R6
|
||||
MOVD $0, 0(R9) // R9 address of SAVSTACK_ASYNC
|
||||
LE_CALL // balr R7, R6 (return #1)
|
||||
NOPH
|
||||
MOVD R3, ret+32(FP)
|
||||
CMP R3, $-1 // compare result to -1
|
||||
BNE done
|
||||
|
||||
// retrieve errno and errno2
|
||||
MOVD zosLibVec<>(SB), R8
|
||||
ADD $(__errno), R8
|
||||
LMG 0(R8), R5, R6
|
||||
LE_CALL // balr R7, R6 __errno (return #3)
|
||||
NOPH
|
||||
MOVWZ 0(R3), R3
|
||||
MOVD R3, err+48(FP)
|
||||
MOVD zosLibVec<>(SB), R8
|
||||
ADD $(__err2ad), R8
|
||||
LMG 0(R8), R5, R6
|
||||
LE_CALL // balr R7, R6 __err2ad (return #2)
|
||||
NOPH
|
||||
MOVW (R3), R2 // retrieve errno2
|
||||
MOVD R2, errno2+40(FP) // store in return area
|
||||
|
||||
done:
|
||||
MOVD R4, 0(R9) // Save stack pointer.
|
||||
RET
|
||||
|
||||
//
|
||||
// Call LE function, if the return is 0
|
||||
// errno and errno2 is retrieved
|
||||
//
|
||||
TEXT ·CallLeFuncWithPtrReturn(SB), NOSPLIT, $0
|
||||
MOVW PSALAA, R8
|
||||
MOVD LCA64(R8), R8
|
||||
MOVD CAA(R8), R9
|
||||
MOVD g, GOCB(R9)
|
||||
|
||||
// Restore LE stack.
|
||||
MOVD SAVSTACK_ASYNC(R8), R9 // R9-> LE stack frame saving address
|
||||
MOVD 0(R9), R4 // R4-> restore previously saved stack frame pointer
|
||||
|
||||
MOVD parms_base+8(FP), R7 // R7 -> argument array
|
||||
MOVD parms_len+16(FP), R8 // R8 number of arguments
|
||||
|
||||
// arg 1 ---> R1
|
||||
CMP R8, $0
|
||||
BEQ docall
|
||||
SUB $1, R8
|
||||
MOVD 0(R7), R1
|
||||
|
||||
// arg 2 ---> R2
|
||||
CMP R8, $0
|
||||
BEQ docall
|
||||
SUB $1, R8
|
||||
ADD $8, R7
|
||||
MOVD 0(R7), R2
|
||||
|
||||
// arg 3 --> R3
|
||||
CMP R8, $0
|
||||
BEQ docall
|
||||
SUB $1, R8
|
||||
ADD $8, R7
|
||||
MOVD 0(R7), R3
|
||||
|
||||
CMP R8, $0
|
||||
BEQ docall
|
||||
MOVD $2176+16, R6 // starting LE stack address-8 to store 4th argument
|
||||
|
||||
repeat:
|
||||
ADD $8, R7
|
||||
MOVD 0(R7), R0 // advance arg pointer by 8 byte
|
||||
ADD $8, R6 // advance LE argument address by 8 byte
|
||||
MOVD R0, (R4)(R6*1) // copy argument from go-slice to le-frame
|
||||
SUB $1, R8
|
||||
CMP R8, $0
|
||||
BNE repeat
|
||||
|
||||
docall:
|
||||
MOVD funcdesc+0(FP), R8 // R8-> function descriptor
|
||||
LMG 0(R8), R5, R6
|
||||
MOVD $0, 0(R9) // R9 address of SAVSTACK_ASYNC
|
||||
LE_CALL // balr R7, R6 (return #1)
|
||||
NOPH
|
||||
MOVD R3, ret+32(FP)
|
||||
CMP R3, $0 // compare result to 0
|
||||
BNE done
|
||||
|
||||
// retrieve errno and errno2
|
||||
MOVD zosLibVec<>(SB), R8
|
||||
ADD $(__errno), R8
|
||||
LMG 0(R8), R5, R6
|
||||
LE_CALL // balr R7, R6 __errno (return #3)
|
||||
NOPH
|
||||
MOVWZ 0(R3), R3
|
||||
MOVD R3, err+48(FP)
|
||||
MOVD zosLibVec<>(SB), R8
|
||||
ADD $(__err2ad), R8
|
||||
LMG 0(R8), R5, R6
|
||||
LE_CALL // balr R7, R6 __err2ad (return #2)
|
||||
NOPH
|
||||
MOVW (R3), R2 // retrieve errno2
|
||||
MOVD R2, errno2+40(FP) // store in return area
|
||||
XOR R2, R2
|
||||
MOVWZ R2, (R3) // clear errno2
|
||||
|
||||
done:
|
||||
MOVD R4, 0(R9) // Save stack pointer.
|
||||
RET
|
||||
|
||||
//
|
||||
// function to test if a pointer can be safely dereferenced (content read)
|
||||
// return 0 for succces
|
||||
//
|
||||
TEXT ·ptrtest(SB), NOSPLIT, $0-16
|
||||
MOVD arg+0(FP), R10 // test pointer in R10
|
||||
|
||||
// set up R2 to point to CEECAADMC
|
||||
BYTE $0xE3; BYTE $0x20; BYTE $0x04; BYTE $0xB8; BYTE $0x00; BYTE $0x17 // llgt 2,1208
|
||||
BYTE $0xB9; BYTE $0x17; BYTE $0x00; BYTE $0x22 // llgtr 2,2
|
||||
BYTE $0xA5; BYTE $0x26; BYTE $0x7F; BYTE $0xFF // nilh 2,32767
|
||||
BYTE $0xE3; BYTE $0x22; BYTE $0x00; BYTE $0x58; BYTE $0x00; BYTE $0x04 // lg 2,88(2)
|
||||
BYTE $0xE3; BYTE $0x22; BYTE $0x00; BYTE $0x08; BYTE $0x00; BYTE $0x04 // lg 2,8(2)
|
||||
BYTE $0x41; BYTE $0x22; BYTE $0x03; BYTE $0x68 // la 2,872(2)
|
||||
|
||||
// set up R5 to point to the "shunt" path which set 1 to R3 (failure)
|
||||
BYTE $0xB9; BYTE $0x82; BYTE $0x00; BYTE $0x33 // xgr 3,3
|
||||
BYTE $0xA7; BYTE $0x55; BYTE $0x00; BYTE $0x04 // bras 5,lbl1
|
||||
BYTE $0xA7; BYTE $0x39; BYTE $0x00; BYTE $0x01 // lghi 3,1
|
||||
|
||||
// if r3 is not zero (failed) then branch to finish
|
||||
BYTE $0xB9; BYTE $0x02; BYTE $0x00; BYTE $0x33 // lbl1 ltgr 3,3
|
||||
BYTE $0xA7; BYTE $0x74; BYTE $0x00; BYTE $0x08 // brc b'0111',lbl2
|
||||
|
||||
// stomic store shunt address in R5 into CEECAADMC
|
||||
BYTE $0xE3; BYTE $0x52; BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x24 // stg 5,0(2)
|
||||
|
||||
// now try reading from the test pointer in R10, if it fails it branches to the "lghi" instruction above
|
||||
BYTE $0xE3; BYTE $0x9A; BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x04 // lg 9,0(10)
|
||||
|
||||
// finish here, restore 0 into CEECAADMC
|
||||
BYTE $0xB9; BYTE $0x82; BYTE $0x00; BYTE $0x99 // lbl2 xgr 9,9
|
||||
BYTE $0xE3; BYTE $0x92; BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x24 // stg 9,0(2)
|
||||
MOVD R3, ret+8(FP) // result in R3
|
||||
RET
|
||||
|
||||
//
|
||||
// function to test if a untptr can be loaded from a pointer
|
||||
// return 1: the 8-byte content
|
||||
// 2: 0 for success, 1 for failure
|
||||
//
|
||||
// func safeload(ptr uintptr) ( value uintptr, error uintptr)
|
||||
TEXT ·safeload(SB), NOSPLIT, $0-24
|
||||
MOVD ptr+0(FP), R10 // test pointer in R10
|
||||
MOVD $0x0, R6
|
||||
BYTE $0xE3; BYTE $0x20; BYTE $0x04; BYTE $0xB8; BYTE $0x00; BYTE $0x17 // llgt 2,1208
|
||||
BYTE $0xB9; BYTE $0x17; BYTE $0x00; BYTE $0x22 // llgtr 2,2
|
||||
BYTE $0xA5; BYTE $0x26; BYTE $0x7F; BYTE $0xFF // nilh 2,32767
|
||||
BYTE $0xE3; BYTE $0x22; BYTE $0x00; BYTE $0x58; BYTE $0x00; BYTE $0x04 // lg 2,88(2)
|
||||
BYTE $0xE3; BYTE $0x22; BYTE $0x00; BYTE $0x08; BYTE $0x00; BYTE $0x04 // lg 2,8(2)
|
||||
BYTE $0x41; BYTE $0x22; BYTE $0x03; BYTE $0x68 // la 2,872(2)
|
||||
BYTE $0xB9; BYTE $0x82; BYTE $0x00; BYTE $0x33 // xgr 3,3
|
||||
BYTE $0xA7; BYTE $0x55; BYTE $0x00; BYTE $0x04 // bras 5,lbl1
|
||||
BYTE $0xA7; BYTE $0x39; BYTE $0x00; BYTE $0x01 // lghi 3,1
|
||||
BYTE $0xB9; BYTE $0x02; BYTE $0x00; BYTE $0x33 // lbl1 ltgr 3,3
|
||||
BYTE $0xA7; BYTE $0x74; BYTE $0x00; BYTE $0x08 // brc b'0111',lbl2
|
||||
BYTE $0xE3; BYTE $0x52; BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x24 // stg 5,0(2)
|
||||
BYTE $0xE3; BYTE $0x6A; BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x04 // lg 6,0(10)
|
||||
BYTE $0xB9; BYTE $0x82; BYTE $0x00; BYTE $0x99 // lbl2 xgr 9,9
|
||||
BYTE $0xE3; BYTE $0x92; BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x24 // stg 9,0(2)
|
||||
MOVD R6, value+8(FP) // result in R6
|
||||
MOVD R3, error+16(FP) // error in R3
|
||||
RET
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// Copyright 2025 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.21 && (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos)
|
||||
|
||||
package unix
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//go:linkname runtime_getAuxv runtime.getAuxv
|
||||
func runtime_getAuxv() []uintptr
|
||||
|
||||
// Auxv returns the ELF auxiliary vector as a sequence of key/value pairs.
|
||||
// The returned slice is always a fresh copy, owned by the caller.
|
||||
// It returns an error on non-ELF platforms, or if the auxiliary vector cannot be accessed,
|
||||
// which happens in some locked-down environments and build modes.
|
||||
func Auxv() ([][2]uintptr, error) {
|
||||
vec := runtime_getAuxv()
|
||||
vecLen := len(vec)
|
||||
|
||||
if vecLen == 0 {
|
||||
return nil, syscall.ENOENT
|
||||
}
|
||||
|
||||
if vecLen%2 != 0 {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
|
||||
result := make([]uintptr, vecLen)
|
||||
copy(result, vec)
|
||||
return unsafe.Slice((*[2]uintptr)(unsafe.Pointer(&result[0])), vecLen/2), nil
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// Copyright 2025 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.21 && (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos)
|
||||
|
||||
package unix
|
||||
|
||||
import "syscall"
|
||||
|
||||
func Auxv() ([][2]uintptr, error) {
|
||||
return nil, syscall.ENOTSUP
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Bluetooth sockets and messages
|
||||
|
||||
package unix
|
||||
|
||||
// Bluetooth Protocols
|
||||
const (
|
||||
BTPROTO_L2CAP = 0
|
||||
BTPROTO_HCI = 1
|
||||
BTPROTO_SCO = 2
|
||||
BTPROTO_RFCOMM = 3
|
||||
BTPROTO_BNEP = 4
|
||||
BTPROTO_CMTP = 5
|
||||
BTPROTO_HIDP = 6
|
||||
BTPROTO_AVDTP = 7
|
||||
)
|
||||
|
||||
const (
|
||||
HCI_CHANNEL_RAW = 0
|
||||
HCI_CHANNEL_USER = 1
|
||||
HCI_CHANNEL_MONITOR = 2
|
||||
HCI_CHANNEL_CONTROL = 3
|
||||
HCI_CHANNEL_LOGGING = 4
|
||||
)
|
||||
|
||||
// Socketoption Level
|
||||
const (
|
||||
SOL_BLUETOOTH = 0x112
|
||||
SOL_HCI = 0x0
|
||||
SOL_L2CAP = 0x6
|
||||
SOL_RFCOMM = 0x12
|
||||
SOL_SCO = 0x11
|
||||
)
|
||||
+657
@@ -0,0 +1,657 @@
|
||||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build zos
|
||||
|
||||
package unix
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//go:noescape
|
||||
func bpxcall(plist []unsafe.Pointer, bpx_offset int64)
|
||||
|
||||
//go:noescape
|
||||
func A2e([]byte)
|
||||
|
||||
//go:noescape
|
||||
func E2a([]byte)
|
||||
|
||||
const (
|
||||
BPX4STA = 192 // stat
|
||||
BPX4FST = 104 // fstat
|
||||
BPX4LST = 132 // lstat
|
||||
BPX4OPN = 156 // open
|
||||
BPX4CLO = 72 // close
|
||||
BPX4CHR = 500 // chattr
|
||||
BPX4FCR = 504 // fchattr
|
||||
BPX4LCR = 1180 // lchattr
|
||||
BPX4CTW = 492 // cond_timed_wait
|
||||
BPX4GTH = 1056 // __getthent
|
||||
BPX4PTQ = 412 // pthread_quiesc
|
||||
BPX4PTR = 320 // ptrace
|
||||
)
|
||||
|
||||
const (
|
||||
//options
|
||||
//byte1
|
||||
BPX_OPNFHIGH = 0x80
|
||||
//byte2
|
||||
BPX_OPNFEXEC = 0x80
|
||||
//byte3
|
||||
BPX_O_NOLARGEFILE = 0x08
|
||||
BPX_O_LARGEFILE = 0x04
|
||||
BPX_O_ASYNCSIG = 0x02
|
||||
BPX_O_SYNC = 0x01
|
||||
//byte4
|
||||
BPX_O_CREXCL = 0xc0
|
||||
BPX_O_CREAT = 0x80
|
||||
BPX_O_EXCL = 0x40
|
||||
BPX_O_NOCTTY = 0x20
|
||||
BPX_O_TRUNC = 0x10
|
||||
BPX_O_APPEND = 0x08
|
||||
BPX_O_NONBLOCK = 0x04
|
||||
BPX_FNDELAY = 0x04
|
||||
BPX_O_RDWR = 0x03
|
||||
BPX_O_RDONLY = 0x02
|
||||
BPX_O_WRONLY = 0x01
|
||||
BPX_O_ACCMODE = 0x03
|
||||
BPX_O_GETFL = 0x0f
|
||||
|
||||
//mode
|
||||
// byte1 (file type)
|
||||
BPX_FT_DIR = 1
|
||||
BPX_FT_CHARSPEC = 2
|
||||
BPX_FT_REGFILE = 3
|
||||
BPX_FT_FIFO = 4
|
||||
BPX_FT_SYMLINK = 5
|
||||
BPX_FT_SOCKET = 6
|
||||
//byte3
|
||||
BPX_S_ISUID = 0x08
|
||||
BPX_S_ISGID = 0x04
|
||||
BPX_S_ISVTX = 0x02
|
||||
BPX_S_IRWXU1 = 0x01
|
||||
BPX_S_IRUSR = 0x01
|
||||
//byte4
|
||||
BPX_S_IRWXU2 = 0xc0
|
||||
BPX_S_IWUSR = 0x80
|
||||
BPX_S_IXUSR = 0x40
|
||||
BPX_S_IRWXG = 0x38
|
||||
BPX_S_IRGRP = 0x20
|
||||
BPX_S_IWGRP = 0x10
|
||||
BPX_S_IXGRP = 0x08
|
||||
BPX_S_IRWXOX = 0x07
|
||||
BPX_S_IROTH = 0x04
|
||||
BPX_S_IWOTH = 0x02
|
||||
BPX_S_IXOTH = 0x01
|
||||
|
||||
CW_INTRPT = 1
|
||||
CW_CONDVAR = 32
|
||||
CW_TIMEOUT = 64
|
||||
|
||||
PGTHA_NEXT = 2
|
||||
PGTHA_CURRENT = 1
|
||||
PGTHA_FIRST = 0
|
||||
PGTHA_LAST = 3
|
||||
PGTHA_PROCESS = 0x80
|
||||
PGTHA_CONTTY = 0x40
|
||||
PGTHA_PATH = 0x20
|
||||
PGTHA_COMMAND = 0x10
|
||||
PGTHA_FILEDATA = 0x08
|
||||
PGTHA_THREAD = 0x04
|
||||
PGTHA_PTAG = 0x02
|
||||
PGTHA_COMMANDLONG = 0x01
|
||||
PGTHA_THREADFAST = 0x80
|
||||
PGTHA_FILEPATH = 0x40
|
||||
PGTHA_THDSIGMASK = 0x20
|
||||
// thread quiece mode
|
||||
QUIESCE_TERM int32 = 1
|
||||
QUIESCE_FORCE int32 = 2
|
||||
QUIESCE_QUERY int32 = 3
|
||||
QUIESCE_FREEZE int32 = 4
|
||||
QUIESCE_UNFREEZE int32 = 5
|
||||
FREEZE_THIS_THREAD int32 = 6
|
||||
FREEZE_EXIT int32 = 8
|
||||
QUIESCE_SRB int32 = 9
|
||||
)
|
||||
|
||||
type Pgtha struct {
|
||||
Pid uint32 // 0
|
||||
Tid0 uint32 // 4
|
||||
Tid1 uint32
|
||||
Accesspid byte // C
|
||||
Accesstid byte // D
|
||||
Accessasid uint16 // E
|
||||
Loginname [8]byte // 10
|
||||
Flag1 byte // 18
|
||||
Flag1b2 byte // 19
|
||||
}
|
||||
|
||||
type Bpxystat_t struct { // DSECT BPXYSTAT
|
||||
St_id [4]uint8 // 0
|
||||
St_length uint16 // 0x4
|
||||
St_version uint16 // 0x6
|
||||
St_mode uint32 // 0x8
|
||||
St_ino uint32 // 0xc
|
||||
St_dev uint32 // 0x10
|
||||
St_nlink uint32 // 0x14
|
||||
St_uid uint32 // 0x18
|
||||
St_gid uint32 // 0x1c
|
||||
St_size uint64 // 0x20
|
||||
St_atime uint32 // 0x28
|
||||
St_mtime uint32 // 0x2c
|
||||
St_ctime uint32 // 0x30
|
||||
St_rdev uint32 // 0x34
|
||||
St_auditoraudit uint32 // 0x38
|
||||
St_useraudit uint32 // 0x3c
|
||||
St_blksize uint32 // 0x40
|
||||
St_createtime uint32 // 0x44
|
||||
St_auditid [4]uint32 // 0x48
|
||||
St_res01 uint32 // 0x58
|
||||
Ft_ccsid uint16 // 0x5c
|
||||
Ft_flags uint16 // 0x5e
|
||||
St_res01a [2]uint32 // 0x60
|
||||
St_res02 uint32 // 0x68
|
||||
St_blocks uint32 // 0x6c
|
||||
St_opaque [3]uint8 // 0x70
|
||||
St_visible uint8 // 0x73
|
||||
St_reftime uint32 // 0x74
|
||||
St_fid uint64 // 0x78
|
||||
St_filefmt uint8 // 0x80
|
||||
St_fspflag2 uint8 // 0x81
|
||||
St_res03 [2]uint8 // 0x82
|
||||
St_ctimemsec uint32 // 0x84
|
||||
St_seclabel [8]uint8 // 0x88
|
||||
St_res04 [4]uint8 // 0x90
|
||||
// end of version 1
|
||||
_ uint32 // 0x94
|
||||
St_atime64 uint64 // 0x98
|
||||
St_mtime64 uint64 // 0xa0
|
||||
St_ctime64 uint64 // 0xa8
|
||||
St_createtime64 uint64 // 0xb0
|
||||
St_reftime64 uint64 // 0xb8
|
||||
_ uint64 // 0xc0
|
||||
St_res05 [16]uint8 // 0xc8
|
||||
// end of version 2
|
||||
}
|
||||
|
||||
type BpxFilestatus struct {
|
||||
Oflag1 byte
|
||||
Oflag2 byte
|
||||
Oflag3 byte
|
||||
Oflag4 byte
|
||||
}
|
||||
|
||||
type BpxMode struct {
|
||||
Ftype byte
|
||||
Mode1 byte
|
||||
Mode2 byte
|
||||
Mode3 byte
|
||||
}
|
||||
|
||||
// Thr attribute structure for extended attributes
|
||||
type Bpxyatt_t struct { // DSECT BPXYATT
|
||||
Att_id [4]uint8
|
||||
Att_version uint16
|
||||
Att_res01 [2]uint8
|
||||
Att_setflags1 uint8
|
||||
Att_setflags2 uint8
|
||||
Att_setflags3 uint8
|
||||
Att_setflags4 uint8
|
||||
Att_mode uint32
|
||||
Att_uid uint32
|
||||
Att_gid uint32
|
||||
Att_opaquemask [3]uint8
|
||||
Att_visblmaskres uint8
|
||||
Att_opaque [3]uint8
|
||||
Att_visibleres uint8
|
||||
Att_size_h uint32
|
||||
Att_size_l uint32
|
||||
Att_atime uint32
|
||||
Att_mtime uint32
|
||||
Att_auditoraudit uint32
|
||||
Att_useraudit uint32
|
||||
Att_ctime uint32
|
||||
Att_reftime uint32
|
||||
// end of version 1
|
||||
Att_filefmt uint8
|
||||
Att_res02 [3]uint8
|
||||
Att_filetag uint32
|
||||
Att_res03 [8]uint8
|
||||
// end of version 2
|
||||
Att_atime64 uint64
|
||||
Att_mtime64 uint64
|
||||
Att_ctime64 uint64
|
||||
Att_reftime64 uint64
|
||||
Att_seclabel [8]uint8
|
||||
Att_ver3res02 [8]uint8
|
||||
// end of version 3
|
||||
}
|
||||
|
||||
func BpxOpen(name string, options *BpxFilestatus, mode *BpxMode) (rv int32, rc int32, rn int32) {
|
||||
if len(name) < 1024 {
|
||||
var namebuf [1024]byte
|
||||
sz := int32(copy(namebuf[:], name))
|
||||
A2e(namebuf[:sz])
|
||||
var parms [7]unsafe.Pointer
|
||||
parms[0] = unsafe.Pointer(&sz)
|
||||
parms[1] = unsafe.Pointer(&namebuf[0])
|
||||
parms[2] = unsafe.Pointer(options)
|
||||
parms[3] = unsafe.Pointer(mode)
|
||||
parms[4] = unsafe.Pointer(&rv)
|
||||
parms[5] = unsafe.Pointer(&rc)
|
||||
parms[6] = unsafe.Pointer(&rn)
|
||||
bpxcall(parms[:], BPX4OPN)
|
||||
return rv, rc, rn
|
||||
}
|
||||
return -1, -1, -1
|
||||
}
|
||||
|
||||
func BpxClose(fd int32) (rv int32, rc int32, rn int32) {
|
||||
var parms [4]unsafe.Pointer
|
||||
parms[0] = unsafe.Pointer(&fd)
|
||||
parms[1] = unsafe.Pointer(&rv)
|
||||
parms[2] = unsafe.Pointer(&rc)
|
||||
parms[3] = unsafe.Pointer(&rn)
|
||||
bpxcall(parms[:], BPX4CLO)
|
||||
return rv, rc, rn
|
||||
}
|
||||
|
||||
func BpxFileFStat(fd int32, st *Bpxystat_t) (rv int32, rc int32, rn int32) {
|
||||
st.St_id = [4]uint8{0xe2, 0xe3, 0xc1, 0xe3}
|
||||
st.St_version = 2
|
||||
stat_sz := uint32(unsafe.Sizeof(*st))
|
||||
var parms [6]unsafe.Pointer
|
||||
parms[0] = unsafe.Pointer(&fd)
|
||||
parms[1] = unsafe.Pointer(&stat_sz)
|
||||
parms[2] = unsafe.Pointer(st)
|
||||
parms[3] = unsafe.Pointer(&rv)
|
||||
parms[4] = unsafe.Pointer(&rc)
|
||||
parms[5] = unsafe.Pointer(&rn)
|
||||
bpxcall(parms[:], BPX4FST)
|
||||
return rv, rc, rn
|
||||
}
|
||||
|
||||
func BpxFileStat(name string, st *Bpxystat_t) (rv int32, rc int32, rn int32) {
|
||||
if len(name) < 1024 {
|
||||
var namebuf [1024]byte
|
||||
sz := int32(copy(namebuf[:], name))
|
||||
A2e(namebuf[:sz])
|
||||
st.St_id = [4]uint8{0xe2, 0xe3, 0xc1, 0xe3}
|
||||
st.St_version = 2
|
||||
stat_sz := uint32(unsafe.Sizeof(*st))
|
||||
var parms [7]unsafe.Pointer
|
||||
parms[0] = unsafe.Pointer(&sz)
|
||||
parms[1] = unsafe.Pointer(&namebuf[0])
|
||||
parms[2] = unsafe.Pointer(&stat_sz)
|
||||
parms[3] = unsafe.Pointer(st)
|
||||
parms[4] = unsafe.Pointer(&rv)
|
||||
parms[5] = unsafe.Pointer(&rc)
|
||||
parms[6] = unsafe.Pointer(&rn)
|
||||
bpxcall(parms[:], BPX4STA)
|
||||
return rv, rc, rn
|
||||
}
|
||||
return -1, -1, -1
|
||||
}
|
||||
|
||||
func BpxFileLStat(name string, st *Bpxystat_t) (rv int32, rc int32, rn int32) {
|
||||
if len(name) < 1024 {
|
||||
var namebuf [1024]byte
|
||||
sz := int32(copy(namebuf[:], name))
|
||||
A2e(namebuf[:sz])
|
||||
st.St_id = [4]uint8{0xe2, 0xe3, 0xc1, 0xe3}
|
||||
st.St_version = 2
|
||||
stat_sz := uint32(unsafe.Sizeof(*st))
|
||||
var parms [7]unsafe.Pointer
|
||||
parms[0] = unsafe.Pointer(&sz)
|
||||
parms[1] = unsafe.Pointer(&namebuf[0])
|
||||
parms[2] = unsafe.Pointer(&stat_sz)
|
||||
parms[3] = unsafe.Pointer(st)
|
||||
parms[4] = unsafe.Pointer(&rv)
|
||||
parms[5] = unsafe.Pointer(&rc)
|
||||
parms[6] = unsafe.Pointer(&rn)
|
||||
bpxcall(parms[:], BPX4LST)
|
||||
return rv, rc, rn
|
||||
}
|
||||
return -1, -1, -1
|
||||
}
|
||||
|
||||
func BpxChattr(path string, attr *Bpxyatt_t) (rv int32, rc int32, rn int32) {
|
||||
if len(path) >= 1024 {
|
||||
return -1, -1, -1
|
||||
}
|
||||
var namebuf [1024]byte
|
||||
sz := int32(copy(namebuf[:], path))
|
||||
A2e(namebuf[:sz])
|
||||
attr_sz := uint32(unsafe.Sizeof(*attr))
|
||||
var parms [7]unsafe.Pointer
|
||||
parms[0] = unsafe.Pointer(&sz)
|
||||
parms[1] = unsafe.Pointer(&namebuf[0])
|
||||
parms[2] = unsafe.Pointer(&attr_sz)
|
||||
parms[3] = unsafe.Pointer(attr)
|
||||
parms[4] = unsafe.Pointer(&rv)
|
||||
parms[5] = unsafe.Pointer(&rc)
|
||||
parms[6] = unsafe.Pointer(&rn)
|
||||
bpxcall(parms[:], BPX4CHR)
|
||||
return rv, rc, rn
|
||||
}
|
||||
|
||||
func BpxLchattr(path string, attr *Bpxyatt_t) (rv int32, rc int32, rn int32) {
|
||||
if len(path) >= 1024 {
|
||||
return -1, -1, -1
|
||||
}
|
||||
var namebuf [1024]byte
|
||||
sz := int32(copy(namebuf[:], path))
|
||||
A2e(namebuf[:sz])
|
||||
attr_sz := uint32(unsafe.Sizeof(*attr))
|
||||
var parms [7]unsafe.Pointer
|
||||
parms[0] = unsafe.Pointer(&sz)
|
||||
parms[1] = unsafe.Pointer(&namebuf[0])
|
||||
parms[2] = unsafe.Pointer(&attr_sz)
|
||||
parms[3] = unsafe.Pointer(attr)
|
||||
parms[4] = unsafe.Pointer(&rv)
|
||||
parms[5] = unsafe.Pointer(&rc)
|
||||
parms[6] = unsafe.Pointer(&rn)
|
||||
bpxcall(parms[:], BPX4LCR)
|
||||
return rv, rc, rn
|
||||
}
|
||||
|
||||
func BpxFchattr(fd int32, attr *Bpxyatt_t) (rv int32, rc int32, rn int32) {
|
||||
attr_sz := uint32(unsafe.Sizeof(*attr))
|
||||
var parms [6]unsafe.Pointer
|
||||
parms[0] = unsafe.Pointer(&fd)
|
||||
parms[1] = unsafe.Pointer(&attr_sz)
|
||||
parms[2] = unsafe.Pointer(attr)
|
||||
parms[3] = unsafe.Pointer(&rv)
|
||||
parms[4] = unsafe.Pointer(&rc)
|
||||
parms[5] = unsafe.Pointer(&rn)
|
||||
bpxcall(parms[:], BPX4FCR)
|
||||
return rv, rc, rn
|
||||
}
|
||||
|
||||
func BpxCondTimedWait(sec uint32, nsec uint32, events uint32, secrem *uint32, nsecrem *uint32) (rv int32, rc int32, rn int32) {
|
||||
var parms [8]unsafe.Pointer
|
||||
parms[0] = unsafe.Pointer(&sec)
|
||||
parms[1] = unsafe.Pointer(&nsec)
|
||||
parms[2] = unsafe.Pointer(&events)
|
||||
parms[3] = unsafe.Pointer(secrem)
|
||||
parms[4] = unsafe.Pointer(nsecrem)
|
||||
parms[5] = unsafe.Pointer(&rv)
|
||||
parms[6] = unsafe.Pointer(&rc)
|
||||
parms[7] = unsafe.Pointer(&rn)
|
||||
bpxcall(parms[:], BPX4CTW)
|
||||
return rv, rc, rn
|
||||
}
|
||||
func BpxGetthent(in *Pgtha, outlen *uint32, out unsafe.Pointer) (rv int32, rc int32, rn int32) {
|
||||
var parms [7]unsafe.Pointer
|
||||
inlen := uint32(26) // nothing else will work. Go says Pgtha is 28-byte because of alignment, but Pgtha is "packed" and must be 26-byte
|
||||
parms[0] = unsafe.Pointer(&inlen)
|
||||
parms[1] = unsafe.Pointer(&in)
|
||||
parms[2] = unsafe.Pointer(outlen)
|
||||
parms[3] = unsafe.Pointer(&out)
|
||||
parms[4] = unsafe.Pointer(&rv)
|
||||
parms[5] = unsafe.Pointer(&rc)
|
||||
parms[6] = unsafe.Pointer(&rn)
|
||||
bpxcall(parms[:], BPX4GTH)
|
||||
return rv, rc, rn
|
||||
}
|
||||
func ZosJobname() (jobname string, err error) {
|
||||
var pgtha Pgtha
|
||||
pgtha.Pid = uint32(Getpid())
|
||||
pgtha.Accesspid = PGTHA_CURRENT
|
||||
pgtha.Flag1 = PGTHA_PROCESS
|
||||
var out [256]byte
|
||||
var outlen uint32
|
||||
outlen = 256
|
||||
rv, rc, rn := BpxGetthent(&pgtha, &outlen, unsafe.Pointer(&out[0]))
|
||||
if rv == 0 {
|
||||
gthc := []byte{0x87, 0xa3, 0x88, 0x83} // 'gthc' in ebcdic
|
||||
ix := bytes.Index(out[:], gthc)
|
||||
if ix == -1 {
|
||||
err = fmt.Errorf("BPX4GTH: gthc return data not found")
|
||||
return
|
||||
}
|
||||
jn := out[ix+80 : ix+88] // we didn't declare Pgthc, but jobname is 8-byte at offset 80
|
||||
E2a(jn)
|
||||
jobname = string(bytes.TrimRight(jn, " "))
|
||||
|
||||
} else {
|
||||
err = fmt.Errorf("BPX4GTH: rc=%d errno=%d reason=code=0x%x", rv, rc, rn)
|
||||
}
|
||||
return
|
||||
}
|
||||
func Bpx4ptq(code int32, data string) (rv int32, rc int32, rn int32) {
|
||||
var userdata [8]byte
|
||||
var parms [5]unsafe.Pointer
|
||||
copy(userdata[:], data+" ")
|
||||
A2e(userdata[:])
|
||||
parms[0] = unsafe.Pointer(&code)
|
||||
parms[1] = unsafe.Pointer(&userdata[0])
|
||||
parms[2] = unsafe.Pointer(&rv)
|
||||
parms[3] = unsafe.Pointer(&rc)
|
||||
parms[4] = unsafe.Pointer(&rn)
|
||||
bpxcall(parms[:], BPX4PTQ)
|
||||
return rv, rc, rn
|
||||
}
|
||||
|
||||
const (
|
||||
PT_TRACE_ME = 0 // Debug this process
|
||||
PT_READ_I = 1 // Read a full word
|
||||
PT_READ_D = 2 // Read a full word
|
||||
PT_READ_U = 3 // Read control info
|
||||
PT_WRITE_I = 4 //Write a full word
|
||||
PT_WRITE_D = 5 //Write a full word
|
||||
PT_CONTINUE = 7 //Continue the process
|
||||
PT_KILL = 8 //Terminate the process
|
||||
PT_READ_GPR = 11 // Read GPR, CR, PSW
|
||||
PT_READ_FPR = 12 // Read FPR
|
||||
PT_READ_VR = 13 // Read VR
|
||||
PT_WRITE_GPR = 14 // Write GPR, CR, PSW
|
||||
PT_WRITE_FPR = 15 // Write FPR
|
||||
PT_WRITE_VR = 16 // Write VR
|
||||
PT_READ_BLOCK = 17 // Read storage
|
||||
PT_WRITE_BLOCK = 19 // Write storage
|
||||
PT_READ_GPRH = 20 // Read GPRH
|
||||
PT_WRITE_GPRH = 21 // Write GPRH
|
||||
PT_REGHSET = 22 // Read all GPRHs
|
||||
PT_ATTACH = 30 // Attach to a process
|
||||
PT_DETACH = 31 // Detach from a process
|
||||
PT_REGSET = 32 // Read all GPRs
|
||||
PT_REATTACH = 33 // Reattach to a process
|
||||
PT_LDINFO = 34 // Read loader info
|
||||
PT_MULTI = 35 // Multi process mode
|
||||
PT_LD64INFO = 36 // RMODE64 Info Area
|
||||
PT_BLOCKREQ = 40 // Block request
|
||||
PT_THREAD_INFO = 60 // Read thread info
|
||||
PT_THREAD_MODIFY = 61
|
||||
PT_THREAD_READ_FOCUS = 62
|
||||
PT_THREAD_WRITE_FOCUS = 63
|
||||
PT_THREAD_HOLD = 64
|
||||
PT_THREAD_SIGNAL = 65
|
||||
PT_EXPLAIN = 66
|
||||
PT_EVENTS = 67
|
||||
PT_THREAD_INFO_EXTENDED = 68
|
||||
PT_REATTACH2 = 71
|
||||
PT_CAPTURE = 72
|
||||
PT_UNCAPTURE = 73
|
||||
PT_GET_THREAD_TCB = 74
|
||||
PT_GET_ALET = 75
|
||||
PT_SWAPIN = 76
|
||||
PT_EXTENDED_EVENT = 98
|
||||
PT_RECOVER = 99 // Debug a program check
|
||||
PT_GPR0 = 0 // General purpose register 0
|
||||
PT_GPR1 = 1 // General purpose register 1
|
||||
PT_GPR2 = 2 // General purpose register 2
|
||||
PT_GPR3 = 3 // General purpose register 3
|
||||
PT_GPR4 = 4 // General purpose register 4
|
||||
PT_GPR5 = 5 // General purpose register 5
|
||||
PT_GPR6 = 6 // General purpose register 6
|
||||
PT_GPR7 = 7 // General purpose register 7
|
||||
PT_GPR8 = 8 // General purpose register 8
|
||||
PT_GPR9 = 9 // General purpose register 9
|
||||
PT_GPR10 = 10 // General purpose register 10
|
||||
PT_GPR11 = 11 // General purpose register 11
|
||||
PT_GPR12 = 12 // General purpose register 12
|
||||
PT_GPR13 = 13 // General purpose register 13
|
||||
PT_GPR14 = 14 // General purpose register 14
|
||||
PT_GPR15 = 15 // General purpose register 15
|
||||
PT_FPR0 = 16 // Floating point register 0
|
||||
PT_FPR1 = 17 // Floating point register 1
|
||||
PT_FPR2 = 18 // Floating point register 2
|
||||
PT_FPR3 = 19 // Floating point register 3
|
||||
PT_FPR4 = 20 // Floating point register 4
|
||||
PT_FPR5 = 21 // Floating point register 5
|
||||
PT_FPR6 = 22 // Floating point register 6
|
||||
PT_FPR7 = 23 // Floating point register 7
|
||||
PT_FPR8 = 24 // Floating point register 8
|
||||
PT_FPR9 = 25 // Floating point register 9
|
||||
PT_FPR10 = 26 // Floating point register 10
|
||||
PT_FPR11 = 27 // Floating point register 11
|
||||
PT_FPR12 = 28 // Floating point register 12
|
||||
PT_FPR13 = 29 // Floating point register 13
|
||||
PT_FPR14 = 30 // Floating point register 14
|
||||
PT_FPR15 = 31 // Floating point register 15
|
||||
PT_FPC = 32 // Floating point control register
|
||||
PT_PSW = 40 // PSW
|
||||
PT_PSW0 = 40 // Left half of the PSW
|
||||
PT_PSW1 = 41 // Right half of the PSW
|
||||
PT_CR0 = 42 // Control register 0
|
||||
PT_CR1 = 43 // Control register 1
|
||||
PT_CR2 = 44 // Control register 2
|
||||
PT_CR3 = 45 // Control register 3
|
||||
PT_CR4 = 46 // Control register 4
|
||||
PT_CR5 = 47 // Control register 5
|
||||
PT_CR6 = 48 // Control register 6
|
||||
PT_CR7 = 49 // Control register 7
|
||||
PT_CR8 = 50 // Control register 8
|
||||
PT_CR9 = 51 // Control register 9
|
||||
PT_CR10 = 52 // Control register 10
|
||||
PT_CR11 = 53 // Control register 11
|
||||
PT_CR12 = 54 // Control register 12
|
||||
PT_CR13 = 55 // Control register 13
|
||||
PT_CR14 = 56 // Control register 14
|
||||
PT_CR15 = 57 // Control register 15
|
||||
PT_GPRH0 = 58 // GP High register 0
|
||||
PT_GPRH1 = 59 // GP High register 1
|
||||
PT_GPRH2 = 60 // GP High register 2
|
||||
PT_GPRH3 = 61 // GP High register 3
|
||||
PT_GPRH4 = 62 // GP High register 4
|
||||
PT_GPRH5 = 63 // GP High register 5
|
||||
PT_GPRH6 = 64 // GP High register 6
|
||||
PT_GPRH7 = 65 // GP High register 7
|
||||
PT_GPRH8 = 66 // GP High register 8
|
||||
PT_GPRH9 = 67 // GP High register 9
|
||||
PT_GPRH10 = 68 // GP High register 10
|
||||
PT_GPRH11 = 69 // GP High register 11
|
||||
PT_GPRH12 = 70 // GP High register 12
|
||||
PT_GPRH13 = 71 // GP High register 13
|
||||
PT_GPRH14 = 72 // GP High register 14
|
||||
PT_GPRH15 = 73 // GP High register 15
|
||||
PT_VR0 = 74 // Vector register 0
|
||||
PT_VR1 = 75 // Vector register 1
|
||||
PT_VR2 = 76 // Vector register 2
|
||||
PT_VR3 = 77 // Vector register 3
|
||||
PT_VR4 = 78 // Vector register 4
|
||||
PT_VR5 = 79 // Vector register 5
|
||||
PT_VR6 = 80 // Vector register 6
|
||||
PT_VR7 = 81 // Vector register 7
|
||||
PT_VR8 = 82 // Vector register 8
|
||||
PT_VR9 = 83 // Vector register 9
|
||||
PT_VR10 = 84 // Vector register 10
|
||||
PT_VR11 = 85 // Vector register 11
|
||||
PT_VR12 = 86 // Vector register 12
|
||||
PT_VR13 = 87 // Vector register 13
|
||||
PT_VR14 = 88 // Vector register 14
|
||||
PT_VR15 = 89 // Vector register 15
|
||||
PT_VR16 = 90 // Vector register 16
|
||||
PT_VR17 = 91 // Vector register 17
|
||||
PT_VR18 = 92 // Vector register 18
|
||||
PT_VR19 = 93 // Vector register 19
|
||||
PT_VR20 = 94 // Vector register 20
|
||||
PT_VR21 = 95 // Vector register 21
|
||||
PT_VR22 = 96 // Vector register 22
|
||||
PT_VR23 = 97 // Vector register 23
|
||||
PT_VR24 = 98 // Vector register 24
|
||||
PT_VR25 = 99 // Vector register 25
|
||||
PT_VR26 = 100 // Vector register 26
|
||||
PT_VR27 = 101 // Vector register 27
|
||||
PT_VR28 = 102 // Vector register 28
|
||||
PT_VR29 = 103 // Vector register 29
|
||||
PT_VR30 = 104 // Vector register 30
|
||||
PT_VR31 = 105 // Vector register 31
|
||||
PT_PSWG = 106 // PSWG
|
||||
PT_PSWG0 = 106 // Bytes 0-3
|
||||
PT_PSWG1 = 107 // Bytes 4-7
|
||||
PT_PSWG2 = 108 // Bytes 8-11 (IA high word)
|
||||
PT_PSWG3 = 109 // Bytes 12-15 (IA low word)
|
||||
)
|
||||
|
||||
func Bpx4ptr(request int32, pid int32, addr unsafe.Pointer, data unsafe.Pointer, buffer unsafe.Pointer) (rv int32, rc int32, rn int32) {
|
||||
var parms [8]unsafe.Pointer
|
||||
parms[0] = unsafe.Pointer(&request)
|
||||
parms[1] = unsafe.Pointer(&pid)
|
||||
parms[2] = unsafe.Pointer(&addr)
|
||||
parms[3] = unsafe.Pointer(&data)
|
||||
parms[4] = unsafe.Pointer(&buffer)
|
||||
parms[5] = unsafe.Pointer(&rv)
|
||||
parms[6] = unsafe.Pointer(&rc)
|
||||
parms[7] = unsafe.Pointer(&rn)
|
||||
bpxcall(parms[:], BPX4PTR)
|
||||
return rv, rc, rn
|
||||
}
|
||||
|
||||
func copyU8(val uint8, dest []uint8) int {
|
||||
if len(dest) < 1 {
|
||||
return 0
|
||||
}
|
||||
dest[0] = val
|
||||
return 1
|
||||
}
|
||||
|
||||
func copyU8Arr(src, dest []uint8) int {
|
||||
if len(dest) < len(src) {
|
||||
return 0
|
||||
}
|
||||
for i, v := range src {
|
||||
dest[i] = v
|
||||
}
|
||||
return len(src)
|
||||
}
|
||||
|
||||
func copyU16(val uint16, dest []uint16) int {
|
||||
if len(dest) < 1 {
|
||||
return 0
|
||||
}
|
||||
dest[0] = val
|
||||
return 1
|
||||
}
|
||||
|
||||
func copyU32(val uint32, dest []uint32) int {
|
||||
if len(dest) < 1 {
|
||||
return 0
|
||||
}
|
||||
dest[0] = val
|
||||
return 1
|
||||
}
|
||||
|
||||
func copyU32Arr(src, dest []uint32) int {
|
||||
if len(dest) < len(src) {
|
||||
return 0
|
||||
}
|
||||
for i, v := range src {
|
||||
dest[i] = v
|
||||
}
|
||||
return len(src)
|
||||
}
|
||||
|
||||
func copyU64(val uint64, dest []uint64) int {
|
||||
if len(dest) < 1 {
|
||||
return 0
|
||||
}
|
||||
dest[0] = val
|
||||
return 1
|
||||
}
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "go_asm.h"
|
||||
#include "textflag.h"
|
||||
|
||||
// function to call USS assembly language services
|
||||
//
|
||||
// doc: https://www.ibm.com/support/knowledgecenter/en/SSLTBW_3.1.0/com.ibm.zos.v3r1.bpxb100/bit64env.htm
|
||||
//
|
||||
// arg1 unsafe.Pointer array that ressembles an OS PLIST
|
||||
//
|
||||
// arg2 function offset as in
|
||||
// doc: https://www.ibm.com/support/knowledgecenter/en/SSLTBW_3.1.0/com.ibm.zos.v3r1.bpxb100/bpx2cr_List_of_offsets.htm
|
||||
//
|
||||
// func bpxcall(plist []unsafe.Pointer, bpx_offset int64)
|
||||
|
||||
TEXT ·bpxcall(SB), NOSPLIT|NOFRAME, $0
|
||||
MOVD plist_base+0(FP), R1 // r1 points to plist
|
||||
MOVD bpx_offset+24(FP), R2 // r2 offset to BPX vector table
|
||||
MOVD R14, R7 // save r14
|
||||
MOVD R15, R8 // save r15
|
||||
MOVWZ 16(R0), R9
|
||||
MOVWZ 544(R9), R9
|
||||
MOVWZ 24(R9), R9 // call vector in r9
|
||||
ADD R2, R9 // add offset to vector table
|
||||
MOVWZ (R9), R9 // r9 points to entry point
|
||||
BYTE $0x0D // BL R14,R9 --> basr r14,r9
|
||||
BYTE $0xE9 // clobbers 0,1,14,15
|
||||
MOVD R8, R15 // restore 15
|
||||
JMP R7 // return via saved return address
|
||||
|
||||
// func A2e(arr [] byte)
|
||||
// code page conversion from 819 to 1047
|
||||
TEXT ·A2e(SB), NOSPLIT|NOFRAME, $0
|
||||
MOVD arg_base+0(FP), R2 // pointer to arry of characters
|
||||
MOVD arg_len+8(FP), R3 // count
|
||||
XOR R0, R0
|
||||
XOR R1, R1
|
||||
BYTE $0xA7; BYTE $0x15; BYTE $0x00; BYTE $0x82 // BRAS 1,(2+(256/2))
|
||||
|
||||
// ASCII -> EBCDIC conversion table:
|
||||
BYTE $0x00; BYTE $0x01; BYTE $0x02; BYTE $0x03
|
||||
BYTE $0x37; BYTE $0x2d; BYTE $0x2e; BYTE $0x2f
|
||||
BYTE $0x16; BYTE $0x05; BYTE $0x15; BYTE $0x0b
|
||||
BYTE $0x0c; BYTE $0x0d; BYTE $0x0e; BYTE $0x0f
|
||||
BYTE $0x10; BYTE $0x11; BYTE $0x12; BYTE $0x13
|
||||
BYTE $0x3c; BYTE $0x3d; BYTE $0x32; BYTE $0x26
|
||||
BYTE $0x18; BYTE $0x19; BYTE $0x3f; BYTE $0x27
|
||||
BYTE $0x1c; BYTE $0x1d; BYTE $0x1e; BYTE $0x1f
|
||||
BYTE $0x40; BYTE $0x5a; BYTE $0x7f; BYTE $0x7b
|
||||
BYTE $0x5b; BYTE $0x6c; BYTE $0x50; BYTE $0x7d
|
||||
BYTE $0x4d; BYTE $0x5d; BYTE $0x5c; BYTE $0x4e
|
||||
BYTE $0x6b; BYTE $0x60; BYTE $0x4b; BYTE $0x61
|
||||
BYTE $0xf0; BYTE $0xf1; BYTE $0xf2; BYTE $0xf3
|
||||
BYTE $0xf4; BYTE $0xf5; BYTE $0xf6; BYTE $0xf7
|
||||
BYTE $0xf8; BYTE $0xf9; BYTE $0x7a; BYTE $0x5e
|
||||
BYTE $0x4c; BYTE $0x7e; BYTE $0x6e; BYTE $0x6f
|
||||
BYTE $0x7c; BYTE $0xc1; BYTE $0xc2; BYTE $0xc3
|
||||
BYTE $0xc4; BYTE $0xc5; BYTE $0xc6; BYTE $0xc7
|
||||
BYTE $0xc8; BYTE $0xc9; BYTE $0xd1; BYTE $0xd2
|
||||
BYTE $0xd3; BYTE $0xd4; BYTE $0xd5; BYTE $0xd6
|
||||
BYTE $0xd7; BYTE $0xd8; BYTE $0xd9; BYTE $0xe2
|
||||
BYTE $0xe3; BYTE $0xe4; BYTE $0xe5; BYTE $0xe6
|
||||
BYTE $0xe7; BYTE $0xe8; BYTE $0xe9; BYTE $0xad
|
||||
BYTE $0xe0; BYTE $0xbd; BYTE $0x5f; BYTE $0x6d
|
||||
BYTE $0x79; BYTE $0x81; BYTE $0x82; BYTE $0x83
|
||||
BYTE $0x84; BYTE $0x85; BYTE $0x86; BYTE $0x87
|
||||
BYTE $0x88; BYTE $0x89; BYTE $0x91; BYTE $0x92
|
||||
BYTE $0x93; BYTE $0x94; BYTE $0x95; BYTE $0x96
|
||||
BYTE $0x97; BYTE $0x98; BYTE $0x99; BYTE $0xa2
|
||||
BYTE $0xa3; BYTE $0xa4; BYTE $0xa5; BYTE $0xa6
|
||||
BYTE $0xa7; BYTE $0xa8; BYTE $0xa9; BYTE $0xc0
|
||||
BYTE $0x4f; BYTE $0xd0; BYTE $0xa1; BYTE $0x07
|
||||
BYTE $0x20; BYTE $0x21; BYTE $0x22; BYTE $0x23
|
||||
BYTE $0x24; BYTE $0x25; BYTE $0x06; BYTE $0x17
|
||||
BYTE $0x28; BYTE $0x29; BYTE $0x2a; BYTE $0x2b
|
||||
BYTE $0x2c; BYTE $0x09; BYTE $0x0a; BYTE $0x1b
|
||||
BYTE $0x30; BYTE $0x31; BYTE $0x1a; BYTE $0x33
|
||||
BYTE $0x34; BYTE $0x35; BYTE $0x36; BYTE $0x08
|
||||
BYTE $0x38; BYTE $0x39; BYTE $0x3a; BYTE $0x3b
|
||||
BYTE $0x04; BYTE $0x14; BYTE $0x3e; BYTE $0xff
|
||||
BYTE $0x41; BYTE $0xaa; BYTE $0x4a; BYTE $0xb1
|
||||
BYTE $0x9f; BYTE $0xb2; BYTE $0x6a; BYTE $0xb5
|
||||
BYTE $0xbb; BYTE $0xb4; BYTE $0x9a; BYTE $0x8a
|
||||
BYTE $0xb0; BYTE $0xca; BYTE $0xaf; BYTE $0xbc
|
||||
BYTE $0x90; BYTE $0x8f; BYTE $0xea; BYTE $0xfa
|
||||
BYTE $0xbe; BYTE $0xa0; BYTE $0xb6; BYTE $0xb3
|
||||
BYTE $0x9d; BYTE $0xda; BYTE $0x9b; BYTE $0x8b
|
||||
BYTE $0xb7; BYTE $0xb8; BYTE $0xb9; BYTE $0xab
|
||||
BYTE $0x64; BYTE $0x65; BYTE $0x62; BYTE $0x66
|
||||
BYTE $0x63; BYTE $0x67; BYTE $0x9e; BYTE $0x68
|
||||
BYTE $0x74; BYTE $0x71; BYTE $0x72; BYTE $0x73
|
||||
BYTE $0x78; BYTE $0x75; BYTE $0x76; BYTE $0x77
|
||||
BYTE $0xac; BYTE $0x69; BYTE $0xed; BYTE $0xee
|
||||
BYTE $0xeb; BYTE $0xef; BYTE $0xec; BYTE $0xbf
|
||||
BYTE $0x80; BYTE $0xfd; BYTE $0xfe; BYTE $0xfb
|
||||
BYTE $0xfc; BYTE $0xba; BYTE $0xae; BYTE $0x59
|
||||
BYTE $0x44; BYTE $0x45; BYTE $0x42; BYTE $0x46
|
||||
BYTE $0x43; BYTE $0x47; BYTE $0x9c; BYTE $0x48
|
||||
BYTE $0x54; BYTE $0x51; BYTE $0x52; BYTE $0x53
|
||||
BYTE $0x58; BYTE $0x55; BYTE $0x56; BYTE $0x57
|
||||
BYTE $0x8c; BYTE $0x49; BYTE $0xcd; BYTE $0xce
|
||||
BYTE $0xcb; BYTE $0xcf; BYTE $0xcc; BYTE $0xe1
|
||||
BYTE $0x70; BYTE $0xdd; BYTE $0xde; BYTE $0xdb
|
||||
BYTE $0xdc; BYTE $0x8d; BYTE $0x8e; BYTE $0xdf
|
||||
|
||||
retry:
|
||||
WORD $0xB9931022 // TROO 2,2,b'0001'
|
||||
BVS retry
|
||||
RET
|
||||
|
||||
// func e2a(arr [] byte)
|
||||
// code page conversion from 1047 to 819
|
||||
TEXT ·E2a(SB), NOSPLIT|NOFRAME, $0
|
||||
MOVD arg_base+0(FP), R2 // pointer to arry of characters
|
||||
MOVD arg_len+8(FP), R3 // count
|
||||
XOR R0, R0
|
||||
XOR R1, R1
|
||||
BYTE $0xA7; BYTE $0x15; BYTE $0x00; BYTE $0x82 // BRAS 1,(2+(256/2))
|
||||
|
||||
// EBCDIC -> ASCII conversion table:
|
||||
BYTE $0x00; BYTE $0x01; BYTE $0x02; BYTE $0x03
|
||||
BYTE $0x9c; BYTE $0x09; BYTE $0x86; BYTE $0x7f
|
||||
BYTE $0x97; BYTE $0x8d; BYTE $0x8e; BYTE $0x0b
|
||||
BYTE $0x0c; BYTE $0x0d; BYTE $0x0e; BYTE $0x0f
|
||||
BYTE $0x10; BYTE $0x11; BYTE $0x12; BYTE $0x13
|
||||
BYTE $0x9d; BYTE $0x0a; BYTE $0x08; BYTE $0x87
|
||||
BYTE $0x18; BYTE $0x19; BYTE $0x92; BYTE $0x8f
|
||||
BYTE $0x1c; BYTE $0x1d; BYTE $0x1e; BYTE $0x1f
|
||||
BYTE $0x80; BYTE $0x81; BYTE $0x82; BYTE $0x83
|
||||
BYTE $0x84; BYTE $0x85; BYTE $0x17; BYTE $0x1b
|
||||
BYTE $0x88; BYTE $0x89; BYTE $0x8a; BYTE $0x8b
|
||||
BYTE $0x8c; BYTE $0x05; BYTE $0x06; BYTE $0x07
|
||||
BYTE $0x90; BYTE $0x91; BYTE $0x16; BYTE $0x93
|
||||
BYTE $0x94; BYTE $0x95; BYTE $0x96; BYTE $0x04
|
||||
BYTE $0x98; BYTE $0x99; BYTE $0x9a; BYTE $0x9b
|
||||
BYTE $0x14; BYTE $0x15; BYTE $0x9e; BYTE $0x1a
|
||||
BYTE $0x20; BYTE $0xa0; BYTE $0xe2; BYTE $0xe4
|
||||
BYTE $0xe0; BYTE $0xe1; BYTE $0xe3; BYTE $0xe5
|
||||
BYTE $0xe7; BYTE $0xf1; BYTE $0xa2; BYTE $0x2e
|
||||
BYTE $0x3c; BYTE $0x28; BYTE $0x2b; BYTE $0x7c
|
||||
BYTE $0x26; BYTE $0xe9; BYTE $0xea; BYTE $0xeb
|
||||
BYTE $0xe8; BYTE $0xed; BYTE $0xee; BYTE $0xef
|
||||
BYTE $0xec; BYTE $0xdf; BYTE $0x21; BYTE $0x24
|
||||
BYTE $0x2a; BYTE $0x29; BYTE $0x3b; BYTE $0x5e
|
||||
BYTE $0x2d; BYTE $0x2f; BYTE $0xc2; BYTE $0xc4
|
||||
BYTE $0xc0; BYTE $0xc1; BYTE $0xc3; BYTE $0xc5
|
||||
BYTE $0xc7; BYTE $0xd1; BYTE $0xa6; BYTE $0x2c
|
||||
BYTE $0x25; BYTE $0x5f; BYTE $0x3e; BYTE $0x3f
|
||||
BYTE $0xf8; BYTE $0xc9; BYTE $0xca; BYTE $0xcb
|
||||
BYTE $0xc8; BYTE $0xcd; BYTE $0xce; BYTE $0xcf
|
||||
BYTE $0xcc; BYTE $0x60; BYTE $0x3a; BYTE $0x23
|
||||
BYTE $0x40; BYTE $0x27; BYTE $0x3d; BYTE $0x22
|
||||
BYTE $0xd8; BYTE $0x61; BYTE $0x62; BYTE $0x63
|
||||
BYTE $0x64; BYTE $0x65; BYTE $0x66; BYTE $0x67
|
||||
BYTE $0x68; BYTE $0x69; BYTE $0xab; BYTE $0xbb
|
||||
BYTE $0xf0; BYTE $0xfd; BYTE $0xfe; BYTE $0xb1
|
||||
BYTE $0xb0; BYTE $0x6a; BYTE $0x6b; BYTE $0x6c
|
||||
BYTE $0x6d; BYTE $0x6e; BYTE $0x6f; BYTE $0x70
|
||||
BYTE $0x71; BYTE $0x72; BYTE $0xaa; BYTE $0xba
|
||||
BYTE $0xe6; BYTE $0xb8; BYTE $0xc6; BYTE $0xa4
|
||||
BYTE $0xb5; BYTE $0x7e; BYTE $0x73; BYTE $0x74
|
||||
BYTE $0x75; BYTE $0x76; BYTE $0x77; BYTE $0x78
|
||||
BYTE $0x79; BYTE $0x7a; BYTE $0xa1; BYTE $0xbf
|
||||
BYTE $0xd0; BYTE $0x5b; BYTE $0xde; BYTE $0xae
|
||||
BYTE $0xac; BYTE $0xa3; BYTE $0xa5; BYTE $0xb7
|
||||
BYTE $0xa9; BYTE $0xa7; BYTE $0xb6; BYTE $0xbc
|
||||
BYTE $0xbd; BYTE $0xbe; BYTE $0xdd; BYTE $0xa8
|
||||
BYTE $0xaf; BYTE $0x5d; BYTE $0xb4; BYTE $0xd7
|
||||
BYTE $0x7b; BYTE $0x41; BYTE $0x42; BYTE $0x43
|
||||
BYTE $0x44; BYTE $0x45; BYTE $0x46; BYTE $0x47
|
||||
BYTE $0x48; BYTE $0x49; BYTE $0xad; BYTE $0xf4
|
||||
BYTE $0xf6; BYTE $0xf2; BYTE $0xf3; BYTE $0xf5
|
||||
BYTE $0x7d; BYTE $0x4a; BYTE $0x4b; BYTE $0x4c
|
||||
BYTE $0x4d; BYTE $0x4e; BYTE $0x4f; BYTE $0x50
|
||||
BYTE $0x51; BYTE $0x52; BYTE $0xb9; BYTE $0xfb
|
||||
BYTE $0xfc; BYTE $0xf9; BYTE $0xfa; BYTE $0xff
|
||||
BYTE $0x5c; BYTE $0xf7; BYTE $0x53; BYTE $0x54
|
||||
BYTE $0x55; BYTE $0x56; BYTE $0x57; BYTE $0x58
|
||||
BYTE $0x59; BYTE $0x5a; BYTE $0xb2; BYTE $0xd4
|
||||
BYTE $0xd6; BYTE $0xd2; BYTE $0xd3; BYTE $0xd5
|
||||
BYTE $0x30; BYTE $0x31; BYTE $0x32; BYTE $0x33
|
||||
BYTE $0x34; BYTE $0x35; BYTE $0x36; BYTE $0x37
|
||||
BYTE $0x38; BYTE $0x39; BYTE $0xb3; BYTE $0xdb
|
||||
BYTE $0xdc; BYTE $0xd9; BYTE $0xda; BYTE $0x9f
|
||||
|
||||
retry:
|
||||
WORD $0xB9931022 // TROO 2,2,b'0001'
|
||||
BVS retry
|
||||
RET
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build freebsd
|
||||
|
||||
package unix
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Go implementation of C mostly found in /usr/src/sys/kern/subr_capability.c
|
||||
|
||||
const (
|
||||
// This is the version of CapRights this package understands. See C implementation for parallels.
|
||||
capRightsGoVersion = CAP_RIGHTS_VERSION_00
|
||||
capArSizeMin = CAP_RIGHTS_VERSION_00 + 2
|
||||
capArSizeMax = capRightsGoVersion + 2
|
||||
)
|
||||
|
||||
var (
|
||||
bit2idx = []int{
|
||||
-1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1,
|
||||
4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
}
|
||||
)
|
||||
|
||||
func capidxbit(right uint64) int {
|
||||
return int((right >> 57) & 0x1f)
|
||||
}
|
||||
|
||||
func rightToIndex(right uint64) (int, error) {
|
||||
idx := capidxbit(right)
|
||||
if idx < 0 || idx >= len(bit2idx) {
|
||||
return -2, fmt.Errorf("index for right 0x%x out of range", right)
|
||||
}
|
||||
return bit2idx[idx], nil
|
||||
}
|
||||
|
||||
func caprver(right uint64) int {
|
||||
return int(right >> 62)
|
||||
}
|
||||
|
||||
func capver(rights *CapRights) int {
|
||||
return caprver(rights.Rights[0])
|
||||
}
|
||||
|
||||
func caparsize(rights *CapRights) int {
|
||||
return capver(rights) + 2
|
||||
}
|
||||
|
||||
// CapRightsSet sets the permissions in setrights in rights.
|
||||
func CapRightsSet(rights *CapRights, setrights []uint64) error {
|
||||
// This is essentially a copy of cap_rights_vset()
|
||||
if capver(rights) != CAP_RIGHTS_VERSION_00 {
|
||||
return fmt.Errorf("bad rights version %d", capver(rights))
|
||||
}
|
||||
|
||||
n := caparsize(rights)
|
||||
if n < capArSizeMin || n > capArSizeMax {
|
||||
return errors.New("bad rights size")
|
||||
}
|
||||
|
||||
for _, right := range setrights {
|
||||
if caprver(right) != CAP_RIGHTS_VERSION_00 {
|
||||
return errors.New("bad right version")
|
||||
}
|
||||
i, err := rightToIndex(right)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if i >= n {
|
||||
return errors.New("index overflow")
|
||||
}
|
||||
if capidxbit(rights.Rights[i]) != capidxbit(right) {
|
||||
return errors.New("index mismatch")
|
||||
}
|
||||
rights.Rights[i] |= right
|
||||
if capidxbit(rights.Rights[i]) != capidxbit(right) {
|
||||
return errors.New("index mismatch (after assign)")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CapRightsClear clears the permissions in clearrights from rights.
|
||||
func CapRightsClear(rights *CapRights, clearrights []uint64) error {
|
||||
// This is essentially a copy of cap_rights_vclear()
|
||||
if capver(rights) != CAP_RIGHTS_VERSION_00 {
|
||||
return fmt.Errorf("bad rights version %d", capver(rights))
|
||||
}
|
||||
|
||||
n := caparsize(rights)
|
||||
if n < capArSizeMin || n > capArSizeMax {
|
||||
return errors.New("bad rights size")
|
||||
}
|
||||
|
||||
for _, right := range clearrights {
|
||||
if caprver(right) != CAP_RIGHTS_VERSION_00 {
|
||||
return errors.New("bad right version")
|
||||
}
|
||||
i, err := rightToIndex(right)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if i >= n {
|
||||
return errors.New("index overflow")
|
||||
}
|
||||
if capidxbit(rights.Rights[i]) != capidxbit(right) {
|
||||
return errors.New("index mismatch")
|
||||
}
|
||||
rights.Rights[i] &= ^(right & 0x01FFFFFFFFFFFFFF)
|
||||
if capidxbit(rights.Rights[i]) != capidxbit(right) {
|
||||
return errors.New("index mismatch (after assign)")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CapRightsIsSet checks whether all the permissions in setrights are present in rights.
|
||||
func CapRightsIsSet(rights *CapRights, setrights []uint64) (bool, error) {
|
||||
// This is essentially a copy of cap_rights_is_vset()
|
||||
if capver(rights) != CAP_RIGHTS_VERSION_00 {
|
||||
return false, fmt.Errorf("bad rights version %d", capver(rights))
|
||||
}
|
||||
|
||||
n := caparsize(rights)
|
||||
if n < capArSizeMin || n > capArSizeMax {
|
||||
return false, errors.New("bad rights size")
|
||||
}
|
||||
|
||||
for _, right := range setrights {
|
||||
if caprver(right) != CAP_RIGHTS_VERSION_00 {
|
||||
return false, errors.New("bad right version")
|
||||
}
|
||||
i, err := rightToIndex(right)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if i >= n {
|
||||
return false, errors.New("index overflow")
|
||||
}
|
||||
if capidxbit(rights.Rights[i]) != capidxbit(right) {
|
||||
return false, errors.New("index mismatch")
|
||||
}
|
||||
if (rights.Rights[i] & right) != right {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func capright(idx uint64, bit uint64) uint64 {
|
||||
return ((1 << (57 + idx)) | bit)
|
||||
}
|
||||
|
||||
// CapRightsInit returns a pointer to an initialised CapRights structure filled with rights.
|
||||
// See man cap_rights_init(3) and rights(4).
|
||||
func CapRightsInit(rights []uint64) (*CapRights, error) {
|
||||
var r CapRights
|
||||
r.Rights[0] = (capRightsGoVersion << 62) | capright(0, 0)
|
||||
r.Rights[1] = capright(1, 0)
|
||||
|
||||
err := CapRightsSet(&r, rights)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
// CapRightsLimit reduces the operations permitted on fd to at most those contained in rights.
|
||||
// The capability rights on fd can never be increased by CapRightsLimit.
|
||||
// See man cap_rights_limit(2) and rights(4).
|
||||
func CapRightsLimit(fd uintptr, rights *CapRights) error {
|
||||
return capRightsLimit(int(fd), rights)
|
||||
}
|
||||
|
||||
// CapRightsGet returns a CapRights structure containing the operations permitted on fd.
|
||||
// See man cap_rights_get(3) and rights(4).
|
||||
func CapRightsGet(fd uintptr) (*CapRights, error) {
|
||||
r, err := CapRightsInit(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = capRightsGet(capRightsGoVersion, int(fd), r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
|
||||
|
||||
package unix
|
||||
|
||||
const (
|
||||
R_OK = 0x4
|
||||
W_OK = 0x2
|
||||
X_OK = 0x1
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user