From 69a0d8f9aac9f031e8c49603c2d0385b0a22bf75 Mon Sep 17 00:00:00 2001 From: Nigel Tao Date: Tue, 14 Apr 2015 16:23:36 +1000 Subject: [PATCH] draw: add mask fields to Options. This change only adds the fields, more or less. Follow-up changes will actually honor the masks. Change-Id: I81411dc1aac4b3c846dcdf13e2cb0b5cd60fb2b4 Reviewed-on: https://go-review.googlesource.com/8902 Reviewed-by: Rob Pike --- draw/gen.go | 112 ++++++++++++++++++++------------ draw/impl.go | 176 ++++++++++++++++++++++++++++++++------------------ draw/scale.go | 48 ++++++++++---- 3 files changed, 220 insertions(+), 116 deletions(-) diff --git a/draw/gen.go b/draw/gen.go index 345c7fd..5d560df 100644 --- a/draw/gen.go +++ b/draw/gen.go @@ -644,7 +644,7 @@ func expnDollar(prefix, dollar, suffix string, d *data) string { func expnSwitch(op, dType string, expandBoth bool, template string) string { if op == "" && dType != "anyDType" { - lines := []string{"switch op {"} + lines := []string{"switch o.Op {"} for _, op = range ops { lines = append(lines, fmt.Sprintf("case %s:", op), @@ -818,51 +818,65 @@ func relName(s string) string { const ( codeRoot = ` func (z $receiver) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, opts *Options) { + var o Options + if opts != nil { + o = *opts + } + // adr is the affected destination pixels, relative to dr.Min. adr := dst.Bounds().Intersect(dr).Sub(dr.Min) + // TODO: clip adr to o.DstMask.Bounds(). if adr.Empty() || sr.Empty() { return } - op := opts.op() - if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil. - op = Src + if o.Op == Over && o.SrcMask == nil && opaque(src) { + o.Op = Src } + // sr is the source pixels. If it extends beyond the src bounds, // we cannot use the type-specific fast paths, as they access // the Pix fields directly without bounds checking. - if !sr.In(src.Bounds()) { - switch op { + // + // Similarly, the fast paths assume that the masks are nil. + if o.DstMask != nil || o.SrcMask != nil || !sr.In(src.Bounds()) { + switch o.Op { case Over: z.scale_Image_Image_Over(dst, dr, adr, src, sr) case Src: z.scale_Image_Image_Src(dst, dr, adr, src, sr) } } else if _, ok := src.(*image.Uniform); ok { - Draw(dst, dr, src, src.Bounds().Min, op) + Draw(dst, dr, src, src.Bounds().Min, o.Op) } else { $switch z.scale_$dTypeRN_$sTypeRN$sratio_$op(dst, dr, adr, src, sr) } } func (z $receiver) Transform(dst Image, s2d *f64.Aff3, src image.Image, sr image.Rectangle, opts *Options) { + var o Options + if opts != nil { + o = *opts + } + dr := transformRect(s2d, &sr) // adr is the affected destination pixels. adr := dst.Bounds().Intersect(dr) + // TODO: clip adr to o.DstMask.Bounds(). if adr.Empty() || sr.Empty() { return } - op := opts.op() - if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil. - op = Src + if o.Op == Over && o.SrcMask == nil && opaque(src) { + o.Op = Src } + d2s := invert(s2d) - // bias is a translation of the mapping from dst co-ordinates to - // src co-ordinates such that the latter temporarily have - // non-negative X and Y co-ordinates. This allows us to write - // int(f) instead of int(math.Floor(f)), since "round to zero" and - // "round down" are equivalent when f >= 0, but the former is much - // cheaper. The X-- and Y-- are because the TransformLeaf methods - // have a "sx -= 0.5" adjustment. + // bias is a translation of the mapping from dst coordinates to src + // coordinates such that the latter temporarily have non-negative X + // and Y coordinates. This allows us to write int(f) instead of + // int(math.Floor(f)), since "round to zero" and "round down" are + // equivalent when f >= 0, but the former is much cheaper. The X-- + // and Y-- are because the TransformLeaf methods have a "sx -= 0.5" + // adjustment. bias := transformRect(&d2s, &adr).Min bias.X-- bias.Y-- @@ -873,15 +887,17 @@ const ( // sr is the source pixels. If it extends beyond the src bounds, // we cannot use the type-specific fast paths, as they access // the Pix fields directly without bounds checking. - if !sr.In(src.Bounds()) { - switch op { + // + // Similarly, the fast paths assume that the masks are nil. + if o.DstMask != nil || o.SrcMask != nil || !sr.In(src.Bounds()) { + switch o.Op { case Over: z.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias) case Src: z.transform_Image_Image_Src(dst, dr, adr, &d2s, src, sr, bias) } } else if u, ok := src.(*image.Uniform); ok { - transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, op) + transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, o.Op) } else { $switch z.transform_$dTypeRN_$sTypeRN$sratio_$op(dst, dr, adr, &d2s, src, sr, bias) } @@ -1042,18 +1058,24 @@ const ( z.kernel.Scale(dst, dr, src, sr, opts) return } + + var o Options + if opts != nil { + o = *opts + } + // adr is the affected destination pixels, relative to dr.Min. adr := dst.Bounds().Intersect(dr).Sub(dr.Min) + // TODO: clip adr to o.DstMask.Bounds(). if adr.Empty() || sr.Empty() { return } - op := opts.op() - if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil. - op = Src + if o.Op == Over && o.SrcMask == nil && opaque(src) { + o.Op = Src } - if _, ok := src.(*image.Uniform); ok && sr.In(src.Bounds()) { - Draw(dst, dr, src, src.Bounds().Min, op) + if _, ok := src.(*image.Uniform); ok && o.DstMask == nil && o.SrcMask == nil && sr.In(src.Bounds()) { + Draw(dst, dr, src, src.Bounds().Min, o.Op) return } @@ -1072,34 +1094,42 @@ const ( // sr is the source pixels. If it extends beyond the src bounds, // we cannot use the type-specific fast paths, as they access // the Pix fields directly without bounds checking. - if !sr.In(src.Bounds()) { + // + // Similarly, the fast paths assume that the masks are nil. + if o.SrcMask != nil || !sr.In(src.Bounds()) { z.scaleX_Image(tmp, src, sr) } else { $switchS z.scaleX_$sTypeRN$sratio(tmp, src, sr) } + // TODO: honor o.DstMask. $switchD z.scaleY_$dTypeRN_$op(dst, dr, adr, tmp) } func (q *Kernel) Transform(dst Image, s2d *f64.Aff3, src image.Image, sr image.Rectangle, opts *Options) { + var o Options + if opts != nil { + o = *opts + } + dr := transformRect(s2d, &sr) // adr is the affected destination pixels. adr := dst.Bounds().Intersect(dr) + // TODO: clip adr to o.DstMask.Bounds(). if adr.Empty() || sr.Empty() { return } - op := opts.op() - if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil. - op = Src + if o.Op == Over && o.SrcMask == nil && opaque(src) { + o.Op = Src } d2s := invert(s2d) - // bias is a translation of the mapping from dst co-ordinates to - // src co-ordinates such that the latter temporarily have - // non-negative X and Y co-ordinates. This allows us to write - // int(f) instead of int(math.Floor(f)), since "round to zero" and - // "round down" are equivalent when f >= 0, but the former is much - // cheaper. The X-- and Y-- are because the TransformLeaf methods - // have a "sx -= 0.5" adjustment. + // bias is a translation of the mapping from dst coordinates to src + // coordinates such that the latter temporarily have non-negative X + // and Y coordinates. This allows us to write int(f) instead of + // int(math.Floor(f)), since "round to zero" and "round down" are + // equivalent when f >= 0, but the former is much cheaper. The X-- + // and Y-- are because the TransformLeaf methods have a "sx -= 0.5" + // adjustment. bias := transformRect(&d2s, &adr).Min bias.X-- bias.Y-- @@ -1108,8 +1138,8 @@ const ( // Make adr relative to dr.Min. adr = adr.Sub(dr.Min) - if u, ok := src.(*image.Uniform); ok && sr.In(src.Bounds()) { - transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, op) + if u, ok := src.(*image.Uniform); ok && o.DstMask != nil && o.SrcMask != nil && sr.In(src.Bounds()) { + transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, o.Op) return } @@ -1125,8 +1155,10 @@ const ( // sr is the source pixels. If it extends beyond the src bounds, // we cannot use the type-specific fast paths, as they access // the Pix fields directly without bounds checking. - if !sr.In(src.Bounds()) { - switch op { + // + // Similarly, the fast paths assume that the masks are nil. + if o.DstMask != nil || o.SrcMask != nil || !sr.In(src.Bounds()) { + switch o.Op { case Over: q.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale) case Src: diff --git a/draw/impl.go b/draw/impl.go index 5292ced..789b8a3 100644 --- a/draw/impl.go +++ b/draw/impl.go @@ -11,29 +11,37 @@ import ( ) func (z nnInterpolator) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, opts *Options) { + var o Options + if opts != nil { + o = *opts + } + // adr is the affected destination pixels, relative to dr.Min. adr := dst.Bounds().Intersect(dr).Sub(dr.Min) + // TODO: clip adr to o.DstMask.Bounds(). if adr.Empty() || sr.Empty() { return } - op := opts.op() - if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil. - op = Src + if o.Op == Over && o.SrcMask == nil && opaque(src) { + o.Op = Src } + // sr is the source pixels. If it extends beyond the src bounds, // we cannot use the type-specific fast paths, as they access // the Pix fields directly without bounds checking. - if !sr.In(src.Bounds()) { - switch op { + // + // Similarly, the fast paths assume that the masks are nil. + if o.DstMask != nil || o.SrcMask != nil || !sr.In(src.Bounds()) { + switch o.Op { case Over: z.scale_Image_Image_Over(dst, dr, adr, src, sr) case Src: z.scale_Image_Image_Src(dst, dr, adr, src, sr) } } else if _, ok := src.(*image.Uniform); ok { - Draw(dst, dr, src, src.Bounds().Min, op) + Draw(dst, dr, src, src.Bounds().Min, o.Op) } else { - switch op { + switch o.Op { case Over: switch dst := dst.(type) { case *image.RGBA: @@ -88,24 +96,30 @@ func (z nnInterpolator) Scale(dst Image, dr image.Rectangle, src image.Image, sr } func (z nnInterpolator) Transform(dst Image, s2d *f64.Aff3, src image.Image, sr image.Rectangle, opts *Options) { + var o Options + if opts != nil { + o = *opts + } + dr := transformRect(s2d, &sr) // adr is the affected destination pixels. adr := dst.Bounds().Intersect(dr) + // TODO: clip adr to o.DstMask.Bounds(). if adr.Empty() || sr.Empty() { return } - op := opts.op() - if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil. - op = Src + if o.Op == Over && o.SrcMask == nil && opaque(src) { + o.Op = Src } + d2s := invert(s2d) - // bias is a translation of the mapping from dst co-ordinates to - // src co-ordinates such that the latter temporarily have - // non-negative X and Y co-ordinates. This allows us to write - // int(f) instead of int(math.Floor(f)), since "round to zero" and - // "round down" are equivalent when f >= 0, but the former is much - // cheaper. The X-- and Y-- are because the TransformLeaf methods - // have a "sx -= 0.5" adjustment. + // bias is a translation of the mapping from dst coordinates to src + // coordinates such that the latter temporarily have non-negative X + // and Y coordinates. This allows us to write int(f) instead of + // int(math.Floor(f)), since "round to zero" and "round down" are + // equivalent when f >= 0, but the former is much cheaper. The X-- + // and Y-- are because the TransformLeaf methods have a "sx -= 0.5" + // adjustment. bias := transformRect(&d2s, &adr).Min bias.X-- bias.Y-- @@ -116,17 +130,19 @@ func (z nnInterpolator) Transform(dst Image, s2d *f64.Aff3, src image.Image, sr // sr is the source pixels. If it extends beyond the src bounds, // we cannot use the type-specific fast paths, as they access // the Pix fields directly without bounds checking. - if !sr.In(src.Bounds()) { - switch op { + // + // Similarly, the fast paths assume that the masks are nil. + if o.DstMask != nil || o.SrcMask != nil || !sr.In(src.Bounds()) { + switch o.Op { case Over: z.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias) case Src: z.transform_Image_Image_Src(dst, dr, adr, &d2s, src, sr, bias) } } else if u, ok := src.(*image.Uniform); ok { - transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, op) + transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, o.Op) } else { - switch op { + switch o.Op { case Over: switch dst := dst.(type) { case *image.RGBA: @@ -934,29 +950,37 @@ func (nnInterpolator) transform_Image_Image_Src(dst Image, dr, adr image.Rectang } func (z ablInterpolator) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, opts *Options) { + var o Options + if opts != nil { + o = *opts + } + // adr is the affected destination pixels, relative to dr.Min. adr := dst.Bounds().Intersect(dr).Sub(dr.Min) + // TODO: clip adr to o.DstMask.Bounds(). if adr.Empty() || sr.Empty() { return } - op := opts.op() - if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil. - op = Src + if o.Op == Over && o.SrcMask == nil && opaque(src) { + o.Op = Src } + // sr is the source pixels. If it extends beyond the src bounds, // we cannot use the type-specific fast paths, as they access // the Pix fields directly without bounds checking. - if !sr.In(src.Bounds()) { - switch op { + // + // Similarly, the fast paths assume that the masks are nil. + if o.DstMask != nil || o.SrcMask != nil || !sr.In(src.Bounds()) { + switch o.Op { case Over: z.scale_Image_Image_Over(dst, dr, adr, src, sr) case Src: z.scale_Image_Image_Src(dst, dr, adr, src, sr) } } else if _, ok := src.(*image.Uniform); ok { - Draw(dst, dr, src, src.Bounds().Min, op) + Draw(dst, dr, src, src.Bounds().Min, o.Op) } else { - switch op { + switch o.Op { case Over: switch dst := dst.(type) { case *image.RGBA: @@ -1011,24 +1035,30 @@ func (z ablInterpolator) Scale(dst Image, dr image.Rectangle, src image.Image, s } func (z ablInterpolator) Transform(dst Image, s2d *f64.Aff3, src image.Image, sr image.Rectangle, opts *Options) { + var o Options + if opts != nil { + o = *opts + } + dr := transformRect(s2d, &sr) // adr is the affected destination pixels. adr := dst.Bounds().Intersect(dr) + // TODO: clip adr to o.DstMask.Bounds(). if adr.Empty() || sr.Empty() { return } - op := opts.op() - if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil. - op = Src + if o.Op == Over && o.SrcMask == nil && opaque(src) { + o.Op = Src } + d2s := invert(s2d) - // bias is a translation of the mapping from dst co-ordinates to - // src co-ordinates such that the latter temporarily have - // non-negative X and Y co-ordinates. This allows us to write - // int(f) instead of int(math.Floor(f)), since "round to zero" and - // "round down" are equivalent when f >= 0, but the former is much - // cheaper. The X-- and Y-- are because the TransformLeaf methods - // have a "sx -= 0.5" adjustment. + // bias is a translation of the mapping from dst coordinates to src + // coordinates such that the latter temporarily have non-negative X + // and Y coordinates. This allows us to write int(f) instead of + // int(math.Floor(f)), since "round to zero" and "round down" are + // equivalent when f >= 0, but the former is much cheaper. The X-- + // and Y-- are because the TransformLeaf methods have a "sx -= 0.5" + // adjustment. bias := transformRect(&d2s, &adr).Min bias.X-- bias.Y-- @@ -1039,17 +1069,19 @@ func (z ablInterpolator) Transform(dst Image, s2d *f64.Aff3, src image.Image, sr // sr is the source pixels. If it extends beyond the src bounds, // we cannot use the type-specific fast paths, as they access // the Pix fields directly without bounds checking. - if !sr.In(src.Bounds()) { - switch op { + // + // Similarly, the fast paths assume that the masks are nil. + if o.DstMask != nil || o.SrcMask != nil || !sr.In(src.Bounds()) { + switch o.Op { case Over: z.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias) case Src: z.transform_Image_Image_Src(dst, dr, adr, &d2s, src, sr, bias) } } else if u, ok := src.(*image.Uniform); ok { - transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, op) + transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, o.Op) } else { - switch op { + switch o.Op { case Over: switch dst := dst.(type) { case *image.RGBA: @@ -4033,18 +4065,24 @@ func (z *kernelScaler) Scale(dst Image, dr image.Rectangle, src image.Image, sr z.kernel.Scale(dst, dr, src, sr, opts) return } + + var o Options + if opts != nil { + o = *opts + } + // adr is the affected destination pixels, relative to dr.Min. adr := dst.Bounds().Intersect(dr).Sub(dr.Min) + // TODO: clip adr to o.DstMask.Bounds(). if adr.Empty() || sr.Empty() { return } - op := opts.op() - if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil. - op = Src + if o.Op == Over && o.SrcMask == nil && opaque(src) { + o.Op = Src } - if _, ok := src.(*image.Uniform); ok && sr.In(src.Bounds()) { - Draw(dst, dr, src, src.Bounds().Min, op) + if _, ok := src.(*image.Uniform); ok && o.DstMask == nil && o.SrcMask == nil && sr.In(src.Bounds()) { + Draw(dst, dr, src, src.Bounds().Min, o.Op) return } @@ -4063,7 +4101,9 @@ func (z *kernelScaler) Scale(dst Image, dr image.Rectangle, src image.Image, sr // sr is the source pixels. If it extends beyond the src bounds, // we cannot use the type-specific fast paths, as they access // the Pix fields directly without bounds checking. - if !sr.In(src.Bounds()) { + // + // Similarly, the fast paths assume that the masks are nil. + if o.SrcMask != nil || !sr.In(src.Bounds()) { z.scaleX_Image(tmp, src, sr) } else { switch src := src.(type) { @@ -4091,7 +4131,8 @@ func (z *kernelScaler) Scale(dst Image, dr image.Rectangle, src image.Image, sr } } - switch op { + // TODO: honor o.DstMask. + switch o.Op { case Over: switch dst := dst.(type) { case *image.RGBA: @@ -4110,24 +4151,29 @@ func (z *kernelScaler) Scale(dst Image, dr image.Rectangle, src image.Image, sr } func (q *Kernel) Transform(dst Image, s2d *f64.Aff3, src image.Image, sr image.Rectangle, opts *Options) { + var o Options + if opts != nil { + o = *opts + } + dr := transformRect(s2d, &sr) // adr is the affected destination pixels. adr := dst.Bounds().Intersect(dr) + // TODO: clip adr to o.DstMask.Bounds(). if adr.Empty() || sr.Empty() { return } - op := opts.op() - if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil. - op = Src + if o.Op == Over && o.SrcMask == nil && opaque(src) { + o.Op = Src } d2s := invert(s2d) - // bias is a translation of the mapping from dst co-ordinates to - // src co-ordinates such that the latter temporarily have - // non-negative X and Y co-ordinates. This allows us to write - // int(f) instead of int(math.Floor(f)), since "round to zero" and - // "round down" are equivalent when f >= 0, but the former is much - // cheaper. The X-- and Y-- are because the TransformLeaf methods - // have a "sx -= 0.5" adjustment. + // bias is a translation of the mapping from dst coordinates to src + // coordinates such that the latter temporarily have non-negative X + // and Y coordinates. This allows us to write int(f) instead of + // int(math.Floor(f)), since "round to zero" and "round down" are + // equivalent when f >= 0, but the former is much cheaper. The X-- + // and Y-- are because the TransformLeaf methods have a "sx -= 0.5" + // adjustment. bias := transformRect(&d2s, &adr).Min bias.X-- bias.Y-- @@ -4136,8 +4182,8 @@ func (q *Kernel) Transform(dst Image, s2d *f64.Aff3, src image.Image, sr image.R // Make adr relative to dr.Min. adr = adr.Sub(dr.Min) - if u, ok := src.(*image.Uniform); ok && sr.In(src.Bounds()) { - transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, op) + if u, ok := src.(*image.Uniform); ok && o.DstMask != nil && o.SrcMask != nil && sr.In(src.Bounds()) { + transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, o.Op) return } @@ -4153,15 +4199,17 @@ func (q *Kernel) Transform(dst Image, s2d *f64.Aff3, src image.Image, sr image.R // sr is the source pixels. If it extends beyond the src bounds, // we cannot use the type-specific fast paths, as they access // the Pix fields directly without bounds checking. - if !sr.In(src.Bounds()) { - switch op { + // + // Similarly, the fast paths assume that the masks are nil. + if o.DstMask != nil || o.SrcMask != nil || !sr.In(src.Bounds()) { + switch o.Op { case Over: q.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale) case Src: q.transform_Image_Image_Src(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale) } } else { - switch op { + switch o.Op { case Over: switch dst := dst.(type) { case *image.RGBA: diff --git a/draw/scale.go b/draw/scale.go index 8207e81..85659cf 100644 --- a/draw/scale.go +++ b/draw/scale.go @@ -19,12 +19,13 @@ import ( // the part of the destination image defined by dst and the translation of sr // so that sr.Min translates to dp. func Copy(dst Image, dp image.Point, src image.Image, sr image.Rectangle, opts *Options) { - mask, mp := image.Image(nil), image.Point{} + var o Options if opts != nil { - // TODO: set mask and mp. + o = *opts } dr := sr.Add(dp.Sub(sr.Min)) - DrawMask(dst, dr, src, sr.Min, mask, mp, opts.op()) + // TODO: honor o.DstMask and o.SrcMask. + DrawMask(dst, dr, src, sr.Min, nil, image.Point{}, o.Op) } // Scaler scales the part of the source image defined by src and sr and writes @@ -59,15 +60,38 @@ type Options struct { // Op is the compositing operator. The default value is Over. Op Op - // TODO: add fields a la - // https://groups.google.com/forum/#!topic/golang-dev/fgn_xM0aeq4 -} + // Masks limit what parts of the dst image are drawn to and what parts of + // the src image are drawn from. + // + // A dst or src mask image having a zero alpha (transparent) pixel value in + // the respective coordinate space means that that dst pixel is entirely + // unaffected or that src pixel is considered transparent black. A full + // alpha (opaque) value means that the dst pixel is maximally affected or + // the src pixel contributes maximally. The default values, nil, are + // equivalent to fully opaque, infinitely large mask images. + // + // The DstMask is otherwise known as a clip mask, and its pixels map 1:1 to + // the dst image's pixels. DstMaskP in DstMask space corresponds to + // image.Point{X:0, Y:0} in dst space. For example, when limiting + // repainting to a 'dirty rectangle', use that image.Rectangle and a zero + // image.Point as the DstMask and DstMaskP. + // + // The SrcMask's pixels map 1:1 to the src image's pixels. SrcMaskP in + // SrcMask space corresponds to image.Point{X:0, Y:0} in src space. For + // example, when drawing font glyphs in a uniform color, use an + // *image.Uniform as the src, and use the glyph atlas image and the + // per-glyph offset as SrcMask and SrcMaskP: + // Copy(dst, dp, image.NewUniform(color), image.Rect(0, 0, glyphWidth, glyphHeight), &Options{ + // SrcMask: glyphAtlas, + // SrcMaskP: glyphOffset, + // }) + DstMask image.Image + DstMaskP image.Point + SrcMask image.Image + SrcMaskP image.Point + // TODO: actually implement DstMask and SrcMask. -func (o *Options) op() Op { - if o == nil { - return Over - } - return o.Op + // TODO: a smooth vs sharp edges option, for arbitrary rotations? } // Interpolator is an interpolation algorithm, when dst and src pixels don't @@ -218,7 +242,7 @@ func newDistrib(q *Kernel, dw, sw int32) distrib { // Make the sources slice, one source for each column or row, and temporarily // appropriate its elements' fields so that invTotalWeight is the scaled - // co-ordinate of the source column or row, and i and j are the lower and + // coordinate of the source column or row, and i and j are the lower and // upper bounds of the range of destination columns or rows affected by the // source column or row. n, sources := int32(0), make([]source, dw)