• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 13772586575

10 Mar 2025 06:56PM UTC coverage: 70.46% (+0.004%) from 70.456%
13772586575

Pull #11943

github

web-flow
Merge 19e37bdaa into fdfe75351
Pull Request #11943: MRF: Fix JPEG max size and caching relative path handling

6 of 7 new or added lines in 3 files covered. (85.71%)

55 existing lines in 34 files now uncovered.

552931 of 784742 relevant lines covered (70.46%)

221838.06 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.59
/frmts/mrf/PNG_band.cpp
1
/*
2
 * Copyright (c) 2002-2012, California Institute of Technology.
3
 * All rights reserved.  Based on Government Sponsored Research under contracts
4
 * NAS7-1407 and/or NAS7-03001.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions are met:
8
 *   1. Redistributions of source code must retain the above copyright notice,
9
 * this list of conditions and the following disclaimer.
10
 *   2. Redistributions in binary form must reproduce the above copyright
11
 * notice, this list of conditions and the following disclaimer in the
12
 * documentation and/or other materials provided with the distribution.
13
 *   3. Neither the name of the California Institute of Technology (Caltech),
14
 * its operating division the Jet Propulsion Laboratory (JPL), the National
15
 * Aeronautics and Space Administration (NASA), nor the names of its
16
 * contributors may be used to endorse or promote products derived from this
17
 * software without specific prior written permission.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
 * ARE DISCLAIMED. IN NO EVENT SHALL THE CALIFORNIA INSTITUTE OF TECHNOLOGY BE
23
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
 * POSSIBILITY OF SUCH DAMAGE.
30
 *
31
 * Copyright (c) 2014-2021 Esri
32
 *
33
 * Licensed under the Apache License, Version 2.0 (the "License");
34
 * you may not use this file except in compliance with the License.
35
 * You may obtain a copy of the License at
36
 *
37
 * http://www.apache.org/licenses/LICENSE-2.0
38
 *
39
 * Unless required by applicable law or agreed to in writing, software
40
 * distributed under the License is distributed on an "AS IS" BASIS,
41
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
42
 * See the License for the specific language governing permissions and
43
 * limitations under the License.
44
 *
45
 * Author: Lucian Plesea
46
 *
47
 */
48

49
/*
50
 * PNG band
51
 * PNG page compression and decompression functions
52
 * These functions are not methods, they reside in the global space
53
 *
54
 */
55

56
#include "marfa.h"
57
#include <cassert>
58

59
CPL_C_START
60
#include <png.h>
61
CPL_C_END
62

63
NAMESPACE_MRF_START
64

65
// Do Nothing
66
static void flush_png(png_structp)
×
67
{
68
}
×
69

70
// Warning Emit
71
static void pngWH(png_struct * /*png*/, png_const_charp message)
×
72
{
73
    CPLError(CE_Warning, CPLE_AppDefined, "MRF: PNG warning %s", message);
×
74
}
×
75

76
// Fatal Warning
77
static void pngEH(png_struct *png, png_const_charp message)
×
78
{
79
    CPLError(CE_Failure, CPLE_AppDefined, "MRF: PNG Failure %s", message);
×
80
    longjmp(png_jmpbuf(png), 1);
×
81
}
82

83
// Read memory handlers for PNG
84
// No check for attempting to read past the end of the buffer
85

86
static void read_png(png_structp pngp, png_bytep data, png_size_t length)
321✔
87
{
88
    buf_mgr *pmgr = (buf_mgr *)png_get_io_ptr(pngp);
321✔
89
    if (pmgr->size < length)
321✔
90
    {
91
        CPLError(CE_Failure, CPLE_AppDefined,
×
92
                 "MRF: PNG Failure: Not enough bytes in buffer");
93
        longjmp(png_jmpbuf(pngp), 1);
×
94
    }
95
    memcpy(data, pmgr->buffer, length);
321✔
96
    pmgr->buffer += length;
321✔
97
    pmgr->size -= length;
321✔
98
}
321✔
99

