Logo Search packages:      
Sourcecode: labplot version File versions

qjp2io.cc

/*****************************************************************************
 JPEG 2000 support for Qt based on JasPer lib
 Copyright (c) 2003 by Dmitry V. Fedorov <www.dimin.net>

 This wrapper is in it's initial state, it's quite slow now because
 of something, so soon will fix this performance probs...

 IMPLEMENTATION

 Programmer: Dima V. Fedorov <mailto:dima@dimin.net> <http://www.dimin.net/>

 History:
   13/06/2003 17:02 - First creation


 Ver : 1
*****************************************************************************/

#include <qglobal.h>
#include <qimage.h>
#include <qfile.h>

#ifdef HAVE_JASPER
#include <jasper/jasper.h>
#endif

#include "qjp2io.h"

unsigned int max(unsigned a,unsigned b) {
      if (a>b)
            return a;
      else
            return b;
}

static void read_jp2_image(QImageIO *iio) {
#ifdef HAVE_JASPER
  QImage img;
  int state=-1;
 
  // pass on file name if known
  const char *fileName;
  QFile *f = (QFile*)(iio->ioDevice());
  
  if (f != 0) fileName = f->name();
  else fileName = "QIODevice";


  long y;
  jas_image_t *jp2_image;
  jas_matrix_t *pixels[4];
  jas_stream_t *jp2_stream;
  register unsigned long i, x;
  int components[4];
  unsigned long number_components;
  unsigned int channel_scale[4], maximum_component_depth=8;

  int_fast32_t height = 0; 
  int_fast32_t width = 0;
  uchar *px;
  uchar pixel[4];

  
  // Initialize JPEG 2000 API.
  if (jas_init()) return;

  //if( !(jp2_stream = jas_stream_fopen( fileName, "r+b" )) ) return;
  if ( !(jp2_stream = jas_stream_fdopen(f->handle(), "r+b")) ) return;

  jp2_image = jas_image_decode(jp2_stream, -1, 0);

  if (jp2_image == (jas_image_t *) NULL) {
    (void) jas_stream_close(jp2_stream);
    return; //UnableToDecodeImageFile
  }


  switch (jas_clrspc_fam(jas_image_clrspc(jp2_image)))
  {
    case JAS_CLRSPC_FAM_RGB:
    {
      components[0]=jas_image_getcmptbytype(jp2_image,
        JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R));
      components[1]=jas_image_getcmptbytype(jp2_image,
        JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G));
      components[2]=jas_image_getcmptbytype(jp2_image,
        JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B));
      if ((components[0] < 0) || (components[1] < 0) || (components[2] < 0))
        {
          (void) jas_stream_close(jp2_stream);
          jas_image_destroy(jp2_image);
          return; //MissingImageChannel
        }
      number_components=3;
      components[3]=jas_image_getcmptbytype(jp2_image,
        JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_OPACITY));
      if (components[3] > 0)
        {
          //image->matte=True; // dima - what a hell is this???
          number_components++;
        }
      break;
    }
    case JAS_CLRSPC_FAM_GRAY:
    {
      components[0]=jas_image_getcmptbytype(jp2_image,
        JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_GRAY_Y));
      if (components[0] < 0)
        {
          (void) jas_stream_close(jp2_stream);
          jas_image_destroy(jp2_image);
          return; //MissingImageChannel
        }
      number_components=1;
      break;
    }
    case JAS_CLRSPC_FAM_YCBCR:
    default:
    {
      (void) jas_stream_close(jp2_stream);
      jas_image_destroy(jp2_image);
      return; //ColorspaceModelIsNotSupported
    }
  }

  //-------------------------------------------------------------------
  // Now create the QImage 
  //-------------------------------------------------------------------

  height = jas_image_height(jp2_image); 
  width  = jas_image_width(jp2_image); 
  
  // test for components geometry
  for (i=0; i < (ulong) number_components; i++)
  {
    if ((jas_image_cmptwidth(jp2_image,components[i]) != width) ||
        (jas_image_cmptheight(jp2_image,components[i]) != height) ||
        (jas_image_cmpttlx(jp2_image,components[i]) != 0) ||
        (jas_image_cmpttly(jp2_image,components[i]) != 0) ||
        (jas_image_cmpthstep(jp2_image,components[i]) != 1) ||
        (jas_image_cmptvstep(jp2_image,components[i]) != 1) ||
        (jas_image_cmptsgnd(jp2_image,components[i]) != FALSE))
      {
        (void) jas_stream_close(jp2_stream);
        jas_image_destroy(jp2_image);
        return; // IrregularChannelGeometryNotSupported
      }
  }

  // now get the max component depth
  for (i=0; i < (ulong) number_components; i++)
  {
    maximum_component_depth = max((unsigned int) jas_image_cmptprec(jp2_image,components[i]),
      maximum_component_depth);
    pixels[i] = jas_matrix_create(1, (unsigned int) width);
    if (pixels[i] == (jas_matrix_t *) NULL)
      {
        jas_image_destroy(jp2_image);
        return; //MemoryAllocationFailed
      }
  }

  // we should set it here, since QT works with only 8bit channels, done for now
  /*
  if (maximum_component_depth <= 8)
    image->depth=Min(QuantumDepth,8);
  else
    image->depth=Min(QuantumDepth,16);
  */

  if (number_components == 1) 
  {
    int i;
    QColor color;

    if (!img.create(width, height, 8, 256, QImage::IgnoreEndian)) return;

    // define gray palette here
    for (i=0; i<256; i++) {
      color.setRgb(i, i, i);
      img.setColor(i, color.rgb());
    }
  }
  else  
    if (!img.create(width, height, 32)) return;  
  

 
  for (i=0; i < (ulong) number_components; i++)
  {
    channel_scale[i]=1;
    if (jas_image_cmptprec(jp2_image,components[i]) < 16)
      channel_scale[i]=
        (1 << (16-jas_image_cmptprec(jp2_image,components[i])))+1;
  }

  //-------------------------------------------------------------------
  // Now copy image data
  //-------------------------------------------------------------------
  
  for (y=0; y < (long) height; y++)
  {
    for (i=0; i < number_components; i++)
      jas_image_readcmpt(jp2_image, components[i], 0, y, width, 1, pixels[i]);

    px = img.scanLine(y);

    switch (number_components)
    {
      
      case 1: // GRAY SCALE IMAGE
      {
        for (x=0; x < (ulong) width; x++)
        {
          px[x] = (unsigned char) jas_matrix_getv(pixels[0],x)*channel_scale[0];
        }

        break;
      }

      case 3: // RGB
      {

        for (x=0; x < (ulong) width; x++)
        {
          QRgb *row = (QRgb*) px;

          pixel[0] = (jas_matrix_getv(pixels[0],x)*channel_scale[0]);
          pixel[1] = (jas_matrix_getv(pixels[1],x)*channel_scale[1]);
          pixel[2] = (jas_matrix_getv(pixels[2],x)*channel_scale[2]);

          row[x] = qRgb(pixel[0], pixel[1], pixel[2] );
        }
        break;
      }
      case 4: // RGBA
      {
        for (x=0; x < (ulong) width; x++)
        {
          QRgb *row = (QRgb*) px;

          pixel[0] = (jas_matrix_getv(pixels[0],x)*channel_scale[0]);
          pixel[1] = (jas_matrix_getv(pixels[1],x)*channel_scale[1]);
          pixel[2] = (jas_matrix_getv(pixels[2],x)*channel_scale[2]);
          pixel[3] = (jas_matrix_getv(pixels[3],x)*channel_scale[3]);
          
          row[x] = qRgba(pixel[0], pixel[1], pixel[2], pixel[3]);
        }
        break;
      }
    }

  }
  
  // clean up all mess in memory
  (void) jas_stream_close(jp2_stream);
  jas_image_destroy(jp2_image);
  for (i=0; i < (ulong) number_components; i++)
    jas_matrix_destroy(pixels[i]);

  jas_init();

  // final things
  iio->setImage(img);
  state=0;
  iio->setStatus(state);
