Example #1
0
func NewImageTile(filename string, r image.Rectangle, width, height int) (err error, tile *ImageTile) {
	l_stream := C.opj_stream_create_default_file_stream_v3(C.CString(filename), 1)
	if l_stream == nil {
		return errors.New("failed to create stream"), nil
	}

	l_codec := C.opj_create_decompress(C.OPJ_CODEC_JP2)

	var parameters C.opj_dparameters_t
	C.opj_set_default_decoder_parameters(&parameters)
	level := desired_progression_level(r, width, height)
	log.Println("desired level:", level)
	//(parameters).cp_reduce = C.OPJ_UINT32(level)

	C.set_handlers(l_codec)

	if err == nil && C.opj_setup_decoder(l_codec, &parameters) == C.OPJ_FALSE {
		err = errors.New("failed to setup decoder")
	}

	if err == nil && C.opj_set_decoded_resolution_factor(l_codec, C.OPJ_UINT32(level)) == C.OPJ_FALSE {
		err = errors.New("failed to set decode resolution factor")
	}

	var img *C.opj_image_t
	if err == nil && C.opj_read_header(l_stream, l_codec, &img) == C.OPJ_FALSE {
		err = errors.New("failed to read the header")
	}

	if err == nil {
		log.Println("num comps:", img.numcomps)
		log.Println("x0:", img.x0, "x1:", img.x1, "y0:", img.y0, "y1:", img.y1)
	}

	if err == nil && C.opj_set_decode_area(l_codec, img, C.OPJ_INT32(r.Min.X), C.OPJ_INT32(r.Min.Y), C.OPJ_INT32(r.Max.X), C.OPJ_INT32(r.Max.Y)) == C.OPJ_FALSE {
		err = errors.New("failed to set the decoded area")
	}

	if err == nil && C.opj_decode(l_codec, l_stream, img) == C.OPJ_FALSE {
		err = errors.New("failed to decode image")
	}
	if err == nil && C.opj_end_decompress(l_codec, l_stream) == C.OPJ_FALSE {
		err = errors.New("failed to decode image")
	}

	C.opj_stream_destroy_v3(l_stream)
	if l_codec != nil {
		C.opj_destroy_codec(l_codec)
	}

	if err == nil {
		var comps []C.opj_image_comp_t
		compsSlice := (*reflect.SliceHeader)((unsafe.Pointer(&comps)))
		compsSlice.Cap = int(img.numcomps)
		compsSlice.Len = int(img.numcomps)
		compsSlice.Data = uintptr(unsafe.Pointer(img.comps))

		bounds := image.Rect(0, 0, int(comps[0].w), int(comps[0].h))

		var data []int32
		dataSlice := (*reflect.SliceHeader)((unsafe.Pointer(&data)))
		dataSlice.Cap = bounds.Dx() * bounds.Dy()
		dataSlice.Len = bounds.Dx() * bounds.Dy()
		dataSlice.Data = uintptr(unsafe.Pointer(comps[0].data))

		tile = &ImageTile{data, bounds, bounds.Dx(), img}
		runtime.SetFinalizer(tile, func(it *ImageTile) {
			C.opj_image_destroy(it.img)
		})
	} else {
		C.opj_image_destroy(img)
	}
	return
}
// DecodeImage returns an image.Image that holds the decoded image data,
// resized and cropped if resizing or cropping was requested.  Both cropping
// and resizing happen here due to the nature of openjpeg, so SetScale,
// SetResizeWH, and SetCrop must be called before this function.
func (i *JP2Image) DecodeImage() (image.Image, error) {
	// We need the codec to be ready for all operations below
	if err := i.initializeCodec(); err != nil {
		goLog(3, "Error initializing codec - aborting")
		return nil, err
	}

	// If we want to resize, but not crop, we have to set the decode area to the
	// full image - which means reading in the image header and then cleaning up
	// all previously-initialized data
	if (i.resizeByPixels || i.resizeByPercent) && !i.crop {
		if err := i.ReadHeader(); err != nil {
			goLog(3, "Error getting dimensions - aborting")
			return nil, err
		}
		i.decodeArea = i.Dimensions()
		i.CleanupResources()
	}

	// If resize is by percent, we now have the decode area, and can use that to
	// get pixel dimensions
	if i.resizeByPercent {
		i.decodeWidth = int(float64(i.decodeArea.Dx()) * i.scaleFactor)
		i.decodeHeight = int(float64(i.decodeArea.Dy()) * i.scaleFactor)
		i.resizeByPixels = true
	}

	// Get progression level if we're resizing to specific dimensions (it's zero
	// if there isn't any scaling of the output)
	if i.resizeByPixels {
		level := desiredProgressionLevel(i.decodeArea, i.decodeWidth, i.decodeHeight)
		if err := i.SetDynamicProgressionLevel(level); err != nil {
			goLog(3, "Unable to set dynamic progression level - aborting")
			return nil, err
		}
	}

	if err := i.ReadHeader(); err != nil {
		goLog(3, "Error reading header before decode - aborting")
		return nil, err
	}

	goLog(6, fmt.Sprintf("num comps: %d", i.image.numcomps))
	goLog(6, fmt.Sprintf("x0: %d, x1: %d, y0: %d, y1: %d", i.image.x0, i.image.x1, i.image.y0, i.image.y1))

	// Setting decode area has to happen *after* reading the header / image data
	if i.crop {
		r := i.decodeArea
		if C.opj_set_decode_area(i.codec, i.image, C.OPJ_INT32(r.Min.X), C.OPJ_INT32(r.Min.Y), C.OPJ_INT32(r.Max.X), C.OPJ_INT32(r.Max.Y)) == C.OPJ_FALSE {
			return nil, errors.New("failed to set the decoded area")
		}
	}

	// Decode the JP2 into the image stream
	if C.opj_decode(i.codec, i.stream, i.image) == C.OPJ_FALSE {
		return nil, errors.New("failed to decode image")
	}
	if C.opj_end_decompress(i.codec, i.stream) == C.OPJ_FALSE {
		return nil, errors.New("failed to close decompression")
	}

	var comps []C.opj_image_comp_t
	compsSlice := (*reflect.SliceHeader)((unsafe.Pointer(&comps)))
	compsSlice.Cap = int(i.image.numcomps)
	compsSlice.Len = int(i.image.numcomps)
	compsSlice.Data = uintptr(unsafe.Pointer(i.image.comps))

	bounds := image.Rect(0, 0, int(comps[0].w), int(comps[0].h))
	var img image.Image

	// We assume grayscale if we don't have at least 3 components, because it's
	// probably the safest default
	if len(comps) < 3 {
		img = &image.Gray{Pix: JP2ComponentData(comps[0]), Stride: bounds.Dx(), Rect: bounds}
	} else {
		// If we have 3+ components, we only care about the first three - I have no
		// idea what else we might have other than alpha, and as a tile server, we
		// don't care about alpha.  It's worth noting that this will almost certainly
		// blow up on any JP2 that isn't using RGB.
		realData := make([]uint8, (bounds.Dx()*bounds.Dy())<<2)
		for x, comp := range comps[0:3] {
			compData := JP2ComponentData(comp)
			for y, point := range compData {
				realData[y*4+x] = point
			}
		}

		img = &image.RGBA{Pix: realData, Stride: bounds.Dx() << 2, Rect: bounds}
	}

	if i.resizeByPixels {
		img = resize.Resize(uint(i.decodeWidth), uint(i.decodeHeight), img, resize.Bilinear)
	}

	return img, nil
}