...

Source file src/gonum.org/v1/plot/plotter/image.go

Documentation: gonum.org/v1/plot/plotter

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

View as plain text