#endif
}
       
//! QImageIO write handler for TIFF files.
//
static void write_jp2_image(QImageIO *iio) 
{
#ifdef HAVE_JASPER
  QImage img;
  int state=-1;

  char fileName[MaxTextExtent];
  QFile *f = (QFile*)(iio->ioDevice());
  if (f != 0) strncpy(fileName, f->name().latin1(), MaxTextExtent);
  else strncpy(fileName, "QIODevice", MaxTextExtent);
  img = iio->image();
  
  char options[MaxTextExtent];
  double rate;
  int format;
  ulong y;
  jas_image_cmptparm_t component_info;
  jas_image_t *jp2_image;
  jas_matrix_t *pixels[4];
  jas_stream_t *jp2_stream;
  register unsigned long i, x;
  int status;
  unsigned long number_components;

  uchar *px;


  // Intialize JasPer JPEG 2000 API
  jas_init();

  if ( !(jp2_stream = jas_stream_fdopen(f->handle(), "w+b")) ) return;

  if (img.depth() == 8) 
    number_components = 1;
  else 
    number_components = 4;

  //if ((image_info->type != TrueColorType) && IsGrayImage(image,&image->exception))
  //  number_components=1;

  jp2_image=jas_image_create0();
  if (jp2_image == (jas_image_t *) NULL) return; //UnableToCreateImage


  for (i=0; i < (ulong) number_components; i++)
  {
    (void) memset((void *) &component_info,0,sizeof(jas_image_cmptparm_t));
    component_info.tlx=0;
    component_info.tly=0;
    component_info.hstep=1;
    component_info.vstep=1;
    component_info.width=(unsigned int) img.width();
    component_info.height=(unsigned int) img.height();
    component_info.prec=(unsigned int) 8;//img.depth() <= 8 ? 8 : 16;
    component_info.sgnd=FALSE;
    if (jas_image_addcmpt(jp2_image,i,&component_info))
    {
      jas_image_destroy(jp2_image);
      return; //UnableToCreateImageComponent
    }
  }

  if (number_components == 1)
  {
    //  sRGB Grayscale
    jas_image_setclrspc(jp2_image, JAS_CLRSPC_SGRAY);
    jas_image_setcmpttype(jp2_image, 0, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_GRAY_Y));
  }
  else
  {
    //  sRGB
    jas_image_setclrspc(jp2_image, JAS_CLRSPC_SRGB);
    jas_image_setcmpttype(jp2_image, 0, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R));
    jas_image_setcmpttype(jp2_image, 1, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G));
    jas_image_setcmpttype(jp2_image, 2, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B));
    if (number_components == 4 )
      jas_image_setcmpttype(jp2_image, 3, JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_OPACITY));
  }


  //  Convert to JPEG 2000 pixels.
  for (i=0; i < (ulong) number_components; i++)
  {
    pixels[i] = jas_matrix_create(1, (unsigned int) img.width());
    if (pixels[i] == (jas_matrix_t *) NULL)
      {
        for (x=0; x < i; x++) jas_matrix_destroy(pixels[x]);
        jas_image_destroy(jp2_image);
        return; //MemoryAllocationFailed
      }
  }
  
  
  // now copy the data itself
  for (y=0; y < (ulong) img.height(); y++)
  {
    px = img.scanLine(y); 

    for (x=0; x < (ulong) img.width(); x++)
    {
      if (number_components == 1)
        jas_matrix_setv(pixels[0], x, px[x] );
      else
      {
        QRgb *row = (QRgb*) px;    
        
        jas_matrix_setv(pixels[0], x, qRed(row[x]) );
        jas_matrix_setv(pixels[1], x, qGreen(row[x]) );
        jas_matrix_setv(pixels[2], x, qBlue(row[x]) );
        if (number_components > 3)
          jas_matrix_setv(pixels[3], x, qAlpha(*(row+x)) );
      }
    }


    for (i=0; i < (ulong) number_components; i++)
      jas_image_writecmpt(jp2_image, i, 0, y, img.width(), 1, pixels[i]);

  }  // end of byte data copy


  format = jas_image_fmtfromname(fileName);
  
  rate=1.0;
  if (img.width()*img.height() > 2500)
    {
      double alpha, header_size, number_pixels, target_size;

      alpha=115-100;
      rate=100.0/(alpha*alpha);
      header_size=550.0;
      header_size+=(number_components-1)*142;
      number_pixels=(double) img.width()*img.height()*(img.depth()/8)*number_components;
      target_size=(number_pixels*rate)+header_size;
      rate=target_size/number_pixels;
    }
  
  sprintf(options, "rate=%g", rate);
  status = jas_image_encode(jp2_image, jp2_stream, format, options);
  
  // clean up our mess
  (void) jas_stream_close(jp2_stream);
  for (i=0; i < (ulong) number_components; i++) jas_matrix_destroy(pixels[i]);
  jas_image_destroy(jp2_image);

  if (status == -1) return; //UnableToEncodeImageFile

  // it's all ok now 
  state = 0;
  iio->setStatus(state);
#endif
}
       
void qInitJp2IO() {
#ifdef HAVE_JASPER
  // this here matches any of first 4 chars and then JPEG2000 magic
  QImageIO::defineIOHandler("JPEG2000", "^....\152\120\040\040", 0, read_jp2_image, write_jp2_image);
#endif
}

Generated by  Doxygen 1.6.0   Back to index