Пример #1
0
apr_status_t
dims_resize_operation (dims_request_rec *d, char *args, char **err) {
    MagickStatusType flags;
    RectangleInfo rec;

    flags = ParseSizeGeometry(GetImageFromMagickWand(d->wand), args, &rec);
    if(!(flags & AllValues)) {
        *err = "Parsing thumbnail geometry failed";
        return DIMS_FAILURE;
    }

    if (d->optimize_resize) {
        size_t orig_width;
        size_t orig_height;

        RectangleInfo sampleRec = rec;
        sampleRec.width *= d->optimize_resize;
        sampleRec.height *= d->optimize_resize;

        orig_width = MagickGetImageWidth(d->wand);
        orig_height = MagickGetImageHeight(d->wand);

        if(sampleRec.width < orig_width && sampleRec.height < orig_height) {
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Sampling image down to %dx%d before resizing.", sampleRec.width, sampleRec.height);
            MAGICK_CHECK(MagickSampleImage(d->wand, sampleRec.width, sampleRec.height), d);
        }
    }

    MAGICK_CHECK(MagickScaleImage(d->wand, rec.width, rec.height), d);

    return DIMS_SUCCESS;
}
Пример #2
0
/**
 * Legacy API support.
 */
apr_status_t
dims_legacy_crop_operation (dims_request_rec *d, char *args, char **err) {
    MagickStatusType flags;
    RectangleInfo rec;
    ExceptionInfo ex_info;
    long width, height;
    int x, y;

    flags = ParseGravityGeometry(GetImageFromMagickWand(d->wand), args, &rec, &ex_info);

    if(!(flags & AllValues)) {
        *err = "Parsing crop geometry failed";
        return DIMS_FAILURE;
    }

    width = MagickGetImageWidth(d->wand);
    height = MagickGetImageHeight(d->wand);
    x = (width / 2) - (rec.width / 2);
    y = (height / 2) - (rec.height / 2);

    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, 
        "legacy_crop will crop to %ldx%ld+%d+%d", 
        rec.width, rec.height, x, y);

    MAGICK_CHECK(MagickCropImage(d->wand, rec.width, rec.height, x, y), d);

    return DIMS_SUCCESS;
}
Пример #3
0
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   N e w P i x e l R e g i o n I t e r a t o r                               %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  NewPixelRegionIterator() returns a new pixel iterator.
%
%  The format of the NewPixelRegionIterator method is:
%
%      PixelIterator *NewPixelRegionIterator(MagickWand *wand,const ssize_t x,
%        const ssize_t y,const size_t width,const size_t height)
%
%  A description of each parameter follows:
%
%    o wand: the magick wand.
%
%    o x,y,columns,rows:  These values define the perimeter of a region of
%      pixels.
%
*/
WandExport PixelIterator *NewPixelRegionIterator(MagickWand *wand,
  const ssize_t x,const ssize_t y,const size_t width,const size_t height)
{
  CacheView
    *view;

  const char
    *quantum;

  ExceptionInfo
    *exception;

  Image
    *image;

  PixelIterator
    *iterator;

  size_t
    depth;

  assert(wand != (MagickWand *) NULL);
  depth=MAGICKCORE_QUANTUM_DEPTH;
  quantum=GetMagickQuantumDepth(&depth);
  if (depth != MAGICKCORE_QUANTUM_DEPTH)
    ThrowWandFatalException(WandError,"QuantumDepthMismatch",quantum);
  if ((width == 0) || (width == 0))
    ThrowWandFatalException(WandError,"ZeroRegionSize",quantum);
  image=GetImageFromMagickWand(wand);
  if (image == (Image *) NULL)
    return((PixelIterator *) NULL);
  exception=AcquireExceptionInfo();
  view=AcquireVirtualCacheView(image,exception);
  if (view == (CacheView *) NULL)
    return((PixelIterator *) NULL);
  iterator=(PixelIterator *) AcquireMagickMemory(sizeof(*iterator));
  if (iterator == (PixelIterator *) NULL)
    ThrowWandFatalException(ResourceLimitFatalError,"MemoryAllocationFailed",
      wand->name);
  (void) ResetMagickMemory(iterator,0,sizeof(*iterator));
  iterator->id=AcquireWandId();
  (void) FormatLocaleString(iterator->name,MaxTextExtent,"%s-%.20g",
    PixelIteratorId,(double) iterator->id);
  iterator->exception=exception;
  iterator->view=view;
  SetGeometry(image,&iterator->region);
  iterator->region.width=width;
  iterator->region.height=height;
  iterator->region.x=x;
  iterator->region.y=y;
  iterator->pixel_wands=NewPixelWands(iterator->region.width);
  iterator->y=0;
  iterator->debug=IsEventLogging();
  if (iterator->debug != MagickFalse)
    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",iterator->name);
  iterator->signature=WandSignature;
  return(iterator);
}
Пример #4
0
ARUint8* loadImage(char* filename, int* xsize, int* ysize)
{
	ARUint8 *dptr;
	
	Image *image;
	MagickWand* magick_wand;

	magick_wand=NewMagickWand(); 
    if( magick_wand == NULL) {
        fprintf(stderr, "bad magickwand\n");
    }

	MagickBooleanType status=MagickReadImage(magick_wand,filename);
	if (status == MagickFalse) {
		//fprintf(stderr, "%s can't be read\n", filename);
		//exit(1);
        //return; //(1);
		ThrowWandException(magick_wand);
	}

	image = GetImageFromMagickWand(magick_wand);

	//ContrastImage(image,MagickTrue); 
	//EnhanceImage(image,&image->exception); 

	int index;

	*xsize = image->columns;
	*ysize = image->rows;
		
	dptr = malloc(sizeof(ARUint8) * 3 * image->rows * *xsize);
	int y;
	index = 0;
	for (y=0; y < (long) image->rows; y++)
	{
		const PixelPacket *p = AcquireImagePixels(image,0,y,*xsize,1,&image->exception);
		if (p == (const PixelPacket *) NULL)
			break;
		int x;
		for (x=0; x < (long) *xsize; x++)
		{
			/// convert to ARUint8 dptr
			/// probably a faster way to give the data straight over
			/// in BGR format
			dptr[index*3+2]   = p->red/256;
			dptr[index*3+1] = p->green/256;
			dptr[index*3] = p->blue/256;
			
			//fprintf(stderr,"%d, %d, %d\t%d\t%d\n", x, y, p->red/256, p->green/256, p->blue/256);
			p++;
			index++;
		}
	}

    DestroyMagickWand(magick_wand);

	return dptr;
}
Пример #5
0
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   N e w P i x e l R e g i o n I t e r a t o r                               %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  NewPixelRegionIterator() returns a new pixel iterator.
%
%  The format of the NewPixelRegionIterator method is:
%
%      PixelIterator NewPixelRegionIterator(MagickWand *wand,const long x,
%        const long y,const unsigned long columns,const unsigned long rows,
%        const MagickBooleanType modify)
%
%  A description of each parameter follows:
%
%    o wand: the magick wand.
%
%    o x,y,columns,rows:  These values define the perimeter of a region of
%      pixels.
%
*/
WandExport PixelIterator *NewPixelRegionIterator(MagickWand *wand,const long x,
  const long y,const unsigned long columns,const unsigned long rows)
{
  const char
    *quantum;

  Image
    *image;

  PixelIterator
    *iterator;

  unsigned long
    depth;

  ViewInfo
    *view;

  assert(wand != (MagickWand *) NULL);
  depth=MAGICKCORE_QUANTUM_DEPTH;
  quantum=GetMagickQuantumDepth(&depth);
  if (depth != MAGICKCORE_QUANTUM_DEPTH)
    ThrowWandFatalException(WandError,"QuantumDepthMismatch",quantum);
  if ((columns == 0) || (rows == 0))
    ThrowWandFatalException(WandError,"ZeroRegionSize",quantum);
  image=GetImageFromMagickWand(wand);
  if (image == (Image *) NULL)
    return((PixelIterator *) NULL);
  view=OpenCacheView(image);
  if (view == (ViewInfo *) NULL)
    return((PixelIterator *) NULL);
  iterator=(PixelIterator *) AcquireMagickMemory(sizeof(*iterator));
  if (iterator == (PixelIterator *) NULL)
    ThrowWandFatalException(ResourceLimitFatalError,"MemoryAllocationFailed",
      wand->name);
  (void) ResetMagickMemory(iterator,0,sizeof(*iterator));
  iterator->id=AcquireWandId();
  (void) FormatMagickString(iterator->name,MaxTextExtent,"%s-%lu",
    PixelIteratorId,iterator->id);
  iterator->exception=AcquireExceptionInfo();
  iterator->view=view;
  SetGeometry(image,&iterator->region);
  iterator->region.width=columns;
  iterator->region.height=rows;
  iterator->region.x=x;
  iterator->region.y=y;
  iterator->pixel_wands=NewPixelWands(iterator->region.width);
  iterator->y=0;
  iterator->debug=IsEventLogging();
  if (iterator->debug != MagickFalse)
    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",iterator->name);
  iterator->signature=WandSignature;
  return(iterator);
}
Пример #6
0
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   N e w P i x e l I t e r a t o r                                           %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  NewPixelIterator() returns a new pixel iterator.
%
%  The format of the NewPixelIterator method is:
%
%      PixelIterator *NewPixelIterator(MagickWand *wand)
%
%  A description of each parameter follows:
%
%    o wand: the magick wand.
%
*/
WandExport PixelIterator *NewPixelIterator(MagickWand *wand)
{
  const char
    *quantum;

  Image
    *image;

  PixelIterator
    *iterator;

  size_t
    depth;

  CacheView
    *view;

  depth=MAGICKCORE_QUANTUM_DEPTH;
  quantum=GetMagickQuantumDepth(&depth);
  if (depth != MAGICKCORE_QUANTUM_DEPTH)
    ThrowWandFatalException(WandError,"QuantumDepthMismatch",quantum);
  assert(wand != (MagickWand *) NULL);
  image=GetImageFromMagickWand(wand);
  if (image == (Image *) NULL)
    return((PixelIterator *) NULL);
  view=AcquireCacheView(image);
  if (view == (CacheView *) NULL)
    return((PixelIterator *) NULL);
  iterator=(PixelIterator *) AcquireMagickMemory(sizeof(*iterator));
  if (iterator == (PixelIterator *) NULL)
    ThrowWandFatalException(ResourceLimitFatalError,"MemoryAllocationFailed",
      GetExceptionMessage(errno));
  (void) ResetMagickMemory(iterator,0,sizeof(*iterator));
  iterator->id=AcquireWandId();
  (void) FormatMagickString(iterator->name,MaxTextExtent,"%s-%.20g",
    PixelIteratorId,(double) iterator->id);
  iterator->exception=AcquireExceptionInfo();
  iterator->view=view;
  SetGeometry(image,&iterator->region);
  iterator->region.width=image->columns;
  iterator->region.height=image->rows;
  iterator->region.x=0;
  iterator->region.y=0;
  iterator->pixel_wands=NewPixelWands(iterator->region.width);
  iterator->y=0;
  iterator->debug=IsEventLogging();
  if (iterator->debug != MagickFalse)
    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",iterator->name);
  iterator->signature=WandSignature;
  return(iterator);
}
Пример #7
0
apr_status_t
dims_crop_operation (dims_request_rec *d, char *args, char **err) {
    MagickStatusType flags;
    RectangleInfo rec;
    ExceptionInfo ex_info;

    flags = ParseGravityGeometry(GetImageFromMagickWand(d->wand), args, &rec, &ex_info);
    if(!(flags & AllValues)) {
        *err = "Parsing crop geometry failed";
        return DIMS_FAILURE;
    }

    MAGICK_CHECK(MagickCropImage(d->wand, rec.width, rec.height, rec.x, rec.y), d);
    MAGICK_CHECK(MagickSetImagePage(d->wand, rec.width, rec.height, rec.x, rec.y), d);

    return DIMS_SUCCESS;
}
Пример #8
0
apr_status_t
dims_legacy_thumbnail_operation (dims_request_rec *d, char *args, char **err) {
    MagickStatusType flags;
    RectangleInfo rec;
    long width, height;
    int x, y;
    char *resize_args = apr_psprintf(d->pool, "%s^", args);

    flags = ParseSizeGeometry(GetImageFromMagickWand(d->wand), resize_args, &rec);
    if(!(flags & AllValues)) {
        *err = "Parsing thumbnail (resize) geometry failed";
        return DIMS_FAILURE;
    }

    if(rec.width < 200 && rec.height < 200) {
        MAGICK_CHECK(MagickThumbnailImage(d->wand, rec.width, rec.height), d);
    } else {
        MAGICK_CHECK(MagickScaleImage(d->wand, rec.width, rec.height), d);
    }

    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, 
        "legacy_thumbnail will resize to %ldx%ld", rec.width, rec.height);

    flags = ParseAbsoluteGeometry(args, &rec);
    if(!(flags & AllValues)) {
        *err = "Parsing thumbnail (crop) geometry failed";
        return DIMS_FAILURE;
    }

    width = MagickGetImageWidth(d->wand);
    height = MagickGetImageHeight(d->wand);
    x = (width / 2) - (rec.width / 2);
    y = (height / 2) - (rec.height / 2);

    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, 
        "legacy_thumbnail will crop to %ldx%ld+%d+%d", rec.width, rec.height, x, y);

    MAGICK_CHECK(MagickCropImage(d->wand, rec.width, rec.height, x, y), d);

    return DIMS_SUCCESS;
}
Пример #9
0
static unsigned long quality(MagickWand *mw)
{
    return GetImageFromMagickWand(mw)->quality;
}
Пример #10
0
/*
 * given a source image, a destination filepath and a set of image metadata thresholds,
 * search for the lowest-quality version of the source image whose properties fall within our
 * thresholds.
 * this will produce an image file that looks the same to the casual observer, but which
 * contains much less information and results in a smaller file.
 * typical savings on unoptimized images vary widely from 10-80%, with 25-50% being most common.
 */
