summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Godeps/Godeps.json19
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/advanced_path.go42
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/arc.go67
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/Makefile11
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/arc.go36
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/cubic_float64.go67
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/cubic_float64_others.go696
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/curve_test.go262
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/quad_float64.go51
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curves.go336
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/dasher.go90
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/demux_converter.go23
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/doc.go5
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/font.go97
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/gc.go55
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/image.go359
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/math.go52
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/paint.go92
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path.go27
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_adder.go70
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_converter.go173
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_storage.go190
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/coverage_table.go203
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerAA.go320
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerV1/fillerAA.go303
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerV2/fillerAA.go320
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fixed_point.go17
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/line.go55
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/polygon.go581
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/raster_test.go200
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/rgba_interpolation.go150
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/stack_gc.go208
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/stroker.go135
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/transform.go306
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/vertex2d.go19
-rw-r--r--Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/geom.go280
-rw-r--r--Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/paint.go292
-rw-r--r--Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/raster.go579
-rw-r--r--Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/stroke.go466
-rw-r--r--Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/glyph.go530
-rw-r--r--Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint.go1764
-rw-r--r--Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint_test.go673
-rw-r--r--Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/opcodes.go289
-rw-r--r--Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype.go554
-rw-r--r--Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype_test.go366
-rw-r--r--LICENSE.txt38
-rw-r--r--NOTICE.md8
-rw-r--r--README.md135
-rw-r--r--api/auto_constants.go2
-rw-r--r--api/channel_test.go12
-rw-r--r--api/command.go23
-rw-r--r--api/file.go1
-rw-r--r--api/post.go20
-rw-r--r--api/post_test.go2
-rw-r--r--api/team.go82
-rw-r--r--api/team_test.go103
-rw-r--r--api/user.go43
-rw-r--r--api/web_socket_test.go2
-rw-r--r--config/config.json4
-rw-r--r--config/config_docker.json4
-rw-r--r--model/channel_list.go6
-rw-r--r--model/channel_member.go5
-rw-r--r--model/client.go18
-rw-r--r--model/team.go1
-rw-r--r--model/utils.go2
-rw-r--r--store/sql_channel_store.go12
-rw-r--r--store/sql_team_store.go6
-rw-r--r--utils/config.go2
-rw-r--r--utils/lru.go8
-rw-r--r--utils/lru_test.go8
-rw-r--r--web/react/components/channel_header.jsx32
-rw-r--r--web/react/components/channel_info_modal.jsx15
-rw-r--r--web/react/components/channel_loader.jsx1
-rw-r--r--web/react/components/channel_notifications.jsx208
-rw-r--r--web/react/components/create_comment.jsx28
-rw-r--r--web/react/components/create_post.jsx34
-rw-r--r--web/react/components/file_upload.jsx18
-rw-r--r--web/react/components/invite_member_modal.jsx14
-rw-r--r--web/react/components/login.jsx16
-rw-r--r--web/react/components/mention.jsx4
-rw-r--r--web/react/components/navbar.jsx2
-rw-r--r--web/react/components/post_list.jsx30
-rw-r--r--web/react/components/setting_item_max.jsx2
-rw-r--r--web/react/components/settings_sidebar.jsx13
-rw-r--r--web/react/components/sidebar_header.jsx3
-rw-r--r--web/react/components/sidebar_right_menu.jsx3
-rw-r--r--web/react/components/signup_team_complete.jsx22
-rw-r--r--web/react/components/signup_user_complete.jsx10
-rw-r--r--web/react/components/team_settings.jsx161
-rw-r--r--web/react/components/team_settings_modal.jsx (renamed from web/react/components/settings_modal.jsx)16
-rw-r--r--web/react/components/user_profile.jsx10
-rw-r--r--web/react/components/user_settings.jsx35
-rw-r--r--web/react/components/user_settings_modal.jsx68
-rw-r--r--web/react/components/view_image.jsx2
-rw-r--r--web/react/pages/channel.jsx15
-rw-r--r--web/react/stores/team_store.jsx100
-rw-r--r--web/react/utils/async_client.jsx24
-rw-r--r--web/react/utils/client.jsx31
-rw-r--r--web/react/utils/constants.jsx4
-rw-r--r--web/react/utils/utils.jsx21
-rw-r--r--web/sass-files/sass/partials/_mentions.scss25
-rw-r--r--web/sass-files/sass/partials/_modal.scss35
-rw-r--r--web/sass-files/sass/partials/_post.scss68
-rw-r--r--web/sass-files/sass/partials/_responsive.scss30
-rw-r--r--web/sass-files/sass/partials/_signup.scss6
-rw-r--r--web/static/config/config.js1
-rw-r--r--web/static/images/ding.mp3bin25004 -> 34734 bytes
-rw-r--r--web/static/images/salamander.jpgbin17687 -> 0 bytes
-rw-r--r--web/static/images/test.pngbin1549 -> 279591 bytes
-rwxr-xr-xweb/static/images/testgif.gifbin0 -> 38689 bytes
-rwxr-xr-xweb/static/images/testjpg.jpgbin0 -> 70920 bytes
-rw-r--r--web/static/images/toothless.gifbin163454 -> 0 bytes
-rw-r--r--web/templates/channel.html3
-rw-r--r--web/templates/signup_team.html3
-rw-r--r--web/web.go1
115 files changed, 1284 insertions, 11807 deletions
diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json
index 87207c5e1..093b2a1fc 100644
--- a/Godeps/Godeps.json
+++ b/Godeps/Godeps.json
@@ -3,21 +3,6 @@
"GoVersion": "go1.4",
"Deps": [
{
- "ImportPath": "code.google.com/p/draw2d/draw2d",
- "Comment": "release-20",
- "Rev": "eaeb833648eee1b7c20e77ffc8180646c0395298"
- },
- {
- "ImportPath": "code.google.com/p/freetype-go/freetype/raster",
- "Comment": "release-85",
- "Rev": "46c3056cafbb4da11c4087a892c7d2bfa4224a8f"
- },
- {
- "ImportPath": "code.google.com/p/freetype-go/freetype/truetype",
- "Comment": "release-85",
- "Rev": "46c3056cafbb4da11c4087a892c7d2bfa4224a8f"
- },
- {
"ImportPath": "code.google.com/p/go-uuid/uuid",
"Comment": "null-15",
"Rev": "35bc42037350f0078e3c974c6ea690f1926603ab"
@@ -151,6 +136,10 @@
"ImportPath": "gopkg.in/redis.v2",
"Comment": "v2.3.2",
"Rev": "e6179049628164864e6e84e973cfb56335748dea"
+ },
+ {
+ "ImportPath": "golang.org/x/image/bmp",
+ "Rev": "eb11b45157c1b71f30b3cec66306f1cd779a689e"
}
]
}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/advanced_path.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/advanced_path.go
deleted file mode 100644
index 68f1d782b..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/advanced_path.go
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 13/12/2010 by Laurent Le Goff
-
-package draw2d
-
-import (
- "math"
-)
-
-//high level path creation
-
-func Rect(path Path, x1, y1, x2, y2 float64) {
- path.MoveTo(x1, y1)
- path.LineTo(x2, y1)
- path.LineTo(x2, y2)
- path.LineTo(x1, y2)
- path.Close()
-}
-
-func RoundRect(path Path, x1, y1, x2, y2, arcWidth, arcHeight float64) {
- arcWidth = arcWidth / 2
- arcHeight = arcHeight / 2
- path.MoveTo(x1, y1+arcHeight)
- path.QuadCurveTo(x1, y1, x1+arcWidth, y1)
- path.LineTo(x2-arcWidth, y1)
- path.QuadCurveTo(x2, y1, x2, y1+arcHeight)
- path.LineTo(x2, y2-arcHeight)
- path.QuadCurveTo(x2, y2, x2-arcWidth, y2)
- path.LineTo(x1+arcWidth, y2)
- path.QuadCurveTo(x1, y2, x1, y2-arcHeight)
- path.Close()
-}
-
-func Ellipse(path Path, cx, cy, rx, ry float64) {
- path.ArcTo(cx, cy, rx, ry, 0, -math.Pi*2)
- path.Close()
-}
-
-func Circle(path Path, cx, cy, radius float64) {
- path.ArcTo(cx, cy, radius, radius, 0, -math.Pi*2)
- path.Close()
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/arc.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/arc.go
deleted file mode 100644
index 0698b8da0..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/arc.go
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 21/11/2010 by Laurent Le Goff
-
-package draw2d
-
-import (
- "code.google.com/p/freetype-go/freetype/raster"
- "math"
-)
-
-func arc(t VertexConverter, x, y, rx, ry, start, angle, scale float64) (lastX, lastY float64) {
- end := start + angle
- clockWise := true
- if angle < 0 {
- clockWise = false
- }
- ra := (math.Abs(rx) + math.Abs(ry)) / 2
- da := math.Acos(ra/(ra+0.125/scale)) * 2
- //normalize
- if !clockWise {
- da = -da
- }
- angle = start + da
- var curX, curY float64
- for {
- if (angle < end-da/4) != clockWise {
- curX = x + math.Cos(end)*rx
- curY = y + math.Sin(end)*ry
- return curX, curY
- }
- curX = x + math.Cos(angle)*rx
- curY = y + math.Sin(angle)*ry
-
- angle += da
- t.Vertex(curX, curY)
- }
- return curX, curY
-}
-
-func arcAdder(adder raster.Adder, x, y, rx, ry, start, angle, scale float64) raster.Point {
- end := start + angle
- clockWise := true
- if angle < 0 {
- clockWise = false
- }
- ra := (math.Abs(rx) + math.Abs(ry)) / 2
- da := math.Acos(ra/(ra+0.125/scale)) * 2
- //normalize
- if !clockWise {
- da = -da
- }
- angle = start + da
- var curX, curY float64
- for {
- if (angle < end-da/4) != clockWise {
- curX = x + math.Cos(end)*rx
- curY = y + math.Sin(end)*ry
- return raster.Point{raster.Fix32(curX * 256), raster.Fix32(curY * 256)}
- }
- curX = x + math.Cos(angle)*rx
- curY = y + math.Sin(angle)*ry
-
- angle += da
- adder.Add1(raster.Point{raster.Fix32(curX * 256), raster.Fix32(curY * 256)})
- }
- return raster.Point{raster.Fix32(curX * 256), raster.Fix32(curY * 256)}
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/Makefile b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/Makefile
deleted file mode 100644
index 15ceee070..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-include $(GOROOT)/src/Make.inc
-
-TARG=draw2d.googlecode.com/hg/draw2d/curve
-GOFILES=\
- cubic_float64.go\
- quad_float64.go\
- cubic_float64_others.go\
-
-
-
-include $(GOROOT)/src/Make.pkg
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/arc.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/arc.go
deleted file mode 100644
index 92850e979..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/arc.go
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 21/11/2010 by Laurent Le Goff
-package curve
-
-import (
- "math"
-)
-
-func SegmentArc(t LineTracer, x, y, rx, ry, start, angle, scale float64) {
- end := start + angle
- clockWise := true
- if angle < 0 {
- clockWise = false
- }
- ra := (math.Abs(rx) + math.Abs(ry)) / 2
- da := math.Acos(ra/(ra+0.125/scale)) * 2
- //normalize
- if !clockWise {
- da = -da
- }
- angle = start + da
- var curX, curY float64
- for {
- if (angle < end-da/4) != clockWise {
- curX = x + math.Cos(end)*rx
- curY = y + math.Sin(end)*ry
- break;
- }
- curX = x + math.Cos(angle)*rx
- curY = y + math.Sin(angle)*ry
-
- angle += da
- t.LineTo(curX, curY)
- }
- t.LineTo(curX, curY)
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/cubic_float64.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/cubic_float64.go
deleted file mode 100644
index 64a7ac639..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/cubic_float64.go
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 17/05/2011 by Laurent Le Goff
-package curve
-
-import (
- "math"
-)
-
-const (
- CurveRecursionLimit = 32
-)
-
-// X1, Y1, X2, Y2, X3, Y3, X4, Y4 float64
-type CubicCurveFloat64 [8]float64
-
-type LineTracer interface {
- LineTo(x, y float64)
-}
-
-func (c *CubicCurveFloat64) Subdivide(c1, c2 *CubicCurveFloat64) (x23, y23 float64) {
- // Calculate all the mid-points of the line segments
- //----------------------
- c1[0], c1[1] = c[0], c[1]
- c2[6], c2[7] = c[6], c[7]
- c1[2] = (c[0] + c[2]) / 2
- c1[3] = (c[1] + c[3]) / 2
- x23 = (c[2] + c[4]) / 2
- y23 = (c[3] + c[5]) / 2
- c2[4] = (c[4] + c[6]) / 2
- c2[5] = (c[5] + c[7]) / 2
- c1[4] = (c1[2] + x23) / 2
- c1[5] = (c1[3] + y23) / 2
- c2[2] = (x23 + c2[4]) / 2
- c2[3] = (y23 + c2[5]) / 2
- c1[6] = (c1[4] + c2[2]) / 2
- c1[7] = (c1[5] + c2[3]) / 2
- c2[0], c2[1] = c1[6], c1[7]
- return
-}
-
-func (curve *CubicCurveFloat64) Segment(t LineTracer, flattening_threshold float64) {
- var curves [CurveRecursionLimit]CubicCurveFloat64
- curves[0] = *curve
- i := 0
- // current curve
- var c *CubicCurveFloat64
-
- var dx, dy, d2, d3 float64
-
- for i >= 0 {
- c = &curves[i]
- dx = c[6] - c[0]
- dy = c[7] - c[1]
-
- d2 = math.Abs(((c[2]-c[6])*dy - (c[3]-c[7])*dx))
- d3 = math.Abs(((c[4]-c[6])*dy - (c[5]-c[7])*dx))
-
- if (d2+d3)*(d2+d3) < flattening_threshold*(dx*dx+dy*dy) || i == len(curves)-1 {
- t.LineTo(c[6], c[7])
- i--
- } else {
- // second half of bezier go lower onto the stack
- c.Subdivide(&curves[i+1], &curves[i])
- i++
- }
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/cubic_float64_others.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/cubic_float64_others.go
deleted file mode 100644
index a888b22a1..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/cubic_float64_others.go
+++ /dev/null
@@ -1,696 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 17/05/2011 by Laurent Le Goff
-package curve
-
-import (
- "math"
-)
-
-const (
- CurveCollinearityEpsilon = 1e-30
- CurveAngleToleranceEpsilon = 0.01
-)
-
-//mu ranges from 0 to 1, start to end of curve
-func (c *CubicCurveFloat64) ArbitraryPoint(mu float64) (x, y float64) {
-
- mum1 := 1 - mu
- mum13 := mum1 * mum1 * mum1
- mu3 := mu * mu * mu
-
- x = mum13*c[0] + 3*mu*mum1*mum1*c[2] + 3*mu*mu*mum1*c[4] + mu3*c[6]
- y = mum13*c[1] + 3*mu*mum1*mum1*c[3] + 3*mu*mu*mum1*c[5] + mu3*c[7]
- return
-}
-
-func (c *CubicCurveFloat64) SubdivideAt(c1, c2 *CubicCurveFloat64, t float64) (x23, y23 float64) {
- inv_t := (1 - t)
- c1[0], c1[1] = c[0], c[1]
- c2[6], c2[7] = c[6], c[7]
-
- c1[2] = inv_t*c[0] + t*c[2]
- c1[3] = inv_t*c[1] + t*c[3]
-
- x23 = inv_t*c[2] + t*c[4]
- y23 = inv_t*c[3] + t*c[5]
-
- c2[4] = inv_t*c[4] + t*c[6]
- c2[5] = inv_t*c[5] + t*c[7]
-
- c1[4] = inv_t*c1[2] + t*x23
- c1[5] = inv_t*c1[3] + t*y23
-
- c2[2] = inv_t*x23 + t*c2[4]
- c2[3] = inv_t*y23 + t*c2[5]
-
- c1[6] = inv_t*c1[4] + t*c2[2]
- c1[7] = inv_t*c1[5] + t*c2[3]
-
- c2[0], c2[1] = c1[6], c1[7]
- return
-}
-
-func (c *CubicCurveFloat64) EstimateDistance() float64 {
- dx1 := c[2] - c[0]
- dy1 := c[3] - c[1]
- dx2 := c[4] - c[2]
- dy2 := c[5] - c[3]
- dx3 := c[6] - c[4]
- dy3 := c[7] - c[5]
- return math.Sqrt(dx1*dx1+dy1*dy1) + math.Sqrt(dx2*dx2+dy2*dy2) + math.Sqrt(dx3*dx3+dy3*dy3)
-}
-
-// subdivide the curve in straight lines using line approximation and Casteljau recursive subdivision
-func (c *CubicCurveFloat64) SegmentRec(t LineTracer, flattening_threshold float64) {
- c.segmentRec(t, flattening_threshold)
- t.LineTo(c[6], c[7])
-}
-
-func (c *CubicCurveFloat64) segmentRec(t LineTracer, flattening_threshold float64) {
- var c1, c2 CubicCurveFloat64
- c.Subdivide(&c1, &c2)
-
- // Try to approximate the full cubic curve by a single straight line
- //------------------
- dx := c[6] - c[0]
- dy := c[7] - c[1]
-
- d2 := math.Abs(((c[2]-c[6])*dy - (c[3]-c[7])*dx))
- d3 := math.Abs(((c[4]-c[6])*dy - (c[5]-c[7])*dx))
-
- if (d2+d3)*(d2+d3) < flattening_threshold*(dx*dx+dy*dy) {
- t.LineTo(c[6], c[7])
- return
- }
- // Continue subdivision
- //----------------------
- c1.segmentRec(t, flattening_threshold)
- c2.segmentRec(t, flattening_threshold)
-}
-
-/*
- The function has the following parameters:
- approximationScale :
- Eventually determines the approximation accuracy. In practice we need to transform points from the World coordinate system to the Screen one.
- It always has some scaling coefficient.
- The curves are usually processed in the World coordinates, while the approximation accuracy should be eventually in pixels.
- Usually it looks as follows:
- curved.approximationScale(transform.scale());
- where transform is the affine matrix that includes all the transformations, including viewport and zoom.
- angleTolerance :
- You set it in radians.
- The less this value is the more accurate will be the approximation at sharp turns.
- But 0 means that we don't consider angle conditions at all.
- cuspLimit :
- An angle in radians.
- If 0, only the real cusps will have bevel cuts.
- If more than 0, it will restrict the sharpness.
- The more this value is the less sharp turns will be cut.
- Typically it should not exceed 10-15 degrees.
-*/
-func (c *CubicCurveFloat64) AdaptiveSegmentRec(t LineTracer, approximationScale, angleTolerance, cuspLimit float64) {
- cuspLimit = computeCuspLimit(cuspLimit)
- distanceToleranceSquare := 0.5 / approximationScale
- distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare
- c.adaptiveSegmentRec(t, 0, distanceToleranceSquare, angleTolerance, cuspLimit)
- t.LineTo(c[6], c[7])
-}
-
-func computeCuspLimit(v float64) (r float64) {
- if v == 0.0 {
- r = 0.0
- } else {
- r = math.Pi - v
- }
- return
-}
-
-func squareDistance(x1, y1, x2, y2 float64) float64 {
- dx := x2 - x1
- dy := y2 - y1
- return dx*dx + dy*dy
-}
-
-/**
- * http://www.antigrain.com/research/adaptive_bezier/index.html
- */
-func (c *CubicCurveFloat64) adaptiveSegmentRec(t LineTracer, level int, distanceToleranceSquare, angleTolerance, cuspLimit float64) {
- if level > CurveRecursionLimit {
- return
- }
- var c1, c2 CubicCurveFloat64
- x23, y23 := c.Subdivide(&c1, &c2)
-
- // Try to approximate the full cubic curve by a single straight line
- //------------------
- dx := c[6] - c[0]
- dy := c[7] - c[1]
-
- d2 := math.Abs(((c[2]-c[6])*dy - (c[3]-c[7])*dx))
- d3 := math.Abs(((c[4]-c[6])*dy - (c[5]-c[7])*dx))
- switch {
- case d2 <= CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon:
- // All collinear OR p1==p4
- //----------------------
- k := dx*dx + dy*dy
- if k == 0 {
- d2 = squareDistance(c[0], c[1], c[2], c[3])
- d3 = squareDistance(c[6], c[7], c[4], c[5])
- } else {
- k = 1 / k
- da1 := c[2] - c[0]
- da2 := c[3] - c[1]
- d2 = k * (da1*dx + da2*dy)
- da1 = c[4] - c[0]
- da2 = c[5] - c[1]
- d3 = k * (da1*dx + da2*dy)
- if d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1 {
- // Simple collinear case, 1---2---3---4
- // We can leave just two endpoints
- return
- }
- if d2 <= 0 {
- d2 = squareDistance(c[2], c[3], c[0], c[1])
- } else if d2 >= 1 {
- d2 = squareDistance(c[2], c[3], c[6], c[7])
- } else {
- d2 = squareDistance(c[2], c[3], c[0]+d2*dx, c[1]+d2*dy)
- }
-
- if d3 <= 0 {
- d3 = squareDistance(c[4], c[5], c[0], c[1])
- } else if d3 >= 1 {
- d3 = squareDistance(c[4], c[5], c[6], c[7])
- } else {
- d3 = squareDistance(c[4], c[5], c[0]+d3*dx, c[1]+d3*dy)
- }
- }
- if d2 > d3 {
- if d2 < distanceToleranceSquare {
- t.LineTo(c[2], c[3])
- return
- }
- } else {
- if d3 < distanceToleranceSquare {
- t.LineTo(c[4], c[5])
- return
- }
- }
-
- case d2 <= CurveCollinearityEpsilon && d3 > CurveCollinearityEpsilon:
- // p1,p2,p4 are collinear, p3 is significant
- //----------------------
- if d3*d3 <= distanceToleranceSquare*(dx*dx+dy*dy) {
- if angleTolerance < CurveAngleToleranceEpsilon {
- t.LineTo(x23, y23)
- return
- }
-
- // Angle Condition
- //----------------------
- da1 := math.Abs(math.Atan2(c[7]-c[5], c[6]-c[4]) - math.Atan2(c[5]-c[3], c[4]-c[2]))
- if da1 >= math.Pi {
- da1 = 2*math.Pi - da1
- }
-
- if da1 < angleTolerance {
- t.LineTo(c[2], c[3])
- t.LineTo(c[4], c[5])
- return
- }
-
- if cuspLimit != 0.0 {
- if da1 > cuspLimit {
- t.LineTo(c[4], c[5])
- return
- }
- }
- }
-
- case d2 > CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon:
- // p1,p3,p4 are collinear, p2 is significant
- //----------------------
- if d2*d2 <= distanceToleranceSquare*(dx*dx+dy*dy) {
- if angleTolerance < CurveAngleToleranceEpsilon {
- t.LineTo(x23, y23)
- return
- }
-
- // Angle Condition
- //----------------------
- da1 := math.Abs(math.Atan2(c[5]-c[3], c[4]-c[2]) - math.Atan2(c[3]-c[1], c[2]-c[0]))
- if da1 >= math.Pi {
- da1 = 2*math.Pi - da1
- }
-
- if da1 < angleTolerance {
- t.LineTo(c[2], c[3])
- t.LineTo(c[4], c[5])
- return
- }
-
- if cuspLimit != 0.0 {
- if da1 > cuspLimit {
- t.LineTo(c[2], c[3])
- return
- }
- }
- }
-
- case d2 > CurveCollinearityEpsilon && d3 > CurveCollinearityEpsilon:
- // Regular case
- //-----------------
- if (d2+d3)*(d2+d3) <= distanceToleranceSquare*(dx*dx+dy*dy) {
- // If the curvature doesn't exceed the distanceTolerance value
- // we tend to finish subdivisions.
- //----------------------
- if angleTolerance < CurveAngleToleranceEpsilon {
- t.LineTo(x23, y23)
- return
- }
-
- // Angle & Cusp Condition
- //----------------------
- k := math.Atan2(c[5]-c[3], c[4]-c[2])
- da1 := math.Abs(k - math.Atan2(c[3]-c[1], c[2]-c[0]))
- da2 := math.Abs(math.Atan2(c[7]-c[5], c[6]-c[4]) - k)
- if da1 >= math.Pi {
- da1 = 2*math.Pi - da1
- }
- if da2 >= math.Pi {
- da2 = 2*math.Pi - da2
- }
-
- if da1+da2 < angleTolerance {
- // Finally we can stop the recursion
- //----------------------
- t.LineTo(x23, y23)
- return
- }
-
- if cuspLimit != 0.0 {
- if da1 > cuspLimit {
- t.LineTo(c[2], c[3])
- return
- }
-
- if da2 > cuspLimit {
- t.LineTo(c[4], c[5])
- return
- }
- }
- }
- }
-
- // Continue subdivision
- //----------------------
- c1.adaptiveSegmentRec(t, level+1, distanceToleranceSquare, angleTolerance, cuspLimit)
- c2.adaptiveSegmentRec(t, level+1, distanceToleranceSquare, angleTolerance, cuspLimit)
-
-}
-
-func (curve *CubicCurveFloat64) AdaptiveSegment(t LineTracer, approximationScale, angleTolerance, cuspLimit float64) {
- cuspLimit = computeCuspLimit(cuspLimit)
- distanceToleranceSquare := 0.5 / approximationScale
- distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare
-
- var curves [CurveRecursionLimit]CubicCurveFloat64
- curves[0] = *curve
- i := 0
- // current curve
- var c *CubicCurveFloat64
- var c1, c2 CubicCurveFloat64
- var dx, dy, d2, d3, k, x23, y23 float64
- for i >= 0 {
- c = &curves[i]
- x23, y23 = c.Subdivide(&c1, &c2)
-
- // Try to approximate the full cubic curve by a single straight line
- //------------------
- dx = c[6] - c[0]
- dy = c[7] - c[1]
-
- d2 = math.Abs(((c[2]-c[6])*dy - (c[3]-c[7])*dx))
- d3 = math.Abs(((c[4]-c[6])*dy - (c[5]-c[7])*dx))
- switch {
- case i == len(curves)-1:
- t.LineTo(c[6], c[7])
- i--
- continue
- case d2 <= CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon:
- // All collinear OR p1==p4
- //----------------------
- k = dx*dx + dy*dy
- if k == 0 {
- d2 = squareDistance(c[0], c[1], c[2], c[3])
- d3 = squareDistance(c[6], c[7], c[4], c[5])
- } else {
- k = 1 / k
- da1 := c[2] - c[0]
- da2 := c[3] - c[1]
- d2 = k * (da1*dx + da2*dy)
- da1 = c[4] - c[0]
- da2 = c[5] - c[1]
- d3 = k * (da1*dx + da2*dy)
- if d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1 {
- // Simple collinear case, 1---2---3---4
- // We can leave just two endpoints
- i--
- continue
- }
- if d2 <= 0 {
- d2 = squareDistance(c[2], c[3], c[0], c[1])
- } else if d2 >= 1 {
- d2 = squareDistance(c[2], c[3], c[6], c[7])
- } else {
- d2 = squareDistance(c[2], c[3], c[0]+d2*dx, c[1]+d2*dy)
- }
-
- if d3 <= 0 {
- d3 = squareDistance(c[4], c[5], c[0], c[1])
- } else if d3 >= 1 {
- d3 = squareDistance(c[4], c[5], c[6], c[7])
- } else {
- d3 = squareDistance(c[4], c[5], c[0]+d3*dx, c[1]+d3*dy)
- }
- }
- if d2 > d3 {
- if d2 < distanceToleranceSquare {
- t.LineTo(c[2], c[3])
- i--
- continue
- }
- } else {
- if d3 < distanceToleranceSquare {
- t.LineTo(c[4], c[5])
- i--
- continue
- }
- }
-
- case d2 <= CurveCollinearityEpsilon && d3 > CurveCollinearityEpsilon:
- // p1,p2,p4 are collinear, p3 is significant
- //----------------------
- if d3*d3 <= distanceToleranceSquare*(dx*dx+dy*dy) {
- if angleTolerance < CurveAngleToleranceEpsilon {
- t.LineTo(x23, y23)
- i--
- continue
- }
-
- // Angle Condition
- //----------------------
- da1 := math.Abs(math.Atan2(c[7]-c[5], c[6]-c[4]) - math.Atan2(c[5]-c[3], c[4]-c[2]))
- if da1 >= math.Pi {
- da1 = 2*math.Pi - da1
- }
-
- if da1 < angleTolerance {
- t.LineTo(c[2], c[3])
- t.LineTo(c[4], c[5])
- i--
- continue
- }
-
- if cuspLimit != 0.0 {
- if da1 > cuspLimit {
- t.LineTo(c[4], c[5])
- i--
- continue
- }
- }
- }
-
- case d2 > CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon:
- // p1,p3,p4 are collinear, p2 is significant
- //----------------------
- if d2*d2 <= distanceToleranceSquare*(dx*dx+dy*dy) {
- if angleTolerance < CurveAngleToleranceEpsilon {
- t.LineTo(x23, y23)
- i--
- continue
- }
-
- // Angle Condition
- //----------------------
- da1 := math.Abs(math.Atan2(c[5]-c[3], c[4]-c[2]) - math.Atan2(c[3]-c[1], c[2]-c[0]))
- if da1 >= math.Pi {
- da1 = 2*math.Pi - da1
- }
-
- if da1 < angleTolerance {
- t.LineTo(c[2], c[3])
- t.LineTo(c[4], c[5])
- i--
- continue
- }
-
- if cuspLimit != 0.0 {
- if da1 > cuspLimit {
- t.LineTo(c[2], c[3])
- i--
- continue
- }
- }
- }
-
- case d2 > CurveCollinearityEpsilon && d3 > CurveCollinearityEpsilon:
- // Regular case
- //-----------------
- if (d2+d3)*(d2+d3) <= distanceToleranceSquare*(dx*dx+dy*dy) {
- // If the curvature doesn't exceed the distanceTolerance value
- // we tend to finish subdivisions.
- //----------------------
- if angleTolerance < CurveAngleToleranceEpsilon {
- t.LineTo(x23, y23)
- i--
- continue
- }
-
- // Angle & Cusp Condition
- //----------------------
- k := math.Atan2(c[5]-c[3], c[4]-c[2])
- da1 := math.Abs(k - math.Atan2(c[3]-c[1], c[2]-c[0]))
- da2 := math.Abs(math.Atan2(c[7]-c[5], c[6]-c[4]) - k)
- if da1 >= math.Pi {
- da1 = 2*math.Pi - da1
- }
- if da2 >= math.Pi {
- da2 = 2*math.Pi - da2
- }
-
- if da1+da2 < angleTolerance {
- // Finally we can stop the recursion
- //----------------------
- t.LineTo(x23, y23)
- i--
- continue
- }
-
- if cuspLimit != 0.0 {
- if da1 > cuspLimit {
- t.LineTo(c[2], c[3])
- i--
- continue
- }
-
- if da2 > cuspLimit {
- t.LineTo(c[4], c[5])
- i--
- continue
- }
- }
- }
- }
-
- // Continue subdivision
- //----------------------
- curves[i+1], curves[i] = c1, c2
- i++
- }
- t.LineTo(curve[6], curve[7])
-}
-
-/********************** Ahmad thesis *******************/
-
-/**************************************************************************************
-* This code is the implementation of the Parabolic Approximation (PA). Although *
-* it uses recursive subdivision as a safe net for the failing cases, this is an *
-* iterative routine and reduces considerably the number of vertices (point) *
-* generation. *
-**************************************************************************************/
-
-func (c *CubicCurveFloat64) ParabolicSegment(t LineTracer, flattening_threshold float64) {
- estimatedIFP := c.numberOfInflectionPoints()
- if estimatedIFP == 0 {
- // If no inflection points then apply PA on the full Bezier segment.
- c.doParabolicApproximation(t, flattening_threshold)
- return
- }
- // If one or more inflection point then we will have to subdivide the curve
- numOfIfP, t1, t2 := c.findInflectionPoints()
- if numOfIfP == 2 {
- // Case when 2 inflection points then divide at the smallest one first
- var sub1, tmp1, sub2, sub3 CubicCurveFloat64
- c.SubdivideAt(&sub1, &tmp1, t1)
- // Now find the second inflection point in the second curve an subdivide
- numOfIfP, t1, t2 = tmp1.findInflectionPoints()
- if numOfIfP == 2 {
- tmp1.SubdivideAt(&sub2, &sub3, t2)
- } else if numOfIfP == 1 {
- tmp1.SubdivideAt(&sub2, &sub3, t1)
- } else {
- return
- }
- // Use PA for first subsegment
- sub1.doParabolicApproximation(t, flattening_threshold)
- // Use RS for the second (middle) subsegment
- sub2.Segment(t, flattening_threshold)
- // Drop the last point in the array will be added by the PA in third subsegment
- //noOfPoints--;
- // Use PA for the third curve
- sub3.doParabolicApproximation(t, flattening_threshold)
- } else if numOfIfP == 1 {
- // Case where there is one inflection point, subdivide once and use PA on
- // both subsegments
- var sub1, sub2 CubicCurveFloat64
- c.SubdivideAt(&sub1, &sub2, t1)
- sub1.doParabolicApproximation(t, flattening_threshold)
- //noOfPoints--;
- sub2.doParabolicApproximation(t, flattening_threshold)
- } else {
- // Case where there is no inflection USA PA directly
- c.doParabolicApproximation(t, flattening_threshold)
- }
-}
-
-// Find the third control point deviation form the axis
-func (c *CubicCurveFloat64) thirdControlPointDeviation() float64 {
- dx := c[2] - c[0]
- dy := c[3] - c[1]
- l2 := dx*dx + dy*dy
- if l2 == 0 {
- return 0
- }
- l := math.Sqrt(l2)
- r := (c[3] - c[1]) / l
- s := (c[0] - c[2]) / l
- u := (c[2]*c[1] - c[0]*c[3]) / l
- return math.Abs(r*c[4] + s*c[5] + u)
-}
-
-// Find the number of inflection point
-func (c *CubicCurveFloat64) numberOfInflectionPoints() int {
- dx21 := (c[2] - c[0])
- dy21 := (c[3] - c[1])
- dx32 := (c[4] - c[2])
- dy32 := (c[5] - c[3])
- dx43 := (c[6] - c[4])
- dy43 := (c[7] - c[5])
- if ((dx21*dy32 - dy21*dx32) * (dx32*dy43 - dy32*dx43)) < 0 {
- return 1 // One inflection point
- } else if ((dx21*dy32 - dy21*dx32) * (dx21*dy43 - dy21*dx43)) > 0 {
- return 0 // No inflection point
- } else {
- // Most cases no inflection point
- b1 := (dx21*dx32 + dy21*dy32) > 0
- b2 := (dx32*dx43 + dy32*dy43) > 0
- if b1 || b2 && !(b1 && b2) { // xor!!
- return 0
- }
- }
- return -1 // cases where there in zero or two inflection points
-}
-
-// This is the main function where all the work is done
-func (curve *CubicCurveFloat64) doParabolicApproximation(tracer LineTracer, flattening_threshold float64) {
- var c *CubicCurveFloat64
- c = curve
- var d, t, dx, dy, d2, d3 float64
- for {
- dx = c[6] - c[0]
- dy = c[7] - c[1]
-
- d2 = math.Abs(((c[2]-c[6])*dy - (c[3]-c[7])*dx))
- d3 = math.Abs(((c[4]-c[6])*dy - (c[5]-c[7])*dx))
-
- if (d2+d3)*(d2+d3) < flattening_threshold*(dx*dx+dy*dy) {
- // If the subsegment deviation satisfy the flatness then store the last
- // point and stop
- tracer.LineTo(c[6], c[7])
- break
- }
- // Find the third control point deviation and the t values for subdivision
- d = c.thirdControlPointDeviation()
- t = 2 * math.Sqrt(flattening_threshold/d/3)
- if t > 1 {
- // Case where the t value calculated is invalid so using RS
- c.Segment(tracer, flattening_threshold)
- break
- }
- // Valid t value to subdivide at that calculated value
- var b1, b2 CubicCurveFloat64
- c.SubdivideAt(&b1, &b2, t)
- // First subsegment should have its deviation equal to flatness
- dx = b1[6] - b1[0]
- dy = b1[7] - b1[1]
-
- d2 = math.Abs(((b1[2]-b1[6])*dy - (b1[3]-b1[7])*dx))
- d3 = math.Abs(((b1[4]-b1[6])*dy - (b1[5]-b1[7])*dx))
-
- if (d2+d3)*(d2+d3) > flattening_threshold*(dx*dx+dy*dy) {
- // if not then use RS to handle any mathematical errors
- b1.Segment(tracer, flattening_threshold)
- } else {
- tracer.LineTo(b1[6], b1[7])
- }
- // repeat the process for the left over subsegment.
- c = &b2
- }
-}
-
-// Find the actual inflection points and return the number of inflection points found
-// if 2 inflection points found, the first one returned will be with smaller t value.
-func (curve *CubicCurveFloat64) findInflectionPoints() (int, firstIfp, secondIfp float64) {
- // For Cubic Bezier curve with equation P=a*t^3 + b*t^2 + c*t + d
- // slope of the curve dP/dt = 3*a*t^2 + 2*b*t + c
- // a = (float)(-bez.p1 + 3*bez.p2 - 3*bez.p3 + bez.p4);
- // b = (float)(3*bez.p1 - 6*bez.p2 + 3*bez.p3);
- // c = (float)(-3*bez.p1 + 3*bez.p2);
- ax := (-curve[0] + 3*curve[2] - 3*curve[4] + curve[6])
- bx := (3*curve[0] - 6*curve[2] + 3*curve[4])
- cx := (-3*curve[0] + 3*curve[2])
- ay := (-curve[1] + 3*curve[3] - 3*curve[5] + curve[7])
- by := (3*curve[1] - 6*curve[3] + 3*curve[5])
- cy := (-3*curve[1] + 3*curve[3])
- a := (3 * (ay*bx - ax*by))
- b := (3 * (ay*cx - ax*cy))
- c := (by*cx - bx*cy)
- r2 := (b*b - 4*a*c)
- firstIfp = 0.0
- secondIfp = 0.0
- if r2 >= 0.0 && a != 0.0 {
- r := math.Sqrt(r2)
- firstIfp = ((-b + r) / (2 * a))
- secondIfp = ((-b - r) / (2 * a))
- if (firstIfp > 0.0 && firstIfp < 1.0) && (secondIfp > 0.0 && secondIfp < 1.0) {
- if firstIfp > secondIfp {
- tmp := firstIfp
- firstIfp = secondIfp
- secondIfp = tmp
- }
- if secondIfp-firstIfp > 0.00001 {
- return 2, firstIfp, secondIfp
- } else {
- return 1, firstIfp, secondIfp
- }
- } else if firstIfp > 0.0 && firstIfp < 1.0 {
- return 1, firstIfp, secondIfp
- } else if secondIfp > 0.0 && secondIfp < 1.0 {
- firstIfp = secondIfp
- return 1, firstIfp, secondIfp
- }
- return 0, firstIfp, secondIfp
- }
- return 0, firstIfp, secondIfp
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/curve_test.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/curve_test.go
deleted file mode 100644
index 5e9eecac0..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/curve_test.go
+++ /dev/null
@@ -1,262 +0,0 @@
-package curve
-
-import (
- "bufio"
- "code.google.com/p/draw2d/draw2d/raster"
- "fmt"
- "image"
- "image/color"
- "image/draw"
- "image/png"
- "log"
- "os"
- "testing"
-)
-
-var (
- flattening_threshold float64 = 0.5
- testsCubicFloat64 = []CubicCurveFloat64{
- CubicCurveFloat64{100, 100, 200, 100, 100, 200, 200, 200},
- CubicCurveFloat64{100, 100, 300, 200, 200, 200, 300, 100},
- CubicCurveFloat64{100, 100, 0, 300, 200, 0, 300, 300},
- CubicCurveFloat64{150, 290, 10, 10, 290, 10, 150, 290},
- CubicCurveFloat64{10, 290, 10, 10, 290, 10, 290, 290},
- CubicCurveFloat64{100, 290, 290, 10, 10, 10, 200, 290},
- }
- testsQuadFloat64 = []QuadCurveFloat64{
- QuadCurveFloat64{100, 100, 200, 100, 200, 200},
- QuadCurveFloat64{100, 100, 290, 200, 290, 100},
- QuadCurveFloat64{100, 100, 0, 290, 200, 290},
- QuadCurveFloat64{150, 290, 10, 10, 290, 290},
- QuadCurveFloat64{10, 290, 10, 10, 290, 290},
- QuadCurveFloat64{100, 290, 290, 10, 120, 290},
- }
-)
-
-type Path struct {
- points []float64
-}
-
-func (p *Path) LineTo(x, y float64) {
- if len(p.points)+2 > cap(p.points) {
- points := make([]float64, len(p.points)+2, len(p.points)+32)
- copy(points, p.points)
- p.points = points
- } else {
- p.points = p.points[0 : len(p.points)+2]
- }
- p.points[len(p.points)-2] = x
- p.points[len(p.points)-1] = y
-}
-
-func init() {
- f, err := os.Create("_test.html")
- if err != nil {
- log.Println(err)
- os.Exit(1)
- }
- defer f.Close()
- log.Printf("Create html viewer")
- f.Write([]byte("<html><body>"))
- for i := 0; i < len(testsCubicFloat64); i++ {
- f.Write([]byte(fmt.Sprintf("<div><img src='_testRec%d.png'/>\n<img src='_test%d.png'/>\n<img src='_testAdaptiveRec%d.png'/>\n<img src='_testAdaptive%d.png'/>\n<img src='_testParabolic%d.png'/>\n</div>\n", i, i, i, i, i)))
- }
- for i := 0; i < len(testsQuadFloat64); i++ {
- f.Write([]byte(fmt.Sprintf("<div><img src='_testQuad%d.png'/>\n</div>\n", i)))
- }
- f.Write([]byte("</body></html>"))
-
-}
-
-func savepng(filePath string, m image.Image) {
- f, err := os.Create(filePath)
- if err != nil {
- log.Println(err)
- os.Exit(1)
- }
- defer f.Close()
- b := bufio.NewWriter(f)
- err = png.Encode(b, m)
- if err != nil {
- log.Println(err)
- os.Exit(1)
- }
- err = b.Flush()
- if err != nil {
- log.Println(err)
- os.Exit(1)
- }
-}
-
-func drawPoints(img draw.Image, c color.Color, s ...float64) image.Image {
- /*for i := 0; i < len(s); i += 2 {
- x, y := int(s[i]+0.5), int(s[i+1]+0.5)
- img.Set(x, y, c)
- img.Set(x, y+1, c)
- img.Set(x, y-1, c)
- img.Set(x+1, y, c)
- img.Set(x+1, y+1, c)
- img.Set(x+1, y-1, c)
- img.Set(x-1, y, c)
- img.Set(x-1, y+1, c)
- img.Set(x-1, y-1, c)
-
- }*/
- return img
-}
-
-func TestCubicCurveRec(t *testing.T) {
- for i, curve := range testsCubicFloat64 {
- var p Path
- p.LineTo(curve[0], curve[1])
- curve.SegmentRec(&p, flattening_threshold)
- img := image.NewNRGBA(image.Rect(0, 0, 300, 300))
- raster.PolylineBresenham(img, color.NRGBA{0xff, 0, 0, 0xff}, curve[:]...)
- raster.PolylineBresenham(img, image.Black, p.points...)
- //drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
- drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.points...)
- savepng(fmt.Sprintf("_testRec%d.png", i), img)
- log.Printf("Num of points: %d\n", len(p.points))
- }
- fmt.Println()
-}
-
-func TestCubicCurve(t *testing.T) {
- for i, curve := range testsCubicFloat64 {
- var p Path
- p.LineTo(curve[0], curve[1])
- curve.Segment(&p, flattening_threshold)
- img := image.NewNRGBA(image.Rect(0, 0, 300, 300))
- raster.PolylineBresenham(img, color.NRGBA{0xff, 0, 0, 0xff}, curve[:]...)
- raster.PolylineBresenham(img, image.Black, p.points...)
- //drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
- drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.points...)
- savepng(fmt.Sprintf("_test%d.png", i), img)
- log.Printf("Num of points: %d\n", len(p.points))
- }
- fmt.Println()
-}
-
-func TestCubicCurveAdaptiveRec(t *testing.T) {
- for i, curve := range testsCubicFloat64 {
- var p Path
- p.LineTo(curve[0], curve[1])
- curve.AdaptiveSegmentRec(&p, 1, 0, 0)
- img := image.NewNRGBA(image.Rect(0, 0, 300, 300))
- raster.PolylineBresenham(img, color.NRGBA{0xff, 0, 0, 0xff}, curve[:]...)
- raster.PolylineBresenham(img, image.Black, p.points...)
- //drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
- drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.points...)
- savepng(fmt.Sprintf("_testAdaptiveRec%d.png", i), img)
- log.Printf("Num of points: %d\n", len(p.points))
- }
- fmt.Println()
-}
-
-func TestCubicCurveAdaptive(t *testing.T) {
- for i, curve := range testsCubicFloat64 {
- var p Path
- p.LineTo(curve[0], curve[1])
- curve.AdaptiveSegment(&p, 1, 0, 0)
- img := image.NewNRGBA(image.Rect(0, 0, 300, 300))
- raster.PolylineBresenham(img, color.NRGBA{0xff, 0, 0, 0xff}, curve[:]...)
- raster.PolylineBresenham(img, image.Black, p.points...)
- //drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
- drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.points...)
- savepng(fmt.Sprintf("_testAdaptive%d.png", i), img)
- log.Printf("Num of points: %d\n", len(p.points))
- }
- fmt.Println()
-}
-
-func TestCubicCurveParabolic(t *testing.T) {
- for i, curve := range testsCubicFloat64 {
- var p Path
- p.LineTo(curve[0], curve[1])
- curve.ParabolicSegment(&p, flattening_threshold)
- img := image.NewNRGBA(image.Rect(0, 0, 300, 300))
- raster.PolylineBresenham(img, color.NRGBA{0xff, 0, 0, 0xff}, curve[:]...)
- raster.PolylineBresenham(img, image.Black, p.points...)
- //drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
- drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.points...)
- savepng(fmt.Sprintf("_testParabolic%d.png", i), img)
- log.Printf("Num of points: %d\n", len(p.points))
- }
- fmt.Println()
-}
-
-func TestQuadCurve(t *testing.T) {
- for i, curve := range testsQuadFloat64 {
- var p Path
- p.LineTo(curve[0], curve[1])
- curve.Segment(&p, flattening_threshold)
- img := image.NewNRGBA(image.Rect(0, 0, 300, 300))
- raster.PolylineBresenham(img, color.NRGBA{0xff, 0, 0, 0xff}, curve[:]...)
- raster.PolylineBresenham(img, image.Black, p.points...)
- //drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
- drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.points...)
- savepng(fmt.Sprintf("_testQuad%d.png", i), img)
- log.Printf("Num of points: %d\n", len(p.points))
- }
- fmt.Println()
-}
-
-func BenchmarkCubicCurveRec(b *testing.B) {
- for i := 0; i < b.N; i++ {
- for _, curve := range testsCubicFloat64 {
- p := Path{make([]float64, 0, 32)}
- p.LineTo(curve[0], curve[1])
- curve.SegmentRec(&p, flattening_threshold)
- }
- }
-}
-
-func BenchmarkCubicCurve(b *testing.B) {
- for i := 0; i < b.N; i++ {
- for _, curve := range testsCubicFloat64 {
- p := Path{make([]float64, 0, 32)}
- p.LineTo(curve[0], curve[1])
- curve.Segment(&p, flattening_threshold)
- }
- }
-}
-
-func BenchmarkCubicCurveAdaptiveRec(b *testing.B) {
- for i := 0; i < b.N; i++ {
- for _, curve := range testsCubicFloat64 {
- p := Path{make([]float64, 0, 32)}
- p.LineTo(curve[0], curve[1])
- curve.AdaptiveSegmentRec(&p, 1, 0, 0)
- }
- }
-}
-
-func BenchmarkCubicCurveAdaptive(b *testing.B) {
- for i := 0; i < b.N; i++ {
- for _, curve := range testsCubicFloat64 {
- p := Path{make([]float64, 0, 32)}
- p.LineTo(curve[0], curve[1])
- curve.AdaptiveSegment(&p, 1, 0, 0)
- }
- }
-}
-
-func BenchmarkCubicCurveParabolic(b *testing.B) {
- for i := 0; i < b.N; i++ {
- for _, curve := range testsCubicFloat64 {
- p := Path{make([]float64, 0, 32)}
- p.LineTo(curve[0], curve[1])
- curve.ParabolicSegment(&p, flattening_threshold)
- }
- }
-}
-
-func BenchmarkQuadCurve(b *testing.B) {
- for i := 0; i < b.N; i++ {
- for _, curve := range testsQuadFloat64 {
- p := Path{make([]float64, 0, 32)}
- p.LineTo(curve[0], curve[1])
- curve.Segment(&p, flattening_threshold)
- }
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/quad_float64.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/quad_float64.go
deleted file mode 100644
index bd72affbb..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/quad_float64.go
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 17/05/2011 by Laurent Le Goff
-package curve
-
-import (
- "math"
-)
-
-//X1, Y1, X2, Y2, X3, Y3 float64
-type QuadCurveFloat64 [6]float64
-
-func (c *QuadCurveFloat64) Subdivide(c1, c2 *QuadCurveFloat64) {
- // Calculate all the mid-points of the line segments
- //----------------------
- c1[0], c1[1] = c[0], c[1]
- c2[4], c2[5] = c[4], c[5]
- c1[2] = (c[0] + c[2]) / 2
- c1[3] = (c[1] + c[3]) / 2
- c2[2] = (c[2] + c[4]) / 2
- c2[3] = (c[3] + c[5]) / 2
- c1[4] = (c1[2] + c2[2]) / 2
- c1[5] = (c1[3] + c2[3]) / 2
- c2[0], c2[1] = c1[4], c1[5]
- return
-}
-
-func (curve *QuadCurveFloat64) Segment(t LineTracer, flattening_threshold float64) {
- var curves [CurveRecursionLimit]QuadCurveFloat64
- curves[0] = *curve
- i := 0
- // current curve
- var c *QuadCurveFloat64
- var dx, dy, d float64
-
- for i >= 0 {
- c = &curves[i]
- dx = c[4] - c[0]
- dy = c[5] - c[1]
-
- d = math.Abs(((c[2]-c[4])*dy - (c[3]-c[5])*dx))
-
- if (d*d) < flattening_threshold*(dx*dx+dy*dy) || i == len(curves)-1 {
- t.LineTo(c[4], c[5])
- i--
- } else {
- // second half of bezier go lower onto the stack
- c.Subdivide(&curves[i+1], &curves[i])
- i++
- }
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curves.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curves.go
deleted file mode 100644
index 4623cd4dc..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curves.go
+++ /dev/null
@@ -1,336 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 21/11/2010 by Laurent Le Goff
-
-package draw2d
-
-import (
- "math"
-)
-
-var (
- CurveRecursionLimit = 32
- CurveCollinearityEpsilon = 1e-30
- CurveAngleToleranceEpsilon = 0.01
-)
-
-/*
- The function has the following parameters:
- approximationScale :
- Eventually determines the approximation accuracy. In practice we need to transform points from the World coordinate system to the Screen one.
- It always has some scaling coefficient.
- The curves are usually processed in the World coordinates, while the approximation accuracy should be eventually in pixels.
- Usually it looks as follows:
- curved.approximationScale(transform.scale());
- where transform is the affine matrix that includes all the transformations, including viewport and zoom.
- angleTolerance :
- You set it in radians.
- The less this value is the more accurate will be the approximation at sharp turns.
- But 0 means that we don't consider angle conditions at all.
- cuspLimit :
- An angle in radians.
- If 0, only the real cusps will have bevel cuts.
- If more than 0, it will restrict the sharpness.
- The more this value is the less sharp turns will be cut.
- Typically it should not exceed 10-15 degrees.
-*/
-func cubicBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, x4, y4, approximationScale, angleTolerance, cuspLimit float64) {
- cuspLimit = computeCuspLimit(cuspLimit)
- distanceToleranceSquare := 0.5 / approximationScale
- distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare
- recursiveCubicBezier(v, x1, y1, x2, y2, x3, y3, x4, y4, 0, distanceToleranceSquare, angleTolerance, cuspLimit)
-}
-
-/*
- * see cubicBezier comments for approximationScale and angleTolerance definition
- */
-func quadraticBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, approximationScale, angleTolerance float64) {
- distanceToleranceSquare := 0.5 / approximationScale
- distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare
-
- recursiveQuadraticBezierBezier(v, x1, y1, x2, y2, x3, y3, 0, distanceToleranceSquare, angleTolerance)
-}
-
-func computeCuspLimit(v float64) (r float64) {
- if v == 0.0 {
- r = 0.0
- } else {
- r = math.Pi - v
- }
- return
-}
-
-/**
- * http://www.antigrain.com/research/adaptive_bezier/index.html
- */
-func recursiveQuadraticBezierBezier(v VertexConverter, x1, y1, x2, y2, x3, y3 float64, level int, distanceToleranceSquare, angleTolerance float64) {
- if level > CurveRecursionLimit {
- return
- }
-
- // Calculate all the mid-points of the line segments
- //----------------------
- x12 := (x1 + x2) / 2
- y12 := (y1 + y2) / 2
- x23 := (x2 + x3) / 2
- y23 := (y2 + y3) / 2
- x123 := (x12 + x23) / 2
- y123 := (y12 + y23) / 2
-
- dx := x3 - x1
- dy := y3 - y1
- d := math.Abs(((x2-x3)*dy - (y2-y3)*dx))
-
- if d > CurveCollinearityEpsilon {
- // Regular case
- //-----------------
- if d*d <= distanceToleranceSquare*(dx*dx+dy*dy) {
- // If the curvature doesn't exceed the distanceTolerance value
- // we tend to finish subdivisions.
- //----------------------
- if angleTolerance < CurveAngleToleranceEpsilon {
- v.Vertex(x123, y123)
- return
- }
-
- // Angle & Cusp Condition
- //----------------------
- da := math.Abs(math.Atan2(y3-y2, x3-x2) - math.Atan2(y2-y1, x2-x1))
- if da >= math.Pi {
- da = 2*math.Pi - da
- }
-
- if da < angleTolerance {
- // Finally we can stop the recursion
- //----------------------
- v.Vertex(x123, y123)
- return
- }
- }
- } else {
- // Collinear case
- //------------------
- da := dx*dx + dy*dy
- if da == 0 {
- d = squareDistance(x1, y1, x2, y2)
- } else {
- d = ((x2-x1)*dx + (y2-y1)*dy) / da
- if d > 0 && d < 1 {
- // Simple collinear case, 1---2---3
- // We can leave just two endpoints
- return
- }
- if d <= 0 {
- d = squareDistance(x2, y2, x1, y1)
- } else if d >= 1 {
- d = squareDistance(x2, y2, x3, y3)
- } else {
- d = squareDistance(x2, y2, x1+d*dx, y1+d*dy)
- }
- }
- if d < distanceToleranceSquare {
- v.Vertex(x2, y2)
- return
- }
- }
-
- // Continue subdivision
- //----------------------
- recursiveQuadraticBezierBezier(v, x1, y1, x12, y12, x123, y123, level+1, distanceToleranceSquare, angleTolerance)
- recursiveQuadraticBezierBezier(v, x123, y123, x23, y23, x3, y3, level+1, distanceToleranceSquare, angleTolerance)
-}
-
-/**
- * http://www.antigrain.com/research/adaptive_bezier/index.html
- */
-func recursiveCubicBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, x4, y4 float64, level int, distanceToleranceSquare, angleTolerance, cuspLimit float64) {
- if level > CurveRecursionLimit {
- return
- }
-
- // Calculate all the mid-points of the line segments
- //----------------------
- x12 := (x1 + x2) / 2
- y12 := (y1 + y2) / 2
- x23 := (x2 + x3) / 2
- y23 := (y2 + y3) / 2
- x34 := (x3 + x4) / 2
- y34 := (y3 + y4) / 2
- x123 := (x12 + x23) / 2
- y123 := (y12 + y23) / 2
- x234 := (x23 + x34) / 2
- y234 := (y23 + y34) / 2
- x1234 := (x123 + x234) / 2
- y1234 := (y123 + y234) / 2
-
- // Try to approximate the full cubic curve by a single straight line
- //------------------
- dx := x4 - x1
- dy := y4 - y1
-
- d2 := math.Abs(((x2-x4)*dy - (y2-y4)*dx))
- d3 := math.Abs(((x3-x4)*dy - (y3-y4)*dx))
-
- switch {
- case d2 <= CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon:
- // All collinear OR p1==p4
- //----------------------
- k := dx*dx + dy*dy
- if k == 0 {
- d2 = squareDistance(x1, y1, x2, y2)
- d3 = squareDistance(x4, y4, x3, y3)
- } else {
- k = 1 / k
- da1 := x2 - x1
- da2 := y2 - y1
- d2 = k * (da1*dx + da2*dy)
- da1 = x3 - x1
- da2 = y3 - y1
- d3 = k * (da1*dx + da2*dy)
- if d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1 {
- // Simple collinear case, 1---2---3---4
- // We can leave just two endpoints
- return
- }
- if d2 <= 0 {
- d2 = squareDistance(x2, y2, x1, y1)
- } else if d2 >= 1 {
- d2 = squareDistance(x2, y2, x4, y4)
- } else {
- d2 = squareDistance(x2, y2, x1+d2*dx, y1+d2*dy)
- }
-
- if d3 <= 0 {
- d3 = squareDistance(x3, y3, x1, y1)
- } else if d3 >= 1 {
- d3 = squareDistance(x3, y3, x4, y4)
- } else {
- d3 = squareDistance(x3, y3, x1+d3*dx, y1+d3*dy)
- }
- }
- if d2 > d3 {
- if d2 < distanceToleranceSquare {
- v.Vertex(x2, y2)
- return
- }
- } else {
- if d3 < distanceToleranceSquare {
- v.Vertex(x3, y3)
- return
- }
- }
- break
-
- case d2 <= CurveCollinearityEpsilon && d3 > CurveCollinearityEpsilon:
- // p1,p2,p4 are collinear, p3 is significant
- //----------------------
- if d3*d3 <= distanceToleranceSquare*(dx*dx+dy*dy) {
- if angleTolerance < CurveAngleToleranceEpsilon {
- v.Vertex(x23, y23)
- return
- }
-
- // Angle Condition
- //----------------------
- da1 := math.Abs(math.Atan2(y4-y3, x4-x3) - math.Atan2(y3-y2, x3-x2))
- if da1 >= math.Pi {
- da1 = 2*math.Pi - da1
- }
-
- if da1 < angleTolerance {
- v.Vertex(x2, y2)
- v.Vertex(x3, y3)
- return
- }
-
- if cuspLimit != 0.0 {
- if da1 > cuspLimit {
- v.Vertex(x3, y3)
- return
- }
- }
- }
- break
-
- case d2 > CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon:
- // p1,p3,p4 are collinear, p2 is significant
- //----------------------
- if d2*d2 <= distanceToleranceSquare*(dx*dx+dy*dy) {
- if angleTolerance < CurveAngleToleranceEpsilon {
- v.Vertex(x23, y23)
- return
- }
-
- // Angle Condition
- //----------------------
- da1 := math.Abs(math.Atan2(y3-y2, x3-x2) - math.Atan2(y2-y1, x2-x1))
- if da1 >= math.Pi {
- da1 = 2*math.Pi - da1
- }
-
- if da1 < angleTolerance {
- v.Vertex(x2, y2)
- v.Vertex(x3, y3)
- return
- }
-
- if cuspLimit != 0.0 {
- if da1 > cuspLimit {
- v.Vertex(x2, y2)
- return
- }
- }
- }
- break
-
- case d2 > CurveCollinearityEpsilon && d3 > CurveCollinearityEpsilon:
- // Regular case
- //-----------------
- if (d2+d3)*(d2+d3) <= distanceToleranceSquare*(dx*dx+dy*dy) {
- // If the curvature doesn't exceed the distanceTolerance value
- // we tend to finish subdivisions.
- //----------------------
- if angleTolerance < CurveAngleToleranceEpsilon {
- v.Vertex(x23, y23)
- return
- }
-
- // Angle & Cusp Condition
- //----------------------
- k := math.Atan2(y3-y2, x3-x2)
- da1 := math.Abs(k - math.Atan2(y2-y1, x2-x1))
- da2 := math.Abs(math.Atan2(y4-y3, x4-x3) - k)
- if da1 >= math.Pi {
- da1 = 2*math.Pi - da1
- }
- if da2 >= math.Pi {
- da2 = 2*math.Pi - da2
- }
-
- if da1+da2 < angleTolerance {
- // Finally we can stop the recursion
- //----------------------
- v.Vertex(x23, y23)
- return
- }
-
- if cuspLimit != 0.0 {
- if da1 > cuspLimit {
- v.Vertex(x2, y2)
- return
- }
-
- if da2 > cuspLimit {
- v.Vertex(x3, y3)
- return
- }
- }
- }
- break
- }
-
- // Continue subdivision
- //----------------------
- recursiveCubicBezier(v, x1, y1, x12, y12, x123, y123, x1234, y1234, level+1, distanceToleranceSquare, angleTolerance, cuspLimit)
- recursiveCubicBezier(v, x1234, y1234, x234, y234, x34, y34, x4, y4, level+1, distanceToleranceSquare, angleTolerance, cuspLimit)
-
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/dasher.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/dasher.go
deleted file mode 100644
index 521029992..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/dasher.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 13/12/2010 by Laurent Le Goff
-
-package draw2d
-
-type DashVertexConverter struct {
- command VertexCommand
- next VertexConverter
- x, y, distance float64
- dash []float64
- currentDash int
- dashOffset float64
-}
-
-func NewDashConverter(dash []float64, dashOffset float64, converter VertexConverter) *DashVertexConverter {
- var dasher DashVertexConverter
- dasher.dash = dash
- dasher.currentDash = 0
- dasher.dashOffset = dashOffset
- dasher.next = converter
- return &dasher
-}
-
-func (dasher *DashVertexConverter) NextCommand(cmd VertexCommand) {
- dasher.command = cmd
- if dasher.command == VertexStopCommand {
- dasher.next.NextCommand(VertexStopCommand)
- }
-}
-
-func (dasher *DashVertexConverter) Vertex(x, y float64) {
- switch dasher.command {
- case VertexStartCommand:
- dasher.start(x, y)
- default:
- dasher.lineTo(x, y)
- }
- dasher.command = VertexNoCommand
-}
-
-func (dasher *DashVertexConverter) start(x, y float64) {
- dasher.next.NextCommand(VertexStartCommand)
- dasher.next.Vertex(x, y)
- dasher.x, dasher.y = x, y
- dasher.distance = dasher.dashOffset
- dasher.currentDash = 0
-}
-
-func (dasher *DashVertexConverter) lineTo(x, y float64) {
- rest := dasher.dash[dasher.currentDash] - dasher.distance
- for rest < 0 {
- dasher.distance = dasher.distance - dasher.dash[dasher.currentDash]
- dasher.currentDash = (dasher.currentDash + 1) % len(dasher.dash)
- rest = dasher.dash[dasher.currentDash] - dasher.distance
- }
- d := distance(dasher.x, dasher.y, x, y)
- for d >= rest {
- k := rest / d
- lx := dasher.x + k*(x-dasher.x)
- ly := dasher.y + k*(y-dasher.y)
- if dasher.currentDash%2 == 0 {
- // line
- dasher.next.Vertex(lx, ly)
- } else {
- // gap
- dasher.next.NextCommand(VertexStopCommand)
- dasher.next.NextCommand(VertexStartCommand)
- dasher.next.Vertex(lx, ly)
- }
- d = d - rest
- dasher.x, dasher.y = lx, ly
- dasher.currentDash = (dasher.currentDash + 1) % len(dasher.dash)
- rest = dasher.dash[dasher.currentDash]
- }
- dasher.distance = d
- if dasher.currentDash%2 == 0 {
- // line
- dasher.next.Vertex(x, y)
- } else {
- // gap
- dasher.next.NextCommand(VertexStopCommand)
- dasher.next.NextCommand(VertexStartCommand)
- dasher.next.Vertex(x, y)
- }
- if dasher.distance >= dasher.dash[dasher.currentDash] {
- dasher.distance = dasher.distance - dasher.dash[dasher.currentDash]
- dasher.currentDash = (dasher.currentDash + 1) % len(dasher.dash)
- }
- dasher.x, dasher.y = x, y
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/demux_converter.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/demux_converter.go
deleted file mode 100644
index b5c871d2c..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/demux_converter.go
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 13/12/2010 by Laurent Le Goff
-
-package draw2d
-
-type DemuxConverter struct {
- converters []VertexConverter
-}
-
-func NewDemuxConverter(converters ...VertexConverter) *DemuxConverter {
- return &DemuxConverter{converters}
-}
-
-func (dc *DemuxConverter) NextCommand(cmd VertexCommand) {
- for _, converter := range dc.converters {
- converter.NextCommand(cmd)
- }
-}
-func (dc *DemuxConverter) Vertex(x, y float64) {
- for _, converter := range dc.converters {
- converter.Vertex(x, y)
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/doc.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/doc.go
deleted file mode 100644
index 3baeffb4d..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/doc.go
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 13/12/2010 by Laurent Le Goff
-
-// The package draw2d provide a Graphic Context that can draw vectorial figure on surface.
-package draw2d
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/font.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/font.go
deleted file mode 100644
index eb0b5325c..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/font.go
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 13/12/2010 by Laurent Le Goff
-
-package draw2d
-
-import (
- "code.google.com/p/freetype-go/freetype/truetype"
- "io/ioutil"
- "log"
- "path"
-)
-
-var (
- fontFolder = "../resource/font/"
- fonts = make(map[string]*truetype.Font)
-)
-
-type FontStyle byte
-
-const (
- FontStyleNormal FontStyle = iota
- FontStyleBold
- FontStyleItalic
-)
-
-type FontFamily byte
-
-const (
- FontFamilySans FontFamily = iota
- FontFamilySerif
- FontFamilyMono
-)
-
-type FontData struct {
- Name string
- Family FontFamily
- Style FontStyle
-}
-
-func fontFileName(fontData FontData) string {
- fontFileName := fontData.Name
- switch fontData.Family {
- case FontFamilySans:
- fontFileName += "s"
- case FontFamilySerif:
- fontFileName += "r"
- case FontFamilyMono:
- fontFileName += "m"
- }
- if fontData.Style&FontStyleBold != 0 {
- fontFileName += "b"
- } else {
- fontFileName += "r"
- }
-
- if fontData.Style&FontStyleItalic != 0 {
- fontFileName += "i"
- }
- fontFileName += ".ttf"
- return fontFileName
-}
-
-func RegisterFont(fontData FontData, font *truetype.Font) {
- fonts[fontFileName(fontData)] = font
-}
-
-func GetFont(fontData FontData) *truetype.Font {
- fontFileName := fontFileName(fontData)
- font := fonts[fontFileName]
- if font != nil {
- return font
- }
- fonts[fontFileName] = loadFont(fontFileName)
- return fonts[fontFileName]
-}
-
-func GetFontFolder() string {
- return fontFolder
-}
-
-func SetFontFolder(folder string) {
- fontFolder = folder
-}
-
-func loadFont(fontFileName string) *truetype.Font {
- fontBytes, err := ioutil.ReadFile(path.Join(fontFolder, fontFileName))
- if err != nil {
- log.Println(err)
- return nil
- }
- font, err := truetype.Parse(fontBytes)
- if err != nil {
- log.Println(err)
- return nil
- }
- return font
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/gc.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/gc.go
deleted file mode 100644
index 66dc5088f..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/gc.go
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 21/11/2010 by Laurent Le Goff
-
-package draw2d
-
-import (
- "image"
- "image/color"
-)
-
-type FillRule int
-
-const (
- FillRuleEvenOdd FillRule = iota
- FillRuleWinding
-)
-
-type GraphicContext interface {
- Path
- // Create a new path
- BeginPath()
- GetMatrixTransform() MatrixTransform
- SetMatrixTransform(tr MatrixTransform)
- ComposeMatrixTransform(tr MatrixTransform)
- Rotate(angle float64)
- Translate(tx, ty float64)
- Scale(sx, sy float64)
- SetStrokeColor(c color.Color)
- SetFillColor(c color.Color)
- SetFillRule(f FillRule)
- SetLineWidth(lineWidth float64)
- SetLineCap(cap Cap)
- SetLineJoin(join Join)
- SetLineDash(dash []float64, dashOffset float64)
- SetFontSize(fontSize float64)
- GetFontSize() float64
- SetFontData(fontData FontData)
- GetFontData() FontData
- DrawImage(image image.Image)
- Save()
- Restore()
- Clear()
- ClearRect(x1, y1, x2, y2 int)
- SetDPI(dpi int)
- GetDPI() int
- GetStringBounds(s string) (left, top, right, bottom float64)
- CreateStringPath(text string, x, y float64) (cursor float64)
- FillString(text string) (cursor float64)
- FillStringAt(text string, x, y float64) (cursor float64)
- StrokeString(text string) (cursor float64)
- StrokeStringAt(text string, x, y float64) (cursor float64)
- Stroke(paths ...*PathStorage)
- Fill(paths ...*PathStorage)
- FillStroke(paths ...*PathStorage)
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/image.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/image.go
deleted file mode 100644
index 9f91bc71f..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/image.go
+++ /dev/null
@@ -1,359 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 21/11/2010 by Laurent Le Goff
-
-package draw2d
-
-import (
- "code.google.com/p/freetype-go/freetype/raster"
- "code.google.com/p/freetype-go/freetype/truetype"
- "errors"
- "image"
- "image/color"
- "image/draw"
- "log"
- "math"
-)
-
-type Painter interface {
- raster.Painter
- SetColor(color color.Color)
-}
-
-var (
- defaultFontData = FontData{"luxi", FontFamilySans, FontStyleNormal}
-)
-
-type ImageGraphicContext struct {
- *StackGraphicContext
- img draw.Image
- painter Painter
- fillRasterizer *raster.Rasterizer
- strokeRasterizer *raster.Rasterizer
- glyphBuf *truetype.GlyphBuf
- DPI int
-}
-
-/**
- * Create a new Graphic context from an image
- */
-func NewGraphicContext(img draw.Image) *ImageGraphicContext {
- var painter Painter
- switch selectImage := img.(type) {
- case *image.RGBA:
- painter = raster.NewRGBAPainter(selectImage)
- default:
- panic("Image type not supported")
- }
- return NewGraphicContextWithPainter(img, painter)
-}
-
-// Create a new Graphic context from an image and a Painter (see Freetype-go)
-func NewGraphicContextWithPainter(img draw.Image, painter Painter) *ImageGraphicContext {
- width, height := img.Bounds().Dx(), img.Bounds().Dy()
- dpi := 92
- gc := &ImageGraphicContext{
- NewStackGraphicContext(),
- img,
- painter,
- raster.NewRasterizer(width, height),
- raster.NewRasterizer(width, height),
- truetype.NewGlyphBuf(),
- dpi,
- }
- return gc
-}
-
-func (gc *ImageGraphicContext) GetDPI() int {
- return gc.DPI
-}
-
-func (gc *ImageGraphicContext) Clear() {
- width, height := gc.img.Bounds().Dx(), gc.img.Bounds().Dy()
- gc.ClearRect(0, 0, width, height)
-}
-
-func (gc *ImageGraphicContext) ClearRect(x1, y1, x2, y2 int) {
- imageColor := image.NewUniform(gc.Current.FillColor)
- draw.Draw(gc.img, image.Rect(x1, y1, x2, y2), imageColor, image.ZP, draw.Over)
-}
-
-func (gc *ImageGraphicContext) DrawImage(img image.Image) {
- DrawImage(img, gc.img, gc.Current.Tr, draw.Over, BilinearFilter)
-}
-
-func (gc *ImageGraphicContext) FillString(text string) (cursor float64) {
- return gc.FillStringAt(text, 0, 0)
-}
-
-func (gc *ImageGraphicContext) FillStringAt(text string, x, y float64) (cursor float64) {
- width := gc.CreateStringPath(text, x, y)
- gc.Fill()
- return width
-}
-
-func (gc *ImageGraphicContext) StrokeString(text string) (cursor float64) {
- return gc.StrokeStringAt(text, 0, 0)
-}
-
-func (gc *ImageGraphicContext) StrokeStringAt(text string, x, y float64) (cursor float64) {
- width := gc.CreateStringPath(text, x, y)
- gc.Stroke()
- return width
-}
-
-func (gc *ImageGraphicContext) loadCurrentFont() (*truetype.Font, error) {
- font := GetFont(gc.Current.FontData)
- if font == nil {
- font = GetFont(defaultFontData)
- }
- if font == nil {
- return nil, errors.New("No font set, and no default font available.")
- }
- gc.SetFont(font)
- gc.SetFontSize(gc.Current.FontSize)
- return font, nil
-}
-
-func fUnitsToFloat64(x int32) float64 {
- scaled := x << 2
- return float64(scaled/256) + float64(scaled%256)/256.0
-}
-
-// p is a truetype.Point measured in FUnits and positive Y going upwards.
-// The returned value is the same thing measured in floating point and positive Y
-// going downwards.
-func pointToF64Point(p truetype.Point) (x, y float64) {
- return fUnitsToFloat64(p.X), -fUnitsToFloat64(p.Y)
-}
-
-// drawContour draws the given closed contour at the given sub-pixel offset.
-func (gc *ImageGraphicContext) drawContour(ps []truetype.Point, dx, dy float64) {
- if len(ps) == 0 {
- return
- }
- startX, startY := pointToF64Point(ps[0])
- gc.MoveTo(startX+dx, startY+dy)
- q0X, q0Y, on0 := startX, startY, true
- for _, p := range ps[1:] {
- qX, qY := pointToF64Point(p)
- on := p.Flags&0x01 != 0
- if on {
- if on0 {
- gc.LineTo(qX+dx, qY+dy)
- } else {
- gc.QuadCurveTo(q0X+dx, q0Y+dy, qX+dx, qY+dy)
- }
- } else {
- if on0 {
- // No-op.
- } else {
- midX := (q0X + qX) / 2
- midY := (q0Y + qY) / 2
- gc.QuadCurveTo(q0X+dx, q0Y+dy, midX+dx, midY+dy)
- }
- }
- q0X, q0Y, on0 = qX, qY, on
- }
- // Close the curve.
- if on0 {
- gc.LineTo(startX+dx, startY+dy)
- } else {
- gc.QuadCurveTo(q0X+dx, q0Y+dy, startX+dx, startY+dy)
- }
-}
-
-func (gc *ImageGraphicContext) drawGlyph(glyph truetype.Index, dx, dy float64) error {
- if err := gc.glyphBuf.Load(gc.Current.font, gc.Current.scale, glyph, truetype.NoHinting); err != nil {
- return err
- }
- e0 := 0
- for _, e1 := range gc.glyphBuf.End {
- gc.drawContour(gc.glyphBuf.Point[e0:e1], dx, dy)
- e0 = e1
- }
- return nil
-}
-
-// CreateStringPath creates a path from the string s at x, y, and returns the string width.
-// The text is placed so that the left edge of the em square of the first character of s
-// and the baseline intersect at x, y. The majority of the affected pixels will be
-// above and to the right of the point, but some may be below or to the left.
-// For example, drawing a string that starts with a 'J' in an italic font may
-// affect pixels below and left of the point.
-func (gc *ImageGraphicContext) CreateStringPath(s string, x, y float64) float64 {
- font, err := gc.loadCurrentFont()
- if err != nil {
- log.Println(err)
- return 0.0
- }
- startx := x
- prev, hasPrev := truetype.Index(0), false
- for _, rune := range s {
- index := font.Index(rune)
- if hasPrev {
- x += fUnitsToFloat64(font.Kerning(gc.Current.scale, prev, index))
- }
- err := gc.drawGlyph(index, x, y)
- if err != nil {
- log.Println(err)
- return startx - x
- }
- x += fUnitsToFloat64(font.HMetric(gc.Current.scale, index).AdvanceWidth)
- prev, hasPrev = index, true
- }
- return x - startx
-}
-
-// GetStringBounds returns the approximate pixel bounds of the string s at x, y.
-// The the left edge of the em square of the first character of s
-// and the baseline intersect at 0, 0 in the returned coordinates.
-// Therefore the top and left coordinates may well be negative.
-func (gc *ImageGraphicContext) GetStringBounds(s string) (left, top, right, bottom float64) {
- font, err := gc.loadCurrentFont()
- if err != nil {
- log.Println(err)
- return 0, 0, 0, 0
- }
- top, left, bottom, right = 10e6, 10e6, -10e6, -10e6
- cursor := 0.0
- prev, hasPrev := truetype.Index(0), false
- for _, rune := range s {
- index := font.Index(rune)
- if hasPrev {
- cursor += fUnitsToFloat64(font.Kerning(gc.Current.scale, prev, index))
- }
- if err := gc.glyphBuf.Load(gc.Current.font, gc.Current.scale, index, truetype.NoHinting); err != nil {
- log.Println(err)
- return 0, 0, 0, 0
- }
- e0 := 0
- for _, e1 := range gc.glyphBuf.End {
- ps := gc.glyphBuf.Point[e0:e1]
- for _, p := range ps {
- x, y := pointToF64Point(p)
- top = math.Min(top, y)
- bottom = math.Max(bottom, y)
- left = math.Min(left, x+cursor)
- right = math.Max(right, x+cursor)
- }
- }
- cursor += fUnitsToFloat64(font.HMetric(gc.Current.scale, index).AdvanceWidth)
- prev, hasPrev = index, true
- }
- return left, top, right, bottom
-}
-
-// recalc recalculates scale and bounds values from the font size, screen
-// resolution and font metrics, and invalidates the glyph cache.
-func (gc *ImageGraphicContext) recalc() {
- gc.Current.scale = int32(gc.Current.FontSize * float64(gc.DPI) * (64.0 / 72.0))
-}
-
-// SetDPI sets the screen resolution in dots per inch.
-func (gc *ImageGraphicContext) SetDPI(dpi int) {
- gc.DPI = dpi
- gc.recalc()
-}
-
-// SetFont sets the font used to draw text.
-func (gc *ImageGraphicContext) SetFont(font *truetype.Font) {
- gc.Current.font = font
-}
-
-// SetFontSize sets the font size in points (as in ``a 12 point font'').
-func (gc *ImageGraphicContext) SetFontSize(fontSize float64) {
- gc.Current.FontSize = fontSize
- gc.recalc()
-}
-
-func (gc *ImageGraphicContext) paint(rasterizer *raster.Rasterizer, color color.Color) {
- gc.painter.SetColor(color)
- rasterizer.Rasterize(gc.painter)
- rasterizer.Clear()
- gc.Current.Path.Clear()
-}
-
-/**** second method ****/
-func (gc *ImageGraphicContext) Stroke(paths ...*PathStorage) {
- paths = append(paths, gc.Current.Path)
- gc.strokeRasterizer.UseNonZeroWinding = true
-
- stroker := NewLineStroker(gc.Current.Cap, gc.Current.Join, NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.strokeRasterizer)))
- stroker.HalfLineWidth = gc.Current.LineWidth / 2
- var pathConverter *PathConverter
- if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 {
- dasher := NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker)
- pathConverter = NewPathConverter(dasher)
- } else {
- pathConverter = NewPathConverter(stroker)
- }
- pathConverter.ApproximationScale = gc.Current.Tr.GetScale()
- pathConverter.Convert(paths...)
-
- gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor)
-}
-
-/**** second method ****/
-func (gc *ImageGraphicContext) Fill(paths ...*PathStorage) {
- paths = append(paths, gc.Current.Path)
- gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding()
-
- /**** first method ****/
- pathConverter := NewPathConverter(NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.fillRasterizer)))
- pathConverter.ApproximationScale = gc.Current.Tr.GetScale()
- pathConverter.Convert(paths...)
-
- gc.paint(gc.fillRasterizer, gc.Current.FillColor)
-}
-
-/* second method */
-func (gc *ImageGraphicContext) FillStroke(paths ...*PathStorage) {
- gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding()
- gc.strokeRasterizer.UseNonZeroWinding = true
-
- filler := NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.fillRasterizer))
-
- stroker := NewLineStroker(gc.Current.Cap, gc.Current.Join, NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.strokeRasterizer)))
- stroker.HalfLineWidth = gc.Current.LineWidth / 2
-
- demux := NewDemuxConverter(filler, stroker)
- paths = append(paths, gc.Current.Path)
- pathConverter := NewPathConverter(demux)
- pathConverter.ApproximationScale = gc.Current.Tr.GetScale()
- pathConverter.Convert(paths...)
-
- gc.paint(gc.fillRasterizer, gc.Current.FillColor)
- gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor)
-}
-
-func (f FillRule) UseNonZeroWinding() bool {
- switch f {
- case FillRuleEvenOdd:
- return false
- case FillRuleWinding:
- return true
- }
- return false
-}
-
-func (c Cap) Convert() raster.Capper {
- switch c {
- case RoundCap:
- return raster.RoundCapper
- case ButtCap:
- return raster.ButtCapper
- case SquareCap:
- return raster.SquareCapper
- }
- return raster.RoundCapper
-}
-
-func (j Join) Convert() raster.Joiner {
- switch j {
- case RoundJoin:
- return raster.RoundJoiner
- case BevelJoin:
- return raster.BevelJoiner
- }
- return raster.RoundJoiner
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/math.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/math.go
deleted file mode 100644
index c4bb761df..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/math.go
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 21/11/2010 by Laurent Le Goff
-
-package draw2d
-
-import (
- "math"
-)
-
-func distance(x1, y1, x2, y2 float64) float64 {
- dx := x2 - x1
- dy := y2 - y1
- return float64(math.Sqrt(dx*dx + dy*dy))
-}
-
-func vectorDistance(dx, dy float64) float64 {
- return float64(math.Sqrt(dx*dx + dy*dy))
-}
-
-func squareDistance(x1, y1, x2, y2 float64) float64 {
- dx := x2 - x1
- dy := y2 - y1
- return dx*dx + dy*dy
-}
-
-func min(x, y float64) float64 {
- if x < y {
- return x
- }
- return y
-}
-
-func max(x, y float64) float64 {
- if x > y {
- return x
- }
- return y
-}
-
-func minMax(x, y float64) (min, max float64) {
- if x > y {
- return y, x
- }
- return x, y
-}
-
-func minUint32(a, b uint32) uint32 {
- if a < b {
- return a
- }
- return b
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/paint.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/paint.go
deleted file mode 100644
index 885d993ae..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/paint.go
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 21/11/2010 by Laurent Le Goff
-
-package draw2d
-
-/*
-import (
- "image/draw"
- "image"
- "freetype-go.googlecode.com/hg/freetype/raster"
-)*/
-
-const M = 1<<16 - 1
-
-/*
-type NRGBAPainter struct {
- // The image to compose onto.
- Image *image.NRGBA
- // The Porter-Duff composition operator.
- Op draw.Op
- // The 16-bit color to paint the spans.
- cr, cg, cb, ca uint32
-}
-
-// Paint satisfies the Painter interface by painting ss onto an image.RGBA.
-func (r *NRGBAPainter) Paint(ss []raster.Span, done bool) {
- b := r.Image.Bounds()
- for _, s := range ss {
- if s.Y < b.Min.Y {
- continue
- }
- if s.Y >= b.Max.Y {
- return
- }
- if s.X0 < b.Min.X {
- s.X0 = b.Min.X
- }
- if s.X1 > b.Max.X {
- s.X1 = b.Max.X
- }
- if s.X0 >= s.X1 {
- continue
- }
- base := s.Y * r.Image.Stride
- p := r.Image.Pix[base+s.X0 : base+s.X1]
- // This code is duplicated from drawGlyphOver in $GOROOT/src/pkg/image/draw/draw.go.
- // TODO(nigeltao): Factor out common code into a utility function, once the compiler
- // can inline such function calls.
- ma := s.A >> 16
- if r.Op == draw.Over {
- for i, nrgba := range p {
- dr, dg, db, da := nrgba.
- a := M - (r.ca*ma)/M
- da = (da*a + r.ca*ma) / M
- if da != 0 {
- dr = minUint32(M, (dr*a+r.cr*ma)/da)
- dg = minUint32(M, (dg*a+r.cg*ma)/da)
- db = minUint32(M, (db*a+r.cb*ma)/da)
- } else {
- dr, dg, db = 0, 0, 0
- }
- p[i] = image.NRGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)}
- }
- } else {
- for i, nrgba := range p {
- dr, dg, db, da := nrgba.RGBA()
- a := M - ma
- da = (da*a + r.ca*ma) / M
- if da != 0 {
- dr = minUint32(M, (dr*a+r.cr*ma)/da)
- dg = minUint32(M, (dg*a+r.cg*ma)/da)
- db = minUint32(M, (db*a+r.cb*ma)/da)
- } else {
- dr, dg, db = 0, 0, 0
- }
- p[i] = image.NRGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)}
- }
- }
- }
-
-}
-
-// SetColor sets the color to paint the spans.
-func (r *NRGBAPainter) SetColor(c image.Color) {
- r.cr, r.cg, r.cb, r.ca = c.RGBA()
-}
-
-// NewRGBAPainter creates a new RGBAPainter for the given image.
-func NewNRGBAPainter(m *image.NRGBA) *NRGBAPainter {
- return &NRGBAPainter{Image: m}
-}
-*/
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path.go
deleted file mode 100644
index b82910e24..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 21/11/2010 by Laurent Le Goff
-
-package draw2d
-
-type Path interface {
- // Return the current point of the path
- LastPoint() (x, y float64)
- // Create a new subpath that start at the specified point
- MoveTo(x, y float64)
- // Create a new subpath that start at the specified point
- // relative to the current point
- RMoveTo(dx, dy float64)
- // Add a line to the current subpath
- LineTo(x, y float64)
- // Add a line to the current subpath
- // relative to the current point
- RLineTo(dx, dy float64)
-
- QuadCurveTo(cx, cy, x, y float64)
- RQuadCurveTo(dcx, dcy, dx, dy float64)
- CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64)
- RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float64)
- ArcTo(cx, cy, rx, ry, startAngle, angle float64)
- RArcTo(dcx, dcy, rx, ry, startAngle, angle float64)
- Close()
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_adder.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_adder.go
deleted file mode 100644
index c5efd2beb..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_adder.go
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 13/12/2010 by Laurent Le Goff
-
-package draw2d
-
-import (
- "code.google.com/p/freetype-go/freetype/raster"
-)
-
-type VertexAdder struct {
- command VertexCommand
- adder raster.Adder
-}
-
-func NewVertexAdder(adder raster.Adder) *VertexAdder {
- return &VertexAdder{VertexNoCommand, adder}
-}
-
-func (vertexAdder *VertexAdder) NextCommand(cmd VertexCommand) {
- vertexAdder.command = cmd
-}
-
-func (vertexAdder *VertexAdder) Vertex(x, y float64) {
- switch vertexAdder.command {
- case VertexStartCommand:
- vertexAdder.adder.Start(raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)})
- default:
- vertexAdder.adder.Add1(raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)})
- }
- vertexAdder.command = VertexNoCommand
-}
-
-type PathAdder struct {
- adder raster.Adder
- firstPoint raster.Point
- ApproximationScale float64
-}
-
-func NewPathAdder(adder raster.Adder) *PathAdder {
- return &PathAdder{adder, raster.Point{0, 0}, 1}
-}
-
-func (pathAdder *PathAdder) Convert(paths ...*PathStorage) {
- for _, path := range paths {
- j := 0
- for _, cmd := range path.commands {
- switch cmd {
- case MoveTo:
- pathAdder.firstPoint = raster.Point{raster.Fix32(path.vertices[j] * 256), raster.Fix32(path.vertices[j+1] * 256)}
- pathAdder.adder.Start(pathAdder.firstPoint)
- j += 2
- case LineTo:
- pathAdder.adder.Add1(raster.Point{raster.Fix32(path.vertices[j] * 256), raster.Fix32(path.vertices[j+1] * 256)})
- j += 2
- case QuadCurveTo:
- pathAdder.adder.Add2(raster.Point{raster.Fix32(path.vertices[j] * 256), raster.Fix32(path.vertices[j+1] * 256)}, raster.Point{raster.Fix32(path.vertices[j+2] * 256), raster.Fix32(path.vertices[j+3] * 256)})
- j += 4
- case CubicCurveTo:
- pathAdder.adder.Add3(raster.Point{raster.Fix32(path.vertices[j] * 256), raster.Fix32(path.vertices[j+1] * 256)}, raster.Point{raster.Fix32(path.vertices[j+2] * 256), raster.Fix32(path.vertices[j+3] * 256)}, raster.Point{raster.Fix32(path.vertices[j+4] * 256), raster.Fix32(path.vertices[j+5] * 256)})
- j += 6
- case ArcTo:
- lastPoint := arcAdder(pathAdder.adder, path.vertices[j], path.vertices[j+1], path.vertices[j+2], path.vertices[j+3], path.vertices[j+4], path.vertices[j+5], pathAdder.ApproximationScale)
- pathAdder.adder.Add1(lastPoint)
- j += 6
- case Close:
- pathAdder.adder.Add1(pathAdder.firstPoint)
- }
- }
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_converter.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_converter.go
deleted file mode 100644
index 0ef96b84d..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_converter.go
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 06/12/2010 by Laurent Le Goff
-
-package draw2d
-
-import (
- "math"
-)
-
-type PathConverter struct {
- converter VertexConverter
- ApproximationScale, AngleTolerance, CuspLimit float64
- startX, startY, x, y float64
-}
-
-func NewPathConverter(converter VertexConverter) *PathConverter {
- return &PathConverter{converter, 1, 0, 0, 0, 0, 0, 0}
-}
-
-func (c *PathConverter) Convert(paths ...*PathStorage) {
- for _, path := range paths {
- j := 0
- for _, cmd := range path.commands {
- j = j + c.ConvertCommand(cmd, path.vertices[j:]...)
- }
- c.converter.NextCommand(VertexStopCommand)
- }
-}
-
-func (c *PathConverter) ConvertCommand(cmd PathCmd, vertices ...float64) int {
- switch cmd {
- case MoveTo:
- c.x, c.y = vertices[0], vertices[1]
- c.startX, c.startY = c.x, c.y
- c.converter.NextCommand(VertexStopCommand)
- c.converter.NextCommand(VertexStartCommand)
- c.converter.Vertex(c.x, c.y)
- return 2
- case LineTo:
- c.x, c.y = vertices[0], vertices[1]
- if c.startX == c.x && c.startY == c.y {
- c.converter.NextCommand(VertexCloseCommand)
- }
- c.converter.Vertex(c.x, c.y)
- c.converter.NextCommand(VertexJoinCommand)
- return 2
- case QuadCurveTo:
- quadraticBezier(c.converter, c.x, c.y, vertices[0], vertices[1], vertices[2], vertices[3], c.ApproximationScale, c.AngleTolerance)
- c.x, c.y = vertices[2], vertices[3]
- if c.startX == c.x && c.startY == c.y {
- c.converter.NextCommand(VertexCloseCommand)
- }
- c.converter.Vertex(c.x, c.y)
- return 4
- case CubicCurveTo:
- cubicBezier(c.converter, c.x, c.y, vertices[0], vertices[1], vertices[2], vertices[3], vertices[4], vertices[5], c.ApproximationScale, c.AngleTolerance, c.CuspLimit)
- c.x, c.y = vertices[4], vertices[5]
- if c.startX == c.x && c.startY == c.y {
- c.converter.NextCommand(VertexCloseCommand)
- }
- c.converter.Vertex(c.x, c.y)
- return 6
- case ArcTo:
- c.x, c.y = arc(c.converter, vertices[0], vertices[1], vertices[2], vertices[3], vertices[4], vertices[5], c.ApproximationScale)
- if c.startX == c.x && c.startY == c.y {
- c.converter.NextCommand(VertexCloseCommand)
- }
- c.converter.Vertex(c.x, c.y)
- return 6
- case Close:
- c.converter.NextCommand(VertexCloseCommand)
- c.converter.Vertex(c.startX, c.startY)
- return 0
- }
- return 0
-}
-
-func (c *PathConverter) MoveTo(x, y float64) *PathConverter {
- c.x, c.y = x, y
- c.startX, c.startY = c.x, c.y
- c.converter.NextCommand(VertexStopCommand)
- c.converter.NextCommand(VertexStartCommand)
- c.converter.Vertex(c.x, c.y)
- return c
-}
-
-func (c *PathConverter) RMoveTo(dx, dy float64) *PathConverter {
- c.MoveTo(c.x+dx, c.y+dy)
- return c
-}
-
-func (c *PathConverter) LineTo(x, y float64) *PathConverter {
- c.x, c.y = x, y
- if c.startX == c.x && c.startY == c.y {
- c.converter.NextCommand(VertexCloseCommand)
- }
- c.converter.Vertex(c.x, c.y)
- c.converter.NextCommand(VertexJoinCommand)
- return c
-}
-
-func (c *PathConverter) RLineTo(dx, dy float64) *PathConverter {
- c.LineTo(c.x+dx, c.y+dy)
- return c
-}
-
-func (c *PathConverter) QuadCurveTo(cx, cy, x, y float64) *PathConverter {
- quadraticBezier(c.converter, c.x, c.y, cx, cy, x, y, c.ApproximationScale, c.AngleTolerance)
- c.x, c.y = x, y
- if c.startX == c.x && c.startY == c.y {
- c.converter.NextCommand(VertexCloseCommand)
- }
- c.converter.Vertex(c.x, c.y)
- return c
-}
-
-func (c *PathConverter) RQuadCurveTo(dcx, dcy, dx, dy float64) *PathConverter {
- c.QuadCurveTo(c.x+dcx, c.y+dcy, c.x+dx, c.y+dy)
- return c
-}
-
-func (c *PathConverter) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) *PathConverter {
- cubicBezier(c.converter, c.x, c.y, cx1, cy1, cx2, cy2, x, y, c.ApproximationScale, c.AngleTolerance, c.CuspLimit)
- c.x, c.y = x, y
- if c.startX == c.x && c.startY == c.y {
- c.converter.NextCommand(VertexCloseCommand)
- }
- c.converter.Vertex(c.x, c.y)
- return c
-}
-
-func (c *PathConverter) RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float64) *PathConverter {
- c.CubicCurveTo(c.x+dcx1, c.y+dcy1, c.x+dcx2, c.y+dcy2, c.x+dx, c.y+dy)
- return c
-}
-
-func (c *PathConverter) ArcTo(cx, cy, rx, ry, startAngle, angle float64) *PathConverter {
- endAngle := startAngle + angle
- clockWise := true
- if angle < 0 {
- clockWise = false
- }
- // normalize
- if clockWise {
- for endAngle < startAngle {
- endAngle += math.Pi * 2.0
- }
- } else {
- for startAngle < endAngle {
- startAngle += math.Pi * 2.0
- }
- }
- startX := cx + math.Cos(startAngle)*rx
- startY := cy + math.Sin(startAngle)*ry
- c.MoveTo(startX, startY)
- c.x, c.y = arc(c.converter, cx, cy, rx, ry, startAngle, angle, c.ApproximationScale)
- if c.startX == c.x && c.startY == c.y {
- c.converter.NextCommand(VertexCloseCommand)
- }
- c.converter.Vertex(c.x, c.y)
- return c
-}
-
-func (c *PathConverter) RArcTo(dcx, dcy, rx, ry, startAngle, angle float64) *PathConverter {
- c.ArcTo(c.x+dcx, c.y+dcy, rx, ry, startAngle, angle)
- return c
-}
-
-func (c *PathConverter) Close() *PathConverter {
- c.converter.NextCommand(VertexCloseCommand)
- c.converter.Vertex(c.startX, c.startY)
- return c
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_storage.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_storage.go
deleted file mode 100644
index c2a887037..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_storage.go
+++ /dev/null
@@ -1,190 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 21/11/2010 by Laurent Le Goff
-
-package draw2d
-
-import (
- "fmt"
- "math"
-)
-
-type PathCmd int
-
-const (
- MoveTo PathCmd = iota
- LineTo
- QuadCurveTo
- CubicCurveTo
- ArcTo
- Close
-)
-
-type PathStorage struct {
- commands []PathCmd
- vertices []float64
- x, y float64
-}
-
-func NewPathStorage() (p *PathStorage) {
- p = new(PathStorage)
- p.commands = make([]PathCmd, 0, 256)
- p.vertices = make([]float64, 0, 256)
- return
-}
-
-func (p *PathStorage) Clear() {
- p.commands = p.commands[0:0]
- p.vertices = p.vertices[0:0]
- return
-}
-
-func (p *PathStorage) appendToPath(cmd PathCmd, vertices ...float64) {
- if cap(p.vertices) <= len(p.vertices)+6 {
- a := make([]PathCmd, len(p.commands), cap(p.commands)+256)
- b := make([]float64, len(p.vertices), cap(p.vertices)+256)
- copy(a, p.commands)
- p.commands = a
- copy(b, p.vertices)
- p.vertices = b
- }
- p.commands = p.commands[0 : len(p.commands)+1]
- p.commands[len(p.commands)-1] = cmd
- copy(p.vertices[len(p.vertices):len(p.vertices)+len(vertices)], vertices)
- p.vertices = p.vertices[0 : len(p.vertices)+len(vertices)]
-}
-
-func (src *PathStorage) Copy() (dest *PathStorage) {
- dest = new(PathStorage)
- dest.commands = make([]PathCmd, len(src.commands))
- copy(dest.commands, src.commands)
- dest.vertices = make([]float64, len(src.vertices))
- copy(dest.vertices, src.vertices)
- return dest
-}
-
-func (p *PathStorage) LastPoint() (x, y float64) {
- return p.x, p.y
-}
-
-func (p *PathStorage) IsEmpty() bool {
- return len(p.commands) == 0
-}
-
-func (p *PathStorage) Close() *PathStorage {
- p.appendToPath(Close)
- return p
-}
-
-func (p *PathStorage) MoveTo(x, y float64) *PathStorage {
- p.appendToPath(MoveTo, x, y)
- p.x = x
- p.y = y
- return p
-}
-
-func (p *PathStorage) RMoveTo(dx, dy float64) *PathStorage {
- x, y := p.LastPoint()
- p.MoveTo(x+dx, y+dy)
- return p
-}
-
-func (p *PathStorage) LineTo(x, y float64) *PathStorage {
- p.appendToPath(LineTo, x, y)
- p.x = x
- p.y = y
- return p
-}
-
-func (p *PathStorage) RLineTo(dx, dy float64) *PathStorage {
- x, y := p.LastPoint()
- p.LineTo(x+dx, y+dy)
- return p
-}
-
-func (p *PathStorage) QuadCurveTo(cx, cy, x, y float64) *PathStorage {
- p.appendToPath(QuadCurveTo, cx, cy, x, y)
- p.x = x
- p.y = y
- return p
-}
-
-func (p *PathStorage) RQuadCurveTo(dcx, dcy, dx, dy float64) *PathStorage {
- x, y := p.LastPoint()
- p.QuadCurveTo(x+dcx, y+dcy, x+dx, y+dy)
- return p
-}
-
-func (p *PathStorage) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) *PathStorage {
- p.appendToPath(CubicCurveTo, cx1, cy1, cx2, cy2, x, y)
- p.x = x
- p.y = y
- return p
-}
-
-func (p *PathStorage) RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float64) *PathStorage {
- x, y := p.LastPoint()
- p.CubicCurveTo(x+dcx1, y+dcy1, x+dcx2, y+dcy2, x+dx, y+dy)
- return p
-}
-
-func (p *PathStorage) ArcTo(cx, cy, rx, ry, startAngle, angle float64) *PathStorage {
- endAngle := startAngle + angle
- clockWise := true
- if angle < 0 {
- clockWise = false
- }
- // normalize
- if clockWise {
- for endAngle < startAngle {
- endAngle += math.Pi * 2.0
- }
- } else {
- for startAngle < endAngle {
- startAngle += math.Pi * 2.0
- }
- }
- startX := cx + math.Cos(startAngle)*rx
- startY := cy + math.Sin(startAngle)*ry
- if len(p.commands) > 0 {
- p.LineTo(startX, startY)
- } else {
- p.MoveTo(startX, startY)
- }
- p.appendToPath(ArcTo, cx, cy, rx, ry, startAngle, angle)
- p.x = cx + math.Cos(endAngle)*rx
- p.y = cy + math.Sin(endAngle)*ry
- return p
-}
-
-func (p *PathStorage) RArcTo(dcx, dcy, rx, ry, startAngle, angle float64) *PathStorage {
- x, y := p.LastPoint()
- p.ArcTo(x+dcx, y+dcy, rx, ry, startAngle, angle)
- return p
-}
-
-func (p *PathStorage) String() string {
- s := ""
- j := 0
- for _, cmd := range p.commands {
- switch cmd {
- case MoveTo:
- s += fmt.Sprintf("MoveTo: %f, %f\n", p.vertices[j], p.vertices[j+1])
- j = j + 2
- case LineTo:
- s += fmt.Sprintf("LineTo: %f, %f\n", p.vertices[j], p.vertices[j+1])
- j = j + 2
- case QuadCurveTo:
- s += fmt.Sprintf("QuadCurveTo: %f, %f, %f, %f\n", p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3])
- j = j + 4
- case CubicCurveTo:
- s += fmt.Sprintf("CubicCurveTo: %f, %f, %f, %f, %f, %f\n", p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], p.vertices[j+4], p.vertices[j+5])
- j = j + 6
- case ArcTo:
- s += fmt.Sprintf("ArcTo: %f, %f, %f, %f, %f, %f\n", p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], p.vertices[j+4], p.vertices[j+5])
- j = j + 6
- case Close:
- s += "Close\n"
- }
- }
- return s
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/coverage_table.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/coverage_table.go
deleted file mode 100644
index 429836f39..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/coverage_table.go
+++ /dev/null
@@ -1,203 +0,0 @@
-// Copyright 2011 The draw2d Authors. All rights reserved.
-// created: 27/05/2011 by Laurent Le Goff
-package raster
-
-var SUBPIXEL_OFFSETS_SAMPLE_8 = [8]float64{
- 5.0 / 8,
- 0.0 / 8,
- 3.0 / 8,
- 6.0 / 8,
- 1.0 / 8,
- 4.0 / 8,
- 7.0 / 8,
- 2.0 / 8,
-}
-
-var SUBPIXEL_OFFSETS_SAMPLE_8_FIXED = [8]Fix{
- Fix(SUBPIXEL_OFFSETS_SAMPLE_8[0] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_8[1] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_8[2] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_8[3] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_8[4] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_8[5] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_8[6] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_8[7] * FIXED_FLOAT_COEF),
-}
-
-var SUBPIXEL_OFFSETS_SAMPLE_16 = [16]float64{
- 1.0 / 16,
- 8.0 / 16,
- 4.0 / 16,
- 15.0 / 16,
- 11.0 / 16,
- 2.0 / 16,
- 6.0 / 16,
- 14.0 / 16,
- 10.0 / 16,
- 3.0 / 16,
- 7.0 / 16,
- 12.0 / 16,
- 0.0 / 16,
- 9.0 / 16,
- 5.0 / 16,
- 13.0 / 16,
-}
-
-var SUBPIXEL_OFFSETS_SAMPLE_16_FIXED = [16]Fix{
- Fix(SUBPIXEL_OFFSETS_SAMPLE_16[0] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_16[1] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_16[2] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_16[3] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_16[4] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_16[5] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_16[6] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_16[7] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_16[8] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_16[9] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_16[10] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_16[11] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_16[12] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_16[13] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_16[14] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_16[15] * FIXED_FLOAT_COEF),
-}
-
-var SUBPIXEL_OFFSETS_SAMPLE_32 = [32]float64{
- 28.0 / 32,
- 13.0 / 32,
- 6.0 / 32,
- 23.0 / 32,
- 0.0 / 32,
- 17.0 / 32,
- 10.0 / 32,
- 27.0 / 32,
- 4.0 / 32,
- 21.0 / 32,
- 14.0 / 32,
- 31.0 / 32,
- 8.0 / 32,
- 25.0 / 32,
- 18.0 / 32,
- 3.0 / 32,
- 12.0 / 32,
- 29.0 / 32,
- 22.0 / 32,
- 7.0 / 32,
- 16.0 / 32,
- 1.0 / 32,
- 26.0 / 32,
- 11.0 / 32,
- 20.0 / 32,
- 5.0 / 32,
- 30.0 / 32,
- 15.0 / 32,
- 24.0 / 32,
- 9.0 / 32,
- 2.0 / 32,
- 19.0 / 32,
-}
-var SUBPIXEL_OFFSETS_SAMPLE_32_FIXED = [32]Fix{
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[0] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[1] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[2] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[3] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[4] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[5] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[6] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[7] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[8] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[9] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[10] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[11] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[12] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[13] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[14] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[15] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[16] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[17] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[18] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[19] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[20] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[21] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[22] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[23] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[24] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[25] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[26] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[27] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[28] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[29] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[30] * FIXED_FLOAT_COEF),
- Fix(SUBPIXEL_OFFSETS_SAMPLE_32[31] * FIXED_FLOAT_COEF),
-}
-
-var coverageTable = [256]uint8{
- pixelCoverage(0x00), pixelCoverage(0x01), pixelCoverage(0x02), pixelCoverage(0x03),
- pixelCoverage(0x04), pixelCoverage(0x05), pixelCoverage(0x06), pixelCoverage(0x07),
- pixelCoverage(0x08), pixelCoverage(0x09), pixelCoverage(0x0a), pixelCoverage(0x0b),
- pixelCoverage(0x0c), pixelCoverage(0x0d), pixelCoverage(0x0e), pixelCoverage(0x0f),
- pixelCoverage(0x10), pixelCoverage(0x11), pixelCoverage(0x12), pixelCoverage(0x13),
- pixelCoverage(0x14), pixelCoverage(0x15), pixelCoverage(0x16), pixelCoverage(0x17),
- pixelCoverage(0x18), pixelCoverage(0x19), pixelCoverage(0x1a), pixelCoverage(0x1b),
- pixelCoverage(0x1c), pixelCoverage(0x1d), pixelCoverage(0x1e), pixelCoverage(0x1f),
- pixelCoverage(0x20), pixelCoverage(0x21), pixelCoverage(0x22), pixelCoverage(0x23),
- pixelCoverage(0x24), pixelCoverage(0x25), pixelCoverage(0x26), pixelCoverage(0x27),
- pixelCoverage(0x28), pixelCoverage(0x29), pixelCoverage(0x2a), pixelCoverage(0x2b),
- pixelCoverage(0x2c), pixelCoverage(0x2d), pixelCoverage(0x2e), pixelCoverage(0x2f),
- pixelCoverage(0x30), pixelCoverage(0x31), pixelCoverage(0x32), pixelCoverage(0x33),
- pixelCoverage(0x34), pixelCoverage(0x35), pixelCoverage(0x36), pixelCoverage(0x37),
- pixelCoverage(0x38), pixelCoverage(0x39), pixelCoverage(0x3a), pixelCoverage(0x3b),
- pixelCoverage(0x3c), pixelCoverage(0x3d), pixelCoverage(0x3e), pixelCoverage(0x3f),
- pixelCoverage(0x40), pixelCoverage(0x41), pixelCoverage(0x42), pixelCoverage(0x43),
- pixelCoverage(0x44), pixelCoverage(0x45), pixelCoverage(0x46), pixelCoverage(0x47),
- pixelCoverage(0x48), pixelCoverage(0x49), pixelCoverage(0x4a), pixelCoverage(0x4b),
- pixelCoverage(0x4c), pixelCoverage(0x4d), pixelCoverage(0x4e), pixelCoverage(0x4f),
- pixelCoverage(0x50), pixelCoverage(0x51), pixelCoverage(0x52), pixelCoverage(0x53),
- pixelCoverage(0x54), pixelCoverage(0x55), pixelCoverage(0x56), pixelCoverage(0x57),
- pixelCoverage(0x58), pixelCoverage(0x59), pixelCoverage(0x5a), pixelCoverage(0x5b),
- pixelCoverage(0x5c), pixelCoverage(0x5d), pixelCoverage(0x5e), pixelCoverage(0x5f),
- pixelCoverage(0x60), pixelCoverage(0x61), pixelCoverage(0x62), pixelCoverage(0x63),
- pixelCoverage(0x64), pixelCoverage(0x65), pixelCoverage(0x66), pixelCoverage(0x67),
- pixelCoverage(0x68), pixelCoverage(0x69), pixelCoverage(0x6a), pixelCoverage(0x6b),
- pixelCoverage(0x6c), pixelCoverage(0x6d), pixelCoverage(0x6e), pixelCoverage(0x6f),
- pixelCoverage(0x70), pixelCoverage(0x71), pixelCoverage(0x72), pixelCoverage(0x73),
- pixelCoverage(0x74), pixelCoverage(0x75), pixelCoverage(0x76), pixelCoverage(0x77),
- pixelCoverage(0x78), pixelCoverage(0x79), pixelCoverage(0x7a), pixelCoverage(0x7b),
- pixelCoverage(0x7c), pixelCoverage(0x7d), pixelCoverage(0x7e), pixelCoverage(0x7f),
- pixelCoverage(0x80), pixelCoverage(0x81), pixelCoverage(0x82), pixelCoverage(0x83),
- pixelCoverage(0x84), pixelCoverage(0x85), pixelCoverage(0x86), pixelCoverage(0x87),
- pixelCoverage(0x88), pixelCoverage(0x89), pixelCoverage(0x8a), pixelCoverage(0x8b),
- pixelCoverage(0x8c), pixelCoverage(0x8d), pixelCoverage(0x8e), pixelCoverage(0x8f),
- pixelCoverage(0x90), pixelCoverage(0x91), pixelCoverage(0x92), pixelCoverage(0x93),
- pixelCoverage(0x94), pixelCoverage(0x95), pixelCoverage(0x96), pixelCoverage(0x97),
- pixelCoverage(0x98), pixelCoverage(0x99), pixelCoverage(0x9a), pixelCoverage(0x9b),
- pixelCoverage(0x9c), pixelCoverage(0x9d), pixelCoverage(0x9e), pixelCoverage(0x9f),
- pixelCoverage(0xa0), pixelCoverage(0xa1), pixelCoverage(0xa2), pixelCoverage(0xa3),
- pixelCoverage(0xa4), pixelCoverage(0xa5), pixelCoverage(0xa6), pixelCoverage(0xa7),
- pixelCoverage(0xa8), pixelCoverage(0xa9), pixelCoverage(0xaa), pixelCoverage(0xab),
- pixelCoverage(0xac), pixelCoverage(0xad), pixelCoverage(0xae), pixelCoverage(0xaf),
- pixelCoverage(0xb0), pixelCoverage(0xb1), pixelCoverage(0xb2), pixelCoverage(0xb3),
- pixelCoverage(0xb4), pixelCoverage(0xb5), pixelCoverage(0xb6), pixelCoverage(0xb7),
- pixelCoverage(0xb8), pixelCoverage(0xb9), pixelCoverage(0xba), pixelCoverage(0xbb),
- pixelCoverage(0xbc), pixelCoverage(0xbd), pixelCoverage(0xbe), pixelCoverage(0xbf),
- pixelCoverage(0xc0), pixelCoverage(0xc1), pixelCoverage(0xc2), pixelCoverage(0xc3),
- pixelCoverage(0xc4), pixelCoverage(0xc5), pixelCoverage(0xc6), pixelCoverage(0xc7),
- pixelCoverage(0xc8), pixelCoverage(0xc9), pixelCoverage(0xca), pixelCoverage(0xcb),
- pixelCoverage(0xcc), pixelCoverage(0xcd), pixelCoverage(0xce), pixelCoverage(0xcf),
- pixelCoverage(0xd0), pixelCoverage(0xd1), pixelCoverage(0xd2), pixelCoverage(0xd3),
- pixelCoverage(0xd4), pixelCoverage(0xd5), pixelCoverage(0xd6), pixelCoverage(0xd7),
- pixelCoverage(0xd8), pixelCoverage(0xd9), pixelCoverage(0xda), pixelCoverage(0xdb),
- pixelCoverage(0xdc), pixelCoverage(0xdd), pixelCoverage(0xde), pixelCoverage(0xdf),
- pixelCoverage(0xe0), pixelCoverage(0xe1), pixelCoverage(0xe2), pixelCoverage(0xe3),
- pixelCoverage(0xe4), pixelCoverage(0xe5), pixelCoverage(0xe6), pixelCoverage(0xe7),
- pixelCoverage(0xe8), pixelCoverage(0xe9), pixelCoverage(0xea), pixelCoverage(0xeb),
- pixelCoverage(0xec), pixelCoverage(0xed), pixelCoverage(0xee), pixelCoverage(0xef),
- pixelCoverage(0xf0), pixelCoverage(0xf1), pixelCoverage(0xf2), pixelCoverage(0xf3),
- pixelCoverage(0xf4), pixelCoverage(0xf5), pixelCoverage(0xf6), pixelCoverage(0xf7),
- pixelCoverage(0xf8), pixelCoverage(0xf9), pixelCoverage(0xfa), pixelCoverage(0xfb),
- pixelCoverage(0xfc), pixelCoverage(0xfd), pixelCoverage(0xfe), pixelCoverage(0xff),
-}
-
-func pixelCoverage(a uint8) uint8 {
- return a&1 + a>>1&1 + a>>2&1 + a>>3&1 + a>>4&1 + a>>5&1 + a>>6&1 + a>>7&1
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerAA.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerAA.go
deleted file mode 100644
index dbff87f1e..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerAA.go
+++ /dev/null
@@ -1,320 +0,0 @@
-// Copyright 2011 The draw2d Authors. All rights reserved.
-// created: 27/05/2011 by Laurent Le Goff
-package raster
-
-import (
- "image"
- "image/color"
- "unsafe"
-)
-
-const (
- SUBPIXEL_SHIFT = 3
- SUBPIXEL_COUNT = 1 << SUBPIXEL_SHIFT
-)
-
-var SUBPIXEL_OFFSETS = SUBPIXEL_OFFSETS_SAMPLE_8_FIXED
-
-type SUBPIXEL_DATA uint8
-type NON_ZERO_MASK_DATA_UNIT uint8
-
-type Rasterizer8BitsSample struct {
- MaskBuffer []SUBPIXEL_DATA
- WindingBuffer []NON_ZERO_MASK_DATA_UNIT
-
- Width int
- BufferWidth int
- Height int
- ClipBound [4]float64
- RemappingMatrix [6]float64
-}
-
-/* width and height define the maximum output size for the filler.
- * The filler will output to larger bitmaps as well, but the output will
- * be cropped.
- */
-func NewRasterizer8BitsSample(width, height int) *Rasterizer8BitsSample {
- var r Rasterizer8BitsSample
- // Scale the coordinates by SUBPIXEL_COUNT in vertical direction
- // The sampling point for the sub-pixel is at the top right corner. This
- // adjustment moves it to the pixel center.
- r.RemappingMatrix = [6]float64{1, 0, 0, SUBPIXEL_COUNT, 0.5 / SUBPIXEL_COUNT, -0.5 * SUBPIXEL_COUNT}
- r.Width = width
- r.Height = height
- // The buffer used for filling needs to be one pixel wider than the bitmap.
- // This is because the end flag that turns the fill of is the first pixel
- // after the actually drawn edge.
- r.BufferWidth = width + 1
-
- r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*height)
- r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*height*SUBPIXEL_COUNT)
- r.ClipBound = clip(0, 0, width, height, SUBPIXEL_COUNT)
- return &r
-}
-
-func clip(x, y, width, height, scale int) [4]float64 {
- var clipBound [4]float64
-
- offset := 0.99 / float64(scale)
-
- clipBound[0] = float64(x) + offset
- clipBound[2] = float64(x+width) - offset
-
- clipBound[1] = float64(y * scale)
- clipBound[3] = float64((y + height) * scale)
- return clipBound
-}
-
-func intersect(r1, r2 [4]float64) [4]float64 {
- if r1[0] < r2[0] {
- r1[0] = r2[0]
- }
- if r1[2] > r2[2] {
- r1[2] = r2[2]
- }
- if r1[0] > r1[2] {
- r1[0] = r1[2]
- }
-
- if r1[1] < r2[1] {
- r1[1] = r2[1]
- }
- if r1[3] > r2[3] {
- r1[3] = r2[3]
- }
- if r1[1] > r1[3] {
- r1[1] = r1[3]
- }
- return r1
-}
-
-func (r *Rasterizer8BitsSample) RenderEvenOdd(img *image.RGBA, color *color.RGBA, polygon *Polygon, tr [6]float64) {
- // memset 0 the mask buffer
- r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height)
-
- // inline matrix multiplication
- transform := [6]float64{
- tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2],
- tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1],
- tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2],
- tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1],
- tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4],
- tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5],
- }
-
- clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT)
- clipRect = intersect(clipRect, r.ClipBound)
- p := 0
- l := len(*polygon) / 2
- var edges [32]PolygonEdge
- for p < l {
- edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect)
- for k := 0; k < edgeCount; k++ {
- r.addEvenOddEdge(&edges[k])
- }
- p += 16
- }
-
- r.fillEvenOdd(img, color, clipRect)
-}
-
-//! Adds an edge to be used with even-odd fill.
-func (r *Rasterizer8BitsSample) addEvenOddEdge(edge *PolygonEdge) {
- x := Fix(edge.X * FIXED_FLOAT_COEF)
- slope := Fix(edge.Slope * FIXED_FLOAT_COEF)
- slopeFix := Fix(0)
- if edge.LastLine-edge.FirstLine >= SLOPE_FIX_STEP {
- slopeFix = Fix(edge.Slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - slope<<SLOPE_FIX_SHIFT
- }
-
- var mask SUBPIXEL_DATA
- var ySub uint32
- var xp, yLine int
- for y := edge.FirstLine; y <= edge.LastLine; y++ {
- ySub = uint32(y & (SUBPIXEL_COUNT - 1))
- xp = int((x + SUBPIXEL_OFFSETS[ySub]) >> FIXED_SHIFT)
- mask = SUBPIXEL_DATA(1 << ySub)
- yLine = y >> SUBPIXEL_SHIFT
- r.MaskBuffer[yLine*r.BufferWidth+xp] ^= mask
- x += slope
- if y&SLOPE_FIX_MASK == 0 {
- x += slopeFix
- }
- }
-}
-
-//! Adds an edge to be used with non-zero winding fill.
-func (r *Rasterizer8BitsSample) addNonZeroEdge(edge *PolygonEdge) {
- x := Fix(edge.X * FIXED_FLOAT_COEF)
- slope := Fix(edge.Slope * FIXED_FLOAT_COEF)
- slopeFix := Fix(0)
- if edge.LastLine-edge.FirstLine >= SLOPE_FIX_STEP {
- slopeFix = Fix(edge.Slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - slope<<SLOPE_FIX_SHIFT
- }
- var mask SUBPIXEL_DATA
- var ySub uint32
- var xp, yLine int
- winding := NON_ZERO_MASK_DATA_UNIT(edge.Winding)
- for y := edge.FirstLine; y <= edge.LastLine; y++ {
- ySub = uint32(y & (SUBPIXEL_COUNT - 1))
- xp = int((x + SUBPIXEL_OFFSETS[ySub]) >> FIXED_SHIFT)
- mask = SUBPIXEL_DATA(1 << ySub)
- yLine = y >> SUBPIXEL_SHIFT
- r.MaskBuffer[yLine*r.BufferWidth+xp] |= mask
- r.WindingBuffer[(yLine*r.BufferWidth+xp)*SUBPIXEL_COUNT+int(ySub)] += winding
- x += slope
- if y&SLOPE_FIX_MASK == 0 {
- x += slopeFix
- }
- }
-}
-
-// Renders the mask to the canvas with even-odd fill.
-func (r *Rasterizer8BitsSample) fillEvenOdd(img *image.RGBA, color *color.RGBA, clipBound [4]float64) {
- var x, y uint32
-
- minX := uint32(clipBound[0])
- maxX := uint32(clipBound[2])
-
- minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT
- maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT
-
- //pixColor := (uint32(color.R) << 24) | (uint32(color.G) << 16) | (uint32(color.B) << 8) | uint32(color.A)
- pixColor := (*uint32)(unsafe.Pointer(color))
- cs1 := *pixColor & 0xff00ff
- cs2 := *pixColor >> 8 & 0xff00ff
-
- stride := uint32(img.Stride)
- var mask SUBPIXEL_DATA
-
- for y = minY; y < maxY; y++ {
- tp := img.Pix[y*stride:]
-
- mask = 0
- for x = minX; x <= maxX; x++ {
- p := (*uint32)(unsafe.Pointer(&tp[x]))
- mask ^= r.MaskBuffer[y*uint32(r.BufferWidth)+x]
- // 8bits
- alpha := uint32(coverageTable[mask])
- // 16bits
- //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff])
- // 32bits
- //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff] + coverageTable[(mask >> 16) & 0xff] + coverageTable[(mask >> 24) & 0xff])
-
- // alpha is in range of 0 to SUBPIXEL_COUNT
- invAlpha := SUBPIXEL_COUNT - alpha
-
- ct1 := *p & 0xff00ff * invAlpha
- ct2 := *p >> 8 & 0xff00ff * invAlpha
-
- ct1 = (ct1 + cs1*alpha) >> SUBPIXEL_SHIFT & 0xff00ff
- ct2 = (ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT) & 0xff00ff00
-
- *p = ct1 + ct2
- }
- }
-}
-
-/*
- * Renders the polygon with non-zero winding fill.
- * param aTarget the target bitmap.
- * param aPolygon the polygon to render.
- * param aColor the color to be used for rendering.
- * param aTransformation the transformation matrix.
- */
-func (r *Rasterizer8BitsSample) RenderNonZeroWinding(img *image.RGBA, color *color.RGBA, polygon *Polygon, tr [6]float64) {
-
- r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height)
- r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*r.Height*SUBPIXEL_COUNT)
-
- // inline matrix multiplication
- transform := [6]float64{
- tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2],
- tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1],
- tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2],
- tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1],
- tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4],
- tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5],
- }
-
- clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT)
- clipRect = intersect(clipRect, r.ClipBound)
-
- p := 0
- l := len(*polygon) / 2
- var edges [32]PolygonEdge
- for p < l {
- edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect)
- for k := 0; k < edgeCount; k++ {
- r.addNonZeroEdge(&edges[k])
- }
- p += 16
- }
-
- r.fillNonZero(img, color, clipRect)
-}
-
-//! Renders the mask to the canvas with non-zero winding fill.
-func (r *Rasterizer8BitsSample) fillNonZero(img *image.RGBA, color *color.RGBA, clipBound [4]float64) {
- var x, y uint32
-
- minX := uint32(clipBound[0])
- maxX := uint32(clipBound[2])
-
- minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT
- maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT
-
- //pixColor := (uint32(color.R) << 24) | (uint32(color.G) << 16) | (uint32(color.B) << 8) | uint32(color.A)
- pixColor := (*uint32)(unsafe.Pointer(color))
- cs1 := *pixColor & 0xff00ff
- cs2 := *pixColor >> 8 & 0xff00ff
-
- stride := uint32(img.Stride)
- var mask SUBPIXEL_DATA
- var n uint32
- var values [SUBPIXEL_COUNT]NON_ZERO_MASK_DATA_UNIT
- for n = 0; n < SUBPIXEL_COUNT; n++ {
- values[n] = 0
- }
-
- for y = minY; y < maxY; y++ {
- tp := img.Pix[y*stride:]
-
- mask = 0
- for x = minX; x <= maxX; x++ {
- p := (*uint32)(unsafe.Pointer(&tp[x]))
- temp := r.MaskBuffer[y*uint32(r.BufferWidth)+x]
- if temp != 0 {
- var bit SUBPIXEL_DATA = 1
- for n = 0; n < SUBPIXEL_COUNT; n++ {
- if temp&bit != 0 {
- t := values[n]
- values[n] += r.WindingBuffer[(y*uint32(r.BufferWidth)+x)*SUBPIXEL_COUNT+n]
- if (t == 0 || values[n] == 0) && t != values[n] {
- mask ^= bit
- }
- }
- bit <<= 1
- }
- }
-
- // 8bits
- alpha := uint32(coverageTable[mask])
- // 16bits
- //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff])
- // 32bits
- //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff] + coverageTable[(mask >> 16) & 0xff] + coverageTable[(mask >> 24) & 0xff])
-
- // alpha is in range of 0 to SUBPIXEL_COUNT
- invAlpha := uint32(SUBPIXEL_COUNT) - alpha
-
- ct1 := *p & 0xff00ff * invAlpha
- ct2 := *p >> 8 & 0xff00ff * invAlpha
-
- ct1 = (ct1 + cs1*alpha) >> SUBPIXEL_SHIFT & 0xff00ff
- ct2 = (ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT) & 0xff00ff00
-
- *p = ct1 + ct2
- }
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerV1/fillerAA.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerV1/fillerAA.go
deleted file mode 100644
index a85d34c77..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerV1/fillerAA.go
+++ /dev/null
@@ -1,303 +0,0 @@
-// Copyright 2011 The draw2d Authors. All rights reserved.
-// created: 27/05/2011 by Laurent Le Goff
-package raster
-
-import (
- "image"
- "image/color"
- "unsafe"
-)
-
-const (
- SUBPIXEL_SHIFT = 3
- SUBPIXEL_COUNT = 1 << SUBPIXEL_SHIFT
-)
-
-var SUBPIXEL_OFFSETS = SUBPIXEL_OFFSETS_SAMPLE_8
-
-type SUBPIXEL_DATA uint16
-type NON_ZERO_MASK_DATA_UNIT uint8
-
-type Rasterizer8BitsSample struct {
- MaskBuffer []SUBPIXEL_DATA
- WindingBuffer []NON_ZERO_MASK_DATA_UNIT
-
- Width int
- BufferWidth int
- Height int
- ClipBound [4]float64
- RemappingMatrix [6]float64
-}
-
-/* width and height define the maximum output size for the filler.
- * The filler will output to larger bitmaps as well, but the output will
- * be cropped.
- */
-func NewRasterizer8BitsSample(width, height int) *Rasterizer8BitsSample {
- var r Rasterizer8BitsSample
- // Scale the coordinates by SUBPIXEL_COUNT in vertical direction
- // The sampling point for the sub-pixel is at the top right corner. This
- // adjustment moves it to the pixel center.
- r.RemappingMatrix = [6]float64{1, 0, 0, SUBPIXEL_COUNT, 0.5 / SUBPIXEL_COUNT, -0.5 * SUBPIXEL_COUNT}
- r.Width = width
- r.Height = height
- // The buffer used for filling needs to be one pixel wider than the bitmap.
- // This is because the end flag that turns the fill of is the first pixel
- // after the actually drawn edge.
- r.BufferWidth = width + 1
-
- r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*height)
- r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*height*SUBPIXEL_COUNT)
- r.ClipBound = clip(0, 0, width, height, SUBPIXEL_COUNT)
- return &r
-}
-
-func clip(x, y, width, height, scale int) [4]float64 {
- var clipBound [4]float64
-
- offset := 0.99 / float64(scale)
-
- clipBound[0] = float64(x) + offset
- clipBound[2] = float64(x+width) - offset
-
- clipBound[1] = float64(y * scale)
- clipBound[3] = float64((y + height) * scale)
- return clipBound
-}
-
-func intersect(r1, r2 [4]float64) [4]float64 {
- if r1[0] < r2[0] {
- r1[0] = r2[0]
- }
- if r1[2] > r2[2] {
- r1[2] = r2[2]
- }
- if r1[0] > r1[2] {
- r1[0] = r1[2]
- }
-
- if r1[1] < r2[1] {
- r1[1] = r2[1]
- }
- if r1[3] > r2[3] {
- r1[3] = r2[3]
- }
- if r1[1] > r1[3] {
- r1[1] = r1[3]
- }
- return r1
-}
-
-func (r *Rasterizer8BitsSample) RenderEvenOdd(img *image.RGBA, color *color.RGBA, polygon *Polygon, tr [6]float64) {
- // memset 0 the mask buffer
- r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height)
-
- // inline matrix multiplication
- transform := [6]float64{
- tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2],
- tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1],
- tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2],
- tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1],
- tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4],
- tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5],
- }
-
- clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT)
- clipRect = intersect(clipRect, r.ClipBound)
- p := 0
- l := len(*polygon) / 2
- var edges [32]PolygonEdge
- for p < l {
- edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect)
- for k := 0; k < edgeCount; k++ {
- r.addEvenOddEdge(&edges[k])
- }
- p += 16
- }
-
- r.fillEvenOdd(img, color, clipRect)
-}
-
-//! Adds an edge to be used with even-odd fill.
-func (r *Rasterizer8BitsSample) addEvenOddEdge(edge *PolygonEdge) {
- x := edge.X
- slope := edge.Slope
- var ySub, mask SUBPIXEL_DATA
- var xp, yLine int
- for y := edge.FirstLine; y <= edge.LastLine; y++ {
- ySub = SUBPIXEL_DATA(y & (SUBPIXEL_COUNT - 1))
- xp = int(x + SUBPIXEL_OFFSETS[ySub])
- mask = SUBPIXEL_DATA(1 << ySub)
- yLine = y >> SUBPIXEL_SHIFT
- r.MaskBuffer[yLine*r.BufferWidth+xp] ^= mask
- x += slope
- }
-}
-
-// Renders the mask to the canvas with even-odd fill.
-func (r *Rasterizer8BitsSample) fillEvenOdd(img *image.RGBA, color *color.RGBA, clipBound [4]float64) {
- var x, y uint32
-
- minX := uint32(clipBound[0])
- maxX := uint32(clipBound[2])
-
- minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT
- maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT
-
- //pixColor := (uint32(color.R) << 24) | (uint32(color.G) << 16) | (uint32(color.B) << 8) | uint32(color.A)
- pixColor := (*uint32)(unsafe.Pointer(color))
- cs1 := *pixColor & 0xff00ff
- cs2 := *pixColor >> 8 & 0xff00ff
-
- stride := uint32(img.Stride)
- var mask SUBPIXEL_DATA
-
- for y = minY; y < maxY; y++ {
- tp := img.Pix[y*stride:]
-
- mask = 0
- for x = minX; x <= maxX; x++ {
- p := (*uint32)(unsafe.Pointer(&tp[x]))
- mask ^= r.MaskBuffer[y*uint32(r.BufferWidth)+x]
- // 8bits
- alpha := uint32(coverageTable[mask])
- // 16bits
- //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff])
- // 32bits
- //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff] + coverageTable[(mask >> 16) & 0xff] + coverageTable[(mask >> 24) & 0xff])
-
- // alpha is in range of 0 to SUBPIXEL_COUNT
- invAlpha := uint32(SUBPIXEL_COUNT) - alpha
-
- ct1 := *p & 0xff00ff * invAlpha
- ct2 := *p >> 8 & 0xff00ff * invAlpha
-
- ct1 = (ct1 + cs1*alpha) >> SUBPIXEL_SHIFT & 0xff00ff
- ct2 = (ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT) & 0xff00ff00
-
- *p = ct1 + ct2
- }
- }
-}
-
-/*
- * Renders the polygon with non-zero winding fill.
- * param aTarget the target bitmap.
- * param aPolygon the polygon to render.
- * param aColor the color to be used for rendering.
- * param aTransformation the transformation matrix.
- */
-func (r *Rasterizer8BitsSample) RenderNonZeroWinding(img *image.RGBA, color *color.RGBA, polygon *Polygon, tr [6]float64) {
-
- r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height)
- r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*r.Height*SUBPIXEL_COUNT)
-
- // inline matrix multiplication
- transform := [6]float64{
- tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2],
- tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1],
- tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2],
- tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1],
- tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4],
- tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5],
- }
-
- clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT)
- clipRect = intersect(clipRect, r.ClipBound)
-
- p := 0
- l := len(*polygon) / 2
- var edges [32]PolygonEdge
- for p < l {
- edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect)
- for k := 0; k < edgeCount; k++ {
- r.addNonZeroEdge(&edges[k])
- }
- p += 16
- }
-
- r.fillNonZero(img, color, clipRect)
-}
-
-//! Adds an edge to be used with non-zero winding fill.
-func (r *Rasterizer8BitsSample) addNonZeroEdge(edge *PolygonEdge) {
- x := edge.X
- slope := edge.Slope
- var ySub, mask SUBPIXEL_DATA
- var xp, yLine int
- winding := NON_ZERO_MASK_DATA_UNIT(edge.Winding)
- for y := edge.FirstLine; y <= edge.LastLine; y++ {
- ySub = SUBPIXEL_DATA(y & (SUBPIXEL_COUNT - 1))
- xp = int(x + SUBPIXEL_OFFSETS[ySub])
- mask = SUBPIXEL_DATA(1 << ySub)
- yLine = y >> SUBPIXEL_SHIFT
- r.MaskBuffer[yLine*r.BufferWidth+xp] |= mask
- r.WindingBuffer[(yLine*r.BufferWidth+xp)*SUBPIXEL_COUNT+int(ySub)] += winding
- x += slope
- }
-}
-
-//! Renders the mask to the canvas with non-zero winding fill.
-func (r *Rasterizer8BitsSample) fillNonZero(img *image.RGBA, color *color.RGBA, clipBound [4]float64) {
- var x, y uint32
-
- minX := uint32(clipBound[0])
- maxX := uint32(clipBound[2])
-
- minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT
- maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT
-
- //pixColor := (uint32(color.R) << 24) | (uint32(color.G) << 16) | (uint32(color.B) << 8) | uint32(color.A)
- pixColor := (*uint32)(unsafe.Pointer(color))
- cs1 := *pixColor & 0xff00ff
- cs2 := *pixColor >> 8 & 0xff00ff
-
- stride := uint32(img.Stride)
- var mask SUBPIXEL_DATA
- var n uint32
- var values [SUBPIXEL_COUNT]NON_ZERO_MASK_DATA_UNIT
- for n = 0; n < SUBPIXEL_COUNT; n++ {
- values[n] = 0
- }
-
- for y = minY; y < maxY; y++ {
- tp := img.Pix[y*stride:]
-
- mask = 0
- for x = minX; x <= maxX; x++ {
- p := (*uint32)(unsafe.Pointer(&tp[x]))
- temp := r.MaskBuffer[y*uint32(r.BufferWidth)+x]
- if temp != 0 {
- var bit SUBPIXEL_DATA = 1
- for n = 0; n < SUBPIXEL_COUNT; n++ {
- if temp&bit != 0 {
- t := values[n]
- values[n] += r.WindingBuffer[(y*uint32(r.BufferWidth)+x)*SUBPIXEL_COUNT+n]
- if (t == 0 || values[n] == 0) && t != values[n] {
- mask ^= bit
- }
- }
- bit <<= 1
- }
- }
-
- // 8bits
- alpha := uint32(coverageTable[mask])
- // 16bits
- //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff])
- // 32bits
- //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff] + coverageTable[(mask >> 16) & 0xff] + coverageTable[(mask >> 24) & 0xff])
-
- // alpha is in range of 0 to SUBPIXEL_COUNT
- invAlpha := uint32(SUBPIXEL_COUNT) - alpha
-
- ct1 := *p & 0xff00ff * invAlpha
- ct2 := *p >> 8 & 0xff00ff * invAlpha
-
- ct1 = (ct1 + cs1*alpha) >> SUBPIXEL_SHIFT & 0xff00ff
- ct2 = (ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT) & 0xff00ff00
-
- *p = ct1 + ct2
- }
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerV2/fillerAA.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerV2/fillerAA.go
deleted file mode 100644
index 0bda5a4db..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerV2/fillerAA.go
+++ /dev/null
@@ -1,320 +0,0 @@
-// Copyright 2011 The draw2d Authors. All rights reserved.
-// created: 27/05/2011 by Laurent Le Goff
-package raster
-
-import (
- "image"
- "image/color"
- "unsafe"
-)
-
-const (
- SUBPIXEL_SHIFT = 5
- SUBPIXEL_COUNT = 1 << SUBPIXEL_SHIFT
-)
-
-var SUBPIXEL_OFFSETS = SUBPIXEL_OFFSETS_SAMPLE_32_FIXED
-
-type SUBPIXEL_DATA uint32
-type NON_ZERO_MASK_DATA_UNIT uint8
-
-type Rasterizer8BitsSample struct {
- MaskBuffer []SUBPIXEL_DATA
- WindingBuffer []NON_ZERO_MASK_DATA_UNIT
-
- Width int
- BufferWidth int
- Height int
- ClipBound [4]float64
- RemappingMatrix [6]float64
-}
-
-/* width and height define the maximum output size for the filler.
- * The filler will output to larger bitmaps as well, but the output will
- * be cropped.
- */
-func NewRasterizer8BitsSample(width, height int) *Rasterizer8BitsSample {
- var r Rasterizer8BitsSample
- // Scale the coordinates by SUBPIXEL_COUNT in vertical direction
- // The sampling point for the sub-pixel is at the top right corner. This
- // adjustment moves it to the pixel center.
- r.RemappingMatrix = [6]float64{1, 0, 0, SUBPIXEL_COUNT, 0.5 / SUBPIXEL_COUNT, -0.5 * SUBPIXEL_COUNT}
- r.Width = width
- r.Height = height
- // The buffer used for filling needs to be one pixel wider than the bitmap.
- // This is because the end flag that turns the fill of is the first pixel
- // after the actually drawn edge.
- r.BufferWidth = width + 1
-
- r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*height)
- r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*height*SUBPIXEL_COUNT)
- r.ClipBound = clip(0, 0, width, height, SUBPIXEL_COUNT)
- return &r
-}
-
-func clip(x, y, width, height, scale int) [4]float64 {
- var clipBound [4]float64
-
- offset := 0.99 / float64(scale)
-
- clipBound[0] = float64(x) + offset
- clipBound[2] = float64(x+width) - offset
-
- clipBound[1] = float64(y * scale)
- clipBound[3] = float64((y + height) * scale)
- return clipBound
-}
-
-func intersect(r1, r2 [4]float64) [4]float64 {
- if r1[0] < r2[0] {
- r1[0] = r2[0]
- }
- if r1[2] > r2[2] {
- r1[2] = r2[2]
- }
- if r1[0] > r1[2] {
- r1[0] = r1[2]
- }
-
- if r1[1] < r2[1] {
- r1[1] = r2[1]
- }
- if r1[3] > r2[3] {
- r1[3] = r2[3]
- }
- if r1[1] > r1[3] {
- r1[1] = r1[3]
- }
- return r1
-}
-
-func (r *Rasterizer8BitsSample) RenderEvenOdd(img *image.RGBA, color *color.RGBA, polygon *Polygon, tr [6]float64) {
- // memset 0 the mask buffer
- r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height)
-
- // inline matrix multiplication
- transform := [6]float64{
- tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2],
- tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1],
- tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2],
- tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1],
- tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4],
- tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5],
- }
-
- clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT)
- clipRect = intersect(clipRect, r.ClipBound)
- p := 0
- l := len(*polygon) / 2
- var edges [32]PolygonEdge
- for p < l {
- edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect)
- for k := 0; k < edgeCount; k++ {
- r.addEvenOddEdge(&edges[k])
- }
- p += 16
- }
-
- r.fillEvenOdd(img, color, clipRect)
-}
-
-//! Adds an edge to be used with even-odd fill.
-func (r *Rasterizer8BitsSample) addEvenOddEdge(edge *PolygonEdge) {
- x := Fix(edge.X * FIXED_FLOAT_COEF)
- slope := Fix(edge.Slope * FIXED_FLOAT_COEF)
- slopeFix := Fix(0)
- if edge.LastLine-edge.FirstLine >= SLOPE_FIX_STEP {
- slopeFix = Fix(edge.Slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - slope<<SLOPE_FIX_SHIFT
- }
-
- var mask SUBPIXEL_DATA
- var ySub uint32
- var xp, yLine int
- for y := edge.FirstLine; y <= edge.LastLine; y++ {
- ySub = uint32(y & (SUBPIXEL_COUNT - 1))
- xp = int((x + SUBPIXEL_OFFSETS[ySub]) >> FIXED_SHIFT)
- mask = SUBPIXEL_DATA(1 << ySub)
- yLine = y >> SUBPIXEL_SHIFT
- r.MaskBuffer[yLine*r.BufferWidth+xp] ^= mask
- x += slope
- if y&SLOPE_FIX_MASK == 0 {
- x += slopeFix
- }
- }
-}
-
-//! Adds an edge to be used with non-zero winding fill.
-func (r *Rasterizer8BitsSample) addNonZeroEdge(edge *PolygonEdge) {
- x := Fix(edge.X * FIXED_FLOAT_COEF)
- slope := Fix(edge.Slope * FIXED_FLOAT_COEF)
- slopeFix := Fix(0)
- if edge.LastLine-edge.FirstLine >= SLOPE_FIX_STEP {
- slopeFix = Fix(edge.Slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - slope<<SLOPE_FIX_SHIFT
- }
- var mask SUBPIXEL_DATA
- var ySub uint32
- var xp, yLine int
- winding := NON_ZERO_MASK_DATA_UNIT(edge.Winding)
- for y := edge.FirstLine; y <= edge.LastLine; y++ {
- ySub = uint32(y & (SUBPIXEL_COUNT - 1))
- xp = int((x + SUBPIXEL_OFFSETS[ySub]) >> FIXED_SHIFT)
- mask = SUBPIXEL_DATA(1 << ySub)
- yLine = y >> SUBPIXEL_SHIFT
- r.MaskBuffer[yLine*r.BufferWidth+xp] |= mask
- r.WindingBuffer[(yLine*r.BufferWidth+xp)*SUBPIXEL_COUNT+int(ySub)] += winding
- x += slope
- if y&SLOPE_FIX_MASK == 0 {
- x += slopeFix
- }
- }
-}
-
-// Renders the mask to the canvas with even-odd fill.
-func (r *Rasterizer8BitsSample) fillEvenOdd(img *image.RGBA, color *color.RGBA, clipBound [4]float64) {
- var x, y uint32
-
- minX := uint32(clipBound[0])
- maxX := uint32(clipBound[2])
-
- minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT
- maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT
-
- //pixColor := (uint32(color.R) << 24) | (uint32(color.G) << 16) | (uint32(color.B) << 8) | uint32(color.A)
- pixColor := (*uint32)(unsafe.Pointer(color))
- cs1 := *pixColor & 0xff00ff
- cs2 := *pixColor >> 8 & 0xff00ff
-
- stride := uint32(img.Stride)
- var mask SUBPIXEL_DATA
-
- for y = minY; y < maxY; y++ {
- tp := img.Pix[y*stride:]
-
- mask = 0
- for x = minX; x <= maxX; x++ {
- p := (*uint32)(unsafe.Pointer(&tp[x]))
- mask ^= r.MaskBuffer[y*uint32(r.BufferWidth)+x]
- // 8bits
- //alpha := uint32(coverageTable[mask])
- // 16bits
- //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff])
- // 32bits
- alpha := uint32(coverageTable[mask&0xff] + coverageTable[mask>>8&0xff] + coverageTable[mask>>16&0xff] + coverageTable[mask>>24&0xff])
-
- // alpha is in range of 0 to SUBPIXEL_COUNT
- invAlpha := uint32(SUBPIXEL_COUNT) - alpha
-
- ct1 := *p & 0xff00ff * invAlpha
- ct2 := *p >> 8 & 0xff00ff * invAlpha
-
- ct1 = (ct1 + cs1*alpha) >> SUBPIXEL_SHIFT & 0xff00ff
- ct2 = (ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT) & 0xff00ff00
-
- *p = ct1 + ct2
- }
- }
-}
-
-/*
- * Renders the polygon with non-zero winding fill.
- * param aTarget the target bitmap.
- * param aPolygon the polygon to render.
- * param aColor the color to be used for rendering.
- * param aTransformation the transformation matrix.
- */
-func (r *Rasterizer8BitsSample) RenderNonZeroWinding(img *image.RGBA, color *color.RGBA, polygon *Polygon, tr [6]float64) {
-
- r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height)
- r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*r.Height*SUBPIXEL_COUNT)
-
- // inline matrix multiplication
- transform := [6]float64{
- tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2],
- tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1],
- tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2],
- tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1],
- tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4],
- tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5],
- }
-
- clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT)
- clipRect = intersect(clipRect, r.ClipBound)
-
- p := 0
- l := len(*polygon) / 2
- var edges [32]PolygonEdge
- for p < l {
- edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect)
- for k := 0; k < edgeCount; k++ {
- r.addNonZeroEdge(&edges[k])
- }
- p += 16
- }
-
- r.fillNonZero(img, color, clipRect)
-}
-
-//! Renders the mask to the canvas with non-zero winding fill.
-func (r *Rasterizer8BitsSample) fillNonZero(img *image.RGBA, color *color.RGBA, clipBound [4]float64) {
- var x, y uint32
-
- minX := uint32(clipBound[0])
- maxX := uint32(clipBound[2])
-
- minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT
- maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT
-
- //pixColor := (uint32(color.R) << 24) | (uint32(color.G) << 16) | (uint32(color.B) << 8) | uint32(color.A)
- pixColor := (*uint32)(unsafe.Pointer(color))
- cs1 := *pixColor & 0xff00ff
- cs2 := *pixColor >> 8 & 0xff00ff
-
- stride := uint32(img.Stride)
- var mask SUBPIXEL_DATA
- var n uint32
- var values [SUBPIXEL_COUNT]NON_ZERO_MASK_DATA_UNIT
- for n = 0; n < SUBPIXEL_COUNT; n++ {
- values[n] = 0
- }
-
- for y = minY; y < maxY; y++ {
- tp := img.Pix[y*stride:]
-
- mask = 0
- for x = minX; x <= maxX; x++ {
- p := (*uint32)(unsafe.Pointer(&tp[x]))
- temp := r.MaskBuffer[y*uint32(r.BufferWidth)+x]
- if temp != 0 {
- var bit SUBPIXEL_DATA = 1
- for n = 0; n < SUBPIXEL_COUNT; n++ {
- if temp&bit != 0 {
- t := values[n]
- values[n] += r.WindingBuffer[(y*uint32(r.BufferWidth)+x)*SUBPIXEL_COUNT+n]
- if (t == 0 || values[n] == 0) && t != values[n] {
- mask ^= bit
- }
- }
- bit <<= 1
- }
- }
-
- // 8bits
- //alpha := uint32(coverageTable[mask])
- // 16bits
- //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff])
- // 32bits
- alpha := uint32(coverageTable[mask&0xff] + coverageTable[mask>>8&0xff] + coverageTable[mask>>16&0xff] + coverageTable[mask>>24&0xff])
-
- // alpha is in range of 0 to SUBPIXEL_COUNT
- invAlpha := uint32(SUBPIXEL_COUNT) - alpha
-
- ct1 := *p & 0xff00ff * invAlpha
- ct2 := *p >> 8 & 0xff00ff * invAlpha
-
- ct1 = (ct1 + cs1*alpha) >> SUBPIXEL_SHIFT & 0xff00ff
- ct2 = (ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT) & 0xff00ff00
-
- *p = ct1 + ct2
- }
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fixed_point.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fixed_point.go
deleted file mode 100644
index 14b8419c3..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fixed_point.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package raster
-
-type Fix int32
-
-const (
- FIXED_SHIFT = 16
- FIXED_FLOAT_COEF = 1 << FIXED_SHIFT
-)
-
-/*! Fixed point math inevitably introduces rounding error to the DDA. The error is
- * fixed every now and then by a separate fix value. The defines below set these.
- */
-const (
- SLOPE_FIX_SHIFT = 8
- SLOPE_FIX_STEP = 1 << SLOPE_FIX_SHIFT
- SLOPE_FIX_MASK = SLOPE_FIX_STEP - 1
-)
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/line.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/line.go
deleted file mode 100644
index 6f6d8863f..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/line.go
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2011 The draw2d Authors. All rights reserved.
-// created: 27/05/2011 by Laurent Le Goff
-package raster
-
-import (
- "image/color"
- "image/draw"
-)
-
-func abs(i int) int {
- if i < 0 {
- return -i
- }
- return i
-}
-
-func PolylineBresenham(img draw.Image, c color.Color, s ...float64) {
- for i := 2; i < len(s); i += 2 {
- Bresenham(img, c, int(s[i-2]+0.5), int(s[i-1]+0.5), int(s[i]+0.5), int(s[i+1]+0.5))
- }
-}
-
-func Bresenham(img draw.Image, color color.Color, x0, y0, x1, y1 int) {
- dx := abs(x1 - x0)
- dy := abs(y1 - y0)
- var sx, sy int
- if x0 < x1 {
- sx = 1
- } else {
- sx = -1
- }
- if y0 < y1 {
- sy = 1
- } else {
- sy = -1
- }
- err := dx - dy
-
- var e2 int
- for {
- img.Set(x0, y0, color)
- if x0 == x1 && y0 == y1 {
- return
- }
- e2 = 2 * err
- if e2 > -dy {
- err = err - dy
- x0 = x0 + sx
- }
- if e2 < dx {
- err = err + dx
- y0 = y0 + sy
- }
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/polygon.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/polygon.go
deleted file mode 100644
index 2a19e7355..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/polygon.go
+++ /dev/null
@@ -1,581 +0,0 @@
-// Copyright 2011 The draw2d Authors. All rights reserved.
-// created: 27/05/2011 by Laurent Le Goff
-package raster
-
-const (
- POLYGON_CLIP_NONE = iota
- POLYGON_CLIP_LEFT
- POLYGON_CLIP_RIGHT
- POLYGON_CLIP_TOP
- POLYGON_CLIP_BOTTOM
-)
-
-type Polygon []float64
-
-type PolygonEdge struct {
- X, Slope float64
- FirstLine, LastLine int
- Winding int16
-}
-
-//! A more optimized representation of a polygon edge.
-type PolygonScanEdge struct {
- FirstLine, LastLine int
- Winding int16
- X Fix
- Slope Fix
- SlopeFix Fix
- NextEdge *PolygonScanEdge
-}
-
-//! Calculates the edges of the polygon with transformation and clipping to edges array.
-/*! \param startIndex the index for the first vertex.
- * \param vertexCount the amount of vertices to convert.
- * \param edges the array for result edges. This should be able to contain 2*aVertexCount edges.
- * \param tr the transformation matrix for the polygon.
- * \param aClipRectangle the clip rectangle.
- * \return the amount of edges in the result.
- */
-func (p Polygon) getEdges(startIndex, vertexCount int, edges []PolygonEdge, tr [6]float64, clipBound [4]float64) int {
- startIndex = startIndex * 2
- endIndex := startIndex + vertexCount*2
- if endIndex > len(p) {
- endIndex = len(p)
- }
-
- x := p[startIndex]
- y := p[startIndex+1]
- // inline transformation
- prevX := x*tr[0] + y*tr[2] + tr[4]
- prevY := x*tr[1] + y*tr[3] + tr[5]
-
- //! Calculates the clip flags for a point.
- prevClipFlags := POLYGON_CLIP_NONE
- if prevX < clipBound[0] {
- prevClipFlags |= POLYGON_CLIP_LEFT
- } else if prevX >= clipBound[2] {
- prevClipFlags |= POLYGON_CLIP_RIGHT
- }
-
- if prevY < clipBound[1] {
- prevClipFlags |= POLYGON_CLIP_TOP
- } else if prevY >= clipBound[3] {
- prevClipFlags |= POLYGON_CLIP_BOTTOM
- }
-
- edgeCount := 0
- var k, clipFlags, clipSum, clipUnion int
- var xleft, yleft, xright, yright, oldY, maxX, minX float64
- var swapWinding int16
- for n := startIndex; n < endIndex; n = n + 2 {
- k = (n + 2) % len(p)
- x = p[k]*tr[0] + p[k+1]*tr[2] + tr[4]
- y = p[k]*tr[1] + p[k+1]*tr[3] + tr[5]
-
- //! Calculates the clip flags for a point.
- clipFlags = POLYGON_CLIP_NONE
- if prevX < clipBound[0] {
- clipFlags |= POLYGON_CLIP_LEFT
- } else if prevX >= clipBound[2] {
- clipFlags |= POLYGON_CLIP_RIGHT
- }
- if prevY < clipBound[1] {
- clipFlags |= POLYGON_CLIP_TOP
- } else if prevY >= clipBound[3] {
- clipFlags |= POLYGON_CLIP_BOTTOM
- }
-
- clipSum = prevClipFlags | clipFlags
- clipUnion = prevClipFlags & clipFlags
-
- // Skip all edges that are either completely outside at the top or at the bottom.
- if clipUnion&(POLYGON_CLIP_TOP|POLYGON_CLIP_BOTTOM) == 0 {
- if clipUnion&POLYGON_CLIP_RIGHT != 0 {
- // Both clip to right, edge is a vertical line on the right side
- if getVerticalEdge(prevY, y, clipBound[2], &edges[edgeCount], clipBound) {
- edgeCount++
- }
- } else if clipUnion&POLYGON_CLIP_LEFT != 0 {
- // Both clip to left, edge is a vertical line on the left side
- if getVerticalEdge(prevY, y, clipBound[0], &edges[edgeCount], clipBound) {
- edgeCount++
- }
- } else if clipSum&(POLYGON_CLIP_RIGHT|POLYGON_CLIP_LEFT) == 0 {
- // No clipping in the horizontal direction
- if getEdge(prevX, prevY, x, y, &edges[edgeCount], clipBound) {
- edgeCount++
- }
- } else {
- // Clips to left or right or both.
-
- if x < prevX {
- xleft, yleft = x, y
- xright, yright = prevX, prevY
- swapWinding = -1
- } else {
- xleft, yleft = prevX, prevY
- xright, yright = x, y
- swapWinding = 1
- }
-
- slope := (yright - yleft) / (xright - xleft)
-
- if clipSum&POLYGON_CLIP_RIGHT != 0 {
- // calculate new position for the right vertex
- oldY = yright
- maxX = clipBound[2]
-
- yright = yleft + (maxX-xleft)*slope
- xright = maxX
-
- // add vertical edge for the overflowing part
- if getVerticalEdge(yright, oldY, maxX, &edges[edgeCount], clipBound) {
- edges[edgeCount].Winding *= swapWinding
- edgeCount++
- }
- }
-
- if clipSum&POLYGON_CLIP_LEFT != 0 {
- // calculate new position for the left vertex
- oldY = yleft
- minX = clipBound[0]
-
- yleft = yleft + (minX-xleft)*slope
- xleft = minX
-
- // add vertical edge for the overflowing part
- if getVerticalEdge(oldY, yleft, minX, &edges[edgeCount], clipBound) {
- edges[edgeCount].Winding *= swapWinding
- edgeCount++
- }
- }
-
- if getEdge(xleft, yleft, xright, yright, &edges[edgeCount], clipBound) {
- edges[edgeCount].Winding *= swapWinding
- edgeCount++
- }
- }
- }
-
- prevClipFlags = clipFlags
- prevX = x
- prevY = y
- }
-
- return edgeCount
-}
-
-//! Creates a polygon edge between two vectors.
-/*! Clips the edge vertically to the clip rectangle. Returns true for edges that
- * should be rendered, false for others.
- */
-func getEdge(x0, y0, x1, y1 float64, edge *PolygonEdge, clipBound [4]float64) bool {
- var startX, startY, endX, endY float64
- var winding int16
-
- if y0 <= y1 {
- startX = x0
- startY = y0
- endX = x1
- endY = y1
- winding = 1
- } else {
- startX = x1
- startY = y1
- endX = x0
- endY = y0
- winding = -1
- }
-
- // Essentially, firstLine is floor(startY + 1) and lastLine is floor(endY).
- // These are refactored to integer casts in order to avoid function
- // calls. The difference with integer cast is that numbers are always
- // rounded towards zero. Since values smaller than zero get clipped away,
- // only coordinates between 0 and -1 require greater attention as they
- // also round to zero. The problems in this range can be avoided by
- // adding one to the values before conversion and subtracting after it.
-
- firstLine := int(startY + 1)
- lastLine := int(endY+1) - 1
-
- minClip := int(clipBound[1])
- maxClip := int(clipBound[3])
-
- // If start and end are on the same line, the edge doesn't cross
- // any lines and thus can be ignored.
- // If the end is smaller than the first line, edge is out.
- // If the start is larger than the last line, edge is out.
- if firstLine > lastLine || lastLine < minClip || firstLine >= maxClip {
- return false
- }
-
- // Adjust the start based on the target.
- if firstLine < minClip {
- firstLine = minClip
- }
-
- if lastLine >= maxClip {
- lastLine = maxClip - 1
- }
- edge.Slope = (endX - startX) / (endY - startY)
- edge.X = startX + (float64(firstLine)-startY)*edge.Slope
- edge.Winding = winding
- edge.FirstLine = firstLine
- edge.LastLine = lastLine
-
- return true
-}
-
-//! Creates a vertical polygon edge between two y values.
-/*! Clips the edge vertically to the clip rectangle. Returns true for edges that
- * should be rendered, false for others.
- */
-func getVerticalEdge(startY, endY, x float64, edge *PolygonEdge, clipBound [4]float64) bool {
- var start, end float64
- var winding int16
- if startY < endY {
- start = startY
- end = endY
- winding = 1
- } else {
- start = endY
- end = startY
- winding = -1
- }
-
- firstLine := int(start + 1)
- lastLine := int(end+1) - 1
-
- minClip := int(clipBound[1])
- maxClip := int(clipBound[3])
-
- // If start and end are on the same line, the edge doesn't cross
- // any lines and thus can be ignored.
- // If the end is smaller than the first line, edge is out.
- // If the start is larger than the last line, edge is out.
- if firstLine > lastLine || lastLine < minClip || firstLine >= maxClip {
- return false
- }
-
- // Adjust the start based on the clip rect.
- if firstLine < minClip {
- firstLine = minClip
- }
- if lastLine >= maxClip {
- lastLine = maxClip - 1
- }
-
- edge.Slope = 0
- edge.X = x
- edge.Winding = winding
- edge.FirstLine = firstLine
- edge.LastLine = lastLine
-
- return true
-}
-
-type VertexData struct {
- X, Y float64
- ClipFlags int
- Line int
-}
-
-//! Calculates the edges of the polygon with transformation and clipping to edges array.
-/*! Note that this may return upto three times the amount of edges that the polygon has vertices,
- * in the unlucky case where both left and right side get clipped for all edges.
- * \param edges the array for result edges. This should be able to contain 2*aVertexCount edges.
- * \param aTransformation the transformation matrix for the polygon.
- * \param aClipRectangle the clip rectangle.
- * \return the amount of edges in the result.
- */
-func (p Polygon) getScanEdges(edges []PolygonScanEdge, tr [6]float64, clipBound [4]float64) int {
- var n int
- vertexData := make([]VertexData, len(p)/2+1)
- for n = 0; n < len(vertexData)-1; n = n + 1 {
- k := n * 2
- vertexData[n].X = p[k]*tr[0] + p[k+1]*tr[2] + tr[4]
- vertexData[n].Y = p[k]*tr[1] + p[k+1]*tr[3] + tr[5]
- // Calculate clip flags for all vertices.
- vertexData[n].ClipFlags = POLYGON_CLIP_NONE
- if vertexData[n].X < clipBound[0] {
- vertexData[n].ClipFlags |= POLYGON_CLIP_LEFT
- } else if vertexData[n].X >= clipBound[2] {
- vertexData[n].ClipFlags |= POLYGON_CLIP_RIGHT
- }
- if vertexData[n].Y < clipBound[1] {
- vertexData[n].ClipFlags |= POLYGON_CLIP_TOP
- } else if vertexData[n].Y >= clipBound[3] {
- vertexData[n].ClipFlags |= POLYGON_CLIP_BOTTOM
- }
-
- // Calculate line of the vertex. If the vertex is clipped by top or bottom, the line
- // is determined by the clip rectangle.
- if vertexData[n].ClipFlags&POLYGON_CLIP_TOP != 0 {
- vertexData[n].Line = int(clipBound[1])
- } else if vertexData[n].ClipFlags&POLYGON_CLIP_BOTTOM != 0 {
- vertexData[n].Line = int(clipBound[3] - 1)
- } else {
- vertexData[n].Line = int(vertexData[n].Y+1) - 1
- }
- }
-
- // Copy the data from 0 to the last entry to make the data to loop.
- vertexData[len(vertexData)-1] = vertexData[0]
-
- // Transform the first vertex; store.
- // Process mVertexCount - 1 times, next is n+1
- // copy the first vertex to
- // Process 1 time, next is n
-
- edgeCount := 0
- for n = 0; n < len(vertexData)-1; n++ {
- clipSum := vertexData[n].ClipFlags | vertexData[n+1].ClipFlags
- clipUnion := vertexData[n].ClipFlags & vertexData[n+1].ClipFlags
-
- if clipUnion&(POLYGON_CLIP_TOP|POLYGON_CLIP_BOTTOM) == 0 &&
- vertexData[n].Line != vertexData[n+1].Line {
- var startIndex, endIndex int
- var winding int16
- if vertexData[n].Y < vertexData[n+1].Y {
- startIndex = n
- endIndex = n + 1
- winding = 1
- } else {
- startIndex = n + 1
- endIndex = n
- winding = -1
- }
-
- firstLine := vertexData[startIndex].Line + 1
- lastLine := vertexData[endIndex].Line
-
- if clipUnion&POLYGON_CLIP_RIGHT != 0 {
- // Both clip to right, edge is a vertical line on the right side
- edges[edgeCount].FirstLine = firstLine
- edges[edgeCount].LastLine = lastLine
- edges[edgeCount].Winding = winding
- edges[edgeCount].X = Fix(clipBound[2] * FIXED_FLOAT_COEF)
- edges[edgeCount].Slope = 0
- edges[edgeCount].SlopeFix = 0
-
- edgeCount++
- } else if clipUnion&POLYGON_CLIP_LEFT != 0 {
- // Both clip to left, edge is a vertical line on the left side
- edges[edgeCount].FirstLine = firstLine
- edges[edgeCount].LastLine = lastLine
- edges[edgeCount].Winding = winding
- edges[edgeCount].X = Fix(clipBound[0] * FIXED_FLOAT_COEF)
- edges[edgeCount].Slope = 0
- edges[edgeCount].SlopeFix = 0
-
- edgeCount++
- } else if clipSum&(POLYGON_CLIP_RIGHT|POLYGON_CLIP_LEFT) == 0 {
- // No clipping in the horizontal direction
- slope := (vertexData[endIndex].X -
- vertexData[startIndex].X) /
- (vertexData[endIndex].Y -
- vertexData[startIndex].Y)
-
- // If there is vertical clip (for the top) it will be processed here. The calculation
- // should be done for all non-clipping edges as well to determine the accurate position
- // where the edge crosses the first scanline.
- startx := vertexData[startIndex].X +
- (float64(firstLine)-vertexData[startIndex].Y)*slope
-
- edges[edgeCount].FirstLine = firstLine
- edges[edgeCount].LastLine = lastLine
- edges[edgeCount].Winding = winding
- edges[edgeCount].X = Fix(startx * FIXED_FLOAT_COEF)
- edges[edgeCount].Slope = Fix(slope * FIXED_FLOAT_COEF)
-
- if lastLine-firstLine >= SLOPE_FIX_STEP {
- edges[edgeCount].SlopeFix = Fix(slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) -
- edges[edgeCount].Slope<<SLOPE_FIX_SHIFT
- } else {
- edges[edgeCount].SlopeFix = 0
- }
-
- edgeCount++
- } else {
- // Clips to left or right or both.
- slope := (vertexData[endIndex].X -
- vertexData[startIndex].X) /
- (vertexData[endIndex].Y -
- vertexData[startIndex].Y)
-
- // The edge may clip to both left and right.
- // The clip results in one or two new vertices, and one to three segments.
- // The rounding for scanlines may produce a result where any of the segments is
- // ignored.
-
- // The start is always above the end. Calculate the clip positions to clipVertices.
- // It is possible that only one of the vertices exist. This will be detected from the
- // clip flags of the vertex later, so they are initialized here.
- var clipVertices [2]VertexData
-
- if vertexData[startIndex].X <
- vertexData[endIndex].X {
- clipVertices[0].X = clipBound[0]
- clipVertices[1].X = clipBound[2]
- clipVertices[0].ClipFlags = POLYGON_CLIP_LEFT
- clipVertices[1].ClipFlags = POLYGON_CLIP_RIGHT
- } else {
- clipVertices[0].X = clipBound[2]
- clipVertices[1].X = clipBound[0]
- clipVertices[0].ClipFlags = POLYGON_CLIP_RIGHT
- clipVertices[1].ClipFlags = POLYGON_CLIP_LEFT
- }
-
- var p int
- for p = 0; p < 2; p++ {
- // Check if either of the vertices crosses the edge marked for the clip vertex
- if clipSum&clipVertices[p].ClipFlags != 0 {
- // The the vertex is required, calculate it.
- clipVertices[p].Y = vertexData[startIndex].Y +
- (clipVertices[p].X-
- vertexData[startIndex].X)/slope
-
- // If there is clipping in the vertical direction, the new vertex may be clipped.
- if clipSum&(POLYGON_CLIP_TOP|POLYGON_CLIP_BOTTOM) != 0 {
- if clipVertices[p].Y < clipBound[1] {
- clipVertices[p].ClipFlags = POLYGON_CLIP_TOP
- clipVertices[p].Line = int(clipBound[1])
- } else if clipVertices[p].Y > clipBound[3] {
- clipVertices[p].ClipFlags = POLYGON_CLIP_BOTTOM
- clipVertices[p].Line = int(clipBound[3] - 1)
- } else {
- clipVertices[p].ClipFlags = 0
- clipVertices[p].Line = int(clipVertices[p].Y+1) - 1
- }
- } else {
- clipVertices[p].ClipFlags = 0
- clipVertices[p].Line = int(clipVertices[p].Y+1) - 1
- }
- }
- }
-
- // Now there are three or four vertices, in the top-to-bottom order of start, clip0, clip1,
- // end. What kind of edges are required for connecting these can be determined from the
- // clip flags.
- // -if clip vertex has horizontal clip flags, it doesn't exist. No edge is generated.
- // -if start vertex or end vertex has horizontal clip flag, the edge to/from the clip vertex is vertical
- // -if the line of two vertices is the same, the edge is not generated, since the edge doesn't
- // cross any scanlines.
-
- // The alternative patterns are:
- // start - clip0 - clip1 - end
- // start - clip0 - end
- // start - clip1 - end
-
- var topClipIndex, bottomClipIndex int
- if (clipVertices[0].ClipFlags|clipVertices[1].ClipFlags)&
- (POLYGON_CLIP_LEFT|POLYGON_CLIP_RIGHT) == 0 {
- // Both sides are clipped, the order is start-clip0-clip1-end
- topClipIndex = 0
- bottomClipIndex = 1
-
- // Add the edge from clip0 to clip1
- // Check that the line is different for the vertices.
- if clipVertices[0].Line != clipVertices[1].Line {
- firstClipLine := clipVertices[0].Line + 1
-
- startx := vertexData[startIndex].X +
- (float64(firstClipLine)-vertexData[startIndex].Y)*slope
-
- edges[edgeCount].X = Fix(startx * FIXED_FLOAT_COEF)
- edges[edgeCount].Slope = Fix(slope * FIXED_FLOAT_COEF)
- edges[edgeCount].FirstLine = firstClipLine
- edges[edgeCount].LastLine = clipVertices[1].Line
- edges[edgeCount].Winding = winding
-
- if edges[edgeCount].LastLine-edges[edgeCount].FirstLine >= SLOPE_FIX_STEP {
- edges[edgeCount].SlopeFix = Fix(slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) -
- edges[edgeCount].Slope<<SLOPE_FIX_SHIFT
- } else {
- edges[edgeCount].SlopeFix = 0
- }
-
- edgeCount++
- }
- } else {
- // Clip at either side, check which side. The clip flag is on for the vertex
- // that doesn't exist, i.e. has not been clipped to be inside the rect.
- if clipVertices[0].ClipFlags&(POLYGON_CLIP_LEFT|POLYGON_CLIP_RIGHT) != 0 {
- topClipIndex = 1
- bottomClipIndex = 1
- } else {
- topClipIndex = 0
- bottomClipIndex = 0
- }
- }
-
- // Generate the edges from start - clip top and clip bottom - end
- // Clip top and clip bottom may be the same vertex if there is only one
- // clipped vertex.
-
- // Check that the line is different for the vertices.
- if vertexData[startIndex].Line != clipVertices[topClipIndex].Line {
- edges[edgeCount].FirstLine = firstLine
- edges[edgeCount].LastLine = clipVertices[topClipIndex].Line
- edges[edgeCount].Winding = winding
-
- // If startIndex is clipped, the edge is a vertical one.
- if vertexData[startIndex].ClipFlags&(POLYGON_CLIP_LEFT|POLYGON_CLIP_RIGHT) != 0 {
- edges[edgeCount].X = Fix(clipVertices[topClipIndex].X * FIXED_FLOAT_COEF)
- edges[edgeCount].Slope = 0
- edges[edgeCount].SlopeFix = 0
- } else {
- startx := vertexData[startIndex].X +
- (float64(firstLine)-vertexData[startIndex].Y)*slope
-
- edges[edgeCount].X = Fix(startx * FIXED_FLOAT_COEF)
- edges[edgeCount].Slope = Fix(slope * FIXED_FLOAT_COEF)
-
- if edges[edgeCount].LastLine-edges[edgeCount].FirstLine >= SLOPE_FIX_STEP {
- edges[edgeCount].SlopeFix = Fix(slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) -
- edges[edgeCount].Slope<<SLOPE_FIX_SHIFT
- } else {
- edges[edgeCount].SlopeFix = 0
- }
- }
-
- edgeCount++
- }
-
- // Check that the line is different for the vertices.
- if clipVertices[bottomClipIndex].Line != vertexData[endIndex].Line {
- firstClipLine := clipVertices[bottomClipIndex].Line + 1
-
- edges[edgeCount].FirstLine = firstClipLine
- edges[edgeCount].LastLine = lastLine
- edges[edgeCount].Winding = winding
-
- // If endIndex is clipped, the edge is a vertical one.
- if vertexData[endIndex].ClipFlags&(POLYGON_CLIP_LEFT|POLYGON_CLIP_RIGHT) != 0 {
- edges[edgeCount].X = Fix(clipVertices[bottomClipIndex].X * FIXED_FLOAT_COEF)
- edges[edgeCount].Slope = 0
- edges[edgeCount].SlopeFix = 0
- } else {
- startx := vertexData[startIndex].X +
- (float64(firstClipLine)-vertexData[startIndex].Y)*slope
-
- edges[edgeCount].X = Fix(startx * FIXED_FLOAT_COEF)
- edges[edgeCount].Slope = Fix(slope * FIXED_FLOAT_COEF)
-
- if edges[edgeCount].LastLine-edges[edgeCount].FirstLine >= SLOPE_FIX_STEP {
- edges[edgeCount].SlopeFix = Fix(slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) -
- edges[edgeCount].Slope<<SLOPE_FIX_SHIFT
- } else {
- edges[edgeCount].SlopeFix = 0
- }
- }
-
- edgeCount++
- }
-
- }
- }
- }
-
- return edgeCount
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/raster_test.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/raster_test.go
deleted file mode 100644
index 7872d8d03..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/raster_test.go
+++ /dev/null
@@ -1,200 +0,0 @@
-package raster
-
-import (
- "bufio"
- "code.google.com/p/draw2d/draw2d/curve"
- "code.google.com/p/freetype-go/freetype/raster"
- "image"
- "image/color"
- "image/png"
- "log"
- "os"
- "testing"
-)
-
-var flattening_threshold float64 = 0.5
-
-func savepng(filePath string, m image.Image) {
- f, err := os.Create(filePath)
- if err != nil {
- log.Println(err)
- os.Exit(1)
- }
- defer f.Close()
- b := bufio.NewWriter(f)
- err = png.Encode(b, m)
- if err != nil {
- log.Println(err)
- os.Exit(1)
- }
- err = b.Flush()
- if err != nil {
- log.Println(err)
- os.Exit(1)
- }
-}
-
-type Path struct {
- points []float64
-}
-
-func (p *Path) LineTo(x, y float64) {
- if len(p.points)+2 > cap(p.points) {
- points := make([]float64, len(p.points)+2, len(p.points)+32)
- copy(points, p.points)
- p.points = points
- } else {
- p.points = p.points[0 : len(p.points)+2]
- }
- p.points[len(p.points)-2] = x
- p.points[len(p.points)-1] = y
-}
-
-func TestFreetype(t *testing.T) {
- var p Path
- p.LineTo(10, 190)
- c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
- c.Segment(&p, flattening_threshold)
- poly := Polygon(p.points)
- color := color.RGBA{0, 0, 0, 0xff}
-
- img := image.NewRGBA(image.Rect(0, 0, 200, 200))
- rasterizer := raster.NewRasterizer(200, 200)
- rasterizer.UseNonZeroWinding = false
- rasterizer.Start(raster.Point{raster.Fix32(10 * 256), raster.Fix32(190 * 256)})
- for j := 0; j < len(poly); j = j + 2 {
- rasterizer.Add1(raster.Point{raster.Fix32(poly[j] * 256), raster.Fix32(poly[j+1] * 256)})
- }
- painter := raster.NewRGBAPainter(img)
- painter.SetColor(color)
- rasterizer.Rasterize(painter)
-
- savepng("_testFreetype.png", img)
-}
-
-func TestFreetypeNonZeroWinding(t *testing.T) {
- var p Path
- p.LineTo(10, 190)
- c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
- c.Segment(&p, flattening_threshold)
- poly := Polygon(p.points)
- color := color.RGBA{0, 0, 0, 0xff}
-
- img := image.NewRGBA(image.Rect(0, 0, 200, 200))
- rasterizer := raster.NewRasterizer(200, 200)
- rasterizer.UseNonZeroWinding = true
- rasterizer.Start(raster.Point{raster.Fix32(10 * 256), raster.Fix32(190 * 256)})
- for j := 0; j < len(poly); j = j + 2 {
- rasterizer.Add1(raster.Point{raster.Fix32(poly[j] * 256), raster.Fix32(poly[j+1] * 256)})
- }
- painter := raster.NewRGBAPainter(img)
- painter.SetColor(color)
- rasterizer.Rasterize(painter)
-
- savepng("_testFreetypeNonZeroWinding.png", img)
-}
-
-func TestRasterizer(t *testing.T) {
- img := image.NewRGBA(image.Rect(0, 0, 200, 200))
- var p Path
- p.LineTo(10, 190)
- c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
- c.Segment(&p, flattening_threshold)
- poly := Polygon(p.points)
- color := color.RGBA{0, 0, 0, 0xff}
- tr := [6]float64{1, 0, 0, 1, 0, 0}
- r := NewRasterizer8BitsSample(200, 200)
- //PolylineBresenham(img, image.Black, poly...)
-
- r.RenderEvenOdd(img, &color, &poly, tr)
- savepng("_testRasterizer.png", img)
-}
-
-func TestRasterizerNonZeroWinding(t *testing.T) {
- img := image.NewRGBA(image.Rect(0, 0, 200, 200))
- var p Path
- p.LineTo(10, 190)
- c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
- c.Segment(&p, flattening_threshold)
- poly := Polygon(p.points)
- color := color.RGBA{0, 0, 0, 0xff}
- tr := [6]float64{1, 0, 0, 1, 0, 0}
- r := NewRasterizer8BitsSample(200, 200)
- //PolylineBresenham(img, image.Black, poly...)
-
- r.RenderNonZeroWinding(img, &color, &poly, tr)
- savepng("_testRasterizerNonZeroWinding.png", img)
-}
-
-func BenchmarkFreetype(b *testing.B) {
- var p Path
- p.LineTo(10, 190)
- c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
- c.Segment(&p, flattening_threshold)
- poly := Polygon(p.points)
- color := color.RGBA{0, 0, 0, 0xff}
-
- for i := 0; i < b.N; i++ {
- img := image.NewRGBA(image.Rect(0, 0, 200, 200))
- rasterizer := raster.NewRasterizer(200, 200)
- rasterizer.UseNonZeroWinding = false
- rasterizer.Start(raster.Point{raster.Fix32(10 * 256), raster.Fix32(190 * 256)})
- for j := 0; j < len(poly); j = j + 2 {
- rasterizer.Add1(raster.Point{raster.Fix32(poly[j] * 256), raster.Fix32(poly[j+1] * 256)})
- }
- painter := raster.NewRGBAPainter(img)
- painter.SetColor(color)
- rasterizer.Rasterize(painter)
- }
-}
-func BenchmarkFreetypeNonZeroWinding(b *testing.B) {
- var p Path
- p.LineTo(10, 190)
- c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
- c.Segment(&p, flattening_threshold)
- poly := Polygon(p.points)
- color := color.RGBA{0, 0, 0, 0xff}
-
- for i := 0; i < b.N; i++ {
- img := image.NewRGBA(image.Rect(0, 0, 200, 200))
- rasterizer := raster.NewRasterizer(200, 200)
- rasterizer.UseNonZeroWinding = true
- rasterizer.Start(raster.Point{raster.Fix32(10 * 256), raster.Fix32(190 * 256)})
- for j := 0; j < len(poly); j = j + 2 {
- rasterizer.Add1(raster.Point{raster.Fix32(poly[j] * 256), raster.Fix32(poly[j+1] * 256)})
- }
- painter := raster.NewRGBAPainter(img)
- painter.SetColor(color)
- rasterizer.Rasterize(painter)
- }
-}
-
-func BenchmarkRasterizerNonZeroWinding(b *testing.B) {
- var p Path
- p.LineTo(10, 190)
- c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
- c.Segment(&p, flattening_threshold)
- poly := Polygon(p.points)
- color := color.RGBA{0, 0, 0, 0xff}
- tr := [6]float64{1, 0, 0, 1, 0, 0}
- for i := 0; i < b.N; i++ {
- img := image.NewRGBA(image.Rect(0, 0, 200, 200))
- rasterizer := NewRasterizer8BitsSample(200, 200)
- rasterizer.RenderNonZeroWinding(img, &color, &poly, tr)
- }
-}
-
-func BenchmarkRasterizer(b *testing.B) {
- var p Path
- p.LineTo(10, 190)
- c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
- c.Segment(&p, flattening_threshold)
- poly := Polygon(p.points)
- color := color.RGBA{0, 0, 0, 0xff}
- tr := [6]float64{1, 0, 0, 1, 0, 0}
- for i := 0; i < b.N; i++ {
- img := image.NewRGBA(image.Rect(0, 0, 200, 200))
- rasterizer := NewRasterizer8BitsSample(200, 200)
- rasterizer.RenderEvenOdd(img, &color, &poly, tr)
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/rgba_interpolation.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/rgba_interpolation.go
deleted file mode 100644
index 92534e7eb..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/rgba_interpolation.go
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 21/11/2010 by Laurent Le Goff
-// see http://pippin.gimp.org/image_processing/chap_resampling.html
-
-package draw2d
-
-import (
- "image"
- "image/color"
- "image/draw"
- "math"
-)
-
-type ImageFilter int
-
-const (
- LinearFilter ImageFilter = iota
- BilinearFilter
- BicubicFilter
-)
-
-//see http://pippin.gimp.org/image_processing/chap_resampling.html
-func getColorLinear(img image.Image, x, y float64) color.Color {
- return img.At(int(x), int(y))
-}
-
-func getColorBilinear(img image.Image, x, y float64) color.Color {
- x0 := math.Floor(x)
- y0 := math.Floor(y)
- dx := x - x0
- dy := y - y0
-
- rt, gt, bt, at := img.At(int(x0), int(y0)).RGBA()
- r0, g0, b0, a0 := float64(rt), float64(gt), float64(bt), float64(at)
- rt, gt, bt, at = img.At(int(x0+1), int(y0)).RGBA()
- r1, g1, b1, a1 := float64(rt), float64(gt), float64(bt), float64(at)
- rt, gt, bt, at = img.At(int(x0+1), int(y0+1)).RGBA()
- r2, g2, b2, a2 := float64(rt), float64(gt), float64(bt), float64(at)
- rt, gt, bt, at = img.At(int(x0), int(y0+1)).RGBA()
- r3, g3, b3, a3 := float64(rt), float64(gt), float64(bt), float64(at)
-
- r := int(lerp(lerp(r0, r1, dx), lerp(r3, r2, dx), dy))
- g := int(lerp(lerp(g0, g1, dx), lerp(g3, g2, dx), dy))
- b := int(lerp(lerp(b0, b1, dx), lerp(b3, b2, dx), dy))
- a := int(lerp(lerp(a0, a1, dx), lerp(a3, a2, dx), dy))
- return color.RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)}
-}
-
-/**
--- LERP
--- /lerp/, vi.,n.
---
--- Quasi-acronym for Linear Interpolation, used as a verb or noun for
--- the operation. "Bresenham's algorithm lerps incrementally between the
--- two endpoints of the line." (From Jargon File (4.4.4, 14 Aug 2003)
-*/
-func lerp(v1, v2, ratio float64) float64 {
- return v1*(1-ratio) + v2*ratio
-}
-
-func getColorCubicRow(img image.Image, x, y, offset float64) color.Color {
- c0 := img.At(int(x), int(y))
- c1 := img.At(int(x+1), int(y))
- c2 := img.At(int(x+2), int(y))
- c3 := img.At(int(x+3), int(y))
- rt, gt, bt, at := c0.RGBA()
- r0, g0, b0, a0 := float64(rt), float64(gt), float64(bt), float64(at)
- rt, gt, bt, at = c1.RGBA()
- r1, g1, b1, a1 := float64(rt), float64(gt), float64(bt), float64(at)
- rt, gt, bt, at = c2.RGBA()
- r2, g2, b2, a2 := float64(rt), float64(gt), float64(bt), float64(at)
- rt, gt, bt, at = c3.RGBA()
- r3, g3, b3, a3 := float64(rt), float64(gt), float64(bt), float64(at)
- r, g, b, a := cubic(offset, r0, r1, r2, r3), cubic(offset, g0, g1, g2, g3), cubic(offset, b0, b1, b2, b3), cubic(offset, a0, a1, a2, a3)
- return color.RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)}
-}
-
-func getColorBicubic(img image.Image, x, y float64) color.Color {
- x0 := math.Floor(x)
- y0 := math.Floor(y)
- dx := x - x0
- dy := y - y0
- c0 := getColorCubicRow(img, x0-1, y0-1, dx)
- c1 := getColorCubicRow(img, x0-1, y0, dx)
- c2 := getColorCubicRow(img, x0-1, y0+1, dx)
- c3 := getColorCubicRow(img, x0-1, y0+2, dx)
- rt, gt, bt, at := c0.RGBA()
- r0, g0, b0, a0 := float64(rt), float64(gt), float64(bt), float64(at)
- rt, gt, bt, at = c1.RGBA()
- r1, g1, b1, a1 := float64(rt), float64(gt), float64(bt), float64(at)
- rt, gt, bt, at = c2.RGBA()
- r2, g2, b2, a2 := float64(rt), float64(gt), float64(bt), float64(at)
- rt, gt, bt, at = c3.RGBA()
- r3, g3, b3, a3 := float64(rt), float64(gt), float64(bt), float64(at)
- r, g, b, a := cubic(dy, r0, r1, r2, r3), cubic(dy, g0, g1, g2, g3), cubic(dy, b0, b1, b2, b3), cubic(dy, a0, a1, a2, a3)
- return color.RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)}
-}
-
-func cubic(offset, v0, v1, v2, v3 float64) uint32 {
- // offset is the offset of the sampled value between v1 and v2
- return uint32(((((-7*v0+21*v1-21*v2+7*v3)*offset+
- (15*v0-36*v1+27*v2-6*v3))*offset+
- (-9*v0+9*v2))*offset + (v0 + 16*v1 + v2)) / 18.0)
-}
-
-func DrawImage(src image.Image, dest draw.Image, tr MatrixTransform, op draw.Op, filter ImageFilter) {
- bounds := src.Bounds()
- x0, y0, x1, y1 := float64(bounds.Min.X), float64(bounds.Min.Y), float64(bounds.Max.X), float64(bounds.Max.Y)
- tr.TransformRectangle(&x0, &y0, &x1, &y1)
- var x, y, u, v float64
- var c1, c2, cr color.Color
- var r, g, b, a, ia, r1, g1, b1, a1, r2, g2, b2, a2 uint32
- var color color.RGBA
- for x = x0; x < x1; x++ {
- for y = y0; y < y1; y++ {
- u = x
- v = y
- tr.InverseTransform(&u, &v)
- if bounds.Min.X <= int(u) && bounds.Max.X > int(u) && bounds.Min.Y <= int(v) && bounds.Max.Y > int(v) {
- c1 = dest.At(int(x), int(y))
- switch filter {
- case LinearFilter:
- c2 = src.At(int(u), int(v))
- case BilinearFilter:
- c2 = getColorBilinear(src, u, v)
- case BicubicFilter:
- c2 = getColorBicubic(src, u, v)
- }
- switch op {
- case draw.Over:
- r1, g1, b1, a1 = c1.RGBA()
- r2, g2, b2, a2 = c2.RGBA()
- ia = M - a2
- r = ((r1 * ia) / M) + r2
- g = ((g1 * ia) / M) + g2
- b = ((b1 * ia) / M) + b2
- a = ((a1 * ia) / M) + a2
- color.R = uint8(r >> 8)
- color.G = uint8(g >> 8)
- color.B = uint8(b >> 8)
- color.A = uint8(a >> 8)
- cr = color
- default:
- cr = c2
- }
- dest.Set(int(x), int(y), cr)
- }
- }
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/stack_gc.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/stack_gc.go
deleted file mode 100644
index b2cf63fc4..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/stack_gc.go
+++ /dev/null
@@ -1,208 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 21/11/2010 by Laurent Le Goff
-
-package draw2d
-
-import (
- "code.google.com/p/freetype-go/freetype/truetype"
- "image"
- "image/color"
-)
-
-type StackGraphicContext struct {
- Current *ContextStack
-}
-
-type ContextStack struct {
- Tr MatrixTransform
- Path *PathStorage
- LineWidth float64
- Dash []float64
- DashOffset float64
- StrokeColor color.Color
- FillColor color.Color
- FillRule FillRule
- Cap Cap
- Join Join
- FontSize float64
- FontData FontData
-
- font *truetype.Font
- // fontSize and dpi are used to calculate scale. scale is the number of
- // 26.6 fixed point units in 1 em.
- scale int32
-
- previous *ContextStack
-}
-
-/**
- * Create a new Graphic context from an image
- */
-func NewStackGraphicContext() *StackGraphicContext {
- gc := &StackGraphicContext{}
- gc.Current = new(ContextStack)
- gc.Current.Tr = NewIdentityMatrix()
- gc.Current.Path = NewPathStorage()
- gc.Current.LineWidth = 1.0
- gc.Current.StrokeColor = image.Black
- gc.Current.FillColor = image.White
- gc.Current.Cap = RoundCap
- gc.Current.FillRule = FillRuleEvenOdd
- gc.Current.Join = RoundJoin
- gc.Current.FontSize = 10
- gc.Current.FontData = defaultFontData
- return gc
-}
-
-func (gc *StackGraphicContext) GetMatrixTransform() MatrixTransform {
- return gc.Current.Tr
-}
-
-func (gc *StackGraphicContext) SetMatrixTransform(Tr MatrixTransform) {
- gc.Current.Tr = Tr
-}
-
-func (gc *StackGraphicContext) ComposeMatrixTransform(Tr MatrixTransform) {
- gc.Current.Tr = Tr.Multiply(gc.Current.Tr)
-}
-
-func (gc *StackGraphicContext) Rotate(angle float64) {
- gc.Current.Tr = NewRotationMatrix(angle).Multiply(gc.Current.Tr)
-}
-
-func (gc *StackGraphicContext) Translate(tx, ty float64) {
- gc.Current.Tr = NewTranslationMatrix(tx, ty).Multiply(gc.Current.Tr)
-}
-
-func (gc *StackGraphicContext) Scale(sx, sy float64) {
- gc.Current.Tr = NewScaleMatrix(sx, sy).Multiply(gc.Current.Tr)
-}
-
-func (gc *StackGraphicContext) SetStrokeColor(c color.Color) {
- gc.Current.StrokeColor = c
-}
-
-func (gc *StackGraphicContext) SetFillColor(c color.Color) {
- gc.Current.FillColor = c
-}
-
-func (gc *StackGraphicContext) SetFillRule(f FillRule) {
- gc.Current.FillRule = f
-}
-
-func (gc *StackGraphicContext) SetLineWidth(LineWidth float64) {
- gc.Current.LineWidth = LineWidth
-}
-
-func (gc *StackGraphicContext) SetLineCap(Cap Cap) {
- gc.Current.Cap = Cap
-}
-
-func (gc *StackGraphicContext) SetLineJoin(Join Join) {
- gc.Current.Join = Join
-}
-
-func (gc *StackGraphicContext) SetLineDash(Dash []float64, DashOffset float64) {
- gc.Current.Dash = Dash
- gc.Current.DashOffset = DashOffset
-}
-
-func (gc *StackGraphicContext) SetFontSize(FontSize float64) {
- gc.Current.FontSize = FontSize
-}
-
-func (gc *StackGraphicContext) GetFontSize() float64 {
- return gc.Current.FontSize
-}
-
-func (gc *StackGraphicContext) SetFontData(FontData FontData) {
- gc.Current.FontData = FontData
-}
-
-func (gc *StackGraphicContext) GetFontData() FontData {
- return gc.Current.FontData
-}
-
-func (gc *StackGraphicContext) BeginPath() {
- gc.Current.Path.Clear()
-}
-
-func (gc *StackGraphicContext) IsEmpty() bool {
- return gc.Current.Path.IsEmpty()
-}
-
-func (gc *StackGraphicContext) LastPoint() (float64, float64) {
- return gc.Current.Path.LastPoint()
-}
-
-func (gc *StackGraphicContext) MoveTo(x, y float64) {
- gc.Current.Path.MoveTo(x, y)
-}
-
-func (gc *StackGraphicContext) RMoveTo(dx, dy float64) {
- gc.Current.Path.RMoveTo(dx, dy)
-}
-
-func (gc *StackGraphicContext) LineTo(x, y float64) {
- gc.Current.Path.LineTo(x, y)
-}
-
-func (gc *StackGraphicContext) RLineTo(dx, dy float64) {
- gc.Current.Path.RLineTo(dx, dy)
-}
-
-func (gc *StackGraphicContext) QuadCurveTo(cx, cy, x, y float64) {
- gc.Current.Path.QuadCurveTo(cx, cy, x, y)
-}
-
-func (gc *StackGraphicContext) RQuadCurveTo(dcx, dcy, dx, dy float64) {
- gc.Current.Path.RQuadCurveTo(dcx, dcy, dx, dy)
-}
-
-func (gc *StackGraphicContext) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) {
- gc.Current.Path.CubicCurveTo(cx1, cy1, cx2, cy2, x, y)
-}
-
-func (gc *StackGraphicContext) RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float64) {
- gc.Current.Path.RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy)
-}
-
-func (gc *StackGraphicContext) ArcTo(cx, cy, rx, ry, startAngle, angle float64) {
- gc.Current.Path.ArcTo(cx, cy, rx, ry, startAngle, angle)
-}
-
-func (gc *StackGraphicContext) RArcTo(dcx, dcy, rx, ry, startAngle, angle float64) {
- gc.Current.Path.RArcTo(dcx, dcy, rx, ry, startAngle, angle)
-}
-
-func (gc *StackGraphicContext) Close() {
- gc.Current.Path.Close()
-}
-
-func (gc *StackGraphicContext) Save() {
- context := new(ContextStack)
- context.FontSize = gc.Current.FontSize
- context.FontData = gc.Current.FontData
- context.LineWidth = gc.Current.LineWidth
- context.StrokeColor = gc.Current.StrokeColor
- context.FillColor = gc.Current.FillColor
- context.FillRule = gc.Current.FillRule
- context.Dash = gc.Current.Dash
- context.DashOffset = gc.Current.DashOffset
- context.Cap = gc.Current.Cap
- context.Join = gc.Current.Join
- context.Path = gc.Current.Path.Copy()
- context.font = gc.Current.font
- context.scale = gc.Current.scale
- copy(context.Tr[:], gc.Current.Tr[:])
- context.previous = gc.Current
- gc.Current = context
-}
-
-func (gc *StackGraphicContext) Restore() {
- if gc.Current.previous != nil {
- oldContext := gc.Current
- gc.Current = gc.Current.previous
- oldContext.previous = nil
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/stroker.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/stroker.go
deleted file mode 100644
index 9331187f6..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/stroker.go
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 13/12/2010 by Laurent Le Goff
-
-package draw2d
-
-type Cap int
-
-const (
- RoundCap Cap = iota
- ButtCap
- SquareCap
-)
-
-type Join int
-
-const (
- BevelJoin Join = iota
- RoundJoin
- MiterJoin
-)
-
-type LineStroker struct {
- Next VertexConverter
- HalfLineWidth float64
- Cap Cap
- Join Join
- vertices []float64
- rewind []float64
- x, y, nx, ny float64
- command VertexCommand
-}
-
-func NewLineStroker(c Cap, j Join, converter VertexConverter) *LineStroker {
- l := new(LineStroker)
- l.Next = converter
- l.HalfLineWidth = 0.5
- l.vertices = make([]float64, 0, 256)
- l.rewind = make([]float64, 0, 256)
- l.Cap = c
- l.Join = j
- l.command = VertexNoCommand
- return l
-}
-
-func (l *LineStroker) NextCommand(command VertexCommand) {
- l.command = command
- if command == VertexStopCommand {
- l.Next.NextCommand(VertexStartCommand)
- for i, j := 0, 1; j < len(l.vertices); i, j = i+2, j+2 {
- l.Next.Vertex(l.vertices[i], l.vertices[j])
- l.Next.NextCommand(VertexNoCommand)
- }
- for i, j := len(l.rewind)-2, len(l.rewind)-1; j > 0; i, j = i-2, j-2 {
- l.Next.NextCommand(VertexNoCommand)
- l.Next.Vertex(l.rewind[i], l.rewind[j])
- }
- if len(l.vertices) > 1 {
- l.Next.NextCommand(VertexNoCommand)
- l.Next.Vertex(l.vertices[0], l.vertices[1])
- }
- l.Next.NextCommand(VertexStopCommand)
- // reinit vertices
- l.vertices = l.vertices[0:0]
- l.rewind = l.rewind[0:0]
- l.x, l.y, l.nx, l.ny = 0, 0, 0, 0
- }
-}
-
-func (l *LineStroker) Vertex(x, y float64) {
- switch l.command {
- case VertexNoCommand:
- l.line(l.x, l.y, x, y)
- case VertexJoinCommand:
- l.joinLine(l.x, l.y, l.nx, l.ny, x, y)
- case VertexStartCommand:
- l.x, l.y = x, y
- case VertexCloseCommand:
- l.line(l.x, l.y, x, y)
- l.joinLine(l.x, l.y, l.nx, l.ny, x, y)
- l.closePolygon()
- }
- l.command = VertexNoCommand
-}
-
-func (l *LineStroker) appendVertex(vertices ...float64) {
- s := len(vertices) / 2
- if len(l.vertices)+s >= cap(l.vertices) {
- v := make([]float64, len(l.vertices), cap(l.vertices)+128)
- copy(v, l.vertices)
- l.vertices = v
- v = make([]float64, len(l.rewind), cap(l.rewind)+128)
- copy(v, l.rewind)
- l.rewind = v
- }
-
- copy(l.vertices[len(l.vertices):len(l.vertices)+s], vertices[:s])
- l.vertices = l.vertices[0 : len(l.vertices)+s]
- copy(l.rewind[len(l.rewind):len(l.rewind)+s], vertices[s:])
- l.rewind = l.rewind[0 : len(l.rewind)+s]
-
-}
-
-func (l *LineStroker) closePolygon() {
- if len(l.vertices) > 1 {
- l.appendVertex(l.vertices[0], l.vertices[1], l.rewind[0], l.rewind[1])
- }
-}
-
-func (l *LineStroker) line(x1, y1, x2, y2 float64) {
- dx := (x2 - x1)
- dy := (y2 - y1)
- d := vectorDistance(dx, dy)
- if d != 0 {
- nx := dy * l.HalfLineWidth / d
- ny := -(dx * l.HalfLineWidth / d)
- l.appendVertex(x1+nx, y1+ny, x2+nx, y2+ny, x1-nx, y1-ny, x2-nx, y2-ny)
- l.x, l.y, l.nx, l.ny = x2, y2, nx, ny
- }
-}
-
-func (l *LineStroker) joinLine(x1, y1, nx1, ny1, x2, y2 float64) {
- dx := (x2 - x1)
- dy := (y2 - y1)
- d := vectorDistance(dx, dy)
-
- if d != 0 {
- nx := dy * l.HalfLineWidth / d
- ny := -(dx * l.HalfLineWidth / d)
- /* l.join(x1, y1, x1 + nx, y1 - ny, nx, ny, x1 + ny2, y1 + nx2, nx2, ny2)
- l.join(x1, y1, x1 - ny1, y1 - nx1, nx1, ny1, x1 - ny2, y1 - nx2, nx2, ny2)*/
-
- l.appendVertex(x1+nx, y1+ny, x2+nx, y2+ny, x1-nx, y1-ny, x2-nx, y2-ny)
- l.x, l.y, l.nx, l.ny = x2, y2, nx, ny
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/transform.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/transform.go
deleted file mode 100644
index 1d89bfa9b..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/transform.go
+++ /dev/null
@@ -1,306 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 21/11/2010 by Laurent Le Goff
-
-package draw2d
-
-import (
- "code.google.com/p/freetype-go/freetype/raster"
- "math"
-)
-
-type MatrixTransform [6]float64
-
-const (
- epsilon = 1e-6
-)
-
-func (tr MatrixTransform) Determinant() float64 {
- return tr[0]*tr[3] - tr[1]*tr[2]
-}
-
-func (tr MatrixTransform) Transform(points ...*float64) {
- for i, j := 0, 1; j < len(points); i, j = i+2, j+2 {
- x := *points[i]
- y := *points[j]
- *points[i] = x*tr[0] + y*tr[2] + tr[4]
- *points[j] = x*tr[1] + y*tr[3] + tr[5]
- }
-}
-
-func (tr MatrixTransform) TransformArray(points []float64) {
- for i, j := 0, 1; j < len(points); i, j = i+2, j+2 {
- x := points[i]
- y := points[j]
- points[i] = x*tr[0] + y*tr[2] + tr[4]
- points[j] = x*tr[1] + y*tr[3] + tr[5]
- }
-}
-
-func (tr MatrixTransform) TransformRectangle(x0, y0, x2, y2 *float64) {
- x1 := *x2
- y1 := *y0
- x3 := *x0
- y3 := *y2
- tr.Transform(x0, y0, &x1, &y1, x2, y2, &x3, &y3)
- *x0, x1 = minMax(*x0, x1)
- *x2, x3 = minMax(*x2, x3)
- *y0, y1 = minMax(*y0, y1)
- *y2, y3 = minMax(*y2, y3)
-
- *x0 = min(*x0, *x2)
- *y0 = min(*y0, *y2)
- *x2 = max(x1, x3)
- *y2 = max(y1, y3)
-}
-
-func (tr MatrixTransform) TransformRasterPoint(points ...*raster.Point) {
- for _, point := range points {
- x := float64(point.X) / 256
- y := float64(point.Y) / 256
- point.X = raster.Fix32((x*tr[0] + y*tr[2] + tr[4]) * 256)
- point.Y = raster.Fix32((x*tr[1] + y*tr[3] + tr[5]) * 256)
- }
-}
-
-func (tr MatrixTransform) InverseTransform(points ...*float64) {
- d := tr.Determinant() // matrix determinant
- for i, j := 0, 1; j < len(points); i, j = i+2, j+2 {
- x := *points[i]
- y := *points[j]
- *points[i] = ((x-tr[4])*tr[3] - (y-tr[5])*tr[2]) / d
- *points[j] = ((y-tr[5])*tr[0] - (x-tr[4])*tr[1]) / d
- }
-}
-
-// ******************** Vector transformations ********************
-
-func (tr MatrixTransform) VectorTransform(points ...*float64) {
- for i, j := 0, 1; j < len(points); i, j = i+2, j+2 {
- x := *points[i]
- y := *points[j]
- *points[i] = x*tr[0] + y*tr[2]
- *points[j] = x*tr[1] + y*tr[3]
- }
-}
-
-// ******************** Transformations creation ********************
-
-/** Creates an identity transformation. */
-func NewIdentityMatrix() MatrixTransform {
- return [6]float64{1, 0, 0, 1, 0, 0}
-}
-
-/**
- * Creates a transformation with a translation, that,
- * transform point1 into point2.
- */
-func NewTranslationMatrix(tx, ty float64) MatrixTransform {
- return [6]float64{1, 0, 0, 1, tx, ty}
-}
-
-/**
- * Creates a transformation with a sx, sy scale factor
- */
-func NewScaleMatrix(sx, sy float64) MatrixTransform {
- return [6]float64{sx, 0, 0, sy, 0, 0}
-}
-
-/**
- * Creates a rotation transformation.
- */
-func NewRotationMatrix(angle float64) MatrixTransform {
- c := math.Cos(angle)
- s := math.Sin(angle)
- return [6]float64{c, s, -s, c, 0, 0}
-}
-
-/**
- * Creates a transformation, combining a scale and a translation, that transform rectangle1 into rectangle2.
- */
-func NewMatrixTransform(rectangle1, rectangle2 [4]float64) MatrixTransform {
- xScale := (rectangle2[2] - rectangle2[0]) / (rectangle1[2] - rectangle1[0])
- yScale := (rectangle2[3] - rectangle2[1]) / (rectangle1[3] - rectangle1[1])
- xOffset := rectangle2[0] - (rectangle1[0] * xScale)
- yOffset := rectangle2[1] - (rectangle1[1] * yScale)
- return [6]float64{xScale, 0, 0, yScale, xOffset, yOffset}
-}
-
-// ******************** Transformations operations ********************
-
-/**
- * Returns a transformation that is the inverse of the given transformation.
- */
-func (tr MatrixTransform) GetInverseTransformation() MatrixTransform {
- d := tr.Determinant() // matrix determinant
- return [6]float64{
- tr[3] / d,
- -tr[1] / d,
- -tr[2] / d,
- tr[0] / d,
- (tr[2]*tr[5] - tr[3]*tr[4]) / d,
- (tr[1]*tr[4] - tr[0]*tr[5]) / d}
-}
-
-func (tr1 MatrixTransform) Multiply(tr2 MatrixTransform) MatrixTransform {
- return [6]float64{
- tr1[0]*tr2[0] + tr1[1]*tr2[2],
- tr1[1]*tr2[3] + tr1[0]*tr2[1],
- tr1[2]*tr2[0] + tr1[3]*tr2[2],
- tr1[3]*tr2[3] + tr1[2]*tr2[1],
- tr1[4]*tr2[0] + tr1[5]*tr2[2] + tr2[4],
- tr1[5]*tr2[3] + tr1[4]*tr2[1] + tr2[5]}
-}
-
-func (tr *MatrixTransform) Scale(sx, sy float64) *MatrixTransform {
- tr[0] = sx * tr[0]
- tr[1] = sx * tr[1]
- tr[2] = sy * tr[2]
- tr[3] = sy * tr[3]
- return tr
-}
-
-func (tr *MatrixTransform) Translate(tx, ty float64) *MatrixTransform {
- tr[4] = tx*tr[0] + ty*tr[2] + tr[4]
- tr[5] = ty*tr[3] + tx*tr[1] + tr[5]
- return tr
-}
-
-func (tr *MatrixTransform) Rotate(angle float64) *MatrixTransform {
- c := math.Cos(angle)
- s := math.Sin(angle)
- t0 := c*tr[0] + s*tr[2]
- t1 := s*tr[3] + c*tr[1]
- t2 := c*tr[2] - s*tr[0]
- t3 := c*tr[3] - s*tr[1]
- tr[0] = t0
- tr[1] = t1
- tr[2] = t2
- tr[3] = t3
- return tr
-}
-
-func (tr MatrixTransform) GetTranslation() (x, y float64) {
- return tr[4], tr[5]
-}
-
-func (tr MatrixTransform) GetScaling() (x, y float64) {
- return tr[0], tr[3]
-}
-
-func (tr MatrixTransform) GetScale() float64 {
- x := 0.707106781*tr[0] + 0.707106781*tr[1]
- y := 0.707106781*tr[2] + 0.707106781*tr[3]
- return math.Sqrt(x*x + y*y)
-}
-
-func (tr MatrixTransform) GetMaxAbsScaling() (s float64) {
- sx := math.Abs(tr[0])
- sy := math.Abs(tr[3])
- if sx > sy {
- return sx
- }
- return sy
-}
-
-func (tr MatrixTransform) GetMinAbsScaling() (s float64) {
- sx := math.Abs(tr[0])
- sy := math.Abs(tr[3])
- if sx > sy {
- return sy
- }
- return sx
-}
-
-// ******************** Testing ********************
-
-/**
- * Tests if a two transformation are equal. A tolerance is applied when
- * comparing matrix elements.
- */
-func (tr1 MatrixTransform) Equals(tr2 MatrixTransform) bool {
- for i := 0; i < 6; i = i + 1 {
- if !fequals(tr1[i], tr2[i]) {
- return false
- }
- }
- return true
-}
-
-/**
- * Tests if a transformation is the identity transformation. A tolerance
- * is applied when comparing matrix elements.
- */
-func (tr MatrixTransform) IsIdentity() bool {
- return fequals(tr[4], 0) && fequals(tr[5], 0) && tr.IsTranslation()
-}
-
-/**
- * Tests if a transformation is is a pure translation. A tolerance
- * is applied when comparing matrix elements.
- */
-func (tr MatrixTransform) IsTranslation() bool {
- return fequals(tr[0], 1) && fequals(tr[1], 0) && fequals(tr[2], 0) && fequals(tr[3], 1)
-}
-
-/**
- * Compares two floats.
- * return true if the distance between the two floats is less than epsilon, false otherwise
- */
-func fequals(float1, float2 float64) bool {
- return math.Abs(float1-float2) <= epsilon
-}
-
-// this VertexConverter apply the Matrix transformation tr
-type VertexMatrixTransform struct {
- tr MatrixTransform
- Next VertexConverter
-}
-
-func NewVertexMatrixTransform(tr MatrixTransform, converter VertexConverter) *VertexMatrixTransform {
- return &VertexMatrixTransform{tr, converter}
-}
-
-// Vertex Matrix Transform
-func (vmt *VertexMatrixTransform) NextCommand(command VertexCommand) {
- vmt.Next.NextCommand(command)
-}
-
-func (vmt *VertexMatrixTransform) Vertex(x, y float64) {
- u := x*vmt.tr[0] + y*vmt.tr[2] + vmt.tr[4]
- v := x*vmt.tr[1] + y*vmt.tr[3] + vmt.tr[5]
- vmt.Next.Vertex(u, v)
-}
-
-// this adder apply a Matrix transformation to points
-type MatrixTransformAdder struct {
- tr MatrixTransform
- next raster.Adder
-}
-
-func NewMatrixTransformAdder(tr MatrixTransform, adder raster.Adder) *MatrixTransformAdder {
- return &MatrixTransformAdder{tr, adder}
-}
-
-// Start starts a new curve at the given point.
-func (mta MatrixTransformAdder) Start(a raster.Point) {
- mta.tr.TransformRasterPoint(&a)
- mta.next.Start(a)
-}
-
-// Add1 adds a linear segment to the current curve.
-func (mta MatrixTransformAdder) Add1(b raster.Point) {
- mta.tr.TransformRasterPoint(&b)
- mta.next.Add1(b)
-}
-
-// Add2 adds a quadratic segment to the current curve.
-func (mta MatrixTransformAdder) Add2(b, c raster.Point) {
- mta.tr.TransformRasterPoint(&b, &c)
- mta.next.Add2(b, c)
-}
-
-// Add3 adds a cubic segment to the current curve.
-func (mta MatrixTransformAdder) Add3(b, c, d raster.Point) {
- mta.tr.TransformRasterPoint(&b, &c, &d)
- mta.next.Add3(b, c, d)
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/vertex2d.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/vertex2d.go
deleted file mode 100644
index 4e4d4fd83..000000000
--- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/vertex2d.go
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 21/11/2010 by Laurent Le Goff
-
-package draw2d
-
-type VertexCommand byte
-
-const (
- VertexNoCommand VertexCommand = iota
- VertexStartCommand
- VertexJoinCommand
- VertexCloseCommand
- VertexStopCommand
-)
-
-type VertexConverter interface {
- NextCommand(cmd VertexCommand)
- Vertex(x, y float64)
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/geom.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/geom.go
deleted file mode 100644
index 63c86e6ab..000000000
--- a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/geom.go
+++ /dev/null
@@ -1,280 +0,0 @@
-// Copyright 2010 The Freetype-Go Authors. All rights reserved.
-// Use of this source code is governed by your choice of either the
-// FreeType License or the GNU General Public License version 2 (or
-// any later version), both of which can be found in the LICENSE file.
-
-package raster
-
-import (
- "fmt"
- "math"
-)
-
-// A Fix32 is a 24.8 fixed point number.
-type Fix32 int32
-
-// A Fix64 is a 48.16 fixed point number.
-type Fix64 int64
-
-// String returns a human-readable representation of a 24.8 fixed point number.
-// For example, the number one-and-a-quarter becomes "1:064".
-func (x Fix32) String() string {
- if x < 0 {
- x = -x
- return fmt.Sprintf("-%d:%03d", int32(x/256), int32(x%256))
- }
- return fmt.Sprintf("%d:%03d", int32(x/256), int32(x%256))
-}
-
-// String returns a human-readable representation of a 48.16 fixed point number.
-// For example, the number one-and-a-quarter becomes "1:16384".
-func (x Fix64) String() string {
- if x < 0 {
- x = -x
- return fmt.Sprintf("-%d:%05d", int64(x/65536), int64(x%65536))
- }
- return fmt.Sprintf("%d:%05d", int64(x/65536), int64(x%65536))
-}
-
-// maxAbs returns the maximum of abs(a) and abs(b).
-func maxAbs(a, b Fix32) Fix32 {
- if a < 0 {
- a = -a
- }
- if b < 0 {
- b = -b
- }
- if a < b {
- return b
- }
- return a
-}
-
-// A Point represents a two-dimensional point or vector, in 24.8 fixed point
-// format.
-type Point struct {
- X, Y Fix32
-}
-
-// String returns a human-readable representation of a Point.
-func (p Point) String() string {
- return "(" + p.X.String() + ", " + p.Y.String() + ")"
-}
-
-// Add returns the vector p + q.
-func (p Point) Add(q Point) Point {
- return Point{p.X + q.X, p.Y + q.Y}
-}
-
-// Sub returns the vector p - q.
-func (p Point) Sub(q Point) Point {
- return Point{p.X - q.X, p.Y - q.Y}
-}
-
-// Mul returns the vector k * p.
-func (p Point) Mul(k Fix32) Point {
- return Point{p.X * k / 256, p.Y * k / 256}
-}
-
-// Neg returns the vector -p, or equivalently p rotated by 180 degrees.
-func (p Point) Neg() Point {
- return Point{-p.X, -p.Y}
-}
-
-// Dot returns the dot product p·q.
-func (p Point) Dot(q Point) Fix64 {
- px, py := int64(p.X), int64(p.Y)
- qx, qy := int64(q.X), int64(q.Y)
- return Fix64(px*qx + py*qy)
-}
-
-// Len returns the length of the vector p.
-func (p Point) Len() Fix32 {
- // TODO(nigeltao): use fixed point math.
- x := float64(p.X)
- y := float64(p.Y)
- return Fix32(math.Sqrt(x*x + y*y))
-}
-
-// Norm returns the vector p normalized to the given length, or the zero Point
-// if p is degenerate.
-func (p Point) Norm(length Fix32) Point {
- d := p.Len()
- if d == 0 {
- return Point{}
- }
- s, t := int64(length), int64(d)
- x := int64(p.X) * s / t
- y := int64(p.Y) * s / t
- return Point{Fix32(x), Fix32(y)}
-}
-
-// Rot45CW returns the vector p rotated clockwise by 45 degrees.
-// Note that the Y-axis grows downwards, so {1, 0}.Rot45CW is {1/√2, 1/√2}.
-func (p Point) Rot45CW() Point {
- // 181/256 is approximately 1/√2, or sin(π/4).
- px, py := int64(p.X), int64(p.Y)
- qx := (+px - py) * 181 / 256
- qy := (+px + py) * 181 / 256
- return Point{Fix32(qx), Fix32(qy)}
-}
-
-// Rot90CW returns the vector p rotated clockwise by 90 degrees.
-// Note that the Y-axis grows downwards, so {1, 0}.Rot90CW is {0, 1}.
-func (p Point) Rot90CW() Point {
- return Point{-p.Y, p.X}
-}
-
-// Rot135CW returns the vector p rotated clockwise by 135 degrees.
-// Note that the Y-axis grows downwards, so {1, 0}.Rot135CW is {-1/√2, 1/√2}.
-func (p Point) Rot135CW() Point {
- // 181/256 is approximately 1/√2, or sin(π/4).
- px, py := int64(p.X), int64(p.Y)
- qx := (-px - py) * 181 / 256
- qy := (+px - py) * 181 / 256
- return Point{Fix32(qx), Fix32(qy)}
-}
-
-// Rot45CCW returns the vector p rotated counter-clockwise by 45 degrees.
-// Note that the Y-axis grows downwards, so {1, 0}.Rot45CCW is {1/√2, -1/√2}.
-func (p Point) Rot45CCW() Point {
- // 181/256 is approximately 1/√2, or sin(π/4).
- px, py := int64(p.X), int64(p.Y)
- qx := (+px + py) * 181 / 256
- qy := (-px + py) * 181 / 256
- return Point{Fix32(qx), Fix32(qy)}
-}
-
-// Rot90CCW returns the vector p rotated counter-clockwise by 90 degrees.
-// Note that the Y-axis grows downwards, so {1, 0}.Rot90CCW is {0, -1}.
-func (p Point) Rot90CCW() Point {
- return Point{p.Y, -p.X}
-}
-
-// Rot135CCW returns the vector p rotated counter-clockwise by 135 degrees.
-// Note that the Y-axis grows downwards, so {1, 0}.Rot135CCW is {-1/√2, -1/√2}.
-func (p Point) Rot135CCW() Point {
- // 181/256 is approximately 1/√2, or sin(π/4).
- px, py := int64(p.X), int64(p.Y)
- qx := (-px + py) * 181 / 256
- qy := (-px - py) * 181 / 256
- return Point{Fix32(qx), Fix32(qy)}
-}
-
-// An Adder accumulates points on a curve.
-type Adder interface {
- // Start starts a new curve at the given point.
- Start(a Point)
- // Add1 adds a linear segment to the current curve.
- Add1(b Point)
- // Add2 adds a quadratic segment to the current curve.
- Add2(b, c Point)
- // Add3 adds a cubic segment to the current curve.
- Add3(b, c, d Point)
-}
-
-// A Path is a sequence of curves, and a curve is a start point followed by a
-// sequence of linear, quadratic or cubic segments.
-type Path []Fix32
-
-// String returns a human-readable representation of a Path.
-func (p Path) String() string {
- s := ""
- for i := 0; i < len(p); {
- if i != 0 {
- s += " "
- }
- switch p[i] {
- case 0:
- s += "S0" + fmt.Sprint([]Fix32(p[i+1:i+3]))
- i += 4
- case 1:
- s += "A1" + fmt.Sprint([]Fix32(p[i+1:i+3]))
- i += 4
- case 2:
- s += "A2" + fmt.Sprint([]Fix32(p[i+1:i+5]))
- i += 6
- case 3:
- s += "A3" + fmt.Sprint([]Fix32(p[i+1:i+7]))
- i += 8
- default:
- panic("freetype/raster: bad path")
- }
- }
- return s
-}
-
-// Clear cancels any previous calls to p.Start or p.AddXxx.
-func (p *Path) Clear() {
- *p = (*p)[:0]
-}
-
-// Start starts a new curve at the given point.
-func (p *Path) Start(a Point) {
- *p = append(*p, 0, a.X, a.Y, 0)
-}
-
-// Add1 adds a linear segment to the current curve.
-func (p *Path) Add1(b Point) {
- *p = append(*p, 1, b.X, b.Y, 1)
-}
-
-// Add2 adds a quadratic segment to the current curve.
-func (p *Path) Add2(b, c Point) {
- *p = append(*p, 2, b.X, b.Y, c.X, c.Y, 2)
-}
-
-// Add3 adds a cubic segment to the current curve.
-func (p *Path) Add3(b, c, d Point) {
- *p = append(*p, 3, b.X, b.Y, c.X, c.Y, d.X, d.Y, 3)
-}
-
-// AddPath adds the Path q to p.
-func (p *Path) AddPath(q Path) {
- *p = append(*p, q...)
-}
-
-// AddStroke adds a stroked Path.
-func (p *Path) AddStroke(q Path, width Fix32, cr Capper, jr Joiner) {
- Stroke(p, q, width, cr, jr)
-}
-
-// firstPoint returns the first point in a non-empty Path.
-func (p Path) firstPoint() Point {
- return Point{p[1], p[2]}
-}
-
-// lastPoint returns the last point in a non-empty Path.
-func (p Path) lastPoint() Point {
- return Point{p[len(p)-3], p[len(p)-2]}
-}
-
-// addPathReversed adds q reversed to p.
-// For example, if q consists of a linear segment from A to B followed by a
-// quadratic segment from B to C to D, then the values of q looks like:
-// index: 01234567890123
-// value: 0AA01BB12CCDD2
-// So, when adding q backwards to p, we want to Add2(C, B) followed by Add1(A).
-func addPathReversed(p Adder, q Path) {
- if len(q) == 0 {
- return
- }
- i := len(q) - 1
- for {
- switch q[i] {
- case 0:
- return
- case 1:
- i -= 4
- p.Add1(Point{q[i-2], q[i-1]})
- case 2:
- i -= 6
- p.Add2(Point{q[i+2], q[i+3]}, Point{q[i-2], q[i-1]})
- case 3:
- i -= 8
- p.Add3(Point{q[i+4], q[i+5]}, Point{q[i+2], q[i+3]}, Point{q[i-2], q[i-1]})
- default:
- panic("freetype/raster: bad path")
- }
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/paint.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/paint.go
deleted file mode 100644
index 13cccc192..000000000
--- a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/paint.go
+++ /dev/null
@@ -1,292 +0,0 @@
-// Copyright 2010 The Freetype-Go Authors. All rights reserved.
-// Use of this source code is governed by your choice of either the
-// FreeType License or the GNU General Public License version 2 (or
-// any later version), both of which can be found in the LICENSE file.
-
-package raster
-
-import (
- "image"
- "image/color"
- "image/draw"
- "math"
-)
-
-// A Span is a horizontal segment of pixels with constant alpha. X0 is an
-// inclusive bound and X1 is exclusive, the same as for slices. A fully
-// opaque Span has A == 1<<32 - 1.
-type Span struct {
- Y, X0, X1 int
- A uint32
-}
-
-// A Painter knows how to paint a batch of Spans. Rasterization may involve
-// Painting multiple batches, and done will be true for the final batch.
-// The Spans' Y values are monotonically increasing during a rasterization.
-// Paint may use all of ss as scratch space during the call.
-type Painter interface {
- Paint(ss []Span, done bool)
-}
-
-// The PainterFunc type adapts an ordinary function to the Painter interface.
-type PainterFunc func(ss []Span, done bool)
-
-// Paint just delegates the call to f.
-func (f PainterFunc) Paint(ss []Span, done bool) { f(ss, done) }
-
-// An AlphaOverPainter is a Painter that paints Spans onto an image.Alpha
-// using the Over Porter-Duff composition operator.
-type AlphaOverPainter struct {
- Image *image.Alpha
-}
-
-// Paint satisfies the Painter interface by painting ss onto an image.Alpha.
-func (r AlphaOverPainter) Paint(ss []Span, done bool) {
- b := r.Image.Bounds()
- for _, s := range ss {
- if s.Y < b.Min.Y {
- continue
- }
- if s.Y >= b.Max.Y {
- return
- }
- if s.X0 < b.Min.X {
- s.X0 = b.Min.X
- }
- if s.X1 > b.Max.X {
- s.X1 = b.Max.X
- }
- if s.X0 >= s.X1 {
- continue
- }
- base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X
- p := r.Image.Pix[base+s.X0 : base+s.X1]
- a := int(s.A >> 24)
- for i, c := range p {
- v := int(c)
- p[i] = uint8((v*255 + (255-v)*a) / 255)
- }
- }
-}
-
-// NewAlphaOverPainter creates a new AlphaOverPainter for the given image.
-func NewAlphaOverPainter(m *image.Alpha) AlphaOverPainter {
- return AlphaOverPainter{m}
-}
-
-// An AlphaSrcPainter is a Painter that paints Spans onto an image.Alpha
-// using the Src Porter-Duff composition operator.
-type AlphaSrcPainter struct {
- Image *image.Alpha
-}
-
-// Paint satisfies the Painter interface by painting ss onto an image.Alpha.
-func (r AlphaSrcPainter) Paint(ss []Span, done bool) {
- b := r.Image.Bounds()
- for _, s := range ss {
- if s.Y < b.Min.Y {
- continue
- }
- if s.Y >= b.Max.Y {
- return
- }
- if s.X0 < b.Min.X {
- s.X0 = b.Min.X
- }
- if s.X1 > b.Max.X {
- s.X1 = b.Max.X
- }
- if s.X0 >= s.X1 {
- continue
- }
- base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X
- p := r.Image.Pix[base+s.X0 : base+s.X1]
- color := uint8(s.A >> 24)
- for i := range p {
- p[i] = color
- }
- }
-}
-
-// NewAlphaSrcPainter creates a new AlphaSrcPainter for the given image.
-func NewAlphaSrcPainter(m *image.Alpha) AlphaSrcPainter {
- return AlphaSrcPainter{m}
-}
-
-type RGBAPainter struct {
- // The image to compose onto.
- Image *image.RGBA
- // The Porter-Duff composition operator.
- Op draw.Op
- // The 16-bit color to paint the spans.
- cr, cg, cb, ca uint32
-}
-
-// Paint satisfies the Painter interface by painting ss onto an image.RGBA.
-func (r *RGBAPainter) Paint(ss []Span, done bool) {
- b := r.Image.Bounds()
- for _, s := range ss {
- if s.Y < b.Min.Y {
- continue
- }
- if s.Y >= b.Max.Y {
- return
- }
- if s.X0 < b.Min.X {
- s.X0 = b.Min.X
- }
- if s.X1 > b.Max.X {
- s.X1 = b.Max.X
- }
- if s.X0 >= s.X1 {
- continue
- }
- // This code is similar to drawGlyphOver in $GOROOT/src/pkg/image/draw/draw.go.
- ma := s.A >> 16
- const m = 1<<16 - 1
- i0 := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride + (s.X0-r.Image.Rect.Min.X)*4
- i1 := i0 + (s.X1-s.X0)*4
- if r.Op == draw.Over {
- for i := i0; i < i1; i += 4 {
- dr := uint32(r.Image.Pix[i+0])
- dg := uint32(r.Image.Pix[i+1])
- db := uint32(r.Image.Pix[i+2])
- da := uint32(r.Image.Pix[i+3])
- a := (m - (r.ca * ma / m)) * 0x101
- r.Image.Pix[i+0] = uint8((dr*a + r.cr*ma) / m >> 8)
- r.Image.Pix[i+1] = uint8((dg*a + r.cg*ma) / m >> 8)
- r.Image.Pix[i+2] = uint8((db*a + r.cb*ma) / m >> 8)
- r.Image.Pix[i+3] = uint8((da*a + r.ca*ma) / m >> 8)
- }
- } else {
- for i := i0; i < i1; i += 4 {
- r.Image.Pix[i+0] = uint8(r.cr * ma / m >> 8)
- r.Image.Pix[i+1] = uint8(r.cg * ma / m >> 8)
- r.Image.Pix[i+2] = uint8(r.cb * ma / m >> 8)
- r.Image.Pix[i+3] = uint8(r.ca * ma / m >> 8)
- }
- }
- }
-}
-
-// SetColor sets the color to paint the spans.
-func (r *RGBAPainter) SetColor(c color.Color) {
- r.cr, r.cg, r.cb, r.ca = c.RGBA()
-}
-
-// NewRGBAPainter creates a new RGBAPainter for the given image.
-func NewRGBAPainter(m *image.RGBA) *RGBAPainter {
- return &RGBAPainter{Image: m}
-}
-
-// A MonochromePainter wraps another Painter, quantizing each Span's alpha to
-// be either fully opaque or fully transparent.
-type MonochromePainter struct {
- Painter Painter
- y, x0, x1 int
-}
-
-// Paint delegates to the wrapped Painter after quantizing each Span's alpha
-// value and merging adjacent fully opaque Spans.
-func (m *MonochromePainter) Paint(ss []Span, done bool) {
- // We compact the ss slice, discarding any Spans whose alpha quantizes to zero.
- j := 0
- for _, s := range ss {
- if s.A >= 1<<31 {
- if m.y == s.Y && m.x1 == s.X0 {
- m.x1 = s.X1
- } else {
- ss[j] = Span{m.y, m.x0, m.x1, 1<<32 - 1}
- j++
- m.y, m.x0, m.x1 = s.Y, s.X0, s.X1
- }
- }
- }
- if done {
- // Flush the accumulated Span.
- finalSpan := Span{m.y, m.x0, m.x1, 1<<32 - 1}
- if j < len(ss) {
- ss[j] = finalSpan
- j++
- m.Painter.Paint(ss[:j], true)
- } else if j == len(ss) {
- m.Painter.Paint(ss, false)
- if cap(ss) > 0 {
- ss = ss[:1]
- } else {
- ss = make([]Span, 1)
- }
- ss[0] = finalSpan
- m.Painter.Paint(ss, true)
- } else {
- panic("unreachable")
- }
- // Reset the accumulator, so that this Painter can be re-used.
- m.y, m.x0, m.x1 = 0, 0, 0
- } else {
- m.Painter.Paint(ss[:j], false)
- }
-}
-
-// NewMonochromePainter creates a new MonochromePainter that wraps the given
-// Painter.
-func NewMonochromePainter(p Painter) *MonochromePainter {
- return &MonochromePainter{Painter: p}
-}
-
-// A GammaCorrectionPainter wraps another Painter, performing gamma-correction
-// on each Span's alpha value.
-type GammaCorrectionPainter struct {
- // The wrapped Painter.
- Painter Painter
- // Precomputed alpha values for linear interpolation, with fully opaque == 1<<16-1.
- a [256]uint16
- // Whether gamma correction is a no-op.
- gammaIsOne bool
-}
-
-// Paint delegates to the wrapped Painter after performing gamma-correction
-// on each Span.
-func (g *GammaCorrectionPainter) Paint(ss []Span, done bool) {
- if !g.gammaIsOne {
- const (
- M = 0x1010101 // 255*M == 1<<32-1
- N = 0x8080 // N = M>>9, and N < 1<<16-1
- )
- for i, s := range ss {
- if s.A == 0 || s.A == 1<<32-1 {
- continue
- }
- p, q := s.A/M, (s.A%M)>>9
- // The resultant alpha is a linear interpolation of g.a[p] and g.a[p+1].
- a := uint32(g.a[p])*(N-q) + uint32(g.a[p+1])*q
- a = (a + N/2) / N
- // Convert the alpha from 16-bit (which is g.a's range) to 32-bit.
- a |= a << 16
- ss[i].A = a
- }
- }
- g.Painter.Paint(ss, done)
-}
-
-// SetGamma sets the gamma value.
-func (g *GammaCorrectionPainter) SetGamma(gamma float64) {
- if gamma == 1.0 {
- g.gammaIsOne = true
- return
- }
- g.gammaIsOne = false
- for i := 0; i < 256; i++ {
- a := float64(i) / 0xff
- a = math.Pow(a, gamma)
- g.a[i] = uint16(0xffff * a)
- }
-}
-
-// NewGammaCorrectionPainter creates a new GammaCorrectionPainter that wraps
-// the given Painter.
-func NewGammaCorrectionPainter(p Painter, gamma float64) *GammaCorrectionPainter {
- g := &GammaCorrectionPainter{Painter: p}
- g.SetGamma(gamma)
- return g
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/raster.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/raster.go
deleted file mode 100644
index 45af7eaa2..000000000
--- a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/raster.go
+++ /dev/null
@@ -1,579 +0,0 @@
-// Copyright 2010 The Freetype-Go Authors. All rights reserved.
-// Use of this source code is governed by your choice of either the
-// FreeType License or the GNU General Public License version 2 (or
-// any later version), both of which can be found in the LICENSE file.
-
-// The raster package provides an anti-aliasing 2-D rasterizer.
-//
-// It is part of the larger Freetype-Go suite of font-related packages,
-// but the raster package is not specific to font rasterization, and can
-// be used standalone without any other Freetype-Go package.
-//
-// Rasterization is done by the same area/coverage accumulation algorithm
-// as the Freetype "smooth" module, and the Anti-Grain Geometry library.
-// A description of the area/coverage algorithm is at
-// http://projects.tuxee.net/cl-vectors/section-the-cl-aa-algorithm
-package raster
-
-import (
- "strconv"
-)
-
-// A cell is part of a linked list (for a given yi co-ordinate) of accumulated
-// area/coverage for the pixel at (xi, yi).
-type cell struct {
- xi int
- area, cover int
- next int
-}
-
-type Rasterizer struct {
- // If false, the default behavior is to use the even-odd winding fill
- // rule during Rasterize.
- UseNonZeroWinding bool
- // An offset (in pixels) to the painted spans.
- Dx, Dy int
-
- // The width of the Rasterizer. The height is implicit in len(cellIndex).
- width int
- // splitScaleN is the scaling factor used to determine how many times
- // to decompose a quadratic or cubic segment into a linear approximation.
- splitScale2, splitScale3 int
-
- // The current pen position.
- a Point
- // The current cell and its area/coverage being accumulated.
- xi, yi int
- area, cover int
-
- // Saved cells.
- cell []cell
- // Linked list of cells, one per row.
- cellIndex []int
- // Buffers.
- cellBuf [256]cell
- cellIndexBuf [64]int
- spanBuf [64]Span
-}
-
-// findCell returns the index in r.cell for the cell corresponding to
-// (r.xi, r.yi). The cell is created if necessary.
-func (r *Rasterizer) findCell() int {
- if r.yi < 0 || r.yi >= len(r.cellIndex) {
- return -1
- }
- xi := r.xi
- if xi < 0 {
- xi = -1
- } else if xi > r.width {
- xi = r.width
- }
- i, prev := r.cellIndex[r.yi], -1
- for i != -1 && r.cell[i].xi <= xi {
- if r.cell[i].xi == xi {
- return i
- }
- i, prev = r.cell[i].next, i
- }
- c := len(r.cell)
- if c == cap(r.cell) {
- buf := make([]cell, c, 4*c)
- copy(buf, r.cell)
- r.cell = buf[0 : c+1]
- } else {
- r.cell = r.cell[0 : c+1]
- }
- r.cell[c] = cell{xi, 0, 0, i}
- if prev == -1 {
- r.cellIndex[r.yi] = c
- } else {
- r.cell[prev].next = c
- }
- return c
-}
-
-// saveCell saves any accumulated r.area/r.cover for (r.xi, r.yi).
-func (r *Rasterizer) saveCell() {
- if r.area != 0 || r.cover != 0 {
- i := r.findCell()
- if i != -1 {
- r.cell[i].area += r.area
- r.cell[i].cover += r.cover
- }
- r.area = 0
- r.cover = 0
- }
-}
-
-// setCell sets the (xi, yi) cell that r is accumulating area/coverage for.
-func (r *Rasterizer) setCell(xi, yi int) {
- if r.xi != xi || r.yi != yi {
- r.saveCell()
- r.xi, r.yi = xi, yi
- }
-}
-
-// scan accumulates area/coverage for the yi'th scanline, going from
-// x0 to x1 in the horizontal direction (in 24.8 fixed point co-ordinates)
-// and from y0f to y1f fractional vertical units within that scanline.
-func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f Fix32) {
- // Break the 24.8 fixed point X co-ordinates into integral and fractional parts.
- x0i := int(x0) / 256
- x0f := x0 - Fix32(256*x0i)
- x1i := int(x1) / 256
- x1f := x1 - Fix32(256*x1i)
-
- // A perfectly horizontal scan.
- if y0f == y1f {
- r.setCell(x1i, yi)
- return
- }
- dx, dy := x1-x0, y1f-y0f
- // A single cell scan.
- if x0i == x1i {
- r.area += int((x0f + x1f) * dy)
- r.cover += int(dy)
- return
- }
- // There are at least two cells. Apart from the first and last cells,
- // all intermediate cells go through the full width of the cell,
- // or 256 units in 24.8 fixed point format.
- var (
- p, q, edge0, edge1 Fix32
- xiDelta int
- )
- if dx > 0 {
- p, q = (256-x0f)*dy, dx
- edge0, edge1, xiDelta = 0, 256, 1
- } else {
- p, q = x0f*dy, -dx
- edge0, edge1, xiDelta = 256, 0, -1
- }
- yDelta, yRem := p/q, p%q
- if yRem < 0 {
- yDelta -= 1
- yRem += q
- }
- // Do the first cell.
- xi, y := x0i, y0f
- r.area += int((x0f + edge1) * yDelta)
- r.cover += int(yDelta)
- xi, y = xi+xiDelta, y+yDelta
- r.setCell(xi, yi)
- if xi != x1i {
- // Do all the intermediate cells.
- p = 256 * (y1f - y + yDelta)
- fullDelta, fullRem := p/q, p%q
- if fullRem < 0 {
- fullDelta -= 1
- fullRem += q
- }
- yRem -= q
- for xi != x1i {
- yDelta = fullDelta
- yRem += fullRem
- if yRem >= 0 {
- yDelta += 1
- yRem -= q
- }
- r.area += int(256 * yDelta)
- r.cover += int(yDelta)
- xi, y = xi+xiDelta, y+yDelta
- r.setCell(xi, yi)
- }
- }
- // Do the last cell.
- yDelta = y1f - y
- r.area += int((edge0 + x1f) * yDelta)
- r.cover += int(yDelta)
-}
-
-// Start starts a new curve at the given point.
-func (r *Rasterizer) Start(a Point) {
- r.setCell(int(a.X/256), int(a.Y/256))
- r.a = a
-}
-
-// Add1 adds a linear segment to the current curve.
-func (r *Rasterizer) Add1(b Point) {
- x0, y0 := r.a.X, r.a.Y
- x1, y1 := b.X, b.Y
- dx, dy := x1-x0, y1-y0
- // Break the 24.8 fixed point Y co-ordinates into integral and fractional parts.
- y0i := int(y0) / 256
- y0f := y0 - Fix32(256*y0i)
- y1i := int(y1) / 256
- y1f := y1 - Fix32(256*y1i)
-
- if y0i == y1i {
- // There is only one scanline.
- r.scan(y0i, x0, y0f, x1, y1f)
-
- } else if dx == 0 {
- // This is a vertical line segment. We avoid calling r.scan and instead
- // manipulate r.area and r.cover directly.
- var (
- edge0, edge1 Fix32
- yiDelta int
- )
- if dy > 0 {
- edge0, edge1, yiDelta = 0, 256, 1
- } else {
- edge0, edge1, yiDelta = 256, 0, -1
- }
- x0i, yi := int(x0)/256, y0i
- x0fTimes2 := (int(x0) - (256 * x0i)) * 2
- // Do the first pixel.
- dcover := int(edge1 - y0f)
- darea := int(x0fTimes2 * dcover)
- r.area += darea
- r.cover += dcover
- yi += yiDelta
- r.setCell(x0i, yi)
- // Do all the intermediate pixels.
- dcover = int(edge1 - edge0)
- darea = int(x0fTimes2 * dcover)
- for yi != y1i {
- r.area += darea
- r.cover += dcover
- yi += yiDelta
- r.setCell(x0i, yi)
- }
- // Do the last pixel.
- dcover = int(y1f - edge0)
- darea = int(x0fTimes2 * dcover)
- r.area += darea
- r.cover += dcover
-
- } else {
- // There are at least two scanlines. Apart from the first and last scanlines,
- // all intermediate scanlines go through the full height of the row, or 256
- // units in 24.8 fixed point format.
- var (
- p, q, edge0, edge1 Fix32
- yiDelta int
- )
- if dy > 0 {
- p, q = (256-y0f)*dx, dy
- edge0, edge1, yiDelta = 0, 256, 1
- } else {
- p, q = y0f*dx, -dy
- edge0, edge1, yiDelta = 256, 0, -1
- }
- xDelta, xRem := p/q, p%q
- if xRem < 0 {
- xDelta -= 1
- xRem += q
- }
- // Do the first scanline.
- x, yi := x0, y0i
- r.scan(yi, x, y0f, x+xDelta, edge1)
- x, yi = x+xDelta, yi+yiDelta
- r.setCell(int(x)/256, yi)
- if yi != y1i {
- // Do all the intermediate scanlines.
- p = 256 * dx
- fullDelta, fullRem := p/q, p%q
- if fullRem < 0 {
- fullDelta -= 1
- fullRem += q
- }
- xRem -= q
- for yi != y1i {
- xDelta = fullDelta
- xRem += fullRem
- if xRem >= 0 {
- xDelta += 1
- xRem -= q
- }
- r.scan(yi, x, edge0, x+xDelta, edge1)
- x, yi = x+xDelta, yi+yiDelta
- r.setCell(int(x)/256, yi)
- }
- }
- // Do the last scanline.
- r.scan(yi, x, edge0, x1, y1f)
- }
- // The next lineTo starts from b.
- r.a = b
-}
-
-// Add2 adds a quadratic segment to the current curve.
-func (r *Rasterizer) Add2(b, c Point) {
- // Calculate nSplit (the number of recursive decompositions) based on how `curvy' it is.
- // Specifically, how much the middle point b deviates from (a+c)/2.
- dev := maxAbs(r.a.X-2*b.X+c.X, r.a.Y-2*b.Y+c.Y) / Fix32(r.splitScale2)
- nsplit := 0
- for dev > 0 {
- dev /= 4
- nsplit++
- }
- // dev is 32-bit, and nsplit++ every time we shift off 2 bits, so maxNsplit is 16.
- const maxNsplit = 16
- if nsplit > maxNsplit {
- panic("freetype/raster: Add2 nsplit too large: " + strconv.Itoa(nsplit))
- }
- // Recursively decompose the curve nSplit levels deep.
- var (
- pStack [2*maxNsplit + 3]Point
- sStack [maxNsplit + 1]int
- i int
- )
- sStack[0] = nsplit
- pStack[0] = c
- pStack[1] = b
- pStack[2] = r.a
- for i >= 0 {
- s := sStack[i]
- p := pStack[2*i:]
- if s > 0 {
- // Split the quadratic curve p[:3] into an equivalent set of two shorter curves:
- // p[:3] and p[2:5]. The new p[4] is the old p[2], and p[0] is unchanged.
- mx := p[1].X
- p[4].X = p[2].X
- p[3].X = (p[4].X + mx) / 2
- p[1].X = (p[0].X + mx) / 2
- p[2].X = (p[1].X + p[3].X) / 2
- my := p[1].Y
- p[4].Y = p[2].Y
- p[3].Y = (p[4].Y + my) / 2
- p[1].Y = (p[0].Y + my) / 2
- p[2].Y = (p[1].Y + p[3].Y) / 2
- // The two shorter curves have one less split to do.
- sStack[i] = s - 1
- sStack[i+1] = s - 1
- i++
- } else {
- // Replace the level-0 quadratic with a two-linear-piece approximation.
- midx := (p[0].X + 2*p[1].X + p[2].X) / 4
- midy := (p[0].Y + 2*p[1].Y + p[2].Y) / 4
- r.Add1(Point{midx, midy})
- r.Add1(p[0])
- i--
- }
- }
-}
-
-// Add3 adds a cubic segment to the current curve.
-func (r *Rasterizer) Add3(b, c, d Point) {
- // Calculate nSplit (the number of recursive decompositions) based on how `curvy' it is.
- dev2 := maxAbs(r.a.X-3*(b.X+c.X)+d.X, r.a.Y-3*(b.Y+c.Y)+d.Y) / Fix32(r.splitScale2)
- dev3 := maxAbs(r.a.X-2*b.X+d.X, r.a.Y-2*b.Y+d.Y) / Fix32(r.splitScale3)
- nsplit := 0
- for dev2 > 0 || dev3 > 0 {
- dev2 /= 8
- dev3 /= 4
- nsplit++
- }
- // devN is 32-bit, and nsplit++ every time we shift off 2 bits, so maxNsplit is 16.
- const maxNsplit = 16
- if nsplit > maxNsplit {
- panic("freetype/raster: Add3 nsplit too large: " + strconv.Itoa(nsplit))
- }
- // Recursively decompose the curve nSplit levels deep.
- var (
- pStack [3*maxNsplit + 4]Point
- sStack [maxNsplit + 1]int
- i int
- )
- sStack[0] = nsplit
- pStack[0] = d
- pStack[1] = c
- pStack[2] = b
- pStack[3] = r.a
- for i >= 0 {
- s := sStack[i]
- p := pStack[3*i:]
- if s > 0 {
- // Split the cubic curve p[:4] into an equivalent set of two shorter curves:
- // p[:4] and p[3:7]. The new p[6] is the old p[3], and p[0] is unchanged.
- m01x := (p[0].X + p[1].X) / 2
- m12x := (p[1].X + p[2].X) / 2
- m23x := (p[2].X + p[3].X) / 2
- p[6].X = p[3].X
- p[5].X = m23x
- p[1].X = m01x
- p[2].X = (m01x + m12x) / 2
- p[4].X = (m12x + m23x) / 2
- p[3].X = (p[2].X + p[4].X) / 2
- m01y := (p[0].Y + p[1].Y) / 2
- m12y := (p[1].Y + p[2].Y) / 2
- m23y := (p[2].Y + p[3].Y) / 2
- p[6].Y = p[3].Y
- p[5].Y = m23y
- p[1].Y = m01y
- p[2].Y = (m01y + m12y) / 2
- p[4].Y = (m12y + m23y) / 2
- p[3].Y = (p[2].Y + p[4].Y) / 2
- // The two shorter curves have one less split to do.
- sStack[i] = s - 1
- sStack[i+1] = s - 1
- i++
- } else {
- // Replace the level-0 cubic with a two-linear-piece approximation.
- midx := (p[0].X + 3*(p[1].X+p[2].X) + p[3].X) / 8
- midy := (p[0].Y + 3*(p[1].Y+p[2].Y) + p[3].Y) / 8
- r.Add1(Point{midx, midy})
- r.Add1(p[0])
- i--
- }
- }
-}
-
-// AddPath adds the given Path.
-func (r *Rasterizer) AddPath(p Path) {
- for i := 0; i < len(p); {
- switch p[i] {
- case 0:
- r.Start(Point{p[i+1], p[i+2]})
- i += 4
- case 1:
- r.Add1(Point{p[i+1], p[i+2]})
- i += 4
- case 2:
- r.Add2(Point{p[i+1], p[i+2]}, Point{p[i+3], p[i+4]})
- i += 6
- case 3:
- r.Add3(Point{p[i+1], p[i+2]}, Point{p[i+3], p[i+4]}, Point{p[i+5], p[i+6]})
- i += 8
- default:
- panic("freetype/raster: bad path")
- }
- }
-}
-
-// AddStroke adds a stroked Path.
-func (r *Rasterizer) AddStroke(q Path, width Fix32, cr Capper, jr Joiner) {
- Stroke(r, q, width, cr, jr)
-}
-
-// Converts an area value to a uint32 alpha value. A completely filled pixel
-// corresponds to an area of 256*256*2, and an alpha of 1<<32-1. The
-// conversion of area values greater than this depends on the winding rule:
-// even-odd or non-zero.
-func (r *Rasterizer) areaToAlpha(area int) uint32 {
- // The C Freetype implementation (version 2.3.12) does "alpha := area>>1" without
- // the +1. Round-to-nearest gives a more symmetric result than round-down.
- // The C implementation also returns 8-bit alpha, not 32-bit alpha.
- a := (area + 1) >> 1
- if a < 0 {
- a = -a
- }
- alpha := uint32(a)
- if r.UseNonZeroWinding {
- if alpha > 0xffff {
- alpha = 0xffff
- }
- } else {
- alpha &= 0x1ffff
- if alpha > 0x10000 {
- alpha = 0x20000 - alpha
- } else if alpha == 0x10000 {
- alpha = 0x0ffff
- }
- }
- alpha |= alpha << 16
- return alpha
-}
-
-// Rasterize converts r's accumulated curves into Spans for p. The Spans
-// passed to p are non-overlapping, and sorted by Y and then X. They all
-// have non-zero width (and 0 <= X0 < X1 <= r.width) and non-zero A, except
-// for the final Span, which has Y, X0, X1 and A all equal to zero.
-func (r *Rasterizer) Rasterize(p Painter) {
- r.saveCell()
- s := 0
- for yi := 0; yi < len(r.cellIndex); yi++ {
- xi, cover := 0, 0
- for c := r.cellIndex[yi]; c != -1; c = r.cell[c].next {
- if cover != 0 && r.cell[c].xi > xi {
- alpha := r.areaToAlpha(cover * 256 * 2)
- if alpha != 0 {
- xi0, xi1 := xi, r.cell[c].xi
- if xi0 < 0 {
- xi0 = 0
- }
- if xi1 >= r.width {
- xi1 = r.width
- }
- if xi0 < xi1 {
- r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha}
- s++
- }
- }
- }
- cover += r.cell[c].cover
- alpha := r.areaToAlpha(cover*256*2 - r.cell[c].area)
- xi = r.cell[c].xi + 1
- if alpha != 0 {
- xi0, xi1 := r.cell[c].xi, xi
- if xi0 < 0 {
- xi0 = 0
- }
- if xi1 >= r.width {
- xi1 = r.width
- }
- if xi0 < xi1 {
- r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha}
- s++
- }
- }
- if s > len(r.spanBuf)-2 {
- p.Paint(r.spanBuf[:s], false)
- s = 0
- }
- }
- }
- p.Paint(r.spanBuf[:s], true)
-}
-
-// Clear cancels any previous calls to r.Start or r.AddXxx.
-func (r *Rasterizer) Clear() {
- r.a = Point{}
- r.xi = 0
- r.yi = 0
- r.area = 0
- r.cover = 0
- r.cell = r.cell[:0]
- for i := 0; i < len(r.cellIndex); i++ {
- r.cellIndex[i] = -1
- }
-}
-
-// SetBounds sets the maximum width and height of the rasterized image and
-// calls Clear. The width and height are in pixels, not Fix32 units.
-func (r *Rasterizer) SetBounds(width, height int) {
- if width < 0 {
- width = 0
- }
- if height < 0 {
- height = 0
- }
- // Use the same ssN heuristic as the C Freetype implementation.
- // The C implementation uses the values 32, 16, but those are in
- // 26.6 fixed point units, and we use 24.8 fixed point everywhere.
- ss2, ss3 := 128, 64
- if width > 24 || height > 24 {
- ss2, ss3 = 2*ss2, 2*ss3
- if width > 120 || height > 120 {
- ss2, ss3 = 2*ss2, 2*ss3
- }
- }
- r.width = width
- r.splitScale2 = ss2
- r.splitScale3 = ss3
- r.cell = r.cellBuf[:0]
- if height > len(r.cellIndexBuf) {
- r.cellIndex = make([]int, height)
- } else {
- r.cellIndex = r.cellIndexBuf[:height]
- }
- r.Clear()
-}
-
-// NewRasterizer creates a new Rasterizer with the given bounds.
-func NewRasterizer(width, height int) *Rasterizer {
- r := new(Rasterizer)
- r.SetBounds(width, height)
- return r
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/stroke.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/stroke.go
deleted file mode 100644
index d49b1cee9..000000000
--- a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/stroke.go
+++ /dev/null
@@ -1,466 +0,0 @@
-// Copyright 2010 The Freetype-Go Authors. All rights reserved.
-// Use of this source code is governed by your choice of either the
-// FreeType License or the GNU General Public License version 2 (or
-// any later version), both of which can be found in the LICENSE file.
-
-package raster
-
-// Two points are considered practically equal if the square of the distance
-// between them is less than one quarter (i.e. 16384 / 65536 in Fix64).
-const epsilon = 16384
-
-// A Capper signifies how to begin or end a stroked path.
-type Capper interface {
- // Cap adds a cap to p given a pivot point and the normal vector of a
- // terminal segment. The normal's length is half of the stroke width.
- Cap(p Adder, halfWidth Fix32, pivot, n1 Point)
-}
-
-// The CapperFunc type adapts an ordinary function to be a Capper.
-type CapperFunc func(Adder, Fix32, Point, Point)
-
-func (f CapperFunc) Cap(p Adder, halfWidth Fix32, pivot, n1 Point) {
- f(p, halfWidth, pivot, n1)
-}
-
-// A Joiner signifies how to join interior nodes of a stroked path.
-type Joiner interface {
- // Join adds a join to the two sides of a stroked path given a pivot
- // point and the normal vectors of the trailing and leading segments.
- // Both normals have length equal to half of the stroke width.
- Join(lhs, rhs Adder, halfWidth Fix32, pivot, n0, n1 Point)
-}
-
-// The JoinerFunc type adapts an ordinary function to be a Joiner.
-type JoinerFunc func(lhs, rhs Adder, halfWidth Fix32, pivot, n0, n1 Point)
-
-func (f JoinerFunc) Join(lhs, rhs Adder, halfWidth Fix32, pivot, n0, n1 Point) {
- f(lhs, rhs, halfWidth, pivot, n0, n1)
-}
-
-// RoundCapper adds round caps to a stroked path.
-var RoundCapper Capper = CapperFunc(roundCapper)
-
-func roundCapper(p Adder, halfWidth Fix32, pivot, n1 Point) {
- // The cubic Bézier approximation to a circle involves the magic number
- // (√2 - 1) * 4/3, which is approximately 141/256.
- const k = 141
- e := n1.Rot90CCW()
- side := pivot.Add(e)
- start, end := pivot.Sub(n1), pivot.Add(n1)
- d, e := n1.Mul(k), e.Mul(k)
- p.Add3(start.Add(e), side.Sub(d), side)
- p.Add3(side.Add(d), end.Add(e), end)
-}
-
-// ButtCapper adds butt caps to a stroked path.
-var ButtCapper Capper = CapperFunc(buttCapper)
-
-func buttCapper(p Adder, halfWidth Fix32, pivot, n1 Point) {
- p.Add1(pivot.Add(n1))
-}
-
-// SquareCapper adds square caps to a stroked path.
-var SquareCapper Capper = CapperFunc(squareCapper)
-
-func squareCapper(p Adder, halfWidth Fix32, pivot, n1 Point) {
- e := n1.Rot90CCW()
- side := pivot.Add(e)
- p.Add1(side.Sub(n1))
- p.Add1(side.Add(n1))
- p.Add1(pivot.Add(n1))
-}
-
-// RoundJoiner adds round joins to a stroked path.
-var RoundJoiner Joiner = JoinerFunc(roundJoiner)
-
-func roundJoiner(lhs, rhs Adder, haflWidth Fix32, pivot, n0, n1 Point) {
- dot := n0.Rot90CW().Dot(n1)
- if dot >= 0 {
- addArc(lhs, pivot, n0, n1)
- rhs.Add1(pivot.Sub(n1))
- } else {
- lhs.Add1(pivot.Add(n1))
- addArc(rhs, pivot, n0.Neg(), n1.Neg())
- }
-}
-
-// BevelJoiner adds bevel joins to a stroked path.
-var BevelJoiner Joiner = JoinerFunc(bevelJoiner)
-
-func bevelJoiner(lhs, rhs Adder, haflWidth Fix32, pivot, n0, n1 Point) {
- lhs.Add1(pivot.Add(n1))
- rhs.Add1(pivot.Sub(n1))
-}
-
-// addArc adds a circular arc from pivot+n0 to pivot+n1 to p. The shorter of
-// the two possible arcs is taken, i.e. the one spanning <= 180 degrees.
-// The two vectors n0 and n1 must be of equal length.
-func addArc(p Adder, pivot, n0, n1 Point) {
- // r2 is the square of the length of n0.
- r2 := n0.Dot(n0)
- if r2 < epsilon {
- // The arc radius is so small that we collapse to a straight line.
- p.Add1(pivot.Add(n1))
- return
- }
- // We approximate the arc by 0, 1, 2 or 3 45-degree quadratic segments plus
- // a final quadratic segment from s to n1. Each 45-degree segment has control
- // points {1, 0}, {1, tan(π/8)} and {1/√2, 1/√2} suitably scaled, rotated and
- // translated. tan(π/8) is approximately 106/256.
- const tpo8 = 106
- var s Point
- // We determine which octant the angle between n0 and n1 is in via three dot products.
- // m0, m1 and m2 are n0 rotated clockwise by 45, 90 and 135 degrees.
- m0 := n0.Rot45CW()
- m1 := n0.Rot90CW()
- m2 := m0.Rot90CW()
- if m1.Dot(n1) >= 0 {
- if n0.Dot(n1) >= 0 {
- if m2.Dot(n1) <= 0 {
- // n1 is between 0 and 45 degrees clockwise of n0.
- s = n0
- } else {
- // n1 is between 45 and 90 degrees clockwise of n0.
- p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0))
- s = m0
- }
- } else {
- pm1, n0t := pivot.Add(m1), n0.Mul(tpo8)
- p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0))
- p.Add2(pm1.Add(n0t), pm1)
- if m0.Dot(n1) >= 0 {
- // n1 is between 90 and 135 degrees clockwise of n0.
- s = m1
- } else {
- // n1 is between 135 and 180 degrees clockwise of n0.
- p.Add2(pm1.Sub(n0t), pivot.Add(m2))
- s = m2
- }
- }
- } else {
- if n0.Dot(n1) >= 0 {
- if m0.Dot(n1) >= 0 {
- // n1 is between 0 and 45 degrees counter-clockwise of n0.
- s = n0
- } else {
- // n1 is between 45 and 90 degrees counter-clockwise of n0.
- p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2))
- s = m2.Neg()
- }
- } else {
- pm1, n0t := pivot.Sub(m1), n0.Mul(tpo8)
- p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2))
- p.Add2(pm1.Add(n0t), pm1)
- if m2.Dot(n1) <= 0 {
- // n1 is between 90 and 135 degrees counter-clockwise of n0.
- s = m1.Neg()
- } else {
- // n1 is between 135 and 180 degrees counter-clockwise of n0.
- p.Add2(pm1.Sub(n0t), pivot.Sub(m0))
- s = m0.Neg()
- }
- }
- }
- // The final quadratic segment has two endpoints s and n1 and the middle
- // control point is a multiple of s.Add(n1), i.e. it is on the angle bisector
- // of those two points. The multiple ranges between 128/256 and 150/256 as
- // the angle between s and n1 ranges between 0 and 45 degrees.
- // When the angle is 0 degrees (i.e. s and n1 are coincident) then s.Add(n1)
- // is twice s and so the middle control point of the degenerate quadratic
- // segment should be half s.Add(n1), and half = 128/256.
- // When the angle is 45 degrees then 150/256 is the ratio of the lengths of
- // the two vectors {1, tan(π/8)} and {1 + 1/√2, 1/√2}.
- // d is the normalized dot product between s and n1. Since the angle ranges
- // between 0 and 45 degrees then d ranges between 256/256 and 181/256.
- d := 256 * s.Dot(n1) / r2
- multiple := Fix32(150 - 22*(d-181)/(256-181))
- p.Add2(pivot.Add(s.Add(n1).Mul(multiple)), pivot.Add(n1))
-}
-
-// midpoint returns the midpoint of two Points.
-func midpoint(a, b Point) Point {
- return Point{(a.X + b.X) / 2, (a.Y + b.Y) / 2}
-}
-
-// angleGreaterThan45 returns whether the angle between two vectors is more
-// than 45 degrees.
-func angleGreaterThan45(v0, v1 Point) bool {
- v := v0.Rot45CCW()
- return v.Dot(v1) < 0 || v.Rot90CW().Dot(v1) < 0
-}
-
-// interpolate returns the point (1-t)*a + t*b.
-func interpolate(a, b Point, t Fix64) Point {
- s := 65536 - t
- x := s*Fix64(a.X) + t*Fix64(b.X)
- y := s*Fix64(a.Y) + t*Fix64(b.Y)
- return Point{Fix32(x >> 16), Fix32(y >> 16)}
-}
-
-// curviest2 returns the value of t for which the quadratic parametric curve
-// (1-t)²*a + 2*t*(1-t).b + t²*c has maximum curvature.
-//
-// The curvature of the parametric curve f(t) = (x(t), y(t)) is
-// |x′y″-y′x″| / (x′²+y′²)^(3/2).
-//
-// Let d = b-a and e = c-2*b+a, so that f′(t) = 2*d+2*e*t and f″(t) = 2*e.
-// The curvature's numerator is (2*dx+2*ex*t)*(2*ey)-(2*dy+2*ey*t)*(2*ex),
-// which simplifies to 4*dx*ey-4*dy*ex, which is constant with respect to t.
-//
-// Thus, curvature is extreme where the denominator is extreme, i.e. where
-// (x′²+y′²) is extreme. The first order condition is that
-// 2*x′*x″+2*y′*y″ = 0, or (dx+ex*t)*ex + (dy+ey*t)*ey = 0.
-// Solving for t gives t = -(dx*ex+dy*ey) / (ex*ex+ey*ey).
-func curviest2(a, b, c Point) Fix64 {
- dx := int64(b.X - a.X)
- dy := int64(b.Y - a.Y)
- ex := int64(c.X - 2*b.X + a.X)
- ey := int64(c.Y - 2*b.Y + a.Y)
- if ex == 0 && ey == 0 {
- return 32768
- }
- return Fix64(-65536 * (dx*ex + dy*ey) / (ex*ex + ey*ey))
-}
-
-// A stroker holds state for stroking a path.
-type stroker struct {
- // p is the destination that records the stroked path.
- p Adder
- // u is the half-width of the stroke.
- u Fix32
- // cr and jr specify how to end and connect path segments.
- cr Capper
- jr Joiner
- // r is the reverse path. Stroking a path involves constructing two
- // parallel paths 2*u apart. The first path is added immediately to p,
- // the second path is accumulated in r and eventually added in reverse.
- r Path
- // a is the most recent segment point. anorm is the segment normal of
- // length u at that point.
- a, anorm Point
-}
-
-// addNonCurvy2 adds a quadratic segment to the stroker, where the segment
-// defined by (k.a, b, c) achieves maximum curvature at either k.a or c.
-func (k *stroker) addNonCurvy2(b, c Point) {
- // We repeatedly divide the segment at its middle until it is straight
- // enough to approximate the stroke by just translating the control points.
- // ds and ps are stacks of depths and points. t is the top of the stack.
- const maxDepth = 5
- var (
- ds [maxDepth + 1]int
- ps [2*maxDepth + 3]Point
- t int
- )
- // Initially the ps stack has one quadratic segment of depth zero.
- ds[0] = 0
- ps[2] = k.a
- ps[1] = b
- ps[0] = c
- anorm := k.anorm
- var cnorm Point
-
- for {
- depth := ds[t]
- a := ps[2*t+2]
- b := ps[2*t+1]
- c := ps[2*t+0]
- ab := b.Sub(a)
- bc := c.Sub(b)
- abIsSmall := ab.Dot(ab) < Fix64(1<<16)
- bcIsSmall := bc.Dot(bc) < Fix64(1<<16)
- if abIsSmall && bcIsSmall {
- // Approximate the segment by a circular arc.
- cnorm = bc.Norm(k.u).Rot90CCW()
- mac := midpoint(a, c)
- addArc(k.p, mac, anorm, cnorm)
- addArc(&k.r, mac, anorm.Neg(), cnorm.Neg())
- } else if depth < maxDepth && angleGreaterThan45(ab, bc) {
- // Divide the segment in two and push both halves on the stack.
- mab := midpoint(a, b)
- mbc := midpoint(b, c)
- t++
- ds[t+0] = depth + 1
- ds[t-1] = depth + 1
- ps[2*t+2] = a
- ps[2*t+1] = mab
- ps[2*t+0] = midpoint(mab, mbc)
- ps[2*t-1] = mbc
- continue
- } else {
- // Translate the control points.
- bnorm := c.Sub(a).Norm(k.u).Rot90CCW()
- cnorm = bc.Norm(k.u).Rot90CCW()
- k.p.Add2(b.Add(bnorm), c.Add(cnorm))
- k.r.Add2(b.Sub(bnorm), c.Sub(cnorm))
- }
- if t == 0 {
- k.a, k.anorm = c, cnorm
- return
- }
- t--
- anorm = cnorm
- }
- panic("unreachable")
-}
-
-// Add1 adds a linear segment to the stroker.
-func (k *stroker) Add1(b Point) {
- bnorm := b.Sub(k.a).Norm(k.u).Rot90CCW()
- if len(k.r) == 0 {
- k.p.Start(k.a.Add(bnorm))
- k.r.Start(k.a.Sub(bnorm))
- } else {
- k.jr.Join(k.p, &k.r, k.u, k.a, k.anorm, bnorm)
- }
- k.p.Add1(b.Add(bnorm))
- k.r.Add1(b.Sub(bnorm))
- k.a, k.anorm = b, bnorm
-}
-
-// Add2 adds a quadratic segment to the stroker.
-func (k *stroker) Add2(b, c Point) {
- ab := b.Sub(k.a)
- bc := c.Sub(b)
- abnorm := ab.Norm(k.u).Rot90CCW()
- if len(k.r) == 0 {
- k.p.Start(k.a.Add(abnorm))
- k.r.Start(k.a.Sub(abnorm))
- } else {
- k.jr.Join(k.p, &k.r, k.u, k.a, k.anorm, abnorm)
- }
-
- // Approximate nearly-degenerate quadratics by linear segments.
- abIsSmall := ab.Dot(ab) < epsilon
- bcIsSmall := bc.Dot(bc) < epsilon
- if abIsSmall || bcIsSmall {
- acnorm := c.Sub(k.a).Norm(k.u).Rot90CCW()
- k.p.Add1(c.Add(acnorm))
- k.r.Add1(c.Sub(acnorm))
- k.a, k.anorm = c, acnorm
- return
- }
-
- // The quadratic segment (k.a, b, c) has a point of maximum curvature.
- // If this occurs at an end point, we process the segment as a whole.
- t := curviest2(k.a, b, c)
- if t <= 0 || t >= 65536 {
- k.addNonCurvy2(b, c)
- return
- }
-
- // Otherwise, we perform a de Casteljau decomposition at the point of
- // maximum curvature and process the two straighter parts.
- mab := interpolate(k.a, b, t)
- mbc := interpolate(b, c, t)
- mabc := interpolate(mab, mbc, t)
-
- // If the vectors ab and bc are close to being in opposite directions,
- // then the decomposition can become unstable, so we approximate the
- // quadratic segment by two linear segments joined by an arc.
- bcnorm := bc.Norm(k.u).Rot90CCW()
- if abnorm.Dot(bcnorm) < -Fix64(k.u)*Fix64(k.u)*2047/2048 {
- pArc := abnorm.Dot(bc) < 0
-
- k.p.Add1(mabc.Add(abnorm))
- if pArc {
- z := abnorm.Rot90CW()
- addArc(k.p, mabc, abnorm, z)
- addArc(k.p, mabc, z, bcnorm)
- }
- k.p.Add1(mabc.Add(bcnorm))
- k.p.Add1(c.Add(bcnorm))
-
- k.r.Add1(mabc.Sub(abnorm))
- if !pArc {
- z := abnorm.Rot90CW()
- addArc(&k.r, mabc, abnorm.Neg(), z)
- addArc(&k.r, mabc, z, bcnorm.Neg())
- }
- k.r.Add1(mabc.Sub(bcnorm))
- k.r.Add1(c.Sub(bcnorm))
-
- k.a, k.anorm = c, bcnorm
- return
- }
-
- // Process the decomposed parts.
- k.addNonCurvy2(mab, mabc)
- k.addNonCurvy2(mbc, c)
-}
-
-// Add3 adds a cubic segment to the stroker.
-func (k *stroker) Add3(b, c, d Point) {
- panic("freetype/raster: stroke unimplemented for cubic segments")
-}
-
-// stroke adds the stroked Path q to p, where q consists of exactly one curve.
-func (k *stroker) stroke(q Path) {
- // Stroking is implemented by deriving two paths each k.u apart from q.
- // The left-hand-side path is added immediately to k.p; the right-hand-side
- // path is accumulated in k.r. Once we've finished adding the LHS to k.p,
- // we add the RHS in reverse order.
- k.r = make(Path, 0, len(q))
- k.a = Point{q[1], q[2]}
- for i := 4; i < len(q); {
- switch q[i] {
- case 1:
- k.Add1(Point{q[i+1], q[i+2]})
- i += 4
- case 2:
- k.Add2(Point{q[i+1], q[i+2]}, Point{q[i+3], q[i+4]})
- i += 6
- case 3:
- k.Add3(Point{q[i+1], q[i+2]}, Point{q[i+3], q[i+4]}, Point{q[i+5], q[i+6]})
- i += 8
- default:
- panic("freetype/raster: bad path")
- }
- }
- if len(k.r) == 0 {
- return
- }
- // TODO(nigeltao): if q is a closed curve then we should join the first and
- // last segments instead of capping them.
- k.cr.Cap(k.p, k.u, q.lastPoint(), k.anorm.Neg())
- addPathReversed(k.p, k.r)
- pivot := q.firstPoint()
- k.cr.Cap(k.p, k.u, pivot, pivot.Sub(Point{k.r[1], k.r[2]}))
-}
-
-// Stroke adds q stroked with the given width to p. The result is typically
-// self-intersecting and should be rasterized with UseNonZeroWinding.
-// cr and jr may be nil, which defaults to a RoundCapper or RoundJoiner.
-func Stroke(p Adder, q Path, width Fix32, cr Capper, jr Joiner) {
- if len(q) == 0 {
- return
- }
- if cr == nil {
- cr = RoundCapper
- }
- if jr == nil {
- jr = RoundJoiner
- }
- if q[0] != 0 {
- panic("freetype/raster: bad path")
- }
- s := stroker{p: p, u: width / 2, cr: cr, jr: jr}
- i := 0
- for j := 4; j < len(q); {
- switch q[j] {
- case 0:
- s.stroke(q[i:j])
- i, j = j, j+4
- case 1:
- j += 4
- case 2:
- j += 6
- case 3:
- j += 8
- default:
- panic("freetype/raster: bad path")
- }
- }
- s.stroke(q[i:])
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/glyph.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/glyph.go
deleted file mode 100644
index b5f327851..000000000
--- a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/glyph.go
+++ /dev/null
@@ -1,530 +0,0 @@
-// Copyright 2010 The Freetype-Go Authors. All rights reserved.
-// Use of this source code is governed by your choice of either the
-// FreeType License or the GNU General Public License version 2 (or
-// any later version), both of which can be found in the LICENSE file.
-
-package truetype
-
-// Hinting is the policy for snapping a glyph's contours to pixel boundaries.
-type Hinting int32
-
-const (
- // NoHinting means to not perform any hinting.
- NoHinting Hinting = iota
- // FullHinting means to use the font's hinting instructions.
- FullHinting
-
- // TODO: implement VerticalHinting.
-)
-
-// A Point is a co-ordinate pair plus whether it is ``on'' a contour or an
-// ``off'' control point.
-type Point struct {
- X, Y int32
- // The Flags' LSB means whether or not this Point is ``on'' the contour.
- // Other bits are reserved for internal use.
- Flags uint32
-}
-
-// A GlyphBuf holds a glyph's contours. A GlyphBuf can be re-used to load a
-// series of glyphs from a Font.
-type GlyphBuf struct {
- // AdvanceWidth is the glyph's advance width.
- AdvanceWidth int32
- // B is the glyph's bounding box.
- B Bounds
- // Point contains all Points from all contours of the glyph. If
- // hinting was used to load a glyph then Unhinted contains those
- // Points before they were hinted, and InFontUnits contains those
- // Points before they were hinted and scaled.
- Point, Unhinted, InFontUnits []Point
- // End is the point indexes of the end point of each countour. The
- // length of End is the number of contours in the glyph. The i'th
- // contour consists of points Point[End[i-1]:End[i]], where End[-1]
- // is interpreted to mean zero.
- End []int
-
- font *Font
- scale int32
- hinting Hinting
- hinter hinter
- // phantomPoints are the co-ordinates of the synthetic phantom points
- // used for hinting and bounding box calculations.
- phantomPoints [4]Point
- // pp1x is the X co-ordinate of the first phantom point. The '1' is
- // using 1-based indexing; pp1x is almost always phantomPoints[0].X.
- // TODO: eliminate this and consistently use phantomPoints[0].X.
- pp1x int32
- // metricsSet is whether the glyph's metrics have been set yet. For a
- // compound glyph, a sub-glyph may override the outer glyph's metrics.
- metricsSet bool
- // tmp is a scratch buffer.
- tmp []Point
-}
-
-// Flags for decoding a glyph's contours. These flags are documented at
-// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
-const (
- flagOnCurve = 1 << iota
- flagXShortVector
- flagYShortVector
- flagRepeat
- flagPositiveXShortVector
- flagPositiveYShortVector
-
- // The remaining flags are for internal use.
- flagTouchedX
- flagTouchedY
-)
-
-// The same flag bits (0x10 and 0x20) are overloaded to have two meanings,
-// dependent on the value of the flag{X,Y}ShortVector bits.
-const (
- flagThisXIsSame = flagPositiveXShortVector
- flagThisYIsSame = flagPositiveYShortVector
-)
-
-// Load loads a glyph's contours from a Font, overwriting any previously
-// loaded contours for this GlyphBuf. scale is the number of 26.6 fixed point
-// units in 1 em, i is the glyph index, and h is the hinting policy.
-func (g *GlyphBuf) Load(f *Font, scale int32, i Index, h Hinting) error {
- g.Point = g.Point[:0]
- g.Unhinted = g.Unhinted[:0]
- g.InFontUnits = g.InFontUnits[:0]
- g.End = g.End[:0]
- g.font = f
- g.hinting = h
- g.scale = scale
- g.pp1x = 0
- g.phantomPoints = [4]Point{}
- g.metricsSet = false
-
- if h != NoHinting {
- if err := g.hinter.init(f, scale); err != nil {
- return err
- }
- }
- if err := g.load(0, i, true); err != nil {
- return err
- }
- // TODO: this selection of either g.pp1x or g.phantomPoints[0].X isn't ideal,
- // and should be cleaned up once we have all the testScaling tests passing,
- // plus additional tests for Freetype-Go's bounding boxes matching C Freetype's.
- pp1x := g.pp1x
- if h != NoHinting {
- pp1x = g.phantomPoints[0].X
- }
- if pp1x != 0 {
- for i := range g.Point {
- g.Point[i].X -= pp1x
- }
- }
-
- advanceWidth := g.phantomPoints[1].X - g.phantomPoints[0].X
- if h != NoHinting {
- if len(f.hdmx) >= 8 {
- if n := u32(f.hdmx, 4); n > 3+uint32(i) {
- for hdmx := f.hdmx[8:]; uint32(len(hdmx)) >= n; hdmx = hdmx[n:] {
- if int32(hdmx[0]) == scale>>6 {
- advanceWidth = int32(hdmx[2+i]) << 6
- break
- }
- }
- }
- }
- advanceWidth = (advanceWidth + 32) &^ 63
- }
- g.AdvanceWidth = advanceWidth
-
- // Set g.B to the 'control box', which is the bounding box of the Bézier
- // curves' control points. This is easier to calculate, no smaller than
- // and often equal to the tightest possible bounding box of the curves
- // themselves. This approach is what C Freetype does. We can't just scale
- // the nominal bounding box in the glyf data as the hinting process and
- // phantom point adjustment may move points outside of that box.
- if len(g.Point) == 0 {
- g.B = Bounds{}
- } else {
- p := g.Point[0]
- g.B.XMin = p.X
- g.B.XMax = p.X
- g.B.YMin = p.Y
- g.B.YMax = p.Y
- for _, p := range g.Point[1:] {
- if g.B.XMin > p.X {
- g.B.XMin = p.X
- } else if g.B.XMax < p.X {
- g.B.XMax = p.X
- }
- if g.B.YMin > p.Y {
- g.B.YMin = p.Y
- } else if g.B.YMax < p.Y {
- g.B.YMax = p.Y
- }
- }
- // Snap the box to the grid, if hinting is on.
- if h != NoHinting {
- g.B.XMin &^= 63
- g.B.YMin &^= 63
- g.B.XMax += 63
- g.B.XMax &^= 63
- g.B.YMax += 63
- g.B.YMax &^= 63
- }
- }
- return nil
-}
-
-func (g *GlyphBuf) load(recursion int32, i Index, useMyMetrics bool) (err error) {
- // The recursion limit here is arbitrary, but defends against malformed glyphs.
- if recursion >= 32 {
- return UnsupportedError("excessive compound glyph recursion")
- }
- // Find the relevant slice of g.font.glyf.
- var g0, g1 uint32
- if g.font.locaOffsetFormat == locaOffsetFormatShort {
- g0 = 2 * uint32(u16(g.font.loca, 2*int(i)))
- g1 = 2 * uint32(u16(g.font.loca, 2*int(i)+2))
- } else {
- g0 = u32(g.font.loca, 4*int(i))
- g1 = u32(g.font.loca, 4*int(i)+4)
- }
-
- // Decode the contour count and nominal bounding box, from the first
- // 10 bytes of the glyf data. boundsYMin and boundsXMax, at offsets 4
- // and 6, are unused.
- glyf, ne, boundsXMin, boundsYMax := []byte(nil), 0, int32(0), int32(0)
- if g0+10 <= g1 {
- glyf = g.font.glyf[g0:g1]
- ne = int(int16(u16(glyf, 0)))
- boundsXMin = int32(int16(u16(glyf, 2)))
- boundsYMax = int32(int16(u16(glyf, 8)))
- }
-
- // Create the phantom points.
- uhm, pp1x := g.font.unscaledHMetric(i), int32(0)
- uvm := g.font.unscaledVMetric(i, boundsYMax)
- g.phantomPoints = [4]Point{
- {X: boundsXMin - uhm.LeftSideBearing},
- {X: boundsXMin - uhm.LeftSideBearing + uhm.AdvanceWidth},
- {X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing},
- {X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing - uvm.AdvanceHeight},
- }
- if len(glyf) == 0 {
- g.addPhantomsAndScale(len(g.Point), len(g.Point), true, true)
- copy(g.phantomPoints[:], g.Point[len(g.Point)-4:])
- g.Point = g.Point[:len(g.Point)-4]
- return nil
- }
-
- // Load and hint the contours.
- if ne < 0 {
- if ne != -1 {
- // http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html says that
- // "the values -2, -3, and so forth, are reserved for future use."
- return UnsupportedError("negative number of contours")
- }
- pp1x = g.font.scale(g.scale * (boundsXMin - uhm.LeftSideBearing))
- if err := g.loadCompound(recursion, uhm, i, glyf, useMyMetrics); err != nil {
- return err
- }
- } else {
- np0, ne0 := len(g.Point), len(g.End)
- program := g.loadSimple(glyf, ne)
- g.addPhantomsAndScale(np0, np0, true, true)
- pp1x = g.Point[len(g.Point)-4].X
- if g.hinting != NoHinting {
- if len(program) != 0 {
- err := g.hinter.run(
- program,
- g.Point[np0:],
- g.Unhinted[np0:],
- g.InFontUnits[np0:],
- g.End[ne0:],
- )
- if err != nil {
- return err
- }
- }
- // Drop the four phantom points.
- g.InFontUnits = g.InFontUnits[:len(g.InFontUnits)-4]
- g.Unhinted = g.Unhinted[:len(g.Unhinted)-4]
- }
- if useMyMetrics {
- copy(g.phantomPoints[:], g.Point[len(g.Point)-4:])
- }
- g.Point = g.Point[:len(g.Point)-4]
- if np0 != 0 {
- // The hinting program expects the []End values to be indexed relative
- // to the inner glyph, not the outer glyph, so we delay adding np0 until
- // after the hinting program (if any) has run.
- for i := ne0; i < len(g.End); i++ {
- g.End[i] += np0
- }
- }
- }
- if useMyMetrics && !g.metricsSet {
- g.metricsSet = true
- g.pp1x = pp1x
- }
- return nil
-}
-
-// loadOffset is the initial offset for loadSimple and loadCompound. The first
-// 10 bytes are the number of contours and the bounding box.
-const loadOffset = 10
-
-func (g *GlyphBuf) loadSimple(glyf []byte, ne int) (program []byte) {
- offset := loadOffset
- for i := 0; i < ne; i++ {
- g.End = append(g.End, 1+int(u16(glyf, offset)))
- offset += 2
- }
-
- // Note the TrueType hinting instructions.
- instrLen := int(u16(glyf, offset))
- offset += 2
- program = glyf[offset : offset+instrLen]
- offset += instrLen
-
- np0 := len(g.Point)
- np1 := np0 + int(g.End[len(g.End)-1])
-
- // Decode the flags.
- for i := np0; i < np1; {
- c := uint32(glyf[offset])
- offset++
- g.Point = append(g.Point, Point{Flags: c})
- i++
- if c&flagRepeat != 0 {
- count := glyf[offset]
- offset++
- for ; count > 0; count-- {
- g.Point = append(g.Point, Point{Flags: c})
- i++
- }
- }
- }
-
- // Decode the co-ordinates.
- var x int16
- for i := np0; i < np1; i++ {
- f := g.Point[i].Flags
- if f&flagXShortVector != 0 {
- dx := int16(glyf[offset])
- offset++
- if f&flagPositiveXShortVector == 0 {
- x -= dx
- } else {
- x += dx
- }
- } else if f&flagThisXIsSame == 0 {
- x += int16(u16(glyf, offset))
- offset += 2
- }
- g.Point[i].X = int32(x)
- }
- var y int16
- for i := np0; i < np1; i++ {
- f := g.Point[i].Flags
- if f&flagYShortVector != 0 {
- dy := int16(glyf[offset])
- offset++
- if f&flagPositiveYShortVector == 0 {
- y -= dy
- } else {
- y += dy
- }
- } else if f&flagThisYIsSame == 0 {
- y += int16(u16(glyf, offset))
- offset += 2
- }
- g.Point[i].Y = int32(y)
- }
-
- return program
-}
-
-func (g *GlyphBuf) loadCompound(recursion int32, uhm HMetric, i Index,
- glyf []byte, useMyMetrics bool) error {
-
- // Flags for decoding a compound glyph. These flags are documented at
- // http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
- const (
- flagArg1And2AreWords = 1 << iota
- flagArgsAreXYValues
- flagRoundXYToGrid
- flagWeHaveAScale
- flagUnused
- flagMoreComponents
- flagWeHaveAnXAndYScale
- flagWeHaveATwoByTwo
- flagWeHaveInstructions
- flagUseMyMetrics
- flagOverlapCompound
- )
- np0, ne0 := len(g.Point), len(g.End)
- offset := loadOffset
- for {
- flags := u16(glyf, offset)
- component := Index(u16(glyf, offset+2))
- dx, dy, transform, hasTransform := int32(0), int32(0), [4]int32{}, false
- if flags&flagArg1And2AreWords != 0 {
- dx = int32(int16(u16(glyf, offset+4)))
- dy = int32(int16(u16(glyf, offset+6)))
- offset += 8
- } else {
- dx = int32(int16(int8(glyf[offset+4])))
- dy = int32(int16(int8(glyf[offset+5])))
- offset += 6
- }
- if flags&flagArgsAreXYValues == 0 {
- return UnsupportedError("compound glyph transform vector")
- }
- if flags&(flagWeHaveAScale|flagWeHaveAnXAndYScale|flagWeHaveATwoByTwo) != 0 {
- hasTransform = true
- switch {
- case flags&flagWeHaveAScale != 0:
- transform[0] = int32(int16(u16(glyf, offset+0)))
- transform[3] = transform[0]
- offset += 2
- case flags&flagWeHaveAnXAndYScale != 0:
- transform[0] = int32(int16(u16(glyf, offset+0)))
- transform[3] = int32(int16(u16(glyf, offset+2)))
- offset += 4
- case flags&flagWeHaveATwoByTwo != 0:
- transform[0] = int32(int16(u16(glyf, offset+0)))
- transform[1] = int32(int16(u16(glyf, offset+2)))
- transform[2] = int32(int16(u16(glyf, offset+4)))
- transform[3] = int32(int16(u16(glyf, offset+6)))
- offset += 8
- }
- }
- savedPP := g.phantomPoints
- np0 := len(g.Point)
- componentUMM := useMyMetrics && (flags&flagUseMyMetrics != 0)
- if err := g.load(recursion+1, component, componentUMM); err != nil {
- return err
- }
- if flags&flagUseMyMetrics == 0 {
- g.phantomPoints = savedPP
- }
- if hasTransform {
- for j := np0; j < len(g.Point); j++ {
- p := &g.Point[j]
- newX := int32((int64(p.X)*int64(transform[0])+1<<13)>>14) +
- int32((int64(p.Y)*int64(transform[2])+1<<13)>>14)
- newY := int32((int64(p.X)*int64(transform[1])+1<<13)>>14) +
- int32((int64(p.Y)*int64(transform[3])+1<<13)>>14)
- p.X, p.Y = newX, newY
- }
- }
- dx = g.font.scale(g.scale * dx)
- dy = g.font.scale(g.scale * dy)
- if flags&flagRoundXYToGrid != 0 {
- dx = (dx + 32) &^ 63
- dy = (dy + 32) &^ 63
- }
- for j := np0; j < len(g.Point); j++ {
- p := &g.Point[j]
- p.X += dx
- p.Y += dy
- }
- // TODO: also adjust g.InFontUnits and g.Unhinted?
- if flags&flagMoreComponents == 0 {
- break
- }
- }
-
- instrLen := 0
- if g.hinting != NoHinting && offset+2 <= len(glyf) {
- instrLen = int(u16(glyf, offset))
- offset += 2
- }
-
- g.addPhantomsAndScale(np0, len(g.Point), false, instrLen > 0)
- points, ends := g.Point[np0:], g.End[ne0:]
- g.Point = g.Point[:len(g.Point)-4]
- for j := range points {
- points[j].Flags &^= flagTouchedX | flagTouchedY
- }
-
- if instrLen == 0 {
- if !g.metricsSet {
- copy(g.phantomPoints[:], points[len(points)-4:])
- }
- return nil
- }
-
- // Hint the compound glyph.
- program := glyf[offset : offset+instrLen]
- // Temporarily adjust the ends to be relative to this compound glyph.
- if np0 != 0 {
- for i := range ends {
- ends[i] -= np0
- }
- }
- // Hinting instructions of a composite glyph completely refer to the
- // (already) hinted subglyphs.
- g.tmp = append(g.tmp[:0], points...)
- if err := g.hinter.run(program, points, g.tmp, g.tmp, ends); err != nil {
- return err
- }
- if np0 != 0 {
- for i := range ends {
- ends[i] += np0
- }
- }
- if !g.metricsSet {
- copy(g.phantomPoints[:], points[len(points)-4:])
- }
- return nil
-}
-
-func (g *GlyphBuf) addPhantomsAndScale(np0, np1 int, simple, adjust bool) {
- // Add the four phantom points.
- g.Point = append(g.Point, g.phantomPoints[:]...)
- // Scale the points.
- if simple && g.hinting != NoHinting {
- g.InFontUnits = append(g.InFontUnits, g.Point[np1:]...)
- }
- for i := np1; i < len(g.Point); i++ {
- p := &g.Point[i]
- p.X = g.font.scale(g.scale * p.X)
- p.Y = g.font.scale(g.scale * p.Y)
- }
- if g.hinting == NoHinting {
- return
- }
- // Round the 1st phantom point to the grid, shifting all other points equally.
- // Note that "all other points" starts from np0, not np1.
- // TODO: delete this adjustment and the np0/np1 distinction, when
- // we update the compatibility tests to C Freetype 2.5.3.
- // See http://git.savannah.gnu.org/cgit/freetype/freetype2.git/commit/?id=05c786d990390a7ca18e62962641dac740bacb06
- if adjust {
- pp1x := g.Point[len(g.Point)-4].X
- if dx := ((pp1x + 32) &^ 63) - pp1x; dx != 0 {
- for i := np0; i < len(g.Point); i++ {
- g.Point[i].X += dx
- }
- }
- }
- if simple {
- g.Unhinted = append(g.Unhinted, g.Point[np1:]...)
- }
- // Round the 2nd and 4th phantom point to the grid.
- p := &g.Point[len(g.Point)-3]
- p.X = (p.X + 32) &^ 63
- p = &g.Point[len(g.Point)-1]
- p.Y = (p.Y + 32) &^ 63
-}
-
-// TODO: is this necessary? The zero-valued GlyphBuf is perfectly usable.
-
-// NewGlyphBuf returns a newly allocated GlyphBuf.
-func NewGlyphBuf() *GlyphBuf {
- return &GlyphBuf{
- Point: make([]Point, 0, 256),
- End: make([]int, 0, 32),
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint.go
deleted file mode 100644
index 26c631436..000000000
--- a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint.go
+++ /dev/null
@@ -1,1764 +0,0 @@
-// Copyright 2012 The Freetype-Go Authors. All rights reserved.
-// Use of this source code is governed by your choice of either the
-// FreeType License or the GNU General Public License version 2 (or
-// any later version), both of which can be found in the LICENSE file.
-
-package truetype
-
-// This file implements a Truetype bytecode interpreter.
-// The opcodes are described at https://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html
-
-import (
- "errors"
- "math"
-)
-
-const (
- twilightZone = 0
- glyphZone = 1
- numZone = 2
-)
-
-type pointType uint32
-
-const (
- current pointType = 0
- unhinted pointType = 1
- inFontUnits pointType = 2
- numPointType = 3
-)
-
-// callStackEntry is a bytecode call stack entry.
-type callStackEntry struct {
- program []byte
- pc int
- loopCount int32
-}
-
-// hinter implements bytecode hinting. A hinter can be re-used to hint a series
-// of glyphs from a Font.
-type hinter struct {
- stack, store []int32
-
- // functions is a map from function number to bytecode.
- functions map[int32][]byte
-
- // font and scale are the font and scale last used for this hinter.
- // Changing the font will require running the new font's fpgm bytecode.
- // Changing either will require running the font's prep bytecode.
- font *Font
- scale int32
-
- // gs and defaultGS are the current and default graphics state. The
- // default graphics state is the global default graphics state after
- // the font's fpgm and prep programs have been run.
- gs, defaultGS graphicsState
-
- // points and ends are the twilight zone's points, glyph's points
- // and glyph's contour boundaries.
- points [numZone][numPointType][]Point
- ends []int
-
- // scaledCVT is the lazily initialized scaled Control Value Table.
- scaledCVTInitialized bool
- scaledCVT []f26dot6
-}
-
-// graphicsState is described at https://developer.apple.com/fonts/TTRefMan/RM04/Chap4.html
-type graphicsState struct {
- // Projection vector, freedom vector and dual projection vector.
- pv, fv, dv [2]f2dot14
- // Reference points and zone pointers.
- rp, zp [3]int32
- // Control Value / Single Width Cut-In.
- controlValueCutIn, singleWidthCutIn, singleWidth f26dot6
- // Delta base / shift.
- deltaBase, deltaShift int32
- // Minimum distance.
- minDist f26dot6
- // Loop count.
- loop int32
- // Rounding policy.
- roundPeriod, roundPhase, roundThreshold f26dot6
- roundSuper45 bool
- // Auto-flip.
- autoFlip bool
-}
-
-var globalDefaultGS = graphicsState{
- pv: [2]f2dot14{0x4000, 0}, // Unit vector along the X axis.
- fv: [2]f2dot14{0x4000, 0},
- dv: [2]f2dot14{0x4000, 0},
- zp: [3]int32{1, 1, 1},
- controlValueCutIn: (17 << 6) / 16, // 17/16 as an f26dot6.
- deltaBase: 9,
- deltaShift: 3,
- minDist: 1 << 6, // 1 as an f26dot6.
- loop: 1,
- roundPeriod: 1 << 6, // 1 as an f26dot6.
- roundThreshold: 1 << 5, // 1/2 as an f26dot6.
- roundSuper45: false,
- autoFlip: true,
-}
-
-func resetTwilightPoints(f *Font, p []Point) []Point {
- if n := int(f.maxTwilightPoints) + 4; n <= cap(p) {
- p = p[:n]
- for i := range p {
- p[i] = Point{}
- }
- } else {
- p = make([]Point, n)
- }
- return p
-}
-
-func (h *hinter) init(f *Font, scale int32) error {
- h.points[twilightZone][0] = resetTwilightPoints(f, h.points[twilightZone][0])
- h.points[twilightZone][1] = resetTwilightPoints(f, h.points[twilightZone][1])
- h.points[twilightZone][2] = resetTwilightPoints(f, h.points[twilightZone][2])
-
- rescale := h.scale != scale
- if h.font != f {
- h.font, rescale = f, true
- if h.functions == nil {
- h.functions = make(map[int32][]byte)
- } else {
- for k := range h.functions {
- delete(h.functions, k)
- }
- }
-
- if x := int(f.maxStackElements); x > len(h.stack) {
- x += 255
- x &^= 255
- h.stack = make([]int32, x)
- }
- if x := int(f.maxStorage); x > len(h.store) {
- x += 15
- x &^= 15
- h.store = make([]int32, x)
- }
- if len(f.fpgm) != 0 {
- if err := h.run(f.fpgm, nil, nil, nil, nil); err != nil {
- return err
- }
- }
- }
-
- if rescale {
- h.scale = scale
- h.scaledCVTInitialized = false
-
- h.defaultGS = globalDefaultGS
-
- if len(f.prep) != 0 {
- if err := h.run(f.prep, nil, nil, nil, nil); err != nil {
- return err
- }
- h.defaultGS = h.gs
- // The MS rasterizer doesn't allow the following graphics state
- // variables to be modified by the CVT program.
- h.defaultGS.pv = globalDefaultGS.pv
- h.defaultGS.fv = globalDefaultGS.fv
- h.defaultGS.dv = globalDefaultGS.dv
- h.defaultGS.rp = globalDefaultGS.rp
- h.defaultGS.zp = globalDefaultGS.zp
- h.defaultGS.loop = globalDefaultGS.loop
- }
- }
- return nil
-}
-
-func (h *hinter) run(program []byte, pCurrent, pUnhinted, pInFontUnits []Point, ends []int) error {
- h.gs = h.defaultGS
- h.points[glyphZone][current] = pCurrent
- h.points[glyphZone][unhinted] = pUnhinted
- h.points[glyphZone][inFontUnits] = pInFontUnits
- h.ends = ends
-
- if len(program) > 50000 {
- return errors.New("truetype: hinting: too many instructions")
- }
- var (
- steps, pc, top int
- opcode uint8
-
- callStack [32]callStackEntry
- callStackTop int
- )
-
- for 0 <= pc && pc < len(program) {
- steps++
- if steps == 100000 {
- return errors.New("truetype: hinting: too many steps")
- }
- opcode = program[pc]
- if top < int(popCount[opcode]) {
- return errors.New("truetype: hinting: stack underflow")
- }
- switch opcode {
-
- case opSVTCA0:
- h.gs.pv = [2]f2dot14{0, 0x4000}
- h.gs.fv = [2]f2dot14{0, 0x4000}
- h.gs.dv = [2]f2dot14{0, 0x4000}
-
- case opSVTCA1:
- h.gs.pv = [2]f2dot14{0x4000, 0}
- h.gs.fv = [2]f2dot14{0x4000, 0}
- h.gs.dv = [2]f2dot14{0x4000, 0}
-
- case opSPVTCA0:
- h.gs.pv = [2]f2dot14{0, 0x4000}
- h.gs.dv = [2]f2dot14{0, 0x4000}
-
- case opSPVTCA1:
- h.gs.pv = [2]f2dot14{0x4000, 0}
- h.gs.dv = [2]f2dot14{0x4000, 0}
-
- case opSFVTCA0:
- h.gs.fv = [2]f2dot14{0, 0x4000}
-
- case opSFVTCA1:
- h.gs.fv = [2]f2dot14{0x4000, 0}
-
- case opSPVTL0, opSPVTL1, opSFVTL0, opSFVTL1:
- top -= 2
- p1 := h.point(0, current, h.stack[top+0])
- p2 := h.point(0, current, h.stack[top+1])
- if p1 == nil || p2 == nil {
- return errors.New("truetype: hinting: point out of range")
- }
- dx := f2dot14(p1.X - p2.X)
- dy := f2dot14(p1.Y - p2.Y)
- if dx == 0 && dy == 0 {
- dx = 0x4000
- } else if opcode&1 != 0 {
- // Counter-clockwise rotation.
- dx, dy = -dy, dx
- }
- v := normalize(dx, dy)
- if opcode < opSFVTL0 {
- h.gs.pv = v
- h.gs.dv = v
- } else {
- h.gs.fv = v
- }
-
- case opSPVFS:
- top -= 2
- h.gs.pv = normalize(f2dot14(h.stack[top]), f2dot14(h.stack[top+1]))
- h.gs.dv = h.gs.pv
-
- case opSFVFS:
- top -= 2
- h.gs.fv = normalize(f2dot14(h.stack[top]), f2dot14(h.stack[top+1]))
-
- case opGPV:
- if top+1 >= len(h.stack) {
- return errors.New("truetype: hinting: stack overflow")
- }
- h.stack[top+0] = int32(h.gs.pv[0])
- h.stack[top+1] = int32(h.gs.pv[1])
- top += 2
-
- case opGFV:
- if top+1 >= len(h.stack) {
- return errors.New("truetype: hinting: stack overflow")
- }
- h.stack[top+0] = int32(h.gs.fv[0])
- h.stack[top+1] = int32(h.gs.fv[1])
- top += 2
-
- case opSFVTPV:
- h.gs.fv = h.gs.pv
-
- case opISECT:
- top -= 5
- p := h.point(2, current, h.stack[top+0])
- a0 := h.point(1, current, h.stack[top+1])
- a1 := h.point(1, current, h.stack[top+2])
- b0 := h.point(0, current, h.stack[top+3])
- b1 := h.point(0, current, h.stack[top+4])
- if p == nil || a0 == nil || a1 == nil || b0 == nil || b1 == nil {
- return errors.New("truetype: hinting: point out of range")
- }
-
- dbx := b1.X - b0.X
- dby := b1.Y - b0.Y
- dax := a1.X - a0.X
- day := a1.Y - a0.Y
- dx := b0.X - a0.X
- dy := b0.Y - a0.Y
- discriminant := mulDiv(int64(dax), int64(-dby), 0x40) +
- mulDiv(int64(day), int64(dbx), 0x40)
- dotProduct := mulDiv(int64(dax), int64(dbx), 0x40) +
- mulDiv(int64(day), int64(dby), 0x40)
- // The discriminant above is actually a cross product of vectors
- // da and db. Together with the dot product, they can be used as
- // surrogates for sine and cosine of the angle between the vectors.
- // Indeed,
- // dotproduct = |da||db|cos(angle)
- // discriminant = |da||db|sin(angle)
- // We use these equations to reject grazing intersections by
- // thresholding abs(tan(angle)) at 1/19, corresponding to 3 degrees.
- absDisc, absDotP := discriminant, dotProduct
- if absDisc < 0 {
- absDisc = -absDisc
- }
- if absDotP < 0 {
- absDotP = -absDotP
- }
- if 19*absDisc > absDotP {
- val := mulDiv(int64(dx), int64(-dby), 0x40) +
- mulDiv(int64(dy), int64(dbx), 0x40)
- rx := mulDiv(val, int64(dax), discriminant)
- ry := mulDiv(val, int64(day), discriminant)
- p.X = a0.X + int32(rx)
- p.Y = a0.Y + int32(ry)
- } else {
- p.X = (a0.X + a1.X + b0.X + b1.X) / 4
- p.Y = (a0.Y + a1.Y + b0.Y + b1.Y) / 4
- }
- p.Flags |= flagTouchedX | flagTouchedY
-
- case opSRP0, opSRP1, opSRP2:
- top--
- h.gs.rp[opcode-opSRP0] = h.stack[top]
-
- case opSZP0, opSZP1, opSZP2:
- top--
- h.gs.zp[opcode-opSZP0] = h.stack[top]
-
- case opSZPS:
- top--
- h.gs.zp[0] = h.stack[top]
- h.gs.zp[1] = h.stack[top]
- h.gs.zp[2] = h.stack[top]
-
- case opSLOOP:
- top--
- if h.stack[top] <= 0 {
- return errors.New("truetype: hinting: invalid data")
- }
- h.gs.loop = h.stack[top]
-
- case opRTG:
- h.gs.roundPeriod = 1 << 6
- h.gs.roundPhase = 0
- h.gs.roundThreshold = 1 << 5
- h.gs.roundSuper45 = false
-
- case opRTHG:
- h.gs.roundPeriod = 1 << 6
- h.gs.roundPhase = 1 << 5
- h.gs.roundThreshold = 1 << 5
- h.gs.roundSuper45 = false
-
- case opSMD:
- top--
- h.gs.minDist = f26dot6(h.stack[top])
-
- case opELSE:
- opcode = 1
- goto ifelse
-
- case opJMPR:
- top--
- pc += int(h.stack[top])
- continue
-
- case opSCVTCI:
- top--
- h.gs.controlValueCutIn = f26dot6(h.stack[top])
-
- case opSSWCI:
- top--
- h.gs.singleWidthCutIn = f26dot6(h.stack[top])
-
- case opSSW:
- top--
- h.gs.singleWidth = f26dot6(h.font.scale(h.scale * h.stack[top]))
-
- case opDUP:
- if top >= len(h.stack) {
- return errors.New("truetype: hinting: stack overflow")
- }
- h.stack[top] = h.stack[top-1]
- top++
-
- case opPOP:
- top--
-
- case opCLEAR:
- top = 0
-
- case opSWAP:
- h.stack[top-1], h.stack[top-2] = h.stack[top-2], h.stack[top-1]
-
- case opDEPTH:
- if top >= len(h.stack) {
- return errors.New("truetype: hinting: stack overflow")
- }
- h.stack[top] = int32(top)
- top++
-
- case opCINDEX, opMINDEX:
- x := int(h.stack[top-1])
- if x <= 0 || x >= top {
- return errors.New("truetype: hinting: invalid data")
- }
- h.stack[top-1] = h.stack[top-1-x]
- if opcode == opMINDEX {
- copy(h.stack[top-1-x:top-1], h.stack[top-x:top])
- top--
- }
-
- case opALIGNPTS:
- top -= 2
- p := h.point(1, current, h.stack[top])
- q := h.point(0, current, h.stack[top+1])
- if p == nil || q == nil {
- return errors.New("truetype: hinting: point out of range")
- }
- d := dotProduct(f26dot6(q.X-p.X), f26dot6(q.Y-p.Y), h.gs.pv) / 2
- h.move(p, +d, true)
- h.move(q, -d, true)
-
- case opUTP:
- top--
- p := h.point(0, current, h.stack[top])
- if p == nil {
- return errors.New("truetype: hinting: point out of range")
- }
- p.Flags &^= flagTouchedX | flagTouchedY
-
- case opLOOPCALL, opCALL:
- if callStackTop >= len(callStack) {
- return errors.New("truetype: hinting: call stack overflow")
- }
- top--
- f, ok := h.functions[h.stack[top]]
- if !ok {
- return errors.New("truetype: hinting: undefined function")
- }
- callStack[callStackTop] = callStackEntry{program, pc, 1}
- if opcode == opLOOPCALL {
- top--
- if h.stack[top] == 0 {
- break
- }
- callStack[callStackTop].loopCount = h.stack[top]
- }
- callStackTop++
- program, pc = f, 0
- continue
-
- case opFDEF:
- // Save all bytecode up until the next ENDF.
- startPC := pc + 1
- fdefloop:
- for {
- pc++
- if pc >= len(program) {
- return errors.New("truetype: hinting: unbalanced FDEF")
- }
- switch program[pc] {
- case opFDEF:
- return errors.New("truetype: hinting: nested FDEF")
- case opENDF:
- top--
- h.functions[h.stack[top]] = program[startPC : pc+1]
- break fdefloop
- default:
- var ok bool
- pc, ok = skipInstructionPayload(program, pc)
- if !ok {
- return errors.New("truetype: hinting: unbalanced FDEF")
- }
- }
- }
-
- case opENDF:
- if callStackTop == 0 {
- return errors.New("truetype: hinting: call stack underflow")
- }
- callStackTop--
- callStack[callStackTop].loopCount--
- if callStack[callStackTop].loopCount != 0 {
- callStackTop++
- pc = 0
- continue
- }
- program, pc = callStack[callStackTop].program, callStack[callStackTop].pc
-
- case opMDAP0, opMDAP1:
- top--
- i := h.stack[top]
- p := h.point(0, current, i)
- if p == nil {
- return errors.New("truetype: hinting: point out of range")
- }
- distance := f26dot6(0)
- if opcode == opMDAP1 {
- distance = dotProduct(f26dot6(p.X), f26dot6(p.Y), h.gs.pv)
- // TODO: metrics compensation.
- distance = h.round(distance) - distance
- }
- h.move(p, distance, true)
- h.gs.rp[0] = i
- h.gs.rp[1] = i
-
- case opIUP0, opIUP1:
- iupY, mask := opcode == opIUP0, uint32(flagTouchedX)
- if iupY {
- mask = flagTouchedY
- }
- prevEnd := 0
- for _, end := range h.ends {
- for i := prevEnd; i < end; i++ {
- for i < end && h.points[glyphZone][current][i].Flags&mask == 0 {
- i++
- }
- if i == end {
- break
- }
- firstTouched, curTouched := i, i
- i++
- for ; i < end; i++ {
- if h.points[glyphZone][current][i].Flags&mask != 0 {
- h.iupInterp(iupY, curTouched+1, i-1, curTouched, i)
- curTouched = i
- }
- }
- if curTouched == firstTouched {
- h.iupShift(iupY, prevEnd, end, curTouched)
- } else {
- h.iupInterp(iupY, curTouched+1, end-1, curTouched, firstTouched)
- if firstTouched > 0 {
- h.iupInterp(iupY, prevEnd, firstTouched-1, curTouched, firstTouched)
- }
- }
- }
- prevEnd = end
- }
-
- case opSHP0, opSHP1:
- if top < int(h.gs.loop) {
- return errors.New("truetype: hinting: stack underflow")
- }
- _, _, d, ok := h.displacement(opcode&1 == 0)
- if !ok {
- return errors.New("truetype: hinting: point out of range")
- }
- for ; h.gs.loop != 0; h.gs.loop-- {
- top--
- p := h.point(2, current, h.stack[top])
- if p == nil {
- return errors.New("truetype: hinting: point out of range")
- }
- h.move(p, d, true)
- }
- h.gs.loop = 1
-
- case opSHC0, opSHC1:
- top--
- zonePointer, i, d, ok := h.displacement(opcode&1 == 0)
- if !ok {
- return errors.New("truetype: hinting: point out of range")
- }
- if h.gs.zp[2] == 0 {
- // TODO: implement this when we have a glyph that does this.
- return errors.New("hinting: unimplemented SHC instruction")
- }
- contour := h.stack[top]
- if contour < 0 || len(ends) <= int(contour) {
- return errors.New("truetype: hinting: contour out of range")
- }
- j0, j1 := int32(0), int32(h.ends[contour])
- if contour > 0 {
- j0 = int32(h.ends[contour-1])
- }
- move := h.gs.zp[zonePointer] != h.gs.zp[2]
- for j := j0; j < j1; j++ {
- if move || j != i {
- h.move(h.point(2, current, j), d, true)
- }
- }
-
- case opSHZ0, opSHZ1:
- top--
- zonePointer, i, d, ok := h.displacement(opcode&1 == 0)
- if !ok {
- return errors.New("truetype: hinting: point out of range")
- }
-
- // As per C Freetype, SHZ doesn't move the phantom points, or mark
- // the points as touched.
- limit := int32(len(h.points[h.gs.zp[2]][current]))
- if h.gs.zp[2] == glyphZone {
- limit -= 4
- }
- for j := int32(0); j < limit; j++ {
- if i != j || h.gs.zp[zonePointer] != h.gs.zp[2] {
- h.move(h.point(2, current, j), d, false)
- }
- }
-
- case opSHPIX:
- top--
- d := f26dot6(h.stack[top])
- if top < int(h.gs.loop) {
- return errors.New("truetype: hinting: stack underflow")
- }
- for ; h.gs.loop != 0; h.gs.loop-- {
- top--
- p := h.point(2, current, h.stack[top])
- if p == nil {
- return errors.New("truetype: hinting: point out of range")
- }
- h.move(p, d, true)
- }
- h.gs.loop = 1
-
- case opIP:
- if top < int(h.gs.loop) {
- return errors.New("truetype: hinting: stack underflow")
- }
- pointType := inFontUnits
- twilight := h.gs.zp[0] == 0 || h.gs.zp[1] == 0 || h.gs.zp[2] == 0
- if twilight {
- pointType = unhinted
- }
- p := h.point(1, pointType, h.gs.rp[2])
- oldP := h.point(0, pointType, h.gs.rp[1])
- oldRange := dotProduct(f26dot6(p.X-oldP.X), f26dot6(p.Y-oldP.Y), h.gs.dv)
-
- p = h.point(1, current, h.gs.rp[2])
- curP := h.point(0, current, h.gs.rp[1])
- curRange := dotProduct(f26dot6(p.X-curP.X), f26dot6(p.Y-curP.Y), h.gs.pv)
- for ; h.gs.loop != 0; h.gs.loop-- {
- top--
- i := h.stack[top]
- p = h.point(2, pointType, i)
- oldDist := dotProduct(f26dot6(p.X-oldP.X), f26dot6(p.Y-oldP.Y), h.gs.dv)
- p = h.point(2, current, i)
- curDist := dotProduct(f26dot6(p.X-curP.X), f26dot6(p.Y-curP.Y), h.gs.pv)
- newDist := f26dot6(0)
- if oldDist != 0 {
- if oldRange != 0 {
- newDist = f26dot6(mulDiv(int64(oldDist), int64(curRange), int64(oldRange)))
- } else {
- newDist = -oldDist
- }
- }
- h.move(p, newDist-curDist, true)
- }
- h.gs.loop = 1
-
- case opMSIRP0, opMSIRP1:
- top -= 2
- i := h.stack[top]
- distance := f26dot6(h.stack[top+1])
-
- // TODO: special case h.gs.zp[1] == 0 in C Freetype.
- ref := h.point(0, current, h.gs.rp[0])
- p := h.point(1, current, i)
- if ref == nil || p == nil {
- return errors.New("truetype: hinting: point out of range")
- }
- curDist := dotProduct(f26dot6(p.X-ref.X), f26dot6(p.Y-ref.Y), h.gs.pv)
-
- // Set-RP0 bit.
- if opcode == opMSIRP1 {
- h.gs.rp[0] = i
- }
- h.gs.rp[1] = h.gs.rp[0]
- h.gs.rp[2] = i
-
- // Move the point.
- h.move(p, distance-curDist, true)
-
- case opALIGNRP:
- if top < int(h.gs.loop) {
- return errors.New("truetype: hinting: stack underflow")
- }
- ref := h.point(0, current, h.gs.rp[0])
- if ref == nil {
- return errors.New("truetype: hinting: point out of range")
- }
- for ; h.gs.loop != 0; h.gs.loop-- {
- top--
- p := h.point(1, current, h.stack[top])
- if p == nil {
- return errors.New("truetype: hinting: point out of range")
- }
- h.move(p, -dotProduct(f26dot6(p.X-ref.X), f26dot6(p.Y-ref.Y), h.gs.pv), true)
- }
- h.gs.loop = 1
-
- case opRTDG:
- h.gs.roundPeriod = 1 << 5
- h.gs.roundPhase = 0
- h.gs.roundThreshold = 1 << 4
- h.gs.roundSuper45 = false
-
- case opMIAP0, opMIAP1:
- top -= 2
- i := h.stack[top]
- distance := h.getScaledCVT(h.stack[top+1])
- if h.gs.zp[0] == 0 {
- p := h.point(0, unhinted, i)
- q := h.point(0, current, i)
- p.X = int32((int64(distance) * int64(h.gs.fv[0])) >> 14)
- p.Y = int32((int64(distance) * int64(h.gs.fv[1])) >> 14)
- *q = *p
- }
- p := h.point(0, current, i)
- oldDist := dotProduct(f26dot6(p.X), f26dot6(p.Y), h.gs.pv)
- if opcode == opMIAP1 {
- if (distance - oldDist).abs() > h.gs.controlValueCutIn {
- distance = oldDist
- }
- // TODO: metrics compensation.
- distance = h.round(distance)
- }
- h.move(p, distance-oldDist, true)
- h.gs.rp[0] = i
- h.gs.rp[1] = i
-
- case opNPUSHB:
- opcode = 0
- goto push
-
- case opNPUSHW:
- opcode = 0x80
- goto push
-
- case opWS:
- top -= 2
- i := int(h.stack[top])
- if i < 0 || len(h.store) <= i {
- return errors.New("truetype: hinting: invalid data")
- }
- h.store[i] = h.stack[top+1]
-
- case opRS:
- i := int(h.stack[top-1])
- if i < 0 || len(h.store) <= i {
- return errors.New("truetype: hinting: invalid data")
- }
- h.stack[top-1] = h.store[i]
-
- case opWCVTP:
- top -= 2
- h.setScaledCVT(h.stack[top], f26dot6(h.stack[top+1]))
-
- case opRCVT:
- h.stack[top-1] = int32(h.getScaledCVT(h.stack[top-1]))
-
- case opGC0, opGC1:
- i := h.stack[top-1]
- if opcode == opGC0 {
- p := h.point(2, current, i)
- h.stack[top-1] = int32(dotProduct(f26dot6(p.X), f26dot6(p.Y), h.gs.pv))
- } else {
- p := h.point(2, unhinted, i)
- // Using dv as per C Freetype.
- h.stack[top-1] = int32(dotProduct(f26dot6(p.X), f26dot6(p.Y), h.gs.dv))
- }
-
- case opSCFS:
- top -= 2
- i := h.stack[top]
- p := h.point(2, current, i)
- if p == nil {
- return errors.New("truetype: hinting: point out of range")
- }
- c := dotProduct(f26dot6(p.X), f26dot6(p.Y), h.gs.pv)
- h.move(p, f26dot6(h.stack[top+1])-c, true)
- if h.gs.zp[2] != 0 {
- break
- }
- q := h.point(2, unhinted, i)
- if q == nil {
- return errors.New("truetype: hinting: point out of range")
- }
- q.X = p.X
- q.Y = p.Y
-
- case opMD0, opMD1:
- top--
- pt, v, scale := pointType(0), [2]f2dot14{}, false
- if opcode == opMD0 {
- pt = current
- v = h.gs.pv
- } else if h.gs.zp[0] == 0 || h.gs.zp[1] == 0 {
- pt = unhinted
- v = h.gs.dv
- } else {
- pt = inFontUnits
- v = h.gs.dv
- scale = true
- }
- p := h.point(0, pt, h.stack[top-1])
- q := h.point(1, pt, h.stack[top])
- if p == nil || q == nil {
- return errors.New("truetype: hinting: point out of range")
- }
- d := int32(dotProduct(f26dot6(p.X-q.X), f26dot6(p.Y-q.Y), v))
- if scale {
- d = int32(int64(d*h.scale) / int64(h.font.fUnitsPerEm))
- }
- h.stack[top-1] = d
-
- case opMPPEM, opMPS:
- if top >= len(h.stack) {
- return errors.New("truetype: hinting: stack overflow")
- }
- // For MPS, point size should be irrelevant; we return the PPEM.
- h.stack[top] = h.scale >> 6
- top++
-
- case opFLIPON, opFLIPOFF:
- h.gs.autoFlip = opcode == opFLIPON
-
- case opDEBUG:
- // No-op.
-
- case opLT:
- top--
- h.stack[top-1] = bool2int32(h.stack[top-1] < h.stack[top])
-
- case opLTEQ:
- top--
- h.stack[top-1] = bool2int32(h.stack[top-1] <= h.stack[top])
-
- case opGT:
- top--
- h.stack[top-1] = bool2int32(h.stack[top-1] > h.stack[top])
-
- case opGTEQ:
- top--
- h.stack[top-1] = bool2int32(h.stack[top-1] >= h.stack[top])
-
- case opEQ:
- top--
- h.stack[top-1] = bool2int32(h.stack[top-1] == h.stack[top])
-
- case opNEQ:
- top--
- h.stack[top-1] = bool2int32(h.stack[top-1] != h.stack[top])
-
- case opODD, opEVEN:
- i := h.round(f26dot6(h.stack[top-1])) >> 6
- h.stack[top-1] = int32(i&1) ^ int32(opcode-opODD)
-
- case opIF:
- top--
- if h.stack[top] == 0 {
- opcode = 0
- goto ifelse
- }
-
- case opEIF:
- // No-op.
-
- case opAND:
- top--
- h.stack[top-1] = bool2int32(h.stack[top-1] != 0 && h.stack[top] != 0)
-
- case opOR:
- top--
- h.stack[top-1] = bool2int32(h.stack[top-1]|h.stack[top] != 0)
-
- case opNOT:
- h.stack[top-1] = bool2int32(h.stack[top-1] == 0)
-
- case opDELTAP1:
- goto delta
-
- case opSDB:
- top--
- h.gs.deltaBase = h.stack[top]
-
- case opSDS:
- top--
- h.gs.deltaShift = h.stack[top]
-
- case opADD:
- top--
- h.stack[top-1] += h.stack[top]
-
- case opSUB:
- top--
- h.stack[top-1] -= h.stack[top]
-
- case opDIV:
- top--
- if h.stack[top] == 0 {
- return errors.New("truetype: hinting: division by zero")
- }
- h.stack[top-1] = int32(f26dot6(h.stack[top-1]).div(f26dot6(h.stack[top])))
-
- case opMUL:
- top--
- h.stack[top-1] = int32(f26dot6(h.stack[top-1]).mul(f26dot6(h.stack[top])))
-
- case opABS:
- if h.stack[top-1] < 0 {
- h.stack[top-1] = -h.stack[top-1]
- }
-
- case opNEG:
- h.stack[top-1] = -h.stack[top-1]
-
- case opFLOOR:
- h.stack[top-1] &^= 63
-
- case opCEILING:
- h.stack[top-1] += 63
- h.stack[top-1] &^= 63
-
- case opROUND00, opROUND01, opROUND10, opROUND11:
- // The four flavors of opROUND are equivalent. See the comment below on
- // opNROUND for the rationale.
- h.stack[top-1] = int32(h.round(f26dot6(h.stack[top-1])))
-
- case opNROUND00, opNROUND01, opNROUND10, opNROUND11:
- // No-op. The spec says to add one of four "compensations for the engine
- // characteristics", to cater for things like "different dot-size printers".
- // https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#engine_compensation
- // This code does not implement engine compensation, as we don't expect to
- // be used to output on dot-matrix printers.
-
- case opWCVTF:
- top -= 2
- h.setScaledCVT(h.stack[top], f26dot6(h.font.scale(h.scale*h.stack[top+1])))
-
- case opDELTAP2, opDELTAP3, opDELTAC1, opDELTAC2, opDELTAC3:
- goto delta
-
- case opSROUND, opS45ROUND:
- top--
- switch (h.stack[top] >> 6) & 0x03 {
- case 0:
- h.gs.roundPeriod = 1 << 5
- case 1, 3:
- h.gs.roundPeriod = 1 << 6
- case 2:
- h.gs.roundPeriod = 1 << 7
- }
- h.gs.roundSuper45 = opcode == opS45ROUND
- if h.gs.roundSuper45 {
- // The spec says to multiply by √2, but the C Freetype code says 1/√2.
- // We go with 1/√2.
- h.gs.roundPeriod *= 46341
- h.gs.roundPeriod /= 65536
- }
- h.gs.roundPhase = h.gs.roundPeriod * f26dot6((h.stack[top]>>4)&0x03) / 4
- if x := h.stack[top] & 0x0f; x != 0 {
- h.gs.roundThreshold = h.gs.roundPeriod * f26dot6(x-4) / 8
- } else {
- h.gs.roundThreshold = h.gs.roundPeriod - 1
- }
-
- case opJROT:
- top -= 2
- if h.stack[top+1] != 0 {
- pc += int(h.stack[top])
- continue
- }
-
- case opJROF:
- top -= 2
- if h.stack[top+1] == 0 {
- pc += int(h.stack[top])
- continue
- }
-
- case opROFF:
- h.gs.roundPeriod = 0
- h.gs.roundPhase = 0
- h.gs.roundThreshold = 0
- h.gs.roundSuper45 = false
-
- case opRUTG:
- h.gs.roundPeriod = 1 << 6
- h.gs.roundPhase = 0
- h.gs.roundThreshold = 1<<6 - 1
- h.gs.roundSuper45 = false
-
- case opRDTG:
- h.gs.roundPeriod = 1 << 6
- h.gs.roundPhase = 0
- h.gs.roundThreshold = 0
- h.gs.roundSuper45 = false
-
- case opSANGW, opAA:
- // These ops are "anachronistic" and no longer used.
- top--
-
- case opFLIPPT:
- if top < int(h.gs.loop) {
- return errors.New("truetype: hinting: stack underflow")
- }
- points := h.points[glyphZone][current]
- for ; h.gs.loop != 0; h.gs.loop-- {
- top--
- i := h.stack[top]
- if i < 0 || len(points) <= int(i) {
- return errors.New("truetype: hinting: point out of range")
- }
- points[i].Flags ^= flagOnCurve
- }
- h.gs.loop = 1
-
- case opFLIPRGON, opFLIPRGOFF:
- top -= 2
- i, j, points := h.stack[top], h.stack[top+1], h.points[glyphZone][current]
- if i < 0 || len(points) <= int(i) || j < 0 || len(points) <= int(j) {
- return errors.New("truetype: hinting: point out of range")
- }
- for ; i <= j; i++ {
- if opcode == opFLIPRGON {
- points[i].Flags |= flagOnCurve
- } else {
- points[i].Flags &^= flagOnCurve
- }
- }
-
- case opSCANCTRL:
- // We do not support dropout control, as we always rasterize grayscale glyphs.
- top--
-
- case opSDPVTL0, opSDPVTL1:
- top -= 2
- for i := 0; i < 2; i++ {
- pt := unhinted
- if i != 0 {
- pt = current
- }
- p := h.point(1, pt, h.stack[top])
- q := h.point(2, pt, h.stack[top+1])
- if p == nil || q == nil {
- return errors.New("truetype: hinting: point out of range")
- }
- dx := f2dot14(p.X - q.X)
- dy := f2dot14(p.Y - q.Y)
- if dx == 0 && dy == 0 {
- dx = 0x4000
- } else if opcode&1 != 0 {
- // Counter-clockwise rotation.
- dx, dy = -dy, dx
- }
- if i == 0 {
- h.gs.dv = normalize(dx, dy)
- } else {
- h.gs.pv = normalize(dx, dy)
- }
- }
-
- case opGETINFO:
- res := int32(0)
- if h.stack[top-1]&(1<<0) != 0 {
- // Set the engine version. We hard-code this to 35, the same as
- // the C freetype code, which says that "Version~35 corresponds
- // to MS rasterizer v.1.7 as used e.g. in Windows~98".
- res |= 35
- }
- if h.stack[top-1]&(1<<5) != 0 {
- // Set that we support grayscale.
- res |= 1 << 12
- }
- // We set no other bits, as we do not support rotated or stretched glyphs.
- h.stack[top-1] = res
-
- case opIDEF:
- // IDEF is for ancient versions of the bytecode interpreter, and is no longer used.
- return errors.New("truetype: hinting: unsupported IDEF instruction")
-
- case opROLL:
- h.stack[top-1], h.stack[top-3], h.stack[top-2] =
- h.stack[top-3], h.stack[top-2], h.stack[top-1]
-
- case opMAX:
- top--
- if h.stack[top-1] < h.stack[top] {
- h.stack[top-1] = h.stack[top]
- }
-
- case opMIN:
- top--
- if h.stack[top-1] > h.stack[top] {
- h.stack[top-1] = h.stack[top]
- }
-
- case opSCANTYPE:
- // We do not support dropout control, as we always rasterize grayscale glyphs.
- top--
-
- case opINSTCTRL:
- // TODO: support instruction execution control? It seems rare, and even when
- // nominally used (e.g. Source Sans Pro), it seems conditional on extreme or
- // unusual rasterization conditions. For example, the code snippet at
- // https://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html#INSTCTRL
- // uses INSTCTRL when grid-fitting a rotated or stretched glyph, but
- // freetype-go does not support rotated or stretched glyphs.
- top -= 2
-
- default:
- if opcode < opPUSHB000 {
- return errors.New("truetype: hinting: unrecognized instruction")
- }
-
- if opcode < opMDRP00000 {
- // PUSHxxxx opcode.
-
- if opcode < opPUSHW000 {
- opcode -= opPUSHB000 - 1
- } else {
- opcode -= opPUSHW000 - 1 - 0x80
- }
- goto push
- }
-
- if opcode < opMIRP00000 {
- // MDRPxxxxx opcode.
-
- top--
- i := h.stack[top]
- ref := h.point(0, current, h.gs.rp[0])
- p := h.point(1, current, i)
- if ref == nil || p == nil {
- return errors.New("truetype: hinting: point out of range")
- }
-
- oldDist := f26dot6(0)
- if h.gs.zp[0] == 0 || h.gs.zp[1] == 0 {
- p0 := h.point(1, unhinted, i)
- p1 := h.point(0, unhinted, h.gs.rp[0])
- oldDist = dotProduct(f26dot6(p0.X-p1.X), f26dot6(p0.Y-p1.Y), h.gs.dv)
- } else {
- p0 := h.point(1, inFontUnits, i)
- p1 := h.point(0, inFontUnits, h.gs.rp[0])
- oldDist = dotProduct(f26dot6(p0.X-p1.X), f26dot6(p0.Y-p1.Y), h.gs.dv)
- oldDist = f26dot6(h.font.scale(h.scale * int32(oldDist)))
- }
-
- // Single-width cut-in test.
- if x := (oldDist - h.gs.singleWidth).abs(); x < h.gs.singleWidthCutIn {
- if oldDist >= 0 {
- oldDist = +h.gs.singleWidth
- } else {
- oldDist = -h.gs.singleWidth
- }
- }
-
- // Rounding bit.
- // TODO: metrics compensation.
- distance := oldDist
- if opcode&0x04 != 0 {
- distance = h.round(oldDist)
- }
-
- // Minimum distance bit.
- if opcode&0x08 != 0 {
- if oldDist >= 0 {
- if distance < h.gs.minDist {
- distance = h.gs.minDist
- }
- } else {
- if distance > -h.gs.minDist {
- distance = -h.gs.minDist
- }
- }
- }
-
- // Set-RP0 bit.
- h.gs.rp[1] = h.gs.rp[0]
- h.gs.rp[2] = i
- if opcode&0x10 != 0 {
- h.gs.rp[0] = i
- }
-
- // Move the point.
- oldDist = dotProduct(f26dot6(p.X-ref.X), f26dot6(p.Y-ref.Y), h.gs.pv)
- h.move(p, distance-oldDist, true)
-
- } else {
- // MIRPxxxxx opcode.
-
- top -= 2
- i := h.stack[top]
- cvtDist := h.getScaledCVT(h.stack[top+1])
- if (cvtDist - h.gs.singleWidth).abs() < h.gs.singleWidthCutIn {
- if cvtDist >= 0 {
- cvtDist = +h.gs.singleWidth
- } else {
- cvtDist = -h.gs.singleWidth
- }
- }
-
- if h.gs.zp[1] == 0 {
- // TODO: implement once we have a .ttf file that triggers
- // this, so that we can step through C's freetype.
- return errors.New("truetype: hinting: unimplemented twilight point adjustment")
- }
-
- ref := h.point(0, unhinted, h.gs.rp[0])
- p := h.point(1, unhinted, i)
- if ref == nil || p == nil {
- return errors.New("truetype: hinting: point out of range")
- }
- oldDist := dotProduct(f26dot6(p.X-ref.X), f26dot6(p.Y-ref.Y), h.gs.dv)
-
- ref = h.point(0, current, h.gs.rp[0])
- p = h.point(1, current, i)
- if ref == nil || p == nil {
- return errors.New("truetype: hinting: point out of range")
- }
- curDist := dotProduct(f26dot6(p.X-ref.X), f26dot6(p.Y-ref.Y), h.gs.pv)
-
- if h.gs.autoFlip && oldDist^cvtDist < 0 {
- cvtDist = -cvtDist
- }
-
- // Rounding bit.
- // TODO: metrics compensation.
- distance := cvtDist
- if opcode&0x04 != 0 {
- // The CVT value is only used if close enough to oldDist.
- if (h.gs.zp[0] == h.gs.zp[1]) &&
- ((cvtDist - oldDist).abs() > h.gs.controlValueCutIn) {
-
- distance = oldDist
- }
- distance = h.round(distance)
- }
-
- // Minimum distance bit.
- if opcode&0x08 != 0 {
- if oldDist >= 0 {
- if distance < h.gs.minDist {
- distance = h.gs.minDist
- }
- } else {
- if distance > -h.gs.minDist {
- distance = -h.gs.minDist
- }
- }
- }
-
- // Set-RP0 bit.
- h.gs.rp[1] = h.gs.rp[0]
- h.gs.rp[2] = i
- if opcode&0x10 != 0 {
- h.gs.rp[0] = i
- }
-
- // Move the point.
- h.move(p, distance-curDist, true)
- }
- }
- pc++
- continue
-
- ifelse:
- // Skip past bytecode until the next ELSE (if opcode == 0) or the
- // next EIF (for all opcodes). Opcode == 0 means that we have come
- // from an IF. Opcode == 1 means that we have come from an ELSE.
- {
- ifelseloop:
- for depth := 0; ; {
- pc++
- if pc >= len(program) {
- return errors.New("truetype: hinting: unbalanced IF or ELSE")
- }
- switch program[pc] {
- case opIF:
- depth++
- case opELSE:
- if depth == 0 && opcode == 0 {
- break ifelseloop
- }
- case opEIF:
- depth--
- if depth < 0 {
- break ifelseloop
- }
- default:
- var ok bool
- pc, ok = skipInstructionPayload(program, pc)
- if !ok {
- return errors.New("truetype: hinting: unbalanced IF or ELSE")
- }
- }
- }
- pc++
- continue
- }
-
- push:
- // Push n elements from the program to the stack, where n is the low 7 bits of
- // opcode. If the low 7 bits are zero, then n is the next byte from the program.
- // The high bit being 0 means that the elements are zero-extended bytes.
- // The high bit being 1 means that the elements are sign-extended words.
- {
- width := 1
- if opcode&0x80 != 0 {
- opcode &^= 0x80
- width = 2
- }
- if opcode == 0 {
- pc++
- if pc >= len(program) {
- return errors.New("truetype: hinting: insufficient data")
- }
- opcode = program[pc]
- }
- pc++
- if top+int(opcode) > len(h.stack) {
- return errors.New("truetype: hinting: stack overflow")
- }
- if pc+width*int(opcode) > len(program) {
- return errors.New("truetype: hinting: insufficient data")
- }
- for ; opcode > 0; opcode-- {
- if width == 1 {
- h.stack[top] = int32(program[pc])
- } else {
- h.stack[top] = int32(int8(program[pc]))<<8 | int32(program[pc+1])
- }
- top++
- pc += width
- }
- continue
- }
-
- delta:
- {
- if opcode >= opDELTAC1 && !h.scaledCVTInitialized {
- h.initializeScaledCVT()
- }
- top--
- n := h.stack[top]
- if int32(top) < 2*n {
- return errors.New("truetype: hinting: stack underflow")
- }
- for ; n > 0; n-- {
- top -= 2
- b := h.stack[top]
- c := (b & 0xf0) >> 4
- switch opcode {
- case opDELTAP2, opDELTAC2:
- c += 16
- case opDELTAP3, opDELTAC3:
- c += 32
- }
- c += h.gs.deltaBase
- if ppem := (h.scale + 1<<5) >> 6; ppem != c {
- continue
- }
- b = (b & 0x0f) - 8
- if b >= 0 {
- b++
- }
- b = b * 64 / (1 << uint32(h.gs.deltaShift))
- if opcode >= opDELTAC1 {
- a := h.stack[top+1]
- if a < 0 || len(h.scaledCVT) <= int(a) {
- return errors.New("truetype: hinting: index out of range")
- }
- h.scaledCVT[a] += f26dot6(b)
- } else {
- p := h.point(0, current, h.stack[top+1])
- if p == nil {
- return errors.New("truetype: hinting: point out of range")
- }
- h.move(p, f26dot6(b), true)
- }
- }
- pc++
- continue
- }
- }
- return nil
-}
-
-func (h *hinter) initializeScaledCVT() {
- h.scaledCVTInitialized = true
- if n := len(h.font.cvt) / 2; n <= cap(h.scaledCVT) {
- h.scaledCVT = h.scaledCVT[:n]
- } else {
- if n < 32 {
- n = 32
- }
- h.scaledCVT = make([]f26dot6, len(h.font.cvt)/2, n)
- }
- for i := range h.scaledCVT {
- unscaled := uint16(h.font.cvt[2*i])<<8 | uint16(h.font.cvt[2*i+1])
- h.scaledCVT[i] = f26dot6(h.font.scale(h.scale * int32(int16(unscaled))))
- }
-}
-
-// getScaledCVT returns the scaled value from the font's Control Value Table.
-func (h *hinter) getScaledCVT(i int32) f26dot6 {
- if !h.scaledCVTInitialized {
- h.initializeScaledCVT()
- }
- if i < 0 || len(h.scaledCVT) <= int(i) {
- return 0
- }
- return h.scaledCVT[i]
-}
-
-// setScaledCVT overrides the scaled value from the font's Control Value Table.
-func (h *hinter) setScaledCVT(i int32, v f26dot6) {
- if !h.scaledCVTInitialized {
- h.initializeScaledCVT()
- }
- if i < 0 || len(h.scaledCVT) <= int(i) {
- return
- }
- h.scaledCVT[i] = v
-}
-
-func (h *hinter) point(zonePointer uint32, pt pointType, i int32) *Point {
- points := h.points[h.gs.zp[zonePointer]][pt]
- if i < 0 || len(points) <= int(i) {
- return nil
- }
- return &points[i]
-}
-
-func (h *hinter) move(p *Point, distance f26dot6, touch bool) {
- fvx := int64(h.gs.fv[0])
- pvx := int64(h.gs.pv[0])
- if fvx == 0x4000 && pvx == 0x4000 {
- p.X += int32(distance)
- if touch {
- p.Flags |= flagTouchedX
- }
- return
- }
-
- fvy := int64(h.gs.fv[1])
- pvy := int64(h.gs.pv[1])
- if fvy == 0x4000 && pvy == 0x4000 {
- p.Y += int32(distance)
- if touch {
- p.Flags |= flagTouchedY
- }
- return
- }
-
- fvDotPv := (fvx*pvx + fvy*pvy) >> 14
-
- if fvx != 0 {
- p.X += int32(mulDiv(fvx, int64(distance), fvDotPv))
- if touch {
- p.Flags |= flagTouchedX
- }
- }
-
- if fvy != 0 {
- p.Y += int32(mulDiv(fvy, int64(distance), fvDotPv))
- if touch {
- p.Flags |= flagTouchedY
- }
- }
-}
-
-func (h *hinter) iupInterp(interpY bool, p1, p2, ref1, ref2 int) {
- if p1 > p2 {
- return
- }
- if ref1 >= len(h.points[glyphZone][current]) ||
- ref2 >= len(h.points[glyphZone][current]) {
- return
- }
-
- var ifu1, ifu2 int32
- if interpY {
- ifu1 = h.points[glyphZone][inFontUnits][ref1].Y
- ifu2 = h.points[glyphZone][inFontUnits][ref2].Y
- } else {
- ifu1 = h.points[glyphZone][inFontUnits][ref1].X
- ifu2 = h.points[glyphZone][inFontUnits][ref2].X
- }
- if ifu1 > ifu2 {
- ifu1, ifu2 = ifu2, ifu1
- ref1, ref2 = ref2, ref1
- }
-
- var unh1, unh2, delta1, delta2 int32
- if interpY {
- unh1 = h.points[glyphZone][unhinted][ref1].Y
- unh2 = h.points[glyphZone][unhinted][ref2].Y
- delta1 = h.points[glyphZone][current][ref1].Y - unh1
- delta2 = h.points[glyphZone][current][ref2].Y - unh2
- } else {
- unh1 = h.points[glyphZone][unhinted][ref1].X
- unh2 = h.points[glyphZone][unhinted][ref2].X
- delta1 = h.points[glyphZone][current][ref1].X - unh1
- delta2 = h.points[glyphZone][current][ref2].X - unh2
- }
-
- var xy, ifuXY int32
- if ifu1 == ifu2 {
- for i := p1; i <= p2; i++ {
- if interpY {
- xy = h.points[glyphZone][unhinted][i].Y
- } else {
- xy = h.points[glyphZone][unhinted][i].X
- }
-
- if xy <= unh1 {
- xy += delta1
- } else {
- xy += delta2
- }
-
- if interpY {
- h.points[glyphZone][current][i].Y = xy
- } else {
- h.points[glyphZone][current][i].X = xy
- }
- }
- return
- }
-
- scale, scaleOK := int64(0), false
- for i := p1; i <= p2; i++ {
- if interpY {
- xy = h.points[glyphZone][unhinted][i].Y
- ifuXY = h.points[glyphZone][inFontUnits][i].Y
- } else {
- xy = h.points[glyphZone][unhinted][i].X
- ifuXY = h.points[glyphZone][inFontUnits][i].X
- }
-
- if xy <= unh1 {
- xy += delta1
- } else if xy >= unh2 {
- xy += delta2
- } else {
- if !scaleOK {
- scaleOK = true
- scale = mulDiv(int64(unh2+delta2-unh1-delta1), 0x10000, int64(ifu2-ifu1))
- }
- numer := int64(ifuXY-ifu1) * scale
- if numer >= 0 {
- numer += 0x8000
- } else {
- numer -= 0x8000
- }
- xy = unh1 + delta1 + int32(numer/0x10000)
- }
-
- if interpY {
- h.points[glyphZone][current][i].Y = xy
- } else {
- h.points[glyphZone][current][i].X = xy
- }
- }
-}
-
-func (h *hinter) iupShift(interpY bool, p1, p2, p int) {
- var delta int32
- if interpY {
- delta = h.points[glyphZone][current][p].Y - h.points[glyphZone][unhinted][p].Y
- } else {
- delta = h.points[glyphZone][current][p].X - h.points[glyphZone][unhinted][p].X
- }
- if delta == 0 {
- return
- }
- for i := p1; i < p2; i++ {
- if i == p {
- continue
- }
- if interpY {
- h.points[glyphZone][current][i].Y += delta
- } else {
- h.points[glyphZone][current][i].X += delta
- }
- }
-}
-
-func (h *hinter) displacement(useZP1 bool) (zonePointer uint32, i int32, d f26dot6, ok bool) {
- zonePointer, i = uint32(0), h.gs.rp[1]
- if useZP1 {
- zonePointer, i = 1, h.gs.rp[2]
- }
- p := h.point(zonePointer, current, i)
- q := h.point(zonePointer, unhinted, i)
- if p == nil || q == nil {
- return 0, 0, 0, false
- }
- d = dotProduct(f26dot6(p.X-q.X), f26dot6(p.Y-q.Y), h.gs.pv)
- return zonePointer, i, d, true
-}
-
-// skipInstructionPayload increments pc by the extra data that follows a
-// variable length PUSHB or PUSHW instruction.
-func skipInstructionPayload(program []byte, pc int) (newPC int, ok bool) {
- switch program[pc] {
- case opNPUSHB:
- pc++
- if pc >= len(program) {
- return 0, false
- }
- pc += int(program[pc])
- case opNPUSHW:
- pc++
- if pc >= len(program) {
- return 0, false
- }
- pc += 2 * int(program[pc])
- case opPUSHB000, opPUSHB001, opPUSHB010, opPUSHB011,
- opPUSHB100, opPUSHB101, opPUSHB110, opPUSHB111:
- pc += int(program[pc] - (opPUSHB000 - 1))
- case opPUSHW000, opPUSHW001, opPUSHW010, opPUSHW011,
- opPUSHW100, opPUSHW101, opPUSHW110, opPUSHW111:
- pc += 2 * int(program[pc]-(opPUSHW000-1))
- }
- return pc, true
-}
-
-// f2dot14 is a 2.14 fixed point number.
-type f2dot14 int16
-
-func normalize(x, y f2dot14) [2]f2dot14 {
- fx, fy := float64(x), float64(y)
- l := 0x4000 / math.Hypot(fx, fy)
- fx *= l
- if fx >= 0 {
- fx += 0.5
- } else {
- fx -= 0.5
- }
- fy *= l
- if fy >= 0 {
- fy += 0.5
- } else {
- fy -= 0.5
- }
- return [2]f2dot14{f2dot14(fx), f2dot14(fy)}
-}
-
-// f26dot6 is a 26.6 fixed point number.
-type f26dot6 int32
-
-// abs returns abs(x) in 26.6 fixed point arithmetic.
-func (x f26dot6) abs() f26dot6 {
- if x < 0 {
- return -x
- }
- return x
-}
-
-// div returns x/y in 26.6 fixed point arithmetic.
-func (x f26dot6) div(y f26dot6) f26dot6 {
- return f26dot6((int64(x) << 6) / int64(y))
-}
-
-// mul returns x*y in 26.6 fixed point arithmetic.
-func (x f26dot6) mul(y f26dot6) f26dot6 {
- return f26dot6((int64(x)*int64(y) + 1<<5) >> 6)
-}
-
-// dotProduct returns the dot product of [x, y] and q. It is almost the same as
-// px := int64(x)
-// py := int64(y)
-// qx := int64(q[0])
-// qy := int64(q[1])
-// return f26dot6((px*qx + py*qy + 1<<13) >> 14)
-// except that the computation is done with 32-bit integers to produce exactly
-// the same rounding behavior as C Freetype.
-func dotProduct(x, y f26dot6, q [2]f2dot14) f26dot6 {
- // Compute x*q[0] as 64-bit value.
- l := uint32((int32(x) & 0xFFFF) * int32(q[0]))
- m := (int32(x) >> 16) * int32(q[0])
-
- lo1 := l + (uint32(m) << 16)
- hi1 := (m >> 16) + (int32(l) >> 31) + bool2int32(lo1 < l)
-
- // Compute y*q[1] as 64-bit value.
- l = uint32((int32(y) & 0xFFFF) * int32(q[1]))
- m = (int32(y) >> 16) * int32(q[1])
-
- lo2 := l + (uint32(m) << 16)
- hi2 := (m >> 16) + (int32(l) >> 31) + bool2int32(lo2 < l)
-
- // Add them.
- lo := lo1 + lo2
- hi := hi1 + hi2 + bool2int32(lo < lo1)
-
- // Divide the result by 2^14 with rounding.
- s := hi >> 31
- l = lo + uint32(s)
- hi += s + bool2int32(l < lo)
- lo = l
-
- l = lo + 0x2000
- hi += bool2int32(l < lo)
-
- return f26dot6((uint32(hi) << 18) | (l >> 14))
-}
-
-// mulDiv returns x*y/z, rounded to the nearest integer.
-func mulDiv(x, y, z int64) int64 {
- xy := x * y
- if z < 0 {
- xy, z = -xy, -z
- }
- if xy >= 0 {
- xy += z / 2
- } else {
- xy -= z / 2
- }
- return xy / z
-}
-
-// round rounds the given number. The rounding algorithm is described at
-// https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#rounding
-func (h *hinter) round(x f26dot6) f26dot6 {
- if h.gs.roundPeriod == 0 {
- // Rounding is off.
- return x
- }
- if x >= 0 {
- ret := x - h.gs.roundPhase + h.gs.roundThreshold
- if h.gs.roundSuper45 {
- ret /= h.gs.roundPeriod
- ret *= h.gs.roundPeriod
- } else {
- ret &= -h.gs.roundPeriod
- }
- if x != 0 && ret < 0 {
- ret = 0
- }
- return ret + h.gs.roundPhase
- }
- ret := -x - h.gs.roundPhase + h.gs.roundThreshold
- if h.gs.roundSuper45 {
- ret /= h.gs.roundPeriod
- ret *= h.gs.roundPeriod
- } else {
- ret &= -h.gs.roundPeriod
- }
- if ret < 0 {
- ret = 0
- }
- return -ret - h.gs.roundPhase
-}
-
-func bool2int32(b bool) int32 {
- if b {
- return 1
- }
- return 0
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint_test.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint_test.go
deleted file mode 100644
index c8b8d604d..000000000
--- a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint_test.go
+++ /dev/null
@@ -1,673 +0,0 @@
-// Copyright 2012 The Freetype-Go Authors. All rights reserved.
-// Use of this source code is governed by your choice of either the
-// FreeType License or the GNU General Public License version 2 (or
-// any later version), both of which can be found in the LICENSE file.
-
-package truetype
-
-import (
- "reflect"
- "strings"
- "testing"
-)
-
-func TestBytecode(t *testing.T) {
- testCases := []struct {
- desc string
- prog []byte
- want []int32
- errStr string
- }{
- {
- "underflow",
- []byte{
- opDUP,
- },
- nil,
- "underflow",
- },
- {
- "infinite loop",
- []byte{
- opPUSHW000, // [-1]
- 0xff,
- 0xff,
- opDUP, // [-1, -1]
- opJMPR, // [-1]
- },
- nil,
- "too many steps",
- },
- {
- "unbalanced if/else",
- []byte{
- opPUSHB000, // [0]
- 0,
- opIF,
- },
- nil,
- "unbalanced",
- },
- {
- "vector set/gets",
- []byte{
- opSVTCA1, // []
- opGPV, // [0x4000, 0]
- opSVTCA0, // [0x4000, 0]
- opGFV, // [0x4000, 0, 0, 0x4000]
- opNEG, // [0x4000, 0, 0, -0x4000]
- opSPVFS, // [0x4000, 0]
- opSFVTPV, // [0x4000, 0]
- opPUSHB000, // [0x4000, 0, 1]
- 1,
- opGFV, // [0x4000, 0, 1, 0, -0x4000]
- opPUSHB000, // [0x4000, 0, 1, 0, -0x4000, 2]
- 2,
- },
- []int32{0x4000, 0, 1, 0, -0x4000, 2},
- "",
- },
- {
- "jumps",
- []byte{
- opPUSHB001, // [10, 2]
- 10,
- 2,
- opJMPR, // [10]
- opDUP, // not executed
- opDUP, // [10, 10]
- opPUSHB010, // [10, 10, 20, 2, 1]
- 20,
- 2,
- 1,
- opJROT, // [10, 10, 20]
- opDUP, // not executed
- opDUP, // [10, 10, 20, 20]
- opPUSHB010, // [10, 10, 20, 20, 30, 2, 1]
- 30,
- 2,
- 1,
- opJROF, // [10, 10, 20, 20, 30]
- opDUP, // [10, 10, 20, 20, 30, 30]
- opDUP, // [10, 10, 20, 20, 30, 30, 30]
- },
- []int32{10, 10, 20, 20, 30, 30, 30},
- "",
- },
- {
- "stack ops",
- []byte{
- opPUSHB010, // [10, 20, 30]
- 10,
- 20,
- 30,
- opCLEAR, // []
- opPUSHB010, // [40, 50, 60]
- 40,
- 50,
- 60,
- opSWAP, // [40, 60, 50]
- opDUP, // [40, 60, 50, 50]
- opDUP, // [40, 60, 50, 50, 50]
- opPOP, // [40, 60, 50, 50]
- opDEPTH, // [40, 60, 50, 50, 4]
- opCINDEX, // [40, 60, 50, 50, 40]
- opPUSHB000, // [40, 60, 50, 50, 40, 4]
- 4,
- opMINDEX, // [40, 50, 50, 40, 60]
- },
- []int32{40, 50, 50, 40, 60},
- "",
- },
- {
- "push ops",
- []byte{
- opPUSHB000, // [255]
- 255,
- opPUSHW001, // [255, -2, 253]
- 255,
- 254,
- 0,
- 253,
- opNPUSHB, // [1, -2, 253, 1, 2]
- 2,
- 1,
- 2,
- opNPUSHW, // [1, -2, 253, 1, 2, 0x0405, 0x0607, 0x0809]
- 3,
- 4,
- 5,
- 6,
- 7,
- 8,
- 9,
- },
- []int32{255, -2, 253, 1, 2, 0x0405, 0x0607, 0x0809},
- "",
- },
- {
- "store ops",
- []byte{
- opPUSHB011, // [1, 22, 3, 44]
- 1,
- 22,
- 3,
- 44,
- opWS, // [1, 22]
- opWS, // []
- opPUSHB000, // [3]
- 3,
- opRS, // [44]
- },
- []int32{44},
- "",
- },
- {
- "comparison ops",
- []byte{
- opPUSHB001, // [10, 20]
- 10,
- 20,
- opLT, // [1]
- opPUSHB001, // [1, 10, 20]
- 10,
- 20,
- opLTEQ, // [1, 1]
- opPUSHB001, // [1, 1, 10, 20]
- 10,
- 20,
- opGT, // [1, 1, 0]
- opPUSHB001, // [1, 1, 0, 10, 20]
- 10,
- 20,
- opGTEQ, // [1, 1, 0, 0]
- opEQ, // [1, 1, 1]
- opNEQ, // [1, 0]
- },
- []int32{1, 0},
- "",
- },
- {
- "odd/even",
- // Calculate odd(2+31/64), odd(2+32/64), even(2), even(1).
- []byte{
- opPUSHB000, // [159]
- 159,
- opODD, // [0]
- opPUSHB000, // [0, 160]
- 160,
- opODD, // [0, 1]
- opPUSHB000, // [0, 1, 128]
- 128,
- opEVEN, // [0, 1, 1]
- opPUSHB000, // [0, 1, 1, 64]
- 64,
- opEVEN, // [0, 1, 1, 0]
- },
- []int32{0, 1, 1, 0},
- "",
- },
- {
- "if true",
- []byte{
- opPUSHB001, // [255, 1]
- 255,
- 1,
- opIF,
- opPUSHB000, // [255, 2]
- 2,
- opEIF,
- opPUSHB000, // [255, 2, 254]
- 254,
- },
- []int32{255, 2, 254},
- "",
- },
- {
- "if false",
- []byte{
- opPUSHB001, // [255, 0]
- 255,
- 0,
- opIF,
- opPUSHB000, // [255]
- 2,
- opEIF,
- opPUSHB000, // [255, 254]
- 254,
- },
- []int32{255, 254},
- "",
- },
- {
- "if/else true",
- []byte{
- opPUSHB000, // [1]
- 1,
- opIF,
- opPUSHB000, // [2]
- 2,
- opELSE,
- opPUSHB000, // not executed
- 3,
- opEIF,
- },
- []int32{2},
- "",
- },
- {
- "if/else false",
- []byte{
- opPUSHB000, // [0]
- 0,
- opIF,
- opPUSHB000, // not executed
- 2,
- opELSE,
- opPUSHB000, // [3]
- 3,
- opEIF,
- },
- []int32{3},
- "",
- },
- {
- "if/else true if/else false",
- // 0x58 is the opcode for opIF. The literal 0x58s below are pushed data.
- []byte{
- opPUSHB010, // [255, 0, 1]
- 255,
- 0,
- 1,
- opIF,
- opIF,
- opPUSHB001, // not executed
- 0x58,
- 0x58,
- opELSE,
- opPUSHW000, // [255, 0x5858]
- 0x58,
- 0x58,
- opEIF,
- opELSE,
- opIF,
- opNPUSHB, // not executed
- 3,
- 0x58,
- 0x58,
- 0x58,
- opELSE,
- opNPUSHW, // not executed
- 2,
- 0x58,
- 0x58,
- 0x58,
- 0x58,
- opEIF,
- opEIF,
- opPUSHB000, // [255, 0x5858, 254]
- 254,
- },
- []int32{255, 0x5858, 254},
- "",
- },
- {
- "if/else false if/else true",
- // 0x58 is the opcode for opIF. The literal 0x58s below are pushed data.
- []byte{
- opPUSHB010, // [255, 1, 0]
- 255,
- 1,
- 0,
- opIF,
- opIF,
- opPUSHB001, // not executed
- 0x58,
- 0x58,
- opELSE,
- opPUSHW000, // not executed
- 0x58,
- 0x58,
- opEIF,
- opELSE,
- opIF,
- opNPUSHB, // [255, 0x58, 0x58, 0x58]
- 3,
- 0x58,
- 0x58,
- 0x58,
- opELSE,
- opNPUSHW, // not executed
- 2,
- 0x58,
- 0x58,
- 0x58,
- 0x58,
- opEIF,
- opEIF,
- opPUSHB000, // [255, 0x58, 0x58, 0x58, 254]
- 254,
- },
- []int32{255, 0x58, 0x58, 0x58, 254},
- "",
- },
- {
- "logical ops",
- []byte{
- opPUSHB010, // [0, 10, 20]
- 0,
- 10,
- 20,
- opAND, // [0, 1]
- opOR, // [1]
- opNOT, // [0]
- },
- []int32{0},
- "",
- },
- {
- "arithmetic ops",
- // Calculate abs((-(1 - (2*3)))/2 + 1/64).
- // The answer is 5/2 + 1/64 in ideal numbers, or 161 in 26.6 fixed point math.
- []byte{
- opPUSHB010, // [64, 128, 192]
- 1 << 6,
- 2 << 6,
- 3 << 6,
- opMUL, // [64, 384]
- opSUB, // [-320]
- opNEG, // [320]
- opPUSHB000, // [320, 128]
- 2 << 6,
- opDIV, // [160]
- opPUSHB000, // [160, 1]
- 1,
- opADD, // [161]
- opABS, // [161]
- },
- []int32{161},
- "",
- },
- {
- "floor, ceiling",
- []byte{
- opPUSHB000, // [96]
- 96,
- opFLOOR, // [64]
- opPUSHB000, // [64, 96]
- 96,
- opCEILING, // [64, 128]
- },
- []int32{64, 128},
- "",
- },
- {
- "rounding",
- // Round 1.40625 (which is 90/64) under various rounding policies.
- // See figure 20 of https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#rounding
- []byte{
- opROFF, // []
- opPUSHB000, // [90]
- 90,
- opROUND00, // [90]
- opRTG, // [90]
- opPUSHB000, // [90, 90]
- 90,
- opROUND00, // [90, 64]
- opRTHG, // [90, 64]
- opPUSHB000, // [90, 64, 90]
- 90,
- opROUND00, // [90, 64, 96]
- opRDTG, // [90, 64, 96]
- opPUSHB000, // [90, 64, 96, 90]
- 90,
- opROUND00, // [90, 64, 96, 64]
- opRUTG, // [90, 64, 96, 64]
- opPUSHB000, // [90, 64, 96, 64, 90]
- 90,
- opROUND00, // [90, 64, 96, 64, 128]
- opRTDG, // [90, 64, 96, 64, 128]
- opPUSHB000, // [90, 64, 96, 64, 128, 90]
- 90,
- opROUND00, // [90, 64, 96, 64, 128, 96]
- },
- []int32{90, 64, 96, 64, 128, 96},
- "",
- },
- {
- "super-rounding",
- // See figure 20 of https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#rounding
- // and the sign preservation steps of the "Order of rounding operations" section.
- []byte{
- opPUSHB000, // [0x58]
- 0x58,
- opSROUND, // []
- opPUSHW000, // [-81]
- 0xff,
- 0xaf,
- opROUND00, // [-80]
- opPUSHW000, // [-80, -80]
- 0xff,
- 0xb0,
- opROUND00, // [-80, -80]
- opPUSHW000, // [-80, -80, -17]
- 0xff,
- 0xef,
- opROUND00, // [-80, -80, -16]
- opPUSHW000, // [-80, -80, -16, -16]
- 0xff,
- 0xf0,
- opROUND00, // [-80, -80, -16, -16]
- opPUSHB000, // [-80, -80, -16, -16, 0]
- 0,
- opROUND00, // [-80, -80, -16, -16, 16]
- opPUSHB000, // [-80, -80, -16, -16, 16, 16]
- 16,
- opROUND00, // [-80, -80, -16, -16, 16, 16]
- opPUSHB000, // [-80, -80, -16, -16, 16, 16, 47]
- 47,
- opROUND00, // [-80, -80, -16, -16, 16, 16, 16]
- opPUSHB000, // [-80, -80, -16, -16, 16, 16, 16, 48]
- 48,
- opROUND00, // [-80, -80, -16, -16, 16, 16, 16, 80]
- },
- []int32{-80, -80, -16, -16, 16, 16, 16, 80},
- "",
- },
- {
- "roll",
- []byte{
- opPUSHB010, // [1, 2, 3]
- 1,
- 2,
- 3,
- opROLL, // [2, 3, 1]
- },
- []int32{2, 3, 1},
- "",
- },
- {
- "max/min",
- []byte{
- opPUSHW001, // [-2, -3]
- 0xff,
- 0xfe,
- 0xff,
- 0xfd,
- opMAX, // [-2]
- opPUSHW001, // [-2, -4, -5]
- 0xff,
- 0xfc,
- 0xff,
- 0xfb,
- opMIN, // [-2, -5]
- },
- []int32{-2, -5},
- "",
- },
- {
- "functions",
- []byte{
- opPUSHB011, // [3, 7, 0, 3]
- 3,
- 7,
- 0,
- 3,
-
- opFDEF, // Function #3 (not called)
- opPUSHB000,
- 98,
- opENDF,
-
- opFDEF, // Function #0
- opDUP,
- opADD,
- opENDF,
-
- opFDEF, // Function #7
- opPUSHB001,
- 10,
- 0,
- opCALL,
- opDUP,
- opENDF,
-
- opFDEF, // Function #3 (again)
- opPUSHB000,
- 99,
- opENDF,
-
- opPUSHB001, // [2, 0]
- 2,
- 0,
- opCALL, // [4]
- opPUSHB000, // [4, 3]
- 3,
- opLOOPCALL, // [99, 99, 99, 99]
- opPUSHB000, // [99, 99, 99, 99, 7]
- 7,
- opCALL, // [99, 99, 99, 99, 20, 20]
- },
- []int32{99, 99, 99, 99, 20, 20},
- "",
- },
- }
-
- for _, tc := range testCases {
- h := &hinter{}
- h.init(&Font{
- maxStorage: 32,
- maxStackElements: 100,
- }, 768)
- err, errStr := h.run(tc.prog, nil, nil, nil, nil), ""
- if err != nil {
- errStr = err.Error()
- }
- if tc.errStr != "" {
- if errStr == "" {
- t.Errorf("%s: got no error, want %q", tc.desc, tc.errStr)
- } else if !strings.Contains(errStr, tc.errStr) {
- t.Errorf("%s: got error %q, want one containing %q", tc.desc, errStr, tc.errStr)
- }
- continue
- }
- if errStr != "" {
- t.Errorf("%s: got error %q, want none", tc.desc, errStr)
- continue
- }
- got := h.stack[:len(tc.want)]
- if !reflect.DeepEqual(got, tc.want) {
- t.Errorf("%s: got %v, want %v", tc.desc, got, tc.want)
- continue
- }
- }
-}
-
-// TestMove tests that the hinter.move method matches the output of the C
-// Freetype implementation.
-func TestMove(t *testing.T) {
- h, p := hinter{}, Point{}
- testCases := []struct {
- pvX, pvY, fvX, fvY f2dot14
- wantX, wantY int32
- }{
- {+0x4000, +0x0000, +0x4000, +0x0000, +1000, +0},
- {+0x4000, +0x0000, -0x4000, +0x0000, +1000, +0},
- {-0x4000, +0x0000, +0x4000, +0x0000, -1000, +0},
- {-0x4000, +0x0000, -0x4000, +0x0000, -1000, +0},
- {+0x0000, +0x4000, +0x0000, +0x4000, +0, +1000},
- {+0x0000, +0x4000, +0x0000, -0x4000, +0, +1000},
- {+0x4000, +0x0000, +0x2d41, +0x2d41, +1000, +1000},
- {+0x4000, +0x0000, -0x2d41, +0x2d41, +1000, -1000},
- {+0x4000, +0x0000, +0x2d41, -0x2d41, +1000, -1000},
- {+0x4000, +0x0000, -0x2d41, -0x2d41, +1000, +1000},
- {-0x4000, +0x0000, +0x2d41, +0x2d41, -1000, -1000},
- {-0x4000, +0x0000, -0x2d41, +0x2d41, -1000, +1000},
- {-0x4000, +0x0000, +0x2d41, -0x2d41, -1000, +1000},
- {-0x4000, +0x0000, -0x2d41, -0x2d41, -1000, -1000},
- {+0x376d, +0x2000, +0x2d41, +0x2d41, +732, +732},
- {-0x376d, +0x2000, +0x2d41, +0x2d41, -2732, -2732},
- {+0x376d, +0x2000, +0x2d41, -0x2d41, +2732, -2732},
- {-0x376d, +0x2000, +0x2d41, -0x2d41, -732, +732},
- {-0x376d, -0x2000, +0x2d41, +0x2d41, -732, -732},
- {+0x376d, +0x2000, +0x4000, +0x0000, +1155, +0},
- {+0x376d, +0x2000, +0x0000, +0x4000, +0, +2000},
- }
- for _, tc := range testCases {
- p = Point{}
- h.gs.pv = [2]f2dot14{tc.pvX, tc.pvY}
- h.gs.fv = [2]f2dot14{tc.fvX, tc.fvY}
- h.move(&p, 1000, true)
- tx := p.Flags&flagTouchedX != 0
- ty := p.Flags&flagTouchedY != 0
- wantTX := tc.fvX != 0
- wantTY := tc.fvY != 0
- if p.X != tc.wantX || p.Y != tc.wantY || tx != wantTX || ty != wantTY {
- t.Errorf("pv=%v, fv=%v\ngot %d, %d, %t, %t\nwant %d, %d, %t, %t",
- h.gs.pv, h.gs.fv, p.X, p.Y, tx, ty, tc.wantX, tc.wantY, wantTX, wantTY)
- continue
- }
-
- // Check that p is aligned with the freedom vector.
- a := int64(p.X) * int64(tc.fvY)
- b := int64(p.Y) * int64(tc.fvX)
- if a != b {
- t.Errorf("pv=%v, fv=%v, p=%v not aligned with fv", h.gs.pv, h.gs.fv, p)
- continue
- }
-
- // Check that the projected p is 1000 away from the origin.
- dotProd := (int64(p.X)*int64(tc.pvX) + int64(p.Y)*int64(tc.pvY) + 1<<13) >> 14
- if dotProd != 1000 {
- t.Errorf("pv=%v, fv=%v, p=%v not 1000 from origin", h.gs.pv, h.gs.fv, p)
- continue
- }
- }
-}
-
-// TestNormalize tests that the normalize function matches the output of the C
-// Freetype implementation.
-func TestNormalize(t *testing.T) {
- testCases := [][2]f2dot14{
- {-15895, 3974},
- {-15543, 5181},
- {-14654, 7327},
- {-11585, 11585},
- {0, 16384},
- {11585, 11585},
- {14654, 7327},
- {15543, 5181},
- {15895, 3974},
- {16066, 3213},
- {16161, 2694},
- {16219, 2317},
- {16257, 2032},
- {16284, 1809},
- }
- for i, want := range testCases {
- got := normalize(f2dot14(i)-4, 1)
- if got != want {
- t.Errorf("i=%d: got %v, want %v", i, got, want)
- }
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/opcodes.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/opcodes.go
deleted file mode 100644
index 1880e1e63..000000000
--- a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/opcodes.go
+++ /dev/null
@@ -1,289 +0,0 @@
-// Copyright 2012 The Freetype-Go Authors. All rights reserved.
-// Use of this source code is governed by your choice of either the
-// FreeType License or the GNU General Public License version 2 (or
-// any later version), both of which can be found in the LICENSE file.
-
-package truetype
-
-// The Truetype opcodes are summarized at
-// https://developer.apple.com/fonts/TTRefMan/RM07/appendixA.html
-
-const (
- opSVTCA0 = 0x00 // Set freedom and projection Vectors To Coordinate Axis
- opSVTCA1 = 0x01 // .
- opSPVTCA0 = 0x02 // Set Projection Vector To Coordinate Axis
- opSPVTCA1 = 0x03 // .
- opSFVTCA0 = 0x04 // Set Freedom Vector to Coordinate Axis
- opSFVTCA1 = 0x05 // .
- opSPVTL0 = 0x06 // Set Projection Vector To Line
- opSPVTL1 = 0x07 // .
- opSFVTL0 = 0x08 // Set Freedom Vector To Line
- opSFVTL1 = 0x09 // .
- opSPVFS = 0x0a // Set Projection Vector From Stack
- opSFVFS = 0x0b // Set Freedom Vector From Stack
- opGPV = 0x0c // Get Projection Vector
- opGFV = 0x0d // Get Freedom Vector
- opSFVTPV = 0x0e // Set Freedom Vector To Projection Vector
- opISECT = 0x0f // moves point p to the InterSECTion of two lines
- opSRP0 = 0x10 // Set Reference Point 0
- opSRP1 = 0x11 // Set Reference Point 1
- opSRP2 = 0x12 // Set Reference Point 2
- opSZP0 = 0x13 // Set Zone Pointer 0
- opSZP1 = 0x14 // Set Zone Pointer 1
- opSZP2 = 0x15 // Set Zone Pointer 2
- opSZPS = 0x16 // Set Zone PointerS
- opSLOOP = 0x17 // Set LOOP variable
- opRTG = 0x18 // Round To Grid
- opRTHG = 0x19 // Round To Half Grid
- opSMD = 0x1a // Set Minimum Distance
- opELSE = 0x1b // ELSE clause
- opJMPR = 0x1c // JuMP Relative
- opSCVTCI = 0x1d // Set Control Value Table Cut-In
- opSSWCI = 0x1e // Set Single Width Cut-In
- opSSW = 0x1f // Set Single Width
- opDUP = 0x20 // DUPlicate top stack element
- opPOP = 0x21 // POP top stack element
- opCLEAR = 0x22 // CLEAR the stack
- opSWAP = 0x23 // SWAP the top two elements on the stack
- opDEPTH = 0x24 // DEPTH of the stack
- opCINDEX = 0x25 // Copy the INDEXed element to the top of the stack
- opMINDEX = 0x26 // Move the INDEXed element to the top of the stack
- opALIGNPTS = 0x27 // ALIGN PoinTS
- op_0x28 = 0x28 // deprecated
- opUTP = 0x29 // UnTouch Point
- opLOOPCALL = 0x2a // LOOP and CALL function
- opCALL = 0x2b // CALL function
- opFDEF = 0x2c // Function DEFinition
- opENDF = 0x2d // END Function definition
- opMDAP0 = 0x2e // Move Direct Absolute Point
- opMDAP1 = 0x2f // .
- opIUP0 = 0x30 // Interpolate Untouched Points through the outline
- opIUP1 = 0x31 // .
- opSHP0 = 0x32 // SHift Point using reference point
- opSHP1 = 0x33 // .
- opSHC0 = 0x34 // SHift Contour using reference point
- opSHC1 = 0x35 // .
- opSHZ0 = 0x36 // SHift Zone using reference point
- opSHZ1 = 0x37 // .
- opSHPIX = 0x38 // SHift point by a PIXel amount
- opIP = 0x39 // Interpolate Point
- opMSIRP0 = 0x3a // Move Stack Indirect Relative Point
- opMSIRP1 = 0x3b // .
- opALIGNRP = 0x3c // ALIGN to Reference Point
- opRTDG = 0x3d // Round To Double Grid
- opMIAP0 = 0x3e // Move Indirect Absolute Point
- opMIAP1 = 0x3f // .
- opNPUSHB = 0x40 // PUSH N Bytes
- opNPUSHW = 0x41 // PUSH N Words
- opWS = 0x42 // Write Store
- opRS = 0x43 // Read Store
- opWCVTP = 0x44 // Write Control Value Table in Pixel units
- opRCVT = 0x45 // Read Control Value Table entry
- opGC0 = 0x46 // Get Coordinate projected onto the projection vector
- opGC1 = 0x47 // .
- opSCFS = 0x48 // Sets Coordinate From the Stack using projection vector and freedom vector
- opMD0 = 0x49 // Measure Distance
- opMD1 = 0x4a // .
- opMPPEM = 0x4b // Measure Pixels Per EM
- opMPS = 0x4c // Measure Point Size
- opFLIPON = 0x4d // set the auto FLIP Boolean to ON
- opFLIPOFF = 0x4e // set the auto FLIP Boolean to OFF
- opDEBUG = 0x4f // DEBUG call
- opLT = 0x50 // Less Than
- opLTEQ = 0x51 // Less Than or EQual
- opGT = 0x52 // Greater Than
- opGTEQ = 0x53 // Greater Than or EQual
- opEQ = 0x54 // EQual
- opNEQ = 0x55 // Not EQual
- opODD = 0x56 // ODD
- opEVEN = 0x57 // EVEN
- opIF = 0x58 // IF test
- opEIF = 0x59 // End IF
- opAND = 0x5a // logical AND
- opOR = 0x5b // logical OR
- opNOT = 0x5c // logical NOT
- opDELTAP1 = 0x5d // DELTA exception P1
- opSDB = 0x5e // Set Delta Base in the graphics state
- opSDS = 0x5f // Set Delta Shift in the graphics state
- opADD = 0x60 // ADD
- opSUB = 0x61 // SUBtract
- opDIV = 0x62 // DIVide
- opMUL = 0x63 // MULtiply
- opABS = 0x64 // ABSolute value
- opNEG = 0x65 // NEGate
- opFLOOR = 0x66 // FLOOR
- opCEILING = 0x67 // CEILING
- opROUND00 = 0x68 // ROUND value
- opROUND01 = 0x69 // .
- opROUND10 = 0x6a // .
- opROUND11 = 0x6b // .
- opNROUND00 = 0x6c // No ROUNDing of value
- opNROUND01 = 0x6d // .
- opNROUND10 = 0x6e // .
- opNROUND11 = 0x6f // .
- opWCVTF = 0x70 // Write Control Value Table in Funits
- opDELTAP2 = 0x71 // DELTA exception P2
- opDELTAP3 = 0x72 // DELTA exception P3
- opDELTAC1 = 0x73 // DELTA exception C1
- opDELTAC2 = 0x74 // DELTA exception C2
- opDELTAC3 = 0x75 // DELTA exception C3
- opSROUND = 0x76 // Super ROUND
- opS45ROUND = 0x77 // Super ROUND 45 degrees
- opJROT = 0x78 // Jump Relative On True
- opJROF = 0x79 // Jump Relative On False
- opROFF = 0x7a // Round OFF
- op_0x7b = 0x7b // deprecated
- opRUTG = 0x7c // Round Up To Grid
- opRDTG = 0x7d // Round Down To Grid
- opSANGW = 0x7e // Set ANGle Weight
- opAA = 0x7f // Adjust Angle
- opFLIPPT = 0x80 // FLIP PoinT
- opFLIPRGON = 0x81 // FLIP RanGe ON
- opFLIPRGOFF = 0x82 // FLIP RanGe OFF
- op_0x83 = 0x83 // deprecated
- op_0x84 = 0x84 // deprecated
- opSCANCTRL = 0x85 // SCAN conversion ConTRoL
- opSDPVTL0 = 0x86 // Set Dual Projection Vector To Line
- opSDPVTL1 = 0x87 // .
- opGETINFO = 0x88 // GET INFOrmation
- opIDEF = 0x89 // Instruction DEFinition
- opROLL = 0x8a // ROLL the top three stack elements
- opMAX = 0x8b // MAXimum of top two stack elements
- opMIN = 0x8c // MINimum of top two stack elements
- opSCANTYPE = 0x8d // SCANTYPE
- opINSTCTRL = 0x8e // INSTRuction execution ConTRoL
- op_0x8f = 0x8f
- op_0x90 = 0x90
- op_0x91 = 0x91
- op_0x92 = 0x92
- op_0x93 = 0x93
- op_0x94 = 0x94
- op_0x95 = 0x95
- op_0x96 = 0x96
- op_0x97 = 0x97
- op_0x98 = 0x98
- op_0x99 = 0x99
- op_0x9a = 0x9a
- op_0x9b = 0x9b
- op_0x9c = 0x9c
- op_0x9d = 0x9d
- op_0x9e = 0x9e
- op_0x9f = 0x9f
- op_0xa0 = 0xa0
- op_0xa1 = 0xa1
- op_0xa2 = 0xa2
- op_0xa3 = 0xa3
- op_0xa4 = 0xa4
- op_0xa5 = 0xa5
- op_0xa6 = 0xa6
- op_0xa7 = 0xa7
- op_0xa8 = 0xa8
- op_0xa9 = 0xa9
- op_0xaa = 0xaa
- op_0xab = 0xab
- op_0xac = 0xac
- op_0xad = 0xad
- op_0xae = 0xae
- op_0xaf = 0xaf
- opPUSHB000 = 0xb0 // PUSH Bytes
- opPUSHB001 = 0xb1 // .
- opPUSHB010 = 0xb2 // .
- opPUSHB011 = 0xb3 // .
- opPUSHB100 = 0xb4 // .
- opPUSHB101 = 0xb5 // .
- opPUSHB110 = 0xb6 // .
- opPUSHB111 = 0xb7 // .
- opPUSHW000 = 0xb8 // PUSH Words
- opPUSHW001 = 0xb9 // .
- opPUSHW010 = 0xba // .
- opPUSHW011 = 0xbb // .
- opPUSHW100 = 0xbc // .
- opPUSHW101 = 0xbd // .
- opPUSHW110 = 0xbe // .
- opPUSHW111 = 0xbf // .
- opMDRP00000 = 0xc0 // Move Direct Relative Point
- opMDRP00001 = 0xc1 // .
- opMDRP00010 = 0xc2 // .
- opMDRP00011 = 0xc3 // .
- opMDRP00100 = 0xc4 // .
- opMDRP00101 = 0xc5 // .
- opMDRP00110 = 0xc6 // .
- opMDRP00111 = 0xc7 // .
- opMDRP01000 = 0xc8 // .
- opMDRP01001 = 0xc9 // .
- opMDRP01010 = 0xca // .
- opMDRP01011 = 0xcb // .
- opMDRP01100 = 0xcc // .
- opMDRP01101 = 0xcd // .
- opMDRP01110 = 0xce // .
- opMDRP01111 = 0xcf // .
- opMDRP10000 = 0xd0 // .
- opMDRP10001 = 0xd1 // .
- opMDRP10010 = 0xd2 // .
- opMDRP10011 = 0xd3 // .
- opMDRP10100 = 0xd4 // .
- opMDRP10101 = 0xd5 // .
- opMDRP10110 = 0xd6 // .
- opMDRP10111 = 0xd7 // .
- opMDRP11000 = 0xd8 // .
- opMDRP11001 = 0xd9 // .
- opMDRP11010 = 0xda // .
- opMDRP11011 = 0xdb // .
- opMDRP11100 = 0xdc // .
- opMDRP11101 = 0xdd // .
- opMDRP11110 = 0xde // .
- opMDRP11111 = 0xdf // .
- opMIRP00000 = 0xe0 // Move Indirect Relative Point
- opMIRP00001 = 0xe1 // .
- opMIRP00010 = 0xe2 // .
- opMIRP00011 = 0xe3 // .
- opMIRP00100 = 0xe4 // .
- opMIRP00101 = 0xe5 // .
- opMIRP00110 = 0xe6 // .
- opMIRP00111 = 0xe7 // .
- opMIRP01000 = 0xe8 // .
- opMIRP01001 = 0xe9 // .
- opMIRP01010 = 0xea // .
- opMIRP01011 = 0xeb // .
- opMIRP01100 = 0xec // .
- opMIRP01101 = 0xed // .
- opMIRP01110 = 0xee // .
- opMIRP01111 = 0xef // .
- opMIRP10000 = 0xf0 // .
- opMIRP10001 = 0xf1 // .
- opMIRP10010 = 0xf2 // .
- opMIRP10011 = 0xf3 // .
- opMIRP10100 = 0xf4 // .
- opMIRP10101 = 0xf5 // .
- opMIRP10110 = 0xf6 // .
- opMIRP10111 = 0xf7 // .
- opMIRP11000 = 0xf8 // .
- opMIRP11001 = 0xf9 // .
- opMIRP11010 = 0xfa // .
- opMIRP11011 = 0xfb // .
- opMIRP11100 = 0xfc // .
- opMIRP11101 = 0xfd // .
- opMIRP11110 = 0xfe // .
- opMIRP11111 = 0xff // .
-)
-
-// popCount is the number of stack elements that each opcode pops.
-var popCount = [256]uint8{
- // 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f
- 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 5, // 0x00 - 0x0f
- 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, // 0x10 - 0x1f
- 1, 1, 0, 2, 0, 1, 1, 2, 0, 1, 2, 1, 1, 0, 1, 1, // 0x20 - 0x2f
- 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 2, 2, 0, 0, 2, 2, // 0x30 - 0x3f
- 0, 0, 2, 1, 2, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, // 0x40 - 0x4f
- 2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 2, 2, 1, 1, 1, 1, // 0x50 - 0x5f
- 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 - 0x6f
- 2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0, 0, 0, 1, 1, // 0x70 - 0x7f
- 0, 2, 2, 0, 0, 1, 2, 2, 1, 1, 3, 2, 2, 1, 2, 0, // 0x80 - 0x8f
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90 - 0x9f
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xa0 - 0xaf
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0 - 0xbf
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xc0 - 0xcf
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xd0 - 0xdf
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xe0 - 0xef
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xf0 - 0xff
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype.go
deleted file mode 100644
index 96ceef547..000000000
--- a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype.go
+++ /dev/null
@@ -1,554 +0,0 @@
-// Copyright 2010 The Freetype-Go Authors. All rights reserved.
-// Use of this source code is governed by your choice of either the
-// FreeType License or the GNU General Public License version 2 (or
-// any later version), both of which can be found in the LICENSE file.
-
-// Package truetype provides a parser for the TTF and TTC file formats.
-// Those formats are documented at http://developer.apple.com/fonts/TTRefMan/
-// and http://www.microsoft.com/typography/otspec/
-//
-// Some of a font's methods provide lengths or co-ordinates, e.g. bounds, font
-// metrics and control points. All these methods take a scale parameter, which
-// is the number of device units in 1 em. For example, if 1 em is 10 pixels and
-// 1 pixel is 64 units, then scale is 640. If the device space involves pixels,
-// 64 units per pixel is recommended, since that is what the bytecode hinter
-// uses when snapping point co-ordinates to the pixel grid.
-//
-// To measure a TrueType font in ideal FUnit space, use scale equal to
-// font.FUnitsPerEm().
-package truetype
-
-import (
- "fmt"
-)
-
-// An Index is a Font's index of a rune.
-type Index uint16
-
-// A Bounds holds the co-ordinate range of one or more glyphs.
-// The endpoints are inclusive.
-type Bounds struct {
- XMin, YMin, XMax, YMax int32
-}
-
-// An HMetric holds the horizontal metrics of a single glyph.
-type HMetric struct {
- AdvanceWidth, LeftSideBearing int32
-}
-
-// A VMetric holds the vertical metrics of a single glyph.
-type VMetric struct {
- AdvanceHeight, TopSideBearing int32
-}
-
-// A FormatError reports that the input is not a valid TrueType font.
-type FormatError string
-
-func (e FormatError) Error() string {
- return "freetype: invalid TrueType format: " + string(e)
-}
-
-// An UnsupportedError reports that the input uses a valid but unimplemented
-// TrueType feature.
-type UnsupportedError string
-
-func (e UnsupportedError) Error() string {
- return "freetype: unsupported TrueType feature: " + string(e)
-}
-
-// u32 returns the big-endian uint32 at b[i:].
-func u32(b []byte, i int) uint32 {
- return uint32(b[i])<<24 | uint32(b[i+1])<<16 | uint32(b[i+2])<<8 | uint32(b[i+3])
-}
-
-// u16 returns the big-endian uint16 at b[i:].
-func u16(b []byte, i int) uint16 {
- return uint16(b[i])<<8 | uint16(b[i+1])
-}
-
-// readTable returns a slice of the TTF data given by a table's directory entry.
-func readTable(ttf []byte, offsetLength []byte) ([]byte, error) {
- offset := int(u32(offsetLength, 0))
- if offset < 0 {
- return nil, FormatError(fmt.Sprintf("offset too large: %d", uint32(offset)))
- }
- length := int(u32(offsetLength, 4))
- if length < 0 {
- return nil, FormatError(fmt.Sprintf("length too large: %d", uint32(length)))
- }
- end := offset + length
- if end < 0 || end > len(ttf) {
- return nil, FormatError(fmt.Sprintf("offset + length too large: %d", uint32(offset)+uint32(length)))
- }
- return ttf[offset:end], nil
-}
-
-const (
- locaOffsetFormatUnknown int = iota
- locaOffsetFormatShort
- locaOffsetFormatLong
-)
-
-// A cm holds a parsed cmap entry.
-type cm struct {
- start, end, delta, offset uint32
-}
-
-// A Font represents a Truetype font.
-type Font struct {
- // Tables sliced from the TTF data. The different tables are documented
- // at http://developer.apple.com/fonts/TTRefMan/RM06/Chap6.html
- cmap, cvt, fpgm, glyf, hdmx, head, hhea, hmtx, kern, loca, maxp, os2, prep, vmtx []byte
-
- cmapIndexes []byte
-
- // Cached values derived from the raw ttf data.
- cm []cm
- locaOffsetFormat int
- nGlyph, nHMetric, nKern int
- fUnitsPerEm int32
- bounds Bounds
- // Values from the maxp section.
- maxTwilightPoints, maxStorage, maxFunctionDefs, maxStackElements uint16
-}
-
-func (f *Font) parseCmap() error {
- const (
- cmapFormat4 = 4
- cmapFormat12 = 12
- languageIndependent = 0
-
- // A 32-bit encoding consists of a most-significant 16-bit Platform ID and a
- // least-significant 16-bit Platform Specific ID. The magic numbers are
- // specified at https://www.microsoft.com/typography/otspec/name.htm
- unicodeEncoding = 0x00000003 // PID = 0 (Unicode), PSID = 3 (Unicode 2.0)
- microsoftSymbolEncoding = 0x00030000 // PID = 3 (Microsoft), PSID = 0 (Symbol)
- microsoftUCS2Encoding = 0x00030001 // PID = 3 (Microsoft), PSID = 1 (UCS-2)
- microsoftUCS4Encoding = 0x0003000a // PID = 3 (Microsoft), PSID = 10 (UCS-4)
- )
-
- if len(f.cmap) < 4 {
- return FormatError("cmap too short")
- }
- nsubtab := int(u16(f.cmap, 2))
- if len(f.cmap) < 8*nsubtab+4 {
- return FormatError("cmap too short")
- }
- offset, found, x := 0, false, 4
- for i := 0; i < nsubtab; i++ {
- // We read the 16-bit Platform ID and 16-bit Platform Specific ID as a single uint32.
- // All values are big-endian.
- pidPsid, o := u32(f.cmap, x), u32(f.cmap, x+4)
- x += 8
- // We prefer the Unicode cmap encoding. Failing to find that, we fall
- // back onto the Microsoft cmap encoding.
- if pidPsid == unicodeEncoding {
- offset, found = int(o), true
- break
-
- } else if pidPsid == microsoftSymbolEncoding ||
- pidPsid == microsoftUCS2Encoding ||
- pidPsid == microsoftUCS4Encoding {
-
- offset, found = int(o), true
- // We don't break out of the for loop, so that Unicode can override Microsoft.
- }
- }
- if !found {
- return UnsupportedError("cmap encoding")
- }
- if offset <= 0 || offset > len(f.cmap) {
- return FormatError("bad cmap offset")
- }
-
- cmapFormat := u16(f.cmap, offset)
- switch cmapFormat {
- case cmapFormat4:
- language := u16(f.cmap, offset+4)
- if language != languageIndependent {
- return UnsupportedError(fmt.Sprintf("language: %d", language))
- }
- segCountX2 := int(u16(f.cmap, offset+6))
- if segCountX2%2 == 1 {
- return FormatError(fmt.Sprintf("bad segCountX2: %d", segCountX2))
- }
- segCount := segCountX2 / 2
- offset += 14
- f.cm = make([]cm, segCount)
- for i := 0; i < segCount; i++ {
- f.cm[i].end = uint32(u16(f.cmap, offset))
- offset += 2
- }
- offset += 2
- for i := 0; i < segCount; i++ {
- f.cm[i].start = uint32(u16(f.cmap, offset))
- offset += 2
- }
- for i := 0; i < segCount; i++ {
- f.cm[i].delta = uint32(u16(f.cmap, offset))
- offset += 2
- }
- for i := 0; i < segCount; i++ {
- f.cm[i].offset = uint32(u16(f.cmap, offset))
- offset += 2
- }
- f.cmapIndexes = f.cmap[offset:]
- return nil
-
- case cmapFormat12:
- if u16(f.cmap, offset+2) != 0 {
- return FormatError(fmt.Sprintf("cmap format: % x", f.cmap[offset:offset+4]))
- }
- length := u32(f.cmap, offset+4)
- language := u32(f.cmap, offset+8)
- if language != languageIndependent {
- return UnsupportedError(fmt.Sprintf("language: %d", language))
- }
- nGroups := u32(f.cmap, offset+12)
- if length != 12*nGroups+16 {
- return FormatError("inconsistent cmap length")
- }
- offset += 16
- f.cm = make([]cm, nGroups)
- for i := uint32(0); i < nGroups; i++ {
- f.cm[i].start = u32(f.cmap, offset+0)
- f.cm[i].end = u32(f.cmap, offset+4)
- f.cm[i].delta = u32(f.cmap, offset+8) - f.cm[i].start
- offset += 12
- }
- return nil
- }
- return UnsupportedError(fmt.Sprintf("cmap format: %d", cmapFormat))
-}
-
-func (f *Font) parseHead() error {
- if len(f.head) != 54 {
- return FormatError(fmt.Sprintf("bad head length: %d", len(f.head)))
- }
- f.fUnitsPerEm = int32(u16(f.head, 18))
- f.bounds.XMin = int32(int16(u16(f.head, 36)))
- f.bounds.YMin = int32(int16(u16(f.head, 38)))
- f.bounds.XMax = int32(int16(u16(f.head, 40)))
- f.bounds.YMax = int32(int16(u16(f.head, 42)))
- switch i := u16(f.head, 50); i {
- case 0:
- f.locaOffsetFormat = locaOffsetFormatShort
- case 1:
- f.locaOffsetFormat = locaOffsetFormatLong
- default:
- return FormatError(fmt.Sprintf("bad indexToLocFormat: %d", i))
- }
- return nil
-}
-
-func (f *Font) parseHhea() error {
- if len(f.hhea) != 36 {
- return FormatError(fmt.Sprintf("bad hhea length: %d", len(f.hhea)))
- }
- f.nHMetric = int(u16(f.hhea, 34))
- if 4*f.nHMetric+2*(f.nGlyph-f.nHMetric) != len(f.hmtx) {
- return FormatError(fmt.Sprintf("bad hmtx length: %d", len(f.hmtx)))
- }
- return nil
-}
-
-func (f *Font) parseKern() error {
- // Apple's TrueType documentation (http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html) says:
- // "Previous versions of the 'kern' table defined both the version and nTables fields in the header
- // as UInt16 values and not UInt32 values. Use of the older format on the Mac OS is discouraged
- // (although AAT can sense an old kerning table and still make correct use of it). Microsoft
- // Windows still uses the older format for the 'kern' table and will not recognize the newer one.
- // Fonts targeted for the Mac OS only should use the new format; fonts targeted for both the Mac OS
- // and Windows should use the old format."
- // Since we expect that almost all fonts aim to be Windows-compatible, we only parse the "older" format,
- // just like the C Freetype implementation.
- if len(f.kern) == 0 {
- if f.nKern != 0 {
- return FormatError("bad kern table length")
- }
- return nil
- }
- if len(f.kern) < 18 {
- return FormatError("kern data too short")
- }
- version, offset := u16(f.kern, 0), 2
- if version != 0 {
- return UnsupportedError(fmt.Sprintf("kern version: %d", version))
- }
- n, offset := u16(f.kern, offset), offset+2
- if n != 1 {
- return UnsupportedError(fmt.Sprintf("kern nTables: %d", n))
- }
- offset += 2
- length, offset := int(u16(f.kern, offset)), offset+2
- coverage, offset := u16(f.kern, offset), offset+2
- if coverage != 0x0001 {
- // We only support horizontal kerning.
- return UnsupportedError(fmt.Sprintf("kern coverage: 0x%04x", coverage))
- }
- f.nKern, offset = int(u16(f.kern, offset)), offset+2
- if 6*f.nKern != length-14 {
- return FormatError("bad kern table length")
- }
- return nil
-}
-
-func (f *Font) parseMaxp() error {
- if len(f.maxp) != 32 {
- return FormatError(fmt.Sprintf("bad maxp length: %d", len(f.maxp)))
- }
- f.nGlyph = int(u16(f.maxp, 4))
- f.maxTwilightPoints = u16(f.maxp, 16)
- f.maxStorage = u16(f.maxp, 18)
- f.maxFunctionDefs = u16(f.maxp, 20)
- f.maxStackElements = u16(f.maxp, 24)
- return nil
-}
-
-// scale returns x divided by f.fUnitsPerEm, rounded to the nearest integer.
-func (f *Font) scale(x int32) int32 {
- if x >= 0 {
- x += f.fUnitsPerEm / 2
- } else {
- x -= f.fUnitsPerEm / 2
- }
- return x / f.fUnitsPerEm
-}
-
-// Bounds returns the union of a Font's glyphs' bounds.
-func (f *Font) Bounds(scale int32) Bounds {
- b := f.bounds
- b.XMin = f.scale(scale * b.XMin)
- b.YMin = f.scale(scale * b.YMin)
- b.XMax = f.scale(scale * b.XMax)
- b.YMax = f.scale(scale * b.YMax)
- return b
-}
-
-// FUnitsPerEm returns the number of FUnits in a Font's em-square's side.
-func (f *Font) FUnitsPerEm() int32 {
- return f.fUnitsPerEm
-}
-
-// Index returns a Font's index for the given rune.
-func (f *Font) Index(x rune) Index {
- c := uint32(x)
- for i, j := 0, len(f.cm); i < j; {
- h := i + (j-i)/2
- cm := &f.cm[h]
- if c < cm.start {
- j = h
- } else if cm.end < c {
- i = h + 1
- } else if cm.offset == 0 {
- return Index(c + cm.delta)
- } else {
- offset := int(cm.offset) + 2*(h-len(f.cm)+int(c-cm.start))
- return Index(u16(f.cmapIndexes, offset))
- }
- }
- return 0
-}
-
-// unscaledHMetric returns the unscaled horizontal metrics for the glyph with
-// the given index.
-func (f *Font) unscaledHMetric(i Index) (h HMetric) {
- j := int(i)
- if j < 0 || f.nGlyph <= j {
- return HMetric{}
- }
- if j >= f.nHMetric {
- p := 4 * (f.nHMetric - 1)
- return HMetric{
- AdvanceWidth: int32(u16(f.hmtx, p)),
- LeftSideBearing: int32(int16(u16(f.hmtx, p+2*(j-f.nHMetric)+4))),
- }
- }
- return HMetric{
- AdvanceWidth: int32(u16(f.hmtx, 4*j)),
- LeftSideBearing: int32(int16(u16(f.hmtx, 4*j+2))),
- }
-}
-
-// HMetric returns the horizontal metrics for the glyph with the given index.
-func (f *Font) HMetric(scale int32, i Index) HMetric {
- h := f.unscaledHMetric(i)
- h.AdvanceWidth = f.scale(scale * h.AdvanceWidth)
- h.LeftSideBearing = f.scale(scale * h.LeftSideBearing)
- return h
-}
-
-// unscaledVMetric returns the unscaled vertical metrics for the glyph with
-// the given index. yMax is the top of the glyph's bounding box.
-func (f *Font) unscaledVMetric(i Index, yMax int32) (v VMetric) {
- j := int(i)
- if j < 0 || f.nGlyph <= j {
- return VMetric{}
- }
- if 4*j+4 <= len(f.vmtx) {
- return VMetric{
- AdvanceHeight: int32(u16(f.vmtx, 4*j)),
- TopSideBearing: int32(int16(u16(f.vmtx, 4*j+2))),
- }
- }
- // The OS/2 table has grown over time.
- // https://developer.apple.com/fonts/TTRefMan/RM06/Chap6OS2.html
- // says that it was originally 68 bytes. Optional fields, including
- // the ascender and descender, are described at
- // http://www.microsoft.com/typography/otspec/os2.htm
- if len(f.os2) >= 72 {
- sTypoAscender := int32(int16(u16(f.os2, 68)))
- sTypoDescender := int32(int16(u16(f.os2, 70)))
- return VMetric{
- AdvanceHeight: sTypoAscender - sTypoDescender,
- TopSideBearing: sTypoAscender - yMax,
- }
- }
- return VMetric{
- AdvanceHeight: f.fUnitsPerEm,
- TopSideBearing: 0,
- }
-}
-
-// VMetric returns the vertical metrics for the glyph with the given index.
-func (f *Font) VMetric(scale int32, i Index) VMetric {
- // TODO: should 0 be bounds.YMax?
- v := f.unscaledVMetric(i, 0)
- v.AdvanceHeight = f.scale(scale * v.AdvanceHeight)
- v.TopSideBearing = f.scale(scale * v.TopSideBearing)
- return v
-}
-
-// Kerning returns the kerning for the given glyph pair.
-func (f *Font) Kerning(scale int32, i0, i1 Index) int32 {
- if f.nKern == 0 {
- return 0
- }
- g := uint32(i0)<<16 | uint32(i1)
- lo, hi := 0, f.nKern
- for lo < hi {
- i := (lo + hi) / 2
- ig := u32(f.kern, 18+6*i)
- if ig < g {
- lo = i + 1
- } else if ig > g {
- hi = i
- } else {
- return f.scale(scale * int32(int16(u16(f.kern, 22+6*i))))
- }
- }
- return 0
-}
-
-// Parse returns a new Font for the given TTF or TTC data.
-//
-// For TrueType Collections, the first font in the collection is parsed.
-func Parse(ttf []byte) (font *Font, err error) {
- return parse(ttf, 0)
-}
-
-func parse(ttf []byte, offset int) (font *Font, err error) {
- if len(ttf)-offset < 12 {
- err = FormatError("TTF data is too short")
- return
- }
- originalOffset := offset
- magic, offset := u32(ttf, offset), offset+4
- switch magic {
- case 0x00010000:
- // No-op.
- case 0x74746366: // "ttcf" as a big-endian uint32.
- if originalOffset != 0 {
- err = FormatError("recursive TTC")
- return
- }
- ttcVersion, offset := u32(ttf, offset), offset+4
- if ttcVersion != 0x00010000 {
- // TODO: support TTC version 2.0, once I have such a .ttc file to test with.
- err = FormatError("bad TTC version")
- return
- }
- numFonts, offset := int(u32(ttf, offset)), offset+4
- if numFonts <= 0 {
- err = FormatError("bad number of TTC fonts")
- return
- }
- if len(ttf[offset:])/4 < numFonts {
- err = FormatError("TTC offset table is too short")
- return
- }
- // TODO: provide an API to select which font in a TrueType collection to return,
- // not just the first one. This may require an API to parse a TTC's name tables,
- // so users of this package can select the font in a TTC by name.
- offset = int(u32(ttf, offset))
- if offset <= 0 || offset > len(ttf) {
- err = FormatError("bad TTC offset")
- return
- }
- return parse(ttf, offset)
- default:
- err = FormatError("bad TTF version")
- return
- }
- n, offset := int(u16(ttf, offset)), offset+2
- if len(ttf) < 16*n+12 {
- err = FormatError("TTF data is too short")
- return
- }
- f := new(Font)
- // Assign the table slices.
- for i := 0; i < n; i++ {
- x := 16*i + 12
- switch string(ttf[x : x+4]) {
- case "cmap":
- f.cmap, err = readTable(ttf, ttf[x+8:x+16])
- case "cvt ":
- f.cvt, err = readTable(ttf, ttf[x+8:x+16])
- case "fpgm":
- f.fpgm, err = readTable(ttf, ttf[x+8:x+16])
- case "glyf":
- f.glyf, err = readTable(ttf, ttf[x+8:x+16])
- case "hdmx":
- f.hdmx, err = readTable(ttf, ttf[x+8:x+16])
- case "head":
- f.head, err = readTable(ttf, ttf[x+8:x+16])
- case "hhea":
- f.hhea, err = readTable(ttf, ttf[x+8:x+16])
- case "hmtx":
- f.hmtx, err = readTable(ttf, ttf[x+8:x+16])
- case "kern":
- f.kern, err = readTable(ttf, ttf[x+8:x+16])
- case "loca":
- f.loca, err = readTable(ttf, ttf[x+8:x+16])
- case "maxp":
- f.maxp, err = readTable(ttf, ttf[x+8:x+16])
- case "OS/2":
- f.os2, err = readTable(ttf, ttf[x+8:x+16])
- case "prep":
- f.prep, err = readTable(ttf, ttf[x+8:x+16])
- case "vmtx":
- f.vmtx, err = readTable(ttf, ttf[x+8:x+16])
- }
- if err != nil {
- return
- }
- }
- // Parse and sanity-check the TTF data.
- if err = f.parseHead(); err != nil {
- return
- }
- if err = f.parseMaxp(); err != nil {
- return
- }
- if err = f.parseCmap(); err != nil {
- return
- }
- if err = f.parseKern(); err != nil {
- return
- }
- if err = f.parseHhea(); err != nil {
- return
- }
- font = f
- return
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype_test.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype_test.go
deleted file mode 100644
index 9ef6ec8d2..000000000
--- a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype_test.go
+++ /dev/null
@@ -1,366 +0,0 @@
-// Copyright 2012 The Freetype-Go Authors. All rights reserved.
-// Use of this source code is governed by your choice of either the
-// FreeType License or the GNU General Public License version 2 (or
-// any later version), both of which can be found in the LICENSE file.
-
-package truetype
-
-import (
- "bufio"
- "fmt"
- "io"
- "io/ioutil"
- "os"
- "strconv"
- "strings"
- "testing"
-)
-
-func parseTestdataFont(name string) (font *Font, testdataIsOptional bool, err error) {
- b, err := ioutil.ReadFile(fmt.Sprintf("../../testdata/%s.ttf", name))
- if err != nil {
- // The "x-foo" fonts are optional tests, as they are not checked
- // in for copyright or file size reasons.
- return nil, strings.HasPrefix(name, "x-"), fmt.Errorf("%s: ReadFile: %v", name, err)
- }
- font, err = Parse(b)
- if err != nil {
- return nil, true, fmt.Errorf("%s: Parse: %v", name, err)
- }
- return font, false, nil
-}
-
-// TestParse tests that the luxisr.ttf metrics and glyphs are parsed correctly.
-// The numerical values can be manually verified by examining luxisr.ttx.
-func TestParse(t *testing.T) {
- font, _, err := parseTestdataFont("luxisr")
- if err != nil {
- t.Fatal(err)
- }
- if got, want := font.FUnitsPerEm(), int32(2048); got != want {
- t.Errorf("FUnitsPerEm: got %v, want %v", got, want)
- }
- fupe := font.FUnitsPerEm()
- if got, want := font.Bounds(fupe), (Bounds{-441, -432, 2024, 2033}); got != want {
- t.Errorf("Bounds: got %v, want %v", got, want)
- }
-
- i0 := font.Index('A')
- i1 := font.Index('V')
- if i0 != 36 || i1 != 57 {
- t.Fatalf("Index: i0, i1 = %d, %d, want 36, 57", i0, i1)
- }
- if got, want := font.HMetric(fupe, i0), (HMetric{1366, 19}); got != want {
- t.Errorf("HMetric: got %v, want %v", got, want)
- }
- if got, want := font.VMetric(fupe, i0), (VMetric{2465, 553}); got != want {
- t.Errorf("VMetric: got %v, want %v", got, want)
- }
- if got, want := font.Kerning(fupe, i0, i1), int32(-144); got != want {
- t.Errorf("Kerning: got %v, want %v", got, want)
- }
-
- g := NewGlyphBuf()
- err = g.Load(font, fupe, i0, NoHinting)
- if err != nil {
- t.Fatalf("Load: %v", err)
- }
- g0 := &GlyphBuf{
- B: g.B,
- Point: g.Point,
- End: g.End,
- }
- g1 := &GlyphBuf{
- B: Bounds{19, 0, 1342, 1480},
- Point: []Point{
- {19, 0, 51},
- {581, 1480, 1},
- {789, 1480, 51},
- {1342, 0, 1},
- {1116, 0, 35},
- {962, 410, 3},
- {368, 410, 33},
- {214, 0, 3},
- {428, 566, 19},
- {904, 566, 33},
- {667, 1200, 3},
- },
- End: []int{8, 11},
- }
- if got, want := fmt.Sprint(g0), fmt.Sprint(g1); got != want {
- t.Errorf("GlyphBuf:\ngot %v\nwant %v", got, want)
- }
-}
-
-func TestIndex(t *testing.T) {
- testCases := map[string]map[rune]Index{
- "luxisr": {
- ' ': 3,
- '!': 4,
- 'A': 36,
- 'V': 57,
- 'É': 101,
- 'fl': 193,
- '\u22c5': 385,
- '中': 0,
- },
-
- // The x-etc test cases use those versions of the .ttf files provided
- // by Ubuntu 14.04. See testdata/make-other-hinting-txts.sh for details.
-
- "x-arial-bold": {
- ' ': 3,
- '+': 14,
- '0': 19,
- '_': 66,
- 'w': 90,
- '~': 97,
- 'Ä': 98,
- 'fl': 192,
- '½': 242,
- 'σ': 305,
- 'λ': 540,
- 'ỹ': 1275,
- '\u04e9': 1319,
- '中': 0,
- },
- "x-deja-vu-sans-oblique": {
- ' ': 3,
- '*': 13,
- 'Œ': 276,
- 'ω': 861,
- '‡': 2571,
- '⊕': 3110,
- 'fl': 4728,
- '\ufb03': 4729,
- '\ufffd': 4813,
- // TODO: '\U0001f640': ???,
- '中': 0,
- },
- "x-droid-sans-japanese": {
- ' ': 0,
- '\u3000': 3,
- '\u3041': 25,
- '\u30fe': 201,
- '\uff61': 202,
- '\uff67': 208,
- '\uff9e': 263,
- '\uff9f': 264,
- '\u4e00': 265,
- '\u557e': 1000,
- '\u61b6': 2024,
- '\u6ede': 3177,
- '\u7505': 3555,
- '\u81e3': 4602,
- '\u81e5': 4603,
- '\u81e7': 4604,
- '\u81e8': 4605,
- '\u81ea': 4606,
- '\u81ed': 4607,
- '\u81f3': 4608,
- '\u81f4': 4609,
- '\u91c7': 5796,
- '\u9fa0': 6620,
- '\u203e': 12584,
- },
- "x-times-new-roman": {
- ' ': 3,
- ':': 29,
- 'fl': 192,
- 'Ŀ': 273,
- '♠': 388,
- 'Ŗ': 451,
- 'Σ': 520,
- '\u200D': 745,
- 'Ẽ': 1216,
- '\u04e9': 1319,
- '中': 0,
- },
- }
- for name, wants := range testCases {
- font, testdataIsOptional, err := parseTestdataFont(name)
- if err != nil {
- if testdataIsOptional {
- t.Log(err)
- } else {
- t.Fatal(err)
- }
- continue
- }
- for r, want := range wants {
- if got := font.Index(r); got != want {
- t.Errorf("%s: Index of %q, aka %U: got %d, want %d", name, r, r, got, want)
- }
- }
- }
-}
-
-type scalingTestData struct {
- advanceWidth int32
- bounds Bounds
- points []Point
-}
-
-// scalingTestParse parses a line of points like
-// 213 -22 -111 236 555;-22 -111 1, 178 555 1, 236 555 1, 36 -111 1
-// The line will not have a trailing "\n".
-func scalingTestParse(line string) (ret scalingTestData) {
- next := func(s string) (string, int32) {
- t, i := "", strings.Index(s, " ")
- if i != -1 {
- s, t = s[:i], s[i+1:]
- }
- x, _ := strconv.Atoi(s)
- return t, int32(x)
- }
-
- i := strings.Index(line, ";")
- prefix, line := line[:i], line[i+1:]
-
- prefix, ret.advanceWidth = next(prefix)
- prefix, ret.bounds.XMin = next(prefix)
- prefix, ret.bounds.YMin = next(prefix)
- prefix, ret.bounds.XMax = next(prefix)
- prefix, ret.bounds.YMax = next(prefix)
-
- ret.points = make([]Point, 0, 1+strings.Count(line, ","))
- for len(line) > 0 {
- s := line
- if i := strings.Index(line, ","); i != -1 {
- s, line = line[:i], line[i+1:]
- for len(line) > 0 && line[0] == ' ' {
- line = line[1:]
- }
- } else {
- line = ""
- }
- s, x := next(s)
- s, y := next(s)
- s, f := next(s)
- ret.points = append(ret.points, Point{X: x, Y: y, Flags: uint32(f)})
- }
- return ret
-}
-
-// scalingTestEquals is equivalent to, but faster than, calling
-// reflect.DeepEquals(a, b), and also returns the index of the first non-equal
-// element. It also treats a nil []Point and an empty non-nil []Point as equal.
-// a and b must have equal length.
-func scalingTestEquals(a, b []Point) (index int, equals bool) {
- for i, p := range a {
- if p != b[i] {
- return i, false
- }
- }
- return 0, true
-}
-
-var scalingTestCases = []struct {
- name string
- size int32
-}{
- {"luxisr", 12},
- {"x-arial-bold", 11},
- {"x-deja-vu-sans-oblique", 17},
- {"x-droid-sans-japanese", 9},
- {"x-times-new-roman", 13},
-}
-
-func testScaling(t *testing.T, h Hinting) {
- for _, tc := range scalingTestCases {
- font, testdataIsOptional, err := parseTestdataFont(tc.name)
- if err != nil {
- if testdataIsOptional {
- t.Log(err)
- } else {
- t.Error(err)
- }
- continue
- }
- hintingStr := "sans"
- if h != NoHinting {
- hintingStr = "with"
- }
- f, err := os.Open(fmt.Sprintf(
- "../../testdata/%s-%dpt-%s-hinting.txt", tc.name, tc.size, hintingStr))
- if err != nil {
- t.Errorf("%s: Open: %v", tc.name, err)
- continue
- }
- defer f.Close()
-
- wants := []scalingTestData{}
- scanner := bufio.NewScanner(f)
- if scanner.Scan() {
- major, minor, patch := 0, 0, 0
- _, err := fmt.Sscanf(scanner.Text(), "freetype version %d.%d.%d", &major, &minor, &patch)
- if err != nil {
- t.Errorf("%s: version information: %v", tc.name, err)
- }
- if (major < 2) || (major == 2 && minor < 5) || (major == 2 && minor == 5 && patch < 1) {
- t.Errorf("%s: need freetype version >= 2.5.1.\n"+
- "Try setting LD_LIBRARY_PATH=/path/to/freetype_built_from_src/objs/.libs/\n"+
- "and re-running testdata/make-other-hinting-txts.sh",
- tc.name)
- continue
- }
- } else {
- t.Errorf("%s: no version information", tc.name)
- continue
- }
- for scanner.Scan() {
- wants = append(wants, scalingTestParse(scanner.Text()))
- }
- if err := scanner.Err(); err != nil && err != io.EOF {
- t.Errorf("%s: Scanner: %v", tc.name, err)
- continue
- }
-
- glyphBuf := NewGlyphBuf()
- for i, want := range wants {
- if err = glyphBuf.Load(font, tc.size*64, Index(i), h); err != nil {
- t.Errorf("%s: glyph #%d: Load: %v", tc.name, i, err)
- continue
- }
- got := scalingTestData{
- advanceWidth: glyphBuf.AdvanceWidth,
- bounds: glyphBuf.B,
- points: glyphBuf.Point,
- }
-
- if got.advanceWidth != want.advanceWidth {
- t.Errorf("%s: glyph #%d advance width:\ngot %v\nwant %v",
- tc.name, i, got.advanceWidth, want.advanceWidth)
- continue
- }
-
- if got.bounds != want.bounds {
- t.Errorf("%s: glyph #%d bounds:\ngot %v\nwant %v",
- tc.name, i, got.bounds, want.bounds)
- continue
- }
-
- for i := range got.points {
- got.points[i].Flags &= 0x01
- }
- if len(got.points) != len(want.points) {
- t.Errorf("%s: glyph #%d:\ngot %v\nwant %v\ndifferent slice lengths: %d versus %d",
- tc.name, i, got.points, want.points, len(got.points), len(want.points))
- continue
- }
- if j, equals := scalingTestEquals(got.points, want.points); !equals {
- t.Errorf("%s: glyph #%d:\ngot %v\nwant %v\nat index %d: %v versus %v",
- tc.name, i, got.points, want.points, j, got.points[j], want.points[j])
- continue
- }
- }
- }
-}
-
-func TestScalingSansHinting(t *testing.T) {
- testScaling(t, NoHinting)
-}
-
-func TestScalingWithHinting(t *testing.T) {
- testScaling(t, FullHinting)
-}
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 000000000..82a14ab91
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,38 @@
+Mattermost Licensing
+
+SOFTWARE LICENSING
+
+Mattermost server is made available under two separate licensing options:
+
+- Free Software Foundation’s GNU AGPL v.3.0, subject to the exceptions outlined in this policy; or
+- Commercial licenses available from SpinPunch, Inc. by contacting commercial@mattermost.com
+
+Admin Tools and Configuration Files (model/, web/static/, web/templates/, web/react/utils/, api/templates/ and all
+subdirectories thereof) are made available under:
+
+- Apache License v2.0
+
+LICENSING POLICY
+
+The objective of the Mattermost server license is to require enhancements to Mattermost server be shared with the community
+while allowing for non-enhanced use in proprietary applications.
+
+Therefore, the Mattermost server is free to use, modify and redistribute in open source applications via the
+copyleft AGPL license. For proprietary applications (systems that don’t share source back to the community),
+Mattermost is free to use and redistribute so long as you’re not withholding proprietary enhancements to the
+Mattermost server and you’re only linking directly to or changing Admin Tools and Configuration Files (defined above), which
+are released under an Apache 2.0 license, and copyleft free.
+
+We promise that we will not enforce the copyleft provisions in AGPL v3.0 against you if your application (a) does
+not link to the Mattermost server directly, but exclusively uses the Mattermost Admin Tools and Configuration Files,
+and (b) you have not modified, added to or adapted the source code of Mattermost in a way that results in the creation
+of a “modified version” or “work based on” Mattermost as these terms are defined in the AGPL v3.0 license.
+
+If the above is not enough to satisfy your organization’s legal department (some will not approve GPL in any form),
+commercial licenses are available from commercial@mattermost.com.
+
+MATTERMOST TRADEMARK GUIDELINES
+
+Your use of the mark Mattermost is subject to SpinPunch’s prior written approval and our organization’s Trademark
+Standards of Use at http://www.mattermost.org/trademark-standards-of-use/. For trademark approval or any questions
+you have about using these trademarks, please email trademark@mattermost.com
diff --git a/NOTICE.md b/NOTICE.md
index 811390dcd..b8b919607 100644
--- a/NOTICE.md
+++ b/NOTICE.md
@@ -57,3 +57,11 @@ The above copyright notice and this permission notice shall be included in all c
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+This product contains a modified portion of 'golang-lru', a golang LRU cache by hashicorp, based on Groupcache by Google Inc.
+
+* HOMEPAGE:
+ * https://github.com/hashicorp/golang-lru
+
+* LICENSE:
+
+This Source Code Form is subject to the terms of the Mozilla Public License, v.2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
diff --git a/README.md b/README.md
index fa692c59b..08d2f4210 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
**Mattermost Preview**
**Team Communication Service**
-**Version 0.40**
+**Version 0.50**
About Mattermost
@@ -11,12 +11,12 @@ Mattermost is a team communication service. It brings team messaging and file sh
We built Mattermost to help teams focus on what matters most to them. It works for us, we hope it works for you too.
-Installing the Mattermost Preview
-=================================
+Installing the Mattermost
+=========================
-You're installing "Mattermost Preview", a pre-released 0.40 version intended for an early look at what we're building. While SpinPunch runs this version internally, it's not recommended for production deployments since we can't guarantee API stability or backwards compatibility until our 1.0 version release.
+You're installing "Mattermost Preview", a pre-released 0.50 version intended for an early look at what we're building. While SpinPunch runs this version internally, it's not recommended for production deployments since we can't guarantee API stability or backwards compatibility until our 1.0 version release.
-That said, any issues at all, please let us know on the Mattermost forum at: http://bit.ly/1MY1kul
+That said, any issues at all, please let us know on the Mattermost forum at: http://discourse.mattermost.org
Local Machine Setup (Docker)
-----------------------------
@@ -33,38 +33,38 @@ Local Machine Setup (Docker)
6. When docker is done fetching the image, open http://dockerhost:8065/ in your browser
### Ubuntu ###
-1. Follow the instructions at https://docs.docker.com/installation/ubuntulinux/ or use the summery below.
-
-`sudo apt-get update`
-
-`sudo apt-get install wget`
-
-`wget -qO- https://get.docker.com/ | sh`
-
-`sudo usermod -aG docker <username>`
-
-`sudo service docker start`
-
-`newgrp docker`
-
-2. Run `docker run --name mattermost-dev -d --publish 8065:80 mattermost/platform:helium
+1. Follow the instructions at https://docs.docker.com/installation/ubuntulinux/ or use the summary below.
+
+ ``` bash
+ sudo apt-get update
+ sudo apt-get install wget
+ wget -qO- https://get.docker.com/ | sh
+ sudo usermod -aG docker <username>
+ sudo service docker start
+ newgrp docker
+ ```
+
+2. Run `docker run --name mattermost-dev -d --publish 8065:80 mattermost/platform:helium`
3. When docker is done fetching the image, open http://localhost:8065/ in your browser
### Arch ###
-1. Install docker using the following commands
+1. Install docker using the following commands:
-`pacman -S docker`
+ ``` bash
+ pacman -S docker
+ systemctl enable docker.service
+ systemctl start docker.service
+ gpasswd -a <username> docker
+ newgrp docker
+ ```
-`systemctl enable docker.service`
+2. Start docker container:
-`systemctl start docker.service`
+ ``` bash
+ docker run --name mattermost-dev -d --publish 8065:80 mattermost/platform:helium
+ ```
-`gpasswd -a <username> docker`
-
-`newgrp docker`
-
-2. docker run --name mattermost-dev -d --publish 8065:80 mattermost/platform:helium
-3. When docker is done fetching the image, open http://localhost:8065/ in your browser
+3. When docker is done fetching the image, open http://localhost:8065/ in your browser.
### Notes ###
If your ISP blocks port 25 then you may install locally but email will not be sent.
@@ -76,44 +76,61 @@ You can update to the latest bits by running
`docker pull mattermost/platform:latest`
If you wish to remove mattermost-dev use the following commands
+
1. `docker stop mattermost-dev`
2. `docker rm -v mattermost-dev`
+If you wish to gain access to the container use the following commands
+1. `docker exec -ti mattermost-dev /bin/bash`
AWS Elastic Beanstalk Setup (Docker)
------------------------------------
-1. From the AWS console select Elastic Beanstalk
-2. Select "Create New Application" from the top right.
-3. Name the application and press next
-4. Select "Create a web server" environment.
-5. If asked, select create and AIM role and instance profile and press next.
-6. For predefined configuration select docker. For environment type select single instance.
-7. For application source, select upload your own and upload Dockerrun.aws.json from docker/Dockerrun.aws.json. Everything else may be left at default.
-8. Select an environment name, this is how you will refer to your environment. Make sure the URL is available then press next.
-9. The options on the additional resources page may be left at default unless you wish to change them. Press Next.
-10. On the configuration details place. Select an instance type of t2.small or larger.
-11. You can set the configuration details as you please but they may be left at their defaults. When you are done press next.
-12. Environment tags my be left blank. Press next.
-13. You will be asked to review your information. Press Launch.
-14. Up near the top of the dashboard you will see a domain of the form \*.elasticbeanstalk.com copy this as you will need it later.
-15. From the AWS console select route 53
-16. From the sidebar select Hosted Zones
-17. Select the domain you want to use or create a new one.
-18. Modify an existing CNAME record set or create a new one with the name * and the value of the domain you copied in step 13.
-19. Save the record set
-20. Return the Elastic Beanstalk from the AWS console.
-21. Select the environment you created.
-22. Select configuration from the sidebar.
-23. Click the gear beside software configuration.
-24. Add an environment property with the name “MATTERMOST\_DOMAIN” and a value of the domain you mapped in route 53. For example if your domain is \*.example.com you would enter example.com not www.example.com.
-25. Select apply.
-26. Return to the dashboard on the sidebar and wait for beanstalk update the environment.
-27. Try it out by entering the domain you mapped into your browser.
+1. Create a new elastic beanstalk docker application using the Dockerrun.aws.json file provided.
+ 1. From the AWS console select Elastic Beanstalk
+ 2. Select "Create New Application" from the top right.
+ 3. Name the application and press next
+ 4. Select "Create a web server" environment.
+ 5. If asked, select create and IAM role and instance profile and press next.
+ 6. For predefined configuration select docker. For environment type select single instance.
+ 7. For application source, select upload your own and upload Dockerrun.aws.json from docker/Dockerrun.aws.json. Everything else may be left at default.
+ 8. Select an environment name, this is how you will refer to your environment. Make sure the URL is available then press next.
+ 9. The options on the additional resources page may be left at default unless you wish to change them. Press Next.
+ 10. On the configuration details place. Select an instance type of t2.small or larger.
+ 11. You can set the configuration details as you please but they may be left at their defaults. When you are done press next.
+ 12. Environment tags my be left blank. Press next.
+ 13. You will be asked to review your information. Press Launch.
+ 14. Up near the top of the dashboard you will see a domain of the form \*.elasticbeanstalk.com copy this as you will need it later.
+
+2. Map a wildcard domain to the new elastic beanstalk application
+ 15. From the AWS console select route 53
+ 16. From the sidebar select Hosted Zones
+ 17. Select the domain you want to use or create a new one.
+ 18. Modify an existing CNAME record set or create a new one with the name * and the value of the domain you copied in step 1.13.
+ 19. Save the record set
+
+3. Set the environment variable "MATTERMOST\_DOMAIN" to the domain you mapped above (example.com not www.example.com)
+ 20. Return the Elastic Beanstalk from the AWS console.
+ 21. Select the environment you created.
+ 22. Select configuration from the sidebar.
+ 23. Click the gear beside software configuration.
+ 24. Add an environment property with the name “MATTERMOST\_DOMAIN” and a value of the domain you mapped in route 53. For example if your domain is \*.example.com you would enter example.com not www.example.com.
+ 25. Select apply.
+
+4. Try it out!
+ 26. Return to the dashboard on the sidebar and wait for beanstalk update the environment.
+ 27. Try it out by entering the domain you mapped into your browser.
+
+Contributing
+------------
+
+To contribute to this open source project please review the Mattermost Contribution Guidelines at http://www.mattermost.org/contribute-to-mattermost/.
License
-------
-Most Mattermost source files are made available under the terms of the GNU Affero General Public License (AGPL). See individual files for details.
+Most Mattermost source files are made available under the terms of the GNU Affero General Public License (AGPL). See individual files for details.
+
+As an exception, Admin Tools and Configuration Files are are made available under the terms of the Apache License, version 2.0. See LICENSE.txt for more information.
+
-As an exception, the files in the /model, /web/static, /web/templates, /web/react/utils and all subdirectories thereof are made available under the terms of the Apache License, version 2.0.
diff --git a/api/auto_constants.go b/api/auto_constants.go
index 7af90a5f1..3f8831055 100644
--- a/api/auto_constants.go
+++ b/api/auto_constants.go
@@ -32,5 +32,5 @@ var (
POST_MESSAGE_LEN = utils.Range{100, 400}
POST_HASHTAGS_NUM = utils.Range{5, 10}
POST_MENTIONS_NUM = utils.Range{0, 3}
- TEST_IMAGE_FILENAMES = []string{"test.png", "salamander.jpg", "toothless.gif"}
+ TEST_IMAGE_FILENAMES = []string{"test.png", "testjpg.jpg", "testgif.gif"}
)
diff --git a/api/channel_test.go b/api/channel_test.go
index e8aaf4e3f..2e2e3683a 100644
--- a/api/channel_test.go
+++ b/api/channel_test.go
@@ -679,6 +679,8 @@ func TestUpdateNotifyLevel(t *testing.T) {
data["user_id"] = user.Id
data["notify_level"] = model.CHANNEL_NOTIFY_MENTION
+ timeBeforeUpdate := model.GetMillis()
+
if _, err := Client.UpdateNotifyLevel(data); err != nil {
t.Fatal(err)
}
@@ -689,6 +691,10 @@ func TestUpdateNotifyLevel(t *testing.T) {
t.Fatal("NotifyLevel did not update properly")
}
+ if rdata.Members[channel1.Id].LastUpdateAt <= timeBeforeUpdate {
+ t.Fatal("LastUpdateAt did not update")
+ }
+
data["user_id"] = "junk"
if _, err := Client.UpdateNotifyLevel(data); err == nil {
t.Fatal("Should have errored - bad user id")
@@ -735,7 +741,7 @@ func TestUpdateNotifyLevel(t *testing.T) {
}
func TestFuzzyChannel(t *testing.T) {
- Setup();
+ Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
@@ -747,9 +753,9 @@ func TestFuzzyChannel(t *testing.T) {
Client.LoginByEmail(team.Domain, user.Email, "pwd")
// Strings that should pass as acceptable channel names
- var fuzzyStringsPass = []string {
+ var fuzzyStringsPass = []string{
"*", "?", ".", "}{][)(><", "{}[]()<>",
-
+
"qahwah ( قهوة)",
"שָׁלוֹם עֲלֵיכֶם",
"Ramen チャーシュー chāshū",
diff --git a/api/command.go b/api/command.go
index 449483bbf..aedbe07cc 100644
--- a/api/command.go
+++ b/api/command.go
@@ -9,6 +9,8 @@ import (
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils"
"net/http"
+ "reflect"
+ "runtime"
"strconv"
"strings"
)
@@ -19,16 +21,13 @@ var commands = []commandHandler{
logoutCommand,
joinCommand,
loadTestCommand,
+ echoCommand,
}
func InitCommand(r *mux.Router) {
l4g.Debug("Initializing command api routes")
r.Handle("/command", ApiUserRequired(command)).Methods("POST")
- if utils.Cfg.TeamSettings.AllowValet {
- commands = append(commands, echoCommand)
- }
-
hub.Start()
}
@@ -59,6 +58,8 @@ func checkCommand(c *Context, command *model.Command) bool {
return false
}
+ tchan := Srv.Store.Team().Get(c.Session.TeamId)
+
if len(command.ChannelId) > 0 {
cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, command.ChannelId, c.Session.UserId)
@@ -67,7 +68,21 @@ func checkCommand(c *Context, command *model.Command) bool {
}
}
+ allowValet := false
+ if tResult := <-tchan; tResult.Err != nil {
+ c.Err = model.NewAppError("checkCommand", "Could not find the team for this session, team_id="+c.Session.TeamId, "")
+ return false
+ } else {
+ allowValet = tResult.Data.(*model.Team).AllowValet
+ }
+
+ ec := runtime.FuncForPC(reflect.ValueOf(echoCommand).Pointer()).Name()
+
for _, v := range commands {
+ if !allowValet && ec == runtime.FuncForPC(reflect.ValueOf(v).Pointer()).Name() {
+ continue
+ }
+
if v(c, command) {
return true
} else if c.Err != nil {
diff --git a/api/file.go b/api/file.go
index 10167c6ff..0e08567d6 100644
--- a/api/file.go
+++ b/api/file.go
@@ -15,6 +15,7 @@ import (
"github.com/nfnt/resize"
"image"
_ "image/gif"
+ _ "golang.org/x/image/bmp"
"image/jpeg"
"io"
"net/http"
diff --git a/api/post.go b/api/post.go
index 3acc95551..99cbdcb85 100644
--- a/api/post.go
+++ b/api/post.go
@@ -58,11 +58,7 @@ func createPost(c *Context, w http.ResponseWriter, r *http.Request) {
}
func createValetPost(c *Context, w http.ResponseWriter, r *http.Request) {
- if !utils.Cfg.TeamSettings.AllowValet {
- c.Err = model.NewAppError("createValetPost", "The valet feature is currently turned off. Please contact your system administrator for details.", "")
- c.Err.StatusCode = http.StatusNotImplemented
- return
- }
+ tchan := Srv.Store.Team().Get(c.Session.TeamId)
post := model.PostFromJson(r.Body)
if post == nil {
@@ -70,13 +66,25 @@ func createValetPost(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- // Any one with access to the team can post as valet to any open channel
cchan := Srv.Store.Channel().CheckOpenChannelPermissions(c.Session.TeamId, post.ChannelId)
+ // Any one with access to the team can post as valet to any open channel
if !c.HasPermissionsToChannel(cchan, "createValetPost") {
return
}
+ // Make sure this team has the valet feature enabled
+ if tResult := <-tchan; tResult.Err != nil {
+ c.Err = model.NewAppError("createValetPost", "Could not find the team for this session, team_id="+c.Session.TeamId, "")
+ return
+ } else {
+ if !tResult.Data.(*model.Team).AllowValet {
+ c.Err = model.NewAppError("createValetPost", "The valet feature is currently turned off. Please contact your team administrator for details.", "")
+ c.Err.StatusCode = http.StatusNotImplemented
+ return
+ }
+ }
+
if rp, err := CreateValetPost(c, post); err != nil {
c.Err = err
diff --git a/api/post_test.go b/api/post_test.go
index b322a5017..03f70bff7 100644
--- a/api/post_test.go
+++ b/api/post_test.go
@@ -147,7 +147,7 @@ func TestCreateValetPost(t *testing.T) {
channel2 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
- if utils.Cfg.TeamSettings.AllowValet {
+ if utils.Cfg.TeamSettings.AllowValetDefault {
post1 := &model.Post{ChannelId: channel1.Id, Message: "#hashtag a" + model.NewId() + "a"}
rpost1, err := Client.CreateValetPost(post1)
if err != nil {
diff --git a/api/team.go b/api/team.go
index cb60602c6..775bc29ae 100644
--- a/api/team.go
+++ b/api/team.go
@@ -29,6 +29,8 @@ func InitTeam(r *mux.Router) {
sr.Handle("/email_teams", ApiAppHandler(emailTeams)).Methods("POST")
sr.Handle("/invite_members", ApiUserRequired(inviteMembers)).Methods("POST")
sr.Handle("/update_name", ApiUserRequired(updateTeamName)).Methods("POST")
+ sr.Handle("/update_valet_feature", ApiUserRequired(updateValetFeature)).Methods("POST")
+ sr.Handle("/me", ApiUserRequired(getMyTeam)).Methods("GET")
}
func signupTeam(c *Context, w http.ResponseWriter, r *http.Request) {
@@ -136,6 +138,8 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
+ teamSignup.Team.AllowValet = utils.Cfg.TeamSettings.AllowValetDefault
+
if result := <-Srv.Store.Team().Save(&teamSignup.Team); result.Err != nil {
c.Err = result.Err
return
@@ -157,7 +161,7 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- if utils.Cfg.TeamSettings.AllowValet {
+ if teamSignup.Team.AllowValet {
CreateValet(c, rteam)
if c.Err != nil {
return
@@ -200,6 +204,13 @@ func createTeam(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ if rteam.AllowValet {
+ CreateValet(c, rteam)
+ if c.Err != nil {
+ return
+ }
+ }
+
w.Write([]byte(rteam.ToJson()))
}
}
@@ -542,3 +553,72 @@ func updateTeamName(c *Context, w http.ResponseWriter, r *http.Request) {
w.Write([]byte(model.MapToJson(props)))
}
+
+func updateValetFeature(c *Context, w http.ResponseWriter, r *http.Request) {
+
+ props := model.MapFromJson(r.Body)
+
+ allowValetStr := props["allow_valet"]
+ if len(allowValetStr) == 0 {
+ c.SetInvalidParam("updateValetFeature", "allow_valet")
+ return
+ }
+
+ allowValet := allowValetStr == "true"
+
+ teamId := props["team_id"]
+ if len(teamId) > 0 && len(teamId) != 26 {
+ c.SetInvalidParam("updateValetFeature", "team_id")
+ return
+ } else if len(teamId) == 0 {
+ teamId = c.Session.TeamId
+ }
+
+ tchan := Srv.Store.Team().Get(teamId)
+
+ if !c.HasPermissionsToTeam(teamId, "updateValetFeature") {
+ return
+ }
+
+ if !strings.Contains(c.Session.Roles, model.ROLE_ADMIN) {
+ c.Err = model.NewAppError("updateValetFeature", "You do not have the appropriate permissions", "userId="+c.Session.UserId)
+ c.Err.StatusCode = http.StatusForbidden
+ return
+ }
+
+ var team *model.Team
+ if tResult := <-tchan; tResult.Err != nil {
+ c.Err = tResult.Err
+ return
+ } else {
+ team = tResult.Data.(*model.Team)
+ }
+
+ team.AllowValet = allowValet
+
+ if result := <-Srv.Store.Team().Update(team); result.Err != nil {
+ c.Err = result.Err
+ return
+ }
+
+ w.Write([]byte(model.MapToJson(props)))
+}
+
+func getMyTeam(c *Context, w http.ResponseWriter, r *http.Request) {
+
+ if len(c.Session.TeamId) == 0 {
+ return
+ }
+
+ if result := <-Srv.Store.Team().Get(c.Session.TeamId); result.Err != nil {
+ c.Err = result.Err
+ return
+ } else if HandleEtag(result.Data.(*model.Team).Etag(), w, r) {
+ return
+ } else {
+ w.Header().Set(model.HEADER_ETAG_SERVER, result.Data.(*model.Team).Etag())
+ w.Header().Set("Expires", "-1")
+ w.Write([]byte(result.Data.(*model.Team).ToJson()))
+ return
+ }
+}
diff --git a/api/team_test.go b/api/team_test.go
index 74a184634..042c0a2e9 100644
--- a/api/team_test.go
+++ b/api/team_test.go
@@ -286,3 +286,106 @@ func TestFuzzyTeamCreate(t *testing.T) {
}
}
}
+
+func TestGetMyTeam(t *testing.T) {
+ Setup()
+
+ team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ rteam, _ := Client.CreateTeam(&team)
+
+ user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ ruser, _ := Client.CreateUser(&user, "")
+ Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id)
+
+ Client.LoginByEmail(team.Domain, user.Email, user.Password)
+
+ if result, err := Client.GetMyTeam(""); err != nil {
+ t.Fatal("Failed to get user")
+ } else {
+ if result.Data.(*model.Team).Name != team.Name {
+ t.Fatal("team names did not match")
+ }
+ if result.Data.(*model.Team).Domain != team.Domain {
+ t.Fatal("team domains did not match")
+ }
+ if result.Data.(*model.Team).Type != team.Type {
+ t.Fatal("team types did not match")
+ }
+ }
+}
+
+func TestUpdateValetFeature(t *testing.T) {
+ Setup()
+
+ team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
+
+ user := &model.User{TeamId: team.Id, Email: "test@nowhere.com", FullName: "Corey Hulen", Password: "pwd"}
+ user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
+ Srv.Store.User().VerifyEmail(user.Id)
+
+ user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
+ Srv.Store.User().VerifyEmail(user2.Id)
+
+ team2 := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team)
+
+ user3 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User)
+ Srv.Store.User().VerifyEmail(user3.Id)
+
+ Client.LoginByEmail(team.Domain, user2.Email, "pwd")
+
+ data := make(map[string]string)
+ data["allow_valet"] = "true"
+ if _, err := Client.UpdateValetFeature(data); err == nil {
+ t.Fatal("Should have errored, not admin")
+ }
+
+ Client.LoginByEmail(team.Domain, user.Email, "pwd")
+
+ data["allow_valet"] = ""
+ if _, err := Client.UpdateValetFeature(data); err == nil {
+ t.Fatal("Should have errored, empty allow_valet field")
+ }
+
+ data["allow_valet"] = "true"
+ if _, err := Client.UpdateValetFeature(data); err != nil {
+ t.Fatal(err)
+ }
+
+ rteam := Client.Must(Client.GetMyTeam("")).Data.(*model.Team)
+ if rteam.AllowValet != true {
+ t.Fatal("Should have errored - allow valet property not updated")
+ }
+
+ data["team_id"] = "junk"
+ if _, err := Client.UpdateValetFeature(data); err == nil {
+ t.Fatal("Should have errored, junk team id")
+ }
+
+ data["team_id"] = "12345678901234567890123456"
+ if _, err := Client.UpdateValetFeature(data); err == nil {
+ t.Fatal("Should have errored, bad team id")
+ }
+
+ data["team_id"] = team.Id
+ data["allow_valet"] = "false"
+ if _, err := Client.UpdateValetFeature(data); err != nil {
+ t.Fatal(err)
+ }
+
+ rteam = Client.Must(Client.GetMyTeam("")).Data.(*model.Team)
+ if rteam.AllowValet != false {
+ t.Fatal("Should have errored - allow valet property not updated")
+ }
+
+ Client.LoginByEmail(team2.Domain, user3.Email, "pwd")
+
+ data["team_id"] = team.Id
+ data["allow_valet"] = "true"
+ if _, err := Client.UpdateValetFeature(data); err == nil {
+ t.Fatal("Should have errored, not part of team")
+ }
+}
diff --git a/api/user.go b/api/user.go
index 6af737df3..f8382cf2f 100644
--- a/api/user.go
+++ b/api/user.go
@@ -5,7 +5,6 @@ package api
import (
"bytes"
- "code.google.com/p/draw2d/draw2d"
l4g "code.google.com/p/log4go"
"fmt"
"github.com/goamz/goamz/aws"
@@ -19,6 +18,7 @@ import (
"hash/fnv"
"image"
"image/color"
+ "image/draw"
_ "image/gif"
_ "image/jpeg"
"image/png"
@@ -145,10 +145,6 @@ func createUser(c *Context, w http.ResponseWriter, r *http.Request) {
}
func CreateValet(c *Context, team *model.Team) *model.User {
- if !utils.Cfg.TeamSettings.AllowValet {
- return &model.User{}
- }
-
valet := &model.User{}
valet.TeamId = team.Id
valet.Email = utils.Cfg.EmailSettings.FeedbackEmail
@@ -602,42 +598,13 @@ func createProfileImage(username string, userId string) ([]byte, *model.AppError
h.Write([]byte(userId))
seed := h.Sum32()
- initials := ""
- parts := strings.Split(username, " ")
-
- for _, v := range parts {
-
- if len(v) > 0 {
- initials += string(strings.ToUpper(v)[0])
- }
- }
-
- if len(initials) == 0 {
- initials = "^"
- }
-
- if len(initials) > 2 {
- initials = initials[0:2]
- }
-
- draw2d.SetFontFolder(utils.FindDir("web/static/fonts"))
- i := image.NewRGBA(image.Rect(0, 0, 128, 128))
- gc := draw2d.NewGraphicContext(i)
- draw2d.Rect(gc, 0, 0, 128, 128)
- gc.SetFillColor(colors[int(seed)%len(colors)])
- gc.Fill()
- gc.SetFontSize(50)
- gc.SetFontData(draw2d.FontData{"luxi", draw2d.FontFamilyMono, draw2d.FontStyleBold | draw2d.FontStyleItalic})
- left, top, right, bottom := gc.GetStringBounds("CH")
- width := (128 - (right - left + 10)) / 2
- height := (128 - (top - bottom + 6)) / 2
- gc.Translate(width, height)
- gc.SetFillColor(image.White)
- gc.FillString(initials)
+ color := colors[int(seed)%len(colors)]
+ img := image.NewRGBA(image.Rect(0, 0, int(utils.Cfg.ImageSettings.ProfileWidth), int(utils.Cfg.ImageSettings.ProfileHeight)))
+ draw.Draw(img, img.Bounds(), &image.Uniform{color}, image.ZP, draw.Src)
buf := new(bytes.Buffer)
- if imgErr := png.Encode(buf, i); imgErr != nil {
+ if imgErr := png.Encode(buf, img); imgErr != nil {
return nil, model.NewAppError("getProfileImage", "Could not encode default profile image", imgErr.Error())
} else {
return buf.Bytes(), nil
diff --git a/api/web_socket_test.go b/api/web_socket_test.go
index c7b612cde..15bc3baeb 100644
--- a/api/web_socket_test.go
+++ b/api/web_socket_test.go
@@ -119,7 +119,7 @@ func TestSocket(t *testing.T) {
}
-func TestZZWebScoketTearDown(t *testing.T) {
+func TestZZWebSocketTearDown(t *testing.T) {
// *IMPORTANT* - Kind of hacky
// This should be the last function in any test file
// that calls Setup()
diff --git a/config/config.json b/config/config.json
index 82e40a1c5..e38f1701a 100644
--- a/config/config.json
+++ b/config/config.json
@@ -8,7 +8,7 @@
"FileLocation": ""
},
"ServiceSettings": {
- "SiteName": "Mattermost Preview",
+ "SiteName": "Mattermost",
"Domain": "xxxxxxmustbefilledin.com",
"Mode" : "dev",
"AllowTesting" : false,
@@ -73,7 +73,7 @@
"TeamSettings": {
"MaxUsersPerTeam": 150,
"AllowPublicLink": true,
- "AllowValet": false,
+ "AllowValetDefault": false,
"TermsLink": "/static/help/configure_links.html",
"PrivacyLink": "/static/help/configure_links.html",
"AboutLink": "/static/help/configure_links.html",
diff --git a/config/config_docker.json b/config/config_docker.json
index a90722a1d..85f0d9c73 100644
--- a/config/config_docker.json
+++ b/config/config_docker.json
@@ -8,9 +8,9 @@
"FileLocation": ""
},
"ServiceSettings": {
- "SiteName": "Mattermost Preview",
+ "SiteName": "Mattermost",
"Domain": "",
- "Mode" : "prod",
+ "Mode" : "dev",
"AllowTesting" : false,
"UseSSL": false,
"Port": "80",
diff --git a/model/channel_list.go b/model/channel_list.go
index 088dbea2a..09f14a986 100644
--- a/model/channel_list.go
+++ b/model/channel_list.go
@@ -53,6 +53,12 @@ func (o *ChannelList) Etag() string {
t = member.LastViewedAt
id = v.Id
}
+
+ if member.LastUpdateAt > t {
+ t = member.LastUpdateAt
+ id = v.Id
+ }
+
}
}
diff --git a/model/channel_member.go b/model/channel_member.go
index 720ac4c42..50f51304b 100644
--- a/model/channel_member.go
+++ b/model/channel_member.go
@@ -25,6 +25,7 @@ type ChannelMember struct {
MsgCount int64 `json:"msg_count"`
MentionCount int64 `json:"mention_count"`
NotifyLevel string `json:"notify_level"`
+ LastUpdateAt int64 `json:"last_update_at"`
}
func (o *ChannelMember) ToJson() string {
@@ -70,6 +71,10 @@ func (o *ChannelMember) IsValid() *AppError {
return nil
}
+func (o *ChannelMember) PreSave() {
+ o.LastUpdateAt = GetMillis()
+}
+
func IsChannelNotifyLevelValid(notifyLevel string) bool {
return notifyLevel == CHANNEL_NOTIFY_ALL || notifyLevel == CHANNEL_NOTIFY_MENTION || notifyLevel == CHANNEL_NOTIFY_NONE || notifyLevel == CHANNEL_NOTIFY_QUIET
}
diff --git a/model/client.go b/model/client.go
index 0448828bb..ab01e7d62 100644
--- a/model/client.go
+++ b/model/client.go
@@ -186,6 +186,15 @@ func (c *Client) UpdateTeamName(data map[string]string) (*Result, *AppError) {
}
}
+func (c *Client) UpdateValetFeature(data map[string]string) (*Result, *AppError) {
+ if r, err := c.DoPost("/teams/update_valet_feature", MapToJson(data)); err != nil {
+ return nil, err
+ } else {
+ return &Result{r.Header.Get(HEADER_REQUEST_ID),
+ r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
+ }
+}
+
func (c *Client) CreateUser(user *User, hash string) (*Result, *AppError) {
if r, err := c.DoPost("/users/create", user.ToJson()); err != nil {
return nil, err
@@ -647,6 +656,15 @@ func (c *Client) GetStatuses() (*Result, *AppError) {
}
}
+func (c *Client) GetMyTeam(etag string) (*Result, *AppError) {
+ if r, err := c.DoGet("/teams/me", "", etag); err != nil {
+ return nil, err
+ } else {
+ return &Result{r.Header.Get(HEADER_REQUEST_ID),
+ r.Header.Get(HEADER_ETAG_SERVER), TeamFromJson(r.Body)}, nil
+ }
+}
+
func (c *Client) MockSession(sessionToken string) {
c.AuthToken = sessionToken
}
diff --git a/model/team.go b/model/team.go
index a510cde78..5c66f3b1f 100644
--- a/model/team.go
+++ b/model/team.go
@@ -24,6 +24,7 @@ type Team struct {
Type string `json:"type"`
CompanyName string `json:"company_name"`
AllowedDomains string `json:"allowed_domains"`
+ AllowValet bool `json:"allow_valet"`
}
type Invites struct {
diff --git a/model/utils.go b/model/utils.go
index 262bda319..cc51dfe47 100644
--- a/model/utils.go
+++ b/model/utils.go
@@ -17,7 +17,7 @@ import (
)
const (
- ETAG_ROOT_VERSION = "10"
+ ETAG_ROOT_VERSION = "11"
)
type StringMap map[string]string
diff --git a/store/sql_channel_store.go b/store/sql_channel_store.go
index 592657c1c..463fce16f 100644
--- a/store/sql_channel_store.go
+++ b/store/sql_channel_store.go
@@ -37,6 +37,7 @@ func NewSqlChannelStore(sqlStore *SqlStore) ChannelStore {
}
func (s SqlChannelStore) UpgradeSchemaIfNeeded() {
+ s.CreateColumnIfNotExists("ChannelMembers", "LastUpdateAt", "NotifyLevel", "bigint(20)", "0") // Remove after 6/7/2015 prod push
}
func (s SqlChannelStore) CreateIndexesIfNotExists() {
@@ -273,6 +274,7 @@ func (s SqlChannelStore) SaveMember(member *model.ChannelMember) StoreChannel {
go func() {
result := StoreResult{}
+ member.PreSave()
if result.Err = member.IsValid(); result.Err != nil {
storeChannel <- result
return
@@ -484,7 +486,8 @@ func (s SqlChannelStore) UpdateLastViewedAt(channelId string, userId string) Sto
SET
ChannelMembers.MentionCount = 0,
ChannelMembers.MsgCount = Channels.TotalMsgCount,
- ChannelMembers.LastViewedAt = Channels.LastPostAt
+ ChannelMembers.LastViewedAt = Channels.LastPostAt,
+ ChannelMembers.LastUpdateAt = Channels.LastPostAt
WHERE
Channels.Id = ChannelMembers.ChannelId
AND UserId = ?
@@ -533,15 +536,18 @@ func (s SqlChannelStore) UpdateNotifyLevel(channelId, userId, notifyLevel string
go func() {
result := StoreResult{}
+ updateAt := model.GetMillis()
+
_, err := s.GetMaster().Exec(
`UPDATE
ChannelMembers
SET
- NotifyLevel = ?
+ NotifyLevel = ?,
+ LastUpdateAt = ?
WHERE
UserId = ?
AND ChannelId = ?`,
- notifyLevel, userId, channelId)
+ notifyLevel, updateAt, userId, channelId)
if err != nil {
result.Err = model.NewAppError("SqlChannelStore.UpdateNotifyLevel", "We couldn't update the notify level", "channel_id="+channelId+", user_id="+userId+", "+err.Error())
}
diff --git a/store/sql_team_store.go b/store/sql_team_store.go
index 6e7fc1c1e..ffb9f8093 100644
--- a/store/sql_team_store.go
+++ b/store/sql_team_store.go
@@ -5,6 +5,7 @@ package store
import (
"github.com/mattermost/platform/model"
+ "github.com/mattermost/platform/utils"
"strings"
)
@@ -29,6 +30,11 @@ func NewSqlTeamStore(sqlStore *SqlStore) TeamStore {
}
func (s SqlTeamStore) UpgradeSchemaIfNeeded() {
+ defaultValue := "0"
+ if utils.Cfg.TeamSettings.AllowValetDefault {
+ defaultValue = "1"
+ }
+ s.CreateColumnIfNotExists("Teams", "AllowValet", "AllowedDomains", "tinyint(1)", defaultValue)
}
func (s SqlTeamStore) CreateIndexesIfNotExists() {
diff --git a/utils/config.go b/utils/config.go
index 6a7e4589c..eb2ae3050 100644
--- a/utils/config.go
+++ b/utils/config.go
@@ -98,7 +98,7 @@ type PrivacySettings struct {
type TeamSettings struct {
MaxUsersPerTeam int
AllowPublicLink bool
- AllowValet bool
+ AllowValetDefault bool
TermsLink string
PrivacyLink string
AboutLink string
diff --git a/utils/lru.go b/utils/lru.go
index 9e47c3de3..61a515e14 100644
--- a/utils/lru.go
+++ b/utils/lru.go
@@ -1,5 +1,9 @@
-// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
-// See License.txt for license information.
+// This files was copied/modified from https://github.com/hashicorp/golang-lru
+// which was (see below)
+
+// This package provides a simple LRU cache. It is based on the
+// LRU implementation in groupcache:
+// https://github.com/golang/groupcache/tree/master/lru
package utils
diff --git a/utils/lru_test.go b/utils/lru_test.go
index e26af032f..3255f5c1a 100644
--- a/utils/lru_test.go
+++ b/utils/lru_test.go
@@ -1,5 +1,9 @@
-// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
-// See License.txt for license information.
+// This files was copied/modified from https://github.com/hashicorp/golang-lru
+// which was (see below)
+
+// This package provides a simple LRU cache. It is based on the
+// LRU implementation in groupcache:
+// https://github.com/golang/groupcache/tree/master/lru
package utils
diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx
index 006c168ba..428d3ed81 100644
--- a/web/react/components/channel_header.jsx
+++ b/web/react/components/channel_header.jsx
@@ -15,17 +15,8 @@ var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes;
-function getExtraInfoStateFromStores() {
- return {
- extra_info: ChannelStore.getCurrentExtraInfo()
- };
-}
-
var ExtraMembers = React.createClass({
componentDidMount: function() {
- ChannelStore.addExtraInfoChangeListener(this._onChange);
- ChannelStore.addChangeListener(this._onChange);
-
var originalLeave = $.fn.popover.Constructor.prototype.leave;
$.fn.popover.Constructor.prototype.leave = function(obj) {
var self = obj instanceof this.constructor ? obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type);
@@ -49,25 +40,12 @@ var ExtraMembers = React.createClass({
});
},
- componentWillUnmount: function() {
- ChannelStore.removeExtraInfoChangeListener(this._onChange);
- ChannelStore.removeChangeListener(this._onChange);
- },
- _onChange: function() {
- var newState = getExtraInfoStateFromStores();
- if (!utils.areStatesEqual(newState, this.state)) {
- this.setState(newState);
- }
- },
- getInitialState: function() {
- return getExtraInfoStateFromStores();
- },
render: function() {
- var count = this.state.extra_info.members.length == 0 ? "-" : this.state.extra_info.members.length;
- count = this.state.extra_info.members.length > 19 ? "20+" : count;
+ var count = this.props.members.length == 0 ? "-" : this.props.members.length;
+ count = this.props.members.length > 19 ? "20+" : count;
var data_content = "";
- this.state.extra_info.members.forEach(function(m) {
+ this.props.members.forEach(function(m) {
data_content += "<div style='white-space: nowrap'>" + m.username + "</div>";
});
@@ -201,7 +179,7 @@ module.exports = React.createClass({
</a>
<ul className="dropdown-menu" role="menu" aria-labelledby="channel_header_dropdown">
<li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_info" data-channelid={this.state.channel.id} href="#">View Info</a></li>
- <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Invite Members</a></li>
+ <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Add Members</a></li>
{ isAdmin ?
<li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_members" href="#">Manage Members</a></li>
: ""
@@ -228,7 +206,7 @@ module.exports = React.createClass({
<a href="#"><strong className="heading">{channelTitle}</strong></a>
}
</th>
- <th><ExtraMembers channelId={this.state.channel.id} /></th>
+ <th><ExtraMembers members={this.state.users} channelId={this.state.channel.id} /></th>
{ searchForm }
<th>
<div className="dropdown" style={{"marginLeft":"5px", "marginRight":"10px"}}>
diff --git a/web/react/components/channel_info_modal.jsx b/web/react/components/channel_info_modal.jsx
index 191297ce4..18addb52f 100644
--- a/web/react/components/channel_info_modal.jsx
+++ b/web/react/components/channel_info_modal.jsx
@@ -35,9 +35,18 @@ module.exports = React.createClass({
<h4 className="modal-title" id="myModalLabel">{channel.display_name}</h4>
</div>
<div className="modal-body">
- <p><strong>Channel Name: </strong>{channel.display_name}</p>
- <p><strong>Channel Handle: </strong>{channel.name}</p>
- <p><strong>Channel ID: </strong>{channel.id}</p>
+ <div className="row form-group">
+ <div className="col-sm-3 info__label">Channel Name: </div>
+ <div className="col-sm-9">{channel.display_name}</div>
+ </div>
+ <div className="row form-group">
+ <div className="col-sm-3 info__label">Channel Handle:</div>
+ <div className="col-sm-9">{channel.name}</div>
+ </div>
+ <div className="row">
+ <div className="col-sm-3 info__label">Channel ID:</div>
+ <div className="col-sm-9">{channel.id}</div>
+ </div>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
diff --git a/web/react/components/channel_loader.jsx b/web/react/components/channel_loader.jsx
index 5252f275c..537a41d03 100644
--- a/web/react/components/channel_loader.jsx
+++ b/web/react/components/channel_loader.jsx
@@ -18,6 +18,7 @@ module.exports = React.createClass({
AsyncClient.getChannelExtraInfo(true);
AsyncClient.findTeams();
AsyncClient.getStatuses();
+ AsyncClient.getMyTeam();
/* End of async loads */
diff --git a/web/react/components/channel_notifications.jsx b/web/react/components/channel_notifications.jsx
index 085536a0a..fa9ab42ae 100644
--- a/web/react/components/channel_notifications.jsx
+++ b/web/react/components/channel_notifications.jsx
@@ -1,6 +1,8 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
+var SettingItemMin = require('./setting_item_min.jsx');
+var SettingItemMax = require('./setting_item_max.jsx');
var utils = require('../utils/utils.jsx');
var client = require('../utils/client.jsx');
@@ -9,26 +11,50 @@ var ChannelStore = require('../stores/channel_store.jsx');
module.exports = React.createClass({
componentDidMount: function() {
+ ChannelStore.addChangeListener(this._onChange);
+
var self = this;
$(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) {
var button = e.relatedTarget;
var channel_id = button.dataset.channelid;
var notifyLevel = ChannelStore.getMember(channel_id).notify_level;
- self.setState({ notify_level: notifyLevel, title: button.dataset.title, channel_id: channel_id });
+ var quietMode = false;
+ if (notifyLevel === "quiet") quietMode = true;
+ self.setState({ notify_level: notifyLevel, quiet_mode: quietMode, title: button.dataset.title, channel_id: channel_id });
});
},
+ componentWillUnmount: function() {
+ ChannelStore.removeChangeListener(this._onChange);
+ },
+ _onChange: function() {
+ if (!this.state.channel_id) return;
+ var notifyLevel = ChannelStore.getMember(this.state.channel_id).notify_level;
+ var quietMode = false;
+ if (notifyLevel === "quiet") quietMode = true;
+
+ var newState = this.state;
+ newState.notify_level = notifyLevel;
+ newState.quiet_mode = quietMode;
+
+ if (!utils.areStatesEqual(this.state, newState)) {
+ this.setState(newState);
+ }
+ },
+ updateSection: function(section) {
+ this.setState({ activeSection: section });
+ },
getInitialState: function() {
- return { notify_level: "", title: "", channel_id: "" };
+ return { notify_level: "", title: "", channel_id: "", activeSection: "" };
},
- handleUpdate: function(e) {
+ handleUpdate: function() {
var channel_id = this.state.channel_id;
- var notify_level = this.state.notify_level;
+ var notify_level = this.state.quiet_mode ? "quiet" : this.state.notify_level;
var data = {};
data["channel_id"] = channel_id;
data["user_id"] = UserStore.getCurrentId();
- data["notify_level"] = this.state.notify_level;
+ data["notify_level"] = notify_level;
if (!data["notify_level"] || data["notify_level"].length === 0) return;
@@ -37,7 +63,7 @@ module.exports = React.createClass({
var member = ChannelStore.getMember(channel_id);
member.notify_level = notify_level;
ChannelStore.setChannelMember(member);
- $(this.refs.modal.getDOMNode()).modal('hide');
+ this.updateSection("");
}.bind(this),
function(err) {
this.setState({ server_error: err.message });
@@ -45,42 +71,138 @@ module.exports = React.createClass({
);
},
handleRadioClick: function(notifyLevel) {
- this.setState({ notify_level: notifyLevel });
+ this.setState({ notify_level: notifyLevel, quiet_mode: false });
this.refs.modal.getDOMNode().focus();
},
- handleQuietToggle: function() {
- if (this.state.notify_level === "quiet") {
- this.setState({ notify_level: "none" });
- this.refs.modal.getDOMNode().focus();
- } else {
- this.setState({ notify_level: "quiet" });
- this.refs.modal.getDOMNode().focus();
- }
+ handleQuietToggle: function(quietMode) {
+ this.setState({ notify_level: "none", quiet_mode: quietMode });
+ this.refs.modal.getDOMNode().focus();
},
render: function() {
var server_error = this.state.server_error ? <div className='form-group has-error'><label className='control-label'>{ this.state.server_error }</label></div> : null;
- var allActive = "";
- var mentionActive = "";
- var noneActive = "";
- var quietActive = "";
- var desktopHidden = "";
-
- if (this.state.notify_level === "quiet") {
- desktopHidden = "hidden";
- quietActive = "active";
- } else if (this.state.notify_level === "mention") {
- mentionActive = "active";
- } else if (this.state.notify_level === "none") {
- noneActive = "active";
+ var self = this;
+
+ var desktopSection;
+ if (this.state.activeSection === 'desktop') {
+ var notifyActive = [false, false, false];
+ if (this.state.notify_level === "mention") {
+ notifyActive[1] = true;
+ } else if (this.state.notify_level === "all") {
+ notifyActive[0] = true;
+ } else {
+ notifyActive[2] = true;
+ }
+
+ var inputs = [];
+
+ inputs.push(
+ <div className="col-sm-12">
+ <div className="radio">
+ <label>
+ <input type="radio" checked={notifyActive[0]} onClick={function(){self.handleRadioClick("all")}}>For all activity</input>
+ </label>
+ <br/>
+ </div>
+ <div className="radio">
+ <label>
+ <input type="radio" checked={notifyActive[1]} onClick={function(){self.handleRadioClick("mention")}}>Only for mentions</input>
+ </label>
+ <br/>
+ </div>
+ <div className="radio">
+ <label>
+ <input type="radio" checked={notifyActive[2]} onClick={function(){self.handleRadioClick("none")}}>Never</input>
+ </label>
+ </div>
+ </div>
+ );
+
+ desktopSection = (
+ <SettingItemMax
+ title="Send desktop notifications"
+ inputs={inputs}
+ submit={this.handleUpdate}
+ server_error={server_error}
+ updateSection={function(e){self.updateSection("");self._onChange();e.preventDefault();}}
+ />
+ );
+ } else {
+ var describe = "";
+ if (this.state.notify_level === "mention") {
+ describe = "Only for mentions";
+ } else if (this.state.notify_level === "all") {
+ describe = "For all activity";
+ } else {
+ describe = "Never";
+ }
+
+ desktopSection = (
+ <SettingItemMin
+ title="Send desktop notifications"
+ describe={describe}
+ updateSection={function(e){self.updateSection("desktop");e.preventDefault();}}
+ />
+ );
+ }
+
+ var quietSection;
+ if (this.state.activeSection === 'quiet') {
+ var quietActive = ["",""];
+ if (this.state.quiet_mode) {
+ quietActive[0] = "active";
+ } else {
+ quietActive[1] = "active";
+ }
+
+ var inputs = [];
+
+ inputs.push(
+ <div className="col-sm-12">
+ <div className="btn-group" data-toggle="buttons-radio">
+ <button className={"btn btn-default "+quietActive[0]} onClick={function(){self.handleQuietToggle(true)}}>On</button>
+ <button className={"btn btn-default "+quietActive[1]} onClick={function(){self.handleQuietToggle(false)}}>Off</button>
+ </div>
+ </div>
+ );
+
+ inputs.push(
+ <div className="col-sm-12">
+ <br/>
+ Enabling quiet mode will turn off desktop notifications and only mark the channel as unread if you have been mentioned.
+ </div>
+ );
+
+ quietSection = (
+ <SettingItemMax
+ title="Quiet mode"
+ inputs={inputs}
+ submit={this.handleUpdate}
+ server_error={server_error}
+ updateSection={function(e){self.updateSection("");self._onChange();e.preventDefault();}}
+ />
+ );
} else {
- allActive = "active";
+ var describe = "";
+ if (this.state.quiet_mode) {
+ describe = "On";
+ } else {
+ describe = "Off";
+ }
+
+ quietSection = (
+ <SettingItemMin
+ title="Quiet mode"
+ describe={describe}
+ updateSection={function(e){self.updateSection("quiet");e.preventDefault();}}
+ />
+ );
}
var self = this;
return (
<div className="modal fade" id="channel_notifications" ref="modal" tabIndex="-1" role="dialog" aria-hidden="true">
- <div className="modal-dialog">
+ <div className="modal-dialog settings-modal">
<div className="modal-content">
<div className="modal-header">
<button type="button" className="close" data-dismiss="modal">
@@ -90,31 +212,23 @@ module.exports = React.createClass({
<h4 className="modal-title">{"Notification Preferences for " + this.state.title}</h4>
</div>
<div className="modal-body">
- <div className={desktopHidden}>
- <span>Desktop Notifications</span>
- <br/>
- <div className="btn-group" data-toggle="buttons-radio">
- <button className={"btn btn-default "+allActive} onClick={function(){self.handleRadioClick("all")}}>Any activity (default)</button>
- <button className={"btn btn-default "+mentionActive} onClick={function(){self.handleRadioClick("mention")}}>Mentions of my name</button>
- <button className={"btn btn-default "+noneActive} onClick={function(){self.handleRadioClick("none")}}>Nothing</button>
+ <div className="settings-table">
+ <div className="settings-content">
+ <div ref="wrapper" className="user-settings">
+ <br/>
+ <div className="divider-dark first"/>
+ {desktopSection}
+ <div className="divider-light"/>
+ {quietSection}
+ <div className="divider-dark"/>
</div>
- <br/>
- <br/>
</div>
- <span>Quiet Mode</span>
- <br/>
- <div className="btn-group" data-toggle="buttons-checkbox">
- <button className={"btn btn-default "+quietActive} onClick={this.handleQuietToggle}>Quiet Mode</button>
</div>
{ server_error }
</div>
- <div className="modal-footer">
- <button type="button" className="btn btn-primary" onClick={this.handleUpdate}>Done</button>
- </div>
</div>
</div>
</div>
-
);
}
});
diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx
index cb7aa371c..9e3feb25c 100644
--- a/web/react/components/create_comment.jsx
+++ b/web/react/components/create_comment.jsx
@@ -112,13 +112,28 @@ module.exports = React.createClass({
return { messageText: '', uploadsInProgress: 0, previews: [], submitting: false };
},
setUploads: function(val) {
- var num = this.state.uploadsInProgress + val;
- this.setState({uploadsInProgress: num});
+ var oldInProgress = this.state.uploadsInProgress
+ var newInProgress = oldInProgress + val;
+
+ if (newInProgress + this.state.previews.length > Constants.MAX_UPLOAD_FILES) {
+ newInProgress = Constants.MAX_UPLOAD_FILES - this.state.previews.length;
+ this.setState({limit_error: "Uploads limited to " + Constants.MAX_UPLOAD_FILES + " files maximum. Please use additional comments for more files."});
+ } else {
+ this.setState({limit_error: null});
+ }
+
+ var numToUpload = newInProgress - oldInProgress;
+ if (numToUpload <= 0) return 0;
+
+ this.setState({uploadsInProgress: newInProgress});
+
+ return numToUpload;
},
render: function() {
var server_error = this.state.server_error ? <div className='form-group has-error'><label className='control-label'>{ this.state.server_error }</label></div> : null;
var post_error = this.state.post_error ? <label className='control-label'>{this.state.post_error}</label> : null;
+ var limit_error = this.state.limit_error ? <div className='has-error'><label className='control-label'>{this.state.limit_error}</label></div> : null;
var preview = <div/>;
if (this.state.previews.length > 0 || this.state.uploadsInProgress > 0) {
@@ -129,13 +144,6 @@ module.exports = React.createClass({
uploadsInProgress={this.state.uploadsInProgress} />
);
}
- var limit_previews = ""
- if (this.state.previews.length > 5) {
- limit_previews = <div className='has-error'><label className='control-label'>{ "Note: While all files will be available, only first five will show thumbnails." }</label></div>
- }
- if (this.state.previews.length > 20) {
- limit_previews = <div className='has-error'><label className='control-label'>{ "Note: Uploads limited to 20 files maximum. Please use additional posts for more files." }</label></div>
- }
return (
<form onSubmit={this.handleSubmit}>
@@ -159,7 +167,7 @@ module.exports = React.createClass({
<input type="button" className="btn btn-primary comment-btn pull-right" value="Add Comment" onClick={this.handleSubmit} />
{ post_error }
{ server_error }
- { limit_previews }
+ { limit_error }
</div>
</div>
{ preview }
diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx
index 5a0b6f85f..0c23dcfac 100644
--- a/web/react/components/create_post.jsx
+++ b/web/react/components/create_post.jsx
@@ -51,7 +51,7 @@ module.exports = React.createClass({
false,
function(data) {
PostStore.storeDraft(data.channel_id, user_id, null);
- this.setState({ messageText: '', submitting: false, post_error: null, previews: [], server_error: null });
+ this.setState({ messageText: '', submitting: false, post_error: null, previews: [], server_error: null, limit_error: null });
if (data.goto_location.length > 0) {
window.location.href = data.goto_location;
@@ -71,7 +71,7 @@ module.exports = React.createClass({
client.createPost(post, ChannelStore.getCurrent(),
function(data) {
PostStore.storeDraft(data.channel_id, data.user_id, null);
- this.setState({ messageText: '', submitting: false, post_error: null, previews: [], server_error: null });
+ this.setState({ messageText: '', submitting: false, post_error: null, previews: [], server_error: null, limit_error: null });
this.resizePostHolder();
AsyncClient.getPosts(true);
@@ -207,21 +207,36 @@ module.exports = React.createClass({
return { channel_id: ChannelStore.getCurrentId(), messageText: messageText, uploadsInProgress: 0, previews: previews, submitting: false, initialText: messageText };
},
setUploads: function(val) {
- var num = this.state.uploadsInProgress + val;
+ var oldInProgress = this.state.uploadsInProgress
+ var newInProgress = oldInProgress + val;
+
+ if (newInProgress + this.state.previews.length > Constants.MAX_UPLOAD_FILES) {
+ newInProgress = Constants.MAX_UPLOAD_FILES - this.state.previews.length;
+ this.setState({limit_error: "Uploads limited to " + Constants.MAX_UPLOAD_FILES + " files maximum. Please use additional posts for more files."});
+ } else {
+ this.setState({limit_error: null});
+ }
+
+ var numToUpload = newInProgress - oldInProgress;
+ if (numToUpload <= 0) return 0;
+
var draft = PostStore.getCurrentDraft();
if (!draft) {
draft = {}
draft['message'] = '';
draft['previews'] = [];
}
- draft['uploadsInProgress'] = num;
+ draft['uploadsInProgress'] = newInProgress;
PostStore.storeCurrentDraft(draft);
- this.setState({uploadsInProgress: num});
+ this.setState({uploadsInProgress: newInProgress});
+
+ return numToUpload;
},
render: function() {
var server_error = this.state.server_error ? <div className='form-group has-error'><label className='control-label'>{ this.state.server_error }</label></div> : null;
var post_error = this.state.post_error ? <label className='control-label'>{this.state.post_error}</label> : null;
+ var limit_error = this.state.limit_error ? <div className='has-error'><label className='control-label'>{this.state.limit_error}</label></div> : null;
var preview = <div/>;
if (this.state.previews.length > 0 || this.state.uploadsInProgress > 0) {
@@ -232,13 +247,6 @@ module.exports = React.createClass({
uploadsInProgress={this.state.uploadsInProgress} />
);
}
- var limit_previews = ""
- if (this.state.previews.length > 5) {
- limit_previews = <div className='has-error'><label className='control-label'>{ "Note: While all files will be available, only first five will show thumbnails." }</label></div>
- }
- if (this.state.previews.length > 20) {
- limit_previews = <div className='has-error'><label className='control-label'>{ "Note: Uploads limited to 20 files maximum. Please use additional posts for more files." }</label></div>
- }
return (
<form id="create_post" ref="topDiv" role="form" onSubmit={this.handleSubmit}>
@@ -260,7 +268,7 @@ module.exports = React.createClass({
<div className={post_error ? 'post-create-footer has-error' : 'post-create-footer'}>
{ post_error }
{ server_error }
- { limit_previews }
+ { limit_error }
{ preview }
<MsgTyping channelId={this.state.channel_id} parentId=""/>
</div>
diff --git a/web/react/components/file_upload.jsx b/web/react/components/file_upload.jsx
index c03a61c63..f2429f17e 100644
--- a/web/react/components/file_upload.jsx
+++ b/web/react/components/file_upload.jsx
@@ -12,18 +12,18 @@ module.exports = React.createClass({
this.props.onUploadError(null);
- //This looks redundant, but must be done this way due to
- //setState being an asynchronous call
+ // This looks redundant, but must be done this way due to
+ // setState being an asynchronous call
var numFiles = 0;
- for(var i = 0; i < files.length && i <= 20 ; i++) {
+ for(var i = 0; i < files.length && i < Constants.MAX_UPLOAD_FILES; i++) {
if (files[i].size <= Constants.MAX_FILE_SIZE) {
numFiles++;
}
}
- this.props.setUploads(numFiles);
+ var numToUpload = this.props.setUploads(numFiles);
- for (var i = 0; i < files.length && i <= 20; i++) {
+ for (var i = 0; i < files.length && i < numToUpload; i++) {
if (files[i].size > Constants.MAX_FILE_SIZE) {
this.props.onUploadError("Files must be no more than " + Constants.MAX_FILE_SIZE/1000000 + " MB");
continue;
@@ -70,8 +70,8 @@ module.exports = React.createClass({
self.props.onUploadError(null);
- //This looks redundant, but must be done this way due to
- //setState being an asynchronous call
+ // This looks redundant, but must be done this way due to
+ // setState being an asynchronous call
var items = e.clipboardData.items;
var numItems = 0;
if (items) {
@@ -87,9 +87,9 @@ module.exports = React.createClass({
}
}
- self.props.setUploads(numItems);
+ var numToUpload = self.props.setUploads(numItems);
- for (var i = 0; i < items.length; i++) {
+ for (var i = 0; i < items.length && i < numToUpload; i++) {
if (items[i].type.indexOf("image") !== -1) {
var file = items[i].getAsFile();
diff --git a/web/react/components/invite_member_modal.jsx b/web/react/components/invite_member_modal.jsx
index 5980664de..d1672126d 100644
--- a/web/react/components/invite_member_modal.jsx
+++ b/web/react/components/invite_member_modal.jsx
@@ -57,7 +57,7 @@ module.exports = React.createClass({
if (config.AllowInviteNames) {
invite.first_name = this.refs["first_name"+index].getDOMNode().value.trim();
- if (!invite.first_name ) {
+ if (!invite.first_name && config.RequireInviteNames) {
first_name_errors[index] = "This is a required field";
valid = false;
} else {
@@ -65,7 +65,7 @@ module.exports = React.createClass({
}
invite.last_name = this.refs["last_name"+index].getDOMNode().value.trim();
- if (!invite.last_name ) {
+ if (!invite.last_name && config.RequireInviteNames) {
last_name_errors[index] = "This is a required field";
valid = false;
} else {
@@ -125,10 +125,12 @@ module.exports = React.createClass({
});
},
removeInviteFields: function(index) {
+ var count = this.state.id_count;
var invite_ids = this.state.invite_ids;
var i = invite_ids.indexOf(index);
- if (index > -1) invite_ids.splice(i, 1);
- this.setState({ invite_ids: invite_ids });
+ if (i > -1) invite_ids.splice(i, 1);
+ if (!invite_ids.length) invite_ids.push(++count);
+ this.setState({ invite_ids: invite_ids, id_count: count });
},
getInitialState: function() {
return {
@@ -154,11 +156,9 @@ module.exports = React.createClass({
invite_sections[index] = (
<div key={"key" + index}>
- { i ?
<div>
- <button type="button" className="btn remove__member" onClick={function(){self.removeInviteFields(index);}}>×</button>
+ <button type="button" className="btn remove__member" onClick={this.removeInviteFields.bind(this, index)}>×</button>
</div>
- : ""}
<div className={ email_error ? "form-group invite has-error" : "form-group invite" }>
<input onKeyUp={this.displayNameKeyUp} type="text" ref={"email"+index} className="form-control" placeholder="email@domain.com" maxLength="64" />
{ email_error }
diff --git a/web/react/components/login.jsx b/web/react/components/login.jsx
index 65f1da1f8..3b6f96c2d 100644
--- a/web/react/components/login.jsx
+++ b/web/react/components/login.jsx
@@ -21,6 +21,12 @@ var FindTeamDomain = React.createClass({
return;
}
+ if (!utils.isLocalStorageSupported()) {
+ state.server_error = "This service requires local storage to be enabled. Please enable it or exit private browsing.";
+ this.setState(state);
+ return;
+ }
+
state.server_error = "";
this.setState(state);
@@ -31,7 +37,7 @@ var FindTeamDomain = React.createClass({
window.location.href = window.location.protocol + "//" + domain + "." + utils.getDomainWithOutSub();
}
else {
- this.state.server_error = "We couldn't find your " + strings.TeamPlural + ".";
+ this.state.server_error = "We couldn't find your " + strings.Team + ".";
this.setState(this.state);
}
}.bind(this),
@@ -94,7 +100,7 @@ module.exports = React.createClass({
return;
}
- var email = this.refs.email.getDOMNode().value.trim();
+ var email = this.refs.email.getDOMNode().value.trim();
if (!email) {
state.server_error = "An email is required"
this.setState(state);
@@ -108,6 +114,12 @@ module.exports = React.createClass({
return;
}
+ if (!utils.isLocalStorageSupported()) {
+ state.server_error = "This service requires local storage to be enabled. Please enable it or exit private browsing.";
+ this.setState(state);
+ return;
+ }
+
state.server_error = "";
this.setState(state);
diff --git a/web/react/components/mention.jsx b/web/react/components/mention.jsx
index ba758688b..86a423138 100644
--- a/web/react/components/mention.jsx
+++ b/web/react/components/mention.jsx
@@ -8,8 +8,8 @@ module.exports = React.createClass({
render: function() {
return (
<div className="mentions-name" onClick={this.handleClick}>
- <img className="pull-left mention-img" src={"/api/v1/users/" + this.props.id + "/image"}/>
- <span>@{this.props.username}</span><span style={{'color':'grey', 'marginLeft':'10px'}}>{this.props.name}</span>
+ <img className="mention-img" src={"/api/v1/users/" + this.props.id + "/image"}/>
+ <span>@{this.props.username}</span><span className="mention-fullname">{this.props.name}</span>
</div>
);
}
diff --git a/web/react/components/navbar.jsx b/web/react/components/navbar.jsx
index 3821c2772..35f7d9044 100644
--- a/web/react/components/navbar.jsx
+++ b/web/react/components/navbar.jsx
@@ -301,7 +301,7 @@ module.exports = React.createClass({
<span className="glyphicon glyphicon-chevron-down header-dropdown__icon"></span>
</a>
<ul className="dropdown-menu" role="menu" aria-labelledby="channel_header_dropdown">
- <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Invite Members</a></li>
+ <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Add Members</a></li>
{ isAdmin ?
<li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_members" href="#">Manage Members</a></li>
: ""
diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx
index 65247b705..37e3faef2 100644
--- a/web/react/components/post_list.jsx
+++ b/web/react/components/post_list.jsx
@@ -125,12 +125,12 @@ module.exports = React.createClass({
$('body').on('mouseenter mouseleave', '.post', function(ev){
if(ev.type === 'mouseenter'){
- $(this).parent('div').prev('.date-seperator').addClass('hovered--after');
- $(this).parent('div').next('.date-seperator').addClass('hovered--before');
+ $(this).parent('div').prev('.date-separator, .new-separator').addClass('hovered--after');
+ $(this).parent('div').next('.date-separator, .new-separator').addClass('hovered--before');
}
else {
- $(this).parent('div').prev('.date-seperator').removeClass('hovered--after');
- $(this).parent('div').next('.date-seperator').removeClass('hovered--before');
+ $(this).parent('div').prev('.date-separator, .new-separator').removeClass('hovered--after');
+ $(this).parent('div').next('.date-separator, .new-separator').removeClass('hovered--before');
}
});
@@ -389,7 +389,7 @@ module.exports = React.createClass({
<div className="channel-intro">
<h4 className="channel-intro-title">Welcome</h4>
<p>
- { creator_name != "" ? "This is the start of the " + ui_name + " " + ui_type + ", created by " + creator_name + " on " + utils.displayDate(channel.create_at) + "."
+ { creator_name != "" ? "This is the start of the " + ui_name + " " + ui_type + ", created by " + creator_name + " on " + utils.displayDate(channel.create_at) + "."
: "This is the start of the " + ui_name + " " + ui_type + ", created on "+ utils.displayDate(channel.create_at) + "." }
{ channel.type === 'P' ? " Only invited members can see this private group." : " Any member can join and read this channel." }
<br/>
@@ -434,9 +434,9 @@ module.exports = React.createClass({
currentPostDay = utils.getDateForUnixTicks(post.create_at);
if(currentPostDay.getDate() !== previousPostDay.getDate() || currentPostDay.getMonth() !== previousPostDay.getMonth() || currentPostDay.getFullYear() !== previousPostDay.getFullYear()) {
postCtls.push(
- <div className="date-seperator">
- <hr className="date-seperator__hr" />
- <div className="date-seperator__text">{currentPostDay.toDateString()}</div>
+ <div className="date-separator">
+ <hr className="separator__hr" />
+ <div className="separator__text">{currentPostDay.toDateString()}</div>
</div>
);
}
@@ -444,17 +444,13 @@ module.exports = React.createClass({
if (post.create_at > last_viewed && !rendered_last_viewed) {
rendered_last_viewed = true;
postCtls.push(
- <div>
- <div className="new-seperator">
- <hr id="new_message" className="new-seperator__hr" />
- <div className="new-seperator__text">New Messages</div>
- </div>
- {postCtl}
- </div>
+ <div className="new-separator">
+ <hr id="new_message" className="separator__hr" />
+ <div className="separator__text">New Messages</div>
+ </div>
);
- } else {
- postCtls.push(postCtl);
}
+ postCtls.push(postCtl);
previousPostDay = utils.getDateForUnixTicks(post.create_at);
}
diff --git a/web/react/components/setting_item_max.jsx b/web/react/components/setting_item_max.jsx
index 03f05b0cf..b8b667e1a 100644
--- a/web/react/components/setting_item_max.jsx
+++ b/web/react/components/setting_item_max.jsx
@@ -13,7 +13,7 @@ module.exports = React.createClass({
<li className="col-sm-12 section-title">{this.props.title}</li>
<li className="col-sm-9 col-sm-offset-3">
<ul className="setting-list">
- <li className="row setting-list-item form-group">
+ <li className="setting-list-item">
{inputs}
</li>
<li className="setting-list-item">
diff --git a/web/react/components/settings_sidebar.jsx b/web/react/components/settings_sidebar.jsx
index a1546890f..ae8510cf2 100644
--- a/web/react/components/settings_sidebar.jsx
+++ b/web/react/components/settings_sidebar.jsx
@@ -1,6 +1,8 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
+var utils = require('../utils/utils.jsx');
+
module.exports = React.createClass({
updateTab: function(tab) {
this.props.updateTab(tab);
@@ -11,16 +13,11 @@ module.exports = React.createClass({
return (
<div className="">
<ul className="nav nav-pills nav-stacked">
- <li className={this.props.activeTab == 'general' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("general");}}><i className="glyphicon glyphicon-cog"></i>General</a></li>
- <li className={this.props.activeTab == 'security' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("security");}}><i className="glyphicon glyphicon-lock"></i>Security</a></li>
- <li className={this.props.activeTab == 'notifications' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("notifications");}}><i className="glyphicon glyphicon-exclamation-sign"></i>Notifications</a></li>
- <li className={this.props.activeTab == 'appearance' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("appearance");}}><i className="glyphicon glyphicon-wrench"></i>Appearance</a></li>
+ {this.props.tabs.map(function(tab) {
+ return <li className={self.props.activeTab == tab.name ? 'active' : ''}><a href="#" onClick={function(){self.updateTab(tab.name);}}><i className={tab.icon}></i>{tab.ui_name}</a></li>
+ })}
</ul>
</div>
);
- /* Temporarily removing sessions and activity logs
- <li className={this.props.activeTab == 'sessions' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("sessions");}}><i className="glyphicon glyphicon-globe"></i>Sessions</a></li>
- <li className={this.props.activeTab == 'activity_log' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("activity_log");}}><i className="glyphicon glyphicon-time"></i>Activity Log</a></li>
- */
}
});
diff --git a/web/react/components/sidebar_header.jsx b/web/react/components/sidebar_header.jsx
index 5a872b7a0..0b59d2036 100644
--- a/web/react/components/sidebar_header.jsx
+++ b/web/react/components/sidebar_header.jsx
@@ -94,7 +94,8 @@ var NavbarDropdown = React.createClass({
<i className="dropdown__icon"></i>
</a>
<ul className="dropdown-menu" role="menu">
- <li><a href="#" data-toggle="modal" data-target="#settings_modal">Account Settings</a></li>
+ <li><a href="#" data-toggle="modal" data-target="#user_settings1">Account Settings</a></li>
+ { isAdmin ? <li><a href="#" data-toggle="modal" data-target="#team_settings">Team Settings</a></li> : "" }
{ invite_link }
{ team_link }
{ manage_link }
diff --git a/web/react/components/sidebar_right_menu.jsx b/web/react/components/sidebar_right_menu.jsx
index d0c139d1a..22d1d9ad2 100644
--- a/web/react/components/sidebar_right_menu.jsx
+++ b/web/react/components/sidebar_right_menu.jsx
@@ -59,7 +59,8 @@ module.exports = React.createClass({
<div className="nav-pills__container">
<ul className="nav nav-pills nav-stacked">
- <li><a href="#" data-toggle="modal" data-target="#settings_modal"><i className="glyphicon glyphicon-cog"></i>Account Settings</a></li>
+ <li><a href="#" data-toggle="modal" data-target="#user_settings1"><i className="glyphicon glyphicon-cog"></i>Account Settings</a></li>
+ { isAdmin ? <li><a href="#" data-toggle="modal" data-target="#team_settings"><i className="glyphicon glyphicon-globe"></i>Team Settings</a></li> : "" }
{ invite_link }
{ team_link }
{ manage_link }
diff --git a/web/react/components/signup_team_complete.jsx b/web/react/components/signup_team_complete.jsx
index b038679e6..30fe92af5 100644
--- a/web/react/components/signup_team_complete.jsx
+++ b/web/react/components/signup_team_complete.jsx
@@ -9,6 +9,10 @@ var constants = require('../utils/constants.jsx')
WelcomePage = React.createClass({
submitNext: function (e) {
+ if (!utils.isLocalStorageSupported()) {
+ this.setState({ storage_error: "This service requires local storage to be enabled. Please enable it or exit private browsing."} );
+ return;
+ }
e.preventDefault();
this.props.state.wizard = "team_name";
this.props.updateParent(this.props.state);
@@ -26,6 +30,12 @@ WelcomePage = React.createClass({
if (!email || !utils.isEmail(email)) {
state.email_error = "Please enter a valid email address";
this.setState(state);
+ return;
+ }
+ else if (!utils.isLocalStorageSupported()) {
+ state.email_error = "This service requires local storage to be enabled. Please enable it or exit private browsing.";
+ this.setState(state);
+ return;
}
else {
state.email_error = "";
@@ -50,6 +60,7 @@ WelcomePage = React.createClass({
client.track('signup', 'signup_team_01_welcome');
+ var storage_error = this.state.storage_error ? <label className="control-label">{ this.state.storage_error }</label> : null;
var email_error = this.state.email_error ? <label className="control-label">{ this.state.email_error }</label> : null;
var server_error = this.state.server_error ? <div className={ "form-group has-error" }><label className="control-label">{ this.state.server_error }</label></div> : null;
@@ -66,6 +77,7 @@ WelcomePage = React.createClass({
</p>
<div className="form-group">
<button className="btn-primary btn form-group" onClick={this.submitNext}><i className="glyphicon glyphicon-ok"></i>Yes, this address is correct</button>
+ { storage_error }
</div>
<hr />
<p>If this is not correct, you can switch to a different email. We'll send you a new invite right away.</p>
@@ -312,7 +324,7 @@ EmailItem = React.createClass({
getValue: function() {
return this.refs.email.getDOMNode().value.trim()
},
- validate: function() {
+ validate: function(teamEmail) {
var email = this.refs.email.getDOMNode().value.trim().toLowerCase();
if (!email) {
@@ -324,6 +336,11 @@ EmailItem = React.createClass({
this.setState(this.state);
return false;
}
+ else if (email === teamEmail) {
+ this.state.email_error = "Please use an a different email than the one used at signup";
+ this.setState(this.state);
+ return false;
+ }
else {
this.state.email_error = "";
this.setState(this.state);
@@ -363,7 +380,7 @@ SendInivtesPage = React.createClass({
var emails = [];
for (var i = 0; i < this.props.state.invites.length; i++) {
- if (!this.refs['email_' + i].validate()) {
+ if (!this.refs['email_' + i].validate(this.props.state.team.email)) {
valid = false;
} else {
emails.push(this.refs['email_' + i].getValue());
@@ -491,6 +508,7 @@ PasswordPage = React.createClass({
return;
}
+ this.setState({name_error: ""});
$('#finish-button').button('loading');
var teamSignup = JSON.parse(JSON.stringify(this.props.state));
teamSignup.user.password = password;
diff --git a/web/react/components/signup_user_complete.jsx b/web/react/components/signup_user_complete.jsx
index 146419cf5..b9f32f0bc 100644
--- a/web/react/components/signup_user_complete.jsx
+++ b/web/react/components/signup_user_complete.jsx
@@ -13,16 +13,16 @@ module.exports = React.createClass({
this.state.user.username = this.refs.name.getDOMNode().value.trim();
if (!this.state.user.username) {
- this.setState({name_error: "This field is required", email_error: "", password_error: ""});
+ this.setState({name_error: "This field is required", email_error: "", password_error: "", server_error: ""});
return;
}
var username_error = utils.isValidUsername(this.state.user.username)
if (username_error === "Cannot use a reserved word as a username.") {
- this.setState({name_error: "This username is reserved, please choose a new one." });
+ this.setState({name_error: "This username is reserved, please choose a new one.", email_error: "", password_error: "", server_error: ""});
return;
} else if (username_error) {
- this.setState({name_error: "Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'." });
+ this.setState({name_error: "Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'.", email_error: "", password_error: "", server_error: ""});
return;
}
@@ -34,10 +34,12 @@ module.exports = React.createClass({
this.state.user.password = this.refs.password.getDOMNode().value.trim();
if (!this.state.user.password || this.state.user.password .length < 5) {
- this.setState({name_error: "", email_error: "", password_error: "Please enter at least 5 characters"});
+ this.setState({name_error: "", email_error: "", password_error: "Please enter at least 5 characters", server_error: ""});
return;
}
+ this.setState({name_error: "", email_error: "", password_error: "", server_error: ""});
+
this.state.user.allow_marketing = this.refs.email_service.getDOMNode().checked;
client.createUser(this.state.user, this.state.data, this.state.hash,
diff --git a/web/react/components/team_settings.jsx b/web/react/components/team_settings.jsx
new file mode 100644
index 000000000..a43e5d2f0
--- /dev/null
+++ b/web/react/components/team_settings.jsx
@@ -0,0 +1,161 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+var UserStore = require('../stores/user_store.jsx');
+var TeamStore = require('../stores/team_store.jsx');
+var SettingItemMin = require('./setting_item_min.jsx');
+var SettingItemMax = require('./setting_item_max.jsx');
+var SettingPicture = require('./setting_picture.jsx');
+var utils = require('../utils/utils.jsx');
+
+var client = require('../utils/client.jsx');
+var AsyncClient = require('../utils/async_client.jsx');
+var Constants = require('../utils/constants.jsx');
+
+var FeatureTab = React.createClass({
+ submitValetFeature: function() {
+ data = {};
+ data['allow_valet'] = this.state.allow_valet;
+
+ client.updateValetFeature(data,
+ function(data) {
+ this.props.updateSection("");
+ AsyncClient.getMyTeam();
+ }.bind(this),
+ function(err) {
+ state = this.getInitialState();
+ state.server_error = err;
+ this.setState(state);
+ }.bind(this)
+ );
+ },
+ handleValetRadio: function(val) {
+ this.setState({ allow_valet: val });
+ this.refs.wrapper.getDOMNode().focus();
+ },
+ componentWillReceiveProps: function(newProps) {
+ var team = newProps.team;
+
+ var allow_valet = "false";
+ if (team && team.allow_valet) {
+ allow_valet = "true";
+ }
+
+ this.setState({ allow_valet: allow_valet });
+ },
+ getInitialState: function() {
+ var team = this.props.team;
+
+ var allow_valet = "false";
+ if (team && team.allow_valet) {
+ allow_valet = "true";
+ }
+
+ return { allow_valet: allow_valet };
+ },
+ render: function() {
+ var team = this.props.team;
+
+ var client_error = this.state.client_error ? this.state.client_error : null;
+ var server_error = this.state.server_error ? this.state.server_error : null;
+
+ var valetSection;
+ var self = this;
+
+ if (this.props.activeSection === 'valet') {
+ var valetActive = ["",""];
+ if (this.state.allow_valet === "false") {
+ valetActive[1] = "active";
+ } else {
+ valetActive[0] = "active";
+ }
+
+ var inputs = [];
+
+ inputs.push(
+ <div className="col-sm-12">
+ <div className="btn-group" data-toggle="buttons-radio">
+ <button className={"btn btn-default "+valetActive[0]} onClick={function(){self.handleValetRadio("true")}}>On</button>
+ <button className={"btn btn-default "+valetActive[1]} onClick={function(){self.handleValetRadio("false")}}>Off</button>
+ </div>
+ <div><br/>Warning: Turning on the Valet feature and using it with any third party software increases the risk of a security breach.</div>
+ </div>
+ );
+
+ valetSection = (
+ <SettingItemMax
+ title="Valet"
+ inputs={inputs}
+ submit={this.submitValetFeature}
+ server_error={server_error}
+ client_error={client_error}
+ updateSection={function(e){self.props.updateSection("");e.preventDefault();}}
+ />
+ );
+ } else {
+ var describe = "";
+ if (this.state.allow_valet === "false") {
+ describe = "Off";
+ } else {
+ describe = "On";
+ }
+
+ valetSection = (
+ <SettingItemMin
+ title="Valet"
+ describe={describe}
+ updateSection={function(){self.props.updateSection("valet");}}
+ />
+ );
+ }
+
+ return (
+ <div>
+ <div className="modal-header">
+ <button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+ <h4 className="modal-title" ref="title"><i className="modal-back"></i>Feature Settings</h4>
+ </div>
+ <div ref="wrapper" className="user-settings">
+ <h3 className="tab-header">Feature Settings</h3>
+ <div className="divider-dark first"/>
+ {valetSection}
+ <div className="divider-dark"/>
+ </div>
+ </div>
+ );
+ }
+});
+
+module.exports = React.createClass({
+ componentDidMount: function() {
+ TeamStore.addChangeListener(this._onChange);
+ },
+ componentWillUnmount: function() {
+ TeamStore.removeChangeListener(this._onChange);
+ },
+ _onChange: function () {
+ var team = TeamStore.getCurrent();
+ if (!utils.areStatesEqual(this.state.team, team)) {
+ this.setState({ team: team });
+ }
+ },
+ getInitialState: function() {
+ return { team: TeamStore.getCurrent() };
+ },
+ render: function() {
+ if (this.props.activeTab === 'general') {
+ return (
+ <div>
+ </div>
+ );
+ } else if (this.props.activeTab === 'feature') {
+ return (
+ <div>
+ <FeatureTab team={this.state.team} activeSection={this.props.activeSection} updateSection={this.props.updateSection} />
+ </div>
+ );
+ } else {
+ return <div/>;
+ }
+ }
+});
diff --git a/web/react/components/settings_modal.jsx b/web/react/components/team_settings_modal.jsx
index 57a869f93..e50378b7f 100644
--- a/web/react/components/settings_modal.jsx
+++ b/web/react/components/team_settings_modal.jsx
@@ -2,7 +2,7 @@
// See License.txt for license information.
var SettingsSidebar = require('./settings_sidebar.jsx');
-var UserSettings = require('./user_settings.jsx');
+var TeamSettings = require('./team_settings.jsx');
module.exports = React.createClass({
componentDidMount: function() {
@@ -22,27 +22,31 @@ module.exports = React.createClass({
this.setState({ active_section: section });
},
getInitialState: function() {
- return { active_tab: "general", active_section: "" };
+ return { active_tab: "feature", active_section: "" };
},
render: function() {
+ var tabs = [];
+ tabs.push({name: "feature", ui_name: "Features", icon: "glyphicon glyphicon-wrench"});
+
return (
- <div className="modal fade" ref="modal" id="settings_modal" role="dialog" aria-hidden="true">
+ <div className="modal fade" ref="modal" id="team_settings" role="dialog" aria-hidden="true">
<div className="modal-dialog settings-modal">
<div className="modal-content">
<div className="modal-header">
<button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
- <h4 className="modal-title" ref="title">Account Settings</h4>
+ <h4 className="modal-title" ref="title">Team Settings</h4>
</div>
<div className="modal-body">
<div className="settings-table">
<div className="settings-links">
<SettingsSidebar
+ tabs={tabs}
activeTab={this.state.active_tab}
updateTab={this.updateTab}
/>
</div>
- <div className="settings-content">
- <UserSettings
+ <div className="settings-content minimize-settings">
+ <TeamSettings
activeTab={this.state.active_tab}
activeSection={this.state.active_section}
updateSection={this.updateSection}
diff --git a/web/react/components/user_profile.jsx b/web/react/components/user_profile.jsx
index 8ffad737d..648960471 100644
--- a/web/react/components/user_profile.jsx
+++ b/web/react/components/user_profile.jsx
@@ -10,8 +10,7 @@ function getStateFromStores(userId) {
if (profile == null) {
return { profile: { id: "0", username: "..."} };
- }
- else {
+ } else {
return { profile: profile };
}
}
@@ -54,12 +53,11 @@ module.exports = React.createClass({
var name = this.props.overwriteName ? this.props.overwriteName : this.state.profile.username;
- var data_content = ""
- data_content += "<img style='margin: 10px' src='/api/v1/users/" + this.state.profile.id + "/image' height='128' width='128' />"
+ var data_content = "<img style='margin: 10px' src='/api/v1/users/" + this.state.profile.id + "/image' height='128' width='128' />";
if (!config.ShowEmail) {
- data_content += "<div><span style='white-space:nowrap;'>Email not shared</span></div>";
+ data_content += "<div class='text-nowrap'>Email not shared</div>";
} else {
- data_content += "<div><a href='mailto:'" + this.state.profile.email + "'' style='white-space:nowrap;text-transform:lowercase;'>" + this.state.profile.email + "</a></div>";
+ data_content += "<div><a href='mailto:" + this.state.profile.email + "' class='text-nowrap text-lowercase'>" + this.state.profile.email + "</a></div>";
}
return (
diff --git a/web/react/components/user_settings.jsx b/web/react/components/user_settings.jsx
index 110634b50..147973fb7 100644
--- a/web/react/components/user_settings.jsx
+++ b/web/react/components/user_settings.jsx
@@ -155,7 +155,7 @@ var NotificationsTab = React.createClass({
var inputs = [];
inputs.push(
- <div className="col-sm-12">
+ <div>
<div className="radio">
<label>
<input type="radio" checked={notifyActive[0]} onClick={function(){self.handleNotifyRadio("all")}}>For all activity</input>
@@ -216,7 +216,7 @@ var NotificationsTab = React.createClass({
var inputs = [];
inputs.push(
- <div className="col-sm-12">
+ <div>
<div className="btn-group" data-toggle="buttons-radio">
<button className={"btn btn-default "+soundActive[0]} onClick={function(){self.handleSoundRadio("true")}}>On</button>
<button className={"btn btn-default "+soundActive[1]} onClick={function(){self.handleSoundRadio("false")}}>Off</button>
@@ -262,7 +262,7 @@ var NotificationsTab = React.createClass({
var inputs = [];
inputs.push(
- <div className="col-sm-12">
+ <div>
<div className="btn-group" data-toggle="buttons-radio">
<button className={"btn btn-default "+emailActive[0]} onClick={function(){self.handleEmailRadio("true")}}>On</button>
<button className={"btn btn-default "+emailActive[1]} onClick={function(){self.handleEmailRadio("false")}}>Off</button>
@@ -309,7 +309,7 @@ var NotificationsTab = React.createClass({
if (first_name != "") {
inputs.push(
- <div className="col-sm-12">
+ <div>
<div className="checkbox">
<label>
<input type="checkbox" checked={this.state.first_name_key} onChange={function(e){self.updateFirstNameKey(e.target.checked);}}>{'Your case sensitive first name "' + first_name + '"'}</input>
@@ -320,7 +320,7 @@ var NotificationsTab = React.createClass({
}
inputs.push(
- <div className="col-sm-12">
+ <div>
<div className="checkbox">
<label>
<input type="checkbox" checked={this.state.username_key} onChange={function(e){self.updateUsernameKey(e.target.checked);}}>{'Your non-case sensitive username "' + user.username + '"'}</input>
@@ -330,7 +330,7 @@ var NotificationsTab = React.createClass({
);
inputs.push(
- <div className="col-sm-12">
+ <div>
<div className="checkbox">
<label>
<input type="checkbox" checked={this.state.mention_key} onChange={function(e){self.updateMentionKey(e.target.checked);}}>{'Your username mentioned "@' + user.username + '"'}</input>
@@ -340,7 +340,7 @@ var NotificationsTab = React.createClass({
);
inputs.push(
- <div className="col-sm-12">
+ <div>
<div className="checkbox">
<label>
<input ref="customcheck" type="checkbox" checked={this.state.custom_keys_checked} onChange={this.updateCustomMentionKeys}>{'Other non-case sensitive words, separated by commas:'}</input>
@@ -622,7 +622,7 @@ var SecurityTab = React.createClass({
var inputs = [];
inputs.push(
- <div>
+ <div className="form-group">
<label className="col-sm-5 control-label">Current Password</label>
<div className="col-sm-7">
<input className="form-control" type="password" onChange={this.updateCurrentPassword} value={this.state.current_password}/>
@@ -630,7 +630,7 @@ var SecurityTab = React.createClass({
</div>
);
inputs.push(
- <div>
+ <div className="form-group">
<label className="col-sm-5 control-label">New Password</label>
<div className="col-sm-7">
<input className="form-control" type="password" onChange={this.updateNewPassword} value={this.state.new_password}/>
@@ -638,7 +638,7 @@ var SecurityTab = React.createClass({
</div>
);
inputs.push(
- <div>
+ <div className="form-group">
<label className="col-sm-5 control-label">Retype New Password</label>
<div className="col-sm-7">
<input className="form-control" type="password" onChange={this.updateConfirmPassword} value={this.state.confirm_password}/>
@@ -658,9 +658,10 @@ var SecurityTab = React.createClass({
);
} else {
var d = new Date(this.props.user.last_password_update);
- var hour = d.getHours() < 10 ? "0" + d.getHours() : String(d.getHours());
+ var hour = d.getHours() % 12 ? String(d.getHours() % 12) : "12";
var min = d.getMinutes() < 10 ? "0" + d.getMinutes() : String(d.getMinutes());
- var dateStr = "Last updated " + Constants.MONTHS[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear() + " at " + hour + ":" + min;
+ var timeOfDay = d.getHours() >= 12 ? " pm" : " am";
+ var dateStr = "Last updated " + Constants.MONTHS[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear() + " at " + hour + ":" + min + timeOfDay;
passwordSection = (
<SettingItemMin
@@ -836,7 +837,7 @@ var GeneralTab = React.createClass({
var inputs = [];
inputs.push(
- <div>
+ <div className="form-group">
<label className="col-sm-5 control-label">First Name</label>
<div className="col-sm-7">
<input className="form-control" type="text" onChange={this.updateFirstName} value={this.state.first_name}/>
@@ -845,7 +846,7 @@ var GeneralTab = React.createClass({
);
inputs.push(
- <div>
+ <div className="form-group">
<label className="col-sm-5 control-label">Last Name</label>
<div className="col-sm-7">
<input className="form-control" type="text" onChange={this.updateLastName} value={this.state.last_name}/>
@@ -878,7 +879,7 @@ var GeneralTab = React.createClass({
var inputs = [];
inputs.push(
- <div>
+ <div className="form-group">
<label className="col-sm-5 control-label">{utils.isMobile() ? "": "Username"}</label>
<div className="col-sm-7">
<input className="form-control" type="text" onChange={this.updateUsername} value={this.state.username}/>
@@ -910,7 +911,7 @@ var GeneralTab = React.createClass({
var inputs = [];
inputs.push(
- <div>
+ <div className="form-group">
<label className="col-sm-5 control-label">Primary Email</label>
<div className="col-sm-7">
<input className="form-control" type="text" onChange={this.updateEmail} value={this.state.email}/>
@@ -1047,7 +1048,7 @@ var AppearanceTab = React.createClass({
var inputs = [];
inputs.push(
- <li className="row setting-list-item form-group">
+ <li className="setting-list-item">
<div className="btn-group" data-toggle="buttons-radio">
{ theme_buttons }
</div>
diff --git a/web/react/components/user_settings_modal.jsx b/web/react/components/user_settings_modal.jsx
new file mode 100644
index 000000000..1761e575a
--- /dev/null
+++ b/web/react/components/user_settings_modal.jsx
@@ -0,0 +1,68 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+var SettingsSidebar = require('./settings_sidebar.jsx');
+var UserSettings = require('./user_settings.jsx');
+
+module.exports = React.createClass({
+ componentDidMount: function() {
+ $('body').on('click', '.modal-back', function(){
+ $(this).closest('.modal-dialog').removeClass('display--content');
+ });
+ $('body').on('click', '.modal-header .close', function(){
+ setTimeout(function() {
+ $('.modal-dialog.display--content').removeClass('display--content');
+ }, 500);
+ });
+ },
+ updateTab: function(tab) {
+ this.setState({ active_tab: tab });
+ },
+ updateSection: function(section) {
+ this.setState({ active_section: section });
+ },
+ getInitialState: function() {
+ return { active_tab: "general", active_section: "" };
+ },
+ render: function() {
+ var tabs = [];
+ tabs.push({name: "general", ui_name: "General", icon: "glyphicon glyphicon-cog"});
+ tabs.push({name: "security", ui_name: "Security", icon: "glyphicon glyphicon-lock"});
+ tabs.push({name: "notifications", ui_name: "Notifications", icon: "glyphicon glyphicon-exclamation-sign"});
+ tabs.push({name: "appearance", ui_name: "Appearance", icon: "glyphicon glyphicon-wrench"});
+ //tabs.push({name: "sessions", ui_name: "Sessions", icon: "glyphicon glyphicon-globe"});
+ //tabs.push({name: "activity_log", ui_name: "Activity Log", icon: "glyphicon glyphicon-time"});
+
+ return (
+ <div className="modal fade" ref="modal" id="user_settings1" role="dialog" aria-hidden="true">
+ <div className="modal-dialog settings-modal">
+ <div className="modal-content">
+ <div className="modal-header">
+ <button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+ <h4 className="modal-title" ref="title">Account Settings</h4>
+ </div>
+ <div className="modal-body">
+ <div className="settings-table">
+ <div className="settings-links">
+ <SettingsSidebar
+ tabs={tabs}
+ activeTab={this.state.active_tab}
+ updateTab={this.updateTab}
+ />
+ </div>
+ <div className="settings-content minimize-settings">
+ <UserSettings
+ activeTab={this.state.active_tab}
+ activeSection={this.state.active_section}
+ updateSection={this.updateSection}
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+});
+
diff --git a/web/react/components/view_image.jsx b/web/react/components/view_image.jsx
index 4cb30e1d3..c573e9dbb 100644
--- a/web/react/components/view_image.jsx
+++ b/web/react/components/view_image.jsx
@@ -124,7 +124,7 @@ module.exports = React.createClass({
<div key={name+"_loading"}>
<img ref="placeholder" className="loader-image" src="/static/images/load.gif" />
{ percentage > 0 ?
- <span className="loader-percent" >{"Downloading " + percentage + "%"}</span>
+ <span className="loader-percent" >{"Previewing " + percentage + "%"}</span>
: ""}
</div>
);
diff --git a/web/react/pages/channel.jsx b/web/react/pages/channel.jsx
index df67d4360..3aa985863 100644
--- a/web/react/pages/channel.jsx
+++ b/web/react/pages/channel.jsx
@@ -22,7 +22,8 @@ var MoreChannelsModal = require('../components/more_channels.jsx');
var NewChannelModal = require('../components/new_channel.jsx');
var PostDeletedModal = require('../components/post_deleted_modal.jsx');
var ChannelNotificationsModal = require('../components/channel_notifications.jsx');
-var UserSettingsModal = require('../components/settings_modal.jsx');
+var UserSettingsModal = require('../components/user_settings_modal.jsx');
+var TeamSettingsModal = require('../components/team_settings_modal.jsx');
var ChannelMembersModal = require('../components/channel_members.jsx');
var ChannelInviteModal = require('../components/channel_invite_modal.jsx');
var TeamMembersModal = require('../components/team_members.jsx');
@@ -36,7 +37,7 @@ var ChannelInfoModal = require('../components/channel_info_modal.jsx');
var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes;
-global.window.setup_channel_page = function(team_name, team_type, channel_name, channel_id) {
+global.window.setup_channel_page = function(team_name, team_type, team_id, channel_name, channel_id) {
AppDispatcher.handleViewAction({
type: ActionTypes.CLICK_CHANNEL,
@@ -44,6 +45,11 @@ global.window.setup_channel_page = function(team_name, team_type, channel_name,
id: channel_id
});
+ AppDispatcher.handleViewAction({
+ type: ActionTypes.CLICK_TEAM,
+ id: team_id
+ });
+
React.render(
<ErrorBar/>,
document.getElementById('error_bar')
@@ -80,6 +86,11 @@ global.window.setup_channel_page = function(team_name, team_type, channel_name,
);
React.render(
+ <TeamSettingsModal />,
+ document.getElementById('team_settings_modal')
+ );
+
+ React.render(
<TeamMembersModal teamName={team_name} />,
document.getElementById('team_members_modal')
);
diff --git a/web/react/stores/team_store.jsx b/web/react/stores/team_store.jsx
new file mode 100644
index 000000000..e95daeeba
--- /dev/null
+++ b/web/react/stores/team_store.jsx
@@ -0,0 +1,100 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
+var EventEmitter = require('events').EventEmitter;
+var assign = require('object-assign');
+
+var Constants = require('../utils/constants.jsx');
+var ActionTypes = Constants.ActionTypes;
+
+
+var CHANGE_EVENT = 'change';
+
+var TeamStore = assign({}, EventEmitter.prototype, {
+ emitChange: function() {
+ this.emit(CHANGE_EVENT);
+ },
+ addChangeListener: function(callback) {
+ this.on(CHANGE_EVENT, callback);
+ },
+ removeChangeListener: function(callback) {
+ this.removeListener(CHANGE_EVENT, callback);
+ },
+ get: function(id) {
+ var c = this._getTeams();
+ return c[id];
+ },
+ getByName: function(name) {
+ var current = null;
+ var t = this._getTeams();
+
+ for (id in t) {
+ if (t[id].name == name) {
+ return t[id];
+ }
+ }
+
+ return null;
+ },
+ getAll: function() {
+ return this._getTeams();
+ },
+ setCurrentId: function(id) {
+ if (id == null)
+ sessionStorage.removeItem("current_team_id");
+ else
+ sessionStorage.setItem("current_team_id", id);
+ },
+ getCurrentId: function() {
+ return sessionStorage.getItem("current_team_id");
+ },
+ getCurrent: function() {
+ var currentId = TeamStore.getCurrentId();
+
+ if (currentId != null)
+ return this.get(currentId);
+ else
+ return null;
+ },
+ storeTeam: function(team) {
+ var teams = this._getTeams();
+ teams[team.id] = team;
+ this._storeTeams(teams);
+ },
+ _storeTeams: function(teams) {
+ sessionStorage.setItem("user_teams", JSON.stringify(teams));
+ },
+ _getTeams: function() {
+ var teams = {};
+
+ try {
+ teams = JSON.parse(sessionStorage.user_teams);
+ }
+ catch (err) {
+ }
+
+ return teams;
+ }
+});
+
+TeamStore.dispatchToken = AppDispatcher.register(function(payload) {
+ var action = payload.action;
+
+ switch(action.type) {
+
+ case ActionTypes.CLICK_TEAM:
+ TeamStore.setCurrentId(action.id);
+ TeamStore.emitChange();
+ break;
+
+ case ActionTypes.RECIEVED_TEAM:
+ TeamStore.storeTeam(action.team);
+ TeamStore.emitChange();
+ break;
+
+ default:
+ }
+});
+
+module.exports = TeamStore;
diff --git a/web/react/utils/async_client.jsx b/web/react/utils/async_client.jsx
index bb7ca458f..9383057c3 100644
--- a/web/react/utils/async_client.jsx
+++ b/web/react/utils/async_client.jsx
@@ -15,6 +15,8 @@ var ActionTypes = Constants.ActionTypes;
var callTracker = {};
var dispatchError = function(err, method) {
+ if (err.message === "There appears to be a problem with your internet connection") return;
+
AppDispatcher.handleServerAction({
type: ActionTypes.RECIEVED_ERROR,
err: err,
@@ -355,3 +357,25 @@ module.exports.getStatuses = function() {
}
);
}
+
+module.exports.getMyTeam = function() {
+ if (isCallInProgress("getMyTeam")) return;
+
+ callTracker["getMyTeam"] = utils.getTimestamp();
+ client.getMyTeam(
+ function(data, textStatus, xhr) {
+ callTracker["getMyTeam"] = 0;
+
+ if (xhr.status === 304 || !data) return;
+
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECIEVED_TEAM,
+ team: data
+ });
+ },
+ function(err) {
+ callTracker["getMyTeam"] = 0;
+ dispatchError(err, "getMyTeam");
+ }
+ );
+}
diff --git a/web/react/utils/client.jsx b/web/react/utils/client.jsx
index 786e6dcea..15b6ace91 100644
--- a/web/react/utils/client.jsx
+++ b/web/react/utils/client.jsx
@@ -811,3 +811,34 @@ module.exports.getStatuses = function(success, error) {
}
});
};
+
+module.exports.getMyTeam = function(success, error) {
+ $.ajax({
+ url: "/api/v1/teams/me",
+ dataType: 'json',
+ type: 'GET',
+ success: success,
+ ifModified: true,
+ error: function(xhr, status, err) {
+ e = handleError("getMyTeam", xhr, status, err);
+ error(e);
+ }
+ });
+};
+
+module.exports.updateValetFeature = function(data, success, error) {
+ $.ajax({
+ url: "/api/v1/teams/update_valet_feature",
+ dataType: 'json',
+ contentType: 'application/json',
+ type: 'POST',
+ data: JSON.stringify(data),
+ success: success,
+ error: function(xhr, status, err) {
+ e = handleError("updateValetFeature", xhr, status, err);
+ error(e);
+ }
+ });
+
+ module.exports.track('api', 'api_teams_update_valet_feature');
+};
diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx
index deb07409b..e5f42c8a0 100644
--- a/web/react/utils/constants.jsx
+++ b/web/react/utils/constants.jsx
@@ -27,6 +27,9 @@ module.exports = {
RECIEVED_STATUSES: null,
RECIEVED_MSG: null,
+
+ CLICK_TEAM: null,
+ RECIEVED_TEAM: null,
}),
PayloadSources: keyMirror({
@@ -45,6 +48,7 @@ module.exports = {
PATCH_TYPES: ['patch'],
ICON_FROM_TYPE: {'audio': 'audio', 'video': 'video', 'spreadsheet': 'ppt', 'pdf': 'pdf', 'code': 'code' , 'word': 'word' , 'excel': 'excel' , 'patch': 'patch', 'other': 'generic'},
MAX_DISPLAY_FILES: 5,
+ MAX_UPLOAD_FILES: 5,
MAX_FILE_SIZE: 50000000, // 50 MB
DEFAULT_CHANNEL: 'town-square',
POST_CHUNK_SIZE: 60,
diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index fb4f3a34e..75c583c8f 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -96,6 +96,21 @@ module.exports.getCookie = function(name) {
if (parts.length == 2) return parts.pop().split(";").shift();
}
+module.exports.isLocalStorageSupported = function() {
+ try {
+ sessionStorage.setItem("testSession", '1');
+ sessionStorage.removeItem("testSession");
+
+ localStorage.setItem("testLocal", '1');
+ localStorage.removeItem("testLocal", '1');
+
+ return true;
+ }
+ catch (e) {
+ return false;
+ }
+}
+
module.exports.notifyMe = function(title, body, channel) {
if ("Notification" in window && Notification.permission !== 'denied') {
Notification.requestPermission(function (permission) {
@@ -418,7 +433,7 @@ module.exports.textToJsx = function(text, options) {
highlightSearchClass = " search-highlight";
}
- inner.push(<span key={name+i+z+"_span"}>{prefix}<a className={mClass + highlightSearchClass + " mention-link"} key={name+i+z+"_link"} href="#" onClick={function() {module.exports.searchForTerm(name);}}>@{name}</a>{suffix} </span>);
+ inner.push(<span key={name+i+z+"_span"}>{prefix}<a className={mClass + highlightSearchClass + " mention-link"} key={name+i+z+"_link"} href="#" onClick={function(value) { return function() { module.exports.searchForTerm(value); } }(name)}>@{name}</a>{suffix} </span>);
} else if (urlMatcher.test(word)) {
var match = urlMatcher.match(word)[0];
var link = match.url;
@@ -431,7 +446,7 @@ module.exports.textToJsx = function(text, options) {
} else if (trimWord.match(hashRegex)) {
var suffix = word.match(puncEndRegex);
var prefix = word.match(puncStartRegex);
- var mClass = trimWord in implicitKeywords ? mentionClass : "";
+ var mClass = trimWord in implicitKeywords || trimWord.toLowerCase() in implicitKeywords ? mentionClass : "";
if (searchTerm === trimWord.substring(1).toLowerCase() || searchTerm === trimWord.toLowerCase()) {
highlightSearchClass = " search-highlight";
@@ -439,7 +454,7 @@ module.exports.textToJsx = function(text, options) {
inner.push(<span key={word+i+z+"_span"}>{prefix}<a key={word+i+z+"_hash"} className={"theme " + mClass + highlightSearchClass} href="#" onClick={function(value) { return function() { module.exports.searchForTerm(value); } }(trimWord)}>{trimWord}</a>{suffix} </span>);
- } else if (trimWord in implicitKeywords) {
+ } else if (trimWord in implicitKeywords || trimWord.toLowerCase() in implicitKeywords) {
var suffix = word.match(puncEndRegex);
var prefix = word.match(puncStartRegex);
diff --git a/web/sass-files/sass/partials/_mentions.scss b/web/sass-files/sass/partials/_mentions.scss
index 11cd4e9e4..cb6ff16c5 100644
--- a/web/sass-files/sass/partials/_mentions.scss
+++ b/web/sass-files/sass/partials/_mentions.scss
@@ -3,21 +3,19 @@
background: $primary-color;
position: relative;
z-index: 10;
- padding-bottom: 1px;
+ padding-bottom: 2px;
@include border-radius(3px);
- -moz-box-sizing: border-box;
- -webkit-box-sizing: border-box;
- box-sizing: border-box;
}
.mentions--top {
position: absolute;
- z-index:99999;
+ z-index: 1040;
.mentions-box {
position:absolute;
background-color:#fff;
- border:1px solid #ddd;
- overflow:scroll;
+ border: $border-gray;
+ overflow-x: hidden;
+ overflow-y: scroll;
bottom:0;
}
}
@@ -29,10 +27,10 @@
height:37px;
padding:2px;
z-index:101;
-}
-
-.mentions-name:hover {
- background-color:#e8eaed;
+ cursor: pointer;
+ &:hover {
+ background-color:#e8eaed;
+ }
}
.mentions-text {
@@ -46,6 +44,11 @@
border-radius: 10%;
}
+.mention-fullname {
+ color: grey;
+ padding-left: 10px;
+}
+
.mention-highlight {
background-color:#fff2bb;
}
diff --git a/web/sass-files/sass/partials/_modal.scss b/web/sass-files/sass/partials/_modal.scss
index 43dbdc077..4427cb7dd 100644
--- a/web/sass-files/sass/partials/_modal.scss
+++ b/web/sass-files/sass/partials/_modal.scss
@@ -1,9 +1,17 @@
+.modal-body {
+ padding: 20px 15px;
+}
.modal {
&.image_modal {
.modal-backdrop.in {
@include opacity(0.7);
}
}
+ .info__label {
+ font-weight: bold;
+ text-align: right;
+ padding-right: 0;
+ }
.remove__member {
float: right;
}
@@ -29,7 +37,7 @@
border-radius: 0;
background: $primary-color;
color: #FFF;
- padding: 15px 15px 11px;
+ padding: 15px 15px 11px;
border: none;
min-height: 56px;
@include clearfix;
@@ -41,11 +49,21 @@
margin: 0;
}
button.close {
- margin-top: 0;
+ margin: -2px -2px 0 0;
color: #fff;
@include opacity(1);
z-index: 5;
+ width: 30px;
+ height: 30px;
+ line-height: 30px;
+ @include single-transition(all, 0.25s, ease-in);
position: relative;
+ &:hover {
+ background: rgba(0, 0, 0, 0.1);
+ }
+ span {
+ line-height: 10px;
+ }
}
.btn {
margin-right: 10px;
@@ -129,15 +147,16 @@
width:100%;
margin: 0 auto;
.image-wrapper {
- padding: 4px;
background: #FFF;
position: relative;
max-width: 80%;
- min-height: 280px;
min-width: 280px;
- @include border-radius(4px);
+ @include border-radius(3px);
display: table;
margin: 0 auto;
+ &:hover {
+ @include border-radius(3px 3px 0 0);
+ }
&:hover .modal-close {
@include opacity(1);
}
@@ -217,10 +236,11 @@
}
.modal-button-bar {
position:absolute;
- bottom:0px;
+ bottom:-40px;
left:0px;
right:0px;
- background-color:rgba(0, 0, 0, 0.8);
+ background-color: #222;
+ @include border-radius(0 0 3px 3px);
@include opacity(0);
-webkit-transition: opacity 0.6s;
-moz-transition: opacity 0.6s;
@@ -228,7 +248,6 @@
transition: opacity 0.6s;
line-height: 40px;
padding: 0 10px;
- margin: 4px;
&.footer--show {
@include opacity(1);
}
diff --git a/web/sass-files/sass/partials/_post.scss b/web/sass-files/sass/partials/_post.scss
index 6da516cf9..745d50173 100644
--- a/web/sass-files/sass/partials/_post.scss
+++ b/web/sass-files/sass/partials/_post.scss
@@ -42,49 +42,63 @@ body.ios {
}
#post-list {
- .new-seperator {
+ .date-separator, .new-separator {
text-align: center;
- padding: 1em 0;
- .new-seperator__hr {
- border-color: #FFAF53;
- margin: 0;
+ height: 2em;
+ margin: 0;
+ position: relative;
+ &:before, &:after {
+ content: "";
+ height: 1em;
+ position: absolute;
+ left: 0;
+ width: 100%;
+ display: none;
}
- .new-seperator__text {
- margin-top: -11px;
- color: #FF8800;
- background: #FFF;
- display: inline-block;
- padding: 0 1em;
- font-weight: normal;
+ &:before {
+ bottom: 0;
+ }
+ &:after {
+ top: 0;
}
- }
- .date-seperator {
- text-align: center;
- margin: 1em 0;
- height: 0;
&.hovered--after {
- margin-bottom: 0;
- padding-bottom: 1em;
- background: #f6f6f6;
+ &:before {
+ background: #f6f6f6;
+ display: block;
+ }
}
&.hovered--before {
- margin-top: 0;
- padding-top: 1em;
- background: #f6f6f6;
+ &:after {
+ background: #f6f6f6;
+ display: block;
+ }
}
- .date-seperator__hr {
+ .separator__hr {
border-color: #ccc;
margin: 0;
+ position: relative;
+ z-index: 5;
+ top: 1em;
}
- .date-seperator__text {
- margin-top: -13px;
- line-height: 24px;
+ .separator__text {
+ line-height: 2em;
color: #555;
background: #FFF;
display: inline-block;
padding: 0 1em;
font-weight: 900;
@include border-radius(50px);
+ position: relative;
+ z-index: 5;
+ }
+ }
+ .new-separator {
+ .separator__hr {
+ border-color: #FFAF53;
+ }
+ .separator__text {
+ color: #F80;
+ font-weight: normal;
}
}
.post-list-holder-by-time {
diff --git a/web/sass-files/sass/partials/_responsive.scss b/web/sass-files/sass/partials/_responsive.scss
index bed2f6324..25533c770 100644
--- a/web/sass-files/sass/partials/_responsive.scss
+++ b/web/sass-files/sass/partials/_responsive.scss
@@ -231,17 +231,14 @@
}
}
.modal {
+ .info__label {
+ text-align: left;
+ padding-bottom: 5px;
+ }
.modal-header {
- padding-left: 20px;
- padding-right: 20px;
.modal-action {
margin-top: 10px;
}
- button.close {
- width: 35px;
- height: 32px;
- margin: -5px -10px 0;
- }
.modal-title {
float: none;
}
@@ -261,9 +258,11 @@
.settings-table {
display: block;
.settings-content {
- display: block;
- .section-edit {
- text-align: left;
+ &.minimize-settings {
+ display: block;
+ .section-edit {
+ text-align: left;
+ }
}
}
.settings-links {
@@ -287,10 +286,12 @@
}
.settings-table {
.settings-content {
- padding: 0;
- display: none;
- .user-settings {
- padding: 70px 20px 30px;
+ &.minimize-settings {
+ padding: 0;
+ display: none;
+ .user-settings {
+ padding: 70px 20px 30px;
+ }
}
}
.settings-links {
@@ -538,7 +539,6 @@
.modal {
.modal-image {
.image-wrapper {
- padding-bottom: 40px;
.modal-close {
@include opacity(1);
}
diff --git a/web/sass-files/sass/partials/_signup.scss b/web/sass-files/sass/partials/_signup.scss
index 11ccc0fc9..8917ebb9f 100644
--- a/web/sass-files/sass/partials/_signup.scss
+++ b/web/sass-files/sass/partials/_signup.scss
@@ -17,7 +17,7 @@
font-weight: 600;
margin-bottom: 0.5em;
letter-spacing: -0.5px;
- font-size: em(30px);
+ font-size: em(28px);
}
h3 {
font-weight: 600;
@@ -28,6 +28,10 @@
font-size: em(18px);
font-weight: 600;
margin-bottom: 1em;
+ &.text--light {
+ font-weight: 300;
+ color: #999;
+ }
}
p {
color: #555;
diff --git a/web/static/config/config.js b/web/static/config/config.js
index 82d4bbf70..45c713da2 100644
--- a/web/static/config/config.js
+++ b/web/static/config/config.js
@@ -13,6 +13,7 @@ var config = {
// Feature switches
AllowPublicLink: true,
AllowInviteNames: true,
+ RequireInviteNames: false,
AllowSignupDomainsWizard: false,
// Privacy switches
diff --git a/web/static/images/ding.mp3 b/web/static/images/ding.mp3
index b08407e03..bfbd9bb82 100644
--- a/web/static/images/ding.mp3
+++ b/web/static/images/ding.mp3
Binary files differ
diff --git a/web/static/images/salamander.jpg b/web/static/images/salamander.jpg
deleted file mode 100644
index e841b12d5..000000000
--- a/web/static/images/salamander.jpg
+++ /dev/null
Binary files differ
diff --git a/web/static/images/test.png b/web/static/images/test.png
index 7d95d80e5..d8a9513d8 100644
--- a/web/static/images/test.png
+++ b/web/static/images/test.png
Binary files differ
diff --git a/web/static/images/testgif.gif b/web/static/images/testgif.gif
new file mode 100755
index 000000000..83d5eed72
--- /dev/null
+++ b/web/static/images/testgif.gif
Binary files differ
diff --git a/web/static/images/testjpg.jpg b/web/static/images/testjpg.jpg
new file mode 100755
index 000000000..025c8c581
--- /dev/null
+++ b/web/static/images/testjpg.jpg
Binary files differ
diff --git a/web/static/images/toothless.gif b/web/static/images/toothless.gif
deleted file mode 100644
index 8c269c131..000000000
--- a/web/static/images/toothless.gif
+++ /dev/null
Binary files differ
diff --git a/web/templates/channel.html b/web/templates/channel.html
index d313b5395..d10ae2304 100644
--- a/web/templates/channel.html
+++ b/web/templates/channel.html
@@ -26,6 +26,7 @@
<div id="edit_mention_tab"></div>
<div id="get_link_modal"></div>
<div id="user_settings_modal"></div>
+ <div id="team_settings_modal"></div>
<div id="invite_member_modal"></div>
<div id="edit_channel_modal"></div>
<div id="delete_channel_modal"></div>
@@ -43,7 +44,7 @@
<div id="direct_channel_modal"></div>
<div id="channel_info_modal"></div>
<script>
-window.setup_channel_page('{{ .Props.TeamName }}', '{{ .Props.TeamType }}', '{{ .Props.ChannelName }}', '{{ .Props.ChannelId }}');
+window.setup_channel_page('{{ .Props.TeamName }}', '{{ .Props.TeamType }}', '{{ .Props.TeamId }}', '{{ .Props.ChannelName }}', '{{ .Props.ChannelId }}');
</script>
</body>
</html>
diff --git a/web/templates/signup_team.html b/web/templates/signup_team.html
index f4e73f0a1..f7e277340 100644
--- a/web/templates/signup_team.html
+++ b/web/templates/signup_team.html
@@ -8,7 +8,8 @@
<div class="col-sm-12">
<div class="signup-team__container">
<img class="signup-team-logo" src="/static/images/logo.png" />
- <h4>{{ .SiteName }} is free for an unlimited time, for unlimited users. </h4>
+ <h2>All team communication in one place, searchable and accessible anywhere</h2>
+ <h4 class="text--light">{{ .SiteName }} is free for an unlimited time, for unlimited users </h4 class="text--light">
<div id="signup-team"></div>
<a class="signup-team-login" href="/login">or Sign In</a>
</div>
diff --git a/web/web.go b/web/web.go
index 3210ede1e..7357124b5 100644
--- a/web/web.go
+++ b/web/web.go
@@ -319,6 +319,7 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) {
page.Title = name + " - " + team.Name + " " + page.SiteName
page.Props["TeamName"] = team.Name
page.Props["TeamType"] = team.Type
+ page.Props["TeamId"] = team.Id
page.Props["ChannelName"] = name
page.Props["ChannelId"] = channelId
page.Props["UserId"] = c.Session.UserId