func makeSourceManager(src io.Reader, dinfo *C.struct_jpeg_decompress_struct) (mgr *sourceManager) { mgr = new(sourceManager) mgr.src = src mgr.pub = C.malloc_jpeg_source_mgr() if mgr.pub == nil { panic("Failed to allocate C.struct_jpeg_source_mgr") } mgr.buffer = C.malloc(readBufferSize) if mgr.buffer == nil { panic("Failed to allocate buffer") } mgr.pub.init_source = (*[0]byte)(C.sourceInit) mgr.pub.fill_input_buffer = (*[0]byte)(C.sourceFill) mgr.pub.skip_input_data = (*[0]byte)(C.sourceSkip) mgr.pub.resync_to_restart = (*[0]byte)(C._get_jpeg_resync_to_restart()) mgr.pub.term_source = (*[0]byte)(C.sourceTerm) mgr.pub.bytes_in_buffer = 0 mgr.pub.next_input_byte = nil dinfo.src = mgr.pub sourceManagerMapMutex.Lock() defer sourceManagerMapMutex.Unlock() sourceManagerMap[uintptr(unsafe.Pointer(mgr.pub))] = mgr return }
// TODO: supports decoding into image.RGBA instead of rgb.Image. 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 readScanLines(dinfo, dest.Pix, dest.Stride) return }
func Decode(r io.Reader) (img image.Image, er error) { /* Reading the whole file in may be inefficient, but libjpeg wants callbacks * to functions to read in more data, and that is a nightmare to implement. We * don't want to read the entire stream, however, which means pulling the header. * We may be able to read enough to call jpeg_read_header with a [10]byte, but * I'll change it later if need be, since this probably doesn't play nicely * with a non-closing io.Reader */ var wholeFile []byte if wholeFile, er = ioutil.ReadAll(r); er != nil { return } var cinfo C.struct_jpeg_decompress_struct var jerr C.struct_jpeg_error_mgr cinfo.err = C.jpeg_std_error(&jerr) C.jpeg_CreateDecompress(&cinfo, C.JPEG_LIB_VERSION, C.size_t(unsafe.Sizeof(cinfo))) C.jpeg_mem_src(&cinfo, (*C.uchar)(unsafe.Pointer(&wholeFile[0])), C.ulong(len(wholeFile))) if C.jpeg_read_header(&cinfo, C.TRUE) == C.JPEG_HEADER_OK { C.jpeg_start_decompress(&cinfo) if cinfo.num_components == 1 { img = decodeGrayscale(&cinfo) } else if cinfo.num_components == 3 { img = decodeRGB(&cinfo) } else if cinfo.num_components == 4 { img = decodeCMYK(&cinfo) } else { er = fmt.Errorf("Invalid number of components (%d)", cinfo.num_components) } if er == nil { C.jpeg_finish_decompress(&cinfo) } } C.jpeg_destroy_decompress(&cinfo) 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 } }
func decodeGray(dinfo *C.struct_jpeg_decompress_struct) (dest *image.Gray, err error) { // output dawnsampled raw data before starting decompress dinfo.raw_data_out = C.TRUE C.jpeg_start_decompress(dinfo) 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) C.decode_gray(dinfo, C.JSAMPROW(unsafe.Pointer(&dest.Pix[0])), C.int(dest.Stride), C.int(iMCURows)) C.jpeg_finish_decompress(dinfo) return }
func makeSourceManager(src io.Reader, dinfo *C.struct_jpeg_decompress_struct) (ret *sourceManager) { ret = (*sourceManager)(C.malloc(C.size_t(unsafe.Sizeof(sourceManager{})))) if ret == nil { panic("Failed to allocate sourceManager") } ret.magic = magic ret.src = src ret.pub.init_source = (*[0]byte)(C.sourceInit) ret.pub.fill_input_buffer = (*[0]byte)(C.sourceFill) ret.pub.skip_input_data = (*[0]byte)(C.sourceSkip) ret.pub.resync_to_restart = (*[0]byte)(C.jpeg_resync_to_restart) // default implementation ret.pub.term_source = (*[0]byte)(C.sourceTerm) ret.pub.bytes_in_buffer = 0 ret.pub.next_input_byte = nil dinfo.src = &ret.pub return }
func decodeYCbCr(dinfo *C.struct_jpeg_decompress_struct) (dest *image.YCbCr, err error) { // output dawnsampled raw data before starting decompress dinfo.raw_data_out = C.TRUE C.jpeg_start_decompress(dinfo) 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 //fmt.Printf("%d %d %d %d\n", dwY, dhY, dwC, dhC) if dwC != compInfo[Cr].downsampled_width || dhC != compInfo[Cr].downsampled_height { return nil, errors.New("Unsupported color subsampling (Cb and Cr differ)") } // Since the decisions about which DCT size and subsampling mode // to use, if any, are complex, instead just check the calculated // output plane sizes and infer the subsampling mode from that. var subsampleRatio image.YCbCrSubsampleRatio colorVDiv := 1 switch { case dwY == dwC && dhY == dhC: subsampleRatio = image.YCbCrSubsampleRatio444 case dwY == dwC && (dhY+1)/2 == dhC: subsampleRatio = image.YCbCrSubsampleRatio440 colorVDiv = 2 case (dwY+1)/2 == dwC && dhY == dhC: subsampleRatio = image.YCbCrSubsampleRatio422 case (dwY+1)/2 == dwC && (dhY+1)/2 == dhC: subsampleRatio = image.YCbCrSubsampleRatio420 colorVDiv = 2 default: return nil, errors.New("Unsupported color subsampling") } // Allocate distination iamge 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 } } //fmt.Printf("iMCU_rows: %d (div: %d)\n", iMCURows, colorVDiv) C.decode_ycbcr(dinfo, C.JSAMPROW(unsafe.Pointer(&dest.Y[0])), C.JSAMPROW(unsafe.Pointer(&dest.Cb[0])), C.JSAMPROW(unsafe.Pointer(&dest.Cr[0])), C.int(dest.YStride), C.int(dest.CStride), C.int(colorVDiv), C.int(iMCURows), ) C.jpeg_finish_decompress(dinfo) return }