forked from gonum/plot
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathimage.go
More file actions
144 lines (130 loc) · 3.75 KB
/
image.go
File metadata and controls
144 lines (130 loc) · 3.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// Copyright ©2016 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package plotter
import (
"image"
"math"
"gonum.org/v1/plot"
"gonum.org/v1/plot/vg"
"gonum.org/v1/plot/vg/draw"
)
// Image is a plotter that draws a scaled, raster image.
type Image struct {
img image.Image
cols int
rows int
xmin, xmax, dx float64
ymin, ymax, dy float64
}
// NewImage creates a new image plotter.
// Image will plot img inside the rectangle defined by the
// (xmin, ymin) and (xmax, ymax) points given in the data space.
// The img will be scaled to fit inside the rectangle.
func NewImage(img image.Image, xmin, ymin, xmax, ymax float64) *Image {
if src, ok := img.(*image.Uniform); ok {
img = uniform{
src,
image.Rect(0, 0, int(xmax-xmin+0.5), int(ymax-ymin+0.5)),
}
}
bounds := img.Bounds()
cols := bounds.Dx()
rows := bounds.Dy()
dx := math.Abs(xmax-xmin) / float64(cols)
dy := math.Abs(ymax-ymin) / float64(rows)
return &Image{
img: img,
cols: cols,
rows: rows,
xmin: xmin,
xmax: xmax,
dx: dx,
ymin: ymin,
ymax: ymax,
dy: dy,
}
}
// Plot implements the Plot method of the plot.Plotter interface.
func (img *Image) Plot(c draw.Canvas, p *plot.Plot) {
trX, trY := p.Transforms(&c)
xmin := trX(img.xmin)
ymin := trY(img.ymin)
xmax := trX(img.xmax)
ymax := trY(img.ymax)
rect := vg.Rectangle{
Min: vg.Point{X: xmin, Y: ymin},
Max: vg.Point{X: xmax, Y: ymax},
}
c.DrawImage(rect, img.transformFor(p))
}
// DataRange implements the DataRange method
// of the plot.DataRanger interface.
func (img *Image) DataRange() (xmin, xmax, ymin, ymax float64) {
return img.xmin, img.xmax, img.ymin, img.ymax
}
// GlyphBoxes implements the GlyphBoxes method
// of the plot.GlyphBoxer interface.
func (img *Image) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox {
return nil
}
// transform warps the image to align with non-linear axes.
func (img *Image) transformFor(p *plot.Plot) image.Image {
_, xLinear := p.X.Scale.(plot.LinearScale)
_, yLinear := p.Y.Scale.(plot.LinearScale)
if xLinear && yLinear {
return img.img
}
b := img.img.Bounds()
o := image.NewNRGBA64(b)
for c := range img.cols {
// Find the equivalent image column after applying axis transforms.
cTrans := int(p.X.Norm(img.x(c)) * float64(img.cols))
// Find the equivalent column of the previous image column after applying
// axis transforms.
cPrevTrans := int(p.X.Norm(img.x(maxInt(c-1, 0))) * float64(img.cols))
for r := range img.rows {
// Find the equivalent image row after applying axis transforms.
rTrans := int(p.Y.Norm(img.y(r)) * float64(img.rows))
// Find the equivalent row of the previous image row after applying
// axis transforms.
rPrevTrans := int(p.Y.Norm(img.y(maxInt(r-1, 0))) * float64(img.rows))
crColor := img.img.At(c, img.rows-r-1)
// Set all the pixels in the new image between (cPrevTrans, rPrevTrans)
// and (cTrans, rTrans) to the color at (c,r) in the original image.
// TODO: Improve interpolation.
for cPrime := cPrevTrans; cPrime <= cTrans; cPrime++ {
for rPrime := rPrevTrans; rPrime <= rTrans; rPrime++ {
o.Set(cPrime, img.rows-rPrime-1, crColor)
}
}
}
}
return o
}
func maxInt(a, b int) int {
if a > b {
return a
}
return b
}
func (img *Image) x(c int) float64 {
if c >= img.cols || c < 0 {
panic("plotter/image: illegal range")
}
return img.xmin + float64(c)*img.dx
}
func (img *Image) y(r int) float64 {
if r >= img.rows || r < 0 {
panic("plotter/image: illegal range")
}
return img.ymin + float64(r)*img.dy
}
// uniform is a cropped uniform image.
type uniform struct {
*image.Uniform
rect image.Rectangle
}
func (img uniform) Bounds() image.Rectangle {
return img.rect
}