1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                              JJJ  N   N  X   X                              %
7 %                               J   NN  N   X X                               %
8 %                               J   N N N    X                                %
9 %                            J  J   N  NN   X X                               %
10 %                             JJ    N   N  X   X                              %
11 %                                                                             %
12 %                                                                             %
13 %                       Read/Write Garmin Image Format                        %
14 %                                                                             %
15 %                                   Cristy                                    %
16 %                                 July 2012                                   %
17 %                                                                             %
18 %                                                                             %
19 %  Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization      %
20 %  dedicated to making software imaging solutions freely available.           %
21 %                                                                             %
22 %  You may not use this file except in compliance with the License.  You may  %
23 %  obtain a copy of the License at                                            %
24 %                                                                             %
25 %    https://imagemagick.org/script/license.php                               %
26 %                                                                             %
27 %  Unless required by applicable law or agreed to in writing, software        %
28 %  distributed under the License is distributed on an "AS IS" BASIS,          %
29 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
30 %  See the License for the specific language governing permissions and        %
31 %  limitations under the License.                                             %
32 %                                                                             %
33 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
34 %
35 %
36 */
37 
38 /*
39   Include declarations.
40 */
41 #include "MagickCore/studio.h"
42 #include "MagickCore/blob.h"
43 #include "MagickCore/blob-private.h"
44 #include "MagickCore/cache.h"
45 #include "MagickCore/colorspace.h"
46 #include "MagickCore/colorspace-private.h"
47 #include "MagickCore/draw.h"
48 #include "MagickCore/exception.h"
49 #include "MagickCore/exception-private.h"
50 #include "MagickCore/image.h"
51 #include "MagickCore/image-private.h"
52 #include "MagickCore/list.h"
53 #include "MagickCore/magick.h"
54 #include "MagickCore/memory_.h"
55 #include "MagickCore/module.h"
56 #include "MagickCore/monitor.h"
57 #include "MagickCore/monitor-private.h"
58 #include "MagickCore/property.h"
59 #include "MagickCore/pixel-accessor.h"
60 #include "MagickCore/quantum-private.h"
61 #include "MagickCore/static.h"
62 #include "MagickCore/string_.h"
63 
64 typedef struct _JNXInfo
65 {
66   int
67     version,
68     serial;
69 
70   PointInfo
71     northeast,
72     southwest;
73 
74   int
75     levels,
76     expire,
77     id,
78     crc,
79     signature;
80 
81   unsigned int
82     offset;
83 
84   int
85     order;
86 } JNXInfo;
87 
88 typedef struct _JNXLevelInfo
89 {
90   int
91     count,
92     offset;
93 
94   unsigned int
95     scale;
96 
97   unsigned short
98     copyright[MagickPathExtent];
99 } JNXLevelInfo;
100 
101 /*
102 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
103 %                                                                             %
104 %                                                                             %
105 %                                                                             %
106 %   R e a d J N X I m a g e                                                   %
107 %                                                                             %
108 %                                                                             %
109 %                                                                             %
110 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
111 %
112 %  ReadJNXImage() reads an image in the Garmin tile storage format and returns
113 %  it.  It allocates the memory necessary for the new Image structure and
114 %  returns a pointer to the new image.
115 %
116 %  The format of the ReadJNXImage method is:
117 %
118 %      Image *ReadJNXImage(const ImageInfo *image_info,ExceptionInfo *exception)
119 %
120 %  A description of each parameter follows:
121 %
122 %    o image_info: the image info.
123 %
124 %    o exception: return any errors or warnings in this structure.
125 %
126 */
ReadJNXImage(const ImageInfo * image_info,ExceptionInfo * exception)127 static Image *ReadJNXImage(const ImageInfo *image_info,ExceptionInfo *exception)
128 {
129 #define JNXMaxLevels  20
130 
131   Image
132     *image,
133     *images;
134 
135   JNXInfo
136     jnx_info;
137 
138   JNXLevelInfo
139     jnx_level_info[JNXMaxLevels];
140 
141   MagickBooleanType
142     status;
143 
144   ssize_t
145     i;
146 
147   /*
148     Open image file.
149   */
150   assert(image_info != (const ImageInfo *) NULL);
151   assert(image_info->signature == MagickCoreSignature);
152   if (image_info->debug != MagickFalse)
153     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
154       image_info->filename);
155   assert(exception != (ExceptionInfo *) NULL);
156   assert(exception->signature == MagickCoreSignature);
157   image=AcquireImage(image_info,exception);
158   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
159   if (status == MagickFalse)
160     {
161       image=DestroyImageList(image);
162       return((Image *) NULL);
163     }
164   /*
165     Read JNX header.
166   */
167   (void) memset(&jnx_info,0,sizeof(jnx_info));
168   jnx_info.version=ReadBlobLSBSignedLong(image);
169   if ((jnx_info.version != 3) && (jnx_info.version != 4))
170     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
171   jnx_info.serial=ReadBlobLSBSignedLong(image);
172   jnx_info.northeast.x=180.0*ReadBlobLSBSignedLong(image)/0x7fffffff;
173   jnx_info.northeast.y=180.0*ReadBlobLSBSignedLong(image)/0x7fffffff;
174   jnx_info.southwest.x=180.0*ReadBlobLSBSignedLong(image)/0x7fffffff;
175   jnx_info.southwest.y=180.0*ReadBlobLSBSignedLong(image)/0x7fffffff;
176   jnx_info.levels=ReadBlobLSBSignedLong(image);
177   if (jnx_info.levels > JNXMaxLevels)
178     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
179   jnx_info.expire=ReadBlobLSBSignedLong(image);
180   jnx_info.id=ReadBlobLSBSignedLong(image);
181   jnx_info.crc=ReadBlobLSBSignedLong(image);
182   jnx_info.signature=ReadBlobLSBSignedLong(image);
183   jnx_info.offset=ReadBlobLSBLong(image);
184   if (jnx_info.version > 3)
185     jnx_info.order=ReadBlobLSBSignedLong(image);
186   else
187     if (jnx_info.version == 3)
188       jnx_info.order=30;
189   if (EOFBlob(image) != MagickFalse)
190     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
191   /*
192     Read JNX levels.
193   */
194   (void) memset(&jnx_level_info,0,sizeof(jnx_level_info));
195   for (i=0; i < (ssize_t) jnx_info.levels; i++)
196   {
197     jnx_level_info[i].count=ReadBlobLSBSignedLong(image);
198     if (jnx_level_info[i].count > 50000)
199       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
200     jnx_level_info[i].offset=ReadBlobLSBSignedLong(image);
201     jnx_level_info[i].scale=ReadBlobLSBLong(image);
202     *jnx_level_info[i].copyright='\0';
203     if (jnx_info.version > 3)
204       {
205         ssize_t
206           j;
207 
208         unsigned short
209           c;
210 
211         (void) ReadBlobLSBLong(image);
212         j=0;
213         while ((c=ReadBlobLSBShort(image)) != 0)
214           if (j < (MagickPathExtent-1))
215             jnx_level_info[i].copyright[j++]=c;
216         jnx_level_info[i].copyright[j]='\0';
217       }
218     if (EOFBlob(image) != MagickFalse)
219       ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
220   }
221   /*
222     Read JNX tiles.
223   */
224   images=NewImageList();
225   for (i=0; i < (ssize_t) jnx_info.levels; i++)
226   {
227     MagickOffsetType
228       offset;
229 
230     ssize_t
231       j;
232 
233     offset=SeekBlob(image,(MagickOffsetType) jnx_level_info[i].offset,SEEK_SET);
234     if (offset != (MagickOffsetType) jnx_level_info[i].offset)
235       continue;
236     for (j=0; j < (ssize_t) jnx_level_info[i].count; j++)
237     {
238       Image
239         *tile_image;
240 
241       ImageInfo
242         *read_info;
243 
244       int
245         tile_offset;
246 
247       MagickOffsetType
248         restore_offset;
249 
250       PointInfo
251         northeast,
252         southwest;
253 
254       ssize_t
255         count;
256 
257       unsigned char
258         *blob;
259 
260       unsigned int
261         tile_length;
262 
263       northeast.x=180.0*ReadBlobLSBSignedLong(image)/0x7fffffff;
264       northeast.y=180.0*ReadBlobLSBSignedLong(image)/0x7fffffff;
265       southwest.x=180.0*ReadBlobLSBSignedLong(image)/0x7fffffff;
266       southwest.y=180.0*ReadBlobLSBSignedLong(image)/0x7fffffff;
267       (void) ReadBlobLSBShort(image); /* width */
268       (void) ReadBlobLSBShort(image); /* height */
269       if (EOFBlob(image) != MagickFalse)
270         {
271           images=DestroyImageList(images);
272           ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
273         }
274       tile_length=ReadBlobLSBLong(image);
275       tile_offset=ReadBlobLSBSignedLong(image);
276       if (tile_offset == -1)
277         continue;
278       restore_offset=TellBlob(image);
279       if (restore_offset < 0)
280         continue;
281       offset=SeekBlob(image,(MagickOffsetType) tile_offset,SEEK_SET);
282       if (offset != (MagickOffsetType) tile_offset)
283         continue;
284       /*
285         Read a tile.
286       */
287       if (((MagickSizeType) tile_length) > GetBlobSize(image))
288         {
289           images=DestroyImageList(images);
290           ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
291         }
292       blob=(unsigned char *) AcquireQuantumMemory((size_t) tile_length+2,
293         sizeof(*blob));
294       if (blob == (unsigned char *) NULL)
295         {
296           images=DestroyImageList(images);
297           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
298         }
299       blob[0]=0xFF;
300       blob[1]=0xD8;
301       count=ReadBlob(image,tile_length,blob+2);
302       if (count != (ssize_t) tile_length)
303         {
304           images=DestroyImageList(images);
305           blob=(unsigned char *) RelinquishMagickMemory(blob);
306           ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
307         }
308       read_info=CloneImageInfo(image_info);
309       (void) CopyMagickString(read_info->magick,"JPEG",MagickPathExtent);
310       tile_image=BlobToImage(read_info,blob,tile_length+2,exception);
311       read_info=DestroyImageInfo(read_info);
312       blob=(unsigned char *) RelinquishMagickMemory(blob);
313       offset=SeekBlob(image,restore_offset,SEEK_SET);
314       if (tile_image == (Image *) NULL)
315         continue;
316       tile_image->depth=8;
317       (void) CopyMagickString(tile_image->magick,image->magick,
318         MagickPathExtent);
319       (void) FormatImageProperty(tile_image,"jnx:northeast","%.20g,%.20g",
320         northeast.x,northeast.y);
321       (void) FormatImageProperty(tile_image,"jnx:southwest","%.20g,%.20g",
322         southwest.x,southwest.y);
323       AppendImageToList(&images,tile_image);
324     }
325     if (image->progress_monitor != (MagickProgressMonitor) NULL)
326       {
327         MagickBooleanType
328           proceed;
329 
330         proceed=SetImageProgress(image,LoadImageTag,(MagickOffsetType) i,
331           (MagickSizeType) jnx_info.levels);
332         if (proceed == MagickFalse)
333           status=MagickFalse;
334       }
335   }
336   (void) CloseBlob(image);
337   image=DestroyImage(image);
338   return(GetFirstImageInList(images));
339 }
340 
341 /*
342 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
343 %                                                                             %
344 %                                                                             %
345 %                                                                             %
346 %   R e g i s t e r J N X I m a g e                                           %
347 %                                                                             %
348 %                                                                             %
349 %                                                                             %
350 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
351 %
352 %  RegisterJNXImage() adds attributes for the JNX image format to the list
353 %  of supported formats.  The attributes include the image format tag, a
354 %  method to read and/or write the format, whether the format supports the
355 %  saving of more than one frame to the same file or blob, whether the format
356 %  supports native in-memory I/O, and a brief description of the format.
357 %
358 %  The format of the RegisterJNXImage method is:
359 %
360 %      size_t RegisterJNXImage(void)
361 %
362 */
RegisterJNXImage(void)363 ModuleExport size_t RegisterJNXImage(void)
364 {
365   MagickInfo
366     *entry;
367 
368   entry=AcquireMagickInfo("JNX","JNX","Garmin tile format");
369   entry->decoder=(DecodeImageHandler *) ReadJNXImage;
370   entry->flags|=CoderDecoderSeekableStreamFlag;
371   (void) RegisterMagickInfo(entry);
372   return(MagickImageCoderSignature);
373 }
374 
375 /*
376 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
377 %                                                                             %
378 %                                                                             %
379 %                                                                             %
380 %   U n r e g i s t e r J N X I m a g e                                       %
381 %                                                                             %
382 %                                                                             %
383 %                                                                             %
384 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
385 %
386 %  UnregisterJNXImage() removes format registrations made by the
387 %  JNX module from the list of supported formats.
388 %
389 %  The format of the UnregisterJNXImage method is:
390 %
391 %      UnregisterJNXImage(void)
392 %
393 */
UnregisterJNXImage(void)394 ModuleExport void UnregisterJNXImage(void)
395 {
396   (void) UnregisterMagickInfo("JNX");
397 }
398