blob: cec404017421e3f520f16ed07d7e451dcac23776 [file] [log] [blame]
cristy3f6d1482010-01-20 21:01:21 +00001/*
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 %
cristy0d127ab2010-05-14 23:29:46 +000016% John Cristy %
cristy3f6d1482010-01-20 21:01:21 +000017% January 2010 %
18% %
19% %
cristyfe676ee2013-11-18 13:03:38 +000020% Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization %
cristy3f6d1482010-01-20 21:01:21 +000021% 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%
cristy0d127ab2010-05-14 23:29:46 +000036% Morphology is the the application of various kernals, of any size and even
cristy3f6d1482010-01-20 21:01:21 +000037% 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
49/*
50 Include declarations.
51*/
cristy4c08aed2011-07-01 19:47:50 +000052#include "MagickCore/studio.h"
53#include "MagickCore/accelerate.h"
54#include "MagickCore/artifact.h"
cristy35f33492011-07-07 16:54:49 +000055#include "MagickCore/cache.h"
cristyd1dd6e42011-09-04 01:46:08 +000056#include "MagickCore/cache-private.h"
cristy4c08aed2011-07-01 19:47:50 +000057#include "MagickCore/cache-view.h"
58#include "MagickCore/color-private.h"
cristy7f866842013-07-11 01:15:58 +000059#include "MagickCore/delegate-private.h"
cristy4c08aed2011-07-01 19:47:50 +000060#include "MagickCore/enhance.h"
61#include "MagickCore/exception.h"
62#include "MagickCore/exception-private.h"
63#include "MagickCore/gem.h"
64#include "MagickCore/hashmap.h"
65#include "MagickCore/image.h"
66#include "MagickCore/image-private.h"
67#include "MagickCore/list.h"
68#include "MagickCore/memory_.h"
69#include "MagickCore/monitor-private.h"
70#include "MagickCore/accelerate.h"
71#include "MagickCore/option.h"
72#include "MagickCore/pixel-accessor.h"
73#include "MagickCore/prepress.h"
74#include "MagickCore/quantize.h"
75#include "MagickCore/registry.h"
76#include "MagickCore/semaphore.h"
77#include "MagickCore/splay-tree.h"
78#include "MagickCore/statistic.h"
79#include "MagickCore/string_.h"
80#include "MagickCore/string-private.h"
81#include "MagickCore/token.h"
cristy3f6d1482010-01-20 21:01:21 +000082
83/*
84%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
85% %
86% %
87% %
88% A c c e l e r a t e C o n v o l v e I m a g e %
89% %
90% %
91% %
92%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
93%
94% AccelerateConvolveImage() applies a custom convolution kernel to the image.
95% It is accelerated by taking advantage of speed-ups offered by executing in
96% concert across heterogeneous platforms consisting of CPUs, GPUs, and other
97% processors.
98%
99% The format of the AccelerateConvolveImage method is:
100%
101% Image *AccelerateConvolveImage(const Image *image,
cristy2be15382010-01-21 02:38:03 +0000102% const KernelInfo *kernel,Image *convolve_image,
cristy3f6d1482010-01-20 21:01:21 +0000103% ExceptionInfo *exception)
104%
105% A description of each parameter follows:
106%
107% o image: the image.
108%
109% o kernel: the convolution kernel.
110%
111% o convole_image: the convoleed image.
112%
113% o exception: return any errors or warnings in this structure.
114%
115*/
cristyd43a46b2010-01-21 02:13:41 +0000116
117#if defined(MAGICKCORE_OPENCL_SUPPORT)
118
119#if defined(MAGICKCORE_HDRI_SUPPORT)
120#define CLOptions "-DMAGICKCORE_HDRI_SUPPORT=1 -DCLQuantum=float " \
121 "-DCLPixelType=float4 -DQuantumRange=%g -DMagickEpsilon=%g"
cristy101ab702011-10-13 13:06:32 +0000122#define CLPixelInfo cl_float4
cristyd43a46b2010-01-21 02:13:41 +0000123#else
124#if (MAGICKCORE_QUANTUM_DEPTH == 8)
125#define CLOptions "-DCLQuantum=uchar -DCLPixelType=uchar4 " \
126 "-DQuantumRange=%g -DMagickEpsilon=%g"
cristy101ab702011-10-13 13:06:32 +0000127#define CLPixelInfo cl_uchar4
cristyd43a46b2010-01-21 02:13:41 +0000128#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
129#define CLOptions "-DCLQuantum=ushort -DCLPixelType=ushort4 " \
130 "-DQuantumRange=%g -DMagickEpsilon=%g"
cristy101ab702011-10-13 13:06:32 +0000131#define CLPixelInfo cl_ushort4
cristyd43a46b2010-01-21 02:13:41 +0000132#elif (MAGICKCORE_QUANTUM_DEPTH == 32)
133#define CLOptions "-DCLQuantum=uint -DCLPixelType=uint4 " \
134 "-DQuantumRange=%g -DMagickEpsilon=%g"
cristy101ab702011-10-13 13:06:32 +0000135#define CLPixelInfo cl_uint4
cristy4434d7b2011-09-01 18:19:57 +0000136#elif (MAGICKCORE_QUANTUM_DEPTH == 64)
cristybb503372010-05-27 20:51:26 +0000137#define CLOptions "-DCLQuantum=ussize_t -DCLPixelType=ussize_t4 " \
cristyd43a46b2010-01-21 02:13:41 +0000138 "-DQuantumRange=%g -DMagickEpsilon=%g"
cristy101ab702011-10-13 13:06:32 +0000139#define CLPixelInfo cl_ulong4
cristyd43a46b2010-01-21 02:13:41 +0000140#endif
141#endif
142
143typedef struct _ConvolveInfo
144{
145 cl_context
146 context;
147
148 cl_device_id
149 *devices;
150
151 cl_command_queue
152 command_queue;
153
154 cl_kernel
155 kernel;
156
157 cl_program
158 program;
159
160 cl_mem
161 pixels,
162 convolve_pixels;
163
cristy5f959472010-05-27 22:19:46 +0000164 cl_ulong
cristyd43a46b2010-01-21 02:13:41 +0000165 width,
166 height;
167
cristy966032e2011-09-12 19:12:00 +0000168 cl_uint
cristyd43a46b2010-01-21 02:13:41 +0000169 matte;
170
171 cl_mem
172 filter;
173} ConvolveInfo;
174
cristy1daf0e82011-09-26 18:10:05 +0000175static const char
cristyd43a46b2010-01-21 02:13:41 +0000176 *ConvolveKernel =
cristy5f959472010-05-27 22:19:46 +0000177 "static inline long ClampToCanvas(const long offset,const unsigned long range)\n"
cristyd43a46b2010-01-21 02:13:41 +0000178 "{\n"
179 " if (offset < 0L)\n"
180 " return(0L);\n"
181 " if (offset >= range)\n"
cristy5f959472010-05-27 22:19:46 +0000182 " return((long) (range-1L));\n"
cristyd43a46b2010-01-21 02:13:41 +0000183 " return(offset);\n"
184 "}\n"
185 "\n"
cristy1a2e2762011-09-13 00:31:55 +0000186 "static inline CLQuantum ClampToQuantum(const float value)\n"
cristyd43a46b2010-01-21 02:13:41 +0000187 "{\n"
188 "#if defined(MAGICKCORE_HDRI_SUPPORT)\n"
cristyc9ab8902012-01-08 13:44:42 +0000189 " return((CLQuantum) value);\n"
cristyd43a46b2010-01-21 02:13:41 +0000190 "#else\n"
191 " if (value < 0.0)\n"
192 " return((CLQuantum) 0);\n"
cristy1a2e2762011-09-13 00:31:55 +0000193 " if (value >= (float) QuantumRange)\n"
cristyd43a46b2010-01-21 02:13:41 +0000194 " return((CLQuantum) QuantumRange);\n"
195 " return((CLQuantum) (value+0.5));\n"
196 "#endif\n"
197 "}\n"
198 "\n"
cristy3e3ec3a2012-11-03 23:11:06 +0000199 "static inline float PerceptibleReciprocal(const float x)\n"
cristy2d5be002012-08-05 12:11:45 +0000200 "{\n"
201 " float sign = x < (float) 0.0 ? (float) -1.0 : (float) 1.0;\n"
202 " return((sign*x) >= MagickEpsilon ? (float) 1.0/x : sign*((float) 1.0/\n"
203 " MagickEpsilon));\n"
204 "}\n"
205 "\n"
cristyd43a46b2010-01-21 02:13:41 +0000206 "__kernel void Convolve(const __global CLPixelType *input,\n"
cristy1a2e2762011-09-13 00:31:55 +0000207 " __constant float *filter,const unsigned long width,const unsigned long height,\n"
cristy966032e2011-09-12 19:12:00 +0000208 " const unsigned int matte,__global CLPixelType *output)\n"
cristyd43a46b2010-01-21 02:13:41 +0000209 "{\n"
cristy5f959472010-05-27 22:19:46 +0000210 " const unsigned long columns = get_global_size(0);\n"
211 " const unsigned long rows = get_global_size(1);\n"
cristyd43a46b2010-01-21 02:13:41 +0000212 "\n"
cristy5f959472010-05-27 22:19:46 +0000213 " const long x = get_global_id(0);\n"
214 " const long y = get_global_id(1);\n"
cristyd43a46b2010-01-21 02:13:41 +0000215 "\n"
cristy1a2e2762011-09-13 00:31:55 +0000216 " const float scale = (1.0/QuantumRange);\n"
cristy5f959472010-05-27 22:19:46 +0000217 " const long mid_width = (width-1)/2;\n"
218 " const long mid_height = (height-1)/2;\n"
cristy1a2e2762011-09-13 00:31:55 +0000219 " float4 sum = { 0.0, 0.0, 0.0, 0.0 };\n"
220 " float gamma = 0.0;\n"
cristy5f959472010-05-27 22:19:46 +0000221 " register unsigned long i = 0;\n"
cristyd43a46b2010-01-21 02:13:41 +0000222 "\n"
223 " int method = 0;\n"
224 " if (matte != false)\n"
225 " method=1;\n"
226 " if ((x >= width) && (x < (columns-width-1)) &&\n"
227 " (y >= height) && (y < (rows-height-1)))\n"
228 " {\n"
229 " method=2;\n"
230 " if (matte != false)\n"
231 " method=3;\n"
232 " }\n"
233 " switch (method)\n"
234 " {\n"
235 " case 0:\n"
236 " {\n"
cristy5f959472010-05-27 22:19:46 +0000237 " for (long v=(-mid_height); v <= mid_height; v++)\n"
cristyd43a46b2010-01-21 02:13:41 +0000238 " {\n"
cristy5f959472010-05-27 22:19:46 +0000239 " for (long u=(-mid_width); u <= mid_width; u++)\n"
cristyd43a46b2010-01-21 02:13:41 +0000240 " {\n"
cristy5f959472010-05-27 22:19:46 +0000241 " const long index=ClampToCanvas(y+v,rows)*columns+\n"
cristyd43a46b2010-01-21 02:13:41 +0000242 " ClampToCanvas(x+u,columns);\n"
243 " sum.x+=filter[i]*input[index].x;\n"
244 " sum.y+=filter[i]*input[index].y;\n"
245 " sum.z+=filter[i]*input[index].z;\n"
246 " gamma+=filter[i];\n"
247 " i++;\n"
248 " }\n"
249 " }\n"
250 " break;\n"
251 " }\n"
252 " case 1:\n"
253 " {\n"
cristy5f959472010-05-27 22:19:46 +0000254 " for (long v=(-mid_height); v <= mid_height; v++)\n"
cristyd43a46b2010-01-21 02:13:41 +0000255 " {\n"
cristy5f959472010-05-27 22:19:46 +0000256 " for (long u=(-mid_width); u <= mid_width; u++)\n"
cristyd43a46b2010-01-21 02:13:41 +0000257 " {\n"
cristy5f959472010-05-27 22:19:46 +0000258 " const unsigned long index=ClampToCanvas(y+v,rows)*columns+\n"
cristyd43a46b2010-01-21 02:13:41 +0000259 " ClampToCanvas(x+u,columns);\n"
cristy1a2e2762011-09-13 00:31:55 +0000260 " const float alpha=scale*input[index].w;\n"
cristyd43a46b2010-01-21 02:13:41 +0000261 " sum.x+=alpha*filter[i]*input[index].x;\n"
262 " sum.y+=alpha*filter[i]*input[index].y;\n"
263 " sum.z+=alpha*filter[i]*input[index].z;\n"
264 " sum.w+=filter[i]*input[index].w;\n"
265 " gamma+=alpha*filter[i];\n"
266 " i++;\n"
267 " }\n"
268 " }\n"
269 " break;\n"
270 " }\n"
271 " case 2:\n"
272 " {\n"
cristy5f959472010-05-27 22:19:46 +0000273 " for (long v=(-mid_height); v <= mid_height; v++)\n"
cristyd43a46b2010-01-21 02:13:41 +0000274 " {\n"
cristy5f959472010-05-27 22:19:46 +0000275 " for (long u=(-mid_width); u <= mid_width; u++)\n"
cristyd43a46b2010-01-21 02:13:41 +0000276 " {\n"
cristy5f959472010-05-27 22:19:46 +0000277 " const unsigned long index=(y+v)*columns+(x+u);\n"
cristyd43a46b2010-01-21 02:13:41 +0000278 " sum.x+=filter[i]*input[index].x;\n"
279 " sum.y+=filter[i]*input[index].y;\n"
280 " sum.z+=filter[i]*input[index].z;\n"
281 " gamma+=filter[i];\n"
282 " i++;\n"
283 " }\n"
284 " }\n"
285 " break;\n"
286 " }\n"
287 " case 3:\n"
288 " {\n"
cristy5f959472010-05-27 22:19:46 +0000289 " for (long v=(-mid_height); v <= mid_height; v++)\n"
cristyd43a46b2010-01-21 02:13:41 +0000290 " {\n"
cristy5f959472010-05-27 22:19:46 +0000291 " for (long u=(-mid_width); u <= mid_width; u++)\n"
cristyd43a46b2010-01-21 02:13:41 +0000292 " {\n"
cristy5f959472010-05-27 22:19:46 +0000293 " const unsigned long index=(y+v)*columns+(x+u);\n"
cristy1a2e2762011-09-13 00:31:55 +0000294 " const float alpha=scale*input[index].w;\n"
cristyd43a46b2010-01-21 02:13:41 +0000295 " sum.x+=alpha*filter[i]*input[index].x;\n"
296 " sum.y+=alpha*filter[i]*input[index].y;\n"
297 " sum.z+=alpha*filter[i]*input[index].z;\n"
298 " sum.w+=filter[i]*input[index].w;\n"
299 " gamma+=alpha*filter[i];\n"
300 " i++;\n"
301 " }\n"
302 " }\n"
303 " break;\n"
304 " }\n"
305 " }\n"
cristy3e3ec3a2012-11-03 23:11:06 +0000306 " gamma=PerceptibleReciprocal(gamma);\n"
cristy5f959472010-05-27 22:19:46 +0000307 " const unsigned long index = y*columns+x;\n"
cristyd43a46b2010-01-21 02:13:41 +0000308 " output[index].x=ClampToQuantum(gamma*sum.x);\n"
309 " output[index].y=ClampToQuantum(gamma*sum.y);\n"
310 " output[index].z=ClampToQuantum(gamma*sum.z);\n"
311 " if (matte == false)\n"
312 " output[index].w=input[index].w;\n"
313 " else\n"
314 " output[index].w=ClampToQuantum(sum.w);\n"
315 "}\n";
316
cristy7f866842013-07-11 01:15:58 +0000317static MagickDLLCall void ConvolveNotify(const char *message,const void *data,
318 size_t length,void *user_context)
cristyd43a46b2010-01-21 02:13:41 +0000319{
320 ExceptionInfo
321 *exception;
322
323 (void) data;
324 (void) length;
325 exception=(ExceptionInfo *) user_context;
cristy32cca402010-01-23 04:02:23 +0000326 (void) ThrowMagickException(exception,GetMagickModule(),DelegateWarning,
cristyefe601c2013-01-05 17:51:12 +0000327 "DelegateFailed","`%s'",message);
cristyd43a46b2010-01-21 02:13:41 +0000328}
329
330static MagickBooleanType BindConvolveParameters(ConvolveInfo *convolve_info,
cristy1a2e2762011-09-13 00:31:55 +0000331 const Image *image,const void *pixels,float *filter,const size_t width,
cristyc8523c12011-09-13 00:02:53 +0000332 const size_t height,void *convolve_pixels)
cristyd43a46b2010-01-21 02:13:41 +0000333{
334 cl_int
335 status;
336
cristy5f959472010-05-27 22:19:46 +0000337 register cl_uint
cristyd43a46b2010-01-21 02:13:41 +0000338 i;
339
340 size_t
341 length;
342
343 /*
344 Allocate OpenCL buffers.
345 */
346 length=image->columns*image->rows;
cristy5f959472010-05-27 22:19:46 +0000347 convolve_info->pixels=clCreateBuffer(convolve_info->context,(cl_mem_flags)
cristy101ab702011-10-13 13:06:32 +0000348 (CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR),length*sizeof(CLPixelInfo),
cristy5f959472010-05-27 22:19:46 +0000349 (void *) pixels,&status);
cristyd43a46b2010-01-21 02:13:41 +0000350 if ((convolve_info->pixels == (cl_mem) NULL) || (status != CL_SUCCESS))
351 return(MagickFalse);
352 length=width*height;
cristy5f959472010-05-27 22:19:46 +0000353 convolve_info->filter=clCreateBuffer(convolve_info->context,(cl_mem_flags)
cristy1a2e2762011-09-13 00:31:55 +0000354 (CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR),length*sizeof(cl_float),filter,
cristy5f959472010-05-27 22:19:46 +0000355 &status);
cristyd43a46b2010-01-21 02:13:41 +0000356 if ((convolve_info->filter == (cl_mem) NULL) || (status != CL_SUCCESS))
357 return(MagickFalse);
358 length=image->columns*image->rows;
359 convolve_info->convolve_pixels=clCreateBuffer(convolve_info->context,
cristy5f959472010-05-27 22:19:46 +0000360 (cl_mem_flags) (CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR),length*
cristy101ab702011-10-13 13:06:32 +0000361 sizeof(CLPixelInfo),convolve_pixels,&status);
cristyd43a46b2010-01-21 02:13:41 +0000362 if ((convolve_info->convolve_pixels == (cl_mem) NULL) ||
363 (status != CL_SUCCESS))
364 return(MagickFalse);
365 /*
366 Bind OpenCL buffers.
367 */
368 i=0;
369 status=clSetKernelArg(convolve_info->kernel,i++,sizeof(cl_mem),(void *)
370 &convolve_info->pixels);
371 if (status != CL_SUCCESS)
372 return(MagickFalse);
373 status=clSetKernelArg(convolve_info->kernel,i++,sizeof(cl_mem),(void *)
374 &convolve_info->filter);
375 if (status != CL_SUCCESS)
376 return(MagickFalse);
cristy5f959472010-05-27 22:19:46 +0000377 convolve_info->width=(cl_ulong) width;
378 status=clSetKernelArg(convolve_info->kernel,i++,sizeof(cl_ulong),(void *)
cristyd43a46b2010-01-21 02:13:41 +0000379 &convolve_info->width);
380 if (status != CL_SUCCESS)
381 return(MagickFalse);
cristy5f959472010-05-27 22:19:46 +0000382 convolve_info->height=(cl_ulong) height;
383 status=clSetKernelArg(convolve_info->kernel,i++,sizeof(cl_ulong),(void *)
cristyd43a46b2010-01-21 02:13:41 +0000384 &convolve_info->height);
385 if (status != CL_SUCCESS)
386 return(MagickFalse);
cristy644d5d02012-08-29 11:20:44 +0000387 convolve_info->matte=(cl_uint) image->alpha_trait == BlendPixelTrait ?
388 MagickTrue : MagickFalse;
cristy966032e2011-09-12 19:12:00 +0000389 status=clSetKernelArg(convolve_info->kernel,i++,sizeof(cl_uint),(void *)
cristy644d5d02012-08-29 11:20:44 +0000390 &convolve_info->matte);
cristyd43a46b2010-01-21 02:13:41 +0000391 if (status != CL_SUCCESS)
392 return(MagickFalse);
393 status=clSetKernelArg(convolve_info->kernel,i++,sizeof(cl_mem),(void *)
394 &convolve_info->convolve_pixels);
395 if (status != CL_SUCCESS)
396 return(MagickFalse);
397 status=clFinish(convolve_info->command_queue);
398 if (status != CL_SUCCESS)
399 return(MagickFalse);
400 return(MagickTrue);
401}
402
403static void DestroyConvolveBuffers(ConvolveInfo *convolve_info)
404{
405 cl_int
406 status;
407
cristy9f027d12011-09-21 01:17:17 +0000408 status=0;
cristyd43a46b2010-01-21 02:13:41 +0000409 if (convolve_info->convolve_pixels != (cl_mem) NULL)
410 status=clReleaseMemObject(convolve_info->convolve_pixels);
411 if (convolve_info->pixels != (cl_mem) NULL)
412 status=clReleaseMemObject(convolve_info->pixels);
413 if (convolve_info->filter != (cl_mem) NULL)
414 status=clReleaseMemObject(convolve_info->filter);
cristyaa83c2c2011-09-21 13:36:25 +0000415 (void) status;
cristyd43a46b2010-01-21 02:13:41 +0000416}
417
418static ConvolveInfo *DestroyConvolveInfo(ConvolveInfo *convolve_info)
419{
420 cl_int
421 status;
422
cristy9f027d12011-09-21 01:17:17 +0000423 status=0;
cristyd43a46b2010-01-21 02:13:41 +0000424 if (convolve_info->kernel != (cl_kernel) NULL)
425 status=clReleaseKernel(convolve_info->kernel);
426 if (convolve_info->program != (cl_program) NULL)
427 status=clReleaseProgram(convolve_info->program);
428 if (convolve_info->command_queue != (cl_command_queue) NULL)
429 status=clReleaseCommandQueue(convolve_info->command_queue);
430 if (convolve_info->context != (cl_context) NULL)
431 status=clReleaseContext(convolve_info->context);
cristyaa83c2c2011-09-21 13:36:25 +0000432 (void) status;
cristyd43a46b2010-01-21 02:13:41 +0000433 convolve_info=(ConvolveInfo *) RelinquishMagickMemory(convolve_info);
434 return(convolve_info);
435}
436
437static MagickBooleanType EnqueueConvolveKernel(ConvolveInfo *convolve_info,
cristy1a2e2762011-09-13 00:31:55 +0000438 const Image *image,const void *pixels,float *filter,const size_t width,
cristyc8523c12011-09-13 00:02:53 +0000439 const size_t height,void *convolve_pixels)
cristyd43a46b2010-01-21 02:13:41 +0000440{
441 cl_int
442 status;
443
444 size_t
445 global_work_size[2],
446 length;
447
448 length=image->columns*image->rows;
449 status=clEnqueueWriteBuffer(convolve_info->command_queue,
cristy101ab702011-10-13 13:06:32 +0000450 convolve_info->pixels,CL_TRUE,0,length*sizeof(CLPixelInfo),pixels,0,NULL,
cristyd43a46b2010-01-21 02:13:41 +0000451 NULL);
452 length=width*height;
453 status=clEnqueueWriteBuffer(convolve_info->command_queue,
cristy1a2e2762011-09-13 00:31:55 +0000454 convolve_info->filter,CL_TRUE,0,length*sizeof(cl_float),filter,0,NULL,
cristyd43a46b2010-01-21 02:13:41 +0000455 NULL);
456 if (status != CL_SUCCESS)
457 return(MagickFalse);
458 global_work_size[0]=image->columns;
459 global_work_size[1]=image->rows;
460 status=clEnqueueNDRangeKernel(convolve_info->command_queue,
461 convolve_info->kernel,2,NULL,global_work_size,NULL,0,NULL,NULL);
462 if (status != CL_SUCCESS)
463 return(MagickFalse);
464 length=image->columns*image->rows;
465 status=clEnqueueReadBuffer(convolve_info->command_queue,
cristy101ab702011-10-13 13:06:32 +0000466 convolve_info->convolve_pixels,CL_TRUE,0,length*sizeof(CLPixelInfo),
cristyd43a46b2010-01-21 02:13:41 +0000467 convolve_pixels,0,NULL,NULL);
468 if (status != CL_SUCCESS)
469 return(MagickFalse);
470 status=clFinish(convolve_info->command_queue);
471 if (status != CL_SUCCESS)
472 return(MagickFalse);
473 return(MagickTrue);
474}
475
476static ConvolveInfo *GetConvolveInfo(const Image *image,const char *name,
477 const char *source,ExceptionInfo *exception)
478{
479 char
480 options[MaxTextExtent];
481
cristy966032e2011-09-12 19:12:00 +0000482 cl_context_properties
483 context_properties[3];
484
cristyd43a46b2010-01-21 02:13:41 +0000485 cl_int
486 status;
487
cristy966032e2011-09-12 19:12:00 +0000488 cl_platform_id
489 platforms[1];
490
491 cl_uint
492 number_platforms;
493
cristyd43a46b2010-01-21 02:13:41 +0000494 ConvolveInfo
495 *convolve_info;
496
497 size_t
498 length,
499 lengths[] = { strlen(source) };
500
501 /*
502 Create OpenCL info.
503 */
cristy73bd4a52010-10-05 11:24:23 +0000504 convolve_info=(ConvolveInfo *) AcquireMagickMemory(sizeof(*convolve_info));
cristyd43a46b2010-01-21 02:13:41 +0000505 if (convolve_info == (ConvolveInfo *) NULL)
506 {
507 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000508 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristyd43a46b2010-01-21 02:13:41 +0000509 return((ConvolveInfo *) NULL);
510 }
511 (void) ResetMagickMemory(convolve_info,0,sizeof(*convolve_info));
512 /*
513 Create OpenCL context.
514 */
cristy61b76e62011-09-13 12:04:12 +0000515 status=clGetPlatformIDs(0,(cl_platform_id *) NULL,&number_platforms);
516 if ((status == CL_SUCCESS) && (number_platforms > 0))
cristy966032e2011-09-12 19:12:00 +0000517 status=clGetPlatformIDs(1,platforms,NULL);
518 if (status != CL_SUCCESS)
519 {
520 (void) ThrowMagickException(exception,GetMagickModule(),DelegateWarning,
anthonye5b39652012-04-21 05:37:29 +0000521 "failed to create OpenCL context","'%s' (%d)",image->filename,status);
cristy966032e2011-09-12 19:12:00 +0000522 convolve_info=DestroyConvolveInfo(convolve_info);
523 return((ConvolveInfo *) NULL);
524 }
525 context_properties[0]=CL_CONTEXT_PLATFORM;
526 context_properties[1]=(cl_context_properties) platforms[0];
527 context_properties[2]=0;
528 convolve_info->context=clCreateContextFromType(context_properties,
529 (cl_device_type) CL_DEVICE_TYPE_GPU,ConvolveNotify,exception,&status);
cristyd43a46b2010-01-21 02:13:41 +0000530 if ((convolve_info->context == (cl_context) NULL) || (status != CL_SUCCESS))
cristy966032e2011-09-12 19:12:00 +0000531 convolve_info->context=clCreateContextFromType(context_properties,
532 (cl_device_type) CL_DEVICE_TYPE_CPU,ConvolveNotify,exception,&status);
cristyd43a46b2010-01-21 02:13:41 +0000533 if ((convolve_info->context == (cl_context) NULL) || (status != CL_SUCCESS))
cristy966032e2011-09-12 19:12:00 +0000534 convolve_info->context=clCreateContextFromType(context_properties,
535 (cl_device_type) CL_DEVICE_TYPE_DEFAULT,ConvolveNotify,exception,&status);
cristyd43a46b2010-01-21 02:13:41 +0000536 if ((convolve_info->context == (cl_context) NULL) || (status != CL_SUCCESS))
537 {
cristy32cca402010-01-23 04:02:23 +0000538 (void) ThrowMagickException(exception,GetMagickModule(),DelegateWarning,
anthonye5b39652012-04-21 05:37:29 +0000539 "failed to create OpenCL context","'%s' (%d)",image->filename,status);
cristy5f959472010-05-27 22:19:46 +0000540 convolve_info=DestroyConvolveInfo(convolve_info);
cristyd43a46b2010-01-21 02:13:41 +0000541 return((ConvolveInfo *) NULL);
542 }
543 /*
544 Detect OpenCL devices.
545 */
546 status=clGetContextInfo(convolve_info->context,CL_CONTEXT_DEVICES,0,NULL,
547 &length);
548 if ((status != CL_SUCCESS) || (length == 0))
549 {
cristy5f959472010-05-27 22:19:46 +0000550 convolve_info=DestroyConvolveInfo(convolve_info);
cristyd43a46b2010-01-21 02:13:41 +0000551 return((ConvolveInfo *) NULL);
552 }
553 convolve_info->devices=(cl_device_id *) AcquireMagickMemory(length);
554 if (convolve_info->devices == (cl_device_id *) NULL)
555 {
556 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000557 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy5f959472010-05-27 22:19:46 +0000558 convolve_info=DestroyConvolveInfo(convolve_info);
cristyd43a46b2010-01-21 02:13:41 +0000559 return((ConvolveInfo *) NULL);
560 }
561 status=clGetContextInfo(convolve_info->context,CL_CONTEXT_DEVICES,length,
562 convolve_info->devices,NULL);
563 if (status != CL_SUCCESS)
564 {
cristy5f959472010-05-27 22:19:46 +0000565 convolve_info=DestroyConvolveInfo(convolve_info);
cristyd43a46b2010-01-21 02:13:41 +0000566 return((ConvolveInfo *) NULL);
567 }
cristydc9c80d2011-10-26 23:47:02 +0000568 if (image->debug != MagickFalse)
569 {
570 char
571 attribute[MaxTextExtent];
572
573 size_t
574 length;
575
576 clGetDeviceInfo(convolve_info->devices[0],CL_DEVICE_NAME,
577 sizeof(attribute),attribute,&length);
578 (void) LogMagickEvent(AccelerateEvent,GetMagickModule(),"Name: %s",
579 attribute);
580 clGetDeviceInfo(convolve_info->devices[0],CL_DEVICE_VENDOR,
581 sizeof(attribute),attribute,&length);
582 (void) LogMagickEvent(AccelerateEvent,GetMagickModule(),"Vendor: %s",
583 attribute);
584 clGetDeviceInfo(convolve_info->devices[0],CL_DEVICE_VERSION,
585 sizeof(attribute),attribute,&length);
586 (void) LogMagickEvent(AccelerateEvent,GetMagickModule(),
587 "Driver Version: %s",attribute);
588 clGetDeviceInfo(convolve_info->devices[0],CL_DEVICE_PROFILE,
589 sizeof(attribute),attribute,&length);
590 (void) LogMagickEvent(AccelerateEvent,GetMagickModule(),"Profile: %s",
591 attribute);
592 clGetDeviceInfo(convolve_info->devices[0],CL_DRIVER_VERSION,
593 sizeof(attribute),attribute,&length);
594 (void) LogMagickEvent(AccelerateEvent,GetMagickModule(),"Driver: %s",
595 attribute);
596 clGetDeviceInfo(convolve_info->devices[0],CL_DEVICE_EXTENSIONS,
597 sizeof(attribute),attribute,&length);
598 (void) LogMagickEvent(AccelerateEvent,GetMagickModule(),"Extensions: %s",
599 attribute);
600 }
cristyd43a46b2010-01-21 02:13:41 +0000601 /*
602 Create OpenCL command queue.
603 */
604 convolve_info->command_queue=clCreateCommandQueue(convolve_info->context,
605 convolve_info->devices[0],0,&status);
606 if ((convolve_info->command_queue == (cl_command_queue) NULL) ||
607 (status != CL_SUCCESS))
608 {
cristy5f959472010-05-27 22:19:46 +0000609 convolve_info=DestroyConvolveInfo(convolve_info);
cristyd43a46b2010-01-21 02:13:41 +0000610 return((ConvolveInfo *) NULL);
611 }
612 /*
613 Build OpenCL program.
614 */
615 convolve_info->program=clCreateProgramWithSource(convolve_info->context,1,
616 &source,lengths,&status);
617 if ((convolve_info->program == (cl_program) NULL) || (status != CL_SUCCESS))
618 {
cristy5f959472010-05-27 22:19:46 +0000619 convolve_info=DestroyConvolveInfo(convolve_info);
cristyd43a46b2010-01-21 02:13:41 +0000620 return((ConvolveInfo *) NULL);
621 }
cristy1a2e2762011-09-13 00:31:55 +0000622 (void) FormatLocaleString(options,MaxTextExtent,CLOptions,(float)
cristyd43a46b2010-01-21 02:13:41 +0000623 QuantumRange,MagickEpsilon);
cristy00243d12010-01-21 02:45:27 +0000624 status=clBuildProgram(convolve_info->program,1,convolve_info->devices,options,
625 NULL,NULL);
cristyd43a46b2010-01-21 02:13:41 +0000626 if ((convolve_info->program == (cl_program) NULL) || (status != CL_SUCCESS))
627 {
628 char
629 *log;
630
631 status=clGetProgramBuildInfo(convolve_info->program,
632 convolve_info->devices[0],CL_PROGRAM_BUILD_LOG,0,NULL,&length);
633 log=(char *) AcquireMagickMemory(length);
634 if (log == (char *) NULL)
635 {
cristy5f959472010-05-27 22:19:46 +0000636 convolve_info=DestroyConvolveInfo(convolve_info);
cristyd43a46b2010-01-21 02:13:41 +0000637 return((ConvolveInfo *) NULL);
638 }
639 status=clGetProgramBuildInfo(convolve_info->program,
640 convolve_info->devices[0],CL_PROGRAM_BUILD_LOG,length,log,&length);
cristy32cca402010-01-23 04:02:23 +0000641 (void) ThrowMagickException(exception,GetMagickModule(),DelegateWarning,
anthonye5b39652012-04-21 05:37:29 +0000642 "failed to build OpenCL program","'%s' (%s)",image->filename,log);
cristyd43a46b2010-01-21 02:13:41 +0000643 log=DestroyString(log);
cristy5f959472010-05-27 22:19:46 +0000644 convolve_info=DestroyConvolveInfo(convolve_info);
cristyd43a46b2010-01-21 02:13:41 +0000645 return((ConvolveInfo *) NULL);
646 }
647 /*
648 Get a kernel object.
649 */
650 convolve_info->kernel=clCreateKernel(convolve_info->program,name,&status);
651 if ((convolve_info->kernel == (cl_kernel) NULL) || (status != CL_SUCCESS))
652 {
cristy5f959472010-05-27 22:19:46 +0000653 convolve_info=DestroyConvolveInfo(convolve_info);
cristyd43a46b2010-01-21 02:13:41 +0000654 return((ConvolveInfo *) NULL);
655 }
656 return(convolve_info);
657}
658
659#endif
660
cristy3f6d1482010-01-20 21:01:21 +0000661MagickExport MagickBooleanType AccelerateConvolveImage(const Image *image,
cristy2be15382010-01-21 02:38:03 +0000662 const KernelInfo *kernel,Image *convolve_image,ExceptionInfo *exception)
cristy3f6d1482010-01-20 21:01:21 +0000663{
664 assert(image != (Image *) NULL);
665 assert(image->signature == MagickSignature);
666 if (image->debug != MagickFalse)
667 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy2be15382010-01-21 02:38:03 +0000668 assert(kernel != (KernelInfo *) NULL);
cristyd43a46b2010-01-21 02:13:41 +0000669 assert(kernel->signature == MagickSignature);
cristy3f6d1482010-01-20 21:01:21 +0000670 assert(convolve_image != (Image *) NULL);
671 assert(convolve_image->signature == MagickSignature);
672 assert(exception != (ExceptionInfo *) NULL);
673 assert(exception->signature == MagickSignature);
cristyaeded782012-09-11 23:39:36 +0000674 if ((image->storage_class != DirectClass) ||
cristy394651a2010-01-23 21:05:55 +0000675 (image->colorspace == CMYKColorspace))
cristybfa02fa2011-09-13 00:40:44 +0000676 return(MagickFalse);
cristyd43a46b2010-01-21 02:13:41 +0000677 if ((GetImageVirtualPixelMethod(image) != UndefinedVirtualPixelMethod) &&
678 (GetImageVirtualPixelMethod(image) != EdgeVirtualPixelMethod))
679 return(MagickFalse);
cristyb572edf2011-09-13 12:25:22 +0000680 if (GetPixelChannels(image) != 4)
681 return(MagickFalse);
cristyd43a46b2010-01-21 02:13:41 +0000682#if !defined(MAGICKCORE_OPENCL_SUPPORT)
cristy3f6d1482010-01-20 21:01:21 +0000683 return(MagickFalse);
cristyd43a46b2010-01-21 02:13:41 +0000684#else
685 {
686 const void
687 *pixels;
688
cristy1a2e2762011-09-13 00:31:55 +0000689 float
690 *filter;
691
cristyd43a46b2010-01-21 02:13:41 +0000692 ConvolveInfo
693 *convolve_info;
694
695 MagickBooleanType
696 status;
697
698 MagickSizeType
699 length;
700
cristy1a2e2762011-09-13 00:31:55 +0000701 register ssize_t
702 i;
703
cristyd43a46b2010-01-21 02:13:41 +0000704 void
705 *convolve_pixels;
706
cristyd43a46b2010-01-21 02:13:41 +0000707 convolve_info=GetConvolveInfo(image,"Convolve",ConvolveKernel,exception);
708 if (convolve_info == (ConvolveInfo *) NULL)
709 return(MagickFalse);
710 pixels=AcquirePixelCachePixels(image,&length,exception);
711 if (pixels == (const void *) NULL)
712 {
cristy1a2e2762011-09-13 00:31:55 +0000713 convolve_info=DestroyConvolveInfo(convolve_info);
cristyd43a46b2010-01-21 02:13:41 +0000714 (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
cristyefe601c2013-01-05 17:51:12 +0000715 "UnableToReadPixelCache","`%s'",image->filename);
cristyd43a46b2010-01-21 02:13:41 +0000716 return(MagickFalse);
717 }
718 convolve_pixels=GetPixelCachePixels(convolve_image,&length,exception);
719 if (convolve_pixels == (void *) NULL)
720 {
cristy1a2e2762011-09-13 00:31:55 +0000721 convolve_info=DestroyConvolveInfo(convolve_info);
cristyd43a46b2010-01-21 02:13:41 +0000722 (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
cristyefe601c2013-01-05 17:51:12 +0000723 "UnableToReadPixelCache","`%s'",image->filename);
cristyd43a46b2010-01-21 02:13:41 +0000724 return(MagickFalse);
725 }
cristy1a2e2762011-09-13 00:31:55 +0000726 filter=(float *) AcquireQuantumMemory(kernel->width,kernel->height*
727 sizeof(*filter));
728 if (filter == (float *) NULL)
729 {
730 DestroyConvolveBuffers(convolve_info);
731 convolve_info=DestroyConvolveInfo(convolve_info);
732 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000733 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy1a2e2762011-09-13 00:31:55 +0000734 return(MagickFalse);
735 }
736 for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
737 filter[i]=(float) kernel->values[i];
738 status=BindConvolveParameters(convolve_info,image,pixels,filter,
cristyd43a46b2010-01-21 02:13:41 +0000739 kernel->width,kernel->height,convolve_pixels);
740 if (status == MagickFalse)
741 {
cristy1a2e2762011-09-13 00:31:55 +0000742 filter=(float *) RelinquishMagickMemory(filter);
cristyd43a46b2010-01-21 02:13:41 +0000743 DestroyConvolveBuffers(convolve_info);
744 convolve_info=DestroyConvolveInfo(convolve_info);
745 return(MagickFalse);
746 }
cristy1a2e2762011-09-13 00:31:55 +0000747 status=EnqueueConvolveKernel(convolve_info,image,pixels,filter,
cristyd43a46b2010-01-21 02:13:41 +0000748 kernel->width,kernel->height,convolve_pixels);
cristy1a2e2762011-09-13 00:31:55 +0000749 filter=(float *) RelinquishMagickMemory(filter);
cristyd43a46b2010-01-21 02:13:41 +0000750 if (status == MagickFalse)
751 {
752 DestroyConvolveBuffers(convolve_info);
753 convolve_info=DestroyConvolveInfo(convolve_info);
754 return(MagickFalse);
755 }
756 DestroyConvolveBuffers(convolve_info);
757 convolve_info=DestroyConvolveInfo(convolve_info);
758 return(MagickTrue);
759 }
760#endif
cristy3f6d1482010-01-20 21:01:21 +0000761}