Line data Source code
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 : #include <algorithm>
59 :
60 : #if defined(__clang__)
61 : #pragma clang diagnostic push
62 : #pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
63 : #endif
64 :
65 : CPL_C_START
66 : #include <png.h>
67 : CPL_C_END
68 :
69 : #if defined(__clang__)
70 : #pragma clang diagnostic pop
71 : #endif
72 :
73 : NAMESPACE_MRF_START
74 :
75 : // Do Nothing
76 0 : static void flush_png(png_structp)
77 : {
78 0 : }
79 :
80 : // Warning Emit
81 0 : static void pngWH(png_struct * /*png*/, png_const_charp message)
82 : {
83 0 : CPLError(CE_Warning, CPLE_AppDefined, "MRF: PNG warning %s", message);
84 0 : }
85 :
86 : // Fatal Warning
87 0 : static void pngEH(png_struct *png, png_const_charp message)
88 : {
89 0 : CPLError(CE_Failure, CPLE_AppDefined, "MRF: PNG Failure %s", message);
90 0 : longjmp(png_jmpbuf(png), 1);
91 : }
92 :
93 : // Read memory handlers for PNG
94 : // No check for attempting to read past the end of the buffer
95 :
96 321 : static void read_png(png_structp pngp, png_bytep data, png_size_t length)
97 : {
98 321 : buf_mgr *pmgr = (buf_mgr *)png_get_io_ptr(pngp);
99 321 : if (pmgr->size < length)
100 : {
101 0 : CPLError(CE_Failure, CPLE_AppDefined,
102 : "MRF: PNG Failure: Not enough bytes in buffer");
103 0 : longjmp(png_jmpbuf(pngp), 1);
104 : }
105 321 : memcpy(data, pmgr->buffer, length);
106 321 : pmgr->buffer += length;
107 321 : pmgr->size -= length;
108 321 : }
109 :
110 420 : static void write_png(png_structp pngp, png_bytep data, png_size_t length)
111 : {
112 420 : buf_mgr *mgr = (buf_mgr *)png_get_io_ptr(pngp);
113 : // Buffer could be too small, trigger an error on debug mode
114 420 : assert(length <= mgr->size);
115 420 : memcpy(mgr->buffer, data, length);
116 420 : mgr->buffer += length;
117 420 : mgr->size -= length;
118 420 : }
119 :
120 : /**
121 : *\brief In memory decompression of PNG file
122 : */
123 :
124 11 : CPLErr PNG_Codec::DecompressPNG(buf_mgr &dst, buf_mgr &src)
125 : {
126 11 : const buf_mgr src_ori = src;
127 11 : png_bytep *png_rowp = nullptr;
128 11 : volatile png_bytep *p_volatile_png_rowp =
129 : reinterpret_cast<volatile png_bytep *>(&png_rowp);
130 :
131 : // pngp=png_create_read_struct(PNG_LIBPNG_VER_STRING,0,pngEH,pngWH);
132 11 : png_structp pngp = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr,
133 11 : nullptr, nullptr);
134 11 : if (nullptr == pngp)
135 : {
136 0 : CPLError(CE_Failure, CPLE_AppDefined,
137 : "MRF: Error creating PNG decompress");
138 0 : return CE_Failure;
139 : }
140 :
141 11 : png_infop infop = png_create_info_struct(pngp);
142 11 : if (nullptr == infop)
143 : {
144 0 : png_destroy_read_struct(&pngp, &infop, nullptr);
145 0 : CPLError(CE_Failure, CPLE_AppDefined, "MRF: Error creating PNG info");
146 0 : return CE_Failure;
147 : }
148 :
149 11 : if (setjmp(png_jmpbuf(pngp)))
150 : {
151 0 : CPLError(CE_Failure, CPLE_AppDefined,
152 : "MRF: Error during PNG decompress");
153 0 : CPLFree((void *)(*p_volatile_png_rowp));
154 0 : png_destroy_read_struct(&pngp, &infop, nullptr);
155 0 : return CE_Failure;
156 : }
157 :
158 : // The mgr data ptr is already set up
159 11 : png_set_read_fn(pngp, &src, read_png);
160 : // Ready to read
161 11 : png_read_info(pngp, infop);
162 :
163 11 : if (png_get_bit_depth(pngp, infop) == 8)
164 : {
165 : // Use the PNG driver for decompression of 8-bit images, as it
166 : // has optimizations for whole image decompression.
167 9 : const CPLString osTmpFilename(VSIMemGenerateHiddenFilename("mrf.png"));
168 9 : VSIFCloseL(VSIFileFromMemBuffer(
169 9 : osTmpFilename.c_str(), reinterpret_cast<GByte *>(src_ori.buffer),
170 9 : src_ori.size, false));
171 9 : const char *const apszAllowedDrivers[] = {"PNG", nullptr};
172 : auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
173 9 : osTmpFilename.c_str(), GDAL_OF_RASTER, apszAllowedDrivers));
174 18 : if (poDS && static_cast<GUIntBig>(poDS->GetRasterXSize()) *
175 9 : poDS->GetRasterYSize() * poDS->GetRasterCount() ==
176 18 : dst.size)
177 : {
178 27 : if (poDS->RasterIO(
179 : GF_Read, 0, 0, poDS->GetRasterXSize(),
180 9 : poDS->GetRasterYSize(), dst.buffer, poDS->GetRasterXSize(),
181 : poDS->GetRasterYSize(), GDT_Byte, poDS->GetRasterCount(),
182 18 : nullptr, poDS->GetRasterCount(), 0, 1, nullptr) == CE_None)
183 : {
184 9 : png_destroy_read_struct(&pngp, &infop, nullptr);
185 9 : VSIUnlink(osTmpFilename.c_str());
186 9 : return CE_None;
187 : }
188 : }
189 0 : VSIUnlink(osTmpFilename.c_str());
190 : }
191 :
192 2 : GInt32 height = static_cast<GInt32>(png_get_image_height(pngp, infop));
193 : // Check the size
194 2 : if (dst.size < (png_get_rowbytes(pngp, infop) * height))
195 : {
196 0 : CPLError(CE_Failure, CPLE_AppDefined,
197 : "MRF: PNG Page data bigger than the buffer provided");
198 0 : png_destroy_read_struct(&pngp, &infop, nullptr);
199 0 : return CE_Failure;
200 : }
201 :
202 2 : png_rowp = (png_bytep *)CPLMalloc(sizeof(png_bytep) * height);
203 :
204 2 : int rowbytes = static_cast<int>(png_get_rowbytes(pngp, infop));
205 1026 : for (int i = 0; i < height; i++)
206 1024 : png_rowp[i] = (png_bytep)dst.buffer + i * rowbytes;
207 :
208 : #if defined(CPL_LSB)
209 2 : if (png_get_bit_depth(pngp, infop) > 8)
210 : {
211 2 : png_set_swap(pngp);
212 : // Call update info if any png_set is used
213 2 : png_read_update_info(pngp, infop);
214 : }
215 : #endif
216 :
217 : // Finally, the read
218 : // This is the lower level, the png_read_end allows some transforms
219 : // Like palette to RGBA
220 2 : png_read_image(pngp, png_rowp);
221 :
222 : // ppmWrite("Test.ppm",(char *)data,ILSize(512,512,1,4,0));
223 : // Required
224 2 : png_read_end(pngp, infop);
225 :
226 : // png_set_rows(pngp,infop,png_rowp);
227 : // png_read_png(pngp,infop,PNG_TRANSFORM_IDENTITY,0);
228 :
229 2 : CPLFree(png_rowp);
230 2 : png_destroy_read_struct(&pngp, &infop, nullptr);
231 2 : return CE_None;
232 : }
233 :
234 : /**
235 : *\brief Compress a page in PNG format
236 : * Returns the compressed size in dst.size
237 : *
238 : */
239 :
240 16 : CPLErr PNG_Codec::CompressPNG(buf_mgr &dst, const buf_mgr &src)
241 :
242 : {
243 : png_structp pngp;
244 : png_infop infop;
245 16 : buf_mgr mgr = dst;
246 :
247 16 : pngp =
248 16 : png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, pngEH, pngWH);
249 16 : if (!pngp)
250 : {
251 0 : CPLError(CE_Failure, CPLE_AppDefined,
252 : "MRF: Error creating png structure");
253 0 : return CE_Failure;
254 : }
255 16 : infop = png_create_info_struct(pngp);
256 16 : if (!infop)
257 : {
258 0 : png_destroy_write_struct(&pngp, nullptr);
259 0 : CPLError(CE_Failure, CPLE_AppDefined,
260 : "MRF: Error creating png info structure");
261 0 : return CE_Failure;
262 : }
263 :
264 16 : if (setjmp(png_jmpbuf(pngp)))
265 : {
266 0 : png_destroy_write_struct(&pngp, &infop);
267 0 : CPLError(CE_Failure, CPLE_AppDefined, "MRF: Error during png init");
268 0 : return CE_Failure;
269 : }
270 :
271 16 : png_set_write_fn(pngp, &mgr, write_png, flush_png);
272 :
273 : int png_ctype;
274 :
275 16 : switch (img.pagesize.c)
276 : {
277 16 : case 1:
278 16 : if (PNGColors != nullptr)
279 1 : png_ctype = PNG_COLOR_TYPE_PALETTE;
280 : else
281 15 : png_ctype = PNG_COLOR_TYPE_GRAY;
282 16 : break;
283 0 : case 2:
284 0 : png_ctype = PNG_COLOR_TYPE_GRAY_ALPHA;
285 0 : break;
286 0 : case 3:
287 0 : png_ctype = PNG_COLOR_TYPE_RGB;
288 0 : break;
289 0 : case 4:
290 0 : png_ctype = PNG_COLOR_TYPE_RGB_ALPHA;
291 0 : break;
292 0 : default:
293 : { // This never happens if we check at the open
294 0 : CPLError(CE_Failure, CPLE_AppDefined,
295 0 : "MRF:PNG Write with %d colors called", img.pagesize.c);
296 0 : return CE_Failure;
297 : }
298 : }
299 :
300 16 : png_set_IHDR(pngp, infop, img.pagesize.x, img.pagesize.y,
301 16 : GDALGetDataTypeSizeBits(img.dt), png_ctype, PNG_INTERLACE_NONE,
302 : PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
303 :
304 : // Optional, force certain filters only. Makes it somewhat faster but worse
305 : // compression png_set_filter(pngp, PNG_FILTER_TYPE_BASE, PNG_FILTER_SUB);
306 :
307 : #if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER > 10200) && \
308 : defined(PNG_SELECT_READ)
309 : png_uint_32 mask, flags;
310 :
311 : flags = png_get_asm_flags(pngp);
312 : mask = png_get_asm_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE);
313 : png_set_asm_flags(pngp, flags | mask); // use flags &~mask to disable all
314 : #endif
315 :
316 : // Let the quality control the compression level
317 16 : png_set_compression_level(pngp, std::clamp(img.quality / 10, 1, 9));
318 :
319 : // Custom strategy for zlib, set using the band option Z_STRATEGY
320 16 : if (deflate_flags & ZFLAG_SMASK)
321 0 : png_set_compression_strategy(pngp, (deflate_flags & ZFLAG_SMASK) >> 6);
322 :
323 : // Write the palette and the transparencies if they exist
324 16 : if (PNGColors != nullptr)
325 : {
326 1 : png_set_PLTE(pngp, infop, (png_colorp)PNGColors, PalSize);
327 1 : if (TransSize != 0)
328 0 : png_set_tRNS(pngp, infop, (unsigned char *)PNGAlpha, TransSize,
329 : nullptr);
330 : }
331 :
332 16 : png_write_info(pngp, infop);
333 :
334 : #if defined(CPL_LSB)
335 16 : if (img.dt != GDT_Byte)
336 4 : png_set_swap(pngp);
337 : #endif
338 :
339 : png_bytep *png_rowp =
340 16 : (png_bytep *)CPLMalloc(sizeof(png_bytep) * img.pagesize.y);
341 :
342 16 : if (setjmp(png_jmpbuf(pngp)))
343 : {
344 0 : CPLFree(png_rowp);
345 0 : png_destroy_write_struct(&pngp, &infop);
346 0 : CPLError(CE_Failure, CPLE_AppDefined,
347 : "MRF: Error during png compression");
348 0 : return CE_Failure;
349 : }
350 :
351 16 : int rowbytes = static_cast<int>(png_get_rowbytes(pngp, infop));
352 8208 : for (int i = 0; i < img.pagesize.y; i++)
353 8192 : png_rowp[i] = (png_bytep)(src.buffer + i * rowbytes);
354 :
355 16 : png_write_image(pngp, png_rowp);
356 16 : png_write_end(pngp, infop);
357 :
358 : // Done
359 16 : CPLFree(png_rowp);
360 16 : png_destroy_write_struct(&pngp, &infop);
361 :
362 : // Done
363 : // mgr.size holds the available bytes, so the size of the compressed png
364 : // is the original destination size minus the still available bytes
365 16 : dst.size -= mgr.size;
366 :
367 16 : return CE_None;
368 : }
369 :
370 : // Builds a PNG palette from a GDAL color table
371 1 : static void ResetPalette(GDALColorTable *poCT, PNG_Codec &codec)
372 : { // Convert the GDAL LUT to PNG style
373 1 : codec.TransSize = codec.PalSize = poCT->GetColorEntryCount();
374 :
375 : png_color *pasPNGColors =
376 1 : (png_color *)CPLMalloc(sizeof(png_color) * codec.PalSize);
377 1 : unsigned char *pabyAlpha = (unsigned char *)CPLMalloc(codec.TransSize);
378 1 : codec.PNGColors = (void *)pasPNGColors;
379 1 : codec.PNGAlpha = (void *)pabyAlpha;
380 1 : bool NoTranspYet = true;
381 :
382 : // Set the palette from the end to reduce the size of the opacity mask
383 257 : for (int iColor = codec.PalSize - 1; iColor >= 0; iColor--)
384 : {
385 : GDALColorEntry sEntry;
386 256 : poCT->GetColorEntryAsRGB(iColor, &sEntry);
387 :
388 256 : pasPNGColors[iColor].red = (png_byte)sEntry.c1;
389 256 : pasPNGColors[iColor].green = (png_byte)sEntry.c2;
390 256 : pasPNGColors[iColor].blue = (png_byte)sEntry.c3;
391 256 : if (NoTranspYet && sEntry.c4 == 255)
392 256 : codec.TransSize--;
393 : else
394 : {
395 0 : NoTranspYet = false;
396 0 : pabyAlpha[iColor] = (unsigned char)sEntry.c4;
397 : }
398 : }
399 1 : }
400 :
401 11 : CPLErr PNG_Band::Decompress(buf_mgr &dst, buf_mgr &src)
402 : {
403 11 : return codec.DecompressPNG(dst, src);
404 : }
405 :
406 16 : CPLErr PNG_Band::Compress(buf_mgr &dst, buf_mgr &src)
407 : {
408 16 : if (!codec.PNGColors && img.comp == IL_PPNG)
409 : { // Late set PNG palette to conserve memory
410 1 : GDALColorTable *poCT = GetColorTable();
411 1 : if (!poCT)
412 : {
413 0 : CPLError(CE_Failure, CPLE_NotSupported,
414 : "MRF PPNG needs a color table");
415 0 : return CE_Failure;
416 : }
417 1 : ResetPalette(poCT, codec);
418 : }
419 :
420 16 : codec.deflate_flags = deflate_flags;
421 16 : return codec.CompressPNG(dst, src);
422 : }
423 :
424 : /**
425 : * \brief For PPNG, builds the data structures needed to write the palette
426 : * The presence of the PNGColors and PNGAlpha is used as a flag for PPNG only
427 : */
428 :
429 159 : PNG_Band::PNG_Band(MRFDataset *pDS, const ILImage &image, int b, int level)
430 159 : : MRFRasterBand(pDS, image, b, level), codec(image)
431 : { // Check error conditions
432 159 : if (image.dt != GDT_Byte && image.dt != GDT_Int16 && image.dt != GDT_UInt16)
433 : {
434 52 : CPLError(CE_Failure, CPLE_NotSupported,
435 : "Data type not supported by MRF PNG");
436 52 : return;
437 : }
438 107 : if (image.pagesize.c > 4)
439 : {
440 0 : CPLError(CE_Failure, CPLE_NotSupported,
441 : "MRF PNG can only handle up to 4 bands per page");
442 0 : return;
443 : }
444 : // PNGs can be larger than the source, especially for small page size
445 : // If PPNG is used, the palette can take up to 2100 bytes
446 107 : poMRFDS->SetPBufferSize(
447 107 : static_cast<unsigned int>(1.1 * image.pageSizeBytes + 4000));
448 : }
449 :
450 : NAMESPACE_MRF_END
|