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-2019 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 register 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 register 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 register 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 ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
271 tile_length=ReadBlobLSBLong(image);
272 tile_offset=ReadBlobLSBSignedLong(image);
273 if (tile_offset == -1)
274 continue;
275 restore_offset=TellBlob(image);
276 if (restore_offset < 0)
277 continue;
278 offset=SeekBlob(image,(MagickOffsetType) tile_offset,SEEK_SET);
279 if (offset != (MagickOffsetType) tile_offset)
280 continue;
281 /*
282 Read a tile.
283 */
284 if (((MagickSizeType) tile_length) > GetBlobSize(image))
285 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
286 blob=(unsigned char *) AcquireQuantumMemory((size_t) tile_length+2,
287 sizeof(*blob));
288 if (blob == (unsigned char *) NULL)
289 {
290 if (images != (Image *) NULL)
291 images=DestroyImageList(images);
292 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
293 }
294 blob[0]=0xFF;
295 blob[1]=0xD8;
296 count=ReadBlob(image,tile_length,blob+2);
297 if (count != (ssize_t) tile_length)
298 {
299 if (images != (Image *) NULL)
300 images=DestroyImageList(images);
301 blob=(unsigned char *) RelinquishMagickMemory(blob);
302 ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
303 }
304 read_info=CloneImageInfo(image_info);
305 (void) CopyMagickString(read_info->magick,"JPEG",MagickPathExtent);
306 tile_image=BlobToImage(read_info,blob,tile_length+2,exception);
307 read_info=DestroyImageInfo(read_info);
308 blob=(unsigned char *) RelinquishMagickMemory(blob);
309 offset=SeekBlob(image,restore_offset,SEEK_SET);
310 if (tile_image == (Image *) NULL)
311 continue;
312 tile_image->depth=8;
313 (void) CopyMagickString(tile_image->magick,image->magick,
314 MagickPathExtent);
315 (void) FormatImageProperty(tile_image,"jnx:northeast","%.20g,%.20g",
316 northeast.x,northeast.y);
317 (void) FormatImageProperty(tile_image,"jnx:southwest","%.20g,%.20g",
318 southwest.x,southwest.y);
319 AppendImageToList(&images,tile_image);
320 }
321 if (image->progress_monitor != (MagickProgressMonitor) NULL)
322 {
323 MagickBooleanType
324 proceed;
325
326 proceed=SetImageProgress(image,LoadImageTag,(MagickOffsetType) i,
327 (MagickSizeType) jnx_info.levels);
328 if (proceed == MagickFalse)
329 status=MagickFalse;
330 }
331 }
332 (void) CloseBlob(image);
333 image=DestroyImage(image);
334 if (images == (Image *) NULL)
335 return((Image *) NULL);
336 return(GetFirstImageInList(images));
337 }
338
339 /*
340 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
341 % %
342 % %
343 % %
344 % R e g i s t e r J N X I m a g e %
345 % %
346 % %
347 % %
348 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
349 %
350 % RegisterJNXImage() adds attributes for the JNX image format to the list
351 % of supported formats. The attributes include the image format tag, a
352 % method to read and/or write the format, whether the format supports the
353 % saving of more than one frame to the same file or blob, whether the format
354 % supports native in-memory I/O, and a brief description of the format.
355 %
356 % The format of the RegisterJNXImage method is:
357 %
358 % size_t RegisterJNXImage(void)
359 %
360 */
RegisterJNXImage(void)361 ModuleExport size_t RegisterJNXImage(void)
362 {
363 MagickInfo
364 *entry;
365
366 entry=AcquireMagickInfo("JNX","JNX","Garmin tile format");
367 entry->decoder=(DecodeImageHandler *) ReadJNXImage;
368 entry->flags|=CoderDecoderSeekableStreamFlag;
369 (void) RegisterMagickInfo(entry);
370 return(MagickImageCoderSignature);
371 }
372
373 /*
374 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
375 % %
376 % %
377 % %
378 % U n r e g i s t e r J N X I m a g e %
379 % %
380 % %
381 % %
382 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
383 %
384 % UnregisterJNXImage() removes format registrations made by the
385 % JNX module from the list of supported formats.
386 %
387 % The format of the UnregisterJNXImage method is:
388 %
389 % UnregisterJNXImage(void)
390 %
391 */
UnregisterJNXImage(void)392 ModuleExport void UnregisterJNXImage(void)
393 {
394 (void) UnregisterMagickInfo("JNX");
395 }
396