100
static void write_png(png_structp pngp, png_bytep data, png_size_t length)
411✔
101
{
102
    buf_mgr *mgr = (buf_mgr *)png_get_io_ptr(pngp);
411✔
103
    // Buffer could be too small, trigger an error on debug mode
104
    assert(length <= mgr->size);
411✔
105
    memcpy(mgr->buffer, data, length);
411✔
106
    mgr->buffer += length;
411✔
107
    mgr->size -= length;
411✔
108
}
411✔
109

110
/**
111
 *\brief In memory decompression of PNG file
112
 */
113

114
CPLErr PNG_Codec::DecompressPNG(buf_mgr &dst, buf_mgr &src)
11✔
115
{
116
    const buf_mgr src_ori = src;
11✔
117
    png_bytep *png_rowp = nullptr;
11✔
118
    volatile png_bytep *p_volatile_png_rowp = (volatile png_bytep *)&png_rowp;
11✔
119

120
    // pngp=png_create_read_struct(PNG_LIBPNG_VER_STRING,0,pngEH,pngWH);
121
    png_structp pngp = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr,
11✔
122
                                              nullptr, nullptr);
11✔
123
    if (nullptr == pngp)
11✔
124
    {
125
        CPLError(CE_Failure, CPLE_AppDefined,
×
126
                 "MRF: Error creating PNG decompress");
127
        return CE_Failure;
×
128
    }
129

130
    png_infop infop = png_create_info_struct(pngp);
11✔
131
    if (nullptr == infop)
11✔
132
    {
133
        png_destroy_read_struct(&pngp, &infop, nullptr);
×
134
        CPLError(CE_Failure, CPLE_AppDefined, "MRF: Error creating PNG info");
×
135
        return CE_Failure;
×
136
    }
137

138
    if (setjmp(png_jmpbuf(pngp)))
11✔
139
    {
140
        CPLError(CE_Failure, CPLE_AppDefined,
×
141
                 "MRF: Error during PNG decompress");
142
        CPLFree((void *)(*p_volatile_png_rowp));
×
143
        png_destroy_read_struct(&pngp, &infop, nullptr);
×
144
        return CE_Failure;
×
145
    }
146

147
    // The mgr data ptr is already set up
148
    png_set_read_fn(pngp, &src, read_png);
11✔
149
    // Ready to read
150
    png_read_info(pngp, infop);
11✔
151

152
    if (png_get_bit_depth(pngp, infop) == 8)
11✔
153
    {
154
        // Use the PNG driver for decompression of 8-bit images, as it
155
        // has optimizations for whole image decompression.
156
        const CPLString osTmpFilename(VSIMemGenerateHiddenFilename("mrf.png"));
9✔
157
        VSIFCloseL(VSIFileFromMemBuffer(
9✔
158
            osTmpFilename.c_str(), reinterpret_cast<GByte *>(src_ori.buffer),
9✔
159
            src_ori.size, false));
9✔
160
        const char *const apszAllowedDrivers[] = {"PNG", nullptr};
9✔
161
        auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
162
            osTmpFilename.c_str(), GDAL_OF_RASTER, apszAllowedDrivers));
9✔
163
        if (poDS && static_cast<GUIntBig>(poDS->GetRasterXSize()) *
18✔
164
                            poDS->GetRasterYSize() * poDS->GetRasterCount() ==
9✔
165
                        dst.size)
18✔
166
        {
167
            if (poDS->RasterIO(
27✔
168
                    GF_Read, 0, 0, poDS->GetRasterXSize(),
169
                    poDS->GetRasterYSize(), dst.buffer, poDS->GetRasterXSize(),
9✔
170
                    poDS->GetRasterYSize(), GDT_Byte, poDS->GetRasterCount(),
171
                    nullptr, poDS->GetRasterCount(), 0, 1, nullptr) == CE_None)
18✔
172
            {
173
                png_destroy_read_struct(&pngp, &infop, nullptr);
9✔
174
                VSIUnlink(osTmpFilename.c_str());
9✔
175
                return CE_None;
9✔
176
            }
177
        }
