00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048 #include "magick/studio.h"
00049 #include "magick/artifact.h"
00050 #include "magick/blob-private.h"
00051 #include "magick/cache-private.h"
00052 #include "magick/color-private.h"
00053 #include "magick/colorspace-private.h"
00054 #include "magick/composite.h"
00055 #include "magick/composite-private.h"
00056 #include "magick/decorate.h"
00057 #include "magick/distort.h"
00058 #include "magick/draw.h"
00059 #include "magick/exception.h"
00060 #include "magick/exception-private.h"
00061 #include "magick/gem.h"
00062 #include "magick/geometry.h"
00063 #include "magick/image.h"
00064 #include "magick/image-private.h"
00065 #include "magick/memory_.h"
00066 #include "magick/list.h"
00067 #include "magick/monitor.h"
00068 #include "magick/monitor-private.h"
00069 #include "magick/pixel-private.h"
00070 #include "magick/quantum.h"
00071 #include "magick/resource_.h"
00072 #include "magick/shear.h"
00073 #include "magick/statistic.h"
00074 #include "magick/threshold.h"
00075 #include "magick/transform.h"
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106 MagickExport Image *AffineTransformImage(const Image *image,
00107 const AffineMatrix *affine_matrix,ExceptionInfo *exception)
00108 {
00109 double
00110 distort[6];
00111
00112 Image
00113 *affine_image;
00114
00115
00116
00117
00118 assert(image->signature == MagickSignature);
00119 if (image->debug != MagickFalse)
00120 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00121 assert(affine_matrix != (AffineMatrix *) NULL);
00122 assert(exception != (ExceptionInfo *) NULL);
00123 assert(exception->signature == MagickSignature);
00124 distort[0]=affine_matrix->sx;
00125 distort[1]=affine_matrix->rx;
00126 distort[2]=affine_matrix->ry;
00127 distort[3]=affine_matrix->sy;
00128 distort[4]=affine_matrix->tx;
00129 distort[5]=affine_matrix->ty;
00130 affine_image=DistortImage(image,AffineProjectionDistortion,6,distort,
00131 MagickTrue,exception);
00132 return(affine_image);
00133 }
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165 static inline void CropToFitImage(Image **image,const MagickRealType x_shear,
00166 const MagickRealType y_shear,const MagickRealType width,
00167 const MagickRealType height,const MagickBooleanType rotate,
00168 ExceptionInfo *exception)
00169 {
00170 Image
00171 *crop_image;
00172
00173 PointInfo
00174 extent[4],
00175 min,
00176 max;
00177
00178 RectangleInfo
00179 geometry,
00180 page;
00181
00182 register long
00183 i;
00184
00185
00186
00187
00188 extent[0].x=(double) (-width/2.0);
00189 extent[0].y=(double) (-height/2.0);
00190 extent[1].x=(double) width/2.0;
00191 extent[1].y=(double) (-height/2.0);
00192 extent[2].x=(double) (-width/2.0);
00193 extent[2].y=(double) height/2.0;
00194 extent[3].x=(double) width/2.0;
00195 extent[3].y=(double) height/2.0;
00196 for (i=0; i < 4; i++)
00197 {
00198 extent[i].x+=x_shear*extent[i].y;
00199 extent[i].y+=y_shear*extent[i].x;
00200 if (rotate != MagickFalse)
00201 extent[i].x+=x_shear*extent[i].y;
00202 extent[i].x+=(double) (*image)->columns/2.0;
00203 extent[i].y+=(double) (*image)->rows/2.0;
00204 }
00205 min=extent[0];
00206 max=extent[0];
00207 for (i=1; i < 4; i++)
00208 {
00209 if (min.x > extent[i].x)
00210 min.x=extent[i].x;
00211 if (min.y > extent[i].y)
00212 min.y=extent[i].y;
00213 if (max.x < extent[i].x)
00214 max.x=extent[i].x;
00215 if (max.y < extent[i].y)
00216 max.y=extent[i].y;
00217 }
00218 geometry.x=(long) (min.x+0.5);
00219 geometry.y=(long) (min.y+0.5);
00220 geometry.width=(unsigned long) ((long) (max.x+0.5)-(long) (min.x+0.5));
00221 geometry.height=(unsigned long) ((long) (max.y+0.5)-(long) (min.y+0.5));
00222 page=(*image)->page;
00223 (void) ParseAbsoluteGeometry("0x0+0+0",&(*image)->page);
00224 crop_image=CropImage(*image,&geometry,exception);
00225 (*image)->page=page;
00226 if (crop_image != (Image *) NULL)
00227 {
00228 crop_image->page=page;
00229 *image=DestroyImage(*image);
00230 *image=crop_image;
00231 }
00232 }
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265 typedef struct _RadonInfo
00266 {
00267 CacheType
00268 type;
00269
00270 unsigned long
00271 width,
00272 height;
00273
00274 MagickSizeType
00275 length;
00276
00277 MagickBooleanType
00278 mapped;
00279
00280 char
00281 path[MaxTextExtent];
00282
00283 int
00284 file;
00285
00286 unsigned short
00287 *cells;
00288 } RadonInfo;
00289
00290 static RadonInfo *DestroyRadonInfo(RadonInfo *radon_info)
00291 {
00292 assert(radon_info != (RadonInfo *) NULL);
00293 switch (radon_info->type)
00294 {
00295 case MemoryCache:
00296 {
00297 if (radon_info->mapped == MagickFalse)
00298 radon_info->cells=(unsigned short *) RelinquishMagickMemory(
00299 radon_info->cells);
00300 else
00301 radon_info->cells=(unsigned short *) UnmapBlob(radon_info->cells,
00302 (size_t) radon_info->length);
00303 RelinquishMagickResource(MemoryResource,radon_info->length);
00304 break;
00305 }
00306 case MapCache:
00307 {
00308 radon_info->cells=(unsigned short *) UnmapBlob(radon_info->cells,(size_t)
00309 radon_info->length);
00310 RelinquishMagickResource(MapResource,radon_info->length);
00311 }
00312 case DiskCache:
00313 {
00314 if (radon_info->file != -1)
00315 (void) close(radon_info->file);
00316 (void) RelinquishUniqueFileResource(radon_info->path);
00317 RelinquishMagickResource(DiskResource,radon_info->length);
00318 break;
00319 }
00320 default:
00321 break;
00322 }
00323 return((RadonInfo *) RelinquishMagickMemory(radon_info));
00324 }
00325
00326 static MagickBooleanType ResetRadonCells(RadonInfo *radon_info)
00327 {
00328 long
00329 y;
00330
00331 register long
00332 x;
00333
00334 ssize_t
00335 count;
00336
00337 unsigned short
00338 value;
00339
00340 if (radon_info->type != DiskCache)
00341 {
00342 (void) ResetMagickMemory(radon_info->cells,0,(size_t) radon_info->length);
00343 return(MagickTrue);
00344 }
00345 value=0;
00346 (void) MagickSeek(radon_info->file,0,SEEK_SET);
00347 for (y=0; y < (long) radon_info->height; y++)
00348 {
00349 for (x=0; x < (long) radon_info->width; x++)
00350 {
00351 count=write(radon_info->file,&value,sizeof(*radon_info->cells));
00352 if (count != (ssize_t) sizeof(*radon_info->cells))
00353 break;
00354 }
00355 if (x < (long) radon_info->width)
00356 break;
00357 }
00358 return(y < (long) radon_info->height ? MagickFalse : MagickTrue);
00359 }
00360
00361 static RadonInfo *AcquireRadonInfo(const Image *image,const unsigned long width,
00362 const unsigned long height,ExceptionInfo *exception)
00363 {
00364 MagickBooleanType
00365 status;
00366
00367 RadonInfo
00368 *radon_info;
00369
00370 radon_info=(RadonInfo *) AcquireMagickMemory(sizeof(*radon_info));
00371 if (radon_info == (RadonInfo *) NULL)
00372 return((RadonInfo *) NULL);
00373 (void) ResetMagickMemory(radon_info,0,sizeof(*radon_info));
00374 radon_info->width=width;
00375 radon_info->height=height;
00376 radon_info->length=(MagickSizeType) width*height*sizeof(*radon_info->cells);
00377 radon_info->type=MemoryCache;
00378 status=AcquireMagickResource(AreaResource,radon_info->length);
00379 if ((status != MagickFalse) &&
00380 (radon_info->length == (MagickSizeType) ((size_t) radon_info->length)))
00381 {
00382 status=AcquireMagickResource(MemoryResource,radon_info->length);
00383 if (status != MagickFalse)
00384 {
00385 radon_info->mapped=MagickFalse;
00386 radon_info->cells=(unsigned short *) AcquireMagickMemory((size_t)
00387 radon_info->length);
00388 if (radon_info->cells == (unsigned short *) NULL)
00389 {
00390 radon_info->mapped=MagickTrue;
00391 radon_info->cells=(unsigned short *) MapBlob(-1,IOMode,0,(size_t)
00392 radon_info->length);
00393 }
00394 if (radon_info->cells == (unsigned short *) NULL)
00395 RelinquishMagickResource(MemoryResource,radon_info->length);
00396 }
00397 }
00398 radon_info->file=(-1);
00399 if (radon_info->cells == (unsigned short *) NULL)
00400 {
00401 status=AcquireMagickResource(DiskResource,radon_info->length);
00402 if (status == MagickFalse)
00403 {
00404 (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
00405 "CacheResourcesExhausted","`%s'",image->filename);
00406 return(DestroyRadonInfo(radon_info));
00407 }
00408 radon_info->type=DiskCache;
00409 (void) AcquireMagickResource(MemoryResource,radon_info->length);
00410 radon_info->file=AcquireUniqueFileResource(radon_info->path);
00411 if (radon_info->file == -1)
00412 return(DestroyRadonInfo(radon_info));
00413 status=AcquireMagickResource(MapResource,radon_info->length);
00414 if (status != MagickFalse)
00415 {
00416 status=ResetRadonCells(radon_info);
00417 if (status != MagickFalse)
00418 {
00419 radon_info->cells=(unsigned short *) MapBlob(radon_info->file,
00420 IOMode,0,(size_t) radon_info->length);
00421 if (radon_info->cells != (unsigned short *) NULL)
00422 radon_info->type=MapCache;
00423 else
00424 RelinquishMagickResource(MapResource,radon_info->length);
00425 }
00426 }
00427 }
00428 return(radon_info);
00429 }
00430
00431 static inline size_t MagickMin(const size_t x,const size_t y)
00432 {
00433 if (x < y)
00434 return(x);
00435 return(y);
00436 }
00437
00438 static inline ssize_t ReadRadonCell(const RadonInfo *radon_info,
00439 const off_t offset,const size_t length,unsigned char *buffer)
00440 {
00441 register ssize_t
00442 i;
00443
00444 ssize_t
00445 count;
00446
00447 #if !defined(MAGICKCORE_HAVE_PPREAD)
00448 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00449 #pragma omp critical
00450 #endif
00451 {
00452 i=(-1);
00453 if (MagickSeek(radon_info->file,offset,SEEK_SET) >= 0)
00454 {
00455 #endif
00456 count=0;
00457 for (i=0; i < (ssize_t) length; i+=count)
00458 {
00459 #if !defined(MAGICKCORE_HAVE_PPREAD)
00460 count=read(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
00461 SSIZE_MAX));
00462 #else
00463 count=pread(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
00464 SSIZE_MAX),(off_t) (offset+i));
00465 #endif
00466 if (count > 0)
00467 continue;
00468 count=0;
00469 if (errno != EINTR)
00470 {
00471 i=(-1);
00472 break;
00473 }
00474 }
00475 #if !defined(MAGICKCORE_HAVE_PPREAD)
00476 }
00477 }
00478 #endif
00479 return(i);
00480 }
00481
00482 static inline ssize_t WriteRadonCell(const RadonInfo *radon_info,
00483 const off_t offset,const size_t length,const unsigned char *buffer)
00484 {
00485 register ssize_t
00486 i;
00487
00488 ssize_t
00489 count;
00490
00491 #if !defined(MAGICKCORE_HAVE_PPWRITE)
00492 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00493 #pragma omp critical
00494 #endif
00495 {
00496 if (MagickSeek(radon_info->file,offset,SEEK_SET) >= 0)
00497 {
00498 #endif
00499 count=0;
00500 for (i=0; i < (ssize_t) length; i+=count)
00501 {
00502 #if !defined(MAGICKCORE_HAVE_PPWRITE)
00503 count=write(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
00504 SSIZE_MAX));
00505 #else
00506 count=pwrite(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
00507 SSIZE_MAX),(off_t) (offset+i));
00508 #endif
00509 if (count > 0)
00510 continue;
00511 count=0;
00512 if (errno != EINTR)
00513 {
00514 i=(-1);
00515 break;
00516 }
00517 }
00518 #if !defined(MAGICKCORE_HAVE_PPWRITE)
00519 }
00520 }
00521 #endif
00522 return(i);
00523 }
00524
00525 static inline unsigned short GetRadonCell(const RadonInfo *radon_info,
00526 const long x,const long y)
00527 {
00528 off_t
00529 i;
00530
00531 unsigned short
00532 value;
00533
00534 i=(off_t) radon_info->height*x+y;
00535 if ((i < 0) ||
00536 ((MagickSizeType) (i*sizeof(*radon_info->cells)) >= radon_info->length))
00537 return(0);
00538 if (radon_info->type != DiskCache)
00539 return(radon_info->cells[i]);
00540 value=0;
00541 (void) ReadRadonCell(radon_info,i*sizeof(*radon_info->cells),
00542 sizeof(*radon_info->cells),(unsigned char *) &value);
00543 return(value);
00544 }
00545
00546 static inline MagickBooleanType SetRadonCell(const RadonInfo *radon_info,
00547 const long x,const long y,const unsigned short value)
00548 {
00549 off_t
00550 i;
00551
00552 ssize_t
00553 count;
00554
00555 i=(off_t) radon_info->height*x+y;
00556 if ((i < 0) ||
00557 ((MagickSizeType) (i*sizeof(*radon_info->cells)) >= radon_info->length))
00558 return(MagickFalse);
00559 if (radon_info->type != DiskCache)
00560 {
00561 radon_info->cells[i]=value;
00562 return(MagickTrue);
00563 }
00564 count=WriteRadonCell(radon_info,i*sizeof(*radon_info->cells),
00565 sizeof(*radon_info->cells),(unsigned char *) &value);
00566 if (count != (ssize_t) sizeof(*radon_info->cells))
00567 return(MagickFalse);
00568 return(MagickTrue);
00569 }
00570
00571 static void RadonProjection(RadonInfo *source_cells,
00572 RadonInfo *destination_cells,const long sign,unsigned long *projection)
00573 {
00574 RadonInfo
00575 *swap;
00576
00577 register long
00578 x;
00579
00580 register RadonInfo
00581 *p,
00582 *q;
00583
00584 unsigned long
00585 step;
00586
00587 p=source_cells;
00588 q=destination_cells;
00589 for (step=1; step < p->width; step*=2)
00590 {
00591 for (x=0; x < (long) p->width; x+=2*step)
00592 {
00593 long
00594 y;
00595
00596 register long
00597 i;
00598
00599 unsigned short
00600 cell;
00601
00602 for (i=0; i < (long) step; i++)
00603 {
00604 for (y=0; y < (long) (p->height-i-1); y++)
00605 {
00606 cell=GetRadonCell(p,x+i,y);
00607 (void) SetRadonCell(q,x+2*i,y,cell+GetRadonCell(p,x+i+step,y+i));
00608 (void) SetRadonCell(q,x+2*i+1,y,cell+GetRadonCell(p,x+i+step,y+i+1));
00609 }
00610 for ( ; y < (long) (p->height-i); y++)
00611 {
00612 cell=GetRadonCell(p,x+i,y);
00613 (void) SetRadonCell(q,x+2*i,y,cell+GetRadonCell(p,x+i+step,y+i));
00614 (void) SetRadonCell(q,x+2*i+1,y,cell);
00615 }
00616 for ( ; y < (long) p->height; y++)
00617 {
00618 cell=GetRadonCell(p,x+i,y);
00619 (void) SetRadonCell(q,x+2*i,y,cell);
00620 (void) SetRadonCell(q,x+2*i+1,y,cell);
00621 }
00622 }
00623 }
00624 swap=p;
00625 p=q;
00626 q=swap;
00627 }
00628 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00629 #pragma omp parallel for
00630 #endif
00631 for (x=0; x < (long) p->width; x++)
00632 {
00633 register long
00634 y;
00635
00636 unsigned long
00637 sum;
00638
00639 sum=0;
00640 for (y=0; y < (long) (p->height-1); y++)
00641 {
00642 long
00643 delta;
00644
00645 delta=GetRadonCell(p,x,y)-(long) GetRadonCell(p,x,y+1);
00646 sum+=delta*delta;
00647 }
00648 projection[p->width+sign*x-1]=sum;
00649 }
00650 }
00651
00652 static MagickBooleanType RadonTransform(const Image *image,
00653 const double threshold,unsigned long *projection,ExceptionInfo *exception)
00654 {
00655 long
00656 y;
00657
00658 MagickBooleanType
00659 status;
00660
00661 RadonInfo
00662 *destination_cells,
00663 *source_cells;
00664
00665 register long
00666 i;
00667
00668 unsigned char
00669 byte;
00670
00671 unsigned long
00672 count,
00673 width;
00674
00675 unsigned short
00676 bits[256];
00677
00678 ViewInfo
00679 *image_view;
00680
00681 for (width=1; width < ((image->columns+7)/8); width<<=1) ;
00682 source_cells=AcquireRadonInfo(image,width,image->rows,exception);
00683 destination_cells=AcquireRadonInfo(image,width,image->rows,exception);
00684 if ((source_cells == (RadonInfo *) NULL) ||
00685 (destination_cells == (RadonInfo *) NULL))
00686 {
00687 if (destination_cells != (RadonInfo *) NULL)
00688 destination_cells=DestroyRadonInfo(destination_cells);
00689 if (source_cells != (RadonInfo *) NULL)
00690 source_cells=DestroyRadonInfo(source_cells);
00691 return(MagickFalse);
00692 }
00693 if (ResetRadonCells(source_cells) == MagickFalse)
00694 {
00695 destination_cells=DestroyRadonInfo(destination_cells);
00696 source_cells=DestroyRadonInfo(source_cells);
00697 return(MagickFalse);
00698 }
00699 for (i=0; i < 256; i++)
00700 {
00701 byte=(unsigned char) i;
00702 for (count=0; byte != 0; byte>>=1)
00703 count+=byte & 0x01;
00704 bits[i]=(unsigned short) count;
00705 }
00706 status=MagickTrue;
00707 image_view=AcquireCacheView(image);
00708 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00709 #pragma omp parallel for schedule(dynamic,8) shared(status)
00710 #endif
00711 for (y=0; y < (long) image->rows; y++)
00712 {
00713 register const PixelPacket
00714 *p;
00715
00716 register long
00717 i,
00718 x;
00719
00720 unsigned long
00721 bit,
00722 byte;
00723
00724 if (status == MagickFalse)
00725 continue;
00726 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00727 if (p == (const PixelPacket *) NULL)
00728 {
00729 status=MagickFalse;
00730 continue;
00731 }
00732 bit=0;
00733 byte=0;
00734 i=(long) (image->columns+7)/8;
00735 for (x=0; x < (long) image->columns; x++)
00736 {
00737 byte<<=1;
00738 if (((MagickRealType) p->red < threshold) ||
00739 ((MagickRealType) p->green < threshold) ||
00740 ((MagickRealType) p->blue < threshold))
00741 byte|=0x01;
00742 bit++;
00743 if (bit == 8)
00744 {
00745 (void) SetRadonCell(source_cells,--i,y,bits[byte]);
00746 bit=0;
00747 byte=0;
00748 }
00749 p++;
00750 }
00751 if (bit != 0)
00752 {
00753 byte<<=(8-bit);
00754 (void) SetRadonCell(source_cells,--i,y,bits[byte]);
00755 }
00756 }
00757 RadonProjection(source_cells,destination_cells,-1,projection);
00758 (void) ResetRadonCells(source_cells);
00759 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00760 #pragma omp parallel for schedule(dynamic,8) shared(status)
00761 #endif
00762 for (y=0; y < (long) image->rows; y++)
00763 {
00764 register const PixelPacket
00765 *p;
00766
00767 register long
00768 i,
00769 x;
00770
00771 unsigned long
00772 bit,
00773 byte;
00774
00775 if (status == MagickFalse)
00776 continue;
00777 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00778 if (p == (const PixelPacket *) NULL)
00779 {
00780 status=MagickFalse;
00781 continue;
00782 }
00783 bit=0;
00784 byte=0;
00785 i=0;
00786 for (x=0; x < (long) image->columns; x++)
00787 {
00788 byte<<=1;
00789 if (((MagickRealType) p->red < threshold) ||
00790 ((MagickRealType) p->green < threshold) ||
00791 ((MagickRealType) p->blue < threshold))
00792 byte|=0x01;
00793 bit++;
00794 if (bit == 8)
00795 {
00796 (void) SetRadonCell(source_cells,i++,y,bits[byte]);
00797 bit=0;
00798 byte=0;
00799 }
00800 p++;
00801 }
00802 if (bit != 0)
00803 {
00804 byte<<=(8-bit);
00805 (void) SetRadonCell(source_cells,i++,y,bits[byte]);
00806 }
00807 }
00808 RadonProjection(source_cells,destination_cells,1,projection);
00809 image_view=DestroyCacheView(image_view);
00810 destination_cells=DestroyRadonInfo(destination_cells);
00811 source_cells=DestroyRadonInfo(source_cells);
00812 return(MagickTrue);
00813 }
00814
00815 static void GetImageBackgroundColor(Image *image,const long offset,
00816 ExceptionInfo *exception)
00817 {
00818 long
00819 y;
00820
00821 MagickPixelPacket
00822 background;
00823
00824 MagickRealType
00825 count;
00826
00827 ViewInfo
00828 *image_view;
00829
00830
00831
00832
00833 if (offset <= 0)
00834 return;
00835 GetMagickPixelPacket(image,&background);
00836 count=0.0;
00837 image_view=AcquireCacheView(image);
00838 for (y=0; y < (long) image->rows; y++)
00839 {
00840 register const PixelPacket
00841 *p;
00842
00843 register long
00844 x;
00845
00846 if ((y >= offset) && (y < ((long) image->rows-offset)))
00847 continue;
00848 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00849 if (p == (const PixelPacket *) NULL)
00850 continue;
00851 for (x=0; x < (long) image->columns; x++)
00852 {
00853 if ((x >= offset) && (x < ((long) image->columns-offset)))
00854 continue;
00855 background.red+=QuantumScale*p->red;
00856 background.green+=QuantumScale*p->green;
00857 background.blue+=QuantumScale*p->blue;
00858 background.opacity+=QuantumScale*p->opacity;
00859 count++;
00860 p++;
00861 }
00862 }
00863 image_view=DestroyCacheView(image_view);
00864 image->background_color.red=RoundToQuantum((MagickRealType) QuantumRange*
00865 background.red/count);
00866 image->background_color.green=RoundToQuantum((MagickRealType) QuantumRange*
00867 background.green/count);
00868 image->background_color.blue=RoundToQuantum((MagickRealType) QuantumRange*
00869 background.blue/count);
00870 image->background_color.opacity=RoundToQuantum((MagickRealType) QuantumRange*
00871 background.opacity/count);
00872 }
00873
00874 MagickExport Image *DeskewImage(const Image *image,const double threshold,
00875 ExceptionInfo *exception)
00876 {
00877 const char
00878 *artifact;
00879
00880 double
00881 degrees,
00882 sum;
00883
00884 Image
00885 *clone_image,
00886 *crop_image,
00887 *median_image,
00888 *rotate_image;
00889
00890 long
00891 skew;
00892
00893 MagickBooleanType
00894 status;
00895
00896 RectangleInfo
00897 geometry;
00898
00899 register long
00900 i;
00901
00902 unsigned long
00903 max_projection,
00904 *projection,
00905 width;
00906
00907
00908
00909
00910 for (width=1; width < ((image->columns+7)/8); width<<=1) ;
00911 projection=(unsigned long *) AcquireQuantumMemory((size_t) (2*width-1),
00912 sizeof(*projection));
00913 if (projection == (unsigned long *) NULL)
00914 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00915 status=RadonTransform(image,threshold,projection,exception);
00916 if (status == MagickFalse)
00917 {
00918 projection=(unsigned long *) RelinquishMagickMemory(projection);
00919 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00920 }
00921 max_projection=0;
00922 sum=0.0;
00923 skew=0;
00924 for (i=0; i < (long) (2*width-1); i++)
00925 {
00926 if (projection[i] > max_projection)
00927 {
00928 skew=i-(long) width+1;
00929 max_projection=projection[i];
00930 }
00931 sum+=projection[i];
00932 }
00933 projection=(unsigned long *) RelinquishMagickMemory(projection);
00934
00935
00936
00937 degrees=RadiansToDegrees(-atan((double) skew/width/8));
00938 if (image->debug != MagickFalse)
00939 (void) LogMagickEvent(TransformEvent,GetMagickModule()," Deskew angle: %g",
00940 degrees);
00941 artifact=GetImageArtifact(image,"deskew:auto-crop");
00942 if (artifact == (const char *) NULL)
00943 return(RotateImage(image,degrees,exception));
00944
00945
00946
00947 clone_image=CloneImage(image,0,0,MagickTrue,exception);
00948 if (clone_image == (Image *) NULL)
00949 return((Image *) NULL);
00950 GetImageBackgroundColor(clone_image,atol(artifact),exception);
00951 rotate_image=RotateImage(clone_image,degrees,exception);
00952 clone_image=DestroyImage(clone_image);
00953 if (rotate_image == (Image *) NULL)
00954 return((Image *) NULL);
00955 median_image=MedianFilterImage(rotate_image,0.0,exception);
00956 if (median_image == (Image *) NULL)
00957 {
00958 rotate_image=DestroyImage(rotate_image);
00959 return((Image *) NULL);
00960 }
00961 geometry=GetImageBoundingBox(median_image,exception);
00962 median_image=DestroyImage(median_image);
00963 if (image->debug != MagickFalse)
00964 (void) LogMagickEvent(TransformEvent,GetMagickModule()," Deskew geometry: "
00965 "%lux%lu%+ld%+ld",geometry.width,geometry.height,geometry.x,geometry.y);
00966 crop_image=CropImage(rotate_image,&geometry,exception);
00967 rotate_image=DestroyImage(rotate_image);
00968 return(crop_image);
00969 }
00970
00971
00972
00973
00974
00975
00976
00977
00978
00979
00980
00981
00982
00983
00984
00985
00986
00987
00988
00989
00990
00991
00992
00993
00994
00995
00996
00997
00998 static Image *IntegralRotateImage(const Image *image,unsigned long rotations,
00999 ExceptionInfo *exception)
01000 {
01001 #define TileHeight 128
01002 #define TileWidth 128
01003 #define RotateImageTag "Rotate/Image"
01004
01005 Image
01006 *rotate_image;
01007
01008 long
01009 progress,
01010 y;
01011
01012 MagickBooleanType
01013 status;
01014
01015 RectangleInfo
01016 page;
01017
01018 ViewInfo
01019 *image_view,
01020 *rotate_view;
01021
01022
01023
01024
01025 assert(image != (Image *) NULL);
01026 page=image->page;
01027 rotations%=4;
01028 if ((rotations == 1) || (rotations == 3))
01029 rotate_image=CloneImage(image,image->rows,image->columns,MagickTrue,
01030 exception);
01031 else
01032 rotate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
01033 exception);
01034 if (rotate_image == (Image *) NULL)
01035 return((Image *) NULL);
01036
01037
01038
01039 status=MagickTrue;
01040 progress=0;
01041 image_view=AcquireCacheView(image);
01042 rotate_view=AcquireCacheView(rotate_image);
01043 switch (rotations)
01044 {
01045 case 0:
01046 {
01047
01048
01049
01050 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01051 #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
01052 #endif
01053 for (y=0; y < (long) image->rows; y++)
01054 {
01055 MagickBooleanType
01056 sync;
01057
01058 register const IndexPacket
01059 *indexes;
01060
01061 register const PixelPacket
01062 *p;
01063
01064 register IndexPacket
01065 *rotate_indexes;
01066
01067 register PixelPacket
01068 *q;
01069
01070 if (status == MagickFalse)
01071 continue;
01072 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,
01073 exception);
01074 q=QueueCacheViewAuthenticPixels(rotate_view,0,y,
01075 rotate_image->columns,1,exception);
01076 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
01077 {
01078 status=MagickFalse;
01079 continue;
01080 }
01081 indexes=GetCacheViewVirtualIndexQueue(image_view);
01082 (void) CopyMagickMemory(q,p,image->columns*sizeof(*p));
01083 if (indexes != (IndexPacket *) NULL)
01084 {
01085 rotate_indexes=GetCacheViewAuthenticIndexQueue(rotate_view);
01086 (void) CopyMagickMemory(rotate_indexes,indexes,image->columns*
01087 sizeof(*indexes));
01088 }
01089 sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
01090 if (sync ==