decorate.c revision b09db11cc8fa61710ac2bc9c46c06733c39d67b7
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%            DDDD   EEEEE   CCCC   OOO   RRRR    AAA   TTTTT  EEEEE           %
7%            D   D  E      C      O   O  R   R  A   A    T    E               %
8%            D   D  EEE    C      O   O  RRRR   AAAAA    T    EEE             %
9%            D   D  E      C      O   O  R R    A   A    T    E               %
10%            DDDD   EEEEE   CCCC   OOO   R  R   A   A    T    EEEEE           %
11%                                                                             %
12%                                                                             %
13%                     MagickCore Image Decoration Methods                     %
14%                                                                             %
15%                                Software Design                              %
16%                                  John Cristy                                %
17%                                   July 1992                                 %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
21%  dedicated to making software imaging solutions freely available.           %
22%                                                                             %
23%  You may not use this file except in compliance with the License.  You may  %
24%  obtain a copy of the License at                                            %
25%                                                                             %
26%    http://www.imagemagick.org/script/license.php                            %
27%                                                                             %
28%  Unless required by applicable law or agreed to in writing, software        %
29%  distributed under the License is distributed on an "AS IS" BASIS,          %
30%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31%  See the License for the specific language governing permissions and        %
32%  limitations under the License.                                             %
33%                                                                             %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41  Include declarations.
42*/
43#include "MagickCore/studio.h"
44#include "MagickCore/cache-view.h"
45#include "MagickCore/color-private.h"
46#include "MagickCore/colorspace-private.h"
47#include "MagickCore/composite.h"
48#include "MagickCore/decorate.h"
49#include "MagickCore/exception.h"
50#include "MagickCore/exception-private.h"
51#include "MagickCore/image.h"
52#include "MagickCore/memory_.h"
53#include "MagickCore/monitor.h"
54#include "MagickCore/monitor-private.h"
55#include "MagickCore/pixel-accessor.h"
56#include "MagickCore/quantum.h"
57#include "MagickCore/quantum-private.h"
58#include "MagickCore/resource_.h"
59#include "MagickCore/thread-private.h"
60#include "MagickCore/transform.h"
61
62/*
63  Define declarations.
64*/
65#define AccentuateModulate  ScaleCharToQuantum(80)
66#define HighlightModulate  ScaleCharToQuantum(125)
67#define ShadowModulate  ScaleCharToQuantum(135)
68#define DepthModulate  ScaleCharToQuantum(185)
69#define TroughModulate  ScaleCharToQuantum(110)
70
71/*
72%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
73%                                                                             %
74%                                                                             %
75%                                                                             %
76%   B o r d e r I m a g e                                                     %
77%                                                                             %
78%                                                                             %
79%                                                                             %
80%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
81%
82%  BorderImage() surrounds the image with a border of the color defined by
83%  the bordercolor member of the image structure.  The width and height
84%  of the border are defined by the corresponding members of the border_info
85%  structure.
86%
87%  The format of the BorderImage method is:
88%
89%      Image *BorderImage(const Image *image,const RectangleInfo *border_info,
90%        const CompositeOperator compose,ExceptionInfo *exception)
91%
92%  A description of each parameter follows:
93%
94%    o image: the image.
95%
96%    o border_info:  define the width and height of the border.
97%
98%    o compose:  the composite operator.
99%
100%    o exception: return any errors or warnings in this structure.
101%
102*/
103MagickExport Image *BorderImage(const Image *image,
104  const RectangleInfo *border_info,const CompositeOperator compose,
105  ExceptionInfo *exception)
106{
107  Image
108    *border_image,
109    *clone_image;
110
111  FrameInfo
112    frame_info;
113
114  assert(image != (const Image *) NULL);
115  assert(image->signature == MagickSignature);
116  if (image->debug != MagickFalse)
117    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
118  assert(border_info != (RectangleInfo *) NULL);
119  frame_info.width=image->columns+(border_info->width << 1);
120  frame_info.height=image->rows+(border_info->height << 1);
121  frame_info.x=(ssize_t) border_info->width;
122  frame_info.y=(ssize_t) border_info->height;
123  frame_info.inner_bevel=0;
124  frame_info.outer_bevel=0;
125  clone_image=CloneImage(image,0,0,MagickTrue,exception);
126  if (clone_image == (Image *) NULL)
127    return((Image *) NULL);
128  clone_image->matte_color=image->border_color;
129  border_image=FrameImage(clone_image,&frame_info,compose,exception);
130  clone_image=DestroyImage(clone_image);
131  if (border_image != (Image *) NULL)
132    border_image->matte_color=image->matte_color;
133  return(border_image);
134}
135
136/*
137%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
138%                                                                             %
139%                                                                             %
140%                                                                             %
141%   F r a m e I m a g e                                                       %
142%                                                                             %
143%                                                                             %
144%                                                                             %
145%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
146%
147%  FrameImage() adds a simulated three-dimensional border around the image.
148%  The color of the border is defined by the matte_color member of image.
149%  Members width and height of frame_info specify the border width of the
150%  vertical and horizontal sides of the frame.  Members inner and outer
151%  indicate the width of the inner and outer shadows of the frame.
152%
153%  The format of the FrameImage method is:
154%
155%      Image *FrameImage(const Image *image,const FrameInfo *frame_info,
156%        const CompositeOperator compose,ExceptionInfo *exception)
157%
158%  A description of each parameter follows:
159%
160%    o image: the image.
161%
162%    o frame_info: Define the width and height of the frame and its bevels.
163%
164%    o compose: the composite operator.
165%
166%    o exception: return any errors or warnings in this structure.
167%
168*/
169MagickExport Image *FrameImage(const Image *image,const FrameInfo *frame_info,
170  const CompositeOperator compose,ExceptionInfo *exception)
171{
172#define FrameImageTag  "Frame/Image"
173
174  CacheView
175    *image_view,
176    *frame_view;
177
178  Image
179    *frame_image;
180
181  MagickBooleanType
182    status;
183
184  MagickOffsetType
185    progress;
186
187  PixelInfo
188    accentuate,
189    highlight,
190    interior,
191    matte,
192    shadow,
193    trough;
194
195  register ssize_t
196    x;
197
198  size_t
199    bevel_width,
200    height,
201    width;
202
203  ssize_t
204    y;
205
206  /*
207    Check frame geometry.
208  */
209  assert(image != (Image *) NULL);
210  assert(image->signature == MagickSignature);
211  if (image->debug != MagickFalse)
212    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
213  assert(frame_info != (FrameInfo *) NULL);
214  if ((frame_info->outer_bevel < 0) || (frame_info->inner_bevel < 0))
215    ThrowImageException(OptionError,"FrameIsLessThanImageSize");
216  bevel_width=(size_t) (frame_info->outer_bevel+frame_info->inner_bevel);
217  width=frame_info->width-frame_info->x-bevel_width;
218  height=frame_info->height-frame_info->y-bevel_width;
219  if ((width < image->columns) || (height < image->rows))
220    ThrowImageException(OptionError,"FrameIsLessThanImageSize");
221  /*
222    Initialize framed image attributes.
223  */
224  frame_image=CloneImage(image,frame_info->width,frame_info->height,MagickTrue,
225    exception);
226  if (frame_image == (Image *) NULL)
227    return((Image *) NULL);
228  if (SetImageStorageClass(frame_image,DirectClass,exception) == MagickFalse)
229    {
230      frame_image=DestroyImage(frame_image);
231      return((Image *) NULL);
232    }
233  if ((IsPixelInfoGray(&frame_image->matte_color) == MagickFalse) &&
234      (IsGrayColorspace(frame_image->colorspace) != MagickFalse))
235    (void) SetImageColorspace(frame_image,RGBColorspace,exception);
236  if ((frame_image->matte_color.matte != MagickFalse) &&
237      (frame_image->matte == MagickFalse))
238    (void) SetImageAlpha(frame_image,OpaqueAlpha,exception);
239  frame_image->page=image->page;
240  if ((image->page.width != 0) && (image->page.height != 0))
241    {
242      frame_image->page.width+=frame_image->columns-image->columns;
243      frame_image->page.height+=frame_image->rows-image->rows;
244    }
245  /*
246    Initialize 3D effects color.
247  */
248  interior=image->border_color;
249  matte=image->matte_color;
250  accentuate=matte;
251  accentuate.red=(MagickRealType) (QuantumScale*((QuantumRange-
252    AccentuateModulate)*matte.red+(QuantumRange*AccentuateModulate)));
253  accentuate.green=(MagickRealType) (QuantumScale*((QuantumRange-
254    AccentuateModulate)*matte.green+(QuantumRange*AccentuateModulate)));
255  accentuate.blue=(MagickRealType) (QuantumScale*((QuantumRange-
256    AccentuateModulate)*matte.blue+(QuantumRange*AccentuateModulate)));
257  accentuate.black=(MagickRealType) (QuantumScale*((QuantumRange-
258    AccentuateModulate)*matte.black+(QuantumRange*AccentuateModulate)));
259  accentuate.alpha=matte.alpha;
260  highlight=matte;
261  highlight.red=(MagickRealType) (QuantumScale*((QuantumRange-
262    HighlightModulate)*matte.red+(QuantumRange*HighlightModulate)));
263  highlight.green=(MagickRealType) (QuantumScale*((QuantumRange-
264    HighlightModulate)*matte.green+(QuantumRange*HighlightModulate)));
265  highlight.blue=(MagickRealType) (QuantumScale*((QuantumRange-
266    HighlightModulate)*matte.blue+(QuantumRange*HighlightModulate)));
267  highlight.black=(MagickRealType) (QuantumScale*((QuantumRange-
268    HighlightModulate)*matte.black+(QuantumRange*HighlightModulate)));
269  highlight.alpha=matte.alpha;
270  shadow=matte;
271  shadow.red=QuantumScale*matte.red*ShadowModulate;
272  shadow.green=QuantumScale*matte.green*ShadowModulate;
273  shadow.blue=QuantumScale*matte.blue*ShadowModulate;
274  shadow.black=QuantumScale*matte.black*ShadowModulate;
275  shadow.alpha=matte.alpha;
276  trough=matte;
277  trough.red=QuantumScale*matte.red*TroughModulate;
278  trough.green=QuantumScale*matte.green*TroughModulate;
279  trough.blue=QuantumScale*matte.blue*TroughModulate;
280  trough.black=QuantumScale*matte.black*TroughModulate;
281  trough.alpha=matte.alpha;
282  status=MagickTrue;
283  progress=0;
284  image_view=AcquireVirtualCacheView(image,exception);
285  frame_view=AcquireAuthenticCacheView(frame_image,exception);
286  height=(size_t) (frame_info->outer_bevel+(frame_info->y-bevel_width)+
287    frame_info->inner_bevel);
288  if (height != 0)
289    {
290      register ssize_t
291        x;
292
293      register Quantum
294        *restrict q;
295
296      /*
297        Draw top of ornamental border.
298      */
299      q=QueueCacheViewAuthenticPixels(frame_view,0,0,frame_image->columns,
300        height,exception);
301      if (q != (Quantum *) NULL)
302        {
303          /*
304            Draw top of ornamental border.
305          */
306          for (y=0; y < (ssize_t) frame_info->outer_bevel; y++)
307          {
308            for (x=0; x < (ssize_t) (frame_image->columns-y); x++)
309            {
310              if (x < y)
311                SetPixelInfoPixel(frame_image,&highlight,q);
312              else
313                SetPixelInfoPixel(frame_image,&accentuate,q);
314              q+=GetPixelChannels(frame_image);
315            }
316            for ( ; x < (ssize_t) frame_image->columns; x++)
317            {
318              SetPixelInfoPixel(frame_image,&shadow,q);
319              q+=GetPixelChannels(frame_image);
320            }
321          }
322          for (y=0; y < (ssize_t) (frame_info->y-bevel_width); y++)
323          {
324            for (x=0; x < (ssize_t) frame_info->outer_bevel; x++)
325            {
326              SetPixelInfoPixel(frame_image,&highlight,q);
327              q+=GetPixelChannels(frame_image);
328            }
329            width=frame_image->columns-2*frame_info->outer_bevel;
330            for (x=0; x < (ssize_t) width; x++)
331            {
332              SetPixelInfoPixel(frame_image,&matte,q);
333              q+=GetPixelChannels(frame_image);
334            }
335            for (x=0; x < (ssize_t) frame_info->outer_bevel; x++)
336            {
337              SetPixelInfoPixel(frame_image,&shadow,q);
338              q+=GetPixelChannels(frame_image);
339            }
340          }
341          for (y=0; y < (ssize_t) frame_info->inner_bevel; y++)
342          {
343            for (x=0; x < (ssize_t) frame_info->outer_bevel; x++)
344            {
345              SetPixelInfoPixel(frame_image,&highlight,q);
346              q+=GetPixelChannels(frame_image);
347            }
348            for (x=0; x < (ssize_t) (frame_info->x-bevel_width); x++)
349            {
350              SetPixelInfoPixel(frame_image,&matte,q);
351              q+=GetPixelChannels(frame_image);
352            }
353            width=image->columns+((size_t) frame_info->inner_bevel << 1)-
354              y;
355            for (x=0; x < (ssize_t) width; x++)
356            {
357              if (x < y)
358                SetPixelInfoPixel(frame_image,&shadow,q);
359              else
360                SetPixelInfoPixel(frame_image,&trough,q);
361              q+=GetPixelChannels(frame_image);
362            }
363            for ( ; x < (ssize_t) (image->columns+2*frame_info->inner_bevel); x++)
364            {
365              SetPixelInfoPixel(frame_image,&highlight,q);
366              q+=GetPixelChannels(frame_image);
367            }
368            width=frame_info->width-frame_info->x-image->columns-bevel_width;
369            for (x=0; x < (ssize_t) width; x++)
370            {
371              SetPixelInfoPixel(frame_image,&matte,q);
372              q+=GetPixelChannels(frame_image);
373            }
374            for (x=0; x < (ssize_t) frame_info->outer_bevel; x++)
375            {
376              SetPixelInfoPixel(frame_image,&shadow,q);
377              q+=GetPixelChannels(frame_image);
378            }
379          }
380          (void) SyncCacheViewAuthenticPixels(frame_view,exception);
381        }
382    }
383  /*
384    Draw sides of ornamental border.
385  */
386#if defined(MAGICKCORE_OPENMP_SUPPORT)
387  #pragma omp parallel for schedule(static) shared(progress,status) \
388    dynamic_number_threads(image,image->columns,image->rows,1)
389#endif
390  for (y=0; y < (ssize_t) image->rows; y++)
391  {
392    register ssize_t
393      x;
394
395    register Quantum
396      *restrict q;
397
398    size_t
399      width;
400
401    /*
402      Initialize scanline with matte color.
403    */
404    if (status == MagickFalse)
405      continue;
406    q=QueueCacheViewAuthenticPixels(frame_view,0,frame_info->y+y,
407      frame_image->columns,1,exception);
408    if (q == (Quantum *) NULL)
409      {
410        status=MagickFalse;
411        continue;
412      }
413    for (x=0; x < (ssize_t) frame_info->outer_bevel; x++)
414    {
415      SetPixelInfoPixel(frame_image,&highlight,q);
416      q+=GetPixelChannels(frame_image);
417    }
418    for (x=0; x < (ssize_t) (frame_info->x-bevel_width); x++)
419    {
420      SetPixelInfoPixel(frame_image,&matte,q);
421      q+=GetPixelChannels(frame_image);
422    }
423    for (x=0; x < (ssize_t) frame_info->inner_bevel; x++)
424    {
425      SetPixelInfoPixel(frame_image,&shadow,q);
426      q+=GetPixelChannels(frame_image);
427    }
428    /*
429      Set frame interior to interior color.
430    */
431    if ((compose != CopyCompositeOp) && ((compose != OverCompositeOp) ||
432        (image->matte != MagickFalse)))
433      for (x=0; x < (ssize_t) image->columns; x++)
434      {
435        SetPixelInfoPixel(frame_image,&interior,q);
436        q+=GetPixelChannels(frame_image);
437      }
438    else
439      {
440        register const Quantum
441          *p;
442
443        p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
444        if (p == (const Quantum *) NULL)
445          {
446            status=MagickFalse;
447            continue;
448          }
449        for (x=0; x < (ssize_t) image->columns; x++)
450        {
451          if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
452            SetPixelRed(frame_image,GetPixelRed(image,p),q);
453          if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
454            SetPixelGreen(frame_image,GetPixelGreen(image,p),q);
455          if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
456            SetPixelBlue(frame_image,GetPixelBlue(image,p),q);
457          if ((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0)
458            SetPixelBlack(frame_image,GetPixelBlack(image,p),q);
459          if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
460            SetPixelAlpha(frame_image,GetPixelAlpha(image,p),q);
461          p+=GetPixelChannels(image);
462          q+=GetPixelChannels(frame_image);
463        }
464      }
465    for (x=0; x < (ssize_t) frame_info->inner_bevel; x++)
466    {
467      SetPixelInfoPixel(frame_image,&highlight,q);
468      q+=GetPixelChannels(frame_image);
469    }
470    width=frame_info->width-frame_info->x-image->columns-bevel_width;
471    for (x=0; x < (ssize_t) width; x++)
472    {
473      SetPixelInfoPixel(frame_image,&matte,q);
474      q+=GetPixelChannels(frame_image);
475    }
476    for (x=0; x < (ssize_t) frame_info->outer_bevel; x++)
477    {
478      SetPixelInfoPixel(frame_image,&shadow,q);
479      q+=GetPixelChannels(frame_image);
480    }
481    if (SyncCacheViewAuthenticPixels(frame_view,exception) == MagickFalse)
482      status=MagickFalse;
483    if (image->progress_monitor != (MagickProgressMonitor) NULL)
484      {
485        MagickBooleanType
486          proceed;
487
488#if defined(MAGICKCORE_OPENMP_SUPPORT)
489        #pragma omp critical (MagickCore_FrameImage)
490#endif
491        proceed=SetImageProgress(image,FrameImageTag,progress++,image->rows);
492        if (proceed == MagickFalse)
493          status=MagickFalse;
494      }
495  }
496  height=(size_t) (frame_info->inner_bevel+frame_info->height-
497    frame_info->y-image->rows-bevel_width+frame_info->outer_bevel);
498  if (height != 0)
499    {
500      register ssize_t
501        x;
502
503      register Quantum
504        *restrict q;
505
506      /*
507        Draw bottom of ornamental border.
508      */
509      q=QueueCacheViewAuthenticPixels(frame_view,0,(ssize_t) (frame_image->rows-
510        height),frame_image->columns,height,exception);
511      if (q != (Quantum *) NULL)
512        {
513          /*
514            Draw bottom of ornamental border.
515          */
516          for (y=frame_info->inner_bevel-1; y >= 0; y--)
517          {
518            for (x=0; x < (ssize_t) frame_info->outer_bevel; x++)
519            {
520              SetPixelInfoPixel(frame_image,&highlight,q);
521              q+=GetPixelChannels(frame_image);
522            }
523            for (x=0; x < (ssize_t) (frame_info->x-bevel_width); x++)
524            {
525              SetPixelInfoPixel(frame_image,&matte,q);
526              q+=GetPixelChannels(frame_image);
527            }
528            for (x=0; x < y; x++)
529            {
530              SetPixelInfoPixel(frame_image,&shadow,q);
531              q+=GetPixelChannels(frame_image);
532            }
533            for ( ; x < (ssize_t) (image->columns+2*frame_info->inner_bevel); x++)
534            {
535              if (x >= (ssize_t) (image->columns+2*frame_info->inner_bevel-y))
536                SetPixelInfoPixel(frame_image,&highlight,q);
537              else
538                SetPixelInfoPixel(frame_image,&accentuate,q);
539              q+=GetPixelChannels(frame_image);
540            }
541            width=frame_info->width-frame_info->x-image->columns-bevel_width;
542            for (x=0; x < (ssize_t) width; x++)
543            {
544              SetPixelInfoPixel(frame_image,&matte,q);
545              q+=GetPixelChannels(frame_image);
546            }
547            for (x=0; x < (ssize_t) frame_info->outer_bevel; x++)
548            {
549              SetPixelInfoPixel(frame_image,&shadow,q);
550              q+=GetPixelChannels(frame_image);
551            }
552          }
553          height=frame_info->height-frame_info->y-image->rows-bevel_width;
554          for (y=0; y < (ssize_t) height; y++)
555          {
556            for (x=0; x < (ssize_t) frame_info->outer_bevel; x++)
557            {
558              SetPixelInfoPixel(frame_image,&highlight,q);
559              q+=GetPixelChannels(frame_image);
560            }
561            width=frame_image->columns-2*frame_info->outer_bevel;
562            for (x=0; x < (ssize_t) width; x++)
563            {
564              SetPixelInfoPixel(frame_image,&matte,q);
565              q+=GetPixelChannels(frame_image);
566            }
567            for (x=0; x < (ssize_t) frame_info->outer_bevel; x++)
568            {
569              SetPixelInfoPixel(frame_image,&shadow,q);
570              q+=GetPixelChannels(frame_image);
571            }
572          }
573          for (y=frame_info->outer_bevel-1; y >= 0; y--)
574          {
575            for (x=0; x < y; x++)
576            {
577              SetPixelInfoPixel(frame_image,&highlight,q);
578              q+=GetPixelChannels(frame_image);
579            }
580            for ( ; x < (ssize_t) frame_image->columns; x++)
581            {
582              if (x >= (ssize_t) (frame_image->columns-y))
583                SetPixelInfoPixel(frame_image,&shadow,q);
584              else
585                SetPixelInfoPixel(frame_image,&trough,q);
586              q+=GetPixelChannels(frame_image);
587            }
588          }
589          (void) SyncCacheViewAuthenticPixels(frame_view,exception);
590        }
591    }
592  frame_view=DestroyCacheView(frame_view);
593  image_view=DestroyCacheView(image_view);
594  if ((compose != CopyCompositeOp) && ((compose != OverCompositeOp) ||
595      (image->matte != MagickFalse)))
596    {
597      x=(ssize_t) (frame_info->outer_bevel+(frame_info->x-bevel_width)+
598        frame_info->inner_bevel);
599      y=(ssize_t) (frame_info->outer_bevel+(frame_info->y-bevel_width)+
600        frame_info->inner_bevel);
601      (void) CompositeImage(frame_image,image,compose,MagickTrue,x,y,
602        exception);
603    }
604  return(frame_image);
605}
606
607/*
608%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
609%                                                                             %
610%                                                                             %
611%                                                                             %
612%   R a i s e I m a g e                                                       %
613%                                                                             %
614%                                                                             %
615%                                                                             %
616%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
617%
618%  RaiseImage() creates a simulated three-dimensional button-like effect
619%  by lightening and darkening the edges of the image.  Members width and
620%  height of raise_info define the width of the vertical and horizontal
621%  edge of the effect.
622%
623%  The format of the RaiseImage method is:
624%
625%      MagickBooleanType RaiseImage(const Image *image,
626%        const RectangleInfo *raise_info,const MagickBooleanType raise,
627%        ExceptionInfo *exception)
628%
629%  A description of each parameter follows:
630%
631%    o image: the image.
632%
633%    o raise_info: Define the width and height of the raise area.
634%
635%    o raise: A value other than zero creates a 3-D raise effect,
636%      otherwise it has a lowered effect.
637%
638%    o exception: return any errors or warnings in this structure.
639%
640*/
641MagickExport MagickBooleanType RaiseImage(Image *image,
642  const RectangleInfo *raise_info,const MagickBooleanType raise,
643  ExceptionInfo *exception)
644{
645#define AccentuateFactor  ScaleCharToQuantum(135)
646#define HighlightFactor  ScaleCharToQuantum(190)
647#define ShadowFactor  ScaleCharToQuantum(190)
648#define RaiseImageTag  "Raise/Image"
649#define TroughFactor  ScaleCharToQuantum(135)
650
651  CacheView
652    *image_view;
653
654  MagickBooleanType
655    status;
656
657  MagickOffsetType
658    progress;
659
660  Quantum
661    foreground,
662    background;
663
664  ssize_t
665    y;
666
667  assert(image != (Image *) NULL);
668  assert(image->signature == MagickSignature);
669  if (image->debug != MagickFalse)
670    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
671  assert(raise_info != (RectangleInfo *) NULL);
672  if ((image->columns <= (raise_info->width << 1)) ||
673      (image->rows <= (raise_info->height << 1)))
674    ThrowBinaryException(OptionError,"ImageSizeMustExceedBevelWidth",
675      image->filename);
676  foreground=QuantumRange;
677  background=(Quantum) 0;
678  if (raise == MagickFalse)
679    {
680      foreground=(Quantum) 0;
681      background=QuantumRange;
682    }
683  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
684    return(MagickFalse);
685  /*
686    Raise image.
687  */
688  status=MagickTrue;
689  progress=0;
690  image_view=AcquireAuthenticCacheView(image,exception);
691#if defined(MAGICKCORE_OPENMP_SUPPORT)
692  #pragma omp parallel for schedule(static) shared(progress,status) \
693    dynamic_number_threads(image,image->columns,image->rows,1)
694#endif
695  for (y=0; y < (ssize_t) raise_info->height; y++)
696  {
697    register ssize_t
698      i,
699      x;
700
701    register Quantum
702      *restrict q;
703
704    if (status == MagickFalse)
705      continue;
706    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
707    if (q == (Quantum *) NULL)
708      {
709        status=MagickFalse;
710        continue;
711      }
712    for (x=0; x < y; x++)
713    {
714      if (GetPixelMask(image,q) != 0)
715        {
716          q+=GetPixelChannels(image);
717          continue;
718        }
719      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
720      {
721        PixelChannel
722          channel;
723
724        PixelTrait
725          traits;
726
727        channel=GetPixelChannelMapChannel(image,i);
728        traits=GetPixelChannelMapTraits(image,channel);
729        if ((traits & UpdatePixelTrait) == 0)
730          continue;
731        q[i]=ClampToQuantum(QuantumScale*((MagickRealType) q[i]*HighlightFactor+
732          (MagickRealType) foreground*(QuantumRange-HighlightFactor)));
733      }
734      q+=GetPixelChannels(image);
735    }
736    for ( ; x < (ssize_t) (image->columns-y); x++)
737    {
738      if (GetPixelMask(image,q) != 0)
739        {
740          q+=GetPixelChannels(image);
741          continue;
742        }
743      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
744      {
745        PixelChannel
746          channel;
747
748        PixelTrait
749          traits;
750
751        channel=GetPixelChannelMapChannel(image,i);
752        traits=GetPixelChannelMapTraits(image,channel);
753        if ((traits & UpdatePixelTrait) == 0)
754          continue;
755        q[i]=ClampToQuantum(QuantumScale*((MagickRealType) q[i]*
756          AccentuateFactor+(MagickRealType) foreground*(QuantumRange-
757          AccentuateFactor)));
758      }
759      q+=GetPixelChannels(image);
760    }
761    for ( ; x < (ssize_t) image->columns; x++)
762    {
763      if (GetPixelMask(image,q) != 0)
764        {
765          q+=GetPixelChannels(image);
766          continue;
767        }
768      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
769      {
770        PixelChannel
771          channel;
772
773        PixelTrait
774          traits;
775
776        channel=GetPixelChannelMapChannel(image,i);
777        traits=GetPixelChannelMapTraits(image,channel);
778        if ((traits & UpdatePixelTrait) == 0)
779          continue;
780        q[i]=ClampToQuantum(QuantumScale*((MagickRealType) q[i]*ShadowFactor+
781          (MagickRealType) background*(QuantumRange-ShadowFactor)));
782      }
783      q+=GetPixelChannels(image);
784    }
785    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
786      status=MagickFalse;
787    if (image->progress_monitor != (MagickProgressMonitor) NULL)
788      {
789        MagickBooleanType
790          proceed;
791
792        proceed=SetImageProgress(image,RaiseImageTag,progress++,image->rows);
793        if (proceed == MagickFalse)
794          status=MagickFalse;
795      }
796  }
797#if defined(MAGICKCORE_OPENMP_SUPPORT)
798  #pragma omp parallel for schedule(static) shared(progress,status) \
799    dynamic_number_threads(image,image->columns,image->rows,1)
800#endif
801  for (y=(ssize_t) raise_info->height; y < (ssize_t) (image->rows-raise_info->height); y++)
802  {
803    register ssize_t
804      i,
805      x;
806
807    register Quantum
808      *restrict q;
809
810    if (status == MagickFalse)
811      continue;
812    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
813    if (q == (Quantum *) NULL)
814      {
815        status=MagickFalse;
816        continue;
817      }
818    for (x=0; x < (ssize_t) raise_info->width; x++)
819    {
820      if (GetPixelMask(image,q) != 0)
821        {
822          q+=GetPixelChannels(image);
823          continue;
824        }
825      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
826      {
827        PixelChannel
828          channel;
829
830        PixelTrait
831          traits;
832
833        channel=GetPixelChannelMapChannel(image,i);
834        traits=GetPixelChannelMapTraits(image,channel);
835        if ((traits & UpdatePixelTrait) == 0)
836          continue;
837        q[i]=ClampToQuantum(QuantumScale*((MagickRealType) q[i]*HighlightFactor+
838          (MagickRealType) foreground*(QuantumRange-HighlightFactor)));
839      }
840      q+=GetPixelChannels(image);
841    }
842    for ( ; x < (ssize_t) (image->columns-raise_info->width); x++)
843      q+=GetPixelChannels(image);
844    for ( ; x < (ssize_t) image->columns; x++)
845    {
846      if (GetPixelMask(image,q) != 0)
847        {
848          q+=GetPixelChannels(image);
849          continue;
850        }
851      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
852      {
853        PixelChannel
854          channel;
855
856        PixelTrait
857          traits;
858
859        channel=GetPixelChannelMapChannel(image,i);
860        traits=GetPixelChannelMapTraits(image,channel);
861        if ((traits & UpdatePixelTrait) == 0)
862          continue;
863        q[i]=ClampToQuantum(QuantumScale*((MagickRealType) q[i]*ShadowFactor+
864          (MagickRealType) background*(QuantumRange-ShadowFactor)));
865      }
866      q+=GetPixelChannels(image);
867    }
868    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
869      status=MagickFalse;
870    if (image->progress_monitor != (MagickProgressMonitor) NULL)
871      {
872        MagickBooleanType
873          proceed;
874
875        proceed=SetImageProgress(image,RaiseImageTag,progress++,image->rows);
876        if (proceed == MagickFalse)
877          status=MagickFalse;
878      }
879  }
880#if defined(MAGICKCORE_OPENMP_SUPPORT)
881  #pragma omp parallel for schedule(static) shared(progress,status) \
882    dynamic_number_threads(image,image->columns,image->rows,1)
883#endif
884  for (y=(ssize_t) (image->rows-raise_info->height); y < (ssize_t) image->rows; y++)
885  {
886    register ssize_t
887      i,
888      x;
889
890    register Quantum
891      *restrict q;
892
893    if (status == MagickFalse)
894      continue;
895    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
896    if (q == (Quantum *) NULL)
897      {
898        status=MagickFalse;
899        continue;
900      }
901    for (x=0; x < (ssize_t) (image->rows-y); x++)
902    {
903      if (GetPixelMask(image,q) != 0)
904        {
905          q+=GetPixelChannels(image);
906          continue;
907        }
908      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
909      {
910        PixelChannel
911          channel;
912
913        PixelTrait
914          traits;
915
916        channel=GetPixelChannelMapChannel(image,i);
917        traits=GetPixelChannelMapTraits(image,channel);
918        if ((traits & UpdatePixelTrait) == 0)
919          continue;
920        q[i]=ClampToQuantum(QuantumScale*((MagickRealType) q[i]*HighlightFactor+
921          (MagickRealType) foreground*(QuantumRange-HighlightFactor)));
922      }
923      q+=GetPixelChannels(image);
924    }
925    for ( ; x < (ssize_t) (image->columns-(image->rows-y)); x++)
926    {
927      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
928      {
929        PixelChannel
930          channel;
931
932        PixelTrait
933          traits;
934
935        channel=GetPixelChannelMapChannel(image,i);
936        traits=GetPixelChannelMapTraits(image,channel);
937        if ((traits & UpdatePixelTrait) == 0)
938          continue;
939        q[i]=ClampToQuantum(QuantumScale*((MagickRealType) q[i]*TroughFactor+
940          (MagickRealType) background*(QuantumRange-TroughFactor)));
941      }
942      q+=GetPixelChannels(image);
943    }
944    for ( ; x < (ssize_t) image->columns; x++)
945    {
946      if (GetPixelMask(image,q) != 0)
947        {
948          q+=GetPixelChannels(image);
949          continue;
950        }
951      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
952      {
953        PixelChannel
954          channel;
955
956        PixelTrait
957          traits;
958
959        channel=GetPixelChannelMapChannel(image,i);
960        traits=GetPixelChannelMapTraits(image,channel);
961        if ((traits & UpdatePixelTrait) == 0)
962          continue;
963        q[i]=ClampToQuantum(QuantumScale*((MagickRealType) q[i]*ShadowFactor+
964          (MagickRealType) background*(QuantumRange-ShadowFactor)));
965      }
966      q+=GetPixelChannels(image);
967    }
968    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
969      status=MagickFalse;
970    if (image->progress_monitor != (MagickProgressMonitor) NULL)
971      {
972        MagickBooleanType
973          proceed;
974
975#if defined(MAGICKCORE_OPENMP_SUPPORT)
976        #pragma omp critical (MagickCore_RaiseImage)
977#endif
978        proceed=SetImageProgress(image,RaiseImageTag,progress++,image->rows);
979        if (proceed == MagickFalse)
980          status=MagickFalse;
981      }
982  }
983  image_view=DestroyCacheView(image_view);
984  return(status);
985}
986