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) }
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 }
// WriteJPEG writes a YUVImage as a JPEG into dest. func WriteJPEG(img *YUVImage, dest io.Writer, params CompressionParameters) (err error) { defer func() { if r := recover(); r != nil { img = nil var ok bool err, ok = r.(error) if !ok { err = fmt.Errorf("JPEG error: %v", r) } } }() cinfo := (*C.struct_jpeg_compress_struct)(C.malloc(C.size_t(unsafe.Sizeof(C.struct_jpeg_compress_struct{})))) if cinfo == nil { panic("Failed to allocate cinfo") } defer C.free(unsafe.Pointer(cinfo)) cinfo.err = (*C.struct_jpeg_error_mgr)(C.malloc(C.size_t(unsafe.Sizeof(C.struct_jpeg_error_mgr{})))) if cinfo.err == nil { panic("Failed to allocate cinfo.err") } defer C.free(unsafe.Pointer(cinfo.err)) // No subsampling suport for now if img.Format != YUV444 && img.Format != Grayscale { panic("Unsupported colorspace") } // Setup error handling C.jpeg_std_error(cinfo.err) cinfo.err.error_exit = (*[0]byte)(C.error_panic) // Initialize compression object C.c_jpeg_create_compress(cinfo) defer C.jpeg_destroy_compress(cinfo) destManager := makeDestinationManager(dest, cinfo) defer C.free(unsafe.Pointer(destManager)) // Set up compression parameters cinfo.image_width = C.JDIMENSION(img.Width) cinfo.image_height = C.JDIMENSION(img.Height) cinfo.input_components = 3 cinfo.in_color_space = C.JCS_YCbCr if img.Format == Grayscale { cinfo.input_components = 1 cinfo.in_color_space = C.JCS_GRAYSCALE } C.jpeg_set_defaults(cinfo) C.jpeg_set_quality(cinfo, C.int(params.Quality), C.TRUE) if params.Optimize { cinfo.optimize_coding = C.TRUE } else { cinfo.optimize_coding = C.FALSE } C.jpeg_set_colorspace(cinfo, cinfo.in_color_space) if params.FastDCT { cinfo.dct_method = C.JDCT_IFAST } else { cinfo.dct_method = C.JDCT_ISLOW } compInfo := (*[3]C.jpeg_component_info)(unsafe.Pointer(cinfo.comp_info)) for i := 0; i < int(cinfo.input_components); i++ { compInfo[i].h_samp_factor = 1 compInfo[i].v_samp_factor = 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 yuvPtrInt [3][AlignSize]C.JSAMPROW yuvPtr := [3]C.JSAMPARRAY{ C.JSAMPARRAY(unsafe.Pointer(&yuvPtrInt[0][0])), C.JSAMPARRAY(unsafe.Pointer(&yuvPtrInt[1][0])), C.JSAMPARRAY(unsafe.Pointer(&yuvPtrInt[2][0])), } // Encode the image. var row C.JDIMENSION for row = 0; row < cinfo.image_height; { // First fill in the pointers into the plane data buffers for i := 0; i < int(cinfo.num_components); i++ { for j := 0; j < int(C.DCTSIZE*compInfo[i].v_samp_factor); j++ { compRow := (int(row) + j) yuvPtrInt[i][j] = C.JSAMPROW(unsafe.Pointer(&img.Data[i][img.Stride[i]*compRow])) } } // Get the data row += C.jpeg_write_raw_data(cinfo, C.JSAMPIMAGE(unsafe.Pointer(&yuvPtr[0])), C.JDIMENSION(C.DCTSIZE*compInfo[0].v_samp_factor)) } // Clean up C.jpeg_finish_compress(cinfo) return }