Coverage Report

Created: 2026-09-13 07:02

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