blob: 2d4ac35870d876a1542ad484869c367844e6abd5 [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"
cristyf034abb2013-11-24 14:16:14 +000065#include "MagickCore/pixel-private.h"
cristy4c08aed2011-07-01 19:47:50 +000066#include "MagickCore/prepress.h"
67#include "MagickCore/quantize.h"
dirk8a5cf512014-07-28 20:16:27 +000068#include "MagickCore/quantum-private.h"
cristye85d0f72013-11-27 02:25:43 +000069#include "MagickCore/random_.h"
70#include "MagickCore/random-private.h"
cristy4c08aed2011-07-01 19:47:50 +000071#include "MagickCore/registry.h"
cristyf034abb2013-11-24 14:16:14 +000072#include "MagickCore/resize.h"
73#include "MagickCore/resize-private.h"
cristy4c08aed2011-07-01 19:47:50 +000074#include "MagickCore/semaphore.h"
75#include "MagickCore/splay-tree.h"
76#include "MagickCore/statistic.h"
77#include "MagickCore/string_.h"
78#include "MagickCore/string-private.h"
79#include "MagickCore/token.h"
cristyf034abb2013-11-24 14:16:14 +000080
81#ifdef MAGICKCORE_CLPERFMARKER
82#include "CLPerfMarker.h"
83#endif
84
cristye85d0f72013-11-27 02:25:43 +000085#define MAGICK_MAX(x,y) (((x) >= (y))?(x):(y))
86#define MAGICK_MIN(x,y) (((x) <= (y))?(x):(y))
87
cristyf034abb2013-11-24 14:16:14 +000088#if defined(MAGICKCORE_OPENCL_SUPPORT)
89
90#define ALIGNED(pointer,type) ((((long)(pointer)) & (sizeof(type)-1)) == 0)
cristyf034abb2013-11-24 14:16:14 +000091
cristy0c832c62014-03-07 22:21:04 +000092/* pad the global workgroup size to the next multiple of
93 the local workgroup size */
dirk832becc2014-08-04 19:44:34 +000094inline static unsigned int padGlobalWorkgroupSizeToLocalWorkgroupSize(
95 const unsigned int orgGlobalSize,const unsigned int localGroupSize)
96{
97 return ((orgGlobalSize+(localGroupSize-1))/localGroupSize*localGroupSize);
98}
99
100static MagickBooleanType checkOpenCLEnvironment(ExceptionInfo* exception)
101{
102 MagickBooleanType
103 flag;
104
105 MagickCLEnv
106 clEnv;
107
108 clEnv=GetDefaultOpenCLEnv();
109
110 GetMagickOpenCLEnvParam(clEnv,MAGICK_OPENCL_ENV_PARAM_OPENCL_DISABLED,
111 sizeof(MagickBooleanType),&flag,exception);
112 if (flag != MagickFalse)
113 return(MagickFalse);
114
115 GetMagickOpenCLEnvParam(clEnv,MAGICK_OPENCL_ENV_PARAM_OPENCL_INITIALIZED,
116 sizeof(MagickBooleanType),&flag,exception);
117 if (flag == MagickFalse)
118 {
119 if (InitOpenCLEnv(clEnv,exception) == MagickFalse)
120 return(MagickFalse);
121
122 GetMagickOpenCLEnvParam(clEnv,MAGICK_OPENCL_ENV_PARAM_OPENCL_DISABLED,
123 sizeof(MagickBooleanType),&flag,exception);
124 if (flag != MagickFalse)
125 return(MagickFalse);
126 }
127
128 return(MagickTrue);
129}
130
131static MagickBooleanType checkAccelerateCondition(const Image* image,
132 const ChannelType channel)
133{
134 /* check if the image's colorspace is supported */
135 if (image->colorspace != RGBColorspace &&
136 image->colorspace != sRGBColorspace &&
137 image->colorspace != GRAYColorspace)
138 return(MagickFalse);
139
140 /* check if the channel is supported */
141 if (((channel & RedChannel) == 0) ||
142 ((channel & GreenChannel) == 0) ||
143 ((channel & BlueChannel) == 0))
144 return(MagickFalse);
145
146 /* check if the virtual pixel method is compatible with the OpenCL implementation */
147 if ((GetImageVirtualPixelMethod(image) != UndefinedVirtualPixelMethod) &&
148 (GetImageVirtualPixelMethod(image) != EdgeVirtualPixelMethod))
149 return(MagickFalse);
150
151 /* check if the image has read / write mask */
152 if (image->read_mask != MagickFalse || image->write_mask != MagickFalse)
153 return(MagickFalse);
154
155 return(MagickTrue);
156}
157
158static MagickBooleanType checkHistogramCondition(Image *image,
159 const ChannelType channel)
160{
161 /* ensure this is the only pass get in for now. */
162 if ((channel & SyncChannels) == 0)
163 return MagickFalse;
164
165 if (image->intensity == Rec601LuminancePixelIntensityMethod ||
166 image->intensity == Rec709LuminancePixelIntensityMethod)
167 return MagickFalse;
168
169 if (image->colorspace != sRGBColorspace)
170 return MagickFalse;
171
172 return MagickTrue;
173}
174
175static MagickBooleanType splitImage(const Image* image)
176{
177 MagickBooleanType
178 split;
179
180 MagickCLEnv
181 clEnv;
182
183 unsigned long
184 allocSize,
185 tempSize;
186
187 clEnv=GetDefaultOpenCLEnv();
188
189 allocSize=GetOpenCLDeviceMaxMemAllocSize(clEnv);
190 tempSize=image->columns * image->rows * 4 * 4;
191
192 split = ((tempSize > allocSize) ? MagickTrue : MagickFalse);
193 return(split);
cristy0c832c62014-03-07 22:21:04 +0000194}
195
dirk8a5cf512014-07-28 20:16:27 +0000196/*
197%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
198% %
199% %
200% %
201% C o n v o l v e I m a g e w i t h O p e n C L %
202% %
203% %
204% %
205%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
206%
207% ConvolveImage() applies a custom convolution kernel to the image.
208%
209% The format of the ConvolveImage method is:
210%
211% Image *ConvolveImage(const Image *image,const size_t order,
212% const double *kernel,ExceptionInfo *exception)
213% Image *ConvolveImageChannel(const Image *image,const ChannelType channel,
214% const size_t order,const double *kernel,ExceptionInfo *exception)
215%
216% A description of each parameter follows:
217%
218% o image: the image.
219%
220% o channel: the channel type.
221%
222% o kernel: kernel info.
223%
224% o exception: return any errors or warnings in this structure.
225%
226*/
cristyf034abb2013-11-24 14:16:14 +0000227
dirk832becc2014-08-04 19:44:34 +0000228static Image *ComputeConvolveImage(const Image* image,
229 const ChannelType channel,const KernelInfo *kernel,ExceptionInfo *exception)
230{
231 CacheView
232 *filteredImage_view,
233 *image_view;
234
235 cl_command_queue
236 queue;
237
238 cl_context
239 context;
240
241 cl_kernel
242 clkernel;
243
244 cl_int
245 clStatus;
246
247 cl_mem
248 convolutionKernel,
249 filteredImageBuffer,
250 imageBuffer;
251
252 cl_mem_flags
253 mem_flags;
254
255 cl_ulong
256 deviceLocalMemorySize;
257
258 const void
259 *inputPixels;
260
261 float
262 *kernelBufferPtr;
263
264 Image
265 *filteredImage;
266
267 MagickBooleanType
268 outputReady;
269
270 MagickCLEnv
271 clEnv;
272
273 MagickSizeType
274 length;
275
276 size_t
277 global_work_size[3],
278 localGroupSize[3],
279 localMemoryRequirement;
280
281 unsigned
282 kernelSize;
283
284 unsigned int
285 filterHeight,
286 filterWidth,
287 i,
288 imageHeight,
289 imageWidth,
290 matte;
291
292 void
293 *filteredPixels,
294 *hostPtr;
295
296 /* intialize all CL objects to NULL */
297 context = NULL;
298 imageBuffer = NULL;
299 filteredImageBuffer = NULL;
300 convolutionKernel = NULL;
301 clkernel = NULL;
302 queue = NULL;
303
304 filteredImage = NULL;
305 filteredImage_view = NULL;
cristyf034abb2013-11-24 14:16:14 +0000306 outputReady = MagickFalse;
307
308 clEnv = GetDefaultOpenCLEnv();
309 context = GetOpenCLContext(clEnv);
310
dirk832becc2014-08-04 19:44:34 +0000311 image_view=AcquireVirtualCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +0000312 inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
cristyf034abb2013-11-24 14:16:14 +0000313 if (inputPixels == (const void *) NULL)
314 {
dirk8a5cf512014-07-28 20:16:27 +0000315 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristyf034abb2013-11-24 14:16:14 +0000316 goto cleanup;
317 }
318
319 /* Create and initialize OpenCL buffers. */
320
321 /* If the host pointer is aligned to the size of CLPixelPacket,
322 then use the host buffer directly from the GPU; otherwise,
323 create a buffer on the GPU and copy the data over */
324 if (ALIGNED(inputPixels,CLPixelPacket))
325 {
326 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
327 }
328 else
329 {
330 mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
331 }
332 /* create a CL buffer from image pixel buffer */
dirk832becc2014-08-04 19:44:34 +0000333 length = image->columns * image->rows;
dirk8a5cf512014-07-28 20:16:27 +0000334 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +0000335 if (clStatus != CL_SUCCESS)
336 {
cristy0c832c62014-03-07 22:21:04 +0000337 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +0000338 goto cleanup;
339 }
340
dirk8a5cf512014-07-28 20:16:27 +0000341 filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristyf034abb2013-11-24 14:16:14 +0000342 assert(filteredImage != NULL);
dirk8a5cf512014-07-28 20:16:27 +0000343 if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
cristyf034abb2013-11-24 14:16:14 +0000344 {
cristya22457d2013-12-07 14:03:06 +0000345 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000346 goto cleanup;
347 }
dirk832becc2014-08-04 19:44:34 +0000348 filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
dirk8a5cf512014-07-28 20:16:27 +0000349 filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
cristyf034abb2013-11-24 14:16:14 +0000350 if (filteredPixels == (void *) NULL)
351 {
cristya22457d2013-12-07 14:03:06 +0000352 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
cristyf034abb2013-11-24 14:16:14 +0000353 goto cleanup;
354 }
355
356 if (ALIGNED(filteredPixels,CLPixelPacket))
357 {
358 mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
359 hostPtr = filteredPixels;
360 }
361 else
362 {
363 mem_flags = CL_MEM_WRITE_ONLY;
364 hostPtr = NULL;
365 }
366 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +0000367 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +0000368 filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
cristyf034abb2013-11-24 14:16:14 +0000369 if (clStatus != CL_SUCCESS)
370 {
cristy0c832c62014-03-07 22:21:04 +0000371 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +0000372 goto cleanup;
373 }
374
375 kernelSize = kernel->width * kernel->height;
cristy0c832c62014-03-07 22:21:04 +0000376 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 +0000377 if (clStatus != CL_SUCCESS)
378 {
cristy0c832c62014-03-07 22:21:04 +0000379 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +0000380 goto cleanup;
381 }
382
383 queue = AcquireOpenCLCommandQueue(clEnv);
384
cristy0c832c62014-03-07 22:21:04 +0000385 kernelBufferPtr = (float*)clEnv->library->clEnqueueMapBuffer(queue, convolutionKernel, CL_TRUE, CL_MAP_WRITE, 0, kernelSize * sizeof(float)
cristyf034abb2013-11-24 14:16:14 +0000386 , 0, NULL, NULL, &clStatus);
387 if (clStatus != CL_SUCCESS)
388 {
cristy0c832c62014-03-07 22:21:04 +0000389 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueMapBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +0000390 goto cleanup;
391 }
392 for (i = 0; i < kernelSize; i++)
393 {
394 kernelBufferPtr[i] = (float) kernel->values[i];
395 }
cristy0c832c62014-03-07 22:21:04 +0000396 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, convolutionKernel, kernelBufferPtr, 0, NULL, NULL);
397 if (clStatus != CL_SUCCESS)
cristyf034abb2013-11-24 14:16:14 +0000398 {
cristy0c832c62014-03-07 22:21:04 +0000399 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000400 goto cleanup;
401 }
cristy0c832c62014-03-07 22:21:04 +0000402 clEnv->library->clFlush(queue);
403
404 deviceLocalMemorySize = GetOpenCLDeviceLocalMemorySize(clEnv);
cristyf034abb2013-11-24 14:16:14 +0000405
406 /* Compute the local memory requirement for a 16x16 workgroup.
407 If it's larger than 16k, reduce the workgroup size to 8x8 */
408 localGroupSize[0] = 16;
409 localGroupSize[1] = 16;
410 localMemoryRequirement = (localGroupSize[0]+kernel->width-1) * (localGroupSize[1]+kernel->height-1) * sizeof(CLPixelPacket)
411 + kernel->width*kernel->height*sizeof(float);
cristy0c832c62014-03-07 22:21:04 +0000412
413 if (localMemoryRequirement > deviceLocalMemorySize)
cristyf034abb2013-11-24 14:16:14 +0000414 {
cristyf034abb2013-11-24 14:16:14 +0000415 localGroupSize[0] = 8;
416 localGroupSize[1] = 8;
cristyf034abb2013-11-24 14:16:14 +0000417 localMemoryRequirement = (localGroupSize[0]+kernel->width-1) * (localGroupSize[1]+kernel->height-1) * sizeof(CLPixelPacket)
418 + kernel->width*kernel->height*sizeof(float);
419 }
cristyf034abb2013-11-24 14:16:14 +0000420 if (localMemoryRequirement <= deviceLocalMemorySize)
421 {
422 /* get the OpenCL kernel */
cristya22457d2013-12-07 14:03:06 +0000423 clkernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "ConvolveOptimized");
cristyf034abb2013-11-24 14:16:14 +0000424 if (clkernel == NULL)
425 {
cristya22457d2013-12-07 14:03:06 +0000426 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000427 goto cleanup;
428 }
429
430 /* set the kernel arguments */
431 i = 0;
dirk8a5cf512014-07-28 20:16:27 +0000432 clStatus =clEnv->library->clSetKernelArg(clkernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +0000433 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(cl_mem),(void *)&filteredImageBuffer);
dirk8a5cf512014-07-28 20:16:27 +0000434 imageWidth = image->columns;
435 imageHeight = image->rows;
cristy0c832c62014-03-07 22:21:04 +0000436 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&imageWidth);
437 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&imageHeight);
438 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(cl_mem),(void *)&convolutionKernel);
cristyf034abb2013-11-24 14:16:14 +0000439 filterWidth = kernel->width;
440 filterHeight = kernel->height;
cristy0c832c62014-03-07 22:21:04 +0000441 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&filterWidth);
442 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&filterHeight);
dirk8a5cf512014-07-28 20:16:27 +0000443 matte = (image->alpha_trait==BlendPixelTrait)?1:0;
cristy0c832c62014-03-07 22:21:04 +0000444 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&matte);
445 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(ChannelType),(void *)&channel);
446 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++, (localGroupSize[0] + kernel->width-1)*(localGroupSize[1] + kernel->height-1)*sizeof(CLPixelPacket),NULL);
447 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++, kernel->width*kernel->height*sizeof(float),NULL);
cristyf034abb2013-11-24 14:16:14 +0000448 if (clStatus != CL_SUCCESS)
449 {
cristy0c832c62014-03-07 22:21:04 +0000450 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000451 goto cleanup;
452 }
453
454 /* pad the global size to a multiple of the local work size dimension */
dirk8a5cf512014-07-28 20:16:27 +0000455 global_work_size[0] = ((image->columns + localGroupSize[0] - 1)/localGroupSize[0] ) * localGroupSize[0] ;
456 global_work_size[1] = ((image->rows + localGroupSize[1] - 1)/localGroupSize[1]) * localGroupSize[1];
cristyf034abb2013-11-24 14:16:14 +0000457
458 /* launch the kernel */
cristy0c832c62014-03-07 22:21:04 +0000459 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, clkernel, 2, NULL, global_work_size, localGroupSize, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +0000460 if (clStatus != CL_SUCCESS)
461 {
cristy0c832c62014-03-07 22:21:04 +0000462 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000463 goto cleanup;
464 }
465 }
466 else
467 {
468 /* get the OpenCL kernel */
469 clkernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "Convolve");
470 if (clkernel == NULL)
471 {
cristya22457d2013-12-07 14:03:06 +0000472 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000473 goto cleanup;
474 }
475
476 /* set the kernel arguments */
477 i = 0;
dirk8a5cf512014-07-28 20:16:27 +0000478 clStatus =clEnv->library->clSetKernelArg(clkernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +0000479 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(cl_mem),(void *)&filteredImageBuffer);
dirk8a5cf512014-07-28 20:16:27 +0000480 imageWidth = image->columns;
481 imageHeight = image->rows;
cristy0c832c62014-03-07 22:21:04 +0000482 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&imageWidth);
483 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&imageHeight);
484 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(cl_mem),(void *)&convolutionKernel);
cristyf034abb2013-11-24 14:16:14 +0000485 filterWidth = kernel->width;
486 filterHeight = kernel->height;
cristy0c832c62014-03-07 22:21:04 +0000487 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&filterWidth);
488 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&filterHeight);
dirk8a5cf512014-07-28 20:16:27 +0000489 matte = (image->alpha_trait==BlendPixelTrait)?1:0;
cristy0c832c62014-03-07 22:21:04 +0000490 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&matte);
491 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(ChannelType),(void *)&channel);
cristyf034abb2013-11-24 14:16:14 +0000492 if (clStatus != CL_SUCCESS)
493 {
cristy0c832c62014-03-07 22:21:04 +0000494 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000495 goto cleanup;
496 }
497
cristy0c832c62014-03-07 22:21:04 +0000498 localGroupSize[0] = 8;
499 localGroupSize[1] = 8;
dirk8a5cf512014-07-28 20:16:27 +0000500 global_work_size[0] = (image->columns + (localGroupSize[0]-1))/localGroupSize[0] * localGroupSize[0];
501 global_work_size[1] = (image->rows + (localGroupSize[1]-1))/localGroupSize[1] * localGroupSize[1];
cristy0c832c62014-03-07 22:21:04 +0000502 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, clkernel, 2, NULL, global_work_size, localGroupSize, 0, NULL, NULL);
503
cristyf034abb2013-11-24 14:16:14 +0000504 if (clStatus != CL_SUCCESS)
505 {
cristy0c832c62014-03-07 22:21:04 +0000506 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000507 goto cleanup;
508 }
509 }
cristy0c832c62014-03-07 22:21:04 +0000510 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +0000511
512 if (ALIGNED(filteredPixels,CLPixelPacket))
513 {
dirk8a5cf512014-07-28 20:16:27 +0000514 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +0000515 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 +0000516 }
517 else
518 {
dirk8a5cf512014-07-28 20:16:27 +0000519 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +0000520 clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +0000521 }
522 if (clStatus != CL_SUCCESS)
523 {
cristya22457d2013-12-07 14:03:06 +0000524 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000525 goto cleanup;
526 }
527
dirk832becc2014-08-04 19:44:34 +0000528 outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
529
530cleanup:
531 OpenCLLogException(__FUNCTION__,__LINE__,exception);
532
533 image_view=DestroyCacheView(image_view);
534 if (filteredImage_view != NULL)
dirk8a5cf512014-07-28 20:16:27 +0000535 filteredImage_view=DestroyCacheView(filteredImage_view);
cristyf034abb2013-11-24 14:16:14 +0000536
dirk8a5cf512014-07-28 20:16:27 +0000537 if (imageBuffer != NULL)
538 clEnv->library->clReleaseMemObject(imageBuffer);
cristyf034abb2013-11-24 14:16:14 +0000539
540 if (filteredImageBuffer != NULL)
cristy0c832c62014-03-07 22:21:04 +0000541 clEnv->library->clReleaseMemObject(filteredImageBuffer);
cristyf034abb2013-11-24 14:16:14 +0000542
543 if (convolutionKernel != NULL)
cristy0c832c62014-03-07 22:21:04 +0000544 clEnv->library->clReleaseMemObject(convolutionKernel);
cristyf034abb2013-11-24 14:16:14 +0000545
546 if (clkernel != NULL)
547 RelinquishOpenCLKernel(clEnv, clkernel);
548
549 if (queue != NULL)
550 RelinquishOpenCLCommandQueue(clEnv, queue);
551
552 if (outputReady == MagickFalse)
553 {
554 if (filteredImage != NULL)
555 {
556 DestroyImage(filteredImage);
557 filteredImage = NULL;
558 }
559 }
560
dirk8a5cf512014-07-28 20:16:27 +0000561 return(filteredImage);
cristyf034abb2013-11-24 14:16:14 +0000562}
563
dirk832becc2014-08-04 19:44:34 +0000564MagickExport Image *AccelerateConvolveImageChannel(const Image *image,
565 const ChannelType channel,const KernelInfo *kernel,ExceptionInfo *exception)
566{
567 Image
568 *filteredImage;
569
570 assert(image != NULL);
571 assert(kernel != (KernelInfo *) NULL);
572 assert(exception != (ExceptionInfo *) NULL);
573
574 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
575 (checkAccelerateCondition(image, channel) == MagickFalse))
576 return NULL;
577
578 filteredImage=ComputeConvolveImage(image, channel, kernel, exception);
579 return(filteredImage);
cristyf034abb2013-11-24 14:16:14 +0000580}
581
dirk832becc2014-08-04 19:44:34 +0000582static MagickBooleanType ComputeFunctionImage(Image *image,
583 const ChannelType channel,const MagickFunction function,
584 const size_t number_parameters,const double *parameters,
585 ExceptionInfo *exception)
586{
587 CacheView
588 *image_view;
589
590 cl_command_queue
591 queue;
592
593 cl_context
594 context;
595
596 cl_int
597 clStatus;
598
599 cl_kernel
600 clkernel;
601
602 cl_mem
603 imageBuffer,
604 parametersBuffer;
605
606 cl_mem_flags
607 mem_flags;
608
609 float
610 *parametersBufferPtr;
611
612 MagickBooleanType
613 status;
614
615 MagickCLEnv
616 clEnv;
617
618 MagickSizeType
619 length;
620
621 size_t
622 globalWorkSize[2];
623
624 unsigned int
625 i;
626
627 void
628 *pixels;
629
630 status = MagickFalse;
631
632 context = NULL;
633 clkernel = NULL;
634 queue = NULL;
635 imageBuffer = NULL;
636 parametersBuffer = NULL;
637
638 clEnv = GetDefaultOpenCLEnv();
639 context = GetOpenCLContext(clEnv);
640
641 image_view=AcquireAuthenticCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +0000642 pixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
cristyf034abb2013-11-24 14:16:14 +0000643 if (pixels == (void *) NULL)
644 {
cristya22457d2013-12-07 14:03:06 +0000645 (void) OpenCLThrowMagickException(exception, GetMagickModule(), CacheWarning,
cristyf034abb2013-11-24 14:16:14 +0000646 "GetPixelCachePixels failed.",
647 "'%s'", image->filename);
648 goto cleanup;
649 }
650
651
652 if (ALIGNED(pixels,CLPixelPacket))
653 {
654 mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
655 }
656 else
657 {
658 mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
659 }
660 /* create a CL buffer from image pixel buffer */
661 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +0000662 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)pixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +0000663 if (clStatus != CL_SUCCESS)
664 {
cristy0c832c62014-03-07 22:21:04 +0000665 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +0000666 goto cleanup;
667 }
668
cristy0c832c62014-03-07 22:21:04 +0000669 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 +0000670 if (clStatus != CL_SUCCESS)
671 {
cristy0c832c62014-03-07 22:21:04 +0000672 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +0000673 goto cleanup;
674 }
675
676 queue = AcquireOpenCLCommandQueue(clEnv);
677
cristy0c832c62014-03-07 22:21:04 +0000678 parametersBufferPtr = (float*)clEnv->library->clEnqueueMapBuffer(queue, parametersBuffer, CL_TRUE, CL_MAP_WRITE, 0, number_parameters * sizeof(float)
cristyf034abb2013-11-24 14:16:14 +0000679 , 0, NULL, NULL, &clStatus);
680 if (clStatus != CL_SUCCESS)
681 {
cristy0c832c62014-03-07 22:21:04 +0000682 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueMapBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +0000683 goto cleanup;
684 }
685 for (i = 0; i < number_parameters; i++)
686 {
687 parametersBufferPtr[i] = (float)parameters[i];
688 }
cristy0c832c62014-03-07 22:21:04 +0000689 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, parametersBuffer, parametersBufferPtr, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +0000690 if (clStatus != CL_SUCCESS)
691 {
cristy0c832c62014-03-07 22:21:04 +0000692 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000693 goto cleanup;
694 }
cristy0c832c62014-03-07 22:21:04 +0000695 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +0000696
697 clkernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "FunctionImage");
698 if (clkernel == NULL)
699 {
cristya22457d2013-12-07 14:03:06 +0000700 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000701 goto cleanup;
702 }
703
704 /* set the kernel arguments */
705 i = 0;
cristy0c832c62014-03-07 22:21:04 +0000706 clStatus =clEnv->library->clSetKernelArg(clkernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
707 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(ChannelType),(void *)&channel);
708 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(MagickFunction),(void *)&function);
709 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(unsigned int),(void *)&number_parameters);
710 clStatus|=clEnv->library->clSetKernelArg(clkernel,i++,sizeof(cl_mem),(void *)&parametersBuffer);
cristyf034abb2013-11-24 14:16:14 +0000711 if (clStatus != CL_SUCCESS)
712 {
cristy0c832c62014-03-07 22:21:04 +0000713 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000714 goto cleanup;
715 }
716
717 globalWorkSize[0] = image->columns;
718 globalWorkSize[1] = image->rows;
719 /* launch the kernel */
cristy0c832c62014-03-07 22:21:04 +0000720 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, clkernel, 2, NULL, globalWorkSize, NULL, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +0000721 if (clStatus != CL_SUCCESS)
722 {
cristy0c832c62014-03-07 22:21:04 +0000723 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000724 goto cleanup;
725 }
cristy0c832c62014-03-07 22:21:04 +0000726 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +0000727
728
729 if (ALIGNED(pixels,CLPixelPacket))
730 {
731 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +0000732 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 +0000733 }
734 else
735 {
736 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +0000737 clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), pixels, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +0000738 }
739 if (clStatus != CL_SUCCESS)
740 {
cristya22457d2013-12-07 14:03:06 +0000741 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000742 goto cleanup;
743 }
dirk832becc2014-08-04 19:44:34 +0000744 status=SyncCacheViewAuthenticPixels(image_view,exception);
745
746cleanup:
747 OpenCLLogException(__FUNCTION__,__LINE__,exception);
748
dirk8a5cf512014-07-28 20:16:27 +0000749 image_view=DestroyCacheView(image_view);
cristyf034abb2013-11-24 14:16:14 +0000750
751 if (clkernel != NULL) RelinquishOpenCLKernel(clEnv, clkernel);
752 if (queue != NULL) RelinquishOpenCLCommandQueue(clEnv, queue);
cristy0c832c62014-03-07 22:21:04 +0000753 if (imageBuffer != NULL) clEnv->library->clReleaseMemObject(imageBuffer);
754 if (parametersBuffer != NULL) clEnv->library->clReleaseMemObject(parametersBuffer);
cristyf034abb2013-11-24 14:16:14 +0000755
dirk8a5cf512014-07-28 20:16:27 +0000756 return(status);
cristyf034abb2013-11-24 14:16:14 +0000757}
758
dirk832becc2014-08-04 19:44:34 +0000759MagickExport MagickBooleanType AccelerateFunctionImage(Image *image,
760 const ChannelType channel,const MagickFunction function,
761 const size_t number_parameters,const double *parameters,
762 ExceptionInfo *exception)
763{
764 MagickBooleanType
765 status;
766
767 assert(image != NULL);
768 assert(exception != (ExceptionInfo *) NULL);
769
770 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
771 (checkAccelerateCondition(image, channel) == MagickFalse))
772 return(MagickFalse);
773
774 status=ComputeFunctionImage(image, channel, function, number_parameters, parameters, exception);
775 return(status);
cristyf034abb2013-11-24 14:16:14 +0000776}
777
dirk8a5cf512014-07-28 20:16:27 +0000778/*
779%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
780% %
781% %
782% %
783% B l u r I m a g e w i t h O p e n C L %
784% %
785% %
786% %
787%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
788%
789% BlurImage() blurs an image. We convolve the image with a Gaussian operator
790% of the given radius and standard deviation (sigma). For reasonable results,
791% the radius should be larger than sigma. Use a radius of 0 and BlurImage()
792% selects a suitable radius for you.
793%
794% The format of the BlurImage method is:
795%
796% Image *BlurImage(const Image *image,const double radius,
797% const double sigma,ExceptionInfo *exception)
798% Image *BlurImageChannel(const Image *image,const ChannelType channel,
799% const double radius,const double sigma,ExceptionInfo *exception)
800%
801% A description of each parameter follows:
802%
803% o image: the image.
804%
805% o channel: the channel type.
806%
807% o radius: the radius of the Gaussian, in pixels, not counting the center
808% pixel.
809%
810% o sigma: the standard deviation of the Gaussian, in pixels.
811%
812% o exception: return any errors or warnings in this structure.
813%
814*/
cristyf034abb2013-11-24 14:16:14 +0000815
dirk832becc2014-08-04 19:44:34 +0000816static Image *ComputeBlurImage(const Image* image,const ChannelType channel,
817 const double radius,const double sigma,ExceptionInfo *exception)
818{
819 CacheView
820 *filteredImage_view,
821 *image_view;
822
823 char
824 geometry[MaxTextExtent];
825
826 cl_command_queue
827 queue;
828
829 cl_context
830 context;
831
832 cl_int
833 clStatus;
834
835 cl_kernel
836 blurColumnKernel,
837 blurRowKernel;
838
839 cl_mem
840 filteredImageBuffer,
841 imageBuffer,
842 imageKernelBuffer,
843 tempImageBuffer;
844
845 cl_mem_flags
846 mem_flags;
847
848 const void
849 *inputPixels;
850
851 float
852 *kernelBufferPtr;
853
854 Image
855 *filteredImage;
856
857 MagickBooleanType
858 outputReady;
859
860 MagickCLEnv
861 clEnv;
862
863 MagickSizeType
864 length;
865
866 KernelInfo
867 *kernel;
868
869 unsigned int
870 i,
871 imageColumns,
872 imageRows,
873 kernelWidth;
874
875 void
876 *filteredPixels,
877 *hostPtr;
878
879 context = NULL;
880 filteredImage = NULL;
881 filteredImage_view = NULL;
882 imageBuffer = NULL;
883 tempImageBuffer = NULL;
884 filteredImageBuffer = NULL;
885 imageKernelBuffer = NULL;
886 blurRowKernel = NULL;
887 blurColumnKernel = NULL;
888 queue = NULL;
889 kernel = NULL;
890
891 outputReady = MagickFalse;
892
893 clEnv = GetDefaultOpenCLEnv();
894 context = GetOpenCLContext(clEnv);
895 queue = AcquireOpenCLCommandQueue(clEnv);
896
897 /* Create and initialize OpenCL buffers. */
898 {
899 image_view=AcquireVirtualCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +0000900 inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
cristyf034abb2013-11-24 14:16:14 +0000901 if (inputPixels == (const void *) NULL)
902 {
dirk8a5cf512014-07-28 20:16:27 +0000903 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristyf034abb2013-11-24 14:16:14 +0000904 goto cleanup;
905 }
906 /* If the host pointer is aligned to the size of CLPixelPacket,
907 then use the host buffer directly from the GPU; otherwise,
908 create a buffer on the GPU and copy the data over */
909 if (ALIGNED(inputPixels,CLPixelPacket))
910 {
911 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
912 }
913 else
914 {
915 mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
916 }
917 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +0000918 length = image->columns * image->rows;
919 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +0000920 if (clStatus != CL_SUCCESS)
921 {
cristy0c832c62014-03-07 22:21:04 +0000922 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +0000923 goto cleanup;
924 }
925 }
926
927 /* create output */
928 {
dirk8a5cf512014-07-28 20:16:27 +0000929 filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristyf034abb2013-11-24 14:16:14 +0000930 assert(filteredImage != NULL);
dirk8a5cf512014-07-28 20:16:27 +0000931 if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
cristyf034abb2013-11-24 14:16:14 +0000932 {
cristya22457d2013-12-07 14:03:06 +0000933 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000934 goto cleanup;
935 }
dirk832becc2014-08-04 19:44:34 +0000936 filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
dirk8a5cf512014-07-28 20:16:27 +0000937 filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
cristyf034abb2013-11-24 14:16:14 +0000938 if (filteredPixels == (void *) NULL)
939 {
cristya22457d2013-12-07 14:03:06 +0000940 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
cristyf034abb2013-11-24 14:16:14 +0000941 goto cleanup;
942 }
943
944 if (ALIGNED(filteredPixels,CLPixelPacket))
945 {
946 mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
947 hostPtr = filteredPixels;
948 }
949 else
950 {
951 mem_flags = CL_MEM_WRITE_ONLY;
952 hostPtr = NULL;
953 }
954 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +0000955 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +0000956 filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
cristyf034abb2013-11-24 14:16:14 +0000957 if (clStatus != CL_SUCCESS)
958 {
cristy0c832c62014-03-07 22:21:04 +0000959 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +0000960 goto cleanup;
961 }
962 }
963
964 /* create processing kernel */
965 {
966 (void) FormatLocaleString(geometry,MaxTextExtent,"blur:%.20gx%.20g;blur:%.20gx%.20g+90",radius,sigma,radius,sigma);
967 kernel=AcquireKernelInfo(geometry);
968 if (kernel == (KernelInfo *) NULL)
969 {
cristya22457d2013-12-07 14:03:06 +0000970 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "MemoryAllocationFailed.",".");
cristyf034abb2013-11-24 14:16:14 +0000971 goto cleanup;
972 }
973
cristy0c832c62014-03-07 22:21:04 +0000974 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 +0000975 if (clStatus != CL_SUCCESS)
976 {
cristy0c832c62014-03-07 22:21:04 +0000977 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +0000978 goto cleanup;
979 }
cristy0c832c62014-03-07 22:21:04 +0000980 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 +0000981 if (clStatus != CL_SUCCESS)
982 {
cristy0c832c62014-03-07 22:21:04 +0000983 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueMapBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +0000984 goto cleanup;
985 }
986
987 for (i = 0; i < kernel->width; i++)
988 {
989 kernelBufferPtr[i] = (float) kernel->values[i];
990 }
991
cristy0c832c62014-03-07 22:21:04 +0000992 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, imageKernelBuffer, kernelBufferPtr, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +0000993 if (clStatus != CL_SUCCESS)
994 {
cristy0c832c62014-03-07 22:21:04 +0000995 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +0000996 goto cleanup;
997 }
998 }
999
1000 {
1001
1002 /* create temp buffer */
1003 {
dirk8a5cf512014-07-28 20:16:27 +00001004 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00001005 tempImageBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_WRITE, length * 4 * sizeof(float), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00001006 if (clStatus != CL_SUCCESS)
1007 {
cristy0c832c62014-03-07 22:21:04 +00001008 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001009 goto cleanup;
1010 }
1011 }
1012
1013 /* get the OpenCL kernels */
1014 {
1015 blurRowKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "BlurRow");
1016 if (blurRowKernel == NULL)
1017 {
cristya22457d2013-12-07 14:03:06 +00001018 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001019 goto cleanup;
1020 };
1021
1022 blurColumnKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "BlurColumn");
1023 if (blurColumnKernel == NULL)
1024 {
cristya22457d2013-12-07 14:03:06 +00001025 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001026 goto cleanup;
1027 };
1028 }
1029
1030 {
1031 /* need logic to decide this value */
1032 int chunkSize = 256;
1033
1034 {
dirk8a5cf512014-07-28 20:16:27 +00001035 imageColumns = image->columns;
1036 imageRows = image->rows;
cristyf034abb2013-11-24 14:16:14 +00001037
1038 /* set the kernel arguments */
1039 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00001040 clStatus=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00001041 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&tempImageBuffer);
1042 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(ChannelType),&channel);
1043 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&imageKernelBuffer);
cristyf034abb2013-11-24 14:16:14 +00001044 kernelWidth = kernel->width;
cristy0c832c62014-03-07 22:21:04 +00001045 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&kernelWidth);
1046 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&imageColumns);
1047 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&imageRows);
1048 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(CLPixelPacket)*(chunkSize+kernel->width),(void *)NULL);
cristyf034abb2013-11-24 14:16:14 +00001049 if (clStatus != CL_SUCCESS)
1050 {
cristy0c832c62014-03-07 22:21:04 +00001051 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001052 goto cleanup;
1053 }
1054 }
1055
1056 /* launch the kernel */
1057 {
1058 size_t gsize[2];
1059 size_t wsize[2];
1060
dirk8a5cf512014-07-28 20:16:27 +00001061 gsize[0] = chunkSize*((image->columns+chunkSize-1)/chunkSize);
1062 gsize[1] = image->rows;
cristyf034abb2013-11-24 14:16:14 +00001063 wsize[0] = chunkSize;
1064 wsize[1] = 1;
1065
cristy0c832c62014-03-07 22:21:04 +00001066 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, blurRowKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00001067 if (clStatus != CL_SUCCESS)
1068 {
cristy0c832c62014-03-07 22:21:04 +00001069 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001070 goto cleanup;
1071 }
cristy0c832c62014-03-07 22:21:04 +00001072 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00001073 }
1074 }
1075
1076 {
1077 /* need logic to decide this value */
1078 int chunkSize = 256;
1079
1080 {
dirk8a5cf512014-07-28 20:16:27 +00001081 imageColumns = image->columns;
1082 imageRows = image->rows;
cristyf034abb2013-11-24 14:16:14 +00001083
1084 /* set the kernel arguments */
1085 i = 0;
cristy0c832c62014-03-07 22:21:04 +00001086 clStatus=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(cl_mem),(void *)&tempImageBuffer);
1087 clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(cl_mem),(void *)&filteredImageBuffer);
1088 clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(ChannelType),&channel);
1089 clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(cl_mem),(void *)&imageKernelBuffer);
cristyf034abb2013-11-24 14:16:14 +00001090 kernelWidth = kernel->width;
cristy0c832c62014-03-07 22:21:04 +00001091 clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(unsigned int),(void *)&kernelWidth);
1092 clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(unsigned int),(void *)&imageColumns);
1093 clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(unsigned int),(void *)&imageRows);
1094 clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(cl_float4)*(chunkSize+kernel->width),(void *)NULL);
cristyf034abb2013-11-24 14:16:14 +00001095 if (clStatus != CL_SUCCESS)
1096 {
cristy0c832c62014-03-07 22:21:04 +00001097 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001098 goto cleanup;
1099 }
1100 }
1101
1102 /* launch the kernel */
1103 {
1104 size_t gsize[2];
1105 size_t wsize[2];
1106
dirk8a5cf512014-07-28 20:16:27 +00001107 gsize[0] = image->columns;
1108 gsize[1] = chunkSize*((image->rows+chunkSize-1)/chunkSize);
cristyf034abb2013-11-24 14:16:14 +00001109 wsize[0] = 1;
1110 wsize[1] = chunkSize;
1111
cristy0c832c62014-03-07 22:21:04 +00001112 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, blurColumnKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00001113 if (clStatus != CL_SUCCESS)
1114 {
cristy0c832c62014-03-07 22:21:04 +00001115 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001116 goto cleanup;
1117 }
cristy0c832c62014-03-07 22:21:04 +00001118 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00001119 }
1120 }
1121
1122 }
1123
1124 /* get result */
1125 if (ALIGNED(filteredPixels,CLPixelPacket))
1126 {
dirk8a5cf512014-07-28 20:16:27 +00001127 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00001128 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 +00001129 }
1130 else
1131 {
dirk8a5cf512014-07-28 20:16:27 +00001132 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00001133 clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00001134 }
1135 if (clStatus != CL_SUCCESS)
1136 {
cristya22457d2013-12-07 14:03:06 +00001137 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001138 goto cleanup;
1139 }
1140
dirk832becc2014-08-04 19:44:34 +00001141 outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
1142
1143cleanup:
1144 OpenCLLogException(__FUNCTION__,__LINE__,exception);
1145
1146 image_view=DestroyCacheView(image_view);
1147 if (filteredImage_view != NULL)
1148 filteredImage_view=DestroyCacheView(filteredImage_view);
1149
dirk8a5cf512014-07-28 20:16:27 +00001150 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00001151 if (tempImageBuffer!=NULL) clEnv->library->clReleaseMemObject(tempImageBuffer);
1152 if (filteredImageBuffer!=NULL) clEnv->library->clReleaseMemObject(filteredImageBuffer);
1153 if (imageKernelBuffer!=NULL) clEnv->library->clReleaseMemObject(imageKernelBuffer);
cristyf034abb2013-11-24 14:16:14 +00001154 if (blurRowKernel!=NULL) RelinquishOpenCLKernel(clEnv, blurRowKernel);
1155 if (blurColumnKernel!=NULL) RelinquishOpenCLKernel(clEnv, blurColumnKernel);
1156 if (queue != NULL) RelinquishOpenCLCommandQueue(clEnv, queue);
1157 if (kernel!=NULL) DestroyKernelInfo(kernel);
dirk8a5cf512014-07-28 20:16:27 +00001158 if (outputReady == MagickFalse && filteredImage != NULL)
dirk832becc2014-08-04 19:44:34 +00001159 filteredImage=DestroyImage(filteredImage);
dirk8a5cf512014-07-28 20:16:27 +00001160 return(filteredImage);
cristyf034abb2013-11-24 14:16:14 +00001161}
1162
dirk832becc2014-08-04 19:44:34 +00001163static Image* ComputeBlurImageSection(const Image* image,
1164 const ChannelType channel,const double radius,const double sigma,
1165 ExceptionInfo *exception)
1166{
1167 CacheView
1168 *filteredImage_view,
1169 *image_view;
1170
1171 char
1172 geometry[MaxTextExtent];
1173
1174 cl_command_queue
1175 queue;
1176
1177 cl_int
1178 clStatus;
1179
1180 cl_kernel
1181 blurColumnKernel,
1182 blurRowKernel;
1183
1184 cl_mem
1185 imageBuffer,
1186 tempImageBuffer,
1187 filteredImageBuffer,
1188 imageKernelBuffer;
1189
1190 cl_mem_flags
1191 mem_flags;
1192
1193 cl_context
1194 context;
1195
1196 const void
1197 *inputPixels;
1198
1199 float
1200 *kernelBufferPtr;
1201
1202 Image
1203 *filteredImage;
1204
1205 KernelInfo
1206 *kernel;
1207
1208 MagickBooleanType
1209 outputReady;
1210
1211 MagickCLEnv
1212 clEnv;
1213
1214 MagickSizeType
1215 length;
1216
1217 unsigned int
1218 i,
1219 imageColumns,
1220 imageRows,
1221 kernelWidth;
1222
1223 void
1224 *filteredPixels,
1225 *hostPtr;
1226
1227 context = NULL;
1228 filteredImage = NULL;
1229 filteredImage_view = NULL;
1230 imageBuffer = NULL;
1231 tempImageBuffer = NULL;
1232 filteredImageBuffer = NULL;
1233 imageKernelBuffer = NULL;
1234 blurRowKernel = NULL;
1235 blurColumnKernel = NULL;
1236 queue = NULL;
1237 kernel = NULL;
1238
1239 outputReady = MagickFalse;
1240
1241 clEnv = GetDefaultOpenCLEnv();
1242 context = GetOpenCLContext(clEnv);
1243 queue = AcquireOpenCLCommandQueue(clEnv);
1244
1245 /* Create and initialize OpenCL buffers. */
1246 {
1247 image_view=AcquireVirtualCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00001248 inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00001249 if (inputPixels == (const void *) NULL)
1250 {
dirk8a5cf512014-07-28 20:16:27 +00001251 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristyf034abb2013-11-24 14:16:14 +00001252 goto cleanup;
1253 }
1254 /* If the host pointer is aligned to the size of CLPixelPacket,
1255 then use the host buffer directly from the GPU; otherwise,
1256 create a buffer on the GPU and copy the data over */
1257 if (ALIGNED(inputPixels,CLPixelPacket))
1258 {
1259 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
1260 }
1261 else
1262 {
1263 mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
1264 }
1265 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00001266 length = image->columns * image->rows;
1267 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00001268 if (clStatus != CL_SUCCESS)
1269 {
cristy0c832c62014-03-07 22:21:04 +00001270 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001271 goto cleanup;
1272 }
1273 }
1274
1275 /* create output */
1276 {
dirk8a5cf512014-07-28 20:16:27 +00001277 filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristyf034abb2013-11-24 14:16:14 +00001278 assert(filteredImage != NULL);
dirk8a5cf512014-07-28 20:16:27 +00001279 if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
cristyf034abb2013-11-24 14:16:14 +00001280 {
cristya22457d2013-12-07 14:03:06 +00001281 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001282 goto cleanup;
1283 }
dirk832becc2014-08-04 19:44:34 +00001284 filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
dirk8a5cf512014-07-28 20:16:27 +00001285 filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00001286 if (filteredPixels == (void *) NULL)
1287 {
cristya22457d2013-12-07 14:03:06 +00001288 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
cristyf034abb2013-11-24 14:16:14 +00001289 goto cleanup;
1290 }
1291
1292 if (ALIGNED(filteredPixels,CLPixelPacket))
1293 {
1294 mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
1295 hostPtr = filteredPixels;
1296 }
1297 else
1298 {
1299 mem_flags = CL_MEM_WRITE_ONLY;
1300 hostPtr = NULL;
1301 }
1302 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00001303 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00001304 filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00001305 if (clStatus != CL_SUCCESS)
1306 {
cristy0c832c62014-03-07 22:21:04 +00001307 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001308 goto cleanup;
1309 }
1310 }
1311
1312 /* create processing kernel */
1313 {
1314 (void) FormatLocaleString(geometry,MaxTextExtent,"blur:%.20gx%.20g;blur:%.20gx%.20g+90",radius,sigma,radius,sigma);
1315 kernel=AcquireKernelInfo(geometry);
1316 if (kernel == (KernelInfo *) NULL)
1317 {
cristya22457d2013-12-07 14:03:06 +00001318 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "MemoryAllocationFailed.",".");
cristyf034abb2013-11-24 14:16:14 +00001319 goto cleanup;
1320 }
1321
cristy0c832c62014-03-07 22:21:04 +00001322 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 +00001323 if (clStatus != CL_SUCCESS)
1324 {
cristy0c832c62014-03-07 22:21:04 +00001325 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001326 goto cleanup;
1327 }
cristy0c832c62014-03-07 22:21:04 +00001328 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 +00001329 if (clStatus != CL_SUCCESS)
1330 {
cristy0c832c62014-03-07 22:21:04 +00001331 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueMapBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001332 goto cleanup;
1333 }
1334
1335 for (i = 0; i < kernel->width; i++)
1336 {
1337 kernelBufferPtr[i] = (float) kernel->values[i];
1338 }
1339
cristy0c832c62014-03-07 22:21:04 +00001340 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, imageKernelBuffer, kernelBufferPtr, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00001341 if (clStatus != CL_SUCCESS)
1342 {
cristy0c832c62014-03-07 22:21:04 +00001343 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001344 goto cleanup;
1345 }
1346 }
1347
1348 {
1349 unsigned int offsetRows;
1350 unsigned int sec;
1351
1352 /* create temp buffer */
1353 {
dirk8a5cf512014-07-28 20:16:27 +00001354 length = image->columns * (image->rows / 2 + 1 + (kernel->width-1) / 2);
cristy0c832c62014-03-07 22:21:04 +00001355 tempImageBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_WRITE, length * 4 * sizeof(float), NULL, &clStatus);
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->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001359 goto cleanup;
1360 }
1361 }
1362
1363 /* get the OpenCL kernels */
1364 {
1365 blurRowKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "BlurRowSection");
1366 if (blurRowKernel == NULL)
1367 {
cristya22457d2013-12-07 14:03:06 +00001368 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001369 goto cleanup;
1370 };
1371
1372 blurColumnKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "BlurColumnSection");
1373 if (blurColumnKernel == NULL)
1374 {
cristya22457d2013-12-07 14:03:06 +00001375 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001376 goto cleanup;
1377 };
1378 }
1379
1380 for (sec = 0; sec < 2; sec++)
1381 {
1382 {
1383 /* need logic to decide this value */
1384 int chunkSize = 256;
1385
1386 {
dirk8a5cf512014-07-28 20:16:27 +00001387 imageColumns = image->columns;
cristyf034abb2013-11-24 14:16:14 +00001388 if (sec == 0)
dirk8a5cf512014-07-28 20:16:27 +00001389 imageRows = image->rows / 2 + (kernel->width-1) / 2;
cristyf034abb2013-11-24 14:16:14 +00001390 else
dirk8a5cf512014-07-28 20:16:27 +00001391 imageRows = (image->rows - image->rows / 2) + (kernel->width-1) / 2;
cristyf034abb2013-11-24 14:16:14 +00001392
dirk8a5cf512014-07-28 20:16:27 +00001393 offsetRows = sec * image->rows / 2;
cristyf034abb2013-11-24 14:16:14 +00001394
1395 kernelWidth = kernel->width;
1396
1397 /* set the kernel arguments */
1398 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00001399 clStatus=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00001400 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&tempImageBuffer);
1401 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(ChannelType),&channel);
1402 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&imageKernelBuffer);
1403 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&kernelWidth);
1404 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&imageColumns);
1405 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&imageRows);
1406 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(CLPixelPacket)*(chunkSize+kernel->width),(void *)NULL);
1407 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&offsetRows);
1408 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&sec);
cristyf034abb2013-11-24 14:16:14 +00001409 if (clStatus != CL_SUCCESS)
1410 {
cristy0c832c62014-03-07 22:21:04 +00001411 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001412 goto cleanup;
1413 }
1414 }
1415
1416 /* launch the kernel */
1417 {
1418 size_t gsize[2];
1419 size_t wsize[2];
1420
1421 gsize[0] = chunkSize*((imageColumns+chunkSize-1)/chunkSize);
1422 gsize[1] = imageRows;
1423 wsize[0] = chunkSize;
1424 wsize[1] = 1;
1425
cristy0c832c62014-03-07 22:21:04 +00001426 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, blurRowKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00001427 if (clStatus != CL_SUCCESS)
1428 {
cristy0c832c62014-03-07 22:21:04 +00001429 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001430 goto cleanup;
1431 }
cristy0c832c62014-03-07 22:21:04 +00001432 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00001433 }
1434 }
1435
1436 {
1437 /* need logic to decide this value */
1438 int chunkSize = 256;
1439
1440 {
dirk8a5cf512014-07-28 20:16:27 +00001441 imageColumns = image->columns;
cristyf034abb2013-11-24 14:16:14 +00001442 if (sec == 0)
dirk8a5cf512014-07-28 20:16:27 +00001443 imageRows = image->rows / 2;
cristyf034abb2013-11-24 14:16:14 +00001444 else
dirk8a5cf512014-07-28 20:16:27 +00001445 imageRows = (image->rows - image->rows / 2);
cristyf034abb2013-11-24 14:16:14 +00001446
dirk8a5cf512014-07-28 20:16:27 +00001447 offsetRows = sec * image->rows / 2;
cristyf034abb2013-11-24 14:16:14 +00001448
1449 kernelWidth = kernel->width;
1450
1451 /* set the kernel arguments */
1452 i = 0;
cristy0c832c62014-03-07 22:21:04 +00001453 clStatus=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(cl_mem),(void *)&tempImageBuffer);
1454 clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(cl_mem),(void *)&filteredImageBuffer);
1455 clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(ChannelType),&channel);
1456 clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(cl_mem),(void *)&imageKernelBuffer);
1457 clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(unsigned int),(void *)&kernelWidth);
1458 clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(unsigned int),(void *)&imageColumns);
1459 clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(unsigned int),(void *)&imageRows);
1460 clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(cl_float4)*(chunkSize+kernel->width),(void *)NULL);
1461 clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(unsigned int),(void *)&offsetRows);
1462 clStatus|=clEnv->library->clSetKernelArg(blurColumnKernel,i++,sizeof(unsigned int),(void *)&sec);
cristyf034abb2013-11-24 14:16:14 +00001463 if (clStatus != CL_SUCCESS)
1464 {
cristy0c832c62014-03-07 22:21:04 +00001465 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001466 goto cleanup;
1467 }
1468 }
1469
1470 /* launch the kernel */
1471 {
1472 size_t gsize[2];
1473 size_t wsize[2];
1474
1475 gsize[0] = imageColumns;
1476 gsize[1] = chunkSize*((imageRows+chunkSize-1)/chunkSize);
1477 wsize[0] = 1;
1478 wsize[1] = chunkSize;
1479
cristy0c832c62014-03-07 22:21:04 +00001480 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, blurColumnKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00001481 if (clStatus != CL_SUCCESS)
1482 {
cristy0c832c62014-03-07 22:21:04 +00001483 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001484 goto cleanup;
1485 }
cristy0c832c62014-03-07 22:21:04 +00001486 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00001487 }
1488 }
1489 }
1490
1491 }
1492
1493 /* get result */
1494 if (ALIGNED(filteredPixels,CLPixelPacket))
1495 {
dirk8a5cf512014-07-28 20:16:27 +00001496 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00001497 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 +00001498 }
1499 else
1500 {
dirk8a5cf512014-07-28 20:16:27 +00001501 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00001502 clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00001503 }
1504 if (clStatus != CL_SUCCESS)
1505 {
cristya22457d2013-12-07 14:03:06 +00001506 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001507 goto cleanup;
1508 }
1509
dirk832becc2014-08-04 19:44:34 +00001510 outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
1511
1512cleanup:
1513 OpenCLLogException(__FUNCTION__,__LINE__,exception);
1514
1515 image_view=DestroyCacheView(image_view);
1516 if (filteredImage_view != NULL)
1517 filteredImage_view=DestroyCacheView(filteredImage_view);
1518
dirk8a5cf512014-07-28 20:16:27 +00001519 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00001520 if (tempImageBuffer!=NULL) clEnv->library->clReleaseMemObject(tempImageBuffer);
1521 if (filteredImageBuffer!=NULL) clEnv->library->clReleaseMemObject(filteredImageBuffer);
1522 if (imageKernelBuffer!=NULL) clEnv->library->clReleaseMemObject(imageKernelBuffer);
cristyf034abb2013-11-24 14:16:14 +00001523 if (blurRowKernel!=NULL) RelinquishOpenCLKernel(clEnv, blurRowKernel);
1524 if (blurColumnKernel!=NULL) RelinquishOpenCLKernel(clEnv, blurColumnKernel);
1525 if (queue != NULL) RelinquishOpenCLCommandQueue(clEnv, queue);
1526 if (kernel!=NULL) DestroyKernelInfo(kernel);
1527 if (outputReady == MagickFalse)
1528 {
1529 if (filteredImage != NULL)
1530 {
1531 DestroyImage(filteredImage);
1532 filteredImage = NULL;
1533 }
1534 }
1535 return filteredImage;
1536}
1537
dirk859503c2014-08-13 20:39:42 +00001538static Image *ComputeUnsharpMaskImageSingle(const Image *image,
1539 const ChannelType channel,const double radius,const double sigma,
1540 const double gain,const double threshold,int blurOnly, ExceptionInfo *exception);
1541
1542static Image* ComputeBlurImageSingle(const Image* image,
1543 const ChannelType channel,const double radius,const double sigma,
1544 ExceptionInfo *exception)
1545{
1546 return ComputeUnsharpMaskImageSingle(image, channel, radius, sigma, 0.0, 0.0, 1, exception);
1547}
1548
dirk832becc2014-08-04 19:44:34 +00001549MagickExport Image* AccelerateBlurImage(const Image *image,
1550 const ChannelType channel,const double radius,const double sigma,
1551 ExceptionInfo *exception)
1552{
1553 Image
1554 *filteredImage;
1555
1556 assert(image != NULL);
1557 assert(exception != (ExceptionInfo *) NULL);
1558
1559 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
1560 (checkAccelerateCondition(image, channel) == MagickFalse))
1561 return NULL;
1562
dirk859503c2014-08-13 20:39:42 +00001563 if (radius < 12.1)
1564 filteredImage=ComputeBlurImageSingle(image, channel, radius, sigma, exception);
1565 else if (splitImage(image) && (image->rows / 2 > radius))
dirk832becc2014-08-04 19:44:34 +00001566 filteredImage=ComputeBlurImageSection(image, channel, radius, sigma, exception);
1567 else
1568 filteredImage=ComputeBlurImage(image, channel, radius, sigma, exception);
1569
1570 return(filteredImage);
dirk8a5cf512014-07-28 20:16:27 +00001571}
1572
cristyf034abb2013-11-24 14:16:14 +00001573/*
1574%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1575% %
1576% %
1577% %
dirk8a5cf512014-07-28 20:16:27 +00001578% 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 +00001579% %
1580% %
1581% %
1582%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1583%
dirk8a5cf512014-07-28 20:16:27 +00001584% RotationalBlurImage() applies a rotational blur to the image.
cristyf034abb2013-11-24 14:16:14 +00001585%
dirk8a5cf512014-07-28 20:16:27 +00001586% Andrew Protano contributed this effect.
cristyf034abb2013-11-24 14:16:14 +00001587%
dirk8a5cf512014-07-28 20:16:27 +00001588% The format of the RotationalBlurImage method is:
1589%
1590% Image *RotationalBlurImage(const Image *image,const double angle,
1591% ExceptionInfo *exception)
1592% Image *RotationalBlurImageChannel(const Image *image,const ChannelType channel,
1593% const double angle,ExceptionInfo *exception)
cristyf034abb2013-11-24 14:16:14 +00001594%
1595% A description of each parameter follows:
1596%
1597% o image: the image.
1598%
1599% o channel: the channel type.
1600%
dirk8a5cf512014-07-28 20:16:27 +00001601% o angle: the angle of the rotational blur.
cristyf034abb2013-11-24 14:16:14 +00001602%
1603% o exception: return any errors or warnings in this structure.
1604%
1605*/
1606
dirk832becc2014-08-04 19:44:34 +00001607static Image* ComputeRotationalBlurImage(const Image *image,
1608 const ChannelType channel,const double angle,ExceptionInfo *exception)
1609{
1610 CacheView
1611 *image_view,
1612 *filteredImage_view;
1613
1614 cl_command_queue
1615 queue;
1616
1617 cl_context
1618 context;
1619
1620 cl_float2
1621 blurCenter;
1622
1623 cl_float4
1624 biasPixel;
1625
1626 cl_int
1627 clStatus;
1628
1629 cl_mem
1630 cosThetaBuffer,
1631 filteredImageBuffer,
1632 imageBuffer,
1633 sinThetaBuffer;
1634
1635 cl_mem_flags
1636 mem_flags;
1637
1638 cl_kernel
1639 rotationalBlurKernel;
1640
1641 const void
1642 *inputPixels;
1643
1644 float
1645 blurRadius,
1646 *cosThetaPtr,
1647 offset,
1648 *sinThetaPtr,
1649 theta;
1650
1651 Image
1652 *filteredImage;
1653
1654 MagickBooleanType
1655 outputReady;
1656
1657 MagickCLEnv
1658 clEnv;
1659
1660 PixelInfo
1661 bias;
1662
1663 MagickSizeType
1664 length;
1665
1666 size_t
1667 global_work_size[2];
1668
1669 unsigned int
1670 cossin_theta_size,
1671 i,
1672 matte;
1673
1674 void
1675 *filteredPixels,
1676 *hostPtr;
1677
1678 outputReady = MagickFalse;
1679 context = NULL;
1680 filteredImage = NULL;
1681 filteredImage_view = NULL;
1682 imageBuffer = NULL;
1683 filteredImageBuffer = NULL;
1684 sinThetaBuffer = NULL;
1685 cosThetaBuffer = NULL;
1686 queue = NULL;
1687 rotationalBlurKernel = NULL;
1688
1689
1690 clEnv = GetDefaultOpenCLEnv();
1691 context = GetOpenCLContext(clEnv);
1692
1693
1694 /* Create and initialize OpenCL buffers. */
1695
1696 image_view=AcquireVirtualCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00001697 inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00001698 if (inputPixels == (const void *) NULL)
1699 {
dirk8a5cf512014-07-28 20:16:27 +00001700 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristyf034abb2013-11-24 14:16:14 +00001701 goto cleanup;
1702 }
1703
1704 /* If the host pointer is aligned to the size of CLPixelPacket,
1705 then use the host buffer directly from the GPU; otherwise,
1706 create a buffer on the GPU and copy the data over */
1707 if (ALIGNED(inputPixels,CLPixelPacket))
1708 {
1709 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
1710 }
1711 else
1712 {
1713 mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
1714 }
1715 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00001716 length = image->columns * image->rows;
1717 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00001718 if (clStatus != CL_SUCCESS)
1719 {
cristy0c832c62014-03-07 22:21:04 +00001720 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001721 goto cleanup;
1722 }
1723
1724
dirk8a5cf512014-07-28 20:16:27 +00001725 filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristyf034abb2013-11-24 14:16:14 +00001726 assert(filteredImage != NULL);
dirk8a5cf512014-07-28 20:16:27 +00001727 if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
cristyf034abb2013-11-24 14:16:14 +00001728 {
cristya22457d2013-12-07 14:03:06 +00001729 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001730 goto cleanup;
1731 }
dirk832becc2014-08-04 19:44:34 +00001732 filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
dirk8a5cf512014-07-28 20:16:27 +00001733 filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00001734 if (filteredPixels == (void *) NULL)
1735 {
cristya22457d2013-12-07 14:03:06 +00001736 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
cristyf034abb2013-11-24 14:16:14 +00001737 goto cleanup;
1738 }
1739
1740 if (ALIGNED(filteredPixels,CLPixelPacket))
1741 {
1742 mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
1743 hostPtr = filteredPixels;
1744 }
1745 else
1746 {
1747 mem_flags = CL_MEM_WRITE_ONLY;
1748 hostPtr = NULL;
1749 }
1750 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00001751 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00001752 filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00001753 if (clStatus != CL_SUCCESS)
1754 {
cristy0c832c62014-03-07 22:21:04 +00001755 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001756 goto cleanup;
1757 }
1758
dirk8a5cf512014-07-28 20:16:27 +00001759 blurCenter.s[0] = (float) (image->columns-1)/2.0;
1760 blurCenter.s[1] = (float) (image->rows-1)/2.0;
cristyf034abb2013-11-24 14:16:14 +00001761 blurRadius=hypot(blurCenter.s[0],blurCenter.s[1]);
1762 cossin_theta_size=(unsigned int) fabs(4.0*DegreesToRadians(angle)*sqrt((double)blurRadius)+2UL);
1763
1764 /* create a buffer for sin_theta and cos_theta */
cristy0c832c62014-03-07 22:21:04 +00001765 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 +00001766 if (clStatus != CL_SUCCESS)
1767 {
cristy0c832c62014-03-07 22:21:04 +00001768 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001769 goto cleanup;
1770 }
cristy0c832c62014-03-07 22:21:04 +00001771 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 +00001772 if (clStatus != CL_SUCCESS)
1773 {
cristy0c832c62014-03-07 22:21:04 +00001774 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001775 goto cleanup;
1776 }
1777
1778
1779 queue = AcquireOpenCLCommandQueue(clEnv);
cristy0c832c62014-03-07 22:21:04 +00001780 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 +00001781 if (clStatus != CL_SUCCESS)
1782 {
cristya22457d2013-12-07 14:03:06 +00001783 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnqueuemapBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001784 goto cleanup;
1785 }
1786
cristy0c832c62014-03-07 22:21:04 +00001787 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 +00001788 if (clStatus != CL_SUCCESS)
1789 {
cristya22457d2013-12-07 14:03:06 +00001790 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnqueuemapBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00001791 goto cleanup;
1792 }
1793
1794 theta=DegreesToRadians(angle)/(MagickRealType) (cossin_theta_size-1);
1795 offset=theta*(MagickRealType) (cossin_theta_size-1)/2.0;
1796 for (i=0; i < (ssize_t) cossin_theta_size; i++)
1797 {
1798 cosThetaPtr[i]=(float)cos((double) (theta*i-offset));
1799 sinThetaPtr[i]=(float)sin((double) (theta*i-offset));
1800 }
1801
cristy0c832c62014-03-07 22:21:04 +00001802 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, sinThetaBuffer, sinThetaPtr, 0, NULL, NULL);
1803 clStatus |= clEnv->library->clEnqueueUnmapMemObject(queue, cosThetaBuffer, cosThetaPtr, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00001804 if (clStatus != CL_SUCCESS)
1805 {
cristy0c832c62014-03-07 22:21:04 +00001806 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001807 goto cleanup;
1808 }
1809
1810 /* get the OpenCL kernel */
dirk6d612cf2014-03-13 21:17:23 +00001811 rotationalBlurKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "RotationalBlur");
1812 if (rotationalBlurKernel == NULL)
cristyf034abb2013-11-24 14:16:14 +00001813 {
cristya22457d2013-12-07 14:03:06 +00001814 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001815 goto cleanup;
1816 }
1817
1818
1819 /* set the kernel arguments */
1820 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00001821 clStatus=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
dirk6d612cf2014-03-13 21:17:23 +00001822 clStatus|=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(cl_mem),(void *)&filteredImageBuffer);
cristyf034abb2013-11-24 14:16:14 +00001823
dirk8a5cf512014-07-28 20:16:27 +00001824 GetPixelInfo(image,&bias);
cristyf034abb2013-11-24 14:16:14 +00001825 biasPixel.s[0] = bias.red;
1826 biasPixel.s[1] = bias.green;
1827 biasPixel.s[2] = bias.blue;
dirk8a5cf512014-07-28 20:16:27 +00001828 biasPixel.s[3] = bias.alpha;
dirk6d612cf2014-03-13 21:17:23 +00001829 clStatus|=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(cl_float4), &biasPixel);
1830 clStatus|=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(ChannelType), &channel);
cristyf034abb2013-11-24 14:16:14 +00001831
dirk8a5cf512014-07-28 20:16:27 +00001832 matte = (image->alpha_trait==BlendPixelTrait)?1:0;
dirk6d612cf2014-03-13 21:17:23 +00001833 clStatus|=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(unsigned int), &matte);
cristyf034abb2013-11-24 14:16:14 +00001834
dirk6d612cf2014-03-13 21:17:23 +00001835 clStatus=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(cl_float2), &blurCenter);
cristyf034abb2013-11-24 14:16:14 +00001836
dirk6d612cf2014-03-13 21:17:23 +00001837 clStatus|=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(cl_mem),(void *)&cosThetaBuffer);
1838 clStatus|=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(cl_mem),(void *)&sinThetaBuffer);
1839 clStatus|=clEnv->library->clSetKernelArg(rotationalBlurKernel,i++,sizeof(unsigned int), &cossin_theta_size);
cristyf034abb2013-11-24 14:16:14 +00001840 if (clStatus != CL_SUCCESS)
1841 {
cristy0c832c62014-03-07 22:21:04 +00001842 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001843 goto cleanup;
1844 }
1845
1846
dirk8a5cf512014-07-28 20:16:27 +00001847 global_work_size[0] = image->columns;
1848 global_work_size[1] = image->rows;
cristyf034abb2013-11-24 14:16:14 +00001849 /* launch the kernel */
dirk6d612cf2014-03-13 21:17:23 +00001850 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, rotationalBlurKernel, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00001851 if (clStatus != CL_SUCCESS)
1852 {
cristy0c832c62014-03-07 22:21:04 +00001853 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001854 goto cleanup;
1855 }
cristy0c832c62014-03-07 22:21:04 +00001856 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00001857
1858 if (ALIGNED(filteredPixels,CLPixelPacket))
1859 {
dirk8a5cf512014-07-28 20:16:27 +00001860 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00001861 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 +00001862 }
1863 else
1864 {
dirk8a5cf512014-07-28 20:16:27 +00001865 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00001866 clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00001867 }
1868 if (clStatus != CL_SUCCESS)
1869 {
cristya22457d2013-12-07 14:03:06 +00001870 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00001871 goto cleanup;
1872 }
dirk832becc2014-08-04 19:44:34 +00001873 outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
1874
1875cleanup:
1876 OpenCLLogException(__FUNCTION__,__LINE__,exception);
1877
1878 image_view=DestroyCacheView(image_view);
1879 if (filteredImage_view != NULL)
dirk8a5cf512014-07-28 20:16:27 +00001880 filteredImage_view=DestroyCacheView(filteredImage_view);
cristya22457d2013-12-07 14:03:06 +00001881
cristy0c832c62014-03-07 22:21:04 +00001882 if (filteredImageBuffer!=NULL) clEnv->library->clReleaseMemObject(filteredImageBuffer);
dirk8a5cf512014-07-28 20:16:27 +00001883 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00001884 if (sinThetaBuffer!=NULL) clEnv->library->clReleaseMemObject(sinThetaBuffer);
1885 if (cosThetaBuffer!=NULL) clEnv->library->clReleaseMemObject(cosThetaBuffer);
dirk6d612cf2014-03-13 21:17:23 +00001886 if (rotationalBlurKernel!=NULL) RelinquishOpenCLKernel(clEnv, rotationalBlurKernel);
cristyf034abb2013-11-24 14:16:14 +00001887 if (queue != NULL) RelinquishOpenCLCommandQueue(clEnv, queue);
1888 if (outputReady == MagickFalse)
1889 {
1890 if (filteredImage != NULL)
1891 {
1892 DestroyImage(filteredImage);
1893 filteredImage = NULL;
1894 }
1895 }
1896 return filteredImage;
1897}
1898
dirk832becc2014-08-04 19:44:34 +00001899MagickExport Image* AccelerateRotationalBlurImage(const Image *image,
1900 const ChannelType channel,const double angle,ExceptionInfo *exception)
1901{
1902 Image
1903 *filteredImage;
1904
1905 assert(image != NULL);
1906 assert(exception != (ExceptionInfo *) NULL);
1907
1908 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
1909 (checkAccelerateCondition(image, channel) == MagickFalse))
1910 return NULL;
1911
1912 filteredImage=ComputeRotationalBlurImage(image, channel, angle, exception);
1913 return filteredImage;
dirk8a5cf512014-07-28 20:16:27 +00001914}
1915
cristyf034abb2013-11-24 14:16:14 +00001916/*
1917%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1918% %
1919% %
1920% %
dirk8a5cf512014-07-28 20:16:27 +00001921% 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 +00001922% %
1923% %
1924% %
1925%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1926%
dirk8a5cf512014-07-28 20:16:27 +00001927% UnsharpMaskImage() sharpens one or more image channels. We convolve the
1928% image with a Gaussian operator of the given radius and standard deviation
1929% (sigma). For reasonable results, radius should be larger than sigma. Use a
1930% radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
cristyf034abb2013-11-24 14:16:14 +00001931%
dirk8a5cf512014-07-28 20:16:27 +00001932% The format of the UnsharpMaskImage method is:
cristyf034abb2013-11-24 14:16:14 +00001933%
dirk8a5cf512014-07-28 20:16:27 +00001934% Image *UnsharpMaskImage(const Image *image,const double radius,
1935% const double sigma,const double amount,const double threshold,
cristyf034abb2013-11-24 14:16:14 +00001936% ExceptionInfo *exception)
dirk8a5cf512014-07-28 20:16:27 +00001937% Image *UnsharpMaskImageChannel(const Image *image,
1938% const ChannelType channel,const double radius,const double sigma,
1939% const double gain,const double threshold,ExceptionInfo *exception)
cristyf034abb2013-11-24 14:16:14 +00001940%
1941% A description of each parameter follows:
1942%
1943% o image: the image.
1944%
1945% o channel: the channel type.
1946%
dirk8a5cf512014-07-28 20:16:27 +00001947% o radius: the radius of the Gaussian, in pixels, not counting the center
1948% pixel.
1949%
1950% o sigma: the standard deviation of the Gaussian, in pixels.
1951%
1952% o gain: the percentage of the difference between the original and the
1953% blur image that is added back into the original.
1954%
1955% o threshold: the threshold in pixels needed to apply the diffence gain.
cristyf034abb2013-11-24 14:16:14 +00001956%
1957% o exception: return any errors or warnings in this structure.
1958%
1959*/
1960
dirk832becc2014-08-04 19:44:34 +00001961static Image *ComputeUnsharpMaskImage(const Image *image,
1962 const ChannelType channel,const double radius,const double sigma,
1963 const double gain,const double threshold,ExceptionInfo *exception)
1964{
1965 CacheView
1966 *filteredImage_view,
1967 *image_view;
1968
1969 char
1970 geometry[MaxTextExtent];
1971
1972 cl_command_queue
1973 queue;
1974
1975 cl_context
1976 context;
1977
1978 cl_int
1979 clStatus;
1980
1981 cl_kernel
1982 blurRowKernel,
1983 unsharpMaskBlurColumnKernel;
1984
1985 cl_mem
1986 filteredImageBuffer,
1987 imageBuffer,
1988 imageKernelBuffer,
1989 tempImageBuffer;
1990
1991 cl_mem_flags
1992 mem_flags;
1993
1994 const void
1995 *inputPixels;
1996
1997 float
1998 fGain,
1999 fThreshold,
2000 *kernelBufferPtr;
2001
2002 Image
2003 *filteredImage;
2004
2005 int
2006 chunkSize;
2007
2008 KernelInfo
2009 *kernel;
2010
2011 MagickBooleanType
2012 outputReady;
2013
2014 MagickCLEnv
2015 clEnv;
2016
2017 MagickSizeType
2018 length;
2019
2020 void
2021 *filteredPixels,
2022 *hostPtr;
2023
2024 unsigned int
2025 i,
2026 imageColumns,
2027 imageRows,
2028 kernelWidth;
2029
2030 clEnv = NULL;
2031 filteredImage = NULL;
2032 filteredImage_view = NULL;
2033 kernel = NULL;
2034 context = NULL;
2035 imageBuffer = NULL;
2036 filteredImageBuffer = NULL;
2037 tempImageBuffer = NULL;
2038 imageKernelBuffer = NULL;
2039 blurRowKernel = NULL;
2040 unsharpMaskBlurColumnKernel = NULL;
2041 queue = NULL;
2042 outputReady = MagickFalse;
2043
2044 clEnv = GetDefaultOpenCLEnv();
2045 context = GetOpenCLContext(clEnv);
2046 queue = AcquireOpenCLCommandQueue(clEnv);
2047
2048 /* Create and initialize OpenCL buffers. */
2049 {
2050 image_view=AcquireVirtualCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00002051 inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00002052 if (inputPixels == (const void *) NULL)
2053 {
dirk8a5cf512014-07-28 20:16:27 +00002054 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristyf034abb2013-11-24 14:16:14 +00002055 goto cleanup;
2056 }
2057
2058 /* If the host pointer is aligned to the size of CLPixelPacket,
2059 then use the host buffer directly from the GPU; otherwise,
2060 create a buffer on the GPU and copy the data over */
2061 if (ALIGNED(inputPixels,CLPixelPacket))
2062 {
2063 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
2064 }
2065 else
2066 {
2067 mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
2068 }
2069 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00002070 length = image->columns * image->rows;
2071 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00002072 if (clStatus != CL_SUCCESS)
2073 {
cristy0c832c62014-03-07 22:21:04 +00002074 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002075 goto cleanup;
2076 }
2077 }
2078
2079 /* create output */
2080 {
dirk8a5cf512014-07-28 20:16:27 +00002081 filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristyf034abb2013-11-24 14:16:14 +00002082 assert(filteredImage != NULL);
dirk8a5cf512014-07-28 20:16:27 +00002083 if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
cristyf034abb2013-11-24 14:16:14 +00002084 {
cristya22457d2013-12-07 14:03:06 +00002085 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002086 goto cleanup;
2087 }
dirk832becc2014-08-04 19:44:34 +00002088 filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
dirk8a5cf512014-07-28 20:16:27 +00002089 filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00002090 if (filteredPixels == (void *) NULL)
2091 {
cristya22457d2013-12-07 14:03:06 +00002092 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
cristyf034abb2013-11-24 14:16:14 +00002093 goto cleanup;
2094 }
2095
2096 if (ALIGNED(filteredPixels,CLPixelPacket))
2097 {
2098 mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
2099 hostPtr = filteredPixels;
2100 }
2101 else
2102 {
2103 mem_flags = CL_MEM_WRITE_ONLY;
2104 hostPtr = NULL;
2105 }
2106
2107 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00002108 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00002109 filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00002110 if (clStatus != CL_SUCCESS)
2111 {
cristy0c832c62014-03-07 22:21:04 +00002112 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002113 goto cleanup;
2114 }
2115 }
2116
2117 /* create the blur kernel */
2118 {
2119 (void) FormatLocaleString(geometry,MaxTextExtent,"blur:%.20gx%.20g;blur:%.20gx%.20g+90",radius,sigma,radius,sigma);
2120 kernel=AcquireKernelInfo(geometry);
2121 if (kernel == (KernelInfo *) NULL)
2122 {
cristya22457d2013-12-07 14:03:06 +00002123 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireKernelInfo failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002124 goto cleanup;
2125 }
2126
cristy0c832c62014-03-07 22:21:04 +00002127 imageKernelBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_ONLY, kernel->width * sizeof(float), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00002128 if (clStatus != CL_SUCCESS)
2129 {
cristy0c832c62014-03-07 22:21:04 +00002130 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002131 goto cleanup;
2132 }
2133
2134
cristy0c832c62014-03-07 22:21:04 +00002135 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 +00002136 if (clStatus != CL_SUCCESS)
2137 {
cristy0c832c62014-03-07 22:21:04 +00002138 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueMapBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002139 goto cleanup;
2140 }
2141 for (i = 0; i < kernel->width; i++)
2142 {
2143 kernelBufferPtr[i] = (float) kernel->values[i];
2144 }
cristy0c832c62014-03-07 22:21:04 +00002145 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, imageKernelBuffer, kernelBufferPtr, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00002146 if (clStatus != CL_SUCCESS)
2147 {
cristy0c832c62014-03-07 22:21:04 +00002148 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002149 goto cleanup;
2150 }
2151 }
2152
2153 {
2154 /* create temp buffer */
2155 {
dirk8a5cf512014-07-28 20:16:27 +00002156 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00002157 tempImageBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_WRITE, length * 4 * sizeof(float), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00002158 if (clStatus != CL_SUCCESS)
2159 {
cristy0c832c62014-03-07 22:21:04 +00002160 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002161 goto cleanup;
2162 }
2163 }
2164
2165 /* get the opencl kernel */
2166 {
2167 blurRowKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "BlurRow");
2168 if (blurRowKernel == NULL)
2169 {
cristya22457d2013-12-07 14:03:06 +00002170 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002171 goto cleanup;
2172 };
2173
2174 unsharpMaskBlurColumnKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "UnsharpMaskBlurColumn");
2175 if (unsharpMaskBlurColumnKernel == NULL)
2176 {
cristya22457d2013-12-07 14:03:06 +00002177 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002178 goto cleanup;
2179 };
2180 }
2181
2182 {
2183 chunkSize = 256;
2184
dirk8a5cf512014-07-28 20:16:27 +00002185 imageColumns = image->columns;
2186 imageRows = image->rows;
cristyf034abb2013-11-24 14:16:14 +00002187
2188 kernelWidth = kernel->width;
2189
2190 /* set the kernel arguments */
2191 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00002192 clStatus=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00002193 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&tempImageBuffer);
2194 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(ChannelType),&channel);
2195 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&imageKernelBuffer);
2196 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&kernelWidth);
2197 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&imageColumns);
2198 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&imageRows);
2199 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(CLPixelPacket)*(chunkSize+kernel->width),(void *)NULL);
cristyf034abb2013-11-24 14:16:14 +00002200 if (clStatus != CL_SUCCESS)
2201 {
cristy0c832c62014-03-07 22:21:04 +00002202 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002203 goto cleanup;
2204 }
2205 }
2206
2207 /* launch the kernel */
2208 {
2209 size_t gsize[2];
2210 size_t wsize[2];
2211
dirk8a5cf512014-07-28 20:16:27 +00002212 gsize[0] = chunkSize*((image->columns+chunkSize-1)/chunkSize);
2213 gsize[1] = image->rows;
cristyf034abb2013-11-24 14:16:14 +00002214 wsize[0] = chunkSize;
2215 wsize[1] = 1;
2216
cristy0c832c62014-03-07 22:21:04 +00002217 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, blurRowKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00002218 if (clStatus != CL_SUCCESS)
2219 {
cristy0c832c62014-03-07 22:21:04 +00002220 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002221 goto cleanup;
2222 }
cristy0c832c62014-03-07 22:21:04 +00002223 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00002224 }
2225
2226
2227 {
2228 chunkSize = 256;
dirk8a5cf512014-07-28 20:16:27 +00002229 imageColumns = image->columns;
2230 imageRows = image->rows;
cristyf034abb2013-11-24 14:16:14 +00002231 kernelWidth = kernel->width;
2232 fGain = (float)gain;
2233 fThreshold = (float)threshold;
2234
2235 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00002236 clStatus=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00002237 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(cl_mem),(void *)&tempImageBuffer);
2238 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(cl_mem),(void *)&filteredImageBuffer);
2239 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(unsigned int),(void *)&imageColumns);
2240 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(unsigned int),(void *)&imageRows);
2241 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++, (chunkSize+kernelWidth-1)*sizeof(cl_float4),NULL);
2242 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++, kernelWidth*sizeof(float),NULL);
2243 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(ChannelType),&channel);
2244 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(cl_mem),(void *)&imageKernelBuffer);
2245 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(unsigned int),(void *)&kernelWidth);
2246 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(float),(void *)&fGain);
2247 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(float),(void *)&fThreshold);
cristyf034abb2013-11-24 14:16:14 +00002248
2249 if (clStatus != CL_SUCCESS)
2250 {
cristy0c832c62014-03-07 22:21:04 +00002251 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002252 goto cleanup;
2253 }
2254 }
2255
2256 /* launch the kernel */
2257 {
2258 size_t gsize[2];
2259 size_t wsize[2];
2260
dirk8a5cf512014-07-28 20:16:27 +00002261 gsize[0] = image->columns;
2262 gsize[1] = chunkSize*((image->rows+chunkSize-1)/chunkSize);
cristyf034abb2013-11-24 14:16:14 +00002263 wsize[0] = 1;
2264 wsize[1] = chunkSize;
2265
cristy0c832c62014-03-07 22:21:04 +00002266 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, unsharpMaskBlurColumnKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00002267 if (clStatus != CL_SUCCESS)
2268 {
cristy0c832c62014-03-07 22:21:04 +00002269 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002270 goto cleanup;
2271 }
cristy0c832c62014-03-07 22:21:04 +00002272 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00002273 }
2274
2275 }
2276
2277 /* get result */
2278 if (ALIGNED(filteredPixels,CLPixelPacket))
2279 {
dirk8a5cf512014-07-28 20:16:27 +00002280 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00002281 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 +00002282 }
2283 else
2284 {
dirk8a5cf512014-07-28 20:16:27 +00002285 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00002286 clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00002287 }
2288 if (clStatus != CL_SUCCESS)
2289 {
cristya22457d2013-12-07 14:03:06 +00002290 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002291 goto cleanup;
2292 }
2293
dirk832becc2014-08-04 19:44:34 +00002294 outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
2295
2296cleanup:
2297 OpenCLLogException(__FUNCTION__,__LINE__,exception);
2298
2299 image_view=DestroyCacheView(image_view);
2300 if (filteredImage_view != NULL)
dirk8a5cf512014-07-28 20:16:27 +00002301 filteredImage_view=DestroyCacheView(filteredImage_view);
cristya22457d2013-12-07 14:03:06 +00002302
cristyf034abb2013-11-24 14:16:14 +00002303 if (kernel != NULL) kernel=DestroyKernelInfo(kernel);
dirk8a5cf512014-07-28 20:16:27 +00002304 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00002305 if (filteredImageBuffer!=NULL) clEnv->library->clReleaseMemObject(filteredImageBuffer);
2306 if (tempImageBuffer!=NULL) clEnv->library->clReleaseMemObject(tempImageBuffer);
2307 if (imageKernelBuffer!=NULL) clEnv->library->clReleaseMemObject(imageKernelBuffer);
cristyf034abb2013-11-24 14:16:14 +00002308 if (blurRowKernel!=NULL) RelinquishOpenCLKernel(clEnv, blurRowKernel);
2309 if (unsharpMaskBlurColumnKernel!=NULL) RelinquishOpenCLKernel(clEnv, unsharpMaskBlurColumnKernel);
2310 if (queue != NULL) RelinquishOpenCLCommandQueue(clEnv, queue);
2311 if (outputReady == MagickFalse)
2312 {
2313 if (filteredImage != NULL)
2314 {
2315 DestroyImage(filteredImage);
2316 filteredImage = NULL;
2317 }
2318 }
dirk8a5cf512014-07-28 20:16:27 +00002319 return(filteredImage);
cristyf034abb2013-11-24 14:16:14 +00002320}
2321
dirk832becc2014-08-04 19:44:34 +00002322static Image *ComputeUnsharpMaskImageSection(const Image *image,
2323 const ChannelType channel,const double radius,const double sigma,
2324 const double gain,const double threshold,ExceptionInfo *exception)
2325{
2326 CacheView
2327 *filteredImage_view,
2328 *image_view;
2329
2330 char
2331 geometry[MaxTextExtent];
2332
2333 cl_command_queue
2334 queue;
2335
2336 cl_context
2337 context;
2338
2339 cl_int
2340 clStatus;
2341
2342 cl_kernel
2343 blurRowKernel,
2344 unsharpMaskBlurColumnKernel;
2345
2346 cl_mem
2347 filteredImageBuffer,
2348 imageBuffer,
2349 imageKernelBuffer,
2350 tempImageBuffer;
2351
2352 cl_mem_flags
2353 mem_flags;
2354
2355 const void
2356 *inputPixels;
2357
2358 float
2359 fGain,
2360 fThreshold,
2361 *kernelBufferPtr;
2362
2363 Image
2364 *filteredImage;
2365
2366 int
2367 chunkSize;
2368
2369 KernelInfo
2370 *kernel;
2371
2372 MagickBooleanType
2373 outputReady;
2374
2375 MagickCLEnv
2376 clEnv;
2377
2378 MagickSizeType
2379 length;
2380
2381 void
2382 *filteredPixels,
2383 *hostPtr;
2384
2385 unsigned int
2386 i,
2387 imageColumns,
2388 imageRows,
2389 kernelWidth;
2390
2391 clEnv = NULL;
2392 filteredImage = NULL;
2393 filteredImage_view = NULL;
2394 kernel = NULL;
2395 context = NULL;
2396 imageBuffer = NULL;
2397 filteredImageBuffer = NULL;
2398 tempImageBuffer = NULL;
2399 imageKernelBuffer = NULL;
2400 blurRowKernel = NULL;
2401 unsharpMaskBlurColumnKernel = NULL;
2402 queue = NULL;
2403 outputReady = MagickFalse;
2404
2405 clEnv = GetDefaultOpenCLEnv();
2406 context = GetOpenCLContext(clEnv);
2407 queue = AcquireOpenCLCommandQueue(clEnv);
2408
2409 /* Create and initialize OpenCL buffers. */
2410 {
2411 image_view=AcquireVirtualCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00002412 inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00002413 if (inputPixels == (const void *) NULL)
2414 {
dirk8a5cf512014-07-28 20:16:27 +00002415 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristyf034abb2013-11-24 14:16:14 +00002416 goto cleanup;
2417 }
2418
2419 /* If the host pointer is aligned to the size of CLPixelPacket,
2420 then use the host buffer directly from the GPU; otherwise,
2421 create a buffer on the GPU and copy the data over */
2422 if (ALIGNED(inputPixels,CLPixelPacket))
2423 {
2424 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
2425 }
2426 else
2427 {
2428 mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
2429 }
2430 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00002431 length = image->columns * image->rows;
2432 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00002433 if (clStatus != CL_SUCCESS)
2434 {
cristy0c832c62014-03-07 22:21:04 +00002435 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002436 goto cleanup;
2437 }
2438 }
2439
2440 /* create output */
2441 {
dirk8a5cf512014-07-28 20:16:27 +00002442 filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristyf034abb2013-11-24 14:16:14 +00002443 assert(filteredImage != NULL);
dirk8a5cf512014-07-28 20:16:27 +00002444 if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
cristyf034abb2013-11-24 14:16:14 +00002445 {
cristya22457d2013-12-07 14:03:06 +00002446 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002447 goto cleanup;
2448 }
dirk832becc2014-08-04 19:44:34 +00002449 filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
dirk8a5cf512014-07-28 20:16:27 +00002450 filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00002451 if (filteredPixels == (void *) NULL)
2452 {
cristya22457d2013-12-07 14:03:06 +00002453 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
cristyf034abb2013-11-24 14:16:14 +00002454 goto cleanup;
2455 }
2456
2457 if (ALIGNED(filteredPixels,CLPixelPacket))
2458 {
2459 mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
2460 hostPtr = filteredPixels;
2461 }
2462 else
2463 {
2464 mem_flags = CL_MEM_WRITE_ONLY;
2465 hostPtr = NULL;
2466 }
2467
2468 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00002469 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00002470 filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00002471 if (clStatus != CL_SUCCESS)
2472 {
cristy0c832c62014-03-07 22:21:04 +00002473 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002474 goto cleanup;
2475 }
2476 }
2477
2478 /* create the blur kernel */
2479 {
2480 (void) FormatLocaleString(geometry,MaxTextExtent,"blur:%.20gx%.20g;blur:%.20gx%.20g+90",radius,sigma,radius,sigma);
2481 kernel=AcquireKernelInfo(geometry);
2482 if (kernel == (KernelInfo *) NULL)
2483 {
cristya22457d2013-12-07 14:03:06 +00002484 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireKernelInfo failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002485 goto cleanup;
2486 }
2487
cristy0c832c62014-03-07 22:21:04 +00002488 imageKernelBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_ONLY, kernel->width * sizeof(float), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00002489 if (clStatus != CL_SUCCESS)
2490 {
cristy0c832c62014-03-07 22:21:04 +00002491 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002492 goto cleanup;
2493 }
2494
2495
cristy0c832c62014-03-07 22:21:04 +00002496 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 +00002497 if (clStatus != CL_SUCCESS)
2498 {
cristy0c832c62014-03-07 22:21:04 +00002499 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueMapBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002500 goto cleanup;
2501 }
2502 for (i = 0; i < kernel->width; i++)
2503 {
2504 kernelBufferPtr[i] = (float) kernel->values[i];
2505 }
cristy0c832c62014-03-07 22:21:04 +00002506 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, imageKernelBuffer, kernelBufferPtr, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00002507 if (clStatus != CL_SUCCESS)
2508 {
cristy0c832c62014-03-07 22:21:04 +00002509 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002510 goto cleanup;
2511 }
2512 }
2513
2514 {
2515 unsigned int offsetRows;
2516 unsigned int sec;
2517
2518 /* create temp buffer */
2519 {
dirk8a5cf512014-07-28 20:16:27 +00002520 length = image->columns * (image->rows / 2 + 1 + (kernel->width-1) / 2);
cristy0c832c62014-03-07 22:21:04 +00002521 tempImageBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_WRITE, length * 4 * sizeof(float), NULL, &clStatus);
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->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00002525 goto cleanup;
2526 }
2527 }
2528
2529 /* get the opencl kernel */
2530 {
2531 blurRowKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "BlurRowSection");
2532 if (blurRowKernel == NULL)
2533 {
cristya22457d2013-12-07 14:03:06 +00002534 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002535 goto cleanup;
2536 };
2537
2538 unsharpMaskBlurColumnKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "UnsharpMaskBlurColumnSection");
2539 if (unsharpMaskBlurColumnKernel == NULL)
2540 {
cristya22457d2013-12-07 14:03:06 +00002541 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002542 goto cleanup;
2543 };
2544 }
2545
2546 for (sec = 0; sec < 2; sec++)
2547 {
2548 {
2549 chunkSize = 256;
2550
dirk8a5cf512014-07-28 20:16:27 +00002551 imageColumns = image->columns;
cristyf034abb2013-11-24 14:16:14 +00002552 if (sec == 0)
dirk8a5cf512014-07-28 20:16:27 +00002553 imageRows = image->rows / 2 + (kernel->width-1) / 2;
cristyf034abb2013-11-24 14:16:14 +00002554 else
dirk8a5cf512014-07-28 20:16:27 +00002555 imageRows = (image->rows - image->rows / 2) + (kernel->width-1) / 2;
cristyf034abb2013-11-24 14:16:14 +00002556
dirk8a5cf512014-07-28 20:16:27 +00002557 offsetRows = sec * image->rows / 2;
cristyf034abb2013-11-24 14:16:14 +00002558
2559 kernelWidth = kernel->width;
2560
2561 /* set the kernel arguments */
2562 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00002563 clStatus=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00002564 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&tempImageBuffer);
2565 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(ChannelType),&channel);
2566 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(cl_mem),(void *)&imageKernelBuffer);
2567 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&kernelWidth);
2568 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&imageColumns);
2569 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&imageRows);
2570 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(CLPixelPacket)*(chunkSize+kernel->width),(void *)NULL);
2571 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&offsetRows);
2572 clStatus|=clEnv->library->clSetKernelArg(blurRowKernel,i++,sizeof(unsigned int),(void *)&sec);
cristyf034abb2013-11-24 14:16:14 +00002573 if (clStatus != CL_SUCCESS)
2574 {
cristy0c832c62014-03-07 22:21:04 +00002575 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002576 goto cleanup;
2577 }
2578 }
2579 /* launch the kernel */
2580 {
2581 size_t gsize[2];
2582 size_t wsize[2];
2583
2584 gsize[0] = chunkSize*((imageColumns+chunkSize-1)/chunkSize);
2585 gsize[1] = imageRows;
2586 wsize[0] = chunkSize;
2587 wsize[1] = 1;
2588
cristy0c832c62014-03-07 22:21:04 +00002589 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, blurRowKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00002590 if (clStatus != CL_SUCCESS)
2591 {
cristy0c832c62014-03-07 22:21:04 +00002592 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002593 goto cleanup;
2594 }
cristy0c832c62014-03-07 22:21:04 +00002595 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00002596 }
2597
2598
2599 {
2600 chunkSize = 256;
2601
dirk8a5cf512014-07-28 20:16:27 +00002602 imageColumns = image->columns;
cristyf034abb2013-11-24 14:16:14 +00002603 if (sec == 0)
dirk8a5cf512014-07-28 20:16:27 +00002604 imageRows = image->rows / 2;
cristyf034abb2013-11-24 14:16:14 +00002605 else
dirk8a5cf512014-07-28 20:16:27 +00002606 imageRows = (image->rows - image->rows / 2);
cristyf034abb2013-11-24 14:16:14 +00002607
dirk8a5cf512014-07-28 20:16:27 +00002608 offsetRows = sec * image->rows / 2;
cristyf034abb2013-11-24 14:16:14 +00002609
2610 kernelWidth = kernel->width;
2611
2612 fGain = (float)gain;
2613 fThreshold = (float)threshold;
2614
2615 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00002616 clStatus=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00002617 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(cl_mem),(void *)&tempImageBuffer);
2618 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(cl_mem),(void *)&filteredImageBuffer);
2619 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(unsigned int),(void *)&imageColumns);
2620 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(unsigned int),(void *)&imageRows);
2621 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++, (chunkSize+kernelWidth-1)*sizeof(cl_float4),NULL);
2622 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++, kernelWidth*sizeof(float),NULL);
2623 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(ChannelType),&channel);
2624 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(cl_mem),(void *)&imageKernelBuffer);
2625 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(unsigned int),(void *)&kernelWidth);
2626 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(float),(void *)&fGain);
2627 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(float),(void *)&fThreshold);
2628 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(unsigned int),(void *)&offsetRows);
2629 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskBlurColumnKernel,i++,sizeof(unsigned int),(void *)&sec);
cristyf034abb2013-11-24 14:16:14 +00002630
2631 if (clStatus != CL_SUCCESS)
2632 {
cristy0c832c62014-03-07 22:21:04 +00002633 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002634 goto cleanup;
2635 }
2636 }
2637
2638 /* launch the kernel */
2639 {
2640 size_t gsize[2];
2641 size_t wsize[2];
2642
2643 gsize[0] = imageColumns;
2644 gsize[1] = chunkSize*((imageRows+chunkSize-1)/chunkSize);
2645 wsize[0] = 1;
2646 wsize[1] = chunkSize;
2647
cristy0c832c62014-03-07 22:21:04 +00002648 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, unsharpMaskBlurColumnKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00002649 if (clStatus != CL_SUCCESS)
2650 {
cristy0c832c62014-03-07 22:21:04 +00002651 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002652 goto cleanup;
2653 }
cristy0c832c62014-03-07 22:21:04 +00002654 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00002655 }
2656 }
2657 }
2658
2659 /* get result */
2660 if (ALIGNED(filteredPixels,CLPixelPacket))
2661 {
dirk8a5cf512014-07-28 20:16:27 +00002662 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00002663 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 +00002664 }
2665 else
2666 {
dirk8a5cf512014-07-28 20:16:27 +00002667 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00002668 clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00002669 }
2670 if (clStatus != CL_SUCCESS)
2671 {
cristya22457d2013-12-07 14:03:06 +00002672 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00002673 goto cleanup;
2674 }
2675
dirk832becc2014-08-04 19:44:34 +00002676 outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
2677
2678cleanup:
2679 OpenCLLogException(__FUNCTION__,__LINE__,exception);
2680
2681 image_view=DestroyCacheView(image_view);
2682 if (filteredImage_view != NULL)
dirk8a5cf512014-07-28 20:16:27 +00002683 filteredImage_view=DestroyCacheView(filteredImage_view);
cristya22457d2013-12-07 14:03:06 +00002684
cristyf034abb2013-11-24 14:16:14 +00002685 if (kernel != NULL) kernel=DestroyKernelInfo(kernel);
dirk8a5cf512014-07-28 20:16:27 +00002686 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00002687 if (filteredImageBuffer!=NULL) clEnv->library->clReleaseMemObject(filteredImageBuffer);
2688 if (tempImageBuffer!=NULL) clEnv->library->clReleaseMemObject(tempImageBuffer);
2689 if (imageKernelBuffer!=NULL) clEnv->library->clReleaseMemObject(imageKernelBuffer);
cristyf034abb2013-11-24 14:16:14 +00002690 if (blurRowKernel!=NULL) RelinquishOpenCLKernel(clEnv, blurRowKernel);
2691 if (unsharpMaskBlurColumnKernel!=NULL) RelinquishOpenCLKernel(clEnv, unsharpMaskBlurColumnKernel);
2692 if (queue != NULL) RelinquishOpenCLCommandQueue(clEnv, queue);
2693 if (outputReady == MagickFalse)
2694 {
2695 if (filteredImage != NULL)
2696 {
2697 DestroyImage(filteredImage);
2698 filteredImage = NULL;
2699 }
2700 }
2701 return filteredImage;
dirk832becc2014-08-04 19:44:34 +00002702}
2703
dirk859503c2014-08-13 20:39:42 +00002704static Image *ComputeUnsharpMaskImageSingle(const Image *image,
2705 const ChannelType channel,const double radius,const double sigma,
2706 const double gain,const double threshold,int blurOnly, ExceptionInfo *exception)
2707{
2708 CacheView
2709 *filteredImage_view,
2710 *image_view;
2711
2712 char
2713 geometry[MaxTextExtent];
2714
2715 cl_command_queue
2716 queue;
2717
2718 cl_context
2719 context;
2720
2721 cl_int
2722 justBlur,
2723 clStatus;
2724
2725 cl_kernel
2726 unsharpMaskKernel;
2727
2728 cl_mem
2729 filteredImageBuffer,
2730 imageBuffer,
2731 imageKernelBuffer;
2732
2733 cl_mem_flags
2734 mem_flags;
2735
2736 const void
2737 *inputPixels;
2738
2739 float
2740 fGain,
2741 fThreshold,
2742 *kernelBufferPtr;
2743
2744 Image
2745 *filteredImage;
2746
2747 KernelInfo
2748 *kernel;
2749
2750 MagickBooleanType
2751 outputReady;
2752
2753 MagickCLEnv
2754 clEnv;
2755
2756 MagickSizeType
2757 length;
2758
2759 void
2760 *filteredPixels,
2761 *hostPtr;
2762
2763 unsigned int
2764 i,
2765 imageColumns,
2766 imageRows,
2767 kernelWidth;
2768
2769 clEnv = NULL;
2770 filteredImage = NULL;
2771 filteredImage_view = NULL;
2772 kernel = NULL;
2773 context = NULL;
2774 imageBuffer = NULL;
2775 filteredImageBuffer = NULL;
2776 imageKernelBuffer = NULL;
2777 unsharpMaskKernel = NULL;
2778 queue = NULL;
2779 outputReady = MagickFalse;
2780
2781 clEnv = GetDefaultOpenCLEnv();
2782 context = GetOpenCLContext(clEnv);
2783 queue = AcquireOpenCLCommandQueue(clEnv);
2784
2785 /* Create and initialize OpenCL buffers. */
2786 {
2787 image_view=AcquireVirtualCacheView(image,exception);
2788 inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
2789 if (inputPixels == (const void *) NULL)
2790 {
2791 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
2792 goto cleanup;
2793 }
2794
2795 /* If the host pointer is aligned to the size of CLPixelPacket,
2796 then use the host buffer directly from the GPU; otherwise,
2797 create a buffer on the GPU and copy the data over */
2798 if (ALIGNED(inputPixels,CLPixelPacket))
2799 {
2800 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
2801 }
2802 else
2803 {
2804 mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
2805 }
2806 /* create a CL buffer from image pixel buffer */
2807 length = image->columns * image->rows;
2808 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
2809 if (clStatus != CL_SUCCESS)
2810 {
2811 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
2812 goto cleanup;
2813 }
2814 }
2815
2816 /* create output */
2817 {
2818 filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
2819 assert(filteredImage != NULL);
2820 if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
2821 {
2822 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
2823 goto cleanup;
2824 }
2825 filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
2826 filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
2827 if (filteredPixels == (void *) NULL)
2828 {
2829 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
2830 goto cleanup;
2831 }
2832
2833 if (ALIGNED(filteredPixels,CLPixelPacket))
2834 {
2835 mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
2836 hostPtr = filteredPixels;
2837 }
2838 else
2839 {
2840 mem_flags = CL_MEM_WRITE_ONLY;
2841 hostPtr = NULL;
2842 }
2843
2844 /* create a CL buffer from image pixel buffer */
2845 length = image->columns * image->rows;
2846 filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
2847 if (clStatus != CL_SUCCESS)
2848 {
2849 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
2850 goto cleanup;
2851 }
2852 }
2853
2854 /* create the blur kernel */
2855 {
2856 (void) FormatLocaleString(geometry,MaxTextExtent,"blur:%.20gx%.20g;blur:%.20gx%.20g+90",radius,sigma,radius,sigma);
2857 kernel=AcquireKernelInfo(geometry);
2858 if (kernel == (KernelInfo *) NULL)
2859 {
2860 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireKernelInfo failed.",".");
2861 goto cleanup;
2862 }
2863
2864 imageKernelBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_ONLY, kernel->width * sizeof(float), NULL, &clStatus);
2865 if (clStatus != CL_SUCCESS)
2866 {
2867 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
2868 goto cleanup;
2869 }
2870
2871
2872 kernelBufferPtr = (float*)clEnv->library->clEnqueueMapBuffer(queue, imageKernelBuffer, CL_TRUE, CL_MAP_WRITE, 0, kernel->width * sizeof(float), 0, NULL, NULL, &clStatus);
2873 if (clStatus != CL_SUCCESS)
2874 {
2875 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueMapBuffer failed.",".");
2876 goto cleanup;
2877 }
2878 for (i = 0; i < kernel->width; i++)
2879 {
2880 kernelBufferPtr[i] = (float) kernel->values[i];
2881 }
2882 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, imageKernelBuffer, kernelBufferPtr, 0, NULL, NULL);
2883 if (clStatus != CL_SUCCESS)
2884 {
2885 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
2886 goto cleanup;
2887 }
2888 }
2889
2890 {
2891 /* get the opencl kernel */
2892 {
2893 unsharpMaskKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "UnsharpMask");
2894 if (unsharpMaskKernel == NULL)
2895 {
2896 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
2897 goto cleanup;
2898 };
2899 }
2900
2901 {
2902 imageColumns = image->columns;
2903 imageRows = image->rows;
2904 kernelWidth = kernel->width;
2905 fGain = (float)gain;
2906 fThreshold = (float)threshold;
2907 justBlur = blurOnly;
2908
2909 /* set the kernel arguments */
2910 i = 0;
2911 clStatus=clEnv->library->clSetKernelArg(unsharpMaskKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
2912 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskKernel,i++,sizeof(cl_mem),(void *)&filteredImageBuffer);
2913 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskKernel,i++,sizeof(cl_mem),(void *)&imageKernelBuffer);
2914 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskKernel,i++,sizeof(unsigned int),(void *)&kernelWidth);
2915 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskKernel,i++,sizeof(unsigned int),(void *)&imageColumns);
2916 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskKernel,i++,sizeof(unsigned int),(void *)&imageRows);
2917 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskKernel,i++,sizeof(cl_float4)*(8 * (32 + kernel->width)),(void *)NULL);
2918 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskKernel,i++,sizeof(float),(void *)&fGain);
2919 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskKernel,i++,sizeof(float),(void *)&fThreshold);
2920 clStatus|=clEnv->library->clSetKernelArg(unsharpMaskKernel,i++,sizeof(cl_uint),(void *)&justBlur);
2921 if (clStatus != CL_SUCCESS)
2922 {
2923 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
2924 goto cleanup;
2925 }
2926 }
2927
2928 /* launch the kernel */
2929 {
2930 size_t gsize[2];
2931 size_t wsize[2];
2932
2933 gsize[0] = ((image->columns + 7) / 8) * 8;
2934 gsize[1] = ((image->rows + 31) / 32) * 32;
2935 wsize[0] = 8;
2936 wsize[1] = 32;
2937
2938 clStatus = clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, unsharpMaskKernel, 2, NULL, gsize, wsize, 0, NULL, NULL);
2939 if (clStatus != CL_SUCCESS)
2940 {
2941 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
2942 goto cleanup;
2943 }
2944 clEnv->library->clFlush(queue);
2945 }
2946 }
2947
2948 /* get result */
2949 if (ALIGNED(filteredPixels,CLPixelPacket))
2950 {
2951 length = image->columns * image->rows;
2952 clEnv->library->clEnqueueMapBuffer(queue, filteredImageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
2953 }
2954 else
2955 {
2956 length = image->columns * image->rows;
2957 clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
2958 }
2959 if (clStatus != CL_SUCCESS)
2960 {
2961 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
2962 goto cleanup;
2963 }
2964
2965 outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
2966
2967cleanup:
2968 OpenCLLogException(__FUNCTION__,__LINE__,exception);
2969
2970 image_view=DestroyCacheView(image_view);
2971 if (filteredImage_view != NULL)
2972 filteredImage_view=DestroyCacheView(filteredImage_view);
2973
2974 if (kernel != NULL) kernel=DestroyKernelInfo(kernel);
2975 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
2976 if (filteredImageBuffer!=NULL) clEnv->library->clReleaseMemObject(filteredImageBuffer);
2977 if (imageKernelBuffer!=NULL) clEnv->library->clReleaseMemObject(imageKernelBuffer);
2978 if (unsharpMaskKernel!=NULL) RelinquishOpenCLKernel(clEnv, unsharpMaskKernel);
2979 if (queue != NULL) RelinquishOpenCLCommandQueue(clEnv, queue);
2980 if (outputReady == MagickFalse)
2981 {
2982 if (filteredImage != NULL)
2983 {
2984 DestroyImage(filteredImage);
2985 filteredImage = NULL;
2986 }
2987 }
2988 return(filteredImage);
2989}
2990
2991
dirk832becc2014-08-04 19:44:34 +00002992MagickExport Image *AccelerateUnsharpMaskImage(const Image *image,
2993 const ChannelType channel,const double radius,const double sigma,
2994 const double gain,const double threshold,ExceptionInfo *exception)
2995{
2996 Image
2997 *filteredImage;
2998
2999 assert(image != NULL);
3000 assert(exception != (ExceptionInfo *) NULL);
3001
3002 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
3003 (checkAccelerateCondition(image, channel) == MagickFalse))
3004 return NULL;
3005
dirk859503c2014-08-13 20:39:42 +00003006 if (radius < 12.1)
3007 filteredImage = ComputeUnsharpMaskImageSingle(image,channel,radius,sigma,gain,threshold, 0, exception);
3008 else if (splitImage(image) && (image->rows / 2 > radius))
dirk832becc2014-08-04 19:44:34 +00003009 filteredImage = ComputeUnsharpMaskImageSection(image,channel,radius,sigma,gain,threshold,exception);
3010 else
3011 filteredImage = ComputeUnsharpMaskImage(image,channel,radius,sigma,gain,threshold,exception);
3012 return(filteredImage);
cristyf034abb2013-11-24 14:16:14 +00003013}
3014
cristyf034abb2013-11-24 14:16:14 +00003015/*
3016%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3017% %
3018% %
3019% %
dirk8a5cf512014-07-28 20:16:27 +00003020% 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 +00003021% %
3022% %
3023% %
3024%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3025%
dirk8a5cf512014-07-28 20:16:27 +00003026% AccelerateResizeImage() is an OpenCL implementation of ResizeImage()
cristyf034abb2013-11-24 14:16:14 +00003027%
dirk8a5cf512014-07-28 20:16:27 +00003028% AccelerateResizeImage() scales an image to the desired dimensions, using the given
3029% filter (see AcquireFilterInfo()).
cristyf034abb2013-11-24 14:16:14 +00003030%
dirk8a5cf512014-07-28 20:16:27 +00003031% If an undefined filter is given the filter defaults to Mitchell for a
3032% colormapped image, a image with a matte channel, or if the image is
3033% enlarged. Otherwise the filter defaults to a Lanczos.
3034%
3035% AccelerateResizeImage() was inspired by Paul Heckbert's "zoom" program.
3036%
3037% The format of the AccelerateResizeImage method is:
3038%
3039% Image *ResizeImage(Image *image,const size_t columns,
3040% const size_t rows, const ResizeFilter* filter,
3041% ExceptionInfo *exception)
cristyf034abb2013-11-24 14:16:14 +00003042%
3043% A description of each parameter follows:
3044%
3045% o image: the image.
3046%
dirk8a5cf512014-07-28 20:16:27 +00003047% o columns: the number of columns in the scaled image.
cristyf034abb2013-11-24 14:16:14 +00003048%
dirk8a5cf512014-07-28 20:16:27 +00003049% o rows: the number of rows in the scaled image.
cristyf034abb2013-11-24 14:16:14 +00003050%
dirk8a5cf512014-07-28 20:16:27 +00003051% o filter: Image filter to use.
cristyf034abb2013-11-24 14:16:14 +00003052%
3053% o exception: return any errors or warnings in this structure.
3054%
3055*/
3056
dirk832becc2014-08-04 19:44:34 +00003057static MagickBooleanType resizeHorizontalFilter(cl_mem image,
3058 const unsigned int imageColumns,const unsigned int imageRows,
3059 const unsigned int matte,cl_mem resizedImage,
3060 const unsigned int resizedColumns,const unsigned int resizedRows,
3061 const ResizeFilter *resizeFilter,cl_mem resizeFilterCubicCoefficients,
3062 const float xFactor,MagickCLEnv clEnv,cl_command_queue queue,
3063 ExceptionInfo *exception)
3064{
3065 cl_kernel
3066 horizontalKernel;
3067
3068 cl_int clStatus;
3069
3070 const unsigned int
3071 workgroupSize = 256;
3072
3073 float
3074 resizeFilterScale,
3075 resizeFilterSupport,
3076 resizeFilterWindowSupport,
3077 resizeFilterBlur,
3078 scale,
3079 support;
3080
3081 int
3082 cacheRangeStart,
3083 cacheRangeEnd,
3084 numCachedPixels,
3085 resizeFilterType,
3086 resizeWindowType;
3087
3088 MagickBooleanType
3089 status = MagickFalse;
3090
3091 size_t
3092 deviceLocalMemorySize,
3093 gammaAccumulatorLocalMemorySize,
3094 global_work_size[2],
3095 imageCacheLocalMemorySize,
3096 pixelAccumulatorLocalMemorySize,
3097 local_work_size[2],
3098 totalLocalMemorySize,
3099 weightAccumulatorLocalMemorySize;
3100
3101 unsigned int
3102 chunkSize,
3103 i,
3104 pixelPerWorkgroup;
3105
3106 horizontalKernel = NULL;
dirk8a5cf512014-07-28 20:16:27 +00003107 status = MagickFalse;
cristyf034abb2013-11-24 14:16:14 +00003108
3109 /*
3110 Apply filter to resize vertically from image to resize image.
3111 */
cristye85d0f72013-11-27 02:25:43 +00003112 scale=MAGICK_MAX(1.0/xFactor+MagickEpsilon,1.0);
cristyf034abb2013-11-24 14:16:14 +00003113 support=scale*GetResizeFilterSupport(resizeFilter);
3114 if (support < 0.5)
3115 {
3116 /*
3117 Support too small even for nearest neighbour: Reduce to point
3118 sampling.
3119 */
3120 support=(MagickRealType) 0.5;
3121 scale=1.0;
3122 }
3123 scale=PerceptibleReciprocal(scale);
3124
3125 if (resizedColumns < workgroupSize)
3126 {
3127 chunkSize = 32;
3128 pixelPerWorkgroup = 32;
3129 }
3130 else
3131 {
3132 chunkSize = workgroupSize;
3133 pixelPerWorkgroup = workgroupSize;
3134 }
3135
3136 /* get the local memory size supported by the device */
3137 deviceLocalMemorySize = GetOpenCLDeviceLocalMemorySize(clEnv);
3138
dirke3c5f892013-12-10 06:04:40 +00003139DisableMSCWarning(4127)
cristyf034abb2013-11-24 14:16:14 +00003140 while(1)
dirke3c5f892013-12-10 06:04:40 +00003141RestoreMSCWarning
cristyf034abb2013-11-24 14:16:14 +00003142 {
3143 /* calculate the local memory size needed per workgroup */
3144 cacheRangeStart = (int) (((0 + 0.5)/xFactor+MagickEpsilon)-support+0.5);
3145 cacheRangeEnd = (int) ((((pixelPerWorkgroup-1) + 0.5)/xFactor+MagickEpsilon)+support+0.5);
3146 numCachedPixels = cacheRangeEnd - cacheRangeStart + 1;
3147 imageCacheLocalMemorySize = numCachedPixels * sizeof(CLPixelPacket);
3148 totalLocalMemorySize = imageCacheLocalMemorySize;
3149
3150 /* local size for the pixel accumulator */
3151 pixelAccumulatorLocalMemorySize = chunkSize * sizeof(cl_float4);
3152 totalLocalMemorySize+=pixelAccumulatorLocalMemorySize;
3153
3154 /* local memory size for the weight accumulator */
3155 weightAccumulatorLocalMemorySize = chunkSize * sizeof(float);
3156 totalLocalMemorySize+=weightAccumulatorLocalMemorySize;
3157
3158 /* local memory size for the gamma accumulator */
3159 if (matte == 0)
3160 gammaAccumulatorLocalMemorySize = sizeof(float);
3161 else
3162 gammaAccumulatorLocalMemorySize = chunkSize * sizeof(float);
3163 totalLocalMemorySize+=gammaAccumulatorLocalMemorySize;
3164
3165 if (totalLocalMemorySize <= deviceLocalMemorySize)
3166 break;
3167 else
3168 {
3169 pixelPerWorkgroup = pixelPerWorkgroup/2;
3170 chunkSize = chunkSize/2;
3171 if (pixelPerWorkgroup == 0
3172 || chunkSize == 0)
3173 {
3174 /* quit, fallback to CPU */
3175 goto cleanup;
3176 }
3177 }
3178 }
3179
3180 resizeFilterType = (int)GetResizeFilterWeightingType(resizeFilter);
3181 resizeWindowType = (int)GetResizeFilterWindowWeightingType(resizeFilter);
3182
3183
3184 if (resizeFilterType == SincFastWeightingFunction
3185 && resizeWindowType == SincFastWeightingFunction)
3186 {
3187 horizontalKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "ResizeHorizontalFilterSinc");
3188 }
3189 else
3190 {
3191 horizontalKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "ResizeHorizontalFilter");
3192 }
3193 if (horizontalKernel == NULL)
3194 {
cristya22457d2013-12-07 14:03:06 +00003195 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003196 goto cleanup;
3197 }
3198
3199 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00003200 clStatus = clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(cl_mem), (void*)&image);
3201 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&imageColumns);
3202 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&imageRows);
cristy0c832c62014-03-07 22:21:04 +00003203 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&matte);
3204 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&xFactor);
3205 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(cl_mem), (void*)&resizedImage);
cristyf034abb2013-11-24 14:16:14 +00003206
cristy0c832c62014-03-07 22:21:04 +00003207 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&resizedColumns);
3208 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&resizedRows);
cristyf034abb2013-11-24 14:16:14 +00003209
cristy0c832c62014-03-07 22:21:04 +00003210 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(int), (void*)&resizeFilterType);
3211 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(int), (void*)&resizeWindowType);
3212 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(cl_mem), (void*)&resizeFilterCubicCoefficients);
cristyf034abb2013-11-24 14:16:14 +00003213
3214 resizeFilterScale = (float) GetResizeFilterScale(resizeFilter);
cristy0c832c62014-03-07 22:21:04 +00003215 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterScale);
cristyf034abb2013-11-24 14:16:14 +00003216
3217 resizeFilterSupport = (float) GetResizeFilterSupport(resizeFilter);
cristy0c832c62014-03-07 22:21:04 +00003218 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterSupport);
cristyf034abb2013-11-24 14:16:14 +00003219
3220 resizeFilterWindowSupport = (float) GetResizeFilterWindowSupport(resizeFilter);
cristy0c832c62014-03-07 22:21:04 +00003221 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterWindowSupport);
cristyf034abb2013-11-24 14:16:14 +00003222
3223 resizeFilterBlur = (float) GetResizeFilterBlur(resizeFilter);
cristy0c832c62014-03-07 22:21:04 +00003224 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterBlur);
cristyf034abb2013-11-24 14:16:14 +00003225
3226
cristy0c832c62014-03-07 22:21:04 +00003227 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, imageCacheLocalMemorySize, NULL);
3228 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(int), &numCachedPixels);
3229 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), &pixelPerWorkgroup);
3230 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), &chunkSize);
cristyf034abb2013-11-24 14:16:14 +00003231
3232
cristy0c832c62014-03-07 22:21:04 +00003233 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, pixelAccumulatorLocalMemorySize, NULL);
3234 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, weightAccumulatorLocalMemorySize, NULL);
3235 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, gammaAccumulatorLocalMemorySize, NULL);
cristyf034abb2013-11-24 14:16:14 +00003236
3237 if (clStatus != CL_SUCCESS)
3238 {
cristy0c832c62014-03-07 22:21:04 +00003239 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003240 goto cleanup;
3241 }
3242
3243 global_work_size[0] = (resizedColumns+pixelPerWorkgroup-1)/pixelPerWorkgroup*workgroupSize;
3244 global_work_size[1] = resizedRows;
3245
3246 local_work_size[0] = workgroupSize;
3247 local_work_size[1] = 1;
cristy0c832c62014-03-07 22:21:04 +00003248 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, horizontalKernel, 2, NULL, global_work_size, local_work_size, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00003249 if (clStatus != CL_SUCCESS)
3250 {
cristy0c832c62014-03-07 22:21:04 +00003251 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003252 goto cleanup;
3253 }
cristy0c832c62014-03-07 22:21:04 +00003254 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00003255 status = MagickTrue;
3256
3257
3258cleanup:
cristya22457d2013-12-07 14:03:06 +00003259 OpenCLLogException(__FUNCTION__,__LINE__,exception);
3260
cristyf034abb2013-11-24 14:16:14 +00003261 if (horizontalKernel != NULL) RelinquishOpenCLKernel(clEnv, horizontalKernel);
3262
dirk832becc2014-08-04 19:44:34 +00003263 return(status);
3264}
3265
3266static MagickBooleanType resizeVerticalFilter(cl_mem image,
3267 const unsigned int imageColumns,const unsigned int imageRows,
3268 const unsigned int matte,cl_mem resizedImage,
3269 const unsigned int resizedColumns,const unsigned int resizedRows,
3270 const ResizeFilter *resizeFilter,cl_mem resizeFilterCubicCoefficients,
3271 const float yFactor,MagickCLEnv clEnv,cl_command_queue queue,
3272 ExceptionInfo *exception)
3273{
3274 cl_kernel
3275 horizontalKernel;
3276
3277 cl_int clStatus;
3278
3279 const unsigned int
3280 workgroupSize = 256;
3281
3282 float
3283 resizeFilterScale,
3284 resizeFilterSupport,
3285 resizeFilterWindowSupport,
3286 resizeFilterBlur,
3287 scale,
3288 support;
3289
3290 int
3291 cacheRangeStart,
3292 cacheRangeEnd,
3293 numCachedPixels,
3294 resizeFilterType,
3295 resizeWindowType;
3296
3297 MagickBooleanType
3298 status = MagickFalse;
3299
3300 size_t
3301 deviceLocalMemorySize,
3302 gammaAccumulatorLocalMemorySize,
3303 global_work_size[2],
3304 imageCacheLocalMemorySize,
3305 pixelAccumulatorLocalMemorySize,
3306 local_work_size[2],
3307 totalLocalMemorySize,
3308 weightAccumulatorLocalMemorySize;
3309
3310 unsigned int
3311 chunkSize,
3312 i,
3313 pixelPerWorkgroup;
3314
3315 horizontalKernel = NULL;
dirk8a5cf512014-07-28 20:16:27 +00003316 status = MagickFalse;
cristyf034abb2013-11-24 14:16:14 +00003317
3318 /*
3319 Apply filter to resize vertically from image to resize image.
3320 */
cristye85d0f72013-11-27 02:25:43 +00003321 scale=MAGICK_MAX(1.0/yFactor+MagickEpsilon,1.0);
cristyf034abb2013-11-24 14:16:14 +00003322 support=scale*GetResizeFilterSupport(resizeFilter);
3323 if (support < 0.5)
3324 {
3325 /*
3326 Support too small even for nearest neighbour: Reduce to point
3327 sampling.
3328 */
3329 support=(MagickRealType) 0.5;
3330 scale=1.0;
3331 }
3332 scale=PerceptibleReciprocal(scale);
3333
3334 if (resizedRows < workgroupSize)
3335 {
3336 chunkSize = 32;
3337 pixelPerWorkgroup = 32;
3338 }
3339 else
3340 {
3341 chunkSize = workgroupSize;
3342 pixelPerWorkgroup = workgroupSize;
3343 }
3344
3345 /* get the local memory size supported by the device */
3346 deviceLocalMemorySize = GetOpenCLDeviceLocalMemorySize(clEnv);
3347
dirke3c5f892013-12-10 06:04:40 +00003348DisableMSCWarning(4127)
cristyf034abb2013-11-24 14:16:14 +00003349 while(1)
dirke3c5f892013-12-10 06:04:40 +00003350RestoreMSCWarning
cristyf034abb2013-11-24 14:16:14 +00003351 {
3352 /* calculate the local memory size needed per workgroup */
3353 cacheRangeStart = (int) (((0 + 0.5)/yFactor+MagickEpsilon)-support+0.5);
3354 cacheRangeEnd = (int) ((((pixelPerWorkgroup-1) + 0.5)/yFactor+MagickEpsilon)+support+0.5);
3355 numCachedPixels = cacheRangeEnd - cacheRangeStart + 1;
3356 imageCacheLocalMemorySize = numCachedPixels * sizeof(CLPixelPacket);
3357 totalLocalMemorySize = imageCacheLocalMemorySize;
3358
3359 /* local size for the pixel accumulator */
3360 pixelAccumulatorLocalMemorySize = chunkSize * sizeof(cl_float4);
3361 totalLocalMemorySize+=pixelAccumulatorLocalMemorySize;
3362
3363 /* local memory size for the weight accumulator */
3364 weightAccumulatorLocalMemorySize = chunkSize * sizeof(float);
3365 totalLocalMemorySize+=weightAccumulatorLocalMemorySize;
3366
3367 /* local memory size for the gamma accumulator */
3368 if (matte == 0)
3369 gammaAccumulatorLocalMemorySize = sizeof(float);
3370 else
3371 gammaAccumulatorLocalMemorySize = chunkSize * sizeof(float);
3372 totalLocalMemorySize+=gammaAccumulatorLocalMemorySize;
3373
3374 if (totalLocalMemorySize <= deviceLocalMemorySize)
3375 break;
3376 else
3377 {
3378 pixelPerWorkgroup = pixelPerWorkgroup/2;
3379 chunkSize = chunkSize/2;
3380 if (pixelPerWorkgroup == 0
3381 || chunkSize == 0)
3382 {
3383 /* quit, fallback to CPU */
3384 goto cleanup;
3385 }
3386 }
3387 }
3388
3389 resizeFilterType = (int)GetResizeFilterWeightingType(resizeFilter);
3390 resizeWindowType = (int)GetResizeFilterWindowWeightingType(resizeFilter);
3391
3392 if (resizeFilterType == SincFastWeightingFunction
3393 && resizeWindowType == SincFastWeightingFunction)
3394 horizontalKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "ResizeVerticalFilterSinc");
3395 else
3396 horizontalKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "ResizeVerticalFilter");
3397
3398 if (horizontalKernel == NULL)
3399 {
cristya22457d2013-12-07 14:03:06 +00003400 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003401 goto cleanup;
3402 }
3403
3404 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00003405 clStatus = clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(cl_mem), (void*)&image);
3406 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&imageColumns);
3407 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&imageRows);
cristy0c832c62014-03-07 22:21:04 +00003408 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&matte);
3409 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&yFactor);
3410 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(cl_mem), (void*)&resizedImage);
cristyf034abb2013-11-24 14:16:14 +00003411
cristy0c832c62014-03-07 22:21:04 +00003412 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&resizedColumns);
3413 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), (void*)&resizedRows);
cristyf034abb2013-11-24 14:16:14 +00003414
cristy0c832c62014-03-07 22:21:04 +00003415 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(int), (void*)&resizeFilterType);
3416 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(int), (void*)&resizeWindowType);
3417 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(cl_mem), (void*)&resizeFilterCubicCoefficients);
cristyf034abb2013-11-24 14:16:14 +00003418
3419 resizeFilterScale = (float) GetResizeFilterScale(resizeFilter);
cristy0c832c62014-03-07 22:21:04 +00003420 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterScale);
cristyf034abb2013-11-24 14:16:14 +00003421
3422 resizeFilterSupport = (float) GetResizeFilterSupport(resizeFilter);
cristy0c832c62014-03-07 22:21:04 +00003423 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterSupport);
cristyf034abb2013-11-24 14:16:14 +00003424
3425 resizeFilterWindowSupport = (float) GetResizeFilterWindowSupport(resizeFilter);
cristy0c832c62014-03-07 22:21:04 +00003426 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterWindowSupport);
cristyf034abb2013-11-24 14:16:14 +00003427
3428 resizeFilterBlur = (float) GetResizeFilterBlur(resizeFilter);
cristy0c832c62014-03-07 22:21:04 +00003429 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(float), (void*)&resizeFilterBlur);
cristyf034abb2013-11-24 14:16:14 +00003430
3431
cristy0c832c62014-03-07 22:21:04 +00003432 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, imageCacheLocalMemorySize, NULL);
3433 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(int), &numCachedPixels);
3434 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), &pixelPerWorkgroup);
3435 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, sizeof(unsigned int), &chunkSize);
cristyf034abb2013-11-24 14:16:14 +00003436
3437
cristy0c832c62014-03-07 22:21:04 +00003438 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, pixelAccumulatorLocalMemorySize, NULL);
3439 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, weightAccumulatorLocalMemorySize, NULL);
3440 clStatus |= clEnv->library->clSetKernelArg(horizontalKernel, i++, gammaAccumulatorLocalMemorySize, NULL);
cristyf034abb2013-11-24 14:16:14 +00003441
3442 if (clStatus != CL_SUCCESS)
3443 {
cristy0c832c62014-03-07 22:21:04 +00003444 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003445 goto cleanup;
3446 }
3447
3448 global_work_size[0] = resizedColumns;
3449 global_work_size[1] = (resizedRows+pixelPerWorkgroup-1)/pixelPerWorkgroup*workgroupSize;
3450
3451 local_work_size[0] = 1;
3452 local_work_size[1] = workgroupSize;
cristy0c832c62014-03-07 22:21:04 +00003453 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, horizontalKernel, 2, NULL, global_work_size, local_work_size, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00003454 if (clStatus != CL_SUCCESS)
3455 {
cristy0c832c62014-03-07 22:21:04 +00003456 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003457 goto cleanup;
3458 }
cristy0c832c62014-03-07 22:21:04 +00003459 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00003460 status = MagickTrue;
3461
3462
3463cleanup:
cristya22457d2013-12-07 14:03:06 +00003464 OpenCLLogException(__FUNCTION__,__LINE__,exception);
3465
cristyf034abb2013-11-24 14:16:14 +00003466 if (horizontalKernel != NULL) RelinquishOpenCLKernel(clEnv, horizontalKernel);
3467
dirk8a5cf512014-07-28 20:16:27 +00003468 return(status);
cristyf034abb2013-11-24 14:16:14 +00003469}
3470
dirk832becc2014-08-04 19:44:34 +00003471static Image *ComputeResizeImage(const Image* image,
3472 const size_t resizedColumns,const size_t resizedRows,
3473 const ResizeFilter *resizeFilter,ExceptionInfo *exception)
3474{
3475 CacheView
3476 *filteredImage_view,
3477 *image_view;
3478
3479 cl_command_queue
3480 queue;
3481
3482 cl_int
3483 clStatus;
3484
3485 cl_context
3486 context;
3487
3488 cl_mem
3489 cubicCoefficientsBuffer,
3490 filteredImageBuffer,
3491 imageBuffer,
3492 tempImageBuffer;
3493
3494 cl_mem_flags
3495 mem_flags;
3496
3497 const double
3498 *resizeFilterCoefficient;
3499
3500 const void
3501 *inputPixels;
3502
3503 float
3504 *mappedCoefficientBuffer,
3505 xFactor,
3506 yFactor;
3507
3508 MagickBooleanType
3509 outputReady,
3510 status;
3511
3512 MagickCLEnv
3513 clEnv;
3514
3515 MagickSizeType
3516 length;
3517
3518 Image
3519 *filteredImage;
3520
3521 unsigned int
3522 i;
3523
3524 void
3525 *filteredPixels,
3526 *hostPtr;
3527
3528 outputReady = MagickFalse;
3529 filteredImage = NULL;
3530 filteredImage_view = NULL;
3531 clEnv = NULL;
3532 context = NULL;
3533 imageBuffer = NULL;
3534 tempImageBuffer = NULL;
3535 filteredImageBuffer = NULL;
3536 cubicCoefficientsBuffer = NULL;
3537 queue = NULL;
3538
3539 clEnv = GetDefaultOpenCLEnv();
3540 context = GetOpenCLContext(clEnv);
3541
3542 /* Create and initialize OpenCL buffers. */
3543 image_view=AcquireVirtualCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00003544 inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00003545 if (inputPixels == (const void *) NULL)
3546 {
dirk8a5cf512014-07-28 20:16:27 +00003547 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristyf034abb2013-11-24 14:16:14 +00003548 goto cleanup;
3549 }
3550
3551 /* If the host pointer is aligned to the size of CLPixelPacket,
3552 then use the host buffer directly from the GPU; otherwise,
3553 create a buffer on the GPU and copy the data over */
3554 if (ALIGNED(inputPixels,CLPixelPacket))
3555 {
3556 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
3557 }
3558 else
3559 {
3560 mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
3561 }
3562 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00003563 length = image->columns * image->rows;
3564 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00003565 if (clStatus != CL_SUCCESS)
3566 {
cristy0c832c62014-03-07 22:21:04 +00003567 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00003568 goto cleanup;
3569 }
3570
cristy0c832c62014-03-07 22:21:04 +00003571 cubicCoefficientsBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_ONLY, 7 * sizeof(float), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00003572 if (clStatus != CL_SUCCESS)
3573 {
cristy0c832c62014-03-07 22:21:04 +00003574 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00003575 goto cleanup;
3576 }
3577 queue = AcquireOpenCLCommandQueue(clEnv);
cristy0c832c62014-03-07 22:21:04 +00003578 mappedCoefficientBuffer = (float*)clEnv->library->clEnqueueMapBuffer(queue, cubicCoefficientsBuffer, CL_TRUE, CL_MAP_WRITE, 0, 7 * sizeof(float)
cristyf034abb2013-11-24 14:16:14 +00003579 , 0, NULL, NULL, &clStatus);
3580 if (clStatus != CL_SUCCESS)
3581 {
cristy0c832c62014-03-07 22:21:04 +00003582 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueMapBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00003583 goto cleanup;
3584 }
3585 resizeFilterCoefficient = GetResizeFilterCoefficient(resizeFilter);
3586 for (i = 0; i < 7; i++)
3587 {
3588 mappedCoefficientBuffer[i] = (float) resizeFilterCoefficient[i];
3589 }
cristy0c832c62014-03-07 22:21:04 +00003590 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, cubicCoefficientsBuffer, mappedCoefficientBuffer, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00003591 if (clStatus != CL_SUCCESS)
3592 {
cristy0c832c62014-03-07 22:21:04 +00003593 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003594 goto cleanup;
3595 }
3596
dirk8a5cf512014-07-28 20:16:27 +00003597 filteredImage = CloneImage(image,resizedColumns,resizedRows,MagickTrue,exception);
cristyf034abb2013-11-24 14:16:14 +00003598 if (filteredImage == NULL)
3599 goto cleanup;
3600
dirk8a5cf512014-07-28 20:16:27 +00003601 if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
cristyf034abb2013-11-24 14:16:14 +00003602 {
cristya22457d2013-12-07 14:03:06 +00003603 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003604 goto cleanup;
3605 }
dirk832becc2014-08-04 19:44:34 +00003606 filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
dirk8a5cf512014-07-28 20:16:27 +00003607 filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00003608 if (filteredPixels == (void *) NULL)
3609 {
cristya22457d2013-12-07 14:03:06 +00003610 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
cristyf034abb2013-11-24 14:16:14 +00003611 goto cleanup;
3612 }
3613
3614 if (ALIGNED(filteredPixels,CLPixelPacket))
3615 {
3616 mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
3617 hostPtr = filteredPixels;
3618 }
3619 else
3620 {
3621 mem_flags = CL_MEM_WRITE_ONLY;
3622 hostPtr = NULL;
3623 }
3624
3625 /* create a CL buffer from image pixel buffer */
3626 length = filteredImage->columns * filteredImage->rows;
cristy0c832c62014-03-07 22:21:04 +00003627 filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00003628 if (clStatus != CL_SUCCESS)
3629 {
cristy0c832c62014-03-07 22:21:04 +00003630 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00003631 goto cleanup;
3632 }
3633
dirk8a5cf512014-07-28 20:16:27 +00003634 xFactor=(float) resizedColumns/(float) image->columns;
3635 yFactor=(float) resizedRows/(float) image->rows;
cristyf034abb2013-11-24 14:16:14 +00003636 if (xFactor > yFactor)
3637 {
3638
dirk8a5cf512014-07-28 20:16:27 +00003639 length = resizedColumns*image->rows;
cristy0c832c62014-03-07 22:21:04 +00003640 tempImageBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_WRITE, length*sizeof(CLPixelPacket), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00003641 if (clStatus != CL_SUCCESS)
3642 {
cristy0c832c62014-03-07 22:21:04 +00003643 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00003644 goto cleanup;
3645 }
3646
dirk8a5cf512014-07-28 20:16:27 +00003647 status = resizeHorizontalFilter(imageBuffer, image->columns, image->rows, (image->alpha_trait==BlendPixelTrait)?1:0
3648 , tempImageBuffer, resizedColumns, image->rows
cristyf034abb2013-11-24 14:16:14 +00003649 , resizeFilter, cubicCoefficientsBuffer
3650 , xFactor, clEnv, queue, exception);
3651 if (status != MagickTrue)
3652 goto cleanup;
3653
dirk8a5cf512014-07-28 20:16:27 +00003654 status = resizeVerticalFilter(tempImageBuffer, resizedColumns, image->rows, (image->alpha_trait==BlendPixelTrait)?1:0
cristyf034abb2013-11-24 14:16:14 +00003655 , filteredImageBuffer, resizedColumns, resizedRows
3656 , resizeFilter, cubicCoefficientsBuffer
3657 , yFactor, clEnv, queue, exception);
3658 if (status != MagickTrue)
3659 goto cleanup;
3660 }
3661 else
3662 {
dirk8a5cf512014-07-28 20:16:27 +00003663 length = image->columns*resizedRows;
cristy0c832c62014-03-07 22:21:04 +00003664 tempImageBuffer = clEnv->library->clCreateBuffer(context, CL_MEM_READ_WRITE, length*sizeof(CLPixelPacket), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00003665 if (clStatus != CL_SUCCESS)
3666 {
cristy0c832c62014-03-07 22:21:04 +00003667 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00003668 goto cleanup;
3669 }
3670
dirk8a5cf512014-07-28 20:16:27 +00003671 status = resizeVerticalFilter(imageBuffer, image->columns, image->rows, (image->alpha_trait==BlendPixelTrait)?1:0
3672 , tempImageBuffer, image->columns, resizedRows
cristyf034abb2013-11-24 14:16:14 +00003673 , resizeFilter, cubicCoefficientsBuffer
3674 , yFactor, clEnv, queue, exception);
3675 if (status != MagickTrue)
3676 goto cleanup;
3677
dirk8a5cf512014-07-28 20:16:27 +00003678 status = resizeHorizontalFilter(tempImageBuffer, image->columns, resizedRows, (image->alpha_trait==BlendPixelTrait)?1:0
cristyf034abb2013-11-24 14:16:14 +00003679 , filteredImageBuffer, resizedColumns, resizedRows
3680 , resizeFilter, cubicCoefficientsBuffer
3681 , xFactor, clEnv, queue, exception);
3682 if (status != MagickTrue)
3683 goto cleanup;
3684 }
3685 length = resizedColumns*resizedRows;
3686 if (ALIGNED(filteredPixels,CLPixelPacket))
3687 {
cristy0c832c62014-03-07 22:21:04 +00003688 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 +00003689 }
3690 else
3691 {
cristy0c832c62014-03-07 22:21:04 +00003692 clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00003693 }
3694 if (clStatus != CL_SUCCESS)
3695 {
cristya22457d2013-12-07 14:03:06 +00003696 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003697 goto cleanup;
3698 }
dirk832becc2014-08-04 19:44:34 +00003699 outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
3700
3701cleanup:
3702 OpenCLLogException(__FUNCTION__,__LINE__,exception);
3703
3704 image_view=DestroyCacheView(image_view);
3705 if (filteredImage_view != NULL)
dirk8a5cf512014-07-28 20:16:27 +00003706 filteredImage_view=DestroyCacheView(filteredImage_view);
cristyf034abb2013-11-24 14:16:14 +00003707
dirk8a5cf512014-07-28 20:16:27 +00003708 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00003709 if (tempImageBuffer!=NULL) clEnv->library->clReleaseMemObject(tempImageBuffer);
3710 if (filteredImageBuffer!=NULL) clEnv->library->clReleaseMemObject(filteredImageBuffer);
3711 if (cubicCoefficientsBuffer!=NULL) clEnv->library->clReleaseMemObject(cubicCoefficientsBuffer);
cristyf034abb2013-11-24 14:16:14 +00003712 if (queue != NULL) RelinquishOpenCLCommandQueue(clEnv, queue);
dirk8a5cf512014-07-28 20:16:27 +00003713 if (outputReady == MagickFalse && filteredImage != NULL)
3714 filteredImage=DestroyImage(filteredImage);
3715 return(filteredImage);
cristyf034abb2013-11-24 14:16:14 +00003716}
3717
dirk832becc2014-08-04 19:44:34 +00003718const ResizeWeightingFunctionType supportedResizeWeighting[] =
3719{
3720 BoxWeightingFunction,
3721 TriangleWeightingFunction,
3722 HanningWeightingFunction,
3723 HammingWeightingFunction,
3724 BlackmanWeightingFunction,
3725 CubicBCWeightingFunction,
3726 SincWeightingFunction,
3727 SincFastWeightingFunction,
3728 LastWeightingFunction
cristyf034abb2013-11-24 14:16:14 +00003729};
3730
dirk832becc2014-08-04 19:44:34 +00003731static MagickBooleanType gpuSupportedResizeWeighting(
3732 ResizeWeightingFunctionType f)
3733{
3734 unsigned int
3735 i;
3736
3737 for (i = 0; ;i++)
3738 {
3739 if (supportedResizeWeighting[i] == LastWeightingFunction)
3740 break;
3741 if (supportedResizeWeighting[i] == f)
3742 return(MagickTrue);
3743 }
3744 return(MagickFalse);
3745}
3746
3747MagickExport Image *AccelerateResizeImage(const Image *image,
3748 const size_t resizedColumns,const size_t resizedRows,
3749 const ResizeFilter *resizeFilter,ExceptionInfo *exception)
3750{
3751 Image
3752 *filteredImage;
3753
3754 assert(image != NULL);
3755 assert(exception != (ExceptionInfo *) NULL);
3756
3757 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
3758 (checkAccelerateCondition(image, AllChannels) == MagickFalse))
3759 return NULL;
3760
3761 if (gpuSupportedResizeWeighting(GetResizeFilterWeightingType(resizeFilter)) == MagickFalse ||
3762 gpuSupportedResizeWeighting(GetResizeFilterWindowWeightingType(resizeFilter)) == MagickFalse)
3763 return NULL;
3764
3765 filteredImage=ComputeResizeImage(image,resizedColumns,resizedRows,resizeFilter,exception);
3766 return(filteredImage);
cristyf034abb2013-11-24 14:16:14 +00003767}
3768
3769/*
3770%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3771% %
3772% %
3773% %
3774% C o n t r a s t I m a g e w i t h O p e n C L %
3775% %
3776% %
3777% %
3778%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3779%
3780% ContrastImage() enhances the intensity differences between the lighter and
3781% darker elements of the image. Set sharpen to a MagickTrue to increase the
3782% image contrast otherwise the contrast is reduced.
3783%
3784% The format of the ContrastImage method is:
3785%
3786% MagickBooleanType ContrastImage(Image *image,
3787% const MagickBooleanType sharpen)
3788%
3789% A description of each parameter follows:
3790%
3791% o image: the image.
3792%
3793% o sharpen: Increase or decrease image contrast.
3794%
3795*/
3796
dirk832becc2014-08-04 19:44:34 +00003797static MagickBooleanType ComputeContrastImage(Image *image,
3798 const MagickBooleanType sharpen,ExceptionInfo *exception)
3799{
3800 CacheView
3801 *image_view;
3802
3803 cl_command_queue
3804 queue;
3805
3806 cl_context
3807 context;
3808
3809 cl_int
3810 clStatus;
3811
3812 cl_kernel
3813 filterKernel;
3814
3815 cl_mem
3816 imageBuffer;
3817
3818 cl_mem_flags
3819 mem_flags;
3820
3821 MagickBooleanType
3822 outputReady;
3823
3824 MagickCLEnv
3825 clEnv;
3826
3827 MagickSizeType
3828 length;
3829
3830 size_t
3831 global_work_size[2];
3832
3833 unsigned int
3834 i,
3835 uSharpen;
3836
3837 void
3838 *inputPixels;
3839
3840 outputReady = MagickFalse;
3841 clEnv = NULL;
3842 inputPixels = NULL;
3843 context = NULL;
3844 imageBuffer = NULL;
3845 filterKernel = NULL;
3846 queue = NULL;
3847
3848 clEnv = GetDefaultOpenCLEnv();
3849 context = GetOpenCLContext(clEnv);
3850
3851 /* Create and initialize OpenCL buffers. */
3852 image_view=AcquireAuthenticCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00003853 inputPixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00003854 if (inputPixels == (void *) NULL)
cristyd43a46b2010-01-21 02:13:41 +00003855 {
dirk8a5cf512014-07-28 20:16:27 +00003856 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristyf034abb2013-11-24 14:16:14 +00003857 goto cleanup;
cristyd43a46b2010-01-21 02:13:41 +00003858 }
cristyf034abb2013-11-24 14:16:14 +00003859
3860 /* If the host pointer is aligned to the size of CLPixelPacket,
dirk8a5cf512014-07-28 20:16:27 +00003861 then use the host buffer directly from the GPU; otherwise,
3862 create a buffer on the GPU and copy the data over */
cristyf034abb2013-11-24 14:16:14 +00003863 if (ALIGNED(inputPixels,CLPixelPacket))
3864 {
3865 mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
3866 }
3867 else
3868 {
3869 mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
3870 }
3871 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00003872 length = image->columns * image->rows;
3873 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00003874 if (clStatus != CL_SUCCESS)
3875 {
cristy0c832c62014-03-07 22:21:04 +00003876 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00003877 goto cleanup;
3878 }
dirk8a5cf512014-07-28 20:16:27 +00003879
3880 filterKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "Contrast");
3881 if (filterKernel == NULL)
cristyf034abb2013-11-24 14:16:14 +00003882 {
cristya22457d2013-12-07 14:03:06 +00003883 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003884 goto cleanup;
3885 }
3886
cristyf034abb2013-11-24 14:16:14 +00003887 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00003888 clStatus=clEnv->library->clSetKernelArg(filterKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
3889
3890 uSharpen = (sharpen == MagickFalse)?0:1;
3891 clStatus|=clEnv->library->clSetKernelArg(filterKernel,i++,sizeof(cl_uint),&uSharpen);
cristyf034abb2013-11-24 14:16:14 +00003892 if (clStatus != CL_SUCCESS)
3893 {
cristy0c832c62014-03-07 22:21:04 +00003894 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003895 goto cleanup;
3896 }
3897
dirk8a5cf512014-07-28 20:16:27 +00003898 global_work_size[0] = image->columns;
3899 global_work_size[1] = image->rows;
3900 /* launch the kernel */
3901 queue = AcquireOpenCLCommandQueue(clEnv);
3902 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, filterKernel, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
3903 if (clStatus != CL_SUCCESS)
cristyf034abb2013-11-24 14:16:14 +00003904 {
dirk8a5cf512014-07-28 20:16:27 +00003905 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
3906 goto cleanup;
cristyf034abb2013-11-24 14:16:14 +00003907 }
dirk8a5cf512014-07-28 20:16:27 +00003908 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00003909
3910 if (ALIGNED(inputPixels,CLPixelPacket))
3911 {
dirk8a5cf512014-07-28 20:16:27 +00003912 length = image->columns * image->rows;
3913 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 +00003914 }
3915 else
3916 {
dirk8a5cf512014-07-28 20:16:27 +00003917 length = image->columns * image->rows;
3918 clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), inputPixels, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00003919 }
3920 if (clStatus != CL_SUCCESS)
3921 {
cristya22457d2013-12-07 14:03:06 +00003922 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00003923 goto cleanup;
3924 }
dirk832becc2014-08-04 19:44:34 +00003925 outputReady=SyncCacheViewAuthenticPixels(image_view,exception);
3926
3927cleanup:
3928 OpenCLLogException(__FUNCTION__,__LINE__,exception);
3929
dirk8a5cf512014-07-28 20:16:27 +00003930 image_view=DestroyCacheView(image_view);
cristyf034abb2013-11-24 14:16:14 +00003931
dirk8a5cf512014-07-28 20:16:27 +00003932 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
3933 if (filterKernel!=NULL) RelinquishOpenCLKernel(clEnv, filterKernel);
3934 if (queue != NULL) RelinquishOpenCLCommandQueue(clEnv, queue);
3935 return(outputReady);
dirk832becc2014-08-04 19:44:34 +00003936}
3937
3938MagickExport MagickBooleanType AccelerateContrastImage(Image *image,
3939 const MagickBooleanType sharpen,ExceptionInfo *exception)
3940{
3941 MagickBooleanType
3942 status;
3943
3944 assert(image != NULL);
3945 assert(exception != (ExceptionInfo *) NULL);
3946
3947 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
3948 (checkAccelerateCondition(image, AllChannels) == MagickFalse))
3949 return(MagickFalse);
3950
3951 status = ComputeContrastImage(image,sharpen,exception);
3952 return(status);
cristy3f6d1482010-01-20 21:01:21 +00003953}
cristyf034abb2013-11-24 14:16:14 +00003954
3955/*
3956%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3957% %
3958% %
3959% %
3960% M o d u l a t e I m a g e w i t h O p e n C L %
3961% %
3962% %
3963% %
3964%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3965%
3966% ModulateImage() lets you control the brightness, saturation, and hue
3967% of an image. Modulate represents the brightness, saturation, and hue
3968% as one parameter (e.g. 90,150,100). If the image colorspace is HSL, the
3969% modulation is lightness, saturation, and hue. For HWB, use blackness,
3970% whiteness, and hue. And for HCL, use chrome, luma, and hue.
3971%
3972% The format of the ModulateImage method is:
3973%
3974% MagickBooleanType ModulateImage(Image *image,const char *modulate)
3975%
3976% A description of each parameter follows:
3977%
3978% o image: the image.
3979%
3980% o percent_*: Define the percent change in brightness, saturation, and
3981% hue.
3982%
3983*/
3984
dirk832becc2014-08-04 19:44:34 +00003985MagickBooleanType ComputeModulateImage(Image *image,
3986 double percent_brightness,double percent_hue,double percent_saturation,
3987 ColorspaceType colorspace,ExceptionInfo *exception)
3988{
3989 CacheView
3990 *image_view;
3991
3992 cl_float
3993 bright,
3994 hue,
3995 saturation;
3996
3997 cl_context
3998 context;
3999
4000 cl_command_queue
4001 queue;
4002
4003 cl_int
4004 color,
4005 clStatus;
4006
4007 cl_kernel
4008 modulateKernel;
4009
4010 cl_mem
4011 imageBuffer;
4012
4013 cl_mem_flags
4014 mem_flags;
4015
4016 MagickBooleanType
4017 outputReady;
4018
4019 MagickCLEnv
4020 clEnv;
4021
4022 MagickSizeType
4023 length;
4024
4025 register ssize_t
4026 i;
4027
4028 void
4029 *inputPixels;
4030
4031 inputPixels = NULL;
4032 imageBuffer = NULL;
4033 modulateKernel = NULL;
4034
4035 assert(image != (Image *) NULL);
4036 assert(image->signature == MagickSignature);
4037 if (image->debug != MagickFalse)
4038 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4039
4040 /*
4041 * initialize opencl env
4042 */
4043 clEnv = GetDefaultOpenCLEnv();
4044 context = GetOpenCLContext(clEnv);
4045 queue = AcquireOpenCLCommandQueue(clEnv);
4046
4047 outputReady = MagickFalse;
4048
4049 /* Create and initialize OpenCL buffers.
4050 inputPixels = AcquirePixelCachePixels(image, &length, exception);
4051 assume this will get a writable image
4052 */
4053 image_view=AcquireAuthenticCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00004054 inputPixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
cristy0c832c62014-03-07 22:21:04 +00004055 if (inputPixels == (void *) NULL)
4056 {
dirk8a5cf512014-07-28 20:16:27 +00004057 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristy0c832c62014-03-07 22:21:04 +00004058 goto cleanup;
4059 }
4060
4061 /* If the host pointer is aligned to the size of CLPixelPacket,
4062 then use the host buffer directly from the GPU; otherwise,
4063 create a buffer on the GPU and copy the data over
4064 */
4065 if (ALIGNED(inputPixels,CLPixelPacket))
4066 {
4067 mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
4068 }
4069 else
4070 {
4071 mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
4072 }
4073 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00004074 length = image->columns * image->rows;
4075 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristy0c832c62014-03-07 22:21:04 +00004076 if (clStatus != CL_SUCCESS)
4077 {
4078 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
4079 goto cleanup;
4080 }
4081
dirk8a5cf512014-07-28 20:16:27 +00004082 modulateKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "Modulate");
4083 if (modulateKernel == NULL)
cristy0c832c62014-03-07 22:21:04 +00004084 {
4085 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
4086 goto cleanup;
4087 }
4088
dirk8a5cf512014-07-28 20:16:27 +00004089 bright=percent_brightness;
4090 hue=percent_hue;
4091 saturation=percent_saturation;
4092 color=colorspace;
4093
cristy0c832c62014-03-07 22:21:04 +00004094 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00004095 clStatus=clEnv->library->clSetKernelArg(modulateKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
4096 clStatus|=clEnv->library->clSetKernelArg(modulateKernel,i++,sizeof(cl_float),&bright);
4097 clStatus|=clEnv->library->clSetKernelArg(modulateKernel,i++,sizeof(cl_float),&hue);
4098 clStatus|=clEnv->library->clSetKernelArg(modulateKernel,i++,sizeof(cl_float),&saturation);
4099 clStatus|=clEnv->library->clSetKernelArg(modulateKernel,i++,sizeof(cl_float),&color);
cristy0c832c62014-03-07 22:21:04 +00004100 if (clStatus != CL_SUCCESS)
4101 {
4102 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
4103 printf("no kernel\n");
4104 goto cleanup;
4105 }
4106
4107 {
4108 size_t global_work_size[2];
dirk8a5cf512014-07-28 20:16:27 +00004109 global_work_size[0] = image->columns;
4110 global_work_size[1] = image->rows;
cristy0c832c62014-03-07 22:21:04 +00004111 /* launch the kernel */
dirk8a5cf512014-07-28 20:16:27 +00004112 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, modulateKernel, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristy0c832c62014-03-07 22:21:04 +00004113 if (clStatus != CL_SUCCESS)
4114 {
4115 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
4116 goto cleanup;
4117 }
4118 clEnv->library->clFlush(queue);
4119 }
4120
4121 if (ALIGNED(inputPixels,CLPixelPacket))
4122 {
dirk8a5cf512014-07-28 20:16:27 +00004123 length = image->columns * image->rows;
4124 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 +00004125 }
4126 else
4127 {
dirk8a5cf512014-07-28 20:16:27 +00004128 length = image->columns * image->rows;
4129 clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), inputPixels, 0, NULL, NULL);
cristy0c832c62014-03-07 22:21:04 +00004130 }
4131 if (clStatus != CL_SUCCESS)
4132 {
4133 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
4134 goto cleanup;
4135 }
4136
dirk832becc2014-08-04 19:44:34 +00004137 outputReady=SyncCacheViewAuthenticPixels(image_view,exception);
4138
4139cleanup:
4140 OpenCLLogException(__FUNCTION__,__LINE__,exception);
4141
dirk8a5cf512014-07-28 20:16:27 +00004142 image_view=DestroyCacheView(image_view);
cristy0c832c62014-03-07 22:21:04 +00004143
dirk8a5cf512014-07-28 20:16:27 +00004144 if (imageBuffer!=NULL)
4145 clEnv->library->clReleaseMemObject(imageBuffer);
4146 if (modulateKernel!=NULL)
4147 RelinquishOpenCLKernel(clEnv, modulateKernel);
cristy0c832c62014-03-07 22:21:04 +00004148 if (queue != NULL)
4149 RelinquishOpenCLCommandQueue(clEnv, queue);
4150
4151 return outputReady;
4152
4153}
4154
dirk832becc2014-08-04 19:44:34 +00004155MagickExport MagickBooleanType AccelerateModulateImage(Image *image,
4156 double percent_brightness,double percent_hue,double percent_saturation,
4157 ColorspaceType colorspace,ExceptionInfo *exception)
4158{
4159 MagickBooleanType
4160 status;
4161
4162 assert(image != NULL);
4163 assert(exception != (ExceptionInfo *) NULL);
4164
4165 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
4166 (checkAccelerateCondition(image, AllChannels) == MagickFalse))
4167 return(MagickFalse);
4168
4169 if ((colorspace != HSLColorspace && colorspace != UndefinedColorspace))
4170 return(MagickFalse);
4171
4172 status = ComputeModulateImage(image,percent_brightness, percent_hue, percent_saturation, colorspace, exception);
4173 return(status);
dirk8a5cf512014-07-28 20:16:27 +00004174}
cristy0c832c62014-03-07 22:21:04 +00004175
4176/*
4177%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4178% %
4179% %
4180% %
4181% N e g a t e I m a g e w i t h O p e n C L %
4182% %
4183% %
4184% %
4185%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4186%
4187%
4188% A description of each parameter follows:
4189%
4190% o image: the image.
4191%
4192% o channel: the channel.
4193%
4194% o grayscale: If MagickTrue, only negate grayscale pixels within the image.
4195%
4196*/
4197
dirk832becc2014-08-04 19:44:34 +00004198MagickBooleanType ComputeNegateImageChannel(Image *image,
4199 const ChannelType channel,const MagickBooleanType magick_unused(grayscale),
4200 ExceptionInfo* exception)
4201{
4202 CacheView
4203 *image_view;
4204
4205 cl_context
4206 context;
4207
4208 cl_command_queue
4209 queue;
4210
4211 cl_int
4212 clStatus;
4213
4214 cl_kernel
4215 negateKernel;
4216
4217 cl_mem
4218 imageBuffer;
4219
4220 cl_mem_flags
4221 mem_flags;
4222
4223 MagickBooleanType
4224 outputReady;
4225
4226 MagickCLEnv
4227 clEnv;
4228
4229 MagickSizeType
4230 length;
4231
4232 register ssize_t
4233 i;
4234
4235 void
4236 *inputPixels;
4237
4238 magick_unreferenced(grayscale);
4239
4240 inputPixels = NULL;
4241 imageBuffer = NULL;
4242 negateKernel = NULL;
4243
4244 assert(image != (Image *) NULL);
4245 assert(image->signature == MagickSignature);
4246 if (image->debug != MagickFalse)
4247 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4248
4249 /*
4250 * initialize opencl env
4251 */
4252 clEnv = GetDefaultOpenCLEnv();
4253 context = GetOpenCLContext(clEnv);
4254 queue = AcquireOpenCLCommandQueue(clEnv);
4255
4256 outputReady = MagickFalse;
4257
4258 /* Create and initialize OpenCL buffers.
4259 inputPixels = AcquirePixelCachePixels(image, &length, exception);
4260 assume this will get a writable image
4261 */
4262 image_view=AcquireAuthenticCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00004263 inputPixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
cristy0c832c62014-03-07 22:21:04 +00004264 if (inputPixels == (void *) NULL)
4265 {
dirk8a5cf512014-07-28 20:16:27 +00004266 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristy0c832c62014-03-07 22:21:04 +00004267 goto cleanup;
4268 }
4269
4270 /* If the host pointer is aligned to the size of CLPixelPacket,
4271 then use the host buffer directly from the GPU; otherwise,
4272 create a buffer on the GPU and copy the data over
4273 */
4274 if (ALIGNED(inputPixels,CLPixelPacket))
4275 {
4276 mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
4277 }
4278 else
4279 {
4280 mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
4281 }
4282 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00004283 length = image->columns * image->rows;
4284 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristy0c832c62014-03-07 22:21:04 +00004285 if (clStatus != CL_SUCCESS)
4286 {
4287 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
4288 goto cleanup;
4289 }
4290
dirk8a5cf512014-07-28 20:16:27 +00004291 negateKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "Negate");
4292 if (negateKernel == NULL)
cristy0c832c62014-03-07 22:21:04 +00004293 {
4294 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
4295 goto cleanup;
4296 }
4297
4298 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00004299 clStatus=clEnv->library->clSetKernelArg(negateKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
4300 clStatus=clEnv->library->clSetKernelArg(negateKernel,i++,sizeof(ChannelType),(void *)&channel);
cristy0c832c62014-03-07 22:21:04 +00004301 if (clStatus != CL_SUCCESS)
4302 {
4303 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
4304 printf("no kernel\n");
4305 goto cleanup;
4306 }
4307
4308 {
4309 size_t global_work_size[2];
dirk8a5cf512014-07-28 20:16:27 +00004310 global_work_size[0] = image->columns;
4311 global_work_size[1] = image->rows;
cristy0c832c62014-03-07 22:21:04 +00004312 /* launch the kernel */
dirk8a5cf512014-07-28 20:16:27 +00004313 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, negateKernel, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristy0c832c62014-03-07 22:21:04 +00004314 if (clStatus != CL_SUCCESS)
4315 {
4316 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
4317 goto cleanup;
4318 }
4319 clEnv->library->clFlush(queue);
4320 }
4321
4322 if (ALIGNED(inputPixels,CLPixelPacket))
4323 {
dirk8a5cf512014-07-28 20:16:27 +00004324 length = image->columns * image->rows;
4325 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 +00004326 }
4327 else
4328 {
dirk8a5cf512014-07-28 20:16:27 +00004329 length = image->columns * image->rows;
4330 clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), inputPixels, 0, NULL, NULL);
cristy0c832c62014-03-07 22:21:04 +00004331 }
4332 if (clStatus != CL_SUCCESS)
4333 {
4334 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
4335 goto cleanup;
4336 }
4337
dirk832becc2014-08-04 19:44:34 +00004338 outputReady=SyncCacheViewAuthenticPixels(image_view,exception);
4339
4340cleanup:
4341 OpenCLLogException(__FUNCTION__,__LINE__,exception);
4342
dirk8a5cf512014-07-28 20:16:27 +00004343 image_view=DestroyCacheView(image_view);
cristy0c832c62014-03-07 22:21:04 +00004344
dirk8a5cf512014-07-28 20:16:27 +00004345 if (imageBuffer!=NULL)
4346 clEnv->library->clReleaseMemObject(imageBuffer);
4347 if (negateKernel!=NULL)
4348 RelinquishOpenCLKernel(clEnv, negateKernel);
cristy0c832c62014-03-07 22:21:04 +00004349 if (queue != NULL)
4350 RelinquishOpenCLCommandQueue(clEnv, queue);
4351
dirk8a5cf512014-07-28 20:16:27 +00004352 return(outputReady);
dirk832becc2014-08-04 19:44:34 +00004353}
4354
4355MagickExport MagickBooleanType AccelerateNegateImageChannel(Image *image,
4356 const ChannelType channel,const MagickBooleanType grayscale,
4357 ExceptionInfo* exception)
4358{
4359 MagickBooleanType
4360 status;
4361
4362 assert(image != NULL);
4363 assert(exception != (ExceptionInfo *) NULL);
4364
4365 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
4366 (checkAccelerateCondition(image, channel) == MagickFalse))
4367 return(MagickFalse);
4368
4369 status=ComputeNegateImageChannel(image,channel,grayscale,exception);
4370 return(status);
cristy0c832c62014-03-07 22:21:04 +00004371}
dirk8a5cf512014-07-28 20:16:27 +00004372
cristy0c832c62014-03-07 22:21:04 +00004373/*
4374%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4375% %
4376% %
4377% %
4378% G r a y s c a l e I m a g e w i t h O p e n C L %
4379% %
4380% %
4381% %
4382%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4383%
4384% GrayscaleImage() converts the colors in the reference image to gray.
4385%
4386% The format of the GrayscaleImageChannel method is:
4387%
4388% MagickBooleanType GrayscaleImage(Image *image,
4389% const PixelIntensityMethod method)
4390%
4391% A description of each parameter follows:
4392%
4393% o image: the image.
4394%
4395% o channel: the channel.
4396%
4397*/
4398
dirk832becc2014-08-04 19:44:34 +00004399MagickBooleanType ComputeGrayscaleImage(Image *image,
4400 const PixelIntensityMethod method,ExceptionInfo *exception)
4401{
4402 CacheView
4403 *image_view;
4404
4405 cl_command_queue
4406 queue;
4407
4408 cl_context
4409 context;
4410
4411 cl_int
4412 clStatus,
4413 intensityMethod;
4414
4415 cl_int
4416 colorspace;
4417
4418 cl_kernel
4419 grayscaleKernel;
4420
4421 cl_mem
4422 imageBuffer;
4423
4424 cl_mem_flags
4425 mem_flags;
4426
4427 MagickBooleanType
4428 outputReady;
4429
4430 MagickCLEnv
4431 clEnv;
4432
4433 MagickSizeType
4434 length;
4435
4436 register ssize_t
4437 i;
4438
4439 void
4440 *inputPixels;
4441
4442 inputPixels = NULL;
4443 imageBuffer = NULL;
4444 grayscaleKernel = NULL;
4445
4446 assert(image != (Image *) NULL);
4447 assert(image->signature == MagickSignature);
4448 if (image->debug != MagickFalse)
4449 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4450
4451 /*
4452 * initialize opencl env
4453 */
4454 clEnv = GetDefaultOpenCLEnv();
4455 context = GetOpenCLContext(clEnv);
4456 queue = AcquireOpenCLCommandQueue(clEnv);
4457
4458 outputReady = MagickFalse;
4459
4460 /* Create and initialize OpenCL buffers.
4461 inputPixels = AcquirePixelCachePixels(image, &length, exception);
4462 assume this will get a writable image
4463 */
4464 image_view=AcquireAuthenticCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00004465 inputPixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
4466 if (inputPixels == (void *) NULL)
4467 {
4468 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
4469 goto cleanup;
4470 }
cristy0c832c62014-03-07 22:21:04 +00004471
dirk8a5cf512014-07-28 20:16:27 +00004472 /* If the host pointer is aligned to the size of CLPixelPacket,
4473 then use the host buffer directly from the GPU; otherwise,
4474 create a buffer on the GPU and copy the data over
4475 */
4476 if (ALIGNED(inputPixels,CLPixelPacket))
4477 {
4478 mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
4479 }
4480 else
4481 {
4482 mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
4483 }
4484 /* create a CL buffer from image pixel buffer */
4485 length = image->columns * image->rows;
4486 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
4487 if (clStatus != CL_SUCCESS)
4488 {
4489 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
4490 goto cleanup;
4491 }
cristy0c832c62014-03-07 22:21:04 +00004492
dirk8a5cf512014-07-28 20:16:27 +00004493 intensityMethod = method;
4494 colorspace = image->colorspace;
cristy0c832c62014-03-07 22:21:04 +00004495
dirk8a5cf512014-07-28 20:16:27 +00004496 grayscaleKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "Grayscale");
4497 if (grayscaleKernel == NULL)
4498 {
4499 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
4500 goto cleanup;
4501 }
cristy0c832c62014-03-07 22:21:04 +00004502
dirk8a5cf512014-07-28 20:16:27 +00004503 i = 0;
4504 clStatus=clEnv->library->clSetKernelArg(grayscaleKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
4505 clStatus|=clEnv->library->clSetKernelArg(grayscaleKernel,i++,sizeof(cl_int),&intensityMethod);
4506 clStatus|=clEnv->library->clSetKernelArg(grayscaleKernel,i++,sizeof(cl_int),&colorspace);
4507 if (clStatus != CL_SUCCESS)
4508 {
4509 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
4510 printf("no kernel\n");
4511 goto cleanup;
4512 }
cristy0c832c62014-03-07 22:21:04 +00004513
dirk8a5cf512014-07-28 20:16:27 +00004514 {
4515 size_t global_work_size[2];
4516 global_work_size[0] = image->columns;
4517 global_work_size[1] = image->rows;
4518 /* launch the kernel */
4519 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, grayscaleKernel, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
4520 if (clStatus != CL_SUCCESS)
4521 {
4522 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
4523 goto cleanup;
4524 }
4525 clEnv->library->clFlush(queue);
4526 }
cristy0c832c62014-03-07 22:21:04 +00004527
dirk8a5cf512014-07-28 20:16:27 +00004528 if (ALIGNED(inputPixels,CLPixelPacket))
4529 {
4530 length = image->columns * image->rows;
4531 clEnv->library->clEnqueueMapBuffer(queue, imageBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL, NULL, &clStatus);
4532 }
4533 else
4534 {
4535 length = image->columns * image->rows;
4536 clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), inputPixels, 0, NULL, NULL);
4537 }
4538 if (clStatus != CL_SUCCESS)
4539 {
4540 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
4541 goto cleanup;
4542 }
cristy0c832c62014-03-07 22:21:04 +00004543
dirk832becc2014-08-04 19:44:34 +00004544 outputReady=SyncCacheViewAuthenticPixels(image_view,exception);
4545
4546cleanup:
4547 OpenCLLogException(__FUNCTION__,__LINE__,exception);
4548
dirk8a5cf512014-07-28 20:16:27 +00004549 image_view=DestroyCacheView(image_view);
4550
4551 if (imageBuffer!=NULL)
4552 clEnv->library->clReleaseMemObject(imageBuffer);
4553 if (grayscaleKernel!=NULL)
4554 RelinquishOpenCLKernel(clEnv, grayscaleKernel);
4555 if (queue != NULL)
4556 RelinquishOpenCLCommandQueue(clEnv, queue);
4557
4558 return( outputReady);
dirk832becc2014-08-04 19:44:34 +00004559}
4560
4561MagickExport MagickBooleanType AccelerateGrayscaleImage(Image* image,
4562 const PixelIntensityMethod method,ExceptionInfo *exception)
4563{
4564 MagickBooleanType
4565 status;
4566
4567 assert(image != NULL);
4568 assert(exception != (ExceptionInfo *) NULL);
4569
4570 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
4571 (checkAccelerateCondition(image, AllChannels) == MagickFalse))
4572 return(MagickFalse);
4573
4574 if (method == Rec601LuminancePixelIntensityMethod || method == Rec709LuminancePixelIntensityMethod)
4575 return(MagickFalse);
4576
4577 if (image->colorspace != sRGBColorspace)
4578 return(MagickFalse);
4579
4580 status=ComputeGrayscaleImage(image,method,exception);
4581 return(status);
cristy0c832c62014-03-07 22:21:04 +00004582}
4583
dirk8a5cf512014-07-28 20:16:27 +00004584/*
4585%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4586% %
4587% %
4588% %
4589% E q u a l i z e I m a g e w i t h O p e n C L %
4590% %
4591% %
4592% %
4593%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4594%
4595% EqualizeImage() applies a histogram equalization to the image.
4596%
4597% The format of the EqualizeImage method is:
4598%
4599% MagickBooleanType EqualizeImage(Image *image)
4600% MagickBooleanType EqualizeImageChannel(Image *image,
4601% const ChannelType channel)
4602%
4603% A description of each parameter follows:
4604%
4605% o image: the image.
4606%
4607% o channel: the channel.
4608%
4609*/
cristy0c832c62014-03-07 22:21:04 +00004610
dirk832becc2014-08-04 19:44:34 +00004611static MagickBooleanType LaunchHistogramKernel(MagickCLEnv clEnv,
4612 cl_command_queue queue,cl_mem imageBuffer,cl_mem histogramBuffer,
4613 Image *image,const ChannelType channel,ExceptionInfo *exception)
4614{
4615 MagickBooleanType
4616 outputReady;
4617
4618 cl_int
4619 clStatus,
4620 colorspace,
4621 method;
4622
4623 cl_kernel
4624 histogramKernel;
4625
4626 register ssize_t
4627 i;
4628
4629 size_t
4630 global_work_size[2];
4631
cristy0c832c62014-03-07 22:21:04 +00004632 histogramKernel = NULL;
4633
4634 outputReady = MagickFalse;
dirk8a5cf512014-07-28 20:16:27 +00004635 method = image->intensity;
4636 colorspace = image->colorspace;
cristy0c832c62014-03-07 22:21:04 +00004637
4638 /* get the OpenCL kernel */
4639 histogramKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "Histogram");
4640 if (histogramKernel == NULL)
4641 {
4642 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
4643 goto cleanup;
4644 }
4645
4646 /* set the kernel arguments */
4647 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00004648 clStatus=clEnv->library->clSetKernelArg(histogramKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00004649 clStatus|=clEnv->library->clSetKernelArg(histogramKernel,i++,sizeof(ChannelType),&channel);
4650 clStatus|=clEnv->library->clSetKernelArg(histogramKernel,i++,sizeof(cl_int),&method);
4651 clStatus|=clEnv->library->clSetKernelArg(histogramKernel,i++,sizeof(cl_int),&colorspace);
4652 clStatus|=clEnv->library->clSetKernelArg(histogramKernel,i++,sizeof(cl_mem),(void *)&histogramBuffer);
4653 if (clStatus != CL_SUCCESS)
4654 {
4655 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
4656 goto cleanup;
4657 }
4658
4659 /* launch the kernel */
dirk8a5cf512014-07-28 20:16:27 +00004660 global_work_size[0] = image->columns;
4661 global_work_size[1] = image->rows;
cristy0c832c62014-03-07 22:21:04 +00004662
4663 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, histogramKernel, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
4664
4665 if (clStatus != CL_SUCCESS)
4666 {
4667 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
4668 goto cleanup;
4669 }
4670 clEnv->library->clFlush(queue);
4671
4672 outputReady = MagickTrue;
4673
4674cleanup:
4675 OpenCLLogException(__FUNCTION__,__LINE__,exception);
4676
4677 if (histogramKernel!=NULL)
4678 RelinquishOpenCLKernel(clEnv, histogramKernel);
4679
dirk8a5cf512014-07-28 20:16:27 +00004680 return(outputReady);
cristy0c832c62014-03-07 22:21:04 +00004681}
4682
dirk832becc2014-08-04 19:44:34 +00004683MagickExport MagickBooleanType ComputeEqualizeImage(Image *image,
4684 const ChannelType channel,ExceptionInfo *exception)
4685{
4686#define EqualizeImageTag "Equalize/Image"
4687
4688 CacheView
4689 *image_view;
4690
4691 cl_command_queue
4692 queue;
4693
4694 cl_context
4695 context;
4696
4697 cl_int
4698 clStatus;
4699
4700 cl_mem_flags
4701 mem_flags;
4702
4703 cl_mem
4704 equalizeMapBuffer,
4705 histogramBuffer,
4706 imageBuffer;
4707
4708 cl_kernel
4709 equalizeKernel,
4710 histogramKernel;
4711
4712 cl_uint4
4713 *histogram;
4714
4715 FloatPixelPacket
4716 white,
4717 black,
4718 intensity,
4719 *map;
4720
4721 MagickBooleanType
4722 outputReady,
4723 status;
4724
4725 MagickCLEnv
4726 clEnv;
4727
4728 MagickSizeType
4729 length;
4730
4731 PixelPacket
4732 *equalize_map;
4733
4734 register ssize_t
4735 i;
4736
4737 size_t
4738 global_work_size[2];
4739
4740 void
4741 *hostPtr,
4742 *inputPixels;
4743
4744 map=NULL;
4745 histogram=NULL;
4746 equalize_map=NULL;
4747 inputPixels = NULL;
4748 imageBuffer = NULL;
4749 histogramBuffer = NULL;
4750 equalizeMapBuffer = NULL;
4751 histogramKernel = NULL;
4752 equalizeKernel = NULL;
4753 context = NULL;
4754 queue = NULL;
4755 outputReady = MagickFalse;
4756
4757 assert(image != (Image *) NULL);
4758 assert(image->signature == MagickSignature);
4759 if (image->debug != MagickFalse)
4760 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4761
4762 /*
4763 * initialize opencl env
4764 */
4765 clEnv = GetDefaultOpenCLEnv();
4766 context = GetOpenCLContext(clEnv);
4767 queue = AcquireOpenCLCommandQueue(clEnv);
4768
4769 /*
4770 Allocate and initialize histogram arrays.
4771 */
4772 histogram=(cl_uint4 *) AcquireQuantumMemory(MaxMap+1UL, sizeof(*histogram));
4773 if (histogram == (cl_uint4 *) NULL)
4774 ThrowBinaryException(ResourceLimitWarning,"MemoryAllocationFailed", image->filename);
4775
4776 /* reset histogram */
4777 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
4778
4779 /* Create and initialize OpenCL buffers. */
4780 /* inputPixels = AcquirePixelCachePixels(image, &length, exception); */
4781 /* assume this will get a writable image */
4782 image_view=AcquireAuthenticCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00004783 inputPixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00004784
4785 if (inputPixels == (void *) NULL)
4786 {
dirk8a5cf512014-07-28 20:16:27 +00004787 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristyf034abb2013-11-24 14:16:14 +00004788 goto cleanup;
4789 }
4790 /* If the host pointer is aligned to the size of CLPixelPacket,
4791 then use the host buffer directly from the GPU; otherwise,
4792 create a buffer on the GPU and copy the data over */
4793 if (ALIGNED(inputPixels,CLPixelPacket))
4794 {
4795 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
4796 }
4797 else
4798 {
4799 mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
4800 }
4801 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00004802 length = image->columns * image->rows;
4803 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00004804 if (clStatus != CL_SUCCESS)
4805 {
cristy0c832c62014-03-07 22:21:04 +00004806 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00004807 goto cleanup;
4808 }
cristy0c832c62014-03-07 22:21:04 +00004809
cristyf034abb2013-11-24 14:16:14 +00004810 /* If the host pointer is aligned to the size of cl_uint,
4811 then use the host buffer directly from the GPU; otherwise,
4812 create a buffer on the GPU and copy the data over */
4813 if (ALIGNED(histogram,cl_uint4))
4814 {
4815 mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
4816 hostPtr = histogram;
4817 }
4818 else
4819 {
4820 mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
4821 hostPtr = histogram;
4822 }
4823 /* create a CL buffer for histogram */
4824 length = (MaxMap+1);
cristy0c832c62014-03-07 22:21:04 +00004825 histogramBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(cl_uint4), hostPtr, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00004826 if (clStatus != CL_SUCCESS)
4827 {
cristy0c832c62014-03-07 22:21:04 +00004828 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00004829 goto cleanup;
4830 }
4831
dirk8a5cf512014-07-28 20:16:27 +00004832 status = LaunchHistogramKernel(clEnv, queue, imageBuffer, histogramBuffer, image, channel, exception);
cristy0c832c62014-03-07 22:21:04 +00004833 if (status == MagickFalse)
cristyf034abb2013-11-24 14:16:14 +00004834 goto cleanup;
cristyf034abb2013-11-24 14:16:14 +00004835
4836 /* read from the kenel output */
4837 if (ALIGNED(histogram,cl_uint4))
4838 {
4839 length = (MaxMap+1);
cristy0c832c62014-03-07 22:21:04 +00004840 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 +00004841 }
4842 else
4843 {
4844 length = (MaxMap+1);
cristy0c832c62014-03-07 22:21:04 +00004845 clStatus = clEnv->library->clEnqueueReadBuffer(queue, histogramBuffer, CL_TRUE, 0, length * sizeof(cl_uint4), histogram, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00004846 }
4847 if (clStatus != CL_SUCCESS)
4848 {
cristya22457d2013-12-07 14:03:06 +00004849 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00004850 goto cleanup;
4851 }
4852
4853 /* unmap, don't block gpu to use this buffer again. */
4854 if (ALIGNED(histogram,cl_uint4))
4855 {
cristy0c832c62014-03-07 22:21:04 +00004856 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, histogramBuffer, histogram, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00004857 if (clStatus != CL_SUCCESS)
4858 {
cristy0c832c62014-03-07 22:21:04 +00004859 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00004860 goto cleanup;
4861 }
4862 }
4863
cristy0c832c62014-03-07 22:21:04 +00004864 /* recreate input buffer later, in case image updated */
4865#ifdef RECREATEBUFFER
dirk8a5cf512014-07-28 20:16:27 +00004866 if (imageBuffer!=NULL)
4867 clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00004868#endif
4869
4870 /* CPU stuff */
cristyf034abb2013-11-24 14:16:14 +00004871 equalize_map=(PixelPacket *) AcquireQuantumMemory(MaxMap+1UL, sizeof(*equalize_map));
4872 if (equalize_map == (PixelPacket *) NULL)
cristy0c832c62014-03-07 22:21:04 +00004873 ThrowBinaryException(ResourceLimitWarning,"MemoryAllocationFailed", image->filename);
cristyf034abb2013-11-24 14:16:14 +00004874
4875 map=(FloatPixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*map));
4876 if (map == (FloatPixelPacket *) NULL)
cristy0c832c62014-03-07 22:21:04 +00004877 ThrowBinaryException(ResourceLimitWarning,"MemoryAllocationFailed", image->filename);
cristyf034abb2013-11-24 14:16:14 +00004878
4879 /*
4880 Integrate the histogram to get the equalization map.
4881 */
4882 (void) ResetMagickMemory(&intensity,0,sizeof(intensity));
4883 for (i=0; i <= (ssize_t) MaxMap; i++)
4884 {
4885 if ((channel & SyncChannels) != 0)
cristy0c832c62014-03-07 22:21:04 +00004886 {
4887 intensity.red+=histogram[i].s[2];
4888 map[i]=intensity;
4889 continue;
4890 }
cristyf034abb2013-11-24 14:16:14 +00004891 if ((channel & RedChannel) != 0)
4892 intensity.red+=histogram[i].s[2];
4893 if ((channel & GreenChannel) != 0)
4894 intensity.green+=histogram[i].s[1];
4895 if ((channel & BlueChannel) != 0)
4896 intensity.blue+=histogram[i].s[0];
4897 if ((channel & OpacityChannel) != 0)
dirk8a5cf512014-07-28 20:16:27 +00004898 intensity.alpha+=histogram[i].s[3];
cristy0c832c62014-03-07 22:21:04 +00004899 /*
cristyf034abb2013-11-24 14:16:14 +00004900 if (((channel & IndexChannel) != 0) &&
4901 (image->colorspace == CMYKColorspace))
4902 {
cristy0c832c62014-03-07 22:21:04 +00004903 intensity.index+=histogram[i].index;
cristyf034abb2013-11-24 14:16:14 +00004904 }
cristy0c832c62014-03-07 22:21:04 +00004905 */
cristyf034abb2013-11-24 14:16:14 +00004906 map[i]=intensity;
4907 }
4908 black=map[0];
4909 white=map[(int) MaxMap];
4910 (void) ResetMagickMemory(equalize_map,0,(MaxMap+1)*sizeof(*equalize_map));
4911 for (i=0; i <= (ssize_t) MaxMap; i++)
4912 {
4913 if ((channel & SyncChannels) != 0)
cristy0c832c62014-03-07 22:21:04 +00004914 {
4915 if (white.red != black.red)
4916 equalize_map[i].red=ScaleMapToQuantum((MagickRealType) ((MaxMap*
4917 (map[i].red-black.red))/(white.red-black.red)));
4918 continue;
4919 }
cristyf034abb2013-11-24 14:16:14 +00004920 if (((channel & RedChannel) != 0) && (white.red != black.red))
4921 equalize_map[i].red=ScaleMapToQuantum((MagickRealType) ((MaxMap*
cristy0c832c62014-03-07 22:21:04 +00004922 (map[i].red-black.red))/(white.red-black.red)));
cristyf034abb2013-11-24 14:16:14 +00004923 if (((channel & GreenChannel) != 0) && (white.green != black.green))
4924 equalize_map[i].green=ScaleMapToQuantum((MagickRealType) ((MaxMap*
cristy0c832c62014-03-07 22:21:04 +00004925 (map[i].green-black.green))/(white.green-black.green)));
cristyf034abb2013-11-24 14:16:14 +00004926 if (((channel & BlueChannel) != 0) && (white.blue != black.blue))
4927 equalize_map[i].blue=ScaleMapToQuantum((MagickRealType) ((MaxMap*
cristy0c832c62014-03-07 22:21:04 +00004928 (map[i].blue-black.blue))/(white.blue-black.blue)));
dirk8a5cf512014-07-28 20:16:27 +00004929 if (((channel & OpacityChannel) != 0) && (white.alpha != black.alpha))
4930 equalize_map[i].alpha=ScaleMapToQuantum((MagickRealType) ((MaxMap*
4931 (map[i].alpha-black.alpha))/(white.alpha-black.alpha)));
cristyf034abb2013-11-24 14:16:14 +00004932 /*
4933 if ((((channel & IndexChannel) != 0) &&
cristy0c832c62014-03-07 22:21:04 +00004934 (image->colorspace == CMYKColorspace)) &&
cristyf034abb2013-11-24 14:16:14 +00004935 (white.index != black.index))
4936 equalize_map[i].index=ScaleMapToQuantum((MagickRealType) ((MaxMap*
cristy0c832c62014-03-07 22:21:04 +00004937 (map[i].index-black.index))/(white.index-black.index)));
cristyf034abb2013-11-24 14:16:14 +00004938 */
4939 }
4940
cristyf034abb2013-11-24 14:16:14 +00004941 if (image->storage_class == PseudoClass)
4942 {
cristy0c832c62014-03-07 22:21:04 +00004943 /*
4944 Equalize colormap.
4945 */
4946 for (i=0; i < (ssize_t) image->colors; i++)
4947 {
4948 if ((channel & SyncChannels) != 0)
cristyf034abb2013-11-24 14:16:14 +00004949 {
cristy0c832c62014-03-07 22:21:04 +00004950 if (white.red != black.red)
4951 {
cristyf034abb2013-11-24 14:16:14 +00004952 image->colormap[i].red=equalize_map[
4953 ScaleQuantumToMap(image->colormap[i].red)].red;
cristyf034abb2013-11-24 14:16:14 +00004954 image->colormap[i].green=equalize_map[
cristy0c832c62014-03-07 22:21:04 +00004955 ScaleQuantumToMap(image->colormap[i].green)].red;
cristyf034abb2013-11-24 14:16:14 +00004956 image->colormap[i].blue=equalize_map[
cristy0c832c62014-03-07 22:21:04 +00004957 ScaleQuantumToMap(image->colormap[i].blue)].red;
dirk8a5cf512014-07-28 20:16:27 +00004958 image->colormap[i].alpha=equalize_map[
4959 ScaleQuantumToMap(image->colormap[i].alpha)].red;
cristy0c832c62014-03-07 22:21:04 +00004960 }
4961 continue;
cristyf034abb2013-11-24 14:16:14 +00004962 }
cristy0c832c62014-03-07 22:21:04 +00004963 if (((channel & RedChannel) != 0) && (white.red != black.red))
4964 image->colormap[i].red=equalize_map[
4965 ScaleQuantumToMap(image->colormap[i].red)].red;
4966 if (((channel & GreenChannel) != 0) && (white.green != black.green))
4967 image->colormap[i].green=equalize_map[
4968 ScaleQuantumToMap(image->colormap[i].green)].green;
4969 if (((channel & BlueChannel) != 0) && (white.blue != black.blue))
4970 image->colormap[i].blue=equalize_map[
4971 ScaleQuantumToMap(image->colormap[i].blue)].blue;
4972 if (((channel & OpacityChannel) != 0) &&
dirk8a5cf512014-07-28 20:16:27 +00004973 (white.alpha != black.alpha))
4974 image->colormap[i].alpha=equalize_map[
4975 ScaleQuantumToMap(image->colormap[i].alpha)].alpha;
cristy0c832c62014-03-07 22:21:04 +00004976 }
cristyf034abb2013-11-24 14:16:14 +00004977 }
4978
4979 /*
4980 Equalize image.
4981 */
4982
4983 /* GPU can work on this again, image and equalize map as input
4984 image: uchar4 (CLPixelPacket)
4985 equalize_map: uchar4 (PixelPacket)
4986 black, white: float4 (FloatPixelPacket) */
4987
cristy0c832c62014-03-07 22:21:04 +00004988#ifdef RECREATEBUFFER
cristyf034abb2013-11-24 14:16:14 +00004989 /* If the host pointer is aligned to the size of CLPixelPacket,
4990 then use the host buffer directly from the GPU; otherwise,
4991 create a buffer on the GPU and copy the data over */
4992 if (ALIGNED(inputPixels,CLPixelPacket))
4993 {
4994 mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
4995 }
4996 else
4997 {
4998 mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
4999 }
5000 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00005001 length = image->columns * image->rows;
5002 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00005003 if (clStatus != CL_SUCCESS)
5004 {
cristy0c832c62014-03-07 22:21:04 +00005005 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00005006 goto cleanup;
5007 }
cristy0c832c62014-03-07 22:21:04 +00005008#endif
cristyf034abb2013-11-24 14:16:14 +00005009
5010 /* Create and initialize OpenCL buffers. */
5011 if (ALIGNED(equalize_map, PixelPacket))
5012 {
5013 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
5014 hostPtr = equalize_map;
5015 }
5016 else
5017 {
5018 mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
5019 hostPtr = equalize_map;
5020 }
5021 /* create a CL buffer for eqaulize_map */
5022 length = (MaxMap+1);
cristy0c832c62014-03-07 22:21:04 +00005023 equalizeMapBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(PixelPacket), hostPtr, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00005024 if (clStatus != CL_SUCCESS)
5025 {
cristy0c832c62014-03-07 22:21:04 +00005026 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00005027 goto cleanup;
5028 }
5029
5030 /* get the OpenCL kernel */
5031 equalizeKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "Equalize");
5032 if (equalizeKernel == NULL)
5033 {
cristya22457d2013-12-07 14:03:06 +00005034 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00005035 goto cleanup;
5036 }
5037
5038 /* set the kernel arguments */
5039 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00005040 clStatus=clEnv->library->clSetKernelArg(equalizeKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00005041 clStatus|=clEnv->library->clSetKernelArg(equalizeKernel,i++,sizeof(ChannelType),&channel);
5042 clStatus|=clEnv->library->clSetKernelArg(equalizeKernel,i++,sizeof(cl_mem),(void *)&equalizeMapBuffer);
5043 clStatus|=clEnv->library->clSetKernelArg(equalizeKernel,i++,sizeof(FloatPixelPacket),&white);
5044 clStatus|=clEnv->library->clSetKernelArg(equalizeKernel,i++,sizeof(FloatPixelPacket),&black);
cristyf034abb2013-11-24 14:16:14 +00005045 if (clStatus != CL_SUCCESS)
5046 {
cristy0c832c62014-03-07 22:21:04 +00005047 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00005048 goto cleanup;
5049 }
5050
5051 /* launch the kernel */
dirk8a5cf512014-07-28 20:16:27 +00005052 global_work_size[0] = image->columns;
5053 global_work_size[1] = image->rows;
cristyf034abb2013-11-24 14:16:14 +00005054
cristy0c832c62014-03-07 22:21:04 +00005055 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, equalizeKernel, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00005056
5057 if (clStatus != CL_SUCCESS)
5058 {
cristy0c832c62014-03-07 22:21:04 +00005059 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00005060 goto cleanup;
5061 }
cristy0c832c62014-03-07 22:21:04 +00005062 clEnv->library->clFlush(queue);
cristyf034abb2013-11-24 14:16:14 +00005063
5064 /* read the data back */
5065 if (ALIGNED(inputPixels,CLPixelPacket))
5066 {
dirk8a5cf512014-07-28 20:16:27 +00005067 length = image->columns * image->rows;
5068 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 +00005069 }
5070 else
5071 {
dirk8a5cf512014-07-28 20:16:27 +00005072 length = image->columns * image->rows;
5073 clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), inputPixels, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00005074 }
5075 if (clStatus != CL_SUCCESS)
5076 {
cristya22457d2013-12-07 14:03:06 +00005077 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00005078 goto cleanup;
5079 }
5080
dirk832becc2014-08-04 19:44:34 +00005081 outputReady=SyncCacheViewAuthenticPixels(image_view,exception);
5082
5083cleanup:
5084 OpenCLLogException(__FUNCTION__,__LINE__,exception);
5085
dirk8a5cf512014-07-28 20:16:27 +00005086 image_view=DestroyCacheView(image_view);
cristyf034abb2013-11-24 14:16:14 +00005087
dirk8a5cf512014-07-28 20:16:27 +00005088 if (imageBuffer!=NULL)
5089 clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00005090
5091 if (map!=NULL)
5092 map=(FloatPixelPacket *) RelinquishMagickMemory(map);
5093
5094 if (equalizeMapBuffer!=NULL)
5095 clEnv->library->clReleaseMemObject(equalizeMapBuffer);
5096 if (equalize_map!=NULL)
5097 equalize_map=(PixelPacket *) RelinquishMagickMemory(equalize_map);
5098
cristyf034abb2013-11-24 14:16:14 +00005099 if (histogramBuffer!=NULL)
cristy0c832c62014-03-07 22:21:04 +00005100 clEnv->library->clReleaseMemObject(histogramBuffer);
5101 if (histogram!=NULL)
5102 histogram=(cl_uint4 *) RelinquishMagickMemory(histogram);
5103
cristyf034abb2013-11-24 14:16:14 +00005104 if (histogramKernel!=NULL)
5105 RelinquishOpenCLKernel(clEnv, histogramKernel);
cristy0c832c62014-03-07 22:21:04 +00005106 if (equalizeKernel!=NULL)
5107 RelinquishOpenCLKernel(clEnv, equalizeKernel);
5108
cristyf034abb2013-11-24 14:16:14 +00005109 if (queue != NULL)
5110 RelinquishOpenCLCommandQueue(clEnv, queue);
5111
dirk8a5cf512014-07-28 20:16:27 +00005112 return(outputReady);
dirk832becc2014-08-04 19:44:34 +00005113}
5114
5115MagickExport MagickBooleanType AccelerateEqualizeImage(Image *image,
5116 const ChannelType channel,ExceptionInfo *exception)
5117{
5118 MagickBooleanType
5119 status;
5120
5121 assert(image != NULL);
5122 assert(exception != (ExceptionInfo *) NULL);
5123
5124 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
5125 (checkAccelerateCondition(image, channel) == MagickFalse) ||
5126 (checkHistogramCondition(image, channel) == MagickFalse))
5127 return(MagickFalse);
5128
5129 status=ComputeEqualizeImage(image,channel,exception);
5130 return(status);
cristyf034abb2013-11-24 14:16:14 +00005131}
5132
5133/*
5134%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5135% %
5136% %
5137% %
dirk8a5cf512014-07-28 20:16:27 +00005138% 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 +00005139% %
5140% %
5141% %
5142%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5143%
dirk8a5cf512014-07-28 20:16:27 +00005144% ContrastStretchImage() is a simple image enhancement technique that attempts
5145% to improve the contrast in an image by `stretching' the range of intensity
5146% values it contains to span a desired range of values. It differs from the
5147% more sophisticated histogram equalization in that it can only apply a
5148% linear scaling function to the image pixel values. As a result the
5149% `enhancement' is less harsh.
cristyf034abb2013-11-24 14:16:14 +00005150%
dirk8a5cf512014-07-28 20:16:27 +00005151% The format of the ContrastStretchImage method is:
cristyf034abb2013-11-24 14:16:14 +00005152%
dirk8a5cf512014-07-28 20:16:27 +00005153% MagickBooleanType ContrastStretchImage(Image *image,
5154% const char *levels)
5155% MagickBooleanType ContrastStretchImageChannel(Image *image,
5156% const size_t channel,const double black_point,
5157% const double white_point)
cristyf034abb2013-11-24 14:16:14 +00005158%
5159% A description of each parameter follows:
5160%
5161% o image: the image.
5162%
5163% o channel: the channel.
5164%
dirk8a5cf512014-07-28 20:16:27 +00005165% o black_point: the black point.
5166%
5167% o white_point: the white point.
5168%
5169% o levels: Specify the levels where the black and white points have the
5170% range of 0 to number-of-pixels (e.g. 1%, 10x90%, etc.).
5171%
cristyf034abb2013-11-24 14:16:14 +00005172*/
5173
dirk832becc2014-08-04 19:44:34 +00005174MagickExport MagickBooleanType ComputeContrastStretchImageChannel(Image *image,
5175 const ChannelType channel,const double black_point,const double white_point,
5176 ExceptionInfo *exception)
5177{
5178#define ContrastStretchImageTag "ContrastStretch/Image"
5179#define MaxRange(color) ((MagickRealType) ScaleQuantumToMap((Quantum) (color)))
5180
5181 CacheView
5182 *image_view;
5183
5184 cl_command_queue
5185 queue;
5186
5187 cl_context
5188 context;
5189
5190 cl_int
5191 clStatus;
5192
5193 cl_mem_flags
5194 mem_flags;
5195
5196 cl_mem
5197 histogramBuffer,
5198 imageBuffer,
5199 stretchMapBuffer;
5200
5201 cl_kernel
5202 histogramKernel,
5203 stretchKernel;
5204
5205 cl_uint4
5206 *histogram;
5207
5208 double
5209 intensity;
5210
5211 FloatPixelPacket
5212 black,
5213 white;
5214
5215 MagickBooleanType
5216 outputReady,
5217 status;
5218
5219 MagickCLEnv
5220 clEnv;
5221
5222 MagickSizeType
5223 length;
5224
5225 PixelPacket
5226 *stretch_map;
5227
5228 register ssize_t
5229 i;
5230
5231 size_t
5232 global_work_size[2];
5233
5234 void
5235 *hostPtr,
5236 *inputPixels;
5237
5238 histogram=NULL;
5239 stretch_map=NULL;
5240 inputPixels = NULL;
5241 imageBuffer = NULL;
5242 histogramBuffer = NULL;
5243 stretchMapBuffer = NULL;
5244 histogramKernel = NULL;
5245 stretchKernel = NULL;
5246 context = NULL;
5247 queue = NULL;
5248 outputReady = MagickFalse;
5249
5250
5251 assert(image != (Image *) NULL);
5252 assert(image->signature == MagickSignature);
5253 if (image->debug != MagickFalse)
5254 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5255
5256 //exception=(&image->exception);
5257
5258 /*
5259 * initialize opencl env
5260 */
5261 clEnv = GetDefaultOpenCLEnv();
5262 context = GetOpenCLContext(clEnv);
5263 queue = AcquireOpenCLCommandQueue(clEnv);
5264
5265 /*
5266 Allocate and initialize histogram arrays.
5267 */
5268 histogram=(cl_uint4 *) AcquireQuantumMemory(MaxMap+1UL, sizeof(*histogram));
5269
5270 if (histogram == (cl_uint4 *) NULL)
5271 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", image->filename);
5272
5273 /* reset histogram */
5274 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
5275
5276 /*
5277 if (IsGrayImage(image,exception) != MagickFalse)
5278 (void) SetImageColorspace(image,GRAYColorspace);
5279 */
5280
5281 status=MagickTrue;
5282
5283
5284 /*
5285 Form histogram.
5286 */
5287 /* Create and initialize OpenCL buffers. */
5288 /* inputPixels = AcquirePixelCachePixels(image, &length, exception); */
5289 /* assume this will get a writable image */
5290 image_view=AcquireAuthenticCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00005291 inputPixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
cristy0c832c62014-03-07 22:21:04 +00005292
5293 if (inputPixels == (void *) NULL)
5294 {
dirk8a5cf512014-07-28 20:16:27 +00005295 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristy0c832c62014-03-07 22:21:04 +00005296 goto cleanup;
5297 }
5298 /* If the host pointer is aligned to the size of CLPixelPacket,
5299 then use the host buffer directly from the GPU; otherwise,
5300 create a buffer on the GPU and copy the data over */
5301 if (ALIGNED(inputPixels,CLPixelPacket))
5302 {
5303 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
5304 }
5305 else
5306 {
5307 mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
5308 }
5309 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00005310 length = image->columns * image->rows;
5311 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristy0c832c62014-03-07 22:21:04 +00005312 if (clStatus != CL_SUCCESS)
5313 {
5314 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
5315 goto cleanup;
5316 }
5317
5318 /* If the host pointer is aligned to the size of cl_uint,
5319 then use the host buffer directly from the GPU; otherwise,
5320 create a buffer on the GPU and copy the data over */
5321 if (ALIGNED(histogram,cl_uint4))
5322 {
5323 mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
5324 hostPtr = histogram;
5325 }
5326 else
5327 {
5328 mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
5329 hostPtr = histogram;
5330 }
5331 /* create a CL buffer for histogram */
5332 length = (MaxMap+1);
5333 histogramBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(cl_uint4), hostPtr, &clStatus);
5334 if (clStatus != CL_SUCCESS)
5335 {
5336 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
5337 goto cleanup;
5338 }
5339
dirk8a5cf512014-07-28 20:16:27 +00005340 status = LaunchHistogramKernel(clEnv, queue, imageBuffer, histogramBuffer, image, channel, exception);
cristy0c832c62014-03-07 22:21:04 +00005341 if (status == MagickFalse)
5342 goto cleanup;
5343
5344 /* read from the kenel output */
5345 if (ALIGNED(histogram,cl_uint4))
5346 {
5347 length = (MaxMap+1);
5348 clEnv->library->clEnqueueMapBuffer(queue, histogramBuffer, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(cl_uint4), 0, NULL, NULL, &clStatus);
5349 }
5350 else
5351 {
5352 length = (MaxMap+1);
5353 clStatus = clEnv->library->clEnqueueReadBuffer(queue, histogramBuffer, CL_TRUE, 0, length * sizeof(cl_uint4), histogram, 0, NULL, NULL);
5354 }
5355 if (clStatus != CL_SUCCESS)
5356 {
5357 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
5358 goto cleanup;
5359 }
5360
5361 /* unmap, don't block gpu to use this buffer again. */
5362 if (ALIGNED(histogram,cl_uint4))
5363 {
5364 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, histogramBuffer, histogram, 0, NULL, NULL);
5365 if (clStatus != CL_SUCCESS)
5366 {
5367 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
5368 goto cleanup;
5369 }
5370 }
5371
5372 /* recreate input buffer later, in case image updated */
5373#ifdef RECREATEBUFFER
dirk8a5cf512014-07-28 20:16:27 +00005374 if (imageBuffer!=NULL)
5375 clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00005376#endif
5377
5378 /* CPU stuff */
5379 /*
5380 Find the histogram boundaries by locating the black/white levels.
5381 */
5382 black.red=0.0;
5383 white.red=MaxRange(QuantumRange);
5384 if ((channel & RedChannel) != 0)
5385 {
5386 intensity=0.0;
5387 for (i=0; i <= (ssize_t) MaxMap; i++)
5388 {
5389 intensity+=histogram[i].s[2];
5390 if (intensity > black_point)
5391 break;
5392 }
5393 black.red=(MagickRealType) i;
5394 intensity=0.0;
5395 for (i=(ssize_t) MaxMap; i != 0; i--)
5396 {
5397 intensity+=histogram[i].s[2];
5398 if (intensity > ((double) image->columns*image->rows-white_point))
5399 break;
5400 }
5401 white.red=(MagickRealType) i;
5402 }
5403 black.green=0.0;
5404 white.green=MaxRange(QuantumRange);
5405 if ((channel & GreenChannel) != 0)
5406 {
5407 intensity=0.0;
5408 for (i=0; i <= (ssize_t) MaxMap; i++)
5409 {
5410 intensity+=histogram[i].s[2];
5411 if (intensity > black_point)
5412 break;
5413 }
5414 black.green=(MagickRealType) i;
5415 intensity=0.0;
5416 for (i=(ssize_t) MaxMap; i != 0; i--)
5417 {
5418 intensity+=histogram[i].s[2];
5419 if (intensity > ((double) image->columns*image->rows-white_point))
5420 break;
5421 }
5422 white.green=(MagickRealType) i;
5423 }
5424 black.blue=0.0;
5425 white.blue=MaxRange(QuantumRange);
5426 if ((channel & BlueChannel) != 0)
5427 {
5428 intensity=0.0;
5429 for (i=0; i <= (ssize_t) MaxMap; i++)
5430 {
5431 intensity+=histogram[i].s[2];
5432 if (intensity > black_point)
5433 break;
5434 }
5435 black.blue=(MagickRealType) i;
5436 intensity=0.0;
5437 for (i=(ssize_t) MaxMap; i != 0; i--)
5438 {
5439 intensity+=histogram[i].s[2];
5440 if (intensity > ((double) image->columns*image->rows-white_point))
5441 break;
5442 }
5443 white.blue=(MagickRealType) i;
5444 }
dirk8a5cf512014-07-28 20:16:27 +00005445 black.alpha=0.0;
5446 white.alpha=MaxRange(QuantumRange);
cristy0c832c62014-03-07 22:21:04 +00005447 if ((channel & OpacityChannel) != 0)
5448 {
5449 intensity=0.0;
5450 for (i=0; i <= (ssize_t) MaxMap; i++)
5451 {
5452 intensity+=histogram[i].s[2];
5453 if (intensity > black_point)
5454 break;
5455 }
dirk8a5cf512014-07-28 20:16:27 +00005456 black.alpha=(MagickRealType) i;
cristy0c832c62014-03-07 22:21:04 +00005457 intensity=0.0;
5458 for (i=(ssize_t) MaxMap; i != 0; i--)
5459 {
5460 intensity+=histogram[i].s[2];
5461 if (intensity > ((double) image->columns*image->rows-white_point))
5462 break;
5463 }
dirk8a5cf512014-07-28 20:16:27 +00005464 white.alpha=(MagickRealType) i;
cristy0c832c62014-03-07 22:21:04 +00005465 }
5466 /*
5467 black.index=0.0;
5468 white.index=MaxRange(QuantumRange);
5469 if (((channel & IndexChannel) != 0) && (image->colorspace == CMYKColorspace))
5470 {
5471 intensity=0.0;
5472 for (i=0; i <= (ssize_t) MaxMap; i++)
5473 {
5474 intensity+=histogram[i].index;
5475 if (intensity > black_point)
5476 break;
5477 }
5478 black.index=(MagickRealType) i;
5479 intensity=0.0;
5480 for (i=(ssize_t) MaxMap; i != 0; i--)
5481 {
5482 intensity+=histogram[i].index;
5483 if (intensity > ((double) image->columns*image->rows-white_point))
5484 break;
5485 }
5486 white.index=(MagickRealType) i;
5487 }
5488 */
5489
5490
5491 stretch_map=(PixelPacket *) AcquireQuantumMemory(MaxMap+1UL,
5492 sizeof(*stretch_map));
5493
cristy931c8062014-03-10 13:19:17 +00005494 if (stretch_map == (PixelPacket *) NULL)
cristy0c832c62014-03-07 22:21:04 +00005495 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
5496 image->filename);
5497
5498 /*
5499 Stretch the histogram to create the stretched image mapping.
5500 */
5501 (void) ResetMagickMemory(stretch_map,0,(MaxMap+1)*sizeof(*stretch_map));
5502 for (i=0; i <= (ssize_t) MaxMap; i++)
5503 {
5504 if ((channel & RedChannel) != 0)
5505 {
5506 if (i < (ssize_t) black.red)
5507 stretch_map[i].red=(Quantum) 0;
5508 else
5509 if (i > (ssize_t) white.red)
5510 stretch_map[i].red=QuantumRange;
5511 else
5512 if (black.red != white.red)
5513 stretch_map[i].red=ScaleMapToQuantum((MagickRealType) (MaxMap*
5514 (i-black.red)/(white.red-black.red)));
5515 }
5516 if ((channel & GreenChannel) != 0)
5517 {
5518 if (i < (ssize_t) black.green)
5519 stretch_map[i].green=0;
5520 else
5521 if (i > (ssize_t) white.green)
5522 stretch_map[i].green=QuantumRange;
5523 else
5524 if (black.green != white.green)
5525 stretch_map[i].green=ScaleMapToQuantum((MagickRealType) (MaxMap*
5526 (i-black.green)/(white.green-black.green)));
5527 }
5528 if ((channel & BlueChannel) != 0)
5529 {
5530 if (i < (ssize_t) black.blue)
5531 stretch_map[i].blue=0;
5532 else
5533 if (i > (ssize_t) white.blue)
5534 stretch_map[i].blue= QuantumRange;
5535 else
5536 if (black.blue != white.blue)
5537 stretch_map[i].blue=ScaleMapToQuantum((MagickRealType) (MaxMap*
5538 (i-black.blue)/(white.blue-black.blue)));
5539 }
5540 if ((channel & OpacityChannel) != 0)
5541 {
dirk8a5cf512014-07-28 20:16:27 +00005542 if (i < (ssize_t) black.alpha)
5543 stretch_map[i].alpha=0;
cristy0c832c62014-03-07 22:21:04 +00005544 else
dirk8a5cf512014-07-28 20:16:27 +00005545 if (i > (ssize_t) white.alpha)
5546 stretch_map[i].alpha=QuantumRange;
cristy0c832c62014-03-07 22:21:04 +00005547 else
dirk8a5cf512014-07-28 20:16:27 +00005548 if (black.alpha != white.alpha)
5549 stretch_map[i].alpha=ScaleMapToQuantum((MagickRealType) (MaxMap*
5550 (i-black.alpha)/(white.alpha-black.alpha)));
cristy0c832c62014-03-07 22:21:04 +00005551 }
5552 /*
5553 if (((channel & IndexChannel) != 0) &&
5554 (image->colorspace == CMYKColorspace))
5555 {
5556 if (i < (ssize_t) black.index)
5557 stretch_map[i].index=0;
5558 else
5559 if (i > (ssize_t) white.index)
5560 stretch_map[i].index=QuantumRange;
5561 else
5562 if (black.index != white.index)
5563 stretch_map[i].index=ScaleMapToQuantum((MagickRealType) (MaxMap*
5564 (i-black.index)/(white.index-black.index)));
5565 }
5566 */
5567 }
5568
5569 /*
5570 Stretch the image.
5571 */
5572 if (((channel & OpacityChannel) != 0) || (((channel & IndexChannel) != 0) &&
5573 (image->colorspace == CMYKColorspace)))
5574 image->storage_class=DirectClass;
5575 if (image->storage_class == PseudoClass)
5576 {
5577 /*
5578 Stretch colormap.
5579 */
5580 for (i=0; i < (ssize_t) image->colors; i++)
5581 {
5582 if ((channel & RedChannel) != 0)
5583 {
5584 if (black.red != white.red)
5585 image->colormap[i].red=stretch_map[
5586 ScaleQuantumToMap(image->colormap[i].red)].red;
5587 }
5588 if ((channel & GreenChannel) != 0)
5589 {
5590 if (black.green != white.green)
5591 image->colormap[i].green=stretch_map[
5592 ScaleQuantumToMap(image->colormap[i].green)].green;
5593 }
5594 if ((channel & BlueChannel) != 0)
5595 {
5596 if (black.blue != white.blue)
5597 image->colormap[i].blue=stretch_map[
5598 ScaleQuantumToMap(image->colormap[i].blue)].blue;
5599 }
5600 if ((channel & OpacityChannel) != 0)
5601 {
dirk8a5cf512014-07-28 20:16:27 +00005602 if (black.alpha != white.alpha)
5603 image->colormap[i].alpha=stretch_map[
5604 ScaleQuantumToMap(image->colormap[i].alpha)].alpha;
cristy0c832c62014-03-07 22:21:04 +00005605 }
5606 }
5607 }
5608
5609 /*
5610 Stretch image.
5611 */
5612
5613
5614 /* GPU can work on this again, image and equalize map as input
5615 image: uchar4 (CLPixelPacket)
5616 stretch_map: uchar4 (PixelPacket)
5617 black, white: float4 (FloatPixelPacket) */
5618
5619#ifdef RECREATEBUFFER
5620 /* If the host pointer is aligned to the size of CLPixelPacket,
5621 then use the host buffer directly from the GPU; otherwise,
5622 create a buffer on the GPU and copy the data over */
5623 if (ALIGNED(inputPixels,CLPixelPacket))
5624 {
5625 mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
5626 }
5627 else
5628 {
5629 mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
5630 }
5631 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00005632 length = image->columns * image->rows;
5633 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristy0c832c62014-03-07 22:21:04 +00005634 if (clStatus != CL_SUCCESS)
5635 {
5636 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
5637 goto cleanup;
5638 }
5639#endif
5640
5641 /* Create and initialize OpenCL buffers. */
5642 if (ALIGNED(stretch_map, PixelPacket))
5643 {
5644 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
5645 hostPtr = stretch_map;
5646 }
5647 else
5648 {
5649 mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
5650 hostPtr = stretch_map;
5651 }
5652 /* create a CL buffer for stretch_map */
5653 length = (MaxMap+1);
5654 stretchMapBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(PixelPacket), hostPtr, &clStatus);
5655 if (clStatus != CL_SUCCESS)
5656 {
5657 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
5658 goto cleanup;
5659 }
5660
5661 /* get the OpenCL kernel */
5662 stretchKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "Stretch");
5663 if (stretchKernel == NULL)
5664 {
5665 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "AcquireOpenCLKernel failed.", "'%s'", ".");
5666 goto cleanup;
5667 }
5668
5669 /* set the kernel arguments */
5670 i = 0;
dirk8a5cf512014-07-28 20:16:27 +00005671 clStatus=clEnv->library->clSetKernelArg(stretchKernel,i++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00005672 clStatus|=clEnv->library->clSetKernelArg(stretchKernel,i++,sizeof(ChannelType),&channel);
5673 clStatus|=clEnv->library->clSetKernelArg(stretchKernel,i++,sizeof(cl_mem),(void *)&stretchMapBuffer);
5674 clStatus|=clEnv->library->clSetKernelArg(stretchKernel,i++,sizeof(FloatPixelPacket),&white);
5675 clStatus|=clEnv->library->clSetKernelArg(stretchKernel,i++,sizeof(FloatPixelPacket),&black);
5676 if (clStatus != CL_SUCCESS)
5677 {
5678 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
5679 goto cleanup;
5680 }
5681
5682 /* launch the kernel */
dirk8a5cf512014-07-28 20:16:27 +00005683 global_work_size[0] = image->columns;
5684 global_work_size[1] = image->rows;
cristy0c832c62014-03-07 22:21:04 +00005685
5686 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, stretchKernel, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
5687
5688 if (clStatus != CL_SUCCESS)
5689 {
5690 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
5691 goto cleanup;
5692 }
5693 clEnv->library->clFlush(queue);
5694
5695 /* read the data back */
5696 if (ALIGNED(inputPixels,CLPixelPacket))
5697 {
dirk8a5cf512014-07-28 20:16:27 +00005698 length = image->columns * image->rows;
5699 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 +00005700 }
5701 else
5702 {
dirk8a5cf512014-07-28 20:16:27 +00005703 length = image->columns * image->rows;
5704 clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), inputPixels, 0, NULL, NULL);
cristy0c832c62014-03-07 22:21:04 +00005705 }
5706 if (clStatus != CL_SUCCESS)
5707 {
5708 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
5709 goto cleanup;
5710 }
5711
dirk832becc2014-08-04 19:44:34 +00005712 outputReady=SyncCacheViewAuthenticPixels(image_view,exception);
5713
5714cleanup:
5715 OpenCLLogException(__FUNCTION__,__LINE__,exception);
5716
dirk8a5cf512014-07-28 20:16:27 +00005717 image_view=DestroyCacheView(image_view);
cristy0c832c62014-03-07 22:21:04 +00005718
dirk8a5cf512014-07-28 20:16:27 +00005719 if (imageBuffer!=NULL)
5720 clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00005721
5722 if (stretchMapBuffer!=NULL)
5723 clEnv->library->clReleaseMemObject(stretchMapBuffer);
5724 if (stretch_map!=NULL)
5725 stretch_map=(PixelPacket *) RelinquishMagickMemory(stretch_map);
5726
5727
5728 if (histogramBuffer!=NULL)
5729 clEnv->library->clReleaseMemObject(histogramBuffer);
5730 if (histogram!=NULL)
5731 histogram=(cl_uint4 *) RelinquishMagickMemory(histogram);
5732
5733
5734 if (histogramKernel!=NULL)
5735 RelinquishOpenCLKernel(clEnv, histogramKernel);
5736 if (stretchKernel!=NULL)
5737 RelinquishOpenCLKernel(clEnv, stretchKernel);
5738
5739 if (queue != NULL)
5740 RelinquishOpenCLCommandQueue(clEnv, queue);
5741
dirk832becc2014-08-04 19:44:34 +00005742 return(outputReady);
5743}
5744
5745MagickExport MagickBooleanType AccelerateContrastStretchImageChannel(
5746 Image *image,const ChannelType channel,const double black_point,
5747 const double white_point,ExceptionInfo *exception)
5748{
5749 MagickBooleanType
5750 status;
5751
5752 assert(image != NULL);
5753 assert(exception != (ExceptionInfo *) NULL);
5754
5755 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
5756 (checkAccelerateCondition(image, channel) == MagickFalse) ||
5757 (checkHistogramCondition(image, channel) == MagickFalse))
5758 return(MagickFalse);
5759
5760 status=ComputeContrastStretchImageChannel(image,channel, black_point, white_point, exception);
5761 return(status);
cristy0c832c62014-03-07 22:21:04 +00005762}
5763
cristy0c832c62014-03-07 22:21:04 +00005764/*
5765%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5766% %
5767% %
5768% %
dirk8a5cf512014-07-28 20:16:27 +00005769% 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 +00005770% %
5771% %
5772% %
5773%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5774%
dirk8a5cf512014-07-28 20:16:27 +00005775% DespeckleImage() reduces the speckle noise in an image while perserving the
5776% edges of the original image. A speckle removing filter uses a complementary
5777% hulling technique (raising pixels that are darker than their surrounding
5778% neighbors, then complementarily lowering pixels that are brighter than their
5779% surrounding neighbors) to reduce the speckle index of that image (reference
5780% Crimmins speckle removal).
cristy0c832c62014-03-07 22:21:04 +00005781%
dirk8a5cf512014-07-28 20:16:27 +00005782% The format of the DespeckleImage method is:
cristy0c832c62014-03-07 22:21:04 +00005783%
dirk8a5cf512014-07-28 20:16:27 +00005784% Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
cristy0c832c62014-03-07 22:21:04 +00005785%
5786% A description of each parameter follows:
5787%
5788% o image: the image.
5789%
dirk8a5cf512014-07-28 20:16:27 +00005790% o exception: return any errors or warnings in this structure.
cristy0c832c62014-03-07 22:21:04 +00005791%
5792*/
5793
dirk832becc2014-08-04 19:44:34 +00005794static Image *ComputeDespeckleImage(const Image *image,
5795 ExceptionInfo*exception)
5796{
5797 static const int
5798 X[4] = {0, 1, 1,-1},
5799 Y[4] = {1, 0, 1, 1};
5800
5801 CacheView
5802 *filteredImage_view,
5803 *image_view;
5804
5805 cl_command_queue
5806 queue;
5807
5808 cl_context
5809 context;
5810
5811 cl_int
5812 clStatus;
5813
5814 cl_kernel
5815 hullPass1,
5816 hullPass2;
5817
5818 cl_mem_flags
5819 mem_flags;
5820
5821 cl_mem
5822 filteredImageBuffer,
5823 imageBuffer,
5824 tempImageBuffer[2];
5825
5826 const void
5827 *inputPixels;
5828
5829 Image
5830 *filteredImage;
5831
5832 int
5833 k,
5834 matte;
5835
5836 MagickBooleanType
5837 outputReady;
5838
5839 MagickCLEnv
5840 clEnv;
5841
5842 MagickSizeType
5843 length;
5844
5845 size_t
5846 global_work_size[2];
5847
5848 unsigned int
5849 imageHeight,
5850 imageWidth;
5851
5852 void
5853 *filteredPixels,
5854 *hostPtr;
5855
5856 outputReady = MagickFalse;
5857 clEnv = NULL;
5858 inputPixels = NULL;
5859 filteredImage = NULL;
5860 filteredImage_view = NULL;
5861 filteredPixels = NULL;
5862 context = NULL;
5863 imageBuffer = NULL;
5864 filteredImageBuffer = NULL;
5865 hullPass1 = NULL;
5866 hullPass2 = NULL;
5867 queue = NULL;
5868 tempImageBuffer[0] = tempImageBuffer[1] = NULL;
5869 clEnv = GetDefaultOpenCLEnv();
5870 context = GetOpenCLContext(clEnv);
5871 queue = AcquireOpenCLCommandQueue(clEnv);
5872
5873 image_view=AcquireVirtualCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00005874 inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00005875 if (inputPixels == (void *) NULL)
5876 {
dirk8a5cf512014-07-28 20:16:27 +00005877 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristyf034abb2013-11-24 14:16:14 +00005878 goto cleanup;
5879 }
5880
5881 if (ALIGNED(inputPixels,CLPixelPacket))
5882 {
5883 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
5884 }
5885 else
5886 {
5887 mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
5888 }
5889 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00005890 length = image->columns * image->rows;
5891 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00005892 if (clStatus != CL_SUCCESS)
5893 {
cristy0c832c62014-03-07 22:21:04 +00005894 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00005895 goto cleanup;
5896 }
5897
5898 mem_flags = CL_MEM_READ_WRITE;
dirk8a5cf512014-07-28 20:16:27 +00005899 length = image->columns * image->rows;
cristyf034abb2013-11-24 14:16:14 +00005900 for (k = 0; k < 2; k++)
5901 {
cristy0c832c62014-03-07 22:21:04 +00005902 tempImageBuffer[k] = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), NULL, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00005903 if (clStatus != CL_SUCCESS)
5904 {
cristy0c832c62014-03-07 22:21:04 +00005905 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00005906 goto cleanup;
5907 }
5908 }
5909
dirk8a5cf512014-07-28 20:16:27 +00005910 filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristyf034abb2013-11-24 14:16:14 +00005911 assert(filteredImage != NULL);
dirk8a5cf512014-07-28 20:16:27 +00005912 if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
cristyf034abb2013-11-24 14:16:14 +00005913 {
cristya22457d2013-12-07 14:03:06 +00005914 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00005915 goto cleanup;
5916 }
dirk832becc2014-08-04 19:44:34 +00005917 filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
dirk8a5cf512014-07-28 20:16:27 +00005918 filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
cristyf034abb2013-11-24 14:16:14 +00005919 if (filteredPixels == (void *) NULL)
5920 {
cristya22457d2013-12-07 14:03:06 +00005921 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
cristyf034abb2013-11-24 14:16:14 +00005922 goto cleanup;
5923 }
5924
5925 if (ALIGNED(filteredPixels,CLPixelPacket))
5926 {
5927 mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
5928 hostPtr = filteredPixels;
5929 }
5930 else
5931 {
5932 mem_flags = CL_MEM_WRITE_ONLY;
5933 hostPtr = NULL;
5934 }
5935 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00005936 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00005937 filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
cristyf034abb2013-11-24 14:16:14 +00005938 if (clStatus != CL_SUCCESS)
5939 {
cristy0c832c62014-03-07 22:21:04 +00005940 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristyf034abb2013-11-24 14:16:14 +00005941 goto cleanup;
5942 }
5943
5944 hullPass1 = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "HullPass1");
5945 hullPass2 = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "HullPass2");
5946
dirk8a5cf512014-07-28 20:16:27 +00005947 clStatus =clEnv->library->clSetKernelArg(hullPass1,0,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00005948 clStatus |=clEnv->library->clSetKernelArg(hullPass1,1,sizeof(cl_mem),(void *)(tempImageBuffer+1));
dirk8a5cf512014-07-28 20:16:27 +00005949 imageWidth = image->columns;
cristy0c832c62014-03-07 22:21:04 +00005950 clStatus |=clEnv->library->clSetKernelArg(hullPass1,2,sizeof(unsigned int),(void *)&imageWidth);
dirk8a5cf512014-07-28 20:16:27 +00005951 imageHeight = image->rows;
cristy0c832c62014-03-07 22:21:04 +00005952 clStatus |=clEnv->library->clSetKernelArg(hullPass1,3,sizeof(unsigned int),(void *)&imageHeight);
dirk8a5cf512014-07-28 20:16:27 +00005953 matte = (image->alpha_trait==BlendPixelTrait)?0:1;
cristy0c832c62014-03-07 22:21:04 +00005954 clStatus |=clEnv->library->clSetKernelArg(hullPass1,6,sizeof(int),(void *)&matte);
cristyf034abb2013-11-24 14:16:14 +00005955 if (clStatus != CL_SUCCESS)
5956 {
cristy0c832c62014-03-07 22:21:04 +00005957 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00005958 goto cleanup;
5959 }
5960
cristy0c832c62014-03-07 22:21:04 +00005961 clStatus = clEnv->library->clSetKernelArg(hullPass2,0,sizeof(cl_mem),(void *)(tempImageBuffer+1));
5962 clStatus |=clEnv->library->clSetKernelArg(hullPass2,1,sizeof(cl_mem),(void *)tempImageBuffer);
dirk8a5cf512014-07-28 20:16:27 +00005963 imageWidth = image->columns;
cristy0c832c62014-03-07 22:21:04 +00005964 clStatus |=clEnv->library->clSetKernelArg(hullPass2,2,sizeof(unsigned int),(void *)&imageWidth);
dirk8a5cf512014-07-28 20:16:27 +00005965 imageHeight = image->rows;
cristy0c832c62014-03-07 22:21:04 +00005966 clStatus |=clEnv->library->clSetKernelArg(hullPass2,3,sizeof(unsigned int),(void *)&imageHeight);
dirk8a5cf512014-07-28 20:16:27 +00005967 matte = (image->alpha_trait==BlendPixelTrait)?0:1;
cristy0c832c62014-03-07 22:21:04 +00005968 clStatus |=clEnv->library->clSetKernelArg(hullPass2,6,sizeof(int),(void *)&matte);
cristyf034abb2013-11-24 14:16:14 +00005969 if (clStatus != CL_SUCCESS)
5970 {
cristy0c832c62014-03-07 22:21:04 +00005971 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00005972 goto cleanup;
5973 }
5974
5975
dirk8a5cf512014-07-28 20:16:27 +00005976 global_work_size[0] = image->columns;
5977 global_work_size[1] = image->rows;
cristyf034abb2013-11-24 14:16:14 +00005978
5979
5980 for (k = 0; k < 4; k++)
5981 {
5982 cl_int2 offset;
5983 int polarity;
5984
5985
5986 offset.s[0] = X[k];
5987 offset.s[1] = Y[k];
5988 polarity = 1;
cristy0c832c62014-03-07 22:21:04 +00005989 clStatus = clEnv->library->clSetKernelArg(hullPass1,4,sizeof(cl_int2),(void *)&offset);
5990 clStatus|= clEnv->library->clSetKernelArg(hullPass1,5,sizeof(int),(void *)&polarity);
5991 clStatus|=clEnv->library->clSetKernelArg(hullPass2,4,sizeof(cl_int2),(void *)&offset);
5992 clStatus|=clEnv->library->clSetKernelArg(hullPass2,5,sizeof(int),(void *)&polarity);
cristyf034abb2013-11-24 14:16:14 +00005993 if (clStatus != CL_SUCCESS)
5994 {
cristy0c832c62014-03-07 22:21:04 +00005995 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00005996 goto cleanup;
5997 }
5998 /* launch the kernel */
cristy0c832c62014-03-07 22:21:04 +00005999 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass1, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00006000 if (clStatus != CL_SUCCESS)
6001 {
cristy0c832c62014-03-07 22:21:04 +00006002 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006003 goto cleanup;
6004 }
6005 /* launch the kernel */
cristy0c832c62014-03-07 22:21:04 +00006006 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass2, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00006007 if (clStatus != CL_SUCCESS)
6008 {
cristy0c832c62014-03-07 22:21:04 +00006009 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006010 goto cleanup;
6011 }
6012
6013
6014 if (k == 0)
cristy0c832c62014-03-07 22:21:04 +00006015 clStatus =clEnv->library->clSetKernelArg(hullPass1,0,sizeof(cl_mem),(void *)(tempImageBuffer));
cristyf034abb2013-11-24 14:16:14 +00006016 offset.s[0] = -X[k];
6017 offset.s[1] = -Y[k];
6018 polarity = 1;
cristy0c832c62014-03-07 22:21:04 +00006019 clStatus = clEnv->library->clSetKernelArg(hullPass1,4,sizeof(cl_int2),(void *)&offset);
6020 clStatus|= clEnv->library->clSetKernelArg(hullPass1,5,sizeof(int),(void *)&polarity);
6021 clStatus|=clEnv->library->clSetKernelArg(hullPass2,4,sizeof(cl_int2),(void *)&offset);
6022 clStatus|=clEnv->library->clSetKernelArg(hullPass2,5,sizeof(int),(void *)&polarity);
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->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006026 goto cleanup;
6027 }
6028 /* launch the kernel */
cristy0c832c62014-03-07 22:21:04 +00006029 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass1, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00006030 if (clStatus != CL_SUCCESS)
6031 {
cristy0c832c62014-03-07 22:21:04 +00006032 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006033 goto cleanup;
6034 }
6035 /* launch the kernel */
cristy0c832c62014-03-07 22:21:04 +00006036 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass2, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00006037 if (clStatus != CL_SUCCESS)
6038 {
cristy0c832c62014-03-07 22:21:04 +00006039 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006040 goto cleanup;
6041 }
6042
6043 offset.s[0] = -X[k];
6044 offset.s[1] = -Y[k];
6045 polarity = -1;
cristy0c832c62014-03-07 22:21:04 +00006046 clStatus = clEnv->library->clSetKernelArg(hullPass1,4,sizeof(cl_int2),(void *)&offset);
6047 clStatus|= clEnv->library->clSetKernelArg(hullPass1,5,sizeof(int),(void *)&polarity);
6048 clStatus|=clEnv->library->clSetKernelArg(hullPass2,4,sizeof(cl_int2),(void *)&offset);
6049 clStatus|=clEnv->library->clSetKernelArg(hullPass2,5,sizeof(int),(void *)&polarity);
cristyf034abb2013-11-24 14:16:14 +00006050 if (clStatus != CL_SUCCESS)
6051 {
cristy0c832c62014-03-07 22:21:04 +00006052 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006053 goto cleanup;
6054 }
6055 /* launch the kernel */
cristy0c832c62014-03-07 22:21:04 +00006056 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass1, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00006057 if (clStatus != CL_SUCCESS)
6058 {
cristy0c832c62014-03-07 22:21:04 +00006059 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006060 goto cleanup;
6061 }
6062 /* launch the kernel */
cristy0c832c62014-03-07 22:21:04 +00006063 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass2, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00006064 if (clStatus != CL_SUCCESS)
6065 {
cristy0c832c62014-03-07 22:21:04 +00006066 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006067 goto cleanup;
6068 }
6069
6070 offset.s[0] = X[k];
6071 offset.s[1] = Y[k];
6072 polarity = -1;
cristy0c832c62014-03-07 22:21:04 +00006073 clStatus = clEnv->library->clSetKernelArg(hullPass1,4,sizeof(cl_int2),(void *)&offset);
6074 clStatus|= clEnv->library->clSetKernelArg(hullPass1,5,sizeof(int),(void *)&polarity);
6075 clStatus|=clEnv->library->clSetKernelArg(hullPass2,4,sizeof(cl_int2),(void *)&offset);
6076 clStatus|=clEnv->library->clSetKernelArg(hullPass2,5,sizeof(int),(void *)&polarity);
cristyf034abb2013-11-24 14:16:14 +00006077
6078 if (k == 3)
cristy0c832c62014-03-07 22:21:04 +00006079 clStatus |=clEnv->library->clSetKernelArg(hullPass2,1,sizeof(cl_mem),(void *)&filteredImageBuffer);
cristyf034abb2013-11-24 14:16:14 +00006080
6081 if (clStatus != CL_SUCCESS)
6082 {
cristy0c832c62014-03-07 22:21:04 +00006083 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006084 goto cleanup;
6085 }
6086 /* launch the kernel */
cristy0c832c62014-03-07 22:21:04 +00006087 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass1, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00006088 if (clStatus != CL_SUCCESS)
6089 {
cristy0c832c62014-03-07 22:21:04 +00006090 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006091 goto cleanup;
6092 }
6093 /* launch the kernel */
cristy0c832c62014-03-07 22:21:04 +00006094 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, hullPass2, 2, NULL, global_work_size, NULL, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00006095 if (clStatus != CL_SUCCESS)
6096 {
cristy0c832c62014-03-07 22:21:04 +00006097 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006098 goto cleanup;
6099 }
6100 }
6101
6102 if (ALIGNED(filteredPixels,CLPixelPacket))
6103 {
dirk8a5cf512014-07-28 20:16:27 +00006104 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00006105 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 +00006106 }
6107 else
6108 {
dirk8a5cf512014-07-28 20:16:27 +00006109 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00006110 clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
cristyf034abb2013-11-24 14:16:14 +00006111 }
6112 if (clStatus != CL_SUCCESS)
6113 {
cristya22457d2013-12-07 14:03:06 +00006114 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristyf034abb2013-11-24 14:16:14 +00006115 goto cleanup;
6116 }
6117
dirk832becc2014-08-04 19:44:34 +00006118 outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
6119
6120cleanup:
6121 OpenCLLogException(__FUNCTION__,__LINE__,exception);
6122
6123 image_view=DestroyCacheView(image_view);
6124 if (filteredImage_view != NULL)
dirk8a5cf512014-07-28 20:16:27 +00006125 filteredImage_view=DestroyCacheView(filteredImage_view);
cristya22457d2013-12-07 14:03:06 +00006126
cristyf034abb2013-11-24 14:16:14 +00006127 if (queue != NULL) RelinquishOpenCLCommandQueue(clEnv, queue);
dirk8a5cf512014-07-28 20:16:27 +00006128 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
cristyf034abb2013-11-24 14:16:14 +00006129 for (k = 0; k < 2; k++)
6130 {
cristy0c832c62014-03-07 22:21:04 +00006131 if (tempImageBuffer[k]!=NULL) clEnv->library->clReleaseMemObject(tempImageBuffer[k]);
cristyf034abb2013-11-24 14:16:14 +00006132 }
cristy0c832c62014-03-07 22:21:04 +00006133 if (filteredImageBuffer!=NULL) clEnv->library->clReleaseMemObject(filteredImageBuffer);
cristyf034abb2013-11-24 14:16:14 +00006134 if (hullPass1!=NULL) RelinquishOpenCLKernel(clEnv, hullPass1);
6135 if (hullPass2!=NULL) RelinquishOpenCLKernel(clEnv, hullPass2);
dirk8a5cf512014-07-28 20:16:27 +00006136 if (outputReady == MagickFalse && filteredImage != NULL)
6137 filteredImage=DestroyImage(filteredImage);
6138 return(filteredImage);
cristyf034abb2013-11-24 14:16:14 +00006139}
6140
dirk832becc2014-08-04 19:44:34 +00006141MagickExport Image *AccelerateDespeckleImage(const Image* image,
6142 ExceptionInfo* exception)
6143{
6144 Image
6145 *filteredImage;
6146
6147 assert(image != NULL);
6148 assert(exception != (ExceptionInfo *) NULL);
6149
6150 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
6151 (checkAccelerateCondition(image, AllChannels) == MagickFalse))
6152 return NULL;
6153
6154 filteredImage=ComputeDespeckleImage(image,exception);
6155 return(filteredImage);
cristyf034abb2013-11-24 14:16:14 +00006156}
6157
dirk832becc2014-08-04 19:44:34 +00006158static Image *ComputeAddNoiseImage(const Image *image,
6159 const ChannelType channel,const NoiseType noise_type,
6160 ExceptionInfo *exception)
6161{
6162 CacheView
6163 *filteredImage_view,
6164 *image_view;
6165
6166 cl_command_queue
6167 queue;
6168
6169 cl_context
6170 context;
6171
6172 cl_int
dirk859503c2014-08-13 20:39:42 +00006173 inputPixelCount,
6174 pixelsPerWorkitem,
dirk832becc2014-08-04 19:44:34 +00006175 clStatus;
6176
dirk859503c2014-08-13 20:39:42 +00006177 cl_uint
6178 seed0,
6179 seed1;
6180
dirk832becc2014-08-04 19:44:34 +00006181 cl_kernel
6182 addNoiseKernel;
6183
6184 cl_mem_flags
6185 mem_flags;
6186
6187 cl_mem
6188 filteredImageBuffer,
dirk859503c2014-08-13 20:39:42 +00006189 imageBuffer;
dirk832becc2014-08-04 19:44:34 +00006190
6191 const char
6192 *option;
6193
6194 const void
6195 *inputPixels;
6196
6197 float
dirk859503c2014-08-13 20:39:42 +00006198 attenuate;
dirk832becc2014-08-04 19:44:34 +00006199
6200 MagickBooleanType
6201 outputReady;
6202
6203 MagickCLEnv
6204 clEnv;
6205
6206 MagickSizeType
6207 length;
6208
6209 Image
6210 *filteredImage;
6211
dirk832becc2014-08-04 19:44:34 +00006212 RandomInfo
6213 **restrict random_info;
6214
6215 size_t
dirk859503c2014-08-13 20:39:42 +00006216 global_work_size[1],
6217 local_work_size[1];
dirk832becc2014-08-04 19:44:34 +00006218
6219 unsigned int
dirk832becc2014-08-04 19:44:34 +00006220 k,
dirk859503c2014-08-13 20:39:42 +00006221 numRandomNumberPerPixel;
dirk832becc2014-08-04 19:44:34 +00006222
6223#if defined(MAGICKCORE_OPENMP_SUPPORT)
6224 unsigned long
6225 key;
6226#endif
6227
6228 void
6229 *filteredPixels,
6230 *hostPtr;
6231
6232 outputReady = MagickFalse;
6233 clEnv = NULL;
6234 inputPixels = NULL;
6235 filteredImage = NULL;
6236 filteredImage_view = NULL;
6237 filteredPixels = NULL;
dirk832becc2014-08-04 19:44:34 +00006238 context = NULL;
6239 imageBuffer = NULL;
dirk832becc2014-08-04 19:44:34 +00006240 filteredImageBuffer = NULL;
6241 queue = NULL;
6242 addNoiseKernel = NULL;
6243
6244 clEnv = GetDefaultOpenCLEnv();
6245 context = GetOpenCLContext(clEnv);
6246 queue = AcquireOpenCLCommandQueue(clEnv);
6247
6248 image_view=AcquireVirtualCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00006249 inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
cristye85d0f72013-11-27 02:25:43 +00006250 if (inputPixels == (void *) NULL)
6251 {
dirk8a5cf512014-07-28 20:16:27 +00006252 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristye85d0f72013-11-27 02:25:43 +00006253 goto cleanup;
6254 }
6255
6256 if (ALIGNED(inputPixels,CLPixelPacket))
6257 {
6258 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
6259 }
6260 else
6261 {
6262 mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
6263 }
6264 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00006265 length = image->columns * image->rows;
6266 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristye85d0f72013-11-27 02:25:43 +00006267 if (clStatus != CL_SUCCESS)
6268 {
cristy0c832c62014-03-07 22:21:04 +00006269 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristye85d0f72013-11-27 02:25:43 +00006270 goto cleanup;
6271 }
6272
6273
dirk8a5cf512014-07-28 20:16:27 +00006274 filteredImage = CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristye85d0f72013-11-27 02:25:43 +00006275 assert(filteredImage != NULL);
dirk8a5cf512014-07-28 20:16:27 +00006276 if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
cristye85d0f72013-11-27 02:25:43 +00006277 {
cristya22457d2013-12-07 14:03:06 +00006278 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "CloneImage failed.", "'%s'", ".");
cristye85d0f72013-11-27 02:25:43 +00006279 goto cleanup;
6280 }
dirk832becc2014-08-04 19:44:34 +00006281 filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
dirk8a5cf512014-07-28 20:16:27 +00006282 filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
cristye85d0f72013-11-27 02:25:43 +00006283 if (filteredPixels == (void *) NULL)
6284 {
cristya22457d2013-12-07 14:03:06 +00006285 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning, "UnableToReadPixelCache.","`%s'",filteredImage->filename);
cristye85d0f72013-11-27 02:25:43 +00006286 goto cleanup;
6287 }
6288
6289 if (ALIGNED(filteredPixels,CLPixelPacket))
6290 {
6291 mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
6292 hostPtr = filteredPixels;
6293 }
6294 else
6295 {
6296 mem_flags = CL_MEM_WRITE_ONLY;
6297 hostPtr = NULL;
6298 }
6299 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00006300 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00006301 filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), hostPtr, &clStatus);
cristye85d0f72013-11-27 02:25:43 +00006302 if (clStatus != CL_SUCCESS)
6303 {
cristy0c832c62014-03-07 22:21:04 +00006304 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
cristye85d0f72013-11-27 02:25:43 +00006305 goto cleanup;
6306 }
6307
6308 /* find out how many random numbers needed by pixel */
6309 numRandomNumberPerPixel = 0;
6310 {
6311 unsigned int numRandPerChannel = 0;
6312 switch (noise_type)
6313 {
6314 case UniformNoise:
6315 case ImpulseNoise:
6316 case LaplacianNoise:
6317 case RandomNoise:
6318 default:
6319 numRandPerChannel = 1;
6320 break;
6321 case GaussianNoise:
6322 case MultiplicativeGaussianNoise:
6323 case PoissonNoise:
6324 numRandPerChannel = 2;
6325 break;
6326 };
6327
6328 if ((channel & RedChannel) != 0)
6329 numRandomNumberPerPixel+=numRandPerChannel;
6330 if ((channel & GreenChannel) != 0)
6331 numRandomNumberPerPixel+=numRandPerChannel;
6332 if ((channel & BlueChannel) != 0)
6333 numRandomNumberPerPixel+=numRandPerChannel;
6334 if ((channel & OpacityChannel) != 0)
6335 numRandomNumberPerPixel+=numRandPerChannel;
6336 }
6337
cristye85d0f72013-11-27 02:25:43 +00006338 /* set up the random number generators */
6339 attenuate=1.0;
dirk8a5cf512014-07-28 20:16:27 +00006340 option=GetImageArtifact(image,"attenuate");
cristye85d0f72013-11-27 02:25:43 +00006341 if (option != (char *) NULL)
6342 attenuate=StringToDouble(option,(char **) NULL);
6343 random_info=AcquireRandomInfoThreadSet();
6344#if defined(MAGICKCORE_OPENMP_SUPPORT)
6345 key=GetRandomSecretKey(random_info[0]);
6346#endif
6347
dirk859503c2014-08-13 20:39:42 +00006348 addNoiseKernel = AcquireOpenCLKernel(clEnv,MAGICK_OPENCL_ACCELERATE,"GenerateNoiseImage");
6349
6350 {
6351 cl_uint computeUnitCount;
6352 cl_uint workItemCount;
6353 clEnv->library->clGetDeviceInfo(clEnv->device, CL_DEVICE_MAX_COMPUTE_UNITS, sizeof(cl_uint), &computeUnitCount, NULL);
6354 workItemCount = computeUnitCount * 2 * 256; // 256 work items per group, 2 groups per CU
6355 inputPixelCount = image->columns * image->rows;
6356 pixelsPerWorkitem = (inputPixelCount + workItemCount - 1) / workItemCount;
6357 pixelsPerWorkitem = ((pixelsPerWorkitem + 3) / 4) * 4;
6358
6359 local_work_size[0] = 256;
6360 global_work_size[0] = workItemCount;
6361 }
6362 {
6363 RandomInfo* randomInfo = AcquireRandomInfo();
6364 const unsigned long* s = GetRandomInfoSeed(randomInfo);
6365 seed0 = s[0];
6366 GetPseudoRandomValue(randomInfo);
6367 seed1 = s[0];
6368 randomInfo = DestroyRandomInfo(randomInfo);
6369 }
cristye85d0f72013-11-27 02:25:43 +00006370
6371 k = 0;
dirk8a5cf512014-07-28 20:16:27 +00006372 clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(cl_mem),(void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00006373 clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(cl_mem),(void *)&filteredImageBuffer);
dirk859503c2014-08-13 20:39:42 +00006374 clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(cl_uint),(void *)&inputPixelCount);
6375 clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(cl_uint),(void *)&pixelsPerWorkitem);
cristy0c832c62014-03-07 22:21:04 +00006376 clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(ChannelType),(void *)&channel);
6377 clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(NoiseType),(void *)&noise_type);
cristye85d0f72013-11-27 02:25:43 +00006378 attenuate=1.0f;
dirk8a5cf512014-07-28 20:16:27 +00006379 option=GetImageArtifact(image,"attenuate");
cristye85d0f72013-11-27 02:25:43 +00006380 if (option != (char *) NULL)
6381 attenuate=(float)StringToDouble(option,(char **) NULL);
cristy0c832c62014-03-07 22:21:04 +00006382 clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(float),(void *)&attenuate);
dirk859503c2014-08-13 20:39:42 +00006383 clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(cl_uint),(void *)&seed0);
6384 clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(cl_uint),(void *)&seed1);
cristy0c832c62014-03-07 22:21:04 +00006385 clEnv->library->clSetKernelArg(addNoiseKernel,k++,sizeof(unsigned int),(void *)&numRandomNumberPerPixel);
cristye85d0f72013-11-27 02:25:43 +00006386
dirk859503c2014-08-13 20:39:42 +00006387 clEnv->library->clEnqueueNDRangeKernel(queue,addNoiseKernel,1,NULL,global_work_size,NULL,0,NULL,NULL);
cristye85d0f72013-11-27 02:25:43 +00006388
6389 if (ALIGNED(filteredPixels,CLPixelPacket))
6390 {
dirk8a5cf512014-07-28 20:16:27 +00006391 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00006392 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 +00006393 }
6394 else
6395 {
dirk8a5cf512014-07-28 20:16:27 +00006396 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00006397 clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
cristye85d0f72013-11-27 02:25:43 +00006398 }
6399 if (clStatus != CL_SUCCESS)
6400 {
cristya22457d2013-12-07 14:03:06 +00006401 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
cristye85d0f72013-11-27 02:25:43 +00006402 goto cleanup;
6403 }
6404
dirk832becc2014-08-04 19:44:34 +00006405 outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
6406
6407cleanup:
6408 OpenCLLogException(__FUNCTION__,__LINE__,exception);
6409
6410 image_view=DestroyCacheView(image_view);
6411 if (filteredImage_view != NULL)
dirk8a5cf512014-07-28 20:16:27 +00006412 filteredImage_view=DestroyCacheView(filteredImage_view);
cristya22457d2013-12-07 14:03:06 +00006413
cristye85d0f72013-11-27 02:25:43 +00006414 if (queue!=NULL) RelinquishOpenCLCommandQueue(clEnv, queue);
6415 if (addNoiseKernel!=NULL) RelinquishOpenCLKernel(clEnv, addNoiseKernel);
dirk8a5cf512014-07-28 20:16:27 +00006416 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00006417 if (filteredImageBuffer!=NULL) clEnv->library->clReleaseMemObject(filteredImageBuffer);
dirk832becc2014-08-04 19:44:34 +00006418 if (outputReady == MagickFalse && filteredImage != NULL)
6419 filteredImage=DestroyImage(filteredImage);
6420
dirk8a5cf512014-07-28 20:16:27 +00006421 return(filteredImage);
cristye85d0f72013-11-27 02:25:43 +00006422}
6423
cristye85d0f72013-11-27 02:25:43 +00006424
dirk832becc2014-08-04 19:44:34 +00006425MagickExport Image *AccelerateAddNoiseImage(const Image *image,
6426 const ChannelType channel,const NoiseType noise_type,
6427 ExceptionInfo *exception)
6428{
6429 Image
6430 *filteredImage;
6431
6432 assert(image != NULL);
6433 assert(exception != (ExceptionInfo *) NULL);
6434
6435 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
6436 (checkAccelerateCondition(image, channel) == MagickFalse))
6437 return NULL;
6438
dirk859503c2014-08-13 20:39:42 +00006439 filteredImage = ComputeAddNoiseImage(image,channel,noise_type,exception);
dirk832becc2014-08-04 19:44:34 +00006440
6441 return(filteredImage);
cristye85d0f72013-11-27 02:25:43 +00006442}
6443
dirk832becc2014-08-04 19:44:34 +00006444static MagickBooleanType LaunchRandomImageKernel(MagickCLEnv clEnv,
6445 cl_command_queue queue,cl_mem imageBuffer,const unsigned int imageColumns,
6446 const unsigned int imageRows,cl_mem seedBuffer,
6447 const unsigned int numGenerators,ExceptionInfo *exception)
6448{
6449 int
6450 k;
6451
6452 cl_int
6453 clStatus;
6454
6455 cl_kernel
6456 randomImageKernel;
6457
6458 MagickBooleanType
6459 status;
6460
6461 size_t
6462 global_work_size,
6463 local_work_size;
6464
dirk8a5cf512014-07-28 20:16:27 +00006465 status = MagickFalse;
cristy0c832c62014-03-07 22:21:04 +00006466 randomImageKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE, "RandomImage");
6467
6468 k = 0;
dirk8a5cf512014-07-28 20:16:27 +00006469 clEnv->library->clSetKernelArg(randomImageKernel,k++,sizeof(cl_mem),(void*)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00006470 clEnv->library->clSetKernelArg(randomImageKernel,k++,sizeof(cl_uint),(void*)&imageColumns);
6471 clEnv->library->clSetKernelArg(randomImageKernel,k++,sizeof(cl_uint),(void*)&imageRows);
6472 clEnv->library->clSetKernelArg(randomImageKernel,k++,sizeof(cl_mem),(void*)&seedBuffer);
6473 {
6474 const float randNormNumerator = 1.0f;
6475 const unsigned int randNormDenominator = (unsigned int)(~0UL);
6476 clEnv->library->clSetKernelArg(randomImageKernel,k++,
6477 sizeof(float),(void*)&randNormNumerator);
6478 clEnv->library->clSetKernelArg(randomImageKernel,k++,
6479 sizeof(cl_uint),(void*)&randNormDenominator);
6480 }
6481
6482
6483 global_work_size = numGenerators;
6484 local_work_size = 64;
6485
6486 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue,randomImageKernel,1,NULL,&global_work_size,
6487 &local_work_size,0,NULL,NULL);
6488
6489 if (clStatus != CL_SUCCESS)
6490 {
6491 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning,
6492 "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
6493 goto cleanup;
6494 }
6495 status = MagickTrue;
6496
6497cleanup:
6498 if (randomImageKernel!=NULL) RelinquishOpenCLKernel(clEnv, randomImageKernel);
dirk832becc2014-08-04 19:44:34 +00006499 return(status);
6500}
6501
6502static MagickBooleanType ComputeRandomImage(Image* image,
6503 ExceptionInfo* exception)
6504{
6505 CacheView
6506 *image_view;
6507
6508 cl_command_queue
6509 queue;
6510
6511 cl_context
6512 context;
6513
6514 cl_int
6515 clStatus;
6516
6517 /* Don't release this buffer in this function !!! */
6518 cl_mem
6519 randomNumberSeedsBuffer;
6520
6521 cl_mem_flags
6522 mem_flags;
6523
6524 cl_mem
6525 imageBuffer;
6526
6527 MagickBooleanType
6528 outputReady,
6529 status;
6530
6531 MagickCLEnv
6532 clEnv;
6533
6534 MagickSizeType
6535 length;
6536
6537 void
6538 *inputPixels;
6539
6540 status = MagickFalse;
6541 outputReady = MagickFalse;
6542 inputPixels = NULL;
6543 context = NULL;
6544 imageBuffer = NULL;
6545 queue = NULL;
6546
6547 clEnv = GetDefaultOpenCLEnv();
6548 context = GetOpenCLContext(clEnv);
6549
6550 /* Create and initialize OpenCL buffers. */
6551 image_view=AcquireAuthenticCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00006552 inputPixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
cristy0c832c62014-03-07 22:21:04 +00006553 if (inputPixels == (void *) NULL)
6554 {
dirk8a5cf512014-07-28 20:16:27 +00006555 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,"UnableToReadPixelCache.","`%s'",image->filename);
cristy0c832c62014-03-07 22:21:04 +00006556 goto cleanup;
6557 }
6558
6559 /* If the host pointer is aligned to the size of CLPixelPacket,
6560 then use the host buffer directly from the GPU; otherwise,
6561 create a buffer on the GPU and copy the data over */
6562 if (ALIGNED(inputPixels,CLPixelPacket))
6563 {
6564 mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
6565 }
6566 else
6567 {
6568 mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
6569 }
6570 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00006571 length = image->columns * image->rows;
6572 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags, length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
cristy0c832c62014-03-07 22:21:04 +00006573 if (clStatus != CL_SUCCESS)
6574 {
6575 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
6576 goto cleanup;
6577 }
6578
6579 queue = AcquireOpenCLCommandQueue(clEnv);
6580
6581 randomNumberSeedsBuffer = GetAndLockRandSeedBuffer(clEnv);
6582 if (randomNumberSeedsBuffer==NULL)
6583 {
6584 (void) OpenCLThrowMagickException(exception, GetMagickModule(),
6585 ResourceLimitWarning, "Failed to get GPU random number generators.",
6586 "'%s'", ".");
6587 goto cleanup;
6588 }
6589
6590 status = LaunchRandomImageKernel(clEnv,queue,
dirk8a5cf512014-07-28 20:16:27 +00006591 imageBuffer,
6592 image->columns,
6593 image->rows,
cristy0c832c62014-03-07 22:21:04 +00006594 randomNumberSeedsBuffer,
6595 GetNumRandGenerators(clEnv),
6596 exception);
6597 if (status==MagickFalse)
6598 {
6599 goto cleanup;
6600 }
6601
6602 if (ALIGNED(inputPixels,CLPixelPacket))
6603 {
dirk8a5cf512014-07-28 20:16:27 +00006604 length = image->columns * image->rows;
6605 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 +00006606 }
6607 else
6608 {
dirk8a5cf512014-07-28 20:16:27 +00006609 length = image->columns * image->rows;
6610 clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0, length * sizeof(CLPixelPacket), inputPixels, 0, NULL, NULL);
cristy0c832c62014-03-07 22:21:04 +00006611 }
6612 if (clStatus != CL_SUCCESS)
6613 {
6614 (void) OpenCLThrowMagickException(exception, GetMagickModule(), ResourceLimitWarning, "Reading output image from CL buffer failed.", "'%s'", ".");
6615 goto cleanup;
6616 }
dirk832becc2014-08-04 19:44:34 +00006617 outputReady=SyncCacheViewAuthenticPixels(image_view,exception);
6618
6619cleanup:
6620 OpenCLLogException(__FUNCTION__,__LINE__,exception);
6621
dirk8a5cf512014-07-28 20:16:27 +00006622 image_view=DestroyCacheView(image_view);
cristy0c832c62014-03-07 22:21:04 +00006623
6624 UnlockRandSeedBuffer(clEnv);
dirk8a5cf512014-07-28 20:16:27 +00006625 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00006626 if (queue != NULL) RelinquishOpenCLCommandQueue(clEnv, queue);
6627 return outputReady;
6628}
6629
dirk832becc2014-08-04 19:44:34 +00006630MagickExport MagickBooleanType AccelerateRandomImage(Image *image,
6631 ExceptionInfo* exception)
6632{
6633 MagickBooleanType
6634 status;
6635
6636 assert(image != NULL);
6637 assert(exception != (ExceptionInfo *) NULL);
6638
6639 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
6640 (checkAccelerateCondition(image, AllChannels) == MagickFalse))
6641 return(MagickFalse);
6642
6643 status=ComputeRandomImage(image,exception);
6644 return(status);
cristy0c832c62014-03-07 22:21:04 +00006645}
6646
dirk832becc2014-08-04 19:44:34 +00006647static Image* ComputeMotionBlurImage(const Image *image,
6648 const ChannelType channel,const double *kernel,const size_t width,
6649 const OffsetInfo *offset,ExceptionInfo *exception)
6650{
6651 CacheView
6652 *filteredImage_view,
6653 *image_view;
6654
6655 cl_command_queue
6656 queue;
6657
6658 cl_context
6659 context;
6660
6661 cl_float4
6662 biasPixel;
6663
6664 cl_int
6665 clStatus;
6666
6667 cl_kernel
6668 motionBlurKernel;
6669
6670 cl_mem
6671 filteredImageBuffer,
6672 imageBuffer,
6673 imageKernelBuffer,
6674 offsetBuffer;
6675
6676 cl_mem_flags
6677 mem_flags;
6678
6679 const void
6680 *inputPixels;
6681
6682 float
6683 *kernelBufferPtr;
6684
6685 Image
6686 *filteredImage;
6687
6688 int
6689 *offsetBufferPtr;
6690
6691 MagickBooleanType
6692 outputReady;
6693
6694 MagickCLEnv
6695 clEnv;
6696
6697 PixelInfo
6698 bias;
6699
6700 MagickSizeType
6701 length;
6702
6703 size_t
6704 global_work_size[2],
6705 local_work_size[2];
6706
6707 unsigned int
6708 i,
6709 imageHeight,
6710 imageWidth,
6711 matte;
6712
6713 void
6714 *filteredPixels,
6715 *hostPtr;
6716
6717 outputReady = MagickFalse;
6718 context = NULL;
6719 filteredImage = NULL;
6720 filteredImage_view = NULL;
6721 imageBuffer = NULL;
6722 filteredImageBuffer = NULL;
6723 imageKernelBuffer = NULL;
6724 motionBlurKernel = NULL;
6725 queue = NULL;
6726
6727 clEnv = GetDefaultOpenCLEnv();
6728 context = GetOpenCLContext(clEnv);
6729
6730 /* Create and initialize OpenCL buffers. */
6731
6732 image_view=AcquireVirtualCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00006733 inputPixels=GetCacheViewVirtualPixels(image_view,0,0,image->columns,image->rows,exception);
cristy0c832c62014-03-07 22:21:04 +00006734 if (inputPixels == (const void *) NULL)
6735 {
6736 (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
dirk8a5cf512014-07-28 20:16:27 +00006737 "UnableToReadPixelCache.","`%s'",image->filename);
cristy0c832c62014-03-07 22:21:04 +00006738 goto cleanup;
6739 }
6740
6741 // If the host pointer is aligned to the size of CLPixelPacket,
6742 // then use the host buffer directly from the GPU; otherwise,
6743 // create a buffer on the GPU and copy the data over
6744 if (ALIGNED(inputPixels,CLPixelPacket))
6745 {
6746 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
6747 }
6748 else
6749 {
6750 mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
6751 }
6752 // create a CL buffer from image pixel buffer
dirk8a5cf512014-07-28 20:16:27 +00006753 length = image->columns * image->rows;
6754 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags,
cristy0c832c62014-03-07 22:21:04 +00006755 length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
6756 if (clStatus != CL_SUCCESS)
6757 {
6758 (void) ThrowMagickException(exception, GetMagickModule(),
6759 ResourceLimitError, "clEnv->library->clCreateBuffer failed.",".");
6760 goto cleanup;
6761 }
6762
6763
dirk8a5cf512014-07-28 20:16:27 +00006764 filteredImage = CloneImage(image,image->columns,image->rows,
cristy0c832c62014-03-07 22:21:04 +00006765 MagickTrue,exception);
6766 assert(filteredImage != NULL);
dirk8a5cf512014-07-28 20:16:27 +00006767 if (SetImageStorageClass(filteredImage,DirectClass,exception) != MagickTrue)
cristy0c832c62014-03-07 22:21:04 +00006768 {
6769 (void) ThrowMagickException(exception, GetMagickModule(),
6770 ResourceLimitError, "CloneImage failed.", "'%s'", ".");
6771 goto cleanup;
6772 }
dirk832becc2014-08-04 19:44:34 +00006773 filteredImage_view=AcquireAuthenticCacheView(filteredImage,exception);
dirk8a5cf512014-07-28 20:16:27 +00006774 filteredPixels=GetCacheViewAuthenticPixels(filteredImage_view,0,0,filteredImage->columns,filteredImage->rows,exception);
cristy0c832c62014-03-07 22:21:04 +00006775 if (filteredPixels == (void *) NULL)
6776 {
6777 (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
6778 "UnableToReadPixelCache.","`%s'",filteredImage->filename);
6779 goto cleanup;
6780 }
6781
6782 if (ALIGNED(filteredPixels,CLPixelPacket))
6783 {
6784 mem_flags = CL_MEM_WRITE_ONLY|CL_MEM_USE_HOST_PTR;
6785 hostPtr = filteredPixels;
6786 }
6787 else
6788 {
6789 mem_flags = CL_MEM_WRITE_ONLY;
6790 hostPtr = NULL;
6791 }
6792 // create a CL buffer from image pixel buffer
dirk8a5cf512014-07-28 20:16:27 +00006793 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00006794 filteredImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags,
6795 length * sizeof(CLPixelPacket), hostPtr, &clStatus);
6796 if (clStatus != CL_SUCCESS)
6797 {
6798 (void) ThrowMagickException(exception, GetMagickModule(),
6799 ResourceLimitError, "clEnv->library->clCreateBuffer failed.",".");
6800 goto cleanup;
6801 }
6802
6803
6804 imageKernelBuffer = clEnv->library->clCreateBuffer(context,
6805 CL_MEM_READ_ONLY|CL_MEM_ALLOC_HOST_PTR, width * sizeof(float), NULL,
6806 &clStatus);
6807 if (clStatus != CL_SUCCESS)
6808 {
6809 (void) ThrowMagickException(exception, GetMagickModule(),
6810 ResourceLimitError, "clEnv->library->clCreateBuffer failed.",".");
6811 goto cleanup;
6812 }
6813
6814 queue = AcquireOpenCLCommandQueue(clEnv);
6815 kernelBufferPtr = (float*)clEnv->library->clEnqueueMapBuffer(queue, imageKernelBuffer,
6816 CL_TRUE, CL_MAP_WRITE, 0, width * sizeof(float), 0, NULL, NULL, &clStatus);
6817 if (clStatus != CL_SUCCESS)
6818 {
6819 (void) ThrowMagickException(exception, GetMagickModule(),
6820 ResourceLimitError, "clEnv->library->clEnqueueMapBuffer failed.",".");
6821 goto cleanup;
6822 }
6823 for (i = 0; i < width; i++)
6824 {
6825 kernelBufferPtr[i] = (float) kernel[i];
6826 }
6827 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, imageKernelBuffer, kernelBufferPtr,
6828 0, NULL, NULL);
6829 if (clStatus != CL_SUCCESS)
6830 {
6831 (void) ThrowMagickException(exception, GetMagickModule(), ModuleFatalError,
6832 "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
6833 goto cleanup;
6834 }
6835
6836 offsetBuffer = clEnv->library->clCreateBuffer(context,
6837 CL_MEM_READ_ONLY|CL_MEM_ALLOC_HOST_PTR, width * sizeof(cl_int2), NULL,
6838 &clStatus);
6839 if (clStatus != CL_SUCCESS)
6840 {
6841 (void) ThrowMagickException(exception, GetMagickModule(),
6842 ResourceLimitError, "clEnv->library->clCreateBuffer failed.",".");
6843 goto cleanup;
6844 }
6845
6846 offsetBufferPtr = (int*)clEnv->library->clEnqueueMapBuffer(queue, offsetBuffer, CL_TRUE,
6847 CL_MAP_WRITE, 0, width * sizeof(cl_int2), 0, NULL, NULL, &clStatus);
6848 if (clStatus != CL_SUCCESS)
6849 {
6850 (void) ThrowMagickException(exception, GetMagickModule(),
6851 ResourceLimitError, "clEnv->library->clEnqueueMapBuffer failed.",".");
6852 goto cleanup;
6853 }
6854 for (i = 0; i < width; i++)
6855 {
6856 offsetBufferPtr[2*i] = (int)offset[i].x;
6857 offsetBufferPtr[2*i+1] = (int)offset[i].y;
6858 }
6859 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, offsetBuffer, offsetBufferPtr, 0,
6860 NULL, NULL);
6861 if (clStatus != CL_SUCCESS)
6862 {
6863 (void) ThrowMagickException(exception, GetMagickModule(), ModuleFatalError,
6864 "clEnv->library->clEnqueueUnmapMemObject failed.", "'%s'", ".");
6865 goto cleanup;
6866 }
6867
6868
6869 // get the OpenCL kernel
6870 motionBlurKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE,
6871 "MotionBlur");
6872 if (motionBlurKernel == NULL)
6873 {
6874 (void) ThrowMagickException(exception, GetMagickModule(), ModuleFatalError,
6875 "AcquireOpenCLKernel failed.", "'%s'", ".");
6876 goto cleanup;
6877 }
6878
6879 // set the kernel arguments
6880 i = 0;
6881 clStatus=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(cl_mem),
dirk8a5cf512014-07-28 20:16:27 +00006882 (void *)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00006883 clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(cl_mem),
6884 (void *)&filteredImageBuffer);
dirk8a5cf512014-07-28 20:16:27 +00006885 imageWidth = image->columns;
6886 imageHeight = image->rows;
cristy0c832c62014-03-07 22:21:04 +00006887 clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(unsigned int),
6888 &imageWidth);
6889 clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(unsigned int),
6890 &imageHeight);
6891 clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(cl_mem),
6892 (void *)&imageKernelBuffer);
6893 clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(unsigned int),
6894 &width);
6895 clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(cl_mem),
6896 (void *)&offsetBuffer);
6897
dirk8a5cf512014-07-28 20:16:27 +00006898 GetPixelInfo(image,&bias);
cristy0c832c62014-03-07 22:21:04 +00006899 biasPixel.s[0] = bias.red;
6900 biasPixel.s[1] = bias.green;
6901 biasPixel.s[2] = bias.blue;
dirk8a5cf512014-07-28 20:16:27 +00006902 biasPixel.s[3] = bias.alpha;
cristy0c832c62014-03-07 22:21:04 +00006903 clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(cl_float4), &biasPixel);
6904
6905 clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(ChannelType), &channel);
dirk8a5cf512014-07-28 20:16:27 +00006906 matte = (image->alpha_trait==BlendPixelTrait)?1:0;
cristy0c832c62014-03-07 22:21:04 +00006907 clStatus|=clEnv->library->clSetKernelArg(motionBlurKernel,i++,sizeof(unsigned int), &matte);
6908 if (clStatus != CL_SUCCESS)
6909 {
6910 (void) ThrowMagickException(exception, GetMagickModule(), ModuleFatalError,
6911 "clEnv->library->clSetKernelArg failed.", "'%s'", ".");
6912 goto cleanup;
6913 }
6914
6915 // launch the kernel
6916 local_work_size[0] = 16;
6917 local_work_size[1] = 16;
6918 global_work_size[0] = (size_t)padGlobalWorkgroupSizeToLocalWorkgroupSize(
dirk8a5cf512014-07-28 20:16:27 +00006919 image->columns,local_work_size[0]);
cristy0c832c62014-03-07 22:21:04 +00006920 global_work_size[1] = (size_t)padGlobalWorkgroupSizeToLocalWorkgroupSize(
dirk8a5cf512014-07-28 20:16:27 +00006921 image->rows,local_work_size[1]);
cristy0c832c62014-03-07 22:21:04 +00006922 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, motionBlurKernel, 2, NULL,
6923 global_work_size, local_work_size, 0, NULL, NULL);
6924
6925 if (clStatus != CL_SUCCESS)
6926 {
6927 (void) ThrowMagickException(exception, GetMagickModule(), ModuleFatalError,
6928 "clEnv->library->clEnqueueNDRangeKernel failed.", "'%s'", ".");
6929 goto cleanup;
6930 }
6931 clEnv->library->clFlush(queue);
6932
6933 if (ALIGNED(filteredPixels,CLPixelPacket))
6934 {
dirk8a5cf512014-07-28 20:16:27 +00006935 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00006936 clEnv->library->clEnqueueMapBuffer(queue, filteredImageBuffer, CL_TRUE,
6937 CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL,
6938 NULL, &clStatus);
6939 }
6940 else
6941 {
dirk8a5cf512014-07-28 20:16:27 +00006942 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00006943 clStatus = clEnv->library->clEnqueueReadBuffer(queue, filteredImageBuffer, CL_TRUE, 0,
6944 length * sizeof(CLPixelPacket), filteredPixels, 0, NULL, NULL);
6945 }
6946 if (clStatus != CL_SUCCESS)
6947 {
6948 (void) ThrowMagickException(exception, GetMagickModule(), ModuleFatalError,
6949 "Reading output image from CL buffer failed.", "'%s'", ".");
6950 goto cleanup;
6951 }
dirk832becc2014-08-04 19:44:34 +00006952 outputReady=SyncCacheViewAuthenticPixels(filteredImage_view,exception);
6953
6954cleanup:
6955
6956 image_view=DestroyCacheView(image_view);
6957 if (filteredImage_view != NULL)
dirk8a5cf512014-07-28 20:16:27 +00006958 filteredImage_view=DestroyCacheView(filteredImage_view);
cristy0c832c62014-03-07 22:21:04 +00006959
6960 if (filteredImageBuffer!=NULL) clEnv->library->clReleaseMemObject(filteredImageBuffer);
dirk8a5cf512014-07-28 20:16:27 +00006961 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00006962 if (imageKernelBuffer!=NULL) clEnv->library->clReleaseMemObject(imageKernelBuffer);
6963 if (motionBlurKernel!=NULL) RelinquishOpenCLKernel(clEnv, motionBlurKernel);
6964 if (queue != NULL) RelinquishOpenCLCommandQueue(clEnv, queue);
dirk832becc2014-08-04 19:44:34 +00006965 if (outputReady == MagickFalse && filteredImage != NULL)
6966 filteredImage=DestroyImage(filteredImage);
6967
dirk8a5cf512014-07-28 20:16:27 +00006968 return(filteredImage);
cristy0c832c62014-03-07 22:21:04 +00006969}
6970
dirk832becc2014-08-04 19:44:34 +00006971MagickExport Image *AccelerateMotionBlurImage(const Image *image,
6972 const ChannelType channel,const double* kernel,const size_t width,
6973 const OffsetInfo *offset,ExceptionInfo *exception)
6974{
6975 Image
6976 *filteredImage;
6977
6978 assert(image != NULL);
6979 assert(kernel != (double *) NULL);
6980 assert(offset != (OffsetInfo *) NULL);
6981 assert(exception != (ExceptionInfo *) NULL);
6982
6983 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
6984 (checkAccelerateCondition(image, channel) == MagickFalse))
6985 return NULL;
6986
6987 filteredImage=ComputeMotionBlurImage(image, channel, kernel, width,
6988 offset, exception);
6989 return(filteredImage);
cristy0c832c62014-03-07 22:21:04 +00006990}
6991
dirk832becc2014-08-04 19:44:34 +00006992static MagickBooleanType LaunchCompositeKernel(MagickCLEnv clEnv,
6993 cl_command_queue queue,cl_mem imageBuffer,const unsigned int inputWidth,
6994 const unsigned int inputHeight,const unsigned int matte,
6995 const ChannelType channel,const CompositeOperator compose,
6996 const cl_mem compositeImageBuffer,const unsigned int compositeWidth,
6997 const unsigned int compositeHeight,const float destination_dissolve,
6998 const float source_dissolve,ExceptionInfo *magick_unused(exception))
6999{
7000 cl_int
7001 clStatus;
7002
7003 cl_kernel
7004 compositeKernel;
7005
7006 int
7007 k;
7008
7009 size_t
7010 global_work_size[2],
7011 local_work_size[2];
7012
7013 unsigned int
7014 composeOp;
7015
cristy0c832c62014-03-07 22:21:04 +00007016 magick_unreferenced(exception);
7017
7018 compositeKernel = AcquireOpenCLKernel(clEnv, MAGICK_OPENCL_ACCELERATE,
7019 "Composite");
7020
7021 k = 0;
dirk8a5cf512014-07-28 20:16:27 +00007022 clStatus=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(cl_mem),(void*)&imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00007023 clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(unsigned int),(void*)&inputWidth);
7024 clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(unsigned int),(void*)&inputHeight);
7025 clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(cl_mem),(void*)&compositeImageBuffer);
7026 clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(unsigned int),(void*)&compositeWidth);
7027 clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(unsigned int),(void*)&compositeHeight);
7028 composeOp = (unsigned int)compose;
7029 clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(unsigned int),(void*)&composeOp);
7030 clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(ChannelType),(void*)&channel);
7031 clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(unsigned int),(void*)&matte);
7032 clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(float),(void*)&destination_dissolve);
7033 clStatus|=clEnv->library->clSetKernelArg(compositeKernel,k++,sizeof(float),(void*)&source_dissolve);
7034
7035 if (clStatus!=CL_SUCCESS)
7036 return MagickFalse;
7037
7038 local_work_size[0] = 64;
7039 local_work_size[1] = 1;
7040
7041 global_work_size[0] = padGlobalWorkgroupSizeToLocalWorkgroupSize(inputWidth,
7042 local_work_size[0]);
7043 global_work_size[1] = inputHeight;
7044 clStatus = clEnv->library->clEnqueueNDRangeKernel(queue, compositeKernel, 2, NULL,
7045 global_work_size, local_work_size, 0, NULL, NULL);
7046
7047
7048 RelinquishOpenCLKernel(clEnv, compositeKernel);
7049
dirk832becc2014-08-04 19:44:34 +00007050 return((clStatus==CL_SUCCESS) ? MagickTrue : MagickFalse);
7051}
7052
7053static MagickBooleanType ComputeCompositeImage(Image *image,
7054 const ChannelType channel,const CompositeOperator compose,
7055 const Image *compositeImage,const ssize_t magick_unused(x_offset),
7056 const ssize_t magick_unused(y_offset),const float destination_dissolve,
7057 const float source_dissolve,ExceptionInfo *exception)
7058{
7059 CacheView
7060 *image_view;
7061
7062 cl_command_queue
7063 queue;
7064
7065 cl_context
7066 context;
7067
7068 cl_int
7069 clStatus;
7070
7071 cl_mem_flags
7072 mem_flags;
7073
7074 cl_mem
7075 compositeImageBuffer,
7076 imageBuffer;
7077
7078 const void
7079 *composePixels;
7080
7081 MagickBooleanType
7082 outputReady,
7083 status;
7084
7085 MagickCLEnv
7086 clEnv;
7087
7088 MagickSizeType
7089 length;
7090
7091 void
7092 *inputPixels;
7093
7094 magick_unreferenced(x_offset);
7095 magick_unreferenced(y_offset);
7096
7097 status = MagickFalse;
7098 outputReady = MagickFalse;
7099 composePixels = NULL;
7100 imageBuffer = NULL;
7101 compositeImageBuffer = NULL;
7102
7103 clEnv = GetDefaultOpenCLEnv();
7104 context = GetOpenCLContext(clEnv);
7105 queue = AcquireOpenCLCommandQueue(clEnv);
7106
7107 /* Create and initialize OpenCL buffers. */
7108 image_view=AcquireAuthenticCacheView(image,exception);
dirk8a5cf512014-07-28 20:16:27 +00007109 inputPixels=GetCacheViewAuthenticPixels(image_view,0,0,image->columns,image->rows,exception);
cristy0c832c62014-03-07 22:21:04 +00007110 if (inputPixels == (void *) NULL)
7111 {
7112 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,
dirk8a5cf512014-07-28 20:16:27 +00007113 "UnableToReadPixelCache.","`%s'",image->filename);
cristy0c832c62014-03-07 22:21:04 +00007114 goto cleanup;
7115 }
7116
7117 /* If the host pointer is aligned to the size of CLPixelPacket,
7118 then use the host buffer directly from the GPU; otherwise,
7119 create a buffer on the GPU and copy the data over */
7120 if (ALIGNED(inputPixels,CLPixelPacket))
7121 {
7122 mem_flags = CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR;
7123 }
7124 else
7125 {
7126 mem_flags = CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR;
7127 }
7128 /* create a CL buffer from image pixel buffer */
dirk8a5cf512014-07-28 20:16:27 +00007129 length = image->columns * image->rows;
7130 imageBuffer = clEnv->library->clCreateBuffer(context, mem_flags,
cristy0c832c62014-03-07 22:21:04 +00007131 length * sizeof(CLPixelPacket), (void*)inputPixels, &clStatus);
7132 if (clStatus != CL_SUCCESS)
7133 {
7134 (void) OpenCLThrowMagickException(exception, GetMagickModule(),
7135 ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
7136 goto cleanup;
7137 }
7138
7139
7140 /* Create and initialize OpenCL buffers. */
7141 composePixels = AcquirePixelCachePixels(compositeImage, &length, exception);
7142 if (composePixels == (void *) NULL)
7143 {
7144 (void) OpenCLThrowMagickException(exception,GetMagickModule(),CacheWarning,
7145 "UnableToReadPixelCache.","`%s'",compositeImage->filename);
7146 goto cleanup;
7147 }
7148
7149 /* If the host pointer is aligned to the size of CLPixelPacket,
7150 then use the host buffer directly from the GPU; otherwise,
7151 create a buffer on the GPU and copy the data over */
7152 if (ALIGNED(composePixels,CLPixelPacket))
7153 {
7154 mem_flags = CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR;
7155 }
7156 else
7157 {
7158 mem_flags = CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR;
7159 }
7160 /* create a CL buffer from image pixel buffer */
7161 length = compositeImage->columns * compositeImage->rows;
7162 compositeImageBuffer = clEnv->library->clCreateBuffer(context, mem_flags,
7163 length * sizeof(CLPixelPacket), (void*)composePixels, &clStatus);
7164 if (clStatus != CL_SUCCESS)
7165 {
7166 (void) OpenCLThrowMagickException(exception, GetMagickModule(),
7167 ResourceLimitWarning, "clEnv->library->clCreateBuffer failed.",".");
7168 goto cleanup;
7169 }
7170
dirk8a5cf512014-07-28 20:16:27 +00007171 status = LaunchCompositeKernel(clEnv,queue,imageBuffer,
7172 (unsigned int) image->columns,
7173 (unsigned int) image->rows,
7174 (unsigned int) (image->alpha_trait==BlendPixelTrait) ? 1 : 0,
cristy0c832c62014-03-07 22:21:04 +00007175 channel, compose, compositeImageBuffer,
7176 (unsigned int) compositeImage->columns,
7177 (unsigned int) compositeImage->rows,
7178 destination_dissolve,source_dissolve,
7179 exception);
7180
7181 if (status==MagickFalse)
7182 goto cleanup;
7183
dirk8a5cf512014-07-28 20:16:27 +00007184 length = image->columns * image->rows;
cristy0c832c62014-03-07 22:21:04 +00007185 if (ALIGNED(inputPixels,CLPixelPacket))
7186 {
dirk8a5cf512014-07-28 20:16:27 +00007187 clEnv->library->clEnqueueMapBuffer(queue, imageBuffer, CL_TRUE,
cristy0c832c62014-03-07 22:21:04 +00007188 CL_MAP_READ|CL_MAP_WRITE, 0, length * sizeof(CLPixelPacket), 0, NULL,
7189 NULL, &clStatus);
7190 }
7191 else
7192 {
dirk8a5cf512014-07-28 20:16:27 +00007193 clStatus = clEnv->library->clEnqueueReadBuffer(queue, imageBuffer, CL_TRUE, 0,
cristy0c832c62014-03-07 22:21:04 +00007194 length * sizeof(CLPixelPacket), inputPixels, 0, NULL, NULL);
7195 }
7196 if (clStatus==CL_SUCCESS)
dirk832becc2014-08-04 19:44:34 +00007197 outputReady=SyncCacheViewAuthenticPixels(image_view,exception);
7198
7199cleanup:
7200
7201 image_view=DestroyCacheView(image_view);
dirk8a5cf512014-07-28 20:16:27 +00007202 if (imageBuffer!=NULL) clEnv->library->clReleaseMemObject(imageBuffer);
cristy0c832c62014-03-07 22:21:04 +00007203 if (compositeImageBuffer!=NULL) clEnv->library->clReleaseMemObject(compositeImageBuffer);
7204 if (queue != NULL) RelinquishOpenCLCommandQueue(clEnv, queue);
7205
dirk832becc2014-08-04 19:44:34 +00007206 return(outputReady);
7207}
7208
7209MagickExport MagickBooleanType AccelerateCompositeImage(Image *image,
7210 const ChannelType channel,const CompositeOperator compose,
7211 const Image *composite,const ssize_t x_offset,const ssize_t y_offset,
7212 const float destination_dissolve,const float source_dissolve,
7213 ExceptionInfo *exception)
7214{
7215 MagickBooleanType
7216 status;
7217
7218 assert(image != NULL);
7219 assert(exception != (ExceptionInfo *) NULL);
7220
7221 if ((checkOpenCLEnvironment(exception) == MagickFalse) ||
7222 (checkAccelerateCondition(image, channel) == MagickFalse))
dirk8a5cf512014-07-28 20:16:27 +00007223 return(MagickFalse);
cristy0c832c62014-03-07 22:21:04 +00007224
7225 /* only support zero offset and
7226 images with the size for now */
7227 if (x_offset!=0
7228 || y_offset!=0
7229 || image->columns!=composite->columns
7230 || image->rows!=composite->rows)
7231 return MagickFalse;
7232
7233 switch(compose) {
7234 case ColorDodgeCompositeOp:
7235 case BlendCompositeOp:
7236 break;
7237 default:
7238 // unsupported compose operator, quit
7239 return MagickFalse;
7240 };
7241
7242 status = ComputeCompositeImage(image,channel,compose,composite,
7243 x_offset,y_offset,destination_dissolve,source_dissolve,exception);
7244
dirk8a5cf512014-07-28 20:16:27 +00007245 return(status);
cristy0c832c62014-03-07 22:21:04 +00007246}
7247
cristyf034abb2013-11-24 14:16:14 +00007248#else /* MAGICKCORE_OPENCL_SUPPORT */
7249
7250MagickExport Image *AccelerateConvolveImageChannel(
7251 const Image *magick_unused(image),const ChannelType magick_unused(channel),
7252 const KernelInfo *magick_unused(kernel),
7253 ExceptionInfo *magick_unused(exception))
7254{
7255 magick_unreferenced(image);
7256 magick_unreferenced(channel);
7257 magick_unreferenced(kernel);
7258 magick_unreferenced(exception);
7259
7260 return NULL;
7261}
7262
7263MagickExport MagickBooleanType AccelerateFunctionImage(
7264 Image *magick_unused(image),const ChannelType magick_unused(channel),
7265 const MagickFunction magick_unused(function),
7266 const size_t magick_unused(number_parameters),
7267 const double *magick_unused(parameters),
7268 ExceptionInfo *magick_unused(exception))
7269{
7270 magick_unreferenced(image);
7271 magick_unreferenced(channel);
7272 magick_unreferenced(function);
7273 magick_unreferenced(number_parameters);
7274 magick_unreferenced(parameters);
7275 magick_unreferenced(exception);
7276
7277 return MagickFalse;
7278}
7279
7280MagickExport Image *AccelerateBlurImage(const Image *magick_unused(image),
7281 const ChannelType magick_unused(channel),const double magick_unused(radius),
7282 const double magick_unused(sigma),ExceptionInfo *magick_unused(exception))
7283{
7284 magick_unreferenced(image);
7285 magick_unreferenced(channel);
7286 magick_unreferenced(radius);
7287 magick_unreferenced(sigma);
7288 magick_unreferenced(exception);
7289
7290 return NULL;
7291}
7292
dirk6d612cf2014-03-13 21:17:23 +00007293MagickExport Image *AccelerateRotationalBlurImage(
cristyf034abb2013-11-24 14:16:14 +00007294 const Image *magick_unused(image),const ChannelType magick_unused(channel),
7295 const double magick_unused(angle),ExceptionInfo *magick_unused(exception))
7296{
7297 magick_unreferenced(image);
7298 magick_unreferenced(channel);
7299 magick_unreferenced(angle);
7300 magick_unreferenced(exception);
7301
7302 return NULL;
7303}
7304
7305
7306MagickExport Image *AccelerateUnsharpMaskImage(
7307 const Image *magick_unused(image),const ChannelType magick_unused(channel),
7308 const double magick_unused(radius),const double magick_unused(sigma),
7309 const double magick_unused(gain),const double magick_unused(threshold),
7310 ExceptionInfo *magick_unused(exception))
7311{
7312 magick_unreferenced(image);
7313 magick_unreferenced(channel);
7314 magick_unreferenced(radius);
7315 magick_unreferenced(sigma);
7316 magick_unreferenced(gain);
7317 magick_unreferenced(threshold);
7318 magick_unreferenced(exception);
7319
7320 return NULL;
7321}
7322
cristy0b8a2e92014-03-08 00:52:49 +00007323MagickExport
7324MagickBooleanType AccelerateCompositeImage(Image *image,
7325 const ChannelType channel,const CompositeOperator compose,
7326 const Image *composite,const ssize_t x_offset,const ssize_t y_offset,
7327 const float destination_dissolve,const float source_dissolve,
7328 ExceptionInfo *exception)
7329{
7330 magick_unreferenced(image);
cristya219e4b2014-03-08 12:36:16 +00007331 magick_unreferenced(channel);
7332 magick_unreferenced(compose);
7333 magick_unreferenced(composite);
7334 magick_unreferenced(x_offset);
7335 magick_unreferenced(y_offset);
7336 magick_unreferenced(destination_dissolve);
7337 magick_unreferenced(source_dissolve);
7338 magick_unreferenced(exception);
cristy0b8a2e92014-03-08 00:52:49 +00007339
7340 return MagickFalse;
7341}
7342
cristyf034abb2013-11-24 14:16:14 +00007343
7344MagickExport MagickBooleanType AccelerateContrastImage(
7345 Image* magick_unused(image),const MagickBooleanType magick_unused(sharpen),
7346 ExceptionInfo* magick_unused(exception))
7347{
7348 magick_unreferenced(image);
7349 magick_unreferenced(sharpen);
7350 magick_unreferenced(exception);
7351
7352 return MagickFalse;
7353}
7354
cristy0b8a2e92014-03-08 00:52:49 +00007355MagickExport MagickBooleanType AccelerateContrastStretchImageChannel(
cristy0c832c62014-03-07 22:21:04 +00007356 Image * image, const ChannelType channel, const double black_point, const double white_point,
7357 ExceptionInfo* magick_unused(exception))
7358{
7359 magick_unreferenced(image);
7360 magick_unreferenced(channel);
7361 magick_unreferenced(black_point);
7362 magick_unreferenced(white_point);
7363 magick_unreferenced(exception);
7364
7365 return MagickFalse;
7366}
7367
cristyf034abb2013-11-24 14:16:14 +00007368MagickExport MagickBooleanType AccelerateEqualizeImage(
7369 Image* magick_unused(image), const ChannelType magick_unused(channel),
7370 ExceptionInfo* magick_unused(exception))
7371{
7372 magick_unreferenced(image);
7373 magick_unreferenced(channel);
7374 magick_unreferenced(exception);
7375
7376 return MagickFalse;
7377}
7378
7379MagickExport Image *AccelerateDespeckleImage(const Image* magick_unused(image),
7380 ExceptionInfo* magick_unused(exception))
7381{
7382 magick_unreferenced(image);
7383 magick_unreferenced(exception);
7384
7385 return NULL;
7386}
7387
7388MagickExport Image *AccelerateResizeImage(const Image* magick_unused(image),
7389 const size_t magick_unused(resizedColumns),
7390 const size_t magick_unused(resizedRows),
7391 const ResizeFilter* magick_unused(resizeFilter),
7392 ExceptionInfo *magick_unused(exception))
7393{
7394 magick_unreferenced(image);
7395 magick_unreferenced(resizedColumns);
7396 magick_unreferenced(resizedRows);
7397 magick_unreferenced(resizeFilter);
7398 magick_unreferenced(exception);
7399
7400 return NULL;
7401}
7402
cristyf034abb2013-11-24 14:16:14 +00007403MagickExport
7404MagickBooleanType AccelerateModulateImage(
7405 Image* image, double percent_brightness, double percent_hue,
7406 double percent_saturation, ColorspaceType colorspace, ExceptionInfo* exception)
7407{
7408 magick_unreferenced(image);
7409 magick_unreferenced(percent_brightness);
7410 magick_unreferenced(percent_hue);
7411 magick_unreferenced(percent_saturation);
7412 magick_unreferenced(colorspace);
7413 magick_unreferenced(exception);
7414 return(MagickFalse);
7415}
7416
cristy0c832c62014-03-07 22:21:04 +00007417MagickExport
7418MagickBooleanType AccelerateNegateImageChannel(
7419 Image* image, const ChannelType channel, const MagickBooleanType grayscale, ExceptionInfo* exception)
7420{
7421 magick_unreferenced(image);
7422 magick_unreferenced(channel);
7423 magick_unreferenced(grayscale);
7424 magick_unreferenced(exception);
7425 return(MagickFalse);
7426}
7427
7428MagickExport
7429MagickBooleanType AccelerateGrayscaleImage(
7430 Image* image, const PixelIntensityMethod method, ExceptionInfo* exception)
7431{
7432 magick_unreferenced(image);
7433 magick_unreferenced(method);
7434 magick_unreferenced(exception);
7435 return(MagickFalse);
7436}
7437
cristye85d0f72013-11-27 02:25:43 +00007438MagickExport Image *AccelerateAddNoiseImage(const Image *image,
7439 const ChannelType channel, const NoiseType noise_type,ExceptionInfo *exception)
7440{
7441 magick_unreferenced(image);
7442 magick_unreferenced(channel);
7443 magick_unreferenced(noise_type);
7444 magick_unreferenced(exception);
7445 return NULL;
7446}
cristyf034abb2013-11-24 14:16:14 +00007447
cristy0c832c62014-03-07 22:21:04 +00007448
7449MagickExport MagickBooleanType AccelerateRandomImage(Image* image, ExceptionInfo* exception)
7450{
7451 magick_unreferenced(image);
7452 magick_unreferenced(exception);
7453 return MagickFalse;
7454}
7455
7456MagickExport
7457Image* AccelerateMotionBlurImage(const Image *image, const ChannelType channel,
7458 const double* kernel, const size_t width,
7459 const OffsetInfo *offset,
7460 ExceptionInfo *exception)
7461{
7462 magick_unreferenced(image);
7463 magick_unreferenced(channel);
7464 magick_unreferenced(kernel);
7465 magick_unreferenced(width);
7466 magick_unreferenced(offset);
7467 magick_unreferenced(exception);
7468 return NULL;
7469}
7470
cristyf034abb2013-11-24 14:16:14 +00007471#endif /* MAGICKCORE_OPENCL_SUPPORT */
7472
7473MagickExport MagickBooleanType AccelerateConvolveImage(
7474 const Image *magick_unused(image),const KernelInfo *magick_unused(kernel),
7475 Image *magick_unused(convolve_image),ExceptionInfo *magick_unused(exception))
7476{
7477 magick_unreferenced(image);
7478 magick_unreferenced(kernel);
7479 magick_unreferenced(convolve_image);
7480 magick_unreferenced(exception);
7481
7482 /* legacy, do not use */
7483 return(MagickFalse);
7484}
7485