178
        VSIUnlink(osTmpFilename.c_str());
×
179
    }
180

181
    GInt32 height = static_cast<GInt32>(png_get_image_height(pngp, infop));
2✔
182
    // Check the size
183
    if (dst.size < (png_get_rowbytes(pngp, infop) * height))
2✔
184
    {
185
        CPLError(CE_Failure, CPLE_AppDefined,
×
186
                 "MRF: PNG Page data bigger than the buffer provided");
187
        png_destroy_read_struct(&pngp, &infop, nullptr);
×
188
        return CE_Failure;
×
189
    }
190

191
    png_rowp = (png_bytep *)CPLMalloc(sizeof(png_bytep) * height);
2✔
192

193
    int rowbytes = static_cast<int>(png_get_rowbytes(pngp, infop));
2✔
194
    for (int i = 0; i < height; i++)
1,026✔
195
        png_rowp[i] = (png_bytep)dst.buffer + i * rowbytes;
1,024✔
196

197
#if defined(CPL_LSB)
198
    if (png_get_bit_depth(pngp, infop) > 8)
2✔
199
    {
200
        png_set_swap(pngp);
2✔
201
        // Call update info if any png_set is used
202
        png_read_update_info(pngp, infop);
2✔
203
    }
204
#endif
205

206
    // Finally, the read
207
    // This is the lower level, the png_read_end allows some transforms
208
    // Like palette to RGBA
209
    png_read_image(pngp, png_rowp);
2✔
210

211
    //    ppmWrite("Test.ppm",(char *)data,ILSize(512,512,1,4,0));
212
    // Required
213
    png_read_end(pngp, infop);
2✔
214

215
    // png_set_rows(pngp,infop,png_rowp);
216
    // png_read_png(pngp,infop,PNG_TRANSFORM_IDENTITY,0);
217

218
    CPLFree(png_rowp);
2✔
219
    png_destroy_read_struct(&pngp, &infop, nullptr);
2✔
220
    return CE_None;
2✔
221
}
222

223
/**
224
 *\brief Compress a page in PNG format
225
 * Returns the compressed size in dst.size
226
 *
227
 */
228

229
CPLErr PNG_Codec::CompressPNG(buf_mgr &dst, const buf_mgr &src)
15✔
230

231
{
232
    png_structp pngp;
233
    png_infop infop;
234
    buf_mgr mgr = dst;
15✔
235

236
    pngp =
15✔
237
        png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, pngEH, pngWH);
15✔
238
    if (!pngp)
15✔
239
    {
240
        CPLError(CE_Failure, CPLE_AppDefined,
×
241
                 "MRF: Error creating png structure");
242
        return CE_Failure;
×
243
    }
244
    infop = png_create_info_struct(pngp);
15✔
245
    if (!infop)
15✔
246
    {
247
        png_destroy_write_struct(&pngp, nullptr);
×
248
        CPLError(CE_Failure, CPLE_AppDefined,
×
249
                 "MRF: Error creating png info structure");
250
        return CE_Failure;
×
251
    }
252

253
    if (setjmp(png_jmpbuf(pngp)))
15✔
254
    {
255
        png_destroy_write_struct(&pngp, &infop);
×
256
        CPLError(CE_Failure, CPLE_AppDefined, "MRF: Error during png init");
×
257
        return CE_Failure;
×
258
    }
259

260
    png_set_write_fn(pngp, &mgr, write_png, flush_png);
15✔
261

262
    int png_ctype;
263

264
    switch (img.pagesize.c)
15✔
265
    {
266
        case 1:
12✔
267
            if (PNGColors != nullptr)
12✔
268
                png_ctype = PNG_COLOR_TYPE_PALETTE;
1✔
269
            else
270
                png_ctype = PNG_COLOR_TYPE_GRAY;
11✔
271
            break;
12✔
272
        case 2:
1✔
273
            png_ctype = PNG_COLOR_TYPE_GRAY_ALPHA;
1✔
274
            break;
1✔
275
        case 3:
1✔
276
            png_ctype = PNG_COLOR_TYPE_RGB;
1✔
277
            break;
1✔
278
        case 4:
1✔
279
            png_ctype = PNG_COLOR_TYPE_RGB_ALPHA;
1✔
280
            break;
1✔
281
        default:
×
282
        {  // This never happens if we check at the open
283
            CPLError(CE_Failure, CPLE_AppDefined,
×
284
                     "MRF:PNG Write with %d colors called", img.pagesize.c);
×
285
            return CE_Failure;
×
286
        }
287
    }
