diff --git a/ChangeLog b/ChangeLog index 5a7944f2..a3150177 100755 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +18/04/2012: + - Major rewrite of CVT code to unify TIFF and JPEG2000 region + export. Region compositing now in TileManager->getRegion with + modified getRegion Kakadu function. Changes also to View class + and ColourTransforms code to enable greater modularity for + image processing to regions. ColourTransforms renamed to + Transforms. + Thanks to The National Library of Wales, who will be using + iipsrv & iipmooviewer to deliver their Historic Newspapers + in 2012. + + 21/03/2012: - Fix to TPTImage.c to force RGB conversion for YCbCr compressed JPEG TIFFs, which is now the default in VIPS. Thanks to John diff --git a/TODO b/TODO index 98693eef..d050021a 100644 --- a/TODO +++ b/TODO @@ -7,6 +7,7 @@ TODO: * ICC profile integration via lcms library * Lossless Rotation / transposition support for JPEG tiles * JPEG source image support -* Move all contrast adjustments, colour space changes etc into new Filter class -* Clean up CVT code -* Standardize tile cropping +* Look into using malloc_usable_size to trace real allocated space +* Lanczos, bilinear etc interpolation for CVT +* Copy EXIF, IPTC data for CVT exports +* Optimize FIF code for very large files diff --git a/configure b/configure index 84481ddd..9006900c 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.68 for iipsrv 0.9.9. +# Generated by GNU Autoconf 2.68 for iipsrv 1.0. # # Report bugs to . # @@ -570,8 +570,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='iipsrv' PACKAGE_TARNAME='iipsrv' -PACKAGE_VERSION='0.9.9' -PACKAGE_STRING='iipsrv 0.9.9' +PACKAGE_VERSION='1.0' +PACKAGE_STRING='iipsrv 1.0' PACKAGE_BUGREPORT='ruven@users.sourceforge.net' PACKAGE_URL='' @@ -1323,7 +1323,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures iipsrv 0.9.9 to adapt to many kinds of systems. +\`configure' configures iipsrv 1.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1393,7 +1393,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of iipsrv 0.9.9:";; + short | recursive ) echo "Configuration of iipsrv 1.0:";; esac cat <<\_ACEOF @@ -1504,7 +1504,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -iipsrv configure 0.9.9 +iipsrv configure 1.0 generated by GNU Autoconf 2.68 Copyright (C) 2010 Free Software Foundation, Inc. @@ -2048,7 +2048,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by iipsrv $as_me 0.9.9, which was +It was created by iipsrv $as_me 1.0, which was generated by GNU Autoconf 2.68. Invocation command line was $ $0 $@ @@ -2864,7 +2864,7 @@ fi # Define the identity of the package. PACKAGE='iipsrv' - VERSION='0.9.9' + VERSION='1.0' cat >>confdefs.h <<_ACEOF @@ -17344,7 +17344,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by iipsrv $as_me 0.9.9, which was +This file was extended by iipsrv $as_me 1.0, which was generated by GNU Autoconf 2.68. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -17401,7 +17401,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -iipsrv config.status 0.9.9 +iipsrv config.status 1.0 configured by $0, generated by GNU Autoconf 2.68, with options \\"\$ac_cs_config\\" diff --git a/configure.in b/configure.in index 13e324d3..676dfdce 100755 --- a/configure.in +++ b/configure.in @@ -1,6 +1,6 @@ # Process this file with autoconf to produce a configure script. -AC_INIT(iipsrv,0.9.9,ruven@users.sourceforge.net) +AC_INIT(iipsrv,1.0,ruven@users.sourceforge.net) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE diff --git a/libtool b/libtool index 09abb116..5922fd80 100755 --- a/libtool +++ b/libtool @@ -1,7 +1,7 @@ #! /bin/sh # libtool - Provide generalized library-building support services. -# Generated automatically by config.status (iipsrv) 0.9.9 +# Generated automatically by config.status (iipsrv) 1.0 # Libtool was configured on host AgentSmith: # NOTE: Changes made to this file will be lost: look at ltmain.sh. # @@ -237,10 +237,10 @@ finish_eval="" hardcode_into_libs=yes # Compile-time system search path for libraries. -sys_lib_search_path_spec="/usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5 /usr/lib64 /lib64 /usr/x86_64-pc-linux-gnu/lib " +sys_lib_search_path_spec="/usr/lib/gcc/x86_64-pc-linux-gnu/4.5.3 /usr/lib64 /lib64 /usr/x86_64-pc-linux-gnu/lib " # Run-time system search path for libraries. -sys_lib_dlsearch_path_spec="/lib /usr/lib /usr/local/lib /usr/x86_64-pc-linux-gnu/lib //usr/lib32/opengl/nvidia/lib //usr/lib64/opengl/nvidia/lib /lib64 /usr/lib64 /usr/local/lib64 /lib32 /usr/lib32 /usr/local/lib32 /lib /usr/lib /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5 /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5/32 //usr/lib64/xulrunner-1.9.2 /usr/lib64/qt4 /usr/lib32/qt4 /usr/lib/qt4 /opt/xulrunner /usr/games/lib64 /usr/games/lib32 /usr/games/lib /usr/lib32/libstdc++-v3/ " +sys_lib_dlsearch_path_spec="/lib /usr/lib /usr/local/lib /usr/x86_64-pc-linux-gnu/lib /usr/lib32/opengl/nvidia/lib /usr/lib64/opengl/nvidia/lib /lib64 /usr/lib64 /usr/local/lib64 /lib32 /usr/lib32 /usr/local/lib32 /lib /usr/lib /usr/lib/gcc/x86_64-pc-linux-gnu/4.5.3 /usr/lib/gcc/x86_64-pc-linux-gnu/4.5.3/32 /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5 /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5/32 //usr/lib64/xulrunner-1.9.2 /usr/lib64/qt4 /usr/lib32/qt4 /usr/lib/qt4 /opt/xulrunner /usr/games/lib64 /usr/games/lib32 /usr/games/lib /opt/cuda/lib64 /usr/lib32/libstdc++-v3/ " # Whether dlopen is supported. dlopen_support=unknown @@ -9390,17 +9390,17 @@ file_list_spec="" hardcode_action=immediate # The directories searched by this compiler when creating a shared library. -compiler_lib_search_dirs="/usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5 /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5 /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5/../../../../lib64 /lib/../lib64 /usr/lib/../lib64 /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5/../../../../x86_64-pc-linux-gnu/lib /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5/../../.." +compiler_lib_search_dirs="/usr/lib/gcc/x86_64-pc-linux-gnu/4.5.3 /usr/lib/gcc/x86_64-pc-linux-gnu/4.5.3/../../../../lib64 /lib/../lib64 /usr/lib/../lib64 /usr/lib/gcc/x86_64-pc-linux-gnu/4.5.3/../../../../x86_64-pc-linux-gnu/lib /usr/lib/gcc/x86_64-pc-linux-gnu/4.5.3/../../.." # Dependencies to place before and after the objects being linked to # create a shared library. -predep_objects="/usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5/../../../../lib64/crti.o /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5/crtbeginS.o" -postdep_objects="/usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5/crtendS.o /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5/../../../../lib64/crtn.o" +predep_objects="/usr/lib/gcc/x86_64-pc-linux-gnu/4.5.3/../../../../lib64/crti.o /usr/lib/gcc/x86_64-pc-linux-gnu/4.5.3/crtbeginS.o" +postdep_objects="/usr/lib/gcc/x86_64-pc-linux-gnu/4.5.3/crtendS.o /usr/lib/gcc/x86_64-pc-linux-gnu/4.5.3/../../../../lib64/crtn.o" predeps="" postdeps="-lstdc++ -lm -lgcc_s -lc -lgcc_s" # The library search path used internally by the compiler when linking # a shared library. -compiler_lib_search_path="-L/usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5 -L/usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5 -L/usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5/../../../../x86_64-pc-linux-gnu/lib -L/usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5/../../.." +compiler_lib_search_path="-L/usr/lib/gcc/x86_64-pc-linux-gnu/4.5.3 -L/usr/lib/gcc/x86_64-pc-linux-gnu/4.5.3/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-pc-linux-gnu/4.5.3/../../../../x86_64-pc-linux-gnu/lib -L/usr/lib/gcc/x86_64-pc-linux-gnu/4.5.3/../../.." # ### END LIBTOOL TAG CONFIG: CXX diff --git a/src/CVT.cc b/src/CVT.cc index c8421ce2..352a869a 100644 --- a/src/CVT.cc +++ b/src/CVT.cc @@ -1,7 +1,7 @@ /* IIP CVT Command Handler Class Member Function - Copyright (C) 2006-2011 Ruven Pillay. + Copyright (C) 2006-2012 Ruven Pillay. 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 @@ -19,7 +19,7 @@ */ #include "Task.h" -#include "ColourTransforms.h" +#include "Transforms.h" #include #include @@ -57,13 +57,11 @@ void CVT::run( Session* session, const std::string& a ){ if( argument == "jpeg" ){ - int cielab = 0; - unsigned int n; if( session->loglevel >= 3 ) *(session->logfile) << "CVT :: JPEG output handler reached" << endl; - // Get a fake tile in case we are dealing with a sequence + // Reload info in case we are dealing with a sequence (*session->image)->loadImageInfo( session->view->xangle, session->view->yangle ); // Calculate the number of tiles at the requested resolution @@ -71,10 +69,11 @@ void CVT::run( Session* session, const std::string& a ){ unsigned int im_height = (*session->image)->getImageHeight(); int num_res = (*session->image)->getNumResolutions(); + // Setup our view with some basic info session->view->setImageSize( im_width, im_height ); session->view->setMaxResolutions( num_res ); - + // Get the resolution, width and height for this view int requested_res = session->view->getResolution(); im_width = (*session->image)->image_widths[num_res-requested_res-1]; im_height = (*session->image)->image_heights[num_res-requested_res-1]; @@ -85,33 +84,18 @@ void CVT::run( Session* session, const std::string& a ){ << " using resolution " << requested_res << endl; } - // The tile size of the source tile - unsigned int src_tile_width = (*session->image)->getTileWidth(); - unsigned int src_tile_height = (*session->image)->getTileHeight(); - - // The tile size of the destination tile - unsigned int dst_tile_width = src_tile_width; - unsigned int dst_tile_height = src_tile_height; - - // The basic tile size ie. not the current tile - unsigned int basic_tile_width = src_tile_width; - // unsigned int basic_tile_height = src_tile_height; - - unsigned int rem_x = im_width % src_tile_width; - unsigned int rem_y = im_height % src_tile_height; + // Number of channels unsigned int channels = (*session->image)->getNumChannels(); - - // The number of tiles in each direction - unsigned int ntlx = (im_width / src_tile_width) + (rem_x == 0 ? 0 : 1); - unsigned int ntly = (im_height / src_tile_height) + (rem_y == 0 ? 0 : 1); + // Data length int len; - // If we have a region defined, calculate our viewport + // Set up our final image sizes and if we have a region defined, + // calculate our viewport + unsigned int resampled_width, resampled_height; unsigned int view_left, view_top, view_width, view_height; - unsigned int startx, endx, starty, endy, xoffset, yoffset; if( session->view->viewPortSet() ){ @@ -120,23 +104,13 @@ void CVT::run( Session* session, const std::string& a ){ view_top = session->view->getViewTop(); view_width = session->view->getViewWidth(); view_height = session->view->getViewHeight(); - - // Calculate the start tiles - startx = (unsigned int) ( view_left / src_tile_width ); - starty = (unsigned int) ( view_top / src_tile_height ); - xoffset = view_left % src_tile_width; - yoffset = view_top % src_tile_height; - endx = (unsigned int) ( (view_width + view_left) / src_tile_width ) + 1; - endy = (unsigned int) ( (view_height + view_top) / src_tile_height ) + 1; + resampled_width = view_width; + resampled_height = view_height; if( session->loglevel >= 3 ){ *(session->logfile) << "CVT :: view port is set: image: " << im_width << "x" << im_height << ". View Port: " << view_left << "," << view_top - << "," << view_width << "," << view_height << endl - << "CVT :: Tile Start: " << startx << "," << starty << "," - << xoffset << "," << yoffset << endl - << "CVT :: End Tiles: " << endx << "," << endy << endl; - + << "," << view_width << "," << view_height << endl; } } else{ @@ -145,48 +119,16 @@ void CVT::run( Session* session, const std::string& a ){ view_top = 0; view_width = im_width; view_height = im_height; - startx = starty = xoffset = yoffset = 0; - endx = ntlx; - endy = ntly; + resampled_width = session->view->getRequestWidth(); + resampled_height = session->view->getRequestHeight(); } - // Allocate memory for a strip only (tile height x image width) - unsigned int o_channels = channels; - if( session->view->shaded ) o_channels = 1; - - - // Get the scaling required to get the requested size. - float scale = session->view->getScale(); - - - // Calculate our resampled width and height - unsigned int resampled_width = floor(view_width * scale); - unsigned int resampled_height = floor(view_height * scale); - if( session->loglevel >= 3 ){ *(session->logfile) << "CVT :: Requested scaled region size is " << resampled_width << "x" << resampled_height << ". Nearest pyramid region size is " << view_width << "x" << view_height << endl; } - // Our data buffer - unsigned char* buf; - - // Create our rawtile object and initialize with our size, channels etc. - RawTile complete_image( 0, 0, 0, 0, resampled_width, resampled_height, o_channels, 8 ); - if( (*session->image)->getImageType() == "jpx" || (*session->image)->getImageType() == "jp2" ){ - complete_image = RawTile( 0, 0, 0, 0, view_width, view_height, o_channels, 8 ); - complete_image.dataLength = view_width * view_height * o_channels; - buf = new unsigned char[view_width * view_height * o_channels]; - } - else{ - complete_image.dataLength = resampled_width * resampled_height * o_channels; - buf = new unsigned char[view_width * src_tile_height * o_channels]; - } - - complete_image.data = buf; - complete_image.memoryManaged = 0; // We will handle memory ourselves - #ifndef DEBUG @@ -197,6 +139,7 @@ void CVT::run( Session* session, const std::string& a ){ const string separator = "/"; #endif + // Get our image file name and strip of the directory path and any suffix string filename = (*session->image)->getImagePath(); int pos = filename.rfind(separator)+1; @@ -208,272 +151,153 @@ void CVT::run( Session* session, const std::string& a ){ "Last-Modified: %s\r\n" "Content-Type: image/jpeg\r\n" "Content-Disposition: inline;filename=\"%s.jpg\"\r\n" + "Transfer-Encoding: chunked\r\n" "\r\n", VERSION, MAX_AGE, (*session->image)->getTimestamp().c_str(), basename.c_str() ); session->out->printf( (const char*) str ); #endif - - // Initialise our JPEG compression object - if( (*session->image)->getImageType() == "jpx" || (*session->image)->getImageType() == "jp2" ){ - session->jpeg->InitCompression( complete_image, view_height ); - } - else session->jpeg->InitCompression( complete_image, src_tile_height ); - - // Send the JPEG header to the client - len = session->jpeg->getHeaderSize(); - if( session->out->putStr( (const char*) session->jpeg->getHeader(), len ) != len ){ - if( session->loglevel >= 1 ){ - *(session->logfile) << "CVT :: Error writing jpeg header" << endl; + // Get our requested region from our TileManager + TileManager tilemanager( session->tileCache, *session->image, session->watermark, session->jpeg, session->logfile, session-> +loglevel ); + RawTile complete_image = tilemanager.getRegion( requested_res, + session->view->xangle, session->view->yangle, + session->view->getLayers(), + view_left, view_top, view_width, view_height ); + + if( session->loglevel >= 4 ){ + if( session->view->getContrast() != 1.0 ){ + *(session->logfile) << "CVT :: Applying contrast of: " << session->view->getContrast() << endl; } + if( complete_image.bpc == 16 ) *(session->logfile) << "CVT :: Rescaling 16 bit data to 8" << endl; } - // Keep track of the current height in order to correct for any errors due to resample rounding - int current_height = 0; + // Convert CIELAB to sRGB + if( (*session->image)->getColourSpace() == CIELAB ){ + Timer cielab_timer; + if( session->loglevel >= 3 ){ + *(session->logfile) << "CVT :: Converting from CIELAB->sRGB" << endl; + cielab_timer.start(); + } + filter_LAB2sRGB( complete_image ); + if( session->loglevel >= 3 ){ + *(session->logfile) << "CVT :: CIELAB->sRGB conversion in " << cielab_timer.getTime() << " microseconds" << + endl; + } + } - // Temporary work around! We should really generalize this and put the strip tiling into the TPTImage - // class itself - if( (*session->image)->getImageType() == "jpx" || (*session->image)->getImageType() == "jp2" ){ - (*session->image)->getRegion( session->view->xangle, session->view->yangle, - requested_res, session->view->getLayers(), - view_left, view_top, view_width, view_height, buf ); + // Apply hill shading if requested + if( session->view->shaded ){ + if( session->loglevel >= 3 ){ + *(session->logfile) << "CVT :: Applying hill-shading" << endl; + } + filter_shade( complete_image, session->view->shade[0], session->view->shade[1] ); + // Don't forget to reset our channels variable as this is used later + channels = 1; + } - *(session->logfile) << "CVT :: About to JPEG compress image" << endl; - // Compress the strip - len = session->jpeg->CompressStrip( buf, view_height ); + // Apply any contrast adjustments and/or clipping to 8bit from 16bit + filter_contrast( complete_image, session->view->getContrast() ); + // Resize our image as requested + if( (view_width!=resampled_width) && (view_height!=resampled_height) ){ if( session->loglevel >= 3 ){ - *(session->logfile) << "CVT :: Compressed data strip length is " << len << endl; + *(session->logfile) << "CVT :: Resizing using nearest neighbour interpolation" << endl; } + filter_interpolate_nearestneighbour( complete_image, resampled_width, resampled_height ); + } - // Send this strip out to the client - if( len != session->out->putStr( (const char*) complete_image.data, len ) ){ - if( session->loglevel >= 1 ){ - *(session->logfile) << "CVT :: Error writing jpeg strip data: " << len << endl; - } + // Initialise our JPEG compression object + session->jpeg->InitCompression( complete_image, resampled_height ); + len = session->jpeg->getHeaderSize(); +// snprintf( str, 1024, "%X\r\n", len ); +// *(session->logfile) << "CVT :: JPEG Header Chunk : " << str; +// session->out->printf( str ); + if( session->out->putStr( (const char*) session->jpeg->getHeader(), len ) != len ){ + if( session->loglevel >= 1 ){ + *(session->logfile) << "CVT :: Error writing jpeg header" << endl; } + } +// session->out->printf( "\r\n" ); - if( session->out->flush() == -1 ) { - if( session->loglevel >= 1 ){ - *(session->logfile) << "CVT :: Error flushing jpeg tile" << endl; - } + // Flush our block of data + if( session->out->flush() == -1 ) { + if( session->loglevel >= 1 ){ + *(session->logfile) << "CVT :: Error flushing jpeg data" << endl; } - } - else{ - - // Decode the image strip by strip and dynamically compress with JPEG - for( unsigned int i=starty; iloglevel >= 2 ) tile_timer.start(); - // Get an uncompressed tile from our TileManager - TileManager tilemanager( session->tileCache, *session->image, session->watermark, session->jpeg, session->logfile, session->loglevel ); - RawTile rawtile = tilemanager.getTile( requested_res, (i*ntlx) + j, session->view->xangle, session->view->yangle, - session->view->getLayers(), UNCOMPRESSED ); + // Send out the data per strip of 512 pixels in height + unsigned int strip_height = 512; + int strips = (resampled_height/strip_height) + (resampled_height%strip_height == 0 ? 0 : 1); - if( session->loglevel >= 2 ){ - *(session->logfile) << "CVT :: Tile access time " << tile_timer.getTime() << " microseconds for tile " - << (i*ntlx) + j << " at resolution " << requested_res << endl; - } + for( int n=0; nimage)->getColourSpace() == CIELAB ){ - cielab = 1; - if( session->loglevel >= 3 ){ - *(session->logfile) << "CVT :: Converting from CIELAB->sRGB" << endl; - } - } + // The last strip may have a different height + if( n == strips-1 ) strip_height = resampled_height % strip_height; - // Only print this out once per image - if( (session->loglevel >= 4) && (i==starty) && (j==starty) ){ - *(session->logfile) << "CVT :: Tile data is " << rawtile.channels << " channels, " - << rawtile.bpc << " bits per channel" << endl; - } - - // Set the tile width and height to be that of the source tile - // - Use the rawtile data because if we take a tile from cache - // the image pointer will not necessarily be pointing to the - // the current tile - // src_tile_width = (*session->image)->getTileWidth(); - // src_tile_height = (*session->image)->getTileHeight(); - src_tile_width = rawtile.width; - src_tile_height = rawtile.height; - dst_tile_width = src_tile_width; - dst_tile_height = src_tile_height; - - // Variables for the pixel offset within the current tile - unsigned int xf = 0; - unsigned int yf = 0; - - // If our viewport has been set, we need to modify our start - // and end points on the source image - if( session->view->viewPortSet() ){ - - if( j == startx ){ - // Calculate the width used in the current tile - // If there is only 1 tile, the width is just the view width - if( j < endx - 1 ) dst_tile_width = src_tile_width - xoffset; - else dst_tile_width = view_width; - xf = xoffset; - } - else if( j == endx-1 ){ - dst_tile_width = (view_width+view_left) % basic_tile_width; - } - - if( i == starty ){ - // Calculate the height used in the current row of tiles - // If there is only 1 row the height is just the view height - if( i < endy - 1 ) dst_tile_height = src_tile_height - yoffset; - else dst_tile_height = view_height; - yf = yoffset; - } - else if( i == endy-1 ){ - dst_tile_height = (view_height+view_top) % basic_tile_width; - } - - if( session->loglevel >= 4 ){ - *(session->logfile) << "CVT :: destination tile height: " << dst_tile_height - << ", tile width: " << dst_tile_width << endl; - } - } - - - // Copy our tile data into the appropriate part of the strip memory - // one whole tile width at a time - if( !rawtile.padded ){ - if( session->loglevel >= 4 ) *(session->logfile) << "CVT :: unpadded tile" << endl; - basic_tile_width = rawtile.width; - } - for( unsigned int k=0; kview->shaded ){ - int m; - for( n=0, m=0; nview->shade[0], session->view->shade[1], - session->view->getContrast() ); - } - } - // If we have a 16 bit image, multiply by the contrast adjustment if it exists - // and scale to 8 bits. - else if( rawtile.bpc == 16 ){ - unsigned short* sptr = (unsigned short*) rawtile.data; - for( n=0; nview->getContrast() / 256.0); - if( v > 255.0 ) v = 255.0; - buf[buffer_index + n] = (unsigned char) v; - } - } - else if( (rawtile.bpc == 8) && (session->view->getContrast() != 1.0) ){ - unsigned char* sptr = (unsigned char*) rawtile.data; - for( n=0; nview->getContrast(); - if( v > 255.0 ) v = 255.0; - buf[buffer_index + n] = (unsigned char) v; - } - } - else{ - memcpy( &buf[buffer_index], &ptr[inx], dst_tile_width*channels ); - } - } - - current_width += dst_tile_width; - - } - - // OK, we have a strip, now do a nearest neighbour downsamlping to the desired pixel size - // if our requested size is not the same as our resolution size - unsigned int resampled_tile_height = dst_tile_height; - - if( resampled_width < view_width ){ - - resampled_tile_height = floor(dst_tile_height*scale); - if( session->loglevel >= 5 ){ - *(session->logfile) << "CVT :: resampled strip height " << resampled_tile_height << endl; - *(session->logfile) << "CVT :: Performing resampling with scale " << scale << endl; - } - - for( unsigned int jj=0; jjloglevel >= 3 ){ + *(session->logfile) << "CVT :: About to JPEG compress strip with height " << strip_height << endl; } - current_height += resampled_tile_height; - - // If we are on the last strip, make sure we adjust to take into account rounding errors - // in resampled images. - if( i==endy-1 ) resampled_tile_height += resampled_height - current_height; - // Compress the strip - len = session->jpeg->CompressStrip( buf, resampled_tile_height ); - + len = session->jpeg->CompressStrip( input, strip_height ); if( session->loglevel >= 3 ){ *(session->logfile) << "CVT :: Compressed data strip length is " << len << endl; } + // Send chunk length in hex +// snprintf( str, 1024, "%X\r\n", len ); +// *(session->logfile) << "CVT :: Chunk : " << str; +// session->out->printf( str ); // Send this strip out to the client - if( len != session->out->putStr( (const char*) complete_image.data, len ) ){ + if( len != session->out->putStr( (const char*) input, len ) ){ if( session->loglevel >= 1 ){ *(session->logfile) << "CVT :: Error writing jpeg strip data: " << len << endl; } } + // Send closing chunk CRLF +// session->out->printf( "\r\n" ); + + // Flush our block of data if( session->out->flush() == -1 ) { if( session->loglevel >= 1 ){ - *(session->logfile) << "CVT :: Error flushing jpeg tile" << endl; + *(session->logfile) << "CVT :: Error flushing jpeg data" << endl; } } + } - } // End of if JPEG2000 else block // Finish off the image compression len = session->jpeg->Finish(); + +// snprintf( str, 1024, "%X\r\n", len ); +// *(session->logfile) << "CVT :: Final Data Chunk : " << str; +// session->out->printf( str ); if( session->out->putStr( (const char*) complete_image.data, len ) != len ){ if( session->loglevel >= 1 ){ *(session->logfile) << "CVT :: Error writing jpeg EOI markers" << endl; } } + // Send closing chunk CRLF +// session->out->printf( "\r\n" ); - // Finish off the flush the buffer - session->out->printf( "\r\n" ); + // Send closing blank chunk +// session->out->printf( "0\r\n\r\n" ); if( session->out->flush() == -1 ) { if( session->loglevel >= 1 ){ @@ -484,8 +308,6 @@ void CVT::run( Session* session, const std::string& a ){ // Inform our response object that we have sent something to the client session->response->setImageSent(); - // Don't forget to delete our strip of memory - delete[] buf; } // End of if( argument == "jpeg" ) diff --git a/src/ColourTransforms.cc b/src/ColourTransforms.cc deleted file mode 100644 index 1af03ca8..00000000 --- a/src/ColourTransforms.cc +++ /dev/null @@ -1,164 +0,0 @@ -// Colour Transform Functions - -/* IIP fcgi server module - colour conversion routines - - Copyright (C) 2004 Ruven Pillay. - - 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 2 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - - -#include -#include "ColourTransforms.h" - - -/* D65 temp 6504. - */ -#define D65_X0 95.0470 -#define D65_Y0 100.0 -#define D65_Z0 108.8827 - - -float sRGB[3][3] = { { 3.240479, -1.537150, -0.498535 }, - { -0.969256, 1.875992, 0.041556 }, - { 0.055648, -0.204043, 1.057311 } }; - - - -// Hillshading function -void shade( unsigned char *in, unsigned char *out, int h_angle, int v_angle, float contrast ){ - - // Incident light angle - float a = (h_angle * 2 * 3.14159) / 360.0; - // We assume a hypotenous of 1.0 - float s_y = cos(a); - float s_x = sqrt( 1.0 - s_y*s_y ); - if( h_angle > 180 ){ - s_x = -s_x; - } - - a = (v_angle * 2 * 3.14159) / 360.0; - float s_z = - sin(a); - - float s_norm = sqrt( s_x*s_x + s_y*s_y + s_z*s_z ); - s_x = s_x / s_norm; - s_y = s_y / s_norm; - s_z = s_z / s_norm; - - float o_x = (float) - (in[0]-128.0) / 128.0; - float o_y = (float) - (in[1]-128.0) / 128.0; - float o_z = (float) - (in[2]-128.0) / 128.0; - - float dot_product = (s_x*o_x) + (s_y*o_y) + (s_z*o_z); - if( in[0] == 0 && in[1] == 0 && in[2] == 0 ) dot_product = 0.0; - - dot_product = dot_product * 255.0 * contrast; - if( dot_product < 0 ) dot_product = 0.0; - - *out = (unsigned char) dot_product; - -} - - - -// Convert from CIELAB to sRGB -void iip_LAB2sRGB( unsigned char *in, unsigned char *out ){ - - /* First convert to XYZ - */ - int l; - float L, a, b; - float X, Y, Z; - double cby, tmp; - double R, G, B; - - /* Extract our LAB - packed in TIFF as unsigned char for L - and signed char for a/b. We also need to rescale - correctly to 0-100 for L and -127 -> +127 for a/b. - */ - l = in[0]; - L = (float) ( in[0] / 2.55 ); - l = ( (signed char*)in )[1]; - a = (float) l; - l = ( (signed char*)in )[2]; - b = (float) l; - - - if( L < 8.0 ) { - Y = (L * D65_Y0) / 903.3; - cby = 7.787 * (Y / D65_Y0) + 16.0 / 116.0; - } - else { - cby = (L + 16.0) / 116.0; - Y = D65_Y0 * cby * cby * cby; - } - - tmp = a / 500.0 + cby; - if( tmp < 0.2069 ) X = D65_X0 * (tmp - 0.13793) / 7.787; - else X = D65_X0 * tmp * tmp * tmp; - - tmp = cby - b / 200.0; - if( tmp < 0.2069 ) Z = D65_Z0 * (tmp - 0.13793) / 7.787; - else Z = D65_Z0 * tmp * tmp * tmp; - - X /= 100.0; - Y /= 100.0; - Z /= 100.0; - - - /* Then convert to sRGB - */ - R = (X * sRGB[0][0]) + (Y * sRGB[0][1]) + (Z * sRGB[0][2]); - G = (X * sRGB[1][0]) + (Y * sRGB[1][1]) + (Z * sRGB[1][2]); - B = (X * sRGB[2][0]) + (Y * sRGB[2][1]) + (Z * sRGB[2][2]); - - /* Clip any -ve values - */ - if( R < 0.0 ) R = 0.0; - if( G < 0.0 ) G = 0.0; - if( B < 0.0 ) B = 0.0; - - - /* We now need to convert these to non-linear display values - */ - if( R <= 0.0031308 ) R *= 12.92; - else R = 1.055 * pow( R, 1.0/2.4 ) - 0.055; - - if( G <= 0.0031308 ) G *= 12.92; - else G = 1.055 * pow( G, 1.0/2.4 ) - 0.055; - - if( B <= 0.0031308 ) B *= 12.92; - else B = 1.055 * pow( B, 1.0/2.4 ) - 0.055; - - /* Scale to 8bit - */ - R *= 255.0; - G *= 255.0; - B *= 255.0; - - /* Clip to our 8 bit limit - */ - if( R > 255.0 ) R = 255.0; - if( G > 255.0 ) G = 255.0; - if( B > 255.0 ) B = 255.0; - - - /* Return our sRGB values - */ - out[0] = (unsigned char) R; - out[1] = (unsigned char) G; - out[2] = (unsigned char) B; - -} diff --git a/src/ColourTransforms.h b/src/ColourTransforms.h deleted file mode 100644 index 917317fd..00000000 --- a/src/ColourTransforms.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - Image Transforms - - Copyright (C) 2004 Ruven Pillay. - - 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 2 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - - -#ifndef _COLOUR_TRANSFORMS_H -#define _COLOUR_TRANSFORMS_H - - -/// Utility function to convert between CIELAB and sRGB colour spaces -/** \param in pointer to input CIELAB data - \param out pointer to buffer in which to put sRGB output -*/ -void iip_LAB2sRGB( unsigned char *in, unsigned char *out ); - - -/// Hillshading function to simulate raking light images -/** \param in pointer to input data of the normal vector at each point - \param out pointer to buffer in which to put the hillshaded greyscale output - \param h_angle angle in the horizontal plane from 12 o'clock in degrees - \param v_angle angle in the vertical plane in degrees. 0 is flat, 90 pointing directly down. - \param contrast contrast adjustment -*/ -void shade( unsigned char *in, unsigned char *out, int h_angle, int v_angle, float contrast ); - -#endif diff --git a/src/DeepZoom.cc b/src/DeepZoom.cc index ed293059..a5906262 100644 --- a/src/DeepZoom.cc +++ b/src/DeepZoom.cc @@ -7,7 +7,7 @@ Culture of the Czech Republic. - Copyright (C) 2009-2011 Ruven Pillay. + Copyright (C) 2009-2012 Ruven Pillay. 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 @@ -27,7 +27,7 @@ #include #include "Task.h" -#include "ColourTransforms.h" +#include "Transforms.h" @@ -124,7 +124,6 @@ void DeepZoom::run( Session* session, const std::string& argument ){ VERSION, MAX_AGE, (*session->image)->getTimestamp().c_str(), tw, width, height ); session->out->printf( (const char*) str ); - session->out->printf( "\r\n" ); session->response->setImageSent(); return; @@ -182,7 +181,6 @@ void DeepZoom::run( Session* session, const std::string& argument ){ CompressionType ct; if( (*session->image)->getColourSpace() == CIELAB ) ct = UNCOMPRESSED; else if( (*session->image)->getNumBitsPerPixel() == 16 ) ct = UNCOMPRESSED; - else if( session->view->getContrast() != 1.0 ) ct = UNCOMPRESSED; else ct = JPEG; @@ -199,64 +197,25 @@ void DeepZoom::run( Session* session, const std::string& argument ){ } - - float contrast = session->view->getContrast(); - - unsigned char* buf; - unsigned char* ptr = (unsigned char*) rawtile.data; - - - // Convert CIELAB to sRGB, performing tile cropping if necessary + // Convert CIELAB to sRGB if( (*session->image)->getColourSpace() == CIELAB ){ - if( session->loglevel >= 4 ) *(session->logfile) << "DeepZoom :: Converting from CIELAB->sRGB" << endl; - buf = new unsigned char[ rawtile.width*rawtile.height*rawtile.channels ]; - for( unsigned int j=0; jloglevel >= 4 ){ + *(session->logfile) << "JTL :: Converting from CIELAB->sRGB" << endl; + cielab_timer.start(); } - delete[] ptr; - rawtile.data = buf; - } - // Handle 16bit images or contrast adjustments - else if( (rawtile.bpc==16) || (contrast !=1.0) ){ - - // Normalise 16bit images to 8bit for JPEG - if( rawtile.bpc == 16 ) contrast = contrast / 256.0; - - float v; - if( session->loglevel >= 4 ) *(session->logfile) << "DeepZoom :: Applying contrast scaling of " << contrast << endl; - - unsigned int dataLength = rawtile.width*rawtile.height*rawtile.channels; - buf = new unsigned char[ dataLength ]; - for( unsigned int j=0; j 255.0 ) v = 255.0; - if( v < 0.0 ) v = 0.0; - buf[j*rawtile.width*rawtile.channels + i] = (unsigned char) v; - } + filter_LAB2sRGB( rawtile ); + + if( session->loglevel >= 4 ){ + *(session->logfile) << "JTL :: CIELAB->sRGB conversion in " << cielab_timer.getTime() << " microseconds" << endl; } + } - // Copy this new buffer back - memcpy(rawtile.data, buf, dataLength); - rawtile.dataLength = dataLength; - // And delete our buffer - delete[] buf; - } + // Apply any contrast adjustments and/or clipping to 8bit from 16bit + filter_contrast( rawtile, session->view->getContrast() ); // Compress to JPEG diff --git a/src/IIPImage.cc b/src/IIPImage.cc index 969d0736..b4f17866 100644 --- a/src/IIPImage.cc +++ b/src/IIPImage.cc @@ -3,11 +3,11 @@ /* IIP fcgi server module - Copyright (C) 2000-2009 Ruven Pillay. + Copyright (C) 2000-2012 Ruven Pillay. 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 2 of the License, or + 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, @@ -171,6 +171,7 @@ void IIPImage::updateTimestamp( const string& path ) throw(string) { // Get a modification time for our image struct stat sb; + if( stat( path.c_str(), &sb ) == -1 ){ string message = string( "Unable to open file " ) + path; throw message; diff --git a/src/IIPImage.h b/src/IIPImage.h index 4651004d..52ad0ae9 100644 --- a/src/IIPImage.h +++ b/src/IIPImage.h @@ -151,7 +151,7 @@ class IIPImage { const std::string& getImageType() { return type; }; /// Get the image timestamp - void updateTimestamp( const std::string& ) throw( std::string ); + void updateTimestamp( const std::string& ) throw( std::string ); /// Get a HTTP RFC 1123 formatted timestamp const std::string getTimestamp(); @@ -185,7 +185,6 @@ class IIPImage { unsigned int getImageHeight( int n=0 ) { return image_heights[n]; }; /// Return the base tile height in pixels for a given resolution - unsigned int getTileHeight() { return tile_height; }; /// Return the base tile width in pixels @@ -200,7 +199,8 @@ class IIPImage { return metadata[index]; }; - + /// Return whether this image type directly handles region decoding + virtual bool regionDecoding(){ return false; }; /// Load the appropriate codec module for this image type /** Used only for dynamically loading codec modules. Overloaded by DSOImage class. @@ -247,7 +247,7 @@ class IIPImage { \param h height of region \param b image buffer */ - virtual void getRegion( int ha, int va, unsigned int r, int layers, int x, int y, unsigned int w, unsigned int h, unsigned char* b ){ return; }; + virtual RawTile getRegion( int ha, int va, unsigned int r, int layers, int x, int y, unsigned int w, unsigned int h ){ return RawTile(); }; /// Assignment operator const IIPImage& operator = ( const IIPImage& ); diff --git a/src/IIPResponse.cc b/src/IIPResponse.cc index 6fa15e11..2aa7930e 100644 --- a/src/IIPResponse.cc +++ b/src/IIPResponse.cc @@ -1,11 +1,11 @@ /* IIP Response Handler Class - Copyright (C) 2003-2009 Ruven Pillay. + Copyright (C) 2003-2012 Ruven Pillay. 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 2 of the License, or + 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, @@ -114,6 +114,7 @@ string IIPResponse::formatResponse() { string response; if( error.length() ){ response = server + eof + "Cache-Control: no-cache" + eof + mimeType + eof + + "Status: 400 Bad Request" + eof + "Content-Disposition: inline;filename=\"IIPisAMadGameClosedToOurUnderstanding.netfpx\"" + eof + eof + error; } @@ -129,6 +130,7 @@ string IIPResponse::formatResponse() { string IIPResponse::getAdvert( const string& version ){ string advert = server + eof + "Content-Type: text/html" + eof; + advert += "Status: 400 Bad Request" + eof; advert += "Content-Disposition: inline;filename=\"iipsrv.html\"" + eof + eof; advert += "IIP Server

