1// Copyright 2009 The Go 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 image implements a basic 2-D image library. 6// 7// The fundamental interface is called [Image]. An [Image] contains colors, which 8// are described in the image/color package. 9// 10// Values of the [Image] interface are created either by calling functions such 11// as [NewRGBA] and [NewPaletted], or by calling [Decode] on an [io.Reader] containing 12// image data in a format such as GIF, JPEG or PNG. Decoding any particular 13// image format requires the prior registration of a decoder function. 14// Registration is typically automatic as a side effect of initializing that 15// format's package so that, to decode a PNG image, it suffices to have 16// 17// import _ "image/png" 18// 19// in a program's main package. The _ means to import a package purely for its 20// initialization side effects. 21// 22// See "The Go image package" for more details: 23// https://golang.org/doc/articles/image_package.html 24// 25// # Security Considerations 26// 27// The image package can be used to parse arbitrarily large images, which can 28// cause resource exhaustion on machines which do not have enough memory to 29// store them. When operating on arbitrary images, [DecodeConfig] should be called 30// before [Decode], so that the program can decide whether the image, as defined 31// in the returned header, can be safely decoded with the available resources. A 32// call to [Decode] which produces an extremely large image, as defined in the 33// header returned by [DecodeConfig], is not considered a security issue, 34// regardless of whether the image is itself malformed or not. A call to 35// [DecodeConfig] which returns a header which does not match the image returned 36// by [Decode] may be considered a security issue, and should be reported per the 37// [Go Security Policy](https://go.dev/security/policy). 38package image 39 40import ( 41 "image/color" 42) 43 44// Config holds an image's color model and dimensions. 45type Config struct { 46 ColorModel color.Model 47 Width, Height int 48} 49 50// Image is a finite rectangular grid of [color.Color] values taken from a color 51// model. 52type Image interface { 53 // ColorModel returns the Image's color model. 54 ColorModel() color.Model 55 // Bounds returns the domain for which At can return non-zero color. 56 // The bounds do not necessarily contain the point (0, 0). 57 Bounds() Rectangle 58 // At returns the color of the pixel at (x, y). 59 // At(Bounds().Min.X, Bounds().Min.Y) returns the upper-left pixel of the grid. 60 // At(Bounds().Max.X-1, Bounds().Max.Y-1) returns the lower-right one. 61 At(x, y int) color.Color 62} 63 64// RGBA64Image is an [Image] whose pixels can be converted directly to a 65// color.RGBA64. 66type RGBA64Image interface { 67 // RGBA64At returns the RGBA64 color of the pixel at (x, y). It is 68 // equivalent to calling At(x, y).RGBA() and converting the resulting 69 // 32-bit return values to a color.RGBA64, but it can avoid allocations 70 // from converting concrete color types to the color.Color interface type. 71 RGBA64At(x, y int) color.RGBA64 72 Image 73} 74 75// PalettedImage is an image whose colors may come from a limited palette. 76// If m is a PalettedImage and m.ColorModel() returns a [color.Palette] p, 77// then m.At(x, y) should be equivalent to p[m.ColorIndexAt(x, y)]. If m's 78// color model is not a color.Palette, then ColorIndexAt's behavior is 79// undefined. 80type PalettedImage interface { 81 // ColorIndexAt returns the palette index of the pixel at (x, y). 82 ColorIndexAt(x, y int) uint8 83 Image 84} 85 86// pixelBufferLength returns the length of the []uint8 typed Pix slice field 87// for the NewXxx functions. Conceptually, this is just (bpp * width * height), 88// but this function panics if at least one of those is negative or if the 89// computation would overflow the int type. 90// 91// This panics instead of returning an error because of backwards 92// compatibility. The NewXxx functions do not return an error. 93func pixelBufferLength(bytesPerPixel int, r Rectangle, imageTypeName string) int { 94 totalLength := mul3NonNeg(bytesPerPixel, r.Dx(), r.Dy()) 95 if totalLength < 0 { 96 panic("image: New" + imageTypeName + " Rectangle has huge or negative dimensions") 97 } 98 return totalLength 99} 100 101// RGBA is an in-memory image whose At method returns [color.RGBA] values. 102type RGBA struct { 103 // Pix holds the image's pixels, in R, G, B, A order. The pixel at 104 // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*4]. 105 Pix []uint8 106 // Stride is the Pix stride (in bytes) between vertically adjacent pixels. 107 Stride int 108 // Rect is the image's bounds. 109 Rect Rectangle 110} 111 112func (p *RGBA) ColorModel() color.Model { return color.RGBAModel } 113 114func (p *RGBA) Bounds() Rectangle { return p.Rect } 115 116func (p *RGBA) At(x, y int) color.Color { 117 return p.RGBAAt(x, y) 118} 119 120func (p *RGBA) RGBA64At(x, y int) color.RGBA64 { 121 if !(Point{x, y}.In(p.Rect)) { 122 return color.RGBA64{} 123 } 124 i := p.PixOffset(x, y) 125 s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 126 r := uint16(s[0]) 127 g := uint16(s[1]) 128 b := uint16(s[2]) 129 a := uint16(s[3]) 130 return color.RGBA64{ 131 (r << 8) | r, 132 (g << 8) | g, 133 (b << 8) | b, 134 (a << 8) | a, 135 } 136} 137 138func (p *RGBA) RGBAAt(x, y int) color.RGBA { 139 if !(Point{x, y}.In(p.Rect)) { 140 return color.RGBA{} 141 } 142 i := p.PixOffset(x, y) 143 s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 144 return color.RGBA{s[0], s[1], s[2], s[3]} 145} 146 147// PixOffset returns the index of the first element of Pix that corresponds to 148// the pixel at (x, y). 149func (p *RGBA) PixOffset(x, y int) int { 150 return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4 151} 152 153func (p *RGBA) Set(x, y int, c color.Color) { 154 if !(Point{x, y}.In(p.Rect)) { 155 return 156 } 157 i := p.PixOffset(x, y) 158 c1 := color.RGBAModel.Convert(c).(color.RGBA) 159 s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 160 s[0] = c1.R 161 s[1] = c1.G 162 s[2] = c1.B 163 s[3] = c1.A 164} 165 166func (p *RGBA) SetRGBA64(x, y int, c color.RGBA64) { 167 if !(Point{x, y}.In(p.Rect)) { 168 return 169 } 170 i := p.PixOffset(x, y) 171 s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 172 s[0] = uint8(c.R >> 8) 173 s[1] = uint8(c.G >> 8) 174 s[2] = uint8(c.B >> 8) 175 s[3] = uint8(c.A >> 8) 176} 177 178func (p *RGBA) SetRGBA(x, y int, c color.RGBA) { 179 if !(Point{x, y}.In(p.Rect)) { 180 return 181 } 182 i := p.PixOffset(x, y) 183 s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 184 s[0] = c.R 185 s[1] = c.G 186 s[2] = c.B 187 s[3] = c.A 188} 189 190// SubImage returns an image representing the portion of the image p visible 191// through r. The returned value shares pixels with the original image. 192func (p *RGBA) SubImage(r Rectangle) Image { 193 r = r.Intersect(p.Rect) 194 // If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside 195 // either r1 or r2 if the intersection is empty. Without explicitly checking for 196 // this, the Pix[i:] expression below can panic. 197 if r.Empty() { 198 return &RGBA{} 199 } 200 i := p.PixOffset(r.Min.X, r.Min.Y) 201 return &RGBA{ 202 Pix: p.Pix[i:], 203 Stride: p.Stride, 204 Rect: r, 205 } 206} 207 208// Opaque scans the entire image and reports whether it is fully opaque. 209func (p *RGBA) Opaque() bool { 210 if p.Rect.Empty() { 211 return true 212 } 213 i0, i1 := 3, p.Rect.Dx()*4 214 for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { 215 for i := i0; i < i1; i += 4 { 216 if p.Pix[i] != 0xff { 217 return false 218 } 219 } 220 i0 += p.Stride 221 i1 += p.Stride 222 } 223 return true 224} 225 226// NewRGBA returns a new [RGBA] image with the given bounds. 227func NewRGBA(r Rectangle) *RGBA { 228 return &RGBA{ 229 Pix: make([]uint8, pixelBufferLength(4, r, "RGBA")), 230 Stride: 4 * r.Dx(), 231 Rect: r, 232 } 233} 234 235// RGBA64 is an in-memory image whose At method returns [color.RGBA64] values. 236type RGBA64 struct { 237 // Pix holds the image's pixels, in R, G, B, A order and big-endian format. The pixel at 238 // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*8]. 239 Pix []uint8 240 // Stride is the Pix stride (in bytes) between vertically adjacent pixels. 241 Stride int 242 // Rect is the image's bounds. 243 Rect Rectangle 244} 245 246func (p *RGBA64) ColorModel() color.Model { return color.RGBA64Model } 247 248func (p *RGBA64) Bounds() Rectangle { return p.Rect } 249 250func (p *RGBA64) At(x, y int) color.Color { 251 return p.RGBA64At(x, y) 252} 253 254func (p *RGBA64) RGBA64At(x, y int) color.RGBA64 { 255 if !(Point{x, y}.In(p.Rect)) { 256 return color.RGBA64{} 257 } 258 i := p.PixOffset(x, y) 259 s := p.Pix[i : i+8 : i+8] // Small cap improves performance, see https://golang.org/issue/27857 260 return color.RGBA64{ 261 uint16(s[0])<<8 | uint16(s[1]), 262 uint16(s[2])<<8 | uint16(s[3]), 263 uint16(s[4])<<8 | uint16(s[5]), 264 uint16(s[6])<<8 | uint16(s[7]), 265 } 266} 267 268// PixOffset returns the index of the first element of Pix that corresponds to 269// the pixel at (x, y). 270func (p *RGBA64) PixOffset(x, y int) int { 271 return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*8 272} 273 274func (p *RGBA64) Set(x, y int, c color.Color) { 275 if !(Point{x, y}.In(p.Rect)) { 276 return 277 } 278 i := p.PixOffset(x, y) 279 c1 := color.RGBA64Model.Convert(c).(color.RGBA64) 280 s := p.Pix[i : i+8 : i+8] // Small cap improves performance, see https://golang.org/issue/27857 281 s[0] = uint8(c1.R >> 8) 282 s[1] = uint8(c1.R) 283 s[2] = uint8(c1.G >> 8) 284 s[3] = uint8(c1.G) 285 s[4] = uint8(c1.B >> 8) 286 s[5] = uint8(c1.B) 287 s[6] = uint8(c1.A >> 8) 288 s[7] = uint8(c1.A) 289} 290 291func (p *RGBA64) SetRGBA64(x, y int, c color.RGBA64) { 292 if !(Point{x, y}.In(p.Rect)) { 293 return 294 } 295 i := p.PixOffset(x, y) 296 s := p.Pix[i : i+8 : i+8] // Small cap improves performance, see https://golang.org/issue/27857 297 s[0] = uint8(c.R >> 8) 298 s[1] = uint8(c.R) 299 s[2] = uint8(c.G >> 8) 300 s[3] = uint8(c.G) 301 s[4] = uint8(c.B >> 8) 302 s[5] = uint8(c.B) 303 s[6] = uint8(c.A >> 8) 304 s[7] = uint8(c.A) 305} 306 307// SubImage returns an image representing the portion of the image p visible 308// through r. The returned value shares pixels with the original image. 309func (p *RGBA64) SubImage(r Rectangle) Image { 310 r = r.Intersect(p.Rect) 311 // If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside 312 // either r1 or r2 if the intersection is empty. Without explicitly checking for 313 // this, the Pix[i:] expression below can panic. 314 if r.Empty() { 315 return &RGBA64{} 316 } 317 i := p.PixOffset(r.Min.X, r.Min.Y) 318 return &RGBA64{ 319 Pix: p.Pix[i:], 320 Stride: p.Stride, 321 Rect: r, 322 } 323} 324 325// Opaque scans the entire image and reports whether it is fully opaque. 326func (p *RGBA64) Opaque() bool { 327 if p.Rect.Empty() { 328 return true 329 } 330 i0, i1 := 6, p.Rect.Dx()*8 331 for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { 332 for i := i0; i < i1; i += 8 { 333 if p.Pix[i+0] != 0xff || p.Pix[i+1] != 0xff { 334 return false 335 } 336 } 337 i0 += p.Stride 338 i1 += p.Stride 339 } 340 return true 341} 342 343// NewRGBA64 returns a new [RGBA64] image with the given bounds. 344func NewRGBA64(r Rectangle) *RGBA64 { 345 return &RGBA64{ 346 Pix: make([]uint8, pixelBufferLength(8, r, "RGBA64")), 347 Stride: 8 * r.Dx(), 348 Rect: r, 349 } 350} 351 352// NRGBA is an in-memory image whose At method returns [color.NRGBA] values. 353type NRGBA struct { 354 // Pix holds the image's pixels, in R, G, B, A order. The pixel at 355 // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*4]. 356 Pix []uint8 357 // Stride is the Pix stride (in bytes) between vertically adjacent pixels. 358 Stride int 359 // Rect is the image's bounds. 360 Rect Rectangle 361} 362 363func (p *NRGBA) ColorModel() color.Model { return color.NRGBAModel } 364 365func (p *NRGBA) Bounds() Rectangle { return p.Rect } 366 367func (p *NRGBA) At(x, y int) color.Color { 368 return p.NRGBAAt(x, y) 369} 370 371func (p *NRGBA) RGBA64At(x, y int) color.RGBA64 { 372 r, g, b, a := p.NRGBAAt(x, y).RGBA() 373 return color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)} 374} 375 376func (p *NRGBA) NRGBAAt(x, y int) color.NRGBA { 377 if !(Point{x, y}.In(p.Rect)) { 378 return color.NRGBA{} 379 } 380 i := p.PixOffset(x, y) 381 s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 382 return color.NRGBA{s[0], s[1], s[2], s[3]} 383} 384 385// PixOffset returns the index of the first element of Pix that corresponds to 386// the pixel at (x, y). 387func (p *NRGBA) PixOffset(x, y int) int { 388 return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4 389} 390 391func (p *NRGBA) Set(x, y int, c color.Color) { 392 if !(Point{x, y}.In(p.Rect)) { 393 return 394 } 395 i := p.PixOffset(x, y) 396 c1 := color.NRGBAModel.Convert(c).(color.NRGBA) 397 s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 398 s[0] = c1.R 399 s[1] = c1.G 400 s[2] = c1.B 401 s[3] = c1.A 402} 403 404func (p *NRGBA) SetRGBA64(x, y int, c color.RGBA64) { 405 if !(Point{x, y}.In(p.Rect)) { 406 return 407 } 408 r, g, b, a := uint32(c.R), uint32(c.G), uint32(c.B), uint32(c.A) 409 if (a != 0) && (a != 0xffff) { 410 r = (r * 0xffff) / a 411 g = (g * 0xffff) / a 412 b = (b * 0xffff) / a 413 } 414 i := p.PixOffset(x, y) 415 s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 416 s[0] = uint8(r >> 8) 417 s[1] = uint8(g >> 8) 418 s[2] = uint8(b >> 8) 419 s[3] = uint8(a >> 8) 420} 421 422func (p *NRGBA) SetNRGBA(x, y int, c color.NRGBA) { 423 if !(Point{x, y}.In(p.Rect)) { 424 return 425 } 426 i := p.PixOffset(x, y) 427 s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 428 s[0] = c.R 429 s[1] = c.G 430 s[2] = c.B 431 s[3] = c.A 432} 433 434// SubImage returns an image representing the portion of the image p visible 435// through r. The returned value shares pixels with the original image. 436func (p *NRGBA) SubImage(r Rectangle) Image { 437 r = r.Intersect(p.Rect) 438 // If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside 439 // either r1 or r2 if the intersection is empty. Without explicitly checking for 440 // this, the Pix[i:] expression below can panic. 441 if r.Empty() { 442 return &NRGBA{} 443 } 444 i := p.PixOffset(r.Min.X, r.Min.Y) 445 return &NRGBA{ 446 Pix: p.Pix[i:], 447 Stride: p.Stride, 448 Rect: r, 449 } 450} 451 452// Opaque scans the entire image and reports whether it is fully opaque. 453func (p *NRGBA) Opaque() bool { 454 if p.Rect.Empty() { 455 return true 456 } 457 i0, i1 := 3, p.Rect.Dx()*4 458 for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { 459 for i := i0; i < i1; i += 4 { 460 if p.Pix[i] != 0xff { 461 return false 462 } 463 } 464 i0 += p.Stride 465 i1 += p.Stride 466 } 467 return true 468} 469 470// NewNRGBA returns a new [NRGBA] image with the given bounds. 471func NewNRGBA(r Rectangle) *NRGBA { 472 return &NRGBA{ 473 Pix: make([]uint8, pixelBufferLength(4, r, "NRGBA")), 474 Stride: 4 * r.Dx(), 475 Rect: r, 476 } 477} 478 479// NRGBA64 is an in-memory image whose At method returns [color.NRGBA64] values. 480type NRGBA64 struct { 481 // Pix holds the image's pixels, in R, G, B, A order and big-endian format. The pixel at 482 // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*8]. 483 Pix []uint8 484 // Stride is the Pix stride (in bytes) between vertically adjacent pixels. 485 Stride int 486 // Rect is the image's bounds. 487 Rect Rectangle 488} 489 490func (p *NRGBA64) ColorModel() color.Model { return color.NRGBA64Model } 491 492func (p *NRGBA64) Bounds() Rectangle { return p.Rect } 493 494func (p *NRGBA64) At(x, y int) color.Color { 495 return p.NRGBA64At(x, y) 496} 497 498func (p *NRGBA64) RGBA64At(x, y int) color.RGBA64 { 499 r, g, b, a := p.NRGBA64At(x, y).RGBA() 500 return color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)} 501} 502 503func (p *NRGBA64) NRGBA64At(x, y int) color.NRGBA64 { 504 if !(Point{x, y}.In(p.Rect)) { 505 return color.NRGBA64{} 506 } 507 i := p.PixOffset(x, y) 508 s := p.Pix[i : i+8 : i+8] // Small cap improves performance, see https://golang.org/issue/27857 509 return color.NRGBA64{ 510 uint16(s[0])<<8 | uint16(s[1]), 511 uint16(s[2])<<8 | uint16(s[3]), 512 uint16(s[4])<<8 | uint16(s[5]), 513 uint16(s[6])<<8 | uint16(s[7]), 514 } 515} 516 517// PixOffset returns the index of the first element of Pix that corresponds to 518// the pixel at (x, y). 519func (p *NRGBA64) PixOffset(x, y int) int { 520 return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*8 521} 522 523func (p *NRGBA64) Set(x, y int, c color.Color) { 524 if !(Point{x, y}.In(p.Rect)) { 525 return 526 } 527 i := p.PixOffset(x, y) 528 c1 := color.NRGBA64Model.Convert(c).(color.NRGBA64) 529 s := p.Pix[i : i+8 : i+8] // Small cap improves performance, see https://golang.org/issue/27857 530 s[0] = uint8(c1.R >> 8) 531 s[1] = uint8(c1.R) 532 s[2] = uint8(c1.G >> 8) 533 s[3] = uint8(c1.G) 534 s[4] = uint8(c1.B >> 8) 535 s[5] = uint8(c1.B) 536 s[6] = uint8(c1.A >> 8) 537 s[7] = uint8(c1.A) 538} 539 540func (p *NRGBA64) SetRGBA64(x, y int, c color.RGBA64) { 541 if !(Point{x, y}.In(p.Rect)) { 542 return 543 } 544 r, g, b, a := uint32(c.R), uint32(c.G), uint32(c.B), uint32(c.A) 545 if (a != 0) && (a != 0xffff) { 546 r = (r * 0xffff) / a 547 g = (g * 0xffff) / a 548 b = (b * 0xffff) / a 549 } 550 i := p.PixOffset(x, y) 551 s := p.Pix[i : i+8 : i+8] // Small cap improves performance, see https://golang.org/issue/27857 552 s[0] = uint8(r >> 8) 553 s[1] = uint8(r) 554 s[2] = uint8(g >> 8) 555 s[3] = uint8(g) 556 s[4] = uint8(b >> 8) 557 s[5] = uint8(b) 558 s[6] = uint8(a >> 8) 559 s[7] = uint8(a) 560} 561 562func (p *NRGBA64) SetNRGBA64(x, y int, c color.NRGBA64) { 563 if !(Point{x, y}.In(p.Rect)) { 564 return 565 } 566 i := p.PixOffset(x, y) 567 s := p.Pix[i : i+8 : i+8] // Small cap improves performance, see https://golang.org/issue/27857 568 s[0] = uint8(c.R >> 8) 569 s[1] = uint8(c.R) 570 s[2] = uint8(c.G >> 8) 571 s[3] = uint8(c.G) 572 s[4] = uint8(c.B >> 8) 573 s[5] = uint8(c.B) 574 s[6] = uint8(c.A >> 8) 575 s[7] = uint8(c.A) 576} 577 578// SubImage returns an image representing the portion of the image p visible 579// through r. The returned value shares pixels with the original image. 580func (p *NRGBA64) SubImage(r Rectangle) Image { 581 r = r.Intersect(p.Rect) 582 // If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside 583 // either r1 or r2 if the intersection is empty. Without explicitly checking for 584 // this, the Pix[i:] expression below can panic. 585 if r.Empty() { 586 return &NRGBA64{} 587 } 588 i := p.PixOffset(r.Min.X, r.Min.Y) 589 return &NRGBA64{ 590 Pix: p.Pix[i:], 591 Stride: p.Stride, 592 Rect: r, 593 } 594} 595 596// Opaque scans the entire image and reports whether it is fully opaque. 597func (p *NRGBA64) Opaque() bool { 598 if p.Rect.Empty() { 599 return true 600 } 601 i0, i1 := 6, p.Rect.Dx()*8 602 for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { 603 for i := i0; i < i1; i += 8 { 604 if p.Pix[i+0] != 0xff || p.Pix[i+1] != 0xff { 605 return false 606 } 607 } 608 i0 += p.Stride 609 i1 += p.Stride 610 } 611 return true 612} 613 614// NewNRGBA64 returns a new [NRGBA64] image with the given bounds. 615func NewNRGBA64(r Rectangle) *NRGBA64 { 616 return &NRGBA64{ 617 Pix: make([]uint8, pixelBufferLength(8, r, "NRGBA64")), 618 Stride: 8 * r.Dx(), 619 Rect: r, 620 } 621} 622 623// Alpha is an in-memory image whose At method returns [color.Alpha] values. 624type Alpha struct { 625 // Pix holds the image's pixels, as alpha values. The pixel at 626 // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*1]. 627 Pix []uint8 628 // Stride is the Pix stride (in bytes) between vertically adjacent pixels. 629 Stride int 630 // Rect is the image's bounds. 631 Rect Rectangle 632} 633 634func (p *Alpha) ColorModel() color.Model { return color.AlphaModel } 635 636func (p *Alpha) Bounds() Rectangle { return p.Rect } 637 638func (p *Alpha) At(x, y int) color.Color { 639 return p.AlphaAt(x, y) 640} 641 642func (p *Alpha) RGBA64At(x, y int) color.RGBA64 { 643 a := uint16(p.AlphaAt(x, y).A) 644 a |= a << 8 645 return color.RGBA64{a, a, a, a} 646} 647 648func (p *Alpha) AlphaAt(x, y int) color.Alpha { 649 if !(Point{x, y}.In(p.Rect)) { 650 return color.Alpha{} 651 } 652 i := p.PixOffset(x, y) 653 return color.Alpha{p.Pix[i]} 654} 655 656// PixOffset returns the index of the first element of Pix that corresponds to 657// the pixel at (x, y). 658func (p *Alpha) PixOffset(x, y int) int { 659 return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*1 660} 661 662func (p *Alpha) Set(x, y int, c color.Color) { 663 if !(Point{x, y}.In(p.Rect)) { 664 return 665 } 666 i := p.PixOffset(x, y) 667 p.Pix[i] = color.AlphaModel.Convert(c).(color.Alpha).A 668} 669 670func (p *Alpha) SetRGBA64(x, y int, c color.RGBA64) { 671 if !(Point{x, y}.In(p.Rect)) { 672 return 673 } 674 i := p.PixOffset(x, y) 675 p.Pix[i] = uint8(c.A >> 8) 676} 677 678func (p *Alpha) SetAlpha(x, y int, c color.Alpha) { 679 if !(Point{x, y}.In(p.Rect)) { 680 return 681 } 682 i := p.PixOffset(x, y) 683 p.Pix[i] = c.A 684} 685 686// SubImage returns an image representing the portion of the image p visible 687// through r. The returned value shares pixels with the original image. 688func (p *Alpha) SubImage(r Rectangle) Image { 689 r = r.Intersect(p.Rect) 690 // If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside 691 // either r1 or r2 if the intersection is empty. Without explicitly checking for 692 // this, the Pix[i:] expression below can panic. 693 if r.Empty() { 694 return &Alpha{} 695 } 696 i := p.PixOffset(r.Min.X, r.Min.Y) 697 return &Alpha{ 698 Pix: p.Pix[i:], 699 Stride: p.Stride, 700 Rect: r, 701 } 702} 703 704// Opaque scans the entire image and reports whether it is fully opaque. 705func (p *Alpha) Opaque() bool { 706 if p.Rect.Empty() { 707 return true 708 } 709 i0, i1 := 0, p.Rect.Dx() 710 for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { 711 for i := i0; i < i1; i++ { 712 if p.Pix[i] != 0xff { 713 return false 714 } 715 } 716 i0 += p.Stride 717 i1 += p.Stride 718 } 719 return true 720} 721 722// NewAlpha returns a new [Alpha] image with the given bounds. 723func NewAlpha(r Rectangle) *Alpha { 724 return &Alpha{ 725 Pix: make([]uint8, pixelBufferLength(1, r, "Alpha")), 726 Stride: 1 * r.Dx(), 727 Rect: r, 728 } 729} 730 731// Alpha16 is an in-memory image whose At method returns [color.Alpha16] values. 732type Alpha16 struct { 733 // Pix holds the image's pixels, as alpha values in big-endian format. The pixel at 734 // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*2]. 735 Pix []uint8 736 // Stride is the Pix stride (in bytes) between vertically adjacent pixels. 737 Stride int 738 // Rect is the image's bounds. 739 Rect Rectangle 740} 741 742func (p *Alpha16) ColorModel() color.Model { return color.Alpha16Model } 743 744func (p *Alpha16) Bounds() Rectangle { return p.Rect } 745 746func (p *Alpha16) At(x, y int) color.Color { 747 return p.Alpha16At(x, y) 748} 749 750func (p *Alpha16) RGBA64At(x, y int) color.RGBA64 { 751 a := p.Alpha16At(x, y).A 752 return color.RGBA64{a, a, a, a} 753} 754 755func (p *Alpha16) Alpha16At(x, y int) color.Alpha16 { 756 if !(Point{x, y}.In(p.Rect)) { 757 return color.Alpha16{} 758 } 759 i := p.PixOffset(x, y) 760 return color.Alpha16{uint16(p.Pix[i+0])<<8 | uint16(p.Pix[i+1])} 761} 762 763// PixOffset returns the index of the first element of Pix that corresponds to 764// the pixel at (x, y). 765func (p *Alpha16) PixOffset(x, y int) int { 766 return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*2 767} 768 769func (p *Alpha16) Set(x, y int, c color.Color) { 770 if !(Point{x, y}.In(p.Rect)) { 771 return 772 } 773 i := p.PixOffset(x, y) 774 c1 := color.Alpha16Model.Convert(c).(color.Alpha16) 775 p.Pix[i+0] = uint8(c1.A >> 8) 776 p.Pix[i+1] = uint8(c1.A) 777} 778 779func (p *Alpha16) SetRGBA64(x, y int, c color.RGBA64) { 780 if !(Point{x, y}.In(p.Rect)) { 781 return 782 } 783 i := p.PixOffset(x, y) 784 p.Pix[i+0] = uint8(c.A >> 8) 785 p.Pix[i+1] = uint8(c.A) 786} 787 788func (p *Alpha16) SetAlpha16(x, y int, c color.Alpha16) { 789 if !(Point{x, y}.In(p.Rect)) { 790 return 791 } 792 i := p.PixOffset(x, y) 793 p.Pix[i+0] = uint8(c.A >> 8) 794 p.Pix[i+1] = uint8(c.A) 795} 796 797// SubImage returns an image representing the portion of the image p visible 798// through r. The returned value shares pixels with the original image. 799func (p *Alpha16) SubImage(r Rectangle) Image { 800 r = r.Intersect(p.Rect) 801 // If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside 802 // either r1 or r2 if the intersection is empty. Without explicitly checking for 803 // this, the Pix[i:] expression below can panic. 804 if r.Empty() { 805 return &Alpha16{} 806 } 807 i := p.PixOffset(r.Min.X, r.Min.Y) 808 return &Alpha16{ 809 Pix: p.Pix[i:], 810 Stride: p.Stride, 811 Rect: r, 812 } 813} 814 815// Opaque scans the entire image and reports whether it is fully opaque. 816func (p *Alpha16) Opaque() bool { 817 if p.Rect.Empty() { 818 return true 819 } 820 i0, i1 := 0, p.Rect.Dx()*2 821 for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { 822 for i := i0; i < i1; i += 2 { 823 if p.Pix[i+0] != 0xff || p.Pix[i+1] != 0xff { 824 return false 825 } 826 } 827 i0 += p.Stride 828 i1 += p.Stride 829 } 830 return true 831} 832 833// NewAlpha16 returns a new [Alpha16] image with the given bounds. 834func NewAlpha16(r Rectangle) *Alpha16 { 835 return &Alpha16{ 836 Pix: make([]uint8, pixelBufferLength(2, r, "Alpha16")), 837 Stride: 2 * r.Dx(), 838 Rect: r, 839 } 840} 841 842// Gray is an in-memory image whose At method returns [color.Gray] values. 843type Gray struct { 844 // Pix holds the image's pixels, as gray values. The pixel at 845 // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*1]. 846 Pix []uint8 847 // Stride is the Pix stride (in bytes) between vertically adjacent pixels. 848 Stride int 849 // Rect is the image's bounds. 850 Rect Rectangle 851} 852 853func (p *Gray) ColorModel() color.Model { return color.GrayModel } 854 855func (p *Gray) Bounds() Rectangle { return p.Rect } 856 857func (p *Gray) At(x, y int) color.Color { 858 return p.GrayAt(x, y) 859} 860 861func (p *Gray) RGBA64At(x, y int) color.RGBA64 { 862 gray := uint16(p.GrayAt(x, y).Y) 863 gray |= gray << 8 864 return color.RGBA64{gray, gray, gray, 0xffff} 865} 866 867func (p *Gray) GrayAt(x, y int) color.Gray { 868 if !(Point{x, y}.In(p.Rect)) { 869 return color.Gray{} 870 } 871 i := p.PixOffset(x, y) 872 return color.Gray{p.Pix[i]} 873} 874 875// PixOffset returns the index of the first element of Pix that corresponds to 876// the pixel at (x, y). 877func (p *Gray) PixOffset(x, y int) int { 878 return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*1 879} 880 881func (p *Gray) Set(x, y int, c color.Color) { 882 if !(Point{x, y}.In(p.Rect)) { 883 return 884 } 885 i := p.PixOffset(x, y) 886 p.Pix[i] = color.GrayModel.Convert(c).(color.Gray).Y 887} 888 889func (p *Gray) SetRGBA64(x, y int, c color.RGBA64) { 890 if !(Point{x, y}.In(p.Rect)) { 891 return 892 } 893 // This formula is the same as in color.grayModel. 894 gray := (19595*uint32(c.R) + 38470*uint32(c.G) + 7471*uint32(c.B) + 1<<15) >> 24 895 i := p.PixOffset(x, y) 896 p.Pix[i] = uint8(gray) 897} 898 899func (p *Gray) SetGray(x, y int, c color.Gray) { 900 if !(Point{x, y}.In(p.Rect)) { 901 return 902 } 903 i := p.PixOffset(x, y) 904 p.Pix[i] = c.Y 905} 906 907// SubImage returns an image representing the portion of the image p visible 908// through r. The returned value shares pixels with the original image. 909func (p *Gray) SubImage(r Rectangle) Image { 910 r = r.Intersect(p.Rect) 911 // If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside 912 // either r1 or r2 if the intersection is empty. Without explicitly checking for 913 // this, the Pix[i:] expression below can panic. 914 if r.Empty() { 915 return &Gray{} 916 } 917 i := p.PixOffset(r.Min.X, r.Min.Y) 918 return &Gray{ 919 Pix: p.Pix[i:], 920 Stride: p.Stride, 921 Rect: r, 922 } 923} 924 925// Opaque scans the entire image and reports whether it is fully opaque. 926func (p *Gray) Opaque() bool { 927 return true 928} 929 930// NewGray returns a new [Gray] image with the given bounds. 931func NewGray(r Rectangle) *Gray { 932 return &Gray{ 933 Pix: make([]uint8, pixelBufferLength(1, r, "Gray")), 934 Stride: 1 * r.Dx(), 935 Rect: r, 936 } 937} 938 939// Gray16 is an in-memory image whose At method returns [color.Gray16] values. 940type Gray16 struct { 941 // Pix holds the image's pixels, as gray values in big-endian format. The pixel at 942 // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*2]. 943 Pix []uint8 944 // Stride is the Pix stride (in bytes) between vertically adjacent pixels. 945 Stride int 946 // Rect is the image's bounds. 947 Rect Rectangle 948} 949 950func (p *Gray16) ColorModel() color.Model { return color.Gray16Model } 951 952func (p *Gray16) Bounds() Rectangle { return p.Rect } 953 954func (p *Gray16) At(x, y int) color.Color { 955 return p.Gray16At(x, y) 956} 957 958func (p *Gray16) RGBA64At(x, y int) color.RGBA64 { 959 gray := p.Gray16At(x, y).Y 960 return color.RGBA64{gray, gray, gray, 0xffff} 961} 962 963func (p *Gray16) Gray16At(x, y int) color.Gray16 { 964 if !(Point{x, y}.In(p.Rect)) { 965 return color.Gray16{} 966 } 967 i := p.PixOffset(x, y) 968 return color.Gray16{uint16(p.Pix[i+0])<<8 | uint16(p.Pix[i+1])} 969} 970 971// PixOffset returns the index of the first element of Pix that corresponds to 972// the pixel at (x, y). 973func (p *Gray16) PixOffset(x, y int) int { 974 return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*2 975} 976 977func (p *Gray16) Set(x, y int, c color.Color) { 978 if !(Point{x, y}.In(p.Rect)) { 979 return 980 } 981 i := p.PixOffset(x, y) 982 c1 := color.Gray16Model.Convert(c).(color.Gray16) 983 p.Pix[i+0] = uint8(c1.Y >> 8) 984 p.Pix[i+1] = uint8(c1.Y) 985} 986 987func (p *Gray16) SetRGBA64(x, y int, c color.RGBA64) { 988 if !(Point{x, y}.In(p.Rect)) { 989 return 990 } 991 // This formula is the same as in color.gray16Model. 992 gray := (19595*uint32(c.R) + 38470*uint32(c.G) + 7471*uint32(c.B) + 1<<15) >> 16 993 i := p.PixOffset(x, y) 994 p.Pix[i+0] = uint8(gray >> 8) 995 p.Pix[i+1] = uint8(gray) 996} 997 998func (p *Gray16) SetGray16(x, y int, c color.Gray16) { 999 if !(Point{x, y}.In(p.Rect)) { 1000 return 1001 } 1002 i := p.PixOffset(x, y) 1003 p.Pix[i+0] = uint8(c.Y >> 8) 1004 p.Pix[i+1] = uint8(c.Y) 1005} 1006 1007// SubImage returns an image representing the portion of the image p visible 1008// through r. The returned value shares pixels with the original image. 1009func (p *Gray16) SubImage(r Rectangle) Image { 1010 r = r.Intersect(p.Rect) 1011 // If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside 1012 // either r1 or r2 if the intersection is empty. Without explicitly checking for 1013 // this, the Pix[i:] expression below can panic. 1014 if r.Empty() { 1015 return &Gray16{} 1016 } 1017 i := p.PixOffset(r.Min.X, r.Min.Y) 1018 return &Gray16{ 1019 Pix: p.Pix[i:], 1020 Stride: p.Stride, 1021 Rect: r, 1022 } 1023} 1024 1025// Opaque scans the entire image and reports whether it is fully opaque. 1026func (p *Gray16) Opaque() bool { 1027 return true 1028} 1029 1030// NewGray16 returns a new [Gray16] image with the given bounds. 1031func NewGray16(r Rectangle) *Gray16 { 1032 return &Gray16{ 1033 Pix: make([]uint8, pixelBufferLength(2, r, "Gray16")), 1034 Stride: 2 * r.Dx(), 1035 Rect: r, 1036 } 1037} 1038 1039// CMYK is an in-memory image whose At method returns [color.CMYK] values. 1040type CMYK struct { 1041 // Pix holds the image's pixels, in C, M, Y, K order. The pixel at 1042 // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*4]. 1043 Pix []uint8 1044 // Stride is the Pix stride (in bytes) between vertically adjacent pixels. 1045 Stride int 1046 // Rect is the image's bounds. 1047 Rect Rectangle 1048} 1049 1050func (p *CMYK) ColorModel() color.Model { return color.CMYKModel } 1051 1052func (p *CMYK) Bounds() Rectangle { return p.Rect } 1053 1054func (p *CMYK) At(x, y int) color.Color { 1055 return p.CMYKAt(x, y) 1056} 1057 1058func (p *CMYK) RGBA64At(x, y int) color.RGBA64 { 1059 r, g, b, a := p.CMYKAt(x, y).RGBA() 1060 return color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)} 1061} 1062 1063func (p *CMYK) CMYKAt(x, y int) color.CMYK { 1064 if !(Point{x, y}.In(p.Rect)) { 1065 return color.CMYK{} 1066 } 1067 i := p.PixOffset(x, y) 1068 s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 1069 return color.CMYK{s[0], s[1], s[2], s[3]} 1070} 1071 1072// PixOffset returns the index of the first element of Pix that corresponds to 1073// the pixel at (x, y). 1074func (p *CMYK) PixOffset(x, y int) int { 1075 return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4 1076} 1077 1078func (p *CMYK) Set(x, y int, c color.Color) { 1079 if !(Point{x, y}.In(p.Rect)) { 1080 return 1081 } 1082 i := p.PixOffset(x, y) 1083 c1 := color.CMYKModel.Convert(c).(color.CMYK) 1084 s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 1085 s[0] = c1.C 1086 s[1] = c1.M 1087 s[2] = c1.Y 1088 s[3] = c1.K 1089} 1090 1091func (p *CMYK) SetRGBA64(x, y int, c color.RGBA64) { 1092 if !(Point{x, y}.In(p.Rect)) { 1093 return 1094 } 1095 cc, mm, yy, kk := color.RGBToCMYK(uint8(c.R>>8), uint8(c.G>>8), uint8(c.B>>8)) 1096 i := p.PixOffset(x, y) 1097 s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 1098 s[0] = cc 1099 s[1] = mm 1100 s[2] = yy 1101 s[3] = kk 1102} 1103 1104func (p *CMYK) SetCMYK(x, y int, c color.CMYK) { 1105 if !(Point{x, y}.In(p.Rect)) { 1106 return 1107 } 1108 i := p.PixOffset(x, y) 1109 s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 1110 s[0] = c.C 1111 s[1] = c.M 1112 s[2] = c.Y 1113 s[3] = c.K 1114} 1115 1116// SubImage returns an image representing the portion of the image p visible 1117// through r. The returned value shares pixels with the original image. 1118func (p *CMYK) SubImage(r Rectangle) Image { 1119 r = r.Intersect(p.Rect) 1120 // If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside 1121 // either r1 or r2 if the intersection is empty. Without explicitly checking for 1122 // this, the Pix[i:] expression below can panic. 1123 if r.Empty() { 1124 return &CMYK{} 1125 } 1126 i := p.PixOffset(r.Min.X, r.Min.Y) 1127 return &CMYK{ 1128 Pix: p.Pix[i:], 1129 Stride: p.Stride, 1130 Rect: r, 1131 } 1132} 1133 1134// Opaque scans the entire image and reports whether it is fully opaque. 1135func (p *CMYK) Opaque() bool { 1136 return true 1137} 1138 1139// NewCMYK returns a new CMYK image with the given bounds. 1140func NewCMYK(r Rectangle) *CMYK { 1141 return &CMYK{ 1142 Pix: make([]uint8, pixelBufferLength(4, r, "CMYK")), 1143 Stride: 4 * r.Dx(), 1144 Rect: r, 1145 } 1146} 1147 1148// Paletted is an in-memory image of uint8 indices into a given palette. 1149type Paletted struct { 1150 // Pix holds the image's pixels, as palette indices. The pixel at 1151 // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*1]. 1152 Pix []uint8 1153 // Stride is the Pix stride (in bytes) between vertically adjacent pixels. 1154 Stride int 1155 // Rect is the image's bounds. 1156 Rect Rectangle 1157 // Palette is the image's palette. 1158 Palette color.Palette 1159} 1160 1161func (p *Paletted) ColorModel() color.Model { return p.Palette } 1162 1163func (p *Paletted) Bounds() Rectangle { return p.Rect } 1164 1165func (p *Paletted) At(x, y int) color.Color { 1166 if len(p.Palette) == 0 { 1167 return nil 1168 } 1169 if !(Point{x, y}.In(p.Rect)) { 1170 return p.Palette[0] 1171 } 1172 i := p.PixOffset(x, y) 1173 return p.Palette[p.Pix[i]] 1174} 1175 1176func (p *Paletted) RGBA64At(x, y int) color.RGBA64 { 1177 if len(p.Palette) == 0 { 1178 return color.RGBA64{} 1179 } 1180 c := color.Color(nil) 1181 if !(Point{x, y}.In(p.Rect)) { 1182 c = p.Palette[0] 1183 } else { 1184 i := p.PixOffset(x, y) 1185 c = p.Palette[p.Pix[i]] 1186 } 1187 r, g, b, a := c.RGBA() 1188 return color.RGBA64{ 1189 uint16(r), 1190 uint16(g), 1191 uint16(b), 1192 uint16(a), 1193 } 1194} 1195 1196// PixOffset returns the index of the first element of Pix that corresponds to 1197// the pixel at (x, y). 1198func (p *Paletted) PixOffset(x, y int) int { 1199 return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*1 1200} 1201 1202func (p *Paletted) Set(x, y int, c color.Color) { 1203 if !(Point{x, y}.In(p.Rect)) { 1204 return 1205 } 1206 i := p.PixOffset(x, y) 1207 p.Pix[i] = uint8(p.Palette.Index(c)) 1208} 1209 1210func (p *Paletted) SetRGBA64(x, y int, c color.RGBA64) { 1211 if !(Point{x, y}.In(p.Rect)) { 1212 return 1213 } 1214 i := p.PixOffset(x, y) 1215 p.Pix[i] = uint8(p.Palette.Index(c)) 1216} 1217 1218func (p *Paletted) ColorIndexAt(x, y int) uint8 { 1219 if !(Point{x, y}.In(p.Rect)) { 1220 return 0 1221 } 1222 i := p.PixOffset(x, y) 1223 return p.Pix[i] 1224} 1225 1226func (p *Paletted) SetColorIndex(x, y int, index uint8) { 1227 if !(Point{x, y}.In(p.Rect)) { 1228 return 1229 } 1230 i := p.PixOffset(x, y) 1231 p.Pix[i] = index 1232} 1233 1234// SubImage returns an image representing the portion of the image p visible 1235// through r. The returned value shares pixels with the original image. 1236func (p *Paletted) SubImage(r Rectangle) Image { 1237 r = r.Intersect(p.Rect) 1238 // If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside 1239 // either r1 or r2 if the intersection is empty. Without explicitly checking for 1240 // this, the Pix[i:] expression below can panic. 1241 if r.Empty() { 1242 return &Paletted{ 1243 Palette: p.Palette, 1244 } 1245 } 1246 i := p.PixOffset(r.Min.X, r.Min.Y) 1247 return &Paletted{ 1248 Pix: p.Pix[i:], 1249 Stride: p.Stride, 1250 Rect: p.Rect.Intersect(r), 1251 Palette: p.Palette, 1252 } 1253} 1254 1255// Opaque scans the entire image and reports whether it is fully opaque. 1256func (p *Paletted) Opaque() bool { 1257 var present [256]bool 1258 i0, i1 := 0, p.Rect.Dx() 1259 for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { 1260 for _, c := range p.Pix[i0:i1] { 1261 present[c] = true 1262 } 1263 i0 += p.Stride 1264 i1 += p.Stride 1265 } 1266 for i, c := range p.Palette { 1267 if !present[i] { 1268 continue 1269 } 1270 _, _, _, a := c.RGBA() 1271 if a != 0xffff { 1272 return false 1273 } 1274 } 1275 return true 1276} 1277 1278// NewPaletted returns a new [Paletted] image with the given width, height and 1279// palette. 1280func NewPaletted(r Rectangle, p color.Palette) *Paletted { 1281 return &Paletted{ 1282 Pix: make([]uint8, pixelBufferLength(1, r, "Paletted")), 1283 Stride: 1 * r.Dx(), 1284 Rect: r, 1285 Palette: p, 1286 } 1287} 1288