288

289
    png_set_IHDR(pngp, infop, img.pagesize.x, img.pagesize.y,
15✔
290
                 GDALGetDataTypeSize(img.dt), png_ctype, PNG_INTERLACE_NONE,
15✔
291
                 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
292

293
    // Optional, force certain filters only.  Makes it somewhat faster but worse
294
    // compression png_set_filter(pngp, PNG_FILTER_TYPE_BASE, PNG_FILTER_SUB);
295

296
#if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER > 10200) &&                     \
297
    defined(PNG_SELECT_READ)
298
    png_uint_32 mask, flags;
299

300
    flags = png_get_asm_flags(pngp);
301
    mask = png_get_asm_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE);
302
    png_set_asm_flags(pngp, flags | mask);  // use flags &~mask to disable all
303

304
    // Test that the MMX is compiled into PNG
305
    // fprintf(stderr,"MMX support is %d\n", png_mmx_support());
306

307
#endif
308

309
    // Let the quality control the compression level
310
    // Start at level 1, level 0 means uncompressed
311
    int zlvl = img.quality / 10;
15✔
312
    if (1 > zlvl)
15✔
313
        zlvl = 1;
×
314
    // Max zlvl is 9
315
    if (zlvl > 9)
15✔
NEW
316
        zlvl = 9;
×
317

318
    png_set_compression_level(pngp, zlvl);
15✔
319

320
    // Custom strategy for zlib, set using the band option Z_STRATEGY
321
    if (deflate_flags & ZFLAG_SMASK)
15✔
322
        png_set_compression_strategy(pngp, (deflate_flags & ZFLAG_SMASK) >> 6);
×
323

324
    // Write the palette and the transparencies if they exist
325
    if (PNGColors != nullptr)
15✔
326
    {
327
        png_set_PLTE(pngp, infop, (png_colorp)PNGColors, PalSize);
1✔
328
        if (TransSize != 0)
1✔
329
            png_set_tRNS(pngp, infop, (unsigned char *)PNGAlpha, TransSize,
×
330
                         nullptr);
331
    }
332

333
    png_write_info(pngp, infop);
15✔
334

335
#if defined(CPL_LSB)
336
    if (img.dt != GDT_Byte)
15✔
337
        png_set_swap(pngp);
4✔
338
#endif
339

340
    png_bytep *png_rowp =
341
        (png_bytep *)CPLMalloc(sizeof(png_bytep) * img.pagesize.y);
15✔
342

343
    if (setjmp(png_jmpbuf(pngp)))
15✔
344
    {
345
        CPLFree(png_rowp);
×
346
        png_destroy_write_struct(&pngp, &infop);
×
347
        CPLError(CE_Failure, CPLE_AppDefined,
×
348
                 "MRF: Error during png compression");
349
        return CE_Failure;
×
350
    }
351

352
    int rowbytes = static_cast<int>(png_get_rowbytes(pngp, infop));
15✔
353
    for (int i = 0; i < img.pagesize.y; i++)
7,695✔
354
        png_rowp[i] = (png_bytep)(src.buffer + i * rowbytes);
7,680✔
355

356
    png_write_image(pngp, png_rowp);
15✔
357
    png_write_end(pngp, infop);
15✔
358

359
    // Done
360
    CPLFree(png_rowp);
15✔
361
    png_destroy_write_struct(&pngp, &infop);
15✔
362

363
    // Done
364
    // mgr.size holds the available bytes, so the size of the compressed png
365
    // is the original destination size minus the still available bytes
