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; }
/** * 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; }
/* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % 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); }
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; }
/* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % 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); }
/* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % 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); }
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; }
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; }
static unsigned long quality(MagickWand *mw) { return GetImageFromMagickWand(mw)->quality; }
/* * 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; }