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/attribute.h"
00051 #include "magick/blob-private.h"
00052 #include "magick/cache-private.h"
00053 #include "magick/color-private.h"
00054 #include "magick/colorspace-private.h"
00055 #include "magick/composite.h"
00056 #include "magick/composite-private.h"
00057 #include "magick/decorate.h"
00058 #include "magick/distort.h"
00059 #include "magick/draw.h"
00060 #include "magick/exception.h"
00061 #include "magick/exception-private.h"
00062 #include "magick/gem.h"
00063 #include "magick/geometry.h"
00064 #include "magick/image.h"
00065 #include "magick/image-private.h"
00066 #include "magick/memory_.h"
00067 #include "magick/list.h"
00068 #include "magick/monitor.h"
00069 #include "magick/monitor-private.h"
00070 #include "magick/pixel-private.h"
00071 #include "magick/quantum.h"
00072 #include "magick/resource_.h"
00073 #include "magick/shear.h"
00074 #include "magick/statistic.h"
00075 #include "magick/string_.h"
00076 #include "magick/string-private.h"
00077 #include "magick/threshold.h"
00078 #include "magick/transform.h"
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
00107
00108
00109 MagickExport Image *AffineTransformImage(const Image *image,
00110 const AffineMatrix *affine_matrix,ExceptionInfo *exception)
00111 {
00112 double
00113 distort[6];
00114
00115 Image
00116 *deskew_image;
00117
00118
00119
00120
00121 assert(image->signature == MagickSignature);
00122 if (image->debug != MagickFalse)
00123 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00124 assert(affine_matrix != (AffineMatrix *) NULL);
00125 assert(exception != (ExceptionInfo *) NULL);
00126 assert(exception->signature == MagickSignature);
00127 distort[0]=affine_matrix->sx;
00128 distort[1]=affine_matrix->rx;
00129 distort[2]=affine_matrix->ry;
00130 distort[3]=affine_matrix->sy;
00131 distort[4]=affine_matrix->tx;
00132 distort[5]=affine_matrix->ty;
00133 deskew_image=DistortImage(image,AffineProjectionDistortion,6,distort,
00134 MagickTrue,exception);
00135 return(deskew_image);
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
00166
00167
00168 static MagickBooleanType CropToFitImage(Image **image,
00169 const MagickRealType x_shear,const MagickRealType y_shear,
00170 const MagickRealType width,const MagickRealType height,
00171 const MagickBooleanType rotate,ExceptionInfo *exception)
00172 {
00173 Image
00174 *crop_image;
00175
00176 PointInfo
00177 extent[4],
00178 min,
00179 max;
00180
00181 RectangleInfo
00182 geometry,
00183 page;
00184
00185 register long
00186 i;
00187
00188
00189
00190
00191 extent[0].x=(double) (-width/2.0);
00192 extent[0].y=(double) (-height/2.0);
00193 extent[1].x=(double) width/2.0;
00194 extent[1].y=(double) (-height/2.0);
00195 extent[2].x=(double) (-width/2.0);
00196 extent[2].y=(double) height/2.0;
00197 extent[3].x=(double) width/2.0;
00198 extent[3].y=(double) height/2.0;
00199 for (i=0; i < 4; i++)
00200 {
00201 extent[i].x+=x_shear*extent[i].y;
00202 extent[i].y+=y_shear*extent[i].x;
00203 if (rotate != MagickFalse)
00204 extent[i].x+=x_shear*extent[i].y;
00205 extent[i].x+=(double) (*image)->columns/2.0;
00206 extent[i].y+=(double) (*image)->rows/2.0;
00207 }
00208 min=extent[0];
00209 max=extent[0];
00210 for (i=1; i < 4; i++)
00211 {
00212 if (min.x > extent[i].x)
00213 min.x=extent[i].x;
00214 if (min.y > extent[i].y)
00215 min.y=extent[i].y;
00216 if (max.x < extent[i].x)
00217 max.x=extent[i].x;
00218 if (max.y < extent[i].y)
00219 max.y=extent[i].y;
00220 }
00221 geometry.x=(long) ceil(min.x-0.5);
00222 geometry.y=(long) ceil(min.y-0.5);
00223 geometry.width=(unsigned long) floor(max.x-min.x+0.5);
00224 geometry.height=(unsigned long) floor(max.y-min.y+0.5);
00225 page=(*image)->page;
00226 (void) ParseAbsoluteGeometry("0x0+0+0",&(*image)->page);
00227 crop_image=CropImage(*image,&geometry,exception);
00228 if (crop_image == (Image *) NULL)
00229 return(MagickFalse);
00230 crop_image->page=page;
00231 *image=DestroyImage(*image);
00232 *image=crop_image;
00233 return(MagickTrue);
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
00266
00267 typedef struct _RadonInfo
00268 {
00269 CacheType
00270 type;
00271
00272 unsigned long
00273 width,
00274 height;
00275
00276 MagickSizeType
00277 length;
00278
00279 MagickBooleanType
00280 mapped;
00281
00282 char
00283 path[MaxTextExtent];
00284
00285 int
00286 file;
00287
00288 unsigned short
00289 *cells;
00290 } RadonInfo;
00291
00292 static RadonInfo *DestroyRadonInfo(RadonInfo *radon_info)
00293 {
00294 assert(radon_info != (RadonInfo *) NULL);
00295 switch (radon_info->type)
00296 {
00297 case MemoryCache:
00298 {
00299 if (radon_info->mapped == MagickFalse)
00300 radon_info->cells=(unsigned short *) RelinquishMagickMemory(
00301 radon_info->cells);
00302 else
00303 radon_info->cells=(unsigned short *) UnmapBlob(radon_info->cells,
00304 (size_t) radon_info->length);
00305 RelinquishMagickResource(MemoryResource,radon_info->length);
00306 break;
00307 }
00308 case MapCache:
00309 {
00310 radon_info->cells=(unsigned short *) UnmapBlob(radon_info->cells,(size_t)
00311 radon_info->length);
00312 RelinquishMagickResource(MapResource,radon_info->length);
00313 }
00314 case DiskCache:
00315 {
00316 if (radon_info->file != -1)
00317 (void) close(radon_info->file);
00318 (void) RelinquishUniqueFileResource(radon_info->path);
00319 RelinquishMagickResource(DiskResource,radon_info->length);
00320 break;
00321 }
00322 default:
00323 break;
00324 }
00325 return((RadonInfo *) RelinquishMagickMemory(radon_info));
00326 }
00327
00328 static MagickBooleanType ResetRadonCells(RadonInfo *radon_info)
00329 {
00330 long
00331 y;
00332
00333 register long
00334 x;
00335
00336 ssize_t
00337 count;
00338
00339 unsigned short
00340 value;
00341
00342 if (radon_info->type != DiskCache)
00343 {
00344 (void) ResetMagickMemory(radon_info->cells,0,(size_t) radon_info->length);
00345 return(MagickTrue);
00346 }
00347 value=0;
00348 (void) MagickSeek(radon_info->file,0,SEEK_SET);
00349 for (y=0; y < (long) radon_info->height; y++)
00350 {
00351 for (x=0; x < (long) radon_info->width; x++)
00352 {
00353 count=write(radon_info->file,&value,sizeof(*radon_info->cells));
00354 if (count != (ssize_t) sizeof(*radon_info->cells))
00355 break;
00356 }
00357 if (x < (long) radon_info->width)
00358 break;
00359 }
00360 return(y < (long) radon_info->height ? MagickFalse : MagickTrue);
00361 }
00362
00363 static RadonInfo *AcquireRadonInfo(const Image *image,const unsigned long width,
00364 const unsigned long height,ExceptionInfo *exception)
00365 {
00366 MagickBooleanType
00367 status;
00368
00369 RadonInfo
00370 *radon_info;
00371
00372 radon_info=(RadonInfo *) AcquireAlignedMemory(1,sizeof(*radon_info));
00373 if (radon_info == (RadonInfo *) NULL)
00374 return((RadonInfo *) NULL);
00375 (void) ResetMagickMemory(radon_info,0,sizeof(*radon_info));
00376 radon_info->width=width;
00377 radon_info->height=height;
00378 radon_info->length=(MagickSizeType) width*height*sizeof(*radon_info->cells);
00379 radon_info->type=MemoryCache;
00380 status=AcquireMagickResource(AreaResource,radon_info->length);
00381 if ((status != MagickFalse) &&
00382 (radon_info->length == (MagickSizeType) ((size_t) radon_info->length)))
00383 {
00384 status=AcquireMagickResource(MemoryResource,radon_info->length);
00385 if (status != MagickFalse)
00386 {
00387 radon_info->mapped=MagickFalse;
00388 radon_info->cells=(unsigned short *) AcquireMagickMemory((size_t)
00389 radon_info->length);
00390 if (radon_info->cells == (unsigned short *) NULL)
00391 {
00392 radon_info->mapped=MagickTrue;
00393 radon_info->cells=(unsigned short *) MapBlob(-1,IOMode,0,(size_t)
00394 radon_info->length);
00395 }
00396 if (radon_info->cells == (unsigned short *) NULL)
00397 RelinquishMagickResource(MemoryResource,radon_info->length);
00398 }
00399 }
00400 radon_info->file=(-1);
00401 if (radon_info->cells == (unsigned short *) NULL)
00402 {
00403 status=AcquireMagickResource(DiskResource,radon_info->length);
00404 if (status == MagickFalse)
00405 {
00406 (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
00407 "CacheResourcesExhausted","`%s'",image->filename);
00408 return(DestroyRadonInfo(radon_info));
00409 }
00410 radon_info->type=DiskCache;
00411 (void) AcquireMagickResource(MemoryResource,radon_info->length);
00412 radon_info->file=AcquireUniqueFileResource(radon_info->path);
00413 if (radon_info->file == -1)
00414 return(DestroyRadonInfo(radon_info));
00415 status=AcquireMagickResource(MapResource,radon_info->length);
00416 if (status != MagickFalse)
00417 {
00418 status=ResetRadonCells(radon_info);
00419 if (status != MagickFalse)
00420 {
00421 radon_info->cells=(unsigned short *) MapBlob(radon_info->file,
00422 IOMode,0,(size_t) radon_info->length);
00423 if (radon_info->cells != (unsigned short *) NULL)
00424 radon_info->type=MapCache;
00425 else
00426 RelinquishMagickResource(MapResource,radon_info->length);
00427 }
00428 }
00429 }
00430 return(radon_info);
00431 }
00432
00433 static inline size_t MagickMin(const size_t x,const size_t y)
00434 {
00435 if (x < y)
00436 return(x);
00437 return(y);
00438 }
00439
00440 static inline ssize_t ReadRadonCell(const RadonInfo *radon_info,
00441 const off_t offset,const size_t length,unsigned char *buffer)
00442 {
00443 register ssize_t
00444 i;
00445
00446 ssize_t
00447 count;
00448
00449 #if !defined(MAGICKCORE_HAVE_PPREAD)
00450 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00451 #pragma omp critical (MagickCore_ReadRadonCell)
00452 #endif
00453 {
00454 i=(-1);
00455 if (MagickSeek(radon_info->file,offset,SEEK_SET) >= 0)
00456 {
00457 #endif
00458 count=0;
00459 for (i=0; i < (ssize_t) length; i+=count)
00460 {
00461 #if !defined(MAGICKCORE_HAVE_PPREAD)
00462 count=read(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
00463 SSIZE_MAX));
00464 #else
00465 count=pread(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
00466 SSIZE_MAX),(off_t) (offset+i));
00467 #endif
00468 if (count > 0)
00469 continue;
00470 count=0;
00471 if (errno != EINTR)
00472 {
00473 i=(-1);
00474 break;
00475 }
00476 }
00477 #if !defined(MAGICKCORE_HAVE_PPREAD)
00478 }
00479 }
00480 #endif
00481 return(i);
00482 }
00483
00484 static inline ssize_t WriteRadonCell(const RadonInfo *radon_info,
00485 const off_t offset,const size_t length,const unsigned char *buffer)
00486 {
00487 register ssize_t
00488 i;
00489
00490 ssize_t
00491 count;
00492
00493 #if !defined(MAGICKCORE_HAVE_PWRITE)
00494 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00495 #pragma omp critical (MagickCore_WriteRadonCell)
00496 #endif
00497 {
00498 if (MagickSeek(radon_info->file,offset,SEEK_SET) >= 0)
00499 {
00500 #endif
00501 count=0;
00502 for (i=0; i < (ssize_t) length; i+=count)
00503 {
00504 #if !defined(MAGICKCORE_HAVE_PWRITE)
00505 count=write(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
00506 SSIZE_MAX));
00507 #else
00508 count=pwrite(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
00509 SSIZE_MAX),(off_t) (offset+i));
00510 #endif
00511 if (count > 0)
00512 continue;
00513 count=0;
00514 if (errno != EINTR)
00515 {
00516 i=(-1);
00517 break;
00518 }
00519 }
00520 #if !defined(MAGICKCORE_HAVE_PWRITE)
00521 }
00522 }
00523 #endif
00524 return(i);
00525 }
00526
00527 static inline unsigned short GetRadonCell(const RadonInfo *radon_info,
00528 const long x,const long y)
00529 {
00530 off_t
00531 i;
00532
00533 unsigned short
00534 value;
00535
00536 i=(off_t) radon_info->height*x+y;
00537 if ((i < 0) ||
00538 ((MagickSizeType) (i*sizeof(*radon_info->cells)) >= radon_info->length))
00539 return(0);
00540 if (radon_info->type != DiskCache)
00541 return(radon_info->cells[i]);
00542 value=0;
00543 (void) ReadRadonCell(radon_info,i*sizeof(*radon_info->cells),
00544 sizeof(*radon_info->cells),(unsigned char *) &value);
00545 return(value);
00546 }
00547
00548 static inline MagickBooleanType SetRadonCell(const RadonInfo *radon_info,
00549 const long x,const long y,const unsigned short value)
00550 {
00551 off_t
00552 i;
00553
00554 ssize_t
00555 count;
00556
00557 i=(off_t) radon_info->height*x+y;
00558 if ((i < 0) ||
00559 ((MagickSizeType) (i*sizeof(*radon_info->cells)) >= radon_info->length))
00560 return(MagickFalse);
00561 if (radon_info->type != DiskCache)
00562 {
00563 radon_info->cells[i]=value;
00564 return(MagickTrue);
00565 }
00566 count=WriteRadonCell(radon_info,i*sizeof(*radon_info->cells),
00567 sizeof(*radon_info->cells),(const unsigned char *) &value);
00568 if (count != (ssize_t) sizeof(*radon_info->cells))
00569 return(MagickFalse);
00570 return(MagickTrue);
00571 }
00572
00573 static void RadonProjection(RadonInfo *source_cells,
00574 RadonInfo *destination_cells,const long sign,unsigned long *projection)
00575 {
00576 RadonInfo
00577 *swap;
00578
00579 register long
00580 x;
00581
00582 register RadonInfo
00583 *p,
00584 *q;
00585
00586 unsigned long
00587 step;
00588
00589 p=source_cells;
00590 q=destination_cells;
00591 for (step=1; step < p->width; step*=2)
00592 {
00593 for (x=0; x < (long) p->width; x+=2*step)
00594 {
00595 long
00596 y;
00597
00598 register long
00599 i;
00600
00601 unsigned short
00602 cell;
00603
00604 for (i=0; i < (long) step; i++)
00605 {
00606 for (y=0; y < (long) (p->height-i-1); y++)
00607 {
00608 cell=GetRadonCell(p,x+i,y);
00609 (void) SetRadonCell(q,x+2*i,y,cell+GetRadonCell(p,x+i+step,y+i));
00610 (void) SetRadonCell(q,x+2*i+1,y,cell+GetRadonCell(p,x+i+step,y+i+1));
00611 }
00612 for ( ; y < (long) (p->height-i); y++)
00613 {
00614 cell=GetRadonCell(p,x+i,y);
00615 (void) SetRadonCell(q,x+2*i,y,cell+GetRadonCell(p,x+i+step,y+i));
00616 (void) SetRadonCell(q,x+2*i+1,y,cell);
00617 }
00618 for ( ; y < (long) p->height; y++)
00619 {
00620 cell=GetRadonCell(p,x+i,y);
00621 (void) SetRadonCell(q,x+2*i,y,cell);
00622 (void) SetRadonCell(q,x+2*i+1,y,cell);
00623 }
00624 }
00625 }
00626 swap=p;
00627 p=q;
00628 q=swap;
00629 }
00630 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00631 #pragma omp parallel for schedule(dynamic,4)
00632 #endif
00633 for (x=0; x < (long) p->width; x++)
00634 {
00635 register long
00636 y;
00637
00638 unsigned long
00639 sum;
00640
00641 sum=0;
00642 for (y=0; y < (long) (p->height-1); y++)
00643 {
00644 long
00645 delta;
00646
00647 delta=GetRadonCell(p,x,y)-(long) GetRadonCell(p,x,y+1);
00648 sum+=delta*delta;
00649 }
00650 projection[p->width+sign*x-1]=sum;
00651 }
00652 }
00653
00654 static MagickBooleanType RadonTransform(const Image *image,
00655 const double threshold,unsigned long *projection,ExceptionInfo *exception)
00656 {
00657 CacheView
00658 *image_view;
00659
00660 long
00661 y;
00662
00663 MagickBooleanType
00664 status;
00665
00666 RadonInfo
00667 *destination_cells,
00668 *source_cells;
00669
00670 register long
00671 i;
00672
00673 unsigned char
00674 byte;
00675
00676 unsigned long
00677 count,
00678 width;
00679
00680 unsigned short
00681 bits[256];
00682
00683 for (width=1; width < ((image->columns+7)/8); width<<=1) ;
00684 source_cells=AcquireRadonInfo(image,width,image->rows,exception);
00685 destination_cells=AcquireRadonInfo(image,width,image->rows,exception);
00686 if ((source_cells == (RadonInfo *) NULL) ||
00687 (destination_cells == (RadonInfo *) NULL))
00688 {
00689 if (destination_cells != (RadonInfo *) NULL)
00690 destination_cells=DestroyRadonInfo(destination_cells);
00691 if (source_cells != (RadonInfo *) NULL)
00692 source_cells=DestroyRadonInfo(source_cells);
00693 return(MagickFalse);
00694 }
00695 if (ResetRadonCells(source_cells) == MagickFalse)
00696 {
00697 destination_cells=DestroyRadonInfo(destination_cells);
00698 source_cells=DestroyRadonInfo(source_cells);
00699 return(MagickFalse);
00700 }
00701 for (i=0; i < 256; i++)
00702 {
00703 byte=(unsigned char) i;
00704 for (count=0; byte != 0; byte>>=1)
00705 count+=byte & 0x01;
00706 bits[i]=(unsigned short) count;
00707 }
00708 status=MagickTrue;
00709 image_view=AcquireCacheView(image);
00710 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00711 #pragma omp parallel for schedule(dynamic,4) shared(status)
00712 #endif
00713 for (y=0; y < (long) image->rows; y++)
00714 {
00715 register const PixelPacket
00716 *restrict p;
00717
00718 register long
00719 i,
00720 x;
00721
00722 unsigned long
00723 bit,
00724 byte;
00725
00726 if (status == MagickFalse)
00727 continue;
00728 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00729 if (p == (const PixelPacket *) NULL)
00730 {
00731 status=MagickFalse;
00732 continue;
00733 }
00734 bit=0;
00735 byte=0;
00736 i=(long) (image->columns+7)/8;
00737 for (x=0; x < (long) image->columns; x++)
00738 {
00739 byte<<=1;
00740 if (((MagickRealType) p->red < threshold) ||
00741 ((MagickRealType) p->green < threshold) ||
00742 ((MagickRealType) p->blue < threshold))
00743 byte|=0x01;
00744 bit++;
00745 if (bit == 8)
00746 {
00747 (void) SetRadonCell(source_cells,--i,y,bits[byte]);
00748 bit=0;
00749 byte=0;
00750 }
00751 p++;
00752 }
00753 if (bit != 0)
00754 {
00755 byte<<=(8-bit);
00756 (void) SetRadonCell(source_cells,--i,y,bits[byte]);
00757 }
00758 }
00759 RadonProjection(source_cells,destination_cells,-1,projection);
00760 (void) ResetRadonCells(source_cells);
00761 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00762 #pragma omp parallel for schedule(dynamic,4) shared(status)
00763 #endif
00764 for (y=0; y < (long) image->rows; y++)
00765 {
00766 register const PixelPacket
00767 *restrict p;
00768
00769 register long
00770 i,
00771 x;
00772
00773 unsigned long
00774 bit,
00775 byte;
00776
00777 if (status == MagickFalse)
00778 continue;
00779 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00780 if (p == (const PixelPacket *) NULL)
00781 {
00782 status=MagickFalse;
00783 continue;
00784 }
00785 bit=0;
00786 byte=0;
00787 i=0;
00788 for (x=0; x < (long) image->columns; x++)
00789 {
00790 byte<<=1;
00791 if (((MagickRealType) p->red < threshold) ||
00792 ((MagickRealType) p->green < threshold) ||
00793 ((MagickRealType) p->blue < threshold))
00794 byte|=0x01;
00795 bit++;
00796 if (bit == 8)
00797 {
00798 (void) SetRadonCell(source_cells,i++,y,bits[byte]);
00799 bit=0;
00800 byte=0;
00801 }
00802 p++;
00803 }
00804 if (bit != 0)
00805 {
00806 byte<<=(8-bit);
00807 (void) SetRadonCell(source_cells,i++,y,bits[byte]);
00808 }
00809 }
00810 RadonProjection(source_cells,destination_cells,1,projection);
00811 image_view=DestroyCacheView(image_view);
00812 destination_cells=DestroyRadonInfo(destination_cells);
00813 source_cells=DestroyRadonInfo(source_cells);
00814 return(MagickTrue);
00815 }
00816
00817 static void GetImageBackgroundColor(Image *image,const long offset,
00818 ExceptionInfo *exception)
00819 {
00820 CacheView
00821 *image_view;
00822
00823 long
00824 y;
00825
00826 MagickPixelPacket
00827 background;
00828
00829 MagickRealType
00830 count;
00831
00832
00833
00834
00835 if (offset <= 0)
00836 return;
00837 GetMagickPixelPacket(image,&background);
00838 count=0.0;
00839 image_view=AcquireCacheView(image);
00840 for (y=0; y < (long) image->rows; y++)
00841 {
00842 register const PixelPacket
00843 *restrict p;
00844
00845 register long
00846 x;
00847
00848 if ((y >= offset) && (y < ((long) image->rows-offset)))
00849 continue;
00850 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00851 if (p == (const PixelPacket *) NULL)
00852 continue;
00853 for (x=0; x < (long) image->columns; x++)
00854 {
00855 if ((x >= offset) && (x < ((long) image->columns-offset)))
00856 continue;
00857 background.red+=QuantumScale*GetRedPixelComponent(p);
00858 background.green+=QuantumScale*GetGreenPixelComponent(p);
00859 background.blue+=QuantumScale*GetBluePixelComponent(p);
00860 background.opacity+=QuantumScale*GetOpacityPixelComponent(p);
00861 count++;
00862 p++;
00863 }
00864 }
00865 image_view=DestroyCacheView(image_view);
00866 image->background_color.red=ClampToQuantum((MagickRealType) QuantumRange*
00867 background.red/count);
00868 image->background_color.green=ClampToQuantum((MagickRealType) QuantumRange*
00869 background.green/count);
00870 image->background_color.blue=ClampToQuantum((MagickRealType) QuantumRange*
00871 background.blue/count);
00872 image->background_color.opacity=ClampToQuantum((MagickRealType) QuantumRange*
00873 background.opacity/count);
00874 }
00875
00876 MagickExport Image *DeskewImage(const Image *image,const double threshold,
00877 ExceptionInfo *exception)
00878 {
00879 AffineMatrix
00880 affine_matrix;
00881
00882 const char
00883 *artifact;
00884
00885 double
00886 degrees;
00887
00888 Image
00889 *clone_image,
00890 *crop_image,
00891 *deskew_image,
00892 *median_image;
00893
00894 long
00895 skew;
00896
00897 MagickBooleanType
00898 status;
00899
00900 RectangleInfo
00901 geometry;
00902
00903 register long
00904 i;
00905
00906 unsigned long
00907 max_projection,
00908 *projection,
00909 width;
00910
00911
00912
00913
00914 for (width=1; width < ((image->columns+7)/8); width<<=1) ;
00915 projection=(unsigned long *) AcquireQuantumMemory((size_t) (2*width-1),
00916 sizeof(*projection));
00917 if (projection == (unsigned long *) NULL)
00918 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00919 status=RadonTransform(image,threshold,projection,exception);
00920 if (status == MagickFalse)
00921 {
00922 projection=(unsigned long *) RelinquishMagickMemory(projection);
00923 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00924 }
00925 max_projection=0;
00926 skew=0;
00927 for (i=0; i < (long) (2*width-1); i++)
00928 {
00929 if (projection[i] > max_projection)
00930 {
00931 skew=i-(long) width+1;
00932 max_projection=projection[i];
00933 }
00934 }
00935 projection=(unsigned long *) RelinquishMagickMemory(projection);
00936
00937
00938
00939 clone_image=CloneImage(image,0,0,MagickTrue,exception);
00940 if (clone_image == (Image *) NULL)
00941 return((Image *) NULL);
00942 (void) SetImageVirtualPixelMethod(clone_image,BackgroundVirtualPixelMethod);
00943 degrees=RadiansToDegrees(-atan((double) skew/width/8));
00944 if (image->debug != MagickFalse)
00945 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
00946 " Deskew angle: %g",degrees);
00947 affine_matrix.sx=cos(DegreesToRadians(fmod((double) degrees,360.0)));
00948 affine_matrix.rx=sin(DegreesToRadians(fmod((double) degrees,360.0)));
00949 affine_matrix.ry=(-sin(DegreesToRadians(fmod((double) degrees,360.0))));
00950 affine_matrix.sy=cos(DegreesToRadians(fmod((double) degrees,360.0)));
00951 affine_matrix.tx=0.0;
00952 affine_matrix.ty=0.0;
00953 artifact=GetImageArtifact(image,"deskew:auto-crop");
00954 if (artifact == (const char *) NULL)
00955 {
00956 deskew_image=AffineTransformImage(clone_image,&affine_matrix,exception);
00957 clone_image=DestroyImage(clone_image);
00958 return(deskew_image);
00959 }
00960
00961
00962
00963 GetImageBackgroundColor(clone_image,StringToLong(artifact),exception);
00964 deskew_image=AffineTransformImage(clone_image,&affine_matrix,exception);
00965 clone_image=DestroyImage(clone_image);
00966 if (deskew_image == (Image *) NULL)
00967 return((Image *) NULL);
00968 median_image=MedianFilterImage(deskew_image,0.0,exception);
00969 if (median_image == (Image *) NULL)
00970 {
00971 deskew_image=DestroyImage(deskew_image);
00972 return((Image *) NULL);
00973 }
00974 geometry=GetImageBoundingBox(median_image,exception);
00975 median_image=DestroyImage(median_image);
00976 if (image->debug != MagickFalse)
00977 (void) LogMagickEvent(TransformEvent,GetMagickModule()," Deskew geometry: "
00978 "%lux%lu%+ld%+ld",geometry.width,geometry.height,geometry.x,geometry.y);
00979 crop_image=CropImage(deskew_image,&geometry,exception);
00980 deskew_image=DestroyImage(deskew_image);
00981 return(crop_image);
00982 }
00983
00984
00985
00986
00987
00988
00989
00990
00991
00992
00993
00994
00995
00996
00997
00998
00999
01000
01001
01002
01003
01004
01005
01006
01007
01008
01009
01010
01011 static Image *IntegralRotateImage(const Image *image,unsigned long rotations,
01012 ExceptionInfo *exception)
01013 {
01014 #define RotateImageTag "Rotate/Image"
01015
01016 CacheView
01017 *image_view,
01018 *rotate_view;
01019
01020 Image
01021 *rotate_image;
01022
01023 long
01024 progress,
01025 y;
01026
01027 MagickBooleanType
01028 status;
01029
01030 RectangleInfo
01031 page;
01032
01033
01034
01035
01036 assert(image != (Image *) NULL);
01037 page=image->page;
01038 rotations%=4;
01039 if (rotations == 0)
01040 return(CloneImage(image,0,0,MagickTrue,exception));
01041 if ((rotations == 1) || (rotations == 3))
01042 rotate_image=CloneImage(image,image->rows,image->columns,MagickTrue,
01043 exception);
01044 else
01045 rotate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
01046 exception);
01047 if (rotate_image == (Image *) NULL)
01048 return((Image *) NULL);
01049
01050
01051
01052 status=MagickTrue;
01053 progress=0;
01054 image_view=AcquireCacheView(image);
01055 rotate_view=AcquireCacheView(rotate_image);
01056 switch (rotations)
01057 {
01058 case 0:
01059 {
01060
01061
01062
01063 break;
01064 }
01065 case 1:
01066 {
01067 long
01068 tile_y;
01069
01070 unsigned long
01071 tile_height,
01072 tile_width;
01073
01074
01075
01076
01077 GetPixelCacheTileSize(image,&tile_width,&tile_height);
01078 #if defined(MAGICKCORE_OPENMP_SUPPORT) && (_OPENMP > 202001)
01079 #pragma omp parallel for schedule(dynamic,4) shared(progress, status)
01080 #endif
01081 for (tile_y=0; tile_y < (long) image->rows; tile_y+=tile_height)
01082 {
01083 register long
01084 tile_x;
01085
01086 if (status == MagickFalse)
01087 continue;
01088 for (tile_x=0; tile_x < (long) image->columns; tile_x+=tile_width)
01089 {
01090 MagickBooleanType
01091 sync;
01092
01093 register const IndexPacket
01094 *restrict indexes;
01095
01096 register const PixelPacket
01097 *restrict p;
01098
01099 register IndexPacket
01100 *restrict rotate_indexes;
01101
01102 register long
01103 y;
01104
01105 register PixelPacket
01106 *restrict q;
01107
01108 unsigned long
01109 height,
01110 width;
01111
01112 width=tile_width;
01113 if ((tile_x+(long) tile_width) > (long) image->columns)
01114 width=(unsigned long) (tile_width-(tile_x+tile_width-
01115 image->columns));
01116 height=tile_height;
01117 if ((tile_y+(long) tile_height) > (long) image->rows)
01118 height=(unsigned long) (tile_height-(tile_y+tile_height-
01119 image->rows));
01120 p=GetCacheViewVirtualPixels(image_view,tile_x,tile_y,width,height,
01121 exception);
01122 if (p == (const PixelPacket *) NULL)
01123 {
01124 status=MagickFalse;
01125 break;
01126 }
01127 indexes=GetCacheViewVirtualIndexQueue(image_view);
01128 for (y=0; y < (long) width; y++)
01129 {
01130 register const PixelPacket
01131 *restrict tile_pixels;
01132
01133 register long
01134 x;
01135
01136 q=QueueCacheViewAuthenticPixels(rotate_view,(long)
01137 rotate_image->columns-(tile_y+height),y+tile_x,height,
01138 1,exception);
01139 if (q == (PixelPacket *) NULL)
01140 {
01141 status=MagickFalse;
01142 break;
01143 }
01144 tile_pixels=p+(height-1)*width+y;
01145 for (x=0; x < (long) height; x++)
01146 {
01147 *q++=(*tile_pixels);
01148 tile_pixels-=width;
01149 }
01150 rotate_indexes=GetCacheViewAuthenticIndexQueue(rotate_view);
01151 if ((indexes != (IndexPacket *) NULL) &&
01152 (rotate_indexes != (IndexPacket *) NULL))
01153 {
01154 register const IndexPacket
01155 *restrict tile_indexes;
01156
01157 tile_indexes=indexes+(height-1)*width+y;
01158 for (x=0; x < (long) height; x++)
01159 {
01160 *rotate_indexes++=(*tile_indexes);
01161 tile_indexes-=width;
01162 }
01163 }
01164 sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
01165 if (sync == MagickFalse)
01166 status=MagickFalse;
01167 }
01168 }
01169 if (image->progress_monitor != (MagickProgressMonitor) NULL)
01170 {
01171 MagickBooleanType
01172 proceed;
01173
01174 proceed=SetImageProgress(image,RotateImageTag,progress+=tile_height,
01175 image->rows);
01176 if (proceed == MagickFalse)
01177 status=MagickFalse;
01178 }
01179 }
01180 (void) SetImageProgress(image,RotateImageTag,(MagickOffsetType)
01181 image->rows-1,image->rows);
01182 Swap(page.width,page.height);
01183 Swap(page.x,page.y);
01184 if (page.width != 0)
01185 page.x=(long) (page.width-rotate_image->columns-page.x);
01186 break;
01187 }
01188 case 2:
01189 {
01190
01191
01192
01193 #if defined(MAGICKCORE_OPENMP_SUPPORT) && (_OPENMP > 202001)
01194 #pragma omp parallel for schedule(dynamic,4) shared(progress, status)
01195 #endif
01196 for (y=0; y < (long) image->rows; y++)
01197 {
01198 MagickBooleanType
01199 sync;
01200
01201 register const IndexPacket
01202 *restrict indexes;
01203
01204 register const PixelPacket
01205 *restrict p;
01206
01207 register IndexPacket
01208 *restrict rotate_indexes;
01209
01210 register long
01211 x;
01212
01213 register PixelPacket
01214 *restrict q;
01215
01216 if (status == MagickFalse)
01217 continue;
01218 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,
01219 exception);
01220 q=QueueCacheViewAuthenticPixels(rotate_view,0,(long) (image->rows-
01221 y-1),image->columns,1,exception);
01222 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
01223 {
01224 status=MagickFalse;
01225 continue;
01226 }
01227 indexes=GetCacheViewVirtualIndexQueue(image_view);
01228 rotate_indexes=GetCacheViewAuthenticIndexQueue(rotate_view);
01229 q+=image->columns;
01230 for (x=0; x < (long) image->columns; x++)
01231 *--q=(*p++);
01232 if ((indexes != (IndexPacket *) NULL) &&
01233 (rotate_indexes != (IndexPacket *) NULL))
01234 for (x=0; x < (long) image->columns; x++)
01235 rotate_indexes[image->columns-x-1]=indexes[x];
01236 sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
01237 if (sync == MagickFalse)
01238 status=MagickFalse;
01239 if (image->progress_monitor != (MagickProgressMonitor) NULL)
01240 {
01241 MagickBooleanType
01242 proceed;
01243
01244 proceed=SetImageProgress(image,RotateImageTag,progress++,
01245 image->rows);
01246 if (proceed == MagickFalse)
01247 status=MagickFalse;
01248 }
01249 }
01250 if (page.width != 0)
01251 page.x=(long) (page.width-rotate_image->columns-page.x);
01252 if (page.height != 0)
01253 page.y=(long) (page.height-rotate_image->rows-page.y);
01254 break;
01255 }
01256 case 3:
01257 {
01258 long
01259 tile_y;
01260
01261 unsigned long
01262 tile_height,
01263 tile_width;
01264
01265
01266
01267
01268 GetPixelCacheTileSize(image,&tile_width,&tile_height);
01269 #if defined(MAGICKCORE_OPENMP_SUPPORT) && (_OPENMP > 202001)
01270 #pragma omp parallel for schedule(dynamic,4) shared(progress, status)
01271 #endif
01272 for (tile_y=0; tile_y < (long) image->rows; tile_y+=tile_height)
01273 {
01274 register long
01275 tile_x;
01276
01277 if (status == MagickFalse)
01278 continue;
01279 for (tile_x=0; tile_x < (long) image->columns; tile_x+=tile_width)
01280 {
01281 MagickBooleanType
01282 sync;
01283
01284 register const IndexPacket
01285 *restrict indexes;
01286
01287 register const PixelPacket
01288 *restrict p;
01289
01290 register IndexPacket
01291 *restrict rotate_indexes;
01292
01293 register long
01294 y;
01295
01296 register PixelPacket
01297 *restrict q;
01298
01299 unsigned long
01300 height,
01301 width;
01302
01303 width=tile_width;
01304 if ((tile_x+(long) tile_width) > (long) image->columns)
01305 width=(unsigned long) (tile_width-(tile_x+tile_width-
01306 image->columns));
01307 height=tile_height;
01308 if ((tile_y+(long) tile_height) > (long) image->rows)
01309 height=(unsigned long) (tile_height-(tile_y+tile_height-
01310 image->rows));
01311 p=GetCacheViewVirtualPixels(image_view,tile_x,tile_y,width,
01312 height,exception);
01313 if (p == (const PixelPacket *) NULL)
01314 {
01315 status=MagickFalse;
01316 break;
01317 }
01318 indexes=GetCacheViewVirtualIndexQueue(image_view);
01319 for (y=0; y < (long) width; y++)
01320 {
01321 register const PixelPacket
01322 *restrict tile_pixels;
01323
01324 register long
01325 x;
01326
01327 q=QueueCacheViewAuthenticPixels(rotate_view,tile_y,(long)
01328 y+rotate_image->rows-(tile_x+width),height,1,exception);
01329 if (q == (PixelPacket *) NULL)
01330 {
01331 status=MagickFalse;
01332 break;
01333 }
01334 tile_pixels=p+(width-1)-y;
01335 for (x=0; x < (long) height; x++)
01336 {
01337 *q++=(*tile_pixels);
01338 tile_pixels+=width;
01339 }
01340 rotate_indexes=GetCacheViewAuthenticIndexQueue(rotate_view);
01341 if ((indexes != (IndexPacket *) NULL) &&
01342 (rotate_indexes != (IndexPacket *) NULL))
01343 {
01344 register const IndexPacket
01345 *restrict tile_indexes;
01346
01347 tile_indexes=indexes+(width-1)-y;
01348 for (x=0; x < (long) height; x++)
01349 {
01350 *rotate_indexes++=(*tile_indexes);
01351 tile_indexes+=width;
01352 }
01353 }
01354 sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
01355 if (sync == MagickFalse)
01356 status=MagickFalse;
01357 }
01358 }
01359 if (image->progress_monitor != (MagickProgressMonitor) NULL)
01360 {
01361 MagickBooleanType
01362 proceed;
01363
01364 proceed=SetImageProgress(image,RotateImageTag,progress+=tile_height,
01365 image->rows);
01366 if (proceed == MagickFalse)
01367 status=MagickFalse;
01368 }
01369 }
01370 (void) SetImageProgress(image,RotateImageTag,(MagickOffsetType)
01371 image->rows-1,image->rows);
01372 Swap(page.width,page.height);
01373 Swap(page.x,page.y);
01374 if (page.height != 0)
01375 page.y=(long) (page.height-rotate_image->rows-page.y);
01376 break;
01377 }
01378 }
01379 rotate_view=DestroyCacheView(rotate_view);
01380 image_view=DestroyCacheView(image_view);
01381 rotate_image->type=image->type;
01382 rotate_image->page=page;
01383 if (status == MagickFalse)
01384 rotate_image=DestroyImage(rotate_image);
01385 return(rotate_image);
01386 }
01387
01388
01389
01390
01391
01392
01393
01394
01395
01396
01397
01398
01399
01400
01401
01402
01403
01404
01405
01406
01407
01408
01409
01410
01411
01412
01413
01414
01415
01416
01417
01418
01419
01420
01421
01422
01423
01424 static MagickBooleanType XShearImage(Image *image,const MagickRealType degrees,
01425 const unsigned long width,const unsigned long height,const long x_offset,
01426 const long y_offset,ExceptionInfo *exception)
01427 {
01428 #define XShearImageTag "XShear/Image"
01429
01430 typedef enum
01431 {
01432 LEFT,
01433 RIGHT
01434 } ShearDirection;
01435
01436 CacheView
01437 *image_view;
01438
01439 long
01440 progress,
01441 y;
01442
01443 MagickBooleanType
01444 status;
01445
01446 MagickPixelPacket
01447 background;
01448
01449 assert(image != (Image *) NULL);
01450 assert(image->signature == MagickSignature);
01451 if (image->debug != MagickFalse)
01452 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01453 GetMagickPixelPacket(image,&background);
01454 SetMagickPixelPacket(image,&image->background_color,(IndexPacket *) NULL,
01455 &background);
01456 if (image->colorspace == CMYKColorspace)
01457 ConvertRGBToCMYK(&background);
01458
01459
01460
01461 status=MagickTrue;
01462 progress=0;
01463 image_view=AcquireCacheView(image);
01464 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01465 #pragma omp parallel for schedule(dynamic,4) shared(progress, status)
01466 #endif
01467 for (y=0; y < (long) height; y++)
01468 {
01469 long
01470 step;
01471
01472 MagickPixelPacket
01473 pixel,
01474 source,
01475 destination;
01476
01477 MagickRealType
01478 area,
01479 displacement;
01480
01481 register long
01482 i;
01483
01484 register IndexPacket
01485 *restrict indexes,
01486 *restrict shear_indexes;
01487
01488 register PixelPacket
01489 *restrict p,
01490 *restrict q;
01491
01492 ShearDirection
01493 direction;
01494
01495 if (status == MagickFalse)
01496 continue;
01497 p=GetCacheViewAuthenticPixels(image_view,0,y_offset+y,image->columns,1,
01498 exception);
01499 if (p == (PixelPacket *) NULL)
01500 {
01501 status=MagickFalse;
01502 continue;
01503 }
01504 indexes=GetCacheViewAuthenticIndexQueue(image_view);
01505 p+=x_offset;
01506 indexes+=x_offset;
01507 displacement=degrees*(MagickRealType) (y-height/2.0);
01508 if (displacement == 0.0)
01509 continue;
01510 if (displacement > 0.0)
01511 direction=RIGHT;
01512 else
01513 {
01514 displacement*=(-1.0);
01515 direction=LEFT;
01516 }
01517 step=(long) floor((double) displacement);
01518 area=(MagickRealType) (displacement-step);
01519 step++;
01520 pixel=background;
01521 GetMagickPixelPacket(image,&source);
01522 GetMagickPixelPacket(image,&destination);
01523 switch (direction)
01524 {
01525 case LEFT:
01526 {
01527
01528
01529
01530 if (step > x_offset)
01531 break;
01532 q=p-step;
01533 shear_indexes=indexes-step;
01534 for (i=0; i < (long) width; i++)
01535 {
01536 if ((x_offset+i) < step)
01537 {
01538 SetMagickPixelPacket(image,++p,++indexes,&pixel);
01539 q++;
01540 shear_indexes++;
01541 continue;
01542 }
01543 SetMagickPixelPacket(image,p,indexes,&source);
01544 MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
01545 &source,(MagickRealType) p->opacity,area,&destination);
01546 SetPixelPacket(image,&destination,q++,shear_indexes++);
01547 SetMagickPixelPacket(image,p++,indexes++,&pixel);
01548 }
01549 MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
01550 &background,(MagickRealType) background.opacity,area,&destination);
01551 SetPixelPacket(image,&destination,q++,shear_indexes++);
01552 for (i=0; i < (step-1); i++)
01553 SetPixelPacket(image,&background,q++,shear_indexes++);
01554 break;
01555 }
01556 case RIGHT:
01557 {
01558
01559
01560
01561 p+=width;
01562 indexes+=width;
01563 q=p+step;
01564 shear_indexes=indexes+step;
01565 for (i=0; i < (long) width; i++)
01566 {
01567 p--;
01568 indexes--;
01569 q--;
01570 shear_indexes--;
01571 if ((unsigned long) (x_offset+width+step-i) >= image->columns)
01572 continue;
01573 SetMagickPixelPacket(image,p,indexes,&source);
01574 MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
01575 &source,(MagickRealType) p->opacity,area,&destination);
01576 SetPixelPacket(image,&destination,q,shear_indexes);
01577 SetMagickPixelPacket(image,p,indexes,&pixel);
01578 }
01579 MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
01580 &background,(MagickRealType) background.opacity,area,&destination);
01581 SetPixelPacket(image,&destination,--q,--shear_indexes);
01582 for (i=0; i < (step-1); i++)
01583 SetPixelPacket(image,&background,--q,--shear_indexes);
01584 break;
01585 }
01586 }
01587 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
01588 status=MagickFalse;
01589 if (image->progress_monitor != (MagickProgressMonitor) NULL)
01590 {
01591 MagickBooleanType
01592 proceed;
01593
01594 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01595 #pragma omp critical (MagickCore_XShearImage)
01596 #endif
01597 proceed=SetImageProgress(image,XShearImageTag,progress++,height);
01598 if (proceed == MagickFalse)
01599 status=MagickFalse;
01600 }
01601 }
01602 image_view=DestroyCacheView(image_view);
01603 return(status);
01604 }
01605
01606
01607
01608
01609
01610
01611
01612
01613
01614
01615
01616
01617
01618
01619
01620
01621
01622
01623
01624
01625
01626
01627
01628
01629
01630
01631
01632
01633
01634
01635
01636
01637
01638
01639
01640
01641
01642 static MagickBooleanType YShearImage(Image *image,const MagickRealType degrees,
01643 const unsigned long width,const unsigned long height,const long x_offset,
01644 const long y_offset,ExceptionInfo *exception)
01645 {
01646 #define YShearImageTag "YShear/Image"
01647
01648 typedef enum
01649 {
01650 UP,
01651 DOWN
01652 } ShearDirection;
01653
01654 CacheView
01655 *image_view;
01656
01657 long
01658 progress,
01659 x;
01660
01661 MagickBooleanType
01662 status;
01663
01664 MagickPixelPacket
01665 background;
01666
01667 assert(image != (Image *) NULL);
01668 assert(image->signature == MagickSignature);
01669 if (image->debug != MagickFalse)
01670 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01671 GetMagickPixelPacket(image,&background);
01672 SetMagickPixelPacket(image,&image->background_color,(IndexPacket *) NULL,
01673 &background);
01674 if (image->colorspace == CMYKColorspace)
01675 ConvertRGBToCMYK(&background);
01676
01677
01678
01679 status=MagickTrue;
01680 progress=0;
01681 image_view=AcquireCacheView(image);
01682 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01683 #pragma omp parallel for schedule(dynamic,4) shared(progress, status)
01684 #endif
01685 for (x=0; x < (long) width; x++)
01686 {
01687 long
01688 step;
01689
01690 MagickPixelPacket
01691 pixel,
01692 source,
01693 destination;
01694
01695 MagickRealType
01696 area,
01697 displacement;
01698
01699 register IndexPacket
01700 *restrict indexes,
01701 *restrict shear_indexes;
01702
01703 register long
01704 i;
01705
01706 register PixelPacket
01707 *restrict p,
01708 *restrict q;
01709
01710 ShearDirection
01711 direction;
01712
01713 if (status == MagickFalse)
01714 continue;
01715 p=GetCacheViewAuthenticPixels(image_view,x_offset+x,0,1,image->rows,
01716 exception);
01717 if (p == (PixelPacket *) NULL)
01718 {
01719 status=MagickFalse;
01720 continue;
01721 }
01722 indexes=GetCacheViewAuthenticIndexQueue(image_view);
01723 p+=y_offset;
01724 indexes+=y_offset;
01725 displacement=degrees*(MagickRealType) (x-width/2.0);
01726 if (displacement == 0.0)
01727 continue;
01728 if (displacement > 0.0)
01729 direction=DOWN;
01730 else
01731 {
01732 displacement*=(-1.0);
01733 direction=UP;
01734 }
01735 step=(long) floor((double) displacement);
01736 area=(MagickRealType) (displacement-step);
01737 step++;
01738 pixel=background;
01739 GetMagickPixelPacket(image,&source);
01740 GetMagickPixelPacket(image,&destination);
01741 switch (direction)
01742 {
01743 case UP:
01744 {
01745
01746
01747
01748 if (step > y_offset)
01749 break;
01750 q=p-step;
01751 shear_indexes=indexes-step;
01752 for (i=0; i < (long) height; i++)
01753 {
01754 if ((y_offset+i) < step)
01755 {
01756 SetMagickPixelPacket(image,++p,++indexes,&pixel);
01757 q++;
01758 shear_indexes++;
01759 continue;
01760 }
01761 SetMagickPixelPacket(image,p,indexes,&source);
01762 MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
01763 &source,(MagickRealType) p->opacity,area,&destination);
01764 SetPixelPacket(image,&destination,q++,shear_indexes++);
01765 SetMagickPixelPacket(image,p++,indexes++,&pixel);
01766 }
01767 MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
01768 &background,(MagickRealType) background.opacity,area,&destination);
01769 SetPixelPacket(image,&destination,q++,shear_indexes++);
01770 for (i=0; i < (step-1); i++)
01771 SetPixelPacket(image,&background,q++,shear_indexes++);
01772 break;
01773 }
01774 case DOWN:
01775 {
01776
01777
01778
01779 p+=height;
01780 indexes+=height;
01781 q=p+step;
01782 shear_indexes=indexes+step;
01783 for (i=0; i < (long) height; i++)
01784 {
01785 p--;
01786 indexes--;
01787 q--;
01788 shear_indexes--;
01789 if ((unsigned long) (y_offset+height+step-i) >= image->rows)
01790 continue;
01791 SetMagickPixelPacket(image,p,indexes,&source);
01792 MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
01793 &source,(MagickRealType) p->opacity,area,&destination);
01794 SetPixelPacket(image,&destination,q,shear_indexes);
01795 SetMagickPixelPacket(image,p,indexes,&pixel);
01796 }
01797 MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
01798 &background,(MagickRealType) background.opacity,area,&destination);
01799 SetPixelPacket(image,&destination,--q,--shear_indexes);
01800 for (i=0; i < (step-1); i++)
01801 SetPixelPacket(image,&background,--q,--shear_indexes);
01802 break;
01803 }
01804 }
01805 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
01806 status=MagickFalse;
01807 if (image->progress_monitor != (MagickProgressMonitor) NULL)
01808 {
01809 MagickBooleanType
01810 proceed;
01811
01812 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01813 #pragma omp critical (MagickCore_YShearImage)
01814 #endif
01815 proceed=SetImageProgress(image,YShearImageTag,progress++,image->rows);
01816 if (proceed == MagickFalse)
01817 status=MagickFalse;
01818 }
01819 }
01820 image_view=DestroyCacheView(image_view);
01821 return(status);
01822 }
01823
01824
01825
01826
01827
01828
01829
01830
01831
01832
01833
01834
01835
01836
01837
01838
01839
01840
01841
01842
01843
01844
01845
01846
01847
01848
01849
01850
01851
01852
01853
01854
01855
01856
01857
01858
01859
01860
01861
01862
01863 MagickExport Image *RotateImage(const Image *image,const double degrees,
01864 ExceptionInfo *exception)
01865 {
01866 Image
01867 *integral_image,
01868 *rotate_image;
01869
01870 long
01871 x_offset,
01872 y_offset;
01873
01874 MagickBooleanType
01875 status;
01876
01877 MagickRealType
01878 angle;
01879
01880 PointInfo
01881 shear;
01882
01883 RectangleInfo
01884 border_info;
01885
01886 unsigned long
01887 height,
01888 rotations,
01889 width,
01890 y_width;
01891
01892
01893
01894
01895 assert(image != (Image *) NULL);
01896 assert(image->signature == MagickSignature);
01897 if (image->debug != MagickFalse)
01898 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01899 assert(exception != (ExceptionInfo *) NULL);
01900 assert(exception->signature == MagickSignature);
01901 angle=degrees;
01902 while (angle < -45.0)
01903 angle+=360.0;
01904 for (rotations=0; angle > 45.0; rotations++)
01905 angle-=90.0;
01906 rotations%=4;
01907
01908
01909
01910 integral_image=IntegralRotateImage(image,rotations,exception);
01911 if (integral_image == (Image *) NULL)
01912 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01913 shear.x=(-tan((double) DegreesToRadians(angle)/2.0));
01914 shear.y=sin((double) DegreesToRadians(angle));
01915 if ((shear.x == 0.0) && (shear.y == 0.0))
01916 return(integral_image);
01917 if (SetImageStorageClass(integral_image,DirectClass) == MagickFalse)
01918 {
01919 InheritException(exception,&integral_image->exception);
01920 integral_image=DestroyImage(integral_image);
01921 return(integral_image);
01922 }
01923 if (integral_image->matte == MagickFalse)
01924 (void) SetImageAlphaChannel(integral_image,OpaqueAlphaChannel);
01925
01926
01927
01928 width=image->columns;
01929 height=image->rows;
01930 if ((rotations == 1) || (rotations == 3))
01931 {
01932 width=image->rows;
01933 height=image->columns;
01934 }
01935 y_width=width+(long) floor(fabs(shear.x)*height+0.5);
01936 x_offset=(long) ceil(width+((fabs(shear.y)*height)-width)/2.0-0.5);
01937 y_offset=(long) ceil(height+((fabs(shear.y)*y_width)-height)/2.0-0.5);
01938
01939
01940
01941 integral_image->border_color=integral_image->background_color;
01942 integral_image->compose=CopyCompositeOp;
01943 border_info.width=(unsigned long) x_offset;
01944 border_info.height=(unsigned long) y_offset;
01945 rotate_image=BorderImage(integral_image,&border_info,exception);
01946 integral_image=DestroyImage(integral_image);
01947 if (rotate_image == (Image *) NULL)
01948 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01949
01950
01951
01952 status=XShearImage(rotate_image,shear.x,width,height,x_offset,((long)
01953 rotate_image->rows-height)/2,exception);
01954 if (status == MagickFalse)
01955 {
01956 rotate_image=DestroyImage(rotate_image);
01957 return((Image *) NULL);
01958 }
01959 status=YShearImage(rotate_image,shear.y,y_width,height,((long)
01960 rotate_image->columns-y_width)/2,y_offset,exception);
01961 if (status == MagickFalse)
01962 {
01963 rotate_image=DestroyImage(rotate_image);
01964 return((Image *) NULL);
01965 }
01966 status=XShearImage(rotate_image,shear.x,y_width,rotate_image->rows,((long)
01967 rotate_image->columns-y_width)/2,0,exception);
01968 if (status == MagickFalse)
01969 {
01970 rotate_image=DestroyImage(rotate_image);
01971 return((Image *) NULL);
01972 }
01973 status=CropToFitImage(&rotate_image,shear.x,shear.y,(MagickRealType) width,
01974 (MagickRealType) height,MagickTrue,exception);
01975 if (status == MagickFalse)
01976 {
01977 rotate_image=DestroyImage(rotate_image);
01978 return((Image *) NULL);
01979 }
01980 rotate_image->compose=image->compose;
01981 rotate_image->page.width=0;
01982 rotate_image->page.height=0;
01983 return(rotate_image);
01984 }
01985
01986
01987
01988
01989
01990
01991
01992
01993
01994
01995
01996
01997
01998
01999
02000
02001
02002
02003
02004
02005
02006
02007
02008
02009
02010
02011
02012
02013
02014
02015
02016
02017
02018
02019
02020
02021
02022
02023
02024
02025 MagickExport Image *ShearImage(const Image *image,const double x_shear,
02026 const double y_shear,ExceptionInfo *exception)
02027 {
02028 Image
02029 *integral_image,
02030 *shear_image;
02031
02032 long
02033 x_offset,
02034 y_offset;
02035
02036 MagickBooleanType
02037 status;
02038
02039 PointInfo
02040 shear;
02041
02042 RectangleInfo
02043 border_info;
02044
02045 unsigned long
02046 y_width;
02047
02048 assert(image != (Image *) NULL);
02049 assert(image->signature == MagickSignature);
02050 if (image->debug != MagickFalse)
02051 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02052 assert(exception != (ExceptionInfo *) NULL);
02053 assert(exception->signature == MagickSignature);
02054 if ((x_shear != 0.0) && (fmod(x_shear,90.0) == 0.0))
02055 ThrowImageException(ImageError,"AngleIsDiscontinuous");
02056 if ((y_shear != 0.0) && (fmod(y_shear,90.0) == 0.0))
02057 ThrowImageException(ImageError,"AngleIsDiscontinuous");
02058
02059
02060
02061 integral_image=CloneImage(image,0,0,MagickTrue,exception);
02062 if (integral_image == (Image *) NULL)
02063 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
02064 shear.x=(-tan(DegreesToRadians(fmod(x_shear,360.0))));
02065 shear.y=tan(DegreesToRadians(fmod(y_shear,360.0)));
02066 if ((shear.x == 0.0) && (shear.y == 0.0))
02067 return(integral_image);
02068 if (SetImageStorageClass(integral_image,DirectClass) == MagickFalse)
02069 {
02070 InheritException(exception,&integral_image->exception);
02071 integral_image=DestroyImage(integral_image);
02072 return(integral_image);
02073 }
02074 if (integral_image->matte == MagickFalse)
02075 (void) SetImageAlphaChannel(integral_image,OpaqueAlphaChannel);
02076
02077
02078
02079 y_width=image->columns+(long) floor(fabs(shear.x)*image->rows+0.5);
02080 x_offset=(long) ceil(image->columns+((fabs(shear.x)*image->rows)-
02081 image->columns)/2.0-0.5);
02082 y_offset=(long) ceil(image->rows+((fabs(shear.y)*y_width)-image->rows)/2.0-
02083 0.5);
02084
02085
02086
02087 integral_image->border_color=integral_image->background_color;
02088 integral_image->compose=CopyCompositeOp;
02089 border_info.width=(unsigned long) x_offset;
02090 border_info.height=(unsigned long) y_offset;
02091 shear_image=BorderImage(integral_image,&border_info,exception);
02092 integral_image=DestroyImage(integral_image);
02093 if (shear_image == (Image *) NULL)
02094 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
02095
02096
02097
02098 if (shear_image->matte == MagickFalse)
02099 (void) SetImageAlphaChannel(shear_image,OpaqueAlphaChannel);
02100 status=XShearImage(shear_image,shear.x,image->columns,image->rows,x_offset,
02101 ((long) shear_image->rows-image->rows)/2,exception);
02102 if (status == MagickFalse)
02103 {
02104 shear_image=DestroyImage(shear_image);
02105 return((Image *) NULL);
02106 }
02107 status=YShearImage(shear_image,shear.y,y_width,image->rows,((long)
02108 shear_image->columns-y_width)/2,y_offset,exception);
02109 if (status == MagickFalse)
02110 {
02111 shear_image=DestroyImage(shear_image);
02112 return((Image *) NULL);
02113 }
02114 status=CropToFitImage(&shear_image,shear.x,shear.y,(MagickRealType)
02115 image->columns,(MagickRealType) image->rows,MagickFalse,exception);
02116 if (status == MagickFalse)
02117 {
02118 shear_image=DestroyImage(shear_image);
02119 return((Image *) NULL);
02120 }
02121 shear_image->compose=image->compose;
02122 shear_image->page.width=0;
02123 shear_image->page.height=0;
02124 return(shear_image);
02125 }