1
0
mirror of https://github.com/golang/go synced 2024-10-01 05:28:33 -06:00

image/internal/imageutil: new package, used by image/draw and image/jpeg.

The imageutil.DrawYCbCr function lives in an internal package because it
is needed by both the image/draw and image/jpeg packages, but it doesn't
seem right for one of those two to depend on the other.

It could eventually go into the image package, but that would require
committing to an API for the rest of Go 1.x.

Change-Id: I7b12555c970d86409365e99eef9360702aaffa30
Reviewed-on: https://go-review.googlesource.com/7925
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Rob Pike <r@golang.org>
This commit is contained in:
Nigel Tao 2015-03-23 11:03:02 +11:00
parent db454afd23
commit b2f29511dd
4 changed files with 107 additions and 155 deletions

View File

@ -207,9 +207,9 @@ var pkgDeps = map[string][]string{
"flag": {"L4", "OS"}, "flag": {"L4", "OS"},
"go/build": {"L4", "OS", "GOPARSER"}, "go/build": {"L4", "OS", "GOPARSER"},
"html": {"L4"}, "html": {"L4"},
"image/draw": {"L4"}, "image/draw": {"L4", "image/internal/imageutil"},
"image/gif": {"L4", "compress/lzw", "image/color/palette", "image/draw"}, "image/gif": {"L4", "compress/lzw", "image/color/palette", "image/draw"},
"image/jpeg": {"L4"}, "image/jpeg": {"L4", "image/internal/imageutil"},
"image/png": {"L4", "compress/zlib"}, "image/png": {"L4", "compress/zlib"},
"index/suffixarray": {"L4", "regexp"}, "index/suffixarray": {"L4", "regexp"},
"math/big": {"L4"}, "math/big": {"L4"},

View File

@ -11,6 +11,7 @@ package draw
import ( import (
"image" "image"
"image/color" "image/color"
"image/internal/imageutil"
) )
// m is the maximum color value returned by image.Color.RGBA. // m is the maximum color value returned by image.Color.RGBA.
@ -124,7 +125,11 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas
drawNRGBAOver(dst0, r, src0, sp) drawNRGBAOver(dst0, r, src0, sp)
return return
case *image.YCbCr: case *image.YCbCr:
if drawYCbCr(dst0, r, src0, sp) { // An image.YCbCr is always fully opaque, and so if the
// mask is nil (i.e. fully opaque) then the op is
// effectively always Src. Similarly for image.Gray and
// image.CMYK.
if imageutil.DrawYCbCr(dst0, r, src0, sp) {
return return
} }
case *image.Gray: case *image.Gray:
@ -154,7 +159,7 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas
drawNRGBASrc(dst0, r, src0, sp) drawNRGBASrc(dst0, r, src0, sp)
return return
case *image.YCbCr: case *image.YCbCr:
if drawYCbCr(dst0, r, src0, sp) { if imageutil.DrawYCbCr(dst0, r, src0, sp) {
return return
} }
case *image.Gray: case *image.Gray:
@ -408,79 +413,7 @@ func drawNRGBASrc(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image
} }
} }
// TODO(nigeltao): this function is copy/pasted to image/jpeg/reader.go. We
// should un-copy/paste it.
func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Point) (ok bool) {
// An image.YCbCr is always fully opaque, and so if the mask is implicitly nil
// (i.e. fully opaque) then the op is effectively always Src.
x0 := (r.Min.X - dst.Rect.Min.X) * 4
x1 := (r.Max.X - dst.Rect.Min.X) * 4
y0 := r.Min.Y - dst.Rect.Min.Y
y1 := r.Max.Y - dst.Rect.Min.Y
switch src.SubsampleRatio {
case image.YCbCrSubsampleRatio444:
for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
dpix := dst.Pix[y*dst.Stride:]
yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X)
ci := (sy-src.Rect.Min.Y)*src.CStride + (sp.X - src.Rect.Min.X)
for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 {
rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci])
dpix[x+0] = rr
dpix[x+1] = gg
dpix[x+2] = bb
dpix[x+3] = 255
}
}
case image.YCbCrSubsampleRatio422:
for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
dpix := dst.Pix[y*dst.Stride:]
yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X)
ciBase := (sy-src.Rect.Min.Y)*src.CStride - src.Rect.Min.X/2
for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 {
ci := ciBase + sx/2
rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci])
dpix[x+0] = rr
dpix[x+1] = gg
dpix[x+2] = bb
dpix[x+3] = 255
}
}
case image.YCbCrSubsampleRatio420:
for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
dpix := dst.Pix[y*dst.Stride:]
yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X)
ciBase := (sy/2-src.Rect.Min.Y/2)*src.CStride - src.Rect.Min.X/2
for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 {
ci := ciBase + sx/2
rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci])
dpix[x+0] = rr
dpix[x+1] = gg
dpix[x+2] = bb
dpix[x+3] = 255
}
}
case image.YCbCrSubsampleRatio440:
for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
dpix := dst.Pix[y*dst.Stride:]
yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X)
ci := (sy/2-src.Rect.Min.Y/2)*src.CStride + (sp.X - src.Rect.Min.X)
for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 {
rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci])
dpix[x+0] = rr
dpix[x+1] = gg
dpix[x+2] = bb
dpix[x+3] = 255
}
}
default:
return false
}
return true
}
func drawGray(dst *image.RGBA, r image.Rectangle, src *image.Gray, sp image.Point) { func drawGray(dst *image.RGBA, r image.Rectangle, src *image.Gray, sp image.Point) {
// An image.Gray is always fully opaque, and so if the mask is implicitly nil
// (i.e. fully opaque) then the op is effectively always Src.
i0 := (r.Min.X - dst.Rect.Min.X) * 4 i0 := (r.Min.X - dst.Rect.Min.X) * 4
i1 := (r.Max.X - dst.Rect.Min.X) * 4 i1 := (r.Max.X - dst.Rect.Min.X) * 4
si0 := (sp.X - src.Rect.Min.X) * 1 si0 := (sp.X - src.Rect.Min.X) * 1
@ -503,8 +436,6 @@ func drawGray(dst *image.RGBA, r image.Rectangle, src *image.Gray, sp image.Poin
} }
func drawCMYK(dst *image.RGBA, r image.Rectangle, src *image.CMYK, sp image.Point) { func drawCMYK(dst *image.RGBA, r image.Rectangle, src *image.CMYK, sp image.Point) {
// An image.CMYK is always fully opaque, and so if the mask is implicitly nil
// (i.e. fully opaque) then the op is effectively always Src.
i0 := (r.Min.X - dst.Rect.Min.X) * 4 i0 := (r.Min.X - dst.Rect.Min.X) * 4
i1 := (r.Max.X - dst.Rect.Min.X) * 4 i1 := (r.Max.X - dst.Rect.Min.X) * 4
si0 := (sp.X - src.Rect.Min.X) * 4 si0 := (sp.X - src.Rect.Min.X) * 4

View File

@ -0,0 +1,96 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package imageutil contains code shared by image-related packages.
package imageutil
import (
"image"
"image/color"
)
// DrawYCbCr draws the YCbCr source image on the RGBA destination image with
// r.Min in dst aligned with sp in src. It returns whether the draw was
// successful. If it returns false, no dst pixels were changed.
func DrawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Point) (ok bool) {
// This function exists in the image/internal/imageutil package because it
// is needed by both the image/draw and image/jpeg packages, but it doesn't
// seem right for one of those two to depend on the other.
//
// Another option is to have this code be exported in the image package,
// but we'd need to make sure we're totally happy with the API (for the
// rest of Go 1 compatibility), and decide if we want to have a more
// general purpose DrawToRGBA method for other image types. One possibility
// is:
//
// func (src *YCbCr) CopyToRGBA(dst *RGBA, dr, sr Rectangle) (effectiveDr, effectiveSr Rectangle)
//
// in the spirit of the built-in copy function for 1-dimensional slices,
// that also allowed a CopyFromRGBA method if needed.
x0 := (r.Min.X - dst.Rect.Min.X) * 4
x1 := (r.Max.X - dst.Rect.Min.X) * 4
y0 := r.Min.Y - dst.Rect.Min.Y
y1 := r.Max.Y - dst.Rect.Min.Y
switch src.SubsampleRatio {
case image.YCbCrSubsampleRatio444:
for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
dpix := dst.Pix[y*dst.Stride:]
yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X)
ci := (sy-src.Rect.Min.Y)*src.CStride + (sp.X - src.Rect.Min.X)
for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 {
rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci])
dpix[x+0] = rr
dpix[x+1] = gg
dpix[x+2] = bb
dpix[x+3] = 255
}
}
case image.YCbCrSubsampleRatio422:
for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
dpix := dst.Pix[y*dst.Stride:]
yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X)
ciBase := (sy-src.Rect.Min.Y)*src.CStride - src.Rect.Min.X/2
for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 {
ci := ciBase + sx/2
rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci])
dpix[x+0] = rr
dpix[x+1] = gg
dpix[x+2] = bb
dpix[x+3] = 255
}
}
case image.YCbCrSubsampleRatio420:
for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
dpix := dst.Pix[y*dst.Stride:]
yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X)
ciBase := (sy/2-src.Rect.Min.Y/2)*src.CStride - src.Rect.Min.X/2
for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 {
ci := ciBase + sx/2
rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci])
dpix[x+0] = rr
dpix[x+1] = gg
dpix[x+2] = bb
dpix[x+3] = 255
}
}
case image.YCbCrSubsampleRatio440:
for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
dpix := dst.Pix[y*dst.Stride:]
yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X)
ci := (sy/2-src.Rect.Min.Y/2)*src.CStride + (sp.X - src.Rect.Min.X)
for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 {
rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci])
dpix[x+0] = rr
dpix[x+1] = gg
dpix[x+2] = bb
dpix[x+3] = 255
}
}
default:
return false
}
return true
}

