func (l Lines) LineForRead(line int) []Color {
	return l.line(line)
}
LineForRead returns pixels in a given line which can be used for
efficient pixel processing.

You may only read from returned slice. Trying to update the returned slice will
not generate panic, but modified pixels will not be uploaded to AcceleratedImage
when Modify is run. If you want to update the line contents please use LineForWrite
instead.

Please note that returned slice behaves differently than Selection. Line contains only
real pixels and trying to access out-of-bounds pixels generates panic. Therefore
the len of returned slice may be lower than Selection width. The starting offset
can be read by executing Lines.XOffset().

It is not safe to retain returned slice for future use. The image can be modified by
AcceleratedCommand and changes will not be immediately reflected in a slice.
package image
type AcceleratedImage interface {
	Upload(pixels []Color)
	Download(output []Color)
	Width() int
	Height() int
	Delete()
}
func New(acceleratedImage AcceleratedImage) *Image {
	if acceleratedImage == nil {
		panic("nil acceleratedImage")
	}
	width := acceleratedImage.Width()
	height := acceleratedImage.Height()
	if width < 0 {
		panic("negative width")
	}
	if height < 0 {
		panic("negative height")
	}
	return &Image{
		width:            width,
		height:           height,
		heightMinusOne:   height - 1,
		pixels:           make([]Color, width*height),
		acceleratedImage: acceleratedImage,
		selectionsCache:  make([]AcceleratedImageSelection, 0, 4),
	}
}
type Image struct {
	width          int
	height         int
	heightMinusOne int
	pixels                   []Color
	acceleratedImage         AcceleratedImage
	selectionsCache          []AcceleratedImageSelection
	acceleratedImageModified bool
	ramModified              bool
}
func (i *Image) Width() int {
	return i.width
}
func (i *Image) Height() int {
	return i.height
}
func (i *Image) Selection(x int, y int) Selection {
	return Selection{
		x:     x,
		y:     y,
		image: i,
	}
}
func (i *Image) WholeImageSelection() Selection {
	return i.Selection(0, 0).WithSize(i.width, i.height)
}
func (i *Image) Upload() {
	if i.ramModified {
		i.acceleratedImage.Upload(i.pixels)
		i.ramModified = false
	}
}
func (i *Image) Delete() {
	i.acceleratedImage.Delete()
}
type Selection struct {
	image         *Image
	x, y          int
	width, height int
}
func (s Selection) Image() *Image {
	return s.image
}
func (s Selection) Width() int {
	return s.width
}
func (s Selection) Height() int {
	return s.height
}
func (s Selection) ImageX() int {
	return s.x
}
func (s Selection) ImageY() int {
	return s.y
}
func (s Selection) WithSize(width, height int) Selection {
	if width > 0 {
		s.width = width
	} else {
		s.width = 0
	}
	if height > 0 {
		s.height = height
	} else {
		s.height = 0
	}
	return s
}
func (s Selection) Selection(localX, localY int) Selection {
	return Selection{
		x:     localX + s.x,
		y:     localY + s.y,
		image: s.image,
	}
}
func (s Selection) Color(localX, localY int) Color {
	if s.image.acceleratedImageModified {
		s.image.acceleratedImage.Download(s.image.pixels)
		s.image.acceleratedImageModified = false
	}
	x := localX + s.x
	if x < 0 {
		return Transparent
	}
	y := s.image.heightMinusOne - localY - s.y
	if y < 0 {
		return Transparent
	}
	if x >= s.image.width {
		return Transparent
	}
	index := x + y*s.image.width
	if index >= len(s.image.pixels) {
		return Transparent
	}
	return s.image.pixels[index]
}
func (s Selection) SetColor(localX, localY int, color Color) {
	if s.image.acceleratedImageModified {
		s.image.acceleratedImage.Download(s.image.pixels)
		s.image.acceleratedImageModified = false
	}
	x := localX + s.x
	if x < 0 {
		return
	}
	y := s.image.heightMinusOne - localY - s.y
	if y < 0 {
		return
	}
	if x >= s.image.width {
		return
	}
	index := x + y*s.image.width
	if index >= len(s.image.pixels) {
		return
	}
	s.image.ramModified = true
	s.image.pixels[index] = color
}
type AcceleratedImageLocation struct {
	X, Y, Width, Height int
}
type AcceleratedImageSelection struct {
	Location AcceleratedImageLocation
	Image    AcceleratedImage
}
type AcceleratedCommand interface {
	Run(output AcceleratedImageSelection, selections []AcceleratedImageSelection)
}
func (s Selection) Modify(command AcceleratedCommand, selections ...Selection) {
	if command == nil {
		return
	}
	convertedSelections := s.image.selectionsCache[:0]
	for _, selection := range selections {
		selection.image.Upload()
		convertedSelections = append(convertedSelections, selection.toAcceleratedImageSelection())
	}
	s.image.Upload()
	command.Run(s.toAcceleratedImageSelection(), convertedSelections)
	s.image.acceleratedImageModified = true
}
func (s Selection) toAcceleratedImageSelection() AcceleratedImageSelection {
	return AcceleratedImageSelection{
		Location: AcceleratedImageLocation{
			X:      s.x,
			Y:      s.y,
			Width:  s.width,
			Height: s.height,
		},
		Image: s.image.acceleratedImage,
	}
}
func (s Selection) Lines() Lines {
	startLine := s.y
	if startLine < 0 {
		startLine = 0
	}
	endLine := s.y + s.height
	if endLine > s.image.height {
		endLine = s.image.height
	}
	length := endLine - startLine
	if length < 0 {
		length = 0
	}
	yOffset := 0
	if s.y < 0 {
		yOffset = -s.y
	}
	xOffset := 0
	width := s.width
	if s.x < 0 {
		xOffset = -s.x
		width = s.width + s.x
		if width > s.image.width {
			width = s.image.width
		}
	} else if s.x+width > s.image.width {
		width = s.image.width - s.x
	}
	return Lines{
		startY:  s.y,
		startX:  s.x,
		length:  length,
		xOffset: xOffset,
		yOffset: yOffset,
		width:   width,
		image:   s.image,
	}
}
type Lines struct {
	startY  int
	startX  int
	length  int
	xOffset int
	yOffset int
	width   int
	image   *Image
}
func (l Lines) Length() int {
	return l.length
}
func (l Lines) XOffset() int {
	return l.xOffset
}
func (l Lines) YOffset() int {
	return l.yOffset
}
func (l Lines) LineForWrite(line int) []Color {
	pixels := l.line(line)
	l.image.ramModified = true
	return pixels
}