accelerate.c revision b56bb24a985ca4366713bcd8ffdfacbb48a98a2f
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%     AAA     CCCC    CCCC  EEEEE  L      EEEEE  RRRR    AAA   TTTTT  EEEEE   %
7%    A   A   C       C      E      L      E      R   R  A   A    T    E       %
8%    AAAAA   C       C      EEE    L      EEE    RRRR   AAAAA    T    EEE     %
9%    A   A   C       C      E      L      E      R R    A   A    T    E       %
10%    A   A    CCCC    CCCC  EEEEE  LLLLL  EEEEE  R  R   A   A    T    EEEEE   %
11%                                                                             %
12%                                                                             %
13%                       MagickCore Acceleration Methods                       %
14%                                                                             %
15%                              Software Design                                %
16%                                  Cristy                                     %
17%                               SiuChi Chan                                   %
18%                               Guansong Zhang                                %
19%                               January 2010                                  %
20%                                                                             %
21%                                                                             %
22%  Copyright 1999-2015 ImageMagick Studio LLC, a non-profit organization      %
23%  dedicated to making software imaging solutions freely available.           %
24%                                                                             %
25%  You may not use this file except in compliance with the License.  You may  %
26%  obtain a copy of the License at                                            %
27%                                                                             %
28%    http://www.imagemagick.org/script/license.php                            %
29%                                                                             %
30%  Unless required by applicable law or agreed to in writing, software        %
31%  distributed under the License is distributed on an "AS IS" BASIS,          %
32%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
33%  See the License for the specific language governing permissions and        %
34%  limitations under the License.                                             %
35%                                                                             %
36%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
37*/
38
39/*
40Include declarations.
41*/
42#include "MagickCore/studio.h"
43#include "MagickCore/accelerate.h"
44#include "MagickCore/accelerate-private.h"
45#include "MagickCore/artifact.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/cache-private.h"
48#include "MagickCore/cache-view.h"
49#include "MagickCore/color-private.h"
50#include "MagickCore/delegate-private.h"
51#include "MagickCore/enhance.h"
52#include "MagickCore/exception.h"
53#include "MagickCore/exception-private.h"
54#include "MagickCore/gem.h"
55#include "MagickCore/hashmap.h"
56#include "MagickCore/image.h"
57#include "MagickCore/image-private.h"
58#include "MagickCore/list.h"
59#include "MagickCore/memory_.h"
60#include "MagickCore/monitor-private.h"
61#include "MagickCore/accelerate.h"
62#include "MagickCore/opencl.h"
63#include "MagickCore/opencl-private.h"
64#include "MagickCore/option.h"
65#include "MagickCore/pixel-accessor.h"
66#include "MagickCore/pixel-private.h"
67#include "MagickCore/prepress.h"
68#include "MagickCore/quantize.h"
69#include "MagickCore/quantum-private.h"
70#include "MagickCore/random_.h"
71#include "MagickCore/random-private.h"
72#include "MagickCore/registry.h"
73#include "MagickCore/resize.h"
74#include "MagickCore/resize-private.h"
75#include "MagickCore/semaphore.h"
76#include "MagickCore/splay-tree.h"
77#include "MagickCore/statistic.h"
78#include "MagickCore/string_.h"
79#include "MagickCore/string-private.h"
80#include "MagickCore/token.h"
81
82#ifdef MAGICKCORE_CLPERFMARKER
83#include "CLPerfMarker.h"
84#endif
85
86#define MAGICK_MAX(x,y) (((x) >= (y))?(x):(y))
87#define MAGICK_MIN(x,y) (((x) <= (y))?(x):(y))
88
89#if defined(MAGICKCORE_OPENCL_SUPPORT)
90
91#define ALIGNED(pointer,type) ((((size_t)(pointer)) & (sizeof(type)-1)) == 0)
92
93/* pad the global workgroup size to the next multiple of
94   the local workgroup size */
95inline static unsigned int padGlobalWorkgroupSizeToLocalWorkgroupSize(
96  const unsigned int orgGlobalSize,const unsigned int localGroupSize)
97{
98  return ((orgGlobalSize+(localGroupSize-1))/localGroupSize*localGroupSize);
99}
100
101static MagickBooleanType checkOpenCLEnvironment(ExceptionInfo* exception)
102{
103  MagickBooleanType
104    flag;
105
106  MagickCLEnv
107    clEnv;
108
109  clEnv=GetDefaultOpenCLEnv();
110
111  GetMagickOpenCLEnvParam(clEnv,MAGICK_OPENCL_ENV_PARAM_OPENCL_DISABLED,
112    sizeof(MagickBooleanType),&flag,exception);
113  if (flag != MagickFalse)
114    return(MagickFalse);
115
116  GetMagickOpenCLEnvParam(clEnv,MAGICK_OPENCL_ENV_PARAM_OPENCL_INITIALIZED,
117    sizeof(MagickBooleanType),&flag,exception);
118  if (flag == MagickFalse)
119    {
120      if (InitOpenCLEnv(clEnv,exception) == MagickFalse)
121        return(MagickFalse);
122
123      GetMagickOpenCLEnvParam(clEnv,MAGICK_OPENCL_ENV_PARAM_OPENCL_DISABLED,
124        sizeof(MagickBooleanType),&flag,exception);
125      if (flag != MagickFalse)
126        return(MagickFalse);
127    }
128
129  return(MagickTrue);
130}
131
132static MagickBooleanType checkAccelerateCondition(const Image* image,
133  const ChannelType channel)
134{
135  /* check if the image's colorspace is supported */
136  if (image->colorspace != RGBColorspace &&
137      image->colorspace != sRGBColorspace &&
138      image->colorspace != GRAYColorspace)
139    return(MagickFalse);
140
141  /* check if the channel is supported */
142  if (((channel & RedChannel) == 0) ||
143      ((channel & GreenChannel) == 0) ||
144      ((channel & BlueChannel) == 0))
145    return(MagickFalse);
146
147  /* check if the virtual pixel method is compatible with the OpenCL implementation */
148  if ((GetImageVirtualPixelMethod(image) != UndefinedVirtualPixelMethod) &&
149      (GetImageVirtualPixelMethod(image) != EdgeVirtualPixelMethod))
150    return(MagickFalse);
151
152  /* check if the image has read / write mask */
153  if (image->read_mask != MagickFalse || image->write_mask != MagickFalse)
154    return(MagickFalse);
155
156  /* check if pixel order is RGBA */
157  if (GetPixelChannelOffset(image,RedPixelChannel) != 0 ||
158      GetPixelChannelOffset(image,GreenPixelChannel) != 1 ||
159      GetPixelChannelOffset(image,BluePixelChannel) != 2 ||
160      GetPixelChannelOffset(image,AlphaPixelChannel) != 3)
161    return(MagickFalse);
162
163  /* check if all channels are available */
164  if (((GetPixelRedTraits(image) & UpdatePixelTrait) == 0) ||
165      ((GetPixelGreenTraits(image) & UpdatePixelTrait) == 0) ||
166      ((GetPixelBlueTraits(image) & UpdatePixelTrait) == 0) ||
167      ((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0))
168    return(MagickFalse);
169
170  return(MagickTrue);
171}
172
173static MagickBooleanType checkHistogramCondition(Image *image,
174  const ChannelType channel)
175{
176  /* ensure this is the only pass get in for now. */
177  if ((channel & SyncChannels) == 0)
178    return MagickFalse;
179
180  if (image->intensity == Rec601LuminancePixelIntensityMethod ||
181      image->intensity == Rec709LuminancePixelIntensityMethod)
182    return MagickFalse;
183
184  if (image->colorspace != sRGBColorspace)
185    return MagickFalse;
186
187  return MagickTrue;
188}
189
190static MagickBooleanType splitImage(const Image* image)
191{
192  MagickBooleanType
193    split;
194
195  MagickCLEnv
196    clEnv;
197
198  unsigned long
199    allocSize,
200    tempSize;
201
202  clEnv=GetDefaultOpenCLEnv();
203
204  allocSize=GetOpenCLDeviceMaxMemAllocSize(clEnv);
205  tempSize=(unsigned long) (image->columns * image->rows * 4 * 4);
206
207  split = ((tempSize > allocSize) ? MagickTrue : MagickFalse);
208  return(split);
209}
210
211/*
212%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
213%                                                                             %
214%                                                                             %
215%                                                                             %
216%     C o n v o l v e I m a g e  w i t h  O p e n C L                         %
217%                                                                             %
218%                                                                             %
219%                                                                             %
220%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
221%
222%  ConvolveImage() applies a custom convolution kernel to the image.
223%
224%  The format of the ConvolveImage method is:
225%
226%      Image *ConvolveImage(const Image *image,const size_t order,
227%        const double *kernel,ExceptionInfo *exception)
228%      Image *ConvolveImageChannel(const Image *image,const ChannelType channel,
229%        const size_t order,const double *kernel,ExceptionInfo *exception)
230%
231%  A description of each parameter follows:
232%
233%    o image: the image.
234%
235%    o channel: the channel type.
236%
237%    o kernel: kernel info.
238%
239%    o exception: return any errors or warnings in this structure.
240%
241*/
242
243static Image *ComputeConvolveImage(const Image* image,
244  const ChannelType channel,const KernelInfo *kernel,ExceptionInfo *exception)
245{
246  CacheView
247    *filteredImage_view,
248    *image_view;
249
250  cl_command_queue
251    queue;
252
253  cl_context
254    context;
255
256  cl_kernel
257    clkernel;
258
259  cl_int
260    clStatus;
261
262  cl_mem
263    convolutionKernel,
264    filteredImageBuffer,
265    imageBuffer;
266
267  cl_mem_flags
268    mem_flags;
269
270  cl_ulong
271    deviceLocalMemorySize;
272
273  const void
274    *inputPixels;
275
276  float
277    *kernelBufferPtr;
278
279  Image
280    *filteredImage;
281
282  MagickBooleanType
283    outputReady;
284
285  MagickCLEnv
286    clEnv;
287
288  MagickSizeType
289    length;
290
291  size_t
292    global_work_size[3],
293    localGroupSize[3],
294    localMemoryRequirement;
295
296  unsigned
297    kernelSize;
298
299  unsigned int
300    filterHeight,
301    filterWidth,
302    i,
303    imageHeight,
304    imageWidth,
305    matte;
306
307  void
308    *filteredPixels,
309    *hostPtr;
310
311  /* intialize all CL objects to NULL */
312  context = NULL;
313  imageBuffer = NULL;
314  filteredImageBuffer = NULL;
315  convolutionKernel = NULL;
316  clkernel = NULL;
317  queue = NULL;
318
319  filteredImage = NULL;
320  filteredImage_view = NULL;
321  outputReady = MagickFalse;
322
323  clEnv = GetDefaultOpenCLEnv();
324  context = GetOpenCLContext(clEnv);
325
326  image_view=AcquireVirtualCacheView(image,exception);
327  inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
328  if (inputPixels == (const void *) NULL)
329  {
330    (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
331    goto cleanup;
332  }
333
334  /* Create and initialize OpenCL buffers. */
335
336  /* If the host pointer is aligned to the size of CLPixelPacket,
337     then use the host buffer directly from the GPU; otherwise,
338     create a buffer on the GPU and copy the data over */
339  if (ALIGNED(inputPixels,CLPixelPacket))
340  {
341    mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
342  }
343  else
344  {
345    mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
346  }
347  /* create a CL buffer from image pixel buffer */
348  length = image->columns * image->rows;
349  imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
350  if (clStatus != CL_SUCCESS)
351  {
352    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
353    goto cleanup;
354  }
355
356  filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
357  assert(filteredImage != NULL);
358  if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
359  {
360    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
361    goto cleanup;
362  }
363  filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
364  filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
365  if (filteredPixels == (void *) NULL)
366  {
367    (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
368    goto cleanup;
369  }
370
371  if (ALIGNED(filteredPixels,CLPixelPacket))
372  {
373    mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
374    hostPtr = filteredPixels;
375  }
376  else
377  {
378    mem_flags = CL_MEM_WRITE_ONLY;
379    hostPtr = NULL;
380  }
381  /* create a CL buffer from image pixel buffer */
382  length = image->columns * image->rows;
383  filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
384  if (clStatus != CL_SUCCESS)
385  {
386    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
387    goto cleanup;
388  }
389
390  kernelSize = (unsigned int) (kernel->width * kernel->height);
391  convolutionKernel = clEnv->library->clCreateBuffer(context, CL_MEM_READ_ONLY|CL_MEM_ALLOC_HOST_PTR, kernelSize * sizeof(float), NULL, &clStatus);
392  if (clStatus != CL_SUCCESS)
393  {
394    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
395    goto cleanup;
396  }
397
398  queue = AcquireOpenCLCommandQueue(clEnv);
399
400  kernelBufferPtr = (float*)clEnv->library->clEnqueueMapBuffer(queue, convolutionKernel, CL_TRUE, CL_MAP_WRITE, 0, kernelSize * sizeof(float)
401          , 0, NULL, NULL, &clStatus);
402  if (clStatus != CL_SUCCESS)
403  {
404    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueMapBuffer failed.",".");
405    goto cleanup;
406  }
407  for (i = 0; i < kernelSize; i++)
408  {
409    kernelBufferPtr[i] = (float) kernel->values[i];
410  }
411  clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, convolutionKernel, kernelBufferPtr, 0, NULL, NULL);
412  if (clStatus != CL_SUCCESS)
413  {
414    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
415    goto cleanup;
416  }
417  clEnv->library->clFlush(queue);
418
419  deviceLocalMemorySize = GetOpenCLDeviceLocalMemorySize(clEnv);
420
421  /* Compute the local memory requirement for a 16x16 workgroup.
422     If it's larger than 16k, reduce the workgroup size to 8x8 */
423  localGroupSize[0] = 16;
424  localGroupSize[1] = 16;
425  localMemoryRequirement = (localGroupSize[0]+kernel->width-1) * (localGroupSize[1]+kernel->height-1) * sizeof(CLPixelPacket)
426    + kernel->width*kernel->height*sizeof(float);
427
428  if (localMemoryRequirement > deviceLocalMemorySize)
429  {
430    localGroupSize[0] = 8;
431    localGroupSize[1] = 8;
432    localMemoryRequirement = (localGroupSize[0]+kernel->width-1) * (localGroupSize[1]+kernel->height-1) * sizeof(CLPixelPacket)
433      + kernel->width*kernel->height*sizeof(float);
434  }
435  if (localMemoryRequirement <= deviceLocalMemorySize)
436  {
437    /* get the OpenCL kernel */
438    clkernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "ConvolveOptimized");
439    if (clkernel == NULL)
440    {
441      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
442      goto cleanup;
443    }
444
445    /* set the kernel arguments */
446    i = 0;
447    clStatus =clEnv->library->clSetKernelArg(clkernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
448    clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(cl_mem),(void *)&filteredImageBuffer);
449    imageWidth = (unsigned int) image->columns;
450    imageHeight = (unsigned int) image->rows;
451    clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&imageWidth);
452    clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&imageHeight);
453    clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(cl_mem),(void *)&convolutionKernel);
454    filterWidth = (unsigned int) kernel->width;
455    filterHeight = (unsigned int) kernel->height;
456    clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&filterWidth);
457    clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&filterHeight);
458    matte = (image->alpha_trait != BlendPixelTrait)?1:0;
459    clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&matte);
460    clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(ChannelType),(void *)&channel);
461    clStatus|=clEnv->library->clSetKernelArg(clkernel,i++, (localGroupSize[0] + kernel->width-1)*(localGroupSize[1] + kernel->height-1)*sizeof(CLPixelPacket),NULL);
462    clStatus|=clEnv->library->clSetKernelArg(clkernel,i++, kernel->width*kernel->height*sizeof(float),NULL);
463    if (clStatus != CL_SUCCESS)
464    {
465      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
466      goto cleanup;
467    }
468
469    /* pad the global size to a multiple of the local work size dimension */
470    global_work_size[0] = ((image->columns + localGroupSize[0]  - 1)/localGroupSize[0] ) * localGroupSize[0] ;
471    global_work_size[1] = ((image->rows + localGroupSize[1] - 1)/localGroupSize[1]) * localGroupSize[1];
472
473    /* launch the kernel */
474    clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, clkernel, 2, NULL, global_work_size, localGroupSize, 0, NULL, NULL);
475    if (clStatus != CL_SUCCESS)
476    {
477      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
478      goto cleanup;
479    }
480  }
481  else
482  {
483    /* get the OpenCL kernel */
484    clkernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "Convolve");
485    if (clkernel == NULL)
486    {
487      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
488      goto cleanup;
489    }
490
491    /* set the kernel arguments */
492    i = 0;
493    clStatus =clEnv->library->clSetKernelArg(clkernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
494    clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(cl_mem),(void *)&filteredImageBuffer);
495    imageWidth = (unsigned int) image->columns;
496    imageHeight = (unsigned int) image->rows;
497    clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&imageWidth);
498    clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&imageHeight);
499    clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(cl_mem),(void *)&convolutionKernel);
500    filterWidth = (unsigned int) kernel->width;
501    filterHeight = (unsigned int) kernel->height;
502    clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&filterWidth);
503    clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&filterHeight);
504    matte = (image->alpha_trait != BlendPixelTrait)?1:0;
505    clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&matte);
506    clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(ChannelType),(void *)&channel);
507    if (clStatus != CL_SUCCESS)
508    {
509      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
510      goto cleanup;
511    }
512
513    localGroupSize[0] = 8;
514    localGroupSize[1] = 8;
515    global_work_size[0] = (image->columns + (localGroupSize[0]-1))/localGroupSize[0] * localGroupSize[0];
516    global_work_size[1] = (image->rows    + (localGroupSize[1]-1))/localGroupSize[1] * localGroupSize[1];
517    clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, clkernel, 2, NULL, global_work_size, localGroupSize, 0, NULL, NULL);
518
519    if (clStatus != CL_SUCCESS)
520    {
521      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
522      goto cleanup;
523    }
524  }
525  clEnv->library->clFlush(queue);
526
527  if (ALIGNED(filteredPixels,CLPixelPacket))
528  {
529    length = image->columns * image->rows;
530    clEnv->library->clEnqueueMapBuffer(queue, filteredImageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
531  }
532  else
533  {
534    length = image->columns * image->rows;
535    clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
536  }
537  if (clStatus != CL_SUCCESS)
538  {
539    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
540    goto cleanup;
541  }
542
543  outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
544
545cleanup:
546  OpenCLLogException(__FUNCTION__,__LINE__,exception);
547
548  image_view=DestroyCacheView(image_view);
549  if (filteredImage_view != NULL)
550    filteredImage_view=DestroyCacheView(filteredImage_view);
551
552  if (imageBuffer != NULL)
553    clEnv->library->clReleaseMemObject(imageBuffer);
554
555  if (filteredImageBuffer != NULL)
556    clEnv->library->clReleaseMemObject(filteredImageBuffer);
557
558  if (convolutionKernel != NULL)
559    clEnv->library->clReleaseMemObject(convolutionKernel);
560
561  if (clkernel != NULL)
562    RelinquishOpenCLKernel(clEnv, clkernel);
563
564  if (queue != NULL)
565    RelinquishOpenCLCommandQueue(clEnv, queue);
566
567  if (outputReady == MagickFalse)
568  {
569    if (filteredImage != NULL)
570    {
571      DestroyImage(filteredImage);
572      filteredImage = NULL;
573    }
574  }
575
576  return(filteredImage);
577}
578
579MagickExport Image *AccelerateConvolveImageChannel(const Image *image,
580  const ChannelType channel,const KernelInfo *kernel,ExceptionInfo *exception)
581{
582  Image
583    *filteredImage;
584
585  assert(image != NULL);
586  assert(kernel != (KernelInfo *) NULL);
587  assert(exception != (ExceptionInfo *) NULL);
588
589  if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
590      (checkAccelerateCondition(image, channel) == MagickFalse))
591    return NULL;
592
593  filteredImage=ComputeConvolveImage(image, channel, kernel, exception);
594  return(filteredImage);
595}
596
597static MagickBooleanType ComputeFunctionImage(Image *image,
598  const ChannelType channel,const MagickFunction function,
599  const size_t number_parameters,const double *parameters,
600  ExceptionInfo *exception)
601{
602  CacheView
603    *image_view;
604
605  cl_command_queue
606    queue;
607
608  cl_context
609    context;
610
611  cl_int
612    clStatus;
613
614  cl_kernel
615    clkernel;
616
617  cl_mem
618    imageBuffer,
619    parametersBuffer;
620
621  cl_mem_flags
622    mem_flags;
623
624  float
625    *parametersBufferPtr;
626
627  MagickBooleanType
628    status;
629
630  MagickCLEnv
631    clEnv;
632
633  MagickSizeType
634    length;
635
636  size_t
637    globalWorkSize[2];
638
639  unsigned int
640    i;
641
642  void
643    *pixels;
644
645  status = MagickFalse;
646
647  context = NULL;
648  clkernel = NULL;
649  queue = NULL;
650  imageBuffer = NULL;
651  parametersBuffer = NULL;
652
653  clEnv = GetDefaultOpenCLEnv();
654  context = GetOpenCLContext(clEnv);
655
656  image_view=AcquireAuthenticCacheView(image,exception);
657  pixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
658  if (pixels == (void *) NULL)
659  {
660    (void) OpenCLThrowMagickException(exception, GetMagickModule(), CacheWarning,
661      "GetPixelCachePixels failed.",
662      "'%s'", image->filename);
663    goto cleanup;
664  }
665
666
667  if (ALIGNED(pixels,CLPixelPacket))
668  {
669    mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
670  }
671  else
672  {
673    mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
674  }
675  /* create a CL buffer from image pixel buffer */
676  length = image->columns * image->rows;
677  imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)pixels, &clStatus);
678  if (clStatus != CL_SUCCESS)
679  {
680    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
681    goto cleanup;
682  }
683
684  parametersBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_ONLY|CL_MEM_ALLOC_HOST_PTR, number_parameters * sizeof(float), NULL, &clStatus);
685  if (clStatus != CL_SUCCESS)
686  {
687    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
688    goto cleanup;
689  }
690
691  queue = AcquireOpenCLCommandQueue(clEnv);
692
693  parametersBufferPtr = (float*)clEnv->library->clEnqueueMapBuffer(queue, parametersBuffer, CL_TRUE, CL_MAP_WRITE, 0, number_parameters * sizeof(float)
694                , 0, NULL, NULL, &clStatus);
695  if (clStatus != CL_SUCCESS)
696  {
697    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueMapBuffer failed.",".");
698    goto cleanup;
699  }
700  for (i = 0; i < number_parameters; i++)
701  {
702    parametersBufferPtr[i] = (float)parameters[i];
703  }
704  clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, parametersBuffer, parametersBufferPtr, 0, NULL, NULL);
705  if (clStatus != CL_SUCCESS)
706  {
707    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
708    goto cleanup;
709  }
710  clEnv->library->clFlush(queue);
711
712  clkernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "FunctionImage");
713  if (clkernel == NULL)
714  {
715    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
716    goto cleanup;
717  }
718
719  /* set the kernel arguments */
720  i = 0;
721  clStatus =clEnv->library->clSetKernelArg(clkernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
722  clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(ChannelType),(void *)&channel);
723  clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(MagickFunction),(void *)&function);
724  clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&number_parameters);
725  clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(cl_mem),(void *)&parametersBuffer);
726  if (clStatus != CL_SUCCESS)
727  {
728    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
729    goto cleanup;
730  }
731
732  globalWorkSize[0] = image->columns;
733  globalWorkSize[1] = image->rows;
734  /* launch the kernel */
735  clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, clkernel, 2, NULL, globalWorkSize, NULL, 0, NULL, NULL);
736  if (clStatus != CL_SUCCESS)
737  {
738    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
739    goto cleanup;
740  }
741  clEnv->library->clFlush(queue);
742
743
744  if (ALIGNED(pixels,CLPixelPacket))
745  {
746    length = image->columns * image->rows;
747    clEnv->library->clEnqueueMapBuffer(queue, imageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
748  }
749  else
750  {
751    length = image->columns * image->rows;
752    clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), pixels, 0, NULL, NULL);
753  }
754  if (clStatus != CL_SUCCESS)
755  {
756    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
757    goto cleanup;
758  }
759  status=SyncCacheViewAuthenticPixels(image_view,exception);
760
761cleanup:
762  OpenCLLogException(__FUNCTION__,__LINE__,exception);
763
764  image_view=DestroyCacheView(image_view);
765
766  if (clkernel != NULL) RelinquishOpenCLKernel(clEnv, clkernel);
767  if (queue != NULL) RelinquishOpenCLCommandQueue(clEnv, queue);
768  if (imageBuffer != NULL) clEnv->library->clReleaseMemObject(imageBuffer);
769  if (parametersBuffer != NULL) clEnv->library->clReleaseMemObject(parametersBuffer);
770
771  return(status);
772}
773
774MagickExport MagickBooleanType AccelerateFunctionImage(Image *image,
775  const ChannelType channel,const MagickFunction function,
776  const size_t number_parameters,const double *parameters,
777  ExceptionInfo *exception)
778{
779  MagickBooleanType
780    status;
781
782  assert(image != NULL);
783  assert(exception != (ExceptionInfo *) NULL);
784
785  if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
786      (checkAccelerateCondition(image, channel) == MagickFalse))
787    return(MagickFalse);
788
789  status=ComputeFunctionImage(image, channel, function, number_parameters, parameters, exception);
790  return(status);
791}
792
793/*
794%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
795%                                                                             %
796%                                                                             %
797%                                                                             %
798%     B l u r I m a g e  w i t h  O p e n C L                                 %
799%                                                                             %
800%                                                                             %
801%                                                                             %
802%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
803%
804%  BlurImage() blurs an image.  We convolve the image with a Gaussian operator
805%  of the given radius and standard deviation (sigma).  For reasonable results,
806%  the radius should be larger than sigma.  Use a radius of 0 and BlurImage()
807%  selects a suitable radius for you.
808%
809%  The format of the BlurImage method is:
810%
811%      Image *BlurImage(const Image *image,const double radius,
812%        const double sigma,ExceptionInfo *exception)
813%      Image *BlurImageChannel(const Image *image,const ChannelType channel,
814%        const double radius,const double sigma,ExceptionInfo *exception)
815%
816%  A description of each parameter follows:
817%
818%    o image: the image.
819%
820%    o channel: the channel type.
821%
822%    o radius: the radius of the Gaussian, in pixels, not counting the center
823%      pixel.
824%
825%    o sigma: the standard deviation of the Gaussian, in pixels.
826%
827%    o exception: return any errors or warnings in this structure.
828%
829*/
830
831static Image *ComputeBlurImage(const Image* image,const ChannelType channel,
832  const double radius,const double sigma,ExceptionInfo *exception)
833{
834  CacheView
835    *filteredImage_view,
836    *image_view;
837
838  char
839    geometry[MaxTextExtent];
840
841  cl_command_queue
842    queue;
843
844  cl_context
845    context;
846
847  cl_int
848    clStatus;
849
850  cl_kernel
851    blurColumnKernel,
852    blurRowKernel;
853
854  cl_mem
855    filteredImageBuffer,
856    imageBuffer,
857    imageKernelBuffer,
858    tempImageBuffer;
859
860  cl_mem_flags
861    mem_flags;
862
863  const void
864    *inputPixels;
865
866  float
867    *kernelBufferPtr;
868
869  Image
870    *filteredImage;
871
872  MagickBooleanType
873    outputReady;
874
875  MagickCLEnv
876    clEnv;
877
878  MagickSizeType
879    length;
880
881  KernelInfo
882    *kernel;
883
884  unsigned int
885    i,
886    imageColumns,
887    imageRows,
888    kernelWidth;
889
890  void
891    *filteredPixels,
892    *hostPtr;
893
894  context = NULL;
895  filteredImage = NULL;
896  filteredImage_view = NULL;
897  imageBuffer = NULL;
898  tempImageBuffer = NULL;
899  filteredImageBuffer = NULL;
900  imageKernelBuffer = NULL;
901  blurRowKernel = NULL;
902  blurColumnKernel = NULL;
903  queue = NULL;
904  kernel = NULL;
905
906  outputReady = MagickFalse;
907
908  clEnv = GetDefaultOpenCLEnv();
909  context = GetOpenCLContext(clEnv);
910  queue = AcquireOpenCLCommandQueue(clEnv);
911
912  /* Create and initialize OpenCL buffers. */
913  {
914    image_view=AcquireVirtualCacheView(image,exception);
915    inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
916    if (inputPixels == (const void *) NULL)
917    {
918      (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
919      goto cleanup;
920    }
921    /* If the host pointer is aligned to the size of CLPixelPacket,
922     then use the host buffer directly from the GPU; otherwise,
923     create a buffer on the GPU and copy the data over */
924    if (ALIGNED(inputPixels,CLPixelPacket))
925    {
926      mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
927    }
928    else
929    {
930      mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
931    }
932    /* create a CL buffer from image pixel buffer */
933    length = image->columns * image->rows;
934    imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
935    if (clStatus != CL_SUCCESS)
936    {
937      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
938      goto cleanup;
939    }
940  }
941
942  /* create output */
943  {
944    filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
945    assert(filteredImage != NULL);
946    if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
947    {
948      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
949      goto cleanup;
950    }
951    filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
952    filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
953    if (filteredPixels == (void *) NULL)
954    {
955      (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
956      goto cleanup;
957    }
958
959    if (ALIGNED(filteredPixels,CLPixelPacket))
960    {
961      mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
962      hostPtr = filteredPixels;
963    }
964    else
965    {
966      mem_flags = CL_MEM_WRITE_ONLY;
967      hostPtr = NULL;
968    }
969    /* create a CL buffer from image pixel buffer */
970    length = image->columns * image->rows;
971    filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
972    if (clStatus != CL_SUCCESS)
973    {
974      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
975      goto cleanup;
976    }
977  }
978
979  /* create processing kernel */
980  {
981    (void) FormatLocaleString(geometry,MaxTextExtent,"blur:%.20gx%.20g;blur:%.20gx%.20g+90",radius,sigma,radius,sigma);
982    kernel=AcquireKernelInfo(geometry,exception);
983    if (kernel == (KernelInfo *) NULL)
984    {
985      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "MemoryAllocationFailed.",".");
986      goto cleanup;
987    }
988
989    imageKernelBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_ONLY|CL_MEM_ALLOC_HOST_PTR, kernel->width * sizeof(float), NULL, &clStatus);
990    if (clStatus != CL_SUCCESS)
991    {
992      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
993      goto cleanup;
994    }
995    kernelBufferPtr = (float*)clEnv->library->clEnqueueMapBuffer(queue, imageKernelBuffer, CL_TRUE, CL_MAP_WRITE, 0, kernel->width * sizeof(float), 0, NULL, NULL, &clStatus);
996    if (clStatus != CL_SUCCESS)
997    {
998      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueMapBuffer failed.",".");
999      goto cleanup;
1000    }
1001
1002    for (i = 0; i < kernel->width; i++)
1003    {
1004      kernelBufferPtr[i] = (float) kernel->values[i];
1005    }
1006
1007    clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, imageKernelBuffer, kernelBufferPtr, 0, NULL, NULL);
1008    if (clStatus != CL_SUCCESS)
1009    {
1010      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
1011      goto cleanup;
1012    }
1013  }
1014
1015  {
1016
1017    /* create temp buffer */
1018    {
1019      length = image->columns * image->rows;
1020      tempImageBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_WRITE, length * 4 * sizeof(float), NULL, &clStatus);
1021      if (clStatus != CL_SUCCESS)
1022      {
1023        (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
1024        goto cleanup;
1025      }
1026    }
1027
1028    /* get the OpenCL kernels */
1029    {
1030      blurRowKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "BlurRow");
1031      if (blurRowKernel == NULL)
1032      {
1033        (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
1034        goto cleanup;
1035      };
1036
1037      blurColumnKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "BlurColumn");
1038      if (blurColumnKernel == NULL)
1039      {
1040        (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
1041        goto cleanup;
1042      };
1043    }
1044
1045    {
1046      /* need logic to decide this value */
1047      int chunkSize = 256;
1048
1049      {
1050        imageColumns = (unsigned int) image->columns;
1051        imageRows = (unsigned int) image->rows;
1052
1053        /* set the kernel arguments */
1054        i = 0;
1055        clStatus=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
1056        clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&tempImageBuffer);
1057        clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(ChannelType),&channel);
1058        clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&imageKernelBuffer);
1059        kernelWidth = (unsigned int) kernel->width;
1060        clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&kernelWidth);
1061        clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&imageColumns);
1062        clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&imageRows);
1063        clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(CLPixelPacket)*(chunkSize+kernel->width),(void *)NULL);
1064        if (clStatus != CL_SUCCESS)
1065        {
1066          (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
1067          goto cleanup;
1068        }
1069      }
1070
1071      /* launch the kernel */
1072      {
1073        size_t gsize[2];
1074        size_t wsize[2];
1075
1076        gsize[0] = chunkSize*((image->columns+chunkSize-1)/chunkSize);
1077        gsize[1] = image->rows;
1078        wsize[0] = chunkSize;
1079        wsize[1] = 1;
1080
1081        clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, blurRowKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
1082        if (clStatus != CL_SUCCESS)
1083        {
1084          (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
1085          goto cleanup;
1086        }
1087        clEnv->library->clFlush(queue);
1088      }
1089    }
1090
1091    {
1092      /* need logic to decide this value */
1093      int chunkSize = 256;
1094
1095      {
1096        imageColumns = (unsigned int) image->columns;
1097        imageRows = (unsigned int) image->rows;
1098
1099        /* set the kernel arguments */
1100        i = 0;
1101        clStatus=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(cl_mem),(void *)&tempImageBuffer);
1102        clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(cl_mem),(void *)&filteredImageBuffer);
1103        clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(ChannelType),&channel);
1104        clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(cl_mem),(void *)&imageKernelBuffer);
1105        kernelWidth = (unsigned int) kernel->width;
1106        clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(unsigned int),(void *)&kernelWidth);
1107        clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(unsigned int),(void *)&imageColumns);
1108        clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(unsigned int),(void *)&imageRows);
1109        clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(cl_float4)*(chunkSize+kernel->width),(void *)NULL);
1110        if (clStatus != CL_SUCCESS)
1111        {
1112          (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
1113          goto cleanup;
1114        }
1115      }
1116
1117      /* launch the kernel */
1118      {
1119        size_t gsize[2];
1120        size_t wsize[2];
1121
1122        gsize[0] = image->columns;
1123        gsize[1] = chunkSize*((image->rows+chunkSize-1)/chunkSize);
1124        wsize[0] = 1;
1125        wsize[1] = chunkSize;
1126
1127        clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, blurColumnKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
1128        if (clStatus != CL_SUCCESS)
1129        {
1130          (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
1131          goto cleanup;
1132        }
1133        clEnv->library->clFlush(queue);
1134      }
1135    }
1136
1137  }
1138
1139  /* get result */
1140  if (ALIGNED(filteredPixels,CLPixelPacket))
1141  {
1142    length = image->columns * image->rows;
1143    clEnv->library->clEnqueueMapBuffer(queue, filteredImageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
1144  }
1145  else
1146  {
1147    length = image->columns * image->rows;
1148    clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
1149  }
1150  if (clStatus != CL_SUCCESS)
1151  {
1152    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
1153    goto cleanup;
1154  }
1155
1156  outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
1157
1158cleanup:
1159  OpenCLLogException(__FUNCTION__,__LINE__,exception);
1160
1161  image_view=DestroyCacheView(image_view);
1162  if (filteredImage_view != NULL)
1163    filteredImage_view=DestroyCacheView(filteredImage_view);
1164
1165  if (imageBuffer!=NULL)     clEnv->library->clReleaseMemObject(imageBuffer);
1166  if (tempImageBuffer!=NULL)      clEnv->library->clReleaseMemObject(tempImageBuffer);
1167  if (filteredImageBuffer!=NULL)  clEnv->library->clReleaseMemObject(filteredImageBuffer);
1168  if (imageKernelBuffer!=NULL)    clEnv->library->clReleaseMemObject(imageKernelBuffer);
1169  if (blurRowKernel!=NULL)        RelinquishOpenCLKernel(clEnv, blurRowKernel);
1170  if (blurColumnKernel!=NULL)     RelinquishOpenCLKernel(clEnv, blurColumnKernel);
1171  if (queue != NULL)              RelinquishOpenCLCommandQueue(clEnv, queue);
1172  if (kernel!=NULL)               DestroyKernelInfo(kernel);
1173  if (outputReady == MagickFalse && filteredImage != NULL)
1174    filteredImage=DestroyImage(filteredImage);
1175  return(filteredImage);
1176}
1177
1178static Image* ComputeBlurImageSection(const Image* image,
1179  const ChannelType channel,const double radius,const double sigma,
1180  ExceptionInfo *exception)
1181{
1182  CacheView
1183    *filteredImage_view,
1184    *image_view;
1185
1186  char
1187    geometry[MaxTextExtent];
1188
1189  cl_command_queue
1190    queue;
1191
1192  cl_int
1193    clStatus;
1194
1195  cl_kernel
1196    blurColumnKernel,
1197    blurRowKernel;
1198
1199  cl_mem
1200    imageBuffer,
1201    tempImageBuffer,
1202    filteredImageBuffer,
1203    imageKernelBuffer;
1204
1205  cl_mem_flags
1206    mem_flags;
1207
1208  cl_context
1209    context;
1210
1211  const void
1212    *inputPixels;
1213
1214  float
1215    *kernelBufferPtr;
1216
1217  Image
1218    *filteredImage;
1219
1220  KernelInfo
1221    *kernel;
1222
1223  MagickBooleanType
1224    outputReady;
1225
1226  MagickCLEnv
1227    clEnv;
1228
1229  MagickSizeType
1230    length;
1231
1232  unsigned int
1233    i,
1234    imageColumns,
1235    imageRows,
1236    kernelWidth;
1237
1238  void
1239    *filteredPixels,
1240    *hostPtr;
1241
1242  context = NULL;
1243  filteredImage = NULL;
1244  filteredImage_view = NULL;
1245  imageBuffer = NULL;
1246  tempImageBuffer = NULL;
1247  filteredImageBuffer = NULL;
1248  imageKernelBuffer = NULL;
1249  blurRowKernel = NULL;
1250  blurColumnKernel = NULL;
1251  queue = NULL;
1252  kernel = NULL;
1253
1254  outputReady = MagickFalse;
1255
1256  clEnv = GetDefaultOpenCLEnv();
1257  context = GetOpenCLContext(clEnv);
1258  queue = AcquireOpenCLCommandQueue(clEnv);
1259
1260  /* Create and initialize OpenCL buffers. */
1261  {
1262    image_view=AcquireVirtualCacheView(image,exception);
1263    inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
1264    if (inputPixels == (const void *) NULL)
1265    {
1266      (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
1267      goto cleanup;
1268    }
1269    /* If the host pointer is aligned to the size of CLPixelPacket,
1270     then use the host buffer directly from the GPU; otherwise,
1271     create a buffer on the GPU and copy the data over */
1272    if (ALIGNED(inputPixels,CLPixelPacket))
1273    {
1274      mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
1275    }
1276    else
1277    {
1278      mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
1279    }
1280    /* create a CL buffer from image pixel buffer */
1281    length = image->columns * image->rows;
1282    imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
1283    if (clStatus != CL_SUCCESS)
1284    {
1285      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
1286      goto cleanup;
1287    }
1288  }
1289
1290  /* create output */
1291  {
1292    filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1293    assert(filteredImage != NULL);
1294    if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
1295    {
1296      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
1297      goto cleanup;
1298    }
1299    filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
1300    filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
1301    if (filteredPixels == (void *) NULL)
1302    {
1303      (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
1304      goto cleanup;
1305    }
1306
1307    if (ALIGNED(filteredPixels,CLPixelPacket))
1308    {
1309      mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
1310      hostPtr = filteredPixels;
1311    }
1312    else
1313    {
1314      mem_flags = CL_MEM_WRITE_ONLY;
1315      hostPtr = NULL;
1316    }
1317    /* create a CL buffer from image pixel buffer */
1318    length = image->columns * image->rows;
1319    filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
1320    if (clStatus != CL_SUCCESS)
1321    {
1322      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
1323      goto cleanup;
1324    }
1325  }
1326
1327  /* create processing kernel */
1328  {
1329    (void) FormatLocaleString(geometry,MaxTextExtent,"blur:%.20gx%.20g;blur:%.20gx%.20g+90",radius,sigma,radius,sigma);
1330    kernel=AcquireKernelInfo(geometry,exception);
1331    if (kernel == (KernelInfo *) NULL)
1332    {
1333      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "MemoryAllocationFailed.",".");
1334      goto cleanup;
1335    }
1336
1337    imageKernelBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_ONLY|CL_MEM_ALLOC_HOST_PTR, kernel->width * sizeof(float), NULL, &clStatus);
1338    if (clStatus != CL_SUCCESS)
1339    {
1340      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
1341      goto cleanup;
1342    }
1343    kernelBufferPtr = (float*)clEnv->library->clEnqueueMapBuffer(queue, imageKernelBuffer, CL_TRUE, CL_MAP_WRITE, 0, kernel->width * sizeof(float), 0, NULL, NULL, &clStatus);
1344    if (clStatus != CL_SUCCESS)
1345    {
1346      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueMapBuffer failed.",".");
1347      goto cleanup;
1348    }
1349
1350    for (i = 0; i < kernel->width; i++)
1351    {
1352      kernelBufferPtr[i] = (float) kernel->values[i];
1353    }
1354
1355    clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, imageKernelBuffer, kernelBufferPtr, 0, NULL, NULL);
1356    if (clStatus != CL_SUCCESS)
1357    {
1358      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
1359      goto cleanup;
1360    }
1361  }
1362
1363  {
1364    unsigned int offsetRows;
1365    unsigned int sec;
1366
1367    /* create temp buffer */
1368    {
1369      length = image->columns * (image->rows / 2 + 1 + (kernel->width-1) / 2);
1370      tempImageBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_WRITE, length * 4 * sizeof(float), NULL, &clStatus);
1371      if (clStatus != CL_SUCCESS)
1372      {
1373        (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
1374        goto cleanup;
1375      }
1376    }
1377
1378    /* get the OpenCL kernels */
1379    {
1380      blurRowKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "BlurRowSection");
1381      if (blurRowKernel == NULL)
1382      {
1383        (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
1384        goto cleanup;
1385      };
1386
1387      blurColumnKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "BlurColumnSection");
1388      if (blurColumnKernel == NULL)
1389      {
1390        (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
1391        goto cleanup;
1392      };
1393    }
1394
1395    for (sec = 0; sec < 2; sec++)
1396    {
1397      {
1398        /* need logic to decide this value */
1399        int chunkSize = 256;
1400
1401        {
1402          imageColumns = (unsigned int) image->columns;
1403          if (sec == 0)
1404            imageRows = (unsigned int) (image->rows / 2 + (kernel->width-1) / 2);
1405          else
1406            imageRows = (unsigned int) ((image->rows - image->rows / 2) + (kernel->width-1) / 2);
1407
1408          offsetRows = (unsigned int) (sec * image->rows / 2);
1409
1410          kernelWidth = (unsigned int) kernel->width;
1411
1412          /* set the kernel arguments */
1413          i = 0;
1414          clStatus=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
1415          clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&tempImageBuffer);
1416          clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(ChannelType),&channel);
1417          clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&imageKernelBuffer);
1418          clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&kernelWidth);
1419          clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&imageColumns);
1420          clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&imageRows);
1421          clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(CLPixelPacket)*(chunkSize+kernel->width),(void *)NULL);
1422          clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&offsetRows);
1423          clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&sec);
1424          if (clStatus != CL_SUCCESS)
1425          {
1426            (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
1427            goto cleanup;
1428          }
1429        }
1430
1431        /* launch the kernel */
1432        {
1433          size_t gsize[2];
1434          size_t wsize[2];
1435
1436          gsize[0] = chunkSize*((imageColumns+chunkSize-1)/chunkSize);
1437          gsize[1] = imageRows;
1438          wsize[0] = chunkSize;
1439          wsize[1] = 1;
1440
1441          clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, blurRowKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
1442          if (clStatus != CL_SUCCESS)
1443          {
1444            (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
1445            goto cleanup;
1446          }
1447          clEnv->library->clFlush(queue);
1448        }
1449      }
1450
1451      {
1452        /* need logic to decide this value */
1453        int chunkSize = 256;
1454
1455        {
1456          imageColumns = (unsigned int) image->columns;
1457          if (sec == 0)
1458            imageRows = (unsigned int) (image->rows / 2);
1459          else
1460            imageRows = (unsigned int) ((image->rows - image->rows / 2));
1461
1462          offsetRows = (unsigned int) (sec * image->rows / 2);
1463
1464          kernelWidth = (unsigned int) kernel->width;
1465
1466          /* set the kernel arguments */
1467          i = 0;
1468          clStatus=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(cl_mem),(void *)&tempImageBuffer);
1469          clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(cl_mem),(void *)&filteredImageBuffer);
1470          clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(ChannelType),&channel);
1471          clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(cl_mem),(void *)&imageKernelBuffer);
1472          clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(unsigned int),(void *)&kernelWidth);
1473          clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(unsigned int),(void *)&imageColumns);
1474          clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(unsigned int),(void *)&imageRows);
1475          clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(cl_float4)*(chunkSize+kernel->width),(void *)NULL);
1476          clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(unsigned int),(void *)&offsetRows);
1477          clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(unsigned int),(void *)&sec);
1478          if (clStatus != CL_SUCCESS)
1479          {
1480            (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
1481            goto cleanup;
1482          }
1483        }
1484
1485        /* launch the kernel */
1486        {
1487          size_t gsize[2];
1488          size_t wsize[2];
1489
1490          gsize[0] = imageColumns;
1491          gsize[1] = chunkSize*((imageRows+chunkSize-1)/chunkSize);
1492          wsize[0] = 1;
1493          wsize[1] = chunkSize;
1494
1495          clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, blurColumnKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
1496          if (clStatus != CL_SUCCESS)
1497          {
1498            (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
1499            goto cleanup;
1500          }
1501          clEnv->library->clFlush(queue);
1502        }
1503      }
1504    }
1505
1506  }
1507
1508  /* get result */
1509  if (ALIGNED(filteredPixels,CLPixelPacket))
1510  {
1511    length = image->columns * image->rows;
1512    clEnv->library->clEnqueueMapBuffer(queue, filteredImageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
1513  }
1514  else
1515  {
1516    length = image->columns * image->rows;
1517    clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
1518  }
1519  if (clStatus != CL_SUCCESS)
1520  {
1521    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
1522    goto cleanup;
1523  }
1524
1525  outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
1526
1527cleanup:
1528  OpenCLLogException(__FUNCTION__,__LINE__,exception);
1529
1530  image_view=DestroyCacheView(image_view);
1531  if (filteredImage_view != NULL)
1532    filteredImage_view=DestroyCacheView(filteredImage_view);
1533
1534  if (imageBuffer!=NULL)     clEnv->library->clReleaseMemObject(imageBuffer);
1535  if (tempImageBuffer!=NULL)      clEnv->library->clReleaseMemObject(tempImageBuffer);
1536  if (filteredImageBuffer!=NULL)  clEnv->library->clReleaseMemObject(filteredImageBuffer);
1537  if (imageKernelBuffer!=NULL)    clEnv->library->clReleaseMemObject(imageKernelBuffer);
1538  if (blurRowKernel!=NULL)        RelinquishOpenCLKernel(clEnv, blurRowKernel);
1539  if (blurColumnKernel!=NULL)     RelinquishOpenCLKernel(clEnv, blurColumnKernel);
1540  if (queue != NULL)              RelinquishOpenCLCommandQueue(clEnv, queue);
1541  if (kernel!=NULL)               DestroyKernelInfo(kernel);
1542  if (outputReady == MagickFalse)
1543  {
1544    if (filteredImage != NULL)
1545    {
1546      DestroyImage(filteredImage);
1547      filteredImage = NULL;
1548    }
1549  }
1550  return filteredImage;
1551}
1552
1553static Image *ComputeUnsharpMaskImageSingle(const Image *image,
1554  const ChannelType channel,const double radius,const double sigma,
1555  const double gain,const double threshold,int blurOnly, ExceptionInfo *exception);
1556
1557static Image* ComputeBlurImageSingle(const Image* image,
1558  const ChannelType channel,const double radius,const double sigma,
1559  ExceptionInfo *exception)
1560{
1561  return ComputeUnsharpMaskImageSingle(image, channel, radius, sigma, 0.0, 0.0, 1, exception);
1562}
1563
1564MagickExport Image* AccelerateBlurImage(const Image *image,
1565  const ChannelType channel,const double radius,const double sigma,
1566  ExceptionInfo *exception)
1567{
1568  Image
1569    *filteredImage;
1570
1571  assert(image != NULL);
1572  assert(exception != (ExceptionInfo *) NULL);
1573
1574  if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
1575      (checkAccelerateCondition(image, channel) == MagickFalse))
1576    return NULL;
1577
1578  if (radius < 12.1)
1579	filteredImage=ComputeBlurImageSingle(image, channel, radius, sigma, exception);
1580  else if (splitImage(image) && (image->rows / 2 > radius))
1581    filteredImage=ComputeBlurImageSection(image, channel, radius, sigma, exception);
1582  else
1583    filteredImage=ComputeBlurImage(image, channel, radius, sigma, exception);
1584
1585  return(filteredImage);
1586}
1587
1588/*
1589%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1590%                                                                             %
1591%                                                                             %
1592%                                                                             %
1593%     R o t a t i o n a l B l u r I m a g e  w i t h  O p e n C L             %
1594%                                                                             %
1595%                                                                             %
1596%                                                                             %
1597%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1598%
1599%  RotationalBlurImage() applies a rotational blur to the image.
1600%
1601%  Andrew Protano contributed this effect.
1602%
1603%  The format of the RotationalBlurImage method is:
1604%
1605%    Image *RotationalBlurImage(const Image *image,const double angle,
1606%      ExceptionInfo *exception)
1607%    Image *RotationalBlurImageChannel(const Image *image,const ChannelType channel,
1608%      const double angle,ExceptionInfo *exception)
1609%
1610%  A description of each parameter follows:
1611%
1612%    o image: the image.
1613%
1614%    o channel: the channel type.
1615%
1616%    o angle: the angle of the rotational blur.
1617%
1618%    o exception: return any errors or warnings in this structure.
1619%
1620*/
1621
1622static Image* ComputeRotationalBlurImage(const Image *image,
1623  const ChannelType channel,const double angle,ExceptionInfo *exception)
1624{
1625  CacheView
1626    *image_view,
1627    *filteredImage_view;
1628
1629  cl_command_queue
1630    queue;
1631
1632  cl_context
1633    context;
1634
1635  cl_float2
1636    blurCenter;
1637
1638  cl_float4
1639    biasPixel;
1640
1641  cl_int
1642    clStatus;
1643
1644  cl_mem
1645    cosThetaBuffer,
1646    filteredImageBuffer,
1647    imageBuffer,
1648    sinThetaBuffer;
1649
1650  cl_mem_flags
1651    mem_flags;
1652
1653  cl_kernel
1654    rotationalBlurKernel;
1655
1656  const void
1657    *inputPixels;
1658
1659  float
1660    blurRadius,
1661    *cosThetaPtr,
1662    offset,
1663    *sinThetaPtr,
1664    theta;
1665
1666  Image
1667    *filteredImage;
1668
1669  MagickBooleanType
1670    outputReady;
1671
1672  MagickCLEnv
1673    clEnv;
1674
1675  PixelInfo
1676    bias;
1677
1678  MagickSizeType
1679    length;
1680
1681  size_t
1682    global_work_size[2];
1683
1684  unsigned int
1685    cossin_theta_size,
1686    i,
1687    matte;
1688
1689  void
1690    *filteredPixels,
1691    *hostPtr;
1692
1693  outputReady = MagickFalse;
1694  context = NULL;
1695  filteredImage = NULL;
1696  filteredImage_view = NULL;
1697  imageBuffer = NULL;
1698  filteredImageBuffer = NULL;
1699  sinThetaBuffer = NULL;
1700  cosThetaBuffer = NULL;
1701  queue = NULL;
1702  rotationalBlurKernel = NULL;
1703
1704
1705  clEnv = GetDefaultOpenCLEnv();
1706  context = GetOpenCLContext(clEnv);
1707
1708
1709  /* Create and initialize OpenCL buffers. */
1710
1711  image_view=AcquireVirtualCacheView(image,exception);
1712  inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
1713  if (inputPixels == (const void *) NULL)
1714  {
1715    (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
1716    goto cleanup;
1717  }
1718
1719  /* If the host pointer is aligned to the size of CLPixelPacket,
1720     then use the host buffer directly from the GPU; otherwise,
1721     create a buffer on the GPU and copy the data over */
1722  if (ALIGNED(inputPixels,CLPixelPacket))
1723  {
1724    mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
1725  }
1726  else
1727  {
1728    mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
1729  }
1730  /* create a CL buffer from image pixel buffer */
1731  length = image->columns * image->rows;
1732  imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
1733  if (clStatus != CL_SUCCESS)
1734  {
1735    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
1736    goto cleanup;
1737  }
1738
1739
1740  filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1741  assert(filteredImage != NULL);
1742  if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
1743  {
1744    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
1745    goto cleanup;
1746  }
1747  filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
1748  filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
1749  if (filteredPixels == (void *) NULL)
1750  {
1751    (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
1752    goto cleanup;
1753  }
1754
1755  if (ALIGNED(filteredPixels,CLPixelPacket))
1756  {
1757    mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
1758    hostPtr = filteredPixels;
1759  }
1760  else
1761  {
1762    mem_flags = CL_MEM_WRITE_ONLY;
1763    hostPtr = NULL;
1764  }
1765  /* create a CL buffer from image pixel buffer */
1766  length = image->columns * image->rows;
1767  filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
1768  if (clStatus != CL_SUCCESS)
1769  {
1770    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
1771    goto cleanup;
1772  }
1773
1774  blurCenter.s[0] = (float) (image->columns-1)/2.0;
1775  blurCenter.s[1] = (float) (image->rows-1)/2.0;
1776  blurRadius=hypot(blurCenter.s[0],blurCenter.s[1]);
1777  cossin_theta_size=(unsigned int) fabs(4.0*DegreesToRadians(angle)*sqrt((double)blurRadius)+2UL);
1778
1779  /* create a buffer for sin_theta and cos_theta */
1780  sinThetaBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_ONLY|CL_MEM_ALLOC_HOST_PTR, cossin_theta_size * sizeof(float), NULL, &clStatus);
1781  if (clStatus != CL_SUCCESS)
1782  {
1783    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
1784    goto cleanup;
1785  }
1786  cosThetaBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_ONLY|CL_MEM_ALLOC_HOST_PTR, cossin_theta_size * sizeof(float), NULL, &clStatus);
1787  if (clStatus != CL_SUCCESS)
1788  {
1789    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
1790    goto cleanup;
1791  }
1792
1793
1794  queue = AcquireOpenCLCommandQueue(clEnv);
1795  sinThetaPtr = (float*) clEnv->library->clEnqueueMapBuffer(queue, sinThetaBuffer, CL_TRUE, CL_MAP_WRITE, 0, cossin_theta_size*sizeof(float), 0, NULL, NULL, &clStatus);
1796  if (clStatus != CL_SUCCESS)
1797  {
1798    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnqueuemapBuffer failed.",".");
1799    goto cleanup;
1800  }
1801
1802  cosThetaPtr = (float*) clEnv->library->clEnqueueMapBuffer(queue, cosThetaBuffer, CL_TRUE, CL_MAP_WRITE, 0, cossin_theta_size*sizeof(float), 0, NULL, NULL, &clStatus);
1803  if (clStatus != CL_SUCCESS)
1804  {
1805    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnqueuemapBuffer failed.",".");
1806    goto cleanup;
1807  }
1808
1809  theta=DegreesToRadians(angle)/(MagickRealType) (cossin_theta_size-1);
1810  offset=theta*(MagickRealType) (cossin_theta_size-1)/2.0;
1811  for (i=0; i < (ssize_t) cossin_theta_size; i++)
1812  {
1813    cosThetaPtr[i]=(float)cos((double) (theta*i-offset));
1814    sinThetaPtr[i]=(float)sin((double) (theta*i-offset));
1815  }
1816
1817  clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, sinThetaBuffer, sinThetaPtr, 0, NULL, NULL);
1818  clStatus |= clEnv->library->clEnqueueUnmapMemObject(queue, cosThetaBuffer, cosThetaPtr, 0, NULL, NULL);
1819  if (clStatus != CL_SUCCESS)
1820  {
1821    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
1822    goto cleanup;
1823  }
1824
1825  /* get the OpenCL kernel */
1826  rotationalBlurKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "RotationalBlur");
1827  if (rotationalBlurKernel == NULL)
1828  {
1829    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
1830    goto cleanup;
1831  }
1832
1833
1834  /* set the kernel arguments */
1835  i = 0;
1836  clStatus=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
1837  clStatus|=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(cl_mem),(void *)&filteredImageBuffer);
1838
1839  GetPixelInfo(image,&bias);
1840  biasPixel.s[0] = bias.red;
1841  biasPixel.s[1] = bias.green;
1842  biasPixel.s[2] = bias.blue;
1843  biasPixel.s[3] = bias.alpha;
1844  clStatus|=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(cl_float4), &biasPixel);
1845  clStatus|=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(ChannelType), &channel);
1846
1847  matte = (image->alpha_trait != BlendPixelTrait)?1:0;
1848  clStatus|=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(unsigned int), &matte);
1849
1850  clStatus=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(cl_float2), &blurCenter);
1851
1852  clStatus|=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(cl_mem),(void *)&cosThetaBuffer);
1853  clStatus|=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(cl_mem),(void *)&sinThetaBuffer);
1854  clStatus|=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(unsigned int), &cossin_theta_size);
1855  if (clStatus != CL_SUCCESS)
1856  {
1857    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
1858    goto cleanup;
1859  }
1860
1861
1862  global_work_size[0] = image->columns;
1863  global_work_size[1] = image->rows;
1864  /* launch the kernel */
1865  clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, rotationalBlurKernel, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
1866  if (clStatus != CL_SUCCESS)
1867  {
1868    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
1869    goto cleanup;
1870  }
1871  clEnv->library->clFlush(queue);
1872
1873  if (ALIGNED(filteredPixels,CLPixelPacket))
1874  {
1875    length = image->columns * image->rows;
1876    clEnv->library->clEnqueueMapBuffer(queue, filteredImageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
1877  }
1878  else
1879  {
1880    length = image->columns * image->rows;
1881    clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
1882  }
1883  if (clStatus != CL_SUCCESS)
1884  {
1885    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
1886    goto cleanup;
1887  }
1888  outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
1889
1890cleanup:
1891  OpenCLLogException(__FUNCTION__,__LINE__,exception);
1892
1893  image_view=DestroyCacheView(image_view);
1894  if (filteredImage_view != NULL)
1895    filteredImage_view=DestroyCacheView(filteredImage_view);
1896
1897  if (filteredImageBuffer!=NULL)  clEnv->library->clReleaseMemObject(filteredImageBuffer);
1898  if (imageBuffer!=NULL)     clEnv->library->clReleaseMemObject(imageBuffer);
1899  if (sinThetaBuffer!=NULL)       clEnv->library->clReleaseMemObject(sinThetaBuffer);
1900  if (cosThetaBuffer!=NULL)       clEnv->library->clReleaseMemObject(cosThetaBuffer);
1901  if (rotationalBlurKernel!=NULL) RelinquishOpenCLKernel(clEnv, rotationalBlurKernel);
1902  if (queue != NULL)              RelinquishOpenCLCommandQueue(clEnv, queue);
1903  if (outputReady == MagickFalse)
1904  {
1905    if (filteredImage != NULL)
1906    {
1907      DestroyImage(filteredImage);
1908      filteredImage = NULL;
1909    }
1910  }
1911  return filteredImage;
1912}
1913
1914MagickExport Image* AccelerateRotationalBlurImage(const Image *image,
1915  const ChannelType channel,const double angle,ExceptionInfo *exception)
1916{
1917  Image
1918    *filteredImage;
1919
1920  assert(image != NULL);
1921  assert(exception != (ExceptionInfo *) NULL);
1922
1923  if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
1924      (checkAccelerateCondition(image, channel) == MagickFalse))
1925    return NULL;
1926
1927  filteredImage=ComputeRotationalBlurImage(image, channel, angle, exception);
1928  return filteredImage;
1929}
1930
1931/*
1932%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1933%                                                                             %
1934%                                                                             %
1935%                                                                             %
1936%     U n s h a r p M a s k I m a g e  w i t h  O p e n C L                   %
1937%                                                                             %
1938%                                                                             %
1939%                                                                             %
1940%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1941%
1942%  UnsharpMaskImage() sharpens one or more image channels.  We convolve the
1943%  image with a Gaussian operator of the given radius and standard deviation
1944%  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
1945%  radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
1946%
1947%  The format of the UnsharpMaskImage method is:
1948%
1949%    Image *UnsharpMaskImage(const Image *image,const double radius,
1950%      const double sigma,const double amount,const double threshold,
1951%      ExceptionInfo *exception)
1952%    Image *UnsharpMaskImageChannel(const Image *image,
1953%      const ChannelType channel,const double radius,const double sigma,
1954%      const double gain,const double threshold,ExceptionInfo *exception)
1955%
1956%  A description of each parameter follows:
1957%
1958%    o image: the image.
1959%
1960%    o channel: the channel type.
1961%
1962%    o radius: the radius of the Gaussian, in pixels, not counting the center
1963%      pixel.
1964%
1965%    o sigma: the standard deviation of the Gaussian, in pixels.
1966%
1967%    o gain: the percentage of the difference between the original and the
1968%      blur image that is added back into the original.
1969%
1970%    o threshold: the threshold in pixels needed to apply the diffence gain.
1971%
1972%    o exception: return any errors or warnings in this structure.
1973%
1974*/
1975
1976static Image *ComputeUnsharpMaskImage(const Image *image,
1977  const ChannelType channel,const double radius,const double sigma,
1978  const double gain,const double threshold,ExceptionInfo *exception)
1979{
1980  CacheView
1981    *filteredImage_view,
1982    *image_view;
1983
1984  char
1985    geometry[MaxTextExtent];
1986
1987  cl_command_queue
1988    queue;
1989
1990  cl_context
1991    context;
1992
1993  cl_int
1994    clStatus;
1995
1996  cl_kernel
1997    blurRowKernel,
1998    unsharpMaskBlurColumnKernel;
1999
2000  cl_mem
2001    filteredImageBuffer,
2002    imageBuffer,
2003    imageKernelBuffer,
2004    tempImageBuffer;
2005
2006  cl_mem_flags
2007    mem_flags;
2008
2009  const void
2010    *inputPixels;
2011
2012  float
2013    fGain,
2014    fThreshold,
2015    *kernelBufferPtr;
2016
2017  Image
2018    *filteredImage;
2019
2020  int
2021    chunkSize;
2022
2023  KernelInfo
2024    *kernel;
2025
2026  MagickBooleanType
2027    outputReady;
2028
2029  MagickCLEnv
2030    clEnv;
2031
2032  MagickSizeType
2033    length;
2034
2035  void
2036    *filteredPixels,
2037    *hostPtr;
2038
2039  unsigned int
2040    i,
2041    imageColumns,
2042    imageRows,
2043    kernelWidth;
2044
2045  clEnv = NULL;
2046  filteredImage = NULL;
2047  filteredImage_view = NULL;
2048  kernel = NULL;
2049  context = NULL;
2050  imageBuffer = NULL;
2051  filteredImageBuffer = NULL;
2052  tempImageBuffer = NULL;
2053  imageKernelBuffer = NULL;
2054  blurRowKernel = NULL;
2055  unsharpMaskBlurColumnKernel = NULL;
2056  queue = NULL;
2057  outputReady = MagickFalse;
2058
2059  clEnv = GetDefaultOpenCLEnv();
2060  context = GetOpenCLContext(clEnv);
2061  queue = AcquireOpenCLCommandQueue(clEnv);
2062
2063  /* Create and initialize OpenCL buffers. */
2064  {
2065    image_view=AcquireVirtualCacheView(image,exception);
2066    inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
2067    if (inputPixels == (const void *) NULL)
2068    {
2069      (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
2070      goto cleanup;
2071    }
2072
2073    /* If the host pointer is aligned to the size of CLPixelPacket,
2074     then use the host buffer directly from the GPU; otherwise,
2075     create a buffer on the GPU and copy the data over */
2076    if (ALIGNED(inputPixels,CLPixelPacket))
2077    {
2078      mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
2079    }
2080    else
2081    {
2082      mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
2083    }
2084    /* create a CL buffer from image pixel buffer */
2085    length = image->columns * image->rows;
2086    imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
2087    if (clStatus != CL_SUCCESS)
2088    {
2089      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
2090      goto cleanup;
2091    }
2092  }
2093
2094  /* create output */
2095  {
2096    filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
2097    assert(filteredImage != NULL);
2098    if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
2099    {
2100      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
2101      goto cleanup;
2102    }
2103    filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
2104    filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
2105    if (filteredPixels == (void *) NULL)
2106    {
2107      (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
2108      goto cleanup;
2109    }
2110
2111    if (ALIGNED(filteredPixels,CLPixelPacket))
2112    {
2113      mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
2114      hostPtr = filteredPixels;
2115    }
2116    else
2117    {
2118      mem_flags = CL_MEM_WRITE_ONLY;
2119      hostPtr = NULL;
2120    }
2121
2122    /* create a CL buffer from image pixel buffer */
2123    length = image->columns * image->rows;
2124    filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
2125    if (clStatus != CL_SUCCESS)
2126    {
2127      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
2128      goto cleanup;
2129    }
2130  }
2131
2132  /* create the blur kernel */
2133  {
2134    (void) FormatLocaleString(geometry,MaxTextExtent,"blur:%.20gx%.20g;blur:%.20gx%.20g+90",radius,sigma,radius,sigma);
2135    kernel=AcquireKernelInfo(geometry,exception);
2136    if (kernel == (KernelInfo *) NULL)
2137    {
2138      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireKernelInfo failed.",".");
2139      goto cleanup;
2140    }
2141
2142    imageKernelBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_ONLY, kernel->width * sizeof(float), NULL, &clStatus);
2143    if (clStatus != CL_SUCCESS)
2144    {
2145      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
2146      goto cleanup;
2147    }
2148
2149
2150    kernelBufferPtr = (float*)clEnv->library->clEnqueueMapBuffer(queue, imageKernelBuffer, CL_TRUE, CL_MAP_WRITE, 0, kernel->width * sizeof(float), 0, NULL, NULL, &clStatus);
2151    if (clStatus != CL_SUCCESS)
2152    {
2153      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueMapBuffer failed.",".");
2154      goto cleanup;
2155    }
2156    for (i = 0; i < kernel->width; i++)
2157    {
2158      kernelBufferPtr[i] = (float) kernel->values[i];
2159    }
2160    clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, imageKernelBuffer, kernelBufferPtr, 0, NULL, NULL);
2161    if (clStatus != CL_SUCCESS)
2162    {
2163      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
2164      goto cleanup;
2165    }
2166  }
2167
2168  {
2169    /* create temp buffer */
2170    {
2171      length = image->columns * image->rows;
2172      tempImageBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_WRITE, length * 4 * sizeof(float), NULL, &clStatus);
2173      if (clStatus != CL_SUCCESS)
2174      {
2175        (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
2176        goto cleanup;
2177      }
2178    }
2179
2180    /* get the opencl kernel */
2181    {
2182      blurRowKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "BlurRow");
2183      if (blurRowKernel == NULL)
2184      {
2185        (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
2186        goto cleanup;
2187      };
2188
2189      unsharpMaskBlurColumnKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "UnsharpMaskBlurColumn");
2190      if (unsharpMaskBlurColumnKernel == NULL)
2191      {
2192        (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
2193        goto cleanup;
2194      };
2195    }
2196
2197    {
2198      chunkSize = 256;
2199
2200      imageColumns = (unsigned int) image->columns;
2201      imageRows = (unsigned int) image->rows;
2202
2203      kernelWidth = (unsigned int) kernel->width;
2204
2205      /* set the kernel arguments */
2206      i = 0;
2207      clStatus=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
2208      clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&tempImageBuffer);
2209      clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(ChannelType),&channel);
2210      clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&imageKernelBuffer);
2211      clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&kernelWidth);
2212      clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&imageColumns);
2213      clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&imageRows);
2214      clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(CLPixelPacket)*(chunkSize+kernel->width),(void *)NULL);
2215      if (clStatus != CL_SUCCESS)
2216      {
2217        (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
2218        goto cleanup;
2219      }
2220    }
2221
2222    /* launch the kernel */
2223    {
2224      size_t gsize[2];
2225      size_t wsize[2];
2226
2227      gsize[0] = chunkSize*((image->columns+chunkSize-1)/chunkSize);
2228      gsize[1] = image->rows;
2229      wsize[0] = chunkSize;
2230      wsize[1] = 1;
2231
2232      clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, blurRowKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
2233      if (clStatus != CL_SUCCESS)
2234      {
2235        (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
2236        goto cleanup;
2237      }
2238      clEnv->library->clFlush(queue);
2239    }
2240
2241
2242    {
2243      chunkSize = 256;
2244      imageColumns = (unsigned int) image->columns;
2245      imageRows = (unsigned int) image->rows;
2246      kernelWidth = (unsigned int) kernel->width;
2247      fGain = (float) gain;
2248      fThreshold = (float) threshold;
2249
2250      i = 0;
2251      clStatus=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
2252      clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(cl_mem),(void *)&tempImageBuffer);
2253      clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(cl_mem),(void *)&filteredImageBuffer);
2254      clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(unsigned int),(void *)&imageColumns);
2255      clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(unsigned int),(void *)&imageRows);
2256      clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++, (chunkSize+kernelWidth-1)*sizeof(cl_float4),NULL);
2257      clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++, kernelWidth*sizeof(float),NULL);
2258      clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(ChannelType),&channel);
2259      clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(cl_mem),(void *)&imageKernelBuffer);
2260      clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(unsigned int),(void *)&kernelWidth);
2261      clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(float),(void *)&fGain);
2262      clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(float),(void *)&fThreshold);
2263
2264      if (clStatus != CL_SUCCESS)
2265      {
2266        (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
2267        goto cleanup;
2268      }
2269    }
2270
2271    /* launch the kernel */
2272    {
2273      size_t gsize[2];
2274      size_t wsize[2];
2275
2276      gsize[0] = image->columns;
2277      gsize[1] = chunkSize*((image->rows+chunkSize-1)/chunkSize);
2278      wsize[0] = 1;
2279      wsize[1] = chunkSize;
2280
2281      clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, unsharpMaskBlurColumnKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
2282      if (clStatus != CL_SUCCESS)
2283      {
2284        (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
2285        goto cleanup;
2286      }
2287      clEnv->library->clFlush(queue);
2288    }
2289
2290  }
2291
2292  /* get result */
2293  if (ALIGNED(filteredPixels,CLPixelPacket))
2294  {
2295    length = image->columns * image->rows;
2296    clEnv->library->clEnqueueMapBuffer(queue, filteredImageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
2297  }
2298  else
2299  {
2300    length = image->columns * image->rows;
2301    clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
2302  }
2303  if (clStatus != CL_SUCCESS)
2304  {
2305    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
2306    goto cleanup;
2307  }
2308
2309  outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
2310
2311cleanup:
2312  OpenCLLogException(__FUNCTION__,__LINE__,exception);
2313
2314  image_view=DestroyCacheView(image_view);
2315  if (filteredImage_view != NULL)
2316    filteredImage_view=DestroyCacheView(filteredImage_view);
2317
2318  if (kernel != NULL)			      kernel=DestroyKernelInfo(kernel);
2319  if (imageBuffer!=NULL)		      clEnv->library->clReleaseMemObject(imageBuffer);
2320  if (filteredImageBuffer!=NULL)              clEnv->library->clReleaseMemObject(filteredImageBuffer);
2321  if (tempImageBuffer!=NULL)                  clEnv->library->clReleaseMemObject(tempImageBuffer);
2322  if (imageKernelBuffer!=NULL)                clEnv->library->clReleaseMemObject(imageKernelBuffer);
2323  if (blurRowKernel!=NULL)                    RelinquishOpenCLKernel(clEnv, blurRowKernel);
2324  if (unsharpMaskBlurColumnKernel!=NULL)      RelinquishOpenCLKernel(clEnv, unsharpMaskBlurColumnKernel);
2325  if (queue != NULL)                          RelinquishOpenCLCommandQueue(clEnv, queue);
2326  if (outputReady == MagickFalse)
2327  {
2328    if (filteredImage != NULL)
2329    {
2330      DestroyImage(filteredImage);
2331      filteredImage = NULL;
2332    }
2333  }
2334  return(filteredImage);
2335}
2336
2337static Image *ComputeUnsharpMaskImageSection(const Image *image,
2338  const ChannelType channel,const double radius,const double sigma,
2339  const double gain,const double threshold,ExceptionInfo *exception)
2340{
2341  CacheView
2342    *filteredImage_view,
2343    *image_view;
2344
2345  char
2346    geometry[MaxTextExtent];
2347
2348  cl_command_queue
2349    queue;
2350
2351  cl_context
2352    context;
2353
2354  cl_int
2355    clStatus;
2356
2357  cl_kernel
2358    blurRowKernel,
2359    unsharpMaskBlurColumnKernel;
2360
2361  cl_mem
2362    filteredImageBuffer,
2363    imageBuffer,
2364    imageKernelBuffer,
2365    tempImageBuffer;
2366
2367  cl_mem_flags
2368    mem_flags;
2369
2370  const void
2371    *inputPixels;
2372
2373  float
2374    fGain,
2375    fThreshold,
2376    *kernelBufferPtr;
2377
2378  Image
2379    *filteredImage;
2380
2381  int
2382    chunkSize;
2383
2384  KernelInfo
2385    *kernel;
2386
2387  MagickBooleanType
2388    outputReady;
2389
2390  MagickCLEnv
2391    clEnv;
2392
2393  MagickSizeType
2394    length;
2395
2396  void
2397    *filteredPixels,
2398    *hostPtr;
2399
2400  unsigned int
2401    i,
2402    imageColumns,
2403    imageRows,
2404    kernelWidth;
2405
2406  clEnv = NULL;
2407  filteredImage = NULL;
2408  filteredImage_view = NULL;
2409  kernel = NULL;
2410  context = NULL;
2411  imageBuffer = NULL;
2412  filteredImageBuffer = NULL;
2413  tempImageBuffer = NULL;
2414  imageKernelBuffer = NULL;
2415  blurRowKernel = NULL;
2416  unsharpMaskBlurColumnKernel = NULL;
2417  queue = NULL;
2418  outputReady = MagickFalse;
2419
2420  clEnv = GetDefaultOpenCLEnv();
2421  context = GetOpenCLContext(clEnv);
2422  queue = AcquireOpenCLCommandQueue(clEnv);
2423
2424  /* Create and initialize OpenCL buffers. */
2425  {
2426    image_view=AcquireVirtualCacheView(image,exception);
2427    inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
2428    if (inputPixels == (const void *) NULL)
2429    {
2430      (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
2431      goto cleanup;
2432    }
2433
2434    /* If the host pointer is aligned to the size of CLPixelPacket,
2435     then use the host buffer directly from the GPU; otherwise,
2436     create a buffer on the GPU and copy the data over */
2437    if (ALIGNED(inputPixels,CLPixelPacket))
2438    {
2439      mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
2440    }
2441    else
2442    {
2443      mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
2444    }
2445    /* create a CL buffer from image pixel buffer */
2446    length = image->columns * image->rows;
2447    imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
2448    if (clStatus != CL_SUCCESS)
2449    {
2450      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
2451      goto cleanup;
2452    }
2453  }
2454
2455  /* create output */
2456  {
2457    filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
2458    assert(filteredImage != NULL);
2459    if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
2460    {
2461      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
2462      goto cleanup;
2463    }
2464    filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
2465    filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
2466    if (filteredPixels == (void *) NULL)
2467    {
2468      (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
2469      goto cleanup;
2470    }
2471
2472    if (ALIGNED(filteredPixels,CLPixelPacket))
2473    {
2474      mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
2475      hostPtr = filteredPixels;
2476    }
2477    else
2478    {
2479      mem_flags = CL_MEM_WRITE_ONLY;
2480      hostPtr = NULL;
2481    }
2482
2483    /* create a CL buffer from image pixel buffer */
2484    length = image->columns * image->rows;
2485    filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
2486    if (clStatus != CL_SUCCESS)
2487    {
2488      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
2489      goto cleanup;
2490    }
2491  }
2492
2493  /* create the blur kernel */
2494  {
2495    (void) FormatLocaleString(geometry,MaxTextExtent,"blur:%.20gx%.20g;blur:%.20gx%.20g+90",radius,sigma,radius,sigma);
2496    kernel=AcquireKernelInfo(geometry,exception);
2497    if (kernel == (KernelInfo *) NULL)
2498    {
2499      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireKernelInfo failed.",".");
2500      goto cleanup;
2501    }
2502
2503    imageKernelBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_ONLY, kernel->width * sizeof(float), NULL, &clStatus);
2504    if (clStatus != CL_SUCCESS)
2505    {
2506      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
2507      goto cleanup;
2508    }
2509
2510
2511    kernelBufferPtr = (float*)clEnv->library->clEnqueueMapBuffer(queue, imageKernelBuffer, CL_TRUE, CL_MAP_WRITE, 0, kernel->width * sizeof(float), 0, NULL, NULL, &clStatus);
2512    if (clStatus != CL_SUCCESS)
2513    {
2514      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueMapBuffer failed.",".");
2515      goto cleanup;
2516    }
2517    for (i = 0; i < kernel->width; i++)
2518    {
2519      kernelBufferPtr[i] = (float) kernel->values[i];
2520    }
2521    clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, imageKernelBuffer, kernelBufferPtr, 0, NULL, NULL);
2522    if (clStatus != CL_SUCCESS)
2523    {
2524      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
2525      goto cleanup;
2526    }
2527  }
2528
2529  {
2530    unsigned int offsetRows;
2531    unsigned int sec;
2532
2533    /* create temp buffer */
2534    {
2535      length = image->columns * (image->rows / 2 + 1 + (kernel->width-1) / 2);
2536      tempImageBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_WRITE, length * 4 * sizeof(float), NULL, &clStatus);
2537      if (clStatus != CL_SUCCESS)
2538      {
2539        (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
2540        goto cleanup;
2541      }
2542    }
2543
2544    /* get the opencl kernel */
2545    {
2546      blurRowKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "BlurRowSection");
2547      if (blurRowKernel == NULL)
2548      {
2549        (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
2550        goto cleanup;
2551      };
2552
2553      unsharpMaskBlurColumnKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "UnsharpMaskBlurColumnSection");
2554      if (unsharpMaskBlurColumnKernel == NULL)
2555      {
2556        (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
2557        goto cleanup;
2558      };
2559    }
2560
2561    for (sec = 0; sec < 2; sec++)
2562    {
2563      {
2564        chunkSize = 256;
2565
2566        imageColumns = (unsigned int) image->columns;
2567        if (sec == 0)
2568          imageRows = (unsigned int) (image->rows / 2 + (kernel->width-1) / 2);
2569        else
2570          imageRows = (unsigned int) ((image->rows - image->rows / 2) + (kernel->width-1) / 2);
2571
2572        offsetRows = (unsigned int) (sec * image->rows / 2);
2573
2574        kernelWidth = (unsigned int) kernel->width;
2575
2576        /* set the kernel arguments */
2577        i = 0;
2578        clStatus=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
2579        clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&tempImageBuffer);
2580        clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(ChannelType),&channel);
2581        clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&imageKernelBuffer);
2582        clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&kernelWidth);
2583        clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&imageColumns);
2584        clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&imageRows);
2585        clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(CLPixelPacket)*(chunkSize+kernel->width),(void *)NULL);
2586        clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&offsetRows);
2587        clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&sec);
2588        if (clStatus != CL_SUCCESS)
2589        {
2590          (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
2591          goto cleanup;
2592        }
2593      }
2594      /* launch the kernel */
2595      {
2596        size_t gsize[2];
2597        size_t wsize[2];
2598
2599        gsize[0] = chunkSize*((imageColumns+chunkSize-1)/chunkSize);
2600        gsize[1] = imageRows;
2601        wsize[0] = chunkSize;
2602        wsize[1] = 1;
2603
2604        clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, blurRowKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
2605        if (clStatus != CL_SUCCESS)
2606        {
2607          (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
2608          goto cleanup;
2609        }
2610        clEnv->library->clFlush(queue);
2611      }
2612
2613
2614      {
2615        chunkSize = 256;
2616
2617        imageColumns = (unsigned int) image->columns;
2618        if (sec == 0)
2619          imageRows = (unsigned int) (image->rows / 2);
2620        else
2621          imageRows = (unsigned int) (image->rows - image->rows / 2);
2622
2623        offsetRows = (unsigned int) (sec * image->rows / 2);
2624
2625        kernelWidth = (unsigned int) kernel->width;
2626
2627        fGain = (float) gain;
2628        fThreshold = (float) threshold;
2629
2630        i = 0;
2631        clStatus=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
2632        clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(cl_mem),(void *)&tempImageBuffer);
2633        clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(cl_mem),(void *)&filteredImageBuffer);
2634        clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(unsigned int),(void *)&imageColumns);
2635        clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(unsigned int),(void *)&imageRows);
2636        clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++, (chunkSize+kernelWidth-1)*sizeof(cl_float4),NULL);
2637        clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++, kernelWidth*sizeof(float),NULL);
2638        clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(ChannelType),&channel);
2639        clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(cl_mem),(void *)&imageKernelBuffer);
2640        clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(unsigned int),(void *)&kernelWidth);
2641        clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(float),(void *)&fGain);
2642        clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(float),(void *)&fThreshold);
2643        clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(unsigned int),(void *)&offsetRows);
2644        clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(unsigned int),(void *)&sec);
2645
2646        if (clStatus != CL_SUCCESS)
2647        {
2648          (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
2649          goto cleanup;
2650        }
2651      }
2652
2653      /* launch the kernel */
2654      {
2655        size_t gsize[2];
2656        size_t wsize[2];
2657
2658        gsize[0] = imageColumns;
2659        gsize[1] = chunkSize*((imageRows+chunkSize-1)/chunkSize);
2660        wsize[0] = 1;
2661        wsize[1] = chunkSize;
2662
2663        clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, unsharpMaskBlurColumnKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
2664        if (clStatus != CL_SUCCESS)
2665        {
2666          (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
2667          goto cleanup;
2668        }
2669        clEnv->library->clFlush(queue);
2670      }
2671    }
2672  }
2673
2674  /* get result */
2675  if (ALIGNED(filteredPixels,CLPixelPacket))
2676  {
2677    length = image->columns * image->rows;
2678    clEnv->library->clEnqueueMapBuffer(queue, filteredImageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
2679  }
2680  else
2681  {
2682    length = image->columns * image->rows;
2683    clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
2684  }
2685  if (clStatus != CL_SUCCESS)
2686  {
2687    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
2688    goto cleanup;
2689  }
2690
2691  outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
2692
2693cleanup:
2694  OpenCLLogException(__FUNCTION__,__LINE__,exception);
2695
2696  image_view=DestroyCacheView(image_view);
2697  if (filteredImage_view != NULL)
2698    filteredImage_view=DestroyCacheView(filteredImage_view);
2699
2700  if (kernel != NULL)			      kernel=DestroyKernelInfo(kernel);
2701  if (imageBuffer!=NULL)		      clEnv->library->clReleaseMemObject(imageBuffer);
2702  if (filteredImageBuffer!=NULL)              clEnv->library->clReleaseMemObject(filteredImageBuffer);
2703  if (tempImageBuffer!=NULL)                  clEnv->library->clReleaseMemObject(tempImageBuffer);
2704  if (imageKernelBuffer!=NULL)                clEnv->library->clReleaseMemObject(imageKernelBuffer);
2705  if (blurRowKernel!=NULL)                    RelinquishOpenCLKernel(clEnv, blurRowKernel);
2706  if (unsharpMaskBlurColumnKernel!=NULL)      RelinquishOpenCLKernel(clEnv, unsharpMaskBlurColumnKernel);
2707  if (queue != NULL)                          RelinquishOpenCLCommandQueue(clEnv, queue);
2708  if (outputReady == MagickFalse)
2709  {
2710    if (filteredImage != NULL)
2711    {
2712      DestroyImage(filteredImage);
2713      filteredImage = NULL;
2714    }
2715  }
2716  return filteredImage;
2717}
2718
2719static Image *ComputeUnsharpMaskImageSingle(const Image *image,
2720  const ChannelType channel,const double radius,const double sigma,
2721  const double gain,const double threshold,int blurOnly, ExceptionInfo *exception)
2722{
2723  CacheView
2724    *filteredImage_view,
2725    *image_view;
2726
2727  char
2728    geometry[MaxTextExtent];
2729
2730  cl_command_queue
2731    queue;
2732
2733  cl_context
2734    context;
2735
2736  cl_int
2737    justBlur,
2738    clStatus;
2739
2740  cl_kernel
2741    unsharpMaskKernel;
2742
2743  cl_mem
2744    filteredImageBuffer,
2745    imageBuffer,
2746    imageKernelBuffer;
2747
2748  cl_mem_flags
2749    mem_flags;
2750
2751  const void
2752    *inputPixels;
2753
2754  float
2755    fGain,
2756    fThreshold,
2757    *kernelBufferPtr;
2758
2759  Image
2760    *filteredImage;
2761
2762  KernelInfo
2763    *kernel;
2764
2765  MagickBooleanType
2766    outputReady;
2767
2768  MagickCLEnv
2769    clEnv;
2770
2771  MagickSizeType
2772    length;
2773
2774  void
2775    *filteredPixels,
2776    *hostPtr;
2777
2778  unsigned int
2779    i,
2780    imageColumns,
2781    imageRows,
2782    kernelWidth;
2783
2784  clEnv = NULL;
2785  filteredImage = NULL;
2786  filteredImage_view = NULL;
2787  kernel = NULL;
2788  context = NULL;
2789  imageBuffer = NULL;
2790  filteredImageBuffer = NULL;
2791  imageKernelBuffer = NULL;
2792  unsharpMaskKernel = NULL;
2793  queue = NULL;
2794  outputReady = MagickFalse;
2795
2796  clEnv = GetDefaultOpenCLEnv();
2797  context = GetOpenCLContext(clEnv);
2798  queue = AcquireOpenCLCommandQueue(clEnv);
2799
2800  /* Create and initialize OpenCL buffers. */
2801  {
2802    image_view=AcquireVirtualCacheView(image,exception);
2803    inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
2804    if (inputPixels == (const void *) NULL)
2805    {
2806      (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
2807      goto cleanup;
2808    }
2809
2810    /* If the host pointer is aligned to the size of CLPixelPacket,
2811     then use the host buffer directly from the GPU; otherwise,
2812     create a buffer on the GPU and copy the data over */
2813    if (ALIGNED(inputPixels,CLPixelPacket))
2814    {
2815      mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
2816    }
2817    else
2818    {
2819      mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
2820    }
2821    /* create a CL buffer from image pixel buffer */
2822    length = image->columns * image->rows;
2823    imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
2824    if (clStatus != CL_SUCCESS)
2825    {
2826      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
2827      goto cleanup;
2828    }
2829  }
2830
2831  /* create output */
2832  {
2833    filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
2834    assert(filteredImage != NULL);
2835    if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
2836    {
2837      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
2838      goto cleanup;
2839    }
2840    filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
2841    filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
2842    if (filteredPixels == (void *) NULL)
2843    {
2844      (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
2845      goto cleanup;
2846    }
2847
2848    if (ALIGNED(filteredPixels,CLPixelPacket))
2849    {
2850      mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
2851      hostPtr = filteredPixels;
2852    }
2853    else
2854    {
2855      mem_flags = CL_MEM_WRITE_ONLY;
2856      hostPtr = NULL;
2857    }
2858
2859    /* create a CL buffer from image pixel buffer */
2860    length = image->columns * image->rows;
2861    filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
2862    if (clStatus != CL_SUCCESS)
2863    {
2864      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
2865      goto cleanup;
2866    }
2867  }
2868
2869  /* create the blur kernel */
2870  {
2871    (void) FormatLocaleString(geometry,MaxTextExtent,"blur:%.20gx%.20g;blur:%.20gx%.20g+90",radius,sigma,radius,sigma);
2872    kernel=AcquireKernelInfo(geometry,exception);
2873    if (kernel == (KernelInfo *) NULL)
2874    {
2875      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireKernelInfo failed.",".");
2876      goto cleanup;
2877    }
2878
2879    imageKernelBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_ONLY, kernel->width * sizeof(float), NULL, &clStatus);
2880    if (clStatus != CL_SUCCESS)
2881    {
2882      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
2883      goto cleanup;
2884    }
2885
2886
2887    kernelBufferPtr = (float*)clEnv->library->clEnqueueMapBuffer(queue, imageKernelBuffer, CL_TRUE, CL_MAP_WRITE, 0, kernel->width * sizeof(float), 0, NULL, NULL, &clStatus);
2888    if (clStatus != CL_SUCCESS)
2889    {
2890      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueMapBuffer failed.",".");
2891      goto cleanup;
2892    }
2893    for (i = 0; i < kernel->width; i++)
2894    {
2895      kernelBufferPtr[i] = (float) kernel->values[i];
2896    }
2897    clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, imageKernelBuffer, kernelBufferPtr, 0, NULL, NULL);
2898    if (clStatus != CL_SUCCESS)
2899    {
2900      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
2901      goto cleanup;
2902    }
2903  }
2904
2905  {
2906    /* get the opencl kernel */
2907    {
2908      unsharpMaskKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "UnsharpMask");
2909      if (unsharpMaskKernel == NULL)
2910      {
2911        (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
2912        goto cleanup;
2913      };
2914    }
2915
2916    {
2917      imageColumns = (unsigned int) image->columns;
2918      imageRows = (unsigned int) image->rows;
2919      kernelWidth = (unsigned int) kernel->width;
2920      fGain = (float) gain;
2921      fThreshold = (float) threshold;
2922      justBlur = blurOnly;
2923
2924      /* set the kernel arguments */
2925      i = 0;
2926      clStatus=clEnv->library->clSetKernelArg(unsharpMaskKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
2927      clStatus|=clEnv->library->clSetKernelArg(unsharpMaskKernel,i++,sizeof(cl_mem),(void *)&filteredImageBuffer);
2928      clStatus|=clEnv->library->clSetKernelArg(unsharpMaskKernel,i++,sizeof(cl_mem),(void *)&imageKernelBuffer);
2929      clStatus|=clEnv->library->clSetKernelArg(unsharpMaskKernel,i++,sizeof(unsigned int),(void *)&kernelWidth);
2930      clStatus|=clEnv->library->clSetKernelArg(unsharpMaskKernel,i++,sizeof(unsigned int),(void *)&imageColumns);
2931      clStatus|=clEnv->library->clSetKernelArg(unsharpMaskKernel,i++,sizeof(unsigned int),(void *)&imageRows);
2932      clStatus|=clEnv->library->clSetKernelArg(unsharpMaskKernel,i++,sizeof(cl_float4)*(8 * (32 + kernel->width)),(void *)NULL);
2933      clStatus|=clEnv->library->clSetKernelArg(unsharpMaskKernel,i++,sizeof(float),(void *)&fGain);
2934      clStatus|=clEnv->library->clSetKernelArg(unsharpMaskKernel,i++,sizeof(float),(void *)&fThreshold);
2935      clStatus|=clEnv->library->clSetKernelArg(unsharpMaskKernel,i++,sizeof(cl_uint),(void *)&justBlur);
2936      if (clStatus != CL_SUCCESS)
2937      {
2938        (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
2939        goto cleanup;
2940      }
2941    }
2942
2943    /* launch the kernel */
2944    {
2945      size_t gsize[2];
2946      size_t wsize[2];
2947
2948      gsize[0] = ((image->columns + 7) / 8) * 8;
2949      gsize[1] = ((image->rows + 31) / 32) * 32;
2950      wsize[0] = 8;
2951      wsize[1] = 32;
2952
2953      clStatus = clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, unsharpMaskKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
2954      if (clStatus != CL_SUCCESS)
2955      {
2956        (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
2957        goto cleanup;
2958      }
2959      clEnv->library->clFlush(queue);
2960    }
2961  }
2962
2963  /* get result */
2964  if (ALIGNED(filteredPixels,CLPixelPacket))
2965  {
2966    length = image->columns * image->rows;
2967    clEnv->library->clEnqueueMapBuffer(queue, filteredImageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
2968  }
2969  else
2970  {
2971    length = image->columns * image->rows;
2972    clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
2973  }
2974  if (clStatus != CL_SUCCESS)
2975  {
2976    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
2977    goto cleanup;
2978  }
2979
2980  outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
2981
2982cleanup:
2983  OpenCLLogException(__FUNCTION__,__LINE__,exception);
2984
2985  image_view=DestroyCacheView(image_view);
2986  if (filteredImage_view != NULL)
2987    filteredImage_view=DestroyCacheView(filteredImage_view);
2988
2989  if (kernel != NULL)			      kernel=DestroyKernelInfo(kernel);
2990  if (imageBuffer!=NULL)		      clEnv->library->clReleaseMemObject(imageBuffer);
2991  if (filteredImageBuffer!=NULL)              clEnv->library->clReleaseMemObject(filteredImageBuffer);
2992  if (imageKernelBuffer!=NULL)                clEnv->library->clReleaseMemObject(imageKernelBuffer);
2993  if (unsharpMaskKernel!=NULL)                RelinquishOpenCLKernel(clEnv, unsharpMaskKernel);
2994  if (queue != NULL)                          RelinquishOpenCLCommandQueue(clEnv, queue);
2995  if (outputReady == MagickFalse)
2996  {
2997    if (filteredImage != NULL)
2998    {
2999      DestroyImage(filteredImage);
3000      filteredImage = NULL;
3001    }
3002  }
3003  return(filteredImage);
3004}
3005
3006
3007MagickExport Image *AccelerateUnsharpMaskImage(const Image *image,
3008  const ChannelType channel,const double radius,const double sigma,
3009  const double gain,const double threshold,ExceptionInfo *exception)
3010{
3011  Image
3012    *filteredImage;
3013
3014  assert(image != NULL);
3015  assert(exception != (ExceptionInfo *) NULL);
3016
3017  if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
3018      (checkAccelerateCondition(image, channel) == MagickFalse))
3019    return NULL;
3020
3021  if (radius < 12.1)
3022    filteredImage = ComputeUnsharpMaskImageSingle(image,channel,radius,sigma,gain,threshold, 0, exception);
3023  else if (splitImage(image) && (image->rows / 2 > radius))
3024    filteredImage = ComputeUnsharpMaskImageSection(image,channel,radius,sigma,gain,threshold,exception);
3025  else
3026    filteredImage = ComputeUnsharpMaskImage(image,channel,radius,sigma,gain,threshold,exception);
3027  return(filteredImage);
3028}
3029
3030/*
3031%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3032%                                                                             %
3033%                                                                             %
3034%                                                                             %
3035%   A c c e l e r a t e R e s i z e I m a g e                                 %
3036%                                                                             %
3037%                                                                             %
3038%                                                                             %
3039%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3040%
3041%  AccelerateResizeImage() is an OpenCL implementation of ResizeImage()
3042%
3043%  AccelerateResizeImage() scales an image to the desired dimensions, using the given
3044%  filter (see AcquireFilterInfo()).
3045%
3046%  If an undefined filter is given the filter defaults to Mitchell for a
3047%  colormapped image, a image with a matte channel, or if the image is
3048%  enlarged.  Otherwise the filter defaults to a Lanczos.
3049%
3050%  AccelerateResizeImage() was inspired by Paul Heckbert's "zoom" program.
3051%
3052%  The format of the AccelerateResizeImage method is:
3053%
3054%      Image *ResizeImage(Image *image,const size_t columns,
3055%        const size_t rows, const ResizeFilter* filter,
3056%        ExceptionInfo *exception)
3057%
3058%  A description of each parameter follows:
3059%
3060%    o image: the image.
3061%
3062%    o columns: the number of columns in the scaled image.
3063%
3064%    o rows: the number of rows in the scaled image.
3065%
3066%    o filter: Image filter to use.
3067%
3068%    o exception: return any errors or warnings in this structure.
3069%
3070*/
3071
3072static MagickBooleanType resizeHorizontalFilter(cl_mem image,
3073  const unsigned int imageColumns,const unsigned int imageRows,
3074  const unsigned int matte,cl_mem resizedImage,
3075  const unsigned int resizedColumns,const unsigned int resizedRows,
3076  const ResizeFilter *resizeFilter,cl_mem resizeFilterCubicCoefficients,
3077  const float xFactor,MagickCLEnv clEnv,cl_command_queue queue,
3078  ExceptionInfo *exception)
3079{
3080  cl_kernel
3081    horizontalKernel;
3082
3083  cl_int clStatus;
3084
3085  const unsigned int
3086    workgroupSize = 256;
3087
3088  float
3089    resizeFilterScale,
3090    resizeFilterSupport,
3091    resizeFilterWindowSupport,
3092    resizeFilterBlur,
3093    scale,
3094    support;
3095
3096  int
3097    cacheRangeStart,
3098    cacheRangeEnd,
3099    numCachedPixels,
3100    resizeFilterType,
3101    resizeWindowType;
3102
3103  MagickBooleanType
3104    status = MagickFalse;
3105
3106  size_t
3107    deviceLocalMemorySize,
3108    gammaAccumulatorLocalMemorySize,
3109    global_work_size[2],
3110    imageCacheLocalMemorySize,
3111    pixelAccumulatorLocalMemorySize,
3112    local_work_size[2],
3113    totalLocalMemorySize,
3114    weightAccumulatorLocalMemorySize;
3115
3116  unsigned int
3117    chunkSize,
3118    i,
3119    pixelPerWorkgroup;
3120
3121  horizontalKernel = NULL;
3122  status = MagickFalse;
3123
3124  /*
3125  Apply filter to resize vertically from image to resize image.
3126  */
3127  scale=MAGICK_MAX(1.0/xFactor+MagickEpsilon,1.0);
3128  support=scale*GetResizeFilterSupport(resizeFilter);
3129  if (support < 0.5)
3130  {
3131    /*
3132    Support too small even for nearest neighbour: Reduce to point
3133    sampling.
3134    */
3135    support=(MagickRealType) 0.5;
3136    scale=1.0;
3137  }
3138  scale=PerceptibleReciprocal(scale);
3139
3140  if (resizedColumns < workgroupSize)
3141  {
3142    chunkSize = 32;
3143    pixelPerWorkgroup = 32;
3144  }
3145  else
3146  {
3147    chunkSize = workgroupSize;
3148    pixelPerWorkgroup = workgroupSize;
3149  }
3150
3151  /* get the local memory size supported by the device */
3152  deviceLocalMemorySize = GetOpenCLDeviceLocalMemorySize(clEnv);
3153
3154DisableMSCWarning(4127)
3155  while(1)
3156RestoreMSCWarning
3157  {
3158    /* calculate the local memory size needed per workgroup */
3159    cacheRangeStart = (int) (((0 + 0.5)/xFactor+MagickEpsilon)-support+0.5);
3160    cacheRangeEnd = (int) ((((pixelPerWorkgroup-1) + 0.5)/xFactor+MagickEpsilon)+support+0.5);
3161    numCachedPixels = cacheRangeEnd - cacheRangeStart + 1;
3162    imageCacheLocalMemorySize = numCachedPixels * sizeof(CLPixelPacket);
3163    totalLocalMemorySize = imageCacheLocalMemorySize;
3164
3165    /* local size for the pixel accumulator */
3166    pixelAccumulatorLocalMemorySize = chunkSize * sizeof(cl_float4);
3167    totalLocalMemorySize+=pixelAccumulatorLocalMemorySize;
3168
3169    /* local memory size for the weight accumulator */
3170    weightAccumulatorLocalMemorySize = chunkSize * sizeof(float);
3171    totalLocalMemorySize+=weightAccumulatorLocalMemorySize;
3172
3173    /* local memory size for the gamma accumulator */
3174    if (matte == 0)
3175      gammaAccumulatorLocalMemorySize = sizeof(float);
3176    else
3177      gammaAccumulatorLocalMemorySize = chunkSize * sizeof(float);
3178    totalLocalMemorySize+=gammaAccumulatorLocalMemorySize;
3179
3180    if (totalLocalMemorySize <= deviceLocalMemorySize)
3181      break;
3182    else
3183    {
3184      pixelPerWorkgroup = pixelPerWorkgroup/2;
3185      chunkSize = chunkSize/2;
3186      if (pixelPerWorkgroup == 0
3187          || chunkSize == 0)
3188      {
3189        /* quit, fallback to CPU */
3190        goto cleanup;
3191      }
3192    }
3193  }
3194
3195  resizeFilterType = (int)GetResizeFilterWeightingType(resizeFilter);
3196  resizeWindowType = (int)GetResizeFilterWindowWeightingType(resizeFilter);
3197
3198
3199  if (resizeFilterType == SincFastWeightingFunction
3200    && resizeWindowType == SincFastWeightingFunction)
3201  {
3202    horizontalKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "ResizeHorizontalFilterSinc");
3203  }
3204  else
3205  {
3206    horizontalKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "ResizeHorizontalFilter");
3207  }
3208  if (horizontalKernel == NULL)
3209  {
3210    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
3211    goto cleanup;
3212  }
3213
3214  i = 0;
3215  clStatus = clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(cl_mem), (void*)&image);
3216  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&imageColumns);
3217  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&imageRows);
3218  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&matte);
3219  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&xFactor);
3220  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(cl_mem), (void*)&resizedImage);
3221
3222  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&resizedColumns);
3223  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&resizedRows);
3224
3225  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(int), (void*)&resizeFilterType);
3226  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(int), (void*)&resizeWindowType);
3227  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(cl_mem), (void*)&resizeFilterCubicCoefficients);
3228
3229  resizeFilterScale = (float) GetResizeFilterScale(resizeFilter);
3230  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterScale);
3231
3232  resizeFilterSupport = (float) GetResizeFilterSupport(resizeFilter);
3233  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterSupport);
3234
3235  resizeFilterWindowSupport = (float) GetResizeFilterWindowSupport(resizeFilter);
3236  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterWindowSupport);
3237
3238  resizeFilterBlur = (float) GetResizeFilterBlur(resizeFilter);
3239  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterBlur);
3240
3241
3242  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, imageCacheLocalMemorySize, NULL);
3243  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(int), &numCachedPixels);
3244  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), &pixelPerWorkgroup);
3245  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), &chunkSize);
3246
3247
3248  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, pixelAccumulatorLocalMemorySize, NULL);
3249  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, weightAccumulatorLocalMemorySize, NULL);
3250  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, gammaAccumulatorLocalMemorySize, NULL);
3251
3252  if (clStatus != CL_SUCCESS)
3253  {
3254    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
3255    goto cleanup;
3256  }
3257
3258  global_work_size[0] = (resizedColumns+pixelPerWorkgroup-1)/pixelPerWorkgroup*workgroupSize;
3259  global_work_size[1] = resizedRows;
3260
3261  local_work_size[0] = workgroupSize;
3262  local_work_size[1] = 1;
3263  clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, horizontalKernel, 2, NULL, global_work_size, local_work_size, 0, NULL, NULL);
3264  (void) local_work_size;
3265  if (clStatus != CL_SUCCESS)
3266  {
3267    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
3268    goto cleanup;
3269  }
3270  clEnv->library->clFlush(queue);
3271  status = MagickTrue;
3272
3273
3274cleanup:
3275  OpenCLLogException(__FUNCTION__,__LINE__,exception);
3276
3277  if (horizontalKernel != NULL) RelinquishOpenCLKernel(clEnv, horizontalKernel);
3278
3279  return(status);
3280}
3281
3282static MagickBooleanType resizeVerticalFilter(cl_mem image,
3283  const unsigned int imageColumns,const unsigned int imageRows,
3284  const unsigned int matte,cl_mem resizedImage,
3285  const unsigned int resizedColumns,const unsigned int resizedRows,
3286  const ResizeFilter *resizeFilter,cl_mem resizeFilterCubicCoefficients,
3287  const float yFactor,MagickCLEnv clEnv,cl_command_queue queue,
3288  ExceptionInfo *exception)
3289{
3290  cl_kernel
3291    horizontalKernel;
3292
3293  cl_int clStatus;
3294
3295  const unsigned int
3296    workgroupSize = 256;
3297
3298  float
3299    resizeFilterScale,
3300    resizeFilterSupport,
3301    resizeFilterWindowSupport,
3302    resizeFilterBlur,
3303    scale,
3304    support;
3305
3306  int
3307    cacheRangeStart,
3308    cacheRangeEnd,
3309    numCachedPixels,
3310    resizeFilterType,
3311    resizeWindowType;
3312
3313  MagickBooleanType
3314    status = MagickFalse;
3315
3316  size_t
3317    deviceLocalMemorySize,
3318    gammaAccumulatorLocalMemorySize,
3319    global_work_size[2],
3320    imageCacheLocalMemorySize,
3321    pixelAccumulatorLocalMemorySize,
3322    local_work_size[2],
3323    totalLocalMemorySize,
3324    weightAccumulatorLocalMemorySize;
3325
3326  unsigned int
3327    chunkSize,
3328    i,
3329    pixelPerWorkgroup;
3330
3331  horizontalKernel = NULL;
3332  status = MagickFalse;
3333
3334  /*
3335  Apply filter to resize vertically from image to resize image.
3336  */
3337  scale=MAGICK_MAX(1.0/yFactor+MagickEpsilon,1.0);
3338  support=scale*GetResizeFilterSupport(resizeFilter);
3339  if (support < 0.5)
3340  {
3341    /*
3342    Support too small even for nearest neighbour: Reduce to point
3343    sampling.
3344    */
3345    support=(MagickRealType) 0.5;
3346    scale=1.0;
3347  }
3348  scale=PerceptibleReciprocal(scale);
3349
3350  if (resizedRows < workgroupSize)
3351  {
3352    chunkSize = 32;
3353    pixelPerWorkgroup = 32;
3354  }
3355  else
3356  {
3357    chunkSize = workgroupSize;
3358    pixelPerWorkgroup = workgroupSize;
3359  }
3360
3361  /* get the local memory size supported by the device */
3362  deviceLocalMemorySize = GetOpenCLDeviceLocalMemorySize(clEnv);
3363
3364DisableMSCWarning(4127)
3365  while(1)
3366RestoreMSCWarning
3367  {
3368    /* calculate the local memory size needed per workgroup */
3369    cacheRangeStart = (int) (((0 + 0.5)/yFactor+MagickEpsilon)-support+0.5);
3370    cacheRangeEnd = (int) ((((pixelPerWorkgroup-1) + 0.5)/yFactor+MagickEpsilon)+support+0.5);
3371    numCachedPixels = cacheRangeEnd - cacheRangeStart + 1;
3372    imageCacheLocalMemorySize = numCachedPixels * sizeof(CLPixelPacket);
3373    totalLocalMemorySize = imageCacheLocalMemorySize;
3374
3375    /* local size for the pixel accumulator */
3376    pixelAccumulatorLocalMemorySize = chunkSize * sizeof(cl_float4);
3377    totalLocalMemorySize+=pixelAccumulatorLocalMemorySize;
3378
3379    /* local memory size for the weight accumulator */
3380    weightAccumulatorLocalMemorySize = chunkSize * sizeof(float);
3381    totalLocalMemorySize+=weightAccumulatorLocalMemorySize;
3382
3383    /* local memory size for the gamma accumulator */
3384    if (matte == 0)
3385      gammaAccumulatorLocalMemorySize = sizeof(float);
3386    else
3387      gammaAccumulatorLocalMemorySize = chunkSize * sizeof(float);
3388    totalLocalMemorySize+=gammaAccumulatorLocalMemorySize;
3389
3390    if (totalLocalMemorySize <= deviceLocalMemorySize)
3391      break;
3392    else
3393    {
3394      pixelPerWorkgroup = pixelPerWorkgroup/2;
3395      chunkSize = chunkSize/2;
3396      if (pixelPerWorkgroup == 0
3397          || chunkSize == 0)
3398      {
3399        /* quit, fallback to CPU */
3400        goto cleanup;
3401      }
3402    }
3403  }
3404
3405  resizeFilterType = (int)GetResizeFilterWeightingType(resizeFilter);
3406  resizeWindowType = (int)GetResizeFilterWindowWeightingType(resizeFilter);
3407
3408  if (resizeFilterType == SincFastWeightingFunction
3409    && resizeWindowType == SincFastWeightingFunction)
3410    horizontalKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "ResizeVerticalFilterSinc");
3411  else
3412    horizontalKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "ResizeVerticalFilter");
3413
3414  if (horizontalKernel == NULL)
3415  {
3416    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
3417    goto cleanup;
3418  }
3419
3420  i = 0;
3421  clStatus = clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(cl_mem), (void*)&image);
3422  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&imageColumns);
3423  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&imageRows);
3424  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&matte);
3425  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&yFactor);
3426  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(cl_mem), (void*)&resizedImage);
3427
3428  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&resizedColumns);
3429  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&resizedRows);
3430
3431  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(int), (void*)&resizeFilterType);
3432  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(int), (void*)&resizeWindowType);
3433  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(cl_mem), (void*)&resizeFilterCubicCoefficients);
3434
3435  resizeFilterScale = (float) GetResizeFilterScale(resizeFilter);
3436  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterScale);
3437
3438  resizeFilterSupport = (float) GetResizeFilterSupport(resizeFilter);
3439  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterSupport);
3440
3441  resizeFilterWindowSupport = (float) GetResizeFilterWindowSupport(resizeFilter);
3442  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterWindowSupport);
3443
3444  resizeFilterBlur = (float) GetResizeFilterBlur(resizeFilter);
3445  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterBlur);
3446
3447
3448  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, imageCacheLocalMemorySize, NULL);
3449  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(int), &numCachedPixels);
3450  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), &pixelPerWorkgroup);
3451  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), &chunkSize);
3452
3453
3454  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, pixelAccumulatorLocalMemorySize, NULL);
3455  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, weightAccumulatorLocalMemorySize, NULL);
3456  clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, gammaAccumulatorLocalMemorySize, NULL);
3457
3458  if (clStatus != CL_SUCCESS)
3459  {
3460    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
3461    goto cleanup;
3462  }
3463
3464  global_work_size[0] = resizedColumns;
3465  global_work_size[1] = (resizedRows+pixelPerWorkgroup-1)/pixelPerWorkgroup*workgroupSize;
3466
3467  local_work_size[0] = 1;
3468  local_work_size[1] = workgroupSize;
3469  clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, horizontalKernel, 2, NULL, global_work_size, local_work_size, 0, NULL, NULL);
3470  if (clStatus != CL_SUCCESS)
3471  {
3472    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
3473    goto cleanup;
3474  }
3475  clEnv->library->clFlush(queue);
3476  status = MagickTrue;
3477
3478
3479cleanup:
3480  OpenCLLogException(__FUNCTION__,__LINE__,exception);
3481
3482  if (horizontalKernel != NULL) RelinquishOpenCLKernel(clEnv, horizontalKernel);
3483
3484  return(status);
3485}
3486
3487static Image *ComputeResizeImage(const Image* image,
3488  const size_t resizedColumns,const size_t resizedRows,
3489  const ResizeFilter *resizeFilter,ExceptionInfo *exception)
3490{
3491  CacheView
3492    *filteredImage_view,
3493    *image_view;
3494
3495  cl_command_queue
3496    queue;
3497
3498  cl_int
3499    clStatus;
3500
3501  cl_context
3502    context;
3503
3504  cl_mem
3505    cubicCoefficientsBuffer,
3506    filteredImageBuffer,
3507    imageBuffer,
3508    tempImageBuffer;
3509
3510  cl_mem_flags
3511    mem_flags;
3512
3513  const double
3514    *resizeFilterCoefficient;
3515
3516  const void
3517    *inputPixels;
3518
3519  float
3520    *mappedCoefficientBuffer,
3521    xFactor,
3522    yFactor;
3523
3524  MagickBooleanType
3525    outputReady,
3526    status;
3527
3528  MagickCLEnv
3529    clEnv;
3530
3531  MagickSizeType
3532    length;
3533
3534  Image
3535    *filteredImage;
3536
3537  unsigned int
3538    i;
3539
3540  void
3541    *filteredPixels,
3542    *hostPtr;
3543
3544  outputReady = MagickFalse;
3545  filteredImage = NULL;
3546  filteredImage_view = NULL;
3547  clEnv = NULL;
3548  context = NULL;
3549  imageBuffer = NULL;
3550  tempImageBuffer = NULL;
3551  filteredImageBuffer = NULL;
3552  cubicCoefficientsBuffer = NULL;
3553  queue = NULL;
3554
3555  clEnv = GetDefaultOpenCLEnv();
3556  context = GetOpenCLContext(clEnv);
3557
3558  /* Create and initialize OpenCL buffers. */
3559  image_view=AcquireVirtualCacheView(image,exception);
3560  inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
3561  if (inputPixels == (const void *) NULL)
3562  {
3563    (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
3564    goto cleanup;
3565  }
3566
3567  /* If the host pointer is aligned to the size of CLPixelPacket,
3568     then use the host buffer directly from the GPU; otherwise,
3569     create a buffer on the GPU and copy the data over */
3570  if (ALIGNED(inputPixels,CLPixelPacket))
3571  {
3572    mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
3573  }
3574  else
3575  {
3576    mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
3577  }
3578  /* create a CL buffer from image pixel buffer */
3579  length = image->columns * image->rows;
3580  imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
3581  if (clStatus != CL_SUCCESS)
3582  {
3583    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
3584    goto cleanup;
3585  }
3586
3587  cubicCoefficientsBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_ONLY, 7 * sizeof(float), NULL, &clStatus);
3588  if (clStatus != CL_SUCCESS)
3589  {
3590    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
3591    goto cleanup;
3592  }
3593  queue = AcquireOpenCLCommandQueue(clEnv);
3594  mappedCoefficientBuffer = (float*)clEnv->library->clEnqueueMapBuffer(queue, cubicCoefficientsBuffer, CL_TRUE, CL_MAP_WRITE, 0, 7 * sizeof(float)
3595          , 0, NULL, NULL, &clStatus);
3596  if (clStatus != CL_SUCCESS)
3597  {
3598    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueMapBuffer failed.",".");
3599    goto cleanup;
3600  }
3601  resizeFilterCoefficient = GetResizeFilterCoefficient(resizeFilter);
3602  for (i = 0; i < 7; i++)
3603  {
3604    mappedCoefficientBuffer[i] = (float) resizeFilterCoefficient[i];
3605  }
3606  clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, cubicCoefficientsBuffer, mappedCoefficientBuffer, 0, NULL, NULL);
3607  if (clStatus != CL_SUCCESS)
3608  {
3609    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
3610    goto cleanup;
3611  }
3612
3613  filteredImage = CloneImage(image,resizedColumns,resizedRows,MagickTrue,exception);
3614  if (filteredImage == NULL)
3615    goto cleanup;
3616
3617  if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
3618  {
3619    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
3620    goto cleanup;
3621  }
3622  filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
3623  filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
3624  if (filteredPixels == (void *) NULL)
3625  {
3626    (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
3627    goto cleanup;
3628  }
3629
3630  if (ALIGNED(filteredPixels,CLPixelPacket))
3631  {
3632    mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
3633    hostPtr = filteredPixels;
3634  }
3635  else
3636  {
3637    mem_flags = CL_MEM_WRITE_ONLY;
3638    hostPtr = NULL;
3639  }
3640
3641  /* create a CL buffer from image pixel buffer */
3642  length = filteredImage->columns * filteredImage->rows;
3643  filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
3644  if (clStatus != CL_SUCCESS)
3645  {
3646    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
3647    goto cleanup;
3648  }
3649
3650  xFactor=(float) resizedColumns/(float) image->columns;
3651  yFactor=(float) resizedRows/(float) image->rows;
3652  if (xFactor > yFactor)
3653  {
3654
3655    length = resizedColumns*image->rows;
3656    tempImageBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_WRITE, length*sizeof(CLPixelPacket), NULL, &clStatus);
3657    if (clStatus != CL_SUCCESS)
3658    {
3659      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
3660      goto cleanup;
3661    }
3662
3663    status = resizeHorizontalFilter(imageBuffer, (unsigned int) image->columns, (unsigned int) image->rows, (image->alpha_trait != BlendPixelTrait)?1:0
3664          , tempImageBuffer, (unsigned int) resizedColumns, (unsigned int) image->rows
3665          , resizeFilter, cubicCoefficientsBuffer
3666          , xFactor, clEnv, queue, exception);
3667    if (status != MagickTrue)
3668      goto cleanup;
3669
3670    status = resizeVerticalFilter(tempImageBuffer, (unsigned int) resizedColumns, (unsigned int) image->rows, (image->alpha_trait != BlendPixelTrait)?1:0
3671       , filteredImageBuffer, (unsigned int) resizedColumns, (unsigned int) resizedRows
3672       , resizeFilter, cubicCoefficientsBuffer
3673       , yFactor, clEnv, queue, exception);
3674    if (status != MagickTrue)
3675      goto cleanup;
3676  }
3677  else
3678  {
3679    length = image->columns*resizedRows;
3680    tempImageBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_WRITE, length*sizeof(CLPixelPacket), NULL, &clStatus);
3681    if (clStatus != CL_SUCCESS)
3682    {
3683      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
3684      goto cleanup;
3685    }
3686
3687    status = resizeVerticalFilter(imageBuffer, (unsigned int) image->columns, (unsigned int) image->rows, (image->alpha_trait != BlendPixelTrait)?1:0
3688       , tempImageBuffer, (unsigned int) image->columns, (unsigned int) resizedRows
3689       , resizeFilter, cubicCoefficientsBuffer
3690       , yFactor, clEnv, queue, exception);
3691    if (status != MagickTrue)
3692      goto cleanup;
3693
3694    status = resizeHorizontalFilter(tempImageBuffer, (unsigned int) image->columns, (unsigned int) resizedRows, (image->alpha_trait != BlendPixelTrait)?1:0
3695       , filteredImageBuffer, (unsigned int) resizedColumns, (unsigned int) resizedRows
3696       , resizeFilter, cubicCoefficientsBuffer
3697       , xFactor, clEnv, queue, exception);
3698    if (status != MagickTrue)
3699      goto cleanup;
3700  }
3701  length = resizedColumns*resizedRows;
3702  if (ALIGNED(filteredPixels,CLPixelPacket))
3703  {
3704    clEnv->library->clEnqueueMapBuffer(queue, filteredImageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
3705  }
3706  else
3707  {
3708    clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
3709  }
3710  if (clStatus != CL_SUCCESS)
3711  {
3712    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
3713    goto cleanup;
3714  }
3715  outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
3716
3717cleanup:
3718  OpenCLLogException(__FUNCTION__,__LINE__,exception);
3719
3720  image_view=DestroyCacheView(image_view);
3721  if (filteredImage_view != NULL)
3722    filteredImage_view=DestroyCacheView(filteredImage_view);
3723
3724  if (imageBuffer!=NULL)		  clEnv->library->clReleaseMemObject(imageBuffer);
3725  if (tempImageBuffer!=NULL)		  clEnv->library->clReleaseMemObject(tempImageBuffer);
3726  if (filteredImageBuffer!=NULL)	  clEnv->library->clReleaseMemObject(filteredImageBuffer);
3727  if (cubicCoefficientsBuffer!=NULL)      clEnv->library->clReleaseMemObject(cubicCoefficientsBuffer);
3728  if (queue != NULL)  	                  RelinquishOpenCLCommandQueue(clEnv, queue);
3729  if (outputReady == MagickFalse && filteredImage != NULL)
3730    filteredImage=DestroyImage(filteredImage);
3731  return(filteredImage);
3732}
3733
3734const ResizeWeightingFunctionType supportedResizeWeighting[] =
3735{
3736  BoxWeightingFunction,
3737  TriangleWeightingFunction,
3738  HanningWeightingFunction,
3739  HammingWeightingFunction,
3740  BlackmanWeightingFunction,
3741  CubicBCWeightingFunction,
3742  SincWeightingFunction,
3743  SincFastWeightingFunction,
3744  LastWeightingFunction
3745};
3746
3747static MagickBooleanType gpuSupportedResizeWeighting(
3748  ResizeWeightingFunctionType f)
3749{
3750  unsigned int
3751    i;
3752
3753  for (i = 0; ;i++)
3754  {
3755    if (supportedResizeWeighting[i] == LastWeightingFunction)
3756      break;
3757    if (supportedResizeWeighting[i] == f)
3758      return(MagickTrue);
3759  }
3760  return(MagickFalse);
3761}
3762
3763MagickExport Image *AccelerateResizeImage(const Image *image,
3764  const size_t resizedColumns,const size_t resizedRows,
3765  const ResizeFilter *resizeFilter,ExceptionInfo *exception)
3766{
3767  Image
3768    *filteredImage;
3769
3770  assert(image != NULL);
3771  assert(exception != (ExceptionInfo *) NULL);
3772
3773  if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
3774      (checkAccelerateCondition(image, AllChannels) == MagickFalse))
3775    return NULL;
3776
3777  if (gpuSupportedResizeWeighting(GetResizeFilterWeightingType(resizeFilter)) == MagickFalse ||
3778      gpuSupportedResizeWeighting(GetResizeFilterWindowWeightingType(resizeFilter)) == MagickFalse)
3779    return NULL;
3780
3781  filteredImage=ComputeResizeImage(image,resizedColumns,resizedRows,resizeFilter,exception);
3782  return(filteredImage);
3783}
3784
3785/*
3786%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3787%                                                                             %
3788%                                                                             %
3789%                                                                             %
3790%     C o n t r a s t I m a g e  w i t h  O p e n C L                         %
3791%                                                                             %
3792%                                                                             %
3793%                                                                             %
3794%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3795%
3796%  ContrastImage() enhances the intensity differences between the lighter and
3797%  darker elements of the image.  Set sharpen to a MagickTrue to increase the
3798%  image contrast otherwise the contrast is reduced.
3799%
3800%  The format of the ContrastImage method is:
3801%
3802%      MagickBooleanType ContrastImage(Image *image,
3803%        const MagickBooleanType sharpen)
3804%
3805%  A description of each parameter follows:
3806%
3807%    o image: the image.
3808%
3809%    o sharpen: Increase or decrease image contrast.
3810%
3811*/
3812
3813static MagickBooleanType ComputeContrastImage(Image *image,
3814  const MagickBooleanType sharpen,ExceptionInfo *exception)
3815{
3816  CacheView
3817    *image_view;
3818
3819  cl_command_queue
3820    queue;
3821
3822  cl_context
3823    context;
3824
3825  cl_int
3826    clStatus;
3827
3828  cl_kernel
3829    filterKernel;
3830
3831  cl_mem
3832    imageBuffer;
3833
3834  cl_mem_flags
3835    mem_flags;
3836
3837  MagickBooleanType
3838    outputReady;
3839
3840  MagickCLEnv
3841    clEnv;
3842
3843  MagickSizeType
3844    length;
3845
3846  size_t
3847    global_work_size[2];
3848
3849  unsigned int
3850    i,
3851    uSharpen;
3852
3853  void
3854    *inputPixels;
3855
3856  outputReady = MagickFalse;
3857  clEnv = NULL;
3858  inputPixels = NULL;
3859  context = NULL;
3860  imageBuffer = NULL;
3861  filterKernel = NULL;
3862  queue = NULL;
3863
3864  clEnv = GetDefaultOpenCLEnv();
3865  context = GetOpenCLContext(clEnv);
3866
3867  /* Create and initialize OpenCL buffers. */
3868  image_view=AcquireAuthenticCacheView(image,exception);
3869  inputPixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
3870  if (inputPixels == (void *) NULL)
3871  {
3872    (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
3873    goto cleanup;
3874  }
3875
3876  /* If the host pointer is aligned to the size of CLPixelPacket,
3877     then use the host buffer directly from the GPU; otherwise,
3878     create a buffer on the GPU and copy the data over */
3879  if (ALIGNED(inputPixels,CLPixelPacket))
3880  {
3881    mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
3882  }
3883  else
3884  {
3885    mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
3886  }
3887  /* create a CL buffer from image pixel buffer */
3888  length = image->columns * image->rows;
3889  imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
3890  if (clStatus != CL_SUCCESS)
3891  {
3892    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
3893    goto cleanup;
3894  }
3895
3896  filterKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "Contrast");
3897  if (filterKernel == NULL)
3898  {
3899    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
3900    goto cleanup;
3901  }
3902
3903  i = 0;
3904  clStatus=clEnv->library->clSetKernelArg(filterKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
3905
3906  uSharpen = (sharpen == MagickFalse)?0:1;
3907  clStatus|=clEnv->library->clSetKernelArg(filterKernel,i++,sizeof(cl_uint),&uSharpen);
3908  if (clStatus != CL_SUCCESS)
3909  {
3910    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
3911    goto cleanup;
3912  }
3913
3914  global_work_size[0] = image->columns;
3915  global_work_size[1] = image->rows;
3916  /* launch the kernel */
3917  queue = AcquireOpenCLCommandQueue(clEnv);
3918  clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, filterKernel, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
3919  if (clStatus != CL_SUCCESS)
3920  {
3921    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
3922    goto cleanup;
3923  }
3924  clEnv->library->clFlush(queue);
3925
3926  if (ALIGNED(inputPixels,CLPixelPacket))
3927  {
3928    length = image->columns * image->rows;
3929    clEnv->library->clEnqueueMapBuffer(queue, imageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
3930  }
3931  else
3932  {
3933    length = image->columns * image->rows;
3934    clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), inputPixels, 0, NULL, NULL);
3935  }
3936  if (clStatus != CL_SUCCESS)
3937  {
3938    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
3939    goto cleanup;
3940  }
3941  outputReady=SyncCacheViewAuthenticPixels(image_view,exception);
3942
3943cleanup:
3944  OpenCLLogException(__FUNCTION__,__LINE__,exception);
3945
3946  image_view=DestroyCacheView(image_view);
3947
3948  if (imageBuffer!=NULL)		      clEnv->library->clReleaseMemObject(imageBuffer);
3949  if (filterKernel!=NULL)                     RelinquishOpenCLKernel(clEnv, filterKernel);
3950  if (queue != NULL)                          RelinquishOpenCLCommandQueue(clEnv, queue);
3951  return(outputReady);
3952}
3953
3954MagickExport MagickBooleanType AccelerateContrastImage(Image *image,
3955  const MagickBooleanType sharpen,ExceptionInfo *exception)
3956{
3957  MagickBooleanType
3958    status;
3959
3960  assert(image != NULL);
3961  assert(exception != (ExceptionInfo *) NULL);
3962
3963  if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
3964      (checkAccelerateCondition(image, AllChannels) == MagickFalse))
3965    return(MagickFalse);
3966
3967  status = ComputeContrastImage(image,sharpen,exception);
3968  return(status);
3969}
3970
3971/*
3972%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3973%                                                                             %
3974%                                                                             %
3975%                                                                             %
3976%     M o d u l a t e I m a g e  w i t h  O p e n C L                         %
3977%                                                                             %
3978%                                                                             %
3979%                                                                             %
3980%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3981%
3982%  ModulateImage() lets you control the brightness, saturation, and hue
3983%  of an image.  Modulate represents the brightness, saturation, and hue
3984%  as one parameter (e.g. 90,150,100).  If the image colorspace is HSL, the
3985%  modulation is lightness, saturation, and hue.  For HWB, use blackness,
3986%  whiteness, and hue. And for HCL, use chrome, luma, and hue.
3987%
3988%  The format of the ModulateImage method is:
3989%
3990%      MagickBooleanType ModulateImage(Image *image,const char *modulate)
3991%
3992%  A description of each parameter follows:
3993%
3994%    o image: the image.
3995%
3996%    o percent_*: Define the percent change in brightness, saturation, and
3997%      hue.
3998%
3999*/
4000
4001MagickBooleanType ComputeModulateImage(Image *image,
4002  double percent_brightness,double percent_hue,double percent_saturation,
4003  ColorspaceType colorspace,ExceptionInfo *exception)
4004{
4005  CacheView
4006    *image_view;
4007
4008  cl_float
4009    bright,
4010    hue,
4011    saturation;
4012
4013  cl_context
4014    context;
4015
4016  cl_command_queue
4017    queue;
4018
4019  cl_int
4020    color,
4021    clStatus;
4022
4023  cl_kernel
4024    modulateKernel;
4025
4026  cl_mem
4027    imageBuffer;
4028
4029  cl_mem_flags
4030    mem_flags;
4031
4032  MagickBooleanType
4033    outputReady;
4034
4035  MagickCLEnv
4036    clEnv;
4037
4038  MagickSizeType
4039    length;
4040
4041  register ssize_t
4042    i;
4043
4044  void
4045    *inputPixels;
4046
4047  inputPixels = NULL;
4048  imageBuffer = NULL;
4049  modulateKernel = NULL;
4050
4051  assert(image != (Image *) NULL);
4052  assert(image->signature == MagickSignature);
4053  if (image->debug != MagickFalse)
4054    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4055
4056  /*
4057   * initialize opencl env
4058   */
4059  clEnv = GetDefaultOpenCLEnv();
4060  context = GetOpenCLContext(clEnv);
4061  queue = AcquireOpenCLCommandQueue(clEnv);
4062
4063  outputReady = MagickFalse;
4064
4065  /* Create and initialize OpenCL buffers.
4066   inputPixels = AcquirePixelCachePixels(image, &length, exception);
4067   assume this  will get a writable image
4068   */
4069  image_view=AcquireAuthenticCacheView(image,exception);
4070  inputPixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
4071  if (inputPixels == (void *) NULL)
4072  {
4073    (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
4074    goto cleanup;
4075  }
4076
4077  /* If the host pointer is aligned to the size of CLPixelPacket,
4078   then use the host buffer directly from the GPU; otherwise,
4079   create a buffer on the GPU and copy the data over
4080   */
4081  if (ALIGNED(inputPixels,CLPixelPacket))
4082  {
4083    mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
4084  }
4085  else
4086  {
4087    mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
4088  }
4089  /* create a CL buffer from image pixel buffer */
4090  length = image->columns * image->rows;
4091  imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
4092  if (clStatus != CL_SUCCESS)
4093  {
4094    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
4095    goto cleanup;
4096  }
4097
4098  modulateKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "Modulate");
4099  if (modulateKernel == NULL)
4100  {
4101    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
4102    goto cleanup;
4103  }
4104
4105  bright=percent_brightness;
4106  hue=percent_hue;
4107  saturation=percent_saturation;
4108  color=colorspace;
4109
4110  i = 0;
4111  clStatus=clEnv->library->clSetKernelArg(modulateKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
4112  clStatus|=clEnv->library->clSetKernelArg(modulateKernel,i++,sizeof(cl_float),&bright);
4113  clStatus|=clEnv->library->clSetKernelArg(modulateKernel,i++,sizeof(cl_float),&hue);
4114  clStatus|=clEnv->library->clSetKernelArg(modulateKernel,i++,sizeof(cl_float),&saturation);
4115  clStatus|=clEnv->library->clSetKernelArg(modulateKernel,i++,sizeof(cl_float),&color);
4116  if (clStatus != CL_SUCCESS)
4117  {
4118    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
4119    printf("no kernel\n");
4120    goto cleanup;
4121  }
4122
4123  {
4124    size_t global_work_size[2];
4125    global_work_size[0] = image->columns;
4126    global_work_size[1] = image->rows;
4127    /* launch the kernel */
4128    clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, modulateKernel, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
4129    if (clStatus != CL_SUCCESS)
4130    {
4131      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
4132      goto cleanup;
4133    }
4134    clEnv->library->clFlush(queue);
4135  }
4136
4137  if (ALIGNED(inputPixels,CLPixelPacket))
4138  {
4139    length = image->columns * image->rows;
4140    clEnv->library->clEnqueueMapBuffer(queue, imageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
4141  }
4142  else
4143  {
4144    length = image->columns * image->rows;
4145    clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), inputPixels, 0, NULL, NULL);
4146  }
4147  if (clStatus != CL_SUCCESS)
4148  {
4149    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
4150    goto cleanup;
4151  }
4152
4153  outputReady=SyncCacheViewAuthenticPixels(image_view,exception);
4154
4155cleanup:
4156  OpenCLLogException(__FUNCTION__,__LINE__,exception);
4157
4158  image_view=DestroyCacheView(image_view);
4159
4160  if (imageBuffer!=NULL)
4161    clEnv->library->clReleaseMemObject(imageBuffer);
4162  if (modulateKernel!=NULL)
4163    RelinquishOpenCLKernel(clEnv, modulateKernel);
4164  if (queue != NULL)
4165    RelinquishOpenCLCommandQueue(clEnv, queue);
4166
4167  return outputReady;
4168
4169}
4170
4171MagickExport MagickBooleanType AccelerateModulateImage(Image *image,
4172  double percent_brightness,double percent_hue,double percent_saturation,
4173  ColorspaceType colorspace,ExceptionInfo *exception)
4174{
4175  MagickBooleanType
4176    status;
4177
4178  assert(image != NULL);
4179  assert(exception != (ExceptionInfo *) NULL);
4180
4181  if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
4182      (checkAccelerateCondition(image, AllChannels) == MagickFalse))
4183    return(MagickFalse);
4184
4185  if ((colorspace != HSLColorspace && colorspace != UndefinedColorspace))
4186    return(MagickFalse);
4187
4188  status = ComputeModulateImage(image,percent_brightness, percent_hue, percent_saturation, colorspace, exception);
4189  return(status);
4190}
4191
4192/*
4193%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4194%                                                                             %
4195%                                                                             %
4196%                                                                             %
4197%     G r a y s c a l e I m a g e  w i t h  O p e n C L                       %
4198%                                                                             %
4199%                                                                             %
4200%                                                                             %
4201%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4202%
4203%  GrayscaleImage() converts the colors in the reference image to gray.
4204%
4205%  The format of the GrayscaleImageChannel method is:
4206%
4207%      MagickBooleanType GrayscaleImage(Image *image,
4208%        const PixelIntensityMethod method)
4209%
4210%  A description of each parameter follows:
4211%
4212%    o image: the image.
4213%
4214%    o channel: the channel.
4215%
4216*/
4217
4218MagickBooleanType ComputeGrayscaleImage(Image *image,
4219  const PixelIntensityMethod method,ExceptionInfo *exception)
4220{
4221  CacheView
4222    *image_view;
4223
4224  cl_command_queue
4225    queue;
4226
4227  cl_context
4228    context;
4229
4230  cl_int
4231    clStatus,
4232    intensityMethod;
4233
4234  cl_int
4235    colorspace;
4236
4237  cl_kernel
4238    grayscaleKernel;
4239
4240  cl_mem
4241    imageBuffer;
4242
4243  cl_mem_flags
4244    mem_flags;
4245
4246  MagickBooleanType
4247    outputReady;
4248
4249  MagickCLEnv
4250    clEnv;
4251
4252  MagickSizeType
4253    length;
4254
4255  register ssize_t
4256    i;
4257
4258  void
4259    *inputPixels;
4260
4261  inputPixels = NULL;
4262  imageBuffer = NULL;
4263  grayscaleKernel = NULL;
4264
4265  assert(image != (Image *) NULL);
4266  assert(image->signature == MagickSignature);
4267  if (image->debug != MagickFalse)
4268    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4269
4270  /*
4271   * initialize opencl env
4272   */
4273  clEnv = GetDefaultOpenCLEnv();
4274  context = GetOpenCLContext(clEnv);
4275  queue = AcquireOpenCLCommandQueue(clEnv);
4276
4277  outputReady = MagickFalse;
4278
4279  /* Create and initialize OpenCL buffers.
4280   inputPixels = AcquirePixelCachePixels(image, &length, exception);
4281   assume this  will get a writable image
4282   */
4283  image_view=AcquireAuthenticCacheView(image,exception);
4284  inputPixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
4285  if (inputPixels == (void *) NULL)
4286  {
4287    (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
4288    goto cleanup;
4289  }
4290
4291  /* If the host pointer is aligned to the size of CLPixelPacket,
4292   then use the host buffer directly from the GPU; otherwise,
4293   create a buffer on the GPU and copy the data over
4294   */
4295  if (ALIGNED(inputPixels,CLPixelPacket))
4296  {
4297    mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
4298  }
4299  else
4300  {
4301    mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
4302  }
4303  /* create a CL buffer from image pixel buffer */
4304  length = image->columns * image->rows;
4305  imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
4306  if (clStatus != CL_SUCCESS)
4307  {
4308    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
4309    goto cleanup;
4310  }
4311
4312  intensityMethod = method;
4313  colorspace = image->colorspace;
4314
4315  grayscaleKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "Grayscale");
4316  if (grayscaleKernel == NULL)
4317  {
4318    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
4319    goto cleanup;
4320  }
4321
4322  i = 0;
4323  clStatus=clEnv->library->clSetKernelArg(grayscaleKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
4324  clStatus|=clEnv->library->clSetKernelArg(grayscaleKernel,i++,sizeof(cl_int),&intensityMethod);
4325  clStatus|=clEnv->library->clSetKernelArg(grayscaleKernel,i++,sizeof(cl_int),&colorspace);
4326  if (clStatus != CL_SUCCESS)
4327  {
4328    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
4329    printf("no kernel\n");
4330    goto cleanup;
4331  }
4332
4333  {
4334    size_t global_work_size[2];
4335    global_work_size[0] = image->columns;
4336    global_work_size[1] = image->rows;
4337    /* launch the kernel */
4338    clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, grayscaleKernel, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
4339    if (clStatus != CL_SUCCESS)
4340    {
4341      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
4342      goto cleanup;
4343    }
4344    clEnv->library->clFlush(queue);
4345  }
4346
4347  if (ALIGNED(inputPixels,CLPixelPacket))
4348  {
4349    length = image->columns * image->rows;
4350    clEnv->library->clEnqueueMapBuffer(queue, imageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
4351  }
4352  else
4353  {
4354    length = image->columns * image->rows;
4355    clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), inputPixels, 0, NULL, NULL);
4356  }
4357  if (clStatus != CL_SUCCESS)
4358  {
4359    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
4360    goto cleanup;
4361  }
4362
4363  outputReady=SyncCacheViewAuthenticPixels(image_view,exception);
4364
4365cleanup:
4366  OpenCLLogException(__FUNCTION__,__LINE__,exception);
4367
4368  image_view=DestroyCacheView(image_view);
4369
4370  if (imageBuffer!=NULL)
4371    clEnv->library->clReleaseMemObject(imageBuffer);
4372  if (grayscaleKernel!=NULL)
4373    RelinquishOpenCLKernel(clEnv, grayscaleKernel);
4374  if (queue != NULL)
4375    RelinquishOpenCLCommandQueue(clEnv, queue);
4376
4377  return( outputReady);
4378}
4379
4380MagickExport MagickBooleanType AccelerateGrayscaleImage(Image* image,
4381  const PixelIntensityMethod method,ExceptionInfo *exception)
4382{
4383  MagickBooleanType
4384    status;
4385
4386  assert(image != NULL);
4387  assert(exception != (ExceptionInfo *) NULL);
4388
4389  if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
4390      (checkAccelerateCondition(image, AllChannels) == MagickFalse))
4391    return(MagickFalse);
4392
4393  if (method == Rec601LuminancePixelIntensityMethod || method == Rec709LuminancePixelIntensityMethod)
4394    return(MagickFalse);
4395
4396  if (image->colorspace != sRGBColorspace)
4397    return(MagickFalse);
4398
4399  status=ComputeGrayscaleImage(image,method,exception);
4400  return(status);
4401}
4402
4403/*
4404%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4405%                                                                             %
4406%                                                                             %
4407%                                                                             %
4408%     E q u a l i z e I m a g e  w i t h  O p e n C L                         %
4409%                                                                             %
4410%                                                                             %
4411%                                                                             %
4412%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4413%
4414%  EqualizeImage() applies a histogram equalization to the image.
4415%
4416%  The format of the EqualizeImage method is:
4417%
4418%      MagickBooleanType EqualizeImage(Image *image)
4419%      MagickBooleanType EqualizeImageChannel(Image *image,
4420%        const ChannelType channel)
4421%
4422%  A description of each parameter follows:
4423%
4424%    o image: the image.
4425%
4426%    o channel: the channel.
4427%
4428*/
4429
4430static MagickBooleanType LaunchHistogramKernel(MagickCLEnv clEnv,
4431  cl_command_queue queue,cl_mem imageBuffer,cl_mem histogramBuffer,
4432  Image *image,const ChannelType channel,ExceptionInfo *exception)
4433{
4434  MagickBooleanType
4435    outputReady;
4436
4437  cl_int
4438    clStatus,
4439    colorspace,
4440    method;
4441
4442  cl_kernel
4443    histogramKernel;
4444
4445  register ssize_t
4446    i;
4447
4448  size_t
4449    global_work_size[2];
4450
4451  histogramKernel = NULL;
4452
4453  outputReady = MagickFalse;
4454  method = image->intensity;
4455  colorspace = image->colorspace;
4456
4457  /* get the OpenCL kernel */
4458  histogramKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "Histogram");
4459  if (histogramKernel == NULL)
4460  {
4461    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
4462    goto cleanup;
4463  }
4464
4465  /* set the kernel arguments */
4466  i = 0;
4467  clStatus=clEnv->library->clSetKernelArg(histogramKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
4468  clStatus|=clEnv->library->clSetKernelArg(histogramKernel,i++,sizeof(ChannelType),&channel);
4469  clStatus|=clEnv->library->clSetKernelArg(histogramKernel,i++,sizeof(cl_int),&method);
4470  clStatus|=clEnv->library->clSetKernelArg(histogramKernel,i++,sizeof(cl_int),&colorspace);
4471  clStatus|=clEnv->library->clSetKernelArg(histogramKernel,i++,sizeof(cl_mem),(void *)&histogramBuffer);
4472  if (clStatus != CL_SUCCESS)
4473  {
4474    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
4475    goto cleanup;
4476  }
4477
4478  /* launch the kernel */
4479  global_work_size[0] = image->columns;
4480  global_work_size[1] = image->rows;
4481
4482  clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, histogramKernel, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
4483
4484  if (clStatus != CL_SUCCESS)
4485  {
4486    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
4487    goto cleanup;
4488  }
4489  clEnv->library->clFlush(queue);
4490
4491  outputReady = MagickTrue;
4492
4493cleanup:
4494  OpenCLLogException(__FUNCTION__,__LINE__,exception);
4495
4496  if (histogramKernel!=NULL)
4497    RelinquishOpenCLKernel(clEnv, histogramKernel);
4498
4499  return(outputReady);
4500}
4501
4502MagickExport MagickBooleanType ComputeEqualizeImage(Image *image,
4503  const ChannelType channel,ExceptionInfo *exception)
4504{
4505#define EqualizeImageTag  "Equalize/Image"
4506
4507  CacheView
4508    *image_view;
4509
4510  cl_command_queue
4511    queue;
4512
4513  cl_context
4514    context;
4515
4516  cl_int
4517    clStatus;
4518
4519  cl_mem_flags
4520    mem_flags;
4521
4522  cl_mem
4523    equalizeMapBuffer,
4524    histogramBuffer,
4525    imageBuffer;
4526
4527  cl_kernel
4528    equalizeKernel,
4529    histogramKernel;
4530
4531  cl_uint4
4532    *histogram;
4533
4534  FloatPixelPacket
4535    white,
4536    black,
4537    intensity,
4538    *map;
4539
4540  MagickBooleanType
4541    outputReady,
4542    status;
4543
4544  MagickCLEnv
4545    clEnv;
4546
4547  MagickSizeType
4548    length;
4549
4550  PixelPacket
4551    *equalize_map;
4552
4553  register ssize_t
4554    i;
4555
4556  size_t
4557    global_work_size[2];
4558
4559  void
4560    *hostPtr,
4561    *inputPixels;
4562
4563  map=NULL;
4564  histogram=NULL;
4565  equalize_map=NULL;
4566  inputPixels = NULL;
4567  imageBuffer = NULL;
4568  histogramBuffer = NULL;
4569  equalizeMapBuffer = NULL;
4570  histogramKernel = NULL;
4571  equalizeKernel = NULL;
4572  context = NULL;
4573  queue = NULL;
4574  outputReady = MagickFalse;
4575
4576  assert(image != (Image *) NULL);
4577  assert(image->signature == MagickSignature);
4578  if (image->debug != MagickFalse)
4579    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4580
4581  /*
4582   * initialize opencl env
4583   */
4584  clEnv = GetDefaultOpenCLEnv();
4585  context = GetOpenCLContext(clEnv);
4586  queue = AcquireOpenCLCommandQueue(clEnv);
4587
4588  /*
4589    Allocate and initialize histogram arrays.
4590  */
4591  histogram=(cl_uint4 *) AcquireQuantumMemory(MaxMap+1UL, sizeof(*histogram));
4592  if (histogram == (cl_uint4 *) NULL)
4593      ThrowBinaryException(ResourceLimitWarning,"MemoryAllocationFailed", image->filename);
4594
4595  /* reset histogram */
4596  (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
4597
4598  /* Create and initialize OpenCL buffers. */
4599  /* inputPixels = AcquirePixelCachePixels(image, &length, exception); */
4600  /* assume this  will get a writable image */
4601  image_view=AcquireAuthenticCacheView(image,exception);
4602  inputPixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
4603
4604  if (inputPixels == (void *) NULL)
4605  {
4606    (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
4607    goto cleanup;
4608  }
4609  /* If the host pointer is aligned to the size of CLPixelPacket,
4610     then use the host buffer directly from the GPU; otherwise,
4611     create a buffer on the GPU and copy the data over */
4612  if (ALIGNED(inputPixels,CLPixelPacket))
4613  {
4614    mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
4615  }
4616  else
4617  {
4618    mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
4619  }
4620  /* create a CL buffer from image pixel buffer */
4621  length = image->columns * image->rows;
4622  imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
4623  if (clStatus != CL_SUCCESS)
4624  {
4625    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
4626    goto cleanup;
4627  }
4628
4629  /* If the host pointer is aligned to the size of cl_uint,
4630     then use the host buffer directly from the GPU; otherwise,
4631     create a buffer on the GPU and copy the data over */
4632  if (ALIGNED(histogram,cl_uint4))
4633  {
4634    mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
4635    hostPtr = histogram;
4636  }
4637  else
4638  {
4639    mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
4640    hostPtr = histogram;
4641  }
4642  /* create a CL buffer for histogram  */
4643  length = (MaxMap+1);
4644  histogramBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(cl_uint4), hostPtr, &clStatus);
4645  if (clStatus != CL_SUCCESS)
4646  {
4647    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
4648    goto cleanup;
4649  }
4650
4651  status = LaunchHistogramKernel(clEnv, queue, imageBuffer, histogramBuffer, image, channel, exception);
4652  if (status == MagickFalse)
4653    goto cleanup;
4654
4655  /* read from the kenel output */
4656  if (ALIGNED(histogram,cl_uint4))
4657  {
4658    length = (MaxMap+1);
4659    clEnv->library->clEnqueueMapBuffer(queue, histogramBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(cl_uint4), 0, NULL, NULL, &clStatus);
4660  }
4661  else
4662  {
4663    length = (MaxMap+1);
4664    clStatus = clEnv->library->clEnqueueReadBuffer(queue, histogramBuffer, CL_TRUE, 0, length * sizeof(cl_uint4), histogram, 0, NULL, NULL);
4665  }
4666  if (clStatus != CL_SUCCESS)
4667  {
4668    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
4669    goto cleanup;
4670  }
4671
4672  /* unmap, don't block gpu to use this buffer again.  */
4673  if (ALIGNED(histogram,cl_uint4))
4674  {
4675    clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, histogramBuffer, histogram, 0, NULL, NULL);
4676    if (clStatus != CL_SUCCESS)
4677    {
4678      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
4679      goto cleanup;
4680    }
4681  }
4682
4683  /* recreate input buffer later, in case image updated */
4684#ifdef RECREATEBUFFER
4685  if (imageBuffer!=NULL)
4686    clEnv->library->clReleaseMemObject(imageBuffer);
4687#endif
4688
4689  /* CPU stuff */
4690  equalize_map=(PixelPacket *) AcquireQuantumMemory(MaxMap+1UL, sizeof(*equalize_map));
4691  if (equalize_map == (PixelPacket *) NULL)
4692    ThrowBinaryException(ResourceLimitWarning,"MemoryAllocationFailed", image->filename);
4693
4694  map=(FloatPixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*map));
4695  if (map == (FloatPixelPacket *) NULL)
4696    ThrowBinaryException(ResourceLimitWarning,"MemoryAllocationFailed", image->filename);
4697
4698  /*
4699    Integrate the histogram to get the equalization map.
4700  */
4701  (void) ResetMagickMemory(&intensity,0,sizeof(intensity));
4702  for (i=0; i <= (ssize_t) MaxMap; i++)
4703  {
4704    if ((channel & SyncChannels) != 0)
4705    {
4706      intensity.red+=histogram[i].s[2];
4707      map[i]=intensity;
4708      continue;
4709    }
4710    if ((channel & RedChannel) != 0)
4711      intensity.red+=histogram[i].s[2];
4712    if ((channel & GreenChannel) != 0)
4713      intensity.green+=histogram[i].s[1];
4714    if ((channel & BlueChannel) != 0)
4715      intensity.blue+=histogram[i].s[0];
4716    if ((channel & OpacityChannel) != 0)
4717      intensity.alpha+=histogram[i].s[3];
4718    /*
4719    if (((channel & IndexChannel) != 0) &&
4720        (image->colorspace == CMYKColorspace))
4721    {
4722      intensity.index+=histogram[i].index;
4723    }
4724    */
4725    map[i]=intensity;
4726  }
4727  black=map[0];
4728  white=map[(int) MaxMap];
4729  (void) ResetMagickMemory(equalize_map,0,(MaxMap+1)*sizeof(*equalize_map));
4730  for (i=0; i <= (ssize_t) MaxMap; i++)
4731  {
4732    if ((channel & SyncChannels) != 0)
4733    {
4734      if (white.red != black.red)
4735        equalize_map[i].red=ScaleMapToQuantum((MagickRealType) ((MaxMap*
4736                (map[i].red-black.red))/(white.red-black.red)));
4737      continue;
4738    }
4739    if (((channel & RedChannel) != 0) && (white.red != black.red))
4740      equalize_map[i].red=ScaleMapToQuantum((MagickRealType) ((MaxMap*
4741              (map[i].red-black.red))/(white.red-black.red)));
4742    if (((channel & GreenChannel) != 0) && (white.green != black.green))
4743      equalize_map[i].green=ScaleMapToQuantum((MagickRealType) ((MaxMap*
4744              (map[i].green-black.green))/(white.green-black.green)));
4745    if (((channel & BlueChannel) != 0) && (white.blue != black.blue))
4746      equalize_map[i].blue=ScaleMapToQuantum((MagickRealType) ((MaxMap*
4747              (map[i].blue-black.blue))/(white.blue-black.blue)));
4748    if (((channel & OpacityChannel) != 0) && (white.alpha != black.alpha))
4749      equalize_map[i].alpha=ScaleMapToQuantum((MagickRealType) ((MaxMap*
4750              (map[i].alpha-black.alpha))/(white.alpha-black.alpha)));
4751    /*
4752    if ((((channel & IndexChannel) != 0) &&
4753          (image->colorspace == CMYKColorspace)) &&
4754        (white.index != black.index))
4755      equalize_map[i].index=ScaleMapToQuantum((MagickRealType) ((MaxMap*
4756              (map[i].index-black.index))/(white.index-black.index)));
4757    */
4758  }
4759
4760  if (image->storage_class == PseudoClass)
4761  {
4762    /*
4763       Equalize colormap.
4764       */
4765    for (i=0; i < (ssize_t) image->colors; i++)
4766    {
4767      if ((channel & SyncChannels) != 0)
4768      {
4769        if (white.red != black.red)
4770        {
4771          image->colormap[i].red=equalize_map[
4772            ScaleQuantumToMap(image->colormap[i].red)].red;
4773          image->colormap[i].green=equalize_map[
4774            ScaleQuantumToMap(image->colormap[i].green)].red;
4775          image->colormap[i].blue=equalize_map[
4776            ScaleQuantumToMap(image->colormap[i].blue)].red;
4777          image->colormap[i].alpha=equalize_map[
4778            ScaleQuantumToMap(image->colormap[i].alpha)].red;
4779        }
4780        continue;
4781      }
4782      if (((channel & RedChannel) != 0) && (white.red != black.red))
4783        image->colormap[i].red=equalize_map[
4784          ScaleQuantumToMap(image->colormap[i].red)].red;
4785      if (((channel & GreenChannel) != 0) && (white.green != black.green))
4786        image->colormap[i].green=equalize_map[
4787          ScaleQuantumToMap(image->colormap[i].green)].green;
4788      if (((channel & BlueChannel) != 0) && (white.blue != black.blue))
4789        image->colormap[i].blue=equalize_map[
4790          ScaleQuantumToMap(image->colormap[i].blue)].blue;
4791      if (((channel & OpacityChannel) != 0) &&
4792          (white.alpha != black.alpha))
4793        image->colormap[i].alpha=equalize_map[
4794          ScaleQuantumToMap(image->colormap[i].alpha)].alpha;
4795    }
4796  }
4797
4798  /*
4799    Equalize image.
4800  */
4801
4802  /* GPU can work on this again, image and equalize map as input
4803    image:        uchar4 (CLPixelPacket)
4804    equalize_map: uchar4 (PixelPacket)
4805    black, white: float4 (FloatPixelPacket) */
4806
4807#ifdef RECREATEBUFFER
4808  /* If the host pointer is aligned to the size of CLPixelPacket,
4809     then use the host buffer directly from the GPU; otherwise,
4810     create a buffer on the GPU and copy the data over */
4811  if (ALIGNED(inputPixels,CLPixelPacket))
4812  {
4813    mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
4814  }
4815  else
4816  {
4817    mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
4818  }
4819  /* create a CL buffer from image pixel buffer */
4820  length = image->columns * image->rows;
4821  imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
4822  if (clStatus != CL_SUCCESS)
4823  {
4824    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
4825    goto cleanup;
4826  }
4827#endif
4828
4829  /* Create and initialize OpenCL buffers. */
4830  if (ALIGNED(equalize_map, PixelPacket))
4831  {
4832    mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
4833    hostPtr = equalize_map;
4834  }
4835  else
4836  {
4837    mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
4838    hostPtr = equalize_map;
4839  }
4840  /* create a CL buffer for eqaulize_map  */
4841  length = (MaxMap+1);
4842  equalizeMapBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(PixelPacket), hostPtr, &clStatus);
4843  if (clStatus != CL_SUCCESS)
4844  {
4845    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
4846    goto cleanup;
4847  }
4848
4849  /* get the OpenCL kernel */
4850  equalizeKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "Equalize");
4851  if (equalizeKernel == NULL)
4852  {
4853    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
4854    goto cleanup;
4855  }
4856
4857  /* set the kernel arguments */
4858  i = 0;
4859  clStatus=clEnv->library->clSetKernelArg(equalizeKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
4860  clStatus|=clEnv->library->clSetKernelArg(equalizeKernel,i++,sizeof(ChannelType),&channel);
4861  clStatus|=clEnv->library->clSetKernelArg(equalizeKernel,i++,sizeof(cl_mem),(void *)&equalizeMapBuffer);
4862  clStatus|=clEnv->library->clSetKernelArg(equalizeKernel,i++,sizeof(FloatPixelPacket),&white);
4863  clStatus|=clEnv->library->clSetKernelArg(equalizeKernel,i++,sizeof(FloatPixelPacket),&black);
4864  if (clStatus != CL_SUCCESS)
4865  {
4866    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
4867    goto cleanup;
4868  }
4869
4870  /* launch the kernel */
4871  global_work_size[0] = image->columns;
4872  global_work_size[1] = image->rows;
4873
4874  clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, equalizeKernel, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
4875
4876  if (clStatus != CL_SUCCESS)
4877  {
4878    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
4879    goto cleanup;
4880  }
4881  clEnv->library->clFlush(queue);
4882
4883  /* read the data back */
4884  if (ALIGNED(inputPixels,CLPixelPacket))
4885  {
4886    length = image->columns * image->rows;
4887    clEnv->library->clEnqueueMapBuffer(queue, imageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
4888  }
4889  else
4890  {
4891    length = image->columns * image->rows;
4892    clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), inputPixels, 0, NULL, NULL);
4893  }
4894  if (clStatus != CL_SUCCESS)
4895  {
4896    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
4897    goto cleanup;
4898  }
4899
4900  outputReady=SyncCacheViewAuthenticPixels(image_view,exception);
4901
4902cleanup:
4903  OpenCLLogException(__FUNCTION__,__LINE__,exception);
4904
4905  image_view=DestroyCacheView(image_view);
4906
4907  if (imageBuffer!=NULL)
4908    clEnv->library->clReleaseMemObject(imageBuffer);
4909
4910  if (map!=NULL)
4911    map=(FloatPixelPacket *) RelinquishMagickMemory(map);
4912
4913  if (equalizeMapBuffer!=NULL)
4914    clEnv->library->clReleaseMemObject(equalizeMapBuffer);
4915  if (equalize_map!=NULL)
4916    equalize_map=(PixelPacket *) RelinquishMagickMemory(equalize_map);
4917
4918  if (histogramBuffer!=NULL)
4919    clEnv->library->clReleaseMemObject(histogramBuffer);
4920  if (histogram!=NULL)
4921    histogram=(cl_uint4 *) RelinquishMagickMemory(histogram);
4922
4923  if (histogramKernel!=NULL)
4924    RelinquishOpenCLKernel(clEnv, histogramKernel);
4925  if (equalizeKernel!=NULL)
4926    RelinquishOpenCLKernel(clEnv, equalizeKernel);
4927
4928  if (queue != NULL)
4929    RelinquishOpenCLCommandQueue(clEnv, queue);
4930
4931  return(outputReady);
4932}
4933
4934MagickExport MagickBooleanType AccelerateEqualizeImage(Image *image,
4935  const ChannelType channel,ExceptionInfo *exception)
4936{
4937  MagickBooleanType
4938    status;
4939
4940  assert(image != NULL);
4941  assert(exception != (ExceptionInfo *) NULL);
4942
4943  if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
4944      (checkAccelerateCondition(image, channel) == MagickFalse) ||
4945      (checkHistogramCondition(image, channel) == MagickFalse))
4946    return(MagickFalse);
4947
4948  status=ComputeEqualizeImage(image,channel,exception);
4949  return(status);
4950}
4951
4952/*
4953%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4954%                                                                             %
4955%                                                                             %
4956%                                                                             %
4957%     C o n t r a s t S t r e t c h I m a g e  w i t h  O p e n C L           %
4958%                                                                             %
4959%                                                                             %
4960%                                                                             %
4961%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4962%
4963%  ContrastStretchImage() is a simple image enhancement technique that attempts
4964%  to improve the contrast in an image by `stretching' the range of intensity
4965%  values it contains to span a desired range of values. It differs from the
4966%  more sophisticated histogram equalization in that it can only apply a
4967%  linear scaling function to the image pixel values.  As a result the
4968%  `enhancement' is less harsh.
4969%
4970%  The format of the ContrastStretchImage method is:
4971%
4972%      MagickBooleanType ContrastStretchImage(Image *image,
4973%        const char *levels)
4974%      MagickBooleanType ContrastStretchImageChannel(Image *image,
4975%        const size_t channel,const double black_point,
4976%        const double white_point)
4977%
4978%  A description of each parameter follows:
4979%
4980%    o image: the image.
4981%
4982%    o channel: the channel.
4983%
4984%    o black_point: the black point.
4985%
4986%    o white_point: the white point.
4987%
4988%    o levels: Specify the levels where the black and white points have the
4989%      range of 0 to number-of-pixels (e.g. 1%, 10x90%, etc.).
4990%
4991*/
4992
4993MagickExport MagickBooleanType ComputeContrastStretchImageChannel(Image *image,
4994  const ChannelType channel,const double black_point,const double white_point,
4995  ExceptionInfo *exception)
4996{
4997#define ContrastStretchImageTag  "ContrastStretch/Image"
4998#define MaxRange(color)  ((MagickRealType) ScaleQuantumToMap((Quantum) (color)))
4999
5000  CacheView
5001    *image_view;
5002
5003  cl_command_queue
5004    queue;
5005
5006  cl_context
5007    context;
5008
5009  cl_int
5010    clStatus;
5011
5012  cl_mem_flags
5013    mem_flags;
5014
5015  cl_mem
5016    histogramBuffer,
5017    imageBuffer,
5018    stretchMapBuffer;
5019
5020  cl_kernel
5021    histogramKernel,
5022    stretchKernel;
5023
5024  cl_uint4
5025    *histogram;
5026
5027  double
5028    intensity;
5029
5030  FloatPixelPacket
5031    black,
5032    white;
5033
5034  MagickBooleanType
5035    outputReady,
5036    status;
5037
5038  MagickCLEnv
5039    clEnv;
5040
5041  MagickSizeType
5042    length;
5043
5044  PixelPacket
5045    *stretch_map;
5046
5047  register ssize_t
5048    i;
5049
5050  size_t
5051    global_work_size[2];
5052
5053  void
5054    *hostPtr,
5055    *inputPixels;
5056
5057  histogram=NULL;
5058  stretch_map=NULL;
5059  inputPixels = NULL;
5060  imageBuffer = NULL;
5061  histogramBuffer = NULL;
5062  stretchMapBuffer = NULL;
5063  histogramKernel = NULL;
5064  stretchKernel = NULL;
5065  context = NULL;
5066  queue = NULL;
5067  outputReady = MagickFalse;
5068
5069
5070  assert(image != (Image *) NULL);
5071  assert(image->signature == MagickSignature);
5072  if (image->debug != MagickFalse)
5073    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5074
5075  //exception=(&image->exception);
5076
5077  /*
5078   * initialize opencl env
5079   */
5080  clEnv = GetDefaultOpenCLEnv();
5081  context = GetOpenCLContext(clEnv);
5082  queue = AcquireOpenCLCommandQueue(clEnv);
5083
5084  /*
5085    Allocate and initialize histogram arrays.
5086  */
5087  histogram=(cl_uint4 *) AcquireQuantumMemory(MaxMap+1UL, sizeof(*histogram));
5088
5089  if (histogram == (cl_uint4 *) NULL)
5090    ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", image->filename);
5091
5092  /* reset histogram */
5093  (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
5094
5095  /*
5096  if (IsGrayImage(image,exception) != MagickFalse)
5097    (void) SetImageColorspace(image,GRAYColorspace);
5098  */
5099
5100  status=MagickTrue;
5101
5102
5103  /*
5104    Form histogram.
5105  */
5106  /* Create and initialize OpenCL buffers. */
5107  /* inputPixels = AcquirePixelCachePixels(image, &length, exception); */
5108  /* assume this  will get a writable image */
5109  image_view=AcquireAuthenticCacheView(image,exception);
5110  inputPixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
5111
5112  if (inputPixels == (void *) NULL)
5113  {
5114    (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
5115    goto cleanup;
5116  }
5117  /* If the host pointer is aligned to the size of CLPixelPacket,
5118     then use the host buffer directly from the GPU; otherwise,
5119     create a buffer on the GPU and copy the data over */
5120  if (ALIGNED(inputPixels,CLPixelPacket))
5121  {
5122    mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
5123  }
5124  else
5125  {
5126    mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
5127  }
5128  /* create a CL buffer from image pixel buffer */
5129  length = image->columns * image->rows;
5130  imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
5131  if (clStatus != CL_SUCCESS)
5132  {
5133    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
5134    goto cleanup;
5135  }
5136
5137  /* If the host pointer is aligned to the size of cl_uint,
5138     then use the host buffer directly from the GPU; otherwise,
5139     create a buffer on the GPU and copy the data over */
5140  if (ALIGNED(histogram,cl_uint4))
5141  {
5142    mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
5143    hostPtr = histogram;
5144  }
5145  else
5146  {
5147    mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
5148    hostPtr = histogram;
5149  }
5150  /* create a CL buffer for histogram  */
5151  length = (MaxMap+1);
5152  histogramBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(cl_uint4), hostPtr, &clStatus);
5153  if (clStatus != CL_SUCCESS)
5154  {
5155    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
5156    goto cleanup;
5157  }
5158
5159  status = LaunchHistogramKernel(clEnv, queue, imageBuffer, histogramBuffer, image, channel, exception);
5160  if (status == MagickFalse)
5161    goto cleanup;
5162
5163  /* read from the kenel output */
5164  if (ALIGNED(histogram,cl_uint4))
5165  {
5166    length = (MaxMap+1);
5167    clEnv->library->clEnqueueMapBuffer(queue, histogramBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(cl_uint4), 0, NULL, NULL, &clStatus);
5168  }
5169  else
5170  {
5171    length = (MaxMap+1);
5172    clStatus = clEnv->library->clEnqueueReadBuffer(queue, histogramBuffer, CL_TRUE, 0, length * sizeof(cl_uint4), histogram, 0, NULL, NULL);
5173  }
5174  if (clStatus != CL_SUCCESS)
5175  {
5176    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
5177    goto cleanup;
5178  }
5179
5180  /* unmap, don't block gpu to use this buffer again.  */
5181  if (ALIGNED(histogram,cl_uint4))
5182  {
5183    clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, histogramBuffer, histogram, 0, NULL, NULL);
5184    if (clStatus != CL_SUCCESS)
5185    {
5186      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
5187      goto cleanup;
5188    }
5189  }
5190
5191  /* recreate input buffer later, in case image updated */
5192#ifdef RECREATEBUFFER
5193  if (imageBuffer!=NULL)
5194    clEnv->library->clReleaseMemObject(imageBuffer);
5195#endif
5196
5197  /* CPU stuff */
5198  /*
5199     Find the histogram boundaries by locating the black/white levels.
5200  */
5201  black.red=0.0;
5202  white.red=MaxRange(QuantumRange);
5203  if ((channel & RedChannel) != 0)
5204  {
5205    intensity=0.0;
5206    for (i=0; i <= (ssize_t) MaxMap; i++)
5207    {
5208      intensity+=histogram[i].s[2];
5209      if (intensity > black_point)
5210        break;
5211    }
5212    black.red=(MagickRealType) i;
5213    intensity=0.0;
5214    for (i=(ssize_t) MaxMap; i != 0; i--)
5215    {
5216      intensity+=histogram[i].s[2];
5217      if (intensity > ((double) image->columns*image->rows-white_point))
5218        break;
5219    }
5220    white.red=(MagickRealType) i;
5221  }
5222  black.green=0.0;
5223  white.green=MaxRange(QuantumRange);
5224  if ((channel & GreenChannel) != 0)
5225  {
5226    intensity=0.0;
5227    for (i=0; i <= (ssize_t) MaxMap; i++)
5228    {
5229      intensity+=histogram[i].s[2];
5230      if (intensity > black_point)
5231        break;
5232    }
5233    black.green=(MagickRealType) i;
5234    intensity=0.0;
5235    for (i=(ssize_t) MaxMap; i != 0; i--)
5236    {
5237      intensity+=histogram[i].s[2];
5238      if (intensity > ((double) image->columns*image->rows-white_point))
5239        break;
5240    }
5241    white.green=(MagickRealType) i;
5242  }
5243  black.blue=0.0;
5244  white.blue=MaxRange(QuantumRange);
5245  if ((channel & BlueChannel) != 0)
5246  {
5247    intensity=0.0;
5248    for (i=0; i <= (ssize_t) MaxMap; i++)
5249    {
5250      intensity+=histogram[i].s[2];
5251      if (intensity > black_point)
5252        break;
5253    }
5254    black.blue=(MagickRealType) i;
5255    intensity=0.0;
5256    for (i=(ssize_t) MaxMap; i != 0; i--)
5257    {
5258      intensity+=histogram[i].s[2];
5259      if (intensity > ((double) image->columns*image->rows-white_point))
5260        break;
5261    }
5262    white.blue=(MagickRealType) i;
5263  }
5264  black.alpha=0.0;
5265  white.alpha=MaxRange(QuantumRange);
5266  if ((channel & OpacityChannel) != 0)
5267  {
5268    intensity=0.0;
5269    for (i=0; i <= (ssize_t) MaxMap; i++)
5270    {
5271      intensity+=histogram[i].s[2];
5272      if (intensity > black_point)
5273        break;
5274    }
5275    black.alpha=(MagickRealType) i;
5276    intensity=0.0;
5277    for (i=(ssize_t) MaxMap; i != 0; i--)
5278    {
5279      intensity+=histogram[i].s[2];
5280      if (intensity > ((double) image->columns*image->rows-white_point))
5281        break;
5282    }
5283    white.alpha=(MagickRealType) i;
5284  }
5285  /*
5286  black.index=0.0;
5287  white.index=MaxRange(QuantumRange);
5288  if (((channel & IndexChannel) != 0) && (image->colorspace == CMYKColorspace))
5289  {
5290    intensity=0.0;
5291    for (i=0; i <= (ssize_t) MaxMap; i++)
5292    {
5293      intensity+=histogram[i].index;
5294      if (intensity > black_point)
5295        break;
5296    }
5297    black.index=(MagickRealType) i;
5298    intensity=0.0;
5299    for (i=(ssize_t) MaxMap; i != 0; i--)
5300    {
5301      intensity+=histogram[i].index;
5302      if (intensity > ((double) image->columns*image->rows-white_point))
5303        break;
5304    }
5305    white.index=(MagickRealType) i;
5306  }
5307  */
5308
5309
5310  stretch_map=(PixelPacket *) AcquireQuantumMemory(MaxMap+1UL,
5311    sizeof(*stretch_map));
5312
5313  if (stretch_map == (PixelPacket *) NULL)
5314    ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
5315      image->filename);
5316
5317  /*
5318    Stretch the histogram to create the stretched image mapping.
5319  */
5320  (void) ResetMagickMemory(stretch_map,0,(MaxMap+1)*sizeof(*stretch_map));
5321  for (i=0; i <= (ssize_t) MaxMap; i++)
5322  {
5323    if ((channel & RedChannel) != 0)
5324    {
5325      if (i < (ssize_t) black.red)
5326        stretch_map[i].red=(Quantum) 0;
5327      else
5328        if (i > (ssize_t) white.red)
5329          stretch_map[i].red=QuantumRange;
5330        else
5331          if (black.red != white.red)
5332            stretch_map[i].red=ScaleMapToQuantum((MagickRealType) (MaxMap*
5333                  (i-black.red)/(white.red-black.red)));
5334    }
5335    if ((channel & GreenChannel) != 0)
5336    {
5337      if (i < (ssize_t) black.green)
5338        stretch_map[i].green=0;
5339      else
5340        if (i > (ssize_t) white.green)
5341          stretch_map[i].green=QuantumRange;
5342        else
5343          if (black.green != white.green)
5344            stretch_map[i].green=ScaleMapToQuantum((MagickRealType) (MaxMap*
5345                  (i-black.green)/(white.green-black.green)));
5346    }
5347    if ((channel & BlueChannel) != 0)
5348    {
5349      if (i < (ssize_t) black.blue)
5350        stretch_map[i].blue=0;
5351      else
5352        if (i > (ssize_t) white.blue)
5353          stretch_map[i].blue= QuantumRange;
5354        else
5355          if (black.blue != white.blue)
5356            stretch_map[i].blue=ScaleMapToQuantum((MagickRealType) (MaxMap*
5357                  (i-black.blue)/(white.blue-black.blue)));
5358    }
5359    if ((channel & OpacityChannel) != 0)
5360    {
5361      if (i < (ssize_t) black.alpha)
5362        stretch_map[i].alpha=0;
5363      else
5364        if (i > (ssize_t) white.alpha)
5365          stretch_map[i].alpha=QuantumRange;
5366        else
5367          if (black.alpha != white.alpha)
5368            stretch_map[i].alpha=ScaleMapToQuantum((MagickRealType) (MaxMap*
5369                  (i-black.alpha)/(white.alpha-black.alpha)));
5370    }
5371    /*
5372    if (((channel & IndexChannel) != 0) &&
5373        (image->colorspace == CMYKColorspace))
5374    {
5375      if (i < (ssize_t) black.index)
5376        stretch_map[i].index=0;
5377      else
5378        if (i > (ssize_t) white.index)
5379          stretch_map[i].index=QuantumRange;
5380        else
5381          if (black.index != white.index)
5382            stretch_map[i].index=ScaleMapToQuantum((MagickRealType) (MaxMap*
5383                  (i-black.index)/(white.index-black.index)));
5384    }
5385    */
5386  }
5387
5388  /*
5389    Stretch the image.
5390  */
5391  if (((channel & OpacityChannel) != 0) || (((channel & IndexChannel) != 0) &&
5392      (image->colorspace == CMYKColorspace)))
5393    image->storage_class=DirectClass;
5394  if (image->storage_class == PseudoClass)
5395  {
5396    /*
5397       Stretch colormap.
5398       */
5399    for (i=0; i < (ssize_t) image->colors; i++)
5400    {
5401      if ((channel & RedChannel) != 0)
5402      {
5403        if (black.red != white.red)
5404          image->colormap[i].red=stretch_map[
5405            ScaleQuantumToMap(image->colormap[i].red)].red;
5406      }
5407      if ((channel & GreenChannel) != 0)
5408      {
5409        if (black.green != white.green)
5410          image->colormap[i].green=stretch_map[
5411            ScaleQuantumToMap(image->colormap[i].green)].green;
5412      }
5413      if ((channel & BlueChannel) != 0)
5414      {
5415        if (black.blue != white.blue)
5416          image->colormap[i].blue=stretch_map[
5417            ScaleQuantumToMap(image->colormap[i].blue)].blue;
5418      }
5419      if ((channel & OpacityChannel) != 0)
5420      {
5421        if (black.alpha != white.alpha)
5422          image->colormap[i].alpha=stretch_map[
5423            ScaleQuantumToMap(image->colormap[i].alpha)].alpha;
5424      }
5425    }
5426  }
5427
5428  /*
5429    Stretch image.
5430  */
5431
5432
5433  /* GPU can work on this again, image and equalize map as input
5434    image:        uchar4 (CLPixelPacket)
5435    stretch_map:  uchar4 (PixelPacket)
5436    black, white: float4 (FloatPixelPacket) */
5437
5438#ifdef RECREATEBUFFER
5439  /* If the host pointer is aligned to the size of CLPixelPacket,
5440     then use the host buffer directly from the GPU; otherwise,
5441     create a buffer on the GPU and copy the data over */
5442  if (ALIGNED(inputPixels,CLPixelPacket))
5443  {
5444    mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
5445  }
5446  else
5447  {
5448    mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
5449  }
5450  /* create a CL buffer from image pixel buffer */
5451  length = image->columns * image->rows;
5452  imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
5453  if (clStatus != CL_SUCCESS)
5454  {
5455    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
5456    goto cleanup;
5457  }
5458#endif
5459
5460  /* Create and initialize OpenCL buffers. */
5461  if (ALIGNED(stretch_map, PixelPacket))
5462  {
5463    mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
5464    hostPtr = stretch_map;
5465  }
5466  else
5467  {
5468    mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
5469    hostPtr = stretch_map;
5470  }
5471  /* create a CL buffer for stretch_map  */
5472  length = (MaxMap+1);
5473  stretchMapBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(PixelPacket), hostPtr, &clStatus);
5474  if (clStatus != CL_SUCCESS)
5475  {
5476    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
5477    goto cleanup;
5478  }
5479
5480  /* get the OpenCL kernel */
5481  stretchKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "Stretch");
5482  if (stretchKernel == NULL)
5483  {
5484    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
5485    goto cleanup;
5486  }
5487
5488  /* set the kernel arguments */
5489  i = 0;
5490  clStatus=clEnv->library->clSetKernelArg(stretchKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
5491  clStatus|=clEnv->library->clSetKernelArg(stretchKernel,i++,sizeof(ChannelType),&channel);
5492  clStatus|=clEnv->library->clSetKernelArg(stretchKernel,i++,sizeof(cl_mem),(void *)&stretchMapBuffer);
5493  clStatus|=clEnv->library->clSetKernelArg(stretchKernel,i++,sizeof(FloatPixelPacket),&white);
5494  clStatus|=clEnv->library->clSetKernelArg(stretchKernel,i++,sizeof(FloatPixelPacket),&black);
5495  if (clStatus != CL_SUCCESS)
5496  {
5497    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
5498    goto cleanup;
5499  }
5500
5501  /* launch the kernel */
5502  global_work_size[0] = image->columns;
5503  global_work_size[1] = image->rows;
5504
5505  clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, stretchKernel, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
5506
5507  if (clStatus != CL_SUCCESS)
5508  {
5509    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
5510    goto cleanup;
5511  }
5512  clEnv->library->clFlush(queue);
5513
5514  /* read the data back */
5515  if (ALIGNED(inputPixels,CLPixelPacket))
5516  {
5517    length = image->columns * image->rows;
5518    clEnv->library->clEnqueueMapBuffer(queue, imageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
5519  }
5520  else
5521  {
5522    length = image->columns * image->rows;
5523    clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), inputPixels, 0, NULL, NULL);
5524  }
5525  if (clStatus != CL_SUCCESS)
5526  {
5527    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
5528    goto cleanup;
5529  }
5530
5531  outputReady=SyncCacheViewAuthenticPixels(image_view,exception);
5532
5533cleanup:
5534  OpenCLLogException(__FUNCTION__,__LINE__,exception);
5535
5536  image_view=DestroyCacheView(image_view);
5537
5538  if (imageBuffer!=NULL)
5539    clEnv->library->clReleaseMemObject(imageBuffer);
5540
5541  if (stretchMapBuffer!=NULL)
5542    clEnv->library->clReleaseMemObject(stretchMapBuffer);
5543  if (stretch_map!=NULL)
5544    stretch_map=(PixelPacket *) RelinquishMagickMemory(stretch_map);
5545
5546
5547  if (histogramBuffer!=NULL)
5548    clEnv->library->clReleaseMemObject(histogramBuffer);
5549  if (histogram!=NULL)
5550    histogram=(cl_uint4 *) RelinquishMagickMemory(histogram);
5551
5552
5553  if (histogramKernel!=NULL)
5554    RelinquishOpenCLKernel(clEnv, histogramKernel);
5555  if (stretchKernel!=NULL)
5556    RelinquishOpenCLKernel(clEnv, stretchKernel);
5557
5558  if (queue != NULL)
5559    RelinquishOpenCLCommandQueue(clEnv, queue);
5560
5561  return(outputReady);
5562}
5563
5564MagickExport MagickBooleanType AccelerateContrastStretchImageChannel(
5565  Image *image,const ChannelType channel,const double black_point,
5566  const double white_point,ExceptionInfo *exception)
5567{
5568  MagickBooleanType
5569    status;
5570
5571  assert(image != NULL);
5572  assert(exception != (ExceptionInfo *) NULL);
5573
5574  if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
5575      (checkAccelerateCondition(image, channel) == MagickFalse) ||
5576      (checkHistogramCondition(image, channel) == MagickFalse))
5577    return(MagickFalse);
5578
5579  status=ComputeContrastStretchImageChannel(image,channel, black_point, white_point, exception);
5580  return(status);
5581}
5582
5583/*
5584%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5585%                                                                             %
5586%                                                                             %
5587%                                                                             %
5588%     D e s p e c k l e I m a g e  w i t h  O p e n C L                       %
5589%                                                                             %
5590%                                                                             %
5591%                                                                             %
5592%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5593%
5594%  DespeckleImage() reduces the speckle noise in an image while perserving the
5595%  edges of the original image.  A speckle removing filter uses a complementary
5596%  hulling technique (raising pixels that are darker than their surrounding
5597%  neighbors, then complementarily lowering pixels that are brighter than their
5598%  surrounding neighbors) to reduce the speckle index of that image (reference
5599%  Crimmins speckle removal).
5600%
5601%  The format of the DespeckleImage method is:
5602%
5603%      Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
5604%
5605%  A description of each parameter follows:
5606%
5607%    o image: the image.
5608%
5609%    o exception: return any errors or warnings in this structure.
5610%
5611*/
5612
5613static Image *ComputeDespeckleImage(const Image *image,
5614  ExceptionInfo*exception)
5615{
5616  static const int
5617    X[4] = {0, 1, 1,-1},
5618    Y[4] = {1, 0, 1, 1};
5619
5620  CacheView
5621    *filteredImage_view,
5622    *image_view;
5623
5624  cl_command_queue
5625    queue;
5626
5627  cl_context
5628    context;
5629
5630  cl_int
5631    clStatus;
5632
5633  cl_kernel
5634    hullPass1,
5635    hullPass2;
5636
5637  cl_mem_flags
5638    mem_flags;
5639
5640  cl_mem
5641    filteredImageBuffer,
5642    imageBuffer,
5643    tempImageBuffer[2];
5644
5645  const void
5646    *inputPixels;
5647
5648  Image
5649    *filteredImage;
5650
5651  int
5652    k,
5653    matte;
5654
5655  MagickBooleanType
5656    outputReady;
5657
5658  MagickCLEnv
5659    clEnv;
5660
5661  MagickSizeType
5662    length;
5663
5664  size_t
5665    global_work_size[2];
5666
5667  unsigned int
5668    imageHeight,
5669    imageWidth;
5670
5671  void
5672    *filteredPixels,
5673    *hostPtr;
5674
5675  outputReady = MagickFalse;
5676  clEnv = NULL;
5677  inputPixels = NULL;
5678  filteredImage = NULL;
5679  filteredImage_view = NULL;
5680  filteredPixels = NULL;
5681  context = NULL;
5682  imageBuffer = NULL;
5683  filteredImageBuffer = NULL;
5684  hullPass1 = NULL;
5685  hullPass2 = NULL;
5686  queue = NULL;
5687  tempImageBuffer[0] = tempImageBuffer[1] = NULL;
5688  clEnv = GetDefaultOpenCLEnv();
5689  context = GetOpenCLContext(clEnv);
5690  queue = AcquireOpenCLCommandQueue(clEnv);
5691
5692  image_view=AcquireVirtualCacheView(image,exception);
5693  inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
5694  if (inputPixels == (void *) NULL)
5695  {
5696    (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
5697    goto cleanup;
5698  }
5699
5700  if (ALIGNED(inputPixels,CLPixelPacket))
5701  {
5702    mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
5703  }
5704  else
5705  {
5706    mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
5707  }
5708  /* create a CL buffer from image pixel buffer */
5709  length = image->columns * image->rows;
5710  imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
5711  if (clStatus != CL_SUCCESS)
5712  {
5713    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
5714    goto cleanup;
5715  }
5716
5717  mem_flags = CL_MEM_READ_WRITE;
5718  length = image->columns * image->rows;
5719  for (k = 0; k < 2; k++)
5720  {
5721    tempImageBuffer[k] = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), NULL, &clStatus);
5722    if (clStatus != CL_SUCCESS)
5723    {
5724      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
5725      goto cleanup;
5726    }
5727  }
5728
5729  filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5730  assert(filteredImage != NULL);
5731  if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
5732  {
5733    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
5734    goto cleanup;
5735  }
5736  filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
5737  filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
5738  if (filteredPixels == (void *) NULL)
5739  {
5740    (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
5741    goto cleanup;
5742  }
5743
5744  if (ALIGNED(filteredPixels,CLPixelPacket))
5745  {
5746    mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
5747    hostPtr = filteredPixels;
5748  }
5749  else
5750  {
5751    mem_flags = CL_MEM_WRITE_ONLY;
5752    hostPtr = NULL;
5753  }
5754  /* create a CL buffer from image pixel buffer */
5755  length = image->columns * image->rows;
5756  filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
5757  if (clStatus != CL_SUCCESS)
5758  {
5759    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
5760    goto cleanup;
5761  }
5762
5763  hullPass1 = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "HullPass1");
5764  hullPass2 = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "HullPass2");
5765
5766  clStatus =clEnv->library->clSetKernelArg(hullPass1,0,sizeof(cl_mem),(void *)&imageBuffer);
5767  clStatus |=clEnv->library->clSetKernelArg(hullPass1,1,sizeof(cl_mem),(void *)(tempImageBuffer+1));
5768  imageWidth = (unsigned int) image->columns;
5769  clStatus |=clEnv->library->clSetKernelArg(hullPass1,2,sizeof(unsigned int),(void *)&imageWidth);
5770  imageHeight = (unsigned int) image->rows;
5771  clStatus |=clEnv->library->clSetKernelArg(hullPass1,3,sizeof(unsigned int),(void *)&imageHeight);
5772  matte = (image->alpha_trait != BlendPixelTrait)?0:1;
5773  clStatus |=clEnv->library->clSetKernelArg(hullPass1,6,sizeof(int),(void *)&matte);
5774  if (clStatus != CL_SUCCESS)
5775  {
5776    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
5777    goto cleanup;
5778  }
5779
5780  clStatus = clEnv->library->clSetKernelArg(hullPass2,0,sizeof(cl_mem),(void *)(tempImageBuffer+1));
5781  clStatus |=clEnv->library->clSetKernelArg(hullPass2,1,sizeof(cl_mem),(void *)tempImageBuffer);
5782  imageWidth = (unsigned int) image->columns;
5783  clStatus |=clEnv->library->clSetKernelArg(hullPass2,2,sizeof(unsigned int),(void *)&imageWidth);
5784  imageHeight = (unsigned int) image->rows;
5785  clStatus |=clEnv->library->clSetKernelArg(hullPass2,3,sizeof(unsigned int),(void *)&imageHeight);
5786  matte = (image->alpha_trait != BlendPixelTrait)?0:1;
5787  clStatus |=clEnv->library->clSetKernelArg(hullPass2,6,sizeof(int),(void *)&matte);
5788  if (clStatus != CL_SUCCESS)
5789  {
5790    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
5791    goto cleanup;
5792  }
5793
5794
5795  global_work_size[0] = image->columns;
5796  global_work_size[1] = image->rows;
5797
5798
5799  for (k = 0; k < 4; k++)
5800  {
5801    cl_int2 offset;
5802    int polarity;
5803
5804
5805    offset.s[0] = X[k];
5806    offset.s[1] = Y[k];
5807    polarity = 1;
5808    clStatus = clEnv->library->clSetKernelArg(hullPass1,4,sizeof(cl_int2),(void *)&offset);
5809    clStatus|= clEnv->library->clSetKernelArg(hullPass1,5,sizeof(int),(void *)&polarity);
5810    clStatus|=clEnv->library->clSetKernelArg(hullPass2,4,sizeof(cl_int2),(void *)&offset);
5811    clStatus|=clEnv->library->clSetKernelArg(hullPass2,5,sizeof(int),(void *)&polarity);
5812    if (clStatus != CL_SUCCESS)
5813    {
5814      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
5815      goto cleanup;
5816    }
5817    /* launch the kernel */
5818    clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass1, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
5819    if (clStatus != CL_SUCCESS)
5820    {
5821      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
5822      goto cleanup;
5823    }
5824    /* launch the kernel */
5825    clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass2, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
5826    if (clStatus != CL_SUCCESS)
5827    {
5828      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
5829      goto cleanup;
5830    }
5831
5832
5833    if (k == 0)
5834      clStatus =clEnv->library->clSetKernelArg(hullPass1,0,sizeof(cl_mem),(void *)(tempImageBuffer));
5835    offset.s[0] = -X[k];
5836    offset.s[1] = -Y[k];
5837    polarity = 1;
5838    clStatus = clEnv->library->clSetKernelArg(hullPass1,4,sizeof(cl_int2),(void *)&offset);
5839    clStatus|= clEnv->library->clSetKernelArg(hullPass1,5,sizeof(int),(void *)&polarity);
5840    clStatus|=clEnv->library->clSetKernelArg(hullPass2,4,sizeof(cl_int2),(void *)&offset);
5841    clStatus|=clEnv->library->clSetKernelArg(hullPass2,5,sizeof(int),(void *)&polarity);
5842    if (clStatus != CL_SUCCESS)
5843    {
5844      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
5845      goto cleanup;
5846    }
5847    /* launch the kernel */
5848    clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass1, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
5849    if (clStatus != CL_SUCCESS)
5850    {
5851      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
5852      goto cleanup;
5853    }
5854    /* launch the kernel */
5855    clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass2, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
5856    if (clStatus != CL_SUCCESS)
5857    {
5858      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
5859      goto cleanup;
5860    }
5861
5862    offset.s[0] = -X[k];
5863    offset.s[1] = -Y[k];
5864    polarity = -1;
5865    clStatus = clEnv->library->clSetKernelArg(hullPass1,4,sizeof(cl_int2),(void *)&offset);
5866    clStatus|= clEnv->library->clSetKernelArg(hullPass1,5,sizeof(int),(void *)&polarity);
5867    clStatus|=clEnv->library->clSetKernelArg(hullPass2,4,sizeof(cl_int2),(void *)&offset);
5868    clStatus|=clEnv->library->clSetKernelArg(hullPass2,5,sizeof(int),(void *)&polarity);
5869    if (clStatus != CL_SUCCESS)
5870    {
5871      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
5872      goto cleanup;
5873    }
5874    /* launch the kernel */
5875    clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass1, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
5876    if (clStatus != CL_SUCCESS)
5877    {
5878      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
5879      goto cleanup;
5880    }
5881    /* launch the kernel */
5882    clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass2, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
5883    if (clStatus != CL_SUCCESS)
5884    {
5885      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
5886      goto cleanup;
5887    }
5888
5889    offset.s[0] = X[k];
5890    offset.s[1] = Y[k];
5891    polarity = -1;
5892    clStatus = clEnv->library->clSetKernelArg(hullPass1,4,sizeof(cl_int2),(void *)&offset);
5893    clStatus|= clEnv->library->clSetKernelArg(hullPass1,5,sizeof(int),(void *)&polarity);
5894    clStatus|=clEnv->library->clSetKernelArg(hullPass2,4,sizeof(cl_int2),(void *)&offset);
5895    clStatus|=clEnv->library->clSetKernelArg(hullPass2,5,sizeof(int),(void *)&polarity);
5896
5897    if (k == 3)
5898      clStatus |=clEnv->library->clSetKernelArg(hullPass2,1,sizeof(cl_mem),(void *)&filteredImageBuffer);
5899
5900    if (clStatus != CL_SUCCESS)
5901    {
5902      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
5903      goto cleanup;
5904    }
5905    /* launch the kernel */
5906    clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass1, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
5907    if (clStatus != CL_SUCCESS)
5908    {
5909      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
5910      goto cleanup;
5911    }
5912    /* launch the kernel */
5913    clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass2, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
5914    if (clStatus != CL_SUCCESS)
5915    {
5916      (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
5917      goto cleanup;
5918    }
5919  }
5920
5921  if (ALIGNED(filteredPixels,CLPixelPacket))
5922  {
5923    length = image->columns * image->rows;
5924    clEnv->library->clEnqueueMapBuffer(queue, filteredImageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
5925  }
5926  else
5927  {
5928    length = image->columns * image->rows;
5929    clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
5930  }
5931  if (clStatus != CL_SUCCESS)
5932  {
5933    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
5934    goto cleanup;
5935  }
5936
5937  outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
5938
5939cleanup:
5940  OpenCLLogException(__FUNCTION__,__LINE__,exception);
5941
5942  image_view=DestroyCacheView(image_view);
5943  if (filteredImage_view != NULL)
5944    filteredImage_view=DestroyCacheView(filteredImage_view);
5945
5946  if (queue != NULL)                          RelinquishOpenCLCommandQueue(clEnv, queue);
5947  if (imageBuffer!=NULL)		      clEnv->library->clReleaseMemObject(imageBuffer);
5948  for (k = 0; k < 2; k++)
5949  {
5950    if (tempImageBuffer[k]!=NULL)	      clEnv->library->clReleaseMemObject(tempImageBuffer[k]);
5951  }
5952  if (filteredImageBuffer!=NULL)	      clEnv->library->clReleaseMemObject(filteredImageBuffer);
5953  if (hullPass1!=NULL)			      RelinquishOpenCLKernel(clEnv, hullPass1);
5954  if (hullPass2!=NULL)			      RelinquishOpenCLKernel(clEnv, hullPass2);
5955  if (outputReady == MagickFalse && filteredImage != NULL)
5956    filteredImage=DestroyImage(filteredImage);
5957  return(filteredImage);
5958}
5959
5960MagickExport Image *AccelerateDespeckleImage(const Image* image,
5961  ExceptionInfo* exception)
5962{
5963  Image
5964    *filteredImage;
5965
5966  assert(image != NULL);
5967  assert(exception != (ExceptionInfo *) NULL);
5968
5969  if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
5970      (checkAccelerateCondition(image, AllChannels) == MagickFalse))
5971    return NULL;
5972
5973  filteredImage=ComputeDespeckleImage(image,exception);
5974  return(filteredImage);
5975}
5976
5977static Image *ComputeAddNoiseImage(const Image *image,
5978  const ChannelType channel,const NoiseType noise_type,
5979  ExceptionInfo *exception)
5980{
5981  CacheView
5982    *filteredImage_view,
5983    *image_view;
5984
5985  cl_command_queue
5986    queue;
5987
5988  cl_context
5989    context;
5990
5991  cl_int
5992    inputPixelCount,
5993    pixelsPerWorkitem,
5994    clStatus;
5995
5996  cl_uint
5997    seed0,
5998    seed1;
5999
6000  cl_kernel
6001    addNoiseKernel;
6002
6003  cl_mem_flags
6004    mem_flags;
6005
6006  cl_mem
6007    filteredImageBuffer,
6008    imageBuffer;
6009
6010  const char
6011    *option;
6012
6013  const void
6014    *inputPixels;
6015
6016  float
6017    attenuate;
6018
6019  MagickBooleanType
6020    outputReady;
6021
6022  MagickCLEnv
6023    clEnv;
6024
6025  MagickSizeType
6026    length;
6027
6028  Image
6029    *filteredImage;
6030
6031  RandomInfo
6032    **restrict random_info;
6033
6034  size_t
6035    global_work_size[1],
6036    local_work_size[1];
6037
6038  unsigned int
6039    k,
6040    numRandomNumberPerPixel;
6041
6042#if defined(MAGICKCORE_OPENMP_SUPPORT)
6043  unsigned long
6044    key;
6045#endif
6046
6047  void
6048    *filteredPixels,
6049    *hostPtr;
6050
6051  outputReady = MagickFalse;
6052  clEnv = NULL;
6053  inputPixels = NULL;
6054  filteredImage = NULL;
6055  filteredImage_view = NULL;
6056  filteredPixels = NULL;
6057  context = NULL;
6058  imageBuffer = NULL;
6059  filteredImageBuffer = NULL;
6060  queue = NULL;
6061  addNoiseKernel = NULL;
6062
6063  clEnv = GetDefaultOpenCLEnv();
6064  context = GetOpenCLContext(clEnv);
6065  queue = AcquireOpenCLCommandQueue(clEnv);
6066
6067  image_view=AcquireVirtualCacheView(image,exception);
6068  inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
6069  if (inputPixels == (void *) NULL)
6070  {
6071    (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
6072    goto cleanup;
6073  }
6074
6075  if (ALIGNED(inputPixels,CLPixelPacket))
6076  {
6077    mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
6078  }
6079  else
6080  {
6081    mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
6082  }
6083  /* create a CL buffer from image pixel buffer */
6084  length = image->columns * image->rows;
6085  imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
6086  if (clStatus != CL_SUCCESS)
6087  {
6088    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
6089    goto cleanup;
6090  }
6091
6092
6093  filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
6094  assert(filteredImage != NULL);
6095  if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
6096  {
6097    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
6098    goto cleanup;
6099  }
6100  filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
6101  filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
6102  if (filteredPixels == (void *) NULL)
6103  {
6104    (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
6105    goto cleanup;
6106  }
6107
6108  if (ALIGNED(filteredPixels,CLPixelPacket))
6109  {
6110    mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
6111    hostPtr = filteredPixels;
6112  }
6113  else
6114  {
6115    mem_flags = CL_MEM_WRITE_ONLY;
6116    hostPtr = NULL;
6117  }
6118  /* create a CL buffer from image pixel buffer */
6119  length = image->columns * image->rows;
6120  filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
6121  if (clStatus != CL_SUCCESS)
6122  {
6123    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
6124    goto cleanup;
6125  }
6126
6127  /* find out how many random numbers needed by pixel */
6128  numRandomNumberPerPixel = 0;
6129  {
6130    unsigned int numRandPerChannel = 0;
6131    switch (noise_type)
6132    {
6133    case UniformNoise:
6134    case ImpulseNoise:
6135    case LaplacianNoise:
6136    case RandomNoise:
6137    default:
6138      numRandPerChannel = 1;
6139      break;
6140    case GaussianNoise:
6141    case MultiplicativeGaussianNoise:
6142    case PoissonNoise:
6143      numRandPerChannel = 2;
6144      break;
6145    };
6146
6147    if ((channel & RedChannel) != 0)
6148      numRandomNumberPerPixel+=numRandPerChannel;
6149    if ((channel & GreenChannel) != 0)
6150      numRandomNumberPerPixel+=numRandPerChannel;
6151    if ((channel & BlueChannel) != 0)
6152      numRandomNumberPerPixel+=numRandPerChannel;
6153    if ((channel & OpacityChannel) != 0)
6154      numRandomNumberPerPixel+=numRandPerChannel;
6155  }
6156
6157  /* set up the random number generators */
6158  attenuate=1.0;
6159  option=GetImageArtifact(image,"attenuate");
6160  if (option != (char *) NULL)
6161    attenuate=StringToDouble(option,(char **) NULL);
6162  random_info=AcquireRandomInfoThreadSet();
6163#if defined(MAGICKCORE_OPENMP_SUPPORT)
6164  key=GetRandomSecretKey(random_info[0]);
6165  (void) key;
6166#endif
6167
6168  addNoiseKernel = AcquireOpenCLKernel(clEnv,MAGICK_OPENCL_ACCELERATE,"GenerateNoiseImage");
6169
6170  {
6171    cl_uint computeUnitCount;
6172    cl_uint workItemCount;
6173    clEnv->library->clGetDeviceInfo(clEnv->device, CL_DEVICE_MAX_COMPUTE_UNITS, sizeof(cl_uint), &computeUnitCount, NULL);
6174    workItemCount = computeUnitCount * 2 * 256;			// 256 work items per group, 2 groups per CU
6175    inputPixelCount = (cl_int) (image->columns * image->rows);
6176    pixelsPerWorkitem = (inputPixelCount + workItemCount - 1) / workItemCount;
6177    pixelsPerWorkitem = ((pixelsPerWorkitem + 3) / 4) * 4;
6178
6179    local_work_size[0] = 256;
6180    global_work_size[0] = workItemCount;
6181  }
6182  {
6183    RandomInfo* randomInfo = AcquireRandomInfo();
6184	const unsigned long* s = GetRandomInfoSeed(randomInfo);
6185	seed0 = s[0];
6186	GetPseudoRandomValue(randomInfo);
6187	seed1 = s[0];
6188	randomInfo = DestroyRandomInfo(randomInfo);
6189  }
6190
6191  k = 0;
6192  clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(cl_mem),(void *)&imageBuffer);
6193  clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(cl_mem),(void *)&filteredImageBuffer);
6194  clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(cl_uint),(void *)&inputPixelCount);
6195  clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(cl_uint),(void *)&pixelsPerWorkitem);
6196  clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(ChannelType),(void *)&channel);
6197  clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(NoiseType),(void *)&noise_type);
6198  attenuate=1.0f;
6199  option=GetImageArtifact(image,"attenuate");
6200  if (option != (char *) NULL)
6201    attenuate=(float)StringToDouble(option,(char **) NULL);
6202  clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(float),(void *)&attenuate);
6203  clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(cl_uint),(void *)&seed0);
6204  clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(cl_uint),(void *)&seed1);
6205  clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(unsigned int),(void *)&numRandomNumberPerPixel);
6206
6207  clEnv->library->clEnqueueNDRangeKernel(queue,addNoiseKernel,1,NULL,global_work_size,NULL,0,NULL,NULL);
6208
6209  if (ALIGNED(filteredPixels,CLPixelPacket))
6210  {
6211    length = image->columns * image->rows;
6212    clEnv->library->clEnqueueMapBuffer(queue, filteredImageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
6213  }
6214  else
6215  {
6216    length = image->columns * image->rows;
6217    clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
6218  }
6219  if (clStatus != CL_SUCCESS)
6220  {
6221    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
6222    goto cleanup;
6223  }
6224
6225  outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
6226
6227cleanup:
6228  OpenCLLogException(__FUNCTION__,__LINE__,exception);
6229
6230  image_view=DestroyCacheView(image_view);
6231  if (filteredImage_view != NULL)
6232    filteredImage_view=DestroyCacheView(filteredImage_view);
6233
6234  if (queue!=NULL)                  RelinquishOpenCLCommandQueue(clEnv, queue);
6235  if (addNoiseKernel!=NULL)         RelinquishOpenCLKernel(clEnv, addNoiseKernel);
6236  if (imageBuffer!=NULL)		    clEnv->library->clReleaseMemObject(imageBuffer);
6237  if (filteredImageBuffer!=NULL)	  clEnv->library->clReleaseMemObject(filteredImageBuffer);
6238  if (outputReady == MagickFalse && filteredImage != NULL)
6239    filteredImage=DestroyImage(filteredImage);
6240
6241  return(filteredImage);
6242}
6243
6244
6245MagickExport Image *AccelerateAddNoiseImage(const Image *image,
6246  const ChannelType channel,const NoiseType noise_type,
6247  ExceptionInfo *exception)
6248{
6249  Image
6250    *filteredImage;
6251
6252  assert(image != NULL);
6253  assert(exception != (ExceptionInfo *) NULL);
6254
6255  if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
6256      (checkAccelerateCondition(image, channel) == MagickFalse))
6257    return NULL;
6258
6259  filteredImage = ComputeAddNoiseImage(image,channel,noise_type,exception);
6260
6261  return(filteredImage);
6262}
6263
6264static MagickBooleanType LaunchRandomImageKernel(MagickCLEnv clEnv,
6265  cl_command_queue queue,cl_mem imageBuffer,const unsigned int imageColumns,
6266  const unsigned int imageRows,cl_mem seedBuffer,
6267  const unsigned int numGenerators,ExceptionInfo *exception)
6268{
6269  int
6270    k;
6271
6272  cl_int
6273    clStatus;
6274
6275  cl_kernel
6276    randomImageKernel;
6277
6278  MagickBooleanType
6279    status;
6280
6281  size_t
6282    global_work_size,
6283    local_work_size;
6284
6285  status = MagickFalse;
6286  randomImageKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "RandomImage");
6287
6288  k = 0;
6289  clEnv->library->clSetKernelArg(randomImageKernel,k++,sizeof(cl_mem),(void*)&imageBuffer);
6290  clEnv->library->clSetKernelArg(randomImageKernel,k++,sizeof(cl_uint),(void*)&imageColumns);
6291  clEnv->library->clSetKernelArg(randomImageKernel,k++,sizeof(cl_uint),(void*)&imageRows);
6292  clEnv->library->clSetKernelArg(randomImageKernel,k++,sizeof(cl_mem),(void*)&seedBuffer);
6293  {
6294    const float randNormNumerator = 1.0f;
6295    const unsigned int randNormDenominator = (unsigned int)(~0UL);
6296    clEnv->library->clSetKernelArg(randomImageKernel,k++,
6297          sizeof(float),(void*)&randNormNumerator);
6298    clEnv->library->clSetKernelArg(randomImageKernel,k++,
6299          sizeof(cl_uint),(void*)&randNormDenominator);
6300  }
6301
6302
6303  global_work_size = numGenerators;
6304  local_work_size = 64;
6305
6306  clStatus = clEnv->library->clEnqueueNDRangeKernel(queue,randomImageKernel,1,NULL,&global_work_size,
6307                                    &local_work_size,0,NULL,NULL);
6308
6309  if (clStatus != CL_SUCCESS)
6310  {
6311    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning,
6312                                      "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
6313    goto cleanup;
6314  }
6315  status = MagickTrue;
6316
6317cleanup:
6318  if (randomImageKernel!=NULL) RelinquishOpenCLKernel(clEnv, randomImageKernel);
6319  return(status);
6320}
6321
6322static MagickBooleanType ComputeRandomImage(Image* image,
6323  ExceptionInfo* exception)
6324{
6325  CacheView
6326    *image_view;
6327
6328  cl_command_queue
6329    queue;
6330
6331  cl_context
6332    context;
6333
6334  cl_int
6335    clStatus;
6336
6337  /* Don't release this buffer in this function !!! */
6338  cl_mem
6339    randomNumberSeedsBuffer;
6340
6341  cl_mem_flags
6342    mem_flags;
6343
6344  cl_mem
6345   imageBuffer;
6346
6347  MagickBooleanType
6348    outputReady,
6349    status;
6350
6351  MagickCLEnv
6352    clEnv;
6353
6354  MagickSizeType
6355    length;
6356
6357  void
6358    *inputPixels;
6359
6360  status = MagickFalse;
6361  outputReady = MagickFalse;
6362  inputPixels = NULL;
6363  context = NULL;
6364  imageBuffer = NULL;
6365  queue = NULL;
6366
6367  clEnv = GetDefaultOpenCLEnv();
6368  context = GetOpenCLContext(clEnv);
6369
6370  /* Create and initialize OpenCL buffers. */
6371  image_view=AcquireAuthenticCacheView(image,exception);
6372  inputPixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
6373  if (inputPixels == (void *) NULL)
6374  {
6375    (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
6376    goto cleanup;
6377  }
6378
6379  /* If the host pointer is aligned to the size of CLPixelPacket,
6380     then use the host buffer directly from the GPU; otherwise,
6381     create a buffer on the GPU and copy the data over */
6382  if (ALIGNED(inputPixels,CLPixelPacket))
6383  {
6384    mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
6385  }
6386  else
6387  {
6388    mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
6389  }
6390  /* create a CL buffer from image pixel buffer */
6391  length = image->columns * image->rows;
6392  imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
6393  if (clStatus != CL_SUCCESS)
6394  {
6395    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
6396    goto cleanup;
6397  }
6398
6399  queue = AcquireOpenCLCommandQueue(clEnv);
6400
6401  randomNumberSeedsBuffer = GetAndLockRandSeedBuffer(clEnv);
6402  if (randomNumberSeedsBuffer==NULL)
6403  {
6404    (void) OpenCLThrowMagickException(exception, GetMagickModule(),
6405           ResourceLimitWarning, "Failed to get GPU random number generators.",
6406           "'%s'", ".");
6407    goto cleanup;
6408  }
6409
6410  status = LaunchRandomImageKernel(clEnv,queue,
6411                                   imageBuffer,
6412                                   (unsigned int) image->columns,
6413                                   (unsigned int) image->rows,
6414                                   randomNumberSeedsBuffer,
6415                                   GetNumRandGenerators(clEnv),
6416                                   exception);
6417  if (status==MagickFalse)
6418  {
6419    goto cleanup;
6420  }
6421
6422  if (ALIGNED(inputPixels,CLPixelPacket))
6423  {
6424    length = image->columns * image->rows;
6425    clEnv->library->clEnqueueMapBuffer(queue, imageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
6426  }
6427  else
6428  {
6429    length = image->columns * image->rows;
6430    clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), inputPixels, 0, NULL, NULL);
6431  }
6432  if (clStatus != CL_SUCCESS)
6433  {
6434    (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
6435    goto cleanup;
6436  }
6437  outputReady=SyncCacheViewAuthenticPixels(image_view,exception);
6438
6439cleanup:
6440  OpenCLLogException(__FUNCTION__,__LINE__,exception);
6441
6442  image_view=DestroyCacheView(image_view);
6443
6444  UnlockRandSeedBuffer(clEnv);
6445  if (imageBuffer!=NULL)		      clEnv->library->clReleaseMemObject(imageBuffer);
6446  if (queue != NULL)                  RelinquishOpenCLCommandQueue(clEnv, queue);
6447  return outputReady;
6448}
6449
6450MagickExport MagickBooleanType AccelerateRandomImage(Image *image,
6451  ExceptionInfo* exception)
6452{
6453  MagickBooleanType
6454    status;
6455
6456  assert(image != NULL);
6457  assert(exception != (ExceptionInfo *) NULL);
6458
6459  if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
6460      (checkAccelerateCondition(image, AllChannels) == MagickFalse))
6461    return(MagickFalse);
6462
6463  status=ComputeRandomImage(image,exception);
6464  return(status);
6465}
6466
6467static Image* ComputeMotionBlurImage(const Image *image,
6468  const ChannelType channel,const double *kernel,const size_t width,
6469  const OffsetInfo *offset,ExceptionInfo *exception)
6470{
6471  CacheView
6472    *filteredImage_view,
6473    *image_view;
6474
6475  cl_command_queue
6476    queue;
6477
6478  cl_context
6479    context;
6480
6481  cl_float4
6482    biasPixel;
6483
6484  cl_int
6485    clStatus;
6486
6487  cl_kernel
6488    motionBlurKernel;
6489
6490  cl_mem
6491    filteredImageBuffer,
6492    imageBuffer,
6493    imageKernelBuffer,
6494    offsetBuffer;
6495
6496  cl_mem_flags
6497    mem_flags;
6498
6499  const void
6500    *inputPixels;
6501
6502  float
6503    *kernelBufferPtr;
6504
6505  Image
6506    *filteredImage;
6507
6508  int
6509    *offsetBufferPtr;
6510
6511  MagickBooleanType
6512    outputReady;
6513
6514  MagickCLEnv
6515   clEnv;
6516
6517  PixelInfo
6518    bias;
6519
6520  MagickSizeType
6521    length;
6522
6523  size_t
6524    global_work_size[2],
6525    local_work_size[2];
6526
6527  unsigned int
6528    i,
6529    imageHeight,
6530    imageWidth,
6531    matte;
6532
6533  void
6534    *filteredPixels,
6535    *hostPtr;
6536
6537  outputReady = MagickFalse;
6538  context = NULL;
6539  filteredImage = NULL;
6540  filteredImage_view = NULL;
6541  imageBuffer = NULL;
6542  filteredImageBuffer = NULL;
6543  imageKernelBuffer = NULL;
6544  motionBlurKernel = NULL;
6545  queue = NULL;
6546
6547  clEnv = GetDefaultOpenCLEnv();
6548  context = GetOpenCLContext(clEnv);
6549
6550  /* Create and initialize OpenCL buffers. */
6551
6552  image_view=AcquireVirtualCacheView(image,exception);
6553  inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
6554  if (inputPixels == (const void *) NULL)
6555  {
6556    (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
6557      "UnableToReadPixelCache.","`%s'",image->filename);
6558    goto cleanup;
6559  }
6560
6561  // If the host pointer is aligned to the size of CLPixelPacket,
6562  // then use the host buffer directly from the GPU; otherwise,
6563  // create a buffer on the GPU and copy the data over
6564  if (ALIGNED(inputPixels,CLPixelPacket))
6565  {
6566    mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
6567  }
6568  else
6569  {
6570    mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
6571  }
6572  // create a CL buffer from image pixel buffer
6573  length = image->columns * image->rows;
6574  imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags,
6575    length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
6576  if (clStatus != CL_SUCCESS)
6577  {
6578    (void) ThrowMagickException(exception, GetMagickModule(),
6579      ResourceLimitError, "clEnv->library->clCreateBuffer failed.",".");
6580    goto cleanup;
6581  }
6582
6583
6584  filteredImage = CloneImage(image,image->columns,image->rows,
6585    MagickTrue,exception);
6586  assert(filteredImage != NULL);
6587  if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
6588  {
6589    (void) ThrowMagickException(exception, GetMagickModule(),
6590      ResourceLimitError, "CloneImage failed.", "'%s'", ".");
6591    goto cleanup;
6592  }
6593  filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
6594  filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
6595  if (filteredPixels == (void *) NULL)
6596  {
6597    (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
6598      "UnableToReadPixelCache.","`%s'",filteredImage->filename);
6599    goto cleanup;
6600  }
6601
6602  if (ALIGNED(filteredPixels,CLPixelPacket))
6603  {
6604    mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
6605    hostPtr = filteredPixels;
6606  }
6607  else
6608  {
6609    mem_flags = CL_MEM_WRITE_ONLY;
6610    hostPtr = NULL;
6611  }
6612  // create a CL buffer from image pixel buffer
6613  length = image->columns * image->rows;
6614  filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags,
6615    length * sizeof(CLPixelPacket), hostPtr, &clStatus);
6616  if (clStatus != CL_SUCCESS)
6617  {
6618    (void) ThrowMagickException(exception, GetMagickModule(),
6619      ResourceLimitError, "clEnv->library->clCreateBuffer failed.",".");
6620    goto cleanup;
6621  }
6622
6623
6624  imageKernelBuffer = clEnv->library->clCreateBuffer(context,
6625    CL_MEM_READ_ONLY|CL_MEM_ALLOC_HOST_PTR, width * sizeof(float), NULL,
6626    &clStatus);
6627  if (clStatus != CL_SUCCESS)
6628  {
6629    (void) ThrowMagickException(exception, GetMagickModule(),
6630      ResourceLimitError, "clEnv->library->clCreateBuffer failed.",".");
6631    goto cleanup;
6632  }
6633
6634  queue = AcquireOpenCLCommandQueue(clEnv);
6635  kernelBufferPtr = (float*)clEnv->library->clEnqueueMapBuffer(queue, imageKernelBuffer,
6636    CL_TRUE, CL_MAP_WRITE, 0, width * sizeof(float), 0, NULL, NULL, &clStatus);
6637  if (clStatus != CL_SUCCESS)
6638  {
6639    (void) ThrowMagickException(exception, GetMagickModule(),
6640      ResourceLimitError, "clEnv->library->clEnqueueMapBuffer failed.",".");
6641    goto cleanup;
6642  }
6643  for (i = 0; i < width; i++)
6644  {
6645    kernelBufferPtr[i] = (float) kernel[i];
6646  }
6647  clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, imageKernelBuffer, kernelBufferPtr,
6648    0, NULL, NULL);
6649 if (clStatus != CL_SUCCESS)
6650  {
6651    (void) ThrowMagickException(exception, GetMagickModule(), ModuleFatalError,
6652      "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
6653    goto cleanup;
6654  }
6655
6656  offsetBuffer = clEnv->library->clCreateBuffer(context,
6657    CL_MEM_READ_ONLY|CL_MEM_ALLOC_HOST_PTR, width * sizeof(cl_int2), NULL,
6658    &clStatus);
6659  if (clStatus != CL_SUCCESS)
6660  {
6661    (void) ThrowMagickException(exception, GetMagickModule(),
6662      ResourceLimitError, "clEnv->library->clCreateBuffer failed.",".");
6663    goto cleanup;
6664  }
6665
6666  offsetBufferPtr = (int*)clEnv->library->clEnqueueMapBuffer(queue, offsetBuffer, CL_TRUE,
6667    CL_MAP_WRITE, 0, width * sizeof(cl_int2), 0, NULL, NULL, &clStatus);
6668  if (clStatus != CL_SUCCESS)
6669  {
6670    (void) ThrowMagickException(exception, GetMagickModule(),
6671      ResourceLimitError, "clEnv->library->clEnqueueMapBuffer failed.",".");
6672    goto cleanup;
6673  }
6674  for (i = 0; i < width; i++)
6675  {
6676    offsetBufferPtr[2*i] = (int)offset[i].x;
6677    offsetBufferPtr[2*i+1] = (int)offset[i].y;
6678  }
6679  clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, offsetBuffer, offsetBufferPtr, 0,
6680    NULL, NULL);
6681 if (clStatus != CL_SUCCESS)
6682  {
6683    (void) ThrowMagickException(exception, GetMagickModule(), ModuleFatalError,
6684      "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
6685    goto cleanup;
6686  }
6687
6688
6689 // get the OpenCL kernel
6690  motionBlurKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE,
6691    "MotionBlur");
6692  if (motionBlurKernel == NULL)
6693  {
6694    (void) ThrowMagickException(exception, GetMagickModule(), ModuleFatalError,
6695      "AcquireOpenCLKernel failed.", "'%s'", ".");
6696    goto cleanup;
6697  }
6698
6699  // set the kernel arguments
6700  i = 0;
6701  clStatus=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(cl_mem),
6702    (void *)&imageBuffer);
6703  clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(cl_mem),
6704    (void *)&filteredImageBuffer);
6705  imageWidth = (unsigned int) image->columns;
6706  imageHeight = (unsigned int) image->rows;
6707  clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(unsigned int),
6708    &imageWidth);
6709  clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(unsigned int),
6710    &imageHeight);
6711  clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(cl_mem),
6712    (void *)&imageKernelBuffer);
6713  clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(unsigned int),
6714    &width);
6715  clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(cl_mem),
6716    (void *)&offsetBuffer);
6717
6718  GetPixelInfo(image,&bias);
6719  biasPixel.s[0] = bias.red;
6720  biasPixel.s[1] = bias.green;
6721  biasPixel.s[2] = bias.blue;
6722  biasPixel.s[3] = bias.alpha;
6723  clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(cl_float4), &biasPixel);
6724
6725  clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(ChannelType), &channel);
6726  matte = (image->alpha_trait != BlendPixelTrait)?1:0;
6727  clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(unsigned int), &matte);
6728  if (clStatus != CL_SUCCESS)
6729  {
6730    (void) ThrowMagickException(exception, GetMagickModule(), ModuleFatalError,
6731      "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
6732    goto cleanup;
6733  }
6734
6735  // launch the kernel
6736  local_work_size[0] = 16;
6737  local_work_size[1] = 16;
6738  global_work_size[0] = (size_t)padGlobalWorkgroupSizeToLocalWorkgroupSize(
6739                                (unsigned int) image->columns,(unsigned int) local_work_size[0]);
6740  global_work_size[1] = (size_t)padGlobalWorkgroupSizeToLocalWorkgroupSize(
6741                                (unsigned int) image->rows,(unsigned int) local_work_size[1]);
6742  clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, motionBlurKernel, 2, NULL,
6743    global_work_size, local_work_size, 0, NULL, NULL);
6744
6745  if (clStatus != CL_SUCCESS)
6746  {
6747    (void) ThrowMagickException(exception, GetMagickModule(), ModuleFatalError,
6748      "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
6749    goto cleanup;
6750  }
6751  clEnv->library->clFlush(queue);
6752
6753  if (ALIGNED(filteredPixels,CLPixelPacket))
6754  {
6755    length = image->columns * image->rows;
6756    clEnv->library->clEnqueueMapBuffer(queue, filteredImageBuffer, CL_TRUE,
6757      CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL,
6758      NULL, &clStatus);
6759  }
6760  else
6761  {
6762    length = image->columns * image->rows;
6763    clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0,
6764      length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
6765  }
6766  if (clStatus != CL_SUCCESS)
6767  {
6768    (void) ThrowMagickException(exception, GetMagickModule(), ModuleFatalError,
6769      "Reading output image from CL buffer failed.", "'%s'", ".");
6770    goto cleanup;
6771  }
6772  outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
6773
6774cleanup:
6775
6776  image_view=DestroyCacheView(image_view);
6777  if (filteredImage_view != NULL)
6778    filteredImage_view=DestroyCacheView(filteredImage_view);
6779
6780  if (filteredImageBuffer!=NULL)  clEnv->library->clReleaseMemObject(filteredImageBuffer);
6781  if (imageBuffer!=NULL)     clEnv->library->clReleaseMemObject(imageBuffer);
6782  if (imageKernelBuffer!=NULL)    clEnv->library->clReleaseMemObject(imageKernelBuffer);
6783  if (motionBlurKernel!=NULL)  RelinquishOpenCLKernel(clEnv, motionBlurKernel);
6784  if (queue != NULL)           RelinquishOpenCLCommandQueue(clEnv, queue);
6785  if (outputReady == MagickFalse && filteredImage != NULL)
6786    filteredImage=DestroyImage(filteredImage);
6787
6788  return(filteredImage);
6789}
6790
6791MagickExport Image *AccelerateMotionBlurImage(const Image *image,
6792  const ChannelType channel,const double* kernel,const size_t width,
6793  const OffsetInfo *offset,ExceptionInfo *exception)
6794{
6795  Image
6796    *filteredImage;
6797
6798  assert(image != NULL);
6799  assert(kernel != (double *) NULL);
6800  assert(offset != (OffsetInfo *) NULL);
6801  assert(exception != (ExceptionInfo *) NULL);
6802
6803  if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
6804      (checkAccelerateCondition(image, channel) == MagickFalse))
6805    return NULL;
6806
6807  filteredImage=ComputeMotionBlurImage(image, channel, kernel, width,
6808    offset, exception);
6809  return(filteredImage);
6810}
6811
6812static MagickBooleanType LaunchCompositeKernel(MagickCLEnv clEnv,
6813  cl_command_queue queue,cl_mem imageBuffer,const unsigned int inputWidth,
6814  const unsigned int inputHeight,const unsigned int matte,
6815  const ChannelType channel,const CompositeOperator compose,
6816  const cl_mem compositeImageBuffer,const unsigned int compositeWidth,
6817  const unsigned int compositeHeight,const float destination_dissolve,
6818  const float source_dissolve,ExceptionInfo *magick_unused(exception))
6819{
6820  cl_int
6821    clStatus;
6822
6823  cl_kernel
6824    compositeKernel;
6825
6826  int
6827    k;
6828
6829  size_t
6830    global_work_size[2],
6831    local_work_size[2];
6832
6833  unsigned int
6834    composeOp;
6835
6836  magick_unreferenced(exception);
6837
6838  compositeKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE,
6839    "Composite");
6840
6841  k = 0;
6842  clStatus=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(cl_mem),(void*)&imageBuffer);
6843  clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(unsigned int),(void*)&inputWidth);
6844  clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(unsigned int),(void*)&inputHeight);
6845  clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(cl_mem),(void*)&compositeImageBuffer);
6846  clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(unsigned int),(void*)&compositeWidth);
6847  clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(unsigned int),(void*)&compositeHeight);
6848  composeOp = (unsigned int)compose;
6849  clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(unsigned int),(void*)&composeOp);
6850  clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(ChannelType),(void*)&channel);
6851  clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(unsigned int),(void*)&matte);
6852  clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(float),(void*)&destination_dissolve);
6853  clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(float),(void*)&source_dissolve);
6854
6855  if (clStatus!=CL_SUCCESS)
6856    return MagickFalse;
6857
6858  local_work_size[0] = 64;
6859  local_work_size[1] = 1;
6860
6861  global_work_size[0] = padGlobalWorkgroupSizeToLocalWorkgroupSize(inputWidth,
6862    (unsigned int) local_work_size[0]);
6863  global_work_size[1] = inputHeight;
6864  clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, compositeKernel, 2, NULL,
6865    global_work_size, local_work_size, 0, NULL, NULL);
6866
6867
6868  RelinquishOpenCLKernel(clEnv, compositeKernel);
6869
6870  return((clStatus==CL_SUCCESS) ? MagickTrue : MagickFalse);
6871}
6872
6873static MagickBooleanType ComputeCompositeImage(Image *image,
6874  const ChannelType channel,const CompositeOperator compose,
6875  const Image *compositeImage,const ssize_t magick_unused(x_offset),
6876  const ssize_t magick_unused(y_offset),const float destination_dissolve,
6877  const float source_dissolve,ExceptionInfo *exception)
6878{
6879  CacheView
6880    *image_view;
6881
6882  cl_command_queue
6883    queue;
6884
6885  cl_context
6886    context;
6887
6888  cl_int
6889    clStatus;
6890
6891  cl_mem_flags
6892    mem_flags;
6893
6894  cl_mem
6895    compositeImageBuffer,
6896    imageBuffer;
6897
6898  const void
6899    *composePixels;
6900
6901  MagickBooleanType
6902    outputReady,
6903    status;
6904
6905  MagickCLEnv
6906    clEnv;
6907
6908  MagickSizeType
6909    length;
6910
6911  void
6912    *inputPixels;
6913
6914  magick_unreferenced(x_offset);
6915  magick_unreferenced(y_offset);
6916
6917  status = MagickFalse;
6918  outputReady = MagickFalse;
6919  composePixels = NULL;
6920  imageBuffer = NULL;
6921  compositeImageBuffer = NULL;
6922
6923  clEnv = GetDefaultOpenCLEnv();
6924  context = GetOpenCLContext(clEnv);
6925  queue = AcquireOpenCLCommandQueue(clEnv);
6926
6927  /* Create and initialize OpenCL buffers. */
6928  image_view=AcquireAuthenticCacheView(image,exception);
6929  inputPixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
6930  if (inputPixels == (void *) NULL)
6931  {
6932    (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,
6933      "UnableToReadPixelCache.","`%s'",image->filename);
6934    goto cleanup;
6935  }
6936
6937  /* If the host pointer is aligned to the size of CLPixelPacket,
6938     then use the host buffer directly from the GPU; otherwise,
6939     create a buffer on the GPU and copy the data over */
6940  if (ALIGNED(inputPixels,CLPixelPacket))
6941  {
6942    mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
6943  }
6944  else
6945  {
6946    mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
6947  }
6948  /* create a CL buffer from image pixel buffer */
6949  length = image->columns * image->rows;
6950  imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags,
6951    length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
6952  if (clStatus != CL_SUCCESS)
6953  {
6954    (void) OpenCLThrowMagickException(exception, GetMagickModule(),
6955      ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
6956    goto cleanup;
6957  }
6958
6959
6960  /* Create and initialize OpenCL buffers. */
6961  composePixels = AcquirePixelCachePixels(compositeImage, &length, exception);
6962  if (composePixels == (void *) NULL)
6963  {
6964    (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,
6965      "UnableToReadPixelCache.","`%s'",compositeImage->filename);
6966    goto cleanup;
6967  }
6968
6969  /* If the host pointer is aligned to the size of CLPixelPacket,
6970     then use the host buffer directly from the GPU; otherwise,
6971     create a buffer on the GPU and copy the data over */
6972  if (ALIGNED(composePixels,CLPixelPacket))
6973  {
6974    mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
6975  }
6976  else
6977  {
6978    mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
6979  }
6980  /* create a CL buffer from image pixel buffer */
6981  length = compositeImage->columns * compositeImage->rows;
6982  compositeImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags,
6983    length * sizeof(CLPixelPacket), (void*)composePixels, &clStatus);
6984  if (clStatus != CL_SUCCESS)
6985  {
6986    (void) OpenCLThrowMagickException(exception, GetMagickModule(),
6987      ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
6988    goto cleanup;
6989  }
6990
6991  status = LaunchCompositeKernel(clEnv,queue,imageBuffer,
6992           (unsigned int) image->columns,
6993           (unsigned int) image->rows,
6994           (unsigned int) (image->alpha_trait != BlendPixelTrait) ? 1 : 0,
6995           channel, compose, compositeImageBuffer,
6996           (unsigned int) compositeImage->columns,
6997           (unsigned int) compositeImage->rows,
6998           destination_dissolve,source_dissolve,
6999           exception);
7000
7001  if (status==MagickFalse)
7002    goto cleanup;
7003
7004  length = image->columns * image->rows;
7005  if (ALIGNED(inputPixels,CLPixelPacket))
7006  {
7007    clEnv->library->clEnqueueMapBuffer(queue, imageBuffer, CL_TRUE,
7008      CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL,
7009      NULL, &clStatus);
7010  }
7011  else
7012  {
7013    clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0,
7014      length * sizeof(CLPixelPacket), inputPixels, 0, NULL, NULL);
7015  }
7016  if (clStatus==CL_SUCCESS)
7017    outputReady=SyncCacheViewAuthenticPixels(image_view,exception);
7018
7019cleanup:
7020
7021  image_view=DestroyCacheView(image_view);
7022  if (imageBuffer!=NULL)      clEnv->library->clReleaseMemObject(imageBuffer);
7023  if (compositeImageBuffer!=NULL)  clEnv->library->clReleaseMemObject(compositeImageBuffer);
7024  if (queue != NULL)               RelinquishOpenCLCommandQueue(clEnv, queue);
7025
7026  return(outputReady);
7027}
7028
7029MagickExport MagickBooleanType AccelerateCompositeImage(Image *image,
7030  const ChannelType channel,const CompositeOperator compose,
7031  const Image *composite,const ssize_t x_offset,const ssize_t y_offset,
7032  const float destination_dissolve,const float source_dissolve,
7033  ExceptionInfo *exception)
7034{
7035  MagickBooleanType
7036    status;
7037
7038  assert(image != NULL);
7039  assert(exception != (ExceptionInfo *) NULL);
7040
7041  if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
7042      (checkAccelerateCondition(image, channel) == MagickFalse))
7043    return(MagickFalse);
7044
7045  /* only support zero offset and
7046     images with the size for now */
7047  if (x_offset!=0
7048    || y_offset!=0
7049    || image->columns!=composite->columns
7050    || image->rows!=composite->rows)
7051    return MagickFalse;
7052
7053  switch(compose) {
7054  case ColorDodgeCompositeOp:
7055  case BlendCompositeOp:
7056    break;
7057  default:
7058    // unsupported compose operator, quit
7059    return MagickFalse;
7060  };
7061
7062  status = ComputeCompositeImage(image,channel,compose,composite,
7063    x_offset,y_offset,destination_dissolve,source_dissolve,exception);
7064
7065  return(status);
7066}
7067
7068#else  /* MAGICKCORE_OPENCL_SUPPORT  */
7069
7070MagickExport Image *AccelerateConvolveImageChannel(
7071  const Image *magick_unused(image),const ChannelType magick_unused(channel),
7072  const KernelInfo *magick_unused(kernel),
7073  ExceptionInfo *magick_unused(exception))
7074{
7075  magick_unreferenced(image);
7076  magick_unreferenced(channel);
7077  magick_unreferenced(kernel);
7078  magick_unreferenced(exception);
7079
7080  return NULL;
7081}
7082
7083MagickExport MagickBooleanType AccelerateFunctionImage(
7084  Image *magick_unused(image),const ChannelType magick_unused(channel),
7085  const MagickFunction magick_unused(function),
7086  const size_t magick_unused(number_parameters),
7087  const double *magick_unused(parameters),
7088  ExceptionInfo *magick_unused(exception))
7089{
7090  magick_unreferenced(image);
7091  magick_unreferenced(channel);
7092  magick_unreferenced(function);
7093  magick_unreferenced(number_parameters);
7094  magick_unreferenced(parameters);
7095  magick_unreferenced(exception);
7096
7097  return MagickFalse;
7098}
7099
7100MagickExport Image *AccelerateBlurImage(const Image *magick_unused(image),
7101  const ChannelType magick_unused(channel),const double magick_unused(radius),
7102  const double magick_unused(sigma),ExceptionInfo *magick_unused(exception))
7103{
7104  magick_unreferenced(image);
7105  magick_unreferenced(channel);
7106  magick_unreferenced(radius);
7107  magick_unreferenced(sigma);
7108  magick_unreferenced(exception);
7109
7110  return NULL;
7111}
7112
7113MagickExport Image *AccelerateRotationalBlurImage(
7114  const Image *magick_unused(image),const ChannelType magick_unused(channel),
7115  const double magick_unused(angle),ExceptionInfo *magick_unused(exception))
7116{
7117  magick_unreferenced(image);
7118  magick_unreferenced(channel);
7119  magick_unreferenced(angle);
7120  magick_unreferenced(exception);
7121
7122  return NULL;
7123}
7124
7125
7126MagickExport Image *AccelerateUnsharpMaskImage(
7127  const Image *magick_unused(image),const ChannelType magick_unused(channel),
7128  const double magick_unused(radius),const double magick_unused(sigma),
7129  const double magick_unused(gain),const double magick_unused(threshold),
7130  ExceptionInfo *magick_unused(exception))
7131{
7132  magick_unreferenced(image);
7133  magick_unreferenced(channel);
7134  magick_unreferenced(radius);
7135  magick_unreferenced(sigma);
7136  magick_unreferenced(gain);
7137  magick_unreferenced(threshold);
7138  magick_unreferenced(exception);
7139
7140  return NULL;
7141}
7142
7143MagickExport
7144MagickBooleanType AccelerateCompositeImage(Image *image,
7145  const ChannelType channel,const CompositeOperator compose,
7146  const Image *composite,const ssize_t x_offset,const ssize_t y_offset,
7147  const float destination_dissolve,const float source_dissolve,
7148  ExceptionInfo *exception)
7149{
7150  magick_unreferenced(image);
7151  magick_unreferenced(channel);
7152  magick_unreferenced(compose);
7153  magick_unreferenced(composite);
7154  magick_unreferenced(x_offset);
7155  magick_unreferenced(y_offset);
7156  magick_unreferenced(destination_dissolve);
7157  magick_unreferenced(source_dissolve);
7158  magick_unreferenced(exception);
7159
7160  return MagickFalse;
7161}
7162
7163
7164MagickExport MagickBooleanType AccelerateContrastImage(
7165  Image* magick_unused(image),const MagickBooleanType magick_unused(sharpen),
7166  ExceptionInfo* magick_unused(exception))
7167{
7168  magick_unreferenced(image);
7169  magick_unreferenced(sharpen);
7170  magick_unreferenced(exception);
7171
7172  return MagickFalse;
7173}
7174
7175MagickExport MagickBooleanType AccelerateContrastStretchImageChannel(
7176    Image * image, const ChannelType channel, const double black_point, const double white_point,
7177    ExceptionInfo* magick_unused(exception))
7178{
7179  magick_unreferenced(image);
7180  magick_unreferenced(channel);
7181  magick_unreferenced(black_point);
7182  magick_unreferenced(white_point);
7183  magick_unreferenced(exception);
7184
7185  return MagickFalse;
7186}
7187
7188MagickExport MagickBooleanType AccelerateEqualizeImage(
7189  Image* magick_unused(image), const ChannelType magick_unused(channel),
7190  ExceptionInfo* magick_unused(exception))
7191{
7192  magick_unreferenced(image);
7193  magick_unreferenced(channel);
7194  magick_unreferenced(exception);
7195
7196  return MagickFalse;
7197}
7198
7199MagickExport Image *AccelerateDespeckleImage(const Image* magick_unused(image),
7200  ExceptionInfo* magick_unused(exception))
7201{
7202  magick_unreferenced(image);
7203  magick_unreferenced(exception);
7204
7205  return NULL;
7206}
7207
7208MagickExport Image *AccelerateResizeImage(const Image* magick_unused(image),
7209  const size_t magick_unused(resizedColumns),
7210  const size_t magick_unused(resizedRows),
7211  const ResizeFilter* magick_unused(resizeFilter),
7212  ExceptionInfo *magick_unused(exception))
7213{
7214  magick_unreferenced(image);
7215  magick_unreferenced(resizedColumns);
7216  magick_unreferenced(resizedRows);
7217  magick_unreferenced(resizeFilter);
7218  magick_unreferenced(exception);
7219
7220  return NULL;
7221}
7222
7223MagickExport
7224MagickBooleanType AccelerateModulateImage(
7225  Image* image, double percent_brightness, double percent_hue,
7226  double percent_saturation, ColorspaceType colorspace, ExceptionInfo* exception)
7227{
7228  magick_unreferenced(image);
7229  magick_unreferenced(percent_brightness);
7230  magick_unreferenced(percent_hue);
7231  magick_unreferenced(percent_saturation);
7232  magick_unreferenced(colorspace);
7233  magick_unreferenced(exception);
7234  return(MagickFalse);
7235}
7236
7237MagickExport
7238MagickBooleanType AccelerateGrayscaleImage(
7239  Image* image, const PixelIntensityMethod method, ExceptionInfo* exception)
7240{
7241  magick_unreferenced(image);
7242  magick_unreferenced(method);
7243  magick_unreferenced(exception);
7244  return(MagickFalse);
7245}
7246
7247MagickExport Image *AccelerateAddNoiseImage(const Image *image,
7248  const ChannelType channel, const NoiseType noise_type,ExceptionInfo *exception)
7249{
7250  magick_unreferenced(image);
7251  magick_unreferenced(channel);
7252  magick_unreferenced(noise_type);
7253  magick_unreferenced(exception);
7254  return NULL;
7255}
7256
7257
7258MagickExport MagickBooleanType AccelerateRandomImage(Image* image, ExceptionInfo* exception)
7259{
7260  magick_unreferenced(image);
7261  magick_unreferenced(exception);
7262  return MagickFalse;
7263}
7264
7265MagickExport
7266Image* AccelerateMotionBlurImage(const Image *image, const ChannelType channel,
7267                                const double* kernel, const size_t width,
7268                                const OffsetInfo *offset,
7269                                ExceptionInfo *exception)
7270{
7271  magick_unreferenced(image);
7272  magick_unreferenced(channel);
7273  magick_unreferenced(kernel);
7274  magick_unreferenced(width);
7275  magick_unreferenced(offset);
7276  magick_unreferenced(exception);
7277  return NULL;
7278}
7279
7280#endif /* MAGICKCORE_OPENCL_SUPPORT */