func NewYCbCrAligned(r image.Rectangle, subsampleRatio image.YCbCrSubsampleRatio) *image.YCbCr {
	w, h, cw, ch := r.Dx(), r.Dy(), 0, 0
	switch subsampleRatio {
	case image.YCbCrSubsampleRatio422:
		cw = (r.Max.X+1)/2 - r.Min.X/2
		ch = h
	case image.YCbCrSubsampleRatio420:
		cw = (r.Max.X+1)/2 - r.Min.X/2
		ch = (r.Max.Y+1)/2 - r.Min.Y/2
	case image.YCbCrSubsampleRatio440:
		cw = w
		ch = (r.Max.Y+1)/2 - r.Min.Y/2
	default:
		cw = w
		ch = h
	}
	yStride := pad(w, alignSize) + alignSize
	cStride := pad(cw, alignSize) + alignSize
	yHeight := pad(h, alignSize) + alignSize
	cHeight := pad(ch, alignSize) + alignSize
	b := make([]byte, yStride*yHeight+2*cStride*cHeight)
	return &image.YCbCr{
		Y:              b[:yStride*yHeight],
		Cb:             b[yStride*yHeight+0*cStride*cHeight : yStride*yHeight+1*cStride*cHeight],
		Cr:             b[yStride*yHeight+1*cStride*cHeight : yStride*yHeight+2*cStride*cHeight],
		SubsampleRatio: subsampleRatio,
		YStride:        yStride,
		CStride:        cStride,
		Rect:           r,
	}
}
NewYCbCrAligned Allocates YCbCr image with padding.
Because LibJPEG needs extra padding to decoding buffer, This func add an
extra alignSize (16) padding to cover overflow from any such modes.
package jpeg
import "C"
import (
	"errors"
	"fmt"
	"image"
	"image/color"
	"io"
	"unsafe"
	"github.com/pixiv/go-libjpeg/rgb"
)
func newDecompress(r io.Reader) *C.struct_jpeg_decompress_struct {
	dinfo := C.new_decompress()
	if dinfo == nil {
		return nil
	}
	makeSourceManager(r, dinfo)
	return dinfo
}
func destroyDecompress(dinfo *C.struct_jpeg_decompress_struct) {
	if dinfo == nil {
		return
	}
	sourceManager := getSourceManager(dinfo)
	if sourceManager != nil {
		releaseSourceManager(sourceManager)
	}
	C.destroy_decompress(dinfo)
}
func readHeader(dinfo *C.struct_jpeg_decompress_struct) error {
	if C.read_header(dinfo, C.TRUE) != 0 {
		return errors.New(jpegErrorMessage(unsafe.Pointer(dinfo)))
	}
	return nil
}
func startDecompress(dinfo *C.struct_jpeg_decompress_struct) error {
	if C.start_decompress(dinfo) != 0 {
		return errors.New(jpegErrorMessage(unsafe.Pointer(dinfo)))
	}
	return nil
}
func finishDecompress(dinfo *C.struct_jpeg_decompress_struct) error {
	if C.finish_decompress(dinfo) != 0 {
		return errors.New(jpegErrorMessage(unsafe.Pointer(dinfo)))
	}
	return nil
}
func readScanlines(dinfo *C.struct_jpeg_decompress_struct, row *C.uchar, stride, height C.int) (lines C.JDIMENSION, err error) {
	code := C.int(0)
	lines = C.read_scanlines(dinfo, row, stride, height, &code)
	if code != 0 {
		err = errors.New(jpegErrorMessage(unsafe.Pointer(dinfo)))
	} else if lines == 0 {
		err = errors.New("unexpected EOF")
	}
	return
}
func readMCUGray(dinfo *C.struct_jpeg_decompress_struct, pix C.JSAMPROW, stride, iMCURows int) (line C.JDIMENSION, err error) {
	code := C.int(0)
	line = C.read_mcu_gray(dinfo, pix, C.int(stride), C.int(iMCURows), &code)
	if code != 0 {
		err = errors.New(jpegErrorMessage(unsafe.Pointer(dinfo)))
	}
	return
}
func readMCUYCbCr(dinfo *C.struct_jpeg_decompress_struct, y, cb, cr C.JSAMPROW, yStride, cStride int, iMCURows int) (line C.JDIMENSION, err error) {
	code := C.int(0)
	line = C.read_mcu_ycbcr(dinfo, y, cb, cr, C.int(yStride), C.int(cStride), C.int(iMCURows), &code)
	if code != 0 {
		err = errors.New(jpegErrorMessage(unsafe.Pointer(dinfo)))
	}
	return
}
type DecoderOptions struct {
	ScaleTarget            image.Rectangle  
	DCTMethod              DCTMethod        
	DisableFancyUpsampling bool             
	DisableBlockSmoothing  bool             
}
func SupportRGBA() bool {
	if getJCS_EXT_RGBA() == C.JCS_UNKNOWN {
		return false
	}
	return true
}
func Decode(r io.Reader, options *DecoderOptions) (dest image.Image, err error) {
	dinfo := newDecompress(r)
	if dinfo == nil {
		return nil, errors.New("allocation failed")
	}
	defer destroyDecompress(dinfo)
	err = readHeader(dinfo)
	if err != nil {
		return nil, err
	}
	setupDecoderOptions(dinfo, options)
	switch dinfo.num_components {
	case 1:
		if dinfo.jpeg_color_space != C.JCS_GRAYSCALE {
			return nil, errors.New("unsupported colorspace")
		}
		dest, err = decodeGray(dinfo)
	case 3:
		switch dinfo.jpeg_color_space {
		case C.JCS_YCbCr:
			dest, err = decodeYCbCr(dinfo)
		case C.JCS_RGB:
			dest, err = decodeRGB(dinfo)
		default:
			return nil, errors.New("unsupported colorspace")
		}
	default:
		return nil, fmt.Errorf("unsupported number of components: %d", dinfo.num_components)
	}
	return
}
func decodeGray(dinfo *C.struct_jpeg_decompress_struct) (dest *image.Gray, err error) {
	dinfo.raw_data_out = C.TRUE
	err = startDecompress(dinfo)
	if err != nil {
		return nil, err
	}
	defer func() {
		ferr := finishDecompress(dinfo)
		if ferr != nil && err == nil {
			err = ferr
		}
	}()
	compInfo := (*[1]C.jpeg_component_info)(unsafe.Pointer(dinfo.comp_info))
	dest = NewGrayAligned(image.Rect(0, 0, int(compInfo[0].downsampled_width), int(compInfo[0].downsampled_height)))
	iMCURows := int(C.DCT_v_scaled_size(dinfo, C.int(0)) * compInfo[0].v_samp_factor)
	for dinfo.output_scanline < dinfo.output_height {
		_, err = readMCUGray(dinfo, C.JSAMPROW(unsafe.Pointer(&dest.Pix[dest.Stride*int(dinfo.output_scanline)])), dest.Stride, iMCURows)
		if err != nil {
			return
		}
	}
	return
}
func decodeYCbCr(dinfo *C.struct_jpeg_decompress_struct) (dest *image.YCbCr, err error) {
	dinfo.raw_data_out = C.TRUE
	err = startDecompress(dinfo)
	if err != nil {
		return nil, err
	}
	compInfo := (*[3]C.jpeg_component_info)(unsafe.Pointer(dinfo.comp_info))
	dwY := compInfo[Y].downsampled_width
	dhY := compInfo[Y].downsampled_height
	dwC := compInfo[Cb].downsampled_width
	dhC := compInfo[Cb].downsampled_height
	if dwC != compInfo[Cr].downsampled_width || dhC != compInfo[Cr].downsampled_height {
		return nil, errors.New("Unsupported color subsampling (Cb and Cr differ)")
	}
	var subsampleRatio image.YCbCrSubsampleRatio
	cVDiv := 1
	switch {
	case dwY == dwC && dhY == dhC:
		subsampleRatio = image.YCbCrSubsampleRatio444
	case dwY == dwC && (dhY+1)/2 == dhC:
		subsampleRatio = image.YCbCrSubsampleRatio440
		cVDiv = 2
	case (dwY+1)/2 == dwC && dhY == dhC:
		subsampleRatio = image.YCbCrSubsampleRatio422
	case (dwY+1)/2 == dwC && (dhY+1)/2 == dhC:
		subsampleRatio = image.YCbCrSubsampleRatio420
		cVDiv = 2
	default:
		return nil, errors.New("Unsupported color subsampling")
	}
	dest = NewYCbCrAligned(image.Rect(0, 0, int(dinfo.output_width), int(dinfo.output_height)), subsampleRatio)
	var iMCURows int
	for i := 0; i < int(dinfo.num_components); i++ {
		compRows := int(C.DCT_v_scaled_size(dinfo, C.int(i)) * compInfo[i].v_samp_factor)
		if compRows > iMCURows {
			iMCURows = compRows
		}
	}
	yStride, cStride := dest.YStride, dest.CStride
	for dinfo.output_scanline < dinfo.output_height {
		y := C.JSAMPROW(unsafe.Pointer(&dest.Y[yStride*int(dinfo.output_scanline)]))
		cb := C.JSAMPROW(unsafe.Pointer(&dest.Cb[cStride*int(dinfo.output_scanline)/cVDiv]))
		cr := C.JSAMPROW(unsafe.Pointer(&dest.Cr[cStride*int(dinfo.output_scanline)/cVDiv]))
		_, err = readMCUYCbCr(dinfo, y, cb, cr, yStride, cStride, iMCURows)
		if err != nil {
			return
		}
	}
	return
}
func readRGBScanlines(dinfo *C.struct_jpeg_decompress_struct, pix []uint8, stride int) (err error) {
	err = startDecompress(dinfo)
	if err != nil {
		return
	}
	defer func() {
		ferr := finishDecompress(dinfo)
		if ferr != nil && err == nil {
			err = ferr
		}
	}()
	for dinfo.output_scanline < dinfo.output_height {
		pbuf := (*C.uchar)(unsafe.Pointer(&pix[stride*int(dinfo.output_scanline)]))
		_, err = readScanlines(dinfo, pbuf, C.int(stride), dinfo.rec_outbuf_height)
		if err != nil {
			return
		}
	}
	return
}
func decodeRGB(dinfo *C.struct_jpeg_decompress_struct) (dest *rgb.Image, err error) {
	C.jpeg_calc_output_dimensions(dinfo)
	dest = rgb.NewImage(image.Rect(0, 0, int(dinfo.output_width), int(dinfo.output_height)))
	dinfo.out_color_space = C.JCS_RGB
	err = readRGBScanlines(dinfo, dest.Pix, dest.Stride)
	return
}
func DecodeIntoRGB(r io.Reader, options *DecoderOptions) (dest *rgb.Image, err error) {
	dinfo := newDecompress(r)
	if dinfo == nil {
		return nil, errors.New("allocation failed")
	}
	defer destroyDecompress(dinfo)
	err = readHeader(dinfo)
	if err != nil {
		return nil, err
	}
	setupDecoderOptions(dinfo, options)
	return decodeRGB(dinfo)
}
func DecodeIntoRGBA(r io.Reader, options *DecoderOptions) (dest *image.RGBA, err error) {
	dinfo := newDecompress(r)
	if dinfo == nil {
		return nil, errors.New("allocation failed")
	}
	defer destroyDecompress(dinfo)
	defer func() {
		if r := recover(); r != nil {
			if _, ok := r.(error); !ok {
				err = fmt.Errorf("JPEG error: %v", r)
			}
		}
	}()
	err = readHeader(dinfo)
	if err != nil {
		return nil, err
	}
	setupDecoderOptions(dinfo, options)
	C.jpeg_calc_output_dimensions(dinfo)
	dest = image.NewRGBA(image.Rect(0, 0, int(dinfo.output_width), int(dinfo.output_height)))
	colorSpace := getJCS_EXT_RGBA()
	if colorSpace == C.JCS_UNKNOWN {
		return nil, errors.New("JCS_EXT_RGBA is not supported (probably built without libjpeg-turbo)")
	}
	dinfo.out_color_space = colorSpace
	err = readRGBScanlines(dinfo, dest.Pix, dest.Stride)
	return
}
func DecodeConfig(r io.Reader) (config image.Config, err error) {
	dinfo := newDecompress(r)
	if dinfo == nil {
		err = errors.New("allocation failed")
		return
	}
	defer destroyDecompress(dinfo)
	defer func() {
		if r := recover(); r != nil {
			if _, ok := r.(error); !ok {
				err = fmt.Errorf("JPEG error: %v", r)
			}
		}
	}()
	err = readHeader(dinfo)
	if err != nil {
		return
	}
	config = image.Config{
		ColorModel: color.YCbCrModel,
		Width:      int(dinfo.image_width),
		Height:     int(dinfo.image_height),
	}
	return
}
func setupDecoderOptions(dinfo *C.struct_jpeg_decompress_struct, opt *DecoderOptions) {
	tw, th := opt.ScaleTarget.Dx(), opt.ScaleTarget.Dy()
	if tw > 0 && th > 0 {
		var scaleFactor int
		for scaleFactor = 1; scaleFactor <= 8; scaleFactor++ {
			if ((scaleFactor*int(dinfo.image_width)+7)/8) >= tw &&
				((scaleFactor*int(dinfo.image_height)+7)/8) >= th {
				break
			}
		}
		if scaleFactor < 8 {
			dinfo.scale_num = C.uint(scaleFactor)
			dinfo.scale_denom = 8
		}
	}
	dinfo.dct_method = C.J_DCT_METHOD(opt.DCTMethod)
	if opt.DisableFancyUpsampling {
		dinfo.do_fancy_upsampling = C.FALSE
	} else {
		dinfo.do_fancy_upsampling = C.TRUE
	}
	if opt.DisableBlockSmoothing {
		dinfo.do_block_smoothing = C.FALSE
	} else {
		dinfo.do_block_smoothing = C.TRUE
	}
}
const alignSize int = C.ALIGN_SIZE