Exemple #1
0
// Encode encodes src image and writes into w as JPEG format data.
func Encode(w io.Writer, src image.Image, opt *EncoderOptions) (err error) {
	// Recover panic
	defer func() {
		if r := recover(); r != nil {
			var ok bool
			err, ok = r.(error)
			if !ok {
				err = fmt.Errorf("JPEG error: %v", r)
			}
		}
	}()

	var cinfo C.struct_jpeg_compress_struct
	var jerr C.struct_jpeg_error_mgr

	C.initialize_compress(&cinfo, &jerr)
	defer C.jpeg_destroy_compress(&cinfo)
	makeDestinationManager(w, &cinfo)

	switch s := src.(type) {
	case *image.YCbCr:
		err = encodeYCbCr(&cinfo, s, opt)
	case *image.Gray:
		err = encodeGray(&cinfo, s, opt)
	default:
		return errors.New("unsupported image type")
	}

	return
}
Exemple #2
0
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
}
Exemple #3
0
// 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
}