MagickWand * search_quality(MagickWand *mw, const char *dst,
                                   const struct imgmin_options *opt)
{
    MagickWand *tmp = NULL;
    char tmpfile[MAX_PATH] = "/tmp/imgminXXXXXX";
    if (0 == strcmp("-", dst))
    {
        if (-1 == mkstemp(tmpfile))
        {
            perror("mkstemp");
            return CloneMagickWand(mw);
        }
    } else {
        strcpy(tmpfile, dst);
    }

    /*
     * The overwhelming majority of JPEGs are TrueColorType; it is those types, with a low
     * unique color count, that we must avoid.
     */
    if (unique_colors(mw) < opt->min_unique_colors && MagickGetType(mw) != GrayscaleType)
    {
        fprintf(stderr, " Color count is too low, skipping...\n");
        return CloneMagickWand(mw);
    }

    if (quality(mw) < opt->quality_in_min)
    {
        fprintf(stderr, " Quality < %u, won't second-guess...\n", opt->quality_in_min);
        return CloneMagickWand(mw);
    }

    {
        ExceptionInfo *exception = AcquireExceptionInfo();

        const double original_density = color_density(mw);
        unsigned qmax = min(quality(mw), opt->quality_out_max);
        unsigned qmin = opt->quality_out_min;
        unsigned steps = 0;

        /*
         * binary search of quality space for optimally lowest quality that
         * produces an acceptable level of distortion
         */
        while (qmax > qmin + 1 && steps < opt->max_steps)
        {
            double distortion[CompositeChannels+1];
            double error;
            double density_ratio;
            unsigned q;

            steps++;
            q = (qmax + qmin) / 2;

            /* change quality */
            tmp = CloneMagickWand(mw);
            MagickSetImageCompressionQuality(tmp, q);

            /* apply quality change */
            MagickWriteImages(tmp, tmpfile, MagickTrue);
            DestroyMagickWand(tmp);
            tmp = NewMagickWand();
            MagickReadImage(tmp, tmpfile);

            /* quantify distortion produced by quality change
             * NOTE: writes to distortion[], which we don't care about
             * and to image(tmp)->error, which we do care about
             */
            (void) GetImageDistortion(GetImageFromMagickWand(tmp),
                                      GetImageFromMagickWand(mw),
                                      MeanErrorPerPixelMetric,
                                      distortion,
                                      exception);
            /* FIXME: in perlmagick i was getting back a number [0,255.0],
             * here something else is happening... the hardcoded divisor
             * is an imperfect attempt to scale the number back to the
             * perlmagick results. I really need to look into this.
             */
            error = GetImageFromMagickWand(tmp)->error.mean_error_per_pixel / 380.;
            density_ratio = fabs(color_density(tmp) - original_density) / original_density;
        
            /* eliminate half search space based on whether distortion within thresholds */
            if (error > opt->error_threshold || density_ratio > opt->color_density_ratio)
            {
                qmin = q;
            } else {
                qmax = q;
            }
            if (opt->show_progress)
            {
                fprintf(stderr, "%.2f/%.2f@%u ", error, density_ratio, q);
            }
        }
        if (opt->show_progress)
        {
            putc('\n', stderr);
        }

        MagickSetImageCompressionQuality(mw, qmax);

        /* "Chroma sub-sampling works because human vision is relatively insensitive to
         * small areas of colour. It gives a significant reduction in file sizes, with
         * little loss of perceived quality." [3]
         */
        (void) MagickSetImageProperty(mw, "jpeg:sampling-factor", "2x2");

        /* strip an image of all profiles and comments */
        (void) MagickStripImage(mw);

        MagickWriteImages(mw, tmpfile, MagickTrue);
        (void) DestroyMagickWand(tmp);
        tmp = NewMagickWand();
        MagickReadImage(tmp, tmpfile);

        exception = DestroyExceptionInfo(exception);
    }

    return tmp;
}