scale.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. // Copyright 2015 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. //go:generate go run gen.go
  5. package draw
  6. import (
  7. "image"
  8. "image/color"
  9. "math"
  10. "sync"
  11. "golang.org/x/image/math/f64"
  12. )
  13. // Copy copies the part of the source image defined by src and sr and writes
  14. // the result of a Porter-Duff composition to the part of the destination image
  15. // defined by dst and the translation of sr so that sr.Min translates to dp.
  16. func Copy(dst Image, dp image.Point, src image.Image, sr image.Rectangle, op Op, opts *Options) {
  17. var o Options
  18. if opts != nil {
  19. o = *opts
  20. }
  21. dr := sr.Add(dp.Sub(sr.Min))
  22. if o.DstMask == nil {
  23. DrawMask(dst, dr, src, sr.Min, o.SrcMask, o.SrcMaskP.Add(sr.Min), op)
  24. } else {
  25. NearestNeighbor.Scale(dst, dr, src, sr, op, opts)
  26. }
  27. }
  28. // Scaler scales the part of the source image defined by src and sr and writes
  29. // the result of a Porter-Duff composition to the part of the destination image
  30. // defined by dst and dr.
  31. //
  32. // A Scaler is safe to use concurrently.
  33. type Scaler interface {
  34. Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, op Op, opts *Options)
  35. }
  36. // Transformer transforms the part of the source image defined by src and sr
  37. // and writes the result of a Porter-Duff composition to the part of the
  38. // destination image defined by dst and the affine transform m applied to sr.
  39. //
  40. // For example, if m is the matrix
  41. //
  42. // m00 m01 m02
  43. // m10 m11 m12
  44. //
  45. // then the src-space point (sx, sy) maps to the dst-space point
  46. // (m00*sx + m01*sy + m02, m10*sx + m11*sy + m12).
  47. //
  48. // A Transformer is safe to use concurrently.
  49. type Transformer interface {
  50. Transform(dst Image, m f64.Aff3, src image.Image, sr image.Rectangle, op Op, opts *Options)
  51. }
  52. // Options are optional parameters to Copy, Scale and Transform.
  53. //
  54. // A nil *Options means to use the default (zero) values of each field.
  55. type Options struct {
  56. // Masks limit what parts of the dst image are drawn to and what parts of
  57. // the src image are drawn from.
  58. //
  59. // A dst or src mask image having a zero alpha (transparent) pixel value in
  60. // the respective coordinate space means that dst pixel is entirely
  61. // unaffected or that src pixel is considered transparent black. A full
  62. // alpha (opaque) value means that the dst pixel is maximally affected or
  63. // the src pixel contributes maximally. The default values, nil, are
  64. // equivalent to fully opaque, infinitely large mask images.
  65. //
  66. // The DstMask is otherwise known as a clip mask, and its pixels map 1:1 to
  67. // the dst image's pixels. DstMaskP in DstMask space corresponds to
  68. // image.Point{X:0, Y:0} in dst space. For example, when limiting
  69. // repainting to a 'dirty rectangle', use that image.Rectangle and a zero
  70. // image.Point as the DstMask and DstMaskP.
  71. //
  72. // The SrcMask's pixels map 1:1 to the src image's pixels. SrcMaskP in
  73. // SrcMask space corresponds to image.Point{X:0, Y:0} in src space. For
  74. // example, when drawing font glyphs in a uniform color, use an
  75. // *image.Uniform as the src, and use the glyph atlas image and the
  76. // per-glyph offset as SrcMask and SrcMaskP:
  77. // Copy(dst, dp, image.NewUniform(color), image.Rect(0, 0, glyphWidth, glyphHeight), &Options{
  78. // SrcMask: glyphAtlas,
  79. // SrcMaskP: glyphOffset,
  80. // })
  81. DstMask image.Image
  82. DstMaskP image.Point
  83. SrcMask image.Image
  84. SrcMaskP image.Point
  85. // TODO: a smooth vs sharp edges option, for arbitrary rotations?
  86. }
  87. // Interpolator is an interpolation algorithm, when dst and src pixels don't
  88. // have a 1:1 correspondence.
  89. //
  90. // Of the interpolators provided by this package:
  91. // - NearestNeighbor is fast but usually looks worst.
  92. // - CatmullRom is slow but usually looks best.
  93. // - ApproxBiLinear has reasonable speed and quality.
  94. //
  95. // The time taken depends on the size of dr. For kernel interpolators, the
  96. // speed also depends on the size of sr, and so are often slower than
  97. // non-kernel interpolators, especially when scaling down.
  98. type Interpolator interface {
  99. Scaler
  100. Transformer
  101. }
  102. // Kernel is an interpolator that blends source pixels weighted by a symmetric
  103. // kernel function.
  104. type Kernel struct {
  105. // Support is the kernel support and must be >= 0. At(t) is assumed to be
  106. // zero when t >= Support.
  107. Support float64
  108. // At is the kernel function. It will only be called with t in the
  109. // range [0, Support).
  110. At func(t float64) float64
  111. }
  112. // Scale implements the Scaler interface.
  113. func (q *Kernel) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, op Op, opts *Options) {
  114. q.newScaler(dr.Dx(), dr.Dy(), sr.Dx(), sr.Dy(), false).Scale(dst, dr, src, sr, op, opts)
  115. }
  116. // NewScaler returns a Scaler that is optimized for scaling multiple times with
  117. // the same fixed destination and source width and height.
  118. func (q *Kernel) NewScaler(dw, dh, sw, sh int) Scaler {
  119. return q.newScaler(dw, dh, sw, sh, true)
  120. }
  121. func (q *Kernel) newScaler(dw, dh, sw, sh int, usePool bool) Scaler {
  122. z := &kernelScaler{
  123. kernel: q,
  124. dw: int32(dw),
  125. dh: int32(dh),
  126. sw: int32(sw),
  127. sh: int32(sh),
  128. horizontal: newDistrib(q, int32(dw), int32(sw)),
  129. vertical: newDistrib(q, int32(dh), int32(sh)),
  130. }
  131. if usePool {
  132. z.pool.New = func() interface{} {
  133. tmp := z.makeTmpBuf()
  134. return &tmp
  135. }
  136. }
  137. return z
  138. }
  139. var (
  140. // NearestNeighbor is the nearest neighbor interpolator. It is very fast,
  141. // but usually gives very low quality results. When scaling up, the result
  142. // will look 'blocky'.
  143. NearestNeighbor = Interpolator(nnInterpolator{})
  144. // ApproxBiLinear is a mixture of the nearest neighbor and bi-linear
  145. // interpolators. It is fast, but usually gives medium quality results.
  146. //
  147. // It implements bi-linear interpolation when upscaling and a bi-linear
  148. // blend of the 4 nearest neighbor pixels when downscaling. This yields
  149. // nicer quality than nearest neighbor interpolation when upscaling, but
  150. // the time taken is independent of the number of source pixels, unlike the
  151. // bi-linear interpolator. When downscaling a large image, the performance
  152. // difference can be significant.
  153. ApproxBiLinear = Interpolator(ablInterpolator{})
  154. // BiLinear is the tent kernel. It is slow, but usually gives high quality
  155. // results.
  156. BiLinear = &Kernel{1, func(t float64) float64 {
  157. return 1 - t
  158. }}
  159. // CatmullRom is the Catmull-Rom kernel. It is very slow, but usually gives
  160. // very high quality results.
  161. //
  162. // It is an instance of the more general cubic BC-spline kernel with parameters
  163. // B=0 and C=0.5. See Mitchell and Netravali, "Reconstruction Filters in
  164. // Computer Graphics", Computer Graphics, Vol. 22, No. 4, pp. 221-228.
  165. CatmullRom = &Kernel{2, func(t float64) float64 {
  166. if t < 1 {
  167. return (1.5*t-2.5)*t*t + 1
  168. }
  169. return ((-0.5*t+2.5)*t-4)*t + 2
  170. }}
  171. // TODO: a Kaiser-Bessel kernel?
  172. )
  173. type nnInterpolator struct{}
  174. type ablInterpolator struct{}
  175. type kernelScaler struct {
  176. kernel *Kernel
  177. dw, dh, sw, sh int32
  178. horizontal, vertical distrib
  179. pool sync.Pool
  180. }
  181. func (z *kernelScaler) makeTmpBuf() [][4]float64 {
  182. return make([][4]float64, z.dw*z.sh)
  183. }
  184. // source is a range of contribs, their inverse total weight, and that ITW
  185. // divided by 0xffff.
  186. type source struct {
  187. i, j int32
  188. invTotalWeight float64
  189. invTotalWeightFFFF float64
  190. }
  191. // contrib is the weight of a column or row.
  192. type contrib struct {
  193. coord int32
  194. weight float64
  195. }
  196. // distrib measures how source pixels are distributed over destination pixels.
  197. type distrib struct {
  198. // sources are what contribs each column or row in the source image owns,
  199. // and the total weight of those contribs.
  200. sources []source
  201. // contribs are the contributions indexed by sources[s].i and sources[s].j.
  202. contribs []contrib
  203. }
  204. // newDistrib returns a distrib that distributes sw source columns (or rows)
  205. // over dw destination columns (or rows).
  206. func newDistrib(q *Kernel, dw, sw int32) distrib {
  207. scale := float64(sw) / float64(dw)
  208. halfWidth, kernelArgScale := q.Support, 1.0
  209. // When shrinking, broaden the effective kernel support so that we still
  210. // visit every source pixel.
  211. if scale > 1 {
  212. halfWidth *= scale
  213. kernelArgScale = 1 / scale
  214. }
  215. // Make the sources slice, one source for each column or row, and temporarily
  216. // appropriate its elements' fields so that invTotalWeight is the scaled
  217. // coordinate of the source column or row, and i and j are the lower and
  218. // upper bounds of the range of destination columns or rows affected by the
  219. // source column or row.
  220. n, sources := int32(0), make([]source, dw)
  221. for x := range sources {
  222. center := (float64(x)+0.5)*scale - 0.5
  223. i := int32(math.Floor(center - halfWidth))
  224. if i < 0 {
  225. i = 0
  226. }
  227. j := int32(math.Ceil(center + halfWidth))
  228. if j > sw {
  229. j = sw
  230. if j < i {
  231. j = i
  232. }
  233. }
  234. sources[x] = source{i: i, j: j, invTotalWeight: center}
  235. n += j - i
  236. }
  237. contribs := make([]contrib, 0, n)
  238. for k, b := range sources {
  239. totalWeight := 0.0
  240. l := int32(len(contribs))
  241. for coord := b.i; coord < b.j; coord++ {
  242. t := abs((b.invTotalWeight - float64(coord)) * kernelArgScale)
  243. if t >= q.Support {
  244. continue
  245. }
  246. weight := q.At(t)
  247. if weight == 0 {
  248. continue
  249. }
  250. totalWeight += weight
  251. contribs = append(contribs, contrib{coord, weight})
  252. }
  253. totalWeight = 1 / totalWeight
  254. sources[k] = source{
  255. i: l,
  256. j: int32(len(contribs)),
  257. invTotalWeight: totalWeight,
  258. invTotalWeightFFFF: totalWeight / 0xffff,
  259. }
  260. }
  261. return distrib{sources, contribs}
  262. }
  263. // abs is like math.Abs, but it doesn't care about negative zero, infinities or
  264. // NaNs.
  265. func abs(f float64) float64 {
  266. if f < 0 {
  267. f = -f
  268. }
  269. return f
  270. }
  271. // ftou converts the range [0.0, 1.0] to [0, 0xffff].
  272. func ftou(f float64) uint16 {
  273. i := int32(0xffff*f + 0.5)
  274. if i > 0xffff {
  275. return 0xffff
  276. }
  277. if i > 0 {
  278. return uint16(i)
  279. }
  280. return 0
  281. }
  282. // fffftou converts the range [0.0, 65535.0] to [0, 0xffff].
  283. func fffftou(f float64) uint16 {
  284. i := int32(f + 0.5)
  285. if i > 0xffff {
  286. return 0xffff
  287. }
  288. if i > 0 {
  289. return uint16(i)
  290. }
  291. return 0
  292. }
  293. // invert returns the inverse of m.
  294. //
  295. // TODO: move this into the f64 package, once we work out the convention for
  296. // matrix methods in that package: do they modify the receiver, take a dst
  297. // pointer argument, or return a new value?
  298. func invert(m *f64.Aff3) f64.Aff3 {
  299. m00 := +m[3*1+1]
  300. m01 := -m[3*0+1]
  301. m02 := +m[3*1+2]*m[3*0+1] - m[3*1+1]*m[3*0+2]
  302. m10 := -m[3*1+0]
  303. m11 := +m[3*0+0]
  304. m12 := +m[3*1+0]*m[3*0+2] - m[3*1+2]*m[3*0+0]
  305. det := m00*m11 - m10*m01
  306. return f64.Aff3{
  307. m00 / det,
  308. m01 / det,
  309. m02 / det,
  310. m10 / det,
  311. m11 / det,
  312. m12 / det,
  313. }
  314. }
  315. func matMul(p, q *f64.Aff3) f64.Aff3 {
  316. return f64.Aff3{
  317. p[3*0+0]*q[3*0+0] + p[3*0+1]*q[3*1+0],
  318. p[3*0+0]*q[3*0+1] + p[3*0+1]*q[3*1+1],
  319. p[3*0+0]*q[3*0+2] + p[3*0+1]*q[3*1+2] + p[3*0+2],
  320. p[3*1+0]*q[3*0+0] + p[3*1+1]*q[3*1+0],
  321. p[3*1+0]*q[3*0+1] + p[3*1+1]*q[3*1+1],
  322. p[3*1+0]*q[3*0+2] + p[3*1+1]*q[3*1+2] + p[3*1+2],
  323. }
  324. }
  325. // transformRect returns a rectangle dr that contains sr transformed by s2d.
  326. func transformRect(s2d *f64.Aff3, sr *image.Rectangle) (dr image.Rectangle) {
  327. ps := [...]image.Point{
  328. {sr.Min.X, sr.Min.Y},
  329. {sr.Max.X, sr.Min.Y},
  330. {sr.Min.X, sr.Max.Y},
  331. {sr.Max.X, sr.Max.Y},
  332. }
  333. for i, p := range ps {
  334. sxf := float64(p.X)
  335. syf := float64(p.Y)
  336. dx := int(math.Floor(s2d[0]*sxf + s2d[1]*syf + s2d[2]))
  337. dy := int(math.Floor(s2d[3]*sxf + s2d[4]*syf + s2d[5]))
  338. // The +1 adjustments below are because an image.Rectangle is inclusive
  339. // on the low end but exclusive on the high end.
  340. if i == 0 {
  341. dr = image.Rectangle{
  342. Min: image.Point{dx + 0, dy + 0},
  343. Max: image.Point{dx + 1, dy + 1},
  344. }
  345. continue
  346. }
  347. if dr.Min.X > dx {
  348. dr.Min.X = dx
  349. }
  350. dx++
  351. if dr.Max.X < dx {
  352. dr.Max.X = dx
  353. }
  354. if dr.Min.Y > dy {
  355. dr.Min.Y = dy
  356. }
  357. dy++
  358. if dr.Max.Y < dy {
  359. dr.Max.Y = dy
  360. }
  361. }
  362. return dr
  363. }
  364. func clipAffectedDestRect(adr image.Rectangle, dstMask image.Image, dstMaskP image.Point) (image.Rectangle, image.Image) {
  365. if dstMask == nil {
  366. return adr, nil
  367. }
  368. if r, ok := dstMask.(image.Rectangle); ok {
  369. return adr.Intersect(r.Sub(dstMaskP)), nil
  370. }
  371. // TODO: clip to dstMask.Bounds() if the color model implies that out-of-bounds means 0 alpha?
  372. return adr, dstMask
  373. }
  374. func transform_Uniform(dst Image, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.Uniform, sr image.Rectangle, bias image.Point, op Op) {
  375. switch op {
  376. case Over:
  377. switch dst := dst.(type) {
  378. case *image.RGBA:
  379. pr, pg, pb, pa := src.C.RGBA()
  380. pa1 := (0xffff - pa) * 0x101
  381. for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
  382. dyf := float64(dr.Min.Y+int(dy)) + 0.5
  383. d := dst.PixOffset(dr.Min.X+adr.Min.X, dr.Min.Y+int(dy))
  384. for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
  385. dxf := float64(dr.Min.X+int(dx)) + 0.5
  386. sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
  387. sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
  388. if !(image.Point{sx0, sy0}).In(sr) {
  389. continue
  390. }
  391. dst.Pix[d+0] = uint8((uint32(dst.Pix[d+0])*pa1/0xffff + pr) >> 8)
  392. dst.Pix[d+1] = uint8((uint32(dst.Pix[d+1])*pa1/0xffff + pg) >> 8)
  393. dst.Pix[d+2] = uint8((uint32(dst.Pix[d+2])*pa1/0xffff + pb) >> 8)
  394. dst.Pix[d+3] = uint8((uint32(dst.Pix[d+3])*pa1/0xffff + pa) >> 8)
  395. }
  396. }
  397. default:
  398. pr, pg, pb, pa := src.C.RGBA()
  399. pa1 := 0xffff - pa
  400. dstColorRGBA64 := &color.RGBA64{}
  401. dstColor := color.Color(dstColorRGBA64)
  402. for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
  403. dyf := float64(dr.Min.Y+int(dy)) + 0.5
  404. for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ {
  405. dxf := float64(dr.Min.X+int(dx)) + 0.5
  406. sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
  407. sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
  408. if !(image.Point{sx0, sy0}).In(sr) {
  409. continue
  410. }
  411. qr, qg, qb, qa := dst.At(dr.Min.X+int(dx), dr.Min.Y+int(dy)).RGBA()
  412. dstColorRGBA64.R = uint16(qr*pa1/0xffff + pr)
  413. dstColorRGBA64.G = uint16(qg*pa1/0xffff + pg)
  414. dstColorRGBA64.B = uint16(qb*pa1/0xffff + pb)
  415. dstColorRGBA64.A = uint16(qa*pa1/0xffff + pa)
  416. dst.Set(dr.Min.X+int(dx), dr.Min.Y+int(dy), dstColor)
  417. }
  418. }
  419. }
  420. case Src:
  421. switch dst := dst.(type) {
  422. case *image.RGBA:
  423. pr, pg, pb, pa := src.C.RGBA()
  424. pr8 := uint8(pr >> 8)
  425. pg8 := uint8(pg >> 8)
  426. pb8 := uint8(pb >> 8)
  427. pa8 := uint8(pa >> 8)
  428. for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
  429. dyf := float64(dr.Min.Y+int(dy)) + 0.5
  430. d := dst.PixOffset(dr.Min.X+adr.Min.X, dr.Min.Y+int(dy))
  431. for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
  432. dxf := float64(dr.Min.X+int(dx)) + 0.5
  433. sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
  434. sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
  435. if !(image.Point{sx0, sy0}).In(sr) {
  436. continue
  437. }
  438. dst.Pix[d+0] = pr8
  439. dst.Pix[d+1] = pg8
  440. dst.Pix[d+2] = pb8
  441. dst.Pix[d+3] = pa8
  442. }
  443. }
  444. default:
  445. pr, pg, pb, pa := src.C.RGBA()
  446. dstColorRGBA64 := &color.RGBA64{
  447. uint16(pr),
  448. uint16(pg),
  449. uint16(pb),
  450. uint16(pa),
  451. }
  452. dstColor := color.Color(dstColorRGBA64)
  453. for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
  454. dyf := float64(dr.Min.Y+int(dy)) + 0.5
  455. for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ {
  456. dxf := float64(dr.Min.X+int(dx)) + 0.5
  457. sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
  458. sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
  459. if !(image.Point{sx0, sy0}).In(sr) {
  460. continue
  461. }
  462. dst.Set(dr.Min.X+int(dx), dr.Min.Y+int(dy), dstColor)
  463. }
  464. }
  465. }
  466. }
  467. }
  468. func opaque(m image.Image) bool {
  469. o, ok := m.(interface {
  470. Opaque() bool
  471. })
  472. return ok && o.Opaque()
  473. }