func setupEncoderOptions(cinfo *C.struct_jpeg_compress_struct, opt *EncoderOptions) { C.jpeg_set_quality(cinfo, C.int(opt.Quality), C.TRUE) if opt.OptimizeCoding { cinfo.optimize_coding = C.TRUE } else { cinfo.optimize_coding = C.FALSE } cinfo.dct_method = C.J_DCT_METHOD(opt.DCTMethod) }
// encode image.YCbCr func encodeYCbCr(cinfo *C.struct_jpeg_compress_struct, src *image.YCbCr, p *EncoderOptions) (err error) { // Set up compression parameters cinfo.image_width = C.JDIMENSION(src.Bounds().Dx()) cinfo.image_height = C.JDIMENSION(src.Bounds().Dy()) cinfo.input_components = 3 cinfo.in_color_space = C.JCS_YCbCr C.jpeg_set_defaults(cinfo) setupEncoderOptions(cinfo, p) compInfo := (*[3]C.jpeg_component_info)(unsafe.Pointer(cinfo.comp_info)) colorVDiv := 1 switch src.SubsampleRatio { case image.YCbCrSubsampleRatio444: // 1x1,1x1,1x1 compInfo[Y].h_samp_factor, compInfo[Y].v_samp_factor = 1, 1 compInfo[Cb].h_samp_factor, compInfo[Cb].v_samp_factor = 1, 1 compInfo[Cr].h_samp_factor, compInfo[Cr].v_samp_factor = 1, 1 case image.YCbCrSubsampleRatio440: // 1x2,1x1,1x1 compInfo[Y].h_samp_factor, compInfo[Y].v_samp_factor = 1, 2 compInfo[Cb].h_samp_factor, compInfo[Cb].v_samp_factor = 1, 1 compInfo[Cr].h_samp_factor, compInfo[Cr].v_samp_factor = 1, 1 colorVDiv = 2 case image.YCbCrSubsampleRatio422: // 2x1,1x1,1x1 compInfo[Y].h_samp_factor, compInfo[Y].v_samp_factor = 2, 1 compInfo[Cb].h_samp_factor, compInfo[Cb].v_samp_factor = 1, 1 compInfo[Cr].h_samp_factor, compInfo[Cr].v_samp_factor = 1, 1 case image.YCbCrSubsampleRatio420: // 2x2,1x1,1x1 compInfo[Y].h_samp_factor, compInfo[Y].v_samp_factor = 2, 2 compInfo[Cb].h_samp_factor, compInfo[Cb].v_samp_factor = 1, 1 compInfo[Cr].h_samp_factor, compInfo[Cr].v_samp_factor = 1, 1 colorVDiv = 2 } // libjpeg raw data in is in planar format, which avoids unnecessary // planar->packed->planar conversions. cinfo.raw_data_in = C.TRUE // Start compression C.jpeg_start_compress(cinfo, C.TRUE) C.encode_ycbcr( cinfo, C.JSAMPROW(unsafe.Pointer(&src.Y[0])), C.JSAMPROW(unsafe.Pointer(&src.Cb[0])), C.JSAMPROW(unsafe.Pointer(&src.Cr[0])), C.int(src.YStride), C.int(src.CStride), C.int(colorVDiv), ) C.jpeg_finish_compress(cinfo) return }
func makeDestinationManager(dest io.Writer, cinfo *C.struct_jpeg_compress_struct) (ret destinationManager) { ret.magic = magic ret.dest = dest ret.pub.init_destination = (*[0]byte)(C.destinationInit) ret.pub.empty_output_buffer = (*[0]byte)(C.destinationEmpty) ret.pub.term_destination = (*[0]byte)(C.destinationTerm) ret.pub.free_in_buffer = writeBufferSize ret.pub.next_output_byte = (*C.JOCTET)(&ret.buffer[0]) cinfo.dest = &ret.pub return }
// encode image.Gray func encodeGray(cinfo *C.struct_jpeg_compress_struct, src *image.Gray, p *EncoderOptions) (err error) { // Set up compression parameters cinfo.image_width = C.JDIMENSION(src.Bounds().Dx()) cinfo.image_height = C.JDIMENSION(src.Bounds().Dy()) cinfo.input_components = 1 cinfo.in_color_space = C.JCS_GRAYSCALE C.jpeg_set_defaults(cinfo) setupEncoderOptions(cinfo, p) compInfo := (*C.jpeg_component_info)(unsafe.Pointer(cinfo.comp_info)) compInfo.h_samp_factor, compInfo.v_samp_factor = 1, 1 // libjpeg raw data in is in planar format, which avoids unnecessary // planar->packed->planar conversions. cinfo.raw_data_in = C.TRUE // Start compression C.jpeg_start_compress(cinfo, C.TRUE) // Allocate JSAMPIMAGE to hold pointers to one iMCU worth of image data // this is a safe overestimate; we use the return value from // jpeg_read_raw_data to figure out what is the actual iMCU row count. var rowPtr [AlignSize]C.JSAMPROW arrayPtr := [1]C.JSAMPARRAY{ C.JSAMPARRAY(unsafe.Pointer(&rowPtr[0])), } var rows C.JDIMENSION for rows = 0; rows < cinfo.image_height; { // First fill in the pointers into the plane data buffers for j := 0; j < int(C.DCTSIZE*compInfo.v_samp_factor); j++ { rowPtr[j] = C.JSAMPROW(unsafe.Pointer(&src.Pix[src.Stride*(int(rows)+j)])) } // Get the data rows += C.jpeg_write_raw_data(cinfo, C.JSAMPIMAGE(unsafe.Pointer(&arrayPtr[0])), C.JDIMENSION(C.DCTSIZE*compInfo.v_samp_factor)) } C.jpeg_finish_compress(cinfo) return }
// encode image.Gray func encodeGray(cinfo *C.struct_jpeg_compress_struct, src *image.Gray, p *EncoderOptions) (err error) { // Set up compression parameters cinfo.image_width = C.JDIMENSION(src.Bounds().Dx()) cinfo.image_height = C.JDIMENSION(src.Bounds().Dy()) cinfo.input_components = 1 cinfo.in_color_space = C.JCS_GRAYSCALE C.jpeg_set_defaults(cinfo) setupEncoderOptions(cinfo, p) compInfo := (*C.jpeg_component_info)(unsafe.Pointer(cinfo.comp_info)) compInfo.h_samp_factor, compInfo.v_samp_factor = 1, 1 // libjpeg raw data in is in planar format, which avoids unnecessary // planar->packed->planar conversions. cinfo.raw_data_in = C.TRUE // Start compression C.jpeg_start_compress(cinfo, C.TRUE) C.encode_gray(cinfo, C.JSAMPROW(unsafe.Pointer(&src.Pix[0])), C.int(src.Stride)) C.jpeg_finish_compress(cinfo) return }
func makeDestinationManager(dest io.Writer, cinfo *C.struct_jpeg_compress_struct) (ret *destinationManager) { ret = (*destinationManager)(C.malloc(C.size_t(unsafe.Sizeof(destinationManager{})))) if ret == nil { panic("Failed to allocate destinationManager") } ret.magic = magic ret.dest = dest ret.pub.init_destination = (*[0]byte)(C.destinationInit) ret.pub.empty_output_buffer = (*[0]byte)(C.destinationEmpty) ret.pub.term_destination = (*[0]byte)(C.destinationTerm) ret.pub.free_in_buffer = writeBufferSize ret.pub.next_output_byte = (*C.JOCTET)(&ret.buffer[0]) cinfo.dest = &ret.pub return }
func Encode(w io.Writer, m image.Image, o *Options) error { quality := 75 if o != nil { quality = o.Quality } var cinfo C.struct_jpeg_compress_struct var jerr C.struct_jpeg_error_mgr var workBuf *C.uchar var workBufLen C.ulong cinfo.err = C.jpeg_std_error(&jerr) C.jpeg_CreateCompress(&cinfo, C.JPEG_LIB_VERSION, C.size_t(unsafe.Sizeof(cinfo))) C.jpeg_mem_dest(&cinfo, &workBuf, &workBufLen) bounds := m.Bounds() cinfo.image_width = C.JDIMENSION(bounds.Dx()) cinfo.image_height = C.JDIMENSION(bounds.Dy()) cinfo.input_components = 3 cinfo.in_color_space = C.JCS_RGB C.jpeg_set_defaults(&cinfo) C.jpeg_set_quality(&cinfo, C.int(quality), C.boolean(1)) C.jpeg_start_compress(&cinfo, C.boolean(1)) rowBuf := make([]byte, cinfo.image_width*3) for cinfo.next_scanline < cinfo.image_height { for x := 0; x < int(cinfo.image_width); x += 1 { r, g, b, _ := m.At(x, int(cinfo.next_scanline)).RGBA() rowBuf[x*3] = byte(r >> 8) rowBuf[x*3+1] = byte(g >> 8) rowBuf[x*3+2] = byte(b >> 8) } rowPointer := C.JSAMPROW(unsafe.Pointer(&rowBuf[0])) C.jpeg_write_scanlines(&cinfo, &rowPointer, 1) } C.jpeg_finish_compress(&cinfo) C.jpeg_destroy_compress(&cinfo) outBs := C.GoBytes(unsafe.Pointer(workBuf), C.int(workBufLen)) w.Write(outBs) C.free(unsafe.Pointer(workBuf)) return nil }
func makeDestinationManager(dest io.Writer, cinfo *C.struct_jpeg_compress_struct) (mgr *destinationManager) { mgr = new(destinationManager) mgr.dest = dest mgr.pub = C.malloc_jpeg_destination_mgr() if mgr.pub == nil { panic("Failed to allocate C.struct_jpeg_destination_mgr") } mgr.buffer = C.malloc(writeBufferSize) if mgr.buffer == nil { panic("Failed to allocate buffer") } mgr.pub.init_destination = (*[0]byte)(C.destinationInit) mgr.pub.empty_output_buffer = (*[0]byte)(C.destinationEmpty) mgr.pub.term_destination = (*[0]byte)(C.destinationTerm) mgr.pub.free_in_buffer = writeBufferSize mgr.pub.next_output_byte = (*C.JOCTET)(mgr.buffer) cinfo.dest = mgr.pub destinationManagerMapMutex.Lock() defer destinationManagerMapMutex.Unlock() destinationManagerMap[uintptr(unsafe.Pointer(mgr.pub))] = mgr return }
// encode image.YCbCr func encodeYCbCr(cinfo *C.struct_jpeg_compress_struct, src *image.YCbCr, p *EncoderOptions) (err error) { // Set up compression parameters cinfo.image_width = C.JDIMENSION(src.Bounds().Dx()) cinfo.image_height = C.JDIMENSION(src.Bounds().Dy()) cinfo.input_components = 3 cinfo.in_color_space = C.JCS_YCbCr C.jpeg_set_defaults(cinfo) setupEncoderOptions(cinfo, p) compInfo := (*[3]C.jpeg_component_info)(unsafe.Pointer(cinfo.comp_info)) colorVDiv := 1 switch src.SubsampleRatio { case image.YCbCrSubsampleRatio444: // 1x1,1x1,1x1 compInfo[Y].h_samp_factor, compInfo[Y].v_samp_factor = 1, 1 compInfo[Cb].h_samp_factor, compInfo[Cb].v_samp_factor = 1, 1 compInfo[Cr].h_samp_factor, compInfo[Cr].v_samp_factor = 1, 1 case image.YCbCrSubsampleRatio440: // 1x2,1x1,1x1 compInfo[Y].h_samp_factor, compInfo[Y].v_samp_factor = 1, 2 compInfo[Cb].h_samp_factor, compInfo[Cb].v_samp_factor = 1, 1 compInfo[Cr].h_samp_factor, compInfo[Cr].v_samp_factor = 1, 1 colorVDiv = 2 case image.YCbCrSubsampleRatio422: // 2x1,1x1,1x1 compInfo[Y].h_samp_factor, compInfo[Y].v_samp_factor = 2, 1 compInfo[Cb].h_samp_factor, compInfo[Cb].v_samp_factor = 1, 1 compInfo[Cr].h_samp_factor, compInfo[Cr].v_samp_factor = 1, 1 case image.YCbCrSubsampleRatio420: // 2x2,1x1,1x1 compInfo[Y].h_samp_factor, compInfo[Y].v_samp_factor = 2, 2 compInfo[Cb].h_samp_factor, compInfo[Cb].v_samp_factor = 1, 1 compInfo[Cr].h_samp_factor, compInfo[Cr].v_samp_factor = 1, 1 colorVDiv = 2 } // libjpeg raw data in is in planar format, which avoids unnecessary // planar->packed->planar conversions. cinfo.raw_data_in = C.TRUE // Start compression C.jpeg_start_compress(cinfo, C.TRUE) // Allocate JSAMPIMAGE to hold pointers to one iMCU worth of image data // this is a safe overestimate; we use the return value from // jpeg_read_raw_data to figure out what is the actual iMCU row count. var yRowPtr [AlignSize]C.JSAMPROW var cbRowPtr [AlignSize]C.JSAMPROW var crRowPtr [AlignSize]C.JSAMPROW yCbCrPtr := [3]C.JSAMPARRAY{ C.JSAMPARRAY(unsafe.Pointer(&yRowPtr[0])), C.JSAMPARRAY(unsafe.Pointer(&cbRowPtr[0])), C.JSAMPARRAY(unsafe.Pointer(&crRowPtr[0])), } var rows C.JDIMENSION for rows = 0; rows < cinfo.image_height; { // First fill in the pointers into the plane data buffers for j := 0; j < int(C.DCTSIZE*compInfo[Y].v_samp_factor); j++ { yRowPtr[j] = C.JSAMPROW(unsafe.Pointer(&src.Y[src.YStride*(int(rows)+j)])) } for j := 0; j < int(C.DCTSIZE*compInfo[Cb].v_samp_factor); j++ { cbRowPtr[j] = C.JSAMPROW(unsafe.Pointer(&src.Cb[src.CStride*(int(rows)/colorVDiv+j)])) crRowPtr[j] = C.JSAMPROW(unsafe.Pointer(&src.Cr[src.CStride*(int(rows)/colorVDiv+j)])) } // Get the data rows += C.jpeg_write_raw_data(cinfo, C.JSAMPIMAGE(unsafe.Pointer(&yCbCrPtr[0])), C.JDIMENSION(C.DCTSIZE*compInfo[0].v_samp_factor)) } C.jpeg_finish_compress(cinfo) return }