diff --git a/frmts/gdalallregister.cpp b/frmts/gdalallregister.cpp index e6c0c98251b7..62569dc1daf7 100644 --- a/frmts/gdalallregister.cpp +++ b/frmts/gdalallregister.cpp @@ -191,6 +191,9 @@ void CPL_STDCALL GDALAllRegister() #if defined(DEFERRED_JP2OPENJPEG_DRIVER) DeclareDeferredOPENJPEGPlugin(); #endif +#if defined(DEFERRED_JPEG_DRIVER) + DeclareDeferredJPEGPlugin(); +#endif #if defined(DEFERRED_JPEGXL_DRIVER) DeclareDeferredJPEGXLPlugin(); #endif diff --git a/frmts/jpeg/CMakeLists.txt b/frmts/jpeg/CMakeLists.txt index 60cf75f48b28..bdbe70891ea0 100644 --- a/frmts/jpeg/CMakeLists.txt +++ b/frmts/jpeg/CMakeLists.txt @@ -1,9 +1,23 @@ add_gdal_driver( TARGET gdal_JPEG SOURCES vsidataio.h jpgdataset.h vsidataio.cpp jpgdataset.cpp + CORE_SOURCES jpegdrivercore.cpp PLUGIN_CAPABLE_IF "NOT GDAL_USE_JPEG_INTERNAL;NOT GDAL_USE_JPEG12_INTERNAL;NOT GDAL_USE_ZLIB_INTERNAL" ) + +if(TARGET gdal_JPEG_core) + target_include_directories(gdal_JPEG_core PRIVATE $) + target_compile_definitions(gdal_JPEG_core PRIVATE $) + if (HAVE_JPEGTURBO_DUAL_MODE_8_12) + target_compile_definitions(gdal_JPEG_core PRIVATE JPEG_DUAL_MODE_8_12 HAVE_JPEGTURBO_DUAL_MODE_8_12) + endif() +endif() + +if(NOT TARGET gdal_JPEG) + return() +endif() + gdal_standard_includes(gdal_JPEG) target_include_directories(gdal_JPEG PRIVATE ${GDAL_RASTER_FORMAT_SOURCE_DIR}/mem ${GDAL_RASTER_FORMAT_SOURCE_DIR}/gtiff) diff --git a/frmts/jpeg/jpegdrivercore.cpp b/frmts/jpeg/jpegdrivercore.cpp new file mode 100644 index 000000000000..670178578a58 --- /dev/null +++ b/frmts/jpeg/jpegdrivercore.cpp @@ -0,0 +1,187 @@ +/****************************************************************************** + * + * Project: JPEG JFIF Driver + * Purpose: Implement GDAL JPEG Support based on IJG libjpeg. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault + * + * Portions Copyright (c) Her majesty the Queen in right of Canada as + * represented by the Minister of National Defence, 2006. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "jpegdrivercore.h" + +// So that D_LOSSLESS_SUPPORTED is visible if defined in jmorecfg of libjpeg-turbo >= 2.2 +#define JPEG_INTERNAL_OPTIONS +#include "jpeglib.h" + +/************************************************************************/ +/* JPEGDatasetIsJPEGLS() */ +/************************************************************************/ + +bool JPEGDatasetIsJPEGLS(GDALOpenInfo *poOpenInfo) + +{ + GByte *pabyHeader = poOpenInfo->pabyHeader; + int nHeaderBytes = poOpenInfo->nHeaderBytes; + + if (nHeaderBytes < 10) + return false; + + if (pabyHeader[0] != 0xff || pabyHeader[1] != 0xd8) + return false; + + for (int nOffset = 2; nOffset + 4 < nHeaderBytes;) + { + if (pabyHeader[nOffset] != 0xFF) + return false; + + int nMarker = pabyHeader[nOffset + 1]; + if (nMarker == 0xDA) + return false; + + if (nMarker == 0xF7) // JPEG Extension 7, JPEG-LS. + return true; + if (nMarker == 0xF8) // JPEG Extension 8, JPEG-LS Extension. + return true; + if (nMarker == 0xC3) // Start of Frame 3 (Lossless Huffman) + return true; + if (nMarker == + 0xC7) // Start of Frame 7 (Differential Lossless Huffman) + return true; + if (nMarker == 0xCB) // Start of Frame 11 (Lossless Arithmetic) + return true; + if (nMarker == + 0xCF) // Start of Frame 15 (Differential Lossless Arithmetic) + return true; + + nOffset += 2 + pabyHeader[nOffset + 2] * 256 + pabyHeader[nOffset + 3]; + } + + return false; +} + +/************************************************************************/ +/* JPEGDriverIdentify() */ +/************************************************************************/ + +int JPEGDriverIdentify(GDALOpenInfo *poOpenInfo) + +{ + // If it is a subfile, read the JPEG header. + if (STARTS_WITH_CI(poOpenInfo->pszFilename, "JPEG_SUBFILE:")) + return TRUE; + if (STARTS_WITH(poOpenInfo->pszFilename, "JPEG:")) + return TRUE; + + // First we check to see if the file has the expected header bytes. + const int nHeaderBytes = poOpenInfo->nHeaderBytes; + + if (nHeaderBytes < 10) + return FALSE; + + GByte *const pabyHeader = poOpenInfo->pabyHeader; + if (pabyHeader[0] != 0xff || pabyHeader[1] != 0xd8 || pabyHeader[2] != 0xff) + return FALSE; + + // libjpeg-turbo >= 2.2 supports lossless mode +#if !defined(D_LOSSLESS_SUPPORTED) + if (JPEGDatasetIsJPEGLS(poOpenInfo)) + { + return FALSE; + } +#endif + + // Some files like + // http://dionecanali.hd.free.fr/~mdione/mapzen/N65E039.hgt.gz could be + // mis-identfied as JPEG + CPLString osFilenameLower = CPLString(poOpenInfo->pszFilename).tolower(); + if (osFilenameLower.endsWith(".hgt") || + osFilenameLower.endsWith(".hgt.gz") || + osFilenameLower.endsWith(".hgt.zip")) + { + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* JPEGDriverSetCommonMetadata() */ +/************************************************************************/ + +void JPEGDriverSetCommonMetadata(GDALDriver *poDriver) +{ + poDriver->SetDescription(DRIVER_NAME); + poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); + poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "JPEG JFIF"); + poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/jpeg.html"); + poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "jpg"); + poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "jpg jpeg"); + poDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/jpeg"); + +#if defined(JPEG_LIB_MK1_OR_12BIT) || defined(JPEG_DUAL_MODE_8_12) + poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte UInt16"); +#else + poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte"); +#endif + poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); + + const char *pszOpenOptions = + "\n" + " \n"; + poDriver->SetMetadataItem(GDAL_DMD_OPENOPTIONLIST, pszOpenOptions); + +#ifdef D_LOSSLESS_SUPPORTED + // For autotest purposes + poDriver->SetMetadataItem("LOSSLESS_JPEG_SUPPORTED", "YES", "JPEG"); +#endif + + poDriver->pfnIdentify = JPEGDriverIdentify; + poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES"); + poDriver->SetMetadataItem(GDAL_DCAP_CREATECOPY, "YES"); +} + +/************************************************************************/ +/* DeclareDeferredJPEGPlugin() */ +/************************************************************************/ + +#ifdef PLUGIN_FILENAME +void DeclareDeferredJPEGPlugin() +{ + if (GDALGetDriverByName(DRIVER_NAME) != nullptr) + { + return; + } + auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME); + JPEGDriverSetCommonMetadata(poDriver); + GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver); +} +#endif diff --git a/frmts/jpeg/jpegdrivercore.h b/frmts/jpeg/jpegdrivercore.h new file mode 100644 index 000000000000..39570c89ace2 --- /dev/null +++ b/frmts/jpeg/jpegdrivercore.h @@ -0,0 +1,46 @@ +/****************************************************************************** + * + * Project: JPEG JFIF Driver + * Purpose: Implement GDAL JPEG Support based on IJG libjpeg. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault + * + * Portions Copyright (c) Her majesty the Queen in right of Canada as + * represented by the Minister of National Defence, 2006. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef JPEGDRIVERCORE_H +#define JPEGDRIVERCORE_H + +#include "gdal_priv.h" + +constexpr const char *DRIVER_NAME = "JPEG"; + +bool CPL_DLL JPEGDatasetIsJPEGLS(GDALOpenInfo *poOpenInfo); + +int CPL_DLL JPEGDriverIdentify(GDALOpenInfo *poOpenInfo); + +void CPL_DLL JPEGDriverSetCommonMetadata(GDALDriver *poDriver); + +#endif diff --git a/frmts/jpeg/jpgdataset.cpp b/frmts/jpeg/jpgdataset.cpp index 833a55f46d1e..3af4c36db7b3 100644 --- a/frmts/jpeg/jpgdataset.cpp +++ b/frmts/jpeg/jpgdataset.cpp @@ -76,6 +76,7 @@ CPL_C_END #include "rawdataset.h" #include "vsidataio.h" #include "vrt/vrtdataset.h" +#include "jpegdrivercore.h" #if defined(EXPECTED_JPEG_LIB_VERSION) && !defined(LIBJPEG_12_PATH) #if EXPECTED_JPEG_LIB_VERSION != JPEG_LIB_VERSION @@ -2630,97 +2631,6 @@ CPLErr JPGDatasetCommon::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, nLineSpace, nBandSpace, psExtraArg); } -/************************************************************************/ -/* JPEGDatasetIsJPEGLS() */ -/************************************************************************/ - -static bool JPEGDatasetIsJPEGLS(GDALOpenInfo *poOpenInfo) - -{ - GByte *pabyHeader = poOpenInfo->pabyHeader; - int nHeaderBytes = poOpenInfo->nHeaderBytes; - - if (nHeaderBytes < 10) - return false; - - if (pabyHeader[0] != 0xff || pabyHeader[1] != 0xd8) - return false; - - for (int nOffset = 2; nOffset + 4 < nHeaderBytes;) - { - if (pabyHeader[nOffset] != 0xFF) - return false; - - int nMarker = pabyHeader[nOffset + 1]; - if (nMarker == 0xDA) - return false; - - if (nMarker == 0xF7) // JPEG Extension 7, JPEG-LS. - return true; - if (nMarker == 0xF8) // JPEG Extension 8, JPEG-LS Extension. - return true; - if (nMarker == 0xC3) // Start of Frame 3 (Lossless Huffman) - return true; - if (nMarker == - 0xC7) // Start of Frame 7 (Differential Lossless Huffman) - return true; - if (nMarker == 0xCB) // Start of Frame 11 (Lossless Arithmetic) - return true; - if (nMarker == - 0xCF) // Start of Frame 15 (Differential Lossless Arithmetic) - return true; - - nOffset += 2 + pabyHeader[nOffset + 2] * 256 + pabyHeader[nOffset + 3]; - } - - return false; -} - -/************************************************************************/ -/* Identify() */ -/************************************************************************/ - -int JPGDatasetCommon::Identify(GDALOpenInfo *poOpenInfo) - -{ - // If it is a subfile, read the JPEG header. - if (STARTS_WITH_CI(poOpenInfo->pszFilename, "JPEG_SUBFILE:")) - return TRUE; - if (STARTS_WITH(poOpenInfo->pszFilename, "JPEG:")) - return TRUE; - - // First we check to see if the file has the expected header bytes. - const int nHeaderBytes = poOpenInfo->nHeaderBytes; - - if (nHeaderBytes < 10) - return FALSE; - - GByte *const pabyHeader = poOpenInfo->pabyHeader; - if (pabyHeader[0] != 0xff || pabyHeader[1] != 0xd8 || pabyHeader[2] != 0xff) - return FALSE; - - // libjpeg-turbo >= 2.2 supports lossless mode -#if !defined(D_LOSSLESS_SUPPORTED) - if (JPEGDatasetIsJPEGLS(poOpenInfo)) - { - return FALSE; - } -#endif - - // Some files like - // http://dionecanali.hd.free.fr/~mdione/mapzen/N65E039.hgt.gz could be - // mis-identfied as JPEG - CPLString osFilenameLower = CPLString(poOpenInfo->pszFilename).tolower(); - if (osFilenameLower.endsWith(".hgt") || - osFilenameLower.endsWith(".hgt.gz") || - osFilenameLower.endsWith(".hgt.zip")) - { - return FALSE; - } - - return TRUE; -} - /************************************************************************/ /* Open() */ /************************************************************************/ @@ -2730,7 +2640,7 @@ GDALDataset *JPGDatasetCommon::Open(GDALOpenInfo *poOpenInfo) { #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION // During fuzzing, do not use Identify to reject crazy content. - if (!Identify(poOpenInfo)) + if (!JPEGDriverIdentify(poOpenInfo)) return nullptr; #endif @@ -4950,46 +4860,15 @@ const char *GDALJPGDriver::GetMetadataItem(const char *pszName, void GDALRegister_JPEG() { - if (GDALGetDriverByName("JPEG") != nullptr) + if (GDALGetDriverByName(DRIVER_NAME) != nullptr) return; GDALDriver *poDriver = new GDALJPGDriver(); + JPEGDriverSetCommonMetadata(poDriver); - poDriver->SetDescription("JPEG"); - poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); - poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "JPEG JFIF"); - poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/jpeg.html"); - poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "jpg"); - poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "jpg jpeg"); - poDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/jpeg"); - -#if defined(JPEG_LIB_MK1_OR_12BIT) || defined(JPEG_DUAL_MODE_8_12) - poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte UInt16"); -#else - poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte"); -#endif - poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); - - const char *pszOpenOptions = - "\n" - " \n"; - poDriver->SetMetadataItem(GDAL_DMD_OPENOPTIONLIST, pszOpenOptions); - - poDriver->pfnIdentify = JPGDatasetCommon::Identify; poDriver->pfnOpen = JPGDatasetCommon::Open; poDriver->pfnCreateCopy = JPGDataset::CreateCopy; -#ifdef D_LOSSLESS_SUPPORTED - // For autotest purposes - poDriver->SetMetadataItem("LOSSLESS_JPEG_SUPPORTED", "YES", "JPEG"); -#endif - GetGDALDriverManager()->RegisterDriver(poDriver); } #endif diff --git a/frmts/jpeg/jpgdataset.h b/frmts/jpeg/jpgdataset.h index e8ed13dd1337..2c4b690056de 100644 --- a/frmts/jpeg/jpgdataset.h +++ b/frmts/jpeg/jpgdataset.h @@ -283,7 +283,6 @@ class JPGDatasetCommon CPL_NON_FINAL : public GDALPamDataset size_t *pnBufferSize, char **ppszDetailedFormat) override; - static int Identify(GDALOpenInfo *); static GDALDataset *Open(GDALOpenInfo *); }; diff --git a/gcore/gdal_frmts.h b/gcore/gdal_frmts.h index 2b263afd40d7..7a0e6c1e1919 100644 --- a/gcore/gdal_frmts.h +++ b/gcore/gdal_frmts.h @@ -62,6 +62,7 @@ void CPL_DLL DeclareDeferredDDSPlugin(void); void CPL_DLL GDALRegister_GTA(void); void CPL_DLL DeclareDeferredGTAPlugin(void); void CPL_DLL GDALRegister_JPEG(void); +void DeclareDeferredJPEGPlugin(void); void CPL_DLL GDALRegister_JP2KAK(void); void DeclareDeferredJP2KAKPlugin(void); void CPL_DLL GDALRegister_JPIPKAK(void);