Beispiel #1
0
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)
}
Beispiel #2
0
// 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
}
Beispiel #4
0
// 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
}
Beispiel #5
0
// 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
}
Beispiel #6
0
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
}
Beispiel #7
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
}
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
}
Beispiel #9
0
// 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
}