/src/imagemagick/MagickCore/attribute.c
Line | Count | Source |
1 | | /* |
2 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3 | | % % |
4 | | % % |
5 | | % % |
6 | | % AAA TTTTT TTTTT RRRR IIIII BBBB U U TTTTT EEEEE % |
7 | | % A A T T R R I B B U U T E % |
8 | | % AAAAA T T RRRR I BBBB U U T EEE % |
9 | | % A A T T R R I B B U U T E % |
10 | | % A A T T R R IIIII BBBB UUU T EEEEE % |
11 | | % % |
12 | | % % |
13 | | % MagickCore Get / Set Image Attributes % |
14 | | % % |
15 | | % Software Design % |
16 | | % Cristy % |
17 | | % October 2002 % |
18 | | % % |
19 | | % % |
20 | | % Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization % |
21 | | % dedicated to making software imaging solutions freely available. % |
22 | | % % |
23 | | % You may not use this file except in compliance with the License. You may % |
24 | | % obtain a copy of the License at % |
25 | | % % |
26 | | % https://imagemagick.org/license/ % |
27 | | % % |
28 | | % Unless required by applicable law or agreed to in writing, software % |
29 | | % distributed under the License is distributed on an "AS IS" BASIS, % |
30 | | % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % |
31 | | % See the License for the specific language governing permissions and % |
32 | | % limitations under the License. % |
33 | | % % |
34 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
35 | | % |
36 | | % |
37 | | % |
38 | | */ |
39 | | |
40 | | /* |
41 | | Include declarations. |
42 | | */ |
43 | | #include "MagickCore/studio.h" |
44 | | #include "MagickCore/artifact.h" |
45 | | #include "MagickCore/attribute.h" |
46 | | #include "MagickCore/blob.h" |
47 | | #include "MagickCore/blob-private.h" |
48 | | #include "MagickCore/cache.h" |
49 | | #include "MagickCore/cache-private.h" |
50 | | #include "MagickCore/cache-view.h" |
51 | | #include "MagickCore/channel.h" |
52 | | #include "MagickCore/client.h" |
53 | | #include "MagickCore/color.h" |
54 | | #include "MagickCore/color-private.h" |
55 | | #include "MagickCore/colormap.h" |
56 | | #include "MagickCore/colormap-private.h" |
57 | | #include "MagickCore/colorspace.h" |
58 | | #include "MagickCore/colorspace-private.h" |
59 | | #include "MagickCore/composite.h" |
60 | | #include "MagickCore/composite-private.h" |
61 | | #include "MagickCore/constitute.h" |
62 | | #include "MagickCore/draw.h" |
63 | | #include "MagickCore/draw-private.h" |
64 | | #include "MagickCore/effect.h" |
65 | | #include "MagickCore/enhance.h" |
66 | | #include "MagickCore/exception.h" |
67 | | #include "MagickCore/exception-private.h" |
68 | | #include "MagickCore/geometry.h" |
69 | | #include "MagickCore/histogram.h" |
70 | | #include "MagickCore/identify.h" |
71 | | #include "MagickCore/image.h" |
72 | | #include "MagickCore/image-private.h" |
73 | | #include "MagickCore/list.h" |
74 | | #include "MagickCore/log.h" |
75 | | #include "MagickCore/memory_.h" |
76 | | #include "MagickCore/magick.h" |
77 | | #include "MagickCore/monitor.h" |
78 | | #include "MagickCore/monitor-private.h" |
79 | | #include "MagickCore/option.h" |
80 | | #include "MagickCore/paint.h" |
81 | | #include "MagickCore/pixel.h" |
82 | | #include "MagickCore/pixel-accessor.h" |
83 | | #include "MagickCore/property.h" |
84 | | #include "MagickCore/quantize.h" |
85 | | #include "MagickCore/quantum-private.h" |
86 | | #include "MagickCore/random_.h" |
87 | | #include "MagickCore/resource_.h" |
88 | | #include "MagickCore/semaphore.h" |
89 | | #include "MagickCore/segment.h" |
90 | | #include "MagickCore/splay-tree.h" |
91 | | #include "MagickCore/string_.h" |
92 | | #include "MagickCore/string-private.h" |
93 | | #include "MagickCore/thread-private.h" |
94 | | #include "MagickCore/threshold.h" |
95 | | #include "MagickCore/transform.h" |
96 | | #include "MagickCore/utility.h" |
97 | | |
98 | | /* |
99 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
100 | | % % |
101 | | % % |
102 | | % % |
103 | | + G e t I m a g e B o u n d i n g B o x % |
104 | | % % |
105 | | % % |
106 | | % % |
107 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
108 | | % |
109 | | % GetImageBoundingBox() returns the bounding box of an image canvas. |
110 | | % |
111 | | % The format of the GetImageBoundingBox method is: |
112 | | % |
113 | | % RectangleInfo GetImageBoundingBox(const Image *image, |
114 | | % ExceptionInfo *exception) |
115 | | % |
116 | | % A description of each parameter follows: |
117 | | % |
118 | | % o bounds: Method GetImageBoundingBox returns the bounding box of an |
119 | | % image canvas. |
120 | | % |
121 | | % o image: the image. |
122 | | % |
123 | | % o exception: return any errors or warnings in this structure. |
124 | | % |
125 | | */ |
126 | | |
127 | | typedef struct _CensusInfo |
128 | | { |
129 | | double |
130 | | left, |
131 | | right, |
132 | | top, |
133 | | bottom; |
134 | | } CensusInfo; |
135 | | |
136 | | static double GetEdgeBackgroundCensus(const Image *image, |
137 | | const CacheView *image_view,const GravityType gravity,const size_t width, |
138 | | const size_t height,const ssize_t x_offset,const ssize_t y_offset, |
139 | | ExceptionInfo *exception) |
140 | 0 | { |
141 | 0 | CacheView |
142 | 0 | *edge_view; |
143 | |
|
144 | 0 | const char |
145 | 0 | *artifact; |
146 | |
|
147 | 0 | const Quantum |
148 | 0 | *p; |
149 | |
|
150 | 0 | double |
151 | 0 | census; |
152 | |
|
153 | 0 | Image |
154 | 0 | *edge_image; |
155 | |
|
156 | 0 | PixelInfo |
157 | 0 | background, |
158 | 0 | pixel; |
159 | |
|
160 | 0 | RectangleInfo |
161 | 0 | edge_geometry; |
162 | |
|
163 | 0 | ssize_t |
164 | 0 | y; |
165 | | |
166 | | /* |
167 | | Determine the percent of image background for this edge. |
168 | | */ |
169 | 0 | switch (gravity) |
170 | 0 | { |
171 | 0 | case NorthWestGravity: |
172 | 0 | case NorthGravity: |
173 | 0 | default: |
174 | 0 | { |
175 | 0 | p=GetCacheViewVirtualPixels(image_view,0,0,1,1,exception); |
176 | 0 | break; |
177 | 0 | } |
178 | 0 | case NorthEastGravity: |
179 | 0 | case EastGravity: |
180 | 0 | { |
181 | 0 | p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,0,1,1, |
182 | 0 | exception); |
183 | 0 | break; |
184 | 0 | } |
185 | 0 | case SouthEastGravity: |
186 | 0 | case SouthGravity: |
187 | 0 | { |
188 | 0 | p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1, |
189 | 0 | (ssize_t) image->rows-1,1,1,exception); |
190 | 0 | break; |
191 | 0 | } |
192 | 0 | case SouthWestGravity: |
193 | 0 | case WestGravity: |
194 | 0 | { |
195 | 0 | p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-1,1,1, |
196 | 0 | exception); |
197 | 0 | break; |
198 | 0 | } |
199 | 0 | } |
200 | 0 | if (p == (const Quantum *) NULL) |
201 | 0 | return(0.0); |
202 | 0 | GetPixelInfoPixel(image,p,&background); |
203 | 0 | artifact=GetImageArtifact(image,"background"); |
204 | 0 | if (artifact != (const char *) NULL) |
205 | 0 | (void) QueryColorCompliance(artifact,AllCompliance,&background,exception); |
206 | 0 | artifact=GetImageArtifact(image,"trim:background-color"); |
207 | 0 | if (artifact != (const char *) NULL) |
208 | 0 | (void) QueryColorCompliance(artifact,AllCompliance,&background,exception); |
209 | 0 | edge_geometry.width=width; |
210 | 0 | edge_geometry.height=height; |
211 | 0 | edge_geometry.x=x_offset; |
212 | 0 | edge_geometry.y=y_offset; |
213 | 0 | GravityAdjustGeometry(image->columns,image->rows,gravity,&edge_geometry); |
214 | 0 | edge_image=CropImage(image,&edge_geometry,exception); |
215 | 0 | if (edge_image == (Image *) NULL) |
216 | 0 | return(0.0); |
217 | 0 | census=0.0; |
218 | 0 | edge_view=AcquireVirtualCacheView(edge_image,exception); |
219 | 0 | for (y=0; y < (ssize_t) edge_image->rows; y++) |
220 | 0 | { |
221 | 0 | ssize_t |
222 | 0 | x; |
223 | |
|
224 | 0 | p=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception); |
225 | 0 | if (p == (const Quantum *) NULL) |
226 | 0 | break; |
227 | 0 | for (x=0; x < (ssize_t) edge_image->columns; x++) |
228 | 0 | { |
229 | 0 | GetPixelInfoPixel(edge_image,p,&pixel); |
230 | 0 | if (IsFuzzyEquivalencePixelInfo(&pixel,&background) == MagickFalse) |
231 | 0 | census++; |
232 | 0 | p+=(ptrdiff_t) GetPixelChannels(edge_image); |
233 | 0 | } |
234 | 0 | } |
235 | 0 | census/=((double) edge_image->columns*edge_image->rows); |
236 | 0 | edge_view=DestroyCacheView(edge_view); |
237 | 0 | edge_image=DestroyImage(edge_image); |
238 | 0 | return(census); |
239 | 0 | } |
240 | | |
241 | | static inline double GetMinEdgeBackgroundCensus(const CensusInfo *edge) |
242 | 0 | { |
243 | 0 | double |
244 | 0 | census; |
245 | |
|
246 | 0 | census=MagickMin(MagickMin(MagickMin(edge->left,edge->right),edge->top), |
247 | 0 | edge->bottom); |
248 | 0 | return(census); |
249 | 0 | } |
250 | | |
251 | | static RectangleInfo GetEdgeBoundingBox(const Image *image, |
252 | | ExceptionInfo *exception) |
253 | 0 | { |
254 | 0 | CacheView |
255 | 0 | *edge_view; |
256 | |
|
257 | 0 | CensusInfo |
258 | 0 | edge, |
259 | 0 | vertex; |
260 | |
|
261 | 0 | const char |
262 | 0 | *artifact; |
263 | |
|
264 | 0 | double |
265 | 0 | background_census, |
266 | 0 | percent_background; |
267 | |
|
268 | 0 | Image |
269 | 0 | *edge_image; |
270 | |
|
271 | 0 | RectangleInfo |
272 | 0 | bounds; |
273 | | |
274 | | /* |
275 | | Get the image bounding box. |
276 | | */ |
277 | 0 | assert(image != (Image *) NULL); |
278 | 0 | assert(image->signature == MagickCoreSignature); |
279 | 0 | if (IsEventLogging() != MagickFalse) |
280 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
281 | 0 | SetGeometry(image,&bounds); |
282 | 0 | edge_image=CloneImage(image,0,0,MagickTrue,exception); |
283 | 0 | if (edge_image == (Image *) NULL) |
284 | 0 | return(bounds); |
285 | 0 | (void) ParseAbsoluteGeometry("0x0+0+0",&edge_image->page); |
286 | 0 | (void) memset(&vertex,0,sizeof(vertex)); |
287 | 0 | edge_view=AcquireVirtualCacheView(edge_image,exception); |
288 | 0 | edge.left=GetEdgeBackgroundCensus(edge_image,edge_view,WestGravity, |
289 | 0 | 1,0,0,0,exception); |
290 | 0 | edge.right=GetEdgeBackgroundCensus(edge_image,edge_view,EastGravity, |
291 | 0 | 1,0,0,0,exception); |
292 | 0 | edge.top=GetEdgeBackgroundCensus(edge_image,edge_view,NorthGravity, |
293 | 0 | 0,1,0,0,exception); |
294 | 0 | edge.bottom=GetEdgeBackgroundCensus(edge_image,edge_view,SouthGravity, |
295 | 0 | 0,1,0,0,exception); |
296 | 0 | percent_background=1.0; |
297 | 0 | artifact=GetImageArtifact(edge_image,"trim:percent-background"); |
298 | 0 | if (artifact != (const char *) NULL) |
299 | 0 | percent_background=StringToDouble(artifact,(char **) NULL)/100.0; |
300 | 0 | percent_background=MagickMin(MagickMax(1.0-percent_background,MagickEpsilon), |
301 | 0 | 1.0); |
302 | 0 | background_census=GetMinEdgeBackgroundCensus(&edge); |
303 | 0 | for ( ; background_census < percent_background; |
304 | 0 | background_census=GetMinEdgeBackgroundCensus(&edge)) |
305 | 0 | { |
306 | 0 | if ((bounds.width == 0) || (bounds.height == 0)) |
307 | 0 | break; |
308 | 0 | if (fabs(edge.left-background_census) < MagickEpsilon) |
309 | 0 | { |
310 | | /* |
311 | | Trim left edge. |
312 | | */ |
313 | 0 | vertex.left++; |
314 | 0 | bounds.width--; |
315 | 0 | edge.left=GetEdgeBackgroundCensus(edge_image,edge_view, |
316 | 0 | NorthWestGravity,1,bounds.height,(ssize_t) vertex.left,(ssize_t) |
317 | 0 | vertex.top,exception); |
318 | 0 | edge.top=GetEdgeBackgroundCensus(edge_image,edge_view, |
319 | 0 | NorthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t) |
320 | 0 | vertex.top,exception); |
321 | 0 | edge.bottom=GetEdgeBackgroundCensus(edge_image,edge_view, |
322 | 0 | SouthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t) |
323 | 0 | vertex.bottom,exception); |
324 | 0 | continue; |
325 | 0 | } |
326 | 0 | if (fabs(edge.right-background_census) < MagickEpsilon) |
327 | 0 | { |
328 | | /* |
329 | | Trim right edge. |
330 | | */ |
331 | 0 | vertex.right++; |
332 | 0 | bounds.width--; |
333 | 0 | edge.right=GetEdgeBackgroundCensus(edge_image,edge_view, |
334 | 0 | NorthEastGravity,1,bounds.height,(ssize_t) vertex.right,(ssize_t) |
335 | 0 | vertex.top,exception); |
336 | 0 | edge.top=GetEdgeBackgroundCensus(edge_image,edge_view, |
337 | 0 | NorthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t) |
338 | 0 | vertex.top,exception); |
339 | 0 | edge.bottom=GetEdgeBackgroundCensus(edge_image,edge_view, |
340 | 0 | SouthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t) |
341 | 0 | vertex.bottom,exception); |
342 | 0 | continue; |
343 | 0 | } |
344 | 0 | if (fabs(edge.top-background_census) < MagickEpsilon) |
345 | 0 | { |
346 | | /* |
347 | | Trim top edge. |
348 | | */ |
349 | 0 | vertex.top++; |
350 | 0 | bounds.height--; |
351 | 0 | edge.left=GetEdgeBackgroundCensus(edge_image,edge_view, |
352 | 0 | NorthWestGravity,1,bounds.height,(ssize_t) vertex.left,(ssize_t) |
353 | 0 | vertex.top,exception); |
354 | 0 | edge.right=GetEdgeBackgroundCensus(edge_image,edge_view, |
355 | 0 | NorthEastGravity,1,bounds.height,(ssize_t) vertex.right,(ssize_t) |
356 | 0 | vertex.top,exception); |
357 | 0 | edge.top=GetEdgeBackgroundCensus(edge_image,edge_view, |
358 | 0 | NorthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t) |
359 | 0 | vertex.top,exception); |
360 | 0 | continue; |
361 | 0 | } |
362 | 0 | if (fabs(edge.bottom-background_census) < MagickEpsilon) |
363 | 0 | { |
364 | | /* |
365 | | Trim bottom edge. |
366 | | */ |
367 | 0 | vertex.bottom++; |
368 | 0 | bounds.height--; |
369 | 0 | edge.left=GetEdgeBackgroundCensus(edge_image,edge_view, |
370 | 0 | NorthWestGravity,1,bounds.height,(ssize_t) vertex.left,(ssize_t) |
371 | 0 | vertex.top,exception); |
372 | 0 | edge.right=GetEdgeBackgroundCensus(edge_image,edge_view, |
373 | 0 | NorthEastGravity,1,bounds.height,(ssize_t) vertex.right,(ssize_t) |
374 | 0 | vertex.top,exception); |
375 | 0 | edge.bottom=GetEdgeBackgroundCensus(edge_image,edge_view, |
376 | 0 | SouthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t) |
377 | 0 | vertex.bottom,exception); |
378 | 0 | continue; |
379 | 0 | } |
380 | 0 | } |
381 | 0 | edge_view=DestroyCacheView(edge_view); |
382 | 0 | edge_image=DestroyImage(edge_image); |
383 | 0 | bounds.x=(ssize_t) vertex.left; |
384 | 0 | bounds.y=(ssize_t) vertex.top; |
385 | 0 | if ((bounds.width == 0) || (bounds.height == 0)) |
386 | 0 | (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, |
387 | 0 | "GeometryDoesNotContainImage","`%s'",image->filename); |
388 | 0 | return(bounds); |
389 | 0 | } |
390 | | |
391 | | MagickExport RectangleInfo GetImageBoundingBox(const Image *image, |
392 | | ExceptionInfo *exception) |
393 | 2.96k | { |
394 | 2.96k | CacheView |
395 | 2.96k | *image_view; |
396 | | |
397 | 2.96k | const char |
398 | 2.96k | *artifact; |
399 | | |
400 | 2.96k | const Quantum |
401 | 2.96k | *p; |
402 | | |
403 | 2.96k | MagickBooleanType |
404 | 2.96k | status; |
405 | | |
406 | 2.96k | PixelInfo |
407 | 2.96k | target[4], |
408 | 2.96k | zero; |
409 | | |
410 | 2.96k | RectangleInfo |
411 | 2.96k | bounds; |
412 | | |
413 | 2.96k | ssize_t |
414 | 2.96k | y; |
415 | | |
416 | 2.96k | assert(image != (Image *) NULL); |
417 | 2.96k | assert(image->signature == MagickCoreSignature); |
418 | 2.96k | if (IsEventLogging() != MagickFalse) |
419 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
420 | 2.96k | artifact=GetImageArtifact(image,"trim:percent-background"); |
421 | 2.96k | if (artifact != (const char *) NULL) |
422 | 0 | return(GetEdgeBoundingBox(image,exception)); |
423 | 2.96k | artifact=GetImageArtifact(image,"trim:edges"); |
424 | 2.96k | if (artifact == (const char *) NULL) |
425 | 2.96k | { |
426 | 2.96k | bounds.width=(size_t) (image->columns == 1 ? 1 : 0); |
427 | 2.96k | bounds.height=(size_t) (image->rows == 1 ? 1 : 0); |
428 | 2.96k | bounds.x=(ssize_t) image->columns; |
429 | 2.96k | bounds.y=(ssize_t) image->rows; |
430 | 2.96k | } |
431 | 0 | else |
432 | 0 | { |
433 | 0 | char |
434 | 0 | *edges, |
435 | 0 | *q, |
436 | 0 | *r; |
437 | |
|
438 | 0 | bounds.width=(size_t) image->columns; |
439 | 0 | bounds.height=(size_t) image->rows; |
440 | 0 | bounds.x=0; |
441 | 0 | bounds.y=0; |
442 | 0 | edges=AcquireString(artifact); |
443 | 0 | r=edges; |
444 | 0 | while ((q=StringToken(",",&r)) != (char *) NULL) |
445 | 0 | { |
446 | 0 | if (LocaleCompare(q,"north") == 0) |
447 | 0 | bounds.y=(ssize_t) image->rows; |
448 | 0 | if (LocaleCompare(q,"east") == 0) |
449 | 0 | bounds.width=0; |
450 | 0 | if (LocaleCompare(q,"south") == 0) |
451 | 0 | bounds.height=0; |
452 | 0 | if (LocaleCompare(q,"west") == 0) |
453 | 0 | bounds.x=(ssize_t) image->columns; |
454 | 0 | } |
455 | 0 | edges=DestroyString(edges); |
456 | 0 | } |
457 | 2.96k | GetPixelInfo(image,&target[0]); |
458 | 2.96k | image_view=AcquireVirtualCacheView(image,exception); |
459 | 2.96k | p=GetCacheViewVirtualPixels(image_view,0,0,1,1,exception); |
460 | 2.96k | if (p == (const Quantum *) NULL) |
461 | 1.66k | { |
462 | 1.66k | image_view=DestroyCacheView(image_view); |
463 | 1.66k | return(bounds); |
464 | 1.66k | } |
465 | 1.30k | GetPixelInfoPixel(image,p,&target[0]); |
466 | 1.30k | GetPixelInfo(image,&target[1]); |
467 | 1.30k | p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,0,1,1, |
468 | 1.30k | exception); |
469 | 1.30k | if (p != (const Quantum *) NULL) |
470 | 1.30k | GetPixelInfoPixel(image,p,&target[1]); |
471 | 1.30k | GetPixelInfo(image,&target[2]); |
472 | 1.30k | p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-1,1,1, |
473 | 1.30k | exception); |
474 | 1.30k | if (p != (const Quantum *) NULL) |
475 | 1.30k | GetPixelInfoPixel(image,p,&target[2]); |
476 | 1.30k | GetPixelInfo(image,&target[3]); |
477 | 1.30k | p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,(ssize_t) |
478 | 1.30k | image->rows-1,1,1,exception); |
479 | 1.30k | if (p != (const Quantum *) NULL) |
480 | 1.30k | GetPixelInfoPixel(image,p,&target[3]); |
481 | 1.30k | status=MagickTrue; |
482 | 1.30k | GetPixelInfo(image,&zero); |
483 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
484 | | #pragma omp parallel for schedule(static) shared(status) \ |
485 | | magick_number_threads(image,image,image->rows,2) |
486 | | #endif |
487 | 22.6k | for (y=0; y < (ssize_t) image->rows; y++) |
488 | 21.3k | { |
489 | 21.3k | const Quantum |
490 | 21.3k | *magick_restrict q; |
491 | | |
492 | 21.3k | PixelInfo |
493 | 21.3k | pixel; |
494 | | |
495 | 21.3k | RectangleInfo |
496 | 21.3k | bounding_box; |
497 | | |
498 | 21.3k | ssize_t |
499 | 21.3k | x; |
500 | | |
501 | 21.3k | if (status == MagickFalse) |
502 | 0 | continue; |
503 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
504 | | # pragma omp critical (MagickCore_GetImageBoundingBox) |
505 | | #endif |
506 | 21.3k | bounding_box=bounds; |
507 | 21.3k | q=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); |
508 | 21.3k | if (q == (const Quantum *) NULL) |
509 | 0 | { |
510 | 0 | status=MagickFalse; |
511 | 0 | continue; |
512 | 0 | } |
513 | 21.3k | pixel=zero; |
514 | 22.0M | for (x=0; x < (ssize_t) image->columns; x++) |
515 | 21.9M | { |
516 | 21.9M | GetPixelInfoPixel(image,q,&pixel); |
517 | 21.9M | if ((x < bounding_box.x) && |
518 | 21.9M | (IsFuzzyEquivalencePixelInfo(&pixel,&target[0]) == MagickFalse)) |
519 | 0 | bounding_box.x=x; |
520 | 21.9M | if ((x > (ssize_t) bounding_box.width) && |
521 | 21.9M | (IsFuzzyEquivalencePixelInfo(&pixel,&target[1]) == MagickFalse)) |
522 | 0 | bounding_box.width=(size_t) x; |
523 | 21.9M | if ((y < bounding_box.y) && |
524 | 21.9M | (IsFuzzyEquivalencePixelInfo(&pixel,&target[0]) == MagickFalse)) |
525 | 0 | bounding_box.y=y; |
526 | 21.9M | if ((y > (ssize_t) bounding_box.height) && |
527 | 21.9M | (IsFuzzyEquivalencePixelInfo(&pixel,&target[2]) == MagickFalse)) |
528 | 0 | bounding_box.height=(size_t) y; |
529 | 21.9M | if ((x < (ssize_t) bounding_box.width) && |
530 | 2.31k | (y > (ssize_t) bounding_box.height) && |
531 | 1.68k | (IsFuzzyEquivalencePixelInfo(&pixel,&target[3]) == MagickFalse)) |
532 | 0 | { |
533 | 0 | bounding_box.width=(size_t) x; |
534 | 0 | bounding_box.height=(size_t) y; |
535 | 0 | } |
536 | 21.9M | q+=(ptrdiff_t) GetPixelChannels(image); |
537 | 21.9M | } |
538 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
539 | | # pragma omp critical (MagickCore_GetImageBoundingBox) |
540 | | #endif |
541 | 21.3k | { |
542 | 21.3k | if (bounding_box.x < bounds.x) |
543 | 0 | bounds.x=bounding_box.x; |
544 | 21.3k | if (bounding_box.y < bounds.y) |
545 | 0 | bounds.y=bounding_box.y; |
546 | 21.3k | if (bounding_box.width > bounds.width) |
547 | 0 | bounds.width=bounding_box.width; |
548 | 21.3k | if (bounding_box.height > bounds.height) |
549 | 0 | bounds.height=bounding_box.height; |
550 | 21.3k | } |
551 | 21.3k | } |
552 | 1.30k | image_view=DestroyCacheView(image_view); |
553 | 1.30k | if ((bounds.width == 0) || (bounds.height == 0)) |
554 | 765 | (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, |
555 | 765 | "GeometryDoesNotContainImage","`%s'",image->filename); |
556 | 538 | else |
557 | 538 | { |
558 | 538 | bounds.width-=(size_t) (bounds.x-1); |
559 | 538 | bounds.height-=(size_t) (bounds.y-1); |
560 | 538 | } |
561 | 1.30k | return(bounds); |
562 | 2.96k | } |
563 | | |
564 | | /* |
565 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
566 | | % % |
567 | | % % |
568 | | % % |
569 | | % G e t I m a g e C o n v e x H u l l % |
570 | | % % |
571 | | % % |
572 | | % % |
573 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
574 | | % |
575 | | % GetImageConvexHull() returns the convex hull points of an image canvas. |
576 | | % |
577 | | % The format of the GetImageConvexHull method is: |
578 | | % |
579 | | % PointInfo *GetImageConvexHull(const Image *image, |
580 | | % size_t number_vertices,ExceptionInfo *exception) |
581 | | % |
582 | | % A description of each parameter follows: |
583 | | % |
584 | | % o image: the image. |
585 | | % |
586 | | % o number_vertices: the number of vertices in the convex hull. |
587 | | % |
588 | | % o exception: return any errors or warnings in this structure. |
589 | | % |
590 | | */ |
591 | | |
592 | | static double LexicographicalOrder(PointInfo *a,PointInfo *b,PointInfo *c) |
593 | 0 | { |
594 | | /* |
595 | | Order by x-coordinate, and in case of a tie, by y-coordinate. |
596 | | */ |
597 | 0 | return((b->x-a->x)*(c->y-a->y)-(b->y-a->y)*(c->x-a->x)); |
598 | 0 | } |
599 | | |
600 | | static PixelInfo GetEdgeBackgroundColor(const Image *image, |
601 | | const CacheView *image_view,ExceptionInfo *exception) |
602 | 1.07k | { |
603 | 1.07k | const char |
604 | 1.07k | *artifact; |
605 | | |
606 | 1.07k | double |
607 | 1.07k | census[4], |
608 | 1.07k | edge_census; |
609 | | |
610 | 1.07k | PixelInfo |
611 | 1.07k | background[4], |
612 | 1.07k | edge_background; |
613 | | |
614 | 1.07k | ssize_t |
615 | 1.07k | i; |
616 | | |
617 | | /* |
618 | | Most dominant color of edges/corners is the background color of the image. |
619 | | */ |
620 | 1.07k | memset(&edge_background,0,sizeof(edge_background)); |
621 | 1.07k | artifact=GetImageArtifact(image,"convex-hull:background-color"); |
622 | 1.07k | if (artifact == (const char *) NULL) |
623 | 1.07k | artifact=GetImageArtifact(image,"background"); |
624 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
625 | | #pragma omp parallel for schedule(static) |
626 | | #endif |
627 | 5.38k | for (i=0; i < 4; i++) |
628 | 4.30k | { |
629 | 4.30k | CacheView |
630 | 4.30k | *edge_view; |
631 | | |
632 | 4.30k | GravityType |
633 | 4.30k | gravity; |
634 | | |
635 | 4.30k | Image |
636 | 4.30k | *edge_image; |
637 | | |
638 | 4.30k | PixelInfo |
639 | 4.30k | pixel; |
640 | | |
641 | 4.30k | RectangleInfo |
642 | 4.30k | edge_geometry; |
643 | | |
644 | 4.30k | const Quantum |
645 | 4.30k | *p; |
646 | | |
647 | 4.30k | ssize_t |
648 | 4.30k | y; |
649 | | |
650 | 4.30k | census[i]=0.0; |
651 | 4.30k | (void) memset(&edge_geometry,0,sizeof(edge_geometry)); |
652 | 4.30k | switch (i) |
653 | 4.30k | { |
654 | 1.07k | case 0: |
655 | 1.07k | default: |
656 | 1.07k | { |
657 | 1.07k | p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-1,1,1, |
658 | 1.07k | exception); |
659 | 1.07k | gravity=WestGravity; |
660 | 1.07k | edge_geometry.width=1; |
661 | 1.07k | edge_geometry.height=0; |
662 | 1.07k | break; |
663 | 1.07k | } |
664 | 1.07k | case 1: |
665 | 1.07k | { |
666 | 1.07k | p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,0,1,1, |
667 | 1.07k | exception); |
668 | 1.07k | gravity=EastGravity; |
669 | 1.07k | edge_geometry.width=1; |
670 | 1.07k | edge_geometry.height=0; |
671 | 1.07k | break; |
672 | 1.07k | } |
673 | 1.07k | case 2: |
674 | 1.07k | { |
675 | 1.07k | p=GetCacheViewVirtualPixels(image_view,0,0,1,1,exception); |
676 | 1.07k | gravity=NorthGravity; |
677 | 1.07k | edge_geometry.width=0; |
678 | 1.07k | edge_geometry.height=1; |
679 | 1.07k | break; |
680 | 1.07k | } |
681 | 1.07k | case 3: |
682 | 1.07k | { |
683 | 1.07k | p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1, |
684 | 1.07k | (ssize_t) image->rows-1,1,1,exception); |
685 | 1.07k | gravity=SouthGravity; |
686 | 1.07k | edge_geometry.width=0; |
687 | 1.07k | edge_geometry.height=1; |
688 | 1.07k | break; |
689 | 1.07k | } |
690 | 4.30k | } |
691 | 4.30k | GetPixelInfoPixel(image,p,background+i); |
692 | 4.30k | if (artifact != (const char *) NULL) |
693 | 0 | (void) QueryColorCompliance(artifact,AllCompliance,background+i, |
694 | 0 | exception); |
695 | 4.30k | GravityAdjustGeometry(image->columns,image->rows,gravity,&edge_geometry); |
696 | 4.30k | edge_image=CropImage(image,&edge_geometry,exception); |
697 | 4.30k | if (edge_image == (Image *) NULL) |
698 | 856 | continue; |
699 | 3.44k | edge_view=AcquireVirtualCacheView(edge_image,exception); |
700 | 104k | for (y=0; y < (ssize_t) edge_image->rows; y++) |
701 | 101k | { |
702 | 101k | ssize_t |
703 | 101k | x; |
704 | | |
705 | 101k | p=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1, |
706 | 101k | exception); |
707 | 101k | if (p == (const Quantum *) NULL) |
708 | 0 | break; |
709 | 325k | for (x=0; x < (ssize_t) edge_image->columns; x++) |
710 | 223k | { |
711 | 223k | GetPixelInfoPixel(edge_image,p,&pixel); |
712 | 223k | if (IsFuzzyEquivalencePixelInfo(&pixel,background+i) == MagickFalse) |
713 | 0 | census[i]++; |
714 | 223k | p+=(ptrdiff_t) GetPixelChannels(edge_image); |
715 | 223k | } |
716 | 101k | } |
717 | 3.44k | edge_view=DestroyCacheView(edge_view); |
718 | 3.44k | edge_image=DestroyImage(edge_image); |
719 | 3.44k | } |
720 | 1.07k | edge_census=(-1.0); |
721 | 5.38k | for (i=0; i < 4; i++) |
722 | 4.30k | if (census[i] > edge_census) |
723 | 1.07k | { |
724 | 1.07k | edge_background=background[i]; |
725 | 1.07k | edge_census=census[i]; |
726 | 1.07k | } |
727 | 1.07k | return(edge_background); |
728 | 1.07k | } |
729 | | |
730 | | void TraceConvexHull(PointInfo *vertices,size_t number_vertices, |
731 | | PointInfo ***monotone_chain,size_t *chain_length) |
732 | 1.07k | { |
733 | 1.07k | PointInfo |
734 | 1.07k | **chain; |
735 | | |
736 | 1.07k | size_t |
737 | 1.07k | demark, |
738 | 1.07k | n; |
739 | | |
740 | 1.07k | ssize_t |
741 | 1.07k | i; |
742 | | |
743 | | /* |
744 | | Construct the upper and lower hulls: rightmost to leftmost counterclockwise. |
745 | | */ |
746 | 1.07k | chain=(*monotone_chain); |
747 | 1.07k | n=0; |
748 | 1.07k | for (i=0; i < (ssize_t) number_vertices; i++) |
749 | 0 | { |
750 | 0 | while ((n >= 2) && |
751 | 0 | (LexicographicalOrder(chain[n-2],chain[n-1],&vertices[i]) <= 0.0)) |
752 | 0 | n--; |
753 | 0 | chain[n++]=(&vertices[i]); |
754 | 0 | } |
755 | 1.07k | demark=n+1; |
756 | 1.07k | for (i=(ssize_t) number_vertices-2; i >= 0; i--) |
757 | 0 | { |
758 | 0 | while ((n >= demark) && |
759 | 0 | (LexicographicalOrder(chain[n-2],chain[n-1],&vertices[i]) <= 0.0)) |
760 | 0 | n--; |
761 | 0 | chain[n++]=(&vertices[i]); |
762 | 0 | } |
763 | 1.07k | *chain_length=n; |
764 | 1.07k | } |
765 | | |
766 | | MagickExport PointInfo *GetImageConvexHull(const Image *image, |
767 | | size_t *number_vertices,ExceptionInfo *exception) |
768 | 1.33k | { |
769 | 1.33k | CacheView |
770 | 1.33k | *image_view; |
771 | | |
772 | 1.33k | MagickBooleanType |
773 | 1.33k | status; |
774 | | |
775 | 1.33k | MemoryInfo |
776 | 1.33k | *monotone_info, |
777 | 1.33k | *vertices_info; |
778 | | |
779 | 1.33k | PixelInfo |
780 | 1.33k | background; |
781 | | |
782 | 1.33k | PointInfo |
783 | 1.33k | *convex_hull, |
784 | 1.33k | **monotone_chain, |
785 | 1.33k | *vertices; |
786 | | |
787 | 1.33k | size_t |
788 | 1.33k | n; |
789 | | |
790 | 1.33k | ssize_t |
791 | 1.33k | y; |
792 | | |
793 | | /* |
794 | | Identify convex hull vertices of image foreground object(s). |
795 | | */ |
796 | 1.33k | assert(image != (Image *) NULL); |
797 | 1.33k | assert(image->signature == MagickCoreSignature); |
798 | 1.33k | if (IsEventLogging() != MagickFalse) |
799 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
800 | 1.33k | *number_vertices=0; |
801 | 1.33k | vertices_info=AcquireVirtualMemory(image->columns,image->rows* |
802 | 1.33k | sizeof(*vertices)); |
803 | 1.33k | monotone_info=AcquireVirtualMemory(2*image->columns,2* |
804 | 1.33k | image->rows*sizeof(*monotone_chain)); |
805 | 1.33k | if ((vertices_info == (MemoryInfo *) NULL) || |
806 | 1.09k | (monotone_info == (MemoryInfo *) NULL)) |
807 | 258 | { |
808 | 258 | if (monotone_info != (MemoryInfo *) NULL) |
809 | 37 | monotone_info=(MemoryInfo *) RelinquishVirtualMemory(monotone_info); |
810 | 258 | if (vertices_info != (MemoryInfo *) NULL) |
811 | 20 | vertices_info=RelinquishVirtualMemory(vertices_info); |
812 | 258 | return((PointInfo *) NULL); |
813 | 258 | } |
814 | 1.07k | vertices=(PointInfo *) GetVirtualMemoryBlob(vertices_info); |
815 | 1.07k | monotone_chain=(PointInfo **) GetVirtualMemoryBlob(monotone_info); |
816 | 1.07k | image_view=AcquireVirtualCacheView(image,exception); |
817 | 1.07k | background=GetEdgeBackgroundColor(image,image_view,exception); |
818 | 1.07k | status=MagickTrue; |
819 | 1.07k | n=0; |
820 | 43.3M | for (y=0; y < (ssize_t) image->rows; y++) |
821 | 43.3M | { |
822 | 43.3M | const Quantum |
823 | 43.3M | *p; |
824 | | |
825 | 43.3M | ssize_t |
826 | 43.3M | x; |
827 | | |
828 | 43.3M | if (status == MagickFalse) |
829 | 43.3M | continue; |
830 | 50.0k | p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); |
831 | 50.0k | if (p == (const Quantum *) NULL) |
832 | 202 | { |
833 | 202 | status=MagickFalse; |
834 | 202 | continue; |
835 | 202 | } |
836 | 81.0M | for (x=0; x < (ssize_t) image->columns; x++) |
837 | 81.0M | { |
838 | 81.0M | PixelInfo |
839 | 81.0M | pixel; |
840 | | |
841 | 81.0M | GetPixelInfoPixel(image,p,&pixel); |
842 | 81.0M | if (IsFuzzyEquivalencePixelInfo(&pixel,&background) == MagickFalse) |
843 | 0 | { |
844 | 0 | vertices[n].x=(double) x; |
845 | 0 | vertices[n].y=(double) y; |
846 | 0 | n++; |
847 | 0 | } |
848 | 81.0M | p+=(ptrdiff_t) GetPixelChannels(image); |
849 | 81.0M | } |
850 | 49.8k | } |
851 | 1.07k | image_view=DestroyCacheView(image_view); |
852 | | /* |
853 | | Return the convex hull of the image foreground object(s). |
854 | | */ |
855 | 1.07k | TraceConvexHull(vertices,n,&monotone_chain,number_vertices); |
856 | 1.07k | convex_hull=(PointInfo *) AcquireQuantumMemory(*number_vertices, |
857 | 1.07k | sizeof(*convex_hull)); |
858 | 1.07k | if (convex_hull != (PointInfo *) NULL) |
859 | 0 | for (n=0; n < *number_vertices; n++) |
860 | 0 | convex_hull[n]=(*monotone_chain[n]); |
861 | 1.07k | monotone_info=RelinquishVirtualMemory(monotone_info); |
862 | 1.07k | vertices_info=RelinquishVirtualMemory(vertices_info); |
863 | 1.07k | return(convex_hull); |
864 | 1.33k | } |
865 | | |
866 | | /* |
867 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
868 | | % % |
869 | | % % |
870 | | % % |
871 | | % G e t I m a g e D e p t h % |
872 | | % % |
873 | | % % |
874 | | % % |
875 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
876 | | % |
877 | | % GetImageDepth() returns the depth of a particular image channel. |
878 | | % |
879 | | % The format of the GetImageDepth method is: |
880 | | % |
881 | | % size_t GetImageDepth(const Image *image,ExceptionInfo *exception) |
882 | | % |
883 | | % A description of each parameter follows: |
884 | | % |
885 | | % o image: the image. |
886 | | % |
887 | | % o exception: return any errors or warnings in this structure. |
888 | | % |
889 | | */ |
890 | | MagickExport size_t GetImageDepth(const Image *image,ExceptionInfo *exception) |
891 | 15.4k | { |
892 | 15.4k | CacheView |
893 | 15.4k | *image_view; |
894 | | |
895 | 15.4k | MagickBooleanType |
896 | 15.4k | status; |
897 | | |
898 | 15.4k | ssize_t |
899 | 15.4k | i; |
900 | | |
901 | 15.4k | size_t |
902 | 15.4k | *current_depth, |
903 | 15.4k | depth, |
904 | 15.4k | number_threads; |
905 | | |
906 | 15.4k | ssize_t |
907 | 15.4k | y; |
908 | | |
909 | | /* |
910 | | Compute image depth. |
911 | | */ |
912 | 15.4k | assert(image != (Image *) NULL); |
913 | 15.4k | assert(image->signature == MagickCoreSignature); |
914 | 15.4k | if (IsEventLogging() != MagickFalse) |
915 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
916 | 15.4k | number_threads=(size_t) GetMagickResourceLimit(ThreadResource); |
917 | 15.4k | current_depth=(size_t *) AcquireQuantumMemory(number_threads, |
918 | 15.4k | sizeof(*current_depth)); |
919 | 15.4k | if (current_depth == (size_t *) NULL) |
920 | 15.4k | ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); |
921 | 15.4k | status=MagickTrue; |
922 | 30.8k | for (i=0; i < (ssize_t) number_threads; i++) |
923 | 15.4k | current_depth[i]=1; |
924 | 15.4k | if ((image->storage_class == PseudoClass) && |
925 | 326 | ((image->alpha_trait & BlendPixelTrait) == 0)) |
926 | 286 | { |
927 | 9.69k | for (i=0; i < (ssize_t) image->colors; i++) |
928 | 9.41k | { |
929 | 9.41k | const int |
930 | 9.41k | id = GetOpenMPThreadId(); |
931 | | |
932 | 10.0k | while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH) |
933 | 10.0k | { |
934 | 10.0k | MagickBooleanType |
935 | 10.0k | atDepth; |
936 | | |
937 | 10.0k | QuantumAny |
938 | 10.0k | range; |
939 | | |
940 | 10.0k | atDepth=MagickTrue; |
941 | 10.0k | range=GetQuantumRange(current_depth[id]); |
942 | 10.0k | if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) |
943 | 10.0k | if (IsPixelAtDepth(ClampToQuantum(image->colormap[i].red),range) == MagickFalse) |
944 | 456 | atDepth=MagickFalse; |
945 | 10.0k | if ((atDepth != MagickFalse) && |
946 | 9.60k | (GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) |
947 | 9.60k | if (IsPixelAtDepth(ClampToQuantum(image->colormap[i].green),range) == MagickFalse) |
948 | 145 | atDepth=MagickFalse; |
949 | 10.0k | if ((atDepth != MagickFalse) && |
950 | 9.45k | (GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) |
951 | 9.45k | if (IsPixelAtDepth(ClampToQuantum(image->colormap[i].blue),range) == MagickFalse) |
952 | 42 | atDepth=MagickFalse; |
953 | 10.0k | if ((atDepth != MagickFalse)) |
954 | 9.41k | break; |
955 | 643 | current_depth[id]++; |
956 | 643 | } |
957 | 9.41k | } |
958 | 286 | depth=current_depth[0]; |
959 | 286 | for (i=1; i < (ssize_t) number_threads; i++) |
960 | 0 | if (depth < current_depth[i]) |
961 | 0 | depth=current_depth[i]; |
962 | 286 | current_depth=(size_t *) RelinquishMagickMemory(current_depth); |
963 | 286 | return(depth); |
964 | 286 | } |
965 | 15.1k | image_view=AcquireVirtualCacheView(image,exception); |
966 | | #if !defined(MAGICKCORE_HDRI_SUPPORT) |
967 | | DisableMSCWarning(4127) |
968 | | if ((1UL*QuantumRange) <= MaxMap) |
969 | | RestoreMSCWarning |
970 | | { |
971 | | size_t |
972 | | *depth_map; |
973 | | |
974 | | /* |
975 | | Scale pixels to desired (optimized with depth map). |
976 | | */ |
977 | | depth_map=(size_t *) AcquireQuantumMemory(MaxMap+1,sizeof(*depth_map)); |
978 | | if (depth_map == (size_t *) NULL) |
979 | | ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); |
980 | | for (i=0; i <= (ssize_t) MaxMap; i++) |
981 | | { |
982 | | for (depth=1; depth < (size_t) MAGICKCORE_QUANTUM_DEPTH; depth++) |
983 | | { |
984 | | Quantum |
985 | | pixel; |
986 | | |
987 | | QuantumAny |
988 | | range; |
989 | | |
990 | | range=GetQuantumRange(depth); |
991 | | pixel=(Quantum) i; |
992 | | if (pixel == ScaleAnyToQuantum(ScaleQuantumToAny(pixel,range),range)) |
993 | | break; |
994 | | } |
995 | | depth_map[i]=depth; |
996 | | } |
997 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
998 | | #pragma omp parallel for schedule(static) shared(status) \ |
999 | | magick_number_threads(image,image,image->rows,1) |
1000 | | #endif |
1001 | | for (y=0; y < (ssize_t) image->rows; y++) |
1002 | | { |
1003 | | const int |
1004 | | id = GetOpenMPThreadId(); |
1005 | | |
1006 | | const Quantum |
1007 | | *magick_restrict p; |
1008 | | |
1009 | | ssize_t |
1010 | | x; |
1011 | | |
1012 | | if (status == MagickFalse) |
1013 | | continue; |
1014 | | p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); |
1015 | | if (p == (const Quantum *) NULL) |
1016 | | continue; |
1017 | | for (x=0; x < (ssize_t) image->columns; x++) |
1018 | | { |
1019 | | ssize_t |
1020 | | j; |
1021 | | |
1022 | | for (j=0; j < (ssize_t) GetPixelChannels(image); j++) |
1023 | | { |
1024 | | PixelChannel channel = GetPixelChannelChannel(image,j); |
1025 | | PixelTrait traits = GetPixelChannelTraits(image,channel); |
1026 | | if ((traits & UpdatePixelTrait) == 0) |
1027 | | continue; |
1028 | | if (depth_map[ScaleQuantumToMap(p[j])] > current_depth[id]) |
1029 | | current_depth[id]=depth_map[ScaleQuantumToMap(p[j])]; |
1030 | | } |
1031 | | p+=(ptrdiff_t) GetPixelChannels(image); |
1032 | | } |
1033 | | if (current_depth[id] == MAGICKCORE_QUANTUM_DEPTH) |
1034 | | status=MagickFalse; |
1035 | | } |
1036 | | image_view=DestroyCacheView(image_view); |
1037 | | depth=current_depth[0]; |
1038 | | for (i=1; i < (ssize_t) number_threads; i++) |
1039 | | if (depth < current_depth[i]) |
1040 | | depth=current_depth[i]; |
1041 | | depth_map=(size_t *) RelinquishMagickMemory(depth_map); |
1042 | | current_depth=(size_t *) RelinquishMagickMemory(current_depth); |
1043 | | return(depth); |
1044 | | } |
1045 | | #endif |
1046 | | /* |
1047 | | Compute pixel depth. |
1048 | | */ |
1049 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
1050 | | #pragma omp parallel for schedule(static) shared(status) \ |
1051 | | magick_number_threads(image,image,image->rows,1) |
1052 | | #endif |
1053 | 432k | for (y=0; y < (ssize_t) image->rows; y++) |
1054 | 417k | { |
1055 | 417k | const int |
1056 | 417k | id = GetOpenMPThreadId(); |
1057 | | |
1058 | 417k | const Quantum |
1059 | 417k | *magick_restrict p; |
1060 | | |
1061 | 417k | ssize_t |
1062 | 417k | x; |
1063 | | |
1064 | 417k | if (status == MagickFalse) |
1065 | 124k | continue; |
1066 | 293k | p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); |
1067 | 293k | if (p == (const Quantum *) NULL) |
1068 | 0 | continue; |
1069 | 330M | for (x=0; x < (ssize_t) image->columns; x++) |
1070 | 330M | { |
1071 | 330M | ssize_t |
1072 | 330M | j; |
1073 | | |
1074 | 1.32G | for (j=0; j < (ssize_t) GetPixelChannels(image); j++) |
1075 | 990M | { |
1076 | 990M | PixelChannel |
1077 | 990M | channel; |
1078 | | |
1079 | 990M | PixelTrait |
1080 | 990M | traits; |
1081 | | |
1082 | 990M | channel=GetPixelChannelChannel(image,j); |
1083 | 990M | traits=GetPixelChannelTraits(image,channel); |
1084 | 990M | if ((traits & UpdatePixelTrait) == 0) |
1085 | 2.50M | continue; |
1086 | 988M | while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH) |
1087 | 987M | { |
1088 | 987M | QuantumAny |
1089 | 987M | range; |
1090 | | |
1091 | 987M | range=GetQuantumRange(current_depth[id]); |
1092 | 987M | if (p[j] == ScaleAnyToQuantum(ScaleQuantumToAny(p[j],range),range)) |
1093 | 987M | break; |
1094 | 9.47k | current_depth[id]++; |
1095 | 9.47k | } |
1096 | 988M | } |
1097 | 330M | p+=(ptrdiff_t) GetPixelChannels(image); |
1098 | 330M | } |
1099 | 293k | if (current_depth[id] == MAGICKCORE_QUANTUM_DEPTH) |
1100 | 463 | status=MagickFalse; |
1101 | 293k | } |
1102 | 15.1k | image_view=DestroyCacheView(image_view); |
1103 | 15.1k | depth=current_depth[0]; |
1104 | 15.1k | for (i=1; i < (ssize_t) number_threads; i++) |
1105 | 0 | if (depth < current_depth[i]) |
1106 | 0 | depth=current_depth[i]; |
1107 | 15.1k | current_depth=(size_t *) RelinquishMagickMemory(current_depth); |
1108 | 15.1k | return(depth); |
1109 | 15.4k | } |
1110 | | |
1111 | | /* |
1112 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1113 | | % % |
1114 | | % % |
1115 | | % % |
1116 | | % G e t I m a g e M i n i m u m B o u n d i n g B o x % |
1117 | | % % |
1118 | | % % |
1119 | | % % |
1120 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1121 | | % |
1122 | | % GetImageMinimumBoundingBox() returns the points that form the minimum |
1123 | | % bounding box around the image foreground objects with the "Rotating |
1124 | | % Calipers" algorithm. The method also returns these properties: |
1125 | | % minimum-bounding-box:area, minimum-bounding-box:width, |
1126 | | % minimum-bounding-box:height, and minimum-bounding-box:angle. |
1127 | | % |
1128 | | % The format of the GetImageMinimumBoundingBox method is: |
1129 | | % |
1130 | | % PointInfo *GetImageMinimumBoundingBox(Image *image, |
1131 | | % size_t number_vertices,ExceptionInfo *exception) |
1132 | | % |
1133 | | % A description of each parameter follows: |
1134 | | % |
1135 | | % o image: the image. |
1136 | | % |
1137 | | % o number_vertices: the number of vertices in the bounding box. |
1138 | | % |
1139 | | % o exception: return any errors or warnings in this structure. |
1140 | | % |
1141 | | */ |
1142 | | |
1143 | | typedef struct _CaliperInfo |
1144 | | { |
1145 | | double |
1146 | | area, |
1147 | | width, |
1148 | | height, |
1149 | | projection; |
1150 | | |
1151 | | ssize_t |
1152 | | p, |
1153 | | q, |
1154 | | v; |
1155 | | } CaliperInfo; |
1156 | | |
1157 | | static inline double getAngle(PointInfo *p,PointInfo *q) |
1158 | 0 | { |
1159 | | /* |
1160 | | Get the angle between line (p,q) and horizontal axis, in degrees. |
1161 | | */ |
1162 | 0 | return(RadiansToDegrees(atan2(q->y-p->y,q->x-p->x))); |
1163 | 0 | } |
1164 | | |
1165 | | static inline double getDistance(PointInfo *p,PointInfo *q) |
1166 | 0 | { |
1167 | 0 | double |
1168 | 0 | distance; |
1169 | |
|
1170 | 0 | distance=hypot(p->x-q->x,p->y-q->y); |
1171 | 0 | return(distance*distance); |
1172 | 0 | } |
1173 | | |
1174 | | static inline double getProjection(PointInfo *p,PointInfo *q,PointInfo *v) |
1175 | 0 | { |
1176 | 0 | double |
1177 | 0 | distance; |
1178 | | |
1179 | | /* |
1180 | | Projection of vector (x,y) - p into a line passing through p and q. |
1181 | | */ |
1182 | 0 | distance=getDistance(p,q); |
1183 | 0 | if (distance < MagickEpsilon) |
1184 | 0 | return(INFINITY); |
1185 | 0 | return((q->x-p->x)*(v->x-p->x)+(v->y-p->y)*(q->y-p->y))/sqrt(distance); |
1186 | 0 | } |
1187 | | |
1188 | | static inline double getFeretDiameter(PointInfo *p,PointInfo *q,PointInfo *v) |
1189 | 0 | { |
1190 | 0 | double |
1191 | 0 | distance; |
1192 | | |
1193 | | /* |
1194 | | Distance from a point (x,y) to a line passing through p and q. |
1195 | | */ |
1196 | 0 | distance=getDistance(p,q); |
1197 | 0 | if (distance < MagickEpsilon) |
1198 | 0 | return(INFINITY); |
1199 | 0 | return((q->x-p->x)*(v->y-p->y)-(v->x-p->x)*(q->y-p->y))/sqrt(distance); |
1200 | 0 | } |
1201 | | |
1202 | | MagickExport PointInfo *GetImageMinimumBoundingBox(Image *image, |
1203 | | size_t *number_vertices,ExceptionInfo *exception) |
1204 | 1.21k | { |
1205 | 1.21k | CaliperInfo |
1206 | 1.21k | caliper_info; |
1207 | | |
1208 | 1.21k | const char |
1209 | 1.21k | *artifact; |
1210 | | |
1211 | 1.21k | double |
1212 | 1.21k | angle, |
1213 | 1.21k | diameter, |
1214 | 1.21k | distance; |
1215 | | |
1216 | 1.21k | PointInfo |
1217 | 1.21k | *bounding_box, |
1218 | 1.21k | *vertices; |
1219 | | |
1220 | 1.21k | size_t |
1221 | 1.21k | number_hull_vertices; |
1222 | | |
1223 | 1.21k | ssize_t |
1224 | 1.21k | i; |
1225 | | |
1226 | | /* |
1227 | | Generate the minimum bounding box with the "Rotating Calipers" algorithm. |
1228 | | */ |
1229 | 1.21k | assert(image != (Image *) NULL); |
1230 | 1.21k | assert(image->signature == MagickCoreSignature); |
1231 | 1.21k | if (IsEventLogging() != MagickFalse) |
1232 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
1233 | 1.21k | *number_vertices=0; |
1234 | 1.21k | vertices=GetImageConvexHull(image,&number_hull_vertices,exception); |
1235 | 1.21k | if (vertices == (PointInfo *) NULL) |
1236 | 1.21k | return((PointInfo *) NULL); |
1237 | 0 | *number_vertices=4; |
1238 | 0 | bounding_box=(PointInfo *) AcquireQuantumMemory(*number_vertices, |
1239 | 0 | sizeof(*bounding_box)); |
1240 | 0 | if (bounding_box == (PointInfo *) NULL) |
1241 | 0 | { |
1242 | 0 | vertices=(PointInfo *) RelinquishMagickMemory(vertices); |
1243 | 0 | return((PointInfo *) NULL); |
1244 | 0 | } |
1245 | 0 | caliper_info.area=2.0*image->columns*image->rows; |
1246 | 0 | caliper_info.width=(double) image->columns+image->rows; |
1247 | 0 | caliper_info.height=0.0; |
1248 | 0 | caliper_info.projection=0.0; |
1249 | 0 | caliper_info.p=(-1); |
1250 | 0 | caliper_info.q=(-1); |
1251 | 0 | caliper_info.v=(-1); |
1252 | 0 | for (i=0; i < (ssize_t) number_hull_vertices; i++) |
1253 | 0 | { |
1254 | 0 | double |
1255 | 0 | area = 0.0, |
1256 | 0 | max_projection = 0.0, |
1257 | 0 | min_diameter = -1.0, |
1258 | 0 | min_projection = 0.0; |
1259 | |
|
1260 | 0 | ssize_t |
1261 | 0 | j, |
1262 | 0 | k; |
1263 | |
|
1264 | 0 | ssize_t |
1265 | 0 | p = -1, |
1266 | 0 | q = -1, |
1267 | 0 | v = -1; |
1268 | |
|
1269 | 0 | for (j=0; j < (ssize_t) number_hull_vertices; j++) |
1270 | 0 | { |
1271 | 0 | diameter=fabs(getFeretDiameter(&vertices[i], |
1272 | 0 | &vertices[(i+1) % (ssize_t) number_hull_vertices],&vertices[j])); |
1273 | 0 | if (min_diameter < diameter) |
1274 | 0 | { |
1275 | 0 | min_diameter=diameter; |
1276 | 0 | p=i; |
1277 | 0 | q=(i+1) % (ssize_t) number_hull_vertices; |
1278 | 0 | v=j; |
1279 | 0 | } |
1280 | 0 | } |
1281 | 0 | for (k=0; k < (ssize_t) number_hull_vertices; k++) |
1282 | 0 | { |
1283 | 0 | double |
1284 | 0 | projection; |
1285 | | |
1286 | | /* |
1287 | | Rotating calipers. |
1288 | | */ |
1289 | 0 | projection=getProjection(&vertices[p],&vertices[q],&vertices[k]); |
1290 | 0 | min_projection=MagickMin(min_projection,projection); |
1291 | 0 | max_projection=MagickMax(max_projection,projection); |
1292 | 0 | } |
1293 | 0 | area=min_diameter*(max_projection-min_projection); |
1294 | 0 | if (caliper_info.area > area) |
1295 | 0 | { |
1296 | 0 | caliper_info.area=area; |
1297 | 0 | caliper_info.width=min_diameter; |
1298 | 0 | caliper_info.height=max_projection-min_projection; |
1299 | 0 | caliper_info.projection=max_projection; |
1300 | 0 | caliper_info.p=p; |
1301 | 0 | caliper_info.q=q; |
1302 | 0 | caliper_info.v=v; |
1303 | 0 | } |
1304 | 0 | } |
1305 | | /* |
1306 | | Initialize minimum bounding box. |
1307 | | */ |
1308 | 0 | diameter=getFeretDiameter(&vertices[caliper_info.p], |
1309 | 0 | &vertices[caliper_info.q],&vertices[caliper_info.v]); |
1310 | 0 | angle=atan2(vertices[caliper_info.q].y-vertices[caliper_info.p].y, |
1311 | 0 | vertices[caliper_info.q].x-vertices[caliper_info.p].x); |
1312 | 0 | bounding_box[0].x=vertices[caliper_info.p].x+cos(angle)* |
1313 | 0 | caliper_info.projection; |
1314 | 0 | bounding_box[0].y=vertices[caliper_info.p].y+sin(angle)* |
1315 | 0 | caliper_info.projection; |
1316 | 0 | bounding_box[1].x=floor(bounding_box[0].x+cos(angle+MagickPI/2.0)*diameter+ |
1317 | 0 | 0.5); |
1318 | 0 | bounding_box[1].y=floor(bounding_box[0].y+sin(angle+MagickPI/2.0)*diameter+ |
1319 | 0 | 0.5); |
1320 | 0 | bounding_box[2].x=floor(bounding_box[1].x+cos(angle)*(-caliper_info.height)+ |
1321 | 0 | 0.5); |
1322 | 0 | bounding_box[2].y=floor(bounding_box[1].y+sin(angle)*(-caliper_info.height)+ |
1323 | 0 | 0.5); |
1324 | 0 | bounding_box[3].x=floor(bounding_box[2].x+cos(angle+MagickPI/2.0)*(-diameter)+ |
1325 | 0 | 0.5); |
1326 | 0 | bounding_box[3].y=floor(bounding_box[2].y+sin(angle+MagickPI/2.0)*(-diameter)+ |
1327 | 0 | 0.5); |
1328 | | /* |
1329 | | Export minimum bounding box properties. |
1330 | | */ |
1331 | 0 | (void) FormatImageProperty(image,"minimum-bounding-box:area","%.*g", |
1332 | 0 | GetMagickPrecision(),caliper_info.area); |
1333 | 0 | (void) FormatImageProperty(image,"minimum-bounding-box:width","%.*g", |
1334 | 0 | GetMagickPrecision(),caliper_info.width); |
1335 | 0 | (void) FormatImageProperty(image,"minimum-bounding-box:height","%.*g", |
1336 | 0 | GetMagickPrecision(),caliper_info.height); |
1337 | 0 | (void) FormatImageProperty(image,"minimum-bounding-box:_p","%.*g,%.*g", |
1338 | 0 | GetMagickPrecision(),vertices[caliper_info.p].x, |
1339 | 0 | GetMagickPrecision(),vertices[caliper_info.p].y); |
1340 | 0 | (void) FormatImageProperty(image,"minimum-bounding-box:_q","%.*g,%.*g", |
1341 | 0 | GetMagickPrecision(),vertices[caliper_info.q].x, |
1342 | 0 | GetMagickPrecision(),vertices[caliper_info.q].y); |
1343 | 0 | (void) FormatImageProperty(image,"minimum-bounding-box:_v","%.*g,%.*g", |
1344 | 0 | GetMagickPrecision(),vertices[caliper_info.v].x, |
1345 | 0 | GetMagickPrecision(),vertices[caliper_info.v].y); |
1346 | | /* |
1347 | | Find smallest angle to origin. |
1348 | | */ |
1349 | 0 | distance=hypot(bounding_box[0].x,bounding_box[0].y); |
1350 | 0 | angle=getAngle(&bounding_box[0],&bounding_box[1]); |
1351 | 0 | for (i=1; i < 4; i++) |
1352 | 0 | { |
1353 | 0 | double d = hypot(bounding_box[i].x,bounding_box[i].y); |
1354 | 0 | if (d < distance) |
1355 | 0 | { |
1356 | 0 | distance=d; |
1357 | 0 | angle=getAngle(&bounding_box[i],&bounding_box[(i+1) % 4]); |
1358 | 0 | } |
1359 | 0 | } |
1360 | 0 | artifact=GetImageArtifact(image,"minimum-bounding-box:orientation"); |
1361 | 0 | if (artifact != (const char *) NULL) |
1362 | 0 | { |
1363 | 0 | double |
1364 | 0 | length, |
1365 | 0 | q_length, |
1366 | 0 | p_length; |
1367 | |
|
1368 | 0 | PointInfo |
1369 | 0 | delta, |
1370 | 0 | point; |
1371 | | |
1372 | | /* |
1373 | | Find smallest perpendicular distance from edge to origin. |
1374 | | */ |
1375 | 0 | point=bounding_box[0]; |
1376 | 0 | for (i=1; i < 4; i++) |
1377 | 0 | { |
1378 | 0 | if (bounding_box[i].x < point.x) |
1379 | 0 | point.x=bounding_box[i].x; |
1380 | 0 | if (bounding_box[i].y < point.y) |
1381 | 0 | point.y=bounding_box[i].y; |
1382 | 0 | } |
1383 | 0 | for (i=0; i < 4; i++) |
1384 | 0 | { |
1385 | 0 | bounding_box[i].x-=point.x; |
1386 | 0 | bounding_box[i].y-=point.y; |
1387 | 0 | } |
1388 | 0 | for (i=0; i < 4; i++) |
1389 | 0 | { |
1390 | 0 | double |
1391 | 0 | d, |
1392 | 0 | intercept, |
1393 | 0 | slope; |
1394 | |
|
1395 | 0 | delta.x=bounding_box[(i+1) % 4].x-bounding_box[i].x; |
1396 | 0 | delta.y=bounding_box[(i+1) % 4].y-bounding_box[i].y; |
1397 | 0 | slope=delta.y*MagickSafeReciprocal(delta.x); |
1398 | 0 | intercept=bounding_box[(i+1) % 4].y-slope*bounding_box[i].x; |
1399 | 0 | d=fabs((slope*bounding_box[i].x-bounding_box[i].y+intercept)* |
1400 | 0 | MagickSafeReciprocal(sqrt(slope*slope+1.0))); |
1401 | 0 | if ((i == 0) || (d < distance)) |
1402 | 0 | { |
1403 | 0 | distance=d; |
1404 | 0 | point=delta; |
1405 | 0 | } |
1406 | 0 | } |
1407 | 0 | angle=RadiansToDegrees(atan(point.y*MagickSafeReciprocal(point.x))); |
1408 | 0 | length=hypot(point.x,point.y); |
1409 | 0 | p_length=fabs((double) MagickMax(caliper_info.width,caliper_info.height)- |
1410 | 0 | length); |
1411 | 0 | q_length=fabs(length-(double) MagickMin(caliper_info.width, |
1412 | 0 | caliper_info.height)); |
1413 | 0 | if (LocaleCompare(artifact,"landscape") == 0) |
1414 | 0 | { |
1415 | 0 | if (p_length > q_length) |
1416 | 0 | angle+=(angle < 0.0) ? 90.0 : -90.0; |
1417 | 0 | } |
1418 | 0 | else |
1419 | 0 | if (LocaleCompare(artifact,"portrait") == 0) |
1420 | 0 | { |
1421 | 0 | if (p_length < q_length) |
1422 | 0 | angle+=(angle >= 0.0) ? 90.0 : -90.0; |
1423 | 0 | } |
1424 | 0 | } |
1425 | 0 | (void) FormatImageProperty(image,"minimum-bounding-box:angle","%.*g", |
1426 | 0 | GetMagickPrecision(),angle); |
1427 | 0 | (void) FormatImageProperty(image,"minimum-bounding-box:unrotate","%.*g", |
1428 | 0 | GetMagickPrecision(),-angle); |
1429 | 0 | vertices=(PointInfo *) RelinquishMagickMemory(vertices); |
1430 | 0 | return(bounding_box); |
1431 | 0 | } |
1432 | | |
1433 | | /* |
1434 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1435 | | % % |
1436 | | % % |
1437 | | % % |
1438 | | % G e t I m a g e Q u a n t u m D e p t h % |
1439 | | % % |
1440 | | % % |
1441 | | % % |
1442 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1443 | | % |
1444 | | % GetImageQuantumDepth() returns the depth of the image rounded to a legal |
1445 | | % quantum depth: 8, 16, or 32. |
1446 | | % |
1447 | | % The format of the GetImageQuantumDepth method is: |
1448 | | % |
1449 | | % size_t GetImageQuantumDepth(const Image *image, |
1450 | | % const MagickBooleanType constrain) |
1451 | | % |
1452 | | % A description of each parameter follows: |
1453 | | % |
1454 | | % o image: the image. |
1455 | | % |
1456 | | % o constrain: A value other than MagickFalse, constrains the depth to |
1457 | | % a maximum of MAGICKCORE_QUANTUM_DEPTH. |
1458 | | % |
1459 | | */ |
1460 | | MagickExport size_t GetImageQuantumDepth(const Image *image, |
1461 | | const MagickBooleanType constrain) |
1462 | 70.2k | { |
1463 | 70.2k | size_t |
1464 | 70.2k | depth; |
1465 | | |
1466 | 70.2k | depth=image->depth; |
1467 | 70.2k | if (depth <= 8) |
1468 | 55.0k | depth=8; |
1469 | 15.1k | else |
1470 | 15.1k | if (depth <= 16) |
1471 | 12.2k | depth=16; |
1472 | 2.90k | else |
1473 | 2.90k | if (depth <= 32) |
1474 | 1.56k | depth=32; |
1475 | 1.33k | else |
1476 | 1.33k | if (depth <= 64) |
1477 | 1.33k | depth=64; |
1478 | 70.2k | if (constrain != MagickFalse) |
1479 | 0 | depth=(size_t) MagickMin((double) depth,(double) MAGICKCORE_QUANTUM_DEPTH); |
1480 | 70.2k | return(depth); |
1481 | 70.2k | } |
1482 | | |
1483 | | /* |
1484 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1485 | | % % |
1486 | | % % |
1487 | | % % |
1488 | | % G e t I m a g e T y p e % |
1489 | | % % |
1490 | | % % |
1491 | | % % |
1492 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1493 | | % |
1494 | | % GetImageType() returns the type of image: |
1495 | | % |
1496 | | % Bilevel Grayscale GrayscaleMatte |
1497 | | % Palette PaletteMatte TrueColor |
1498 | | % TrueColorMatte ColorSeparation ColorSeparationMatte |
1499 | | % |
1500 | | % The format of the GetImageType method is: |
1501 | | % |
1502 | | % ImageType GetImageType(const Image *image) |
1503 | | % |
1504 | | % A description of each parameter follows: |
1505 | | % |
1506 | | % o image: the image. |
1507 | | % |
1508 | | */ |
1509 | | MagickExport ImageType GetImageType(const Image *image) |
1510 | 0 | { |
1511 | 0 | assert(image != (Image *) NULL); |
1512 | 0 | assert(image->signature == MagickCoreSignature); |
1513 | 0 | if (image->colorspace == CMYKColorspace) |
1514 | 0 | { |
1515 | 0 | if ((image->alpha_trait & BlendPixelTrait) == 0) |
1516 | 0 | return(ColorSeparationType); |
1517 | 0 | return(ColorSeparationAlphaType); |
1518 | 0 | } |
1519 | 0 | if (IsImageMonochrome(image) != MagickFalse) |
1520 | 0 | return(BilevelType); |
1521 | 0 | if (IsImageGray(image) != MagickFalse) |
1522 | 0 | { |
1523 | 0 | if (image->alpha_trait != UndefinedPixelTrait) |
1524 | 0 | return(GrayscaleAlphaType); |
1525 | 0 | return(GrayscaleType); |
1526 | 0 | } |
1527 | 0 | if (IsPaletteImage(image) != MagickFalse) |
1528 | 0 | { |
1529 | 0 | if (image->alpha_trait != UndefinedPixelTrait) |
1530 | 0 | return(PaletteAlphaType); |
1531 | 0 | return(PaletteType); |
1532 | 0 | } |
1533 | 0 | if (image->alpha_trait != UndefinedPixelTrait) |
1534 | 0 | return(TrueColorAlphaType); |
1535 | 0 | return(TrueColorType); |
1536 | 0 | } |
1537 | | |
1538 | | /* |
1539 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1540 | | % % |
1541 | | % % |
1542 | | % % |
1543 | | % I d e n t i f y I m a g e G r a y % |
1544 | | % % |
1545 | | % % |
1546 | | % % |
1547 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1548 | | % |
1549 | | % IdentifyImageGray() returns grayscale if all the pixels in the image have |
1550 | | % the same red, green, and blue intensities, and bi-level if the intensity is |
1551 | | % either 0 or QuantumRange. Otherwise undefined is returned. |
1552 | | % |
1553 | | % The format of the IdentifyImageGray method is: |
1554 | | % |
1555 | | % ImageType IdentifyImageGray(const Image *image,ExceptionInfo *exception) |
1556 | | % |
1557 | | % A description of each parameter follows: |
1558 | | % |
1559 | | % o image: the image. |
1560 | | % |
1561 | | % o exception: return any errors or warnings in this structure. |
1562 | | % |
1563 | | */ |
1564 | | MagickExport ImageType IdentifyImageGray(const Image *image, |
1565 | | ExceptionInfo *exception) |
1566 | 33.0k | { |
1567 | 33.0k | CacheView |
1568 | 33.0k | *image_view; |
1569 | | |
1570 | 33.0k | ImageType |
1571 | 33.0k | type = BilevelType; |
1572 | | |
1573 | 33.0k | MagickBooleanType |
1574 | 33.0k | status = MagickTrue; |
1575 | | |
1576 | 33.0k | ssize_t |
1577 | 33.0k | y; |
1578 | | |
1579 | 33.0k | assert(image != (Image *) NULL); |
1580 | 33.0k | assert(image->signature == MagickCoreSignature); |
1581 | 33.0k | if (IsEventLogging() != MagickFalse) |
1582 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
1583 | 33.0k | if (IsImageGray(image) != MagickFalse) |
1584 | 2.87k | return(image->type); |
1585 | 30.1k | if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) |
1586 | 1.28k | return(UndefinedType); |
1587 | 28.8k | image_view=AcquireVirtualCacheView(image,exception); |
1588 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
1589 | | #pragma omp parallel for schedule(static) shared(status,type) \ |
1590 | | magick_number_threads(image,image,image->rows,2) |
1591 | | #endif |
1592 | 5.59M | for (y=0; y < (ssize_t) image->rows; y++) |
1593 | 5.56M | { |
1594 | 5.56M | const Quantum |
1595 | 5.56M | *p; |
1596 | | |
1597 | 5.56M | ssize_t |
1598 | 5.56M | x; |
1599 | | |
1600 | 5.56M | if (status == MagickFalse) |
1601 | 3.93M | continue; |
1602 | 1.62M | p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); |
1603 | 1.62M | if (p == (const Quantum *) NULL) |
1604 | 159 | { |
1605 | 159 | status=MagickFalse; |
1606 | 159 | continue; |
1607 | 159 | } |
1608 | 1.63G | for (x=0; x < (ssize_t) image->columns; x++) |
1609 | 1.63G | { |
1610 | 1.63G | if (IsPixelGray(image,p) == MagickFalse) |
1611 | 10.3k | { |
1612 | 10.3k | status=MagickFalse; |
1613 | 10.3k | break; |
1614 | 10.3k | } |
1615 | 1.63G | if ((type == BilevelType) && (IsPixelMonochrome(image,p) == MagickFalse)) |
1616 | 4.61k | type=GrayscaleType; |
1617 | 1.63G | p+=(ptrdiff_t) GetPixelChannels(image); |
1618 | 1.63G | } |
1619 | 1.62M | } |
1620 | 28.8k | image_view=DestroyCacheView(image_view); |
1621 | 28.8k | if ((type == GrayscaleType) && (image->alpha_trait != UndefinedPixelTrait)) |
1622 | 764 | type=GrayscaleAlphaType; |
1623 | 28.8k | if (status == MagickFalse) |
1624 | 10.4k | return(UndefinedType); |
1625 | 18.3k | return(type); |
1626 | 28.8k | } |
1627 | | |
1628 | | /* |
1629 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1630 | | % % |
1631 | | % % |
1632 | | % % |
1633 | | % I d e n t i f y I m a g e M o n o c h r o m e % |
1634 | | % % |
1635 | | % % |
1636 | | % % |
1637 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1638 | | % |
1639 | | % IdentifyImageMonochrome() returns MagickTrue if all the pixels in the image |
1640 | | % have the same red, green, and blue intensities and the intensity is either |
1641 | | % 0 or QuantumRange. |
1642 | | % |
1643 | | % The format of the IdentifyImageMonochrome method is: |
1644 | | % |
1645 | | % MagickBooleanType IdentifyImageMonochrome(const Image *image, |
1646 | | % ExceptionInfo *exception) |
1647 | | % |
1648 | | % A description of each parameter follows: |
1649 | | % |
1650 | | % o image: the image. |
1651 | | % |
1652 | | % o exception: return any errors or warnings in this structure. |
1653 | | % |
1654 | | */ |
1655 | | MagickExport MagickBooleanType IdentifyImageMonochrome(const Image *image, |
1656 | | ExceptionInfo *exception) |
1657 | 6.82k | { |
1658 | 6.82k | CacheView |
1659 | 6.82k | *image_view; |
1660 | | |
1661 | 6.82k | ImageType |
1662 | 6.82k | type = BilevelType; |
1663 | | |
1664 | 6.82k | ssize_t |
1665 | 6.82k | y; |
1666 | | |
1667 | 6.82k | assert(image != (Image *) NULL); |
1668 | 6.82k | assert(image->signature == MagickCoreSignature); |
1669 | 6.82k | if (IsEventLogging() != MagickFalse) |
1670 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
1671 | 6.82k | if (image->type == BilevelType) |
1672 | 0 | return(MagickTrue); |
1673 | 6.82k | if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) |
1674 | 0 | return(MagickFalse); |
1675 | 6.82k | image_view=AcquireVirtualCacheView(image,exception); |
1676 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
1677 | | #pragma omp parallel for schedule(static) shared(type) \ |
1678 | | magick_number_threads(image,image,image->rows,2) |
1679 | | #endif |
1680 | 1.01M | for (y=0; y < (ssize_t) image->rows; y++) |
1681 | 1.00M | { |
1682 | 1.00M | const Quantum |
1683 | 1.00M | *p; |
1684 | | |
1685 | 1.00M | ssize_t |
1686 | 1.00M | x; |
1687 | | |
1688 | 1.00M | if (type == UndefinedType) |
1689 | 340k | continue; |
1690 | 663k | p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); |
1691 | 663k | if (p == (const Quantum *) NULL) |
1692 | 0 | { |
1693 | 0 | type=UndefinedType; |
1694 | 0 | continue; |
1695 | 0 | } |
1696 | 556M | for (x=0; x < (ssize_t) image->columns; x++) |
1697 | 555M | { |
1698 | 555M | if (IsPixelMonochrome(image,p) == MagickFalse) |
1699 | 1.22k | { |
1700 | 1.22k | type=UndefinedType; |
1701 | 1.22k | break; |
1702 | 1.22k | } |
1703 | 555M | p+=(ptrdiff_t) GetPixelChannels(image); |
1704 | 555M | } |
1705 | 663k | } |
1706 | 6.82k | image_view=DestroyCacheView(image_view); |
1707 | 6.82k | return(type == BilevelType ? MagickTrue : MagickFalse); |
1708 | 6.82k | } |
1709 | | |
1710 | | /* |
1711 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1712 | | % % |
1713 | | % % |
1714 | | % % |
1715 | | % I d e n t i f y I m a g e T y p e % |
1716 | | % % |
1717 | | % % |
1718 | | % % |
1719 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1720 | | % |
1721 | | % IdentifyImageType() returns the potential type of image: |
1722 | | % |
1723 | | % Bilevel Grayscale GrayscaleMatte |
1724 | | % Palette PaletteMatte TrueColor |
1725 | | % TrueColorMatte ColorSeparation ColorSeparationMatte |
1726 | | % |
1727 | | % To ensure the image type matches its potential, use SetImageType(): |
1728 | | % |
1729 | | % (void) SetImageType(image,IdentifyImageType(image,exception),exception); |
1730 | | % |
1731 | | % The format of the IdentifyImageType method is: |
1732 | | % |
1733 | | % ImageType IdentifyImageType(const Image *image,ExceptionInfo *exception) |
1734 | | % |
1735 | | % A description of each parameter follows: |
1736 | | % |
1737 | | % o image: the image. |
1738 | | % |
1739 | | % o exception: return any errors or warnings in this structure. |
1740 | | % |
1741 | | */ |
1742 | | MagickExport ImageType IdentifyImageType(const Image *image, |
1743 | | ExceptionInfo *exception) |
1744 | 5.14k | { |
1745 | 5.14k | ImageType |
1746 | 5.14k | type; |
1747 | | |
1748 | 5.14k | assert(image != (Image *) NULL); |
1749 | 5.14k | assert(image->signature == MagickCoreSignature); |
1750 | 5.14k | if (IsEventLogging() != MagickFalse) |
1751 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
1752 | 5.14k | if (image->colorspace == CMYKColorspace) |
1753 | 0 | { |
1754 | 0 | if ((image->alpha_trait & BlendPixelTrait) == 0) |
1755 | 0 | return(ColorSeparationType); |
1756 | 0 | return(ColorSeparationAlphaType); |
1757 | 0 | } |
1758 | 5.14k | type=IdentifyImageGray(image,exception); |
1759 | 5.14k | if (IsGrayImageType(type)) |
1760 | 4.98k | return(type); |
1761 | 159 | if (IdentifyPaletteImage(image,exception) != MagickFalse) |
1762 | 0 | { |
1763 | 0 | if (image->alpha_trait != UndefinedPixelTrait) |
1764 | 0 | return(PaletteAlphaType); |
1765 | 0 | return(PaletteType); |
1766 | 0 | } |
1767 | 159 | if (image->alpha_trait != UndefinedPixelTrait) |
1768 | 0 | return(TrueColorAlphaType); |
1769 | 159 | return(TrueColorType); |
1770 | 159 | } |
1771 | | |
1772 | | /* |
1773 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1774 | | % % |
1775 | | % % |
1776 | | % % |
1777 | | % I s I m a g e G r a y % |
1778 | | % % |
1779 | | % % |
1780 | | % % |
1781 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1782 | | % |
1783 | | % IsImageGray() returns MagickTrue if the type of the image is grayscale or |
1784 | | % bi-level. |
1785 | | % |
1786 | | % The format of the IsImageGray method is: |
1787 | | % |
1788 | | % MagickBooleanType IsImageGray(const Image *image) |
1789 | | % |
1790 | | % A description of each parameter follows: |
1791 | | % |
1792 | | % o image: the image. |
1793 | | % |
1794 | | */ |
1795 | | MagickExport MagickBooleanType IsImageGray(const Image *image) |
1796 | 34.1k | { |
1797 | 34.1k | assert(image != (Image *) NULL); |
1798 | 34.1k | assert(image->signature == MagickCoreSignature); |
1799 | 34.1k | if (IsGrayImageType(image->type) != MagickFalse) |
1800 | 2.94k | return(MagickTrue); |
1801 | 31.2k | return(MagickFalse); |
1802 | 34.1k | } |
1803 | | |
1804 | | /* |
1805 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1806 | | % % |
1807 | | % % |
1808 | | % % |
1809 | | % I s I m a g e M o n o c h r o m e % |
1810 | | % % |
1811 | | % % |
1812 | | % % |
1813 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1814 | | % |
1815 | | % IsImageMonochrome() returns MagickTrue if type of the image is bi-level. |
1816 | | % |
1817 | | % The format of the IsImageMonochrome method is: |
1818 | | % |
1819 | | % MagickBooleanType IsImageMonochrome(const Image *image) |
1820 | | % |
1821 | | % A description of each parameter follows: |
1822 | | % |
1823 | | % o image: the image. |
1824 | | % |
1825 | | */ |
1826 | | MagickExport MagickBooleanType IsImageMonochrome(const Image *image) |
1827 | 10.7k | { |
1828 | 10.7k | assert(image != (Image *) NULL); |
1829 | 10.7k | assert(image->signature == MagickCoreSignature); |
1830 | 10.7k | if (image->type == BilevelType) |
1831 | 1.34k | return(MagickTrue); |
1832 | 9.41k | return(MagickFalse); |
1833 | 10.7k | } |
1834 | | |
1835 | | /* |
1836 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1837 | | % % |
1838 | | % % |
1839 | | % % |
1840 | | % I s I m a g e O p a q u e % |
1841 | | % % |
1842 | | % % |
1843 | | % % |
1844 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1845 | | % |
1846 | | % IsImageOpaque() returns MagickTrue if none of the pixels in the image have |
1847 | | % an alpha value other than OpaqueAlpha (QuantumRange). |
1848 | | % |
1849 | | % Will return true immediately is alpha channel is not available. |
1850 | | % |
1851 | | % The format of the IsImageOpaque method is: |
1852 | | % |
1853 | | % MagickBooleanType IsImageOpaque(const Image *image, |
1854 | | % ExceptionInfo *exception) |
1855 | | % |
1856 | | % A description of each parameter follows: |
1857 | | % |
1858 | | % o image: the image. |
1859 | | % |
1860 | | % o exception: return any errors or warnings in this structure. |
1861 | | % |
1862 | | */ |
1863 | | MagickExport MagickBooleanType IsImageOpaque(const Image *image, |
1864 | | ExceptionInfo *exception) |
1865 | 4.83k | { |
1866 | 4.83k | CacheView |
1867 | 4.83k | *image_view; |
1868 | | |
1869 | 4.83k | MagickBooleanType |
1870 | 4.83k | opaque = MagickTrue; |
1871 | | |
1872 | 4.83k | ssize_t |
1873 | 4.83k | y; |
1874 | | |
1875 | | /* |
1876 | | Determine if image is opaque. |
1877 | | */ |
1878 | 4.83k | assert(image != (Image *) NULL); |
1879 | 4.83k | assert(image->signature == MagickCoreSignature); |
1880 | 4.83k | if (IsEventLogging() != MagickFalse) |
1881 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
1882 | 4.83k | if ((image->alpha_trait & BlendPixelTrait) == 0) |
1883 | 4.76k | return(MagickTrue); |
1884 | 66 | image_view=AcquireVirtualCacheView(image,exception); |
1885 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
1886 | | #pragma omp parallel for schedule(static) shared(opaque) \ |
1887 | | magick_number_threads(image,image,image->rows,2) |
1888 | | #endif |
1889 | 11.6k | for (y=0; y < (ssize_t) image->rows; y++) |
1890 | 11.5k | { |
1891 | 11.5k | const Quantum |
1892 | 11.5k | *p; |
1893 | | |
1894 | 11.5k | ssize_t |
1895 | 11.5k | x; |
1896 | | |
1897 | 11.5k | if (opaque == MagickFalse) |
1898 | 4.06k | continue; |
1899 | 7.52k | p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); |
1900 | 7.52k | if (p == (const Quantum *) NULL) |
1901 | 0 | { |
1902 | 0 | opaque=MagickFalse; |
1903 | 0 | continue; |
1904 | 0 | } |
1905 | 8.08M | for (x=0; x < (ssize_t) image->columns; x++) |
1906 | 8.08M | { |
1907 | 8.08M | if (GetPixelAlpha(image,p) != OpaqueAlpha) |
1908 | 38 | { |
1909 | 38 | opaque=MagickFalse; |
1910 | 38 | break; |
1911 | 38 | } |
1912 | 8.08M | p+=(ptrdiff_t) GetPixelChannels(image); |
1913 | 8.08M | } |
1914 | 7.52k | } |
1915 | 66 | image_view=DestroyCacheView(image_view); |
1916 | 66 | return(opaque); |
1917 | 4.83k | } |
1918 | | |
1919 | | /* |
1920 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1921 | | % % |
1922 | | % % |
1923 | | % % |
1924 | | % S e t I m a g e D e p t h % |
1925 | | % % |
1926 | | % % |
1927 | | % % |
1928 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1929 | | % |
1930 | | % SetImageDepth() sets the depth of the image. |
1931 | | % |
1932 | | % The format of the SetImageDepth method is: |
1933 | | % |
1934 | | % MagickBooleanType SetImageDepth(Image *image,const size_t depth, |
1935 | | % ExceptionInfo *exception) |
1936 | | % |
1937 | | % A description of each parameter follows: |
1938 | | % |
1939 | | % o image: the image. |
1940 | | % |
1941 | | % o channel: the channel. |
1942 | | % |
1943 | | % o depth: the image depth. |
1944 | | % |
1945 | | % o exception: return any errors or warnings in this structure. |
1946 | | % |
1947 | | */ |
1948 | | |
1949 | | static MagickBooleanType FloydSteinbergImageDepth(Image *image, |
1950 | | const size_t depth,ExceptionInfo *exception) |
1951 | 0 | { |
1952 | 0 | CacheView |
1953 | 0 | *image_view; |
1954 | |
|
1955 | 0 | double |
1956 | 0 | *distortion; |
1957 | |
|
1958 | 0 | MagickBooleanType |
1959 | 0 | status; |
1960 | |
|
1961 | 0 | QuantumAny |
1962 | 0 | range; |
1963 | |
|
1964 | 0 | size_t |
1965 | 0 | channels; |
1966 | |
|
1967 | 0 | ssize_t |
1968 | 0 | y; |
1969 | | |
1970 | | /* |
1971 | | Dither pixels with Floyd Steinberg algorithm. |
1972 | | */ |
1973 | 0 | status=SetImageStorageClass(image,DirectClass,exception); |
1974 | 0 | if (status == MagickFalse) |
1975 | 0 | return(MagickFalse); |
1976 | 0 | channels=GetPixelChannels(image); |
1977 | 0 | distortion=(double *) AcquireQuantumMemory(image->columns,3*channels* |
1978 | 0 | sizeof(*distortion)); |
1979 | 0 | if (distortion == (double *) NULL) |
1980 | 0 | return(MagickFalse); |
1981 | 0 | (void) memset(distortion,0,3*image->columns*channels*sizeof(*distortion)); |
1982 | 0 | range=GetQuantumRange(depth); |
1983 | 0 | image_view=AcquireAuthenticCacheView(image,exception); |
1984 | 0 | for (y=0; y < (ssize_t) image->rows; y++) |
1985 | 0 | { |
1986 | 0 | Quantum |
1987 | 0 | *magick_restrict q; |
1988 | |
|
1989 | 0 | ssize_t |
1990 | 0 | u, |
1991 | 0 | v, |
1992 | 0 | x; |
1993 | |
|
1994 | 0 | if (status == MagickFalse) |
1995 | 0 | continue; |
1996 | 0 | q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); |
1997 | 0 | if (q == (Quantum *) NULL) |
1998 | 0 | { |
1999 | 0 | status=MagickFalse; |
2000 | 0 | continue; |
2001 | 0 | } |
2002 | | /* |
2003 | | Reset pixel distortion for current row. |
2004 | | */ |
2005 | 0 | u=(y % 3)*(ssize_t) (image->columns*channels); |
2006 | 0 | (void) memset(distortion+u,0,image->columns*channels*sizeof(*distortion)); |
2007 | 0 | v=((y+1) % 3)*(ssize_t) (image->columns*channels); |
2008 | 0 | for (x=0; x < (ssize_t) image->columns; x++) |
2009 | 0 | { |
2010 | 0 | ssize_t |
2011 | 0 | i; |
2012 | |
|
2013 | 0 | for (i=0; i < (ssize_t) channels; i++) |
2014 | 0 | { |
2015 | 0 | double |
2016 | 0 | error, |
2017 | 0 | pixel; |
2018 | |
|
2019 | 0 | PixelChannel |
2020 | 0 | channel; |
2021 | |
|
2022 | 0 | PixelTrait |
2023 | 0 | traits; |
2024 | | |
2025 | | /* |
2026 | | Add distortion to current pixel then distribute new distortion. |
2027 | | */ |
2028 | 0 | channel=GetPixelChannelChannel(image,i); |
2029 | 0 | traits=GetPixelChannelTraits(image,channel); |
2030 | 0 | if ((traits & UpdatePixelTrait) == 0) |
2031 | 0 | continue; |
2032 | 0 | pixel=(double) q[i]+distortion[u]; |
2033 | 0 | q[i]=ScaleAnyToQuantum(ScaleQuantumToAny(ClampPixel((MagickRealType) |
2034 | 0 | pixel),range),range); |
2035 | | /* |
2036 | | Distribute distortion for right. |
2037 | | */ |
2038 | 0 | error=pixel-(double) q[i]; |
2039 | 0 | if ((x+1) < (ssize_t) image->columns) |
2040 | 0 | distortion[u+(ssize_t) channels]+=7.0*error/16.0; |
2041 | 0 | if ((y+1) < (ssize_t) image->rows) |
2042 | 0 | { |
2043 | | /* |
2044 | | Distribute distortion for bottom left, bottom, and bottom right. |
2045 | | */ |
2046 | 0 | if (x > 0) |
2047 | 0 | distortion[v-(ssize_t) channels]+=3.0*error/16.0; |
2048 | 0 | distortion[v]+=5.0*error/16.0; |
2049 | 0 | if ((x+1) < (ssize_t) image->columns) |
2050 | 0 | distortion[v+(ssize_t) channels]+=1.0*error/16.0; |
2051 | 0 | } |
2052 | 0 | u++; |
2053 | 0 | v++; |
2054 | 0 | } |
2055 | 0 | q+=(ptrdiff_t) GetPixelChannels(image); |
2056 | 0 | } |
2057 | 0 | if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) |
2058 | 0 | { |
2059 | 0 | status=MagickFalse; |
2060 | 0 | continue; |
2061 | 0 | } |
2062 | 0 | } |
2063 | 0 | image_view=DestroyCacheView(image_view); |
2064 | 0 | distortion=(double *) RelinquishMagickMemory(distortion); |
2065 | 0 | if (status != MagickFalse) |
2066 | 0 | image->depth=depth; |
2067 | 0 | return(status); |
2068 | 0 | } |
2069 | | |
2070 | | MagickExport MagickBooleanType SetImageDepth(Image *image, |
2071 | | const size_t depth,ExceptionInfo *exception) |
2072 | 2.24k | { |
2073 | 2.24k | CacheView |
2074 | 2.24k | *image_view; |
2075 | | |
2076 | 2.24k | const char |
2077 | 2.24k | *artifact; |
2078 | | |
2079 | 2.24k | MagickBooleanType |
2080 | 2.24k | status; |
2081 | | |
2082 | 2.24k | QuantumAny |
2083 | 2.24k | range; |
2084 | | |
2085 | 2.24k | ssize_t |
2086 | 2.24k | y; |
2087 | | |
2088 | 2.24k | assert(image != (Image *) NULL); |
2089 | 2.24k | if (IsEventLogging() != MagickFalse) |
2090 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); |
2091 | 2.24k | assert(image->signature == MagickCoreSignature); |
2092 | 2.24k | if (depth >= MAGICKCORE_QUANTUM_DEPTH) |
2093 | 205 | { |
2094 | 205 | image->depth=depth; |
2095 | 205 | return(MagickTrue); |
2096 | 205 | } |
2097 | 2.04k | artifact=GetImageArtifact(image,"dither"); |
2098 | 2.04k | if ((artifact != (const char *) NULL) && |
2099 | 0 | (LocaleCompare(artifact,"FloydSteinberg") == 0)) |
2100 | 0 | return(FloydSteinbergImageDepth(image,depth,exception)); |
2101 | 2.04k | range=GetQuantumRange(depth); |
2102 | 2.04k | if (image->storage_class == PseudoClass) |
2103 | 1.24k | { |
2104 | 1.24k | ssize_t |
2105 | 1.24k | i; |
2106 | | |
2107 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
2108 | | #pragma omp parallel for schedule(static) shared(status) \ |
2109 | | magick_number_threads(image,image,image->colors,1) |
2110 | | #endif |
2111 | 4.17k | for (i=0; i < (ssize_t) image->colors; i++) |
2112 | 2.93k | { |
2113 | 2.93k | if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) |
2114 | 2.93k | image->colormap[i].red=(double) ScaleAnyToQuantum(ScaleQuantumToAny( |
2115 | 2.93k | ClampPixel(image->colormap[i].red),range),range); |
2116 | 2.93k | if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) |
2117 | 2.93k | image->colormap[i].green=(double) ScaleAnyToQuantum(ScaleQuantumToAny( |
2118 | 2.93k | ClampPixel(image->colormap[i].green),range),range); |
2119 | 2.93k | if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) |
2120 | 2.93k | image->colormap[i].blue=(double) ScaleAnyToQuantum(ScaleQuantumToAny( |
2121 | 2.93k | ClampPixel(image->colormap[i].blue),range),range); |
2122 | 2.93k | if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) |
2123 | 84 | image->colormap[i].alpha=(double) ScaleAnyToQuantum(ScaleQuantumToAny( |
2124 | 84 | ClampPixel(image->colormap[i].alpha),range),range); |
2125 | 2.93k | } |
2126 | 1.24k | } |
2127 | 2.04k | status=MagickTrue; |
2128 | 2.04k | image_view=AcquireAuthenticCacheView(image,exception); |
2129 | | #if !defined(MAGICKCORE_HDRI_SUPPORT) |
2130 | | DisableMSCWarning(4127) |
2131 | | if ((1UL*QuantumRange) <= MaxMap) |
2132 | | RestoreMSCWarning |
2133 | | { |
2134 | | Quantum |
2135 | | *depth_map; |
2136 | | |
2137 | | ssize_t |
2138 | | i; |
2139 | | |
2140 | | /* |
2141 | | Scale pixels to desired (optimized with depth map). |
2142 | | */ |
2143 | | depth_map=(Quantum *) AcquireQuantumMemory(MaxMap+1,sizeof(*depth_map)); |
2144 | | if (depth_map == (Quantum *) NULL) |
2145 | | ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); |
2146 | | for (i=0; i <= (ssize_t) MaxMap; i++) |
2147 | | depth_map[i]=ScaleAnyToQuantum(ScaleQuantumToAny((Quantum) i,range), |
2148 | | range); |
2149 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
2150 | | #pragma omp parallel for schedule(static) shared(status) \ |
2151 | | magick_number_threads(image,image,image->rows,2) |
2152 | | #endif |
2153 | | for (y=0; y < (ssize_t) image->rows; y++) |
2154 | | { |
2155 | | ssize_t |
2156 | | x; |
2157 | | |
2158 | | Quantum |
2159 | | *magick_restrict q; |
2160 | | |
2161 | | if (status == MagickFalse) |
2162 | | continue; |
2163 | | q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1, |
2164 | | exception); |
2165 | | if (q == (Quantum *) NULL) |
2166 | | { |
2167 | | status=MagickFalse; |
2168 | | continue; |
2169 | | } |
2170 | | for (x=0; x < (ssize_t) image->columns; x++) |
2171 | | { |
2172 | | ssize_t |
2173 | | j; |
2174 | | |
2175 | | for (j=0; j < (ssize_t) GetPixelChannels(image); j++) |
2176 | | { |
2177 | | PixelChannel |
2178 | | channel; |
2179 | | |
2180 | | PixelTrait |
2181 | | traits; |
2182 | | |
2183 | | channel=GetPixelChannelChannel(image,j); |
2184 | | traits=GetPixelChannelTraits(image,channel); |
2185 | | if ((traits & UpdatePixelTrait) == 0) |
2186 | | continue; |
2187 | | q[j]=depth_map[ScaleQuantumToMap(q[j])]; |
2188 | | } |
2189 | | q+=(ptrdiff_t) GetPixelChannels(image); |
2190 | | } |
2191 | | if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) |
2192 | | { |
2193 | | status=MagickFalse; |
2194 | | continue; |
2195 | | } |
2196 | | } |
2197 | | image_view=DestroyCacheView(image_view); |
2198 | | depth_map=(Quantum *) RelinquishMagickMemory(depth_map); |
2199 | | if (status != MagickFalse) |
2200 | | image->depth=depth; |
2201 | | return(status); |
2202 | | } |
2203 | | #endif |
2204 | | /* |
2205 | | Scale pixels to desired depth. |
2206 | | */ |
2207 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
2208 | | #pragma omp parallel for schedule(static) shared(status) \ |
2209 | | magick_number_threads(image,image,image->rows,2) |
2210 | | #endif |
2211 | 353k | for (y=0; y < (ssize_t) image->rows; y++) |
2212 | 351k | { |
2213 | 351k | ssize_t |
2214 | 351k | x; |
2215 | | |
2216 | 351k | Quantum |
2217 | 351k | *magick_restrict q; |
2218 | | |
2219 | 351k | if (status == MagickFalse) |
2220 | 0 | continue; |
2221 | 351k | q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); |
2222 | 351k | if (q == (Quantum *) NULL) |
2223 | 0 | { |
2224 | 0 | status=MagickFalse; |
2225 | 0 | continue; |
2226 | 0 | } |
2227 | 487M | for (x=0; x < (ssize_t) image->columns; x++) |
2228 | 487M | { |
2229 | 487M | ssize_t |
2230 | 487M | i; |
2231 | | |
2232 | 1.77G | for (i=0; i < (ssize_t) GetPixelChannels(image); i++) |
2233 | 1.29G | { |
2234 | 1.29G | PixelChannel |
2235 | 1.29G | channel; |
2236 | | |
2237 | 1.29G | PixelTrait |
2238 | 1.29G | traits; |
2239 | | |
2240 | 1.29G | channel=GetPixelChannelChannel(image,i); |
2241 | 1.29G | traits=GetPixelChannelTraits(image,channel); |
2242 | 1.29G | if ((traits & UpdatePixelTrait) == 0) |
2243 | 170M | continue; |
2244 | 1.12G | q[i]=ScaleAnyToQuantum(ScaleQuantumToAny(ClampPixel((MagickRealType) |
2245 | 1.12G | q[i]),range),range); |
2246 | 1.12G | } |
2247 | 487M | q+=(ptrdiff_t) GetPixelChannels(image); |
2248 | 487M | } |
2249 | 351k | if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) |
2250 | 0 | { |
2251 | 0 | status=MagickFalse; |
2252 | 0 | continue; |
2253 | 0 | } |
2254 | 351k | } |
2255 | 2.04k | image_view=DestroyCacheView(image_view); |
2256 | 2.04k | if (status != MagickFalse) |
2257 | 2.04k | image->depth=depth; |
2258 | 2.04k | return(status); |
2259 | 2.04k | } |
2260 | | |
2261 | | /* |
2262 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2263 | | % % |
2264 | | % % |
2265 | | % % |
2266 | | % S e t I m a g e T y p e % |
2267 | | % % |
2268 | | % % |
2269 | | % % |
2270 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2271 | | % |
2272 | | % SetImageType() sets the type of image. Choose from these types: |
2273 | | % |
2274 | | % Bilevel Grayscale GrayscaleMatte |
2275 | | % Palette PaletteMatte TrueColor |
2276 | | % TrueColorMatte ColorSeparation ColorSeparationMatte |
2277 | | % OptimizeType |
2278 | | % |
2279 | | % The format of the SetImageType method is: |
2280 | | % |
2281 | | % MagickBooleanType SetImageType(Image *image,const ImageType type, |
2282 | | % ExceptionInfo *exception) |
2283 | | % |
2284 | | % A description of each parameter follows: |
2285 | | % |
2286 | | % o image: the image. |
2287 | | % |
2288 | | % o type: Image type. |
2289 | | % |
2290 | | % o exception: return any errors or warnings in this structure. |
2291 | | % |
2292 | | */ |
2293 | | MagickExport MagickBooleanType SetImageType(Image *image,const ImageType type, |
2294 | | ExceptionInfo *exception) |
2295 | 11.1k | { |
2296 | 11.1k | const char |
2297 | 11.1k | *artifact; |
2298 | | |
2299 | 11.1k | ImageInfo |
2300 | 11.1k | *image_info; |
2301 | | |
2302 | 11.1k | MagickBooleanType |
2303 | 11.1k | status; |
2304 | | |
2305 | 11.1k | QuantizeInfo |
2306 | 11.1k | *quantize_info; |
2307 | | |
2308 | 11.1k | assert(image != (Image *) NULL); |
2309 | 11.1k | if (IsEventLogging() != MagickFalse) |
2310 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); |
2311 | 11.1k | assert(image->signature == MagickCoreSignature); |
2312 | 11.1k | status=MagickTrue; |
2313 | 11.1k | image_info=AcquireImageInfo(); |
2314 | 11.1k | image_info->dither=image->dither; |
2315 | 11.1k | artifact=GetImageArtifact(image,"dither"); |
2316 | 11.1k | if (artifact != (const char *) NULL) |
2317 | 0 | (void) SetImageOption(image_info,"dither",artifact); |
2318 | 11.1k | switch (type) |
2319 | 11.1k | { |
2320 | 4.49k | case BilevelType: |
2321 | 4.49k | { |
2322 | 4.49k | if (IsGrayImageType(image->type) == MagickFalse) |
2323 | 4.19k | status=TransformImageColorspace(image,GRAYColorspace,exception); |
2324 | 4.49k | (void) NormalizeImage(image,exception); |
2325 | 4.49k | (void) BilevelImage(image,(double) QuantumRange/2.0,exception); |
2326 | 4.49k | quantize_info=AcquireQuantizeInfo(image_info); |
2327 | 4.49k | quantize_info->number_colors=2; |
2328 | 4.49k | quantize_info->colorspace=GRAYColorspace; |
2329 | 4.49k | status=QuantizeImage(quantize_info,image,exception); |
2330 | 4.49k | quantize_info=DestroyQuantizeInfo(quantize_info); |
2331 | 4.49k | image->alpha_trait=UndefinedPixelTrait; |
2332 | 4.49k | break; |
2333 | 0 | } |
2334 | 3.32k | case GrayscaleType: |
2335 | 3.32k | { |
2336 | 3.32k | if (IsGrayImageType(image->type) == MagickFalse) |
2337 | 3.32k | status=TransformImageColorspace(image,GRAYColorspace,exception); |
2338 | 3.32k | image->alpha_trait=UndefinedPixelTrait; |
2339 | 3.32k | break; |
2340 | 0 | } |
2341 | 369 | case GrayscaleAlphaType: |
2342 | 369 | { |
2343 | 369 | if (IsGrayImageType(image->type) == MagickFalse) |
2344 | 369 | status=TransformImageColorspace(image,GRAYColorspace,exception); |
2345 | 369 | if ((image->alpha_trait & BlendPixelTrait) == 0) |
2346 | 0 | (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception); |
2347 | 369 | break; |
2348 | 0 | } |
2349 | 1.18k | case PaletteType: |
2350 | 1.18k | { |
2351 | 1.18k | if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) |
2352 | 0 | status=TransformImageColorspace(image,sRGBColorspace,exception); |
2353 | 1.18k | if ((image->storage_class == DirectClass) || (image->colors > 256)) |
2354 | 866 | { |
2355 | 866 | quantize_info=AcquireQuantizeInfo(image_info); |
2356 | 866 | quantize_info->number_colors=256; |
2357 | 866 | status=QuantizeImage(quantize_info,image,exception); |
2358 | 866 | quantize_info=DestroyQuantizeInfo(quantize_info); |
2359 | 866 | } |
2360 | 1.18k | image->alpha_trait=UndefinedPixelTrait; |
2361 | 1.18k | break; |
2362 | 0 | } |
2363 | 720 | case PaletteBilevelAlphaType: |
2364 | 720 | { |
2365 | 720 | ChannelType |
2366 | 720 | channel_mask; |
2367 | | |
2368 | 720 | if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) |
2369 | 0 | status=TransformImageColorspace(image,sRGBColorspace,exception); |
2370 | 720 | if ((image->alpha_trait & BlendPixelTrait) == 0) |
2371 | 0 | (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception); |
2372 | 720 | channel_mask=SetImageChannelMask(image,AlphaChannel); |
2373 | 720 | (void) BilevelImage(image,(double) QuantumRange/2.0,exception); |
2374 | 720 | (void) SetImageChannelMask(image,channel_mask); |
2375 | 720 | quantize_info=AcquireQuantizeInfo(image_info); |
2376 | 720 | status=QuantizeImage(quantize_info,image,exception); |
2377 | 720 | quantize_info=DestroyQuantizeInfo(quantize_info); |
2378 | 720 | break; |
2379 | 0 | } |
2380 | 61 | case PaletteAlphaType: |
2381 | 61 | { |
2382 | 61 | if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) |
2383 | 0 | status=TransformImageColorspace(image,sRGBColorspace,exception); |
2384 | 61 | if ((image->alpha_trait & BlendPixelTrait) == 0) |
2385 | 0 | (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception); |
2386 | 61 | quantize_info=AcquireQuantizeInfo(image_info); |
2387 | 61 | quantize_info->colorspace=TransparentColorspace; |
2388 | 61 | status=QuantizeImage(quantize_info,image,exception); |
2389 | 61 | quantize_info=DestroyQuantizeInfo(quantize_info); |
2390 | 61 | break; |
2391 | 0 | } |
2392 | 804 | case TrueColorType: |
2393 | 804 | { |
2394 | 804 | if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) |
2395 | 0 | status=TransformImageColorspace(image,sRGBColorspace,exception); |
2396 | 804 | if (image->storage_class != DirectClass) |
2397 | 576 | status=SetImageStorageClass(image,DirectClass,exception); |
2398 | 804 | image->alpha_trait=UndefinedPixelTrait; |
2399 | 804 | break; |
2400 | 0 | } |
2401 | 196 | case TrueColorAlphaType: |
2402 | 196 | { |
2403 | 196 | if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) |
2404 | 0 | status=TransformImageColorspace(image,sRGBColorspace,exception); |
2405 | 196 | if (image->storage_class != DirectClass) |
2406 | 54 | status=SetImageStorageClass(image,DirectClass,exception); |
2407 | 196 | if ((image->alpha_trait & BlendPixelTrait) == 0) |
2408 | 0 | (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception); |
2409 | 196 | break; |
2410 | 0 | } |
2411 | 0 | case ColorSeparationType: |
2412 | 0 | { |
2413 | 0 | if (image->colorspace != CMYKColorspace) |
2414 | 0 | status=TransformImageColorspace(image,CMYKColorspace,exception); |
2415 | 0 | if (image->storage_class != DirectClass) |
2416 | 0 | status=SetImageStorageClass(image,DirectClass,exception); |
2417 | 0 | image->alpha_trait=UndefinedPixelTrait; |
2418 | 0 | break; |
2419 | 0 | } |
2420 | 0 | case ColorSeparationAlphaType: |
2421 | 0 | { |
2422 | 0 | if (image->colorspace != CMYKColorspace) |
2423 | 0 | status=TransformImageColorspace(image,CMYKColorspace,exception); |
2424 | 0 | if (image->storage_class != DirectClass) |
2425 | 0 | status=SetImageStorageClass(image,DirectClass,exception); |
2426 | 0 | if ((image->alpha_trait & BlendPixelTrait) == 0) |
2427 | 0 | status=SetImageAlphaChannel(image,OpaqueAlphaChannel,exception); |
2428 | 0 | break; |
2429 | 0 | } |
2430 | 0 | case OptimizeType: |
2431 | 0 | case UndefinedType: |
2432 | 0 | break; |
2433 | 11.1k | } |
2434 | 11.1k | image_info=DestroyImageInfo(image_info); |
2435 | 11.1k | if (status == MagickFalse) |
2436 | 5 | return(status); |
2437 | 11.1k | image->type=type; |
2438 | 11.1k | return(MagickTrue); |
2439 | 11.1k | } |