1 /******************************************************************************
2  *
3  * Project:  Raster Matrix Format
4  * Purpose:  Implementation of the JPEG decompression algorithm as used in
5  *           GIS "Panorama" raster files.
6  * Author:   Andrew Sudorgin (drons [a] list dot ru)
7  *
8  ******************************************************************************
9  * Copyright (c) 2018, Andrew Sudorgin
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #ifdef HAVE_LIBJPEG
31 
32 #include <algorithm>
33 #include "cpl_conv.h"
34 #include "rmfdataset.h"
35 #include "../mem/memdataset.h"
36 
37 /************************************************************************/
38 /*                          JPEGDecompress()                            */
39 /************************************************************************/
40 
41 size_t RMFDataset::JPEGDecompress(const GByte* pabyIn, GUInt32 nSizeIn,
42                                   GByte* pabyOut, GUInt32 nSizeOut,
43                                   GUInt32 nRawXSize, GUInt32 nRawYSize)
44 {
45     if(pabyIn == nullptr ||
46        pabyOut == nullptr ||
47        nSizeOut < nSizeIn ||
48        nSizeIn < 2)
49        return 0;
50 
51     CPLString   osTmpFilename;
52     VSILFILE*   fp;
53 
54     osTmpFilename.Printf("/vsimem/rmfjpeg/%p.jpg", pabyIn);
55 
56     fp = VSIFileFromMemBuffer(osTmpFilename, const_cast<GByte*>(pabyIn),
57                               nSizeIn, FALSE);
58 
59     if(fp == nullptr)
60     {
61         CPLError(CE_Failure, CPLE_AppDefined,
62                  "RMF JPEG: Can't create %s file", osTmpFilename.c_str());
63         return 0;
64     }
65 
66     const char*     apszAllowedDrivers[] = {"JPEG", nullptr};
67     GDALDatasetH    hTile;
68 
69 
70     CPLConfigOptionSetter   oNoReadDir("GDAL_DISABLE_READDIR_ON_OPEN",
71                                        "EMPTY_DIR", false);
72 
73     hTile = GDALOpenEx(osTmpFilename, GDAL_OF_RASTER | GDAL_OF_INTERNAL,
74                        apszAllowedDrivers, nullptr, nullptr);
75 
76     if(hTile == nullptr)
77     {
78         CPLError(CE_Failure, CPLE_AppDefined,
79                  "RMF JPEG: Can't open %s file", osTmpFilename.c_str());
80         VSIFCloseL(fp);
81         VSIUnlink(osTmpFilename);
82         return 0;
83     }
84 
85     if(GDALGetRasterCount(hTile) != RMF_JPEG_BAND_COUNT)
86     {
87         CPLError(CE_Failure, CPLE_AppDefined,
88                  "RMF JPEG: Invalid band count %d in tile, must be %d",
89                  GDALGetRasterCount(hTile), (int)RMF_JPEG_BAND_COUNT);
90         GDALClose(hTile);
91         VSIFCloseL(fp);
92         VSIUnlink(osTmpFilename);
93         return 0;
94     }
95 
96     int nBandCount = GDALGetRasterCount(hTile);
97 
98     int nImageWidth = std::min(GDALGetRasterXSize(hTile),
99                                static_cast<int>(nRawXSize));
100     int nImageHeight = std::min(GDALGetRasterYSize(hTile),
101                                 static_cast<int>(nRawYSize));
102 
103     if( nRawXSize * nBandCount * nImageHeight > nSizeOut )
104     {
105         CPLError(CE_Failure, CPLE_AppDefined,
106                  "RMF JPEG: Too small output buffer");
107         GDALClose(hTile);
108         VSIFCloseL(fp);
109         VSIUnlink(osTmpFilename);
110         return 0;
111     }
112 
113     CPLErr  eErr;
114     size_t  nRet;
115     int     aBandMap[RMF_JPEG_BAND_COUNT] = {3, 2, 1};
116     eErr = GDALDatasetRasterIO(hTile, GF_Read, 0, 0,
117                                nImageWidth, nImageHeight, pabyOut,
118                                nImageWidth, nImageHeight, GDT_Byte,
119                                nBandCount, aBandMap,
120                                nBandCount, nRawXSize * nBandCount, 1);
121     if(CE_None != eErr)
122     {
123         CPLError(CE_Failure, CPLE_AppDefined,
124                  "RMF JPEG: Error decompress JPEG tile");
125         nRet = 0;
126     }
127     else
128     {
129         nRet = static_cast<size_t>(nRawXSize * nBandCount * nImageHeight);
130     }
131 
132     GDALClose(hTile);
133     VSIFCloseL(fp);
134     VSIUnlink(osTmpFilename);
135 
136     return nRet;
137 }
138 
139 /************************************************************************/
140 /*                            JPEGCompress()                            */
141 /************************************************************************/
142 
143 size_t RMFDataset::JPEGCompress(const GByte* pabyIn, GUInt32 nSizeIn,
144                                 GByte* pabyOut, GUInt32 nSizeOut,
145                                 GUInt32 nRawXSize, GUInt32 nRawYSize,
146                                 const RMFDataset* poDS)
147 {
148     if(pabyIn == nullptr ||
149        pabyOut == nullptr ||
150        nSizeIn < 2)
151        return 0;
152 
153     GDALDriverH hJpegDriver = GDALGetDriverByName("JPEG");
154 
155     if(hJpegDriver == nullptr)
156     {
157         CPLError(CE_Failure, CPLE_AppDefined,
158                  "RMF: JPEG driver not found");
159         return 0;
160     }
161 
162     GDALDataType    eType = GDT_Byte;
163     GDALDataset*    poMemDS = MEMDataset::Create("", nRawXSize, nRawYSize, 0,
164                                                  eType, nullptr);
165 
166     for(int iBand = 0; iBand < RMF_JPEG_BAND_COUNT; ++iBand)
167     {
168         char szBuffer[32] = {};
169         const GByte* pabyBand = pabyIn + (RMF_JPEG_BAND_COUNT - iBand - 1);
170         int nRet = CPLPrintPointer(szBuffer, (void*)(pabyBand),
171                                    sizeof(szBuffer));
172         szBuffer[nRet] = 0;
173 
174         char szBuffer0[64] = {};
175         snprintf( szBuffer0, sizeof(szBuffer0), "DATAPOINTER=%s", szBuffer );
176 
177         char szBuffer1[64] = "PIXELOFFSET=3";
178         char szBuffer2[64] = {};
179         snprintf( szBuffer2, sizeof(szBuffer2),
180                   "LINEOFFSET=%d", nRawXSize*RMF_JPEG_BAND_COUNT );
181 
182         char* apszOptions[4] = { szBuffer0, szBuffer1, szBuffer2, nullptr };
183 
184         poMemDS->AddBand(eType, apszOptions);
185     }
186 
187     CPLString   osTmpFilename;
188     osTmpFilename.Printf("/vsimem/rmfjpeg/%p.jpg", pabyIn);
189 
190     char szQuality[32] = {};
191     if(poDS != nullptr && poDS->sHeader.iJpegQuality > 0)
192     {
193         snprintf(szQuality, sizeof(szQuality),
194                  "QUALITY=%d", (int)poDS->sHeader.iJpegQuality);
195     }
196     else
197     {
198         snprintf(szQuality, sizeof(szQuality), "QUALITY=75");
199     }
200 
201     char* apszJpegOptions[2] = {szQuality, nullptr};
202 
203     GDALDatasetH   hJpeg = GDALCreateCopy(hJpegDriver, osTmpFilename, poMemDS,
204                                           0, apszJpegOptions, nullptr, nullptr);
205     GDALClose(poMemDS);
206 
207     if(hJpeg == nullptr)
208     {
209         CPLError(CE_Failure, CPLE_AppDefined,
210                  "RMF JPEG: Error compress JPEG tile");
211         VSIUnlink(osTmpFilename);
212         return 0;
213     }
214 
215     GDALClose(hJpeg);
216 
217     vsi_l_offset    nDataLength = 0;
218     GByte* pabyBuffer = VSIGetMemFileBuffer(osTmpFilename, &nDataLength, TRUE);
219 
220     if(nDataLength < nSizeOut)
221     {
222         memcpy(pabyOut, pabyBuffer, static_cast<size_t>(nDataLength));
223         CPLFree(pabyBuffer);
224         return static_cast<size_t>(nDataLength);
225     }
226 
227     CPLFree(pabyBuffer);
228     return 0;
229 }
230 #endif //HAVE_LIBJPEG
231