Internet Imaging Protocol Server

Version " + version + diff --git a/src/JTL.cc b/src/JTL.cc index a514b752..aea29cae 100644 --- a/src/JTL.cc +++ b/src/JTL.cc @@ -1,7 +1,7 @@ /* IIP JTLS Command Handler Class Member Function - Copyright (C) 2006-2011 Ruven Pillay. + Copyright (C) 2006-2012 Ruven Pillay. 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 @@ -19,7 +19,7 @@ */ #include "Task.h" -#include "ColourTransforms.h" +#include "Transforms.h" #include @@ -65,6 +65,7 @@ void JTL::run( Session* session, const std::string& argument ){ if( (*session->image)->getColourSpace() == CIELAB ) ct = UNCOMPRESSED; else if( (*session->image)->getNumBitsPerPixel() == 16 ) ct = UNCOMPRESSED; else if( session->view->getContrast() != 1.0 ) ct = UNCOMPRESSED; + else if( session->view->shaded ) ct = UNCOMPRESSED; else ct = JPEG; RawTile rawtile = tilemanager.getTile( resolution, tile, session->view->xangle, @@ -76,73 +77,38 @@ void JTL::run( Session* session, const std::string& argument ){ *(session->logfile) << "JTL :: Tile size: " << rawtile.width << " x " << rawtile.height << endl << "JTL :: Channels per sample: " << rawtile.channels << endl << "JTL :: Bits per channel: " << rawtile.bpc << endl - << "JTL :: Compressed tile size is " << len << endl; + << "JTL :: Data size is " << len << " bytes" << endl; } - float contrast = session->view->getContrast(); - - // Get the tile width, which depends on whether tiles are padded or not. Don't need to know the height - unsigned int w = rawtile.padded ? (*session->image)->getTileWidth() : rawtile.width; - // unsigned int h = (*session->image)->getTileHeight(); + // Convert CIELAB to sRGB + if( (*session->image)->getColourSpace() == CIELAB ){ + Timer cielab_timer; + if( session->loglevel >= 4 ){ + *(session->logfile) << "JTL :: Converting from CIELAB->sRGB" << endl; + cielab_timer.start(); + } + filter_LAB2sRGB( rawtile ); - // Convert CIELAB to sRGB, performing tile cropping if necessary - if( (*session->image)->getColourSpace() == CIELAB ){ - if( session->loglevel >= 4 ) *(session->logfile) << "JTL :: Converting from CIELAB->sRGB" << endl; - - unsigned char* buf = new unsigned char[ rawtile.width*rawtile.height*rawtile.channels ]; - unsigned char* ptr = (unsigned char*) rawtile.data; - for( unsigned int j=0; jloglevel >= 4 ){ + *(session->logfile) << "JTL :: CIELAB->sRGB conversion in " << cielab_timer.getTime() << " microseconds" << endl; } - - // Delete our old tile data and set it to our new buffer - delete[] ptr; - rawtile.data = buf; } - // Handle 16bit images or contrast adjustments, performing tile cropping if necessary - else if( (rawtile.bpc==16) || (contrast!=1.0) ){ - - // Normalise 16bit images to 8bit for JPEG - if( rawtile.bpc == 16 ) contrast = contrast / 256.0; - float v; - if( session->loglevel >= 4 ) *(session->logfile) << "JTL :: Applying contrast scaling of " << contrast << endl; - - unsigned int dataLength = rawtile.width*rawtile.height*rawtile.channels; - unsigned char* buf = new unsigned char[ dataLength ]; - - for( unsigned int j=0; j 255.0 ) v = 255.0; - if( v < 0.0 ) v = 0.0; - buf[j*rawtile.width*rawtile.channels + i] = (unsigned char) v; - } + // Apply hill shading if requested + if( session->view->shaded ){ + if( session->loglevel >= 3 ){ + *(session->logfile) << "CVT :: Applying hill-shading" << endl; } + filter_shade( rawtile, session->view->shade[0], session->view->shade[1] ); + } - // Copy this new buffer back - memcpy(rawtile.data, buf, dataLength); - rawtile.dataLength = dataLength; - // And delete our buffer - delete[] buf; - } + // Apply any contrast adjustments and/or clipping to 8bit from 16bit + filter_contrast( rawtile, session->view->getContrast() ); // Compress to JPEG diff --git a/src/KakaduImage.cc b/src/KakaduImage.cc index 882f2526..104505e5 100644 --- a/src/KakaduImage.cc +++ b/src/KakaduImage.cc @@ -41,6 +41,7 @@ using namespace std; extern std::ofstream logfile; #endif + void KakaduImage::openImage() throw (string) { @@ -62,9 +63,9 @@ void KakaduImage::openImage() throw (string) try{ src.open( filename.c_str(), true ); } - catch (...){ - throw string( "Kakadu :: Unable to open '"+filename+"'"); // Rethrow the exception - } + catch (...){ + throw string( "Kakadu :: Unable to open '"+filename+"'"); // Rethrow the exception + } if( jpx_input.open( &src, false ) != 1 ) throw string( "Kakadu :: Error opening '"+filename+"'" ); @@ -172,6 +173,8 @@ void KakaduImage::loadImageInfo( int seq, int ang ) throw(string) max_layers = codestream.get_max_tile_layers(); #ifdef DEBUG logfile << "Kakadu :: " << bpp << " bit data" << endl + << "Kakadu :: " << channels << " channels" << endl + << "Kakadu :: colour space " << j2k_colour.get_space() << endl << "Kakadu :: " << max_layers << " quality layers detected" << endl; #endif kt.close(); @@ -260,7 +263,6 @@ RawTile KakaduImage::getTile( int seq, int ang, unsigned int res, int layers, un // Create our Rawtile object and initialize with data - // Set bpp to 8 here as we have limited this in the process() function RawTile rawtile( tile, res, seq, ang, tw, th, channels, bpp ); @@ -271,12 +273,6 @@ RawTile KakaduImage::getTile( int seq, int ang, unsigned int res, int layers, un rawtile.filename = getImagePath(); rawtile.timestamp = timestamp; - // Set the number of layers to half of the number of detected layers if we have not set the - // layers parameter manually. Also make sure we have at least 1 layer - if( layers <= 0 ) layers = ceil( max_layers/2.0 ); - if( layers < 1 ) layers = 1; - - // Process the tile process( res, layers, xoffset, yoffset, tw, th, rawtile.data ); @@ -292,41 +288,29 @@ RawTile KakaduImage::getTile( int seq, int ang, unsigned int res, int layers, un // Get an entire region and not just a tile -void KakaduImage::getRegion( int seq, int ang, unsigned int res, int layers, int x, int y, unsigned int w, unsigned int h, unsigned char* buf ) throw (string) +RawTile KakaduImage::getRegion( int seq, int ang, unsigned int res, int layers, int x, int y, unsigned int w, unsigned int h ) throw (string) { #ifdef DEBUG Timer timer; timer.start(); #endif - // The function assumes 8bit data, so handle 16bit and do conversion - // -- should really handle this in the CVT function itself perhaps, but - // let's leave this here for now ... - void* buffer; - - // Handle both 8 and 16bit data - if( bpp == 16 ) buffer = new unsigned short[w*h*channels]; - else buffer = buf; - - // Set the number of layers to half of the number of detected layers if we have not set the - // layers parameter manually - if( layers <= 0 ) layers = ceil( max_layers/2.0 ); - if( layers < 1 ) layers = 1; + RawTile rawtile( 0, res, seq, ang, w, h, channels, bpp ); + if( bpp == 16 ) rawtile.data = new unsigned short[w*h*channels]; + else rawtile.data = new unsigned char[w*h*channels]; - process( res, layers, x, y, w, h, buffer ); + rawtile.dataLength = w*h*channels*bpp/8; + rawtile.filename = getImagePath(); + rawtile.timestamp = timestamp; - if( bpp == 16 ){ - for( unsigned int j=0; j> 8 ); - } - } - delete[] (unsigned short*) buffer; - } + process( res, layers, x, y, w, h, rawtile.data ); #ifdef DEBUG logfile << "Kadaku :: getRegion() :: " << timer.getTime() << " microseconds" << endl; #endif + + return rawtile; + } @@ -345,8 +329,10 @@ void KakaduImage::process( unsigned int res, int layers, int xoffset, int yoffse vipsres = numResolutions - 1 - (virtual_levels-res); } - // Create a local buffer - void *buffer = NULL; + // Set the number of layers to half of the number of detected layers if we have not set the + // layers parameter manually. Also make sure we have at least 1 layer + if( layers <= 0 ) layers = ceil( max_layers/2.0 ); + if( layers < 1 ) layers = 1; // Set up the bounding box for our tile @@ -386,51 +372,58 @@ void KakaduImage::process( unsigned int res, int layers, int xoffset, int yoffse #endif + // Setup tile and stripe buffers + void *buffer = NULL; void *stripe_buffer = NULL; + try{ codestream.apply_input_restrictions( 0, 0, vipsres, layers, &image_dims, KDU_WANT_OUTPUT_COMPONENTS ); decompressor.start( codestream, false, true, env_ref, NULL ); + int stripe_heights[channels]; + codestream.get_dims(0,comp_dims,true); + #ifdef DEBUG logfile << "Kakadu :: decompressor starting" << endl; logfile << "Kakadu :: requested region on high resolution canvas: position: " << image_dims.pos.x << "x" << image_dims.pos.y << ". size: " << image_dims.size.x << "x" << image_dims.size.y << endl; -#endif - codestream.get_dims(0,comp_dims,true); - -#ifdef DEBUG logfile << "Kakadu :: mapped resolution region size: " << comp_dims.size.x << "x" << comp_dims.size.y << endl; -#endif - - int stripe_heights[channels]; - -#ifdef DEBUG logfile << "Kakadu :: About to pull stripes" << endl; #endif + int index = 0; bool continues = true; + // Get our stripe heights so that we can allocate our stripe buffer + // Assume that first stripe height is largest + decompressor.get_recommended_stripe_heights( comp_dims.size.y, + 1024, stripe_heights, NULL ); + +#ifdef DEBUG + logfile << "Kakadu :: Allocating memory for stripe height " << stripe_heights[0] << endl; +#endif + // Create our buffers if( bpp == 16 ){ - stripe_buffer = new kdu_uint16[tw*th*channels]; + stripe_buffer = new kdu_uint16[tw*stripe_heights[0]*channels]; buffer = new unsigned short[tw*th*channels]; } else{ - stripe_buffer = new kdu_byte[tw*th*channels]; + stripe_buffer = new kdu_byte[tw*stripe_heights[0]*channels]; buffer = new unsigned char[tw*th*channels]; } while( continues ){ - decompressor.get_recommended_stripe_heights( comp_dims.size.y, + decompressor.get_recommended_stripe_heights( comp_dims.size.y, 1024, stripe_heights, NULL ); if( bpp == 16 ){ @@ -454,7 +447,7 @@ void KakaduImage::process( unsigned int res, int layers, int xoffset, int yoffse } else{ b1 = &( ((kdu_byte*)stripe_buffer)[0] ); - b2 = &( ((unsigned char*)buffer)[index] ) ; + b2 = &( ((unsigned char*)buffer)[index] ); } memcpy( b2, b1, tw * stripe_heights[0] * channels * bpp/8 ); diff --git a/src/KakaduImage.h b/src/KakaduImage.h index 8fe57e30..8eeb0b7c 100644 --- a/src/KakaduImage.h +++ b/src/KakaduImage.h @@ -9,7 +9,7 @@ Culture of the Czech Republic. - Copyright (C) 2009-2011 IIPImage. + Copyright (C) 2009-2012 IIPImage. Authors: Ruven Pillay & Petr Pridal This program is free software; you can redistribute it and/or modify @@ -110,20 +110,19 @@ class KakaduImage : public IIPImage { /// Number of levels that don't physically exist unsigned int virtual_levels; - /// Main processing function - /** \param r resolution - \param l number of quality levels to decode - \param x x coordinate - \param y y coordinate - \param w width of region - \param h height of region - \param d buffer to fill + /** @param r resolution + @param l number of quality levels to decode + @param x x coordinate + @param y y coordinate + @param w width of region + @param h height of region + @param d buffer to fill */ void process( unsigned int r, int l, int x, int y, unsigned int w, unsigned int h, void* d ) throw (std::string); /// Convenience function to delete allocated buffers - /** \param b pointer to buffer + /** @param b pointer to buffer */ void delete_buffer( void* b ); @@ -131,23 +130,23 @@ class KakaduImage : public IIPImage { public: /// Constructor - KakaduImage():IIPImage() { + KakaduImage():IIPImage() { tile_width = TILESIZE; tile_height = TILESIZE; numResolutions = 0; virtual_levels = 0; }; /// Constructor - /** \param path image path + /** @param path image path */ - KakaduImage( const std::string& path ): IIPImage( path ) { + KakaduImage( const std::string& path ): IIPImage( path ) { tile_width = TILESIZE; tile_height = TILESIZE; numResolutions = 0; virtual_levels = 0; }; /// Copy Constructor - /** \param image IIPImage object + /** @param image IIPImage object */ - KakaduImage( const IIPImage& image ): IIPImage( image ) { + KakaduImage( const IIPImage& image ): IIPImage( image ) { tile_width = TILESIZE; tile_height = TILESIZE; numResolutions = 0; virtual_levels = 0; }; @@ -160,36 +159,39 @@ class KakaduImage : public IIPImage { /// Overloaded function for loading TIFF image information - /** \param x horizontal sequence angle - \param y vertical sequence angle + /** @param x horizontal sequence angle + @param y vertical sequence angle */ void loadImageInfo( int x, int y ) throw (std::string); /// Overloaded function for closing a TIFF image void closeImage(); + /// Return whether this image type directly handles region decoding + bool regionDecoding(){ return true; }; + /// Overloaded function for getting a particular tile - /** \param x horizontal sequence angle - \param y vertical sequence angle - \param r resolution - \param l number of quality layers to decode - \param t tile number + /** @param x horizontal sequence angle + @param y vertical sequence angle + @param r resolution + @param l number of quality layers to decode + @param t tile number */ - RawTile getTile( int x, int y, unsigned int r, int l, unsigned int t ) throw (std::string); + RawTile getTile( int x, int y, unsigned int r, int l, unsigned int t ) throw (std::string); /// Overloaded function for returning a region for a given angle and resolution /** Return a RawTile object: Overloaded by child class. - \param ha horizontal angle - \param va vertical angle - \param r resolution - \param l number of quality layers to decode - \param x x coordinate - \param y y coordinate - \param w width of region - \param h height of region - \param b buffer to fill + @param ha horizontal angle + @param va vertical angle + @param r resolution + @param l number of quality layers to decode + @param x x coordinate + @param y y coordinate + @param w width of region + @param h height of region + @param b buffer to fill */ - void getRegion( int ha, int va, unsigned int r, int l, int x, int y, unsigned int w, unsigned int h, unsigned char* b ) throw (std::string); + RawTile getRegion( int ha, int va, unsigned int r, int l, int x, int y, unsigned int w, unsigned int h ) throw (std::string); }; diff --git a/src/Makefile.am b/src/Makefile.am index 688e1ac1..f6a99801 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -36,8 +36,8 @@ iipsrv_fcgi_SOURCES = \ IIPResponse.cc \ View.h \ View.cc \ - ColourTransforms.h \ - ColourTransforms.cc \ + Transforms.h \ + Transforms.cc \ Environment.h \ Writer.h \ Task.h \ diff --git a/src/Makefile.in b/src/Makefile.in index 50a202cc..9f32a801 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -52,11 +52,10 @@ CONFIG_CLEAN_VPATH_FILES = PROGRAMS = $(noinst_PROGRAMS) am_iipsrv_fcgi_OBJECTS = IIPImage.$(OBJEXT) TPTImage.$(OBJEXT) \ JPEGCompressor.$(OBJEXT) TileManager.$(OBJEXT) \ - IIPResponse.$(OBJEXT) View.$(OBJEXT) \ - ColourTransforms.$(OBJEXT) Task.$(OBJEXT) OBJ.$(OBJEXT) \ - FIF.$(OBJEXT) JTL.$(OBJEXT) TIL.$(OBJEXT) ICC.$(OBJEXT) \ - CVT.$(OBJEXT) Zoomify.$(OBJEXT) DeepZoom.$(OBJEXT) \ - SPECTRA.$(OBJEXT) Watermark.$(OBJEXT) + IIPResponse.$(OBJEXT) View.$(OBJEXT) Transforms.$(OBJEXT) \ + Task.$(OBJEXT) OBJ.$(OBJEXT) FIF.$(OBJEXT) JTL.$(OBJEXT) \ + TIL.$(OBJEXT) ICC.$(OBJEXT) CVT.$(OBJEXT) Zoomify.$(OBJEXT) \ + DeepZoom.$(OBJEXT) SPECTRA.$(OBJEXT) Watermark.$(OBJEXT) iipsrv_fcgi_OBJECTS = $(am_iipsrv_fcgi_OBJECTS) iipsrv_fcgi_DEPENDENCIES = Main.o $(am__append_1) $(am__append_2) DEFAULT_INCLUDES = -I.@am__isrc@ @@ -238,8 +237,8 @@ iipsrv_fcgi_SOURCES = \ IIPResponse.cc \ View.h \ View.cc \ - ColourTransforms.h \ - ColourTransforms.cc \ + Transforms.h \ + Transforms.cc \ Environment.h \ Writer.h \ Task.h \ @@ -310,7 +309,6 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CVT.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ColourTransforms.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DSOImage.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DeepZoom.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FIF.Po@am__quote@ @@ -327,6 +325,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TPTImage.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Task.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TileManager.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Transforms.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/View.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Watermark.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Zoomify.Po@am__quote@ diff --git a/src/TPTImage.h b/src/TPTImage.h index 4a9cb332..46f0253e 100644 --- a/src/TPTImage.h +++ b/src/TPTImage.h @@ -98,8 +98,7 @@ class TPTImage : public IIPImage { \param l quality layers \param t tile number */ - RawTile getTile( int x, int y, unsigned int r, int l, unsigned int t ) throw (std::string); - + RawTile getTile( int x, int y, unsigned int r, int l, unsigned int t ) throw (std::string); }; diff --git a/src/TileManager.cc b/src/TileManager.cc index 83ca484d..c6b5d311 100644 --- a/src/TileManager.cc +++ b/src/TileManager.cc @@ -4,11 +4,11 @@ /* IIP Server: Tile Cache Handler - Copyright (C) 2005-2010 Ruven Pillay. + Copyright (C) 2005-2012 Ruven Pillay. 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 2 of the License, or + 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, @@ -58,6 +58,13 @@ RawTile TileManager::getNewTile( int resolution, int tile, int xangle, int yangl } + // We need to crop our edge tiles if they are not the full tile size + if( ((ttt.width != image->getTileWidth()) || (ttt.height != image->getTileHeight())) && ttt.padded ){ + if( loglevel >= 5 ) * logfile << "TileManager :: Cropping tile" << endl; + this->crop( &ttt ); + } + + // Add our uncompressed tile directly into our cache if( c == UNCOMPRESSED ){ // Add to our tile cache @@ -69,14 +76,6 @@ RawTile TileManager::getNewTile( int resolution, int tile, int xangle, int yangl } - /* We need to crop our edge tiles if they are not the full tile size - */ - if( ((ttt.width != image->getTileWidth()) || (ttt.height != image->getTileHeight())) && (c == JPEG) && ttt.padded ){ - if( loglevel >= 5 ) * logfile << "TileManager :: Cropping tile" << endl; - this->crop( &ttt ); - } - - switch( c ){ case JPEG: @@ -149,6 +148,7 @@ void TileManager::crop( RawTile *ttt ){ // Reset the data length len = ttt->width * ttt->height * ttt->channels * ttt->bpc/8; ttt->dataLength = len; + ttt->padded = false; } @@ -282,3 +282,186 @@ RawTile TileManager::getTile( int resolution, int tile, int xangle, int yangle, } + + +RawTile TileManager::getRegion( unsigned int res, int seq, int ang, int layers, unsigned int x, unsigned int y, unsigned int width, unsigned int height ){ + + // If our image type can directly handle region compositing, simply return that + if( image->regionDecoding() ){ + if( loglevel >= 3 ){ + *logfile << "TileManager getRegion :: requesting region directly from image" << endl; + } + return image->getRegion( seq, ang, res, layers, x, y, width, height ); + } + + // Otherwise do the compositing ourselves + + // The tile size of the source tile + unsigned int src_tile_width = image->getTileWidth(); + unsigned int src_tile_height = image->getTileHeight(); + + // The tile size of the destination tile + unsigned int dst_tile_width = src_tile_width; + unsigned int dst_tile_height = src_tile_height; + + // The basic tile size ie. not the current tile + unsigned int basic_tile_width = src_tile_width; + // unsigned int basic_tile_height = src_tile_height; + + int num_res = image->getNumResolutions(); + unsigned int im_width = image->image_widths[num_res-res-1]; + unsigned int im_height = image->image_heights[num_res-res-1]; + + unsigned int rem_x = im_width % src_tile_width; + unsigned int rem_y = im_height % src_tile_height; + + // The number of tiles in each direction + unsigned int ntlx = (im_width / src_tile_width) + (rem_x == 0 ? 0 : 1); + unsigned int ntly = (im_height / src_tile_height) + (rem_y == 0 ? 0 : 1); + + // Start and end tiles and pixel offsets + unsigned int startx, endx, starty, endy, xoffset, yoffset; + + + if( ! ( x==0 && y==0 && width==im_width && height==im_height ) ){ + // Calculate the start tiles + startx = (unsigned int) ( x / src_tile_width ); + starty = (unsigned int) ( y / src_tile_height ); + xoffset = x % src_tile_width; + yoffset = y % src_tile_height; + endx = (unsigned int) ( (width + x) / src_tile_width ) + 1; + endy = (unsigned int) ( (height + y) / src_tile_height ) + 1; + + if( loglevel >= 3 ){ + *logfile << "TileManager getRegion :: Tile Start: " << startx << "," << starty << "," + << xoffset << "," << yoffset << endl + << "TileManager getRegion :: End Tiles: " << endx << "," << endy << endl; + } + } + else{ + startx = starty = xoffset = yoffset = 0; + endx = ntlx; + endy = ntly; + } + + + unsigned int channels = image->getNumChannels(); + unsigned int bpp = image->getNumBitsPerPixel(); + + // Create an empty tile with the correct dimensions + RawTile region( 0, res, seq, ang, width, height, channels, bpp ); + region.dataLength = width * height * channels * bpp/8; + + // Allocate memory for the region + if( bpp == 16 ) region.data = new unsigned short[width*height*channels]; + else region.data = new unsigned char[width*height*channels]; + + unsigned int current_height = 0; + + // Decode the image strip by strip + for( unsigned int i=starty; i= 2 ) tile_timer.start(); + + // Get an uncompressed tile + RawTile rawtile = this->getTile( res, (i*ntlx) + j, seq, ang, layers, UNCOMPRESSED ); + + if( loglevel >= 2 ){ + *logfile << "TileManager getRegion :: Tile access time " << tile_timer.getTime() << " microseconds for tile " + << (i*ntlx) + j << " at resolution " << res << endl; + } + + + // Only print this out once per image + if( (loglevel >= 4) && (i==starty) && (j==starty) ){ + *logfile << "TileManager getRegion :: Tile data is " << rawtile.channels << " channels, " + << rawtile.bpc << " bits per channel" << endl; + } + + // Set the tile width and height to be that of the source tile + // - Use the rawtile data because if we take a tile from cache + // the image pointer will not necessarily be pointing to the + // the current tile + // src_tile_width = (*session->image)->getTileWidth(); + // src_tile_height = (*session->image)->getTileHeight(); + src_tile_width = rawtile.width; + src_tile_height = rawtile.height; + dst_tile_width = src_tile_width; + dst_tile_height = src_tile_height; + + // Variables for the pixel offset within the current tile + unsigned int xf = 0; + unsigned int yf = 0; + + // If our viewport has been set, we need to modify our start + // and end points on the source image + if( !( x==0 && y==0 && width==im_width && height==im_height ) ){ + + if( j == startx ){ + // Calculate the width used in the current tile + // If there is only 1 tile, the width is just the view width + if( j < endx - 1 ) dst_tile_width = src_tile_width - xoffset; + else dst_tile_width = width; + xf = xoffset; + } + else if( j == endx-1 ){ + dst_tile_width = (width+x) % basic_tile_width; + } + + if( i == starty ){ + // Calculate the height used in the current row of tiles + // If there is only 1 row the height is just the view height + if( i < endy - 1 ) dst_tile_height = src_tile_height - yoffset; + else dst_tile_height = height; + yf = yoffset; + } + else if( i == endy-1 ){ + dst_tile_height = (height+y) % basic_tile_width; + } + + if( loglevel >= 4 ){ + *logfile << "TileManager getRegion :: destination tile height: " << dst_tile_height + << ", tile width: " << dst_tile_width << endl; + } + } + + + // Copy our tile data into the appropriate part of the strip memory + // one whole tile width at a time + for( unsigned int k=0; k - Copyright (C) 2004 by Patrick Audley + Copyright (C) 2005-2012 Ruven Pillay. 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 2 of the License, or + 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, @@ -113,6 +111,23 @@ class TileManager{ RawTile getTile( int resolution, int tile, int xangle, int yangle, int layers, CompressionType c ); + + /// Generate a complete region + /** + * Build up an arbitrary region by extracting tiles from the cache by using getTile function. + * Data returned as uncompressed data. + * @param res resolution number + * @param xangle horizontal sequence number + * @param yangle vertical sequence number + * @param layers number of quality layers within image to decode + * @param x left offset with respect to full image + * @param y top offset with respect to full image + * @param w width of region requested + * @param h height of region requested + * @return RawTile + */ + RawTile getRegion( unsigned int res, int xangle, int yangle, int layers, unsigned int x, unsigned int y, unsigned int w, unsigned int h ); + }; diff --git a/src/Transforms.cc b/src/Transforms.cc new file mode 100644 index 00000000..ab09926d --- /dev/null +++ b/src/Transforms.cc @@ -0,0 +1,261 @@ +// Image Transform Functions + +/* IIP fcgi server module - image processing routines + + Copyright (C) 2004-2012 Ruven Pillay. + + 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. + + 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include +#include "Transforms.h" + + +/* D65 temp 6504. + */ +#define D65_X0 95.0470 +#define D65_Y0 100.0 +#define D65_Z0 108.8827 + + +static const float _sRGB[3][3] = { { 3.240479, -1.537150, -0.498535 }, + { -0.969256, 1.875992, 0.041556 }, + { 0.055648, -0.204043, 1.057311 } }; + + + +// Hillshading function +void filter_shade( RawTile& in, int h_angle, int v_angle ){ + + unsigned char* buffer = new unsigned char[in.width*in.height]; + + // Incident light angle + float a = (h_angle * 2 * 3.14159) / 360.0; + // We assume a hypotenous of 1.0 + float s_y = cos(a); + float s_x = sqrt( 1.0 - s_y*s_y ); + if( h_angle > 180 ){ + s_x = -s_x; + } + + a = (v_angle * 2 * 3.14159) / 360.0; + float s_z = - sin(a); + + float s_norm = sqrt( s_x*s_x + s_y*s_y + s_z*s_z ); + s_x = s_x / s_norm; + s_y = s_y / s_norm; + s_z = s_z / s_norm; + + unsigned char* ptr = (unsigned char*) in.data; + unsigned int k = 0; + + for( int n=0; n +127 for a/b. + */ + l = in[0]; + L = (float) ( in[0] / 2.55 ); + l = ( (signed char*)in )[1]; + a = (float) l; + l = ( (signed char*)in )[2]; + b = (float) l; + + + if( L < 8.0 ) { + Y = (L * D65_Y0) / 903.3; + cby = 7.787 * (Y / D65_Y0) + 16.0 / 116.0; + } + else { + cby = (L + 16.0) / 116.0; + Y = D65_Y0 * cby * cby * cby; + } + + tmp = a / 500.0 + cby; + if( tmp < 0.2069 ) X = D65_X0 * (tmp - 0.13793) / 7.787; + else X = D65_X0 * tmp * tmp * tmp; + + tmp = cby - b / 200.0; + if( tmp < 0.2069 ) Z = D65_Z0 * (tmp - 0.13793) / 7.787; + else Z = D65_Z0 * tmp * tmp * tmp; + + X /= 100.0; + Y /= 100.0; + Z /= 100.0; + + + /* Then convert to sRGB + */ + R = (X * _sRGB[0][0]) + (Y * _sRGB[0][1]) + (Z * _sRGB[0][2]); + G = (X * _sRGB[1][0]) + (Y * _sRGB[1][1]) + (Z * _sRGB[1][2]); + B = (X * _sRGB[2][0]) + (Y * _sRGB[2][1]) + (Z * _sRGB[2][2]); + + /* Clip any -ve values + */ + if( R < 0.0 ) R = 0.0; + if( G < 0.0 ) G = 0.0; + if( B < 0.0 ) B = 0.0; + + + /* We now need to convert these to non-linear display values + */ + if( R <= 0.0031308 ) R *= 12.92; + else R = 1.055 * pow( R, 1.0/2.4 ) - 0.055; + + if( G <= 0.0031308 ) G *= 12.92; + else G = 1.055 * pow( G, 1.0/2.4 ) - 0.055; + + if( B <= 0.0031308 ) B *= 12.92; + else B = 1.055 * pow( B, 1.0/2.4 ) - 0.055; + + /* Scale to 8bit + */ + R *= 255.0; + G *= 255.0; + B *= 255.0; + + /* Clip to our 8 bit limit + */ + if( R > 255.0 ) R = 255.0; + if( G > 255.0 ) G = 255.0; + if( B > 255.0 ) B = 255.0; + + + /* Return our sRGB values + */ + out[0] = (unsigned char) R; + out[1] = (unsigned char) G; + out[2] = (unsigned char) B; + +} + + +// Convert whole tile from CIELAB to sRGB +void filter_LAB2sRGB( RawTile& in ){ + + unsigned long np = in.dataLength; + + for( unsigned long n=0; n + void View::calculateResolution( unsigned int dimension, @@ -88,7 +90,7 @@ unsigned int View::getResolution(){ } -float View::getScale(){ +double View::getScale(){ unsigned int rw; unsigned int rh; @@ -102,9 +104,9 @@ float View::getScale(){ } else rh = requested_height; - float scale = static_cast(rw) / static_cast(width); + double scale = static_cast(rw) / static_cast(width); - if( static_cast(rh) / static_cast(height) < scale ) scale = static_cast(rh) / static_cast(height); + if( static_cast(rh) / static_cast(height) < scale ) scale = static_cast(rh) / static_cast(height); // Sanity check if( scale <= 0 || scale > 1.0 ) scale = 1.0; @@ -113,30 +115,30 @@ float View::getScale(){ } -void View::setViewLeft( float x ) { +void View::setViewLeft( double x ) { if( x > 1.0 ) view_left = 1.0; else if( x < 0.0 ) view_left = 0.0; else view_left = x; } -void View::setViewTop( float y ) { +void View::setViewTop( double y ) { if( y > 1.0 ) view_top = 1.0; else if( y < 0.0 ) view_top = 0.0; else view_top = y; } -void View::setViewWidth( float w ) { +void View::setViewWidth( double w ) { if( w > 1.0 ) view_width = 1.0; - else if( w <= 0.0 ) view_width = 0.001; + else if( w <= 0.0 ) view_width = 0.0001; else view_width = w; } -void View::setViewHeight( float h ) { +void View::setViewHeight( double h ) { if( h > 1.0 ) view_height = 1.0; - else if( h <= 0.0 ) view_height = 0.001; + else if( h <= 0.0 ) view_height = 0.0001; else view_height = h; } @@ -182,6 +184,36 @@ unsigned int View::getViewHeight(){ } +unsigned int View::getRequestWidth(){ + if( requested_width == 0 && requested_height > 0 ){ + requested_width = (unsigned int) round( (double)(width*requested_height) / (double)height ); + } + if( requested_width > width ) requested_width = width; + if( requested_width > max_size ) requested_width = max_size; + // If no width has been set, use our full size + if( requested_width <= 0 ) requested_width = width; + + // If we have a region request, scale our request accordingly + if( view_width != 1.0 ) requested_width = (unsigned int) round( requested_width * view_width ); + return requested_width; +}; + + +unsigned int View::getRequestHeight(){ + if( requested_height == 0 && requested_width > 0 ){ + requested_height = (unsigned int) round( (double)(height*requested_width) / (double)width ); + } + if( requested_height > height ) requested_height = height; + if( requested_height > max_size ) requested_height = max_size; + // If no height has been set, use our full size + if( requested_height <= 0 ) requested_height = height; + + // If we have a region request, scale our request accordingly + if( view_height != 1.0 ) requested_height = (unsigned int) round( requested_height * view_height ); + return requested_height; +}; + + /// Return the number of layers to decode unsigned int View::getLayers(){ // If max_layers is set, limit to this value, otherwise return layers diff --git a/src/View.h b/src/View.h index 16c7d679..d8100068 100644 --- a/src/View.h +++ b/src/View.h @@ -1,7 +1,7 @@ /* Image View Parameters - Copyright (C) 2003-2011 Ruven Pillay. + Copyright (C) 2003-2012 Ruven Pillay. 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 @@ -34,7 +34,7 @@ class View{ private: // Resolution independent x,y,w,h region viewport - float view_left, view_top, view_width, view_height; + double view_left, view_top, view_width, view_height; int resolution; /// Requested resolution unsigned int max_resolutions; /// Total available resolutions @@ -48,8 +48,8 @@ class View{ /// Internal function to calculate the resolution associated with a width /// or height request. This also takes into account maximum size limits. - /** \param m maximum size - \param r requested size + /** @param m maximum size + @param r requested size */ void calculateResolution( unsigned int m, unsigned int r ); @@ -78,58 +78,41 @@ class View{ /// Set the contrast adjustment - /** \param c contrast (where 1.0 is no adjustment) */ + /** @param c contrast (where 1.0 is no adjustment) */ void setContrast( float c ){ contrast = c; }; /// Set the maximum view port dimension - /** \param m maximum viewport dimension */ + /** @param m maximum viewport dimension */ void setMaxSize( unsigned int m ){ max_size = m; }; /// Set the maximum view port dimension - /** \param r number of availale resolutions */ + /** @param r number of availale resolutions */ void setMaxResolutions( unsigned int r ){ max_resolutions = r; }; /// Get the size of the requested width - unsigned int getRequestWidth(){ - if( requested_width == 0 && requested_height > 0 ){ - requested_width = static_cast( width * requested_height / height ); - } - if( requested_width > width ) requested_width = width; - if( requested_width > max_size ) requested_width = max_size; - // If no width has been set, use our full size - if( requested_width <= 0 ) requested_width = width; - return requested_width; - }; + unsigned int getRequestWidth(); /// Set the size of the requested width - /** \param w requested image width */ + /** @param w requested image width */ void setRequestWidth( unsigned int w ){ - if( w < max_size ) requested_width = w; - else requested_width = max_size; + if( (max_size > 0) && (w > max_size) ) requested_width = max_size; + else requested_width = w; }; /// Get the size of the requested height - unsigned int getRequestHeight(){ - if( requested_height == 0 && requested_width > 0 ){ - requested_height = static_cast( height * requested_width / width ); - } - if( requested_height > height ) requested_height = height; - if( requested_height > max_size ) requested_height = max_size; - // If no height has been set, use our full size - if( requested_height <= 0 ) requested_height = height; - return requested_height; - }; + unsigned int getRequestHeight(); + /// Set the size of the requested height - /** \param h requested image height */ + /** @param h requested image height */ void setRequestHeight( unsigned int h ){ - if( h < max_size ) requested_height = h; - else requested_height = max_size; + if( (max_size > 0) && (h > max_size) ) requested_height = max_size; + else requested_height = h; }; @@ -138,42 +121,42 @@ class View{ /// Return the scaling required in case our requested width or height is in between available resolutions - float getScale(); + double getScale(); /// Set the left co-ordinate of the viewport - /** \param x left resolution independent co-ordinate */ - void setViewLeft( float x ); + /** @param x left resolution independent co-ordinate */ + void setViewLeft( double x ); /// Set the top co-ordinate of the viewport - /** \param y top resolution independent co-ordinate */ - void setViewTop( float y ); + /** @param y top resolution independent co-ordinate */ + void setViewTop( double y ); /// Set the width co-ordinate of the viewport - /** \param w width resolution independent co-ordinate */ - void setViewWidth( float w ); + /** @param w width resolution independent co-ordinate */ + void setViewWidth( double w ); /// Set the height co-ordinate of the viewport - /** \param h height resolution independent co-ordinate */ - void setViewHeight( float h ); + /** @param h height resolution independent co-ordinate */ + void setViewHeight( double h ); /// Set the source image pixel size - /** \param w pixel width - \param h pixel height - */ + /** @param w pixel width + @param h pixel height + */ void setImageSize( unsigned int w, unsigned int h ){ width = w; height = h; }; /// Limit the maximum number of quality layers we are allowed to decode - /** \param l Max number of layers to decode */ + /** @param l Max number of layers to decode */ void setMaxLayers( int l ){ max_layers = l; }; /// Set the number of quality layers to decode, limiting to our max value - /** \param l Number of layers to decode */ + /** @param l Number of layers to decode */ void setLayers( int l ){ layers = l; }; /// Return the number of layers to decode diff --git a/src/Zoomify.cc b/src/Zoomify.cc index 8b599b25..ee69ecf3 100644 --- a/src/Zoomify.cc +++ b/src/Zoomify.cc @@ -5,7 +5,7 @@ * (www.oldmapsonline.org) from Ministry of Culture of the Czech Republic * - Copyright (C) 2008-2011 Ruven Pillay. + Copyright (C) 2008-2012 Ruven Pillay. 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 @@ -25,7 +25,7 @@ #include #include "Task.h" -#include "ColourTransforms.h" +#include "Transforms.h" #include "Tokenizer.h" @@ -118,7 +118,6 @@ void Zoomify::run( Session* session, const std::string& argument ){ VERSION, MAX_AGE,(*session->image)->getTimestamp().c_str(), width, height, ntiles, tw ); session->out->printf( (const char*) str ); - session->out->printf( "\r\n" ); session->response->setImageSent(); return; @@ -163,7 +162,6 @@ void Zoomify::run( Session* session, const std::string& argument ){ CompressionType ct; if( (*session->image)->getColourSpace() == CIELAB ) ct = UNCOMPRESSED; else if( (*session->image)->getNumBitsPerPixel() == 16 ) ct = UNCOMPRESSED; - else if( session->view->getContrast() != 1.0 ) ct = UNCOMPRESSED; else ct = JPEG; @@ -180,64 +178,25 @@ void Zoomify::run( Session* session, const std::string& argument ){ } - - float contrast = session->view->getContrast(); - - unsigned char* buf; - unsigned char* ptr = (unsigned char*) rawtile.data; - - - // Convert CIELAB to sRGB, performing tile cropping if necessary + // Convert CIELAB to sRGB if( (*session->image)->getColourSpace() == CIELAB ){ - if( session->loglevel >= 4 ) *(session->logfile) << "Zoomify :: Converting from CIELAB->sRGB" << endl; - buf = new unsigned char[ rawtile.width*rawtile.height*rawtile.channels ]; - for( unsigned int j=0; jloglevel >= 4 ){ + *(session->logfile) << "JTL :: Converting from CIELAB->sRGB" << endl; + cielab_timer.start(); } - delete[] ptr; - rawtile.data = buf; - } - // Handle 16bit images or contrast adjustments - else if( (rawtile.bpc==16) || (contrast !=1.0) ){ - - // Normalise 16bit images to 8bit for JPEG - if( rawtile.bpc == 16 ) contrast = contrast / 256.0; - - float v; - if( session->loglevel >= 4 ) *(session->logfile) << "Zoomify :: Applying contrast scaling of " << contrast << endl; - - unsigned int dataLength = rawtile.width*rawtile.height*rawtile.channels; - buf = new unsigned char[ dataLength ]; - for( unsigned int j=0; j 255.0 ) v = 255.0; - if( v < 0.0 ) v = 0.0; - buf[j*rawtile.width*rawtile.channels + i] = (unsigned char) v; - } + filter_LAB2sRGB( rawtile ); + + if( session->loglevel >= 4 ){ + *(session->logfile) << "JTL :: CIELAB->sRGB conversion in " << cielab_timer.getTime() << " microseconds" << endl; } + } - // Copy this new buffer back - memcpy(rawtile.data, buf, dataLength); - rawtile.dataLength = dataLength; - // And delete our buffer - delete[] buf; - } + // Apply any contrast adjustments and/or clipping to 8bit from 16bit + filter_contrast( rawtile, session->view->getContrast() ); // Compress to JPEG diff --git a/windows/iipsrv.sln b/windows/iipsrv.sln index 92d2c26c..81b6424a 100644 --- a/windows/iipsrv.sln +++ b/windows/iipsrv.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IIPSrv", "IIPSrv.vcxproj", "{4F0657B6-F71D-A315-0249-1968286E7F68}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "iipsrv", "iipsrv.vcxproj", "{4F0657B6-F71D-A315-0249-1968286E7F68}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/windows/iipsrv.vcxproj b/windows/iipsrv.vcxproj index 42ec6a84..582494be 100644 --- a/windows/iipsrv.vcxproj +++ b/windows/iipsrv.vcxproj @@ -11,17 +11,22 @@ + {18F529A3-258B-40C9-986C-57826894DD0B} Win32Proj + IIPIMAGE + iipsrv Application true + Unicode Application false - Static + true + Unicode @@ -32,59 +37,53 @@ - - - - false + true + .fcgi false - true - - - true + .fcgi - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - Level3 - ProgramDatabase + + + Level1 Disabled - $(ProjectDir)dependencies\includes;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;HAVE_TIME_H;VERSION="0.9.9";_BASETSD_H;%(PreprocessorDefinitions) + C:\Documents and Settings\Administrateur\Bureau\IIPImage\libmemcached-0.48;C:\Documents and Settings\Administrateur\Bureau\IIPImage\zlib-1.2.5;C:\Documents and Settings\Administrateur\Bureau\IIPImage\tiff-4.0.0beta6\libtiff;C:\Documents and Settings\Administrateur\Bureau\IIPImage\jpeg-8c;C:\Documents and Settings\Administrateur\Bureau\IIPImage\fcgi-2.4.0\include;%(AdditionalIncludeDirectories) - MachineX86 - true Console - $(ProjectDir)dependencies\libs;%(AdditionalLibraryDirectories) - jpeg.lib;libfcgi.lib;libtiff.lib;zlibwapi.lib;%(AdditionalDependencies) - /LTCG %(AdditionalOptions) - false - MSVCRT;%(IgnoreSpecificDefaultLibraries) + true + C:\Documents and Settings\Administrateur\Bureau\IIPImage\libmemcached-0.48\libmemcached\.libs;C:\Documents and Settings\Administrateur\Bureau\IIPImage\zlib-1.2.5\lib;C:\Documents and Settings\Administrateur\Bureau\IIPImage\jpeg-8c;C:\Documents and Settings\Administrateur\Bureau\IIPImage\tiff-4.0.0beta6\libtiff;C:\Documents and Settings\Administrateur\Bureau\IIPImage\fcgi-2.4.0\libfcgi\Release;%(AdditionalLibraryDirectories) + libfcgi.lib;libtiff.lib;libjpeg.lib;zdll.lib;%(AdditionalDependencies) - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreadedDLL - Level3 - ProgramDatabase - $(ProjectDir)dependencies\includes;%(AdditionalIncludeDirectories) + Level2 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;VERSION="0.9.9";HAVE_TIME_H;_BASETSD_H;%(PreprocessorDefinitions) + C:\Documents and Settings\Administrateur\Bureau\IIPImage\tiff-4.0.0beta6\libtiff;C:\Documents and Settings\Administrateur\Bureau\IIPImage\zlib-1.2.5\include;C:\Documents and Settings\Administrateur\Bureau\IIPImage\jpeg-8c;C:\Documents and Settings\Administrateur\Bureau\IIPImage\fcgi-2.4.0\include;%(AdditionalIncludeDirectories) + Speed + true + true + false - MachineX86 - false Console - false - false - $(ProjectDir)dependencies\libs;%(AdditionalLibraryDirectories) - libfcgi.lib;libtiff.lib;%(AdditionalDependencies) - LIBCMT;%(IgnoreSpecificDefaultLibraries) - /LTCG %(AdditionalOptions) - false + true + true + true + C:\Documents and Settings\Administrateur\Bureau\IIPImage\tiff-4.0.0beta6\libtiff;C:\Documents and Settings\Administrateur\Bureau\IIPImage\jpeg-8c;C:\Documents and Settings\Administrateur\Bureau\IIPImage\zlib-1.2.5\;C:\Documents and Settings\Administrateur\Bureau\IIPImage\fcgi-2.4.0\libfcgi\Release;%(AdditionalLibraryDirectories) + libjpeg.lib;zlib.lib;libfcgi.lib;libtiff.lib;%(AdditionalDependencies) @@ -98,7 +97,6 @@ - @@ -119,7 +117,6 @@ - @@ -135,12 +132,4 @@ - - - - - - - - - \ No newline at end of file +