blob: 54bcd83521faa35381be42d8b01e069203fdbd43 [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 %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristyf034abb2013-11-24 14:16:14 +000017% SiuChi Chan %
18% Guansong Zhang %
cristy3f6d1482010-01-20 21:01:21 +000019% January 2010 %
20% %
21% %
cristyfe676ee2013-11-18 13:03:38 +000022% Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization %
cristy3f6d1482010-01-20 21:01:21 +000023% 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%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
cristy3f6d1482010-01-20 21:01:21 +000037*/
cristyf034abb2013-11-24 14:16:14 +000038
cristy3f6d1482010-01-20 21:01:21 +000039/*
cristyf034abb2013-11-24 14:16:14 +000040Include declarations.
cristy3f6d1482010-01-20 21:01:21 +000041*/
cristy4c08aed2011-07-01 19:47:50 +000042#include "MagickCore/studio.h"
43#include "MagickCore/accelerate.h"
cristyf034abb2013-11-24 14:16:14 +000044#include "MagickCore/accelerate-private.h"
cristy4c08aed2011-07-01 19:47:50 +000045#include "MagickCore/artifact.h"
cristy35f33492011-07-07 16:54:49 +000046#include "MagickCore/cache.h"
cristyd1dd6e42011-09-04 01:46:08 +000047#include "MagickCore/cache-private.h"
cristy4c08aed2011-07-01 19:47:50 +000048#include "MagickCore/cache-view.h"
49#include "MagickCore/color-private.h"
cristy7f866842013-07-11 01:15:58 +000050#include "MagickCore/delegate-private.h"
cristy4c08aed2011-07-01 19:47:50 +000051#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"
cristyf034abb2013-11-24 14:16:14 +000062#include "MagickCore/opencl.h"
63#include "MagickCore/opencl-private.h"
cristy4c08aed2011-07-01 19:47:50 +000064#include "MagickCore/option.h"
dirk434ce8a2014-08-17 08:33:47 +000065#include "MagickCore/pixel-accessor.h"
cristyf034abb2013-11-24 14:16:14 +000066#include "MagickCore/pixel-private.h"
cristy4c08aed2011-07-01 19:47:50 +000067#include "MagickCore/prepress.h"
68#include "MagickCore/quantize.h"
dirk8a5cf512014-07-28 20:16:27 +000069#include "MagickCore/quantum-private.h"
cristye85d0f72013-11-27 02:25:43 +000070#include "MagickCore/random_.h"
71#include "MagickCore/random-private.h"
cristy4c08aed2011-07-01 19:47:50 +000072#include "MagickCore/registry.h"
cristyf034abb2013-11-24 14:16:14 +000073#include "MagickCore/resize.h"
74#include "MagickCore/resize-private.h"
cristy4c08aed2011-07-01 19:47:50 +000075#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"
cristyf034abb2013-11-24 14:16:14 +000081
82#ifdef MAGICKCORE_CLPERFMARKER
83#include "CLPerfMarker.h"
84#endif
85
cristye85d0f72013-11-27 02:25:43 +000086#define MAGICK_MAX(x,y) (((x) >= (y))?(x):(y))
87#define MAGICK_MIN(x,y) (((x) <= (y))?(x):(y))
88
cristyf034abb2013-11-24 14:16:14 +000089#if defined(MAGICKCORE_OPENCL_SUPPORT)
90
91#define ALIGNED(pointer,type) ((((long)(pointer)) & (sizeof(type)-1)) == 0)
cristyf034abb2013-11-24 14:16:14 +000092
cristy0c832c62014-03-07 22:21:04 +000093/* pad the global workgroup size to the next multiple of
94 the local workgroup size */
dirk832becc2014-08-04 19:44:34 +000095inline 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
dirk434ce8a2014-08-17 08:33:47 +0000156 /* 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
dirk832becc2014-08-04 19:44:34 +0000170 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=image->columns * image->rows * 4 * 4;
206
207 split = ((tempSize > allocSize) ? MagickTrue : MagickFalse);
208 return(split);
cristy0c832c62014-03-07 22:21:04 +0000209}
210
dirk8a5cf512014-07-28 20:16:27 +0000211/*
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*/
cristyf034abb2013-11-24 14:16:14 +0000242
dirk832becc2014-08-04 19:44:34 +0000243static 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;
cristyf034abb2013-11-24 14:16:14 +0000321 outputReady = MagickFalse;
322
323 clEnv = GetDefaultOpenCLEnv();
324 context = GetOpenCLContext(clEnv);
325
dirk832becc2014-08-04 19:44:34 +0000326 image_view=AcquireVirtualCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +0000327 inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
cristyf034abb2013-11-24 14:16:14 +0000328 if (inputPixels == (const void *) NULL)
329 {
dirk8a5cf512014-07-28 20:16:27 +0000330 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristyf034abb2013-11-24 14:16:14 +0000331 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 */
dirk832becc2014-08-04 19:44:34 +0000348 length = image->columns * image->rows;
dirk8a5cf512014-07-28 20:16:27 +0000349 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +0000350 if (clStatus != CL_SUCCESS)
351 {
cristy0c832c62014-03-07 22:21:04 +0000352 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +0000353 goto cleanup;
354 }
355
dirk8a5cf512014-07-28 20:16:27 +0000356 filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristyf034abb2013-11-24 14:16:14 +0000357 assert(filteredImage != NULL);
dirk8a5cf512014-07-28 20:16:27 +0000358 if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
cristyf034abb2013-11-24 14:16:14 +0000359 {
cristya22457d2013-12-07 14:03:06 +0000360 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000361 goto cleanup;
362 }
dirk832becc2014-08-04 19:44:34 +0000363 filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
dirk8a5cf512014-07-28 20:16:27 +0000364 filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
cristyf034abb2013-11-24 14:16:14 +0000365 if (filteredPixels == (void *) NULL)
366 {
cristya22457d2013-12-07 14:03:06 +0000367 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
cristyf034abb2013-11-24 14:16:14 +0000368 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 */
dirk8a5cf512014-07-28 20:16:27 +0000382 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +0000383 filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
cristyf034abb2013-11-24 14:16:14 +0000384 if (clStatus != CL_SUCCESS)
385 {
cristy0c832c62014-03-07 22:21:04 +0000386 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +0000387 goto cleanup;
388 }
389
390 kernelSize = kernel->width * kernel->height;
cristy0c832c62014-03-07 22:21:04 +0000391 convolutionKernel = clEnv->library->clCreateBuffer(context, CL_MEM_READ_ONLY|CL_MEM_ALLOC_HOST_PTR, kernelSize * sizeof(float), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +0000392 if (clStatus != CL_SUCCESS)
393 {
cristy0c832c62014-03-07 22:21:04 +0000394 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +0000395 goto cleanup;
396 }
397
398 queue = AcquireOpenCLCommandQueue(clEnv);
399
cristy0c832c62014-03-07 22:21:04 +0000400 kernelBufferPtr = (float*)clEnv->library->clEnqueueMapBuffer(queue, convolutionKernel, CL_TRUE, CL_MAP_WRITE, 0, kernelSize * sizeof(float)
cristyf034abb2013-11-24 14:16:14 +0000401 , 0, NULL, NULL, &clStatus);
402 if (clStatus != CL_SUCCESS)
403 {
cristy0c832c62014-03-07 22:21:04 +0000404 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueMapBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +0000405 goto cleanup;
406 }
407 for (i = 0; i < kernelSize; i++)
408 {
409 kernelBufferPtr[i] = (float) kernel->values[i];
410 }
cristy0c832c62014-03-07 22:21:04 +0000411 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, convolutionKernel, kernelBufferPtr, 0, NULL, NULL);
412 if (clStatus != CL_SUCCESS)
cristyf034abb2013-11-24 14:16:14 +0000413 {
cristy0c832c62014-03-07 22:21:04 +0000414 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000415 goto cleanup;
416 }
cristy0c832c62014-03-07 22:21:04 +0000417 clEnv->library->clFlush(queue);
418
419 deviceLocalMemorySize = GetOpenCLDeviceLocalMemorySize(clEnv);
cristyf034abb2013-11-24 14:16:14 +0000420
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);
cristy0c832c62014-03-07 22:21:04 +0000427
428 if (localMemoryRequirement > deviceLocalMemorySize)
cristyf034abb2013-11-24 14:16:14 +0000429 {
cristyf034abb2013-11-24 14:16:14 +0000430 localGroupSize[0] = 8;
431 localGroupSize[1] = 8;
cristyf034abb2013-11-24 14:16:14 +0000432 localMemoryRequirement = (localGroupSize[0]+kernel->width-1) * (localGroupSize[1]+kernel->height-1) * sizeof(CLPixelPacket)
433 + kernel->width*kernel->height*sizeof(float);
434 }
cristyf034abb2013-11-24 14:16:14 +0000435 if (localMemoryRequirement <= deviceLocalMemorySize)
436 {
437 /* get the OpenCL kernel */
cristya22457d2013-12-07 14:03:06 +0000438 clkernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "ConvolveOptimized");
cristyf034abb2013-11-24 14:16:14 +0000439 if (clkernel == NULL)
440 {
cristya22457d2013-12-07 14:03:06 +0000441 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000442 goto cleanup;
443 }
444
445 /* set the kernel arguments */
446 i = 0;
dirk8a5cf512014-07-28 20:16:27 +0000447 clStatus =clEnv->library->clSetKernelArg(clkernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +0000448 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(cl_mem),(void *)&filteredImageBuffer);
dirk8a5cf512014-07-28 20:16:27 +0000449 imageWidth = image->columns;
450 imageHeight = image->rows;
cristy0c832c62014-03-07 22:21:04 +0000451 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);
cristyf034abb2013-11-24 14:16:14 +0000454 filterWidth = kernel->width;
455 filterHeight = kernel->height;
cristy0c832c62014-03-07 22:21:04 +0000456 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&filterWidth);
457 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&filterHeight);
dirk8a5cf512014-07-28 20:16:27 +0000458 matte = (image->alpha_trait==BlendPixelTrait)?1:0;
cristy0c832c62014-03-07 22:21:04 +0000459 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);
cristyf034abb2013-11-24 14:16:14 +0000463 if (clStatus != CL_SUCCESS)
464 {
cristy0c832c62014-03-07 22:21:04 +0000465 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000466 goto cleanup;
467 }
468
469 /* pad the global size to a multiple of the local work size dimension */
dirk8a5cf512014-07-28 20:16:27 +0000470 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];
cristyf034abb2013-11-24 14:16:14 +0000472
473 /* launch the kernel */
cristy0c832c62014-03-07 22:21:04 +0000474 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, clkernel, 2, NULL, global_work_size, localGroupSize, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +0000475 if (clStatus != CL_SUCCESS)
476 {
cristy0c832c62014-03-07 22:21:04 +0000477 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000478 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 {
cristya22457d2013-12-07 14:03:06 +0000487 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000488 goto cleanup;
489 }
490
491 /* set the kernel arguments */
492 i = 0;
dirk8a5cf512014-07-28 20:16:27 +0000493 clStatus =clEnv->library->clSetKernelArg(clkernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +0000494 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(cl_mem),(void *)&filteredImageBuffer);
dirk8a5cf512014-07-28 20:16:27 +0000495 imageWidth = image->columns;
496 imageHeight = image->rows;
cristy0c832c62014-03-07 22:21:04 +0000497 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);
cristyf034abb2013-11-24 14:16:14 +0000500 filterWidth = kernel->width;
501 filterHeight = kernel->height;
cristy0c832c62014-03-07 22:21:04 +0000502 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&filterWidth);
503 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&filterHeight);
dirk8a5cf512014-07-28 20:16:27 +0000504 matte = (image->alpha_trait==BlendPixelTrait)?1:0;
cristy0c832c62014-03-07 22:21:04 +0000505 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&matte);
506 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(ChannelType),(void *)&channel);
cristyf034abb2013-11-24 14:16:14 +0000507 if (clStatus != CL_SUCCESS)
508 {
cristy0c832c62014-03-07 22:21:04 +0000509 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000510 goto cleanup;
511 }
512
cristy0c832c62014-03-07 22:21:04 +0000513 localGroupSize[0] = 8;
514 localGroupSize[1] = 8;
dirk8a5cf512014-07-28 20:16:27 +0000515 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];
cristy0c832c62014-03-07 22:21:04 +0000517 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, clkernel, 2, NULL, global_work_size, localGroupSize, 0, NULL, NULL);
518
cristyf034abb2013-11-24 14:16:14 +0000519 if (clStatus != CL_SUCCESS)
520 {
cristy0c832c62014-03-07 22:21:04 +0000521 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000522 goto cleanup;
523 }
524 }
cristy0c832c62014-03-07 22:21:04 +0000525 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +0000526
527 if (ALIGNED(filteredPixels,CLPixelPacket))
528 {
dirk8a5cf512014-07-28 20:16:27 +0000529 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +0000530 clEnv->library->clEnqueueMapBuffer(queue, filteredImageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +0000531 }
532 else
533 {
dirk8a5cf512014-07-28 20:16:27 +0000534 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +0000535 clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +0000536 }
537 if (clStatus != CL_SUCCESS)
538 {
cristya22457d2013-12-07 14:03:06 +0000539 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000540 goto cleanup;
541 }
542
dirk832becc2014-08-04 19:44:34 +0000543 outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
544
545cleanup:
546 OpenCLLogException(__FUNCTION__,__LINE__,exception);
547
548 image_view=DestroyCacheView(image_view);
549 if (filteredImage_view != NULL)
dirk8a5cf512014-07-28 20:16:27 +0000550 filteredImage_view=DestroyCacheView(filteredImage_view);
cristyf034abb2013-11-24 14:16:14 +0000551
dirk8a5cf512014-07-28 20:16:27 +0000552 if (imageBuffer != NULL)
553 clEnv->library->clReleaseMemObject(imageBuffer);
cristyf034abb2013-11-24 14:16:14 +0000554
555 if (filteredImageBuffer != NULL)
cristy0c832c62014-03-07 22:21:04 +0000556 clEnv->library->clReleaseMemObject(filteredImageBuffer);
cristyf034abb2013-11-24 14:16:14 +0000557
558 if (convolutionKernel != NULL)
cristy0c832c62014-03-07 22:21:04 +0000559 clEnv->library->clReleaseMemObject(convolutionKernel);
cristyf034abb2013-11-24 14:16:14 +0000560
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
dirk8a5cf512014-07-28 20:16:27 +0000576 return(filteredImage);
cristyf034abb2013-11-24 14:16:14 +0000577}
578
dirk832becc2014-08-04 19:44:34 +0000579MagickExport 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);
cristyf034abb2013-11-24 14:16:14 +0000595}
596
dirk832becc2014-08-04 19:44:34 +0000597static 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);
dirk8a5cf512014-07-28 20:16:27 +0000657 pixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
cristyf034abb2013-11-24 14:16:14 +0000658 if (pixels == (void *) NULL)
659 {
cristya22457d2013-12-07 14:03:06 +0000660 (void) OpenCLThrowMagickException(exception, GetMagickModule(), CacheWarning,
cristyf034abb2013-11-24 14:16:14 +0000661 "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;
cristy0c832c62014-03-07 22:21:04 +0000677 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)pixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +0000678 if (clStatus != CL_SUCCESS)
679 {
cristy0c832c62014-03-07 22:21:04 +0000680 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +0000681 goto cleanup;
682 }
683
cristy0c832c62014-03-07 22:21:04 +0000684 parametersBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_ONLY|CL_MEM_ALLOC_HOST_PTR, number_parameters * sizeof(float), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +0000685 if (clStatus != CL_SUCCESS)
686 {
cristy0c832c62014-03-07 22:21:04 +0000687 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +0000688 goto cleanup;
689 }
690
691 queue = AcquireOpenCLCommandQueue(clEnv);
692
cristy0c832c62014-03-07 22:21:04 +0000693 parametersBufferPtr = (float*)clEnv->library->clEnqueueMapBuffer(queue, parametersBuffer, CL_TRUE, CL_MAP_WRITE, 0, number_parameters * sizeof(float)
cristyf034abb2013-11-24 14:16:14 +0000694 , 0, NULL, NULL, &clStatus);
695 if (clStatus != CL_SUCCESS)
696 {
cristy0c832c62014-03-07 22:21:04 +0000697 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueMapBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +0000698 goto cleanup;
699 }
700 for (i = 0; i < number_parameters; i++)
701 {
702 parametersBufferPtr[i] = (float)parameters[i];
703 }
cristy0c832c62014-03-07 22:21:04 +0000704 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, parametersBuffer, parametersBufferPtr, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +0000705 if (clStatus != CL_SUCCESS)
706 {
cristy0c832c62014-03-07 22:21:04 +0000707 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000708 goto cleanup;
709 }
cristy0c832c62014-03-07 22:21:04 +0000710 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +0000711
712 clkernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "FunctionImage");
713 if (clkernel == NULL)
714 {
cristya22457d2013-12-07 14:03:06 +0000715 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000716 goto cleanup;
717 }
718
719 /* set the kernel arguments */
720 i = 0;
cristy0c832c62014-03-07 22:21:04 +0000721 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);
cristyf034abb2013-11-24 14:16:14 +0000726 if (clStatus != CL_SUCCESS)
727 {
cristy0c832c62014-03-07 22:21:04 +0000728 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000729 goto cleanup;
730 }
731
732 globalWorkSize[0] = image->columns;
733 globalWorkSize[1] = image->rows;
734 /* launch the kernel */
cristy0c832c62014-03-07 22:21:04 +0000735 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, clkernel, 2, NULL, globalWorkSize, NULL, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +0000736 if (clStatus != CL_SUCCESS)
737 {
cristy0c832c62014-03-07 22:21:04 +0000738 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000739 goto cleanup;
740 }
cristy0c832c62014-03-07 22:21:04 +0000741 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +0000742
743
744 if (ALIGNED(pixels,CLPixelPacket))
745 {
746 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +0000747 clEnv->library->clEnqueueMapBuffer(queue, imageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +0000748 }
749 else
750 {
751 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +0000752 clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), pixels, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +0000753 }
754 if (clStatus != CL_SUCCESS)
755 {
cristya22457d2013-12-07 14:03:06 +0000756 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000757 goto cleanup;
758 }
dirk832becc2014-08-04 19:44:34 +0000759 status=SyncCacheViewAuthenticPixels(image_view,exception);
760
761cleanup:
762 OpenCLLogException(__FUNCTION__,__LINE__,exception);
763
dirk8a5cf512014-07-28 20:16:27 +0000764 image_view=DestroyCacheView(image_view);
cristyf034abb2013-11-24 14:16:14 +0000765
766 if (clkernel != NULL) RelinquishOpenCLKernel(clEnv, clkernel);
767 if (queue != NULL) RelinquishOpenCLCommandQueue(clEnv, queue);
cristy0c832c62014-03-07 22:21:04 +0000768 if (imageBuffer != NULL) clEnv->library->clReleaseMemObject(imageBuffer);
769 if (parametersBuffer != NULL) clEnv->library->clReleaseMemObject(parametersBuffer);
cristyf034abb2013-11-24 14:16:14 +0000770
dirk8a5cf512014-07-28 20:16:27 +0000771 return(status);
cristyf034abb2013-11-24 14:16:14 +0000772}
773
dirk832becc2014-08-04 19:44:34 +0000774MagickExport 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);
cristyf034abb2013-11-24 14:16:14 +0000791}
792
dirk8a5cf512014-07-28 20:16:27 +0000793/*
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*/
cristyf034abb2013-11-24 14:16:14 +0000830
dirk832becc2014-08-04 19:44:34 +0000831static 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);
dirk8a5cf512014-07-28 20:16:27 +0000915 inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
cristyf034abb2013-11-24 14:16:14 +0000916 if (inputPixels == (const void *) NULL)
917 {
dirk8a5cf512014-07-28 20:16:27 +0000918 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristyf034abb2013-11-24 14:16:14 +0000919 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 */
dirk8a5cf512014-07-28 20:16:27 +0000933 length = image->columns * image->rows;
934 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +0000935 if (clStatus != CL_SUCCESS)
936 {
cristy0c832c62014-03-07 22:21:04 +0000937 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +0000938 goto cleanup;
939 }
940 }
941
942 /* create output */
943 {
dirk8a5cf512014-07-28 20:16:27 +0000944 filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristyf034abb2013-11-24 14:16:14 +0000945 assert(filteredImage != NULL);
dirk8a5cf512014-07-28 20:16:27 +0000946 if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
cristyf034abb2013-11-24 14:16:14 +0000947 {
cristya22457d2013-12-07 14:03:06 +0000948 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000949 goto cleanup;
950 }
dirk832becc2014-08-04 19:44:34 +0000951 filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
dirk8a5cf512014-07-28 20:16:27 +0000952 filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
cristyf034abb2013-11-24 14:16:14 +0000953 if (filteredPixels == (void *) NULL)
954 {
cristya22457d2013-12-07 14:03:06 +0000955 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
cristyf034abb2013-11-24 14:16:14 +0000956 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 */
dirk8a5cf512014-07-28 20:16:27 +0000970 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +0000971 filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
cristyf034abb2013-11-24 14:16:14 +0000972 if (clStatus != CL_SUCCESS)
973 {
cristy0c832c62014-03-07 22:21:04 +0000974 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +0000975 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);
983 if (kernel == (KernelInfo *) NULL)
984 {
cristya22457d2013-12-07 14:03:06 +0000985 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "MemoryAllocationFailed.",".");
cristyf034abb2013-11-24 14:16:14 +0000986 goto cleanup;
987 }
988
cristy0c832c62014-03-07 22:21:04 +0000989 imageKernelBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_ONLY|CL_MEM_ALLOC_HOST_PTR, kernel->width * sizeof(float), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +0000990 if (clStatus != CL_SUCCESS)
991 {
cristy0c832c62014-03-07 22:21:04 +0000992 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +0000993 goto cleanup;
994 }
cristy0c832c62014-03-07 22:21:04 +0000995 kernelBufferPtr = (float*)clEnv->library->clEnqueueMapBuffer(queue, imageKernelBuffer, CL_TRUE, CL_MAP_WRITE, 0, kernel->width * sizeof(float), 0, NULL, NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +0000996 if (clStatus != CL_SUCCESS)
997 {
cristy0c832c62014-03-07 22:21:04 +0000998 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueMapBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +0000999 goto cleanup;
1000 }
1001
1002 for (i = 0; i < kernel->width; i++)
1003 {
1004 kernelBufferPtr[i] = (float) kernel->values[i];
1005 }
1006
cristy0c832c62014-03-07 22:21:04 +00001007 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, imageKernelBuffer, kernelBufferPtr, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00001008 if (clStatus != CL_SUCCESS)
1009 {
cristy0c832c62014-03-07 22:21:04 +00001010 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001011 goto cleanup;
1012 }
1013 }
1014
1015 {
1016
1017 /* create temp buffer */
1018 {
dirk8a5cf512014-07-28 20:16:27 +00001019 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00001020 tempImageBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_WRITE, length * 4 * sizeof(float), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00001021 if (clStatus != CL_SUCCESS)
1022 {
cristy0c832c62014-03-07 22:21:04 +00001023 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001024 goto cleanup;
1025 }
1026 }
1027
1028 /* get the OpenCL kernels */
1029 {
1030 blurRowKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "BlurRow");
1031 if (blurRowKernel == NULL)
1032 {
cristya22457d2013-12-07 14:03:06 +00001033 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001034 goto cleanup;
1035 };
1036
1037 blurColumnKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "BlurColumn");
1038 if (blurColumnKernel == NULL)
1039 {
cristya22457d2013-12-07 14:03:06 +00001040 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001041 goto cleanup;
1042 };
1043 }
1044
1045 {
1046 /* need logic to decide this value */
1047 int chunkSize = 256;
1048
1049 {
dirk8a5cf512014-07-28 20:16:27 +00001050 imageColumns = image->columns;
1051 imageRows = image->rows;
cristyf034abb2013-11-24 14:16:14 +00001052
1053 /* set the kernel arguments */
1054 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00001055 clStatus=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00001056 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);
cristyf034abb2013-11-24 14:16:14 +00001059 kernelWidth = kernel->width;
cristy0c832c62014-03-07 22:21:04 +00001060 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);
cristyf034abb2013-11-24 14:16:14 +00001064 if (clStatus != CL_SUCCESS)
1065 {
cristy0c832c62014-03-07 22:21:04 +00001066 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001067 goto cleanup;
1068 }
1069 }
1070
1071 /* launch the kernel */
1072 {
1073 size_t gsize[2];
1074 size_t wsize[2];
1075
dirk8a5cf512014-07-28 20:16:27 +00001076 gsize[0] = chunkSize*((image->columns+chunkSize-1)/chunkSize);
1077 gsize[1] = image->rows;
cristyf034abb2013-11-24 14:16:14 +00001078 wsize[0] = chunkSize;
1079 wsize[1] = 1;
1080
cristy0c832c62014-03-07 22:21:04 +00001081 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, blurRowKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00001082 if (clStatus != CL_SUCCESS)
1083 {
cristy0c832c62014-03-07 22:21:04 +00001084 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001085 goto cleanup;
1086 }
cristy0c832c62014-03-07 22:21:04 +00001087 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00001088 }
1089 }
1090
1091 {
1092 /* need logic to decide this value */
1093 int chunkSize = 256;
1094
1095 {
dirk8a5cf512014-07-28 20:16:27 +00001096 imageColumns = image->columns;
1097 imageRows = image->rows;
cristyf034abb2013-11-24 14:16:14 +00001098
1099 /* set the kernel arguments */
1100 i = 0;
cristy0c832c62014-03-07 22:21:04 +00001101 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);
cristyf034abb2013-11-24 14:16:14 +00001105 kernelWidth = kernel->width;
cristy0c832c62014-03-07 22:21:04 +00001106 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);
cristyf034abb2013-11-24 14:16:14 +00001110 if (clStatus != CL_SUCCESS)
1111 {
cristy0c832c62014-03-07 22:21:04 +00001112 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001113 goto cleanup;
1114 }
1115 }
1116
1117 /* launch the kernel */
1118 {
1119 size_t gsize[2];
1120 size_t wsize[2];
1121
dirk8a5cf512014-07-28 20:16:27 +00001122 gsize[0] = image->columns;
1123 gsize[1] = chunkSize*((image->rows+chunkSize-1)/chunkSize);
cristyf034abb2013-11-24 14:16:14 +00001124 wsize[0] = 1;
1125 wsize[1] = chunkSize;
1126
cristy0c832c62014-03-07 22:21:04 +00001127 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, blurColumnKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00001128 if (clStatus != CL_SUCCESS)
1129 {
cristy0c832c62014-03-07 22:21:04 +00001130 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001131 goto cleanup;
1132 }
cristy0c832c62014-03-07 22:21:04 +00001133 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00001134 }
1135 }
1136
1137 }
1138
1139 /* get result */
1140 if (ALIGNED(filteredPixels,CLPixelPacket))
1141 {
dirk8a5cf512014-07-28 20:16:27 +00001142 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00001143 clEnv->library->clEnqueueMapBuffer(queue, filteredImageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00001144 }
1145 else
1146 {
dirk8a5cf512014-07-28 20:16:27 +00001147 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00001148 clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00001149 }
1150 if (clStatus != CL_SUCCESS)
1151 {
cristya22457d2013-12-07 14:03:06 +00001152 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001153 goto cleanup;
1154 }
1155
dirk832becc2014-08-04 19:44:34 +00001156 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
dirk8a5cf512014-07-28 20:16:27 +00001165 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00001166 if (tempImageBuffer!=NULL) clEnv->library->clReleaseMemObject(tempImageBuffer);
1167 if (filteredImageBuffer!=NULL) clEnv->library->clReleaseMemObject(filteredImageBuffer);
1168 if (imageKernelBuffer!=NULL) clEnv->library->clReleaseMemObject(imageKernelBuffer);
cristyf034abb2013-11-24 14:16:14 +00001169 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);
dirk8a5cf512014-07-28 20:16:27 +00001173 if (outputReady == MagickFalse && filteredImage != NULL)
dirk832becc2014-08-04 19:44:34 +00001174 filteredImage=DestroyImage(filteredImage);
dirk8a5cf512014-07-28 20:16:27 +00001175 return(filteredImage);
cristyf034abb2013-11-24 14:16:14 +00001176}
1177
dirk832becc2014-08-04 19:44:34 +00001178static 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);
dirk8a5cf512014-07-28 20:16:27 +00001263 inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00001264 if (inputPixels == (const void *) NULL)
1265 {
dirk8a5cf512014-07-28 20:16:27 +00001266 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristyf034abb2013-11-24 14:16:14 +00001267 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 */
dirk8a5cf512014-07-28 20:16:27 +00001281 length = image->columns * image->rows;
1282 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00001283 if (clStatus != CL_SUCCESS)
1284 {
cristy0c832c62014-03-07 22:21:04 +00001285 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001286 goto cleanup;
1287 }
1288 }
1289
1290 /* create output */
1291 {
dirk8a5cf512014-07-28 20:16:27 +00001292 filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristyf034abb2013-11-24 14:16:14 +00001293 assert(filteredImage != NULL);
dirk8a5cf512014-07-28 20:16:27 +00001294 if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
cristyf034abb2013-11-24 14:16:14 +00001295 {
cristya22457d2013-12-07 14:03:06 +00001296 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001297 goto cleanup;
1298 }
dirk832becc2014-08-04 19:44:34 +00001299 filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
dirk8a5cf512014-07-28 20:16:27 +00001300 filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00001301 if (filteredPixels == (void *) NULL)
1302 {
cristya22457d2013-12-07 14:03:06 +00001303 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
cristyf034abb2013-11-24 14:16:14 +00001304 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 */
dirk8a5cf512014-07-28 20:16:27 +00001318 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00001319 filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00001320 if (clStatus != CL_SUCCESS)
1321 {
cristy0c832c62014-03-07 22:21:04 +00001322 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001323 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);
1331 if (kernel == (KernelInfo *) NULL)
1332 {
cristya22457d2013-12-07 14:03:06 +00001333 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "MemoryAllocationFailed.",".");
cristyf034abb2013-11-24 14:16:14 +00001334 goto cleanup;
1335 }
1336
cristy0c832c62014-03-07 22:21:04 +00001337 imageKernelBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_ONLY|CL_MEM_ALLOC_HOST_PTR, kernel->width * sizeof(float), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00001338 if (clStatus != CL_SUCCESS)
1339 {
cristy0c832c62014-03-07 22:21:04 +00001340 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001341 goto cleanup;
1342 }
cristy0c832c62014-03-07 22:21:04 +00001343 kernelBufferPtr = (float*)clEnv->library->clEnqueueMapBuffer(queue, imageKernelBuffer, CL_TRUE, CL_MAP_WRITE, 0, kernel->width * sizeof(float), 0, NULL, NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00001344 if (clStatus != CL_SUCCESS)
1345 {
cristy0c832c62014-03-07 22:21:04 +00001346 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueMapBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001347 goto cleanup;
1348 }
1349
1350 for (i = 0; i < kernel->width; i++)
1351 {
1352 kernelBufferPtr[i] = (float) kernel->values[i];
1353 }
1354
cristy0c832c62014-03-07 22:21:04 +00001355 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, imageKernelBuffer, kernelBufferPtr, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00001356 if (clStatus != CL_SUCCESS)
1357 {
cristy0c832c62014-03-07 22:21:04 +00001358 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001359 goto cleanup;
1360 }
1361 }
1362
1363 {
1364 unsigned int offsetRows;
1365 unsigned int sec;
1366
1367 /* create temp buffer */
1368 {
dirk8a5cf512014-07-28 20:16:27 +00001369 length = image->columns * (image->rows / 2 + 1 + (kernel->width-1) / 2);
cristy0c832c62014-03-07 22:21:04 +00001370 tempImageBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_WRITE, length * 4 * sizeof(float), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00001371 if (clStatus != CL_SUCCESS)
1372 {
cristy0c832c62014-03-07 22:21:04 +00001373 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001374 goto cleanup;
1375 }
1376 }
1377
1378 /* get the OpenCL kernels */
1379 {
1380 blurRowKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "BlurRowSection");
1381 if (blurRowKernel == NULL)
1382 {
cristya22457d2013-12-07 14:03:06 +00001383 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001384 goto cleanup;
1385 };
1386
1387 blurColumnKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "BlurColumnSection");
1388 if (blurColumnKernel == NULL)
1389 {
cristya22457d2013-12-07 14:03:06 +00001390 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001391 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 {
dirk8a5cf512014-07-28 20:16:27 +00001402 imageColumns = image->columns;
cristyf034abb2013-11-24 14:16:14 +00001403 if (sec == 0)
dirk8a5cf512014-07-28 20:16:27 +00001404 imageRows = image->rows / 2 + (kernel->width-1) / 2;
cristyf034abb2013-11-24 14:16:14 +00001405 else
dirk8a5cf512014-07-28 20:16:27 +00001406 imageRows = (image->rows - image->rows / 2) + (kernel->width-1) / 2;
cristyf034abb2013-11-24 14:16:14 +00001407
dirk8a5cf512014-07-28 20:16:27 +00001408 offsetRows = sec * image->rows / 2;
cristyf034abb2013-11-24 14:16:14 +00001409
1410 kernelWidth = kernel->width;
1411
1412 /* set the kernel arguments */
1413 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00001414 clStatus=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00001415 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);
cristyf034abb2013-11-24 14:16:14 +00001424 if (clStatus != CL_SUCCESS)
1425 {
cristy0c832c62014-03-07 22:21:04 +00001426 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001427 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
cristy0c832c62014-03-07 22:21:04 +00001441 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, blurRowKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00001442 if (clStatus != CL_SUCCESS)
1443 {
cristy0c832c62014-03-07 22:21:04 +00001444 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001445 goto cleanup;
1446 }
cristy0c832c62014-03-07 22:21:04 +00001447 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00001448 }
1449 }
1450
1451 {
1452 /* need logic to decide this value */
1453 int chunkSize = 256;
1454
1455 {
dirk8a5cf512014-07-28 20:16:27 +00001456 imageColumns = image->columns;
cristyf034abb2013-11-24 14:16:14 +00001457 if (sec == 0)
dirk8a5cf512014-07-28 20:16:27 +00001458 imageRows = image->rows / 2;
cristyf034abb2013-11-24 14:16:14 +00001459 else
dirk8a5cf512014-07-28 20:16:27 +00001460 imageRows = (image->rows - image->rows / 2);
cristyf034abb2013-11-24 14:16:14 +00001461
dirk8a5cf512014-07-28 20:16:27 +00001462 offsetRows = sec * image->rows / 2;
cristyf034abb2013-11-24 14:16:14 +00001463
1464 kernelWidth = kernel->width;
1465
1466 /* set the kernel arguments */
1467 i = 0;
cristy0c832c62014-03-07 22:21:04 +00001468 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);
cristyf034abb2013-11-24 14:16:14 +00001478 if (clStatus != CL_SUCCESS)
1479 {
cristy0c832c62014-03-07 22:21:04 +00001480 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001481 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
cristy0c832c62014-03-07 22:21:04 +00001495 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, blurColumnKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00001496 if (clStatus != CL_SUCCESS)
1497 {
cristy0c832c62014-03-07 22:21:04 +00001498 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001499 goto cleanup;
1500 }
cristy0c832c62014-03-07 22:21:04 +00001501 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00001502 }
1503 }
1504 }
1505
1506 }
1507
1508 /* get result */
1509 if (ALIGNED(filteredPixels,CLPixelPacket))
1510 {
dirk8a5cf512014-07-28 20:16:27 +00001511 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00001512 clEnv->library->clEnqueueMapBuffer(queue, filteredImageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00001513 }
1514 else
1515 {
dirk8a5cf512014-07-28 20:16:27 +00001516 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00001517 clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00001518 }
1519 if (clStatus != CL_SUCCESS)
1520 {
cristya22457d2013-12-07 14:03:06 +00001521 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001522 goto cleanup;
1523 }
1524
dirk832becc2014-08-04 19:44:34 +00001525 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
dirk8a5cf512014-07-28 20:16:27 +00001534 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00001535 if (tempImageBuffer!=NULL) clEnv->library->clReleaseMemObject(tempImageBuffer);
1536 if (filteredImageBuffer!=NULL) clEnv->library->clReleaseMemObject(filteredImageBuffer);
1537 if (imageKernelBuffer!=NULL) clEnv->library->clReleaseMemObject(imageKernelBuffer);
cristyf034abb2013-11-24 14:16:14 +00001538 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
dirk859503c2014-08-13 20:39:42 +00001553static 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
dirk832becc2014-08-04 19:44:34 +00001564MagickExport 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
dirk859503c2014-08-13 20:39:42 +00001578 if (radius < 12.1)
1579 filteredImage=ComputeBlurImageSingle(image, channel, radius, sigma, exception);
1580 else if (splitImage(image) && (image->rows / 2 > radius))
dirk832becc2014-08-04 19:44:34 +00001581 filteredImage=ComputeBlurImageSection(image, channel, radius, sigma, exception);
1582 else
1583 filteredImage=ComputeBlurImage(image, channel, radius, sigma, exception);
1584
1585 return(filteredImage);
dirk8a5cf512014-07-28 20:16:27 +00001586}
1587
cristyf034abb2013-11-24 14:16:14 +00001588/*
1589%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1590% %
1591% %
1592% %
dirk8a5cf512014-07-28 20:16:27 +00001593% 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 %
cristyf034abb2013-11-24 14:16:14 +00001594% %
1595% %
1596% %
1597%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1598%
dirk8a5cf512014-07-28 20:16:27 +00001599% RotationalBlurImage() applies a rotational blur to the image.
cristyf034abb2013-11-24 14:16:14 +00001600%
dirk8a5cf512014-07-28 20:16:27 +00001601% Andrew Protano contributed this effect.
cristyf034abb2013-11-24 14:16:14 +00001602%
dirk8a5cf512014-07-28 20:16:27 +00001603% 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)
cristyf034abb2013-11-24 14:16:14 +00001609%
1610% A description of each parameter follows:
1611%
1612% o image: the image.
1613%
1614% o channel: the channel type.
1615%
dirk8a5cf512014-07-28 20:16:27 +00001616% o angle: the angle of the rotational blur.
cristyf034abb2013-11-24 14:16:14 +00001617%
1618% o exception: return any errors or warnings in this structure.
1619%
1620*/
1621
dirk832becc2014-08-04 19:44:34 +00001622static 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);
dirk8a5cf512014-07-28 20:16:27 +00001712 inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00001713 if (inputPixels == (const void *) NULL)
1714 {
dirk8a5cf512014-07-28 20:16:27 +00001715 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristyf034abb2013-11-24 14:16:14 +00001716 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 */
dirk8a5cf512014-07-28 20:16:27 +00001731 length = image->columns * image->rows;
1732 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00001733 if (clStatus != CL_SUCCESS)
1734 {
cristy0c832c62014-03-07 22:21:04 +00001735 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001736 goto cleanup;
1737 }
1738
1739
dirk8a5cf512014-07-28 20:16:27 +00001740 filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristyf034abb2013-11-24 14:16:14 +00001741 assert(filteredImage != NULL);
dirk8a5cf512014-07-28 20:16:27 +00001742 if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
cristyf034abb2013-11-24 14:16:14 +00001743 {
cristya22457d2013-12-07 14:03:06 +00001744 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001745 goto cleanup;
1746 }
dirk832becc2014-08-04 19:44:34 +00001747 filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
dirk8a5cf512014-07-28 20:16:27 +00001748 filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00001749 if (filteredPixels == (void *) NULL)
1750 {
cristya22457d2013-12-07 14:03:06 +00001751 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
cristyf034abb2013-11-24 14:16:14 +00001752 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 */
dirk8a5cf512014-07-28 20:16:27 +00001766 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00001767 filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00001768 if (clStatus != CL_SUCCESS)
1769 {
cristy0c832c62014-03-07 22:21:04 +00001770 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001771 goto cleanup;
1772 }
1773
dirk8a5cf512014-07-28 20:16:27 +00001774 blurCenter.s[0] = (float) (image->columns-1)/2.0;
1775 blurCenter.s[1] = (float) (image->rows-1)/2.0;
cristyf034abb2013-11-24 14:16:14 +00001776 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 */
cristy0c832c62014-03-07 22:21:04 +00001780 sinThetaBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_ONLY|CL_MEM_ALLOC_HOST_PTR, cossin_theta_size * sizeof(float), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00001781 if (clStatus != CL_SUCCESS)
1782 {
cristy0c832c62014-03-07 22:21:04 +00001783 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001784 goto cleanup;
1785 }
cristy0c832c62014-03-07 22:21:04 +00001786 cosThetaBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_ONLY|CL_MEM_ALLOC_HOST_PTR, cossin_theta_size * sizeof(float), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00001787 if (clStatus != CL_SUCCESS)
1788 {
cristy0c832c62014-03-07 22:21:04 +00001789 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001790 goto cleanup;
1791 }
1792
1793
1794 queue = AcquireOpenCLCommandQueue(clEnv);
cristy0c832c62014-03-07 22:21:04 +00001795 sinThetaPtr = (float*) clEnv->library->clEnqueueMapBuffer(queue, sinThetaBuffer, CL_TRUE, CL_MAP_WRITE, 0, cossin_theta_size*sizeof(float), 0, NULL, NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00001796 if (clStatus != CL_SUCCESS)
1797 {
cristya22457d2013-12-07 14:03:06 +00001798 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnqueuemapBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001799 goto cleanup;
1800 }
1801
cristy0c832c62014-03-07 22:21:04 +00001802 cosThetaPtr = (float*) clEnv->library->clEnqueueMapBuffer(queue, cosThetaBuffer, CL_TRUE, CL_MAP_WRITE, 0, cossin_theta_size*sizeof(float), 0, NULL, NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00001803 if (clStatus != CL_SUCCESS)
1804 {
cristya22457d2013-12-07 14:03:06 +00001805 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnqueuemapBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001806 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
cristy0c832c62014-03-07 22:21:04 +00001817 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, sinThetaBuffer, sinThetaPtr, 0, NULL, NULL);
1818 clStatus |= clEnv->library->clEnqueueUnmapMemObject(queue, cosThetaBuffer, cosThetaPtr, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00001819 if (clStatus != CL_SUCCESS)
1820 {
cristy0c832c62014-03-07 22:21:04 +00001821 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001822 goto cleanup;
1823 }
1824
1825 /* get the OpenCL kernel */
dirk6d612cf2014-03-13 21:17:23 +00001826 rotationalBlurKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "RotationalBlur");
1827 if (rotationalBlurKernel == NULL)
cristyf034abb2013-11-24 14:16:14 +00001828 {
cristya22457d2013-12-07 14:03:06 +00001829 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001830 goto cleanup;
1831 }
1832
1833
1834 /* set the kernel arguments */
1835 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00001836 clStatus=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
dirk6d612cf2014-03-13 21:17:23 +00001837 clStatus|=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(cl_mem),(void *)&filteredImageBuffer);
cristyf034abb2013-11-24 14:16:14 +00001838
dirk8a5cf512014-07-28 20:16:27 +00001839 GetPixelInfo(image,&bias);
cristyf034abb2013-11-24 14:16:14 +00001840 biasPixel.s[0] = bias.red;
1841 biasPixel.s[1] = bias.green;
1842 biasPixel.s[2] = bias.blue;
dirk8a5cf512014-07-28 20:16:27 +00001843 biasPixel.s[3] = bias.alpha;
dirk6d612cf2014-03-13 21:17:23 +00001844 clStatus|=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(cl_float4), &biasPixel);
1845 clStatus|=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(ChannelType), &channel);
cristyf034abb2013-11-24 14:16:14 +00001846
dirk8a5cf512014-07-28 20:16:27 +00001847 matte = (image->alpha_trait==BlendPixelTrait)?1:0;
dirk6d612cf2014-03-13 21:17:23 +00001848 clStatus|=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(unsigned int), &matte);
cristyf034abb2013-11-24 14:16:14 +00001849
dirk6d612cf2014-03-13 21:17:23 +00001850 clStatus=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(cl_float2), &blurCenter);
cristyf034abb2013-11-24 14:16:14 +00001851
dirk6d612cf2014-03-13 21:17:23 +00001852 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);
cristyf034abb2013-11-24 14:16:14 +00001855 if (clStatus != CL_SUCCESS)
1856 {
cristy0c832c62014-03-07 22:21:04 +00001857 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001858 goto cleanup;
1859 }
1860
1861
dirk8a5cf512014-07-28 20:16:27 +00001862 global_work_size[0] = image->columns;
1863 global_work_size[1] = image->rows;
cristyf034abb2013-11-24 14:16:14 +00001864 /* launch the kernel */
dirk6d612cf2014-03-13 21:17:23 +00001865 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, rotationalBlurKernel, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00001866 if (clStatus != CL_SUCCESS)
1867 {
cristy0c832c62014-03-07 22:21:04 +00001868 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001869 goto cleanup;
1870 }
cristy0c832c62014-03-07 22:21:04 +00001871 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00001872
1873 if (ALIGNED(filteredPixels,CLPixelPacket))
1874 {
dirk8a5cf512014-07-28 20:16:27 +00001875 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00001876 clEnv->library->clEnqueueMapBuffer(queue, filteredImageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00001877 }
1878 else
1879 {
dirk8a5cf512014-07-28 20:16:27 +00001880 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00001881 clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00001882 }
1883 if (clStatus != CL_SUCCESS)
1884 {
cristya22457d2013-12-07 14:03:06 +00001885 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001886 goto cleanup;
1887 }
dirk832becc2014-08-04 19:44:34 +00001888 outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
1889
1890cleanup:
1891 OpenCLLogException(__FUNCTION__,__LINE__,exception);
1892
1893 image_view=DestroyCacheView(image_view);
1894 if (filteredImage_view != NULL)
dirk8a5cf512014-07-28 20:16:27 +00001895 filteredImage_view=DestroyCacheView(filteredImage_view);
cristya22457d2013-12-07 14:03:06 +00001896
cristy0c832c62014-03-07 22:21:04 +00001897 if (filteredImageBuffer!=NULL) clEnv->library->clReleaseMemObject(filteredImageBuffer);
dirk8a5cf512014-07-28 20:16:27 +00001898 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00001899 if (sinThetaBuffer!=NULL) clEnv->library->clReleaseMemObject(sinThetaBuffer);
1900 if (cosThetaBuffer!=NULL) clEnv->library->clReleaseMemObject(cosThetaBuffer);
dirk6d612cf2014-03-13 21:17:23 +00001901 if (rotationalBlurKernel!=NULL) RelinquishOpenCLKernel(clEnv, rotationalBlurKernel);
cristyf034abb2013-11-24 14:16:14 +00001902 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
dirk832becc2014-08-04 19:44:34 +00001914MagickExport 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;
dirk8a5cf512014-07-28 20:16:27 +00001929}
1930
cristyf034abb2013-11-24 14:16:14 +00001931/*
1932%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1933% %
1934% %
1935% %
dirk8a5cf512014-07-28 20:16:27 +00001936% 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 %
cristyf034abb2013-11-24 14:16:14 +00001937% %
1938% %
1939% %
1940%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1941%
dirk8a5cf512014-07-28 20:16:27 +00001942% 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.
cristyf034abb2013-11-24 14:16:14 +00001946%
dirk8a5cf512014-07-28 20:16:27 +00001947% The format of the UnsharpMaskImage method is:
cristyf034abb2013-11-24 14:16:14 +00001948%
dirk8a5cf512014-07-28 20:16:27 +00001949% Image *UnsharpMaskImage(const Image *image,const double radius,
1950% const double sigma,const double amount,const double threshold,
cristyf034abb2013-11-24 14:16:14 +00001951% ExceptionInfo *exception)
dirk8a5cf512014-07-28 20:16:27 +00001952% Image *UnsharpMaskImageChannel(const Image *image,
1953% const ChannelType channel,const double radius,const double sigma,
1954% const double gain,const double threshold,ExceptionInfo *exception)
cristyf034abb2013-11-24 14:16:14 +00001955%
1956% A description of each parameter follows:
1957%
1958% o image: the image.
1959%
1960% o channel: the channel type.
1961%
dirk8a5cf512014-07-28 20:16:27 +00001962% 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.
cristyf034abb2013-11-24 14:16:14 +00001971%
1972% o exception: return any errors or warnings in this structure.
1973%
1974*/
1975
dirk832becc2014-08-04 19:44:34 +00001976static 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);
dirk8a5cf512014-07-28 20:16:27 +00002066 inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00002067 if (inputPixels == (const void *) NULL)
2068 {
dirk8a5cf512014-07-28 20:16:27 +00002069 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristyf034abb2013-11-24 14:16:14 +00002070 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 */
dirk8a5cf512014-07-28 20:16:27 +00002085 length = image->columns * image->rows;
2086 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00002087 if (clStatus != CL_SUCCESS)
2088 {
cristy0c832c62014-03-07 22:21:04 +00002089 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002090 goto cleanup;
2091 }
2092 }
2093
2094 /* create output */
2095 {
dirk8a5cf512014-07-28 20:16:27 +00002096 filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristyf034abb2013-11-24 14:16:14 +00002097 assert(filteredImage != NULL);
dirk8a5cf512014-07-28 20:16:27 +00002098 if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
cristyf034abb2013-11-24 14:16:14 +00002099 {
cristya22457d2013-12-07 14:03:06 +00002100 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002101 goto cleanup;
2102 }
dirk832becc2014-08-04 19:44:34 +00002103 filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
dirk8a5cf512014-07-28 20:16:27 +00002104 filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00002105 if (filteredPixels == (void *) NULL)
2106 {
cristya22457d2013-12-07 14:03:06 +00002107 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
cristyf034abb2013-11-24 14:16:14 +00002108 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 */
dirk8a5cf512014-07-28 20:16:27 +00002123 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00002124 filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00002125 if (clStatus != CL_SUCCESS)
2126 {
cristy0c832c62014-03-07 22:21:04 +00002127 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002128 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);
2136 if (kernel == (KernelInfo *) NULL)
2137 {
cristya22457d2013-12-07 14:03:06 +00002138 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireKernelInfo failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002139 goto cleanup;
2140 }
2141
cristy0c832c62014-03-07 22:21:04 +00002142 imageKernelBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_ONLY, kernel->width * sizeof(float), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00002143 if (clStatus != CL_SUCCESS)
2144 {
cristy0c832c62014-03-07 22:21:04 +00002145 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002146 goto cleanup;
2147 }
2148
2149
cristy0c832c62014-03-07 22:21:04 +00002150 kernelBufferPtr = (float*)clEnv->library->clEnqueueMapBuffer(queue, imageKernelBuffer, CL_TRUE, CL_MAP_WRITE, 0, kernel->width * sizeof(float), 0, NULL, NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00002151 if (clStatus != CL_SUCCESS)
2152 {
cristy0c832c62014-03-07 22:21:04 +00002153 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueMapBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002154 goto cleanup;
2155 }
2156 for (i = 0; i < kernel->width; i++)
2157 {
2158 kernelBufferPtr[i] = (float) kernel->values[i];
2159 }
cristy0c832c62014-03-07 22:21:04 +00002160 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, imageKernelBuffer, kernelBufferPtr, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00002161 if (clStatus != CL_SUCCESS)
2162 {
cristy0c832c62014-03-07 22:21:04 +00002163 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002164 goto cleanup;
2165 }
2166 }
2167
2168 {
2169 /* create temp buffer */
2170 {
dirk8a5cf512014-07-28 20:16:27 +00002171 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00002172 tempImageBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_WRITE, length * 4 * sizeof(float), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00002173 if (clStatus != CL_SUCCESS)
2174 {
cristy0c832c62014-03-07 22:21:04 +00002175 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002176 goto cleanup;
2177 }
2178 }
2179
2180 /* get the opencl kernel */
2181 {
2182 blurRowKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "BlurRow");
2183 if (blurRowKernel == NULL)
2184 {
cristya22457d2013-12-07 14:03:06 +00002185 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002186 goto cleanup;
2187 };
2188
2189 unsharpMaskBlurColumnKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "UnsharpMaskBlurColumn");
2190 if (unsharpMaskBlurColumnKernel == NULL)
2191 {
cristya22457d2013-12-07 14:03:06 +00002192 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002193 goto cleanup;
2194 };
2195 }
2196
2197 {
2198 chunkSize = 256;
2199
dirk8a5cf512014-07-28 20:16:27 +00002200 imageColumns = image->columns;
2201 imageRows = image->rows;
cristyf034abb2013-11-24 14:16:14 +00002202
2203 kernelWidth = kernel->width;
2204
2205 /* set the kernel arguments */
2206 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00002207 clStatus=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00002208 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);
cristyf034abb2013-11-24 14:16:14 +00002215 if (clStatus != CL_SUCCESS)
2216 {
cristy0c832c62014-03-07 22:21:04 +00002217 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002218 goto cleanup;
2219 }
2220 }
2221
2222 /* launch the kernel */
2223 {
2224 size_t gsize[2];
2225 size_t wsize[2];
2226
dirk8a5cf512014-07-28 20:16:27 +00002227 gsize[0] = chunkSize*((image->columns+chunkSize-1)/chunkSize);
2228 gsize[1] = image->rows;
cristyf034abb2013-11-24 14:16:14 +00002229 wsize[0] = chunkSize;
2230 wsize[1] = 1;
2231
cristy0c832c62014-03-07 22:21:04 +00002232 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, blurRowKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00002233 if (clStatus != CL_SUCCESS)
2234 {
cristy0c832c62014-03-07 22:21:04 +00002235 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002236 goto cleanup;
2237 }
cristy0c832c62014-03-07 22:21:04 +00002238 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00002239 }
2240
2241
2242 {
2243 chunkSize = 256;
dirk8a5cf512014-07-28 20:16:27 +00002244 imageColumns = image->columns;
2245 imageRows = image->rows;
cristyf034abb2013-11-24 14:16:14 +00002246 kernelWidth = kernel->width;
2247 fGain = (float)gain;
2248 fThreshold = (float)threshold;
2249
2250 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00002251 clStatus=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00002252 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);
cristyf034abb2013-11-24 14:16:14 +00002263
2264 if (clStatus != CL_SUCCESS)
2265 {
cristy0c832c62014-03-07 22:21:04 +00002266 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002267 goto cleanup;
2268 }
2269 }
2270
2271 /* launch the kernel */
2272 {
2273 size_t gsize[2];
2274 size_t wsize[2];
2275
dirk8a5cf512014-07-28 20:16:27 +00002276 gsize[0] = image->columns;
2277 gsize[1] = chunkSize*((image->rows+chunkSize-1)/chunkSize);
cristyf034abb2013-11-24 14:16:14 +00002278 wsize[0] = 1;
2279 wsize[1] = chunkSize;
2280
cristy0c832c62014-03-07 22:21:04 +00002281 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, unsharpMaskBlurColumnKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00002282 if (clStatus != CL_SUCCESS)
2283 {
cristy0c832c62014-03-07 22:21:04 +00002284 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002285 goto cleanup;
2286 }
cristy0c832c62014-03-07 22:21:04 +00002287 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00002288 }
2289
2290 }
2291
2292 /* get result */
2293 if (ALIGNED(filteredPixels,CLPixelPacket))
2294 {
dirk8a5cf512014-07-28 20:16:27 +00002295 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00002296 clEnv->library->clEnqueueMapBuffer(queue, filteredImageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00002297 }
2298 else
2299 {
dirk8a5cf512014-07-28 20:16:27 +00002300 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00002301 clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00002302 }
2303 if (clStatus != CL_SUCCESS)
2304 {
cristya22457d2013-12-07 14:03:06 +00002305 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002306 goto cleanup;
2307 }
2308
dirk832becc2014-08-04 19:44:34 +00002309 outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
2310
2311cleanup:
2312 OpenCLLogException(__FUNCTION__,__LINE__,exception);
2313
2314 image_view=DestroyCacheView(image_view);
2315 if (filteredImage_view != NULL)
dirk8a5cf512014-07-28 20:16:27 +00002316 filteredImage_view=DestroyCacheView(filteredImage_view);
cristya22457d2013-12-07 14:03:06 +00002317
cristyf034abb2013-11-24 14:16:14 +00002318 if (kernel != NULL) kernel=DestroyKernelInfo(kernel);
dirk8a5cf512014-07-28 20:16:27 +00002319 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00002320 if (filteredImageBuffer!=NULL) clEnv->library->clReleaseMemObject(filteredImageBuffer);
2321 if (tempImageBuffer!=NULL) clEnv->library->clReleaseMemObject(tempImageBuffer);
2322 if (imageKernelBuffer!=NULL) clEnv->library->clReleaseMemObject(imageKernelBuffer);
cristyf034abb2013-11-24 14:16:14 +00002323 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 }
dirk8a5cf512014-07-28 20:16:27 +00002334 return(filteredImage);
cristyf034abb2013-11-24 14:16:14 +00002335}
2336
dirk832becc2014-08-04 19:44:34 +00002337static 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);
dirk8a5cf512014-07-28 20:16:27 +00002427 inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00002428 if (inputPixels == (const void *) NULL)
2429 {
dirk8a5cf512014-07-28 20:16:27 +00002430 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristyf034abb2013-11-24 14:16:14 +00002431 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 */
dirk8a5cf512014-07-28 20:16:27 +00002446 length = image->columns * image->rows;
2447 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00002448 if (clStatus != CL_SUCCESS)
2449 {
cristy0c832c62014-03-07 22:21:04 +00002450 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002451 goto cleanup;
2452 }
2453 }
2454
2455 /* create output */
2456 {
dirk8a5cf512014-07-28 20:16:27 +00002457 filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristyf034abb2013-11-24 14:16:14 +00002458 assert(filteredImage != NULL);
dirk8a5cf512014-07-28 20:16:27 +00002459 if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
cristyf034abb2013-11-24 14:16:14 +00002460 {
cristya22457d2013-12-07 14:03:06 +00002461 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002462 goto cleanup;
2463 }
dirk832becc2014-08-04 19:44:34 +00002464 filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
dirk8a5cf512014-07-28 20:16:27 +00002465 filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00002466 if (filteredPixels == (void *) NULL)
2467 {
cristya22457d2013-12-07 14:03:06 +00002468 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
cristyf034abb2013-11-24 14:16:14 +00002469 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 */
dirk8a5cf512014-07-28 20:16:27 +00002484 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00002485 filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00002486 if (clStatus != CL_SUCCESS)
2487 {
cristy0c832c62014-03-07 22:21:04 +00002488 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002489 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);
2497 if (kernel == (KernelInfo *) NULL)
2498 {
cristya22457d2013-12-07 14:03:06 +00002499 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireKernelInfo failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002500 goto cleanup;
2501 }
2502
cristy0c832c62014-03-07 22:21:04 +00002503 imageKernelBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_ONLY, kernel->width * sizeof(float), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00002504 if (clStatus != CL_SUCCESS)
2505 {
cristy0c832c62014-03-07 22:21:04 +00002506 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002507 goto cleanup;
2508 }
2509
2510
cristy0c832c62014-03-07 22:21:04 +00002511 kernelBufferPtr = (float*)clEnv->library->clEnqueueMapBuffer(queue, imageKernelBuffer, CL_TRUE, CL_MAP_WRITE, 0, kernel->width * sizeof(float), 0, NULL, NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00002512 if (clStatus != CL_SUCCESS)
2513 {
cristy0c832c62014-03-07 22:21:04 +00002514 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueMapBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002515 goto cleanup;
2516 }
2517 for (i = 0; i < kernel->width; i++)
2518 {
2519 kernelBufferPtr[i] = (float) kernel->values[i];
2520 }
cristy0c832c62014-03-07 22:21:04 +00002521 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, imageKernelBuffer, kernelBufferPtr, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00002522 if (clStatus != CL_SUCCESS)
2523 {
cristy0c832c62014-03-07 22:21:04 +00002524 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002525 goto cleanup;
2526 }
2527 }
2528
2529 {
2530 unsigned int offsetRows;
2531 unsigned int sec;
2532
2533 /* create temp buffer */
2534 {
dirk8a5cf512014-07-28 20:16:27 +00002535 length = image->columns * (image->rows / 2 + 1 + (kernel->width-1) / 2);
cristy0c832c62014-03-07 22:21:04 +00002536 tempImageBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_WRITE, length * 4 * sizeof(float), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00002537 if (clStatus != CL_SUCCESS)
2538 {
cristy0c832c62014-03-07 22:21:04 +00002539 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002540 goto cleanup;
2541 }
2542 }
2543
2544 /* get the opencl kernel */
2545 {
2546 blurRowKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "BlurRowSection");
2547 if (blurRowKernel == NULL)
2548 {
cristya22457d2013-12-07 14:03:06 +00002549 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002550 goto cleanup;
2551 };
2552
2553 unsharpMaskBlurColumnKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "UnsharpMaskBlurColumnSection");
2554 if (unsharpMaskBlurColumnKernel == NULL)
2555 {
cristya22457d2013-12-07 14:03:06 +00002556 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002557 goto cleanup;
2558 };
2559 }
2560
2561 for (sec = 0; sec < 2; sec++)
2562 {
2563 {
2564 chunkSize = 256;
2565
dirk8a5cf512014-07-28 20:16:27 +00002566 imageColumns = image->columns;
cristyf034abb2013-11-24 14:16:14 +00002567 if (sec == 0)
dirk8a5cf512014-07-28 20:16:27 +00002568 imageRows = image->rows / 2 + (kernel->width-1) / 2;
cristyf034abb2013-11-24 14:16:14 +00002569 else
dirk8a5cf512014-07-28 20:16:27 +00002570 imageRows = (image->rows - image->rows / 2) + (kernel->width-1) / 2;
cristyf034abb2013-11-24 14:16:14 +00002571
dirk8a5cf512014-07-28 20:16:27 +00002572 offsetRows = sec * image->rows / 2;
cristyf034abb2013-11-24 14:16:14 +00002573
2574 kernelWidth = kernel->width;
2575
2576 /* set the kernel arguments */
2577 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00002578 clStatus=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00002579 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);
cristyf034abb2013-11-24 14:16:14 +00002588 if (clStatus != CL_SUCCESS)
2589 {
cristy0c832c62014-03-07 22:21:04 +00002590 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002591 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
cristy0c832c62014-03-07 22:21:04 +00002604 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, blurRowKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00002605 if (clStatus != CL_SUCCESS)
2606 {
cristy0c832c62014-03-07 22:21:04 +00002607 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002608 goto cleanup;
2609 }
cristy0c832c62014-03-07 22:21:04 +00002610 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00002611 }
2612
2613
2614 {
2615 chunkSize = 256;
2616
dirk8a5cf512014-07-28 20:16:27 +00002617 imageColumns = image->columns;
cristyf034abb2013-11-24 14:16:14 +00002618 if (sec == 0)
dirk8a5cf512014-07-28 20:16:27 +00002619 imageRows = image->rows / 2;
cristyf034abb2013-11-24 14:16:14 +00002620 else
dirk8a5cf512014-07-28 20:16:27 +00002621 imageRows = (image->rows - image->rows / 2);
cristyf034abb2013-11-24 14:16:14 +00002622
dirk8a5cf512014-07-28 20:16:27 +00002623 offsetRows = sec * image->rows / 2;
cristyf034abb2013-11-24 14:16:14 +00002624
2625 kernelWidth = kernel->width;
2626
2627 fGain = (float)gain;
2628 fThreshold = (float)threshold;
2629
2630 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00002631 clStatus=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00002632 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);
cristyf034abb2013-11-24 14:16:14 +00002645
2646 if (clStatus != CL_SUCCESS)
2647 {
cristy0c832c62014-03-07 22:21:04 +00002648 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002649 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
cristy0c832c62014-03-07 22:21:04 +00002663 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, unsharpMaskBlurColumnKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00002664 if (clStatus != CL_SUCCESS)
2665 {
cristy0c832c62014-03-07 22:21:04 +00002666 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002667 goto cleanup;
2668 }
cristy0c832c62014-03-07 22:21:04 +00002669 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00002670 }
2671 }
2672 }
2673
2674 /* get result */
2675 if (ALIGNED(filteredPixels,CLPixelPacket))
2676 {
dirk8a5cf512014-07-28 20:16:27 +00002677 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00002678 clEnv->library->clEnqueueMapBuffer(queue, filteredImageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00002679 }
2680 else
2681 {
dirk8a5cf512014-07-28 20:16:27 +00002682 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00002683 clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00002684 }
2685 if (clStatus != CL_SUCCESS)
2686 {
cristya22457d2013-12-07 14:03:06 +00002687 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002688 goto cleanup;
2689 }
2690
dirk832becc2014-08-04 19:44:34 +00002691 outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
2692
2693cleanup:
2694 OpenCLLogException(__FUNCTION__,__LINE__,exception);
2695
2696 image_view=DestroyCacheView(image_view);
2697 if (filteredImage_view != NULL)
dirk8a5cf512014-07-28 20:16:27 +00002698 filteredImage_view=DestroyCacheView(filteredImage_view);
cristya22457d2013-12-07 14:03:06 +00002699
cristyf034abb2013-11-24 14:16:14 +00002700 if (kernel != NULL) kernel=DestroyKernelInfo(kernel);
dirk8a5cf512014-07-28 20:16:27 +00002701 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00002702 if (filteredImageBuffer!=NULL) clEnv->library->clReleaseMemObject(filteredImageBuffer);
2703 if (tempImageBuffer!=NULL) clEnv->library->clReleaseMemObject(tempImageBuffer);
2704 if (imageKernelBuffer!=NULL) clEnv->library->clReleaseMemObject(imageKernelBuffer);
cristyf034abb2013-11-24 14:16:14 +00002705 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;
dirk832becc2014-08-04 19:44:34 +00002717}
2718
dirk859503c2014-08-13 20:39:42 +00002719static 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);
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 = image->columns;
2918 imageRows = image->rows;
2919 kernelWidth = 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
dirk832becc2014-08-04 19:44:34 +00003007MagickExport 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
dirk859503c2014-08-13 20:39:42 +00003021 if (radius < 12.1)
3022 filteredImage = ComputeUnsharpMaskImageSingle(image,channel,radius,sigma,gain,threshold, 0, exception);
3023 else if (splitImage(image) && (image->rows / 2 > radius))
dirk832becc2014-08-04 19:44:34 +00003024 filteredImage = ComputeUnsharpMaskImageSection(image,channel,radius,sigma,gain,threshold,exception);
3025 else
3026 filteredImage = ComputeUnsharpMaskImage(image,channel,radius,sigma,gain,threshold,exception);
3027 return(filteredImage);
cristyf034abb2013-11-24 14:16:14 +00003028}
3029
cristyf034abb2013-11-24 14:16:14 +00003030/*
3031%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3032% %
3033% %
3034% %
dirk8a5cf512014-07-28 20:16:27 +00003035% A c c e l e r a t e R e s i z e I m a g e %
cristyf034abb2013-11-24 14:16:14 +00003036% %
3037% %
3038% %
3039%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3040%
dirk8a5cf512014-07-28 20:16:27 +00003041% AccelerateResizeImage() is an OpenCL implementation of ResizeImage()
cristyf034abb2013-11-24 14:16:14 +00003042%
dirk8a5cf512014-07-28 20:16:27 +00003043% AccelerateResizeImage() scales an image to the desired dimensions, using the given
3044% filter (see AcquireFilterInfo()).
cristyf034abb2013-11-24 14:16:14 +00003045%
dirk8a5cf512014-07-28 20:16:27 +00003046% 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)
cristyf034abb2013-11-24 14:16:14 +00003057%
3058% A description of each parameter follows:
3059%
3060% o image: the image.
3061%
dirk8a5cf512014-07-28 20:16:27 +00003062% o columns: the number of columns in the scaled image.
cristyf034abb2013-11-24 14:16:14 +00003063%
dirk8a5cf512014-07-28 20:16:27 +00003064% o rows: the number of rows in the scaled image.
cristyf034abb2013-11-24 14:16:14 +00003065%
dirk8a5cf512014-07-28 20:16:27 +00003066% o filter: Image filter to use.
cristyf034abb2013-11-24 14:16:14 +00003067%
3068% o exception: return any errors or warnings in this structure.
3069%
3070*/
3071
dirk832becc2014-08-04 19:44:34 +00003072static 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;
dirk8a5cf512014-07-28 20:16:27 +00003122 status = MagickFalse;
cristyf034abb2013-11-24 14:16:14 +00003123
3124 /*
3125 Apply filter to resize vertically from image to resize image.
3126 */
cristye85d0f72013-11-27 02:25:43 +00003127 scale=MAGICK_MAX(1.0/xFactor+MagickEpsilon,1.0);
cristyf034abb2013-11-24 14:16:14 +00003128 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
dirke3c5f892013-12-10 06:04:40 +00003154DisableMSCWarning(4127)
cristyf034abb2013-11-24 14:16:14 +00003155 while(1)
dirke3c5f892013-12-10 06:04:40 +00003156RestoreMSCWarning
cristyf034abb2013-11-24 14:16:14 +00003157 {
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 {
cristya22457d2013-12-07 14:03:06 +00003210 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003211 goto cleanup;
3212 }
3213
3214 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00003215 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);
cristy0c832c62014-03-07 22:21:04 +00003218 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);
cristyf034abb2013-11-24 14:16:14 +00003221
cristy0c832c62014-03-07 22:21:04 +00003222 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&resizedColumns);
3223 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&resizedRows);
cristyf034abb2013-11-24 14:16:14 +00003224
cristy0c832c62014-03-07 22:21:04 +00003225 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);
cristyf034abb2013-11-24 14:16:14 +00003228
3229 resizeFilterScale = (float) GetResizeFilterScale(resizeFilter);
cristy0c832c62014-03-07 22:21:04 +00003230 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterScale);
cristyf034abb2013-11-24 14:16:14 +00003231
3232 resizeFilterSupport = (float) GetResizeFilterSupport(resizeFilter);
cristy0c832c62014-03-07 22:21:04 +00003233 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterSupport);
cristyf034abb2013-11-24 14:16:14 +00003234
3235 resizeFilterWindowSupport = (float) GetResizeFilterWindowSupport(resizeFilter);
cristy0c832c62014-03-07 22:21:04 +00003236 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterWindowSupport);
cristyf034abb2013-11-24 14:16:14 +00003237
3238 resizeFilterBlur = (float) GetResizeFilterBlur(resizeFilter);
cristy0c832c62014-03-07 22:21:04 +00003239 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterBlur);
cristyf034abb2013-11-24 14:16:14 +00003240
3241
cristy0c832c62014-03-07 22:21:04 +00003242 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);
cristyf034abb2013-11-24 14:16:14 +00003246
3247
cristy0c832c62014-03-07 22:21:04 +00003248 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);
cristyf034abb2013-11-24 14:16:14 +00003251
3252 if (clStatus != CL_SUCCESS)
3253 {
cristy0c832c62014-03-07 22:21:04 +00003254 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003255 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;
cristy0c832c62014-03-07 22:21:04 +00003263 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, horizontalKernel, 2, NULL, global_work_size, local_work_size, 0, NULL, NULL);
cristy9ff50022014-08-26 17:38:59 +00003264 (void) local_work_size;
cristyf034abb2013-11-24 14:16:14 +00003265 if (clStatus != CL_SUCCESS)
3266 {
cristy0c832c62014-03-07 22:21:04 +00003267 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003268 goto cleanup;
3269 }
cristy0c832c62014-03-07 22:21:04 +00003270 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00003271 status = MagickTrue;
3272
3273
3274cleanup:
cristya22457d2013-12-07 14:03:06 +00003275 OpenCLLogException(__FUNCTION__,__LINE__,exception);
3276
cristyf034abb2013-11-24 14:16:14 +00003277 if (horizontalKernel != NULL) RelinquishOpenCLKernel(clEnv, horizontalKernel);
3278
dirk832becc2014-08-04 19:44:34 +00003279 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;
dirk8a5cf512014-07-28 20:16:27 +00003332 status = MagickFalse;
cristyf034abb2013-11-24 14:16:14 +00003333
3334 /*
3335 Apply filter to resize vertically from image to resize image.
3336 */
cristye85d0f72013-11-27 02:25:43 +00003337 scale=MAGICK_MAX(1.0/yFactor+MagickEpsilon,1.0);
cristyf034abb2013-11-24 14:16:14 +00003338 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
dirke3c5f892013-12-10 06:04:40 +00003364DisableMSCWarning(4127)
cristyf034abb2013-11-24 14:16:14 +00003365 while(1)
dirke3c5f892013-12-10 06:04:40 +00003366RestoreMSCWarning
cristyf034abb2013-11-24 14:16:14 +00003367 {
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 {
cristya22457d2013-12-07 14:03:06 +00003416 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003417 goto cleanup;
3418 }
3419
3420 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00003421 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);
cristy0c832c62014-03-07 22:21:04 +00003424 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);
cristyf034abb2013-11-24 14:16:14 +00003427
cristy0c832c62014-03-07 22:21:04 +00003428 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&resizedColumns);
3429 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&resizedRows);
cristyf034abb2013-11-24 14:16:14 +00003430
cristy0c832c62014-03-07 22:21:04 +00003431 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);
cristyf034abb2013-11-24 14:16:14 +00003434
3435 resizeFilterScale = (float) GetResizeFilterScale(resizeFilter);
cristy0c832c62014-03-07 22:21:04 +00003436 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterScale);
cristyf034abb2013-11-24 14:16:14 +00003437
3438 resizeFilterSupport = (float) GetResizeFilterSupport(resizeFilter);
cristy0c832c62014-03-07 22:21:04 +00003439 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterSupport);
cristyf034abb2013-11-24 14:16:14 +00003440
3441 resizeFilterWindowSupport = (float) GetResizeFilterWindowSupport(resizeFilter);
cristy0c832c62014-03-07 22:21:04 +00003442 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterWindowSupport);
cristyf034abb2013-11-24 14:16:14 +00003443
3444 resizeFilterBlur = (float) GetResizeFilterBlur(resizeFilter);
cristy0c832c62014-03-07 22:21:04 +00003445 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterBlur);
cristyf034abb2013-11-24 14:16:14 +00003446
3447
cristy0c832c62014-03-07 22:21:04 +00003448 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);
cristyf034abb2013-11-24 14:16:14 +00003452
3453
cristy0c832c62014-03-07 22:21:04 +00003454 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);
cristyf034abb2013-11-24 14:16:14 +00003457
3458 if (clStatus != CL_SUCCESS)
3459 {
cristy0c832c62014-03-07 22:21:04 +00003460 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003461 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;
cristy0c832c62014-03-07 22:21:04 +00003469 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, horizontalKernel, 2, NULL, global_work_size, local_work_size, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00003470 if (clStatus != CL_SUCCESS)
3471 {
cristy0c832c62014-03-07 22:21:04 +00003472 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003473 goto cleanup;
3474 }
cristy0c832c62014-03-07 22:21:04 +00003475 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00003476 status = MagickTrue;
3477
3478
3479cleanup:
cristya22457d2013-12-07 14:03:06 +00003480 OpenCLLogException(__FUNCTION__,__LINE__,exception);
3481
cristyf034abb2013-11-24 14:16:14 +00003482 if (horizontalKernel != NULL) RelinquishOpenCLKernel(clEnv, horizontalKernel);
3483
dirk8a5cf512014-07-28 20:16:27 +00003484 return(status);
cristyf034abb2013-11-24 14:16:14 +00003485}
3486
dirk832becc2014-08-04 19:44:34 +00003487static 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);
dirk8a5cf512014-07-28 20:16:27 +00003560 inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00003561 if (inputPixels == (const void *) NULL)
3562 {
dirk8a5cf512014-07-28 20:16:27 +00003563 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristyf034abb2013-11-24 14:16:14 +00003564 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 */
dirk8a5cf512014-07-28 20:16:27 +00003579 length = image->columns * image->rows;
3580 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00003581 if (clStatus != CL_SUCCESS)
3582 {
cristy0c832c62014-03-07 22:21:04 +00003583 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00003584 goto cleanup;
3585 }
3586
cristy0c832c62014-03-07 22:21:04 +00003587 cubicCoefficientsBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_ONLY, 7 * sizeof(float), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00003588 if (clStatus != CL_SUCCESS)
3589 {
cristy0c832c62014-03-07 22:21:04 +00003590 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00003591 goto cleanup;
3592 }
3593 queue = AcquireOpenCLCommandQueue(clEnv);
cristy0c832c62014-03-07 22:21:04 +00003594 mappedCoefficientBuffer = (float*)clEnv->library->clEnqueueMapBuffer(queue, cubicCoefficientsBuffer, CL_TRUE, CL_MAP_WRITE, 0, 7 * sizeof(float)
cristyf034abb2013-11-24 14:16:14 +00003595 , 0, NULL, NULL, &clStatus);
3596 if (clStatus != CL_SUCCESS)
3597 {
cristy0c832c62014-03-07 22:21:04 +00003598 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueMapBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00003599 goto cleanup;
3600 }
3601 resizeFilterCoefficient = GetResizeFilterCoefficient(resizeFilter);
3602 for (i = 0; i < 7; i++)
3603 {
3604 mappedCoefficientBuffer[i] = (float) resizeFilterCoefficient[i];
3605 }
cristy0c832c62014-03-07 22:21:04 +00003606 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, cubicCoefficientsBuffer, mappedCoefficientBuffer, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00003607 if (clStatus != CL_SUCCESS)
3608 {
cristy0c832c62014-03-07 22:21:04 +00003609 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003610 goto cleanup;
3611 }
3612
dirk8a5cf512014-07-28 20:16:27 +00003613 filteredImage = CloneImage(image,resizedColumns,resizedRows,MagickTrue,exception);
cristyf034abb2013-11-24 14:16:14 +00003614 if (filteredImage == NULL)
3615 goto cleanup;
3616
dirk8a5cf512014-07-28 20:16:27 +00003617 if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
cristyf034abb2013-11-24 14:16:14 +00003618 {
cristya22457d2013-12-07 14:03:06 +00003619 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003620 goto cleanup;
3621 }
dirk832becc2014-08-04 19:44:34 +00003622 filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
dirk8a5cf512014-07-28 20:16:27 +00003623 filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00003624 if (filteredPixels == (void *) NULL)
3625 {
cristya22457d2013-12-07 14:03:06 +00003626 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
cristyf034abb2013-11-24 14:16:14 +00003627 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;
cristy0c832c62014-03-07 22:21:04 +00003643 filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00003644 if (clStatus != CL_SUCCESS)
3645 {
cristy0c832c62014-03-07 22:21:04 +00003646 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00003647 goto cleanup;
3648 }
3649
dirk8a5cf512014-07-28 20:16:27 +00003650 xFactor=(float) resizedColumns/(float) image->columns;
3651 yFactor=(float) resizedRows/(float) image->rows;
cristyf034abb2013-11-24 14:16:14 +00003652 if (xFactor > yFactor)
3653 {
3654
dirk8a5cf512014-07-28 20:16:27 +00003655 length = resizedColumns*image->rows;
cristy0c832c62014-03-07 22:21:04 +00003656 tempImageBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_WRITE, length*sizeof(CLPixelPacket), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00003657 if (clStatus != CL_SUCCESS)
3658 {
cristy0c832c62014-03-07 22:21:04 +00003659 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00003660 goto cleanup;
3661 }
3662
dirk8a5cf512014-07-28 20:16:27 +00003663 status = resizeHorizontalFilter(imageBuffer, image->columns, image->rows, (image->alpha_trait==BlendPixelTrait)?1:0
3664 , tempImageBuffer, resizedColumns, image->rows
cristyf034abb2013-11-24 14:16:14 +00003665 , resizeFilter, cubicCoefficientsBuffer
3666 , xFactor, clEnv, queue, exception);
3667 if (status != MagickTrue)
3668 goto cleanup;
3669
dirk8a5cf512014-07-28 20:16:27 +00003670 status = resizeVerticalFilter(tempImageBuffer, resizedColumns, image->rows, (image->alpha_trait==BlendPixelTrait)?1:0
cristyf034abb2013-11-24 14:16:14 +00003671 , filteredImageBuffer, resizedColumns, resizedRows
3672 , resizeFilter, cubicCoefficientsBuffer
3673 , yFactor, clEnv, queue, exception);
3674 if (status != MagickTrue)
3675 goto cleanup;
3676 }
3677 else
3678 {
dirk8a5cf512014-07-28 20:16:27 +00003679 length = image->columns*resizedRows;
cristy0c832c62014-03-07 22:21:04 +00003680 tempImageBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_WRITE, length*sizeof(CLPixelPacket), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00003681 if (clStatus != CL_SUCCESS)
3682 {
cristy0c832c62014-03-07 22:21:04 +00003683 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00003684 goto cleanup;
3685 }
3686
dirk8a5cf512014-07-28 20:16:27 +00003687 status = resizeVerticalFilter(imageBuffer, image->columns, image->rows, (image->alpha_trait==BlendPixelTrait)?1:0
3688 , tempImageBuffer, image->columns, resizedRows
cristyf034abb2013-11-24 14:16:14 +00003689 , resizeFilter, cubicCoefficientsBuffer
3690 , yFactor, clEnv, queue, exception);
3691 if (status != MagickTrue)
3692 goto cleanup;
3693
dirk8a5cf512014-07-28 20:16:27 +00003694 status = resizeHorizontalFilter(tempImageBuffer, image->columns, resizedRows, (image->alpha_trait==BlendPixelTrait)?1:0
cristyf034abb2013-11-24 14:16:14 +00003695 , filteredImageBuffer, resizedColumns, 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 {
cristy0c832c62014-03-07 22:21:04 +00003704 clEnv->library->clEnqueueMapBuffer(queue, filteredImageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00003705 }
3706 else
3707 {
cristy0c832c62014-03-07 22:21:04 +00003708 clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00003709 }
3710 if (clStatus != CL_SUCCESS)
3711 {
cristya22457d2013-12-07 14:03:06 +00003712 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003713 goto cleanup;
3714 }
dirk832becc2014-08-04 19:44:34 +00003715 outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
3716
3717cleanup:
3718 OpenCLLogException(__FUNCTION__,__LINE__,exception);
3719
3720 image_view=DestroyCacheView(image_view);
3721 if (filteredImage_view != NULL)
dirk8a5cf512014-07-28 20:16:27 +00003722 filteredImage_view=DestroyCacheView(filteredImage_view);
cristyf034abb2013-11-24 14:16:14 +00003723
dirk8a5cf512014-07-28 20:16:27 +00003724 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00003725 if (tempImageBuffer!=NULL) clEnv->library->clReleaseMemObject(tempImageBuffer);
3726 if (filteredImageBuffer!=NULL) clEnv->library->clReleaseMemObject(filteredImageBuffer);
3727 if (cubicCoefficientsBuffer!=NULL) clEnv->library->clReleaseMemObject(cubicCoefficientsBuffer);
cristyf034abb2013-11-24 14:16:14 +00003728 if (queue != NULL) RelinquishOpenCLCommandQueue(clEnv, queue);
dirk8a5cf512014-07-28 20:16:27 +00003729 if (outputReady == MagickFalse && filteredImage != NULL)
3730 filteredImage=DestroyImage(filteredImage);
3731 return(filteredImage);
cristyf034abb2013-11-24 14:16:14 +00003732}
3733
dirk832becc2014-08-04 19:44:34 +00003734const ResizeWeightingFunctionType supportedResizeWeighting[] =
3735{
3736 BoxWeightingFunction,
3737 TriangleWeightingFunction,
3738 HanningWeightingFunction,
3739 HammingWeightingFunction,
3740 BlackmanWeightingFunction,
3741 CubicBCWeightingFunction,
3742 SincWeightingFunction,
3743 SincFastWeightingFunction,
3744 LastWeightingFunction
cristyf034abb2013-11-24 14:16:14 +00003745};
3746
dirk832becc2014-08-04 19:44:34 +00003747static 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);
cristyf034abb2013-11-24 14:16:14 +00003783}
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
dirk832becc2014-08-04 19:44:34 +00003813static 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);
dirk8a5cf512014-07-28 20:16:27 +00003869 inputPixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00003870 if (inputPixels == (void *) NULL)
cristyd43a46b2010-01-21 02:13:41 +00003871 {
dirk8a5cf512014-07-28 20:16:27 +00003872 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristyf034abb2013-11-24 14:16:14 +00003873 goto cleanup;
cristyd43a46b2010-01-21 02:13:41 +00003874 }
cristyf034abb2013-11-24 14:16:14 +00003875
3876 /* If the host pointer is aligned to the size of CLPixelPacket,
dirk8a5cf512014-07-28 20:16:27 +00003877 then use the host buffer directly from the GPU; otherwise,
3878 create a buffer on the GPU and copy the data over */
cristyf034abb2013-11-24 14:16:14 +00003879 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 */
dirk8a5cf512014-07-28 20:16:27 +00003888 length = image->columns * image->rows;
3889 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00003890 if (clStatus != CL_SUCCESS)
3891 {
cristy0c832c62014-03-07 22:21:04 +00003892 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00003893 goto cleanup;
3894 }
dirk8a5cf512014-07-28 20:16:27 +00003895
3896 filterKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "Contrast");
3897 if (filterKernel == NULL)
cristyf034abb2013-11-24 14:16:14 +00003898 {
cristya22457d2013-12-07 14:03:06 +00003899 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003900 goto cleanup;
3901 }
3902
cristyf034abb2013-11-24 14:16:14 +00003903 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00003904 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);
cristyf034abb2013-11-24 14:16:14 +00003908 if (clStatus != CL_SUCCESS)
3909 {
cristy0c832c62014-03-07 22:21:04 +00003910 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003911 goto cleanup;
3912 }
3913
dirk8a5cf512014-07-28 20:16:27 +00003914 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)
cristyf034abb2013-11-24 14:16:14 +00003920 {
dirk8a5cf512014-07-28 20:16:27 +00003921 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
3922 goto cleanup;
cristyf034abb2013-11-24 14:16:14 +00003923 }
dirk8a5cf512014-07-28 20:16:27 +00003924 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00003925
3926 if (ALIGNED(inputPixels,CLPixelPacket))
3927 {
dirk8a5cf512014-07-28 20:16:27 +00003928 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);
cristyf034abb2013-11-24 14:16:14 +00003930 }
3931 else
3932 {
dirk8a5cf512014-07-28 20:16:27 +00003933 length = image->columns * image->rows;
3934 clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), inputPixels, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00003935 }
3936 if (clStatus != CL_SUCCESS)
3937 {
cristya22457d2013-12-07 14:03:06 +00003938 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003939 goto cleanup;
3940 }
dirk832becc2014-08-04 19:44:34 +00003941 outputReady=SyncCacheViewAuthenticPixels(image_view,exception);
3942
3943cleanup:
3944 OpenCLLogException(__FUNCTION__,__LINE__,exception);
3945
dirk8a5cf512014-07-28 20:16:27 +00003946 image_view=DestroyCacheView(image_view);
cristyf034abb2013-11-24 14:16:14 +00003947
dirk8a5cf512014-07-28 20:16:27 +00003948 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
3949 if (filterKernel!=NULL) RelinquishOpenCLKernel(clEnv, filterKernel);
3950 if (queue != NULL) RelinquishOpenCLCommandQueue(clEnv, queue);
3951 return(outputReady);
dirk832becc2014-08-04 19:44:34 +00003952}
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);
cristy3f6d1482010-01-20 21:01:21 +00003969}
cristyf034abb2013-11-24 14:16:14 +00003970
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
dirk832becc2014-08-04 19:44:34 +00004001MagickBooleanType 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);
dirk8a5cf512014-07-28 20:16:27 +00004070 inputPixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
cristy0c832c62014-03-07 22:21:04 +00004071 if (inputPixels == (void *) NULL)
4072 {
dirk8a5cf512014-07-28 20:16:27 +00004073 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristy0c832c62014-03-07 22:21:04 +00004074 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 */
dirk8a5cf512014-07-28 20:16:27 +00004090 length = image->columns * image->rows;
4091 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristy0c832c62014-03-07 22:21:04 +00004092 if (clStatus != CL_SUCCESS)
4093 {
4094 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
4095 goto cleanup;
4096 }
4097
dirk8a5cf512014-07-28 20:16:27 +00004098 modulateKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "Modulate");
4099 if (modulateKernel == NULL)
cristy0c832c62014-03-07 22:21:04 +00004100 {
4101 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
4102 goto cleanup;
4103 }
4104
dirk8a5cf512014-07-28 20:16:27 +00004105 bright=percent_brightness;
4106 hue=percent_hue;
4107 saturation=percent_saturation;
4108 color=colorspace;
4109
cristy0c832c62014-03-07 22:21:04 +00004110 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00004111 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);
cristy0c832c62014-03-07 22:21:04 +00004116 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];
dirk8a5cf512014-07-28 20:16:27 +00004125 global_work_size[0] = image->columns;
4126 global_work_size[1] = image->rows;
cristy0c832c62014-03-07 22:21:04 +00004127 /* launch the kernel */
dirk8a5cf512014-07-28 20:16:27 +00004128 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, modulateKernel, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristy0c832c62014-03-07 22:21:04 +00004129 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 {
dirk8a5cf512014-07-28 20:16:27 +00004139 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);
cristy0c832c62014-03-07 22:21:04 +00004141 }
4142 else
4143 {
dirk8a5cf512014-07-28 20:16:27 +00004144 length = image->columns * image->rows;
4145 clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), inputPixels, 0, NULL, NULL);
cristy0c832c62014-03-07 22:21:04 +00004146 }
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
dirk832becc2014-08-04 19:44:34 +00004153 outputReady=SyncCacheViewAuthenticPixels(image_view,exception);
4154
4155cleanup:
4156 OpenCLLogException(__FUNCTION__,__LINE__,exception);
4157
dirk8a5cf512014-07-28 20:16:27 +00004158 image_view=DestroyCacheView(image_view);
cristy0c832c62014-03-07 22:21:04 +00004159
dirk8a5cf512014-07-28 20:16:27 +00004160 if (imageBuffer!=NULL)
4161 clEnv->library->clReleaseMemObject(imageBuffer);
4162 if (modulateKernel!=NULL)
4163 RelinquishOpenCLKernel(clEnv, modulateKernel);
cristy0c832c62014-03-07 22:21:04 +00004164 if (queue != NULL)
4165 RelinquishOpenCLCommandQueue(clEnv, queue);
4166
4167 return outputReady;
4168
4169}
4170
dirk832becc2014-08-04 19:44:34 +00004171MagickExport 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);
dirk8a5cf512014-07-28 20:16:27 +00004190}
cristy0c832c62014-03-07 22:21:04 +00004191
4192/*
4193%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4194% %
4195% %
4196% %
4197% N e g a t e I m a g e w i t h O p e n C L %
4198% %
4199% %
4200% %
4201%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4202%
4203%
4204% A description of each parameter follows:
4205%
4206% o image: the image.
4207%
4208% o channel: the channel.
4209%
4210% o grayscale: If MagickTrue, only negate grayscale pixels within the image.
4211%
4212*/
4213
dirk832becc2014-08-04 19:44:34 +00004214MagickBooleanType ComputeNegateImageChannel(Image *image,
4215 const ChannelType channel,const MagickBooleanType magick_unused(grayscale),
4216 ExceptionInfo* exception)
4217{
4218 CacheView
4219 *image_view;
4220
4221 cl_context
4222 context;
4223
4224 cl_command_queue
4225 queue;
4226
4227 cl_int
4228 clStatus;
4229
4230 cl_kernel
4231 negateKernel;
4232
4233 cl_mem
4234 imageBuffer;
4235
4236 cl_mem_flags
4237 mem_flags;
4238
4239 MagickBooleanType
4240 outputReady;
4241
4242 MagickCLEnv
4243 clEnv;
4244
4245 MagickSizeType
4246 length;
4247
4248 register ssize_t
4249 i;
4250
4251 void
4252 *inputPixels;
4253
4254 magick_unreferenced(grayscale);
4255
4256 inputPixels = NULL;
4257 imageBuffer = NULL;
4258 negateKernel = NULL;
4259
4260 assert(image != (Image *) NULL);
4261 assert(image->signature == MagickSignature);
4262 if (image->debug != MagickFalse)
4263 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4264
4265 /*
4266 * initialize opencl env
4267 */
4268 clEnv = GetDefaultOpenCLEnv();
4269 context = GetOpenCLContext(clEnv);
4270 queue = AcquireOpenCLCommandQueue(clEnv);
4271
4272 outputReady = MagickFalse;
4273
4274 /* Create and initialize OpenCL buffers.
4275 inputPixels = AcquirePixelCachePixels(image, &length, exception);
4276 assume this will get a writable image
4277 */
4278 image_view=AcquireAuthenticCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00004279 inputPixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
cristy0c832c62014-03-07 22:21:04 +00004280 if (inputPixels == (void *) NULL)
4281 {
dirk8a5cf512014-07-28 20:16:27 +00004282 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristy0c832c62014-03-07 22:21:04 +00004283 goto cleanup;
4284 }
4285
4286 /* If the host pointer is aligned to the size of CLPixelPacket,
4287 then use the host buffer directly from the GPU; otherwise,
4288 create a buffer on the GPU and copy the data over
4289 */
4290 if (ALIGNED(inputPixels,CLPixelPacket))
4291 {
4292 mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
4293 }
4294 else
4295 {
4296 mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
4297 }
4298 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00004299 length = image->columns * image->rows;
4300 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristy0c832c62014-03-07 22:21:04 +00004301 if (clStatus != CL_SUCCESS)
4302 {
4303 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
4304 goto cleanup;
4305 }
4306
dirk8a5cf512014-07-28 20:16:27 +00004307 negateKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "Negate");
4308 if (negateKernel == NULL)
cristy0c832c62014-03-07 22:21:04 +00004309 {
4310 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
4311 goto cleanup;
4312 }
4313
4314 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00004315 clStatus=clEnv->library->clSetKernelArg(negateKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
4316 clStatus=clEnv->library->clSetKernelArg(negateKernel,i++,sizeof(ChannelType),(void *)&channel);
cristy0c832c62014-03-07 22:21:04 +00004317 if (clStatus != CL_SUCCESS)
4318 {
4319 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
4320 printf("no kernel\n");
4321 goto cleanup;
4322 }
4323
4324 {
4325 size_t global_work_size[2];
dirk8a5cf512014-07-28 20:16:27 +00004326 global_work_size[0] = image->columns;
4327 global_work_size[1] = image->rows;
cristy0c832c62014-03-07 22:21:04 +00004328 /* launch the kernel */
dirk8a5cf512014-07-28 20:16:27 +00004329 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, negateKernel, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristy0c832c62014-03-07 22:21:04 +00004330 if (clStatus != CL_SUCCESS)
4331 {
4332 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
4333 goto cleanup;
4334 }
4335 clEnv->library->clFlush(queue);
4336 }
4337
4338 if (ALIGNED(inputPixels,CLPixelPacket))
4339 {
dirk8a5cf512014-07-28 20:16:27 +00004340 length = image->columns * image->rows;
4341 clEnv->library->clEnqueueMapBuffer(queue, imageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
cristy0c832c62014-03-07 22:21:04 +00004342 }
4343 else
4344 {
dirk8a5cf512014-07-28 20:16:27 +00004345 length = image->columns * image->rows;
4346 clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), inputPixels, 0, NULL, NULL);
cristy0c832c62014-03-07 22:21:04 +00004347 }
4348 if (clStatus != CL_SUCCESS)
4349 {
4350 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
4351 goto cleanup;
4352 }
4353
dirk832becc2014-08-04 19:44:34 +00004354 outputReady=SyncCacheViewAuthenticPixels(image_view,exception);
4355
4356cleanup:
4357 OpenCLLogException(__FUNCTION__,__LINE__,exception);
4358
dirk8a5cf512014-07-28 20:16:27 +00004359 image_view=DestroyCacheView(image_view);
cristy0c832c62014-03-07 22:21:04 +00004360
dirk8a5cf512014-07-28 20:16:27 +00004361 if (imageBuffer!=NULL)
4362 clEnv->library->clReleaseMemObject(imageBuffer);
4363 if (negateKernel!=NULL)
4364 RelinquishOpenCLKernel(clEnv, negateKernel);
cristy0c832c62014-03-07 22:21:04 +00004365 if (queue != NULL)
4366 RelinquishOpenCLCommandQueue(clEnv, queue);
4367
dirk8a5cf512014-07-28 20:16:27 +00004368 return(outputReady);
dirk832becc2014-08-04 19:44:34 +00004369}
4370
4371MagickExport MagickBooleanType AccelerateNegateImageChannel(Image *image,
4372 const ChannelType channel,const MagickBooleanType grayscale,
4373 ExceptionInfo* exception)
4374{
4375 MagickBooleanType
4376 status;
4377
4378 assert(image != NULL);
4379 assert(exception != (ExceptionInfo *) NULL);
4380
4381 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
4382 (checkAccelerateCondition(image, channel) == MagickFalse))
4383 return(MagickFalse);
4384
4385 status=ComputeNegateImageChannel(image,channel,grayscale,exception);
4386 return(status);
cristy0c832c62014-03-07 22:21:04 +00004387}
dirk8a5cf512014-07-28 20:16:27 +00004388
cristy0c832c62014-03-07 22:21:04 +00004389/*
4390%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4391% %
4392% %
4393% %
4394% G r a y s c a l e I m a g e w i t h O p e n C L %
4395% %
4396% %
4397% %
4398%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4399%
4400% GrayscaleImage() converts the colors in the reference image to gray.
4401%
4402% The format of the GrayscaleImageChannel method is:
4403%
4404% MagickBooleanType GrayscaleImage(Image *image,
4405% const PixelIntensityMethod method)
4406%
4407% A description of each parameter follows:
4408%
4409% o image: the image.
4410%
4411% o channel: the channel.
4412%
4413*/
4414
dirk832becc2014-08-04 19:44:34 +00004415MagickBooleanType ComputeGrayscaleImage(Image *image,
4416 const PixelIntensityMethod method,ExceptionInfo *exception)
4417{
4418 CacheView
4419 *image_view;
4420
4421 cl_command_queue
4422 queue;
4423
4424 cl_context
4425 context;
4426
4427 cl_int
4428 clStatus,
4429 intensityMethod;
4430
4431 cl_int
4432 colorspace;
4433
4434 cl_kernel
4435 grayscaleKernel;
4436
4437 cl_mem
4438 imageBuffer;
4439
4440 cl_mem_flags
4441 mem_flags;
4442
4443 MagickBooleanType
4444 outputReady;
4445
4446 MagickCLEnv
4447 clEnv;
4448
4449 MagickSizeType
4450 length;
4451
4452 register ssize_t
4453 i;
4454
4455 void
4456 *inputPixels;
4457
4458 inputPixels = NULL;
4459 imageBuffer = NULL;
4460 grayscaleKernel = NULL;
4461
4462 assert(image != (Image *) NULL);
4463 assert(image->signature == MagickSignature);
4464 if (image->debug != MagickFalse)
4465 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4466
4467 /*
4468 * initialize opencl env
4469 */
4470 clEnv = GetDefaultOpenCLEnv();
4471 context = GetOpenCLContext(clEnv);
4472 queue = AcquireOpenCLCommandQueue(clEnv);
4473
4474 outputReady = MagickFalse;
4475
4476 /* Create and initialize OpenCL buffers.
4477 inputPixels = AcquirePixelCachePixels(image, &length, exception);
4478 assume this will get a writable image
4479 */
4480 image_view=AcquireAuthenticCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00004481 inputPixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
4482 if (inputPixels == (void *) NULL)
4483 {
4484 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
4485 goto cleanup;
4486 }
cristy0c832c62014-03-07 22:21:04 +00004487
dirk8a5cf512014-07-28 20:16:27 +00004488 /* If the host pointer is aligned to the size of CLPixelPacket,
4489 then use the host buffer directly from the GPU; otherwise,
4490 create a buffer on the GPU and copy the data over
4491 */
4492 if (ALIGNED(inputPixels,CLPixelPacket))
4493 {
4494 mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
4495 }
4496 else
4497 {
4498 mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
4499 }
4500 /* create a CL buffer from image pixel buffer */
4501 length = image->columns * image->rows;
4502 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
4503 if (clStatus != CL_SUCCESS)
4504 {
4505 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
4506 goto cleanup;
4507 }
cristy0c832c62014-03-07 22:21:04 +00004508
dirk8a5cf512014-07-28 20:16:27 +00004509 intensityMethod = method;
4510 colorspace = image->colorspace;
cristy0c832c62014-03-07 22:21:04 +00004511
dirk8a5cf512014-07-28 20:16:27 +00004512 grayscaleKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "Grayscale");
4513 if (grayscaleKernel == NULL)
4514 {
4515 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
4516 goto cleanup;
4517 }
cristy0c832c62014-03-07 22:21:04 +00004518
dirk8a5cf512014-07-28 20:16:27 +00004519 i = 0;
4520 clStatus=clEnv->library->clSetKernelArg(grayscaleKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
4521 clStatus|=clEnv->library->clSetKernelArg(grayscaleKernel,i++,sizeof(cl_int),&intensityMethod);
4522 clStatus|=clEnv->library->clSetKernelArg(grayscaleKernel,i++,sizeof(cl_int),&colorspace);
4523 if (clStatus != CL_SUCCESS)
4524 {
4525 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
4526 printf("no kernel\n");
4527 goto cleanup;
4528 }
cristy0c832c62014-03-07 22:21:04 +00004529
dirk8a5cf512014-07-28 20:16:27 +00004530 {
4531 size_t global_work_size[2];
4532 global_work_size[0] = image->columns;
4533 global_work_size[1] = image->rows;
4534 /* launch the kernel */
4535 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, grayscaleKernel, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
4536 if (clStatus != CL_SUCCESS)
4537 {
4538 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
4539 goto cleanup;
4540 }
4541 clEnv->library->clFlush(queue);
4542 }
cristy0c832c62014-03-07 22:21:04 +00004543
dirk8a5cf512014-07-28 20:16:27 +00004544 if (ALIGNED(inputPixels,CLPixelPacket))
4545 {
4546 length = image->columns * image->rows;
4547 clEnv->library->clEnqueueMapBuffer(queue, imageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
4548 }
4549 else
4550 {
4551 length = image->columns * image->rows;
4552 clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), inputPixels, 0, NULL, NULL);
4553 }
4554 if (clStatus != CL_SUCCESS)
4555 {
4556 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
4557 goto cleanup;
4558 }
cristy0c832c62014-03-07 22:21:04 +00004559
dirk832becc2014-08-04 19:44:34 +00004560 outputReady=SyncCacheViewAuthenticPixels(image_view,exception);
4561
4562cleanup:
4563 OpenCLLogException(__FUNCTION__,__LINE__,exception);
4564
dirk8a5cf512014-07-28 20:16:27 +00004565 image_view=DestroyCacheView(image_view);
4566
4567 if (imageBuffer!=NULL)
4568 clEnv->library->clReleaseMemObject(imageBuffer);
4569 if (grayscaleKernel!=NULL)
4570 RelinquishOpenCLKernel(clEnv, grayscaleKernel);
4571 if (queue != NULL)
4572 RelinquishOpenCLCommandQueue(clEnv, queue);
4573
4574 return( outputReady);
dirk832becc2014-08-04 19:44:34 +00004575}
4576
4577MagickExport MagickBooleanType AccelerateGrayscaleImage(Image* image,
4578 const PixelIntensityMethod method,ExceptionInfo *exception)
4579{
4580 MagickBooleanType
4581 status;
4582
4583 assert(image != NULL);
4584 assert(exception != (ExceptionInfo *) NULL);
4585
4586 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
4587 (checkAccelerateCondition(image, AllChannels) == MagickFalse))
4588 return(MagickFalse);
4589
4590 if (method == Rec601LuminancePixelIntensityMethod || method == Rec709LuminancePixelIntensityMethod)
4591 return(MagickFalse);
4592
4593 if (image->colorspace != sRGBColorspace)
4594 return(MagickFalse);
4595
4596 status=ComputeGrayscaleImage(image,method,exception);
4597 return(status);
cristy0c832c62014-03-07 22:21:04 +00004598}
4599
dirk8a5cf512014-07-28 20:16:27 +00004600/*
4601%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4602% %
4603% %
4604% %
4605% E q u a l i z e I m a g e w i t h O p e n C L %
4606% %
4607% %
4608% %
4609%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4610%
4611% EqualizeImage() applies a histogram equalization to the image.
4612%
4613% The format of the EqualizeImage method is:
4614%
4615% MagickBooleanType EqualizeImage(Image *image)
4616% MagickBooleanType EqualizeImageChannel(Image *image,
4617% const ChannelType channel)
4618%
4619% A description of each parameter follows:
4620%
4621% o image: the image.
4622%
4623% o channel: the channel.
4624%
4625*/
cristy0c832c62014-03-07 22:21:04 +00004626
dirk832becc2014-08-04 19:44:34 +00004627static MagickBooleanType LaunchHistogramKernel(MagickCLEnv clEnv,
4628 cl_command_queue queue,cl_mem imageBuffer,cl_mem histogramBuffer,
4629 Image *image,const ChannelType channel,ExceptionInfo *exception)
4630{
4631 MagickBooleanType
4632 outputReady;
4633
4634 cl_int
4635 clStatus,
4636 colorspace,
4637 method;
4638
4639 cl_kernel
4640 histogramKernel;
4641
4642 register ssize_t
4643 i;
4644
4645 size_t
4646 global_work_size[2];
4647
cristy0c832c62014-03-07 22:21:04 +00004648 histogramKernel = NULL;
4649
4650 outputReady = MagickFalse;
dirk8a5cf512014-07-28 20:16:27 +00004651 method = image->intensity;
4652 colorspace = image->colorspace;
cristy0c832c62014-03-07 22:21:04 +00004653
4654 /* get the OpenCL kernel */
4655 histogramKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "Histogram");
4656 if (histogramKernel == NULL)
4657 {
4658 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
4659 goto cleanup;
4660 }
4661
4662 /* set the kernel arguments */
4663 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00004664 clStatus=clEnv->library->clSetKernelArg(histogramKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00004665 clStatus|=clEnv->library->clSetKernelArg(histogramKernel,i++,sizeof(ChannelType),&channel);
4666 clStatus|=clEnv->library->clSetKernelArg(histogramKernel,i++,sizeof(cl_int),&method);
4667 clStatus|=clEnv->library->clSetKernelArg(histogramKernel,i++,sizeof(cl_int),&colorspace);
4668 clStatus|=clEnv->library->clSetKernelArg(histogramKernel,i++,sizeof(cl_mem),(void *)&histogramBuffer);
4669 if (clStatus != CL_SUCCESS)
4670 {
4671 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
4672 goto cleanup;
4673 }
4674
4675 /* launch the kernel */
dirk8a5cf512014-07-28 20:16:27 +00004676 global_work_size[0] = image->columns;
4677 global_work_size[1] = image->rows;
cristy0c832c62014-03-07 22:21:04 +00004678
4679 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, histogramKernel, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
4680
4681 if (clStatus != CL_SUCCESS)
4682 {
4683 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
4684 goto cleanup;
4685 }
4686 clEnv->library->clFlush(queue);
4687
4688 outputReady = MagickTrue;
4689
4690cleanup:
4691 OpenCLLogException(__FUNCTION__,__LINE__,exception);
4692
4693 if (histogramKernel!=NULL)
4694 RelinquishOpenCLKernel(clEnv, histogramKernel);
4695
dirk8a5cf512014-07-28 20:16:27 +00004696 return(outputReady);
cristy0c832c62014-03-07 22:21:04 +00004697}
4698
dirk832becc2014-08-04 19:44:34 +00004699MagickExport MagickBooleanType ComputeEqualizeImage(Image *image,
4700 const ChannelType channel,ExceptionInfo *exception)
4701{
4702#define EqualizeImageTag "Equalize/Image"
4703
4704 CacheView
4705 *image_view;
4706
4707 cl_command_queue
4708 queue;
4709
4710 cl_context
4711 context;
4712
4713 cl_int
4714 clStatus;
4715
4716 cl_mem_flags
4717 mem_flags;
4718
4719 cl_mem
4720 equalizeMapBuffer,
4721 histogramBuffer,
4722 imageBuffer;
4723
4724 cl_kernel
4725 equalizeKernel,
4726 histogramKernel;
4727
4728 cl_uint4
4729 *histogram;
4730
4731 FloatPixelPacket
4732 white,
4733 black,
4734 intensity,
4735 *map;
4736
4737 MagickBooleanType
4738 outputReady,
4739 status;
4740
4741 MagickCLEnv
4742 clEnv;
4743
4744 MagickSizeType
4745 length;
4746
4747 PixelPacket
4748 *equalize_map;
4749
4750 register ssize_t
4751 i;
4752
4753 size_t
4754 global_work_size[2];
4755
4756 void
4757 *hostPtr,
4758 *inputPixels;
4759
4760 map=NULL;
4761 histogram=NULL;
4762 equalize_map=NULL;
4763 inputPixels = NULL;
4764 imageBuffer = NULL;
4765 histogramBuffer = NULL;
4766 equalizeMapBuffer = NULL;
4767 histogramKernel = NULL;
4768 equalizeKernel = NULL;
4769 context = NULL;
4770 queue = NULL;
4771 outputReady = MagickFalse;
4772
4773 assert(image != (Image *) NULL);
4774 assert(image->signature == MagickSignature);
4775 if (image->debug != MagickFalse)
4776 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4777
4778 /*
4779 * initialize opencl env
4780 */
4781 clEnv = GetDefaultOpenCLEnv();
4782 context = GetOpenCLContext(clEnv);
4783 queue = AcquireOpenCLCommandQueue(clEnv);
4784
4785 /*
4786 Allocate and initialize histogram arrays.
4787 */
4788 histogram=(cl_uint4 *) AcquireQuantumMemory(MaxMap+1UL, sizeof(*histogram));
4789 if (histogram == (cl_uint4 *) NULL)
4790 ThrowBinaryException(ResourceLimitWarning,"MemoryAllocationFailed", image->filename);
4791
4792 /* reset histogram */
4793 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
4794
4795 /* Create and initialize OpenCL buffers. */
4796 /* inputPixels = AcquirePixelCachePixels(image, &length, exception); */
4797 /* assume this will get a writable image */
4798 image_view=AcquireAuthenticCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00004799 inputPixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00004800
4801 if (inputPixels == (void *) NULL)
4802 {
dirk8a5cf512014-07-28 20:16:27 +00004803 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristyf034abb2013-11-24 14:16:14 +00004804 goto cleanup;
4805 }
4806 /* If the host pointer is aligned to the size of CLPixelPacket,
4807 then use the host buffer directly from the GPU; otherwise,
4808 create a buffer on the GPU and copy the data over */
4809 if (ALIGNED(inputPixels,CLPixelPacket))
4810 {
4811 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
4812 }
4813 else
4814 {
4815 mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
4816 }
4817 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00004818 length = image->columns * image->rows;
4819 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00004820 if (clStatus != CL_SUCCESS)
4821 {
cristy0c832c62014-03-07 22:21:04 +00004822 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00004823 goto cleanup;
4824 }
cristy0c832c62014-03-07 22:21:04 +00004825
cristyf034abb2013-11-24 14:16:14 +00004826 /* If the host pointer is aligned to the size of cl_uint,
4827 then use the host buffer directly from the GPU; otherwise,
4828 create a buffer on the GPU and copy the data over */
4829 if (ALIGNED(histogram,cl_uint4))
4830 {
4831 mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
4832 hostPtr = histogram;
4833 }
4834 else
4835 {
4836 mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
4837 hostPtr = histogram;
4838 }
4839 /* create a CL buffer for histogram */
4840 length = (MaxMap+1);
cristy0c832c62014-03-07 22:21:04 +00004841 histogramBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(cl_uint4), hostPtr, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00004842 if (clStatus != CL_SUCCESS)
4843 {
cristy0c832c62014-03-07 22:21:04 +00004844 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00004845 goto cleanup;
4846 }
4847
dirk8a5cf512014-07-28 20:16:27 +00004848 status = LaunchHistogramKernel(clEnv, queue, imageBuffer, histogramBuffer, image, channel, exception);
cristy0c832c62014-03-07 22:21:04 +00004849 if (status == MagickFalse)
cristyf034abb2013-11-24 14:16:14 +00004850 goto cleanup;
cristyf034abb2013-11-24 14:16:14 +00004851
4852 /* read from the kenel output */
4853 if (ALIGNED(histogram,cl_uint4))
4854 {
4855 length = (MaxMap+1);
cristy0c832c62014-03-07 22:21:04 +00004856 clEnv->library->clEnqueueMapBuffer(queue, histogramBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(cl_uint4), 0, NULL, NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00004857 }
4858 else
4859 {
4860 length = (MaxMap+1);
cristy0c832c62014-03-07 22:21:04 +00004861 clStatus = clEnv->library->clEnqueueReadBuffer(queue, histogramBuffer, CL_TRUE, 0, length * sizeof(cl_uint4), histogram, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00004862 }
4863 if (clStatus != CL_SUCCESS)
4864 {
cristya22457d2013-12-07 14:03:06 +00004865 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00004866 goto cleanup;
4867 }
4868
4869 /* unmap, don't block gpu to use this buffer again. */
4870 if (ALIGNED(histogram,cl_uint4))
4871 {
cristy0c832c62014-03-07 22:21:04 +00004872 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, histogramBuffer, histogram, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00004873 if (clStatus != CL_SUCCESS)
4874 {
cristy0c832c62014-03-07 22:21:04 +00004875 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00004876 goto cleanup;
4877 }
4878 }
4879
cristy0c832c62014-03-07 22:21:04 +00004880 /* recreate input buffer later, in case image updated */
4881#ifdef RECREATEBUFFER
dirk8a5cf512014-07-28 20:16:27 +00004882 if (imageBuffer!=NULL)
4883 clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00004884#endif
4885
4886 /* CPU stuff */
cristyf034abb2013-11-24 14:16:14 +00004887 equalize_map=(PixelPacket *) AcquireQuantumMemory(MaxMap+1UL, sizeof(*equalize_map));
4888 if (equalize_map == (PixelPacket *) NULL)
cristy0c832c62014-03-07 22:21:04 +00004889 ThrowBinaryException(ResourceLimitWarning,"MemoryAllocationFailed", image->filename);
cristyf034abb2013-11-24 14:16:14 +00004890
4891 map=(FloatPixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*map));
4892 if (map == (FloatPixelPacket *) NULL)
cristy0c832c62014-03-07 22:21:04 +00004893 ThrowBinaryException(ResourceLimitWarning,"MemoryAllocationFailed", image->filename);
cristyf034abb2013-11-24 14:16:14 +00004894
4895 /*
4896 Integrate the histogram to get the equalization map.
4897 */
4898 (void) ResetMagickMemory(&intensity,0,sizeof(intensity));
4899 for (i=0; i <= (ssize_t) MaxMap; i++)
4900 {
4901 if ((channel & SyncChannels) != 0)
cristy0c832c62014-03-07 22:21:04 +00004902 {
4903 intensity.red+=histogram[i].s[2];
4904 map[i]=intensity;
4905 continue;
4906 }
cristyf034abb2013-11-24 14:16:14 +00004907 if ((channel & RedChannel) != 0)
4908 intensity.red+=histogram[i].s[2];
4909 if ((channel & GreenChannel) != 0)
4910 intensity.green+=histogram[i].s[1];
4911 if ((channel & BlueChannel) != 0)
4912 intensity.blue+=histogram[i].s[0];
4913 if ((channel & OpacityChannel) != 0)
dirk8a5cf512014-07-28 20:16:27 +00004914 intensity.alpha+=histogram[i].s[3];
cristy0c832c62014-03-07 22:21:04 +00004915 /*
cristyf034abb2013-11-24 14:16:14 +00004916 if (((channel & IndexChannel) != 0) &&
4917 (image->colorspace == CMYKColorspace))
4918 {
cristy0c832c62014-03-07 22:21:04 +00004919 intensity.index+=histogram[i].index;
cristyf034abb2013-11-24 14:16:14 +00004920 }
cristy0c832c62014-03-07 22:21:04 +00004921 */
cristyf034abb2013-11-24 14:16:14 +00004922 map[i]=intensity;
4923 }
4924 black=map[0];
4925 white=map[(int) MaxMap];
4926 (void) ResetMagickMemory(equalize_map,0,(MaxMap+1)*sizeof(*equalize_map));
4927 for (i=0; i <= (ssize_t) MaxMap; i++)
4928 {
4929 if ((channel & SyncChannels) != 0)
cristy0c832c62014-03-07 22:21:04 +00004930 {
4931 if (white.red != black.red)
4932 equalize_map[i].red=ScaleMapToQuantum((MagickRealType) ((MaxMap*
4933 (map[i].red-black.red))/(white.red-black.red)));
4934 continue;
4935 }
cristyf034abb2013-11-24 14:16:14 +00004936 if (((channel & RedChannel) != 0) && (white.red != black.red))
4937 equalize_map[i].red=ScaleMapToQuantum((MagickRealType) ((MaxMap*
cristy0c832c62014-03-07 22:21:04 +00004938 (map[i].red-black.red))/(white.red-black.red)));
cristyf034abb2013-11-24 14:16:14 +00004939 if (((channel & GreenChannel) != 0) && (white.green != black.green))
4940 equalize_map[i].green=ScaleMapToQuantum((MagickRealType) ((MaxMap*
cristy0c832c62014-03-07 22:21:04 +00004941 (map[i].green-black.green))/(white.green-black.green)));
cristyf034abb2013-11-24 14:16:14 +00004942 if (((channel & BlueChannel) != 0) && (white.blue != black.blue))
4943 equalize_map[i].blue=ScaleMapToQuantum((MagickRealType) ((MaxMap*
cristy0c832c62014-03-07 22:21:04 +00004944 (map[i].blue-black.blue))/(white.blue-black.blue)));
dirk8a5cf512014-07-28 20:16:27 +00004945 if (((channel & OpacityChannel) != 0) && (white.alpha != black.alpha))
4946 equalize_map[i].alpha=ScaleMapToQuantum((MagickRealType) ((MaxMap*
4947 (map[i].alpha-black.alpha))/(white.alpha-black.alpha)));
cristyf034abb2013-11-24 14:16:14 +00004948 /*
4949 if ((((channel & IndexChannel) != 0) &&
cristy0c832c62014-03-07 22:21:04 +00004950 (image->colorspace == CMYKColorspace)) &&
cristyf034abb2013-11-24 14:16:14 +00004951 (white.index != black.index))
4952 equalize_map[i].index=ScaleMapToQuantum((MagickRealType) ((MaxMap*
cristy0c832c62014-03-07 22:21:04 +00004953 (map[i].index-black.index))/(white.index-black.index)));
cristyf034abb2013-11-24 14:16:14 +00004954 */
4955 }
4956
cristyf034abb2013-11-24 14:16:14 +00004957 if (image->storage_class == PseudoClass)
4958 {
cristy0c832c62014-03-07 22:21:04 +00004959 /*
4960 Equalize colormap.
4961 */
4962 for (i=0; i < (ssize_t) image->colors; i++)
4963 {
4964 if ((channel & SyncChannels) != 0)
cristyf034abb2013-11-24 14:16:14 +00004965 {
cristy0c832c62014-03-07 22:21:04 +00004966 if (white.red != black.red)
4967 {
cristyf034abb2013-11-24 14:16:14 +00004968 image->colormap[i].red=equalize_map[
4969 ScaleQuantumToMap(image->colormap[i].red)].red;
cristyf034abb2013-11-24 14:16:14 +00004970 image->colormap[i].green=equalize_map[
cristy0c832c62014-03-07 22:21:04 +00004971 ScaleQuantumToMap(image->colormap[i].green)].red;
cristyf034abb2013-11-24 14:16:14 +00004972 image->colormap[i].blue=equalize_map[
cristy0c832c62014-03-07 22:21:04 +00004973 ScaleQuantumToMap(image->colormap[i].blue)].red;
dirk8a5cf512014-07-28 20:16:27 +00004974 image->colormap[i].alpha=equalize_map[
4975 ScaleQuantumToMap(image->colormap[i].alpha)].red;
cristy0c832c62014-03-07 22:21:04 +00004976 }
4977 continue;
cristyf034abb2013-11-24 14:16:14 +00004978 }
cristy0c832c62014-03-07 22:21:04 +00004979 if (((channel & RedChannel) != 0) && (white.red != black.red))
4980 image->colormap[i].red=equalize_map[
4981 ScaleQuantumToMap(image->colormap[i].red)].red;
4982 if (((channel & GreenChannel) != 0) && (white.green != black.green))
4983 image->colormap[i].green=equalize_map[
4984 ScaleQuantumToMap(image->colormap[i].green)].green;
4985 if (((channel & BlueChannel) != 0) && (white.blue != black.blue))
4986 image->colormap[i].blue=equalize_map[
4987 ScaleQuantumToMap(image->colormap[i].blue)].blue;
4988 if (((channel & OpacityChannel) != 0) &&
dirk8a5cf512014-07-28 20:16:27 +00004989 (white.alpha != black.alpha))
4990 image->colormap[i].alpha=equalize_map[
4991 ScaleQuantumToMap(image->colormap[i].alpha)].alpha;
cristy0c832c62014-03-07 22:21:04 +00004992 }
cristyf034abb2013-11-24 14:16:14 +00004993 }
4994
4995 /*
4996 Equalize image.
4997 */
4998
4999 /* GPU can work on this again, image and equalize map as input
5000 image: uchar4 (CLPixelPacket)
5001 equalize_map: uchar4 (PixelPacket)
5002 black, white: float4 (FloatPixelPacket) */
5003
cristy0c832c62014-03-07 22:21:04 +00005004#ifdef RECREATEBUFFER
cristyf034abb2013-11-24 14:16:14 +00005005 /* If the host pointer is aligned to the size of CLPixelPacket,
5006 then use the host buffer directly from the GPU; otherwise,
5007 create a buffer on the GPU and copy the data over */
5008 if (ALIGNED(inputPixels,CLPixelPacket))
5009 {
5010 mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
5011 }
5012 else
5013 {
5014 mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
5015 }
5016 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00005017 length = image->columns * image->rows;
5018 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00005019 if (clStatus != CL_SUCCESS)
5020 {
cristy0c832c62014-03-07 22:21:04 +00005021 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00005022 goto cleanup;
5023 }
cristy0c832c62014-03-07 22:21:04 +00005024#endif
cristyf034abb2013-11-24 14:16:14 +00005025
5026 /* Create and initialize OpenCL buffers. */
5027 if (ALIGNED(equalize_map, PixelPacket))
5028 {
5029 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
5030 hostPtr = equalize_map;
5031 }
5032 else
5033 {
5034 mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
5035 hostPtr = equalize_map;
5036 }
5037 /* create a CL buffer for eqaulize_map */
5038 length = (MaxMap+1);
cristy0c832c62014-03-07 22:21:04 +00005039 equalizeMapBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(PixelPacket), hostPtr, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00005040 if (clStatus != CL_SUCCESS)
5041 {
cristy0c832c62014-03-07 22:21:04 +00005042 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00005043 goto cleanup;
5044 }
5045
5046 /* get the OpenCL kernel */
5047 equalizeKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "Equalize");
5048 if (equalizeKernel == NULL)
5049 {
cristya22457d2013-12-07 14:03:06 +00005050 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00005051 goto cleanup;
5052 }
5053
5054 /* set the kernel arguments */
5055 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00005056 clStatus=clEnv->library->clSetKernelArg(equalizeKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00005057 clStatus|=clEnv->library->clSetKernelArg(equalizeKernel,i++,sizeof(ChannelType),&channel);
5058 clStatus|=clEnv->library->clSetKernelArg(equalizeKernel,i++,sizeof(cl_mem),(void *)&equalizeMapBuffer);
5059 clStatus|=clEnv->library->clSetKernelArg(equalizeKernel,i++,sizeof(FloatPixelPacket),&white);
5060 clStatus|=clEnv->library->clSetKernelArg(equalizeKernel,i++,sizeof(FloatPixelPacket),&black);
cristyf034abb2013-11-24 14:16:14 +00005061 if (clStatus != CL_SUCCESS)
5062 {
cristy0c832c62014-03-07 22:21:04 +00005063 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00005064 goto cleanup;
5065 }
5066
5067 /* launch the kernel */
dirk8a5cf512014-07-28 20:16:27 +00005068 global_work_size[0] = image->columns;
5069 global_work_size[1] = image->rows;
cristyf034abb2013-11-24 14:16:14 +00005070
cristy0c832c62014-03-07 22:21:04 +00005071 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, equalizeKernel, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00005072
5073 if (clStatus != CL_SUCCESS)
5074 {
cristy0c832c62014-03-07 22:21:04 +00005075 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00005076 goto cleanup;
5077 }
cristy0c832c62014-03-07 22:21:04 +00005078 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00005079
5080 /* read the data back */
5081 if (ALIGNED(inputPixels,CLPixelPacket))
5082 {
dirk8a5cf512014-07-28 20:16:27 +00005083 length = image->columns * image->rows;
5084 clEnv->library->clEnqueueMapBuffer(queue, imageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00005085 }
5086 else
5087 {
dirk8a5cf512014-07-28 20:16:27 +00005088 length = image->columns * image->rows;
5089 clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), inputPixels, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00005090 }
5091 if (clStatus != CL_SUCCESS)
5092 {
cristya22457d2013-12-07 14:03:06 +00005093 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00005094 goto cleanup;
5095 }
5096
dirk832becc2014-08-04 19:44:34 +00005097 outputReady=SyncCacheViewAuthenticPixels(image_view,exception);
5098
5099cleanup:
5100 OpenCLLogException(__FUNCTION__,__LINE__,exception);
5101
dirk8a5cf512014-07-28 20:16:27 +00005102 image_view=DestroyCacheView(image_view);
cristyf034abb2013-11-24 14:16:14 +00005103
dirk8a5cf512014-07-28 20:16:27 +00005104 if (imageBuffer!=NULL)
5105 clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00005106
5107 if (map!=NULL)
5108 map=(FloatPixelPacket *) RelinquishMagickMemory(map);
5109
5110 if (equalizeMapBuffer!=NULL)
5111 clEnv->library->clReleaseMemObject(equalizeMapBuffer);
5112 if (equalize_map!=NULL)
5113 equalize_map=(PixelPacket *) RelinquishMagickMemory(equalize_map);
5114
cristyf034abb2013-11-24 14:16:14 +00005115 if (histogramBuffer!=NULL)
cristy0c832c62014-03-07 22:21:04 +00005116 clEnv->library->clReleaseMemObject(histogramBuffer);
5117 if (histogram!=NULL)
5118 histogram=(cl_uint4 *) RelinquishMagickMemory(histogram);
5119
cristyf034abb2013-11-24 14:16:14 +00005120 if (histogramKernel!=NULL)
5121 RelinquishOpenCLKernel(clEnv, histogramKernel);
cristy0c832c62014-03-07 22:21:04 +00005122 if (equalizeKernel!=NULL)
5123 RelinquishOpenCLKernel(clEnv, equalizeKernel);
5124
cristyf034abb2013-11-24 14:16:14 +00005125 if (queue != NULL)
5126 RelinquishOpenCLCommandQueue(clEnv, queue);
5127
dirk8a5cf512014-07-28 20:16:27 +00005128 return(outputReady);
dirk832becc2014-08-04 19:44:34 +00005129}
5130
5131MagickExport MagickBooleanType AccelerateEqualizeImage(Image *image,
5132 const ChannelType channel,ExceptionInfo *exception)
5133{
5134 MagickBooleanType
5135 status;
5136
5137 assert(image != NULL);
5138 assert(exception != (ExceptionInfo *) NULL);
5139
5140 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
5141 (checkAccelerateCondition(image, channel) == MagickFalse) ||
5142 (checkHistogramCondition(image, channel) == MagickFalse))
5143 return(MagickFalse);
5144
5145 status=ComputeEqualizeImage(image,channel,exception);
5146 return(status);
cristyf034abb2013-11-24 14:16:14 +00005147}
5148
5149/*
5150%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5151% %
5152% %
5153% %
dirk8a5cf512014-07-28 20:16:27 +00005154% 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 %
cristyf034abb2013-11-24 14:16:14 +00005155% %
5156% %
5157% %
5158%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5159%
dirk8a5cf512014-07-28 20:16:27 +00005160% ContrastStretchImage() is a simple image enhancement technique that attempts
5161% to improve the contrast in an image by `stretching' the range of intensity
5162% values it contains to span a desired range of values. It differs from the
5163% more sophisticated histogram equalization in that it can only apply a
5164% linear scaling function to the image pixel values. As a result the
5165% `enhancement' is less harsh.
cristyf034abb2013-11-24 14:16:14 +00005166%
dirk8a5cf512014-07-28 20:16:27 +00005167% The format of the ContrastStretchImage method is:
cristyf034abb2013-11-24 14:16:14 +00005168%
dirk8a5cf512014-07-28 20:16:27 +00005169% MagickBooleanType ContrastStretchImage(Image *image,
5170% const char *levels)
5171% MagickBooleanType ContrastStretchImageChannel(Image *image,
5172% const size_t channel,const double black_point,
5173% const double white_point)
cristyf034abb2013-11-24 14:16:14 +00005174%
5175% A description of each parameter follows:
5176%
5177% o image: the image.
5178%
5179% o channel: the channel.
5180%
dirk8a5cf512014-07-28 20:16:27 +00005181% o black_point: the black point.
5182%
5183% o white_point: the white point.
5184%
5185% o levels: Specify the levels where the black and white points have the
5186% range of 0 to number-of-pixels (e.g. 1%, 10x90%, etc.).
5187%
cristyf034abb2013-11-24 14:16:14 +00005188*/
5189
dirk832becc2014-08-04 19:44:34 +00005190MagickExport MagickBooleanType ComputeContrastStretchImageChannel(Image *image,
5191 const ChannelType channel,const double black_point,const double white_point,
5192 ExceptionInfo *exception)
5193{
5194#define ContrastStretchImageTag "ContrastStretch/Image"
5195#define MaxRange(color) ((MagickRealType) ScaleQuantumToMap((Quantum) (color)))
5196
5197 CacheView
5198 *image_view;
5199
5200 cl_command_queue
5201 queue;
5202
5203 cl_context
5204 context;
5205
5206 cl_int
5207 clStatus;
5208
5209 cl_mem_flags
5210 mem_flags;
5211
5212 cl_mem
5213 histogramBuffer,
5214 imageBuffer,
5215 stretchMapBuffer;
5216
5217 cl_kernel
5218 histogramKernel,
5219 stretchKernel;
5220
5221 cl_uint4
5222 *histogram;
5223
5224 double
5225 intensity;
5226
5227 FloatPixelPacket
5228 black,
5229 white;
5230
5231 MagickBooleanType
5232 outputReady,
5233 status;
5234
5235 MagickCLEnv
5236 clEnv;
5237
5238 MagickSizeType
5239 length;
5240
5241 PixelPacket
5242 *stretch_map;
5243
5244 register ssize_t
5245 i;
5246
5247 size_t
5248 global_work_size[2];
5249
5250 void
5251 *hostPtr,
5252 *inputPixels;
5253
5254 histogram=NULL;
5255 stretch_map=NULL;
5256 inputPixels = NULL;
5257 imageBuffer = NULL;
5258 histogramBuffer = NULL;
5259 stretchMapBuffer = NULL;
5260 histogramKernel = NULL;
5261 stretchKernel = NULL;
5262 context = NULL;
5263 queue = NULL;
5264 outputReady = MagickFalse;
5265
5266
5267 assert(image != (Image *) NULL);
5268 assert(image->signature == MagickSignature);
5269 if (image->debug != MagickFalse)
5270 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5271
5272 //exception=(&image->exception);
5273
5274 /*
5275 * initialize opencl env
5276 */
5277 clEnv = GetDefaultOpenCLEnv();
5278 context = GetOpenCLContext(clEnv);
5279 queue = AcquireOpenCLCommandQueue(clEnv);
5280
5281 /*
5282 Allocate and initialize histogram arrays.
5283 */
5284 histogram=(cl_uint4 *) AcquireQuantumMemory(MaxMap+1UL, sizeof(*histogram));
5285
5286 if (histogram == (cl_uint4 *) NULL)
5287 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", image->filename);
5288
5289 /* reset histogram */
5290 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
5291
5292 /*
5293 if (IsGrayImage(image,exception) != MagickFalse)
5294 (void) SetImageColorspace(image,GRAYColorspace);
5295 */
5296
5297 status=MagickTrue;
5298
5299
5300 /*
5301 Form histogram.
5302 */
5303 /* Create and initialize OpenCL buffers. */
5304 /* inputPixels = AcquirePixelCachePixels(image, &length, exception); */
5305 /* assume this will get a writable image */
5306 image_view=AcquireAuthenticCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00005307 inputPixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
cristy0c832c62014-03-07 22:21:04 +00005308
5309 if (inputPixels == (void *) NULL)
5310 {
dirk8a5cf512014-07-28 20:16:27 +00005311 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristy0c832c62014-03-07 22:21:04 +00005312 goto cleanup;
5313 }
5314 /* If the host pointer is aligned to the size of CLPixelPacket,
5315 then use the host buffer directly from the GPU; otherwise,
5316 create a buffer on the GPU and copy the data over */
5317 if (ALIGNED(inputPixels,CLPixelPacket))
5318 {
5319 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
5320 }
5321 else
5322 {
5323 mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
5324 }
5325 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00005326 length = image->columns * image->rows;
5327 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristy0c832c62014-03-07 22:21:04 +00005328 if (clStatus != CL_SUCCESS)
5329 {
5330 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
5331 goto cleanup;
5332 }
5333
5334 /* If the host pointer is aligned to the size of cl_uint,
5335 then use the host buffer directly from the GPU; otherwise,
5336 create a buffer on the GPU and copy the data over */
5337 if (ALIGNED(histogram,cl_uint4))
5338 {
5339 mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
5340 hostPtr = histogram;
5341 }
5342 else
5343 {
5344 mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
5345 hostPtr = histogram;
5346 }
5347 /* create a CL buffer for histogram */
5348 length = (MaxMap+1);
5349 histogramBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(cl_uint4), hostPtr, &clStatus);
5350 if (clStatus != CL_SUCCESS)
5351 {
5352 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
5353 goto cleanup;
5354 }
5355
dirk8a5cf512014-07-28 20:16:27 +00005356 status = LaunchHistogramKernel(clEnv, queue, imageBuffer, histogramBuffer, image, channel, exception);
cristy0c832c62014-03-07 22:21:04 +00005357 if (status == MagickFalse)
5358 goto cleanup;
5359
5360 /* read from the kenel output */
5361 if (ALIGNED(histogram,cl_uint4))
5362 {
5363 length = (MaxMap+1);
5364 clEnv->library->clEnqueueMapBuffer(queue, histogramBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(cl_uint4), 0, NULL, NULL, &clStatus);
5365 }
5366 else
5367 {
5368 length = (MaxMap+1);
5369 clStatus = clEnv->library->clEnqueueReadBuffer(queue, histogramBuffer, CL_TRUE, 0, length * sizeof(cl_uint4), histogram, 0, NULL, NULL);
5370 }
5371 if (clStatus != CL_SUCCESS)
5372 {
5373 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
5374 goto cleanup;
5375 }
5376
5377 /* unmap, don't block gpu to use this buffer again. */
5378 if (ALIGNED(histogram,cl_uint4))
5379 {
5380 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, histogramBuffer, histogram, 0, NULL, NULL);
5381 if (clStatus != CL_SUCCESS)
5382 {
5383 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
5384 goto cleanup;
5385 }
5386 }
5387
5388 /* recreate input buffer later, in case image updated */
5389#ifdef RECREATEBUFFER
dirk8a5cf512014-07-28 20:16:27 +00005390 if (imageBuffer!=NULL)
5391 clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00005392#endif
5393
5394 /* CPU stuff */
5395 /*
5396 Find the histogram boundaries by locating the black/white levels.
5397 */
5398 black.red=0.0;
5399 white.red=MaxRange(QuantumRange);
5400 if ((channel & RedChannel) != 0)
5401 {
5402 intensity=0.0;
5403 for (i=0; i <= (ssize_t) MaxMap; i++)
5404 {
5405 intensity+=histogram[i].s[2];
5406 if (intensity > black_point)
5407 break;
5408 }
5409 black.red=(MagickRealType) i;
5410 intensity=0.0;
5411 for (i=(ssize_t) MaxMap; i != 0; i--)
5412 {
5413 intensity+=histogram[i].s[2];
5414 if (intensity > ((double) image->columns*image->rows-white_point))
5415 break;
5416 }
5417 white.red=(MagickRealType) i;
5418 }
5419 black.green=0.0;
5420 white.green=MaxRange(QuantumRange);
5421 if ((channel & GreenChannel) != 0)
5422 {
5423 intensity=0.0;
5424 for (i=0; i <= (ssize_t) MaxMap; i++)
5425 {
5426 intensity+=histogram[i].s[2];
5427 if (intensity > black_point)
5428 break;
5429 }
5430 black.green=(MagickRealType) i;
5431 intensity=0.0;
5432 for (i=(ssize_t) MaxMap; i != 0; i--)
5433 {
5434 intensity+=histogram[i].s[2];
5435 if (intensity > ((double) image->columns*image->rows-white_point))
5436 break;
5437 }
5438 white.green=(MagickRealType) i;
5439 }
5440 black.blue=0.0;
5441 white.blue=MaxRange(QuantumRange);
5442 if ((channel & BlueChannel) != 0)
5443 {
5444 intensity=0.0;
5445 for (i=0; i <= (ssize_t) MaxMap; i++)
5446 {
5447 intensity+=histogram[i].s[2];
5448 if (intensity > black_point)
5449 break;
5450 }
5451 black.blue=(MagickRealType) i;
5452 intensity=0.0;
5453 for (i=(ssize_t) MaxMap; i != 0; i--)
5454 {
5455 intensity+=histogram[i].s[2];
5456 if (intensity > ((double) image->columns*image->rows-white_point))
5457 break;
5458 }
5459 white.blue=(MagickRealType) i;
5460 }
dirk8a5cf512014-07-28 20:16:27 +00005461 black.alpha=0.0;
5462 white.alpha=MaxRange(QuantumRange);
cristy0c832c62014-03-07 22:21:04 +00005463 if ((channel & OpacityChannel) != 0)
5464 {
5465 intensity=0.0;
5466 for (i=0; i <= (ssize_t) MaxMap; i++)
5467 {
5468 intensity+=histogram[i].s[2];
5469 if (intensity > black_point)
5470 break;
5471 }
dirk8a5cf512014-07-28 20:16:27 +00005472 black.alpha=(MagickRealType) i;
cristy0c832c62014-03-07 22:21:04 +00005473 intensity=0.0;
5474 for (i=(ssize_t) MaxMap; i != 0; i--)
5475 {
5476 intensity+=histogram[i].s[2];
5477 if (intensity > ((double) image->columns*image->rows-white_point))
5478 break;
5479 }
dirk8a5cf512014-07-28 20:16:27 +00005480 white.alpha=(MagickRealType) i;
cristy0c832c62014-03-07 22:21:04 +00005481 }
5482 /*
5483 black.index=0.0;
5484 white.index=MaxRange(QuantumRange);
5485 if (((channel & IndexChannel) != 0) && (image->colorspace == CMYKColorspace))
5486 {
5487 intensity=0.0;
5488 for (i=0; i <= (ssize_t) MaxMap; i++)
5489 {
5490 intensity+=histogram[i].index;
5491 if (intensity > black_point)
5492 break;
5493 }
5494 black.index=(MagickRealType) i;
5495 intensity=0.0;
5496 for (i=(ssize_t) MaxMap; i != 0; i--)
5497 {
5498 intensity+=histogram[i].index;
5499 if (intensity > ((double) image->columns*image->rows-white_point))
5500 break;
5501 }
5502 white.index=(MagickRealType) i;
5503 }
5504 */
5505
5506
5507 stretch_map=(PixelPacket *) AcquireQuantumMemory(MaxMap+1UL,
5508 sizeof(*stretch_map));
5509
cristy931c8062014-03-10 13:19:17 +00005510 if (stretch_map == (PixelPacket *) NULL)
cristy0c832c62014-03-07 22:21:04 +00005511 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
5512 image->filename);
5513
5514 /*
5515 Stretch the histogram to create the stretched image mapping.
5516 */
5517 (void) ResetMagickMemory(stretch_map,0,(MaxMap+1)*sizeof(*stretch_map));
5518 for (i=0; i <= (ssize_t) MaxMap; i++)
5519 {
5520 if ((channel & RedChannel) != 0)
5521 {
5522 if (i < (ssize_t) black.red)
5523 stretch_map[i].red=(Quantum) 0;
5524 else
5525 if (i > (ssize_t) white.red)
5526 stretch_map[i].red=QuantumRange;
5527 else
5528 if (black.red != white.red)
5529 stretch_map[i].red=ScaleMapToQuantum((MagickRealType) (MaxMap*
5530 (i-black.red)/(white.red-black.red)));
5531 }
5532 if ((channel & GreenChannel) != 0)
5533 {
5534 if (i < (ssize_t) black.green)
5535 stretch_map[i].green=0;
5536 else
5537 if (i > (ssize_t) white.green)
5538 stretch_map[i].green=QuantumRange;
5539 else
5540 if (black.green != white.green)
5541 stretch_map[i].green=ScaleMapToQuantum((MagickRealType) (MaxMap*
5542 (i-black.green)/(white.green-black.green)));
5543 }
5544 if ((channel & BlueChannel) != 0)
5545 {
5546 if (i < (ssize_t) black.blue)
5547 stretch_map[i].blue=0;
5548 else
5549 if (i > (ssize_t) white.blue)
5550 stretch_map[i].blue= QuantumRange;
5551 else
5552 if (black.blue != white.blue)
5553 stretch_map[i].blue=ScaleMapToQuantum((MagickRealType) (MaxMap*
5554 (i-black.blue)/(white.blue-black.blue)));
5555 }
5556 if ((channel & OpacityChannel) != 0)
5557 {
dirk8a5cf512014-07-28 20:16:27 +00005558 if (i < (ssize_t) black.alpha)
5559 stretch_map[i].alpha=0;
cristy0c832c62014-03-07 22:21:04 +00005560 else
dirk8a5cf512014-07-28 20:16:27 +00005561 if (i > (ssize_t) white.alpha)
5562 stretch_map[i].alpha=QuantumRange;
cristy0c832c62014-03-07 22:21:04 +00005563 else
dirk8a5cf512014-07-28 20:16:27 +00005564 if (black.alpha != white.alpha)
5565 stretch_map[i].alpha=ScaleMapToQuantum((MagickRealType) (MaxMap*
5566 (i-black.alpha)/(white.alpha-black.alpha)));
cristy0c832c62014-03-07 22:21:04 +00005567 }
5568 /*
5569 if (((channel & IndexChannel) != 0) &&
5570 (image->colorspace == CMYKColorspace))
5571 {
5572 if (i < (ssize_t) black.index)
5573 stretch_map[i].index=0;
5574 else
5575 if (i > (ssize_t) white.index)
5576 stretch_map[i].index=QuantumRange;
5577 else
5578 if (black.index != white.index)
5579 stretch_map[i].index=ScaleMapToQuantum((MagickRealType) (MaxMap*
5580 (i-black.index)/(white.index-black.index)));
5581 }
5582 */
5583 }
5584
5585 /*
5586 Stretch the image.
5587 */
5588 if (((channel & OpacityChannel) != 0) || (((channel & IndexChannel) != 0) &&
5589 (image->colorspace == CMYKColorspace)))
5590 image->storage_class=DirectClass;
5591 if (image->storage_class == PseudoClass)
5592 {
5593 /*
5594 Stretch colormap.
5595 */
5596 for (i=0; i < (ssize_t) image->colors; i++)
5597 {
5598 if ((channel & RedChannel) != 0)
5599 {
5600 if (black.red != white.red)
5601 image->colormap[i].red=stretch_map[
5602 ScaleQuantumToMap(image->colormap[i].red)].red;
5603 }
5604 if ((channel & GreenChannel) != 0)
5605 {
5606 if (black.green != white.green)
5607 image->colormap[i].green=stretch_map[
5608 ScaleQuantumToMap(image->colormap[i].green)].green;
5609 }
5610 if ((channel & BlueChannel) != 0)
5611 {
5612 if (black.blue != white.blue)
5613 image->colormap[i].blue=stretch_map[
5614 ScaleQuantumToMap(image->colormap[i].blue)].blue;
5615 }
5616 if ((channel & OpacityChannel) != 0)
5617 {
dirk8a5cf512014-07-28 20:16:27 +00005618 if (black.alpha != white.alpha)
5619 image->colormap[i].alpha=stretch_map[
5620 ScaleQuantumToMap(image->colormap[i].alpha)].alpha;
cristy0c832c62014-03-07 22:21:04 +00005621 }
5622 }
5623 }
5624
5625 /*
5626 Stretch image.
5627 */
5628
5629
5630 /* GPU can work on this again, image and equalize map as input
5631 image: uchar4 (CLPixelPacket)
5632 stretch_map: uchar4 (PixelPacket)
5633 black, white: float4 (FloatPixelPacket) */
5634
5635#ifdef RECREATEBUFFER
5636 /* If the host pointer is aligned to the size of CLPixelPacket,
5637 then use the host buffer directly from the GPU; otherwise,
5638 create a buffer on the GPU and copy the data over */
5639 if (ALIGNED(inputPixels,CLPixelPacket))
5640 {
5641 mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
5642 }
5643 else
5644 {
5645 mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
5646 }
5647 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00005648 length = image->columns * image->rows;
5649 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristy0c832c62014-03-07 22:21:04 +00005650 if (clStatus != CL_SUCCESS)
5651 {
5652 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
5653 goto cleanup;
5654 }
5655#endif
5656
5657 /* Create and initialize OpenCL buffers. */
5658 if (ALIGNED(stretch_map, PixelPacket))
5659 {
5660 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
5661 hostPtr = stretch_map;
5662 }
5663 else
5664 {
5665 mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
5666 hostPtr = stretch_map;
5667 }
5668 /* create a CL buffer for stretch_map */
5669 length = (MaxMap+1);
5670 stretchMapBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(PixelPacket), hostPtr, &clStatus);
5671 if (clStatus != CL_SUCCESS)
5672 {
5673 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
5674 goto cleanup;
5675 }
5676
5677 /* get the OpenCL kernel */
5678 stretchKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "Stretch");
5679 if (stretchKernel == NULL)
5680 {
5681 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
5682 goto cleanup;
5683 }
5684
5685 /* set the kernel arguments */
5686 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00005687 clStatus=clEnv->library->clSetKernelArg(stretchKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00005688 clStatus|=clEnv->library->clSetKernelArg(stretchKernel,i++,sizeof(ChannelType),&channel);
5689 clStatus|=clEnv->library->clSetKernelArg(stretchKernel,i++,sizeof(cl_mem),(void *)&stretchMapBuffer);
5690 clStatus|=clEnv->library->clSetKernelArg(stretchKernel,i++,sizeof(FloatPixelPacket),&white);
5691 clStatus|=clEnv->library->clSetKernelArg(stretchKernel,i++,sizeof(FloatPixelPacket),&black);
5692 if (clStatus != CL_SUCCESS)
5693 {
5694 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
5695 goto cleanup;
5696 }
5697
5698 /* launch the kernel */
dirk8a5cf512014-07-28 20:16:27 +00005699 global_work_size[0] = image->columns;
5700 global_work_size[1] = image->rows;
cristy0c832c62014-03-07 22:21:04 +00005701
5702 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, stretchKernel, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
5703
5704 if (clStatus != CL_SUCCESS)
5705 {
5706 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
5707 goto cleanup;
5708 }
5709 clEnv->library->clFlush(queue);
5710
5711 /* read the data back */
5712 if (ALIGNED(inputPixels,CLPixelPacket))
5713 {
dirk8a5cf512014-07-28 20:16:27 +00005714 length = image->columns * image->rows;
5715 clEnv->library->clEnqueueMapBuffer(queue, imageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
cristy0c832c62014-03-07 22:21:04 +00005716 }
5717 else
5718 {
dirk8a5cf512014-07-28 20:16:27 +00005719 length = image->columns * image->rows;
5720 clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), inputPixels, 0, NULL, NULL);
cristy0c832c62014-03-07 22:21:04 +00005721 }
5722 if (clStatus != CL_SUCCESS)
5723 {
5724 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
5725 goto cleanup;
5726 }
5727
dirk832becc2014-08-04 19:44:34 +00005728 outputReady=SyncCacheViewAuthenticPixels(image_view,exception);
5729
5730cleanup:
5731 OpenCLLogException(__FUNCTION__,__LINE__,exception);
5732
dirk8a5cf512014-07-28 20:16:27 +00005733 image_view=DestroyCacheView(image_view);
cristy0c832c62014-03-07 22:21:04 +00005734
dirk8a5cf512014-07-28 20:16:27 +00005735 if (imageBuffer!=NULL)
5736 clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00005737
5738 if (stretchMapBuffer!=NULL)
5739 clEnv->library->clReleaseMemObject(stretchMapBuffer);
5740 if (stretch_map!=NULL)
5741 stretch_map=(PixelPacket *) RelinquishMagickMemory(stretch_map);
5742
5743
5744 if (histogramBuffer!=NULL)
5745 clEnv->library->clReleaseMemObject(histogramBuffer);
5746 if (histogram!=NULL)
5747 histogram=(cl_uint4 *) RelinquishMagickMemory(histogram);
5748
5749
5750 if (histogramKernel!=NULL)
5751 RelinquishOpenCLKernel(clEnv, histogramKernel);
5752 if (stretchKernel!=NULL)
5753 RelinquishOpenCLKernel(clEnv, stretchKernel);
5754
5755 if (queue != NULL)
5756 RelinquishOpenCLCommandQueue(clEnv, queue);
5757
dirk832becc2014-08-04 19:44:34 +00005758 return(outputReady);
5759}
5760
5761MagickExport MagickBooleanType AccelerateContrastStretchImageChannel(
5762 Image *image,const ChannelType channel,const double black_point,
5763 const double white_point,ExceptionInfo *exception)
5764{
5765 MagickBooleanType
5766 status;
5767
5768 assert(image != NULL);
5769 assert(exception != (ExceptionInfo *) NULL);
5770
5771 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
5772 (checkAccelerateCondition(image, channel) == MagickFalse) ||
5773 (checkHistogramCondition(image, channel) == MagickFalse))
5774 return(MagickFalse);
5775
5776 status=ComputeContrastStretchImageChannel(image,channel, black_point, white_point, exception);
5777 return(status);
cristy0c832c62014-03-07 22:21:04 +00005778}
5779
cristy0c832c62014-03-07 22:21:04 +00005780/*
5781%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5782% %
5783% %
5784% %
dirk8a5cf512014-07-28 20:16:27 +00005785% D e s p e c k l e I m a g e w i t h O p e n C L %
cristy0c832c62014-03-07 22:21:04 +00005786% %
5787% %
5788% %
5789%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5790%
dirk8a5cf512014-07-28 20:16:27 +00005791% DespeckleImage() reduces the speckle noise in an image while perserving the
5792% edges of the original image. A speckle removing filter uses a complementary
5793% hulling technique (raising pixels that are darker than their surrounding
5794% neighbors, then complementarily lowering pixels that are brighter than their
5795% surrounding neighbors) to reduce the speckle index of that image (reference
5796% Crimmins speckle removal).
cristy0c832c62014-03-07 22:21:04 +00005797%
dirk8a5cf512014-07-28 20:16:27 +00005798% The format of the DespeckleImage method is:
cristy0c832c62014-03-07 22:21:04 +00005799%
dirk8a5cf512014-07-28 20:16:27 +00005800% Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
cristy0c832c62014-03-07 22:21:04 +00005801%
5802% A description of each parameter follows:
5803%
5804% o image: the image.
5805%
dirk8a5cf512014-07-28 20:16:27 +00005806% o exception: return any errors or warnings in this structure.
cristy0c832c62014-03-07 22:21:04 +00005807%
5808*/
5809
dirk832becc2014-08-04 19:44:34 +00005810static Image *ComputeDespeckleImage(const Image *image,
5811 ExceptionInfo*exception)
5812{
5813 static const int
5814 X[4] = {0, 1, 1,-1},
5815 Y[4] = {1, 0, 1, 1};
5816
5817 CacheView
5818 *filteredImage_view,
5819 *image_view;
5820
5821 cl_command_queue
5822 queue;
5823
5824 cl_context
5825 context;
5826
5827 cl_int
5828 clStatus;
5829
5830 cl_kernel
5831 hullPass1,
5832 hullPass2;
5833
5834 cl_mem_flags
5835 mem_flags;
5836
5837 cl_mem
5838 filteredImageBuffer,
5839 imageBuffer,
5840 tempImageBuffer[2];
5841
5842 const void
5843 *inputPixels;
5844
5845 Image
5846 *filteredImage;
5847
5848 int
5849 k,
5850 matte;
5851
5852 MagickBooleanType
5853 outputReady;
5854
5855 MagickCLEnv
5856 clEnv;
5857
5858 MagickSizeType
5859 length;
5860
5861 size_t
5862 global_work_size[2];
5863
5864 unsigned int
5865 imageHeight,
5866 imageWidth;
5867
5868 void
5869 *filteredPixels,
5870 *hostPtr;
5871
5872 outputReady = MagickFalse;
5873 clEnv = NULL;
5874 inputPixels = NULL;
5875 filteredImage = NULL;
5876 filteredImage_view = NULL;
5877 filteredPixels = NULL;
5878 context = NULL;
5879 imageBuffer = NULL;
5880 filteredImageBuffer = NULL;
5881 hullPass1 = NULL;
5882 hullPass2 = NULL;
5883 queue = NULL;
5884 tempImageBuffer[0] = tempImageBuffer[1] = NULL;
5885 clEnv = GetDefaultOpenCLEnv();
5886 context = GetOpenCLContext(clEnv);
5887 queue = AcquireOpenCLCommandQueue(clEnv);
5888
5889 image_view=AcquireVirtualCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00005890 inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00005891 if (inputPixels == (void *) NULL)
5892 {
dirk8a5cf512014-07-28 20:16:27 +00005893 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristyf034abb2013-11-24 14:16:14 +00005894 goto cleanup;
5895 }
5896
5897 if (ALIGNED(inputPixels,CLPixelPacket))
5898 {
5899 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
5900 }
5901 else
5902 {
5903 mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
5904 }
5905 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00005906 length = image->columns * image->rows;
5907 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00005908 if (clStatus != CL_SUCCESS)
5909 {
cristy0c832c62014-03-07 22:21:04 +00005910 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00005911 goto cleanup;
5912 }
5913
5914 mem_flags = CL_MEM_READ_WRITE;
dirk8a5cf512014-07-28 20:16:27 +00005915 length = image->columns * image->rows;
cristyf034abb2013-11-24 14:16:14 +00005916 for (k = 0; k < 2; k++)
5917 {
cristy0c832c62014-03-07 22:21:04 +00005918 tempImageBuffer[k] = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00005919 if (clStatus != CL_SUCCESS)
5920 {
cristy0c832c62014-03-07 22:21:04 +00005921 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00005922 goto cleanup;
5923 }
5924 }
5925
dirk8a5cf512014-07-28 20:16:27 +00005926 filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristyf034abb2013-11-24 14:16:14 +00005927 assert(filteredImage != NULL);
dirk8a5cf512014-07-28 20:16:27 +00005928 if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
cristyf034abb2013-11-24 14:16:14 +00005929 {
cristya22457d2013-12-07 14:03:06 +00005930 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00005931 goto cleanup;
5932 }
dirk832becc2014-08-04 19:44:34 +00005933 filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
dirk8a5cf512014-07-28 20:16:27 +00005934 filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00005935 if (filteredPixels == (void *) NULL)
5936 {
cristya22457d2013-12-07 14:03:06 +00005937 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
cristyf034abb2013-11-24 14:16:14 +00005938 goto cleanup;
5939 }
5940
5941 if (ALIGNED(filteredPixels,CLPixelPacket))
5942 {
5943 mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
5944 hostPtr = filteredPixels;
5945 }
5946 else
5947 {
5948 mem_flags = CL_MEM_WRITE_ONLY;
5949 hostPtr = NULL;
5950 }
5951 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00005952 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00005953 filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00005954 if (clStatus != CL_SUCCESS)
5955 {
cristy0c832c62014-03-07 22:21:04 +00005956 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00005957 goto cleanup;
5958 }
5959
5960 hullPass1 = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "HullPass1");
5961 hullPass2 = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "HullPass2");
5962
dirk8a5cf512014-07-28 20:16:27 +00005963 clStatus =clEnv->library->clSetKernelArg(hullPass1,0,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00005964 clStatus |=clEnv->library->clSetKernelArg(hullPass1,1,sizeof(cl_mem),(void *)(tempImageBuffer+1));
dirk8a5cf512014-07-28 20:16:27 +00005965 imageWidth = image->columns;
cristy0c832c62014-03-07 22:21:04 +00005966 clStatus |=clEnv->library->clSetKernelArg(hullPass1,2,sizeof(unsigned int),(void *)&imageWidth);
dirk8a5cf512014-07-28 20:16:27 +00005967 imageHeight = image->rows;
cristy0c832c62014-03-07 22:21:04 +00005968 clStatus |=clEnv->library->clSetKernelArg(hullPass1,3,sizeof(unsigned int),(void *)&imageHeight);
dirk8a5cf512014-07-28 20:16:27 +00005969 matte = (image->alpha_trait==BlendPixelTrait)?0:1;
cristy0c832c62014-03-07 22:21:04 +00005970 clStatus |=clEnv->library->clSetKernelArg(hullPass1,6,sizeof(int),(void *)&matte);
cristyf034abb2013-11-24 14:16:14 +00005971 if (clStatus != CL_SUCCESS)
5972 {
cristy0c832c62014-03-07 22:21:04 +00005973 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00005974 goto cleanup;
5975 }
5976
cristy0c832c62014-03-07 22:21:04 +00005977 clStatus = clEnv->library->clSetKernelArg(hullPass2,0,sizeof(cl_mem),(void *)(tempImageBuffer+1));
5978 clStatus |=clEnv->library->clSetKernelArg(hullPass2,1,sizeof(cl_mem),(void *)tempImageBuffer);
dirk8a5cf512014-07-28 20:16:27 +00005979 imageWidth = image->columns;
cristy0c832c62014-03-07 22:21:04 +00005980 clStatus |=clEnv->library->clSetKernelArg(hullPass2,2,sizeof(unsigned int),(void *)&imageWidth);
dirk8a5cf512014-07-28 20:16:27 +00005981 imageHeight = image->rows;
cristy0c832c62014-03-07 22:21:04 +00005982 clStatus |=clEnv->library->clSetKernelArg(hullPass2,3,sizeof(unsigned int),(void *)&imageHeight);
dirk8a5cf512014-07-28 20:16:27 +00005983 matte = (image->alpha_trait==BlendPixelTrait)?0:1;
cristy0c832c62014-03-07 22:21:04 +00005984 clStatus |=clEnv->library->clSetKernelArg(hullPass2,6,sizeof(int),(void *)&matte);
cristyf034abb2013-11-24 14:16:14 +00005985 if (clStatus != CL_SUCCESS)
5986 {
cristy0c832c62014-03-07 22:21:04 +00005987 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00005988 goto cleanup;
5989 }
5990
5991
dirk8a5cf512014-07-28 20:16:27 +00005992 global_work_size[0] = image->columns;
5993 global_work_size[1] = image->rows;
cristyf034abb2013-11-24 14:16:14 +00005994
5995
5996 for (k = 0; k < 4; k++)
5997 {
5998 cl_int2 offset;
5999 int polarity;
6000
6001
6002 offset.s[0] = X[k];
6003 offset.s[1] = Y[k];
6004 polarity = 1;
cristy0c832c62014-03-07 22:21:04 +00006005 clStatus = clEnv->library->clSetKernelArg(hullPass1,4,sizeof(cl_int2),(void *)&offset);
6006 clStatus|= clEnv->library->clSetKernelArg(hullPass1,5,sizeof(int),(void *)&polarity);
6007 clStatus|=clEnv->library->clSetKernelArg(hullPass2,4,sizeof(cl_int2),(void *)&offset);
6008 clStatus|=clEnv->library->clSetKernelArg(hullPass2,5,sizeof(int),(void *)&polarity);
cristyf034abb2013-11-24 14:16:14 +00006009 if (clStatus != CL_SUCCESS)
6010 {
cristy0c832c62014-03-07 22:21:04 +00006011 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006012 goto cleanup;
6013 }
6014 /* launch the kernel */
cristy0c832c62014-03-07 22:21:04 +00006015 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass1, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00006016 if (clStatus != CL_SUCCESS)
6017 {
cristy0c832c62014-03-07 22:21:04 +00006018 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006019 goto cleanup;
6020 }
6021 /* launch the kernel */
cristy0c832c62014-03-07 22:21:04 +00006022 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass2, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00006023 if (clStatus != CL_SUCCESS)
6024 {
cristy0c832c62014-03-07 22:21:04 +00006025 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006026 goto cleanup;
6027 }
6028
6029
6030 if (k == 0)
cristy0c832c62014-03-07 22:21:04 +00006031 clStatus =clEnv->library->clSetKernelArg(hullPass1,0,sizeof(cl_mem),(void *)(tempImageBuffer));
cristyf034abb2013-11-24 14:16:14 +00006032 offset.s[0] = -X[k];
6033 offset.s[1] = -Y[k];
6034 polarity = 1;
cristy0c832c62014-03-07 22:21:04 +00006035 clStatus = clEnv->library->clSetKernelArg(hullPass1,4,sizeof(cl_int2),(void *)&offset);
6036 clStatus|= clEnv->library->clSetKernelArg(hullPass1,5,sizeof(int),(void *)&polarity);
6037 clStatus|=clEnv->library->clSetKernelArg(hullPass2,4,sizeof(cl_int2),(void *)&offset);
6038 clStatus|=clEnv->library->clSetKernelArg(hullPass2,5,sizeof(int),(void *)&polarity);
cristyf034abb2013-11-24 14:16:14 +00006039 if (clStatus != CL_SUCCESS)
6040 {
cristy0c832c62014-03-07 22:21:04 +00006041 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006042 goto cleanup;
6043 }
6044 /* launch the kernel */
cristy0c832c62014-03-07 22:21:04 +00006045 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass1, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00006046 if (clStatus != CL_SUCCESS)
6047 {
cristy0c832c62014-03-07 22:21:04 +00006048 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006049 goto cleanup;
6050 }
6051 /* launch the kernel */
cristy0c832c62014-03-07 22:21:04 +00006052 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass2, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00006053 if (clStatus != CL_SUCCESS)
6054 {
cristy0c832c62014-03-07 22:21:04 +00006055 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006056 goto cleanup;
6057 }
6058
6059 offset.s[0] = -X[k];
6060 offset.s[1] = -Y[k];
6061 polarity = -1;
cristy0c832c62014-03-07 22:21:04 +00006062 clStatus = clEnv->library->clSetKernelArg(hullPass1,4,sizeof(cl_int2),(void *)&offset);
6063 clStatus|= clEnv->library->clSetKernelArg(hullPass1,5,sizeof(int),(void *)&polarity);
6064 clStatus|=clEnv->library->clSetKernelArg(hullPass2,4,sizeof(cl_int2),(void *)&offset);
6065 clStatus|=clEnv->library->clSetKernelArg(hullPass2,5,sizeof(int),(void *)&polarity);
cristyf034abb2013-11-24 14:16:14 +00006066 if (clStatus != CL_SUCCESS)
6067 {
cristy0c832c62014-03-07 22:21:04 +00006068 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006069 goto cleanup;
6070 }
6071 /* launch the kernel */
cristy0c832c62014-03-07 22:21:04 +00006072 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass1, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00006073 if (clStatus != CL_SUCCESS)
6074 {
cristy0c832c62014-03-07 22:21:04 +00006075 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006076 goto cleanup;
6077 }
6078 /* launch the kernel */
cristy0c832c62014-03-07 22:21:04 +00006079 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass2, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00006080 if (clStatus != CL_SUCCESS)
6081 {
cristy0c832c62014-03-07 22:21:04 +00006082 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006083 goto cleanup;
6084 }
6085
6086 offset.s[0] = X[k];
6087 offset.s[1] = Y[k];
6088 polarity = -1;
cristy0c832c62014-03-07 22:21:04 +00006089 clStatus = clEnv->library->clSetKernelArg(hullPass1,4,sizeof(cl_int2),(void *)&offset);
6090 clStatus|= clEnv->library->clSetKernelArg(hullPass1,5,sizeof(int),(void *)&polarity);
6091 clStatus|=clEnv->library->clSetKernelArg(hullPass2,4,sizeof(cl_int2),(void *)&offset);
6092 clStatus|=clEnv->library->clSetKernelArg(hullPass2,5,sizeof(int),(void *)&polarity);
cristyf034abb2013-11-24 14:16:14 +00006093
6094 if (k == 3)
cristy0c832c62014-03-07 22:21:04 +00006095 clStatus |=clEnv->library->clSetKernelArg(hullPass2,1,sizeof(cl_mem),(void *)&filteredImageBuffer);
cristyf034abb2013-11-24 14:16:14 +00006096
6097 if (clStatus != CL_SUCCESS)
6098 {
cristy0c832c62014-03-07 22:21:04 +00006099 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006100 goto cleanup;
6101 }
6102 /* launch the kernel */
cristy0c832c62014-03-07 22:21:04 +00006103 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass1, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00006104 if (clStatus != CL_SUCCESS)
6105 {
cristy0c832c62014-03-07 22:21:04 +00006106 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006107 goto cleanup;
6108 }
6109 /* launch the kernel */
cristy0c832c62014-03-07 22:21:04 +00006110 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass2, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00006111 if (clStatus != CL_SUCCESS)
6112 {
cristy0c832c62014-03-07 22:21:04 +00006113 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006114 goto cleanup;
6115 }
6116 }
6117
6118 if (ALIGNED(filteredPixels,CLPixelPacket))
6119 {
dirk8a5cf512014-07-28 20:16:27 +00006120 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00006121 clEnv->library->clEnqueueMapBuffer(queue, filteredImageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00006122 }
6123 else
6124 {
dirk8a5cf512014-07-28 20:16:27 +00006125 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00006126 clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00006127 }
6128 if (clStatus != CL_SUCCESS)
6129 {
cristya22457d2013-12-07 14:03:06 +00006130 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006131 goto cleanup;
6132 }
6133
dirk832becc2014-08-04 19:44:34 +00006134 outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
6135
6136cleanup:
6137 OpenCLLogException(__FUNCTION__,__LINE__,exception);
6138
6139 image_view=DestroyCacheView(image_view);
6140 if (filteredImage_view != NULL)
dirk8a5cf512014-07-28 20:16:27 +00006141 filteredImage_view=DestroyCacheView(filteredImage_view);
cristya22457d2013-12-07 14:03:06 +00006142
cristyf034abb2013-11-24 14:16:14 +00006143 if (queue != NULL) RelinquishOpenCLCommandQueue(clEnv, queue);
dirk8a5cf512014-07-28 20:16:27 +00006144 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
cristyf034abb2013-11-24 14:16:14 +00006145 for (k = 0; k < 2; k++)
6146 {
cristy0c832c62014-03-07 22:21:04 +00006147 if (tempImageBuffer[k]!=NULL) clEnv->library->clReleaseMemObject(tempImageBuffer[k]);
cristyf034abb2013-11-24 14:16:14 +00006148 }
cristy0c832c62014-03-07 22:21:04 +00006149 if (filteredImageBuffer!=NULL) clEnv->library->clReleaseMemObject(filteredImageBuffer);
cristyf034abb2013-11-24 14:16:14 +00006150 if (hullPass1!=NULL) RelinquishOpenCLKernel(clEnv, hullPass1);
6151 if (hullPass2!=NULL) RelinquishOpenCLKernel(clEnv, hullPass2);
dirk8a5cf512014-07-28 20:16:27 +00006152 if (outputReady == MagickFalse && filteredImage != NULL)
6153 filteredImage=DestroyImage(filteredImage);
6154 return(filteredImage);
cristyf034abb2013-11-24 14:16:14 +00006155}
6156
dirk832becc2014-08-04 19:44:34 +00006157MagickExport Image *AccelerateDespeckleImage(const Image* image,
6158 ExceptionInfo* exception)
6159{
6160 Image
6161 *filteredImage;
6162
6163 assert(image != NULL);
6164 assert(exception != (ExceptionInfo *) NULL);
6165
6166 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
6167 (checkAccelerateCondition(image, AllChannels) == MagickFalse))
6168 return NULL;
6169
6170 filteredImage=ComputeDespeckleImage(image,exception);
6171 return(filteredImage);
cristyf034abb2013-11-24 14:16:14 +00006172}
6173
dirk832becc2014-08-04 19:44:34 +00006174static Image *ComputeAddNoiseImage(const Image *image,
6175 const ChannelType channel,const NoiseType noise_type,
6176 ExceptionInfo *exception)
6177{
6178 CacheView
6179 *filteredImage_view,
6180 *image_view;
6181
6182 cl_command_queue
6183 queue;
6184
6185 cl_context
6186 context;
6187
6188 cl_int
dirk859503c2014-08-13 20:39:42 +00006189 inputPixelCount,
6190 pixelsPerWorkitem,
dirk832becc2014-08-04 19:44:34 +00006191 clStatus;
6192
dirk859503c2014-08-13 20:39:42 +00006193 cl_uint
6194 seed0,
6195 seed1;
6196
dirk832becc2014-08-04 19:44:34 +00006197 cl_kernel
6198 addNoiseKernel;
6199
6200 cl_mem_flags
6201 mem_flags;
6202
6203 cl_mem
6204 filteredImageBuffer,
dirk859503c2014-08-13 20:39:42 +00006205 imageBuffer;
dirk832becc2014-08-04 19:44:34 +00006206
6207 const char
6208 *option;
6209
6210 const void
6211 *inputPixels;
6212
6213 float
dirk859503c2014-08-13 20:39:42 +00006214 attenuate;
dirk832becc2014-08-04 19:44:34 +00006215
6216 MagickBooleanType
6217 outputReady;
6218
6219 MagickCLEnv
6220 clEnv;
6221
6222 MagickSizeType
6223 length;
6224
6225 Image
6226 *filteredImage;
6227
dirk832becc2014-08-04 19:44:34 +00006228 RandomInfo
6229 **restrict random_info;
6230
6231 size_t
dirk859503c2014-08-13 20:39:42 +00006232 global_work_size[1],
6233 local_work_size[1];
dirk832becc2014-08-04 19:44:34 +00006234
6235 unsigned int
dirk832becc2014-08-04 19:44:34 +00006236 k,
dirk859503c2014-08-13 20:39:42 +00006237 numRandomNumberPerPixel;
dirk832becc2014-08-04 19:44:34 +00006238
6239#if defined(MAGICKCORE_OPENMP_SUPPORT)
6240 unsigned long
6241 key;
6242#endif
6243
6244 void
6245 *filteredPixels,
6246 *hostPtr;
6247
6248 outputReady = MagickFalse;
6249 clEnv = NULL;
6250 inputPixels = NULL;
6251 filteredImage = NULL;
6252 filteredImage_view = NULL;
6253 filteredPixels = NULL;
dirk832becc2014-08-04 19:44:34 +00006254 context = NULL;
6255 imageBuffer = NULL;
dirk832becc2014-08-04 19:44:34 +00006256 filteredImageBuffer = NULL;
6257 queue = NULL;
6258 addNoiseKernel = NULL;
6259
6260 clEnv = GetDefaultOpenCLEnv();
6261 context = GetOpenCLContext(clEnv);
6262 queue = AcquireOpenCLCommandQueue(clEnv);
6263
6264 image_view=AcquireVirtualCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00006265 inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
cristye85d0f72013-11-27 02:25:43 +00006266 if (inputPixels == (void *) NULL)
6267 {
dirk8a5cf512014-07-28 20:16:27 +00006268 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristye85d0f72013-11-27 02:25:43 +00006269 goto cleanup;
6270 }
6271
6272 if (ALIGNED(inputPixels,CLPixelPacket))
6273 {
6274 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
6275 }
6276 else
6277 {
6278 mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
6279 }
6280 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00006281 length = image->columns * image->rows;
6282 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristye85d0f72013-11-27 02:25:43 +00006283 if (clStatus != CL_SUCCESS)
6284 {
cristy0c832c62014-03-07 22:21:04 +00006285 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristye85d0f72013-11-27 02:25:43 +00006286 goto cleanup;
6287 }
6288
6289
dirk8a5cf512014-07-28 20:16:27 +00006290 filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristye85d0f72013-11-27 02:25:43 +00006291 assert(filteredImage != NULL);
dirk8a5cf512014-07-28 20:16:27 +00006292 if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
cristye85d0f72013-11-27 02:25:43 +00006293 {
cristya22457d2013-12-07 14:03:06 +00006294 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
cristye85d0f72013-11-27 02:25:43 +00006295 goto cleanup;
6296 }
dirk832becc2014-08-04 19:44:34 +00006297 filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
dirk8a5cf512014-07-28 20:16:27 +00006298 filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
cristye85d0f72013-11-27 02:25:43 +00006299 if (filteredPixels == (void *) NULL)
6300 {
cristya22457d2013-12-07 14:03:06 +00006301 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
cristye85d0f72013-11-27 02:25:43 +00006302 goto cleanup;
6303 }
6304
6305 if (ALIGNED(filteredPixels,CLPixelPacket))
6306 {
6307 mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
6308 hostPtr = filteredPixels;
6309 }
6310 else
6311 {
6312 mem_flags = CL_MEM_WRITE_ONLY;
6313 hostPtr = NULL;
6314 }
6315 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00006316 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00006317 filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
cristye85d0f72013-11-27 02:25:43 +00006318 if (clStatus != CL_SUCCESS)
6319 {
cristy0c832c62014-03-07 22:21:04 +00006320 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristye85d0f72013-11-27 02:25:43 +00006321 goto cleanup;
6322 }
6323
6324 /* find out how many random numbers needed by pixel */
6325 numRandomNumberPerPixel = 0;
6326 {
6327 unsigned int numRandPerChannel = 0;
6328 switch (noise_type)
6329 {
6330 case UniformNoise:
6331 case ImpulseNoise:
6332 case LaplacianNoise:
6333 case RandomNoise:
6334 default:
6335 numRandPerChannel = 1;
6336 break;
6337 case GaussianNoise:
6338 case MultiplicativeGaussianNoise:
6339 case PoissonNoise:
6340 numRandPerChannel = 2;
6341 break;
6342 };
6343
6344 if ((channel & RedChannel) != 0)
6345 numRandomNumberPerPixel+=numRandPerChannel;
6346 if ((channel & GreenChannel) != 0)
6347 numRandomNumberPerPixel+=numRandPerChannel;
6348 if ((channel & BlueChannel) != 0)
6349 numRandomNumberPerPixel+=numRandPerChannel;
6350 if ((channel & OpacityChannel) != 0)
6351 numRandomNumberPerPixel+=numRandPerChannel;
6352 }
6353
cristye85d0f72013-11-27 02:25:43 +00006354 /* set up the random number generators */
6355 attenuate=1.0;
dirk8a5cf512014-07-28 20:16:27 +00006356 option=GetImageArtifact(image,"attenuate");
cristye85d0f72013-11-27 02:25:43 +00006357 if (option != (char *) NULL)
6358 attenuate=StringToDouble(option,(char **) NULL);
6359 random_info=AcquireRandomInfoThreadSet();
6360#if defined(MAGICKCORE_OPENMP_SUPPORT)
6361 key=GetRandomSecretKey(random_info[0]);
cristy9ff50022014-08-26 17:38:59 +00006362 (void) key;
cristye85d0f72013-11-27 02:25:43 +00006363#endif
6364
dirk859503c2014-08-13 20:39:42 +00006365 addNoiseKernel = AcquireOpenCLKernel(clEnv,MAGICK_OPENCL_ACCELERATE,"GenerateNoiseImage");
6366
6367 {
6368 cl_uint computeUnitCount;
6369 cl_uint workItemCount;
6370 clEnv->library->clGetDeviceInfo(clEnv->device, CL_DEVICE_MAX_COMPUTE_UNITS, sizeof(cl_uint), &computeUnitCount, NULL);
6371 workItemCount = computeUnitCount * 2 * 256; // 256 work items per group, 2 groups per CU
6372 inputPixelCount = image->columns * image->rows;
6373 pixelsPerWorkitem = (inputPixelCount + workItemCount - 1) / workItemCount;
6374 pixelsPerWorkitem = ((pixelsPerWorkitem + 3) / 4) * 4;
6375
6376 local_work_size[0] = 256;
6377 global_work_size[0] = workItemCount;
6378 }
6379 {
6380 RandomInfo* randomInfo = AcquireRandomInfo();
6381 const unsigned long* s = GetRandomInfoSeed(randomInfo);
6382 seed0 = s[0];
6383 GetPseudoRandomValue(randomInfo);
6384 seed1 = s[0];
6385 randomInfo = DestroyRandomInfo(randomInfo);
6386 }
cristye85d0f72013-11-27 02:25:43 +00006387
6388 k = 0;
dirk8a5cf512014-07-28 20:16:27 +00006389 clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00006390 clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(cl_mem),(void *)&filteredImageBuffer);
dirk859503c2014-08-13 20:39:42 +00006391 clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(cl_uint),(void *)&inputPixelCount);
6392 clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(cl_uint),(void *)&pixelsPerWorkitem);
cristy0c832c62014-03-07 22:21:04 +00006393 clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(ChannelType),(void *)&channel);
6394 clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(NoiseType),(void *)&noise_type);
cristye85d0f72013-11-27 02:25:43 +00006395 attenuate=1.0f;
dirk8a5cf512014-07-28 20:16:27 +00006396 option=GetImageArtifact(image,"attenuate");
cristye85d0f72013-11-27 02:25:43 +00006397 if (option != (char *) NULL)
6398 attenuate=(float)StringToDouble(option,(char **) NULL);
cristy0c832c62014-03-07 22:21:04 +00006399 clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(float),(void *)&attenuate);
dirk859503c2014-08-13 20:39:42 +00006400 clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(cl_uint),(void *)&seed0);
6401 clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(cl_uint),(void *)&seed1);
cristy0c832c62014-03-07 22:21:04 +00006402 clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(unsigned int),(void *)&numRandomNumberPerPixel);
cristye85d0f72013-11-27 02:25:43 +00006403
dirk859503c2014-08-13 20:39:42 +00006404 clEnv->library->clEnqueueNDRangeKernel(queue,addNoiseKernel,1,NULL,global_work_size,NULL,0,NULL,NULL);
cristye85d0f72013-11-27 02:25:43 +00006405
6406 if (ALIGNED(filteredPixels,CLPixelPacket))
6407 {
dirk8a5cf512014-07-28 20:16:27 +00006408 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00006409 clEnv->library->clEnqueueMapBuffer(queue, filteredImageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
cristye85d0f72013-11-27 02:25:43 +00006410 }
6411 else
6412 {
dirk8a5cf512014-07-28 20:16:27 +00006413 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00006414 clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
cristye85d0f72013-11-27 02:25:43 +00006415 }
6416 if (clStatus != CL_SUCCESS)
6417 {
cristya22457d2013-12-07 14:03:06 +00006418 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristye85d0f72013-11-27 02:25:43 +00006419 goto cleanup;
6420 }
6421
dirk832becc2014-08-04 19:44:34 +00006422 outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
6423
6424cleanup:
6425 OpenCLLogException(__FUNCTION__,__LINE__,exception);
6426
6427 image_view=DestroyCacheView(image_view);
6428 if (filteredImage_view != NULL)
dirk8a5cf512014-07-28 20:16:27 +00006429 filteredImage_view=DestroyCacheView(filteredImage_view);
cristya22457d2013-12-07 14:03:06 +00006430
cristye85d0f72013-11-27 02:25:43 +00006431 if (queue!=NULL) RelinquishOpenCLCommandQueue(clEnv, queue);
6432 if (addNoiseKernel!=NULL) RelinquishOpenCLKernel(clEnv, addNoiseKernel);
dirk8a5cf512014-07-28 20:16:27 +00006433 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00006434 if (filteredImageBuffer!=NULL) clEnv->library->clReleaseMemObject(filteredImageBuffer);
dirk832becc2014-08-04 19:44:34 +00006435 if (outputReady == MagickFalse && filteredImage != NULL)
6436 filteredImage=DestroyImage(filteredImage);
6437
dirk8a5cf512014-07-28 20:16:27 +00006438 return(filteredImage);
cristye85d0f72013-11-27 02:25:43 +00006439}
6440
cristye85d0f72013-11-27 02:25:43 +00006441
dirk832becc2014-08-04 19:44:34 +00006442MagickExport Image *AccelerateAddNoiseImage(const Image *image,
6443 const ChannelType channel,const NoiseType noise_type,
6444 ExceptionInfo *exception)
6445{
6446 Image
6447 *filteredImage;
6448
6449 assert(image != NULL);
6450 assert(exception != (ExceptionInfo *) NULL);
6451
6452 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
6453 (checkAccelerateCondition(image, channel) == MagickFalse))
6454 return NULL;
6455
dirk859503c2014-08-13 20:39:42 +00006456 filteredImage = ComputeAddNoiseImage(image,channel,noise_type,exception);
dirk832becc2014-08-04 19:44:34 +00006457
6458 return(filteredImage);
cristye85d0f72013-11-27 02:25:43 +00006459}
6460
dirk832becc2014-08-04 19:44:34 +00006461static MagickBooleanType LaunchRandomImageKernel(MagickCLEnv clEnv,
6462 cl_command_queue queue,cl_mem imageBuffer,const unsigned int imageColumns,
6463 const unsigned int imageRows,cl_mem seedBuffer,
6464 const unsigned int numGenerators,ExceptionInfo *exception)
6465{
6466 int
6467 k;
6468
6469 cl_int
6470 clStatus;
6471
6472 cl_kernel
6473 randomImageKernel;
6474
6475 MagickBooleanType
6476 status;
6477
6478 size_t
6479 global_work_size,
6480 local_work_size;
6481
dirk8a5cf512014-07-28 20:16:27 +00006482 status = MagickFalse;
cristy0c832c62014-03-07 22:21:04 +00006483 randomImageKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "RandomImage");
6484
6485 k = 0;
dirk8a5cf512014-07-28 20:16:27 +00006486 clEnv->library->clSetKernelArg(randomImageKernel,k++,sizeof(cl_mem),(void*)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00006487 clEnv->library->clSetKernelArg(randomImageKernel,k++,sizeof(cl_uint),(void*)&imageColumns);
6488 clEnv->library->clSetKernelArg(randomImageKernel,k++,sizeof(cl_uint),(void*)&imageRows);
6489 clEnv->library->clSetKernelArg(randomImageKernel,k++,sizeof(cl_mem),(void*)&seedBuffer);
6490 {
6491 const float randNormNumerator = 1.0f;
6492 const unsigned int randNormDenominator = (unsigned int)(~0UL);
6493 clEnv->library->clSetKernelArg(randomImageKernel,k++,
6494 sizeof(float),(void*)&randNormNumerator);
6495 clEnv->library->clSetKernelArg(randomImageKernel,k++,
6496 sizeof(cl_uint),(void*)&randNormDenominator);
6497 }
6498
6499
6500 global_work_size = numGenerators;
6501 local_work_size = 64;
6502
6503 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue,randomImageKernel,1,NULL,&global_work_size,
6504 &local_work_size,0,NULL,NULL);
6505
6506 if (clStatus != CL_SUCCESS)
6507 {
6508 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning,
6509 "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
6510 goto cleanup;
6511 }
6512 status = MagickTrue;
6513
6514cleanup:
6515 if (randomImageKernel!=NULL) RelinquishOpenCLKernel(clEnv, randomImageKernel);
dirk832becc2014-08-04 19:44:34 +00006516 return(status);
6517}
6518
6519static MagickBooleanType ComputeRandomImage(Image* image,
6520 ExceptionInfo* exception)
6521{
6522 CacheView
6523 *image_view;
6524
6525 cl_command_queue
6526 queue;
6527
6528 cl_context
6529 context;
6530
6531 cl_int
6532 clStatus;
6533
6534 /* Don't release this buffer in this function !!! */
6535 cl_mem
6536 randomNumberSeedsBuffer;
6537
6538 cl_mem_flags
6539 mem_flags;
6540
6541 cl_mem
6542 imageBuffer;
6543
6544 MagickBooleanType
6545 outputReady,
6546 status;
6547
6548 MagickCLEnv
6549 clEnv;
6550
6551 MagickSizeType
6552 length;
6553
6554 void
6555 *inputPixels;
6556
6557 status = MagickFalse;
6558 outputReady = MagickFalse;
6559 inputPixels = NULL;
6560 context = NULL;
6561 imageBuffer = NULL;
6562 queue = NULL;
6563
6564 clEnv = GetDefaultOpenCLEnv();
6565 context = GetOpenCLContext(clEnv);
6566
6567 /* Create and initialize OpenCL buffers. */
6568 image_view=AcquireAuthenticCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00006569 inputPixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
cristy0c832c62014-03-07 22:21:04 +00006570 if (inputPixels == (void *) NULL)
6571 {
dirk8a5cf512014-07-28 20:16:27 +00006572 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristy0c832c62014-03-07 22:21:04 +00006573 goto cleanup;
6574 }
6575
6576 /* If the host pointer is aligned to the size of CLPixelPacket,
6577 then use the host buffer directly from the GPU; otherwise,
6578 create a buffer on the GPU and copy the data over */
6579 if (ALIGNED(inputPixels,CLPixelPacket))
6580 {
6581 mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
6582 }
6583 else
6584 {
6585 mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
6586 }
6587 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00006588 length = image->columns * image->rows;
6589 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristy0c832c62014-03-07 22:21:04 +00006590 if (clStatus != CL_SUCCESS)
6591 {
6592 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
6593 goto cleanup;
6594 }
6595
6596 queue = AcquireOpenCLCommandQueue(clEnv);
6597
6598 randomNumberSeedsBuffer = GetAndLockRandSeedBuffer(clEnv);
6599 if (randomNumberSeedsBuffer==NULL)
6600 {
6601 (void) OpenCLThrowMagickException(exception, GetMagickModule(),
6602 ResourceLimitWarning, "Failed to get GPU random number generators.",
6603 "'%s'", ".");
6604 goto cleanup;
6605 }
6606
6607 status = LaunchRandomImageKernel(clEnv,queue,
dirk8a5cf512014-07-28 20:16:27 +00006608 imageBuffer,
6609 image->columns,
6610 image->rows,
cristy0c832c62014-03-07 22:21:04 +00006611 randomNumberSeedsBuffer,
6612 GetNumRandGenerators(clEnv),
6613 exception);
6614 if (status==MagickFalse)
6615 {
6616 goto cleanup;
6617 }
6618
6619 if (ALIGNED(inputPixels,CLPixelPacket))
6620 {
dirk8a5cf512014-07-28 20:16:27 +00006621 length = image->columns * image->rows;
6622 clEnv->library->clEnqueueMapBuffer(queue, imageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
cristy0c832c62014-03-07 22:21:04 +00006623 }
6624 else
6625 {
dirk8a5cf512014-07-28 20:16:27 +00006626 length = image->columns * image->rows;
6627 clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), inputPixels, 0, NULL, NULL);
cristy0c832c62014-03-07 22:21:04 +00006628 }
6629 if (clStatus != CL_SUCCESS)
6630 {
6631 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
6632 goto cleanup;
6633 }
dirk832becc2014-08-04 19:44:34 +00006634 outputReady=SyncCacheViewAuthenticPixels(image_view,exception);
6635
6636cleanup:
6637 OpenCLLogException(__FUNCTION__,__LINE__,exception);
6638
dirk8a5cf512014-07-28 20:16:27 +00006639 image_view=DestroyCacheView(image_view);
cristy0c832c62014-03-07 22:21:04 +00006640
6641 UnlockRandSeedBuffer(clEnv);
dirk8a5cf512014-07-28 20:16:27 +00006642 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00006643 if (queue != NULL) RelinquishOpenCLCommandQueue(clEnv, queue);
6644 return outputReady;
6645}
6646
dirk832becc2014-08-04 19:44:34 +00006647MagickExport MagickBooleanType AccelerateRandomImage(Image *image,
6648 ExceptionInfo* exception)
6649{
6650 MagickBooleanType
6651 status;
6652
6653 assert(image != NULL);
6654 assert(exception != (ExceptionInfo *) NULL);
6655
6656 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
6657 (checkAccelerateCondition(image, AllChannels) == MagickFalse))
6658 return(MagickFalse);
6659
6660 status=ComputeRandomImage(image,exception);
6661 return(status);
cristy0c832c62014-03-07 22:21:04 +00006662}
6663
dirk832becc2014-08-04 19:44:34 +00006664static Image* ComputeMotionBlurImage(const Image *image,
6665 const ChannelType channel,const double *kernel,const size_t width,
6666 const OffsetInfo *offset,ExceptionInfo *exception)
6667{
6668 CacheView
6669 *filteredImage_view,
6670 *image_view;
6671
6672 cl_command_queue
6673 queue;
6674
6675 cl_context
6676 context;
6677
6678 cl_float4
6679 biasPixel;
6680
6681 cl_int
6682 clStatus;
6683
6684 cl_kernel
6685 motionBlurKernel;
6686
6687 cl_mem
6688 filteredImageBuffer,
6689 imageBuffer,
6690 imageKernelBuffer,
6691 offsetBuffer;
6692
6693 cl_mem_flags
6694 mem_flags;
6695
6696 const void
6697 *inputPixels;
6698
6699 float
6700 *kernelBufferPtr;
6701
6702 Image
6703 *filteredImage;
6704
6705 int
6706 *offsetBufferPtr;
6707
6708 MagickBooleanType
6709 outputReady;
6710
6711 MagickCLEnv
6712 clEnv;
6713
6714 PixelInfo
6715 bias;
6716
6717 MagickSizeType
6718 length;
6719
6720 size_t
6721 global_work_size[2],
6722 local_work_size[2];
6723
6724 unsigned int
6725 i,
6726 imageHeight,
6727 imageWidth,
6728 matte;
6729
6730 void
6731 *filteredPixels,
6732 *hostPtr;
6733
6734 outputReady = MagickFalse;
6735 context = NULL;
6736 filteredImage = NULL;
6737 filteredImage_view = NULL;
6738 imageBuffer = NULL;
6739 filteredImageBuffer = NULL;
6740 imageKernelBuffer = NULL;
6741 motionBlurKernel = NULL;
6742 queue = NULL;
6743
6744 clEnv = GetDefaultOpenCLEnv();
6745 context = GetOpenCLContext(clEnv);
6746
6747 /* Create and initialize OpenCL buffers. */
6748
6749 image_view=AcquireVirtualCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00006750 inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
cristy0c832c62014-03-07 22:21:04 +00006751 if (inputPixels == (const void *) NULL)
6752 {
6753 (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
dirk8a5cf512014-07-28 20:16:27 +00006754 "UnableToReadPixelCache.","`%s'",image->filename);
cristy0c832c62014-03-07 22:21:04 +00006755 goto cleanup;
6756 }
6757
6758 // If the host pointer is aligned to the size of CLPixelPacket,
6759 // then use the host buffer directly from the GPU; otherwise,
6760 // create a buffer on the GPU and copy the data over
6761 if (ALIGNED(inputPixels,CLPixelPacket))
6762 {
6763 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
6764 }
6765 else
6766 {
6767 mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
6768 }
6769 // create a CL buffer from image pixel buffer
dirk8a5cf512014-07-28 20:16:27 +00006770 length = image->columns * image->rows;
6771 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags,
cristy0c832c62014-03-07 22:21:04 +00006772 length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
6773 if (clStatus != CL_SUCCESS)
6774 {
6775 (void) ThrowMagickException(exception, GetMagickModule(),
6776 ResourceLimitError, "clEnv->library->clCreateBuffer failed.",".");
6777 goto cleanup;
6778 }
6779
6780
dirk8a5cf512014-07-28 20:16:27 +00006781 filteredImage = CloneImage(image,image->columns,image->rows,
cristy0c832c62014-03-07 22:21:04 +00006782 MagickTrue,exception);
6783 assert(filteredImage != NULL);
dirk8a5cf512014-07-28 20:16:27 +00006784 if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
cristy0c832c62014-03-07 22:21:04 +00006785 {
6786 (void) ThrowMagickException(exception, GetMagickModule(),
6787 ResourceLimitError, "CloneImage failed.", "'%s'", ".");
6788 goto cleanup;
6789 }
dirk832becc2014-08-04 19:44:34 +00006790 filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
dirk8a5cf512014-07-28 20:16:27 +00006791 filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
cristy0c832c62014-03-07 22:21:04 +00006792 if (filteredPixels == (void *) NULL)
6793 {
6794 (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
6795 "UnableToReadPixelCache.","`%s'",filteredImage->filename);
6796 goto cleanup;
6797 }
6798
6799 if (ALIGNED(filteredPixels,CLPixelPacket))
6800 {
6801 mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
6802 hostPtr = filteredPixels;
6803 }
6804 else
6805 {
6806 mem_flags = CL_MEM_WRITE_ONLY;
6807 hostPtr = NULL;
6808 }
6809 // create a CL buffer from image pixel buffer
dirk8a5cf512014-07-28 20:16:27 +00006810 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00006811 filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags,
6812 length * sizeof(CLPixelPacket), hostPtr, &clStatus);
6813 if (clStatus != CL_SUCCESS)
6814 {
6815 (void) ThrowMagickException(exception, GetMagickModule(),
6816 ResourceLimitError, "clEnv->library->clCreateBuffer failed.",".");
6817 goto cleanup;
6818 }
6819
6820
6821 imageKernelBuffer = clEnv->library->clCreateBuffer(context,
6822 CL_MEM_READ_ONLY|CL_MEM_ALLOC_HOST_PTR, width * sizeof(float), NULL,
6823 &clStatus);
6824 if (clStatus != CL_SUCCESS)
6825 {
6826 (void) ThrowMagickException(exception, GetMagickModule(),
6827 ResourceLimitError, "clEnv->library->clCreateBuffer failed.",".");
6828 goto cleanup;
6829 }
6830
6831 queue = AcquireOpenCLCommandQueue(clEnv);
6832 kernelBufferPtr = (float*)clEnv->library->clEnqueueMapBuffer(queue, imageKernelBuffer,
6833 CL_TRUE, CL_MAP_WRITE, 0, width * sizeof(float), 0, NULL, NULL, &clStatus);
6834 if (clStatus != CL_SUCCESS)
6835 {
6836 (void) ThrowMagickException(exception, GetMagickModule(),
6837 ResourceLimitError, "clEnv->library->clEnqueueMapBuffer failed.",".");
6838 goto cleanup;
6839 }
6840 for (i = 0; i < width; i++)
6841 {
6842 kernelBufferPtr[i] = (float) kernel[i];
6843 }
6844 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, imageKernelBuffer, kernelBufferPtr,
6845 0, NULL, NULL);
6846 if (clStatus != CL_SUCCESS)
6847 {
6848 (void) ThrowMagickException(exception, GetMagickModule(), ModuleFatalError,
6849 "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
6850 goto cleanup;
6851 }
6852
6853 offsetBuffer = clEnv->library->clCreateBuffer(context,
6854 CL_MEM_READ_ONLY|CL_MEM_ALLOC_HOST_PTR, width * sizeof(cl_int2), NULL,
6855 &clStatus);
6856 if (clStatus != CL_SUCCESS)
6857 {
6858 (void) ThrowMagickException(exception, GetMagickModule(),
6859 ResourceLimitError, "clEnv->library->clCreateBuffer failed.",".");
6860 goto cleanup;
6861 }
6862
6863 offsetBufferPtr = (int*)clEnv->library->clEnqueueMapBuffer(queue, offsetBuffer, CL_TRUE,
6864 CL_MAP_WRITE, 0, width * sizeof(cl_int2), 0, NULL, NULL, &clStatus);
6865 if (clStatus != CL_SUCCESS)
6866 {
6867 (void) ThrowMagickException(exception, GetMagickModule(),
6868 ResourceLimitError, "clEnv->library->clEnqueueMapBuffer failed.",".");
6869 goto cleanup;
6870 }
6871 for (i = 0; i < width; i++)
6872 {
6873 offsetBufferPtr[2*i] = (int)offset[i].x;
6874 offsetBufferPtr[2*i+1] = (int)offset[i].y;
6875 }
6876 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, offsetBuffer, offsetBufferPtr, 0,
6877 NULL, NULL);
6878 if (clStatus != CL_SUCCESS)
6879 {
6880 (void) ThrowMagickException(exception, GetMagickModule(), ModuleFatalError,
6881 "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
6882 goto cleanup;
6883 }
6884
6885
6886 // get the OpenCL kernel
6887 motionBlurKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE,
6888 "MotionBlur");
6889 if (motionBlurKernel == NULL)
6890 {
6891 (void) ThrowMagickException(exception, GetMagickModule(), ModuleFatalError,
6892 "AcquireOpenCLKernel failed.", "'%s'", ".");
6893 goto cleanup;
6894 }
6895
6896 // set the kernel arguments
6897 i = 0;
6898 clStatus=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(cl_mem),
dirk8a5cf512014-07-28 20:16:27 +00006899 (void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00006900 clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(cl_mem),
6901 (void *)&filteredImageBuffer);
dirk8a5cf512014-07-28 20:16:27 +00006902 imageWidth = image->columns;
6903 imageHeight = image->rows;
cristy0c832c62014-03-07 22:21:04 +00006904 clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(unsigned int),
6905 &imageWidth);
6906 clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(unsigned int),
6907 &imageHeight);
6908 clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(cl_mem),
6909 (void *)&imageKernelBuffer);
6910 clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(unsigned int),
6911 &width);
6912 clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(cl_mem),
6913 (void *)&offsetBuffer);
6914
dirk8a5cf512014-07-28 20:16:27 +00006915 GetPixelInfo(image,&bias);
cristy0c832c62014-03-07 22:21:04 +00006916 biasPixel.s[0] = bias.red;
6917 biasPixel.s[1] = bias.green;
6918 biasPixel.s[2] = bias.blue;
dirk8a5cf512014-07-28 20:16:27 +00006919 biasPixel.s[3] = bias.alpha;
cristy0c832c62014-03-07 22:21:04 +00006920 clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(cl_float4), &biasPixel);
6921
6922 clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(ChannelType), &channel);
dirk8a5cf512014-07-28 20:16:27 +00006923 matte = (image->alpha_trait==BlendPixelTrait)?1:0;
cristy0c832c62014-03-07 22:21:04 +00006924 clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(unsigned int), &matte);
6925 if (clStatus != CL_SUCCESS)
6926 {
6927 (void) ThrowMagickException(exception, GetMagickModule(), ModuleFatalError,
6928 "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
6929 goto cleanup;
6930 }
6931
6932 // launch the kernel
6933 local_work_size[0] = 16;
6934 local_work_size[1] = 16;
6935 global_work_size[0] = (size_t)padGlobalWorkgroupSizeToLocalWorkgroupSize(
dirk8a5cf512014-07-28 20:16:27 +00006936 image->columns,local_work_size[0]);
cristy0c832c62014-03-07 22:21:04 +00006937 global_work_size[1] = (size_t)padGlobalWorkgroupSizeToLocalWorkgroupSize(
dirk8a5cf512014-07-28 20:16:27 +00006938 image->rows,local_work_size[1]);
cristy0c832c62014-03-07 22:21:04 +00006939 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, motionBlurKernel, 2, NULL,
6940 global_work_size, local_work_size, 0, NULL, NULL);
6941
6942 if (clStatus != CL_SUCCESS)
6943 {
6944 (void) ThrowMagickException(exception, GetMagickModule(), ModuleFatalError,
6945 "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
6946 goto cleanup;
6947 }
6948 clEnv->library->clFlush(queue);
6949
6950 if (ALIGNED(filteredPixels,CLPixelPacket))
6951 {
dirk8a5cf512014-07-28 20:16:27 +00006952 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00006953 clEnv->library->clEnqueueMapBuffer(queue, filteredImageBuffer, CL_TRUE,
6954 CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL,
6955 NULL, &clStatus);
6956 }
6957 else
6958 {
dirk8a5cf512014-07-28 20:16:27 +00006959 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00006960 clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0,
6961 length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
6962 }
6963 if (clStatus != CL_SUCCESS)
6964 {
6965 (void) ThrowMagickException(exception, GetMagickModule(), ModuleFatalError,
6966 "Reading output image from CL buffer failed.", "'%s'", ".");
6967 goto cleanup;
6968 }
dirk832becc2014-08-04 19:44:34 +00006969 outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
6970
6971cleanup:
6972
6973 image_view=DestroyCacheView(image_view);
6974 if (filteredImage_view != NULL)
dirk8a5cf512014-07-28 20:16:27 +00006975 filteredImage_view=DestroyCacheView(filteredImage_view);
cristy0c832c62014-03-07 22:21:04 +00006976
6977 if (filteredImageBuffer!=NULL) clEnv->library->clReleaseMemObject(filteredImageBuffer);
dirk8a5cf512014-07-28 20:16:27 +00006978 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00006979 if (imageKernelBuffer!=NULL) clEnv->library->clReleaseMemObject(imageKernelBuffer);
6980 if (motionBlurKernel!=NULL) RelinquishOpenCLKernel(clEnv, motionBlurKernel);
6981 if (queue != NULL) RelinquishOpenCLCommandQueue(clEnv, queue);
dirk832becc2014-08-04 19:44:34 +00006982 if (outputReady == MagickFalse && filteredImage != NULL)
6983 filteredImage=DestroyImage(filteredImage);
6984
dirk8a5cf512014-07-28 20:16:27 +00006985 return(filteredImage);
cristy0c832c62014-03-07 22:21:04 +00006986}
6987
dirk832becc2014-08-04 19:44:34 +00006988MagickExport Image *AccelerateMotionBlurImage(const Image *image,
6989 const ChannelType channel,const double* kernel,const size_t width,
6990 const OffsetInfo *offset,ExceptionInfo *exception)
6991{
6992 Image
6993 *filteredImage;
6994
6995 assert(image != NULL);
6996 assert(kernel != (double *) NULL);
6997 assert(offset != (OffsetInfo *) NULL);
6998 assert(exception != (ExceptionInfo *) NULL);
6999
7000 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
7001 (checkAccelerateCondition(image, channel) == MagickFalse))
7002 return NULL;
7003
7004 filteredImage=ComputeMotionBlurImage(image, channel, kernel, width,
7005 offset, exception);
7006 return(filteredImage);
cristy0c832c62014-03-07 22:21:04 +00007007}
7008
dirk832becc2014-08-04 19:44:34 +00007009static MagickBooleanType LaunchCompositeKernel(MagickCLEnv clEnv,
7010 cl_command_queue queue,cl_mem imageBuffer,const unsigned int inputWidth,
7011 const unsigned int inputHeight,const unsigned int matte,
7012 const ChannelType channel,const CompositeOperator compose,
7013 const cl_mem compositeImageBuffer,const unsigned int compositeWidth,
7014 const unsigned int compositeHeight,const float destination_dissolve,
7015 const float source_dissolve,ExceptionInfo *magick_unused(exception))
7016{
7017 cl_int
7018 clStatus;
7019
7020 cl_kernel
7021 compositeKernel;
7022
7023 int
7024 k;
7025
7026 size_t
7027 global_work_size[2],
7028 local_work_size[2];
7029
7030 unsigned int
7031 composeOp;
7032
cristy0c832c62014-03-07 22:21:04 +00007033 magick_unreferenced(exception);
7034
7035 compositeKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE,
7036 "Composite");
7037
7038 k = 0;
dirk8a5cf512014-07-28 20:16:27 +00007039 clStatus=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(cl_mem),(void*)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00007040 clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(unsigned int),(void*)&inputWidth);
7041 clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(unsigned int),(void*)&inputHeight);
7042 clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(cl_mem),(void*)&compositeImageBuffer);
7043 clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(unsigned int),(void*)&compositeWidth);
7044 clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(unsigned int),(void*)&compositeHeight);
7045 composeOp = (unsigned int)compose;
7046 clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(unsigned int),(void*)&composeOp);
7047 clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(ChannelType),(void*)&channel);
7048 clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(unsigned int),(void*)&matte);
7049 clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(float),(void*)&destination_dissolve);
7050 clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(float),(void*)&source_dissolve);
7051
7052 if (clStatus!=CL_SUCCESS)
7053 return MagickFalse;
7054
7055 local_work_size[0] = 64;
7056 local_work_size[1] = 1;
7057
7058 global_work_size[0] = padGlobalWorkgroupSizeToLocalWorkgroupSize(inputWidth,
7059 local_work_size[0]);
7060 global_work_size[1] = inputHeight;
7061 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, compositeKernel, 2, NULL,
7062 global_work_size, local_work_size, 0, NULL, NULL);
7063
7064
7065 RelinquishOpenCLKernel(clEnv, compositeKernel);
7066
dirk832becc2014-08-04 19:44:34 +00007067 return((clStatus==CL_SUCCESS) ? MagickTrue : MagickFalse);
7068}
7069
7070static MagickBooleanType ComputeCompositeImage(Image *image,
7071 const ChannelType channel,const CompositeOperator compose,
7072 const Image *compositeImage,const ssize_t magick_unused(x_offset),
7073 const ssize_t magick_unused(y_offset),const float destination_dissolve,
7074 const float source_dissolve,ExceptionInfo *exception)
7075{
7076 CacheView
7077 *image_view;
7078
7079 cl_command_queue
7080 queue;
7081
7082 cl_context
7083 context;
7084
7085 cl_int
7086 clStatus;
7087
7088 cl_mem_flags
7089 mem_flags;
7090
7091 cl_mem
7092 compositeImageBuffer,
7093 imageBuffer;
7094
7095 const void
7096 *composePixels;
7097
7098 MagickBooleanType
7099 outputReady,
7100 status;
7101
7102 MagickCLEnv
7103 clEnv;
7104
7105 MagickSizeType
7106 length;
7107
7108 void
7109 *inputPixels;
7110
7111 magick_unreferenced(x_offset);
7112 magick_unreferenced(y_offset);
7113
7114 status = MagickFalse;
7115 outputReady = MagickFalse;
7116 composePixels = NULL;
7117 imageBuffer = NULL;
7118 compositeImageBuffer = NULL;
7119
7120 clEnv = GetDefaultOpenCLEnv();
7121 context = GetOpenCLContext(clEnv);
7122 queue = AcquireOpenCLCommandQueue(clEnv);
7123
7124 /* Create and initialize OpenCL buffers. */
7125 image_view=AcquireAuthenticCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00007126 inputPixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
cristy0c832c62014-03-07 22:21:04 +00007127 if (inputPixels == (void *) NULL)
7128 {
7129 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,
dirk8a5cf512014-07-28 20:16:27 +00007130 "UnableToReadPixelCache.","`%s'",image->filename);
cristy0c832c62014-03-07 22:21:04 +00007131 goto cleanup;
7132 }
7133
7134 /* If the host pointer is aligned to the size of CLPixelPacket,
7135 then use the host buffer directly from the GPU; otherwise,
7136 create a buffer on the GPU and copy the data over */
7137 if (ALIGNED(inputPixels,CLPixelPacket))
7138 {
7139 mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
7140 }
7141 else
7142 {
7143 mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
7144 }
7145 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00007146 length = image->columns * image->rows;
7147 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags,
cristy0c832c62014-03-07 22:21:04 +00007148 length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
7149 if (clStatus != CL_SUCCESS)
7150 {
7151 (void) OpenCLThrowMagickException(exception, GetMagickModule(),
7152 ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
7153 goto cleanup;
7154 }
7155
7156
7157 /* Create and initialize OpenCL buffers. */
7158 composePixels = AcquirePixelCachePixels(compositeImage, &length, exception);
7159 if (composePixels == (void *) NULL)
7160 {
7161 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,
7162 "UnableToReadPixelCache.","`%s'",compositeImage->filename);
7163 goto cleanup;
7164 }
7165
7166 /* If the host pointer is aligned to the size of CLPixelPacket,
7167 then use the host buffer directly from the GPU; otherwise,
7168 create a buffer on the GPU and copy the data over */
7169 if (ALIGNED(composePixels,CLPixelPacket))
7170 {
7171 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
7172 }
7173 else
7174 {
7175 mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
7176 }
7177 /* create a CL buffer from image pixel buffer */
7178 length = compositeImage->columns * compositeImage->rows;
7179 compositeImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags,
7180 length * sizeof(CLPixelPacket), (void*)composePixels, &clStatus);
7181 if (clStatus != CL_SUCCESS)
7182 {
7183 (void) OpenCLThrowMagickException(exception, GetMagickModule(),
7184 ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
7185 goto cleanup;
7186 }
7187
dirk8a5cf512014-07-28 20:16:27 +00007188 status = LaunchCompositeKernel(clEnv,queue,imageBuffer,
7189 (unsigned int) image->columns,
7190 (unsigned int) image->rows,
7191 (unsigned int) (image->alpha_trait==BlendPixelTrait) ? 1 : 0,
cristy0c832c62014-03-07 22:21:04 +00007192 channel, compose, compositeImageBuffer,
7193 (unsigned int) compositeImage->columns,
7194 (unsigned int) compositeImage->rows,
7195 destination_dissolve,source_dissolve,
7196 exception);
7197
7198 if (status==MagickFalse)
7199 goto cleanup;
7200
dirk8a5cf512014-07-28 20:16:27 +00007201 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00007202 if (ALIGNED(inputPixels,CLPixelPacket))
7203 {
dirk8a5cf512014-07-28 20:16:27 +00007204 clEnv->library->clEnqueueMapBuffer(queue, imageBuffer, CL_TRUE,
cristy0c832c62014-03-07 22:21:04 +00007205 CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL,
7206 NULL, &clStatus);
7207 }
7208 else
7209 {
dirk8a5cf512014-07-28 20:16:27 +00007210 clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0,
cristy0c832c62014-03-07 22:21:04 +00007211 length * sizeof(CLPixelPacket), inputPixels, 0, NULL, NULL);
7212 }
7213 if (clStatus==CL_SUCCESS)
dirk832becc2014-08-04 19:44:34 +00007214 outputReady=SyncCacheViewAuthenticPixels(image_view,exception);
7215
7216cleanup:
7217
7218 image_view=DestroyCacheView(image_view);
dirk8a5cf512014-07-28 20:16:27 +00007219 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00007220 if (compositeImageBuffer!=NULL) clEnv->library->clReleaseMemObject(compositeImageBuffer);
7221 if (queue != NULL) RelinquishOpenCLCommandQueue(clEnv, queue);
7222
dirk832becc2014-08-04 19:44:34 +00007223 return(outputReady);
7224}
7225
7226MagickExport MagickBooleanType AccelerateCompositeImage(Image *image,
7227 const ChannelType channel,const CompositeOperator compose,
7228 const Image *composite,const ssize_t x_offset,const ssize_t y_offset,
7229 const float destination_dissolve,const float source_dissolve,
7230 ExceptionInfo *exception)
7231{
7232 MagickBooleanType
7233 status;
7234
7235 assert(image != NULL);
7236 assert(exception != (ExceptionInfo *) NULL);
7237
7238 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
7239 (checkAccelerateCondition(image, channel) == MagickFalse))
dirk8a5cf512014-07-28 20:16:27 +00007240 return(MagickFalse);
cristy0c832c62014-03-07 22:21:04 +00007241
7242 /* only support zero offset and
7243 images with the size for now */
7244 if (x_offset!=0
7245 || y_offset!=0
7246 || image->columns!=composite->columns
7247 || image->rows!=composite->rows)
7248 return MagickFalse;
7249
7250 switch(compose) {
7251 case ColorDodgeCompositeOp:
7252 case BlendCompositeOp:
7253 break;
7254 default:
7255 // unsupported compose operator, quit
7256 return MagickFalse;
7257 };
7258
7259 status = ComputeCompositeImage(image,channel,compose,composite,
7260 x_offset,y_offset,destination_dissolve,source_dissolve,exception);
7261
dirk8a5cf512014-07-28 20:16:27 +00007262 return(status);
cristy0c832c62014-03-07 22:21:04 +00007263}
7264
cristyf034abb2013-11-24 14:16:14 +00007265#else /* MAGICKCORE_OPENCL_SUPPORT */
7266
7267MagickExport Image *AccelerateConvolveImageChannel(
7268 const Image *magick_unused(image),const ChannelType magick_unused(channel),
7269 const KernelInfo *magick_unused(kernel),
7270 ExceptionInfo *magick_unused(exception))
7271{
7272 magick_unreferenced(image);
7273 magick_unreferenced(channel);
7274 magick_unreferenced(kernel);
7275 magick_unreferenced(exception);
7276
7277 return NULL;
7278}
7279
7280MagickExport MagickBooleanType AccelerateFunctionImage(
7281 Image *magick_unused(image),const ChannelType magick_unused(channel),
7282 const MagickFunction magick_unused(function),
7283 const size_t magick_unused(number_parameters),
7284 const double *magick_unused(parameters),
7285 ExceptionInfo *magick_unused(exception))
7286{
7287 magick_unreferenced(image);
7288 magick_unreferenced(channel);
7289 magick_unreferenced(function);
7290 magick_unreferenced(number_parameters);
7291 magick_unreferenced(parameters);
7292 magick_unreferenced(exception);
7293
7294 return MagickFalse;
7295}
7296
7297MagickExport Image *AccelerateBlurImage(const Image *magick_unused(image),
7298 const ChannelType magick_unused(channel),const double magick_unused(radius),
7299 const double magick_unused(sigma),ExceptionInfo *magick_unused(exception))
7300{
7301 magick_unreferenced(image);
7302 magick_unreferenced(channel);
7303 magick_unreferenced(radius);
7304 magick_unreferenced(sigma);
7305 magick_unreferenced(exception);
7306
7307 return NULL;
7308}
7309
dirk6d612cf2014-03-13 21:17:23 +00007310MagickExport Image *AccelerateRotationalBlurImage(
cristyf034abb2013-11-24 14:16:14 +00007311 const Image *magick_unused(image),const ChannelType magick_unused(channel),
7312 const double magick_unused(angle),ExceptionInfo *magick_unused(exception))
7313{
7314 magick_unreferenced(image);
7315 magick_unreferenced(channel);
7316 magick_unreferenced(angle);
7317 magick_unreferenced(exception);
7318
7319 return NULL;
7320}
7321
7322
7323MagickExport Image *AccelerateUnsharpMaskImage(
7324 const Image *magick_unused(image),const ChannelType magick_unused(channel),
7325 const double magick_unused(radius),const double magick_unused(sigma),
7326 const double magick_unused(gain),const double magick_unused(threshold),
7327 ExceptionInfo *magick_unused(exception))
7328{
7329 magick_unreferenced(image);
7330 magick_unreferenced(channel);
7331 magick_unreferenced(radius);
7332 magick_unreferenced(sigma);
7333 magick_unreferenced(gain);
7334 magick_unreferenced(threshold);
7335 magick_unreferenced(exception);
7336
7337 return NULL;
7338}
7339
cristy0b8a2e92014-03-08 00:52:49 +00007340MagickExport
7341MagickBooleanType AccelerateCompositeImage(Image *image,
7342 const ChannelType channel,const CompositeOperator compose,
7343 const Image *composite,const ssize_t x_offset,const ssize_t y_offset,
7344 const float destination_dissolve,const float source_dissolve,
7345 ExceptionInfo *exception)
7346{
7347 magick_unreferenced(image);
cristya219e4b2014-03-08 12:36:16 +00007348 magick_unreferenced(channel);
7349 magick_unreferenced(compose);
7350 magick_unreferenced(composite);
7351 magick_unreferenced(x_offset);
7352 magick_unreferenced(y_offset);
7353 magick_unreferenced(destination_dissolve);
7354 magick_unreferenced(source_dissolve);
7355 magick_unreferenced(exception);
cristy0b8a2e92014-03-08 00:52:49 +00007356
7357 return MagickFalse;
7358}
7359
cristyf034abb2013-11-24 14:16:14 +00007360
7361MagickExport MagickBooleanType AccelerateContrastImage(
7362 Image* magick_unused(image),const MagickBooleanType magick_unused(sharpen),
7363 ExceptionInfo* magick_unused(exception))
7364{
7365 magick_unreferenced(image);
7366 magick_unreferenced(sharpen);
7367 magick_unreferenced(exception);
7368
7369 return MagickFalse;
7370}
7371
cristy0b8a2e92014-03-08 00:52:49 +00007372MagickExport MagickBooleanType AccelerateContrastStretchImageChannel(
cristy0c832c62014-03-07 22:21:04 +00007373 Image * image, const ChannelType channel, const double black_point, const double white_point,
7374 ExceptionInfo* magick_unused(exception))
7375{
7376 magick_unreferenced(image);
7377 magick_unreferenced(channel);
7378 magick_unreferenced(black_point);
7379 magick_unreferenced(white_point);
7380 magick_unreferenced(exception);
7381
7382 return MagickFalse;
7383}
7384
cristyf034abb2013-11-24 14:16:14 +00007385MagickExport MagickBooleanType AccelerateEqualizeImage(
7386 Image* magick_unused(image), const ChannelType magick_unused(channel),
7387 ExceptionInfo* magick_unused(exception))
7388{
7389 magick_unreferenced(image);
7390 magick_unreferenced(channel);
7391 magick_unreferenced(exception);
7392
7393 return MagickFalse;
7394}
7395
7396MagickExport Image *AccelerateDespeckleImage(const Image* magick_unused(image),
7397 ExceptionInfo* magick_unused(exception))
7398{
7399 magick_unreferenced(image);
7400 magick_unreferenced(exception);
7401
7402 return NULL;
7403}
7404
7405MagickExport Image *AccelerateResizeImage(const Image* magick_unused(image),
7406 const size_t magick_unused(resizedColumns),
7407 const size_t magick_unused(resizedRows),
7408 const ResizeFilter* magick_unused(resizeFilter),
7409 ExceptionInfo *magick_unused(exception))
7410{
7411 magick_unreferenced(image);
7412 magick_unreferenced(resizedColumns);
7413 magick_unreferenced(resizedRows);
7414 magick_unreferenced(resizeFilter);
7415 magick_unreferenced(exception);
7416
7417 return NULL;
7418}
7419
cristyf034abb2013-11-24 14:16:14 +00007420MagickExport
7421MagickBooleanType AccelerateModulateImage(
7422 Image* image, double percent_brightness, double percent_hue,
7423 double percent_saturation, ColorspaceType colorspace, ExceptionInfo* exception)
7424{
7425 magick_unreferenced(image);
7426 magick_unreferenced(percent_brightness);
7427 magick_unreferenced(percent_hue);
7428 magick_unreferenced(percent_saturation);
7429 magick_unreferenced(colorspace);
7430 magick_unreferenced(exception);
7431 return(MagickFalse);
7432}
7433
cristy0c832c62014-03-07 22:21:04 +00007434MagickExport
7435MagickBooleanType AccelerateNegateImageChannel(
7436 Image* image, const ChannelType channel, const MagickBooleanType grayscale, ExceptionInfo* exception)
7437{
7438 magick_unreferenced(image);
7439 magick_unreferenced(channel);
7440 magick_unreferenced(grayscale);
7441 magick_unreferenced(exception);
7442 return(MagickFalse);
7443}
7444
7445MagickExport
7446MagickBooleanType AccelerateGrayscaleImage(
7447 Image* image, const PixelIntensityMethod method, ExceptionInfo* exception)
7448{
7449 magick_unreferenced(image);
7450 magick_unreferenced(method);
7451 magick_unreferenced(exception);
7452 return(MagickFalse);
7453}
7454
cristye85d0f72013-11-27 02:25:43 +00007455MagickExport Image *AccelerateAddNoiseImage(const Image *image,
7456 const ChannelType channel, const NoiseType noise_type,ExceptionInfo *exception)
7457{
7458 magick_unreferenced(image);
7459 magick_unreferenced(channel);
7460 magick_unreferenced(noise_type);
7461 magick_unreferenced(exception);
7462 return NULL;
7463}
cristyf034abb2013-11-24 14:16:14 +00007464
cristy0c832c62014-03-07 22:21:04 +00007465
7466MagickExport MagickBooleanType AccelerateRandomImage(Image* image, ExceptionInfo* exception)
7467{
7468 magick_unreferenced(image);
7469 magick_unreferenced(exception);
7470 return MagickFalse;
7471}
7472
7473MagickExport
7474Image* AccelerateMotionBlurImage(const Image *image, const ChannelType channel,
7475 const double* kernel, const size_t width,
7476 const OffsetInfo *offset,
7477 ExceptionInfo *exception)
7478{
7479 magick_unreferenced(image);
7480 magick_unreferenced(channel);
7481 magick_unreferenced(kernel);
7482 magick_unreferenced(width);
7483 magick_unreferenced(offset);
7484 magick_unreferenced(exception);
7485 return NULL;
7486}
7487
cristyf034abb2013-11-24 14:16:14 +00007488#endif /* MAGICKCORE_OPENCL_SUPPORT */
7489
7490MagickExport MagickBooleanType AccelerateConvolveImage(
7491 const Image *magick_unused(image),const KernelInfo *magick_unused(kernel),
7492 Image *magick_unused(convolve_image),ExceptionInfo *magick_unused(exception))
7493{
7494 magick_unreferenced(image);
7495 magick_unreferenced(kernel);
7496 magick_unreferenced(convolve_image);
7497 magick_unreferenced(exception);
7498
7499 /* legacy, do not use */
7500 return(MagickFalse);
7501}
7502