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