Пример #1
0
static bool sWriteMNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel)
{
   TORQUE_UNUSED( bitmap );
   TORQUE_UNUSED( stream );
   TORQUE_UNUSED( compressionLevel );

   return false;
#if 0
   // ONLY RGB bitmap writing supported at this time!
   AssertFatal(getFormat() == GFXFormatR8G8B8 || getFormat() == GFXFormatR8G8B8A8 || getFormat() == GFXFormatA8, "GBitmap::writeMNG: ONLY RGB bitmap writing supported at this time.");
   if(getFormat() != GFXFormatR8G8B8 && getFormat() != GFXFormatR8G8B8A8 && getFormat() != GFXFormatA8)
      return (false);
      
   // maximum image size allowed
   #define MAX_HEIGHT 4096
   if(getHeight() >= MAX_HEIGHT)
      return false;
      
   mngstuff mnginfo;
   dMemset(&mnginfo, 0, sizeof(mngstuff));
   mng_handle mng = mng_initialize(&mnginfo, mngMallocFn, mngFreeFn, MNG_NULL);
   if(mng == NULL) {
      return false;
   }
   
   // setup the callbacks
   mng_setcb_openstream(mng, mngOpenDataFn);
   mng_setcb_closestream(mng, mngCloseDataFn);
   mng_setcb_writedata(mng, mngWriteDataFn);
   
   // create the file in memory
   mng_create(mng);
   
   mng_putchunk_defi(mng, 0, 0, 0, MNG_FALSE, 0, 0, MNG_FALSE, 0, getWidth(), 0, getHeight());
   
   mnginfo.image = (GBitmap*)this;
   mnginfo.stream = &stream;
   
   switch(getFormat()) {
      case GFXFormatA8:
         mng_putchunk_ihdr(mng, getWidth(), getHeight(), 
            MNG_BITDEPTH_8, 
            MNG_COLORTYPE_GRAY, 
            MNG_COMPRESSION_DEFLATE, 
            MNG_FILTER_ADAPTIVE, 
            MNG_INTERLACE_NONE);
         
         // not implemented in lib yet
         //mng_putimgdata_ihdr(mng, getWidth(), getHeight(), 
         //   MNG_COLORTYPE_GRAY, 
         //   MNG_BITDEPTH_8, 
         //   MNG_COMPRESSION_DEFLATE, 
         //   MNG_FILTER_ADAPTIVE, 
         //   MNG_INTERLACE_NONE, 
         //   MNG_CANVAS_GRAY8, mngCanvasLineFn);
         break;
     case GFXFormatR8G8B8:
         mng_putchunk_ihdr(mng, getWidth(), getHeight(), 
            MNG_BITDEPTH_8, 
            MNG_COLORTYPE_RGB, 
            MNG_COMPRESSION_DEFLATE, 
            MNG_FILTER_ADAPTIVE, 
            MNG_INTERLACE_NONE);
            
         // not implemented in lib yet
         //mng_putimgdata_ihdr(mng, getWidth(), getHeight(), 
         //   MNG_COLORTYPE_RGB, 
         //   MNG_BITDEPTH_8, 
         //   MNG_COMPRESSION_DEFLATE, 
         //   MNG_FILTER_ADAPTIVE, 
         //   MNG_INTERLACE_NONE, 
         //   MNG_CANVAS_RGB8, mngCanvasLineFn);
         break;
     case GFXFormatR8G8B8A8:
         mng_putchunk_ihdr(mng, getWidth(), getHeight(), 
            MNG_BITDEPTH_8, 
            MNG_COLORTYPE_RGBA, 
            MNG_COMPRESSION_DEFLATE, 
            MNG_FILTER_ADAPTIVE, 
            MNG_INTERLACE_NONE);
            
         // not implemented in lib yet
         //mng_putimgdata_ihdr(mng, getWidth(), getHeight(), 
         //   MNG_COLORTYPE_RGBA, 
         //   MNG_BITDEPTH_8, 
         //   MNG_COMPRESSION_DEFLATE, 
         //   MNG_FILTER_ADAPTIVE, 
         //   MNG_INTERLACE_NONE, 
         //   MNG_CANVAS_RGBA8, mngCanvasLineFn);
         break;
   }
   
   
   // below is a hack until libmng is mature enough to handle this itself
   //-----------------------------------------------------------------------------
   
  
   U8 *tmpbuffer = new U8[this->byteSize + getHeight()];
	if(tmpbuffer == 0)
	{
	   mng_cleanup(&mng);
	   return false;
	}
	
	// transfer data, add filterbyte
   U32 effwdt = getWidth() * this->bytesPerPixel;
   for(U32 Row = 0; Row < getHeight(); Row++)
   {
      // first Byte in each scanline is filterbyte: currently 0 -> no filter
      tmpbuffer[Row * (effwdt + 1)] = 0; 
      
      // copy the scanline
      dMemcpy(tmpbuffer + Row * (effwdt + 1) + 1, getAddress(0, Row), effwdt);
   } 
   
   // compress data with zlib
   U8 *dstbuffer = new U8[this->byteSize + getHeight()];
   if(dstbuffer == 0)
   {
      delete [] tmpbuffer;
      mng_cleanup(&mng);
      return false;
   }

   U32 dstbufferSize = this->byteSize + getHeight();
   if(Z_OK != compress2((Bytef*)dstbuffer,(uLongf*)&dstbufferSize, (const Bytef*)tmpbuffer, dstbufferSize, 9))
   {
      delete [] tmpbuffer;
      delete [] dstbuffer;
      mng_cleanup(&mng);
      return false;
   }
   
   mng_putchunk_idat(mng, dstbufferSize, (mng_ptr*)dstbuffer);
   
   
   //-----------------------------------------------------------------------------
   
   
   mng_putchunk_iend(mng);
   
   delete [] tmpbuffer;
   delete [] dstbuffer;
   
   mng_write(mng);
   mng_cleanup(&mng);
   
   return true;
#endif
}
Пример #2
0
csPtr<iDataBuffer> csJNGImageIO::Save (iImage *Image,
	iImageIO::FileFormatDescription *, const char* extraoptions)
{
  // we need to get a RGB/RGBA version of the image.
  switch (Image->GetFormat() & CS_IMGFMT_MASK)
  {
    case CS_IMGFMT_PALETTED8:
      //imgRGBA = Image->Clone ();
      //imgRGBA->SetFormat (CS_IMGFMT_TRUECOLOR | (Image->GetFormat() & CS_IMGFMT_ALPHA));
      // act like JPEG plugin; reject paletted image so no
      // unwanted/unnoticed conversions take place.
      return 0;
      break;
    case CS_IMGFMT_TRUECOLOR:
      imgRGBA = csRef<iImage>(Image);
      break;
    default:
      // unknown format
      return 0;
  } 

  // compression options
  int quality = 80;
  bool progressive = false;
  bool alpha_jpeg = false;
  int alpha_png_compress = 6;
  int alpha_jpeg_quality = -1;

  /*
     parse output options.
     options are a comma-separated list and can be either
     'option' or 'option=value'.

     supported options:
       compress=#	  image color compression, 0..100 higher values give smaller files
			  but uglier results.
       progressive	  progressive encoding.
       jng_lossy_alpha	  use lossy JPEG compression for alpha channel (instead of
			  default lossles PNG)
       jng_alpha_compress alpha channel compression, 0..100
			  Impact of higher value depends on alpha channel type.
			  JPEG - smaller files, uglier results.
			  PNG - smaller files, longer time to encode.
			  Note: defaults to value for image color compression if lossy alpha
			  is selected.
	    

     examples:
       compress=50
       progressive,compress=30
   */
  csImageLoaderOptionsParser optparser (extraoptions);
  optparser.GetBool ("progressive", progressive);
  if (optparser.GetInt ("compress", quality))
  {
    quality = 100 - quality;
    if (quality < 0) quality = 0;
    if (quality > 100) quality = 100;
    if (alpha_jpeg_quality == -1) alpha_jpeg_quality = quality;
  }
  if (optparser.GetBool ("jng_lossy_alpha", alpha_jpeg))
  {
    if (alpha_jpeg_quality == -1) alpha_jpeg_quality = quality;
  }
  if (optparser.GetInt ("jng_alpha_compress", alpha_png_compress))
  {
    alpha_jpeg_quality = 100 - alpha_png_compress;
    if (alpha_jpeg_quality < 0) alpha_jpeg_quality = 0;
    if (alpha_jpeg_quality > 100) alpha_jpeg_quality = 100;
    alpha_png_compress /= 10;
    if (alpha_png_compress < 0) alpha_png_compress = 0;
    if (alpha_png_compress > 9) alpha_png_compress = 9;
  }

  mng_handle handle = mng_initialize ( mng_ptr(this), cb_alloc, 
                                      cb_free, MNG_NULL);
  if (!handle)
  {
    Report (object_reg, CS_REPORTER_SEVERITY_WARNING,
      "failed to initialize libmng");
    return 0;
  }

  if ((mng_setcb_openstream (handle, cb_openstream) != MNG_NOERROR) ||
      (mng_setcb_closestream (handle, cb_closestream) != MNG_NOERROR) ||
      (mng_setcb_writedata (handle, cb_writedata) != MNG_NOERROR))
  {
    ReportLibmngError (object_reg, handle, "failed to set libmng callbacks");
    mng_cleanup (&handle);
    return 0;
  }

  outfile = new csMemFile ();

  if (mng_create (handle) != MNG_NOERROR)
  {
    ReportLibmngError (object_reg, handle, "failed to create new jng");
    mng_cleanup (&handle);
    delete outfile;
    imgRGBA = 0;
    return 0;
  }

  bool has_alpha = (imgRGBA->GetFormat() & CS_IMGFMT_ALPHA) != 0;

  if (mng_putchunk_jhdr (handle, imgRGBA->GetWidth(), imgRGBA->GetHeight(),
			 has_alpha ? MNG_COLORTYPE_JPEGCOLORA 
				   : MNG_COLORTYPE_JPEGCOLOR,
			 MNG_BITDEPTH_JPEG8, MNG_COMPRESSION_BASELINEJPEG,
			 progressive ? MNG_INTERLACE_PROGRESSIVE
				     : MNG_INTERLACE_SEQUENTIAL,
				     has_alpha?8:0, has_alpha?(alpha_jpeg?8:0):0, 
			 0, 0) != MNG_NOERROR)
  {
    ReportLibmngError (object_reg, handle, "failed to put JHDR chunk");
    mng_cleanup (&handle);
    delete outfile;
    imgRGBA = 0;
    return 0;
  }

  // @@@ chunk data generation.
  // lots of stuff needs to be done manually.
  // should be changed as libmng evolves.

  // write out alpha channel
  if (has_alpha)
  {
    // extract the alpha channel from the image
    int pixels = imgRGBA->GetWidth() * imgRGBA->GetHeight();
    uint8 *alpha = new uint8 [pixels];
    uint8 *alphaptr = alpha;
    csRGBpixel *imgdata = (csRGBpixel*)imgRGBA->GetImageData();

    while (pixels>0)
    {
      *alphaptr++ = (imgdata++)->alpha;
      pixels--;
    }

    if (alpha_jpeg)
    {
      // compress the alpha data as JPEG and write it out.
      uint8* volatile row = 0;
      struct jpg_datastore ds;
      struct jpeg_compress_struct cinfo;
      struct my_error_mgr jerr;
      cinfo.err = jpeg_std_error (&jerr.pub);
      jerr.pub.error_exit = my_error_exit;

      if (setjmp (jerr.setjmp_buffer))
      {
	Report (object_reg, CS_REPORTER_SEVERITY_WARNING,
		"failed to JPEG compress alpha data");
	mng_cleanup (&handle);
	delete outfile;
	delete [] row;
	delete[] alpha;
	jpeg_destroy_compress (&cinfo);
	imgRGBA = 0;
	return 0;
      }

      jpeg_create_compress (&cinfo);
      jpeg_buffer_dest (&cinfo, &ds);

      cinfo.image_width = imgRGBA->GetWidth ();
      cinfo.image_height = imgRGBA->GetHeight ();
      cinfo.input_components = 1;
      cinfo.in_color_space = JCS_GRAYSCALE;

      row = new uint8[cinfo.image_width];
      jpeg_set_defaults (&cinfo);
      jpeg_set_quality (&cinfo, alpha_jpeg_quality, true);
      jpeg_start_compress (&cinfo, true);

      JSAMPROW row_pointer[1];
      uint8 *image = alpha;
      row_pointer[0] = (JSAMPLE*)&row[0];

      while (cinfo.next_scanline < cinfo.image_height)
      {
	for (size_t i=0; i < cinfo.image_width; i++)
	  row[i] = image[cinfo.next_scanline * cinfo.image_width + i];
	jpeg_write_scanlines (&cinfo, row_pointer, 1);
      }

      jpeg_finish_compress (&cinfo);
      jpeg_destroy_compress (&cinfo);

      delete [] row;

      // funny, mng_putchunk_jdaa is missing from libmng
      //if (mng_putchunk_jdaa (handle, ds.len, ds.data) != MNG_NOERROR)
      if (mng_putchunk_unknown (handle, MNG_UINT_JDAA, 
	(mng_uint32)ds.len, ds.data) != MNG_NOERROR)
      {
	ReportLibmngError (object_reg, handle, "failed to put JDAA chunk");
	mng_cleanup (&handle);
	delete outfile;
	delete[] alpha;
	imgRGBA = 0;
	return 0;
      }
    }
    else
    {
      // generate the IDAT chunk data
      // we use the "Up" filter. 
      uint8* chunkdata = new uint8[(imgRGBA->GetWidth() + 1) * imgRGBA->GetHeight()];
      uint8* lastline = new uint8[imgRGBA->GetWidth()];
      uint8* chunkptr = chunkdata;
      alphaptr = alpha;

      memset (lastline, 0, imgRGBA->GetWidth());
      int lines = imgRGBA->GetHeight();
      while (lines > 0) 
      {
	*chunkptr++ = 2;
	int pix = 0;
	while (pix<imgRGBA->GetWidth())
	{
	  *chunkptr++ = *alphaptr - lastline[pix];
	  lastline[pix] = *alphaptr++;
	  pix++;
	}
	lines--;
      }
      delete[] lastline;

      // now compress the data
      z_stream zs;

      zs.zalloc = (alloc_func) 0;
      zs.zfree = (free_func) 0;
      zs.next_in = (Byte *) chunkdata;
      zs.avail_in = (imgRGBA->GetWidth() + 1) * imgRGBA->GetHeight();

      if (deflateInit (&zs, alpha_png_compress) != Z_OK)
      {
	Report (object_reg, CS_REPORTER_SEVERITY_WARNING,
	  "deflateInit() failed");
	mng_cleanup (&handle);
	delete outfile;
	delete[] chunkdata;
	delete[] alpha;
	imgRGBA = 0;
	return 0;
      }

      char buff[0x8000];

      while (1)
      {
        zs.next_out = (Byte *)buff;
        zs.avail_out = sizeof (buff);

        int rc = deflate (&zs, Z_FINISH);   /* Do actual compression */
        size_t size = sizeof (buff) - zs.avail_out;

	// create a chuk w/compressed data.
	if (mng_putchunk_idat (handle, (mng_uint32)size, &buff) != MNG_NOERROR)
	{
	  ReportLibmngError (object_reg, handle, "failed to put IDAT chunk");
          deflateEnd (&zs);
	  mng_cleanup (&handle);
	  delete outfile;
	  delete[] chunkdata;
	  delete[] alpha;
	  imgRGBA = 0;
	  return 0;
	}
        if (rc == Z_STREAM_END)
          break;            /* finished */
      } 
      deflateEnd (&zs);

      delete[] chunkdata;
    }

    delete[] alpha;
  }

  // compress the color data as JPEG and write it out.
  csRGBcolor* volatile row = 0;
  struct jpg_datastore ds;
  struct jpeg_compress_struct cinfo;
  struct my_error_mgr jerr;
  cinfo.err = jpeg_std_error (&jerr.pub);
  jerr.pub.error_exit = my_error_exit;

  if (setjmp (jerr.setjmp_buffer))
  {
    Report (object_reg, CS_REPORTER_SEVERITY_WARNING,
	    "failed to JPEG compress color data");
    mng_cleanup (&handle);
    delete outfile;
    delete [] row;
    jpeg_destroy_compress (&cinfo);
    imgRGBA = 0;
    return 0;
  }

  jpeg_create_compress (&cinfo);
  jpeg_buffer_dest (&cinfo, &ds);

  cinfo.image_width = imgRGBA->GetWidth ();
  cinfo.image_height = imgRGBA->GetHeight ();
  cinfo.input_components = 3;
  cinfo.in_color_space = JCS_RGB;

  row = new csRGBcolor[cinfo.image_width];
  jpeg_set_defaults (&cinfo);
  jpeg_set_quality (&cinfo, quality, true);
  if (progressive) jpeg_simple_progression (&cinfo);
  jpeg_start_compress (&cinfo, true);

  JSAMPROW row_pointer[1];
  JSAMPLE *image = (JSAMPLE*)csPackRGB::PackRGBpixelToRGB
    ((csRGBpixel*)Image->GetImageData (),
    Image->GetWidth () * Image->GetHeight ());
  row_pointer[0] = (JSAMPLE*)&row[0];

  while (cinfo.next_scanline < cinfo.image_height)
  {
    row_pointer[0] = 
      (JSAMPLE*)&image[cinfo.next_scanline * cinfo.image_width * 3];
    jpeg_write_scanlines (&cinfo, row_pointer, 1);
  }

  jpeg_finish_compress (&cinfo);
  jpeg_destroy_compress (&cinfo);

  delete [] row;

  if (mng_putchunk_jdat (handle, (mng_uint32)ds.len, ds.data) != MNG_NOERROR)
  {
    ReportLibmngError (object_reg, handle, "failed to put JDAT chunk");
    mng_cleanup (&handle);
    delete outfile;
    imgRGBA = 0;
    return 0;
  }
  
  imgRGBA = 0;

  if (mng_putchunk_iend (handle) != MNG_NOERROR)
  {
    ReportLibmngError (object_reg, handle, "failed to put IEND chunk");
    mng_cleanup (&handle);
    delete outfile;
    return 0;
  }

  if (mng_write (handle) != MNG_NOERROR)
  {
    ReportLibmngError (object_reg, handle, "failed to write out JNG data");
    mng_cleanup (&handle);
    delete outfile;
    return 0;
  }

  mng_cleanup (&handle);

  csRef<iDataBuffer> db (outfile->GetAllData ());

  delete outfile;

  return csPtr<iDataBuffer> (db);
}
Пример #3
0
/**
 * @brief write frame to MNG file
 * @param[in] *frame the frame to write to MNG file
 * @param[in] mng libmng handle
 * @param[in] width width of canvas
 * @param[in] height height of canvas
 * @param[in] first_frame if the frame is the first one in the file
 * @return 0 on success, 1 on error
 */