View File

@ -10,6 +10,7 @@ package jpeg
import ( import (
"image" "image"
"image/color" "image/color"
"image/internal/imageutil"
"io" "io"
) )
@ -676,7 +677,7 @@ func (d *decoder) applyBlack() (image.Image, error) {
// above, so in practice, only the fourth channel (black) is inverted. // above, so in practice, only the fourth channel (black) is inverted.
bounds := d.img3.Bounds() bounds := d.img3.Bounds()
img := image.NewRGBA(bounds) img := image.NewRGBA(bounds)
drawYCbCr(img, bounds, d.img3, bounds.Min) imageutil.DrawYCbCr(img, bounds, d.img3, bounds.Min)
for iBase, y := 0, bounds.Min.Y; y < bounds.Max.Y; iBase, y = iBase+img.Stride, y+1 { for iBase, y := 0, bounds.Min.Y; y < bounds.Max.Y; iBase, y = iBase+img.Stride, y+1 {
for i, x := iBase+3, bounds.Min.X; x < bounds.Max.X; i, x = i+4, x+1 { for i, x := iBase+3, bounds.Min.X; x < bounds.Max.X; i, x = i+4, x+1 {
img.Pix[i] = 255 - d.blackPix[(y-bounds.Min.Y)*d.blackStride+(x-bounds.Min.X)] img.Pix[i] = 255 - d.blackPix[(y-bounds.Min.Y)*d.blackStride+(x-bounds.Min.X)]
@ -755,82 +756,6 @@ func (d *decoder) convertToRGB() (image.Image, error) {
return img, nil return img, nil
} }
// drawYCbCr is the non-exported drawYCbCr function copy/pasted from the
// image/draw package. It is copy/pasted because it doesn't seem right for the
// image/jpeg package to depend on image/draw.
//
// TODO(nigeltao): remove the copy/paste, possibly by moving this to be an
// exported method on *image.YCbCr. We'd need to make sure we're totally happy
// with the API (for the rest of Go 1 compatibility) though, and if we want to
// have a more general purpose DrawToRGBA method for other image types.
func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Point) (ok bool) {
// An image.YCbCr is always fully opaque, and so if the mask is implicitly nil
// (i.e. fully opaque) then the op is effectively always Src.
x0 := (r.Min.X - dst.Rect.Min.X) * 4
x1 := (r.Max.X - dst.Rect.Min.X) * 4
y0 := r.Min.Y - dst.Rect.Min.Y
y1 := r.Max.Y - dst.Rect.Min.Y
switch src.SubsampleRatio {
case image.YCbCrSubsampleRatio444:
for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
dpix := dst.Pix[y*dst.Stride:]
yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X)
ci := (sy-src.Rect.Min.Y)*src.CStride + (sp.X - src.Rect.Min.X)
for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 {
rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci])
dpix[x+0] = rr
dpix[x+1] = gg
dpix[x+2] = bb
dpix[x+3] = 255
}
}
case image.YCbCrSubsampleRatio422:
for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
dpix := dst.Pix[y*dst.Stride:]
yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X)
ciBase := (sy-src.Rect.Min.Y)*src.CStride - src.Rect.Min.X/2
for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 {
ci := ciBase + sx/2
rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci])
dpix[x+0] = rr
dpix[x+1] = gg
dpix[x+2] = bb
dpix[x+3] = 255
}
}
case image.YCbCrSubsampleRatio420:
for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
dpix := dst.Pix[y*dst.Stride:]
yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X)
ciBase := (sy/2-src.Rect.Min.Y/2)*src.CStride - src.Rect.Min.X/2
for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 {
ci := ciBase + sx/2
rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci])
dpix[x+0] = rr
dpix[x+1] = gg
dpix[x+2] = bb
dpix[x+3] = 255
}
}
case image.YCbCrSubsampleRatio440:
for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
dpix := dst.Pix[y*dst.Stride:]
yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X)
ci := (sy/2-src.Rect.Min.Y/2)*src.CStride + (sp.X - src.Rect.Min.X)
for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 {
rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci])
dpix[x+0] = rr
dpix[x+1] = gg
dpix[x+2] = bb
dpix[x+3] = 255
}
}
default:
return false
}
return true
}
// Decode reads a JPEG image from r and returns it as an image.Image. // Decode reads a JPEG image from r and returns it as an image.Image.
func Decode(r io.Reader) (image.Image, error) { func Decode(r io.Reader) (image.Image, error) {
var d decoder var d decoder