github.com/zach-klippenstein/go@v0.0.0-20150108044943-fcfbeb3adf58/src/image/draw/draw.go (about) 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 // http://golang.org/doc/articles/image_draw.html 9 package draw 10 11 import ( 12 "image" 13 "image/color" 14 ) 15 16 // m is the maximum color value returned by image.Color.RGBA. 17 const m = 1<<16 - 1 18 19 // Image is an image.Image with a Set method to change a single pixel. 20 type Image interface { 21 image.Image 22 Set(x, y int, c color.Color) 23 } 24 25 // Quantizer produces a palette for an image. 26 type Quantizer interface { 27 // Quantize appends up to cap(p) - len(p) colors to p and returns the 28 // updated palette suitable for converting m to a paletted image. 29 Quantize(p color.Palette, m image.Image) color.Palette 30 } 31 32 // Op is a Porter-Duff compositing operator. 33 type Op int 34 35 const ( 36 // Over specifies ``(src in mask) over dst''. 37 Over Op = iota 38 // Src specifies ``src in mask''. 39 Src 40 ) 41 42 // Draw implements the Drawer interface by calling the Draw function with this 43 // Op. 44 func (op Op) Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point) { 45 DrawMask(dst, r, src, sp, nil, image.Point{}, op) 46 } 47 48 // Drawer contains the Draw method. 49 type Drawer interface { 50 // Draw aligns r.Min in dst with sp in src and then replaces the 51 // rectangle r in dst with the result of drawing src on dst. 52 Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point) 53 } 54 55 // FloydSteinberg is a Drawer that is the Src Op with Floyd-Steinberg error 56 // diffusion. 57 var FloydSteinberg Drawer = floydSteinberg{} 58 59 type floydSteinberg struct{} 60 61 func (floydSteinberg) Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point) { 62 clip(dst, &r, src, &sp, nil, nil) 63 if r.Empty() { 64 return 65 } 66 drawPaletted(dst, r, src, sp, true) 67 } 68 69 // clip clips r against each image's bounds (after translating into the 70 // destination image's co-ordinate space) and shifts the points sp and mp by 71 // the same amount as the change in r.Min. 72 func clip(dst Image, r *image.Rectangle, src image.Image, sp *image.Point, mask image.Image, mp *image.Point) { 73 orig := r.Min 74 *r = r.Intersect(dst.Bounds()) 75 *r = r.Intersect(src.Bounds().Add(orig.Sub(*sp))) 76 if mask != nil { 77 *r = r.Intersect(mask.Bounds().Add(orig.Sub(*mp))) 78 } 79 dx := r.Min.X - orig.X 80 dy := r.Min.Y - orig.Y 81 if dx == 0 && dy == 0 { 82 return 83 } 84 (*sp).X += dx 85 (*sp).Y += dy 86 if mp != nil { 87 (*mp).X += dx 88 (*mp).Y += dy 89 } 90 } 91 92 func processBackward(dst Image, r image.Rectangle, src image.Image, sp image.Point) bool { 93 return image.Image(dst) == src && 94 r.Overlaps(r.Add(sp.Sub(r.Min))) && 95 (sp.Y < r.Min.Y || (sp.Y == r.Min.Y && sp.X < r.Min.X)) 96 } 97 98 // Draw calls DrawMask with a nil mask. 99 func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point, op Op) { 100 DrawMask(dst, r, src, sp, nil, image.Point{}, op) 101 } 102 103 // DrawMask aligns r.Min in dst with sp in src and mp in mask and then replaces the rectangle r 104 // in dst with the result of a Porter-Duff composition. A nil mask is treated as opaque. 105 func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) { 106 clip(dst, &r, src, &sp, mask, &mp) 107 if r.Empty() { 108 return 109 } 110 111 // Fast paths for special cases. If none of them apply, then we fall back to a general but slow implementation. 112 switch dst0 := dst.(type) { 113 case *image.RGBA: 114 if op == Over { 115 if mask == nil { 116 switch src0 := src.(type) { 117 case *image.Uniform: 118 drawFillOver(dst0, r, src0) 119 return 120 case *image.RGBA: 121 drawCopyOver(dst0, r, src0, sp) 122 return 123 case *image.NRGBA: 124 drawNRGBAOver(dst0, r, src0, sp) 125 return 126 case *image.YCbCr: 127 if drawYCbCr(dst0, r, src0, sp) { 128 return 129 } 130 } 131 } else if mask0, ok := mask.(*image.Alpha); ok { 132 switch src0 := src.(type) { 133 case *image.Uniform: 134 drawGlyphOver(dst0, r, src0, mask0, mp) 135 return 136 } 137 } 138 } else { 139 if mask == nil { 140 switch src0 := src.(type) { 141 case *image.Uniform: 142 drawFillSrc(dst0, r, src0) 143 return 144 case *image.RGBA: 145 drawCopySrc(dst0, r, src0, sp) 146 return 147 case *image.NRGBA: 148 drawNRGBASrc(dst0, r, src0, sp) 149 return 150 case *image.YCbCr: 151 if drawYCbCr(dst0, r, src0, sp) { 152 return 153 } 154 } 155 } 156 } 157 drawRGBA(dst0, r, src, sp, mask, mp, op) 158 return 159 case *image.Paletted: 160 if op == Src && mask == nil && !processBackward(dst, r, src, sp) { 161 drawPaletted(dst0, r, src, sp, false) 162 } 163 } 164 165 x0, x1, dx := r.Min.X, r.Max.X, 1 166 y0, y1, dy := r.Min.Y, r.Max.Y, 1 167 if processBackward(dst, r, src, sp) { 168 x0, x1, dx = x1-1, x0-1, -1 169 y0, y1, dy = y1-1, y0-1, -1 170 } 171 172 var out color.RGBA64 173 sy := sp.Y + y0 - r.Min.Y 174 my := mp.Y + y0 - r.Min.Y 175 for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { 176 sx := sp.X + x0 - r.Min.X 177 mx := mp.X + x0 - r.Min.X 178 for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx { 179 ma := uint32(m) 180 if mask != nil { 181 _, _, _, ma = mask.At(mx, my).RGBA() 182 } 183 switch { 184 case ma == 0: 185 if op == Over { 186 // No-op. 187 } else { 188 dst.Set(x, y, color.Transparent) 189 } 190 case ma == m && op == Src: 191 dst.Set(x, y, src.At(sx, sy)) 192 default: 193 sr, sg, sb, sa := src.At(sx, sy).RGBA() 194 if op == Over { 195 dr, dg, db, da := dst.At(x, y).RGBA() 196 a := m - (sa * ma / m) 197 out.R = uint16((dr*a + sr*ma) / m) 198 out.G = uint16((dg*a + sg*ma) / m) 199 out.B = uint16((db*a + sb*ma) / m) 200 out.A = uint16((da*a + sa*ma) / m) 201 } else { 202 out.R = uint16(sr * ma / m) 203 out.G = uint16(sg * ma / m) 204 out.B = uint16(sb * ma / m) 205 out.A = uint16(sa * ma / m) 206 } 207 // The third argument is &out instead of out (and out is 208 // declared outside of the inner loop) to avoid the implicit 209 // conversion to color.Color here allocating memory in the 210 // inner loop if sizeof(color.RGBA64) > sizeof(uintptr). 211 dst.Set(x, y, &out) 212 } 213 } 214 } 215 } 216 217 func drawFillOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform) { 218 sr, sg, sb, sa := src.RGBA() 219 // The 0x101 is here for the same reason as in drawRGBA. 220 a := (m - sa) * 0x101 221 i0 := dst.PixOffset(r.Min.X, r.Min.Y) 222 i1 := i0 + r.Dx()*4 223 for y := r.Min.Y; y != r.Max.Y; y++ { 224 for i := i0; i < i1; i += 4 { 225 dr := uint32(dst.Pix[i+0]) 226 dg := uint32(dst.Pix[i+1]) 227 db := uint32(dst.Pix[i+2]) 228 da := uint32(dst.Pix[i+3]) 229 230 dst.Pix[i+0] = uint8((dr*a/m + sr) >> 8) 231 dst.Pix[i+1] = uint8((dg*a/m + sg) >> 8) 232 dst.Pix[i+2] = uint8((db*a/m + sb) >> 8) 233 dst.Pix[i+3] = uint8((da*a/m + sa) >> 8) 234 } 235 i0 += dst.Stride 236 i1 += dst.Stride 237 } 238 } 239 240 func drawFillSrc(dst *image.RGBA, r image.Rectangle, src *image.Uniform) { 241 sr, sg, sb, sa := src.RGBA() 242 // The built-in copy function is faster than a straightforward for loop to fill the destination with 243 // the color, but copy requires a slice source. We therefore use a for loop to fill the first row, and 244 // then use the first row as the slice source for the remaining rows. 245 i0 := dst.PixOffset(r.Min.X, r.Min.Y) 246 i1 := i0 + r.Dx()*4 247 for i := i0; i < i1; i += 4 { 248 dst.Pix[i+0] = uint8(sr >> 8) 249 dst.Pix[i+1] = uint8(sg >> 8) 250 dst.Pix[i+2] = uint8(sb >> 8) 251 dst.Pix[i+3] = uint8(sa >> 8) 252 } 253 firstRow := dst.Pix[i0:i1] 254 for y := r.Min.Y + 1; y < r.Max.Y; y++ { 255 i0 += dst.Stride 256 i1 += dst.Stride 257 copy(dst.Pix[i0:i1], firstRow) 258 } 259 } 260 261 func drawCopyOver(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point) { 262 dx, dy := r.Dx(), r.Dy() 263 d0 := dst.PixOffset(r.Min.X, r.Min.Y) 264 s0 := src.PixOffset(sp.X, sp.Y) 265 var ( 266 ddelta, sdelta int 267 i0, i1, idelta int 268 ) 269 if r.Min.Y < sp.Y || r.Min.Y == sp.Y && r.Min.X <= sp.X { 270 ddelta = dst.Stride 271 sdelta = src.Stride 272 i0, i1, idelta = 0, dx*4, +4 273 } else { 274 // If the source start point is higher than the destination start point, or equal height but to the left, 275 // then we compose the rows in right-to-left, bottom-up order instead of left-to-right, top-down. 276 d0 += (dy - 1) * dst.Stride 277 s0 += (dy - 1) * src.Stride 278 ddelta = -dst.Stride 279 sdelta = -src.Stride 280 i0, i1, idelta = (dx-1)*4, -4, -4 281 } 282 for ; dy > 0; dy-- { 283 dpix := dst.Pix[d0:] 284 spix := src.Pix[s0:] 285 for i := i0; i != i1; i += idelta { 286 sr := uint32(spix[i+0]) * 0x101 287 sg := uint32(spix[i+1]) * 0x101 288 sb := uint32(spix[i+2]) * 0x101 289 sa := uint32(spix[i+3]) * 0x101 290 291 dr := uint32(dpix[i+0]) 292 dg := uint32(dpix[i+1]) 293 db := uint32(dpix[i+2]) 294 da := uint32(dpix[i+3]) 295 296 // The 0x101 is here for the same reason as in drawRGBA. 297 a := (m - sa) * 0x101 298 299 dpix[i+0] = uint8((dr*a/m + sr) >> 8) 300 dpix[i+1] = uint8((dg*a/m + sg) >> 8) 301 dpix[i+2] = uint8((db*a/m + sb) >> 8) 302 dpix[i+3] = uint8((da*a/m + sa) >> 8) 303 } 304 d0 += ddelta 305 s0 += sdelta 306 } 307 } 308 309 func drawCopySrc(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point) { 310 n, dy := 4*r.Dx(), r.Dy() 311 d0 := dst.PixOffset(r.Min.X, r.Min.Y) 312 s0 := src.PixOffset(sp.X, sp.Y) 313 var ddelta, sdelta int 314 if r.Min.Y <= sp.Y { 315 ddelta = dst.Stride 316 sdelta = src.Stride 317 } else { 318 // If the source start point is higher than the destination start point, then we compose the rows 319 // in bottom-up order instead of top-down. Unlike the drawCopyOver function, we don't have to 320 // check the x co-ordinates because the built-in copy function can handle overlapping slices. 321 d0 += (dy - 1) * dst.Stride 322 s0 += (dy - 1) * src.Stride 323 ddelta = -dst.Stride 324 sdelta = -src.Stride 325 } 326 for ; dy > 0; dy-- { 327 copy(dst.Pix[d0:d0+n], src.Pix[s0:s0+n]) 328 d0 += ddelta 329 s0 += sdelta 330 } 331 } 332 333 func drawNRGBAOver(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image.Point) { 334 i0 := (r.Min.X - dst.Rect.Min.X) * 4 335 i1 := (r.Max.X - dst.Rect.Min.X) * 4 336 si0 := (sp.X - src.Rect.Min.X) * 4 337 yMax := r.Max.Y - dst.Rect.Min.Y 338 339 y := r.Min.Y - dst.Rect.Min.Y 340 sy := sp.Y - src.Rect.Min.Y 341 for ; y != yMax; y, sy = y+1, sy+1 { 342 dpix := dst.Pix[y*dst.Stride:] 343 spix := src.Pix[sy*src.Stride:] 344 345 for i, si := i0, si0; i < i1; i, si = i+4, si+4 { 346 // Convert from non-premultiplied color to pre-multiplied color. 347 sa := uint32(spix[si+3]) * 0x101 348 sr := uint32(spix[si+0]) * sa / 0xff 349 sg := uint32(spix[si+1]) * sa / 0xff 350 sb := uint32(spix[si+2]) * sa / 0xff 351 352 dr := uint32(dpix[i+0]) 353 dg := uint32(dpix[i+1]) 354 db := uint32(dpix[i+2]) 355 da := uint32(dpix[i+3]) 356 357 // The 0x101 is here for the same reason as in drawRGBA. 358 a := (m - sa) * 0x101 359 360 dpix[i+0] = uint8((dr*a/m + sr) >> 8) 361 dpix[i+1] = uint8((dg*a/m + sg) >> 8) 362 dpix[i+2] = uint8((db*a/m + sb) >> 8) 363 dpix[i+3] = uint8((da*a/m + sa) >> 8) 364 } 365 } 366 } 367 368 func drawNRGBASrc(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image.Point) { 369 i0 := (r.Min.X - dst.Rect.Min.X) * 4 370 i1 := (r.Max.X - dst.Rect.Min.X) * 4 371 si0 := (sp.X - src.Rect.Min.X) * 4 372 yMax := r.Max.Y - dst.Rect.Min.Y 373 374 y := r.Min.Y - dst.Rect.Min.Y 375 sy := sp.Y - src.Rect.Min.Y 376 for ; y != yMax; y, sy = y+1, sy+1 { 377 dpix := dst.Pix[y*dst.Stride:] 378 spix := src.Pix[sy*src.Stride:] 379 380 for i, si := i0, si0; i < i1; i, si = i+4, si+4 { 381 // Convert from non-premultiplied color to pre-multiplied color. 382 sa := uint32(spix[si+3]) * 0x101 383 sr := uint32(spix[si+0]) * sa / 0xff 384 sg := uint32(spix[si+1]) * sa / 0xff 385 sb := uint32(spix[si+2]) * sa / 0xff 386 387 dpix[i+0] = uint8(sr >> 8) 388 dpix[i+1] = uint8(sg >> 8) 389 dpix[i+2] = uint8(sb >> 8) 390 dpix[i+3] = uint8(sa >> 8) 391 } 392 } 393 } 394 395 func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Point) (ok bool) { 396 // An image.YCbCr is always fully opaque, and so if the mask is implicitly nil 397 // (i.e. fully opaque) then the op is effectively always Src. 398 x0 := (r.Min.X - dst.Rect.Min.X) * 4 399 x1 := (r.Max.X - dst.Rect.Min.X) * 4 400 y0 := r.Min.Y - dst.Rect.Min.Y 401 y1 := r.Max.Y - dst.Rect.Min.Y 402 switch src.SubsampleRatio { 403 case image.YCbCrSubsampleRatio444: 404 for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 { 405 dpix := dst.Pix[y*dst.Stride:] 406 yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X) 407 ci := (sy-src.Rect.Min.Y)*src.CStride + (sp.X - src.Rect.Min.X) 408 for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 { 409 rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci]) 410 dpix[x+0] = rr 411 dpix[x+1] = gg 412 dpix[x+2] = bb 413 dpix[x+3] = 255 414 } 415 } 416 case image.YCbCrSubsampleRatio422: 417 for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 { 418 dpix := dst.Pix[y*dst.Stride:] 419 yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X) 420 ciBase := (sy-src.Rect.Min.Y)*src.CStride - src.Rect.Min.X/2 421 for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 { 422 ci := ciBase + sx/2 423 rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci]) 424 dpix[x+0] = rr 425 dpix[x+1] = gg 426 dpix[x+2] = bb 427 dpix[x+3] = 255 428 } 429 } 430 case image.YCbCrSubsampleRatio420: 431 for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 { 432 dpix := dst.Pix[y*dst.Stride:] 433 yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X) 434 ciBase := (sy/2-src.Rect.Min.Y/2)*src.CStride - src.Rect.Min.X/2 435 for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 { 436 ci := ciBase + sx/2 437 rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci]) 438 dpix[x+0] = rr 439 dpix[x+1] = gg 440 dpix[x+2] = bb 441 dpix[x+3] = 255 442 } 443 } 444 case image.YCbCrSubsampleRatio440: 445 for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 { 446 dpix := dst.Pix[y*dst.Stride:] 447 yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X) 448 ci := (sy/2-src.Rect.Min.Y/2)*src.CStride + (sp.X - src.Rect.Min.X) 449 for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 { 450 rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci]) 451 dpix[x+0] = rr 452 dpix[x+1] = gg 453 dpix[x+2] = bb 454 dpix[x+3] = 255 455 } 456 } 457 default: 458 return false 459 } 460 return true 461 } 462 463 func drawGlyphOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform, mask *image.Alpha, mp image.Point) { 464 i0 := dst.PixOffset(r.Min.X, r.Min.Y) 465 i1 := i0 + r.Dx()*4 466 mi0 := mask.PixOffset(mp.X, mp.Y) 467 sr, sg, sb, sa := src.RGBA() 468 for y, my := r.Min.Y, mp.Y; y != r.Max.Y; y, my = y+1, my+1 { 469 for i, mi := i0, mi0; i < i1; i, mi = i+4, mi+1 { 470 ma := uint32(mask.Pix[mi]) 471 if ma == 0 { 472 continue 473 } 474 ma |= ma << 8 475 476 dr := uint32(dst.Pix[i+0]) 477 dg := uint32(dst.Pix[i+1]) 478 db := uint32(dst.Pix[i+2]) 479 da := uint32(dst.Pix[i+3]) 480 481 // The 0x101 is here for the same reason as in drawRGBA. 482 a := (m - (sa * ma / m)) * 0x101 483 484 dst.Pix[i+0] = uint8((dr*a + sr*ma) / m >> 8) 485 dst.Pix[i+1] = uint8((dg*a + sg*ma) / m >> 8) 486 dst.Pix[i+2] = uint8((db*a + sb*ma) / m >> 8) 487 dst.Pix[i+3] = uint8((da*a + sa*ma) / m >> 8) 488 } 489 i0 += dst.Stride 490 i1 += dst.Stride 491 mi0 += mask.Stride 492 } 493 } 494 495 func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) { 496 x0, x1, dx := r.Min.X, r.Max.X, 1 497 y0, y1, dy := r.Min.Y, r.Max.Y, 1 498 if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) { 499 if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X { 500 x0, x1, dx = x1-1, x0-1, -1 501 y0, y1, dy = y1-1, y0-1, -1 502 } 503 } 504 505 sy := sp.Y + y0 - r.Min.Y 506 my := mp.Y + y0 - r.Min.Y 507 sx0 := sp.X + x0 - r.Min.X 508 mx0 := mp.X + x0 - r.Min.X 509 sx1 := sx0 + (x1 - x0) 510 i0 := dst.PixOffset(x0, y0) 511 di := dx * 4 512 for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { 513 for i, sx, mx := i0, sx0, mx0; sx != sx1; i, sx, mx = i+di, sx+dx, mx+dx { 514 ma := uint32(m) 515 if mask != nil { 516 _, _, _, ma = mask.At(mx, my).RGBA() 517 } 518 sr, sg, sb, sa := src.At(sx, sy).RGBA() 519 if op == Over { 520 dr := uint32(dst.Pix[i+0]) 521 dg := uint32(dst.Pix[i+1]) 522 db := uint32(dst.Pix[i+2]) 523 da := uint32(dst.Pix[i+3]) 524 525 // dr, dg, db and da are all 8-bit color at the moment, ranging in [0,255]. 526 // We work in 16-bit color, and so would normally do: 527 // dr |= dr << 8 528 // and similarly for dg, db and da, but instead we multiply a 529 // (which is a 16-bit color, ranging in [0,65535]) by 0x101. 530 // This yields the same result, but is fewer arithmetic operations. 531 a := (m - (sa * ma / m)) * 0x101 532 533 dst.Pix[i+0] = uint8((dr*a + sr*ma) / m >> 8) 534 dst.Pix[i+1] = uint8((dg*a + sg*ma) / m >> 8) 535 dst.Pix[i+2] = uint8((db*a + sb*ma) / m >> 8) 536 dst.Pix[i+3] = uint8((da*a + sa*ma) / m >> 8) 537 538 } else { 539 dst.Pix[i+0] = uint8(sr * ma / m >> 8) 540 dst.Pix[i+1] = uint8(sg * ma / m >> 8) 541 dst.Pix[i+2] = uint8(sb * ma / m >> 8) 542 dst.Pix[i+3] = uint8(sa * ma / m >> 8) 543 } 544 } 545 i0 += dy * dst.Stride 546 } 547 } 548 549 // clamp clamps i to the interval [0, 0xffff]. 550 func clamp(i int32) int32 { 551 if i < 0 { 552 return 0 553 } 554 if i > 0xffff { 555 return 0xffff 556 } 557 return i 558 } 559 560 func drawPaletted(dst Image, r image.Rectangle, src image.Image, sp image.Point, floydSteinberg bool) { 561 // TODO(nigeltao): handle the case where the dst and src overlap. 562 // Does it even make sense to try and do Floyd-Steinberg whilst 563 // walking the image backward (right-to-left bottom-to-top)? 564 565 // If dst is an *image.Paletted, we have a fast path for dst.Set and 566 // dst.At. The dst.Set equivalent is a batch version of the algorithm 567 // used by color.Palette's Index method in image/color/color.go, plus 568 // optional Floyd-Steinberg error diffusion. 569 palette, pix, stride := [][3]int32(nil), []byte(nil), 0 570 if p, ok := dst.(*image.Paletted); ok { 571 palette = make([][3]int32, len(p.Palette)) 572 for i, col := range p.Palette { 573 r, g, b, _ := col.RGBA() 574 palette[i][0] = int32(r) 575 palette[i][1] = int32(g) 576 palette[i][2] = int32(b) 577 } 578 pix, stride = p.Pix[p.PixOffset(r.Min.X, r.Min.Y):], p.Stride 579 } 580 581 // quantErrorCurr and quantErrorNext are the Floyd-Steinberg quantization 582 // errors that have been propagated to the pixels in the current and next 583 // rows. The +2 simplifies calculation near the edges. 584 var quantErrorCurr, quantErrorNext [][3]int32 585 if floydSteinberg { 586 quantErrorCurr = make([][3]int32, r.Dx()+2) 587 quantErrorNext = make([][3]int32, r.Dx()+2) 588 } 589 590 // Loop over each source pixel. 591 out := color.RGBA64{A: 0xffff} 592 for y := 0; y != r.Dy(); y++ { 593 for x := 0; x != r.Dx(); x++ { 594 // er, eg and eb are the pixel's R,G,B values plus the 595 // optional Floyd-Steinberg error. 596 sr, sg, sb, _ := src.At(sp.X+x, sp.Y+y).RGBA() 597 er, eg, eb := int32(sr), int32(sg), int32(sb) 598 if floydSteinberg { 599 er = clamp(er + quantErrorCurr[x+1][0]/16) 600 eg = clamp(eg + quantErrorCurr[x+1][1]/16) 601 eb = clamp(eb + quantErrorCurr[x+1][2]/16) 602 } 603 604 if palette != nil { 605 // Find the closest palette color in Euclidean R,G,B space: the 606 // one that minimizes sum-squared-difference. We shift by 1 bit 607 // to avoid potential uint32 overflow in sum-squared-difference. 608 // TODO(nigeltao): consider smarter algorithms. 609 bestIndex, bestSSD := 0, uint32(1<<32-1) 610 for index, p := range palette { 611 delta := (er - p[0]) >> 1 612 ssd := uint32(delta * delta) 613 delta = (eg - p[1]) >> 1 614 ssd += uint32(delta * delta) 615 delta = (eb - p[2]) >> 1 616 ssd += uint32(delta * delta) 617 if ssd < bestSSD { 618 bestIndex, bestSSD = index, ssd 619 if ssd == 0 { 620 break 621 } 622 } 623 } 624 pix[y*stride+x] = byte(bestIndex) 625 626 if !floydSteinberg { 627 continue 628 } 629 er -= int32(palette[bestIndex][0]) 630 eg -= int32(palette[bestIndex][1]) 631 eb -= int32(palette[bestIndex][2]) 632 633 } else { 634 out.R = uint16(er) 635 out.G = uint16(eg) 636 out.B = uint16(eb) 637 // The third argument is &out instead of out (and out is 638 // declared outside of the inner loop) to avoid the implicit 639 // conversion to color.Color here allocating memory in the 640 // inner loop if sizeof(color.RGBA64) > sizeof(uintptr). 641 dst.Set(r.Min.X+x, r.Min.Y+y, &out) 642 643 if !floydSteinberg { 644 continue 645 } 646 sr, sg, sb, _ = dst.At(r.Min.X+x, r.Min.Y+y).RGBA() 647 er -= int32(sr) 648 eg -= int32(sg) 649 eb -= int32(sb) 650 } 651 652 // Propagate the Floyd-Steinberg quantization error. 653 quantErrorNext[x+0][0] += er * 3 654 quantErrorNext[x+0][1] += eg * 3 655 quantErrorNext[x+0][2] += eb * 3 656 quantErrorNext[x+1][0] += er * 5 657 quantErrorNext[x+1][1] += eg * 5 658 quantErrorNext[x+1][2] += eb * 5 659 quantErrorNext[x+2][0] += er * 1 660 quantErrorNext[x+2][1] += eg * 1 661 quantErrorNext[x+2][2] += eb * 1 662 quantErrorCurr[x+2][0] += er * 7 663 quantErrorCurr[x+2][1] += eg * 7 664 quantErrorCurr[x+2][2] += eb * 7 665 } 666 667 // Recycle the quantization error buffers. 668 if floydSteinberg { 669 quantErrorCurr, quantErrorNext = quantErrorNext, quantErrorCurr 670 for i := range quantErrorNext { 671 quantErrorNext[i] = [3]int32{} 672 } 673 } 674 } 675 }