/*************************************************************************** JPEGImage_impl.h - description ------------------- begin : Jul 25, 2015 copyright : (C) 2015 by Tomas Oberhuber email : tomas.oberhuber@fjfi.cvut.cz ***************************************************************************/ /* See Copyright Notice in tnl/Copyright */ #pragma once #include <TNL/Images//JPEGImage.h> #include <setjmp.h> namespace TNL { namespace Images { #ifdef HAVE_JPEG_H inline void my_error_exit( j_common_ptr cinfo ) { /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */ my_error_mgr* myerr = ( my_error_mgr* ) cinfo->err; /* Always display the message. */ /* We could postpone this until after returning, if we chose. */ ( *cinfo->err->output_message )( cinfo ); /* Return control to the setjmp point */ longjmp( myerr->setjmp_buffer, 1 ); } #endif template< typename Index > JPEGImage< Index >:: JPEGImage() : fileOpen( false ) { } template< typename Index > bool JPEGImage< Index >:: readHeader() { #ifdef HAVE_JPEG_H this->decinfo.err = jpeg_std_error(&jerr.pub); this->jerr.pub.error_exit = my_error_exit; /*** * Prepare the long jump back from libjpeg. */ if( setjmp( jerr.setjmp_buffer ) ) { /**** * If we get here, the JPEG code has signaled an error. * We need to clean up the JPEG object, close the input file, and return. */ jpeg_destroy_decompress( &this->decinfo ); return false; } jpeg_create_decompress( &this->decinfo ); jpeg_stdio_src( &this->decinfo, this->file ); if( jpeg_read_header( &this->decinfo, true ) != JPEG_HEADER_OK ) return false; this->height = this->decinfo.image_height; this->width = this->decinfo.image_width; this->components = this->decinfo.num_components; //this->color_space = this->cinfo.jpeg_color_space; //cout << this->height << " x " << this->width << " : " << this->components << " " << this->color_space << std::endl; return true; #else std::cerr << "TNL was not compiled with support of JPEG. You may still use PGM format." << std::endl; return false; #endif } template< typename Index > bool JPEGImage< Index >:: openForRead( const String& fileName ) { this->close(); this->file = fopen( fileName.getString(), "r" ); if( ! this->file ) { std::cerr << "Unable to open the file " << fileName << std::endl; return false; } this->fileOpen = true; if( ! readHeader() ) return false; return true; } template< typename Index > template< typename MeshReal, typename Device, typename Real > bool JPEGImage< Index >:: read( const RegionOfInterest< Index > roi, Functions::MeshFunction< Meshes::Grid< 2, MeshReal, Device, Index >, 2, Real >& function ) { #ifdef HAVE_JPEG_H typedef Meshes::Grid< 2, Real, Device, Index > GridType; const GridType& grid = function.getMesh(); typename GridType::Cell cell( grid ); /*** * Prepare the long jump back from libjpeg. */ if( setjmp( jerr.setjmp_buffer ) ) { /**** * If we get here, the JPEG code has signaled an error. * We need to clean up the JPEG object, close the input file, and return. */ jpeg_destroy_decompress( &this->decinfo ); return false; } jpeg_start_decompress( &this->decinfo ); int row_stride = this->decinfo.output_width * this->decinfo.output_components; JSAMPARRAY row = ( *( this->decinfo.mem->alloc_sarray ) )( ( j_common_ptr ) &this->decinfo, JPOOL_IMAGE, row_stride, 1 ); Index i( 0 ), j; while( this->decinfo.output_scanline < this->decinfo.output_height) { jpeg_read_scanlines( &this->decinfo, row, 1 ); for( j = 0; j < this->width; j ++ ) { if( !roi.isIn( i, j ) ) continue; cell.getCoordinates().x() = j - roi.getLeft(); cell.getCoordinates().y() = roi.getBottom() - 1 - i; //Index cellIndex = grid.getCellIndex( CoordinatesType( j - roi.getLeft(), // roi.getBottom() - 1 - i ) ); cell.refresh(); unsigned char char_color[ 4 ]; unsigned int int_color[ 4 ]; Real value, r, g, b; switch( this->components ) { case 1: char_color[ 0 ] = row[ 0 ][ j ]; value = char_color[ 0 ] / ( Real ) 255.0; function.getData().setElement( cell.getIndex(), value ); break; case 3: char_color[ 0 ] = row[ 0 ][ 3 * j ]; char_color[ 1 ] = row[ 0 ][ 3 * j + 1 ]; char_color[ 2 ] = row[ 0 ][ 3 * j + 2 ]; r = char_color[ 0 ] / ( Real ) 255.0; g = char_color[ 1 ] / ( Real ) 255.0; b = char_color[ 2 ] / ( Real ) 255.0; value = 0.2989 * r + 0.5870 * g + 0.1140 * b; function.getData().setElement( cell.getIndex(), value ); break; default: std::cerr << "Unknown JPEG color type." << std::endl; return false; } } i++; } return true; #else //cerr << "TNL was not compiled with support of JPEG. You may still use PGM format." << std::endl; return false; #endif } template< typename Index > template< typename Real, typename Device > bool JPEGImage< Index >:: writeHeader( const Meshes::Grid< 2, Real, Device, Index >& grid ) { #ifdef HAVE_JPEG_H this->cinfo.err = jpeg_std_error( &this->jerr.pub ); jpeg_create_compress( &this->cinfo ); jpeg_stdio_dest( &this->cinfo, this->file ); this->cinfo.image_width = grid.getDimensions().x(); this->cinfo.image_height = grid.getDimensions().y(); this->cinfo.input_components = 1; this->cinfo.in_color_space = JCS_GRAYSCALE; jpeg_set_defaults( &this->cinfo ); jpeg_start_compress( &this->cinfo, true ); return true; #else //cerr << "TNL was not compiled with support of JPEG. You may still use PGM format." << std::endl; return false; #endif } template< typename Index > template< typename Real, typename Device > bool JPEGImage< Index >:: openForWrite( const String& fileName, Meshes::Grid< 2, Real, Device, Index >& grid ) { this->close(); this->file = fopen( fileName.getString(), "w" ); if( ! this->file ) { std::cerr << "Unable to open the file " << fileName << std::endl; return false; } this->fileOpen = true; if( ! writeHeader( grid ) ) return false; return true; } template< typename Index > template< typename Real, typename Device, typename Vector > bool JPEGImage< Index >:: write( const Meshes::Grid< 2, Real, Device, Index >& grid, Vector& vector ) { typedef Meshes::Grid< 2, Real, Device, Index > GridType; typename GridType::Cell cell( grid ); #ifdef HAVE_JPEG_H Index i( 0 ), j; JSAMPROW row[1]; row[ 0 ] = new JSAMPLE[ grid.getDimensions().x() ]; // JSAMPLE is unsigned char while( this->cinfo.next_scanline < this->cinfo.image_height ) { for( j = 0; j < grid.getDimensions().x(); j ++ ) { cell.getCoordinates().x() = j; cell.getCoordinates().y() = grid.getDimensions().y() - 1 - i; //Index cellIndex = grid.getCellIndex( CoordinatesType( j, // grid.getDimensions().y() - 1 - i ) ); row[ 0 ][ j ] = 255 * vector.getElement( cell.getIndex() ); } jpeg_write_scanlines( &this->cinfo, row, 1 ); i++; } jpeg_finish_compress( &this->cinfo ); jpeg_destroy_compress( &this->cinfo ); delete[] row[ 0 ]; return true; #else return false; #endif } template< typename Index > template< typename MeshReal, typename Device, typename Real > bool JPEGImage< Index >:: write( const Functions::MeshFunction< Meshes::Grid< 2, MeshReal, Device, Index >, 2, Real >& function ) { typedef Meshes::Grid< 2, Real, Device, Index > GridType; const GridType& grid = function.getMesh(); typename GridType::Cell cell( grid ); #ifdef HAVE_JPEG_H Index i( 0 ), j; JSAMPROW row[1]; row[ 0 ] = new JSAMPLE[ grid.getDimensions().x() ]; // JSAMPLE is unsigned char while( this->cinfo.next_scanline < this->cinfo.image_height ) { for( j = 0; j < grid.getDimensions().x(); j ++ ) { cell.getCoordinates().x() = j; cell.getCoordinates().y() = grid.getDimensions().y() - 1 - i; //Index cellIndex = grid.getCellIndex( CoordinatesType( j, // grid.getDimensions().y() - 1 - i ) ); row[ 0 ][ j ] = 255 * function.getData().getElement( cell.getIndex() ); } jpeg_write_scanlines( &this->cinfo, row, 1 ); i++; } jpeg_finish_compress( &this->cinfo ); jpeg_destroy_compress( &this->cinfo ); delete[] row[ 0 ]; return true; #else return false; #endif } template< typename Index > void JPEGImage< Index >:: close() { if( this->fileOpen ) fclose( file ); this->fileOpen = false; } template< typename Index > JPEGImage< Index >:: ~JPEGImage() { close(); } } // namespace Images } // namespace TNL