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