/src/gdal/frmts/jpeg/jpegdrivercore.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: JPEG JFIF Driver |
4 | | * Purpose: Implement GDAL JPEG Support based on IJG libjpeg. |
5 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2000, Frank Warmerdam |
9 | | * Copyright (c) 2007-2014, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * Portions Copyright (c) Her majesty the Queen in right of Canada as |
12 | | * represented by the Minister of National Defence, 2006. |
13 | | * |
14 | | * SPDX-License-Identifier: MIT |
15 | | ****************************************************************************/ |
16 | | |
17 | | #include "jpegdrivercore.h" |
18 | | |
19 | | // So that D_LOSSLESS_SUPPORTED is visible if defined in jmorecfg of libjpeg-turbo >= 2.2 |
20 | | #define JPEG_INTERNAL_OPTIONS |
21 | | #include "jpeglib.h" |
22 | | |
23 | | /************************************************************************/ |
24 | | /* JPEGDatasetIsJPEGLS() */ |
25 | | /************************************************************************/ |
26 | | |
27 | | bool JPEGDatasetIsJPEGLS(GDALOpenInfo *poOpenInfo) |
28 | | |
29 | 1.42k | { |
30 | 1.42k | GByte *pabyHeader = poOpenInfo->pabyHeader; |
31 | 1.42k | int nHeaderBytes = poOpenInfo->nHeaderBytes; |
32 | | |
33 | 1.42k | if (nHeaderBytes < 10) |
34 | 0 | return false; |
35 | | |
36 | 1.42k | if (pabyHeader[0] != 0xff || pabyHeader[1] != 0xd8) |
37 | 0 | return false; |
38 | | |
39 | 4.08k | for (int nOffset = 2; nOffset + 4 < nHeaderBytes;) |
40 | 3.50k | { |
41 | 3.50k | if (pabyHeader[nOffset] != 0xFF) |
42 | 669 | return false; |
43 | | |
44 | 2.83k | int nMarker = pabyHeader[nOffset + 1]; |
45 | 2.83k | if (nMarker == 0xDA) |
46 | 156 | return false; |
47 | | |
48 | 2.67k | if (nMarker == 0xF7) // JPEG Extension 7, JPEG-LS. |
49 | 8 | return true; |
50 | 2.67k | if (nMarker == 0xF8) // JPEG Extension 8, JPEG-LS Extension. |
51 | 0 | return true; |
52 | 2.67k | if (nMarker == 0xC3) // Start of Frame 3 (Lossless Huffman) |
53 | 3 | return true; |
54 | 2.66k | if (nMarker == |
55 | 2.66k | 0xC7) // Start of Frame 7 (Differential Lossless Huffman) |
56 | 0 | return true; |
57 | 2.66k | if (nMarker == 0xCB) // Start of Frame 11 (Lossless Arithmetic) |
58 | 0 | return true; |
59 | 2.66k | if (nMarker == |
60 | 2.66k | 0xCF) // Start of Frame 15 (Differential Lossless Arithmetic) |
61 | 0 | return true; |
62 | | |
63 | 2.66k | nOffset += 2 + pabyHeader[nOffset + 2] * 256 + pabyHeader[nOffset + 3]; |
64 | 2.66k | } |
65 | | |
66 | 584 | return false; |
67 | 1.42k | } |
68 | | |
69 | | /************************************************************************/ |
70 | | /* JPEGDriverIdentify() */ |
71 | | /************************************************************************/ |
72 | | |
73 | | int JPEGDriverIdentify(GDALOpenInfo *poOpenInfo) |
74 | | |
75 | 604k | { |
76 | | // If it is a subfile, read the JPEG header. |
77 | 604k | if (STARTS_WITH_CI(poOpenInfo->pszFilename, "JPEG_SUBFILE:")) |
78 | 28 | return TRUE; |
79 | 604k | if (STARTS_WITH(poOpenInfo->pszFilename, "JPEG:")) |
80 | 0 | return TRUE; |
81 | | |
82 | | // First we check to see if the file has the expected header bytes. |
83 | 604k | const int nHeaderBytes = poOpenInfo->nHeaderBytes; |
84 | | |
85 | 604k | if (nHeaderBytes < 10) |
86 | 511k | return FALSE; |
87 | | |
88 | 93.4k | GByte *const pabyHeader = poOpenInfo->pabyHeader; |
89 | 93.4k | if (pabyHeader[0] != 0xff || pabyHeader[1] != 0xd8 || pabyHeader[2] != 0xff) |
90 | 92.0k | return FALSE; |
91 | | |
92 | | // libjpeg-turbo >= 2.2 supports lossless mode |
93 | 1.42k | #if !defined(D_LOSSLESS_SUPPORTED) |
94 | 1.42k | if (JPEGDatasetIsJPEGLS(poOpenInfo)) |
95 | 11 | { |
96 | 11 | return FALSE; |
97 | 11 | } |
98 | 1.40k | #endif |
99 | | |
100 | | // Some files like |
101 | | // http://dionecanali.hd.free.fr/~mdione/mapzen/N65E039.hgt.gz could be |
102 | | // mis-identfied as JPEG |
103 | 1.40k | CPLString osFilenameLower = CPLString(poOpenInfo->pszFilename).tolower(); |
104 | 1.40k | if (osFilenameLower.endsWith(".hgt") || |
105 | 1.40k | osFilenameLower.endsWith(".hgt.gz") || |
106 | 1.40k | osFilenameLower.endsWith(".hgt.zip")) |
107 | 0 | { |
108 | 0 | return FALSE; |
109 | 0 | } |
110 | | |
111 | 1.40k | return TRUE; |
112 | 1.40k | } |
113 | | |
114 | | /************************************************************************/ |
115 | | /* JPEGDriverSetCommonMetadata() */ |
116 | | /************************************************************************/ |
117 | | |
118 | | void JPEGDriverSetCommonMetadata(GDALDriver *poDriver) |
119 | 22 | { |
120 | 22 | poDriver->SetDescription(DRIVER_NAME); |
121 | 22 | poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); |
122 | 22 | poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "JPEG JFIF"); |
123 | 22 | poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/jpeg.html"); |
124 | 22 | poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "jpg"); |
125 | 22 | poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "jpg jpeg"); |
126 | 22 | poDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/jpeg"); |
127 | | |
128 | 22 | #if defined(JPEG_LIB_MK1_OR_12BIT) || defined(JPEG_DUAL_MODE_8_12) |
129 | 22 | poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte UInt16"); |
130 | | #else |
131 | | poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte"); |
132 | | #endif |
133 | 22 | poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); |
134 | | |
135 | 22 | const char *pszOpenOptions = |
136 | 22 | "<OpenOptionList>\n" |
137 | 22 | " <Option name='USE_INTERNAL_OVERVIEWS' type='boolean' " |
138 | 22 | "description='whether to use implicit internal overviews' " |
139 | 22 | "default='YES'/>\n" |
140 | 22 | " <Option name='APPLY_ORIENTATION' type='boolean' " |
141 | 22 | "description='whether to take into account EXIF Orientation to " |
142 | 22 | "rotate/flip the image' default='NO'/>\n" |
143 | 22 | "</OpenOptionList>\n"; |
144 | 22 | poDriver->SetMetadataItem(GDAL_DMD_OPENOPTIONLIST, pszOpenOptions); |
145 | | |
146 | | #ifdef D_LOSSLESS_SUPPORTED |
147 | | // For autotest purposes |
148 | | poDriver->SetMetadataItem("LOSSLESS_JPEG_SUPPORTED", "YES", "JPEG"); |
149 | | #endif |
150 | | |
151 | 22 | poDriver->pfnIdentify = JPEGDriverIdentify; |
152 | 22 | poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES"); |
153 | 22 | poDriver->SetMetadataItem(GDAL_DCAP_CREATECOPY, "YES"); |
154 | 22 | } |
155 | | |
156 | | /************************************************************************/ |
157 | | /* DeclareDeferredJPEGPlugin() */ |
158 | | /************************************************************************/ |
159 | | |
160 | | #ifdef PLUGIN_FILENAME |
161 | | void DeclareDeferredJPEGPlugin() |
162 | | { |
163 | | if (GDALGetDriverByName(DRIVER_NAME) != nullptr) |
164 | | { |
165 | | return; |
166 | | } |
167 | | auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME); |
168 | | #ifdef PLUGIN_INSTALLATION_MESSAGE |
169 | | poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE, |
170 | | PLUGIN_INSTALLATION_MESSAGE); |
171 | | #endif |
172 | | JPEGDriverSetCommonMetadata(poDriver); |
173 | | GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver); |
174 | | } |
175 | | #endif |