1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                         CCCC  U   U  BBBB   EEEEE                           %
7 %                        C      U   U  B   B  E                               %
8 %                        C      U   U  BBBB   EEE                             %
9 %                        C      U   U  B   B  E                               %
10 %                         CCCC   UUU   BBBB   EEEEE                           %
11 %                                                                             %
12 %                                                                             %
13 %                           Cube LUT Image Format                             %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                   Cristy                                    %
17 %                                 July 2018                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2019 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/script/license.php                               %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 % See Cube LUT specification 1.0 @
37 % https://wwwimages2.adobe.com/content/dam/acom/en/products/speedgrade/cc/pdfs/cube-lut-specification-1.0.pdf
38 %
39 */
40 
41 /*
42   Include declarations.
43 */
44 #include "MagickCore/studio.h"
45 #include "MagickCore/blob.h"
46 #include "MagickCore/blob-private.h"
47 #include "MagickCore/cache.h"
48 #include "MagickCore/colorspace.h"
49 #include "MagickCore/exception.h"
50 #include "MagickCore/exception-private.h"
51 #include "MagickCore/image.h"
52 #include "MagickCore/image-private.h"
53 #include "MagickCore/list.h"
54 #include "MagickCore/magick.h"
55 #include "MagickCore/memory_.h"
56 #include "MagickCore/module.h"
57 #include "MagickCore/monitor.h"
58 #include "MagickCore/monitor-private.h"
59 #include "MagickCore/pixel-accessor.h"
60 #include "MagickCore/property.h"
61 #include "MagickCore/quantum-private.h"
62 #include "MagickCore/resource_.h"
63 #include "MagickCore/static.h"
64 #include "MagickCore/string_.h"
65 #include "MagickCore/string-private.h"
66 #include "MagickCore/thread-private.h"
67 #include "MagickCore/token.h"
68 
69 /*
70 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
71 %                                                                             %
72 %                                                                             %
73 %                                                                             %
74 %   R e a d C U B E I m a g e                                                 %
75 %                                                                             %
76 %                                                                             %
77 %                                                                             %
78 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
79 %
80 %  ReadCUBEImage() creates a Cube color lookup table image and returns it.  It
81 %  allocates the memory necessary for the new Image structure and returns a
82 %  pointer to the new image.
83 %
84 %  The format of the ReadCUBEImage method is:
85 %
86 %      Image *ReadCUBEImage(const ImageInfo *image_info,
87 %        ExceptionInfo *exception)
88 %
89 %  A description of each parameter follows:
90 %
91 %    o image_info: the image info.
92 %
93 %    o exception: return any errors or warnings in this structure.
94 %
95 */
ReadCUBEImage(const ImageInfo * image_info,ExceptionInfo * exception)96 static Image *ReadCUBEImage(const ImageInfo *image_info,
97   ExceptionInfo *exception)
98 {
99 #define FlattenCube(level,b,g,r)  ((ssize_t) ((b)*(level)*(level)+(g)*(level)+(r)))
100 
101   typedef struct _CubePixel
102   {
103     float
104       r,
105       g,
106       b;
107   } CubePixel;
108 
109   char
110     *buffer,
111     token[MagickPathExtent],
112     value[MagickPathExtent];
113 
114   CubePixel
115     *cube;
116 
117   Image
118     *image;
119 
120   MagickBooleanType
121     status;
122 
123   MemoryInfo
124     *cube_info;
125 
126   register char
127     *p;
128 
129   size_t
130     cube_level,
131     hald_level;
132 
133   ssize_t
134     b,
135     i,
136     n;
137 
138   /*
139     Read CUBE color lookup table.
140   */
141   assert(image_info != (const ImageInfo *) NULL);
142   assert(image_info->signature == MagickCoreSignature);
143   if (image_info->debug != MagickFalse)
144     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
145       image_info->filename);
146   assert(exception != (ExceptionInfo *) NULL);
147   assert(exception->signature == MagickCoreSignature);
148   image=AcquireImage(image_info,exception);
149   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
150   if (status == MagickFalse)
151     {
152       image=DestroyImageList(image);
153       return((Image *) NULL);
154     }
155   cube_level=0;
156   cube_info=(MemoryInfo *) NULL;
157   cube=(CubePixel *) NULL;
158   n=0;
159   buffer=AcquireString("");
160   *buffer='\0';
161   p=buffer;
162   while (ReadBlobString(image,p) != (char *) NULL)
163   {
164     const char
165       *q;
166 
167     q=p;
168     GetNextToken(q,&q,MagickPathExtent,token);
169     if ((*token == '#') || (*token == '\0'))
170       continue;
171     if ((LocaleCompare(token,"LUT_1D_SIZE") == 0) ||
172         (LocaleCompare(token,"LUT_3D_SIZE") == 0))
173       {
174         if (cube_info != (MemoryInfo *) NULL)
175           cube_info=RelinquishVirtualMemory(cube_info);
176         GetNextToken(q,&q,MagickPathExtent,value);
177         cube_level=(size_t) StringToLong(value);
178         if (LocaleCompare(token,"LUT_1D_SIZE") == 0)
179           cube_level=(size_t) ceil(pow((double) cube_level,1.0/3.0));
180         if ((cube_level < 2) || (cube_level > 256))
181           {
182             buffer=DestroyString(buffer);
183             ThrowReaderException(CorruptImageError,"ImproperImageHeader");
184           }
185         cube_info=AcquireVirtualMemory(cube_level*cube_level,cube_level*
186           sizeof(*cube));
187         if (cube_info == (MemoryInfo *) NULL)
188           {
189             buffer=DestroyString(buffer);
190             ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
191           }
192         cube=(CubePixel *) GetVirtualMemoryBlob(cube_info);
193         (void) memset(cube,0,cube_level*cube_level*cube_level*sizeof(*cube));
194       }
195     else
196       if (LocaleCompare(token,"TITLE ") == 0)
197         {
198           GetNextToken(q,&q,MagickPathExtent,value);
199           (void) SetImageProperty(image,"title",value,exception);
200         }
201       else
202         if (cube_level != 0)
203           {
204             char
205               *q;
206 
207             if (n >= (ssize_t) (cube_level*cube_level*cube_level))
208               break;
209             q=buffer;
210             cube[n].r=StringToDouble(q,&q);
211             cube[n].g=StringToDouble(q,&q);
212             cube[n].b=StringToDouble(q,&q);
213             n++;
214           }
215         else
216           if (('+' < *buffer) && (*buffer < ':'))
217             break;
218   }
219   buffer=DestroyString(buffer);
220   if (cube_level == 0)
221     {
222       if (cube_info != (MemoryInfo *) NULL)
223         cube_info=RelinquishVirtualMemory(cube_info);
224       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
225     }
226   /*
227     Write HALD image.
228   */
229   status=MagickTrue;
230   hald_level=image_info->scene;
231   if ((hald_level < 2) || (hald_level > 256))
232     hald_level=8;
233   image->columns=(size_t) (hald_level*hald_level*hald_level);
234   image->rows=(size_t) (hald_level*hald_level*hald_level);
235   status=SetImageExtent(image,image->columns,image->rows,exception);
236   if (status == MagickFalse)
237     {
238       cube_info=RelinquishVirtualMemory(cube_info);
239       return(DestroyImageList(image));
240     }
241   for (b=0; b < (ssize_t) (hald_level*hald_level); b++)
242   {
243     register ssize_t
244       g;
245 
246     if (status == MagickFalse)
247       continue;
248     for (g=0; g < (ssize_t) (hald_level*hald_level); g++)
249     {
250       register Quantum
251         *magick_restrict q;
252 
253       register ssize_t
254         r;
255 
256       if (status == MagickFalse)
257         continue;
258       q=QueueAuthenticPixels(image,(g % hald_level)*(hald_level*hald_level),
259         (b*hald_level)+((g/hald_level) % (hald_level*hald_level)),hald_level*
260         hald_level,1,exception);
261       if (q == (Quantum *) NULL)
262         {
263           status=MagickFalse;
264           continue;
265         }
266       for (r=0; r < (ssize_t) (hald_level*hald_level); r++)
267       {
268         CubePixel
269           index,
270           next,
271           offset,
272           scale;
273 
274         offset.r=(PerceptibleReciprocal((double) (hald_level*hald_level)-1.0)*
275           r)*(cube_level-1.0);
276         index.r=floor(offset.r);
277         scale.r=offset.r-index.r;
278         next.r=index.r+1;
279         if ((size_t) index.r == (cube_level-1))
280           next.r=index.r;
281         offset.g=(PerceptibleReciprocal(((double) hald_level*hald_level)-1.0)*
282           g)*(cube_level-1.0);
283         index.g=floor(offset.g);
284         scale.g=offset.g-index.g;
285         next.g=index.g+1;
286         if ((size_t) index.g == (cube_level-1))
287           next.g=index.g;
288         offset.b=(PerceptibleReciprocal(((double) hald_level*hald_level)-1.0)*
289           b)*(cube_level-1.0);
290         index.b=floor(offset.b);
291         scale.b=offset.b-index.b;
292         next.b=index.b+1;
293         if ((size_t) index.b == (cube_level-1))
294           next.b=index.b;
295         SetPixelRed(image,ClampToQuantum(QuantumRange*(
296           cube[FlattenCube(cube_level,index.b,index.g,index.r)].r+scale.r*(
297           cube[FlattenCube(cube_level,index.b,index.g,next.r)].r-
298           cube[FlattenCube(cube_level,index.b,index.g,index.r)].r))),q);
299         SetPixelGreen(image,ClampToQuantum(QuantumRange*(
300           cube[FlattenCube(cube_level,index.b,index.g,index.r)].g+scale.g*(
301           cube[FlattenCube(cube_level,index.b,next.g,index.r)].g-
302           cube[FlattenCube(cube_level,index.b,index.g,index.r)].g))),q);
303         SetPixelBlue(image,ClampToQuantum(QuantumRange*(
304           cube[FlattenCube(cube_level,index.b,index.g,index.r)].b+scale.b*(
305           cube[FlattenCube(cube_level,next.b,index.g,index.r)].b-
306           cube[FlattenCube(cube_level,index.b,index.g,index.r)].b))),q);
307         q+=GetPixelChannels(image);
308       }
309       if (SyncAuthenticPixels(image,exception) == MagickFalse)
310         status=MagickFalse;
311     }
312   }
313   cube_info=RelinquishVirtualMemory(cube_info);
314   (void) CloseBlob(image);
315   if (status == MagickFalse)
316     return(DestroyImageList(image));
317   if (image_info->scene != 0)
318     for (i=0; i < (ssize_t) image_info->scene; i++)
319       AppendImageToList(&image,CloneImage(image,0,0,MagickTrue,exception));
320   return(GetFirstImageInList(image));
321 }
322 
323 /*
324 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
325 %                                                                             %
326 %                                                                             %
327 %                                                                             %
328 %   R e g i s t e r H A L D I m a g e                                         %
329 %                                                                             %
330 %                                                                             %
331 %                                                                             %
332 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
333 %
334 %  RegisterCUBEImage() adds attributes for the Hald color lookup table image
335 %  format to the list of supported formats.  The attributes include the image
336 %  format tag, a method to read and/or write the format, whether the format
337 %  supports the saving of more than one frame to the same file or blob, whether
338 %  the format supports native in-memory I/O, and a brief description of the
339 %  format.
340 %
341 %  The format of the RegisterCUBEImage method is:
342 %
343 %      size_t RegisterCUBEImage(void)
344 %
345 */
RegisterCUBEImage(void)346 ModuleExport size_t RegisterCUBEImage(void)
347 {
348   MagickInfo
349     *entry;
350 
351   entry=AcquireMagickInfo("CUBE","CUBE","Cube LUT");
352   entry->decoder=(DecodeImageHandler *) ReadCUBEImage;
353   entry->flags^=CoderAdjoinFlag;
354   entry->format_type=ImplicitFormatType;
355   entry->flags|=CoderRawSupportFlag;
356   entry->flags|=CoderEndianSupportFlag;
357   (void) RegisterMagickInfo(entry);
358   return(MagickImageCoderSignature);
359 }
360 
361 /*
362 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
363 %                                                                             %
364 %                                                                             %
365 %                                                                             %
366 %   U n r e g i s t e r H A L D I m a g e                                     %
367 %                                                                             %
368 %                                                                             %
369 %                                                                             %
370 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
371 %
372 %  UnregisterCUBEImage() removes format registrations made by the
373 %  CUBE module from the list of supported formats.
374 %
375 %  The format of the UnregisterCUBEImage method is:
376 %
377 %      UnregisterCUBEImage(void)
378 %
379 */
UnregisterCUBEImage(void)380 ModuleExport void UnregisterCUBEImage(void)
381 {
382   (void) UnregisterMagickInfo("CUBE");
383 }
384