]> granicus.if.org Git - imagemagick/blob - magick/accelerate.c
(no commit message)
[imagemagick] / magick / accelerate.c
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 %                               John Cristy                                   %
17 %                               January 2010                                  %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 % Morphology is the the application of various kernals, of any size and even
37 % shape, to a image in various ways (typically binary, but not always).
38 %
39 % Convolution (weighted sum or average) is just one specific type of
40 % accelerate. Just one that is very common for image bluring and sharpening
41 % effects.  Not only 2D Gaussian blurring, but also 2-pass 1D Blurring.
42 %
43 % This module provides not only a general accelerate function, and the ability
44 % to apply more advanced or iterative morphologies, but also functions for the
45 % generation of many different types of kernel arrays from user supplied
46 % arguments. Prehaps even the generation of a kernel from a small image.
47 */
48 \f
49 /*
50   Include declarations.
51 */
52 #include "magick/studio.h"
53 #include "magick/accelerate.h"
54 #include "magick/artifact.h"
55 #include "magick/cache-view.h"
56 #include "magick/color-private.h"
57 #include "magick/enhance.h"
58 #include "magick/exception.h"
59 #include "magick/exception-private.h"
60 #include "magick/gem.h"
61 #include "magick/hashmap.h"
62 #include "magick/image.h"
63 #include "magick/image-private.h"
64 #include "magick/list.h"
65 #include "magick/memory_.h"
66 #include "magick/monitor-private.h"
67 #include "magick/accelerate.h"
68 #include "magick/option.h"
69 #include "magick/pixel-private.h"
70 #include "magick/prepress.h"
71 #include "magick/quantize.h"
72 #include "magick/registry.h"
73 #include "magick/semaphore.h"
74 #include "magick/splay-tree.h"
75 #include "magick/statistic.h"
76 #include "magick/string_.h"
77 #include "magick/string-private.h"
78 #include "magick/token.h"
79 \f
80 /*
81 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
82 %                                                                             %
83 %                                                                             %
84 %                                                                             %
85 %     A c c e l e r a t e C o n v o l v e I m a g e                           %
86 %                                                                             %
87 %                                                                             %
88 %                                                                             %
89 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
90 %
91 %  AccelerateConvolveImage() applies a custom convolution kernel to the image.
92 %  It is accelerated by taking advantage of speed-ups offered by executing in
93 %  concert across heterogeneous platforms consisting of CPUs, GPUs, and other
94 %  processors.
95 %
96 %  The format of the AccelerateConvolveImage method is:
97 %
98 %      Image *AccelerateConvolveImage(const Image *image,
99 %        const KernelInfo *kernel,Image *convolve_image,
100 %        ExceptionInfo *exception)
101 %
102 %  A description of each parameter follows:
103 %
104 %    o image: the image.
105 %
106 %    o kernel: the convolution kernel.
107 %
108 %    o convole_image: the convoleed image.
109 %
110 %    o exception: return any errors or warnings in this structure.
111 %
112 */
113
114 #if defined(MAGICKCORE_OPENCL_SUPPORT)
115
116 #if defined(MAGICKCORE_HDRI_SUPPORT)
117 #define CLOptions "-DMAGICKCORE_HDRI_SUPPORT=1 -DCLQuantum=float " \
118   "-DCLPixelType=float4 -DQuantumRange=%g -DMagickEpsilon=%g"
119 #define CLPixelPacket  cl_float4
120 #else
121 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
122 #define CLOptions "-DCLQuantum=uchar -DCLPixelType=uchar4 " \
123   "-DQuantumRange=%g -DMagickEpsilon=%g"
124 #define CLPixelPacket  cl_uchar4
125 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
126 #define CLOptions "-DCLQuantum=ushort -DCLPixelType=ushort4 " \
127   "-DQuantumRange=%g -DMagickEpsilon=%g"
128 #define CLPixelPacket  cl_ushort4
129 #elif (MAGICKCORE_QUANTUM_DEPTH == 32)
130 #define CLOptions "-DCLQuantum=uint -DCLPixelType=uint4 " \
131   "-DQuantumRange=%g -DMagickEpsilon=%g"
132 #define CLPixelPacket  cl_uint4
133 #elif (MAGICKCORE_QUANTUM_DEPTH == 32)
134 #define CLOptions "-DCLQuantum=ussize_t -DCLPixelType=ussize_t4 " \
135   "-DQuantumRange=%g -DMagickEpsilon=%g"
136 #define CLPixelPacket  cl_ussize_t4
137 #endif
138 #endif
139
140 typedef struct _ConvolveInfo
141 {
142   cl_context
143     context;
144
145   cl_device_id
146     *devices;
147
148   cl_command_queue
149     command_queue;
150
151   cl_kernel
152     kernel;
153
154   cl_program
155     program;
156
157   cl_mem
158     pixels,
159     convolve_pixels;
160
161   cl_ussize_t
162     width,
163     height;
164
165   cl_bool
166     matte;
167
168   cl_mem
169     filter;
170 } ConvolveInfo;
171
172 static char
173   *ConvolveKernel =
174     "static inline ssize_t ClampToCanvas(const ssize_t offset,const ussize_t range)\n"
175     "{\n"
176     "  if (offset < 0L)\n"
177     "    return(0L);\n"
178     "  if (offset >= range)\n"
179     "    return((ssize_t) (range-1L));\n"
180     "  return(offset);\n"
181     "}\n"
182     "\n"
183     "static inline CLQuantum ClampToQuantum(const double value)\n"
184     "{\n"
185     "#if defined(MAGICKCORE_HDRI_SUPPORT)\n"
186     "  return((CLQuantum) value)\n"
187     "#else\n"
188     "  if (value < 0.0)\n"
189     "    return((CLQuantum) 0);\n"
190     "  if (value >= (double) QuantumRange)\n"
191     "    return((CLQuantum) QuantumRange);\n"
192     "  return((CLQuantum) (value+0.5));\n"
193     "#endif\n"
194     "}\n"
195     "\n"
196     "__kernel void Convolve(const __global CLPixelType *input,\n"
197     "  __constant double *filter,const ussize_t width,const ussize_t height,\n"
198     "  const bool matte,__global CLPixelType *output)\n"
199     "{\n"
200     "  const ussize_t columns = get_global_size(0);\n"
201     "  const ussize_t rows = get_global_size(1);\n"
202     "\n"
203     "  const ssize_t x = get_global_id(0);\n"
204     "  const ssize_t y = get_global_id(1);\n"
205     "\n"
206     "  const double scale = (1.0/QuantumRange);\n"
207     "  const ssize_t mid_width = (width-1)/2;\n"
208     "  const ssize_t mid_height = (height-1)/2;\n"
209     "  double4 sum = { 0.0, 0.0, 0.0, 0.0 };\n"
210     "  double gamma = 0.0;\n"
211     "  register ussize_t i = 0;\n"
212     "\n"
213     "  int method = 0;\n"
214     "  if (matte != false)\n"
215     "    method=1;\n"
216     "  if ((x >= width) && (x < (columns-width-1)) &&\n"
217     "      (y >= height) && (y < (rows-height-1)))\n"
218     "    {\n"
219     "      method=2;\n"
220     "      if (matte != false)\n"
221     "        method=3;\n"
222     "    }\n"
223     "  switch (method)\n"
224     "  {\n"
225     "    case 0:\n"
226     "    {\n"
227     "      for (ssize_t v=(-mid_height); v <= mid_height; v++)\n"
228     "      {\n"
229     "        for (ssize_t u=(-mid_width); u <= mid_width; u++)\n"
230     "        {\n"
231     "          const ssize_t index=ClampToCanvas(y+v,rows)*columns+\n"
232     "            ClampToCanvas(x+u,columns);\n"
233     "          sum.x+=filter[i]*input[index].x;\n"
234     "          sum.y+=filter[i]*input[index].y;\n"
235     "          sum.z+=filter[i]*input[index].z;\n"
236     "          gamma+=filter[i];\n"
237     "          i++;\n"
238     "        }\n"
239     "      }\n"
240     "      break;\n"
241     "    }\n"
242     "    case 1:\n"
243     "    {\n"
244     "      for (ssize_t v=(-mid_height); v <= mid_height; v++)\n"
245     "      {\n"
246     "        for (ssize_t u=(-mid_width); u <= mid_width; u++)\n"
247     "        {\n"
248     "          const ussize_t index=ClampToCanvas(y+v,rows)*columns+\n"
249     "            ClampToCanvas(x+u,columns);\n"
250     "          const double alpha=scale*(QuantumRange-input[index].w);\n"
251     "          sum.x+=alpha*filter[i]*input[index].x;\n"
252     "          sum.y+=alpha*filter[i]*input[index].y;\n"
253     "          sum.z+=alpha*filter[i]*input[index].z;\n"
254     "          sum.w+=filter[i]*input[index].w;\n"
255     "          gamma+=alpha*filter[i];\n"
256     "          i++;\n"
257     "        }\n"
258     "      }\n"
259     "      break;\n"
260     "    }\n"
261     "    case 2:\n"
262     "    {\n"
263     "      for (ssize_t v=(-mid_height); v <= mid_height; v++)\n"
264     "      {\n"
265     "        for (ssize_t u=(-mid_width); u <= mid_width; u++)\n"
266     "        {\n"
267     "          const ussize_t index=(y+v)*columns+(x+u);\n"
268     "          sum.x+=filter[i]*input[index].x;\n"
269     "          sum.y+=filter[i]*input[index].y;\n"
270     "          sum.z+=filter[i]*input[index].z;\n"
271     "          gamma+=filter[i];\n"
272     "          i++;\n"
273     "        }\n"
274     "      }\n"
275     "      break;\n"
276     "    }\n"
277     "    case 3:\n"
278     "    {\n"
279     "      for (ssize_t v=(-mid_height); v <= mid_height; v++)\n"
280     "      {\n"
281     "        for (ssize_t u=(-mid_width); u <= mid_width; u++)\n"
282     "        {\n"
283     "          const ussize_t index=(y+v)*columns+(x+u);\n"
284     "          const double alpha=scale*(QuantumRange-input[index].w);\n"
285     "          sum.x+=alpha*filter[i]*input[index].x;\n"
286     "          sum.y+=alpha*filter[i]*input[index].y;\n"
287     "          sum.z+=alpha*filter[i]*input[index].z;\n"
288     "          sum.w+=filter[i]*input[index].w;\n"
289     "          gamma+=alpha*filter[i];\n"
290     "          i++;\n"
291     "        }\n"
292     "      }\n"
293     "      break;\n"
294     "    }\n"
295     "  }\n"
296     "  gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);\n"
297     "  const ussize_t index = y*columns+x;\n"
298     "  output[index].x=ClampToQuantum(gamma*sum.x);\n"
299     "  output[index].y=ClampToQuantum(gamma*sum.y);\n"
300     "  output[index].z=ClampToQuantum(gamma*sum.z);\n"
301     "  if (matte == false)\n"
302     "    output[index].w=input[index].w;\n"
303     "  else\n"
304     "    output[index].w=ClampToQuantum(sum.w);\n"
305     "}\n";
306
307 static void ConvolveNotify(const char *message,const void *data,size_t length,
308   void *user_context)
309 {
310   ExceptionInfo
311     *exception;
312
313   (void) data;
314   (void) length;
315   exception=(ExceptionInfo *) user_context;
316   (void) ThrowMagickException(exception,GetMagickModule(),DelegateWarning,
317     "DelegateFailed","`%s'",message);
318 }
319
320 static MagickBooleanType BindConvolveParameters(ConvolveInfo *convolve_info,
321   const Image *image,const void *pixels,double *filter,
322   const size_t width,const size_t height,void *convolve_pixels)
323 {
324   cl_int
325     status;
326
327   register int
328     i;
329
330   size_t
331     length;
332
333   /*
334     Allocate OpenCL buffers.
335   */
336   length=image->columns*image->rows;
337   convolve_info->pixels=clCreateBuffer(convolve_info->context,CL_MEM_READ_ONLY |
338     CL_MEM_USE_HOST_PTR,length*sizeof(CLPixelPacket),(void *) pixels,&status);
339   if ((convolve_info->pixels == (cl_mem) NULL) || (status != CL_SUCCESS))
340     return(MagickFalse);
341   length=width*height;
342   convolve_info->filter=clCreateBuffer(convolve_info->context,CL_MEM_READ_ONLY |
343     CL_MEM_USE_HOST_PTR,length*sizeof(cl_double),filter,&status);
344   if ((convolve_info->filter == (cl_mem) NULL) || (status != CL_SUCCESS))
345     return(MagickFalse);
346   length=image->columns*image->rows;
347   convolve_info->convolve_pixels=clCreateBuffer(convolve_info->context,
348     CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR,length*sizeof(CLPixelPacket),
349     convolve_pixels,&status);
350   if ((convolve_info->convolve_pixels == (cl_mem) NULL) ||
351       (status != CL_SUCCESS))
352     return(MagickFalse);
353   /*
354     Bind OpenCL buffers.
355   */
356   i=0;
357   status=clSetKernelArg(convolve_info->kernel,i++,sizeof(cl_mem),(void *)
358     &convolve_info->pixels);
359   if (status != CL_SUCCESS)
360     return(MagickFalse);
361   status=clSetKernelArg(convolve_info->kernel,i++,sizeof(cl_mem),(void *)
362     &convolve_info->filter);
363   if (status != CL_SUCCESS)
364     return(MagickFalse);
365   convolve_info->width=(cl_ussize_t) width;
366   status=clSetKernelArg(convolve_info->kernel,i++,sizeof(cl_ussize_t),(void *)
367     &convolve_info->width);
368   if (status != CL_SUCCESS)
369     return(MagickFalse);
370   convolve_info->height=(cl_ussize_t) height;
371   status=clSetKernelArg(convolve_info->kernel,i++,sizeof(cl_ussize_t),(void *)
372     &convolve_info->height);
373   if (status != CL_SUCCESS)
374     return(MagickFalse);
375   convolve_info->matte=(cl_bool) image->matte;
376   status=clSetKernelArg(convolve_info->kernel,i++,sizeof(cl_bool),(void *)
377     &convolve_info->matte);
378   if (status != CL_SUCCESS)
379     return(MagickFalse);
380   status=clSetKernelArg(convolve_info->kernel,i++,sizeof(cl_mem),(void *)
381     &convolve_info->convolve_pixels);
382   if (status != CL_SUCCESS)
383     return(MagickFalse);
384   status=clFinish(convolve_info->command_queue);
385   if (status != CL_SUCCESS)
386     return(MagickFalse);
387   return(MagickTrue);
388 }
389
390 static void DestroyConvolveBuffers(ConvolveInfo *convolve_info)
391 {
392   cl_int
393     status;
394
395   if (convolve_info->convolve_pixels != (cl_mem) NULL)
396     status=clReleaseMemObject(convolve_info->convolve_pixels);
397   if (convolve_info->pixels != (cl_mem) NULL)
398     status=clReleaseMemObject(convolve_info->pixels);
399   if (convolve_info->filter != (cl_mem) NULL)
400     status=clReleaseMemObject(convolve_info->filter);
401 }
402
403 static ConvolveInfo *DestroyConvolveInfo(ConvolveInfo *convolve_info)
404 {
405   cl_int
406     status;
407
408   if (convolve_info->kernel != (cl_kernel) NULL)
409     status=clReleaseKernel(convolve_info->kernel);
410   if (convolve_info->program != (cl_program) NULL)
411     status=clReleaseProgram(convolve_info->program);
412   if (convolve_info->command_queue != (cl_command_queue) NULL)
413     status=clReleaseCommandQueue(convolve_info->command_queue);
414   if (convolve_info->context != (cl_context) NULL)
415     status=clReleaseContext(convolve_info->context);
416   convolve_info=(ConvolveInfo *) RelinquishMagickMemory(convolve_info);
417   return(convolve_info);
418 }
419
420 static MagickBooleanType EnqueueConvolveKernel(ConvolveInfo *convolve_info,
421   const Image *image,const void *pixels,double *filter,
422   const size_t width,const size_t height,void *convolve_pixels)
423 {
424   cl_int
425     status;
426
427   size_t
428     global_work_size[2],
429     length;
430
431   length=image->columns*image->rows;
432   status=clEnqueueWriteBuffer(convolve_info->command_queue,
433     convolve_info->pixels,CL_TRUE,0,length*sizeof(CLPixelPacket),pixels,0,NULL,
434     NULL);
435   length=width*height;
436   status=clEnqueueWriteBuffer(convolve_info->command_queue,
437     convolve_info->filter,CL_TRUE,0,length*sizeof(cl_double),filter,0,NULL,
438     NULL);
439   if (status != CL_SUCCESS)
440     return(MagickFalse);
441   global_work_size[0]=image->columns;
442   global_work_size[1]=image->rows;
443   status=clEnqueueNDRangeKernel(convolve_info->command_queue,
444     convolve_info->kernel,2,NULL,global_work_size,NULL,0,NULL,NULL);
445   if (status != CL_SUCCESS)
446     return(MagickFalse);
447   length=image->columns*image->rows;
448   status=clEnqueueReadBuffer(convolve_info->command_queue,
449     convolve_info->convolve_pixels,CL_TRUE,0,length*sizeof(CLPixelPacket),
450     convolve_pixels,0,NULL,NULL);
451   if (status != CL_SUCCESS)
452     return(MagickFalse);
453   status=clFinish(convolve_info->command_queue);
454   if (status != CL_SUCCESS)
455     return(MagickFalse);
456   return(MagickTrue);
457 }
458
459 static ConvolveInfo *GetConvolveInfo(const Image *image,const char *name,
460   const char *source,ExceptionInfo *exception)
461 {
462   char
463     options[MaxTextExtent];
464
465   cl_int
466     status;
467
468   ConvolveInfo
469     *convolve_info;
470
471   size_t
472     length,
473     lengths[] = { strlen(source) };
474
475   /*
476     Create OpenCL info.
477   */
478   convolve_info=(ConvolveInfo *) AcquireAlignedMemory(1,sizeof(*convolve_info));
479   if (convolve_info == (ConvolveInfo *) NULL)
480     {
481       (void) ThrowMagickException(exception,GetMagickModule(),
482         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
483       return((ConvolveInfo *) NULL);
484     }
485   (void) ResetMagickMemory(convolve_info,0,sizeof(*convolve_info));
486   /*
487     Create OpenCL context.
488   */
489   convolve_info->context=clCreateContextFromType((cl_context_properties *)
490     NULL,CL_DEVICE_TYPE_GPU,ConvolveNotify,exception,&status);
491   if ((convolve_info->context == (cl_context) NULL) || (status != CL_SUCCESS))
492     convolve_info->context=clCreateContextFromType((cl_context_properties *)
493       NULL,CL_DEVICE_TYPE_CPU,ConvolveNotify,exception,&status);
494   if ((convolve_info->context == (cl_context) NULL) || (status != CL_SUCCESS))
495     convolve_info->context=clCreateContextFromType((cl_context_properties *)
496       NULL,CL_DEVICE_TYPE_DEFAULT,ConvolveNotify,exception,&status);
497   if ((convolve_info->context == (cl_context) NULL) || (status != CL_SUCCESS))
498     {
499       (void) ThrowMagickException(exception,GetMagickModule(),DelegateWarning,
500         "failed to create OpenCL context","`%s' (%d)",image->filename,status);
501       DestroyConvolveInfo(convolve_info);
502       return((ConvolveInfo *) NULL);
503     }
504   /*
505     Detect OpenCL devices.
506   */
507   status=clGetContextInfo(convolve_info->context,CL_CONTEXT_DEVICES,0,NULL,
508     &length);
509   if ((status != CL_SUCCESS) || (length == 0))
510     {
511       DestroyConvolveInfo(convolve_info);
512       return((ConvolveInfo *) NULL);
513     }
514   convolve_info->devices=(cl_device_id *) AcquireMagickMemory(length);
515   if (convolve_info->devices == (cl_device_id *) NULL)
516     {
517       (void) ThrowMagickException(exception,GetMagickModule(),
518         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
519       DestroyConvolveInfo(convolve_info);
520       return((ConvolveInfo *) NULL);
521     }
522   status=clGetContextInfo(convolve_info->context,CL_CONTEXT_DEVICES,length,
523     convolve_info->devices,NULL);
524   if (status != CL_SUCCESS)
525     {
526       DestroyConvolveInfo(convolve_info);
527       return((ConvolveInfo *) NULL);
528     }
529   /*
530     Create OpenCL command queue.
531   */
532   convolve_info->command_queue=clCreateCommandQueue(convolve_info->context,
533     convolve_info->devices[0],0,&status);
534   if ((convolve_info->command_queue == (cl_command_queue) NULL) ||
535       (status != CL_SUCCESS))
536     {
537       DestroyConvolveInfo(convolve_info);
538       return((ConvolveInfo *) NULL);
539     }
540   /*
541     Build OpenCL program.
542   */
543   convolve_info->program=clCreateProgramWithSource(convolve_info->context,1,
544     &source,lengths,&status);
545   if ((convolve_info->program == (cl_program) NULL) || (status != CL_SUCCESS))
546     {
547       DestroyConvolveInfo(convolve_info);
548       return((ConvolveInfo *) NULL);
549     }
550   (void) FormatMagickString(options,MaxTextExtent,CLOptions,(double)
551     QuantumRange,MagickEpsilon);
552   status=clBuildProgram(convolve_info->program,1,convolve_info->devices,options,
553     NULL,NULL);
554   if ((convolve_info->program == (cl_program) NULL) || (status != CL_SUCCESS))
555     {
556       char
557         *log;
558
559       status=clGetProgramBuildInfo(convolve_info->program,
560         convolve_info->devices[0],CL_PROGRAM_BUILD_LOG,0,NULL,&length);
561       log=(char *) AcquireMagickMemory(length);
562       if (log == (char *) NULL)
563         {
564           DestroyConvolveInfo(convolve_info);
565           return((ConvolveInfo *) NULL);
566         }
567       status=clGetProgramBuildInfo(convolve_info->program,
568         convolve_info->devices[0],CL_PROGRAM_BUILD_LOG,length,log,&length);
569       (void) ThrowMagickException(exception,GetMagickModule(),DelegateWarning,
570         "failed to build OpenCL program","`%s' (%s)",image->filename,log);
571       log=DestroyString(log);
572       DestroyConvolveInfo(convolve_info);
573       return((ConvolveInfo *) NULL);
574     }
575   /*
576     Get a kernel object.
577   */
578   convolve_info->kernel=clCreateKernel(convolve_info->program,name,&status);
579   if ((convolve_info->kernel == (cl_kernel) NULL) || (status != CL_SUCCESS))
580     {
581       DestroyConvolveInfo(convolve_info);
582       return((ConvolveInfo *) NULL);
583     }
584   return(convolve_info);
585 }
586
587 #endif
588
589 MagickExport MagickBooleanType AccelerateConvolveImage(const Image *image,
590   const KernelInfo *kernel,Image *convolve_image,ExceptionInfo *exception)
591 {
592   assert(image != (Image *) NULL);
593   assert(image->signature == MagickSignature);
594   if (image->debug != MagickFalse)
595     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
596   assert(kernel != (KernelInfo *) NULL);
597   assert(kernel->signature == MagickSignature);
598   assert(convolve_image != (Image *) NULL);
599   assert(convolve_image->signature == MagickSignature);
600   assert(exception != (ExceptionInfo *) NULL);
601   assert(exception->signature == MagickSignature);
602   if ((image->storage_class != DirectClass) || 
603       (image->colorspace == CMYKColorspace))
604   if ((GetImageVirtualPixelMethod(image) != UndefinedVirtualPixelMethod) &&
605       (GetImageVirtualPixelMethod(image) != EdgeVirtualPixelMethod))
606     return(MagickFalse);
607 #if !defined(MAGICKCORE_OPENCL_SUPPORT)
608   return(MagickFalse);
609 #else
610   {
611     const void
612       *pixels;
613
614     ConvolveInfo
615       *convolve_info;
616
617     MagickBooleanType
618       status;
619
620     MagickSizeType
621       length;
622
623     void
624       *convolve_pixels;
625
626     convolve_info=GetConvolveInfo(image,"Convolve",ConvolveKernel,exception);
627     if (convolve_info == (ConvolveInfo *) NULL)
628       return(MagickFalse);
629     pixels=AcquirePixelCachePixels(image,&length,exception);
630     if (pixels == (const void *) NULL)
631       {
632         (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
633           "UnableToReadPixelCache","`%s'",image->filename);
634         convolve_info=DestroyConvolveInfo(convolve_info);
635         return(MagickFalse);
636       }
637     convolve_pixels=GetPixelCachePixels(convolve_image,&length,exception);
638     if (convolve_pixels == (void *) NULL)
639       {
640         (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
641           "UnableToReadPixelCache","`%s'",image->filename);
642         convolve_info=DestroyConvolveInfo(convolve_info);
643         return(MagickFalse);
644       }
645     status=BindConvolveParameters(convolve_info,image,pixels,kernel->values,
646       kernel->width,kernel->height,convolve_pixels);
647     if (status == MagickFalse)
648       {
649         DestroyConvolveBuffers(convolve_info);
650         convolve_info=DestroyConvolveInfo(convolve_info);
651         return(MagickFalse);
652       }
653     status=EnqueueConvolveKernel(convolve_info,image,pixels,kernel->values,
654       kernel->width,kernel->height,convolve_pixels);
655     if (status == MagickFalse)
656       {
657         DestroyConvolveBuffers(convolve_info);
658         convolve_info=DestroyConvolveInfo(convolve_info);
659         return(MagickFalse);
660       }
661     DestroyConvolveBuffers(convolve_info);
662     convolve_info=DestroyConvolveInfo(convolve_info);
663     return(MagickTrue);
664   }
665 #endif
666 }