366
    dst.size -= mgr.size;
15✔
367

368
    return CE_None;
15✔
369
}
370

371
// Builds a PNG palette from a GDAL color table
372
static void ResetPalette(GDALColorTable *poCT, PNG_Codec &codec)
1✔
373
{  // Convert the GDAL LUT to PNG style
374
    codec.TransSize = codec.PalSize = poCT->GetColorEntryCount();
1✔
375

376
    png_color *pasPNGColors =
377
        (png_color *)CPLMalloc(sizeof(png_color) * codec.PalSize);
1✔
378
    unsigned char *pabyAlpha = (unsigned char *)CPLMalloc(codec.TransSize);
1✔
379
    codec.PNGColors = (void *)pasPNGColors;
1✔
380
    codec.PNGAlpha = (void *)pabyAlpha;
1✔
381
    bool NoTranspYet = true;
1✔
382

383
    // Set the palette from the end to reduce the size of the opacity mask
384
    for (int iColor = codec.PalSize - 1; iColor >= 0; iColor--)
257✔
385
    {
386
        GDALColorEntry sEntry;
387
        poCT->GetColorEntryAsRGB(iColor, &sEntry);
256✔
388

389
        pasPNGColors[iColor].red = (png_byte)sEntry.c1;
256✔
390
        pasPNGColors[iColor].green = (png_byte)sEntry.c2;
256✔
391
        pasPNGColors[iColor].blue = (png_byte)sEntry.c3;
256✔
392
        if (NoTranspYet && sEntry.c4 == 255)
256✔
393
            codec.TransSize--;
256✔
394
        else
395
        {
396
            NoTranspYet = false;
×
397
            pabyAlpha[iColor] = (unsigned char)sEntry.c4;
×
398
        }
399
    }
400
}
1✔
401

402
CPLErr PNG_Band::Decompress(buf_mgr &dst, buf_mgr &src)
11✔
403
{
404
    return codec.DecompressPNG(dst, src);
11✔
405
}
406

407
CPLErr PNG_Band::Compress(buf_mgr &dst, buf_mgr &src)
15✔
408
{
409
    if (!codec.PNGColors && img.comp == IL_PPNG)
15✔
410
    {  // Late set PNG palette to conserve memory
411
        GDALColorTable *poCT = GetColorTable();
1✔
412
        if (!poCT)
1✔
413
        {
414
            CPLError(CE_Failure, CPLE_NotSupported,
×
415
                     "MRF PPNG needs a color table");
416
            return CE_Failure;
×
417
        }
418
        ResetPalette(poCT, codec);
1✔
419
    }
420

421
    codec.deflate_flags = deflate_flags;
15✔
422
    return codec.CompressPNG(dst, src);
15✔
423
}
424

425
/**
426
 * \brief For PPNG, builds the data structures needed to write the palette
427
 * The presence of the PNGColors and PNGAlpha is used as a flag for PPNG only
428
 */
429

430
PNG_Band::PNG_Band(MRFDataset *pDS, const ILImage &image, int b, int level)
155✔
431
    : MRFRasterBand(pDS, image, b, level), codec(image)
155✔
432
{  // Check error conditions
433
    if (image.dt != GDT_Byte && image.dt != GDT_Int16 && image.dt != GDT_UInt16)
155✔
434
    {
435
        CPLError(CE_Failure, CPLE_NotSupported,
52✔
436
                 "Data type not supported by MRF PNG");
437
        return;
52✔
438
    }
439
    if (image.pagesize.c > 4)
103✔
440
    {
441
        CPLError(CE_Failure, CPLE_NotSupported,
1✔
442
                 "MRF PNG can only handle up to 4 bands per page");
443
        return;
1✔
444
    }
445
    // PNGs can be larger than the source, especially for small page size
446
    // If PPNG is used, the palette can take up to 2100 bytes
447
    poMRFDS->SetPBufferSize(
102✔
448
        static_cast<unsigned int>(1.1 * image.pageSizeBytes + 4000));
102✔
449
}
450

451
NAMESPACE_MRF_END
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc