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 draw provides image composition functions. 6// 7// See "The Go image/draw package" for an introduction to this package: 8// https://golang.org/doc/articles/image_draw.html 9package draw 10 11import ( 12 "image" 13 "image/color" 14 "image/internal/imageutil" 15) 16 17// m is the maximum color value returned by image.Color.RGBA. 18const m = 1<<16 - 1 19 20// Image is an image.Image with a Set method to change a single pixel. 21type Image interface { 22 image.Image 23 Set(x, y int, c color.Color) 24} 25 26// RGBA64Image extends both the [Image] and [image.RGBA64Image] interfaces with a 27// SetRGBA64 method to change a single pixel. SetRGBA64 is equivalent to 28// calling Set, but it can avoid allocations from converting concrete color 29// types to the [color.Color] interface type. 30type RGBA64Image interface { 31 image.RGBA64Image 32 Set(x, y int, c color.Color) 33 SetRGBA64(x, y int, c color.RGBA64) 34} 35 36// Quantizer produces a palette for an image. 37type Quantizer interface { 38 // Quantize appends up to cap(p) - len(p) colors to p and returns the 39 // updated palette suitable for converting m to a paletted image. 40 Quantize(p color.Palette, m image.Image) color.Palette 41} 42 43// Op is a Porter-Duff compositing operator. 44type Op int 45 46const ( 47 // Over specifies ``(src in mask) over dst''. 48 Over Op = iota 49 // Src specifies ``src in mask''. 50 Src 51) 52 53// Draw implements the [Drawer] interface by calling the Draw function with this 54// [Op]. 55func (op Op) Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point) { 56 DrawMask(dst, r, src, sp, nil, image.Point{}, op) 57} 58 59// Drawer contains the [Draw] method. 60type Drawer interface { 61 // Draw aligns r.Min in dst with sp in src and then replaces the 62 // rectangle r in dst with the result of drawing src on dst. 63 Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point) 64} 65 66// FloydSteinberg is a [Drawer] that is the [Src] [Op] with Floyd-Steinberg error 67// diffusion. 68var FloydSteinberg Drawer = floydSteinberg{} 69 70type floydSteinberg struct{} 71 72func (floydSteinberg) Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point) { 73 clip(dst, &r, src, &sp, nil, nil) 74 if r.Empty() { 75 return 76 } 77 drawPaletted(dst, r, src, sp, true) 78} 79 80// clip clips r against each image's bounds (after translating into the 81// destination image's coordinate space) and shifts the points sp and mp by 82// the same amount as the change in r.Min. 83func clip(dst Image, r *image.Rectangle, src image.Image, sp *image.Point, mask image.Image, mp *image.Point) { 84 orig := r.Min 85 *r = r.Intersect(dst.Bounds()) 86 *r = r.Intersect(src.Bounds().Add(orig.Sub(*sp))) 87 if mask != nil { 88 *r = r.Intersect(mask.Bounds().Add(orig.Sub(*mp))) 89 } 90 dx := r.Min.X - orig.X 91 dy := r.Min.Y - orig.Y 92 if dx == 0 && dy == 0 { 93 return 94 } 95 sp.X += dx 96 sp.Y += dy 97 if mp != nil { 98 mp.X += dx 99 mp.Y += dy 100 } 101} 102 103func processBackward(dst image.Image, r image.Rectangle, src image.Image, sp image.Point) bool { 104 return dst == src && 105 r.Overlaps(r.Add(sp.Sub(r.Min))) && 106 (sp.Y < r.Min.Y || (sp.Y == r.Min.Y && sp.X < r.Min.X)) 107} 108 109// Draw calls [DrawMask] with a nil mask. 110func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point, op Op) { 111 DrawMask(dst, r, src, sp, nil, image.Point{}, op) 112} 113 114// DrawMask aligns r.Min in dst with sp in src and mp in mask and then replaces the rectangle r 115// in dst with the result of a Porter-Duff composition. A nil mask is treated as opaque. 116func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) { 117 clip(dst, &r, src, &sp, mask, &mp) 118 if r.Empty() { 119 return 120 } 121 122 // Fast paths for special cases. If none of them apply, then we fall back 123 // to general but slower implementations. 124 // 125 // For NRGBA and NRGBA64 image types, the code paths aren't just faster. 126 // They also avoid the information loss that would otherwise occur from 127 // converting non-alpha-premultiplied color to and from alpha-premultiplied 128 // color. See TestDrawSrcNonpremultiplied. 129 switch dst0 := dst.(type) { 130 case *image.RGBA: 131 if op == Over { 132 if mask == nil { 133 switch src0 := src.(type) { 134 case *image.Uniform: 135 sr, sg, sb, sa := src0.RGBA() 136 if sa == 0xffff { 137 drawFillSrc(dst0, r, sr, sg, sb, sa) 138 } else { 139 drawFillOver(dst0, r, sr, sg, sb, sa) 140 } 141 return 142 case *image.RGBA: 143 drawCopyOver(dst0, r, src0, sp) 144 return 145 case *image.NRGBA: 146 drawNRGBAOver(dst0, r, src0, sp) 147 return 148 case *image.YCbCr: 149 // An image.YCbCr is always fully opaque, and so if the 150 // mask is nil (i.e. fully opaque) then the op is 151 // effectively always Src. Similarly for image.Gray and 152 // image.CMYK. 153 if imageutil.DrawYCbCr(dst0, r, src0, sp) { 154 return 155 } 156 case *image.Gray: 157 drawGray(dst0, r, src0, sp) 158 return 159 case *image.CMYK: 160 drawCMYK(dst0, r, src0, sp) 161 return 162 } 163 } else if mask0, ok := mask.(*image.Alpha); ok { 164 switch src0 := src.(type) { 165 case *image.Uniform: 166 drawGlyphOver(dst0, r, src0, mask0, mp) 167 return 168 case *image.RGBA: 169 drawRGBAMaskOver(dst0, r, src0, sp, mask0, mp) 170 return 171 case *image.Gray: 172 drawGrayMaskOver(dst0, r, src0, sp, mask0, mp) 173 return 174 // Case order matters. The next case (image.RGBA64Image) is an 175 // interface type that the concrete types above also implement. 176 case image.RGBA64Image: 177 drawRGBA64ImageMaskOver(dst0, r, src0, sp, mask0, mp) 178 return 179 } 180 } 181 } else { 182 if mask == nil { 183 switch src0 := src.(type) { 184 case *image.Uniform: 185 sr, sg, sb, sa := src0.RGBA() 186 drawFillSrc(dst0, r, sr, sg, sb, sa) 187 return 188 case *image.RGBA: 189 d0 := dst0.PixOffset(r.Min.X, r.Min.Y) 190 s0 := src0.PixOffset(sp.X, sp.Y) 191 drawCopySrc( 192 dst0.Pix[d0:], dst0.Stride, r, src0.Pix[s0:], src0.Stride, sp, 4*r.Dx()) 193 return 194 case *image.NRGBA: 195 drawNRGBASrc(dst0, r, src0, sp) 196 return 197 case *image.YCbCr: 198 if imageutil.DrawYCbCr(dst0, r, src0, sp) { 199 return 200 } 201 case *image.Gray: 202 drawGray(dst0, r, src0, sp) 203 return 204 case *image.CMYK: 205 drawCMYK(dst0, r, src0, sp) 206 return 207 } 208 } 209 } 210 drawRGBA(dst0, r, src, sp, mask, mp, op) 211 return 212 case *image.Paletted: 213 if op == Src && mask == nil { 214 if src0, ok := src.(*image.Uniform); ok { 215 colorIndex := uint8(dst0.Palette.Index(src0.C)) 216 i0 := dst0.PixOffset(r.Min.X, r.Min.Y) 217 i1 := i0 + r.Dx() 218 for i := i0; i < i1; i++ { 219 dst0.Pix[i] = colorIndex 220 } 221 firstRow := dst0.Pix[i0:i1] 222 for y := r.Min.Y + 1; y < r.Max.Y; y++ { 223 i0 += dst0.Stride 224 i1 += dst0.Stride 225 copy(dst0.Pix[i0:i1], firstRow) 226 } 227 return 228 } else if !processBackward(dst, r, src, sp) { 229 drawPaletted(dst0, r, src, sp, false) 230 return 231 } 232 } 233 case *image.NRGBA: 234 if op == Src && mask == nil { 235 if src0, ok := src.(*image.NRGBA); ok { 236 d0 := dst0.PixOffset(r.Min.X, r.Min.Y) 237 s0 := src0.PixOffset(sp.X, sp.Y) 238 drawCopySrc( 239 dst0.Pix[d0:], dst0.Stride, r, src0.Pix[s0:], src0.Stride, sp, 4*r.Dx()) 240 return 241 } 242 } 243 case *image.NRGBA64: 244 if op == Src && mask == nil { 245 if src0, ok := src.(*image.NRGBA64); ok { 246 d0 := dst0.PixOffset(r.Min.X, r.Min.Y) 247 s0 := src0.PixOffset(sp.X, sp.Y) 248 drawCopySrc( 249 dst0.Pix[d0:], dst0.Stride, r, src0.Pix[s0:], src0.Stride, sp, 8*r.Dx()) 250 return 251 } 252 } 253 } 254 255 x0, x1, dx := r.Min.X, r.Max.X, 1 256 y0, y1, dy := r.Min.Y, r.Max.Y, 1 257 if processBackward(dst, r, src, sp) { 258 x0, x1, dx = x1-1, x0-1, -1 259 y0, y1, dy = y1-1, y0-1, -1 260 } 261 262 // FALLBACK1.17 263 // 264 // Try the draw.RGBA64Image and image.RGBA64Image interfaces, part of the 265 // standard library since Go 1.17. These are like the draw.Image and 266 // image.Image interfaces but they can avoid allocations from converting 267 // concrete color types to the color.Color interface type. 268 269 if dst0, _ := dst.(RGBA64Image); dst0 != nil { 270 if src0, _ := src.(image.RGBA64Image); src0 != nil { 271 if mask == nil { 272 sy := sp.Y + y0 - r.Min.Y 273 my := mp.Y + y0 - r.Min.Y 274 for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { 275 sx := sp.X + x0 - r.Min.X 276 mx := mp.X + x0 - r.Min.X 277 for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx { 278 if op == Src { 279 dst0.SetRGBA64(x, y, src0.RGBA64At(sx, sy)) 280 } else { 281 srgba := src0.RGBA64At(sx, sy) 282 a := m - uint32(srgba.A) 283 drgba := dst0.RGBA64At(x, y) 284 dst0.SetRGBA64(x, y, color.RGBA64{ 285 R: uint16((uint32(drgba.R)*a)/m) + srgba.R, 286 G: uint16((uint32(drgba.G)*a)/m) + srgba.G, 287 B: uint16((uint32(drgba.B)*a)/m) + srgba.B, 288 A: uint16((uint32(drgba.A)*a)/m) + srgba.A, 289 }) 290 } 291 } 292 } 293 return 294 295 } else if mask0, _ := mask.(image.RGBA64Image); mask0 != nil { 296 sy := sp.Y + y0 - r.Min.Y 297 my := mp.Y + y0 - r.Min.Y 298 for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { 299 sx := sp.X + x0 - r.Min.X 300 mx := mp.X + x0 - r.Min.X 301 for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx { 302 ma := uint32(mask0.RGBA64At(mx, my).A) 303 switch { 304 case ma == 0: 305 if op == Over { 306 // No-op. 307 } else { 308 dst0.SetRGBA64(x, y, color.RGBA64{}) 309 } 310 case ma == m && op == Src: 311 dst0.SetRGBA64(x, y, src0.RGBA64At(sx, sy)) 312 default: 313 srgba := src0.RGBA64At(sx, sy) 314 if op == Over { 315 drgba := dst0.RGBA64At(x, y) 316 a := m - (uint32(srgba.A) * ma / m) 317 dst0.SetRGBA64(x, y, color.RGBA64{ 318 R: uint16((uint32(drgba.R)*a + uint32(srgba.R)*ma) / m), 319 G: uint16((uint32(drgba.G)*a + uint32(srgba.G)*ma) / m), 320 B: uint16((uint32(drgba.B)*a + uint32(srgba.B)*ma) / m), 321 A: uint16((uint32(drgba.A)*a + uint32(srgba.A)*ma) / m), 322 }) 323 } else { 324 dst0.SetRGBA64(x, y, color.RGBA64{ 325 R: uint16(uint32(srgba.R) * ma / m), 326 G: uint16(uint32(srgba.G) * ma / m), 327 B: uint16(uint32(srgba.B) * ma / m), 328 A: uint16(uint32(srgba.A) * ma / m), 329 }) 330 } 331 } 332 } 333 } 334 return 335 } 336 } 337 } 338 339 // FALLBACK1.0 340 // 341 // If none of the faster code paths above apply, use the draw.Image and 342 // image.Image interfaces, part of the standard library since Go 1.0. 343 344 var out color.RGBA64 345 sy := sp.Y + y0 - r.Min.Y 346 my := mp.Y + y0 - r.Min.Y 347 for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { 348 sx := sp.X + x0 - r.Min.X 349 mx := mp.X + x0 - r.Min.X 350 for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx { 351 ma := uint32(m) 352 if mask != nil { 353 _, _, _, ma = mask.At(mx, my).RGBA() 354 } 355 switch { 356 case ma == 0: 357 if op == Over { 358 // No-op. 359 } else { 360 dst.Set(x, y, color.Transparent) 361 } 362 case ma == m && op == Src: 363 dst.Set(x, y, src.At(sx, sy)) 364 default: 365 sr, sg, sb, sa := src.At(sx, sy).RGBA() 366 if op == Over { 367 dr, dg, db, da := dst.At(x, y).RGBA() 368 a := m - (sa * ma / m) 369 out.R = uint16((dr*a + sr*ma) / m) 370 out.G = uint16((dg*a + sg*ma) / m) 371 out.B = uint16((db*a + sb*ma) / m) 372 out.A = uint16((da*a + sa*ma) / m) 373 } else { 374 out.R = uint16(sr * ma / m) 375 out.G = uint16(sg * ma / m) 376 out.B = uint16(sb * ma / m) 377 out.A = uint16(sa * ma / m) 378 } 379 // The third argument is &out instead of out (and out is 380 // declared outside of the inner loop) to avoid the implicit 381 // conversion to color.Color here allocating memory in the 382 // inner loop if sizeof(color.RGBA64) > sizeof(uintptr). 383 dst.Set(x, y, &out) 384 } 385 } 386 } 387} 388 389func drawFillOver(dst *image.RGBA, r image.Rectangle, sr, sg, sb, sa uint32) { 390 // The 0x101 is here for the same reason as in drawRGBA. 391 a := (m - sa) * 0x101 392 i0 := dst.PixOffset(r.Min.X, r.Min.Y) 393 i1 := i0 + r.Dx()*4 394 for y := r.Min.Y; y != r.Max.Y; y++ { 395 for i := i0; i < i1; i += 4 { 396 dr := &dst.Pix[i+0] 397 dg := &dst.Pix[i+1] 398 db := &dst.Pix[i+2] 399 da := &dst.Pix[i+3] 400 401 *dr = uint8((uint32(*dr)*a/m + sr) >> 8) 402 *dg = uint8((uint32(*dg)*a/m + sg) >> 8) 403 *db = uint8((uint32(*db)*a/m + sb) >> 8) 404 *da = uint8((uint32(*da)*a/m + sa) >> 8) 405 } 406 i0 += dst.Stride 407 i1 += dst.Stride 408 } 409} 410 411func drawFillSrc(dst *image.RGBA, r image.Rectangle, sr, sg, sb, sa uint32) { 412 sr8 := uint8(sr >> 8) 413 sg8 := uint8(sg >> 8) 414 sb8 := uint8(sb >> 8) 415 sa8 := uint8(sa >> 8) 416 // The built-in copy function is faster than a straightforward for loop to fill the destination with 417 // the color, but copy requires a slice source. We therefore use a for loop to fill the first row, and 418 // then use the first row as the slice source for the remaining rows. 419 i0 := dst.PixOffset(r.Min.X, r.Min.Y) 420 i1 := i0 + r.Dx()*4 421 for i := i0; i < i1; i += 4 { 422 dst.Pix[i+0] = sr8 423 dst.Pix[i+1] = sg8 424 dst.Pix[i+2] = sb8 425 dst.Pix[i+3] = sa8 426 } 427 firstRow := dst.Pix[i0:i1] 428 for y := r.Min.Y + 1; y < r.Max.Y; y++ { 429 i0 += dst.Stride 430 i1 += dst.Stride 431 copy(dst.Pix[i0:i1], firstRow) 432 } 433} 434 435func drawCopyOver(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point) { 436 dx, dy := r.Dx(), r.Dy() 437 d0 := dst.PixOffset(r.Min.X, r.Min.Y) 438 s0 := src.PixOffset(sp.X, sp.Y) 439 var ( 440 ddelta, sdelta int 441 i0, i1, idelta int 442 ) 443 if r.Min.Y < sp.Y || r.Min.Y == sp.Y && r.Min.X <= sp.X { 444 ddelta = dst.Stride 445 sdelta = src.Stride 446 i0, i1, idelta = 0, dx*4, +4 447 } else { 448 // If the source start point is higher than the destination start point, or equal height but to the left, 449 // then we compose the rows in right-to-left, bottom-up order instead of left-to-right, top-down. 450 d0 += (dy - 1) * dst.Stride 451 s0 += (dy - 1) * src.Stride 452 ddelta = -dst.Stride 453 sdelta = -src.Stride 454 i0, i1, idelta = (dx-1)*4, -4, -4 455 } 456 for ; dy > 0; dy-- { 457 dpix := dst.Pix[d0:] 458 spix := src.Pix[s0:] 459 for i := i0; i != i1; i += idelta { 460 s := spix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 461 sr := uint32(s[0]) * 0x101 462 sg := uint32(s[1]) * 0x101 463 sb := uint32(s[2]) * 0x101 464 sa := uint32(s[3]) * 0x101 465 466 // The 0x101 is here for the same reason as in drawRGBA. 467 a := (m - sa) * 0x101 468 469 d := dpix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 470 d[0] = uint8((uint32(d[0])*a/m + sr) >> 8) 471 d[1] = uint8((uint32(d[1])*a/m + sg) >> 8) 472 d[2] = uint8((uint32(d[2])*a/m + sb) >> 8) 473 d[3] = uint8((uint32(d[3])*a/m + sa) >> 8) 474 } 475 d0 += ddelta 476 s0 += sdelta 477 } 478} 479 480// drawCopySrc copies bytes to dstPix from srcPix. These arguments roughly 481// correspond to the Pix fields of the image package's concrete image.Image 482// implementations, but are offset (dstPix is dst.Pix[dpOffset:] not dst.Pix). 483func drawCopySrc( 484 dstPix []byte, dstStride int, r image.Rectangle, 485 srcPix []byte, srcStride int, sp image.Point, 486 bytesPerRow int) { 487 488 d0, s0, ddelta, sdelta, dy := 0, 0, dstStride, srcStride, r.Dy() 489 if r.Min.Y > sp.Y { 490 // If the source start point is higher than the destination start 491 // point, then we compose the rows in bottom-up order instead of 492 // top-down. Unlike the drawCopyOver function, we don't have to check 493 // the x coordinates because the built-in copy function can handle 494 // overlapping slices. 495 d0 = (dy - 1) * dstStride 496 s0 = (dy - 1) * srcStride 497 ddelta = -dstStride 498 sdelta = -srcStride 499 } 500 for ; dy > 0; dy-- { 501 copy(dstPix[d0:d0+bytesPerRow], srcPix[s0:s0+bytesPerRow]) 502 d0 += ddelta 503 s0 += sdelta 504 } 505} 506 507func drawNRGBAOver(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image.Point) { 508 i0 := (r.Min.X - dst.Rect.Min.X) * 4 509 i1 := (r.Max.X - dst.Rect.Min.X) * 4 510 si0 := (sp.X - src.Rect.Min.X) * 4 511 yMax := r.Max.Y - dst.Rect.Min.Y 512 513 y := r.Min.Y - dst.Rect.Min.Y 514 sy := sp.Y - src.Rect.Min.Y 515 for ; y != yMax; y, sy = y+1, sy+1 { 516 dpix := dst.Pix[y*dst.Stride:] 517 spix := src.Pix[sy*src.Stride:] 518 519 for i, si := i0, si0; i < i1; i, si = i+4, si+4 { 520 // Convert from non-premultiplied color to pre-multiplied color. 521 s := spix[si : si+4 : si+4] // Small cap improves performance, see https://golang.org/issue/27857 522 sa := uint32(s[3]) * 0x101 523 sr := uint32(s[0]) * sa / 0xff 524 sg := uint32(s[1]) * sa / 0xff 525 sb := uint32(s[2]) * sa / 0xff 526 527 d := dpix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 528 dr := uint32(d[0]) 529 dg := uint32(d[1]) 530 db := uint32(d[2]) 531 da := uint32(d[3]) 532 533 // The 0x101 is here for the same reason as in drawRGBA. 534 a := (m - sa) * 0x101 535 536 d[0] = uint8((dr*a/m + sr) >> 8) 537 d[1] = uint8((dg*a/m + sg) >> 8) 538 d[2] = uint8((db*a/m + sb) >> 8) 539 d[3] = uint8((da*a/m + sa) >> 8) 540 } 541 } 542} 543 544func drawNRGBASrc(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image.Point) { 545 i0 := (r.Min.X - dst.Rect.Min.X) * 4 546 i1 := (r.Max.X - dst.Rect.Min.X) * 4 547 si0 := (sp.X - src.Rect.Min.X) * 4 548 yMax := r.Max.Y - dst.Rect.Min.Y 549 550 y := r.Min.Y - dst.Rect.Min.Y 551 sy := sp.Y - src.Rect.Min.Y 552 for ; y != yMax; y, sy = y+1, sy+1 { 553 dpix := dst.Pix[y*dst.Stride:] 554 spix := src.Pix[sy*src.Stride:] 555 556 for i, si := i0, si0; i < i1; i, si = i+4, si+4 { 557 // Convert from non-premultiplied color to pre-multiplied color. 558 s := spix[si : si+4 : si+4] // Small cap improves performance, see https://golang.org/issue/27857 559 sa := uint32(s[3]) * 0x101 560 sr := uint32(s[0]) * sa / 0xff 561 sg := uint32(s[1]) * sa / 0xff 562 sb := uint32(s[2]) * sa / 0xff 563 564 d := dpix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 565 d[0] = uint8(sr >> 8) 566 d[1] = uint8(sg >> 8) 567 d[2] = uint8(sb >> 8) 568 d[3] = uint8(sa >> 8) 569 } 570 } 571} 572 573func drawGray(dst *image.RGBA, r image.Rectangle, src *image.Gray, sp image.Point) { 574 i0 := (r.Min.X - dst.Rect.Min.X) * 4 575 i1 := (r.Max.X - dst.Rect.Min.X) * 4 576 si0 := (sp.X - src.Rect.Min.X) * 1 577 yMax := r.Max.Y - dst.Rect.Min.Y 578 579 y := r.Min.Y - dst.Rect.Min.Y 580 sy := sp.Y - src.Rect.Min.Y 581 for ; y != yMax; y, sy = y+1, sy+1 { 582 dpix := dst.Pix[y*dst.Stride:] 583 spix := src.Pix[sy*src.Stride:] 584 585 for i, si := i0, si0; i < i1; i, si = i+4, si+1 { 586 p := spix[si] 587 d := dpix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 588 d[0] = p 589 d[1] = p 590 d[2] = p 591 d[3] = 255 592 } 593 } 594} 595 596func drawCMYK(dst *image.RGBA, r image.Rectangle, src *image.CMYK, sp image.Point) { 597 i0 := (r.Min.X - dst.Rect.Min.X) * 4 598 i1 := (r.Max.X - dst.Rect.Min.X) * 4 599 si0 := (sp.X - src.Rect.Min.X) * 4 600 yMax := r.Max.Y - dst.Rect.Min.Y 601 602 y := r.Min.Y - dst.Rect.Min.Y 603 sy := sp.Y - src.Rect.Min.Y 604 for ; y != yMax; y, sy = y+1, sy+1 { 605 dpix := dst.Pix[y*dst.Stride:] 606 spix := src.Pix[sy*src.Stride:] 607 608 for i, si := i0, si0; i < i1; i, si = i+4, si+4 { 609 s := spix[si : si+4 : si+4] // Small cap improves performance, see https://golang.org/issue/27857 610 d := dpix[i : i+4 : i+4] 611 d[0], d[1], d[2] = color.CMYKToRGB(s[0], s[1], s[2], s[3]) 612 d[3] = 255 613 } 614 } 615} 616 617func drawGlyphOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform, mask *image.Alpha, mp image.Point) { 618 i0 := dst.PixOffset(r.Min.X, r.Min.Y) 619 i1 := i0 + r.Dx()*4 620 mi0 := mask.PixOffset(mp.X, mp.Y) 621 sr, sg, sb, sa := src.RGBA() 622 for y, my := r.Min.Y, mp.Y; y != r.Max.Y; y, my = y+1, my+1 { 623 for i, mi := i0, mi0; i < i1; i, mi = i+4, mi+1 { 624 ma := uint32(mask.Pix[mi]) 625 if ma == 0 { 626 continue 627 } 628 ma |= ma << 8 629 630 // The 0x101 is here for the same reason as in drawRGBA. 631 a := (m - (sa * ma / m)) * 0x101 632 633 d := dst.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 634 d[0] = uint8((uint32(d[0])*a + sr*ma) / m >> 8) 635 d[1] = uint8((uint32(d[1])*a + sg*ma) / m >> 8) 636 d[2] = uint8((uint32(d[2])*a + sb*ma) / m >> 8) 637 d[3] = uint8((uint32(d[3])*a + sa*ma) / m >> 8) 638 } 639 i0 += dst.Stride 640 i1 += dst.Stride 641 mi0 += mask.Stride 642 } 643} 644 645func drawGrayMaskOver(dst *image.RGBA, r image.Rectangle, src *image.Gray, sp image.Point, mask *image.Alpha, mp image.Point) { 646 x0, x1, dx := r.Min.X, r.Max.X, 1 647 y0, y1, dy := r.Min.Y, r.Max.Y, 1 648 if r.Overlaps(r.Add(sp.Sub(r.Min))) { 649 if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X { 650 x0, x1, dx = x1-1, x0-1, -1 651 y0, y1, dy = y1-1, y0-1, -1 652 } 653 } 654 655 sy := sp.Y + y0 - r.Min.Y 656 my := mp.Y + y0 - r.Min.Y 657 sx0 := sp.X + x0 - r.Min.X 658 mx0 := mp.X + x0 - r.Min.X 659 sx1 := sx0 + (x1 - x0) 660 i0 := dst.PixOffset(x0, y0) 661 di := dx * 4 662 for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { 663 for i, sx, mx := i0, sx0, mx0; sx != sx1; i, sx, mx = i+di, sx+dx, mx+dx { 664 mi := mask.PixOffset(mx, my) 665 ma := uint32(mask.Pix[mi]) 666 ma |= ma << 8 667 si := src.PixOffset(sx, sy) 668 sy := uint32(src.Pix[si]) 669 sy |= sy << 8 670 sa := uint32(0xffff) 671 672 d := dst.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 673 dr := uint32(d[0]) 674 dg := uint32(d[1]) 675 db := uint32(d[2]) 676 da := uint32(d[3]) 677 678 // dr, dg, db and da are all 8-bit color at the moment, ranging in [0,255]. 679 // We work in 16-bit color, and so would normally do: 680 // dr |= dr << 8 681 // and similarly for dg, db and da, but instead we multiply a 682 // (which is a 16-bit color, ranging in [0,65535]) by 0x101. 683 // This yields the same result, but is fewer arithmetic operations. 684 a := (m - (sa * ma / m)) * 0x101 685 686 d[0] = uint8((dr*a + sy*ma) / m >> 8) 687 d[1] = uint8((dg*a + sy*ma) / m >> 8) 688 d[2] = uint8((db*a + sy*ma) / m >> 8) 689 d[3] = uint8((da*a + sa*ma) / m >> 8) 690 } 691 i0 += dy * dst.Stride 692 } 693} 694 695func drawRGBAMaskOver(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point, mask *image.Alpha, mp image.Point) { 696 x0, x1, dx := r.Min.X, r.Max.X, 1 697 y0, y1, dy := r.Min.Y, r.Max.Y, 1 698 if dst == src && r.Overlaps(r.Add(sp.Sub(r.Min))) { 699 if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X { 700 x0, x1, dx = x1-1, x0-1, -1 701 y0, y1, dy = y1-1, y0-1, -1 702 } 703 } 704 705 sy := sp.Y + y0 - r.Min.Y 706 my := mp.Y + y0 - r.Min.Y 707 sx0 := sp.X + x0 - r.Min.X 708 mx0 := mp.X + x0 - r.Min.X 709 sx1 := sx0 + (x1 - x0) 710 i0 := dst.PixOffset(x0, y0) 711 di := dx * 4 712 for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { 713 for i, sx, mx := i0, sx0, mx0; sx != sx1; i, sx, mx = i+di, sx+dx, mx+dx { 714 mi := mask.PixOffset(mx, my) 715 ma := uint32(mask.Pix[mi]) 716 ma |= ma << 8 717 si := src.PixOffset(sx, sy) 718 sr := uint32(src.Pix[si+0]) 719 sg := uint32(src.Pix[si+1]) 720 sb := uint32(src.Pix[si+2]) 721 sa := uint32(src.Pix[si+3]) 722 sr |= sr << 8 723 sg |= sg << 8 724 sb |= sb << 8 725 sa |= sa << 8 726 d := dst.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 727 dr := uint32(d[0]) 728 dg := uint32(d[1]) 729 db := uint32(d[2]) 730 da := uint32(d[3]) 731 732 // dr, dg, db and da are all 8-bit color at the moment, ranging in [0,255]. 733 // We work in 16-bit color, and so would normally do: 734 // dr |= dr << 8 735 // and similarly for dg, db and da, but instead we multiply a 736 // (which is a 16-bit color, ranging in [0,65535]) by 0x101. 737 // This yields the same result, but is fewer arithmetic operations. 738 a := (m - (sa * ma / m)) * 0x101 739 740 d[0] = uint8((dr*a + sr*ma) / m >> 8) 741 d[1] = uint8((dg*a + sg*ma) / m >> 8) 742 d[2] = uint8((db*a + sb*ma) / m >> 8) 743 d[3] = uint8((da*a + sa*ma) / m >> 8) 744 } 745 i0 += dy * dst.Stride 746 } 747} 748 749func drawRGBA64ImageMaskOver(dst *image.RGBA, r image.Rectangle, src image.RGBA64Image, sp image.Point, mask *image.Alpha, mp image.Point) { 750 x0, x1, dx := r.Min.X, r.Max.X, 1 751 y0, y1, dy := r.Min.Y, r.Max.Y, 1 752 if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) { 753 if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X { 754 x0, x1, dx = x1-1, x0-1, -1 755 y0, y1, dy = y1-1, y0-1, -1 756 } 757 } 758 759 sy := sp.Y + y0 - r.Min.Y 760 my := mp.Y + y0 - r.Min.Y 761 sx0 := sp.X + x0 - r.Min.X 762 mx0 := mp.X + x0 - r.Min.X 763 sx1 := sx0 + (x1 - x0) 764 i0 := dst.PixOffset(x0, y0) 765 di := dx * 4 766 for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { 767 for i, sx, mx := i0, sx0, mx0; sx != sx1; i, sx, mx = i+di, sx+dx, mx+dx { 768 mi := mask.PixOffset(mx, my) 769 ma := uint32(mask.Pix[mi]) 770 ma |= ma << 8 771 srgba := src.RGBA64At(sx, sy) 772 d := dst.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 773 dr := uint32(d[0]) 774 dg := uint32(d[1]) 775 db := uint32(d[2]) 776 da := uint32(d[3]) 777 778 // dr, dg, db and da are all 8-bit color at the moment, ranging in [0,255]. 779 // We work in 16-bit color, and so would normally do: 780 // dr |= dr << 8 781 // and similarly for dg, db and da, but instead we multiply a 782 // (which is a 16-bit color, ranging in [0,65535]) by 0x101. 783 // This yields the same result, but is fewer arithmetic operations. 784 a := (m - (uint32(srgba.A) * ma / m)) * 0x101 785 786 d[0] = uint8((dr*a + uint32(srgba.R)*ma) / m >> 8) 787 d[1] = uint8((dg*a + uint32(srgba.G)*ma) / m >> 8) 788 d[2] = uint8((db*a + uint32(srgba.B)*ma) / m >> 8) 789 d[3] = uint8((da*a + uint32(srgba.A)*ma) / m >> 8) 790 } 791 i0 += dy * dst.Stride 792 } 793} 794 795func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) { 796 x0, x1, dx := r.Min.X, r.Max.X, 1 797 y0, y1, dy := r.Min.Y, r.Max.Y, 1 798 if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) { 799 if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X { 800 x0, x1, dx = x1-1, x0-1, -1 801 y0, y1, dy = y1-1, y0-1, -1 802 } 803 } 804 805 sy := sp.Y + y0 - r.Min.Y 806 my := mp.Y + y0 - r.Min.Y 807 sx0 := sp.X + x0 - r.Min.X 808 mx0 := mp.X + x0 - r.Min.X 809 sx1 := sx0 + (x1 - x0) 810 i0 := dst.PixOffset(x0, y0) 811 di := dx * 4 812 813 // Try the image.RGBA64Image interface, part of the standard library since 814 // Go 1.17. 815 // 816 // This optimization is similar to how FALLBACK1.17 optimizes FALLBACK1.0 817 // in DrawMask, except here the concrete type of dst is known to be 818 // *image.RGBA. 819 if src0, _ := src.(image.RGBA64Image); src0 != nil { 820 if mask == nil { 821 if op == Over { 822 for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { 823 for i, sx, mx := i0, sx0, mx0; sx != sx1; i, sx, mx = i+di, sx+dx, mx+dx { 824 srgba := src0.RGBA64At(sx, sy) 825 d := dst.Pix[i : i+4 : i+4] 826 dr := uint32(d[0]) 827 dg := uint32(d[1]) 828 db := uint32(d[2]) 829 da := uint32(d[3]) 830 a := (m - uint32(srgba.A)) * 0x101 831 d[0] = uint8((dr*a/m + uint32(srgba.R)) >> 8) 832 d[1] = uint8((dg*a/m + uint32(srgba.G)) >> 8) 833 d[2] = uint8((db*a/m + uint32(srgba.B)) >> 8) 834 d[3] = uint8((da*a/m + uint32(srgba.A)) >> 8) 835 } 836 i0 += dy * dst.Stride 837 } 838 } else { 839 for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { 840 for i, sx, mx := i0, sx0, mx0; sx != sx1; i, sx, mx = i+di, sx+dx, mx+dx { 841 srgba := src0.RGBA64At(sx, sy) 842 d := dst.Pix[i : i+4 : i+4] 843 d[0] = uint8(srgba.R >> 8) 844 d[1] = uint8(srgba.G >> 8) 845 d[2] = uint8(srgba.B >> 8) 846 d[3] = uint8(srgba.A >> 8) 847 } 848 i0 += dy * dst.Stride 849 } 850 } 851 return 852 853 } else if mask0, _ := mask.(image.RGBA64Image); mask0 != nil { 854 if op == Over { 855 for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { 856 for i, sx, mx := i0, sx0, mx0; sx != sx1; i, sx, mx = i+di, sx+dx, mx+dx { 857 ma := uint32(mask0.RGBA64At(mx, my).A) 858 srgba := src0.RGBA64At(sx, sy) 859 d := dst.Pix[i : i+4 : i+4] 860 dr := uint32(d[0]) 861 dg := uint32(d[1]) 862 db := uint32(d[2]) 863 da := uint32(d[3]) 864 a := (m - (uint32(srgba.A) * ma / m)) * 0x101 865 d[0] = uint8((dr*a + uint32(srgba.R)*ma) / m >> 8) 866 d[1] = uint8((dg*a + uint32(srgba.G)*ma) / m >> 8) 867 d[2] = uint8((db*a + uint32(srgba.B)*ma) / m >> 8) 868 d[3] = uint8((da*a + uint32(srgba.A)*ma) / m >> 8) 869 } 870 i0 += dy * dst.Stride 871 } 872 } else { 873 for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { 874 for i, sx, mx := i0, sx0, mx0; sx != sx1; i, sx, mx = i+di, sx+dx, mx+dx { 875 ma := uint32(mask0.RGBA64At(mx, my).A) 876 srgba := src0.RGBA64At(sx, sy) 877 d := dst.Pix[i : i+4 : i+4] 878 d[0] = uint8(uint32(srgba.R) * ma / m >> 8) 879 d[1] = uint8(uint32(srgba.G) * ma / m >> 8) 880 d[2] = uint8(uint32(srgba.B) * ma / m >> 8) 881 d[3] = uint8(uint32(srgba.A) * ma / m >> 8) 882 } 883 i0 += dy * dst.Stride 884 } 885 } 886 return 887 } 888 } 889 890 // Use the image.Image interface, part of the standard library since Go 891 // 1.0. 892 // 893 // This is similar to FALLBACK1.0 in DrawMask, except here the concrete 894 // type of dst is known to be *image.RGBA. 895 for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { 896 for i, sx, mx := i0, sx0, mx0; sx != sx1; i, sx, mx = i+di, sx+dx, mx+dx { 897 ma := uint32(m) 898 if mask != nil { 899 _, _, _, ma = mask.At(mx, my).RGBA() 900 } 901 sr, sg, sb, sa := src.At(sx, sy).RGBA() 902 d := dst.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 903 if op == Over { 904 dr := uint32(d[0]) 905 dg := uint32(d[1]) 906 db := uint32(d[2]) 907 da := uint32(d[3]) 908 909 // dr, dg, db and da are all 8-bit color at the moment, ranging in [0,255]. 910 // We work in 16-bit color, and so would normally do: 911 // dr |= dr << 8 912 // and similarly for dg, db and da, but instead we multiply a 913 // (which is a 16-bit color, ranging in [0,65535]) by 0x101. 914 // This yields the same result, but is fewer arithmetic operations. 915 a := (m - (sa * ma / m)) * 0x101 916 917 d[0] = uint8((dr*a + sr*ma) / m >> 8) 918 d[1] = uint8((dg*a + sg*ma) / m >> 8) 919 d[2] = uint8((db*a + sb*ma) / m >> 8) 920 d[3] = uint8((da*a + sa*ma) / m >> 8) 921 922 } else { 923 d[0] = uint8(sr * ma / m >> 8) 924 d[1] = uint8(sg * ma / m >> 8) 925 d[2] = uint8(sb * ma / m >> 8) 926 d[3] = uint8(sa * ma / m >> 8) 927 } 928 } 929 i0 += dy * dst.Stride 930 } 931} 932 933// clamp clamps i to the interval [0, 0xffff]. 934func clamp(i int32) int32 { 935 if i < 0 { 936 return 0 937 } 938 if i > 0xffff { 939 return 0xffff 940 } 941 return i 942} 943 944// sqDiff returns the squared-difference of x and y, shifted by 2 so that 945// adding four of those won't overflow a uint32. 946// 947// x and y are both assumed to be in the range [0, 0xffff]. 948func sqDiff(x, y int32) uint32 { 949 // This is an optimized code relying on the overflow/wrap around 950 // properties of unsigned integers operations guaranteed by the language 951 // spec. See sqDiff from the image/color package for more details. 952 d := uint32(x - y) 953 return (d * d) >> 2 954} 955 956func drawPaletted(dst Image, r image.Rectangle, src image.Image, sp image.Point, floydSteinberg bool) { 957 // TODO(nigeltao): handle the case where the dst and src overlap. 958 // Does it even make sense to try and do Floyd-Steinberg whilst 959 // walking the image backward (right-to-left bottom-to-top)? 960 961 // If dst is an *image.Paletted, we have a fast path for dst.Set and 962 // dst.At. The dst.Set equivalent is a batch version of the algorithm 963 // used by color.Palette's Index method in image/color/color.go, plus 964 // optional Floyd-Steinberg error diffusion. 965 palette, pix, stride := [][4]int32(nil), []byte(nil), 0 966 if p, ok := dst.(*image.Paletted); ok { 967 palette = make([][4]int32, len(p.Palette)) 968 for i, col := range p.Palette { 969 r, g, b, a := col.RGBA() 970 palette[i][0] = int32(r) 971 palette[i][1] = int32(g) 972 palette[i][2] = int32(b) 973 palette[i][3] = int32(a) 974 } 975 pix, stride = p.Pix[p.PixOffset(r.Min.X, r.Min.Y):], p.Stride 976 } 977 978 // quantErrorCurr and quantErrorNext are the Floyd-Steinberg quantization 979 // errors that have been propagated to the pixels in the current and next 980 // rows. The +2 simplifies calculation near the edges. 981 var quantErrorCurr, quantErrorNext [][4]int32 982 if floydSteinberg { 983 quantErrorCurr = make([][4]int32, r.Dx()+2) 984 quantErrorNext = make([][4]int32, r.Dx()+2) 985 } 986 pxRGBA := func(x, y int) (r, g, b, a uint32) { return src.At(x, y).RGBA() } 987 // Fast paths for special cases to avoid excessive use of the color.Color 988 // interface which escapes to the heap but need to be discovered for 989 // each pixel on r. See also https://golang.org/issues/15759. 990 switch src0 := src.(type) { 991 case *image.RGBA: 992 pxRGBA = func(x, y int) (r, g, b, a uint32) { return src0.RGBAAt(x, y).RGBA() } 993 case *image.NRGBA: 994 pxRGBA = func(x, y int) (r, g, b, a uint32) { return src0.NRGBAAt(x, y).RGBA() } 995 case *image.YCbCr: 996 pxRGBA = func(x, y int) (r, g, b, a uint32) { return src0.YCbCrAt(x, y).RGBA() } 997 } 998 999 // Loop over each source pixel. 1000 out := color.RGBA64{A: 0xffff} 1001 for y := 0; y != r.Dy(); y++ { 1002 for x := 0; x != r.Dx(); x++ { 1003 // er, eg and eb are the pixel's R,G,B values plus the 1004 // optional Floyd-Steinberg error. 1005 sr, sg, sb, sa := pxRGBA(sp.X+x, sp.Y+y) 1006 er, eg, eb, ea := int32(sr), int32(sg), int32(sb), int32(sa) 1007 if floydSteinberg { 1008 er = clamp(er + quantErrorCurr[x+1][0]/16) 1009 eg = clamp(eg + quantErrorCurr[x+1][1]/16) 1010 eb = clamp(eb + quantErrorCurr[x+1][2]/16) 1011 ea = clamp(ea + quantErrorCurr[x+1][3]/16) 1012 } 1013 1014 if palette != nil { 1015 // Find the closest palette color in Euclidean R,G,B,A space: 1016 // the one that minimizes sum-squared-difference. 1017 // TODO(nigeltao): consider smarter algorithms. 1018 bestIndex, bestSum := 0, uint32(1<<32-1) 1019 for index, p := range palette { 1020 sum := sqDiff(er, p[0]) + sqDiff(eg, p[1]) + sqDiff(eb, p[2]) + sqDiff(ea, p[3]) 1021 if sum < bestSum { 1022 bestIndex, bestSum = index, sum 1023 if sum == 0 { 1024 break 1025 } 1026 } 1027 } 1028 pix[y*stride+x] = byte(bestIndex) 1029 1030 if !floydSteinberg { 1031 continue 1032 } 1033 er -= palette[bestIndex][0] 1034 eg -= palette[bestIndex][1] 1035 eb -= palette[bestIndex][2] 1036 ea -= palette[bestIndex][3] 1037 1038 } else { 1039 out.R = uint16(er) 1040 out.G = uint16(eg) 1041 out.B = uint16(eb) 1042 out.A = uint16(ea) 1043 // The third argument is &out instead of out (and out is 1044 // declared outside of the inner loop) to avoid the implicit 1045 // conversion to color.Color here allocating memory in the 1046 // inner loop if sizeof(color.RGBA64) > sizeof(uintptr). 1047 dst.Set(r.Min.X+x, r.Min.Y+y, &out) 1048 1049 if !floydSteinberg { 1050 continue 1051 } 1052 sr, sg, sb, sa = dst.At(r.Min.X+x, r.Min.Y+y).RGBA() 1053 er -= int32(sr) 1054 eg -= int32(sg) 1055 eb -= int32(sb) 1056 ea -= int32(sa) 1057 } 1058 1059 // Propagate the Floyd-Steinberg quantization error. 1060 quantErrorNext[x+0][0] += er * 3 1061 quantErrorNext[x+0][1] += eg * 3 1062 quantErrorNext[x+0][2] += eb * 3 1063 quantErrorNext[x+0][3] += ea * 3 1064 quantErrorNext[x+1][0] += er * 5 1065 quantErrorNext[x+1][1] += eg * 5 1066 quantErrorNext[x+1][2] += eb * 5 1067 quantErrorNext[x+1][3] += ea * 5 1068 quantErrorNext[x+2][0] += er * 1 1069 quantErrorNext[x+2][1] += eg * 1 1070 quantErrorNext[x+2][2] += eb * 1 1071 quantErrorNext[x+2][3] += ea * 1 1072 quantErrorCurr[x+2][0] += er * 7 1073 quantErrorCurr[x+2][1] += eg * 7 1074 quantErrorCurr[x+2][2] += eb * 7 1075 quantErrorCurr[x+2][3] += ea * 7 1076 } 1077 1078 // Recycle the quantization error buffers. 1079 if floydSteinberg { 1080 quantErrorCurr, quantErrorNext = quantErrorNext, quantErrorCurr 1081 clear(quantErrorNext) 1082 } 1083 } 1084} 1085