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 <r@golang.org>
This commit is contained in:
Nigel Tao 2015-04-14 16:23:36 +10:00
parent e83a2376af
commit 69a0d8f9aa
3 changed files with 220 additions and 116 deletions

View File

@ -644,7 +644,7 @@ func expnDollar(prefix, dollar, suffix string, d *data) string {
func expnSwitch(op, dType string, expandBoth bool, template string) string { func expnSwitch(op, dType string, expandBoth bool, template string) string {
if op == "" && dType != "anyDType" { if op == "" && dType != "anyDType" {
lines := []string{"switch op {"} lines := []string{"switch o.Op {"}
for _, op = range ops { for _, op = range ops {
lines = append(lines, lines = append(lines,
fmt.Sprintf("case %s:", op), fmt.Sprintf("case %s:", op),
@ -818,51 +818,65 @@ func relName(s string) string {
const ( const (
codeRoot = ` codeRoot = `
func (z $receiver) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, opts *Options) { 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 is the affected destination pixels, relative to dr.Min.
adr := dst.Bounds().Intersect(dr).Sub(dr.Min) adr := dst.Bounds().Intersect(dr).Sub(dr.Min)
// TODO: clip adr to o.DstMask.Bounds().
if adr.Empty() || sr.Empty() { if adr.Empty() || sr.Empty() {
return return
} }
op := opts.op() if o.Op == Over && o.SrcMask == nil && opaque(src) {
if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil. o.Op = Src
op = Src
} }
// sr is the source pixels. If it extends beyond the src bounds, // sr is the source pixels. If it extends beyond the src bounds,
// we cannot use the type-specific fast paths, as they access // we cannot use the type-specific fast paths, as they access
// the Pix fields directly without bounds checking. // 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: case Over:
z.scale_Image_Image_Over(dst, dr, adr, src, sr) z.scale_Image_Image_Over(dst, dr, adr, src, sr)
case Src: case Src:
z.scale_Image_Image_Src(dst, dr, adr, src, sr) z.scale_Image_Image_Src(dst, dr, adr, src, sr)
} }
} else if _, ok := src.(*image.Uniform); ok { } 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 { } else {
$switch z.scale_$dTypeRN_$sTypeRN$sratio_$op(dst, dr, adr, src, sr) $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) { 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) dr := transformRect(s2d, &sr)
// adr is the affected destination pixels. // adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr) adr := dst.Bounds().Intersect(dr)
// TODO: clip adr to o.DstMask.Bounds().
if adr.Empty() || sr.Empty() { if adr.Empty() || sr.Empty() {
return return
} }
op := opts.op() if o.Op == Over && o.SrcMask == nil && opaque(src) {
if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil. o.Op = Src
op = Src
} }
d2s := invert(s2d) d2s := invert(s2d)
// bias is a translation of the mapping from dst co-ordinates to // bias is a translation of the mapping from dst coordinates to src
// src co-ordinates such that the latter temporarily have // coordinates such that the latter temporarily have non-negative X
// non-negative X and Y co-ordinates. This allows us to write // and Y coordinates. This allows us to write int(f) instead of
// int(f) instead of int(math.Floor(f)), since "round to zero" and // int(math.Floor(f)), since "round to zero" and "round down" are
// "round down" are equivalent when f >= 0, but the former is much // equivalent when f >= 0, but the former is much cheaper. The X--
// cheaper. The X-- and Y-- are because the TransformLeaf methods // and Y-- are because the TransformLeaf methods have a "sx -= 0.5"
// have a "sx -= 0.5" adjustment. // adjustment.
bias := transformRect(&d2s, &adr).Min bias := transformRect(&d2s, &adr).Min
bias.X-- bias.X--
bias.Y-- bias.Y--
@ -873,15 +887,17 @@ const (
// sr is the source pixels. If it extends beyond the src bounds, // sr is the source pixels. If it extends beyond the src bounds,
// we cannot use the type-specific fast paths, as they access // we cannot use the type-specific fast paths, as they access
// the Pix fields directly without bounds checking. // 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: case Over:
z.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias) z.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias)
case Src: case Src:
z.transform_Image_Image_Src(dst, dr, adr, &d2s, src, sr, bias) z.transform_Image_Image_Src(dst, dr, adr, &d2s, src, sr, bias)
} }
} else if u, ok := src.(*image.Uniform); ok { } 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 { } else {
$switch z.transform_$dTypeRN_$sTypeRN$sratio_$op(dst, dr, adr, &d2s, src, sr, bias) $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) z.kernel.Scale(dst, dr, src, sr, opts)
return return
} }
var o Options
if opts != nil {
o = *opts
}
// adr is the affected destination pixels, relative to dr.Min. // adr is the affected destination pixels, relative to dr.Min.
adr := dst.Bounds().Intersect(dr).Sub(dr.Min) adr := dst.Bounds().Intersect(dr).Sub(dr.Min)
// TODO: clip adr to o.DstMask.Bounds().
if adr.Empty() || sr.Empty() { if adr.Empty() || sr.Empty() {
return return
} }
op := opts.op() if o.Op == Over && o.SrcMask == nil && opaque(src) {
if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil. o.Op = Src
op = Src
} }
if _, ok := src.(*image.Uniform); ok && sr.In(src.Bounds()) { if _, ok := src.(*image.Uniform); ok && o.DstMask == nil && o.SrcMask == nil && sr.In(src.Bounds()) {
Draw(dst, dr, src, src.Bounds().Min, op) Draw(dst, dr, src, src.Bounds().Min, o.Op)
return return
} }
@ -1072,34 +1094,42 @@ const (
// sr is the source pixels. If it extends beyond the src bounds, // sr is the source pixels. If it extends beyond the src bounds,
// we cannot use the type-specific fast paths, as they access // we cannot use the type-specific fast paths, as they access
// the Pix fields directly without bounds checking. // 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) z.scaleX_Image(tmp, src, sr)
} else { } else {
$switchS z.scaleX_$sTypeRN$sratio(tmp, src, sr) $switchS z.scaleX_$sTypeRN$sratio(tmp, src, sr)
} }
// TODO: honor o.DstMask.
$switchD z.scaleY_$dTypeRN_$op(dst, dr, adr, tmp) $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) { 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) dr := transformRect(s2d, &sr)
// adr is the affected destination pixels. // adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr) adr := dst.Bounds().Intersect(dr)
// TODO: clip adr to o.DstMask.Bounds().
if adr.Empty() || sr.Empty() { if adr.Empty() || sr.Empty() {
return return
} }
op := opts.op() if o.Op == Over && o.SrcMask == nil && opaque(src) {
if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil. o.Op = Src
op = Src
} }
d2s := invert(s2d) d2s := invert(s2d)
// bias is a translation of the mapping from dst co-ordinates to // bias is a translation of the mapping from dst coordinates to src
// src co-ordinates such that the latter temporarily have // coordinates such that the latter temporarily have non-negative X
// non-negative X and Y co-ordinates. This allows us to write // and Y coordinates. This allows us to write int(f) instead of
// int(f) instead of int(math.Floor(f)), since "round to zero" and // int(math.Floor(f)), since "round to zero" and "round down" are
// "round down" are equivalent when f >= 0, but the former is much // equivalent when f >= 0, but the former is much cheaper. The X--
// cheaper. The X-- and Y-- are because the TransformLeaf methods // and Y-- are because the TransformLeaf methods have a "sx -= 0.5"
// have a "sx -= 0.5" adjustment. // adjustment.
bias := transformRect(&d2s, &adr).Min bias := transformRect(&d2s, &adr).Min
bias.X-- bias.X--
bias.Y-- bias.Y--
@ -1108,8 +1138,8 @@ const (
// Make adr relative to dr.Min. // Make adr relative to dr.Min.
adr = adr.Sub(dr.Min) adr = adr.Sub(dr.Min)
if u, ok := src.(*image.Uniform); ok && sr.In(src.Bounds()) { 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, op) transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, o.Op)
return return
} }
@ -1125,8 +1155,10 @@ const (
// sr is the source pixels. If it extends beyond the src bounds, // sr is the source pixels. If it extends beyond the src bounds,
// we cannot use the type-specific fast paths, as they access // we cannot use the type-specific fast paths, as they access
// the Pix fields directly without bounds checking. // 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: case Over:
q.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale) q.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale)
case Src: case Src:

