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.
5 // Package draw provides basic graphics and drawing primitives,
6 // in the style of the Plan 9 graphics library
7 // (see http://plan9.bell-labs.com/magic/man2html/2/draw)
8 // and the X Render extension.
16 // m is the maximum color value returned by image.Color.RGBA.
19 // A Porter-Duff compositing operator.
23 // Over specifies ``(src in mask) over dst''.
25 // Src specifies ``src in mask''.
29 var zeroColor image.Color = image.AlphaColor{0}
31 // A draw.Image is an image.Image with a Set method to change a single pixel.
32 type Image interface {
34 Set(x, y int, c image.Color)
37 // Draw calls DrawMask with a nil mask and an Over op.
38 func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point) {
39 DrawMask(dst, r, src, sp, nil, image.ZP, Over)
42 // DrawMask aligns r.Min in dst with sp in src and mp in mask and then replaces the rectangle r
43 // in dst with the result of a Porter-Duff composition. A nil mask is treated as opaque.
44 func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) {
46 dx, dy := sb.Max.X-sp.X, sb.Max.Y-sp.Y
49 if dx > mb.Max.X-mp.X {
52 if dy > mb.Max.Y-mp.Y {
57 r.Max.X = r.Min.X + dx
60 r.Max.Y = r.Min.Y + dy
62 r = r.Intersect(dst.Bounds())
67 // Fast paths for special cases. If none of them apply, then we fall back to a general but slow implementation.
68 if dst0, ok := dst.(*image.RGBA); ok {
71 switch src0 := src.(type) {
72 case *image.ColorImage:
73 drawFillOver(dst0, r, src0)
76 drawCopyOver(dst0, r, src0, sp)
79 drawNRGBAOver(dst0, r, src0, sp)
82 drawYCbCr(dst0, r, src0, sp)
85 } else if mask0, ok := mask.(*image.Alpha); ok {
86 switch src0 := src.(type) {
87 case *image.ColorImage:
88 drawGlyphOver(dst0, r, src0, mask0, mp)
94 switch src0 := src.(type) {
95 case *image.ColorImage:
96 drawFillSrc(dst0, r, src0)
99 drawCopySrc(dst0, r, src0, sp)
102 drawNRGBASrc(dst0, r, src0, sp)
105 drawYCbCr(dst0, r, src0, sp)
110 drawRGBA(dst0, r, src, sp, mask, mp, op)
114 x0, x1, dx := r.Min.X, r.Max.X, 1
115 y0, y1, dy := r.Min.Y, r.Max.Y, 1
116 if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) {
117 // Rectangles overlap: process backward?
118 if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X {
119 x0, x1, dx = x1-1, x0-1, -1
120 y0, y1, dy = y1-1, y0-1, -1
124 var out *image.RGBA64Color
125 sy := sp.Y + y0 - r.Min.Y
126 my := mp.Y + y0 - r.Min.Y
127 for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy {
128 sx := sp.X + x0 - r.Min.X
129 mx := mp.X + x0 - r.Min.X
130 for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx {
133 _, _, _, ma = mask.At(mx, my).RGBA()
140 dst.Set(x, y, zeroColor)
142 case ma == m && op == Src:
143 dst.Set(x, y, src.At(sx, sy))
145 sr, sg, sb, sa := src.At(sx, sy).RGBA()
147 out = new(image.RGBA64Color)
150 dr, dg, db, da := dst.At(x, y).RGBA()
151 a := m - (sa * ma / m)
152 out.R = uint16((dr*a + sr*ma) / m)
153 out.G = uint16((dg*a + sg*ma) / m)
154 out.B = uint16((db*a + sb*ma) / m)
155 out.A = uint16((da*a + sa*ma) / m)
157 out.R = uint16(sr * ma / m)
158 out.G = uint16(sg * ma / m)
159 out.B = uint16(sb * ma / m)
160 out.A = uint16(sa * ma / m)
168 func drawFillOver(dst *image.RGBA, r image.Rectangle, src *image.ColorImage) {
169 cr, cg, cb, ca := src.RGBA()
170 // The 0x101 is here for the same reason as in drawRGBA.
171 a := (m - ca) * 0x101
172 x0, x1 := r.Min.X, r.Max.X
173 y0, y1 := r.Min.Y, r.Max.Y
174 for y := y0; y != y1; y++ {
175 dbase := y * dst.Stride
176 dpix := dst.Pix[dbase+x0 : dbase+x1]
177 for i, rgba := range dpix {
178 dr := (uint32(rgba.R)*a)/m + cr
179 dg := (uint32(rgba.G)*a)/m + cg
180 db := (uint32(rgba.B)*a)/m + cb
181 da := (uint32(rgba.A)*a)/m + ca
182 dpix[i] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)}
187 func drawCopyOver(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point) {
188 dx0, dx1 := r.Min.X, r.Max.X
189 dy0, dy1 := r.Min.Y, r.Max.Y
191 sx0, sx1 := sp.X, sp.X+dx1-dx0
192 d0 := dy0*dst.Stride + dx0
193 d1 := dy0*dst.Stride + dx1
194 s0 := sp.Y*src.Stride + sx0
195 s1 := sp.Y*src.Stride + sx1
200 if r.Min.Y < sp.Y || r.Min.Y == sp.Y && r.Min.X <= sp.X {
203 i0, i1, idelta = 0, d1-d0, +1
205 // If the source start point is higher than the destination start point, or equal height but to the left,
206 // then we compose the rows in right-to-left, bottom-up order instead of left-to-right, top-down.
207 d0 += (nrows - 1) * dst.Stride
208 d1 += (nrows - 1) * dst.Stride
209 s0 += (nrows - 1) * src.Stride
210 s1 += (nrows - 1) * src.Stride
213 i0, i1, idelta = d1-d0-1, -1, -1
215 for ; nrows > 0; nrows-- {
216 dpix := dst.Pix[d0:d1]
217 spix := src.Pix[s0:s1]
218 for i := i0; i != i1; i += idelta {
219 // For unknown reasons, even though both dpix[i] and spix[i] are
220 // image.RGBAColors, on an x86 CPU it seems fastest to call RGBA
221 // for the source but to do it manually for the destination.
222 sr, sg, sb, sa := spix[i].RGBA()
228 // The 0x101 is here for the same reason as in drawRGBA.
229 a := (m - sa) * 0x101
234 dpix[i] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)}
243 func drawNRGBAOver(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image.Point) {
244 for y, sy := r.Min.Y, sp.Y; y != r.Max.Y; y, sy = y+1, sy+1 {
245 dpix := dst.Pix[y*dst.Stride : (y+1)*dst.Stride]
246 spix := src.Pix[sy*src.Stride : (sy+1)*src.Stride]
247 for x, sx := r.Min.X, sp.X; x != r.Max.X; x, sx = x+1, sx+1 {
248 // Convert from non-premultiplied color to pre-multiplied color.
249 // The order of operations here is to match the NRGBAColor.RGBA
250 // method in image/color.go.
252 sa := uint32(snrgba.A)
253 sr := uint32(snrgba.R) * 0x101 * sa / 0xff
254 sg := uint32(snrgba.G) * 0x101 * sa / 0xff
255 sb := uint32(snrgba.B) * 0x101 * sa / 0xff
263 a := (m - sa) * 0x101
264 dr = (dr*a + sr*m) / m
265 dg = (dg*a + sg*m) / m
266 db = (db*a + sb*m) / m
267 da = (da*a + sa*m) / m
268 dpix[x] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)}
273 func drawGlyphOver(dst *image.RGBA, r image.Rectangle, src *image.ColorImage, mask *image.Alpha, mp image.Point) {
274 x0, x1 := r.Min.X, r.Max.X
275 y0, y1 := r.Min.Y, r.Max.Y
276 cr, cg, cb, ca := src.RGBA()
277 for y, my := y0, mp.Y; y != y1; y, my = y+1, my+1 {
278 dbase := y * dst.Stride
279 dpix := dst.Pix[dbase+x0 : dbase+x1]
280 mbase := my * mask.Stride
281 mpix := mask.Pix[mbase+mp.X:]
282 for i, rgba := range dpix {
283 ma := uint32(mpix[i].A)
292 // The 0x101 is here for the same reason as in drawRGBA.
293 a := (m - (ca * ma / m)) * 0x101
294 dr = (dr*a + cr*ma) / m
295 dg = (dg*a + cg*ma) / m
296 db = (db*a + cb*ma) / m
297 da = (da*a + ca*ma) / m
298 dpix[i] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)}
303 func drawFillSrc(dst *image.RGBA, r image.Rectangle, src *image.ColorImage) {
307 cr, cg, cb, ca := src.RGBA()
308 color := image.RGBAColor{uint8(cr >> 8), uint8(cg >> 8), uint8(cb >> 8), uint8(ca >> 8)}
309 // The built-in copy function is faster than a straightforward for loop to fill the destination with
310 // the color, but copy requires a slice source. We therefore use a for loop to fill the first row, and
311 // then use the first row as the slice source for the remaining rows.
312 dx0, dx1 := r.Min.X, r.Max.X
313 dy0, dy1 := r.Min.Y, r.Max.Y
314 dbase := dy0 * dst.Stride
315 i0, i1 := dbase+dx0, dbase+dx1
316 firstRow := dst.Pix[i0:i1]
317 for i := range firstRow {
320 for y := dy0 + 1; y < dy1; y++ {
323 copy(dst.Pix[i0:i1], firstRow)
327 func drawCopySrc(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point) {
328 dx0, dx1 := r.Min.X, r.Max.X
329 dy0, dy1 := r.Min.Y, r.Max.Y
331 sx0, sx1 := sp.X, sp.X+dx1-dx0
332 d0 := dy0*dst.Stride + dx0
333 d1 := dy0*dst.Stride + dx1
334 s0 := sp.Y*src.Stride + sx0
335 s1 := sp.Y*src.Stride + sx1
336 var ddelta, sdelta int
341 // If the source start point is higher than the destination start point, then we compose the rows
342 // in bottom-up order instead of top-down. Unlike the drawCopyOver function, we don't have to
343 // check the x co-ordinates because the built-in copy function can handle overlapping slices.
344 d0 += (nrows - 1) * dst.Stride
345 d1 += (nrows - 1) * dst.Stride
346 s0 += (nrows - 1) * src.Stride
347 s1 += (nrows - 1) * src.Stride
351 for ; nrows > 0; nrows-- {
352 copy(dst.Pix[d0:d1], src.Pix[s0:s1])
360 func drawNRGBASrc(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image.Point) {
361 for y, sy := r.Min.Y, sp.Y; y != r.Max.Y; y, sy = y+1, sy+1 {
362 dpix := dst.Pix[y*dst.Stride : (y+1)*dst.Stride]
363 spix := src.Pix[sy*src.Stride : (sy+1)*src.Stride]
364 for x, sx := r.Min.X, sp.X; x != r.Max.X; x, sx = x+1, sx+1 {
365 // Convert from non-premultiplied color to pre-multiplied color.
366 // The order of operations here is to match the NRGBAColor.RGBA
367 // method in image/color.go.
369 sa := uint32(snrgba.A)
370 sr := uint32(snrgba.R) * 0x101 * sa / 0xff
371 sg := uint32(snrgba.G) * 0x101 * sa / 0xff
372 sb := uint32(snrgba.B) * 0x101 * sa / 0xff
375 dpix[x] = image.RGBAColor{uint8(sr >> 8), uint8(sg >> 8), uint8(sb >> 8), uint8(sa >> 8)}
380 func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *ycbcr.YCbCr, sp image.Point) {
381 // A YCbCr image is always fully opaque, and so if the mask is implicitly nil
382 // (i.e. fully opaque) then the op is effectively always Src.
387 switch src.SubsampleRatio {
388 case ycbcr.SubsampleRatio422:
389 for y, sy := r.Min.Y, sp.Y; y != r.Max.Y; y, sy = y+1, sy+1 {
390 dpix := dst.Pix[y*dst.Stride : (y+1)*dst.Stride]
391 for x, sx := r.Min.X, sp.X; x != r.Max.X; x, sx = x+1, sx+1 {
393 yy = src.Y[sy*src.YStride+sx]
394 cb = src.Cb[sy*src.CStride+i]
395 cr = src.Cr[sy*src.CStride+i]
396 rr, gg, bb = ycbcr.YCbCrToRGB(yy, cb, cr)
397 dpix[x] = image.RGBAColor{rr, gg, bb, 255}
400 case ycbcr.SubsampleRatio420:
401 for y, sy := r.Min.Y, sp.Y; y != r.Max.Y; y, sy = y+1, sy+1 {
402 dpix := dst.Pix[y*dst.Stride : (y+1)*dst.Stride]
403 for x, sx := r.Min.X, sp.X; x != r.Max.X; x, sx = x+1, sx+1 {
405 yy = src.Y[sy*src.YStride+sx]
406 cb = src.Cb[j*src.CStride+i]
407 cr = src.Cr[j*src.CStride+i]
408 rr, gg, bb = ycbcr.YCbCrToRGB(yy, cb, cr)
409 dpix[x] = image.RGBAColor{rr, gg, bb, 255}
413 // Default to 4:4:4 subsampling.
414 for y, sy := r.Min.Y, sp.Y; y != r.Max.Y; y, sy = y+1, sy+1 {
415 dpix := dst.Pix[y*dst.Stride : (y+1)*dst.Stride]
416 for x, sx := r.Min.X, sp.X; x != r.Max.X; x, sx = x+1, sx+1 {
417 yy = src.Y[sy*src.YStride+sx]
418 cb = src.Cb[sy*src.CStride+sx]
419 cr = src.Cr[sy*src.CStride+sx]
420 rr, gg, bb = ycbcr.YCbCrToRGB(yy, cb, cr)
421 dpix[x] = image.RGBAColor{rr, gg, bb, 255}
427 func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) {
428 x0, x1, dx := r.Min.X, r.Max.X, 1
429 y0, y1, dy := r.Min.Y, r.Max.Y, 1
430 if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) {
431 if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X {
432 x0, x1, dx = x1-1, x0-1, -1
433 y0, y1, dy = y1-1, y0-1, -1
437 sy := sp.Y + y0 - r.Min.Y
438 my := mp.Y + y0 - r.Min.Y
439 for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy {
440 sx := sp.X + x0 - r.Min.X
441 mx := mp.X + x0 - r.Min.X
442 dpix := dst.Pix[y*dst.Stride : (y+1)*dst.Stride]
443 for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx {
446 _, _, _, ma = mask.At(mx, my).RGBA()
448 sr, sg, sb, sa := src.At(sx, sy).RGBA()
449 var dr, dg, db, da uint32
456 // dr, dg, db and da are all 8-bit color at the moment, ranging in [0,255].
457 // We work in 16-bit color, and so would normally do:
459 // and similarly for dg, db and da, but instead we multiply a
460 // (which is a 16-bit color, ranging in [0,65535]) by 0x101.
461 // This yields the same result, but is fewer arithmetic operations.
462 a := (m - (sa * ma / m)) * 0x101
463 dr = (dr*a + sr*ma) / m
464 dg = (dg*a + sg*ma) / m
465 db = (db*a + sb*ma) / m
466 da = (da*a + sa*ma) / m
473 dpix[x] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)}