static int vomng_write_frame(struct vomng_frame *frame, mng_handle mng,
                             unsigned int width, unsigned int height,
                             int first_frame)
{
    unsigned int delay_ms;

    /* determine delay */
    if (frame->next)
        delay_ms = frame->next->time_ms - frame->time_ms;
    else
        delay_ms = VOMNG_DEFAULT_DELAY_MS; /* default delay for last frame */

    /* write frame headers to MNG file */
    if (mng_putchunk_seek(mng, 0, MNG_NULL)) {
        mp_msg(MSGT_VO, MSGL_ERR, "vomng: writing SEEK chunk failed\n");
        return 1;
    }
    if (mng_putchunk_fram(mng, MNG_FALSE,
                          /* keep canvas if not 1st frame */
                          first_frame ? MNG_FRAMINGMODE_1
                                      : MNG_FRAMINGMODE_NOCHANGE,
                          0, MNG_NULL,              /* no frame name */
                          MNG_CHANGEDELAY_DEFAULT,  /* change only delay */
                          MNG_CHANGETIMOUT_NO,
                          MNG_CHANGECLIPPING_NO,
                          MNG_CHANGESYNCID_NO,
                          delay_ms,                 /* new delay */
                          0,                        /* no new timeout */
                          0, 0, 0, 0, 0,            /* no new boundary */
                          0, 0)) {                  /* no count, no IDs */
        mp_msg(MSGT_VO, MSGL_ERR, "vomng: writing FRAM chunk failed\n");
        return 1;
    }
    if (mng_putchunk_defi(mng, 0,                   /* no ID */
                          MNG_DONOTSHOW_VISIBLE,
                          MNG_ABSTRACT,
                          MNG_TRUE, 0, 0,           /* top left location */
                          MNG_FALSE, 0, 0, 0, 0)) { /* no clipping */
        mp_msg(MSGT_VO, MSGL_ERR, "vomng: writing DEFI chunk failed\n");
        return 1;
    }
    if (mng_putchunk_ihdr(mng, width, height,       /* dimensions */
                          8, MNG_COLORTYPE_RGB,     /* RBG */
                          MNG_COMPRESSION_DEFLATE,
                          MNG_FILTER_ADAPTIVE,
                          MNG_INTERLACE_NONE)) {
        mp_msg(MSGT_VO, MSGL_ERR, "vomng: writing IHDR chunk failed\n");
        return 1;
    }

    /* write frame data */
    if (mng_putchunk_idat(mng, frame->len, frame->data)) {
        mp_msg(MSGT_VO, MSGL_ERR, "vomng: writing IDAT chunk failed\n");
        return 1;
    }

    /* write frame footers to MNG file */
    if (mng_putchunk_iend(mng)) {
        mp_msg(MSGT_VO, MSGL_ERR, "vomng: writing IEND chunk failed\n");
        return 1;
    }

    return 0;
}