Mercurial > hg > orthanc
changeset 1604:1f5d6a2f9638
JpegReader
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 01 Sep 2015 14:57:13 +0200 |
parents | 905b4db3092b |
children | fd0464ce1962 |
files | CMakeLists.txt Core/ImageFormats/JpegErrorManager.cpp Core/ImageFormats/JpegErrorManager.h Core/ImageFormats/JpegReader.cpp Core/ImageFormats/JpegReader.h Core/ImageFormats/JpegWriter.cpp Plugins/Engine/OrthancPlugins.cpp UnitTestsSources/ImageTests.cpp UnitTestsSources/VersionsTests.cpp |
diffstat | 9 files changed, 469 insertions(+), 85 deletions(-) [+] |
line wrap: on
line diff
--- a/CMakeLists.txt Tue Sep 01 13:08:41 2015 +0200 +++ b/CMakeLists.txt Tue Sep 01 14:57:13 2015 +0200 @@ -124,7 +124,8 @@ Core/ImageFormats/ImageAccessor.cpp Core/ImageFormats/ImageBuffer.cpp Core/ImageFormats/ImageProcessing.cpp - #Core/ImageFormats/JpegReader.cpp # TODO + Core/ImageFormats/JpegErrorManager.cpp + Core/ImageFormats/JpegReader.cpp Core/ImageFormats/JpegWriter.cpp Core/ImageFormats/PngReader.cpp Core/ImageFormats/PngWriter.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/ImageFormats/JpegErrorManager.cpp Tue Sep 01 14:57:13 2015 +0200 @@ -0,0 +1,69 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 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 General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "../PrecompiledHeaders.h" +#include "JpegErrorManager.h" + +namespace Orthanc +{ + namespace Internals + { + void JpegErrorManager::OutputMessage(j_common_ptr cinfo) + { + char message[JMSG_LENGTH_MAX]; + (*cinfo->err->format_message) (cinfo, message); + + JpegErrorManager* that = reinterpret_cast<JpegErrorManager*>(cinfo->err); + that->message = std::string(message); + } + + + void JpegErrorManager::ErrorExit(j_common_ptr cinfo) + { + (*cinfo->err->output_message) (cinfo); + + JpegErrorManager* that = reinterpret_cast<JpegErrorManager*>(cinfo->err); + longjmp(that->setjmp_buffer, 1); + } + + + JpegErrorManager::JpegErrorManager() + { + memset(&pub, 0, sizeof(struct jpeg_error_mgr)); + memset(&setjmp_buffer, 0, sizeof(jmp_buf)); + + jpeg_std_error(&pub); + pub.error_exit = ErrorExit; + pub.output_message = OutputMessage; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/ImageFormats/JpegErrorManager.h Tue Sep 01 14:57:13 2015 +0200 @@ -0,0 +1,74 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 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 General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#pragma once + +#include <string.h> +#include <stdio.h> +#include <jpeglib.h> +#include <setjmp.h> +#include <string> + +namespace Orthanc +{ + namespace Internals + { + class JpegErrorManager + { + private: + struct jpeg_error_mgr pub; /* "public" fields */ + jmp_buf setjmp_buffer; /* for return to caller */ + std::string message; + + static void OutputMessage(j_common_ptr cinfo); + + static void ErrorExit(j_common_ptr cinfo); + + public: + JpegErrorManager(); + + struct jpeg_error_mgr* GetPublic() + { + return &pub; + } + + jmp_buf& GetJumpBuffer() + { + return setjmp_buffer; + } + + const std::string& GetMessage() const + { + return message; + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/ImageFormats/JpegReader.cpp Tue Sep 01 14:57:13 2015 +0200 @@ -0,0 +1,189 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 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 General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "../PrecompiledHeaders.h" +#include "JpegReader.h" + +#include "JpegErrorManager.h" +#include "../OrthancException.h" +#include "../Logging.h" + +namespace Orthanc +{ + JpegReader::JpegReader() + { + } + + + static void Uncompress(struct jpeg_decompress_struct& cinfo, + std::string& content, + ImageAccessor& accessor) + { + jpeg_read_header(&cinfo, TRUE); + jpeg_start_decompress(&cinfo); + + PixelFormat format; + if (cinfo.output_components == 1 && + cinfo.out_color_space == JCS_GRAYSCALE) + { + format = PixelFormat_Grayscale8; + } + else if (cinfo.output_components == 3 && + cinfo.out_color_space == JCS_RGB) + { + format = PixelFormat_RGB24; + } + else + { + throw OrthancException(ErrorCode_NotImplemented); + } + + unsigned int pitch = cinfo.output_width * cinfo.output_components; + + /* Make a one-row-high sample array that will go away when done with image */ + JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, pitch, 1); + + try + { + content.resize(pitch * cinfo.output_height); + } + catch (...) + { + throw OrthancException(ErrorCode_NotEnoughMemory); + } + + accessor.AssignReadOnly(format, cinfo.output_width, cinfo.output_height, pitch, + content.empty() ? NULL : content.c_str()); + + uint8_t* target = reinterpret_cast<uint8_t*>(&content[0]); + while (cinfo.output_scanline < cinfo.output_height) + { + jpeg_read_scanlines(&cinfo, buffer, 1); + memcpy(target, buffer[0], pitch); + target += pitch; + } + + // Everything went fine, "setjmp()" didn't get called + + jpeg_finish_decompress(&cinfo); + } + + + void JpegReader::ReadFromFile(const char* filename) + { + FILE* fp = fopen(filename, "rb"); + if (!fp) + { + throw OrthancException(ErrorCode_InexistentFile); + } + + struct jpeg_decompress_struct cinfo; + memset(&cinfo, 0, sizeof(struct jpeg_decompress_struct)); + + Internals::JpegErrorManager jerr; + cinfo.err = jerr.GetPublic(); + + if (setjmp(jerr.GetJumpBuffer())) + { + jpeg_destroy_decompress(&cinfo); + fclose(fp); + LOG(ERROR) << "Error during JPEG encoding: " << jerr.GetMessage(); + throw OrthancException(ErrorCode_InternalError); + } + + // Below this line, we are under the scope of a "setjmp" + + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo, fp); + + try + { + Uncompress(cinfo, content_, *this); + } + catch (OrthancException&) + { + jpeg_destroy_decompress(&cinfo); + fclose(fp); + throw; + } + + jpeg_destroy_decompress(&cinfo); + fclose(fp); + } + + + void JpegReader::ReadFromMemory(const void* buffer, + size_t size) + { + struct jpeg_decompress_struct cinfo; + memset(&cinfo, 0, sizeof(struct jpeg_decompress_struct)); + + Internals::JpegErrorManager jerr; + cinfo.err = jerr.GetPublic(); + + if (setjmp(jerr.GetJumpBuffer())) + { + jpeg_destroy_decompress(&cinfo); + LOG(ERROR) << "Error during JPEG encoding: " << jerr.GetMessage(); + throw OrthancException(ErrorCode_InternalError); + } + + // Below this line, we are under the scope of a "setjmp" + jpeg_create_decompress(&cinfo); + jpeg_mem_src(&cinfo, const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(buffer)), size); + + try + { + Uncompress(cinfo, content_, *this); + } + catch (OrthancException&) + { + jpeg_destroy_decompress(&cinfo); + throw; + } + + jpeg_destroy_decompress(&cinfo); + } + + + void JpegReader::ReadFromMemory(const std::string& buffer) + { + if (buffer.empty()) + { + ReadFromMemory(NULL, 0); + } + else + { + ReadFromMemory(buffer.c_str(), buffer.size()); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/ImageFormats/JpegReader.h Tue Sep 01 14:57:13 2015 +0200 @@ -0,0 +1,61 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 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 General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "ImageAccessor.h" + +#include <string> + +namespace Orthanc +{ + class JpegReader : public ImageAccessor + { + private: + std::string content_; + + public: + JpegReader(); + + void ReadFromFile(const char* filename); + + void ReadFromFile(const std::string& filename) + { + ReadFromFile(filename.c_str()); + } + + void ReadFromMemory(const void* buffer, + size_t size); + + void ReadFromMemory(const std::string& buffer); + }; +}
--- a/Core/ImageFormats/JpegWriter.cpp Tue Sep 01 13:08:41 2015 +0200 +++ b/Core/ImageFormats/JpegWriter.cpp Tue Sep 01 14:57:13 2015 +0200 @@ -30,77 +30,18 @@ **/ +#include "../PrecompiledHeaders.h" #include "JpegWriter.h" #include "../OrthancException.h" #include "../Logging.h" -#include <jpeglib.h> -#include <setjmp.h> -#include <stdio.h> +#include "JpegErrorManager.h" + #include <vector> -#include <string.h> -#include <stdlib.h> namespace Orthanc { - namespace - { - class ErrorManager - { - private: - struct jpeg_error_mgr pub; /* "public" fields */ - jmp_buf setjmp_buffer; /* for return to caller */ - std::string message; - - static void OutputMessage(j_common_ptr cinfo) - { - char message[JMSG_LENGTH_MAX]; - (*cinfo->err->format_message) (cinfo, message); - - ErrorManager* that = reinterpret_cast<ErrorManager*>(cinfo->err); - that->message = std::string(message); - } - - - static void ErrorExit(j_common_ptr cinfo) - { - (*cinfo->err->output_message) (cinfo); - - ErrorManager* that = reinterpret_cast<ErrorManager*>(cinfo->err); - longjmp(that->setjmp_buffer, 1); - } - - - public: - ErrorManager() - { - memset(&pub, 0, sizeof(struct jpeg_error_mgr)); - memset(&setjmp_buffer, 0, sizeof(jmp_buf)); - - jpeg_std_error(&pub); - pub.error_exit = ErrorExit; - pub.output_message = OutputMessage; - } - - struct jpeg_error_mgr* GetPublic() - { - return &pub; - } - - jmp_buf& GetJumpBuffer() - { - return setjmp_buffer; - } - - const std::string& GetMessage() const - { - return message; - } - }; - } - - static void GetLines(std::vector<uint8_t*>& lines, unsigned int height, unsigned int pitch, @@ -188,7 +129,7 @@ struct jpeg_compress_struct cinfo; memset(&cinfo, 0, sizeof(struct jpeg_compress_struct)); - ErrorManager jerr; + Internals::JpegErrorManager jerr; cinfo.err = jerr.GetPublic(); if (setjmp(jerr.GetJumpBuffer())) @@ -227,7 +168,7 @@ struct jpeg_compress_struct cinfo; memset(&cinfo, 0, sizeof(struct jpeg_compress_struct)); - ErrorManager jerr; + Internals::JpegErrorManager jerr; unsigned char* data = NULL; unsigned long size;
--- a/Plugins/Engine/OrthancPlugins.cpp Tue Sep 01 13:08:41 2015 +0200 +++ b/Plugins/Engine/OrthancPlugins.cpp Tue Sep 01 14:57:13 2015 +0200 @@ -44,6 +44,7 @@ #include "../../Core/Compression/GzipCompressor.h" #include "../../Core/ImageFormats/PngReader.h" #include "../../Core/ImageFormats/PngWriter.h" +#include "../../Core/ImageFormats/JpegReader.h" #include "../../Core/ImageFormats/JpegWriter.h" #include <boost/regex.hpp> @@ -1063,22 +1064,29 @@ { const _OrthancPluginUncompressImage& p = *reinterpret_cast<const _OrthancPluginUncompressImage*>(parameters); + std::auto_ptr<ImageAccessor> image; + switch (p.format) { case OrthancPluginImageFormat_Png: { - std::auto_ptr<PngReader> image(new PngReader); - image->ReadFromMemory(p.data, p.size); - *(p.target) = reinterpret_cast<OrthancPluginImage*>(image.release()); - return; + image.reset(new PngReader); + reinterpret_cast<PngReader&>(*image).ReadFromMemory(p.data, p.size); + break; } case OrthancPluginImageFormat_Jpeg: - // TODO + { + image.reset(new JpegReader); + reinterpret_cast<JpegReader&>(*image).ReadFromMemory(p.data, p.size); + break; + } default: throw OrthancException(ErrorCode_ParameterOutOfRange); } + + *(p.target) = reinterpret_cast<OrthancPluginImage*>(image.release()); } @@ -1098,8 +1106,12 @@ } case OrthancPluginImageFormat_Jpeg: - // TODO - //writer.SetQuality(p.quality); + { + JpegWriter writer; + writer.SetQuality(p.quality); + writer.WriteToMemory(compressed, p.width, p.height, p.pitch, Convert(p.pixelFormat), p.buffer); + break; + } default: throw OrthancException(ErrorCode_ParameterOutOfRange);
--- a/UnitTestsSources/ImageTests.cpp Tue Sep 01 13:08:41 2015 +0200 +++ b/UnitTestsSources/ImageTests.cpp Tue Sep 01 14:57:13 2015 +0200 @@ -37,6 +37,7 @@ #include "../Core/ImageFormats/ImageBuffer.h" #include "../Core/ImageFormats/PngReader.h" #include "../Core/ImageFormats/PngWriter.h" +#include "../Core/ImageFormats/JpegReader.h" #include "../Core/ImageFormats/JpegWriter.h" #include "../Core/Toolbox.h" #include "../Core/Uuid.h" @@ -192,21 +193,50 @@ TEST(JpegWriter, Basic) { - Orthanc::ImageBuffer img(16, 16, Orthanc::PixelFormat_Grayscale8); - Orthanc::ImageAccessor accessor = img.GetAccessor(); - for (unsigned int y = 0, value = 0; y < img.GetHeight(); y++) + std::string s; + { - uint8_t* p = reinterpret_cast<uint8_t*>(accessor.GetRow(y)); - for (unsigned int x = 0; x < img.GetWidth(); x++, p++) + Orthanc::ImageBuffer img(16, 16, Orthanc::PixelFormat_Grayscale8); + Orthanc::ImageAccessor accessor = img.GetAccessor(); + for (unsigned int y = 0, value = 0; y < img.GetHeight(); y++) { - *p = value++; + uint8_t* p = reinterpret_cast<uint8_t*>(accessor.GetRow(y)); + for (unsigned int x = 0; x < img.GetWidth(); x++, p++) + { + *p = value++; + } + } + + Orthanc::JpegWriter w; + w.WriteToFile("UnitTestsResults/hello.jpg", accessor); + + w.WriteToMemory(s, accessor); + Orthanc::Toolbox::WriteFile(s, "UnitTestsResults/hello2.jpg"); + + std::string t; + Orthanc::Toolbox::ReadFile(t, "UnitTestsResults/hello.jpg"); + ASSERT_EQ(s.size(), t.size()); + ASSERT_EQ(0, memcmp(s.c_str(), t.c_str(), s.size())); + } + + { + Orthanc::JpegReader r1, r2; + r1.ReadFromFile("UnitTestsResults/hello.jpg"); + ASSERT_EQ(16, r1.GetWidth()); + ASSERT_EQ(16, r1.GetHeight()); + + r2.ReadFromMemory(s); + ASSERT_EQ(16, r2.GetWidth()); + ASSERT_EQ(16, r2.GetHeight()); + + for (unsigned int y = 0; y < r1.GetHeight(); y++) + { + const uint8_t* p1 = reinterpret_cast<const uint8_t*>(r1.GetConstRow(y)); + const uint8_t* p2 = reinterpret_cast<const uint8_t*>(r2.GetConstRow(y)); + for (unsigned int x = 0; x < r1.GetWidth(); x++) + { + ASSERT_EQ(*p1, *p2); + } } } - - Orthanc::JpegWriter w; - w.WriteToFile("UnitTestsResults/hello.jpg", accessor); - - std::string s; - w.WriteToMemory(s, accessor); - Orthanc::Toolbox::WriteFile(s, "UnitTestsResults/hello2.jpg"); }
--- a/UnitTestsSources/VersionsTests.cpp Tue Sep 01 13:08:41 2015 +0200 +++ b/UnitTestsSources/VersionsTests.cpp Tue Sep 01 14:57:13 2015 +0200 @@ -42,6 +42,7 @@ #include <boost/version.hpp> #include <sqlite3.h> #include <lua.h> +#include <jpeglib.h> #if ORTHANC_SSL_ENABLED == 1 #include <openssl/opensslv.h> @@ -116,6 +117,12 @@ ASSERT_STREQ("1.5.12", PNG_LIBPNG_VER_STRING); } +TEST(Versions, JpegStatic) +{ + ASSERT_EQ(9, JPEG_LIB_VERSION_MAJOR); + ASSERT_EQ(1, JPEG_LIB_VERSION_MINOR); +} + TEST(Versions, CurlSslStatic) { curl_version_info_data * vinfo = curl_version_info(CURLVERSION_NOW);