Source file src/pkg/image/draw/draw.go
1
2
3
4
5
6
7
8
9 package draw
10
11 import (
12 "image"
13 "image/color"
14 )
15
16
17 const m = 1<<16 - 1
18
19
20 type Op int
21
22 const (
23
24 Over Op = iota
25
26 Src
27 )
28
29
30 type Image interface {
31 image.Image
32 Set(x, y int, c color.Color)
33 }
34
35
36 func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point, op Op) {
37 DrawMask(dst, r, src, sp, nil, image.ZP, op)
38 }
39
40
41
42
43 func clip(dst Image, r *image.Rectangle, src image.Image, sp *image.Point, mask image.Image, mp *image.Point) {
44 orig := r.Min
45 *r = r.Intersect(dst.Bounds())
46 *r = r.Intersect(src.Bounds().Add(orig.Sub(*sp)))
47 if mask != nil {
48 *r = r.Intersect(mask.Bounds().Add(orig.Sub(*mp)))
49 }
50 dx := r.Min.X - orig.X
51 dy := r.Min.Y - orig.Y
52 if dx == 0 && dy == 0 {
53 return
54 }
55 (*sp).X += dx
56 (*sp).Y += dy
57 (*mp).X += dx
58 (*mp).Y += dy
59 }
60
61
62
63 func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) {
64 clip(dst, &r, src, &sp, mask, &mp)
65 if r.Empty() {
66 return
67 }
68
69
70 if dst0, ok := dst.(*image.RGBA); ok {
71 if op == Over {
72 if mask == nil {
73 switch src0 := src.(type) {
74 case *image.Uniform:
75 drawFillOver(dst0, r, src0)
76 return
77 case *image.RGBA:
78 drawCopyOver(dst0, r, src0, sp)
79 return
80 case *image.NRGBA:
81 drawNRGBAOver(dst0, r, src0, sp)
82 return
83 case *image.YCbCr:
84 if drawYCbCr(dst0, r, src0, sp) {
85 return
86 }
87 }
88 } else if mask0, ok := mask.(*image.Alpha); ok {
89 switch src0 := src.(type) {
90 case *image.Uniform:
91 drawGlyphOver(dst0, r, src0, mask0, mp)
92 return
93 }
94 }
95 } else {
96 if mask == nil {
97 switch src0 := src.(type) {
98 case *image.Uniform:
99 drawFillSrc(dst0, r, src0)
100 return
101 case *image.RGBA:
102 drawCopySrc(dst0, r, src0, sp)
103 return
104 case *image.NRGBA:
105 drawNRGBASrc(dst0, r, src0, sp)
106 return
107 case *image.YCbCr:
108 if drawYCbCr(dst0, r, src0, sp) {
109 return
110 }
111 }
112 }
113 }
114 drawRGBA(dst0, r, src, sp, mask, mp, op)
115 return
116 }
117
118 x0, x1, dx := r.Min.X, r.Max.X, 1
119 y0, y1, dy := r.Min.Y, r.Max.Y, 1
120 if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) {
121
122 if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X {
123 x0, x1, dx = x1-1, x0-1, -1
124 y0, y1, dy = y1-1, y0-1, -1
125 }
126 }
127
128 var out *color.RGBA64
129 sy := sp.Y + y0 - r.Min.Y
130 my := mp.Y + y0 - r.Min.Y
131 for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy {
132 sx := sp.X + x0 - r.Min.X
133 mx := mp.X + x0 - r.Min.X
134 for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx {
135 ma := uint32(m)
136 if mask != nil {
137 _, _, _, ma = mask.At(mx, my).RGBA()
138 }
139 switch {
140 case ma == 0:
141 if op == Over {
142
143 } else {
144 dst.Set(x, y, color.Transparent)
145 }
146 case ma == m && op == Src:
147 dst.Set(x, y, src.At(sx, sy))
148 default:
149 sr, sg, sb, sa := src.At(sx, sy).RGBA()
150 if out == nil {
151 out = new(color.RGBA64)
152 }
153 if op == Over {
154 dr, dg, db, da := dst.At(x, y).RGBA()
155 a := m - (sa * ma / m)
156 out.R = uint16((dr*a + sr*ma) / m)
157 out.G = uint16((dg*a + sg*ma) / m)
158 out.B = uint16((db*a + sb*ma) / m)
159 out.A = uint16((da*a + sa*ma) / m)
160 } else {
161 out.R = uint16(sr * ma / m)
162 out.G = uint16(sg * ma / m)
163 out.B = uint16(sb * ma / m)
164 out.A = uint16(sa * ma / m)
165 }
166 dst.Set(x, y, out)
167 }
168 }
169 }
170 }
171
172 func drawFillOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform) {
173 sr, sg, sb, sa := src.RGBA()
174
175 a := (m - sa) * 0x101
176 i0 := dst.PixOffset(r.Min.X, r.Min.Y)
177 i1 := i0 + r.Dx()*4
178 for y := r.Min.Y; y != r.Max.Y; y++ {
179 for i := i0; i < i1; i += 4 {
180 dr := uint32(dst.Pix[i+0])
181 dg := uint32(dst.Pix[i+1])
182 db := uint32(dst.Pix[i+2])
183 da := uint32(dst.Pix[i+3])
184
185 dst.Pix[i+0] = uint8((dr*a/m + sr) >> 8)
186 dst.Pix[i+1] = uint8((dg*a/m + sg) >> 8)
187 dst.Pix[i+2] = uint8((db*a/m + sb) >> 8)
188 dst.Pix[i+3] = uint8((da*a/m + sa) >> 8)
189 }
190 i0 += dst.Stride
191 i1 += dst.Stride
192 }
193 }
194
195 func drawFillSrc(dst *image.RGBA, r image.Rectangle, src *image.Uniform) {
196 sr, sg, sb, sa := src.RGBA()
197
198
199
200 i0 := dst.PixOffset(r.Min.X, r.Min.Y)
201 i1 := i0 + r.Dx()*4
202 for i := i0; i < i1; i += 4 {
203 dst.Pix[i+0] = uint8(sr >> 8)
204 dst.Pix[i+1] = uint8(sg >> 8)
205 dst.Pix[i+2] = uint8(sb >> 8)
206 dst.Pix[i+3] = uint8(sa >> 8)
207 }
208 firstRow := dst.Pix[i0:i1]
209 for y := r.Min.Y + 1; y < r.Max.Y; y++ {
210 i0 += dst.Stride
211 i1 += dst.Stride
212 copy(dst.Pix[i0:i1], firstRow)
213 }
214 }
215
216 func drawCopyOver(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point) {
217 dx, dy := r.Dx(), r.Dy()
218 d0 := dst.PixOffset(r.Min.X, r.Min.Y)
219 s0 := src.PixOffset(sp.X, sp.Y)
220 var (
221 ddelta, sdelta int
222 i0, i1, idelta int
223 )
224 if r.Min.Y < sp.Y || r.Min.Y == sp.Y && r.Min.X <= sp.X {
225 ddelta = dst.Stride
226 sdelta = src.Stride
227 i0, i1, idelta = 0, dx*4, +4
228 } else {
229
230
231 d0 += (dy - 1) * dst.Stride
232 s0 += (dy - 1) * src.Stride
233 ddelta = -dst.Stride
234 sdelta = -src.Stride
235 i0, i1, idelta = (dx-1)*4, -4, -4
236 }
237 for ; dy > 0; dy-- {
238 dpix := dst.Pix[d0:]
239 spix := src.Pix[s0:]
240 for i := i0; i != i1; i += idelta {
241 sr := uint32(spix[i+0]) * 0x101
242 sg := uint32(spix[i+1]) * 0x101
243 sb := uint32(spix[i+2]) * 0x101
244 sa := uint32(spix[i+3]) * 0x101
245
246 dr := uint32(dpix[i+0])
247 dg := uint32(dpix[i+1])
248 db := uint32(dpix[i+2])
249 da := uint32(dpix[i+3])
250
251
252 a := (m - sa) * 0x101
253
254 dpix[i+0] = uint8((dr*a/m + sr) >> 8)
255 dpix[i+1] = uint8((dg*a/m + sg) >> 8)
256 dpix[i+2] = uint8((db*a/m + sb) >> 8)
257 dpix[i+3] = uint8((da*a/m + sa) >> 8)
258 }
259 d0 += ddelta
260 s0 += sdelta
261 }
262 }
263
264 func drawCopySrc(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point) {
265 n, dy := 4*r.Dx(), r.Dy()
266 d0 := dst.PixOffset(r.Min.X, r.Min.Y)
267 s0 := src.PixOffset(sp.X, sp.Y)
268 var ddelta, sdelta int
269 if r.Min.Y <= sp.Y {
270 ddelta = dst.Stride
271 sdelta = src.Stride
272 } else {
273
274
275
276 d0 += (dy - 1) * dst.Stride
277 s0 += (dy - 1) * src.Stride
278 ddelta = -dst.Stride
279 sdelta = -src.Stride
280 }
281 for ; dy > 0; dy-- {
282 copy(dst.Pix[d0:d0+n], src.Pix[s0:s0+n])
283 d0 += ddelta
284 s0 += sdelta
285 }
286 }
287
288 func drawNRGBAOver(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image.Point) {
289 i0 := (r.Min.X - dst.Rect.Min.X) * 4
290 i1 := (r.Max.X - dst.Rect.Min.X) * 4
291 si0 := (sp.X - src.Rect.Min.X) * 4
292 yMax := r.Max.Y - dst.Rect.Min.Y
293
294 y := r.Min.Y - dst.Rect.Min.Y
295 sy := sp.Y - src.Rect.Min.Y
296 for ; y != yMax; y, sy = y+1, sy+1 {
297 dpix := dst.Pix[y*dst.Stride:]
298 spix := src.Pix[sy*src.Stride:]
299
300 for i, si := i0, si0; i < i1; i, si = i+4, si+4 {
301
302 sa := uint32(spix[si+3]) * 0x101
303 sr := uint32(spix[si+0]) * sa / 0xff
304 sg := uint32(spix[si+1]) * sa / 0xff
305 sb := uint32(spix[si+2]) * sa / 0xff
306
307 dr := uint32(dpix[i+0])
308 dg := uint32(dpix[i+1])
309 db := uint32(dpix[i+2])
310 da := uint32(dpix[i+3])
311
312
313 a := (m - sa) * 0x101
314
315 dpix[i+0] = uint8((dr*a/m + sr) >> 8)
316 dpix[i+1] = uint8((dg*a/m + sg) >> 8)
317 dpix[i+2] = uint8((db*a/m + sb) >> 8)
318 dpix[i+3] = uint8((da*a/m + sa) >> 8)
319 }
320 }
321 }
322
323 func drawNRGBASrc(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image.Point) {
324 i0 := (r.Min.X - dst.Rect.Min.X) * 4
325 i1 := (r.Max.X - dst.Rect.Min.X) * 4
326 si0 := (sp.X - src.Rect.Min.X) * 4
327 yMax := r.Max.Y - dst.Rect.Min.Y
328
329 y := r.Min.Y - dst.Rect.Min.Y
330 sy := sp.Y - src.Rect.Min.Y
331 for ; y != yMax; y, sy = y+1, sy+1 {
332 dpix := dst.Pix[y*dst.Stride:]
333 spix := src.Pix[sy*src.Stride:]
334
335 for i, si := i0, si0; i < i1; i, si = i+4, si+4 {
336
337 sa := uint32(spix[si+3]) * 0x101
338 sr := uint32(spix[si+0]) * sa / 0xff
339 sg := uint32(spix[si+1]) * sa / 0xff
340 sb := uint32(spix[si+2]) * sa / 0xff
341
342 dpix[i+0] = uint8(sr >> 8)
343 dpix[i+1] = uint8(sg >> 8)
344 dpix[i+2] = uint8(sb >> 8)
345 dpix[i+3] = uint8(sa >> 8)
346 }
347 }
348 }
349
350 func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Point) (ok bool) {
351
352
353 x0 := (r.Min.X - dst.Rect.Min.X) * 4
354 x1 := (r.Max.X - dst.Rect.Min.X) * 4
355 y0 := r.Min.Y - dst.Rect.Min.Y
356 y1 := r.Max.Y - dst.Rect.Min.Y
357 switch src.SubsampleRatio {
358 case image.YCbCrSubsampleRatio444:
359 for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
360 dpix := dst.Pix[y*dst.Stride:]
361 yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X)
362 ci := (sy-src.Rect.Min.Y)*src.CStride + (sp.X - src.Rect.Min.X)
363 for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 {
364 rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci])
365 dpix[x+0] = rr
366 dpix[x+1] = gg
367 dpix[x+2] = bb
368 dpix[x+3] = 255
369 }
370 }
371 case image.YCbCrSubsampleRatio422:
372 for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
373 dpix := dst.Pix[y*dst.Stride:]
374 yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X)
375 ciBase := (sy-src.Rect.Min.Y)*src.CStride - src.Rect.Min.X/2
376 for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 {
377 ci := ciBase + sx/2
378 rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci])
379 dpix[x+0] = rr
380 dpix[x+1] = gg
381 dpix[x+2] = bb
382 dpix[x+3] = 255
383 }
384 }
385 case image.YCbCrSubsampleRatio420:
386 for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
387 dpix := dst.Pix[y*dst.Stride:]
388 yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X)
389 ciBase := (sy/2-src.Rect.Min.Y/2)*src.CStride - src.Rect.Min.X/2
390 for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 {
391 ci := ciBase + sx/2
392 rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci])
393 dpix[x+0] = rr
394 dpix[x+1] = gg
395 dpix[x+2] = bb
396 dpix[x+3] = 255
397 }
398 }
399 case image.YCbCrSubsampleRatio440:
400 for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
401 dpix := dst.Pix[y*dst.Stride:]
402 yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X)
403 ci := (sy/2-src.Rect.Min.Y/2)*src.CStride + (sp.X - src.Rect.Min.X)
404 for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 {
405 rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci])
406 dpix[x+0] = rr
407 dpix[x+1] = gg
408 dpix[x+2] = bb
409 dpix[x+3] = 255
410 }
411 }
412 default:
413 return false
414 }
415 return true
416 }
417
418 func drawGlyphOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform, mask *image.Alpha, mp image.Point) {
419 i0 := dst.PixOffset(r.Min.X, r.Min.Y)
420 i1 := i0 + r.Dx()*4
421 mi0 := mask.PixOffset(mp.X, mp.Y)
422 sr, sg, sb, sa := src.RGBA()
423 for y, my := r.Min.Y, mp.Y; y != r.Max.Y; y, my = y+1, my+1 {
424 for i, mi := i0, mi0; i < i1; i, mi = i+4, mi+1 {
425 ma := uint32(mask.Pix[mi])
426 if ma == 0 {
427 continue
428 }
429 ma |= ma << 8
430
431 dr := uint32(dst.Pix[i+0])
432 dg := uint32(dst.Pix[i+1])
433 db := uint32(dst.Pix[i+2])
434 da := uint32(dst.Pix[i+3])
435
436
437 a := (m - (sa * ma / m)) * 0x101
438
439 dst.Pix[i+0] = uint8((dr*a + sr*ma) / m >> 8)
440 dst.Pix[i+1] = uint8((dg*a + sg*ma) / m >> 8)
441 dst.Pix[i+2] = uint8((db*a + sb*ma) / m >> 8)
442 dst.Pix[i+3] = uint8((da*a + sa*ma) / m >> 8)
443 }
444 i0 += dst.Stride
445 i1 += dst.Stride
446 mi0 += mask.Stride
447 }
448 }
449
450 func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) {
451 x0, x1, dx := r.Min.X, r.Max.X, 1
452 y0, y1, dy := r.Min.Y, r.Max.Y, 1
453 if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) {
454 if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X {
455 x0, x1, dx = x1-1, x0-1, -1
456 y0, y1, dy = y1-1, y0-1, -1
457 }
458 }
459
460 sy := sp.Y + y0 - r.Min.Y
461 my := mp.Y + y0 - r.Min.Y
462 sx0 := sp.X + x0 - r.Min.X
463 mx0 := mp.X + x0 - r.Min.X
464 sx1 := sx0 + (x1 - x0)
465 i0 := dst.PixOffset(x0, y0)
466 di := dx * 4
467 for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy {
468 for i, sx, mx := i0, sx0, mx0; sx != sx1; i, sx, mx = i+di, sx+dx, mx+dx {
469 ma := uint32(m)
470 if mask != nil {
471 _, _, _, ma = mask.At(mx, my).RGBA()
472 }
473 sr, sg, sb, sa := src.At(sx, sy).RGBA()
474 if op == Over {
475 dr := uint32(dst.Pix[i+0])
476 dg := uint32(dst.Pix[i+1])
477 db := uint32(dst.Pix[i+2])
478 da := uint32(dst.Pix[i+3])
479
480
481
482
483
484
485
486 a := (m - (sa * ma / m)) * 0x101
487
488 dst.Pix[i+0] = uint8((dr*a + sr*ma) / m >> 8)
489 dst.Pix[i+1] = uint8((dg*a + sg*ma) / m >> 8)
490 dst.Pix[i+2] = uint8((db*a + sb*ma) / m >> 8)
491 dst.Pix[i+3] = uint8((da*a + sa*ma) / m >> 8)
492
493 } else {
494 dst.Pix[i+0] = uint8(sr * ma / m >> 8)
495 dst.Pix[i+1] = uint8(sg * ma / m >> 8)
496 dst.Pix[i+2] = uint8(sb * ma / m >> 8)
497 dst.Pix[i+3] = uint8(sa * ma / m >> 8)
498 }
499 }
500 i0 += dy * dst.Stride
501 }
502 }
View as plain text