Mercurial > hg > orthanc-wsi
diff Framework/Jpeg2000Writer.cpp @ 0:4a7a53257c7d
initial commit
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Sat, 22 Oct 2016 21:48:33 +0200 |
parents | |
children | 7a88c614be04 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Jpeg2000Writer.cpp Sat Oct 22 21:48:33 2016 +0200 @@ -0,0 +1,364 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "Jpeg2000Writer.h" + +#include "Orthanc/Core/ChunkedBuffer.h" +#include "Orthanc/Core/OrthancException.h" + +#include <openjpeg.h> +#include <string.h> +#include <vector> + +#if ORTHANC_OPENJPEG_MAJOR_VERSION == 1 +# define OPJ_CLRSPC_GRAY CLRSPC_GRAY +# define OPJ_CLRSPC_SRGB CLRSPC_SRGB +# define OPJ_CODEC_J2K CODEC_J2K +typedef opj_cinfo_t opj_codec_t; +typedef opj_cio_t opj_stream_t; +#elif ORTHANC_OPENJPEG_MAJOR_VERSION == 2 +#else +#error Unsupported version of OpenJpeg +#endif + +namespace OrthancWSI +{ + namespace + { + class OpenJpegImage : public boost::noncopyable + { + private: + std::vector<opj_image_cmptparm_t> components_; + COLOR_SPACE colorspace_; + opj_image_t* image_; + + void SetupComponents(unsigned int width, + unsigned int height, + Orthanc::PixelFormat format) + { + switch (format) + { + case Orthanc::PixelFormat_Grayscale8: + colorspace_ = OPJ_CLRSPC_GRAY; + components_.resize(1); + break; + + case Orthanc::PixelFormat_RGB24: + colorspace_ = OPJ_CLRSPC_SRGB; + components_.resize(3); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + for (size_t i = 0; i < components_.size(); i++) + { + memset(&components_[i], 0, sizeof(opj_image_cmptparm_t)); + components_[i].dx = 1; + components_[i].dy = 1; + components_[i].x0 = 0; + components_[i].y0 = 0; + components_[i].w = width; + components_[i].h = height; + components_[i].prec = 8; + components_[i].bpp = 8; + components_[i].sgnd = 0; + } + } + + void CopyRGB24(unsigned int width, + unsigned int height, + unsigned int pitch, + const void* buffer) + { + int32_t* r = image_->comps[0].data; + int32_t* g = image_->comps[1].data; + int32_t* b = image_->comps[2].data; + + for (unsigned int y = 0; y < height; y++) + { + const uint8_t *p = reinterpret_cast<const uint8_t*>(buffer) + y * pitch; + + for (unsigned int x = 0; x < width; x++) + { + *r = p[0]; + *g = p[1]; + *b = p[2]; + p += 3; + r++; + g++; + b++; + } + } + } + + void CopyGrayscale8(unsigned int width, + unsigned int height, + unsigned int pitch, + const void* buffer) + { + int32_t* q = image_->comps[0].data; + + for (unsigned int y = 0; y < height; y++) + { + const uint8_t *p = reinterpret_cast<const uint8_t*>(buffer) + y * pitch; + + for (unsigned int x = 0; x < width; x++) + { + *q = *p; + p++; + q++; + } + } + } + + public: + OpenJpegImage(unsigned int width, + unsigned int height, + unsigned int pitch, + Orthanc::PixelFormat format, + const void* buffer) : image_(NULL) + { + SetupComponents(width, height, format); + + image_ = opj_image_create(components_.size(), &components_[0], colorspace_); + if (!image_) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + image_->x0 = 0; + image_->y0 = 0; + image_->x1 = width; + image_->y1 = height; + + switch (format) + { + case Orthanc::PixelFormat_Grayscale8: + CopyGrayscale8(width, height, pitch, buffer); + break; + + case Orthanc::PixelFormat_RGB24: + CopyRGB24(width, height, pitch, buffer); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + + + ~OpenJpegImage() + { + if (image_) + { + opj_image_destroy(image_); + image_ = NULL; + } + } + + + opj_image_t* GetObject() + { + return image_; + } + }; + + + class OpenJpegEncoder : public boost::noncopyable + { + private: + opj_codec_t* cinfo_; + + public: + OpenJpegEncoder(opj_cparameters_t& parameters, + OpenJpegImage& image) : cinfo_(NULL) + { + cinfo_ = opj_create_compress(OPJ_CODEC_J2K); + if (!cinfo_) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + opj_setup_encoder(cinfo_, ¶meters, image.GetObject()); + } + + ~OpenJpegEncoder() + { + if (cinfo_ != NULL) + { +#if ORTHANC_OPENJPEG_MAJOR_VERSION == 1 + opj_destroy_compress(cinfo_); +#else + opj_destroy_codec(cinfo_); +#endif + cinfo_ = NULL; + } + } + + opj_codec_t* GetObject() + { + return cinfo_; + } + }; + + + class OpenJpegOutput : public boost::noncopyable + { + private: + opj_stream_t* cio_; + +#if ORTHANC_OPENJPEG_MAJOR_VERSION == 2 + Orthanc::ChunkedBuffer buffer_; + + static void Free(void *userData) + { + } + + static OPJ_SIZE_T Write(void *buffer, + OPJ_SIZE_T size, + void *userData) + { + OpenJpegOutput* that = reinterpret_cast<OpenJpegOutput*>(userData); + that->buffer_.AddChunk(reinterpret_cast<const char*>(buffer), size); + return size; + } +#endif + + public: + OpenJpegOutput(OpenJpegEncoder& encoder) : cio_(NULL) + { +#if ORTHANC_OPENJPEG_MAJOR_VERSION == 1 + cio_ = opj_cio_open(reinterpret_cast<opj_common_ptr>(encoder.GetObject()), NULL, 0); +#else + cio_ = opj_stream_default_create(0 /* output stream */); + if (!cio_) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + opj_stream_set_user_data(cio_, this, Free); + opj_stream_set_write_function(cio_, Write); +#endif + } + + ~OpenJpegOutput() + { + if (cio_) + { +#if ORTHANC_OPENJPEG_MAJOR_VERSION == 1 + opj_cio_close(cio_); +#else + opj_stream_destroy(cio_); +#endif + cio_ = NULL; + } + } + + opj_stream_t* GetObject() + { + return cio_; + } + + void Flatten(std::string& target) + { +#if ORTHANC_OPENJPEG_MAJOR_VERSION == 1 + target.assign(reinterpret_cast<const char*>(cio_->buffer), cio_tell(cio_)); +#else + buffer_.Flatten(target); +#endif + } + }; + } + + + static void SetupParameters(opj_cparameters_t& parameters, + Orthanc::PixelFormat format, + bool isLossless) + { + opj_set_default_encoder_parameters(¶meters); + parameters.cp_disto_alloc = 1; + + if (isLossless) + { + parameters.tcp_numlayers = 1; + parameters.tcp_rates[0] = 0; + } + else + { + parameters.tcp_numlayers = 5; + parameters.tcp_rates[0] = 1920; + parameters.tcp_rates[1] = 480; + parameters.tcp_rates[2] = 120; + parameters.tcp_rates[3] = 30; + parameters.tcp_rates[4] = 10; + parameters.irreversible = 1; + + if (format == Orthanc::PixelFormat_RGB24 || + format == Orthanc::PixelFormat_RGBA32) + { + // This must be set to 1 if the number of color channels is >= 3 + parameters.tcp_mct = 1; + } + } + + parameters.cp_comment = const_cast<char*>(""); + } + + + void Jpeg2000Writer::WriteToMemoryInternal(std::string& compressed, + unsigned int width, + unsigned int height, + unsigned int pitch, + Orthanc::PixelFormat format, + const void* buffer) + { + if (format != Orthanc::PixelFormat_Grayscale8 && + format != Orthanc::PixelFormat_RGB24 && + format != Orthanc::PixelFormat_RGBA32) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + opj_cparameters_t parameters; + SetupParameters(parameters, format, isLossless_); + + OpenJpegImage image(width, height, pitch, format, buffer); + OpenJpegEncoder encoder(parameters, image); + OpenJpegOutput output(encoder); + +#if ORTHANC_OPENJPEG_MAJOR_VERSION == 1 + if (!opj_encode(encoder.GetObject(), output.GetObject(), image.GetObject(), parameters.index)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } +#else + if (!opj_start_compress(encoder.GetObject(), image.GetObject(), output.GetObject()) || + !opj_encode(encoder.GetObject(), output.GetObject()) || + !opj_end_compress(encoder.GetObject(), output.GetObject())) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } +#endif + + output.Flatten(compressed); + } +}