/* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % W r i t e M V G I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % WriteMVGImage() writes an image to a file in MVG image format. % % The format of the WriteMVGImage method is: % % MagickBooleanType WriteMVGImage(const ImageInfo *image_info,Image *image) % % A description of each parameter follows. % % o image_info: the image info. % % o image: The image. % */ static MagickBooleanType WriteMVGImage(const ImageInfo *image_info,Image *image) { const char *value; MagickBooleanType status; /* Open output image file. */ assert(image_info != (const ImageInfo *) NULL); assert(image_info->signature == MagickSignature); assert(image != (Image *) NULL); assert(image->signature == MagickSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); value=GetImageArtifact(image,"MVG"); if (value == (const char *) NULL) ThrowWriterException(OptionError,"NoImageVectorGraphics"); status=OpenBlob(image_info,image,WriteBlobMode,&image->exception); if (status == MagickFalse) return(status); (void) WriteBlob(image,strlen(value),(const unsigned char *) value); (void) CloseBlob(image); return(MagickTrue); }
static void OutputArtifacts(Image *image) { const char *artifact, *value; (void) FormatLocaleFile(stdout," Image Artifacts:\n"); ResetImageArtifactIterator(image); while ((artifact=GetNextImageArtifact(image)) != (const char *) NULL ) { (void) FormatLocaleFile(stdout," %s: ",artifact); value=GetImageArtifact(image,artifact); if (value != (const char *) NULL) (void) FormatLocaleFile(stdout,"%s\n",value); } ResetImageArtifactIterator(image); }
MagickExport Image *ConnectedComponentsImage(const Image *image, const size_t connectivity,CCObjectInfo **objects,ExceptionInfo *exception) { #define ConnectedComponentsImageTag "ConnectedComponents/Image" CacheView *image_view, *component_view; CCObjectInfo *object; char *p; const char *artifact; double area_threshold; Image *component_image; MagickBooleanType status; MagickOffsetType progress; MatrixInfo *equivalences; register ssize_t i; size_t size; ssize_t first, last, n, step, y; /* Initialize connected components image attributes. */ assert(image != (Image *) NULL); assert(image->signature == MagickCoreSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickCoreSignature); if (objects != (CCObjectInfo **) NULL) *objects=(CCObjectInfo *) NULL; component_image=CloneImage(image,image->columns,image->rows,MagickTrue, exception); if (component_image == (Image *) NULL) return((Image *) NULL); component_image->depth=MAGICKCORE_QUANTUM_DEPTH; if (AcquireImageColormap(component_image,MaxColormapSize,exception) == MagickFalse) { component_image=DestroyImage(component_image); ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); } /* Initialize connected components equivalences. */ size=image->columns*image->rows; if (image->columns != (size/image->rows)) { component_image=DestroyImage(component_image); ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); } equivalences=AcquireMatrixInfo(size,1,sizeof(ssize_t),exception); if (equivalences == (MatrixInfo *) NULL) { component_image=DestroyImage(component_image); return((Image *) NULL); } for (n=0; n < (ssize_t) (image->columns*image->rows); n++) (void) SetMatrixElement(equivalences,n,0,&n); object=(CCObjectInfo *) AcquireQuantumMemory(MaxColormapSize,sizeof(*object)); if (object == (CCObjectInfo *) NULL) { equivalences=DestroyMatrixInfo(equivalences); component_image=DestroyImage(component_image); ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); } (void) ResetMagickMemory(object,0,MaxColormapSize*sizeof(*object)); for (i=0; i < (ssize_t) MaxColormapSize; i++) { object[i].id=i; object[i].bounding_box.x=(ssize_t) image->columns; object[i].bounding_box.y=(ssize_t) image->rows; GetPixelInfo(image,&object[i].color); } /* Find connected components. */ status=MagickTrue; progress=0; image_view=AcquireVirtualCacheView(image,exception); for (n=0; n < (ssize_t) (connectivity > 4 ? 4 : 2); n++) { ssize_t connect4[2][2] = { { -1, 0 }, { 0, -1 } }, connect8[4][2] = { { -1, -1 }, { -1, 0 }, { -1, 1 }, { 0, -1 } }, dx, dy; if (status == MagickFalse) continue; dy=connectivity > 4 ? connect8[n][0] : connect4[n][0]; dx=connectivity > 4 ? connect8[n][1] : connect4[n][1]; for (y=0; y < (ssize_t) image->rows; y++) { register const Quantum *magick_restrict p; register ssize_t x; if (status == MagickFalse) continue; p=GetCacheViewVirtualPixels(image_view,0,y-1,image->columns,3,exception); if (p == (const Quantum *) NULL) { status=MagickFalse; continue; } p+=GetPixelChannels(image)*image->columns; for (x=0; x < (ssize_t) image->columns; x++) { PixelInfo pixel, target; ssize_t neighbor_offset, object, offset, ox, oy, root; /* Is neighbor an authentic pixel and a different color than the pixel? */ GetPixelInfoPixel(image,p,&pixel); neighbor_offset=dy*(GetPixelChannels(image)*image->columns)+dx* GetPixelChannels(image); GetPixelInfoPixel(image,p+neighbor_offset,&target); if (((x+dx) < 0) || ((x+dx) >= (ssize_t) image->columns) || ((y+dy) < 0) || ((y+dy) >= (ssize_t) image->rows) || (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse)) { p+=GetPixelChannels(image); continue; } /* Resolve this equivalence. */ offset=y*image->columns+x; neighbor_offset=dy*image->columns+dx; ox=offset; status=GetMatrixElement(equivalences,ox,0,&object); while (object != ox) { ox=object; status=GetMatrixElement(equivalences,ox,0,&object); } oy=offset+neighbor_offset; status=GetMatrixElement(equivalences,oy,0,&object); while (object != oy) { oy=object; status=GetMatrixElement(equivalences,oy,0,&object); } if (ox < oy) { status=SetMatrixElement(equivalences,oy,0,&ox); root=ox; } else { status=SetMatrixElement(equivalences,ox,0,&oy); root=oy; } ox=offset; status=GetMatrixElement(equivalences,ox,0,&object); while (object != root) { status=GetMatrixElement(equivalences,ox,0,&object); status=SetMatrixElement(equivalences,ox,0,&root); } oy=offset+neighbor_offset; status=GetMatrixElement(equivalences,oy,0,&object); while (object != root) { status=GetMatrixElement(equivalences,oy,0,&object); status=SetMatrixElement(equivalences,oy,0,&root); } status=SetMatrixElement(equivalences,y*image->columns+x,0,&root); p+=GetPixelChannels(image); } } } image_view=DestroyCacheView(image_view); /* Label connected components. */ n=0; image_view=AcquireVirtualCacheView(image,exception); component_view=AcquireAuthenticCacheView(component_image,exception); for (y=0; y < (ssize_t) component_image->rows; y++) { register const Quantum *magick_restrict p; register Quantum *magick_restrict q; register ssize_t x; if (status == MagickFalse) continue; p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); q=QueueCacheViewAuthenticPixels(component_view,0,y,component_image->columns, 1,exception); if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) { status=MagickFalse; continue; } for (x=0; x < (ssize_t) component_image->columns; x++) { ssize_t id, offset; offset=y*image->columns+x; status=GetMatrixElement(equivalences,offset,0,&id); if (id == offset) { id=n++; if (n > (ssize_t) MaxColormapSize) break; status=SetMatrixElement(equivalences,offset,0,&id); } else { status=GetMatrixElement(equivalences,id,0,&id); status=SetMatrixElement(equivalences,offset,0,&id); } if (x < object[id].bounding_box.x) object[id].bounding_box.x=x; if (x > (ssize_t) object[id].bounding_box.width) object[id].bounding_box.width=(size_t) x; if (y < object[id].bounding_box.y) object[id].bounding_box.y=y; if (y > (ssize_t) object[id].bounding_box.height) object[id].bounding_box.height=(size_t) y; object[id].color.red+=GetPixelRed(image,p); object[id].color.green+=GetPixelGreen(image,p); object[id].color.blue+=GetPixelBlue(image,p); object[id].color.black+=GetPixelBlack(image,p); object[id].color.alpha+=GetPixelAlpha(image,p); object[id].centroid.x+=x; object[id].centroid.y+=y; object[id].area++; SetPixelIndex(component_image,(Quantum) id,q); p+=GetPixelChannels(image); q+=GetPixelChannels(component_image); } if (n > (ssize_t) MaxColormapSize) break; if (SyncCacheViewAuthenticPixels(component_view,exception) == MagickFalse) status=MagickFalse; if (image->progress_monitor != (MagickProgressMonitor) NULL) { MagickBooleanType proceed; proceed=SetImageProgress(image,ConnectedComponentsImageTag,progress++, image->rows); if (proceed == MagickFalse) status=MagickFalse; } } component_view=DestroyCacheView(component_view); image_view=DestroyCacheView(image_view); equivalences=DestroyMatrixInfo(equivalences); if (n > (ssize_t) MaxColormapSize) { object=(CCObjectInfo *) RelinquishMagickMemory(object); component_image=DestroyImage(component_image); ThrowImageException(ResourceLimitError,"TooManyObjects"); } component_image->colors=(size_t) n; for (i=0; i < (ssize_t) component_image->colors; i++) { object[i].bounding_box.width-=(object[i].bounding_box.x-1); object[i].bounding_box.height-=(object[i].bounding_box.y-1); object[i].color.red=object[i].color.red/object[i].area; object[i].color.green=object[i].color.green/object[i].area; object[i].color.blue=object[i].color.blue/object[i].area; object[i].color.alpha=object[i].color.alpha/object[i].area; object[i].color.black=object[i].color.black/object[i].area; object[i].centroid.x=object[i].centroid.x/object[i].area; object[i].centroid.y=object[i].centroid.y/object[i].area; } artifact=GetImageArtifact(image,"connected-components:area-threshold"); area_threshold=0.0; if (artifact != (const char *) NULL) area_threshold=StringToDouble(artifact,(char **) NULL); if (area_threshold > 0.0) { /* Merge object below area threshold. */ component_view=AcquireAuthenticCacheView(component_image,exception); for (i=0; i < (ssize_t) component_image->colors; i++) { double census; RectangleInfo bounding_box; register ssize_t j; size_t id; if (status == MagickFalse) continue; if ((double) object[i].area >= area_threshold) continue; for (j=0; j < (ssize_t) component_image->colors; j++) object[j].census=0; bounding_box=object[i].bounding_box; for (y=0; y < (ssize_t) bounding_box.height+2; y++) { register const Quantum *magick_restrict p; register ssize_t x; if (status == MagickFalse) continue; p=GetCacheViewVirtualPixels(component_view,bounding_box.x-1, bounding_box.y+y-1,bounding_box.width+2,1,exception); if (p == (const Quantum *) NULL) { status=MagickFalse; continue; } for (x=0; x < (ssize_t) bounding_box.width+2; x++) { j=(ssize_t) GetPixelIndex(component_image,p); if (j != i) object[j].census++; } } census=0; id=0; for (j=0; j < (ssize_t) component_image->colors; j++) if (census < object[j].census) { census=object[j].census; id=(size_t) j; } object[id].area+=object[i].area; for (y=0; y < (ssize_t) bounding_box.height; y++) { register Quantum *magick_restrict q; register ssize_t x; if (status == MagickFalse) continue; q=GetCacheViewAuthenticPixels(component_view,bounding_box.x, bounding_box.y+y,bounding_box.width,1,exception); if (q == (Quantum *) NULL) { status=MagickFalse; continue; } for (x=0; x < (ssize_t) bounding_box.width; x++) { if ((ssize_t) GetPixelIndex(component_image,q) == i) SetPixelIndex(image,(Quantum) id,q); q+=GetPixelChannels(component_image); } if (SyncCacheViewAuthenticPixels(component_view,exception) == MagickFalse) status=MagickFalse; } } (void) SyncImage(component_image,exception); }
status=MagickFalse; continue; } for (x=0; x < (ssize_t) bounding_box.width; x++) { if ((ssize_t) GetPixelIndex(component_image,q) == i) SetPixelIndex(image,(Quantum) id,q); q+=GetPixelChannels(component_image); } if (SyncCacheViewAuthenticPixels(component_view,exception) == MagickFalse) status=MagickFalse; } } (void) SyncImage(component_image,exception); } artifact=GetImageArtifact(image,"connected-components:mean-color"); if (IsStringTrue(artifact) != MagickFalse) { /* Replace object with mean color. */ for (i=0; i < (ssize_t) component_image->colors; i++) component_image->colormap[i]=object[i].color; } artifact=GetImageArtifact(image,"connected-components:keep"); if (artifact != (const char *) NULL) { /* Keep these object (make others transparent). */ for (i=0; i < (ssize_t) component_image->colors; i++)
MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file, const MagickBooleanType verbose,ExceptionInfo *exception) { char color[MaxTextExtent], format[MaxTextExtent], key[MaxTextExtent]; ChannelFeatures *channel_features; ChannelStatistics *channel_statistics; ColorspaceType colorspace; const char *artifact, *name, *property, *registry, *value; const MagickInfo *magick_info; double elapsed_time, user_time; ImageType type; MagickBooleanType ping; register const Quantum *p; register ssize_t i, x; size_t distance, scale; ssize_t y; assert(image != (Image *) NULL); assert(image->signature == MagickSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); if (file == (FILE *) NULL) file=stdout; *format='\0'; elapsed_time=GetElapsedTime(&image->timer); user_time=GetUserTime(&image->timer); GetTimerInfo(&image->timer); if (verbose == MagickFalse) { /* Display summary info about the image. */ if (*image->magick_filename != '\0') if (LocaleCompare(image->magick_filename,image->filename) != 0) (void) FormatLocaleFile(file,"%s=>",image->magick_filename); if ((GetPreviousImageInList(image) == (Image *) NULL) && (GetNextImageInList(image) == (Image *) NULL) && (image->scene == 0)) (void) FormatLocaleFile(file,"%s ",image->filename); else (void) FormatLocaleFile(file,"%s[%.20g] ",image->filename,(double) image->scene); (void) FormatLocaleFile(file,"%s ",image->magick); if ((image->magick_columns != 0) || (image->magick_rows != 0)) if ((image->magick_columns != image->columns) || (image->magick_rows != image->rows)) (void) FormatLocaleFile(file,"%.20gx%.20g=>",(double) image->magick_columns,(double) image->magick_rows); (void) FormatLocaleFile(file,"%.20gx%.20g ",(double) image->columns, (double) image->rows); if ((image->page.width != 0) || (image->page.height != 0) || (image->page.x != 0) || (image->page.y != 0)) (void) FormatLocaleFile(file,"%.20gx%.20g%+.20g%+.20g ",(double) image->page.width,(double) image->page.height,(double) image->page.x, (double) image->page.y); (void) FormatLocaleFile(file,"%.20g-bit ",(double) image->depth); if (image->type != UndefinedType) (void) FormatLocaleFile(file,"%s ",CommandOptionToMnemonic( MagickTypeOptions,(ssize_t) image->type)); if (image->storage_class == DirectClass) { (void) FormatLocaleFile(file,"DirectClass "); if (image->total_colors != 0) { (void) FormatMagickSize(image->total_colors,MagickFalse,format); (void) FormatLocaleFile(file,"%s ",format); } } else if (image->total_colors <= image->colors) (void) FormatLocaleFile(file,"PseudoClass %.20gc ",(double) image->colors); else (void) FormatLocaleFile(file,"PseudoClass %.20g=>%.20gc ",(double) image->total_colors,(double) image->colors); if (image->error.mean_error_per_pixel != 0.0) (void) FormatLocaleFile(file,"%.20g/%f/%fdb ",(double) (image->error.mean_error_per_pixel+0.5), image->error.normalized_mean_error, image->error.normalized_maximum_error); if (GetBlobSize(image) != 0) { (void) FormatMagickSize(GetBlobSize(image),MagickFalse,format); (void) FormatLocaleFile(file,"%s ",format); } (void) FormatLocaleFile(file,"%0.3fu %lu:%02lu.%03lu",user_time, (unsigned long) (elapsed_time/60.0),(unsigned long) floor(fmod( elapsed_time,60.0)),(unsigned long) (1000.0*(elapsed_time- floor(elapsed_time)))); (void) FormatLocaleFile(file,"\n"); (void) fflush(file); return(ferror(file) != 0 ? MagickFalse : MagickTrue); } /* Display verbose info about the image. */ p=GetVirtualPixels(image,0,0,1,1,exception); ping=p == (const Quantum *) NULL ? MagickTrue : MagickFalse; type=GetImageType(image,exception); (void) SignatureImage(image,exception); (void) FormatLocaleFile(file,"Image: %s\n",image->filename); if (*image->magick_filename != '\0') if (LocaleCompare(image->magick_filename,image->filename) != 0) { char filename[MaxTextExtent]; GetPathComponent(image->magick_filename,TailPath,filename); (void) FormatLocaleFile(file," Base filename: %s\n",filename); } magick_info=GetMagickInfo(image->magick,exception); if ((magick_info == (const MagickInfo *) NULL) || (*GetMagickDescription(magick_info) == '\0')) (void) FormatLocaleFile(file," Format: %s\n",image->magick); else (void) FormatLocaleFile(file," Format: %s (%s)\n",image->magick, GetMagickDescription(magick_info)); (void) FormatLocaleFile(file," Class: %s\n",CommandOptionToMnemonic( MagickClassOptions,(ssize_t) image->storage_class)); (void) FormatLocaleFile(file," Geometry: %.20gx%.20g%+.20g%+.20g\n",(double) image->columns,(double) image->rows,(double) image->tile_offset.x,(double) image->tile_offset.y); if ((image->magick_columns != 0) || (image->magick_rows != 0)) if ((image->magick_columns != image->columns) || (image->magick_rows != image->rows)) (void) FormatLocaleFile(file," Base geometry: %.20gx%.20g\n",(double) image->magick_columns,(double) image->magick_rows); if ((image->resolution.x != 0.0) && (image->resolution.y != 0.0)) { (void) FormatLocaleFile(file," Resolution: %gx%g\n",image->resolution.x, image->resolution.y); (void) FormatLocaleFile(file," Print size: %gx%g\n",(double) image->columns/image->resolution.x,(double) image->rows/ image->resolution.y); } (void) FormatLocaleFile(file," Units: %s\n",CommandOptionToMnemonic( MagickResolutionOptions,(ssize_t) image->units)); (void) FormatLocaleFile(file," Type: %s\n",CommandOptionToMnemonic( MagickTypeOptions,(ssize_t) type)); if (image->type != UndefinedType) (void) FormatLocaleFile(file," Base type: %s\n",CommandOptionToMnemonic( MagickTypeOptions,(ssize_t) image->type)); (void) FormatLocaleFile(file," Endianess: %s\n",CommandOptionToMnemonic( MagickEndianOptions,(ssize_t) image->endian)); /* Detail channel depth and extrema. */ (void) FormatLocaleFile(file," Colorspace: %s\n",CommandOptionToMnemonic( MagickColorspaceOptions,(ssize_t) image->colorspace)); channel_statistics=(ChannelStatistics *) NULL; channel_features=(ChannelFeatures *) NULL; colorspace=image->colorspace; scale=1; if (ping == MagickFalse) { size_t depth; channel_statistics=GetImageStatistics(image,exception); artifact=GetImageArtifact(image,"identify:features"); if (artifact != (const char *) NULL) { distance=StringToUnsignedLong(artifact); channel_features=GetImageFeatures(image,distance,exception); } depth=GetImageDepth(image,exception); if (image->depth == depth) (void) FormatLocaleFile(file," Depth: %.20g-bit\n",(double) image->depth); else (void) FormatLocaleFile(file," Depth: %.20g/%.20g-bit\n",(double) image->depth,(double) depth); (void) FormatLocaleFile(file," Channel depth:\n"); if (IsImageGray(image,exception) != MagickFalse) colorspace=GRAYColorspace; switch (colorspace) { case RGBColorspace: default: { (void) FormatLocaleFile(file," red: %.20g-bit\n",(double) channel_statistics[RedPixelChannel].depth); (void) FormatLocaleFile(file," green: %.20g-bit\n",(double) channel_statistics[GreenPixelChannel].depth); (void) FormatLocaleFile(file," blue: %.20g-bit\n",(double) channel_statistics[BluePixelChannel].depth); break; } case CMYKColorspace: { (void) FormatLocaleFile(file," cyan: %.20g-bit\n",(double) channel_statistics[CyanPixelChannel].depth); (void) FormatLocaleFile(file," magenta: %.20g-bit\n",(double) channel_statistics[MagentaPixelChannel].depth); (void) FormatLocaleFile(file," yellow: %.20g-bit\n",(double) channel_statistics[YellowPixelChannel].depth); (void) FormatLocaleFile(file," black: %.20g-bit\n",(double) channel_statistics[BlackPixelChannel].depth); break; } case GRAYColorspace: { (void) FormatLocaleFile(file," gray: %.20g-bit\n",(double) channel_statistics[GrayPixelChannel].depth); break; } } if (image->matte != MagickFalse) (void) FormatLocaleFile(file," alpha: %.20g-bit\n",(double) channel_statistics[AlphaPixelChannel].depth); scale=1; if (image->depth <= MAGICKCORE_QUANTUM_DEPTH) scale=QuantumRange/((size_t) QuantumRange >> ((size_t) MAGICKCORE_QUANTUM_DEPTH-image->depth)); }
/* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % R e a d P A N G O I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % ReadPANGOImage() reads an image in the Pango Markup Language Format. % % The format of the ReadPANGOImage method is: % % Image *ReadPANGOImage(const ImageInfo *image_info, % ExceptionInfo *exception) % % A description of each parameter follows: % % o image_info: the image info. % % o exception: return any errors or warnings in this structure. % */ static Image *ReadPANGOImage(const ImageInfo *image_info, ExceptionInfo *exception) { cairo_font_options_t *font_options; cairo_surface_t *surface; char *caption, *property; cairo_t *cairo_image; const char *option; DrawInfo *draw_info; Image *image; MagickBooleanType status; PangoAlignment align; PangoContext *context; PangoFontMap *fontmap; PangoGravity gravity; PangoLayout *layout; PangoRectangle extent; PixelInfo fill_color; RectangleInfo page; register unsigned char *p; size_t stride; ssize_t y; unsigned char *pixels; /* Initialize Image structure. */ assert(image_info != (const ImageInfo *) NULL); assert(image_info->signature == MagickSignature); if (image_info->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", image_info->filename); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickSignature); image=AcquireImage(image_info,exception); (void) ResetImagePage(image,"0x0+0+0"); /* Format caption. */ option=GetImageArtifact(image,"filename"); if (option == (const char *) NULL) property=InterpretImageProperties(image_info,image,image_info->filename, exception); else if (LocaleNCompare(option,"pango:",6) == 0) property=InterpretImageProperties(image_info,image,option+6,exception); else property=InterpretImageProperties(image_info,image,option,exception); (void) SetImageProperty(image,"caption",property,exception); property=DestroyString(property); caption=ConstantString(GetImageProperty(image,"caption",exception)); /* Get context. */ fontmap=pango_cairo_font_map_new(); pango_cairo_font_map_set_resolution(PANGO_CAIRO_FONT_MAP(fontmap), image->resolution.x == 0.0 ? 90.0 : image->resolution.x); font_options=cairo_font_options_create(); option=GetImageArtifact(image,"pango:hinting"); if (option != (const char *) NULL) { if (LocaleCompare(option,"none") != 0) cairo_font_options_set_hint_style(font_options,CAIRO_HINT_STYLE_NONE); if (LocaleCompare(option,"full") != 0) cairo_font_options_set_hint_style(font_options,CAIRO_HINT_STYLE_FULL); } context=pango_font_map_create_context(fontmap); pango_cairo_context_set_font_options(context,font_options); cairo_font_options_destroy(font_options); option=GetImageArtifact(image,"pango:language"); if (option != (const char *) NULL) pango_context_set_language(context,pango_language_from_string(option)); draw_info=CloneDrawInfo(image_info,(DrawInfo *) NULL); pango_context_set_base_dir(context,draw_info->direction == RightToLeftDirection ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR); switch (draw_info->gravity) { case NorthGravity: { gravity=PANGO_GRAVITY_NORTH; break; } case NorthWestGravity: case WestGravity: case SouthWestGravity: { gravity=PANGO_GRAVITY_WEST; break; } case NorthEastGravity: case EastGravity: case SouthEastGravity: { gravity=PANGO_GRAVITY_EAST; break; } case SouthGravity: { gravity=PANGO_GRAVITY_SOUTH; break; } default: { gravity=PANGO_GRAVITY_AUTO; break; } } pango_context_set_base_gravity(context,gravity); option=GetImageArtifact(image,"pango:gravity-hint"); if (option != (const char *) NULL) { if (LocaleCompare(option,"line") == 0) pango_context_set_gravity_hint(context,PANGO_GRAVITY_HINT_LINE); if (LocaleCompare(option,"natural") == 0) pango_context_set_gravity_hint(context,PANGO_GRAVITY_HINT_NATURAL); if (LocaleCompare(option,"strong") == 0) pango_context_set_gravity_hint(context,PANGO_GRAVITY_HINT_STRONG); } /* Configure layout. */ layout=pango_layout_new(context); option=GetImageArtifact(image,"pango:auto-dir"); if (option != (const char *) NULL) pango_layout_set_auto_dir(layout,1); option=GetImageArtifact(image,"pango:ellipsize"); if (option != (const char *) NULL) { if (LocaleCompare(option,"end") == 0) pango_layout_set_ellipsize(layout,PANGO_ELLIPSIZE_END); if (LocaleCompare(option,"middle") == 0) pango_layout_set_ellipsize(layout,PANGO_ELLIPSIZE_MIDDLE); if (LocaleCompare(option,"none") == 0) pango_layout_set_ellipsize(layout,PANGO_ELLIPSIZE_NONE); if (LocaleCompare(option,"start") == 0) pango_layout_set_ellipsize(layout,PANGO_ELLIPSIZE_START); } option=GetImageArtifact(image,"pango:justify"); if (IfMagickTrue(IsStringTrue(option))) pango_layout_set_justify(layout,1); option=GetImageArtifact(image,"pango:single-paragraph"); if (IfMagickTrue(IsStringTrue(option))) pango_layout_set_single_paragraph_mode(layout,1); option=GetImageArtifact(image,"pango:wrap"); if (option != (const char *) NULL) { if (LocaleCompare(option,"char") == 0) pango_layout_set_wrap(layout,PANGO_WRAP_CHAR); if (LocaleCompare(option,"word") == 0) pango_layout_set_wrap(layout,PANGO_WRAP_WORD); if (LocaleCompare(option,"word-char") == 0) pango_layout_set_wrap(layout,PANGO_WRAP_WORD_CHAR); } option=GetImageArtifact(image,"pango:indent"); if (option != (const char *) NULL) pango_layout_set_indent(layout,(int) ((StringToLong(option)* (image->resolution.x == 0.0 ? 90.0 : image->resolution.x)*PANGO_SCALE+36)/ 90.0+0.5)); switch (draw_info->align) { case CenterAlign: align=PANGO_ALIGN_CENTER; break; case RightAlign: align=PANGO_ALIGN_RIGHT; break; case LeftAlign: align=PANGO_ALIGN_LEFT; break; default: { if (draw_info->gravity == CenterGravity) { align=PANGO_ALIGN_CENTER; break; } align=PANGO_ALIGN_LEFT; break; } } if ((align != PANGO_ALIGN_CENTER) && (draw_info->direction == RightToLeftDirection)) align=(PangoAlignment) (PANGO_ALIGN_LEFT+PANGO_ALIGN_RIGHT-align); pango_layout_set_alignment(layout,align); if (draw_info->font != (char *) NULL) { PangoFontDescription *description; /* Set font. */ description=pango_font_description_from_string(draw_info->font); pango_font_description_set_size(description,(int) (PANGO_SCALE* draw_info->pointsize+0.5)); pango_layout_set_font_description(layout,description); pango_font_description_free(description); } option=GetImageArtifact(image,"pango:markup"); if ((option != (const char *) NULL) && (IsStringTrue(option) == MagickFalse)) pango_layout_set_text(layout,caption,-1); else { GError *error; error=(GError *) NULL; if (pango_parse_markup(caption,-1,0,NULL,NULL,NULL,&error) == 0) (void) ThrowMagickException(exception,GetMagickModule(),CoderError, error->message,"`%s'",image_info->filename); pango_layout_set_markup(layout,caption,-1); } pango_layout_context_changed(layout); page.x=0; page.y=0; if (image_info->page != (char *) NULL) (void) ParseAbsoluteGeometry(image_info->page,&page); if (image->columns == 0) { pango_layout_get_extents(layout,NULL,&extent); image->columns=(extent.x+extent.width+PANGO_SCALE/2)/PANGO_SCALE+2*page.x; } else { image->columns-=2*page.x; pango_layout_set_width(layout,(int) ((PANGO_SCALE*image->columns* (image->resolution.x == 0.0 ? 90.0 : image->resolution.x)+45.0)/90.0+ 0.5)); } if (image->rows == 0) { pango_layout_get_extents(layout,NULL,&extent); image->rows=(extent.y+extent.height+PANGO_SCALE/2)/PANGO_SCALE+2*page.y; } else { image->rows-=2*page.y; pango_layout_set_height(layout,(int) ((PANGO_SCALE*image->rows* (image->resolution.y == 0.0 ? 90.0 : image->resolution.y)+45.0)/90.0+ 0.5)); } /* Render markup. */ stride=(size_t) cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, image->columns); pixels=(unsigned char *) AcquireQuantumMemory(image->rows,stride* sizeof(*pixels)); if (pixels == (unsigned char *) NULL) { draw_info=DestroyDrawInfo(draw_info); caption=DestroyString(caption); ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); } surface=cairo_image_surface_create_for_data(pixels,CAIRO_FORMAT_ARGB32, image->columns,image->rows,stride); cairo_image=cairo_create(surface); cairo_set_operator(cairo_image,CAIRO_OPERATOR_CLEAR); cairo_paint(cairo_image); cairo_set_operator(cairo_image,CAIRO_OPERATOR_OVER); cairo_translate(cairo_image,page.x,page.y); pango_cairo_show_layout(cairo_image,layout); cairo_destroy(cairo_image); cairo_surface_destroy(surface); g_object_unref(layout); g_object_unref(fontmap); /* Convert surface to image. */ (void) SetImageBackgroundColor(image,exception); p=pixels; GetPixelInfo(image,&fill_color); for (y=0; y < (ssize_t) image->rows; y++) { register Quantum *q; register ssize_t x; q=GetAuthenticPixels(image,0,y,image->columns,1,exception); if (q == (Quantum *) NULL) break; for (x=0; x < (ssize_t) image->columns; x++) { double gamma; fill_color.blue=(double) ScaleCharToQuantum(*p++); fill_color.green=(double) ScaleCharToQuantum(*p++); fill_color.red=(double) ScaleCharToQuantum(*p++); fill_color.alpha=(double) ScaleCharToQuantum(*p++); /* Disassociate alpha. */ gamma=1.0-QuantumScale*fill_color.alpha; gamma=PerceptibleReciprocal(gamma); fill_color.blue*=gamma; fill_color.green*=gamma; fill_color.red*=gamma; CompositePixelOver(image,&fill_color,fill_color.alpha,q,(double) GetPixelAlpha(image,q),q); q+=GetPixelChannels(image); } if (SyncAuthenticPixels(image,exception) == MagickFalse) break; if (image->previous == (Image *) NULL) { status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y, image->rows); if (status == MagickFalse) break; } } /* Relinquish resources. */ pixels=(unsigned char *) RelinquishMagickMemory(pixels); draw_info=DestroyDrawInfo(draw_info); caption=DestroyString(caption); return(GetFirstImageInList(image)); }
MagickExport MagickBooleanType GradientImage(Image *image, const GradientType type,const SpreadMethod method,const StopInfo *stops, const size_t number_stops,ExceptionInfo *exception) { const char *artifact; DrawInfo *draw_info; GeometryInfo geometry_info; GradientInfo *gradient; MagickBooleanType status; MagickStatusType flags; /* Set gradient start-stop end points. */ assert(image != (const Image *) NULL); assert(image->signature == MagickCoreSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); assert(stops != (const StopInfo *) NULL); assert(number_stops > 0); draw_info=AcquireDrawInfo(); gradient=(&draw_info->gradient); gradient->type=type; gradient->bounding_box.width=image->columns; gradient->bounding_box.height=image->rows; artifact=GetImageArtifact(image,"gradient:bounding-box"); if (artifact != (const char *) NULL) (void) ParseAbsoluteGeometry(artifact,&gradient->bounding_box); gradient->gradient_vector.x2=(double) image->columns-1.0; gradient->gradient_vector.y2=(double) image->rows-1.0; if ((type == LinearGradient) && (gradient->gradient_vector.y2 != 0.0)) gradient->gradient_vector.x2=0.0; artifact=GetImageArtifact(image,"gradient:vector"); if (artifact != (const char *) NULL) { flags=ParseGeometry(artifact,&geometry_info); gradient->gradient_vector.x1=geometry_info.rho; if ((flags & SigmaValue) != 0) gradient->gradient_vector.y1=geometry_info.sigma; if ((flags & XiValue) != 0) gradient->gradient_vector.x2=geometry_info.xi; if ((flags & PsiValue) != 0) gradient->gradient_vector.y2=geometry_info.psi; } gradient->center.x=(double) gradient->gradient_vector.x2/2.0; gradient->center.y=(double) gradient->gradient_vector.y2/2.0; artifact=GetImageArtifact(image,"gradient:center"); if (artifact != (const char *) NULL) { flags=ParseGeometry(artifact,&geometry_info); gradient->center.x=geometry_info.rho; if ((flags & SigmaValue) != 0) gradient->center.y=geometry_info.sigma; } gradient->radius=MagickMax(gradient->center.x,gradient->center.y); artifact=GetImageArtifact(image,"gradient:radius"); if (artifact != (const char *) NULL) gradient->radius=StringToDouble(artifact,(char **) NULL); gradient->spread=method; /* Define the gradient to fill between the stops. */ gradient->number_stops=number_stops; gradient->stops=(StopInfo *) AcquireQuantumMemory(gradient->number_stops, sizeof(*gradient->stops)); if (gradient->stops == (StopInfo *) NULL) ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", image->filename); (void) CopyMagickMemory(gradient->stops,stops,(size_t) number_stops* sizeof(*stops)); /* Draw a gradient on the image. */ status=DrawGradientImage(image,draw_info,exception); draw_info=DestroyDrawInfo(draw_info); return(status); }
MagickExport Image *DistortImage(Image *image,const DistortImageMethod method, const unsigned long number_arguments,const double *arguments, MagickBooleanType bestfit, ExceptionInfo *exception) { #define DistortImageTag "Distort/Image" double coefficients[9], validity; Image *distort_image; register long i, x; long j, y; PointInfo point; /* point to sample (center of filtered resample of area) */ MagickPixelPacket pixel, /* pixel to assign to distorted image */ invalid; /* the color to assign when distort result is invalid */ register IndexPacket *indexes; register PixelPacket *q; ResampleFilter *resample_filter; ViewInfo *distort_view; MagickBooleanType status; RectangleInfo geometry; const char *property; assert(image != (Image *) NULL); assert(image->signature == MagickSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickSignature); (void) ResetMagickMemory(coefficients,0,sizeof(coefficients)); /* Convert input arguments into mapping coefficents for distortion */ switch (method) { case AffineDistortion: { double **matrix; PointInfo *points; /* Affine Distortion u = c0*x + c2*y + c4 v = c1*x + c3*y + c5 Input Arguments are pairs of distorted coodinates (minimum 3 sets) Which will be used to generate the coefficients of the above. u1,v1, x1,y1, u2,v2, x2,y2, u3,v3, x3,y3, ... */ if (number_arguments != 12) { /* to be removed */ (void) ThrowMagickException(exception,GetMagickModule(),OptionError, "InvalidArgument","%s : '%s'","distort Affine", "Needs 12 numbers"); return((Image *) NULL); } if (number_arguments%4 != 0) { (void) ThrowMagickException(exception,GetMagickModule(),OptionError, "InvalidArgument","%s : '%s'","distort Affine", "Requires pairs of coords (4 numbers U,V,X,Y)"); return((Image *) NULL); } if (number_arguments < 12) { (void) ThrowMagickException(exception,GetMagickModule(),OptionError, "InvalidArgument","%s : '%s'","distort Affine", "Minimum of 3 pairs of coords needed (12 numbers)"); return((Image *) NULL); } points=(PointInfo *) AcquireQuantumMemory((number_arguments)/2UL, sizeof(*points)); if (points == (PointInfo *) NULL) ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); for (i=0; i < (long) number_arguments; i++) if ((i % 2 ) == 0) points[i/2].x=arguments[i]; else points[i/2].y=arguments[i]; matrix = AcquireMagickMatrix(6UL,6UL); if (matrix == (double **) NULL) { points=(PointInfo *) RelinquishMagickMemory(points); ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); } status=SolveAffineDistortion(6UL,points,matrix,coefficients); matrix = RelinquishMagickMatrix(matrix, 6UL); points = (PointInfo *) RelinquishMagickMemory(points); if ( status == MagickFalse ) { (void) ThrowMagickException(exception,GetMagickModule(),OptionError, "InvalidArgument","%s : '%s'","distort Affine", "Degenerate Result"); return((Image *) NULL); } break; } case AffineProjectionDistortion: { /* Arguments: Affine Matrix (forward mapping) sx, rx, ry, sy, tx, ty */ if (number_arguments != 6) { (void) ThrowMagickException(exception,GetMagickModule(),OptionError, "InvalidArgument","%s : '%s'","distort AffineProjection", "Needs 6 numbers"); return((Image *) NULL); } InvertAffineCoefficients(arguments, coefficients); break; } case BilinearDistortion: { double **matrix; PointInfo *points; /* Bilinear Distortion (reversed) u = c0*x + c1*y + c2*x*y + c3; v = c4*x + c5*y + c6*x*y + c7; Input Arguments are pairs of distorted coodinates (minimum 4 pairs) Which will be used to generate the coefficients of the above. u1,v1, x1,y1, u2,v2, x2,y2, u3,v3, x3,y3, u4,v4, x4,y4 ... */ if (number_arguments != 16) { /* to be removed */ (void) ThrowMagickException(exception,GetMagickModule(),OptionError, "InvalidArgument","%s : '%s'","distort Bilinear", "Needs 16 numbers"); return((Image *) NULL); } if (number_arguments%4 != 0) { (void) ThrowMagickException(exception,GetMagickModule(),OptionError, "InvalidArgument","%s : '%s'","distort Bilinear", "Requires pairs of coords (4 numbers U,V,X,Y)"); return((Image *) NULL); } if (number_arguments < 16) { (void) ThrowMagickException(exception,GetMagickModule(),OptionError, "InvalidArgument","%s : '%s'","distort Bilinear", "Minimum of 4 pairs of coords needed (16 numbers)"); return((Image *) NULL); } points=(PointInfo *) AcquireQuantumMemory((number_arguments+1UL)/2UL, sizeof(*points)); if (points == (PointInfo *) NULL) ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); for (i=0; i < (long) number_arguments; i++) if ((i % 2 ) == 0) points[i/2].x=arguments[i]; else points[i/2].y=arguments[i]; matrix = AcquireMagickMatrix(8UL,8UL); if (matrix == (double **) NULL) { points=(PointInfo *) RelinquishMagickMemory(points); ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); } status=SolveBilinearDistortion(8UL,points,matrix,coefficients); matrix = RelinquishMagickMatrix(matrix, 8UL); points = (PointInfo *) RelinquishMagickMemory(points); if ( status == MagickFalse ) { (void) ThrowMagickException(exception,GetMagickModule(),OptionError, "InvalidArgument","%s : '%s'","distort Bilinear", "Degenerate Result"); return((Image *) NULL); } break; } case PerspectiveDistortion: { double **matrix; PointInfo *points; /* Perspective Distortion (a ratio of affine distortions) p c0*x + c1*y + c2 u = --- = ------------------ r c6*x + c7*y + 1 q c3*x + c4*y + c5 v = --- = ------------------ r c6*x + c7*y + 1 c8 = Sign of 'r', or the denominator affine, for the actual image. This determines what part of the distorted image is 'ground' side of the horizon, the other part is 'sky' or invalid. Input Arguments are pairs of distorted coodinates (minimum 4 pairs) Which will be used to generate the coefficients of the above. u1,v1, x1,y1, u2,v2, x2,y2, u3,v3, x3,y3, u4,v4, x4,y4 ... */ if (number_arguments != 16) { /* to be removed */ (void) ThrowMagickException(exception,GetMagickModule(),OptionError, "InvalidArgument","%s : '%s'","distort Bilinear", "Needs 16 numbers"); return((Image *) NULL); } if (number_arguments%4 != 0) { (void) ThrowMagickException(exception,GetMagickModule(),OptionError, "InvalidArgument","%s : '%s'","distort Bilinear", "Requires pairs of coords (4 numbers U,V,X,Y)"); return((Image *) NULL); } if (number_arguments < 16) { (void) ThrowMagickException(exception,GetMagickModule(),OptionError, "InvalidArgument","%s : '%s'","distort Bilinear", "Minimum of 4 pairs of coords needed (16 numbers)"); return((Image *) NULL); } points=(PointInfo *) AcquireQuantumMemory((number_arguments+1UL)/2UL, sizeof(*points)); if (points == (PointInfo *) NULL) ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); for (i=0; i < (long) number_arguments; i++) if ((i % 2 ) == 0) points[i/2].x=arguments[i]; else points[i/2].y=arguments[i]; matrix = AcquireMagickMatrix(8UL,8UL); if (matrix == (double **) NULL) { points=(PointInfo *) RelinquishMagickMemory(points); ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); } status=SolvePerspectiveDistortion(8UL,points,matrix,coefficients); matrix = RelinquishMagickMatrix(matrix, 8UL); points = (PointInfo *) RelinquishMagickMemory(points); if ( status == MagickFalse ) { (void) ThrowMagickException(exception,GetMagickModule(),OptionError, "InvalidArgument","%s : '%s'","distort Prespective", "Degenerate Result"); return((Image *) NULL); } /* What is the 'ground' of the perspective distortion. Just find the sign of denomitor affine for any image coordinate. This is a 9'th coefficient! */ coefficients[8] = coefficients[6]*arguments[number_arguments-2] + coefficients[7]*arguments[number_arguments-1] + 1.0; coefficients[8] = (coefficients[8] < 0.0) ? -1.0 : +1.0; break; } case PerspectiveProjectionDistortion: { /* Arguments: Perspective Coefficents (forward mapping) */ if (number_arguments != 8) { (void) ThrowMagickException(exception,GetMagickModule(),OptionError, "InvalidArgument","%s : '%s'", "distort PerspectiveProjection", "Needs 8 numbers"); return((Image *) NULL); } InvertPerspectiveCoefficients(arguments, coefficients); /* What is the 'ground' of the perspective distortion? For a forward mapped perspective the images 0,0 coord will map to c2,c5 in the distorted image, so get the sign of denominator of that. This is a 9'th coefficient! */ coefficients[8] = coefficients[6]*arguments[2] + coefficients[7]*arguments[5] + 1.0; coefficients[8] = (coefficients[8] < 0.0) ? -1.0 : +1.0; break; } case ScaleRotateTranslateDistortion: { double cosine, sine, x,y,sx,sy,a,nx,ny; /* Argument options, by number of arguments given: 7: x,y, sx,sy, a, nx,ny 6: x,y, s, a, nx,ny 5: x,y, sx,sy, a 4: x,y, s, a 3: x,y, a 2: s, a 1: a Where actions are (in order of application) x,y 'center' of transforms (default = image center) sx,sy scale image by this amount (default = 1) a angle of rotation (argument required) nx,ny move 'center' here (default = no movement) And convert to affine mapping coefficients */ x = nx = (double)image->columns/2.0; y = ny = (double)image->rows/2.0; if ( bestfit ) { x = nx += (double)image->page.x; y = ny += (double)image->page.y; } sx = sy = 1.0; switch ( number_arguments ) { case 0: (void) ThrowMagickException(exception,GetMagickModule(),OptionError, "InvalidArgument","%s : '%s'", "distort ScaleTranslateRotate", "Needs at least 1 argument"); return((Image *) NULL); case 1: a = arguments[0]; break; case 2: sx = sy = arguments[0]; a = arguments[1]; break; default: x = nx = arguments[0]; y = ny = arguments[1]; switch ( number_arguments ) { case 3: a = arguments[2]; break; case 4: sx = sy = arguments[2]; a = arguments[3]; break; case 5: sx = arguments[2]; sy = arguments[3]; a = arguments[4]; break; case 6: sx = sy = arguments[2]; a = arguments[3]; nx = arguments[4]; ny = arguments[5]; break; case 7: sx = arguments[2]; sy = arguments[3]; a = arguments[4]; nx = arguments[5]; ny = arguments[6]; break; default: (void) ThrowMagickException(exception,GetMagickModule(),OptionError, "InvalidArgument","%s : '%s'", "distort ScaleTranslateRotate", "Too Many Arguments (7 or less)"); return((Image *) NULL); } break; } a=DegreesToRadians(a); cosine=cos(a); sine=sin(a); coefficients[0]=cosine/sx; coefficients[1]=(-sine)/sy; coefficients[2]=sine/sx; coefficients[3]=cosine/sy; coefficients[4]=x-nx*coefficients[0]-ny*coefficients[2]; coefficients[5]=y-nx*coefficients[1]-ny*coefficients[3]; break; } case ArcDistortion: { /* Arc Distortion Args: arc_width rotate top_edge_radius bottom_edge_radius All but first argument are optional arc_width The angle over which to arc the image side-to-side rotate Angle to rotate image from vertical center top_radius Set top edge of source image at this radius bottom_radius Set bootom edge to this radius (radial scaling) By default, if the radii arguments are nor provided the image radius is calculated so the horizontal center-line is fits the given arc without scaling. The output image size is ALWAYS adjusted to contain the whole image, and an offset is given to position image relative to the 0,0 point of the origin, allowing users to use relative positioning onto larger background (via -flatten). The arguments are converted to these coefficents c0: angle for center of source image c1: angle scale for mapping to source image c2: radius for top of source image c3: radius scale for mapping source image c4: centerline of arc within source image Note the coefficents use a center angle, so asymptotic join is furthest from both sides of the source image. This also means that for arc angles greater than 360 the sides of the image will be trimmed equally. */ if ( number_arguments >= 1 && arguments[0] < MagickEpsilon ) { (void) ThrowMagickException(exception,GetMagickModule(),OptionError, "InvalidArgument","%s : '%s'", "distort Arc", "Arc Angle Too Small"); return((Image *) NULL); } if ( number_arguments >= 3 && arguments[2] < MagickEpsilon ) { (void) ThrowMagickException(exception,GetMagickModule(),OptionError, "InvalidArgument","%s : '%s'", "distort Arc", "Outer Radius Too Small"); return((Image *) NULL); } coefficients[0] = -MagickPI/2.0; if ( number_arguments >= 1 ) coefficients[1] = DegreesToRadians(arguments[0]); else coefficients[1] = MagickPI/2.0; if ( number_arguments >= 2 ) coefficients[0] += DegreesToRadians(arguments[1]); coefficients[0] -= MagickRound(coefficients[0]/(2.0*MagickPI)) *2.0*MagickPI; coefficients[3] = 1.0*image->rows-1; coefficients[2] = 1.0*image->columns/coefficients[1] + coefficients[3]/2.0; if ( number_arguments >= 3 ) { if ( number_arguments >= 4 ) coefficients[3] = arguments[2] - arguments[3]; else coefficients[3] *= arguments[2]/coefficients[2]; coefficients[2] = arguments[2]; } coefficients[4] = (1.0*image->columns-1.0)/2.0; /* Always work out the 'best fit' for Arc Distort (required) */ bestfit = MagickTrue; break; } default: break; } /* Determine the size and offset for a 'bestfit' destination. Usally the four corners of the source image is enough. */ /* default output image bounds, when no 'bestfit' is requested */ geometry.width=image->columns; geometry.height=image->rows; geometry.x=0; geometry.y=0; point.x=0.0; point.y=0.0; if ( bestfit ) { double min_x,max_x,min_y,max_y; /* defines to figure out the bounds of the distorted image */ #define InitalBounds(px,py) \ { \ min_x = max_x = (px); \ min_y = max_y = (py); \ } #define ExpandBounds(px,py) \ { \ if ( (px) < min_x ) min_x = (px); \ if ( (px) > max_x ) max_x = (px); \ if ( (py) < min_y ) min_y = (py); \ if ( (py) > max_y ) max_y = (py); \ } switch (method) { case AffineDistortion: case AffineProjectionDistortion: case ScaleRotateTranslateDistortion: { double inverse[6]; InvertAffineCoefficients(coefficients, inverse); x = image->page.x; y = image->page.y; point.x=inverse[0]*x+inverse[2]*y+inverse[4]; point.y=inverse[1]*x+inverse[3]*y+inverse[5]; InitalBounds( point.x, point.y ); x = image->page.x+image->columns-1; y = image->page.y; point.x=inverse[0]*x+inverse[2]*y+inverse[4]; point.y=inverse[1]*x+inverse[3]*y+inverse[5]; ExpandBounds( point.x, point.y ); x = image->page.x; y = image->page.y+image->rows-1; point.x=inverse[0]*x+inverse[2]*y+inverse[4]; point.y=inverse[1]*x+inverse[3]*y+inverse[5]; ExpandBounds( point.x, point.y ); x = image->page.x+image->columns-1; y = image->page.y+image->rows-1; point.x=inverse[0]*x+inverse[2]*y+inverse[4]; point.y=inverse[1]*x+inverse[3]*y+inverse[5]; ExpandBounds( point.x, point.y ); break; } case PerspectiveDistortion: case PerspectiveProjectionDistortion: { double inverse[8], scale; InvertPerspectiveCoefficients(coefficients, inverse); x = image->page.x; y = image->page.y; scale=inverse[6]*x+inverse[7]*y+1.0; scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale ); point.x=scale*(inverse[0]*x+inverse[1]*y+inverse[2]); point.y=scale*(inverse[3]*x+inverse[4]*y+inverse[5]); InitalBounds( point.x, point.y ); x = image->page.x+image->columns-1; y = image->page.y; scale=inverse[6]*x+inverse[7]*y+1.0; scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale ); point.x=scale*(inverse[0]*x+inverse[1]*y+inverse[2]); point.y=scale*(inverse[3]*x+inverse[4]*y+inverse[5]); ExpandBounds( point.x, point.y ); x = image->page.x; y = image->page.y+image->rows-1; scale=inverse[6]*x+inverse[7]*y+1.0; scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale ); point.x=scale*(inverse[0]*x+inverse[1]*y+inverse[2]); point.y=scale*(inverse[3]*x+inverse[4]*y+inverse[5]); ExpandBounds( point.x, point.y ); x = image->page.x+image->columns-1; y = image->page.y+image->rows-1; scale=inverse[6]*x+inverse[7]*y+1.0; scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale ); point.x=scale*(inverse[0]*x+inverse[1]*y+inverse[2]); point.y=scale*(inverse[3]*x+inverse[4]*y+inverse[5]); ExpandBounds( point.x, point.y ); break; } case ArcDistortion: { double a, ca, sa; /* Forward Map Corners */ a = coefficients[0]-coefficients[1]/2; ca = cos(a); sa = sin(a); point.x = coefficients[2]*ca; point.y = coefficients[2]*sa; InitalBounds( point.x, point.y ); point.x = (coefficients[2]-coefficients[3])*ca; point.y = (coefficients[2]-coefficients[3])*sa; ExpandBounds( point.x, point.y ); a = coefficients[0]+coefficients[1]/2; ca = cos(a); sa = sin(a); point.x = coefficients[2]*ca; point.y = coefficients[2]*sa; ExpandBounds( point.x, point.y ); point.x = (coefficients[2]-coefficients[3])*ca; point.y = (coefficients[2]-coefficients[3])*sa; ExpandBounds( point.x, point.y ); /* orthogonal points along top of arc */ for( a=ceil((coefficients[0]-coefficients[1]/2.0)*2.0/MagickPI) *MagickPI/2.0; a<(coefficients[0]+coefficients[1]/2.0); a+=MagickPI/2.0 ) { ca = cos(a); sa = sin(a); point.x = coefficients[2]*ca; point.y = coefficients[2]*sa; ExpandBounds( point.x, point.y ); } /* Convert the angle_to_width and radius_to_height to appropriate scaling factors, to allow faster processing in the mapping function. */ coefficients[1] = 2.0*MagickPI*image->columns/coefficients[1]; coefficients[3] = 1.0*image->rows/coefficients[3]; break; } case BilinearDistortion: default: /* no bestfit available for this distortion YET */ bestfit = MagickFalse; } /* Set the output image geometry to the 'bestfit' of the image */ if ( bestfit ) { geometry.x=(long) floor(min_x-MagickEpsilon); geometry.y=(long) floor(min_y-MagickEpsilon); geometry.width=(unsigned long) ceil(max_x-geometry.x+1+MagickEpsilon); geometry.height=(unsigned long) ceil(max_y-geometry.y+1+MagickEpsilon); } } /* User provided override of the output geometry */ property=GetImageArtifact(image,"distort:viewport"); if (property != (const char *) NULL) (void) ParseAbsoluteGeometry(property, &geometry); /* Initialize the distort image attributes. */ distort_image=CloneImage(image,geometry.width,geometry.height,MagickTrue, exception); if (distort_image == (Image *) NULL) return((Image *) NULL); if (SetImageStorageClass(distort_image,DirectClass) == MagickFalse) { InheritException(exception,&distort_image->exception); distort_image=DestroyImage(distort_image); return((Image *) NULL); } distort_image->page.x=geometry.x; distort_image->page.y=geometry.y; if (distort_image->background_color.opacity != OpaqueOpacity) distort_image->matte=MagickTrue; /* Open Image views as needed. */ resample_filter=AcquireResampleFilter(image,exception); GetMagickPixelPacket(distort_image,&pixel); distort_view=OpenCacheView(distort_image); /* Define constant scaling vectors for Affine Distortions */ switch (method) { case AffineDistortion: case AffineProjectionDistortion: case ScaleRotateTranslateDistortion: ScaleResampleFilter( resample_filter, coefficients[0], coefficients[2], coefficients[1], coefficients[3] ); break; default: break; } /* Initialize default pixel validity * negative: pixel is invalid output 'matte_color' * 0.0 to 1.0: antialiased, mix with resample output * 1.0 or greater: use resampled output. */ validity = 1.0; GetMagickPixelPacket(distort_image,&invalid); SetMagickPixelPacket(distort_image, &distort_image->matte_color, (IndexPacket *) NULL, &invalid); if (distort_image->colorspace == CMYKColorspace) ConvertRGBToCMYK(&invalid); /* what about other color spaces? */ /* Sample the source image to each pixel in the distort image. */ for (j=0; j < (long) distort_image->rows; j++) { q=SetCacheView(distort_view,0,j,distort_image->columns,1); if (q == (PixelPacket *) NULL) break; indexes=GetCacheViewIndexes(distort_view); y = j+geometry.y; for (i=0; i < (long) distort_image->columns; i++) { x = i+geometry.x; switch (method) { case AffineDistortion: case AffineProjectionDistortion: case ScaleRotateTranslateDistortion: { point.x=coefficients[0]*x+coefficients[2]*y+coefficients[4]; point.y=coefficients[1]*x+coefficients[3]*y+coefficients[5]; /* Affine partical derivitives are constant -- set above */ break; } case BilinearDistortion: { point.x=coefficients[0]*x+coefficients[1]*y+coefficients[2]*x*y+ coefficients[3]; point.y=coefficients[4]*x+coefficients[5]*y+coefficients[6]*x*y+ coefficients[7]; /* Bilinear partical derivitives of scaling vectors */ ScaleResampleFilter( resample_filter, coefficients[0] + coefficients[2]*y, coefficients[1] + coefficients[2]*x, coefficients[4] + coefficients[6]*y, coefficients[5] + coefficients[6]*x ); break; } case PerspectiveDistortion: case PerspectiveProjectionDistortion: { double p,q,r,abs_r,abs_c6,abs_c7,scale; /* perspective is a ratio of affines */ p=coefficients[0]*x+coefficients[1]*y+coefficients[2]; q=coefficients[3]*x+coefficients[4]*y+coefficients[5]; r=coefficients[6]*x+coefficients[7]*y+1.0; /* Pixel Validity -- is it a 'sky' or 'ground' pixel */ validity = (r*coefficients[8] < 0.0) ? 0.0 : 1.0; /* Determine horizon anti-aliase blending */ abs_r = fabs(r)*2; abs_c6 = fabs(coefficients[6]); abs_c7 = fabs(coefficients[7]); if ( abs_c6 > abs_c7 ) { if ( abs_r < abs_c6 ) validity = 0.5 - coefficients[8]*r/coefficients[6]; } else if ( abs_r < abs_c7 ) validity = .5 - coefficients[8]*r/coefficients[7]; /* Perspective Sampling Point (if valid) */ if ( validity > 0.0 ) { scale = 1.0/r; point.x = p*scale; point.y = q*scale; /* Perspective Partial Derivatives or Scaling Vectors */ scale *= scale; ScaleResampleFilter( resample_filter, (r*coefficients[0] - p*coefficients[6])*scale, (r*coefficients[1] - p*coefficients[7])*scale, (r*coefficients[3] - q*coefficients[6])*scale, (r*coefficients[4] - q*coefficients[7])*scale ); } break; } case ArcDistortion: { double radius = sqrt((double) x*x+y*y); point.x = (atan2((double)y,(double)x) - coefficients[0])/(2*MagickPI); point.x -= MagickRound(point.x); point.x = point.x*coefficients[1] + coefficients[4]; point.y = (coefficients[2] - radius) * coefficients[3]; /* Polar Distortion Partial Derivatives or Scaling Vectors We give the deritives of du/dr, dv/dr and du/da, dv/da rather than provide the complex du/dx,dv/dx and du/dy,dv/dy. The result will be the same, but it is simplier to calculate. */ if ( radius > MagickEpsilon ) ScaleResampleFilter( resample_filter, coefficients[1]/(2*MagickPI) / radius, 0, 0, coefficients[3] ); else ScaleResampleFilter( resample_filter, MagickHuge, 0, 0, coefficients[3] ); break; } default: { /* Noop distortion (failsafe). */ point.x=(double) i; point.y=(double) j; break; } } if ( bestfit && method != ArcDistortion ) { point.x -= image->page.x; point.y -= image->page.y; } if ( validity <= 0.0 ) { /* result of distortion is an invalid pixel - don't resample */ SetPixelPacket(distort_image,&invalid,q,indexes); } else { /* resample the source image to find its correct color */ pixel=ResamplePixelColor(resample_filter,point.x,point.y); /* if validity between 0.0 and 1.0 mix result with invalid pixel */ if ( validity < 1.0 ) { /* Do a blend of sample color and invalid pixel */ /* should this be a 'Blend', or an 'Over' compose */ MagickPixelCompositeBlend(&pixel, validity, &invalid, (1.0-validity), &pixel); } SetPixelPacket(distort_image,&pixel,q,indexes); } q++; indexes++; } if (SyncImagePixels(distort_image) == MagickFalse) break; if ((image->progress_monitor != (MagickProgressMonitor) NULL) && (QuantumTick(y,image->rows) != MagickFalse)) { status=image->progress_monitor(DistortImageTag,y,image->rows, image->client_data); if (status == MagickFalse) break; } } distort_view=CloseCacheView(distort_view); resample_filter=DestroyResampleFilter(resample_filter); /* Arc does not return an offset unless 'bestfit' is in effect */ if ( method == ArcDistortion && !bestfit && property==(const char *)NULL ) { distort_image->page.x = 0; distort_image->page.y = 0; } return(distort_image); }
MagickExport Image *ConnectedComponentsImage(const Image *image, const size_t connectivity,ExceptionInfo *exception) { #define ConnectedComponentsImageTag "ConnectedComponents/Image" CacheView *image_view, *component_view; const char *artifact; double area_threshold; Image *component_image; MagickBooleanType status; MagickOffsetType progress; MatrixInfo *equivalences; size_t size; ssize_t n, y; /* Initialize connected components image attributes. */ assert(image != (Image *) NULL); assert(image->signature == MagickSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickSignature); component_image=CloneImage(image,image->columns,image->rows,MagickTrue, exception); if (component_image == (Image *) NULL) return((Image *) NULL); component_image->depth=MAGICKCORE_QUANTUM_DEPTH; component_image->colorspace=GRAYColorspace; if (SetImageStorageClass(component_image,DirectClass) == MagickFalse) { component_image=DestroyImage(component_image); return((Image *) NULL); } /* Initialize connected components equivalences. */ size=image->columns*image->rows; if (image->columns != (size/image->rows)) { component_image=DestroyImage(component_image); ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); } equivalences=AcquireMatrixInfo(size,1,sizeof(ssize_t),exception); if (equivalences == (MatrixInfo *) NULL) { component_image=DestroyImage(component_image); return((Image *) NULL); } for (n=0; n < (ssize_t) (image->columns*image->rows); n++) (void) SetMatrixElement(equivalences,n,0,&n); /* Find connected components. */ status=MagickTrue; progress=0; image_view=AcquireVirtualCacheView(image,exception); for (n=0; n < (ssize_t) (connectivity > 4 ? 4 : 2); n++) { ssize_t connect4[2][2] = { { -1, 0 }, { 0, -1 } }, connect8[4][2] = { { -1, -1 }, { -1, 0 }, { -1, 1 }, { 0, -1 } }, dx, dy; if (status == MagickFalse) continue; dy=connectivity > 4 ? connect8[n][0] : connect4[n][0]; dx=connectivity > 4 ? connect8[n][1] : connect4[n][1]; for (y=0; y < (ssize_t) image->rows; y++) { register const PixelPacket *restrict p; register ssize_t x; if (status == MagickFalse) continue; p=GetCacheViewVirtualPixels(image_view,0,y-1,image->columns,3,exception); if (p == (const PixelPacket *) NULL) { status=MagickFalse; continue; } p+=image->columns; for (x=0; x < (ssize_t) image->columns; x++) { ssize_t neighbor_offset, object, offset, ox, oy, root; /* Is neighbor an authentic pixel and a different color than the pixel? */ neighbor_offset=dy*image->columns+dx; if (((x+dx) < 0) || ((x+dx) >= (ssize_t) image->columns) || ((y+dy) < 0) || ((y+dy) >= (ssize_t) image->rows) || (IsColorSimilar(image,p,p+neighbor_offset) == MagickFalse)) { p++; continue; } /* Resolve this equivalence. */ offset=y*image->columns+x; ox=offset; status=GetMatrixElement(equivalences,ox,0,&object); while (object != ox) { ox=object; status=GetMatrixElement(equivalences,ox,0,&object); } oy=offset+neighbor_offset; status=GetMatrixElement(equivalences,oy,0,&object); while (object != oy) { oy=object; status=GetMatrixElement(equivalences,oy,0,&object); } if (ox < oy) { status=SetMatrixElement(equivalences,oy,0,&ox); root=ox; } else { status=SetMatrixElement(equivalences,ox,0,&oy); root=oy; } ox=offset; status=GetMatrixElement(equivalences,ox,0,&object); while (object != root) { status=GetMatrixElement(equivalences,ox,0,&object); status=SetMatrixElement(equivalences,ox,0,&root); } oy=offset+neighbor_offset; status=GetMatrixElement(equivalences,oy,0,&object); while (object != root) { status=GetMatrixElement(equivalences,oy,0,&object); status=SetMatrixElement(equivalences,oy,0,&root); } status=SetMatrixElement(equivalences,y*image->columns+x,0,&root); p++; } } } image_view=DestroyCacheView(image_view); /* Label connected components. */ n=0; component_view=AcquireAuthenticCacheView(component_image,exception); for (y=0; y < (ssize_t) component_image->rows; y++) { register PixelPacket *restrict q; register ssize_t x; if (status == MagickFalse) continue; q=QueueCacheViewAuthenticPixels(component_view,0,y,component_image->columns, 1,exception); if (q == (PixelPacket *) NULL) { status=MagickFalse; continue; } for (x=0; x < (ssize_t) component_image->columns; x++) { ssize_t id, offset; offset=y*image->columns+x; status=GetMatrixElement(equivalences,offset,0,&id); if (id == offset) { id=n++; status=SetMatrixElement(equivalences,offset,0,&id); } else { status=GetMatrixElement(equivalences,id,0,&id); status=SetMatrixElement(equivalences,offset,0,&id); } q->red=(Quantum) (id > (ssize_t) QuantumRange ? (ssize_t) QuantumRange : id); q->green=q->red; q->blue=q->red; q++; } if (SyncCacheViewAuthenticPixels(component_view,exception) == MagickFalse) status=MagickFalse; if (image->progress_monitor != (MagickProgressMonitor) NULL) { MagickBooleanType proceed; proceed=SetImageProgress(image,ConnectedComponentsImageTag,progress++, image->rows); if (proceed == MagickFalse) status=MagickFalse; } } component_view=DestroyCacheView(component_view); equivalences=DestroyMatrixInfo(equivalences); if (n > (ssize_t) QuantumRange) { component_image=DestroyImage(component_image); ThrowImageException(ResourceLimitError,"TooManyObjects"); } artifact=GetImageArtifact(image,"connected-components:area-threshold"); area_threshold=0.0; if (artifact != (const char *) NULL) area_threshold=StringToDouble(artifact,(char **) NULL); if (area_threshold > 0.0) status=MergeConnectedComponents(component_image,(size_t) n,area_threshold, exception); artifact=GetImageArtifact(image,"connected-components:verbose"); if (IsMagickTrue(artifact) != MagickFalse) status=StatisticsComponentsStatistics(image,component_image,(size_t) n, exception); if (status == MagickFalse) component_image=DestroyImage(component_image); return(component_image); }
/* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % W r i t e J P 2 I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % WriteJP2Image() writes an image in the JPEG 2000 image format. % % JP2 support originally written by Nathan Brown, [email protected] % % The format of the WriteJP2Image method is: % % MagickBooleanType WriteJP2Image(const ImageInfo *image_info, % Image *image,ExceptionInfo *exception) % % A description of each parameter follows. % % o image_info: the image info. % % o image: The image. % % o exception: return any errors or warnings in this structure. % */ static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image, ExceptionInfo *exception) { char *key, magick[MaxTextExtent], *options; const char *option; jas_image_cmptparm_t component_info[4]; jas_image_t *jp2_image; jas_matrix_t *pixels[4]; jas_stream_t *jp2_stream; MagickBooleanType status; QuantumAny range; register const Quantum *p; register ssize_t i, x; size_t number_components; ssize_t format, y; /* Open image file. */ assert(image_info != (const ImageInfo *) NULL); assert(image_info->signature == MagickSignature); assert(image != (Image *) NULL); assert(image->signature == MagickSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickSignature); status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); if (status == MagickFalse) return(status); /* Initialize JPEG 2000 API. */ if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) (void) TransformImageColorspace(image,sRGBColorspace,exception); jp2_stream=JP2StreamManager(image); if (jp2_stream == (jas_stream_t *) NULL) ThrowWriterException(DelegateError,"UnableToManageJP2Stream"); number_components=image->alpha_trait ? 4UL : 3UL; if (IsGrayColorspace(image->colorspace) != MagickFalse) number_components=1; if ((image->columns != (unsigned int) image->columns) || (image->rows != (unsigned int) image->rows)) ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit"); (void) ResetMagickMemory(&component_info,0,sizeof(component_info)); for (i=0; i < (ssize_t) number_components; i++) { component_info[i].tlx=0; component_info[i].tly=0; component_info[i].hstep=1; component_info[i].vstep=1; component_info[i].width=(unsigned int) image->columns; component_info[i].height=(unsigned int) image->rows; component_info[i].prec=(int) MagickMax(MagickMin(image->depth,16),2); component_info[i].sgnd=MagickFalse; } jp2_image=jas_image_create((int) number_components,component_info, JAS_CLRSPC_UNKNOWN); if (jp2_image == (jas_image_t *) NULL) ThrowWriterException(DelegateError,"UnableToCreateImage"); switch (image->colorspace) { case RGBColorspace: case sRGBColorspace: { /* RGB colorspace. */ jas_image_setclrspc(jp2_image,JAS_CLRSPC_SRGB); jas_image_setcmpttype(jp2_image,0, (jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R)); jas_image_setcmpttype(jp2_image,1, (jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G)); jas_image_setcmpttype(jp2_image,2, (jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B)); if (number_components == 4) jas_image_setcmpttype(jp2_image,3,JAS_IMAGE_CT_OPACITY); break; } case GRAYColorspace: { /* Grayscale colorspace. */ jas_image_setclrspc(jp2_image,JAS_CLRSPC_SGRAY); jas_image_setcmpttype(jp2_image,0, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_GRAY_Y)); break; } case YCbCrColorspace: { /* YCbCr colorspace. */ jas_image_setclrspc(jp2_image,JAS_CLRSPC_SYCBCR); jas_image_setcmpttype(jp2_image,0,(jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(0)); jas_image_setcmpttype(jp2_image,1,(jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(1)); jas_image_setcmpttype(jp2_image,2,(jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(2)); if (number_components == 4) jas_image_setcmpttype(jp2_image,3,JAS_IMAGE_CT_OPACITY); break; } case XYZColorspace: { /* XYZ colorspace. */ jas_image_setclrspc(jp2_image,JAS_CLRSPC_CIEXYZ); jas_image_setcmpttype(jp2_image,0,(jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(0)); jas_image_setcmpttype(jp2_image,1,(jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(1)); jas_image_setcmpttype(jp2_image,2,(jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(2)); if (number_components == 4) jas_image_setcmpttype(jp2_image,3,JAS_IMAGE_CT_OPACITY); break; } case LabColorspace: { /* Lab colorspace. */ jas_image_setclrspc(jp2_image,JAS_CLRSPC_CIELAB); jas_image_setcmpttype(jp2_image,0,(jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(0)); jas_image_setcmpttype(jp2_image,1,(jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(1)); jas_image_setcmpttype(jp2_image,2,(jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(2)); if (number_components == 4) jas_image_setcmpttype(jp2_image,3,JAS_IMAGE_CT_OPACITY); break; } default: { /* Unknow. */ jas_image_setclrspc(jp2_image,JAS_CLRSPC_UNKNOWN); jas_image_setcmpttype(jp2_image,0,(jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(0)); jas_image_setcmpttype(jp2_image,1,(jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(1)); jas_image_setcmpttype(jp2_image,2,(jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(2)); if (number_components == 4) jas_image_setcmpttype(jp2_image,3,JAS_IMAGE_CT_OPACITY); break; } } /* Convert to JPEG 2000 pixels. */ for (i=0; i < (ssize_t) number_components; i++) { pixels[i]=jas_matrix_create(1,(int) image->columns); if (pixels[i] == (jas_matrix_t *) NULL) { for (x=0; x < i; x++) jas_matrix_destroy(pixels[x]); jas_image_destroy(jp2_image); ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); } } range=GetQuantumRange((size_t) component_info[0].prec); for (y=0; y < (ssize_t) image->rows; y++) { p=GetVirtualPixels(image,0,y,image->columns,1,exception); if (p == (const Quantum *) NULL) break; for (x=0; x < (ssize_t) image->columns; x++) { if (number_components == 1) jas_matrix_setv(pixels[0],x,(jas_seqent_t) ScaleQuantumToAny( ClampToQuantum(GetPixelLuma(image,p)),range)); else { jas_matrix_setv(pixels[0],x,(jas_seqent_t) ScaleQuantumToAny( GetPixelRed(image,p),range)); jas_matrix_setv(pixels[1],x,(jas_seqent_t) ScaleQuantumToAny( GetPixelGreen(image,p),range)); jas_matrix_setv(pixels[2],x,(jas_seqent_t) ScaleQuantumToAny( GetPixelBlue(image,p),range)); if (number_components > 3) jas_matrix_setv(pixels[3],x,(jas_seqent_t) ScaleQuantumToAny( GetPixelAlpha(image,p),range)); } p+=GetPixelChannels(image); } for (i=0; i < (ssize_t) number_components; i++) (void) jas_image_writecmpt(jp2_image,(short) i,0,(unsigned int) y, (unsigned int) image->columns,1,pixels[i]); status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, image->rows); if (status == MagickFalse) break; } (void) CopyMagickString(magick,image_info->magick,MaxTextExtent); if (LocaleCompare(magick,"J2C") == 0) (void) CopyMagickString(magick,"JPC",MaxTextExtent); LocaleLower(magick); format=jas_image_strtofmt(magick); options=(char *) NULL; ResetImageOptionIterator(image_info); key=GetNextImageOption(image_info); for ( ; key != (char *) NULL; key=GetNextImageOption(image_info)) { option=GetImageArtifact(image,key); if (option == (const char *) NULL) continue; if (LocaleNCompare(key,"jp2:",4) == 0) { (void) ConcatenateString(&options,key+4); if (*option != '\0') { (void) ConcatenateString(&options,"="); (void) ConcatenateString(&options,option); } (void) ConcatenateString(&options," "); } } option=GetImageArtifact(image,"jp2:rate"); if ((option == (const char *) NULL) && (image_info->compression != LosslessJPEGCompression) && (image->quality != UndefinedCompressionQuality) && ((double) image->quality <= 99.5) && ((image->rows*image->columns) > 2500)) { char option[MaxTextExtent]; double alpha, header_size, number_pixels, rate, target_size; alpha=115.0-image->quality; rate=100.0/(alpha*alpha); header_size=550.0; header_size+=(number_components-1)*142; number_pixels=(double) image->rows*image->columns*number_components* (GetImageQuantumDepth(image,MagickTrue)/8); target_size=(number_pixels*rate)+header_size; rate=target_size/number_pixels; (void) FormatLocaleString(option,MaxTextExtent,"rate=%g",rate); (void) ConcatenateString(&options,option); } status=jas_image_encode(jp2_image,jp2_stream,format,options) != 0 ? MagickTrue : MagickFalse; if (options != (char *) NULL) options=DestroyString(options); (void) jas_stream_close(jp2_stream); for (i=0; i < (ssize_t) number_components; i++) jas_matrix_destroy(pixels[i]); jas_image_destroy(jp2_image); if (status != MagickFalse) ThrowWriterException(DelegateError,"UnableToEncodeImageFile"); return(MagickTrue); }
MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file, const MagickBooleanType verbose) { #define IdentifyFormat " %s:\n min: " QuantumFormat \ " (%g)\n max: " QuantumFormat " (%g)\n" \ " mean: %g (%g)\n standard deviation: %g (%g)\n" \ " kurtosis: %g\n skewness: %g\n" char color[MaxTextExtent], format[MaxTextExtent], key[MaxTextExtent]; ColorspaceType colorspace; const char *artifact, *name, *property, *registry, *value; const MagickInfo *magick_info; const PixelPacket *pixels; double elapsed_time, user_time; ExceptionInfo *exception; ImageType type; long y; MagickBooleanType ping; register long i, x; unsigned long scale; assert(image != (Image *) NULL); assert(image->signature == MagickSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); if (file == (FILE *) NULL) file=stdout; *format='\0'; elapsed_time=GetElapsedTime(&image->timer); user_time=GetUserTime(&image->timer); GetTimerInfo(&image->timer); if (verbose == MagickFalse) { /* Display summary info about the image. */ if (*image->magick_filename != '\0') if (LocaleCompare(image->magick_filename,image->filename) != 0) (void) fprintf(file,"%s=>",image->magick_filename); if ((GetPreviousImageInList(image) == (Image *) NULL) && (GetNextImageInList(image) == (Image *) NULL) && (image->scene == 0)) (void) fprintf(file,"%s ",image->filename); else (void) fprintf(file,"%s[%lu] ",image->filename,image->scene); (void) fprintf(file,"%s ",image->magick); if ((image->magick_columns != 0) || (image->magick_rows != 0)) if ((image->magick_columns != image->columns) || (image->magick_rows != image->rows)) (void) fprintf(file,"%lux%lu=>",image->magick_columns, image->magick_rows); (void) fprintf(file,"%lux%lu ",image->columns,image->rows); if ((image->page.width != 0) || (image->page.height != 0) || (image->page.x != 0) || (image->page.y != 0)) (void) fprintf(file,"%lux%lu%+ld%+ld ",image->page.width, image->page.height,image->page.x,image->page.y); (void) fprintf(file,"%lu-bit ",image->depth); if (image->type != UndefinedType) (void) fprintf(file,"%s ",MagickOptionToMnemonic(MagickTypeOptions, (long) image->type)); if (image->storage_class == DirectClass) { (void) fprintf(file,"DirectClass "); if (image->total_colors != 0) { (void) FormatMagickSize(image->total_colors,MagickFalse,format); (void) fprintf(file,"%s ",format); } } else if (image->total_colors <= image->colors) (void) fprintf(file,"PseudoClass %luc ",image->colors); else (void) fprintf(file,"PseudoClass %lu=>%luc ",image->total_colors, image->colors); if (image->error.mean_error_per_pixel != 0.0) (void) fprintf(file,"%ld/%f/%fdb ",(long) (image->error.mean_error_per_pixel+0.5), image->error.normalized_mean_error, image->error.normalized_maximum_error); if (GetBlobSize(image) != 0) { (void) FormatMagickSize(GetBlobSize(image),MagickFalse,format); (void) fprintf(file,"%s ",format); } (void) fprintf(file,"%0.3fu %ld:%02ld.%03ld",user_time,(long) (elapsed_time/60.0),(long) floor(fmod(elapsed_time,60.0)), (long) (1000.0*(elapsed_time-floor(elapsed_time)))); (void) fprintf(file,"\n"); (void) fflush(file); return(ferror(file) != 0 ? MagickFalse : MagickTrue); } /* Display verbose info about the image. */ exception=AcquireExceptionInfo(); pixels=GetVirtualPixels(image,0,0,1,1,exception); exception=DestroyExceptionInfo(exception); ping=pixels == (const PixelPacket *) NULL ? MagickTrue : MagickFalse; type=GetImageType(image,&image->exception); (void) SignatureImage(image); (void) fprintf(file,"Image: %s\n",image->filename); if (*image->magick_filename != '\0') if (LocaleCompare(image->magick_filename,image->filename) != 0) { char filename[MaxTextExtent]; GetPathComponent(image->magick_filename,TailPath,filename); (void) fprintf(file," Base filename: %s\n",filename); } magick_info=GetMagickInfo(image->magick,&image->exception); if ((magick_info == (const MagickInfo *) NULL) || (*GetMagickDescription(magick_info) == '\0')) (void) fprintf(file," Format: %s\n",image->magick); else (void) fprintf(file," Format: %s (%s)\n",image->magick, GetMagickDescription(magick_info)); (void) fprintf(file," Class: %s\n",MagickOptionToMnemonic(MagickClassOptions, (long) image->storage_class)); (void) fprintf(file," Geometry: %lux%lu%+ld%+ld\n",image->columns, image->rows,image->tile_offset.x,image->tile_offset.y); if ((image->magick_columns != 0) || (image->magick_rows != 0)) if ((image->magick_columns != image->columns) || (image->magick_rows != image->rows)) (void) fprintf(file," Base geometry: %lux%lu\n",image->magick_columns, image->magick_rows); if ((image->x_resolution != 0.0) && (image->y_resolution != 0.0)) { (void) fprintf(file," Resolution: %gx%g\n",image->x_resolution, image->y_resolution); (void) fprintf(file," Print size: %gx%g\n",(double) image->columns/ image->x_resolution,(double) image->rows/image->y_resolution); } (void) fprintf(file," Units: %s\n",MagickOptionToMnemonic( MagickResolutionOptions,(long) image->units)); (void) fprintf(file," Type: %s\n",MagickOptionToMnemonic(MagickTypeOptions, (long) type)); if (image->type != UndefinedType) (void) fprintf(file," Base type: %s\n",MagickOptionToMnemonic( MagickTypeOptions,(long) image->type)); (void) fprintf(file," Endianess: %s\n",MagickOptionToMnemonic( MagickEndianOptions,(long) image->endian)); /* Detail channel depth and extrema. */ (void) fprintf(file," Colorspace: %s\n",MagickOptionToMnemonic( MagickColorspaceOptions,(long) image->colorspace)); if (ping == MagickFalse) { ChannelStatistics *channel_statistics; unsigned long depth; depth=GetImageDepth(image,&image->exception); if (image->depth == depth) (void) fprintf(file," Depth: %lu-bit\n",image->depth); else (void) fprintf(file," Depth: %lu/%lu-bit\n",image->depth,depth); channel_statistics=GetImageChannelStatistics(image,&image->exception); (void) fprintf(file," Channel depth:\n"); colorspace=image->colorspace; if (IsGrayImage(image,&image->exception) != MagickFalse) colorspace=GRAYColorspace; switch (colorspace) { case RGBColorspace: default: { (void) fprintf(file," red: %lu-bit\n", channel_statistics[RedChannel].depth); (void) fprintf(file," green: %lu-bit\n", channel_statistics[GreenChannel].depth); (void) fprintf(file," blue: %lu-bit\n", channel_statistics[BlueChannel].depth); if (image->matte != MagickFalse) (void) fprintf(file," alpha: %lu-bit\n", channel_statistics[OpacityChannel].depth); break; } case CMYKColorspace: { (void) fprintf(file," cyan: %lu-bit\n", channel_statistics[CyanChannel].depth); (void) fprintf(file," magenta: %lu-bit\n", channel_statistics[MagentaChannel].depth); (void) fprintf(file," yellow: %lu-bit\n", channel_statistics[YellowChannel].depth); (void) fprintf(file," black: %lu-bit\n", channel_statistics[BlackChannel].depth); if (image->matte != MagickFalse) (void) fprintf(file," alpha: %lu-bit\n", channel_statistics[OpacityChannel].depth); break; } case GRAYColorspace: { (void) fprintf(file," gray: %lu-bit\n", channel_statistics[GrayChannel].depth); if (image->matte != MagickFalse) (void) fprintf(file," alpha: %lu-bit\n", channel_statistics[OpacityChannel].depth); break; } } scale=1; if (image->depth <= MAGICKCORE_QUANTUM_DEPTH) scale=QuantumRange/((unsigned long) QuantumRange >> ((unsigned long) MAGICKCORE_QUANTUM_DEPTH-image->depth)); (void) fprintf(file," Channel statistics:\n"); switch (colorspace) { case RGBColorspace: default: { (void) fprintf(file,IdentifyFormat,"red",(Quantum) (channel_statistics[RedChannel].minima/scale),(double) channel_statistics[RedChannel].minima/(double) QuantumRange, (Quantum) (channel_statistics[RedChannel].maxima/scale),(double) channel_statistics[RedChannel].maxima/(double) QuantumRange, channel_statistics[RedChannel].mean/(double) scale, channel_statistics[RedChannel].mean/(double) QuantumRange, channel_statistics[RedChannel].standard_deviation/(double) scale, channel_statistics[RedChannel].standard_deviation/(double) QuantumRange,channel_statistics[RedChannel].kurtosis, channel_statistics[RedChannel].skewness); (void) fprintf(file,IdentifyFormat,"green",(Quantum) (channel_statistics[GreenChannel].minima/scale),(double) channel_statistics[GreenChannel].minima/(double) QuantumRange, (Quantum) (channel_statistics[GreenChannel].maxima/scale),(double) channel_statistics[GreenChannel].maxima/(double) QuantumRange, channel_statistics[GreenChannel].mean/(double) scale, channel_statistics[GreenChannel].mean/(double) QuantumRange, channel_statistics[GreenChannel].standard_deviation/(double) scale, channel_statistics[GreenChannel].standard_deviation/(double) QuantumRange, channel_statistics[GreenChannel].kurtosis, channel_statistics[GreenChannel].skewness); (void) fprintf(file,IdentifyFormat,"blue",(Quantum) (channel_statistics[BlueChannel].minima/scale),(double) channel_statistics[BlueChannel].minima/(double) QuantumRange, (Quantum) (channel_statistics[BlueChannel].maxima/scale),(double) channel_statistics[BlueChannel].maxima/(double) QuantumRange, channel_statistics[BlueChannel].mean/(double) scale, channel_statistics[BlueChannel].mean/(double) QuantumRange, channel_statistics[BlueChannel].standard_deviation/(double) scale, channel_statistics[BlueChannel].standard_deviation/(double) QuantumRange,channel_statistics[BlueChannel].kurtosis, channel_statistics[BlueChannel].skewness); break; } case CMYKColorspace: { (void) fprintf(file,IdentifyFormat,"cyan",(Quantum) (channel_statistics[CyanChannel].minima/scale),(double) channel_statistics[CyanChannel].minima/(double) QuantumRange, (Quantum) (channel_statistics[CyanChannel].maxima/scale),(double) channel_statistics[CyanChannel].maxima/(double) QuantumRange, channel_statistics[CyanChannel].mean/(double) scale, channel_statistics[CyanChannel].mean/(double) QuantumRange, channel_statistics[CyanChannel].standard_deviation/(double) scale, channel_statistics[CyanChannel].standard_deviation/(double) QuantumRange,channel_statistics[CyanChannel].kurtosis, channel_statistics[CyanChannel].skewness); (void) fprintf(file,IdentifyFormat,"magenta",(Quantum) (channel_statistics[MagentaChannel].minima/scale),(double) channel_statistics[MagentaChannel].minima/(double) QuantumRange, (Quantum) (channel_statistics[MagentaChannel].maxima/scale),(double) channel_statistics[MagentaChannel].maxima/(double) QuantumRange, channel_statistics[MagentaChannel].mean/(double) scale, channel_statistics[MagentaChannel].mean/(double) QuantumRange, channel_statistics[MagentaChannel].standard_deviation/(double) scale,channel_statistics[MagentaChannel].standard_deviation/(double) QuantumRange,channel_statistics[MagentaChannel].kurtosis, channel_statistics[MagentaChannel].skewness); (void) fprintf(file,IdentifyFormat,"yellow",(Quantum) (channel_statistics[YellowChannel].minima/scale),(double) channel_statistics[YellowChannel].minima/(double) QuantumRange, (Quantum) (channel_statistics[YellowChannel].maxima/scale),(double) channel_statistics[YellowChannel].maxima/(double) QuantumRange, channel_statistics[YellowChannel].mean/(double) scale, channel_statistics[YellowChannel].mean/(double) QuantumRange, channel_statistics[YellowChannel].standard_deviation/(double) scale, channel_statistics[YellowChannel].standard_deviation/(double) QuantumRange,channel_statistics[YellowChannel].kurtosis, channel_statistics[YellowChannel].skewness); (void) fprintf(file,IdentifyFormat,"black",(Quantum) (channel_statistics[BlackChannel].minima/scale),(double) channel_statistics[BlackChannel].minima/(double) QuantumRange, (Quantum) (channel_statistics[BlackChannel].maxima/scale),(double) channel_statistics[BlackChannel].maxima/(double) QuantumRange, channel_statistics[BlackChannel].mean/(double) scale, channel_statistics[BlackChannel].mean/(double) QuantumRange, channel_statistics[BlackChannel].standard_deviation/(double) scale, channel_statistics[BlackChannel].standard_deviation/(double) QuantumRange,channel_statistics[BlackChannel].kurtosis, channel_statistics[BlackChannel].skewness); break; } case GRAYColorspace: { (void) fprintf(file,IdentifyFormat,"gray",(Quantum) (channel_statistics[GrayChannel].minima/scale),(double) channel_statistics[GrayChannel].minima/(double) QuantumRange, (Quantum) (channel_statistics[GrayChannel].maxima/scale),(double) channel_statistics[GrayChannel].maxima/(double) QuantumRange, channel_statistics[GrayChannel].mean/(double) scale, channel_statistics[GrayChannel].mean/(double) QuantumRange, channel_statistics[GrayChannel].standard_deviation/(double) scale, channel_statistics[GrayChannel].standard_deviation/(double) QuantumRange,channel_statistics[GrayChannel].kurtosis, channel_statistics[GrayChannel].skewness); break; } } if (image->matte != MagickFalse) (void) fprintf(file,IdentifyFormat,"alpha",(Quantum) ((QuantumRange-channel_statistics[AlphaChannel].maxima)/scale), (double) (QuantumRange-channel_statistics[AlphaChannel].maxima)/ (double) QuantumRange, (Quantum) ((QuantumRange- channel_statistics[AlphaChannel].minima)/scale),(double) (QuantumRange-channel_statistics[AlphaChannel].minima)/(double) QuantumRange,(QuantumRange-channel_statistics[AlphaChannel].mean)/ (double) scale,(QuantumRange-channel_statistics[AlphaChannel].mean)/ (double) QuantumRange, channel_statistics[AlphaChannel].standard_deviation/(double) scale, channel_statistics[AlphaChannel].standard_deviation/(double) QuantumRange,channel_statistics[AlphaChannel].kurtosis, channel_statistics[AlphaChannel].skewness); if (colorspace != GRAYColorspace) { (void) fprintf(file," Image statistics:\n"); (void) fprintf(file,IdentifyFormat,"Overall",(Quantum) (channel_statistics[AllChannels].minima/scale),(double) channel_statistics[AllChannels].minima/(double) QuantumRange, (Quantum) (channel_statistics[AllChannels].maxima/scale),(double) channel_statistics[AllChannels].maxima/(double) QuantumRange, channel_statistics[AllChannels].mean/(double) scale, channel_statistics[AllChannels].mean/(double) QuantumRange, channel_statistics[AllChannels].standard_deviation/(double) scale, channel_statistics[AllChannels].standard_deviation/(double) QuantumRange,channel_statistics[AllChannels].kurtosis, channel_statistics[AllChannels].skewness); } channel_statistics=(ChannelStatistics *) RelinquishMagickMemory( channel_statistics); if (image->colorspace == CMYKColorspace) (void) fprintf(file," Total ink density: %.0f%%\n",100.0* GetImageTotalInkDensity(image)/(double) QuantumRange); x=0; if (image->matte != MagickFalse) { register const IndexPacket *indexes; register const PixelPacket *p; p=(PixelPacket *) NULL; indexes=(IndexPacket *) NULL; for (y=0; y < (long) image->rows; y++) { p=GetVirtualPixels(image,0,y,image->columns,1,exception); if (p == (const PixelPacket *) NULL) break; indexes=GetVirtualIndexQueue(image); for (x=0; x < (long) image->columns; x++) { if (p->opacity == (Quantum) TransparentOpacity) break; p++; } if (x < (long) image->columns) break; } if ((x < (long) image->columns) || (y < (long) image->rows)) { char tuple[MaxTextExtent]; MagickPixelPacket pixel; GetMagickPixelPacket(image,&pixel); SetMagickPixelPacket(image,p,indexes+x,&pixel); (void) QueryMagickColorname(image,&pixel,SVGCompliance,tuple, &image->exception); (void) fprintf(file," Alpha: %s ",tuple); GetColorTuple(&pixel,MagickTrue,tuple); (void) fprintf(file," %s\n",tuple); } } if (ping == MagickFalse) { artifact=GetImageArtifact(image,"identify:unique"); if ((artifact != (const char *) NULL) && (IsMagickTrue(artifact) != MagickFalse)) (void) fprintf(file," Colors: %lu\n",GetNumberColors(image, (FILE *) NULL,&image->exception)); if (IsHistogramImage(image,&image->exception) != MagickFalse) { (void) fprintf(file," Histogram:\n"); (void) GetNumberColors(image,file,&image->exception); } } }