View File

@ -11,29 +11,37 @@ import (
) )
func (z nnInterpolator) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, opts *Options) { 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 is the affected destination pixels, relative to dr.Min.
adr := dst.Bounds().Intersect(dr).Sub(dr.Min) adr := dst.Bounds().Intersect(dr).Sub(dr.Min)
// TODO: clip adr to o.DstMask.Bounds().
if adr.Empty() || sr.Empty() { if adr.Empty() || sr.Empty() {
return return
} }
op := opts.op() if o.Op == Over && o.SrcMask == nil && opaque(src) {
if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil. o.Op = Src
op = Src
} }
// sr is the source pixels. If it extends beyond the src bounds, // sr is the source pixels. If it extends beyond the src bounds,
// we cannot use the type-specific fast paths, as they access // we cannot use the type-specific fast paths, as they access
// the Pix fields directly without bounds checking. // 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: case Over:
z.scale_Image_Image_Over(dst, dr, adr, src, sr) z.scale_Image_Image_Over(dst, dr, adr, src, sr)
case Src: case Src:
z.scale_Image_Image_Src(dst, dr, adr, src, sr) z.scale_Image_Image_Src(dst, dr, adr, src, sr)
} }
} else if _, ok := src.(*image.Uniform); ok { } 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 { } else {
switch op { switch o.Op {
case Over: case Over:
switch dst := dst.(type) { switch dst := dst.(type) {
case *image.RGBA: 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) { 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) dr := transformRect(s2d, &sr)
// adr is the affected destination pixels. // adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr) adr := dst.Bounds().Intersect(dr)
// TODO: clip adr to o.DstMask.Bounds().
if adr.Empty() || sr.Empty() { if adr.Empty() || sr.Empty() {
return return
} }
op := opts.op() if o.Op == Over && o.SrcMask == nil && opaque(src) {
if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil. o.Op = Src
op = Src
} }
d2s := invert(s2d) d2s := invert(s2d)
// bias is a translation of the mapping from dst co-ordinates to // bias is a translation of the mapping from dst coordinates to src
// src co-ordinates such that the latter temporarily have // coordinates such that the latter temporarily have non-negative X
// non-negative X and Y co-ordinates. This allows us to write // and Y coordinates. This allows us to write int(f) instead of
// int(f) instead of int(math.Floor(f)), since "round to zero" and // int(math.Floor(f)), since "round to zero" and "round down" are
// "round down" are equivalent when f >= 0, but the former is much // equivalent when f >= 0, but the former is much cheaper. The X--
// cheaper. The X-- and Y-- are because the TransformLeaf methods // and Y-- are because the TransformLeaf methods have a "sx -= 0.5"
// have a "sx -= 0.5" adjustment. // adjustment.
bias := transformRect(&d2s, &adr).Min bias := transformRect(&d2s, &adr).Min
bias.X-- bias.X--
bias.Y-- 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, // sr is the source pixels. If it extends beyond the src bounds,
// we cannot use the type-specific fast paths, as they access // we cannot use the type-specific fast paths, as they access
// the Pix fields directly without bounds checking. // 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: case Over:
z.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias) z.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias)
case Src: case Src:
z.transform_Image_Image_Src(dst, dr, adr, &d2s, src, sr, bias) z.transform_Image_Image_Src(dst, dr, adr, &d2s, src, sr, bias)
} }
} else if u, ok := src.(*image.Uniform); ok { } 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 { } else {
switch op { switch o.Op {
case Over: case Over:
switch dst := dst.(type) { switch dst := dst.(type) {
case *image.RGBA: 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) { 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 is the affected destination pixels, relative to dr.Min.
adr := dst.Bounds().Intersect(dr).Sub(dr.Min) adr := dst.Bounds().Intersect(dr).Sub(dr.Min)
// TODO: clip adr to o.DstMask.Bounds().
if adr.Empty() || sr.Empty() { if adr.Empty() || sr.Empty() {
return return
} }
op := opts.op() if o.Op == Over && o.SrcMask == nil && opaque(src) {
if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil. o.Op = Src
op = Src
} }
// sr is the source pixels. If it extends beyond the src bounds, // sr is the source pixels. If it extends beyond the src bounds,
// we cannot use the type-specific fast paths, as they access // we cannot use the type-specific fast paths, as they access
// the Pix fields directly without bounds checking. // 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: case Over:
z.scale_Image_Image_Over(dst, dr, adr, src, sr) z.scale_Image_Image_Over(dst, dr, adr, src, sr)
case Src: case Src:
z.scale_Image_Image_Src(dst, dr, adr, src, sr) z.scale_Image_Image_Src(dst, dr, adr, src, sr)
} }
} else if _, ok := src.(*image.Uniform); ok { } 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 { } else {
switch op { switch o.Op {
case Over: case Over:
switch dst := dst.(type) { switch dst := dst.(type) {
case *image.RGBA: 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) { 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) dr := transformRect(s2d, &sr)
// adr is the affected destination pixels. // adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr) adr := dst.Bounds().Intersect(dr)
// TODO: clip adr to o.DstMask.Bounds().
if adr.Empty() || sr.Empty() { if adr.Empty() || sr.Empty() {
return return
} }
op := opts.op() if o.Op == Over && o.SrcMask == nil && opaque(src) {
if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil. o.Op = Src
op = Src
} }
d2s := invert(s2d) d2s := invert(s2d)
// bias is a translation of the mapping from dst co-ordinates to // bias is a translation of the mapping from dst coordinates to src
// src co-ordinates such that the latter temporarily have // coordinates such that the latter temporarily have non-negative X
// non-negative X and Y co-ordinates. This allows us to write // and Y coordinates. This allows us to write int(f) instead of
// int(f) instead of int(math.Floor(f)), since "round to zero" and // int(math.Floor(f)), since "round to zero" and "round down" are
// "round down" are equivalent when f >= 0, but the former is much // equivalent when f >= 0, but the former is much cheaper. The X--
// cheaper. The X-- and Y-- are because the TransformLeaf methods // and Y-- are because the TransformLeaf methods have a "sx -= 0.5"
// have a "sx -= 0.5" adjustment. // adjustment.
bias := transformRect(&d2s, &adr).Min bias := transformRect(&d2s, &adr).Min
bias.X-- bias.X--
bias.Y-- 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, // sr is the source pixels. If it extends beyond the src bounds,
// we cannot use the type-specific fast paths, as they access // we cannot use the type-specific fast paths, as they access
// the Pix fields directly without bounds checking. // 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: case Over:
z.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias) z.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias)
case Src: case Src:
z.transform_Image_Image_Src(dst, dr, adr, &d2s, src, sr, bias) z.transform_Image_Image_Src(dst, dr, adr, &d2s, src, sr, bias)
} }
} else if u, ok := src.(*image.Uniform); ok { } 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 { } else {
switch op { switch o.Op {
case Over: case Over:
switch dst := dst.(type) { switch dst := dst.(type) {
case *image.RGBA: 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) z.kernel.Scale(dst, dr, src, sr, opts)
return return
} }
var o Options
if opts != nil {
o = *opts
}
// adr is the affected destination pixels, relative to dr.Min. // adr is the affected destination pixels, relative to dr.Min.
adr := dst.Bounds().Intersect(dr).Sub(dr.Min) adr := dst.Bounds().Intersect(dr).Sub(dr.Min)
// TODO: clip adr to o.DstMask.Bounds().
if adr.Empty() || sr.Empty() { if adr.Empty() || sr.Empty() {
return return
} }
op := opts.op() if o.Op == Over && o.SrcMask == nil && opaque(src) {
if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil. o.Op = Src
op = Src
} }
if _, ok := src.(*image.Uniform); ok && sr.In(src.Bounds()) { if _, ok := src.(*image.Uniform); ok && o.DstMask == nil && o.SrcMask == nil && sr.In(src.Bounds()) {
Draw(dst, dr, src, src.Bounds().Min, op) Draw(dst, dr, src, src.Bounds().Min, o.Op)
return 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, // sr is the source pixels. If it extends beyond the src bounds,
// we cannot use the type-specific fast paths, as they access // we cannot use the type-specific fast paths, as they access
// the Pix fields directly without bounds checking. // 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) z.scaleX_Image(tmp, src, sr)
} else { } else {
switch src := src.(type) { 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: case Over:
switch dst := dst.(type) { switch dst := dst.(type) {
case *image.RGBA: 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) { 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) dr := transformRect(s2d, &sr)
// adr is the affected destination pixels. // adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr) adr := dst.Bounds().Intersect(dr)
// TODO: clip adr to o.DstMask.Bounds().
if adr.Empty() || sr.Empty() { if adr.Empty() || sr.Empty() {
return return
} }
op := opts.op() if o.Op == Over && o.SrcMask == nil && opaque(src) {
if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil. o.Op = Src
op = Src
} }
d2s := invert(s2d) d2s := invert(s2d)
// bias is a translation of the mapping from dst co-ordinates to // bias is a translation of the mapping from dst coordinates to src
// src co-ordinates such that the latter temporarily have // coordinates such that the latter temporarily have non-negative X
// non-negative X and Y co-ordinates. This allows us to write // and Y coordinates. This allows us to write int(f) instead of
// int(f) instead of int(math.Floor(f)), since "round to zero" and // int(math.Floor(f)), since "round to zero" and "round down" are
// "round down" are equivalent when f >= 0, but the former is much // equivalent when f >= 0, but the former is much cheaper. The X--
// cheaper. The X-- and Y-- are because the TransformLeaf methods // and Y-- are because the TransformLeaf methods have a "sx -= 0.5"
// have a "sx -= 0.5" adjustment. // adjustment.
bias := transformRect(&d2s, &adr).Min bias := transformRect(&d2s, &adr).Min
bias.X-- bias.X--
bias.Y-- 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. // Make adr relative to dr.Min.
adr = adr.Sub(dr.Min) adr = adr.Sub(dr.Min)
if u, ok := src.(*image.Uniform); ok && sr.In(src.Bounds()) { 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, op) transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, o.Op)
return 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, // sr is the source pixels. If it extends beyond the src bounds,
// we cannot use the type-specific fast paths, as they access // we cannot use the type-specific fast paths, as they access
// the Pix fields directly without bounds checking. // 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: case Over:
q.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale) q.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale)
case Src: case Src:
q.transform_Image_Image_Src(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale) q.transform_Image_Image_Src(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale)
} }
} else { } else {
switch op { switch o.Op {
case Over: case Over:
switch dst := dst.(type) { switch dst := dst.(type) {
case *image.RGBA: case *image.RGBA:

View File

@ -19,12 +19,13 @@ import (
// the part of the destination image defined by dst and the translation of sr // the part of the destination image defined by dst and the translation of sr
// so that sr.Min translates to dp. // so that sr.Min translates to dp.
func Copy(dst Image, dp image.Point, src image.Image, sr image.Rectangle, opts *Options) { 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 { if opts != nil {
// TODO: set mask and mp. o = *opts
} }
dr := sr.Add(dp.Sub(sr.Min)) 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 // 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 is the compositing operator. The default value is Over.
Op Op Op Op
// TODO: add fields a la // Masks limit what parts of the dst image are drawn to and what parts of
// https://groups.google.com/forum/#!topic/golang-dev/fgn_xM0aeq4 // 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 { // TODO: a smooth vs sharp edges option, for arbitrary rotations?
if o == nil {
return Over
}
return o.Op
} }
// Interpolator is an interpolation algorithm, when dst and src pixels don't // 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 // Make the sources slice, one source for each column or row, and temporarily
// appropriate its elements' fields so that invTotalWeight is the scaled // 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 // upper bounds of the range of destination columns or rows affected by the
// source column or row. // source column or row.
n, sources := int32(0), make([]source, dw) n, sources := int32(0), make([]source, dw)