# HG changeset patch # User Sebastien Jodogne # Date 1372944315 -7200 # Node ID 238a0c99ced2a1b6161f25989b6f114f227daf01 # Parent 6f47a4262618bb3642a1faf5e876bfb82feb35fe PNG reader diff -r 6f47a4262618 -r 238a0c99ced2 CMakeLists.txt --- a/CMakeLists.txt Thu Jul 04 15:18:33 2013 +0200 +++ b/CMakeLists.txt Thu Jul 04 15:25:15 2013 +0200 @@ -138,6 +138,7 @@ Core/RestApi/RestApi.cpp Core/MultiThreading/BagOfRunnablesBySteps.cpp Core/MultiThreading/SharedMessageQueue.cpp + Core/FileFormats/PngReader.cpp Core/FileFormats/PngWriter.cpp Core/SQLite/Connection.cpp Core/SQLite/FunctionContext.cpp diff -r 6f47a4262618 -r 238a0c99ced2 Core/FileFormats/PngReader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/FileFormats/PngReader.cpp Thu Jul 04 15:25:15 2013 +0200 @@ -0,0 +1,304 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU 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 . + **/ + + +#include "PngReader.h" + +#include "../OrthancException.h" +#include "../Toolbox.h" + +#include + +namespace Orthanc +{ + namespace + { + struct FileRabi + { + FILE* fp_; + + FileRabi(const char* filename) + { + fp_ = fopen(filename, "rb"); + if (!fp_) + { + throw OrthancException(ErrorCode_InexistentFile); + } + } + + ~FileRabi() + { + if (fp_) + fclose(fp_); + } + }; + } + + + struct PngReader::PngRabi + { + png_structp png_; + png_infop info_; + png_infop endInfo_; + + void Destruct() + { + if (png_) + { + png_destroy_read_struct(&png_, &info_, &endInfo_); + + png_ = NULL; + info_ = NULL; + endInfo_ = NULL; + } + } + + PngRabi() + { + png_ = NULL; + info_ = NULL; + endInfo_ = NULL; + + png_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_) + { + throw OrthancException(ErrorCode_NotEnoughMemory); + } + + info_ = png_create_info_struct(png_); + if (!info_) + { + png_destroy_read_struct(&png_, NULL, NULL); + throw OrthancException(ErrorCode_NotEnoughMemory); + } + + endInfo_ = png_create_info_struct(png_); + if (!info_) + { + png_destroy_read_struct(&png_, &info_, NULL); + throw OrthancException(ErrorCode_NotEnoughMemory); + } + } + + ~PngRabi() + { + Destruct(); + } + + static void MemoryCallback(png_structp png_ptr, + png_bytep data, + png_size_t size); + }; + + + void PngReader::CheckHeader(const void* header) + { + int is_png = !png_sig_cmp((png_bytep) header, 0, 8); + if (!is_png) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + } + + PngReader::PngReader() + { + width_ = 0; + height_ = 0; + pitch_ = 0; + format_ = PixelFormat_Grayscale8; + } + + void PngReader::Read(PngRabi& rabi) + { + png_set_sig_bytes(rabi.png_, 8); + + png_read_info(rabi.png_, rabi.info_); + + png_uint_32 width, height; + int bit_depth, color_type, interlace_type; + int compression_type, filter_method; + // get size and bit-depth of the PNG-image + png_get_IHDR(rabi.png_, rabi.info_, + &width, &height, + &bit_depth, &color_type, &interlace_type, + &compression_type, &filter_method); + + width_ = width; + height_ = height; + + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 8) + { + format_ = PixelFormat_Grayscale8; + pitch_ = width_; + } + else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 16) + { + format_ = PixelFormat_Grayscale16; + pitch_ = 2 * width_; + + if (Toolbox::DetectEndianness() == Endianness_Little) + { + png_set_swap(rabi.png_); + } + } + else if (color_type == PNG_COLOR_TYPE_RGB && bit_depth == 8) + { + format_ = PixelFormat_Grayscale8; + pitch_ = 3 * width_; + } + else + { + throw OrthancException(ErrorCode_NotImplemented); + } + + buffer_.resize(height_ * pitch_); + + if (height_ == 0 || width_ == 0) + { + // Empty image, we are done + return; + } + + png_read_update_info(rabi.png_, rabi.info_); + + std::vector rows(height_); + for (size_t i = 0; i < height_; i++) + { + rows[i] = &buffer_[0] + i * pitch_; + } + + png_read_image(rabi.png_, &rows[0]); + } + + void PngReader::ReadFromFile(const char* filename) + { + FileRabi f(filename); + + char header[8]; + if (fread(header, 1, 8, f.fp_) != 8) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + + CheckHeader(header); + + PngRabi rabi; + + if (setjmp(png_jmpbuf(rabi.png_))) + { + rabi.Destruct(); + throw OrthancException(ErrorCode_BadFileFormat); + } + + png_init_io(rabi.png_, f.fp_); + + Read(rabi); + } + + + + namespace + { + struct MemoryBuffer + { + const uint8_t* buffer_; + size_t size_; + size_t pos_; + bool ok_; + }; + } + + + void PngReader::PngRabi::MemoryCallback(png_structp png_ptr, + png_bytep outBytes, + png_size_t byteCountToRead) + { + MemoryBuffer* from = (MemoryBuffer*) png_get_io_ptr(png_ptr); + + if (!from->ok_) + { + return; + } + + if (from->pos_ + byteCountToRead > from->size_) + { + from->ok_ = false; + return; + } + + memcpy(outBytes, from->buffer_ + from->pos_, byteCountToRead); + + from->pos_ += byteCountToRead; + } + + + void PngReader::ReadFromMemory(const void* buffer, + size_t size) + { + if (size < 8) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + + CheckHeader(buffer); + + PngRabi rabi; + + if (setjmp(png_jmpbuf(rabi.png_))) + { + rabi.Destruct(); + throw OrthancException(ErrorCode_BadFileFormat); + } + + MemoryBuffer tmp; + tmp.buffer_ = reinterpret_cast(buffer) + 8; // We skip the header + tmp.size_ = size - 8; + tmp.pos_ = 0; + tmp.ok_ = true; + + png_set_read_fn(rabi.png_, &tmp, PngRabi::MemoryCallback); + + Read(rabi); + + if (!tmp.ok_) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + } + + void PngReader::ReadFromMemory(const std::string& buffer) + { + if (buffer.size() != 0) + ReadFromMemory(&buffer[0], buffer.size()); + else + ReadFromMemory(NULL, 0); + } +} diff -r 6f47a4262618 -r 238a0c99ced2 Core/FileFormats/PngReader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/FileFormats/PngReader.h Thu Jul 04 15:25:15 2013 +0200 @@ -0,0 +1,96 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU 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 . + **/ + + +#pragma once + +#include "../Enumerations.h" + +#include +#include +#include + +namespace Orthanc +{ + class PngReader + { + private: + struct PngRabi; + + PixelFormat format_; + unsigned int width_; + unsigned int height_; + unsigned int pitch_; + std::vector buffer_; + + void CheckHeader(const void* header); + + void Read(PngRabi& rabi); + + public: + PngReader(); + + PixelFormat GetFormat() const + { + return format_; + } + + unsigned int GetWidth() const + { + return width_; + } + + unsigned int GetHeight() const + { + return height_; + } + + unsigned int GetPitch() const + { + return pitch_; + } + + const void* GetBuffer() const + { + if (buffer_.size() > 0) + return &buffer_[0]; + else + return NULL; + } + + void ReadFromFile(const char* filename); + + void ReadFromMemory(const void* buffer, + size_t size); + + void ReadFromMemory(const std::string& buffer); + }; +} diff -r 6f47a4262618 -r 238a0c99ced2 UnitTests/Png.cpp --- a/UnitTests/Png.cpp Thu Jul 04 15:18:33 2013 +0200 +++ b/UnitTests/Png.cpp Thu Jul 04 15:25:15 2013 +0200 @@ -1,6 +1,7 @@ #include "gtest/gtest.h" #include +#include "../Core/FileFormats/PngReader.h" #include "../Core/FileFormats/PngWriter.h" TEST(PngWriter, ColorPattern) @@ -66,3 +67,43 @@ w.WriteToFile("Gray16Pattern.png", width, height, pitch, Orthanc::PixelFormat_Grayscale16, &image[0]); } + +TEST(PngWriter, EndToEnd) +{ + Orthanc::PngWriter w; + int width = 256; + int height = 256; + int pitch = width * 2 + 16; + + std::vector image(height * pitch); + + int v = 0; + for (int y = 0; y < height; y++) + { + uint16_t *p = reinterpret_cast(&image[0] + y * pitch); + for (int x = 0; x < width; x++, p++, v++) + { + *p = v; + } + } + + std::string s; + w.WriteToMemory(s, width, height, pitch, Orthanc::PixelFormat_Grayscale16, &image[0]); + + Orthanc::PngReader r; + r.ReadFromMemory(s); + + ASSERT_EQ(r.GetWidth(), width); + ASSERT_EQ(r.GetHeight(), height); + + v = 0; + for (int y = 0; y < height; y++) + { + uint16_t *p = reinterpret_cast((uint8_t*) r.GetBuffer() + y * r.GetPitch()); + for (int x = 0; x < width; x++, p++, v++) + { + ASSERT_EQ(*p, v); + } + } + +}