1
2
3
4
5
6
7 package draw
8
9 import (
10 "image"
11 "image/color"
12 "math"
13 "sync"
14
15 "golang.org/x/image/math/f64"
16 )
17
18
19
20
21 func Copy(dst Image, dp image.Point, src image.Image, sr image.Rectangle, op Op, opts *Options) {
22 var o Options
23 if opts != nil {
24 o = *opts
25 }
26 dr := sr.Add(dp.Sub(sr.Min))
27 if o.DstMask == nil {
28 DrawMask(dst, dr, src, sr.Min, o.SrcMask, o.SrcMaskP.Add(sr.Min), op)
29 } else {
30 NearestNeighbor.Scale(dst, dr, src, sr, op, opts)
31 }
32 }
33
34
35
36
37
38
39 type Scaler interface {
40 Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, op Op, opts *Options)
41 }
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 type Transformer interface {
57 Transform(dst Image, m f64.Aff3, src image.Image, sr image.Rectangle, op Op, opts *Options)
58 }
59
60
61
62
63 type Options struct {
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 DstMask image.Image
90 DstMaskP image.Point
91 SrcMask image.Image
92 SrcMaskP image.Point
93
94
95 }
96
97
98
99
100
101
102
103
104
105
106
107
108 type Interpolator interface {
109 Scaler
110 Transformer
111 }
112
113
114
115 type Kernel struct {
116
117
118 Support float64
119
120
121 At func(t float64) float64
122 }
123
124
125 func (q *Kernel) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, op Op, opts *Options) {
126 q.newScaler(dr.Dx(), dr.Dy(), sr.Dx(), sr.Dy(), false).Scale(dst, dr, src, sr, op, opts)
127 }
128
129
130
131 func (q *Kernel) NewScaler(dw, dh, sw, sh int) Scaler {
132 return q.newScaler(dw, dh, sw, sh, true)
133 }
134
135 func (q *Kernel) newScaler(dw, dh, sw, sh int, usePool bool) Scaler {
136 z := &kernelScaler{
137 kernel: q,
138 dw: int32(dw),
139 dh: int32(dh),
140 sw: int32(sw),
141 sh: int32(sh),
142 horizontal: newDistrib(q, int32(dw), int32(sw)),
143 vertical: newDistrib(q, int32(dh), int32(sh)),
144 }
145 if usePool {
146 z.pool.New = func() interface{} {
147 tmp := z.makeTmpBuf()
148 return &tmp
149 }
150 }
151 return z
152 }
153
154 var (
155
156
157
158 NearestNeighbor = Interpolator(nnInterpolator{})
159
160
161
162
163
164
165
166
167
168
169 ApproxBiLinear = Interpolator(ablInterpolator{})
170
171
172
173 BiLinear = &Kernel{1, func(t float64) float64 {
174 return 1 - t
175 }}
176
177
178
179
180
181
182
183 CatmullRom = &Kernel{2, func(t float64) float64 {
184 if t < 1 {
185 return (1.5*t-2.5)*t*t + 1
186 }
187 return ((-0.5*t+2.5)*t-4)*t + 2
188 }}
189
190
191 )
192
193 type nnInterpolator struct{}
194
195 type ablInterpolator struct{}
196
197 type kernelScaler struct {
198 kernel *Kernel
199 dw, dh, sw, sh int32
200 horizontal, vertical distrib
201 pool sync.Pool
202 }
203
204 func (z *kernelScaler) makeTmpBuf() [][4]float64 {
205 return make([][4]float64, z.dw*z.sh)
206 }
207
208
209
210 type source struct {
211 i, j int32
212 invTotalWeight float64
213 invTotalWeightFFFF float64
214 }
215
216
217 type contrib struct {
218 coord int32
219 weight float64
220 }
221
222
223 type distrib struct {
224
225
226 sources []source
227
228 contribs []contrib
229 }
230
231
232
233 func newDistrib(q *Kernel, dw, sw int32) distrib {
234 scale := float64(sw) / float64(dw)
235 halfWidth, kernelArgScale := q.Support, 1.0
236
237
238 if scale > 1 {
239 halfWidth *= scale
240 kernelArgScale = 1 / scale
241 }
242
243
244
245
246
247
248 n, sources := int32(0), make([]source, dw)
249 for x := range sources {
250 center := (float64(x)+0.5)*scale - 0.5
251 i := int32(math.Floor(center - halfWidth))
252 if i < 0 {
253 i = 0
254 }
255 j := int32(math.Ceil(center + halfWidth))
256 if j > sw {
257 j = sw
258 if j < i {
259 j = i
260 }
261 }
262 sources[x] = source{i: i, j: j, invTotalWeight: center}
263 n += j - i
264 }
265
266 contribs := make([]contrib, 0, n)
267 for k, b := range sources {
268 totalWeight := 0.0
269 l := int32(len(contribs))
270 for coord := b.i; coord < b.j; coord++ {
271 t := abs((b.invTotalWeight - float64(coord)) * kernelArgScale)
272 if t >= q.Support {
273 continue
274 }
275 weight := q.At(t)
276 if weight == 0 {
277 continue
278 }
279 totalWeight += weight
280 contribs = append(contribs, contrib{coord, weight})
281 }
282 totalWeight = 1 / totalWeight
283 sources[k] = source{
284 i: l,
285 j: int32(len(contribs)),
286 invTotalWeight: totalWeight,
287 invTotalWeightFFFF: totalWeight / 0xffff,
288 }
289 }
290
291 return distrib{sources, contribs}
292 }
293
294
295
296 func abs(f float64) float64 {
297 if f < 0 {
298 f = -f
299 }
300 return f
301 }
302
303
304 func ftou(f float64) uint16 {
305 i := int32(0xffff*f + 0.5)
306 if i > 0xffff {
307 return 0xffff
308 }
309 if i > 0 {
310 return uint16(i)
311 }
312 return 0
313 }
314
315
316 func fffftou(f float64) uint16 {
317 i := int32(f + 0.5)
318 if i > 0xffff {
319 return 0xffff
320 }
321 if i > 0 {
322 return uint16(i)
323 }
324 return 0
325 }
326
327
328
329
330
331
332 func invert(m *f64.Aff3) f64.Aff3 {
333 m00 := +m[3*1+1]
334 m01 := -m[3*0+1]
335 m02 := +m[3*1+2]*m[3*0+1] - m[3*1+1]*m[3*0+2]
336 m10 := -m[3*1+0]
337 m11 := +m[3*0+0]
338 m12 := +m[3*1+0]*m[3*0+2] - m[3*1+2]*m[3*0+0]
339
340 det := m00*m11 - m10*m01
341
342 return f64.Aff3{
343 m00 / det,
344 m01 / det,
345 m02 / det,
346 m10 / det,
347 m11 / det,
348 m12 / det,
349 }
350 }
351
352 func matMul(p, q *f64.Aff3) f64.Aff3 {
353 return f64.Aff3{
354 p[3*0+0]*q[3*0+0] + p[3*0+1]*q[3*1+0],
355 p[3*0+0]*q[3*0+1] + p[3*0+1]*q[3*1+1],
356 p[3*0+0]*q[3*0+2] + p[3*0+1]*q[3*1+2] + p[3*0+2],
357 p[3*1+0]*q[3*0+0] + p[3*1+1]*q[3*1+0],
358 p[3*1+0]*q[3*0+1] + p[3*1+1]*q[3*1+1],
359 p[3*1+0]*q[3*0+2] + p[3*1+1]*q[3*1+2] + p[3*1+2],
360 }
361 }
362
363
364 func transformRect(s2d *f64.Aff3, sr *image.Rectangle) (dr image.Rectangle) {
365 ps := [...]image.Point{
366 {sr.Min.X, sr.Min.Y},
367 {sr.Max.X, sr.Min.Y},
368 {sr.Min.X, sr.Max.Y},
369 {sr.Max.X, sr.Max.Y},
370 }
371 for i, p := range ps {
372 sxf := float64(p.X)
373 syf := float64(p.Y)
374 dx := int(math.Floor(s2d[0]*sxf + s2d[1]*syf + s2d[2]))
375 dy := int(math.Floor(s2d[3]*sxf + s2d[4]*syf + s2d[5]))
376
377
378
379
380 if i == 0 {
381 dr = image.Rectangle{
382 Min: image.Point{dx + 0, dy + 0},
383 Max: image.Point{dx + 1, dy + 1},
384 }
385 continue
386 }
387
388 if dr.Min.X > dx {
389 dr.Min.X = dx
390 }
391 dx++
392 if dr.Max.X < dx {
393 dr.Max.X = dx
394 }
395
396 if dr.Min.Y > dy {
397 dr.Min.Y = dy
398 }
399 dy++
400 if dr.Max.Y < dy {
401 dr.Max.Y = dy
402 }
403 }
404 return dr
405 }
406
407 func clipAffectedDestRect(adr image.Rectangle, dstMask image.Image, dstMaskP image.Point) (image.Rectangle, image.Image) {
408 if dstMask == nil {
409 return adr, nil
410 }
411 if r, ok := dstMask.(image.Rectangle); ok {
412 return adr.Intersect(r.Sub(dstMaskP)), nil
413 }
414
415 return adr, dstMask
416 }
417
418 func transform_Uniform(dst Image, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.Uniform, sr image.Rectangle, bias image.Point, op Op) {
419 switch op {
420 case Over:
421 switch dst := dst.(type) {
422 case *image.RGBA:
423 pr, pg, pb, pa := src.C.RGBA()
424 pa1 := (0xffff - pa) * 0x101
425
426 for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
427 dyf := float64(dr.Min.Y+int(dy)) + 0.5
428 d := dst.PixOffset(dr.Min.X+adr.Min.X, dr.Min.Y+int(dy))
429 for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
430 dxf := float64(dr.Min.X+int(dx)) + 0.5
431 sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
432 sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
433 if !(image.Point{sx0, sy0}).In(sr) {
434 continue
435 }
436 dst.Pix[d+0] = uint8((uint32(dst.Pix[d+0])*pa1/0xffff + pr) >> 8)
437 dst.Pix[d+1] = uint8((uint32(dst.Pix[d+1])*pa1/0xffff + pg) >> 8)
438 dst.Pix[d+2] = uint8((uint32(dst.Pix[d+2])*pa1/0xffff + pb) >> 8)
439 dst.Pix[d+3] = uint8((uint32(dst.Pix[d+3])*pa1/0xffff + pa) >> 8)
440 }
441 }
442
443 default:
444 pr, pg, pb, pa := src.C.RGBA()
445 pa1 := 0xffff - pa
446 dstColorRGBA64 := &color.RGBA64{}
447 dstColor := color.Color(dstColorRGBA64)
448
449 for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
450 dyf := float64(dr.Min.Y+int(dy)) + 0.5
451 for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ {
452 dxf := float64(dr.Min.X+int(dx)) + 0.5
453 sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
454 sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
455 if !(image.Point{sx0, sy0}).In(sr) {
456 continue
457 }
458 qr, qg, qb, qa := dst.At(dr.Min.X+int(dx), dr.Min.Y+int(dy)).RGBA()
459 dstColorRGBA64.R = uint16(qr*pa1/0xffff + pr)
460 dstColorRGBA64.G = uint16(qg*pa1/0xffff + pg)
461 dstColorRGBA64.B = uint16(qb*pa1/0xffff + pb)
462 dstColorRGBA64.A = uint16(qa*pa1/0xffff + pa)
463 dst.Set(dr.Min.X+int(dx), dr.Min.Y+int(dy), dstColor)
464 }
465 }
466 }
467
468 case Src:
469 switch dst := dst.(type) {
470 case *image.RGBA:
471 pr, pg, pb, pa := src.C.RGBA()
472 pr8 := uint8(pr >> 8)
473 pg8 := uint8(pg >> 8)
474 pb8 := uint8(pb >> 8)
475 pa8 := uint8(pa >> 8)
476
477 for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
478 dyf := float64(dr.Min.Y+int(dy)) + 0.5
479 d := dst.PixOffset(dr.Min.X+adr.Min.X, dr.Min.Y+int(dy))
480 for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
481 dxf := float64(dr.Min.X+int(dx)) + 0.5
482 sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
483 sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
484 if !(image.Point{sx0, sy0}).In(sr) {
485 continue
486 }
487 dst.Pix[d+0] = pr8
488 dst.Pix[d+1] = pg8
489 dst.Pix[d+2] = pb8
490 dst.Pix[d+3] = pa8
491 }
492 }
493
494 default:
495 pr, pg, pb, pa := src.C.RGBA()
496 dstColorRGBA64 := &color.RGBA64{
497 uint16(pr),
498 uint16(pg),
499 uint16(pb),
500 uint16(pa),
501 }
502 dstColor := color.Color(dstColorRGBA64)
503
504 for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
505 dyf := float64(dr.Min.Y+int(dy)) + 0.5
506 for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ {
507 dxf := float64(dr.Min.X+int(dx)) + 0.5
508 sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
509 sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
510 if !(image.Point{sx0, sy0}).In(sr) {
511 continue
512 }
513 dst.Set(dr.Min.X+int(dx), dr.Min.Y+int(dy), dstColor)
514 }
515 }
516 }
517 }
518 }
519
520 func opaque(m image.Image) bool {
521 o, ok := m.(interface {
522 Opaque() bool
523 })
524 return ok && o.Opaque()
525 }
526
View as plain text