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