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%                                     Cristy                                  %
17%                                   July 1992                                 %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2016 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 == MagickCoreSignature);
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->alpha_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->alpha_color=image->alpha_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 alpha_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    matte,
191    shadow,
192    trough;
193
194  register ssize_t
195    x;
196
197  size_t
198    bevel_width,
199    height,
200    width;
201
202  ssize_t
203    y;
204
205  /*
206    Check frame geometry.
207  */
208  assert(image != (Image *) NULL);
209  assert(image->signature == MagickCoreSignature);
210  if (image->debug != MagickFalse)
211    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
212  assert(frame_info != (FrameInfo *) NULL);
213  if ((frame_info->outer_bevel < 0) || (frame_info->inner_bevel < 0))
214    ThrowImageException(OptionError,"FrameIsLessThanImageSize");
215  bevel_width=(size_t) (frame_info->outer_bevel+frame_info->inner_bevel);
216  x=(ssize_t) frame_info->width-frame_info->x-bevel_width;
217  y=(ssize_t) frame_info->height-frame_info->y-bevel_width;
218  if ((x < (ssize_t) image->columns) |  (y < (ssize_t) image->rows))
219    ThrowImageException(OptionError,"FrameIsLessThanImageSize");
220  /*
221    Initialize framed image attributes.
222  */
223  frame_image=CloneImage(image,frame_info->width,frame_info->height,MagickTrue,
224    exception);
225  if (frame_image == (Image *) NULL)
226    return((Image *) NULL);
227  if (SetImageStorageClass(frame_image,DirectClass,exception) == MagickFalse)
228    {
229      frame_image=DestroyImage(frame_image);
230      return((Image *) NULL);
231    }
232  if ((IsPixelInfoGray(&frame_image->border_color) == MagickFalse) &&
233      (IsGrayColorspace(frame_image->colorspace) != MagickFalse))
234    (void) SetImageColorspace(frame_image,sRGBColorspace,exception);
235  if ((frame_image->alpha_color.alpha_trait != UndefinedPixelTrait) &&
236      (frame_image->alpha_trait == UndefinedPixelTrait))
237    (void) SetImageAlpha(frame_image,OpaqueAlpha,exception);
238  frame_image->page=image->page;
239  if ((image->page.width != 0) && (image->page.height != 0))
240    {
241      frame_image->page.width+=frame_image->columns-image->columns;
242      frame_image->page.height+=frame_image->rows-image->rows;
243    }
244  /*
245    Initialize 3D effects color.
246  */
247  matte=image->alpha_color;
248  accentuate=matte;
249  accentuate.red=(double) (QuantumScale*((QuantumRange-
250    AccentuateModulate)*matte.red+(QuantumRange*AccentuateModulate)));
251  accentuate.green=(double) (QuantumScale*((QuantumRange-
252    AccentuateModulate)*matte.green+(QuantumRange*AccentuateModulate)));
253  accentuate.blue=(double) (QuantumScale*((QuantumRange-
254    AccentuateModulate)*matte.blue+(QuantumRange*AccentuateModulate)));
255  accentuate.black=(double) (QuantumScale*((QuantumRange-
256    AccentuateModulate)*matte.black+(QuantumRange*AccentuateModulate)));
257  accentuate.alpha=matte.alpha;
258  highlight=matte;
259  highlight.red=(double) (QuantumScale*((QuantumRange-
260    HighlightModulate)*matte.red+(QuantumRange*HighlightModulate)));
261  highlight.green=(double) (QuantumScale*((QuantumRange-
262    HighlightModulate)*matte.green+(QuantumRange*HighlightModulate)));
263  highlight.blue=(double) (QuantumScale*((QuantumRange-
264    HighlightModulate)*matte.blue+(QuantumRange*HighlightModulate)));
265  highlight.black=(double) (QuantumScale*((QuantumRange-
266    HighlightModulate)*matte.black+(QuantumRange*HighlightModulate)));
267  highlight.alpha=matte.alpha;
268  shadow=matte;
269  shadow.red=QuantumScale*matte.red*ShadowModulate;
270  shadow.green=QuantumScale*matte.green*ShadowModulate;
271  shadow.blue=QuantumScale*matte.blue*ShadowModulate;
272  shadow.black=QuantumScale*matte.black*ShadowModulate;
273  shadow.alpha=matte.alpha;
274  trough=matte;
275  trough.red=QuantumScale*matte.red*TroughModulate;
276  trough.green=QuantumScale*matte.green*TroughModulate;
277  trough.blue=QuantumScale*matte.blue*TroughModulate;
278  trough.black=QuantumScale*matte.black*TroughModulate;
279  trough.alpha=matte.alpha;
280  status=MagickTrue;
281  progress=0;
282  image_view=AcquireVirtualCacheView(image,exception);
283  frame_view=AcquireAuthenticCacheView(frame_image,exception);
284  height=(size_t) (frame_info->outer_bevel+(frame_info->y-bevel_width)+
285    frame_info->inner_bevel);
286  if (height != 0)
287    {
288      register ssize_t
289        x;
290
291      register Quantum
292        *magick_restrict q;
293
294      /*
295        Draw top of ornamental border.
296      */
297      q=QueueCacheViewAuthenticPixels(frame_view,0,0,frame_image->columns,
298        height,exception);
299      if (q != (Quantum *) NULL)
300        {
301          /*
302            Draw top of ornamental border.
303          */
304          for (y=0; y < (ssize_t) frame_info->outer_bevel; y++)
305          {
306            for (x=0; x < (ssize_t) (frame_image->columns-y); x++)
307            {
308              if (x < y)
309                SetPixelViaPixelInfo(frame_image,&highlight,q);
310              else
311                SetPixelViaPixelInfo(frame_image,&accentuate,q);
312              q+=GetPixelChannels(frame_image);
313            }
314            for ( ; x < (ssize_t) frame_image->columns; x++)
315            {
316              SetPixelViaPixelInfo(frame_image,&shadow,q);
317              q+=GetPixelChannels(frame_image);
318            }
319          }
320          for (y=0; y < (ssize_t) (frame_info->y-bevel_width); y++)
321          {
322            for (x=0; x < (ssize_t) frame_info->outer_bevel; x++)
323            {
324              SetPixelViaPixelInfo(frame_image,&highlight,q);
325              q+=GetPixelChannels(frame_image);
326            }
327            width=frame_image->columns-2*frame_info->outer_bevel;
328            for (x=0; x < (ssize_t) width; x++)
329            {
330              SetPixelViaPixelInfo(frame_image,&matte,q);
331              q+=GetPixelChannels(frame_image);
332            }
333            for (x=0; x < (ssize_t) frame_info->outer_bevel; x++)
334            {
335              SetPixelViaPixelInfo(frame_image,&shadow,q);
336              q+=GetPixelChannels(frame_image);
337            }
338          }
339          for (y=0; y < (ssize_t) frame_info->inner_bevel; y++)
340          {
341            for (x=0; x < (ssize_t) frame_info->outer_bevel; x++)
342            {
343              SetPixelViaPixelInfo(frame_image,&highlight,q);
344              q+=GetPixelChannels(frame_image);
345            }
346            for (x=0; x < (ssize_t) (frame_info->x-bevel_width); x++)
347            {
348              SetPixelViaPixelInfo(frame_image,&matte,q);
349              q+=GetPixelChannels(frame_image);
350            }
351            width=image->columns+((size_t) frame_info->inner_bevel << 1)-
352              y;
353            for (x=0; x < (ssize_t) width; x++)
354            {
355              if (x < y)
356                SetPixelViaPixelInfo(frame_image,&shadow,q);
357              else
358                SetPixelViaPixelInfo(frame_image,&trough,q);
359              q+=GetPixelChannels(frame_image);
360            }
361            for ( ; x < (ssize_t) (image->columns+2*frame_info->inner_bevel); x++)
362            {
363              SetPixelViaPixelInfo(frame_image,&highlight,q);
364              q+=GetPixelChannels(frame_image);
365            }
366            width=frame_info->width-frame_info->x-image->columns-bevel_width;
367            for (x=0; x < (ssize_t) width; x++)
368            {
369              SetPixelViaPixelInfo(frame_image,&matte,q);
370              q+=GetPixelChannels(frame_image);
371            }
372            for (x=0; x < (ssize_t) frame_info->outer_bevel; x++)
373            {
374              SetPixelViaPixelInfo(frame_image,&shadow,q);
375              q+=GetPixelChannels(frame_image);
376            }
377          }
378          (void) SyncCacheViewAuthenticPixels(frame_view,exception);
379        }
380    }
381  /*
382    Draw sides of ornamental border.
383  */
384#if defined(MAGICKCORE_OPENMP_SUPPORT)
385  #pragma omp parallel for schedule(static,4) shared(progress,status) \
386    magick_threads(image,frame_image,1,1)
387#endif
388  for (y=0; y < (ssize_t) image->rows; y++)
389  {
390    register ssize_t
391      x;
392
393    register Quantum
394      *magick_restrict q;
395
396    size_t
397      width;
398
399    /*
400      Initialize scanline with matte color.
401    */
402    if (status == MagickFalse)
403      continue;
404    q=QueueCacheViewAuthenticPixels(frame_view,0,frame_info->y+y,
405      frame_image->columns,1,exception);
406    if (q == (Quantum *) NULL)
407      {
408        status=MagickFalse;
409        continue;
410      }
411    for (x=0; x < (ssize_t) frame_info->outer_bevel; x++)
412    {
413      SetPixelViaPixelInfo(frame_image,&highlight,q);
414      q+=GetPixelChannels(frame_image);
415    }
416    for (x=0; x < (ssize_t) (frame_info->x-bevel_width); x++)
417    {
418      SetPixelViaPixelInfo(frame_image,&matte,q);
419      q+=GetPixelChannels(frame_image);
420    }
421    for (x=0; x < (ssize_t) frame_info->inner_bevel; x++)
422    {
423      SetPixelViaPixelInfo(frame_image,&shadow,q);
424      q+=GetPixelChannels(frame_image);
425    }
426    /*
427      Set frame interior pixels.
428    */
429    {
430      register const Quantum
431        *p;
432
433      p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
434      if (p == (const Quantum *) NULL)
435        {
436          status=MagickFalse;
437          continue;
438        }
439      for (x=0; x < (ssize_t) image->columns; x++)
440      {
441        register ssize_t
442          i;
443
444        if (GetPixelReadMask(image,q) == 0)
445          {
446            SetPixelBackgoundColor(frame_image,q);
447            p+=GetPixelChannels(image);
448            q+=GetPixelChannels(frame_image);
449            continue;
450          }
451        for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
452        {
453          PixelChannel channel=GetPixelChannelChannel(image,i);
454          PixelTrait traits=GetPixelChannelTraits(image,channel);
455          PixelTrait frame_traits=GetPixelChannelTraits(frame_image,channel);
456          if ((traits == UndefinedPixelTrait) ||
457              (frame_traits == UndefinedPixelTrait))
458            continue;
459          SetPixelChannel(frame_image,channel,p[i],q);
460        }
461        SetPixelRed(frame_image,GetPixelRed(image,p),q);
462        SetPixelGreen(frame_image,GetPixelGreen(image,p),q);
463        SetPixelBlue(frame_image,GetPixelBlue(image,p),q);
464        SetPixelAlpha(frame_image,GetPixelAlpha(image,p),q);
465        p+=GetPixelChannels(image);
466        q+=GetPixelChannels(frame_image);
467      }
468    }
469    for (x=0; x < (ssize_t) frame_info->inner_bevel; x++)
470    {
471      SetPixelViaPixelInfo(frame_image,&highlight,q);
472      q+=GetPixelChannels(frame_image);
473    }
474    width=frame_info->width-frame_info->x-image->columns-bevel_width;
475    for (x=0; x < (ssize_t) width; x++)
476    {
477      SetPixelViaPixelInfo(frame_image,&matte,q);
478      q+=GetPixelChannels(frame_image);
479    }
480    for (x=0; x < (ssize_t) frame_info->outer_bevel; x++)
481    {
482      SetPixelViaPixelInfo(frame_image,&shadow,q);
483      q+=GetPixelChannels(frame_image);
484    }
485    if (SyncCacheViewAuthenticPixels(frame_view,exception) == MagickFalse)
486      status=MagickFalse;
487    if (image->progress_monitor != (MagickProgressMonitor) NULL)
488      {
489        MagickBooleanType
490          proceed;
491
492#if defined(MAGICKCORE_OPENMP_SUPPORT)
493        #pragma omp critical (MagickCore_FrameImage)
494#endif
495        proceed=SetImageProgress(image,FrameImageTag,progress++,image->rows);
496        if (proceed == MagickFalse)
497          status=MagickFalse;
498      }
499  }
500  height=(size_t) (frame_info->inner_bevel+frame_info->height-
501    frame_info->y-image->rows-bevel_width+frame_info->outer_bevel);
502  if (height != 0)
503    {
504      register ssize_t
505        x;
506
507      register Quantum
508        *magick_restrict q;
509
510      /*
511        Draw bottom of ornamental border.
512      */
513      q=QueueCacheViewAuthenticPixels(frame_view,0,(ssize_t) (frame_image->rows-
514        height),frame_image->columns,height,exception);
515      if (q != (Quantum *) NULL)
516        {
517          /*
518            Draw bottom of ornamental border.
519          */
520          for (y=frame_info->inner_bevel-1; y >= 0; y--)
521          {
522            for (x=0; x < (ssize_t) frame_info->outer_bevel; x++)
523            {
524              SetPixelViaPixelInfo(frame_image,&highlight,q);
525              q+=GetPixelChannels(frame_image);
526            }
527            for (x=0; x < (ssize_t) (frame_info->x-bevel_width); x++)
528            {
529              SetPixelViaPixelInfo(frame_image,&matte,q);
530              q+=GetPixelChannels(frame_image);
531            }
532            for (x=0; x < y; x++)
533            {
534              SetPixelViaPixelInfo(frame_image,&shadow,q);
535              q+=GetPixelChannels(frame_image);
536            }
537            for ( ; x < (ssize_t) (image->columns+2*frame_info->inner_bevel); x++)
538            {
539              if (x >= (ssize_t) (image->columns+2*frame_info->inner_bevel-y))
540                SetPixelViaPixelInfo(frame_image,&highlight,q);
541              else
542                SetPixelViaPixelInfo(frame_image,&accentuate,q);
543              q+=GetPixelChannels(frame_image);
544            }
545            width=frame_info->width-frame_info->x-image->columns-bevel_width;
546            for (x=0; x < (ssize_t) width; x++)
547            {
548              SetPixelViaPixelInfo(frame_image,&matte,q);
549              q+=GetPixelChannels(frame_image);
550            }
551            for (x=0; x < (ssize_t) frame_info->outer_bevel; x++)
552            {
553              SetPixelViaPixelInfo(frame_image,&shadow,q);
554              q+=GetPixelChannels(frame_image);
555            }
556          }
557          height=frame_info->height-frame_info->y-image->rows-bevel_width;
558          for (y=0; y < (ssize_t) height; y++)
559          {
560            for (x=0; x < (ssize_t) frame_info->outer_bevel; x++)
561            {
562              SetPixelViaPixelInfo(frame_image,&highlight,q);
563              q+=GetPixelChannels(frame_image);
564            }
565            width=frame_image->columns-2*frame_info->outer_bevel;
566            for (x=0; x < (ssize_t) width; x++)
567            {
568              SetPixelViaPixelInfo(frame_image,&matte,q);
569              q+=GetPixelChannels(frame_image);
570            }
571            for (x=0; x < (ssize_t) frame_info->outer_bevel; x++)
572            {
573              SetPixelViaPixelInfo(frame_image,&shadow,q);
574              q+=GetPixelChannels(frame_image);
575            }
576          }
577          for (y=frame_info->outer_bevel-1; y >= 0; y--)
578          {
579            for (x=0; x < y; x++)
580            {
581              SetPixelViaPixelInfo(frame_image,&highlight,q);
582              q+=GetPixelChannels(frame_image);
583            }
584            for ( ; x < (ssize_t) frame_image->columns; x++)
585            {
586              if (x >= (ssize_t) (frame_image->columns-y))
587                SetPixelViaPixelInfo(frame_image,&shadow,q);
588              else
589                SetPixelViaPixelInfo(frame_image,&trough,q);
590              q+=GetPixelChannels(frame_image);
591            }
592          }
593          (void) SyncCacheViewAuthenticPixels(frame_view,exception);
594        }
595    }
596  frame_view=DestroyCacheView(frame_view);
597  image_view=DestroyCacheView(image_view);
598  x=(ssize_t) (frame_info->outer_bevel+(frame_info->x-bevel_width)+
599    frame_info->inner_bevel);
600  y=(ssize_t) (frame_info->outer_bevel+(frame_info->y-bevel_width)+
601    frame_info->inner_bevel);
602  if (status != MagickFalse)
603    status=CompositeImage(frame_image,image,compose,MagickTrue,x,y,
604      exception);
605  if (status == MagickFalse)
606    frame_image=DestroyImage(frame_image);
607  return(frame_image);
608}
609
610/*
611%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
612%                                                                             %
613%                                                                             %
614%                                                                             %
615%   R a i s e I m a g e                                                       %
616%                                                                             %
617%                                                                             %
618%                                                                             %
619%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
620%
621%  RaiseImage() creates a simulated three-dimensional button-like effect
622%  by lightening and darkening the edges of the image.  Members width and
623%  height of raise_info define the width of the vertical and horizontal
624%  edge of the effect.
625%
626%  The format of the RaiseImage method is:
627%
628%      MagickBooleanType RaiseImage(const Image *image,
629%        const RectangleInfo *raise_info,const MagickBooleanType raise,
630%        ExceptionInfo *exception)
631%
632%  A description of each parameter follows:
633%
634%    o image: the image.
635%
636%    o raise_info: Define the width and height of the raise area.
637%
638%    o raise: A value other than zero creates a 3-D raise effect,
639%      otherwise it has a lowered effect.
640%
641%    o exception: return any errors or warnings in this structure.
642%
643*/
644MagickExport MagickBooleanType RaiseImage(Image *image,
645  const RectangleInfo *raise_info,const MagickBooleanType raise,
646  ExceptionInfo *exception)
647{
648#define AccentuateFactor  ScaleCharToQuantum(135)
649#define HighlightFactor  ScaleCharToQuantum(190)
650#define ShadowFactor  ScaleCharToQuantum(190)
651#define RaiseImageTag  "Raise/Image"
652#define TroughFactor  ScaleCharToQuantum(135)
653
654  CacheView
655    *image_view;
656
657  MagickBooleanType
658    status;
659
660  MagickOffsetType
661    progress;
662
663  Quantum
664    foreground,
665    background;
666
667  ssize_t
668    y;
669
670  assert(image != (Image *) NULL);
671  assert(image->signature == MagickCoreSignature);
672  if (image->debug != MagickFalse)
673    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
674  assert(raise_info != (RectangleInfo *) NULL);
675  if ((image->columns <= (raise_info->width << 1)) ||
676      (image->rows <= (raise_info->height << 1)))
677    ThrowBinaryException(OptionError,"ImageSizeMustExceedBevelWidth",
678      image->filename);
679  foreground=QuantumRange;
680  background=(Quantum) 0;
681  if (raise == MagickFalse)
682    {
683      foreground=(Quantum) 0;
684      background=QuantumRange;
685    }
686  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
687    return(MagickFalse);
688  /*
689    Raise image.
690  */
691  status=MagickTrue;
692  progress=0;
693  image_view=AcquireAuthenticCacheView(image,exception);
694#if defined(MAGICKCORE_OPENMP_SUPPORT)
695  #pragma omp parallel for schedule(static,4) shared(progress,status) \
696    magick_threads(image,image,1,1)
697#endif
698  for (y=0; y < (ssize_t) raise_info->height; y++)
699  {
700    register ssize_t
701      i,
702      x;
703
704    register Quantum
705      *magick_restrict q;
706
707    if (status == MagickFalse)
708      continue;
709    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
710    if (q == (Quantum *) NULL)
711      {
712        status=MagickFalse;
713        continue;
714      }
715    for (x=0; x < y; x++)
716    {
717      if (GetPixelReadMask(image,q) == 0)
718        {
719          q+=GetPixelChannels(image);
720          continue;
721        }
722      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
723      {
724        PixelChannel channel=GetPixelChannelChannel(image,i);
725        PixelTrait traits=GetPixelChannelTraits(image,channel);
726        if ((traits & UpdatePixelTrait) == 0)
727          continue;
728        q[i]=ClampToQuantum(QuantumScale*((double) q[i]*HighlightFactor+(double)
729          foreground*(QuantumRange-HighlightFactor)));
730      }
731      q+=GetPixelChannels(image);
732    }
733    for ( ; x < (ssize_t) (image->columns-y); x++)
734    {
735      if (GetPixelReadMask(image,q) == 0)
736        {
737          q+=GetPixelChannels(image);
738          continue;
739        }
740      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
741      {
742        PixelChannel channel=GetPixelChannelChannel(image,i);
743        PixelTrait traits=GetPixelChannelTraits(image,channel);
744        if ((traits & UpdatePixelTrait) == 0)
745          continue;
746        q[i]=ClampToQuantum(QuantumScale*((double) q[i]*AccentuateFactor+
747          (double) foreground*(QuantumRange-AccentuateFactor)));
748      }
749      q+=GetPixelChannels(image);
750    }
751    for ( ; x < (ssize_t) image->columns; x++)
752    {
753      if (GetPixelReadMask(image,q) == 0)
754        {
755          q+=GetPixelChannels(image);
756          continue;
757        }
758      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
759      {
760        PixelChannel channel=GetPixelChannelChannel(image,i);
761        PixelTrait traits=GetPixelChannelTraits(image,channel);
762        if ((traits & UpdatePixelTrait) == 0)
763          continue;
764        q[i]=ClampToQuantum(QuantumScale*((double) q[i]*ShadowFactor+(double)
765          background*(QuantumRange-ShadowFactor)));
766      }
767      q+=GetPixelChannels(image);
768    }
769    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
770      status=MagickFalse;
771    if (image->progress_monitor != (MagickProgressMonitor) NULL)
772      {
773        MagickBooleanType
774          proceed;
775
776#if defined(MAGICKCORE_OPENMP_SUPPORT)
777        #pragma omp critical (MagickCore_RaiseImage)
778#endif
779        proceed=SetImageProgress(image,RaiseImageTag,progress++,image->rows);
780        if (proceed == MagickFalse)
781          status=MagickFalse;
782      }
783  }
784#if defined(MAGICKCORE_OPENMP_SUPPORT)
785  #pragma omp parallel for schedule(static,4) shared(progress,status) \
786    magick_threads(image,image,1,1)
787#endif
788  for (y=(ssize_t) raise_info->height; y < (ssize_t) (image->rows-raise_info->height); y++)
789  {
790    register ssize_t
791      i,
792      x;
793
794    register Quantum
795      *magick_restrict q;
796
797    if (status == MagickFalse)
798      continue;
799    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
800    if (q == (Quantum *) NULL)
801      {
802        status=MagickFalse;
803        continue;
804      }
805    for (x=0; x < (ssize_t) raise_info->width; x++)
806    {
807      if (GetPixelReadMask(image,q) == 0)
808        {
809          q+=GetPixelChannels(image);
810          continue;
811        }
812      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
813      {
814        PixelChannel channel=GetPixelChannelChannel(image,i);
815        PixelTrait traits=GetPixelChannelTraits(image,channel);
816        if ((traits & UpdatePixelTrait) == 0)
817          continue;
818        q[i]=ClampToQuantum(QuantumScale*((double) q[i]*HighlightFactor+(double)
819          foreground*(QuantumRange-HighlightFactor)));
820      }
821      q+=GetPixelChannels(image);
822    }
823    for ( ; x < (ssize_t) (image->columns-raise_info->width); x++)
824      q+=GetPixelChannels(image);
825    for ( ; x < (ssize_t) image->columns; x++)
826    {
827      if (GetPixelReadMask(image,q) == 0)
828        {
829          q+=GetPixelChannels(image);
830          continue;
831        }
832      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
833      {
834        PixelChannel channel=GetPixelChannelChannel(image,i);
835        PixelTrait traits=GetPixelChannelTraits(image,channel);
836        if ((traits & UpdatePixelTrait) == 0)
837          continue;
838        q[i]=ClampToQuantum(QuantumScale*((double) q[i]*ShadowFactor+(double)
839          background*(QuantumRange-ShadowFactor)));
840      }
841      q+=GetPixelChannels(image);
842    }
843    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
844      status=MagickFalse;
845    if (image->progress_monitor != (MagickProgressMonitor) NULL)
846      {
847        MagickBooleanType
848          proceed;
849
850#if defined(MAGICKCORE_OPENMP_SUPPORT)
851        #pragma omp critical (MagickCore_RaiseImage)
852#endif
853        proceed=SetImageProgress(image,RaiseImageTag,progress++,image->rows);
854        if (proceed == MagickFalse)
855          status=MagickFalse;
856      }
857  }
858#if defined(MAGICKCORE_OPENMP_SUPPORT)
859  #pragma omp parallel for schedule(static,4) shared(progress,status) \
860    magick_threads(image,image,1,1)
861#endif
862  for (y=(ssize_t) (image->rows-raise_info->height); y < (ssize_t) image->rows; y++)
863  {
864    register ssize_t
865      i,
866      x;
867
868    register Quantum
869      *magick_restrict q;
870
871    if (status == MagickFalse)
872      continue;
873    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
874    if (q == (Quantum *) NULL)
875      {
876        status=MagickFalse;
877        continue;
878      }
879    for (x=0; x < (ssize_t) (image->rows-y); x++)
880    {
881      if (GetPixelReadMask(image,q) == 0)
882        {
883          q+=GetPixelChannels(image);
884          continue;
885        }
886      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
887      {
888        PixelChannel channel=GetPixelChannelChannel(image,i);
889        PixelTrait traits=GetPixelChannelTraits(image,channel);
890        if ((traits & UpdatePixelTrait) == 0)
891          continue;
892        q[i]=ClampToQuantum(QuantumScale*((double) q[i]*HighlightFactor+(double)
893          foreground*(QuantumRange-HighlightFactor)));
894      }
895      q+=GetPixelChannels(image);
896    }
897    for ( ; x < (ssize_t) (image->columns-(image->rows-y)); x++)
898    {
899      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
900      {
901        PixelChannel channel=GetPixelChannelChannel(image,i);
902        PixelTrait traits=GetPixelChannelTraits(image,channel);
903        if ((traits & UpdatePixelTrait) == 0)
904          continue;
905        q[i]=ClampToQuantum(QuantumScale*((double) q[i]*TroughFactor+
906          (double) background*(QuantumRange-TroughFactor)));
907      }
908      q+=GetPixelChannels(image);
909    }
910    for ( ; x < (ssize_t) image->columns; x++)
911    {
912      if (GetPixelReadMask(image,q) == 0)
913        {
914          q+=GetPixelChannels(image);
915          continue;
916        }
917      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
918      {
919        PixelChannel channel=GetPixelChannelChannel(image,i);
920        PixelTrait traits=GetPixelChannelTraits(image,channel);
921        if ((traits & UpdatePixelTrait) == 0)
922          continue;
923        q[i]=ClampToQuantum(QuantumScale*((double) q[i]*ShadowFactor+(double)
924          background*(QuantumRange-ShadowFactor)));
925      }
926      q+=GetPixelChannels(image);
927    }
928    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
929      status=MagickFalse;
930    if (image->progress_monitor != (MagickProgressMonitor) NULL)
931      {
932        MagickBooleanType
933          proceed;
934
935#if defined(MAGICKCORE_OPENMP_SUPPORT)
936        #pragma omp critical (MagickCore_RaiseImage)
937#endif
938        proceed=SetImageProgress(image,RaiseImageTag,progress++,image->rows);
939        if (proceed == MagickFalse)
940          status=MagickFalse;
941      }
942  }
943  image_view=DestroyCacheView(image_view);
944  return(status);
945}
946