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