1 : /******************************************************************************
2 : * $Id: gt_jpeg_copy.cpp 24337 2012-04-28 15:06:01Z rouault $
3 : *
4 : * Project: GeoTIFF Driver
5 : * Purpose: Specialized copy of JPEG content into TIFF.
6 : * Author: Even Rouault, <even dot rouault at mines dash paris dot org>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2012, Even Rouault, <even dot rouault at mines dash paris dot org>
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 : #include "cpl_vsi.h"
31 : #include "gt_jpeg_copy.h"
32 :
33 : /* Note: JPEG_DIRECT_COPY is not defined by default, because it is mainly */
34 : /* usefull for debugging purposes */
35 :
36 : CPL_CVSID("$Id: gt_jpeg_copy.cpp 24337 2012-04-28 15:06:01Z rouault $");
37 :
38 : #if defined(JPEG_DIRECT_COPY) || defined(HAVE_LIBJPEG)
39 :
40 : #include "vrt/vrtdataset.h"
41 :
42 : /************************************************************************/
43 : /* GetUnderlyingDataset() */
44 : /************************************************************************/
45 :
46 452 : static GDALDataset* GetUnderlyingDataset(GDALDataset* poSrcDS)
47 : {
48 : /* Test if we can directly copy original JPEG content */
49 : /* if available */
50 904 : if (poSrcDS->GetDriver() != NULL &&
51 452 : poSrcDS->GetDriver() == GDALGetDriverByName("VRT"))
52 : {
53 154 : VRTDataset* poVRTDS = (VRTDataset* )poSrcDS;
54 154 : poSrcDS = poVRTDS->GetSingleSimpleSource();
55 : }
56 :
57 452 : return poSrcDS;
58 : }
59 :
60 : #endif // defined(JPEG_DIRECT_COPY) || defined(HAVE_LIBJPEG)
61 :
62 :
63 : #ifdef JPEG_DIRECT_COPY
64 :
65 : /************************************************************************/
66 : /* IsBaselineDCTJPEG() */
67 : /************************************************************************/
68 :
69 : static int IsBaselineDCTJPEG(VSILFILE* fp)
70 : {
71 : GByte abyBuf[4];
72 :
73 : if (VSIFReadL(abyBuf, 1, 2, fp) != 2 ||
74 : abyBuf[0] != 0xff || abyBuf[1] != 0xd8 )
75 : {
76 : CPLError(CE_Failure, CPLE_AppDefined,
77 : "Not a valid JPEG file");
78 : return FALSE;
79 : }
80 :
81 : int nOffset = 2;
82 : while(TRUE)
83 : {
84 : VSIFSeekL(fp, nOffset, SEEK_SET);
85 : if (VSIFReadL(abyBuf, 1, 4, fp) != 4 ||
86 : abyBuf[0] != 0xFF)
87 : {
88 : CPLError(CE_Failure, CPLE_AppDefined,
89 : "Not a valid JPEG file");
90 : return FALSE;
91 : }
92 :
93 : int nMarker = abyBuf[1];
94 :
95 : if (nMarker == 0xC0 /* Start of Frame 0 = Baseline DCT */)
96 : return TRUE;
97 :
98 : if (nMarker == 0xD9)
99 : return FALSE;
100 :
101 : if (nMarker == 0xF7 /* JPEG Extension 7, JPEG-LS */ ||
102 : nMarker == 0xF8 /* JPEG Extension 8, JPEG-LS Extension */ ||
103 : (nMarker >= 0xC1 && nMarker <= 0xCF) /* Other Start of Frames that we don't want to support */)
104 : {
105 : CPLError(CE_Failure, CPLE_AppDefined,
106 : "Unsupported type of JPEG file for JPEG_DIRECT_COPY mode");
107 : return FALSE;
108 : }
109 :
110 : nOffset += 2 + abyBuf[2] * 256 + abyBuf[3];
111 : }
112 : }
113 :
114 : /************************************************************************/
115 : /* GTIFF_CanDirectCopyFromJPEG() */
116 : /************************************************************************/
117 :
118 : int GTIFF_CanDirectCopyFromJPEG(GDALDataset* poSrcDS, char** &papszCreateOptions)
119 : {
120 : poSrcDS = GetUnderlyingDataset(poSrcDS);
121 : if (poSrcDS == NULL)
122 : return FALSE;
123 : if (poSrcDS->GetDriver() == NULL)
124 : return FALSE;
125 : if (!EQUAL(GDALGetDriverShortName(poSrcDS->GetDriver()), "JPEG"))
126 : return FALSE;
127 :
128 : const char* pszCompress = CSLFetchNameValue(papszCreateOptions, "COMPRESS");
129 : if (pszCompress != NULL && !EQUAL(pszCompress, "JPEG"))
130 : return FALSE;
131 :
132 : const char* pszSrcColorSpace = poSrcDS->GetMetadataItem("SOURCE_COLOR_SPACE", "IMAGE_STRUCTURE");
133 : if (pszSrcColorSpace != NULL &&
134 : (EQUAL(pszSrcColorSpace, "CMYK") || EQUAL(pszSrcColorSpace, "YCbCrK")))
135 : return FALSE;
136 :
137 : int bJPEGDirectCopy = FALSE;
138 :
139 : VSILFILE* fpJPEG = VSIFOpenL(poSrcDS->GetDescription(), "rb");
140 : if (fpJPEG && IsBaselineDCTJPEG(fpJPEG))
141 : {
142 : bJPEGDirectCopy = TRUE;
143 :
144 : if (pszCompress == NULL)
145 : papszCreateOptions = CSLSetNameValue(papszCreateOptions, "COMPRESS", "JPEG");
146 :
147 : papszCreateOptions = CSLSetNameValue(papszCreateOptions, "BLOCKXSIZE", NULL);
148 : papszCreateOptions = CSLSetNameValue(papszCreateOptions, "BLOCKYSIZE",
149 : CPLSPrintf("%d", poSrcDS->GetRasterYSize()));
150 :
151 : if (pszSrcColorSpace != NULL && EQUAL(pszSrcColorSpace, "YCbCr"))
152 : papszCreateOptions = CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", "YCBCR");
153 : else
154 : papszCreateOptions = CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", NULL);
155 :
156 : if (poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte)
157 : papszCreateOptions = CSLSetNameValue(papszCreateOptions, "NBITS", "12");
158 : else
159 : papszCreateOptions = CSLSetNameValue(papszCreateOptions, "NBITS", NULL);
160 :
161 : papszCreateOptions = CSLSetNameValue(papszCreateOptions, "TILED", NULL);
162 : papszCreateOptions = CSLSetNameValue(papszCreateOptions, "JPEG_QUALITY", NULL);
163 : }
164 : if (fpJPEG)
165 : VSIFCloseL(fpJPEG);
166 :
167 : return bJPEGDirectCopy;
168 : }
169 :
170 : /************************************************************************/
171 : /* GTIFF_DirectCopyFromJPEG() */
172 : /************************************************************************/
173 :
174 : CPLErr GTIFF_DirectCopyFromJPEG(GDALDataset* poDS, GDALDataset* poSrcDS,
175 : GDALProgressFunc pfnProgress, void * pProgressData,
176 : int& bShouldFallbackToNormalCopyIfFail)
177 : {
178 : bShouldFallbackToNormalCopyIfFail = TRUE;
179 :
180 : poSrcDS = GetUnderlyingDataset(poSrcDS);
181 : if (poSrcDS == NULL)
182 : return CE_Failure;
183 :
184 : VSILFILE* fpJPEG = VSIFOpenL(poSrcDS->GetDescription(), "rb");
185 : if (fpJPEG == NULL)
186 : return CE_Failure;
187 :
188 : CPLErr eErr = CE_None;
189 :
190 : VSIFSeekL(fpJPEG, 0, SEEK_END);
191 : tmsize_t nSize = (tmsize_t) VSIFTellL(fpJPEG);
192 : VSIFSeekL(fpJPEG, 0, SEEK_SET);
193 :
194 : void* pabyJPEGData = VSIMalloc(nSize);
195 : if (pabyJPEGData == NULL)
196 : {
197 : VSIFCloseL(fpJPEG);
198 : return CE_Failure;
199 : }
200 :
201 : if (pabyJPEGData != NULL &&
202 : (tmsize_t)VSIFReadL(pabyJPEGData, 1, nSize, fpJPEG) == nSize)
203 : {
204 : bShouldFallbackToNormalCopyIfFail = FALSE;
205 :
206 : TIFF* hTIFF = (TIFF*) poDS->GetInternalHandle(NULL);
207 : if (TIFFWriteRawStrip(hTIFF, 0, pabyJPEGData, nSize) != nSize)
208 : eErr = CE_Failure;
209 :
210 : if( !pfnProgress( 1.0, NULL, pProgressData ) )
211 : eErr = CE_Failure;
212 : }
213 : else
214 : {
215 : eErr = CE_Failure;
216 : }
217 :
218 : VSIFree(pabyJPEGData);
219 : VSIFCloseL(fpJPEG);
220 :
221 : return eErr;
222 : }
223 :
224 : #endif // JPEG_DIRECT_COPY
225 :
226 : #ifdef HAVE_LIBJPEG
227 :
228 : #include "vsidataio.h"
229 :
230 : #include <setjmp.h>
231 :
232 : /*
233 : * We are using width_in_blocks which is supposed to be private to
234 : * libjpeg. Unfortunately, the libjpeg delivered with Cygwin has
235 : * renamed this member to width_in_data_units. Since the header has
236 : * also renamed a define, use that unique define name in order to
237 : * detect the problem header and adjust to suit.
238 : */
239 : #if defined(D_MAX_DATA_UNITS_IN_MCU)
240 : #define width_in_blocks width_in_data_units
241 : #endif
242 :
243 : /************************************************************************/
244 : /* GTIFF_CanCopyFromJPEG() */
245 : /************************************************************************/
246 :
247 420 : int GTIFF_CanCopyFromJPEG(GDALDataset* poSrcDS, char** &papszCreateOptions)
248 : {
249 420 : poSrcDS = GetUnderlyingDataset(poSrcDS);
250 420 : if (poSrcDS == NULL)
251 72 : return FALSE;
252 348 : if (poSrcDS->GetDriver() == NULL)
253 2 : return FALSE;
254 346 : if (!EQUAL(GDALGetDriverShortName(poSrcDS->GetDriver()), "JPEG"))
255 330 : return FALSE;
256 :
257 16 : const char* pszCompress = CSLFetchNameValue(papszCreateOptions, "COMPRESS");
258 16 : if (pszCompress == NULL || !EQUAL(pszCompress, "JPEG"))
259 0 : return FALSE;
260 :
261 16 : int nBlockXSize = atoi(CSLFetchNameValueDef(papszCreateOptions, "BLOCKXSIZE", "0"));
262 16 : int nBlockYSize = atoi(CSLFetchNameValueDef(papszCreateOptions, "BLOCKYSIZE", "0"));
263 16 : int nMCUSize = 8;
264 : const char* pszSrcColorSpace =
265 16 : poSrcDS->GetMetadataItem("SOURCE_COLOR_SPACE", "IMAGE_STRUCTURE");
266 22 : if (pszSrcColorSpace != NULL && EQUAL(pszSrcColorSpace, "YCbCr"))
267 6 : nMCUSize = 16;
268 10 : else if (pszSrcColorSpace != NULL &&
269 : (EQUAL(pszSrcColorSpace, "CMYK") || EQUAL(pszSrcColorSpace, "YCbCrK")))
270 0 : return FALSE;
271 :
272 16 : int nXSize = poSrcDS->GetRasterXSize();
273 16 : int nYSize = poSrcDS->GetRasterYSize();
274 16 : int nBands = poSrcDS->GetRasterCount();
275 :
276 16 : const char* pszPhotometric = CSLFetchNameValue(papszCreateOptions, "PHOTOMETRIC");
277 : int bCompatiblePhotometric = (
278 : pszPhotometric == NULL ||
279 : (nMCUSize == 16 && EQUAL(pszPhotometric, "YCbCr")) ||
280 : (nMCUSize == 8 && EQUAL(pszPhotometric, "RGB") && nBands == 3) ||
281 16 : (nMCUSize == 8 && EQUAL(pszPhotometric, "MINISBLACK") && nBands == 1) );
282 16 : if (!bCompatiblePhotometric)
283 0 : return FALSE;
284 :
285 16 : if ( (nBlockXSize == nXSize || (nBlockXSize % nMCUSize) == 0) &&
286 : (nBlockYSize == nYSize || (nBlockYSize % nMCUSize) == 0) &&
287 : poSrcDS->GetRasterBand(1)->GetRasterDataType() == GDT_Byte &&
288 : CSLFetchNameValue(papszCreateOptions, "NBITS") == NULL &&
289 : CSLFetchNameValue(papszCreateOptions, "JPEG_QUALITY") == NULL &&
290 : bCompatiblePhotometric )
291 : {
292 16 : if (nMCUSize == 16 && pszPhotometric == NULL)
293 6 : papszCreateOptions = CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", "YCBCR");
294 16 : return TRUE;
295 : }
296 : else
297 : {
298 0 : return FALSE;
299 : }
300 : }
301 :
302 : /************************************************************************/
303 : /* GTIFF_ErrorExitJPEG() */
304 : /************************************************************************/
305 :
306 0 : static void GTIFF_ErrorExitJPEG(j_common_ptr cinfo)
307 : {
308 0 : jmp_buf *setjmp_buffer = (jmp_buf *) cinfo->client_data;
309 : char buffer[JMSG_LENGTH_MAX];
310 :
311 : /* Create the message */
312 0 : (*cinfo->err->format_message) (cinfo, buffer);
313 :
314 : CPLError( CE_Failure, CPLE_AppDefined,
315 0 : "libjpeg: %s", buffer );
316 :
317 : /* Return control to the setjmp point */
318 0 : longjmp(*setjmp_buffer, 1);
319 : }
320 :
321 : /************************************************************************/
322 : /* GTIFF_Set_TIFFTAG_JPEGTABLES() */
323 : /************************************************************************/
324 :
325 16 : void GTIFF_Set_TIFFTAG_JPEGTABLES(TIFF* hTIFF,
326 : jpeg_decompress_struct& sDInfo,
327 : jpeg_compress_struct& sCInfo)
328 : {
329 : char szTmpFilename[128];
330 16 : sprintf(szTmpFilename, "/vsimem/tables_%p", &sDInfo);
331 16 : VSILFILE* fpTABLES = VSIFOpenL(szTmpFilename, "wb+");
332 :
333 16 : jpeg_vsiio_dest( &sCInfo, fpTABLES );
334 16 : jpeg_write_tables( &sCInfo );
335 :
336 16 : VSIFCloseL(fpTABLES);
337 :
338 16 : vsi_l_offset nSizeTables = 0;
339 16 : GByte* pabyJPEGTablesData = VSIGetMemFileBuffer(szTmpFilename, &nSizeTables, FALSE);
340 16 : TIFFSetField(hTIFF, TIFFTAG_JPEGTABLES, (int)nSizeTables, pabyJPEGTablesData);
341 :
342 16 : VSIUnlink(szTmpFilename);
343 16 : }
344 :
345 : /************************************************************************/
346 : /* GTIFF_CopyFromJPEG_WriteAdditionalTags() */
347 : /************************************************************************/
348 :
349 16 : CPLErr GTIFF_CopyFromJPEG_WriteAdditionalTags(TIFF* hTIFF,
350 : GDALDataset* poSrcDS)
351 : {
352 16 : poSrcDS = GetUnderlyingDataset(poSrcDS);
353 16 : if (poSrcDS == NULL)
354 0 : return CE_Failure;
355 :
356 : /* -------------------------------------------------------------------- */
357 : /* Write TIFFTAG_JPEGTABLES */
358 : /* -------------------------------------------------------------------- */
359 :
360 16 : VSILFILE* fpJPEG = VSIFOpenL(poSrcDS->GetDescription(), "rb");
361 16 : if (fpJPEG == NULL)
362 0 : return CE_Failure;
363 :
364 : struct jpeg_error_mgr sJErr;
365 : struct jpeg_decompress_struct sDInfo;
366 : jmp_buf setjmp_buffer;
367 16 : if (setjmp(setjmp_buffer))
368 : {
369 0 : VSIFCloseL(fpJPEG);
370 0 : return CE_Failure;
371 : }
372 :
373 16 : sDInfo.err = jpeg_std_error( &sJErr );
374 16 : sJErr.error_exit = GTIFF_ErrorExitJPEG;
375 16 : sDInfo.client_data = (void *) &setjmp_buffer;
376 :
377 16 : jpeg_create_decompress(&sDInfo);
378 :
379 16 : jpeg_vsiio_src( &sDInfo, fpJPEG );
380 16 : jpeg_read_header( &sDInfo, TRUE );
381 :
382 : struct jpeg_compress_struct sCInfo;
383 :
384 16 : sCInfo.err = jpeg_std_error( &sJErr );
385 16 : sJErr.error_exit = GTIFF_ErrorExitJPEG;
386 16 : sCInfo.client_data = (void *) &setjmp_buffer;
387 :
388 16 : jpeg_create_compress(&sCInfo);
389 16 : jpeg_copy_critical_parameters(&sDInfo, &sCInfo);
390 16 : GTIFF_Set_TIFFTAG_JPEGTABLES(hTIFF, sDInfo, sCInfo);
391 16 : jpeg_abort_compress(&sCInfo);
392 16 : jpeg_destroy_compress(&sCInfo);
393 :
394 : /* -------------------------------------------------------------------- */
395 : /* Write TIFFTAG_REFERENCEBLACKWHITE if needed. */
396 : /* -------------------------------------------------------------------- */
397 :
398 : uint16 nPhotometric;
399 16 : if( !TIFFGetField( hTIFF, TIFFTAG_PHOTOMETRIC, &(nPhotometric) ) )
400 0 : nPhotometric = PHOTOMETRIC_MINISBLACK;
401 :
402 : uint16 nBitsPerSample;
403 16 : if( !TIFFGetField(hTIFF, TIFFTAG_BITSPERSAMPLE, &(nBitsPerSample)) )
404 0 : nBitsPerSample = 1;
405 :
406 16 : if ( nPhotometric == PHOTOMETRIC_YCBCR )
407 : {
408 : /*
409 : * A ReferenceBlackWhite field *must* be present since the
410 : * default value is inappropriate for YCbCr. Fill in the
411 : * proper value if application didn't set it.
412 : */
413 : float *ref;
414 6 : if (!TIFFGetField(hTIFF, TIFFTAG_REFERENCEBLACKWHITE,
415 : &ref))
416 : {
417 : float refbw[6];
418 6 : long top = 1L << nBitsPerSample;
419 6 : refbw[0] = 0;
420 6 : refbw[1] = (float)(top-1L);
421 6 : refbw[2] = (float)(top>>1);
422 6 : refbw[3] = refbw[1];
423 6 : refbw[4] = refbw[2];
424 6 : refbw[5] = refbw[1];
425 : TIFFSetField(hTIFF, TIFFTAG_REFERENCEBLACKWHITE,
426 6 : refbw);
427 : }
428 : }
429 :
430 : /* -------------------------------------------------------------------- */
431 : /* Write TIFFTAG_YCBCRSUBSAMPLING if needed. */
432 : /* -------------------------------------------------------------------- */
433 :
434 16 : if ( nPhotometric == PHOTOMETRIC_YCBCR && sDInfo.num_components == 3 )
435 : {
436 48 : if ((sDInfo.comp_info[0].h_samp_factor == 1 || sDInfo.comp_info[0].h_samp_factor == 2) &&
437 12 : (sDInfo.comp_info[0].v_samp_factor == 1 || sDInfo.comp_info[0].v_samp_factor == 2) &&
438 6 : sDInfo.comp_info[1].h_samp_factor == 1 &&
439 6 : sDInfo.comp_info[1].v_samp_factor == 1 &&
440 6 : sDInfo.comp_info[2].h_samp_factor == 1 &&
441 6 : sDInfo.comp_info[2].v_samp_factor == 1)
442 : {
443 : TIFFSetField(hTIFF, TIFFTAG_YCBCRSUBSAMPLING,
444 6 : sDInfo.comp_info[0].h_samp_factor,
445 12 : sDInfo.comp_info[0].v_samp_factor);
446 : }
447 : else
448 : {
449 0 : CPLDebug("GTiff", "Unusual sampling factors. TIFFTAG_YCBCRSUBSAMPLING not written.");
450 : }
451 : }
452 :
453 : /* -------------------------------------------------------------------- */
454 : /* Cleanup. */
455 : /* -------------------------------------------------------------------- */
456 :
457 16 : jpeg_abort_decompress( &sDInfo );
458 16 : jpeg_destroy_decompress( &sDInfo );
459 :
460 16 : VSIFCloseL(fpJPEG);
461 :
462 16 : return CE_None;
463 : }
464 :
465 : /************************************************************************/
466 : /* GTIFF_CopyBlockFromJPEG() */
467 : /************************************************************************/
468 :
469 76 : static CPLErr GTIFF_CopyBlockFromJPEG(TIFF* hTIFF,
470 : jpeg_decompress_struct& sDInfo,
471 : int iX, int iY,
472 : int nXBlocks, int nYBlocks,
473 : int nXSize, int nYSize,
474 : int nBlockXSize, int nBlockYSize,
475 : int iMCU_sample_width, int iMCU_sample_height,
476 : jvirt_barray_ptr *pSrcCoeffs)
477 : {
478 76 : CPLString osTmpFilename(CPLSPrintf("/vsimem/%p", &sDInfo));
479 76 : VSILFILE* fpMEM = VSIFOpenL(osTmpFilename, "wb+");
480 :
481 : /* -------------------------------------------------------------------- */
482 : /* Initialization of the compressor */
483 : /* -------------------------------------------------------------------- */
484 : struct jpeg_error_mgr sJErr;
485 : struct jpeg_compress_struct sCInfo;
486 : jmp_buf setjmp_buffer;
487 76 : if (setjmp(setjmp_buffer))
488 : {
489 0 : VSIFCloseL(fpMEM);
490 0 : VSIUnlink(osTmpFilename);
491 0 : return CE_Failure;
492 : }
493 :
494 76 : sCInfo.err = jpeg_std_error( &sJErr );
495 76 : sJErr.error_exit = GTIFF_ErrorExitJPEG;
496 76 : sCInfo.client_data = (void *) &setjmp_buffer;
497 :
498 : /* Initialize destination compression parameters from source values */
499 76 : jpeg_create_compress(&sCInfo);
500 76 : jpeg_copy_critical_parameters(&sDInfo, &sCInfo);
501 :
502 : /* ensure libjpeg won't write any extraneous markers */
503 76 : sCInfo.write_JFIF_header = FALSE;
504 76 : sCInfo.write_Adobe_marker = FALSE;
505 :
506 : /* -------------------------------------------------------------------- */
507 : /* Allocated destination coefficient array */
508 : /* -------------------------------------------------------------------- */
509 76 : int bIsTiled = TIFFIsTiled(hTIFF);
510 :
511 : int nJPEGWidth, nJPEGHeight;
512 76 : if (bIsTiled)
513 : {
514 16 : nJPEGWidth = nBlockXSize;
515 16 : nJPEGHeight = nBlockYSize;
516 : }
517 : else
518 : {
519 60 : nJPEGWidth = MIN(nBlockXSize, nXSize - iX * nBlockXSize);
520 60 : nJPEGHeight = MIN(nBlockYSize, nYSize - iY * nBlockYSize);
521 : }
522 :
523 : /* Code partially derived from libjpeg transupp.c */
524 :
525 : /* Correct the destination's image dimensions as necessary */
526 : #if JPEG_LIB_VERSION >= 70
527 : sCInfo.jpeg_width = nJPEGWidth;
528 : sCInfo.jpeg_height = nJPEGHeight;
529 : #else
530 76 : sCInfo.image_width = nJPEGWidth;
531 76 : sCInfo.image_height = nJPEGHeight;
532 : #endif
533 :
534 : /* Save x/y offsets measured in iMCUs */
535 76 : int x_crop_offset = (iX * nBlockXSize) / iMCU_sample_width;
536 76 : int y_crop_offset = (iY * nBlockYSize) / iMCU_sample_height;
537 :
538 : jvirt_barray_ptr* pDstCoeffs = (jvirt_barray_ptr *)
539 : (*sCInfo.mem->alloc_small) ((j_common_ptr) &sCInfo, JPOOL_IMAGE,
540 76 : sizeof(jvirt_barray_ptr) * sCInfo.num_components);
541 : int ci;
542 :
543 268 : for (ci = 0; ci < sCInfo.num_components; ci++)
544 : {
545 192 : jpeg_component_info *compptr = sCInfo.comp_info + ci;
546 : int h_samp_factor, v_samp_factor;
547 192 : if (sCInfo.num_components == 1)
548 : {
549 : /* we're going to force samp factors to 1x1 in this case */
550 18 : h_samp_factor = v_samp_factor = 1;
551 : }
552 : else
553 : {
554 174 : h_samp_factor = compptr->h_samp_factor;
555 174 : v_samp_factor = compptr->v_samp_factor;
556 : }
557 : int width_in_iMCUs =
558 192 : (nJPEGWidth + iMCU_sample_width - 1) / iMCU_sample_width;
559 : int height_in_iMCUs =
560 192 : (nJPEGHeight + iMCU_sample_height - 1) / iMCU_sample_height;
561 192 : int nWidth_in_blocks = width_in_iMCUs * h_samp_factor;
562 192 : int nHeight_in_blocks = height_in_iMCUs * v_samp_factor;
563 192 : pDstCoeffs[ci] = (*sCInfo.mem->request_virt_barray)
564 : ((j_common_ptr) &sCInfo, JPOOL_IMAGE, FALSE,
565 192 : nWidth_in_blocks, nHeight_in_blocks, (JDIMENSION) v_samp_factor);
566 : }
567 :
568 76 : jpeg_vsiio_dest( &sCInfo, fpMEM );
569 :
570 : /* Start compressor (note no image data is actually written here) */
571 76 : jpeg_write_coefficients(&sCInfo, pDstCoeffs);
572 :
573 76 : jpeg_suppress_tables( &sCInfo, TRUE );
574 :
575 : /* We simply have to copy the right amount of data (the destination's
576 : * image size) starting at the given X and Y offsets in the source.
577 : */
578 268 : for (ci = 0; ci < sCInfo.num_components; ci++)
579 : {
580 192 : jpeg_component_info *compptr = sCInfo.comp_info + ci;
581 192 : int x_crop_blocks = x_crop_offset * compptr->h_samp_factor;
582 192 : int y_crop_blocks = y_crop_offset * compptr->v_samp_factor;
583 192 : JDIMENSION nSrcWidthInBlocks = sDInfo.comp_info[ci].width_in_blocks;
584 192 : JDIMENSION nSrcHeightInBlocks = sDInfo.comp_info[ci].height_in_blocks;
585 :
586 192 : JDIMENSION nXBlocksToCopy = compptr->width_in_blocks;
587 192 : if (x_crop_blocks + compptr->width_in_blocks > nSrcWidthInBlocks)
588 16 : nXBlocksToCopy = nSrcWidthInBlocks - x_crop_blocks;
589 :
590 856 : for (JDIMENSION dst_blk_y = 0;
591 : dst_blk_y < compptr->height_in_blocks;
592 : dst_blk_y += compptr->v_samp_factor)
593 : {
594 : JBLOCKARRAY dst_buffer = (*sDInfo.mem->access_virt_barray)
595 : ((j_common_ptr) &sDInfo, pDstCoeffs[ci],
596 : dst_blk_y,
597 664 : (JDIMENSION) compptr->v_samp_factor, TRUE);
598 :
599 664 : int offset_y = 0;
600 664 : int nYBlocks = compptr->v_samp_factor;
601 852 : if( bIsTiled &&
602 : dst_blk_y + y_crop_blocks + compptr->v_samp_factor >
603 : nSrcHeightInBlocks)
604 : {
605 188 : nYBlocks = nSrcHeightInBlocks - (dst_blk_y + y_crop_blocks);
606 188 : if (nYBlocks > 0)
607 : {
608 : JBLOCKARRAY src_buffer = (*sDInfo.mem->access_virt_barray)
609 : ((j_common_ptr) &sDInfo, pSrcCoeffs[ci],
610 : dst_blk_y + y_crop_blocks,
611 4 : (JDIMENSION) 1, FALSE);
612 8 : for (; offset_y < nYBlocks; offset_y++)
613 : {
614 4 : memcpy(dst_buffer[offset_y],
615 4 : src_buffer[offset_y] + x_crop_blocks,
616 12 : nXBlocksToCopy * (DCTSIZE2 * sizeof(JCOEF)));
617 4 : if (nXBlocksToCopy < compptr->width_in_blocks)
618 : {
619 2 : memset(dst_buffer[offset_y] + nXBlocksToCopy, 0,
620 : (compptr->width_in_blocks - nXBlocksToCopy) *
621 4 : (DCTSIZE2 * sizeof(JCOEF)));
622 : }
623 : }
624 : }
625 :
626 436 : for (; offset_y < compptr->v_samp_factor; offset_y++)
627 : {
628 248 : memset(dst_buffer[offset_y], 0,
629 496 : compptr->width_in_blocks * (DCTSIZE2 * sizeof(JCOEF)));
630 : }
631 : }
632 : else
633 : {
634 : JBLOCKARRAY src_buffer = (*sDInfo.mem->access_virt_barray)
635 : ((j_common_ptr) &sDInfo, pSrcCoeffs[ci],
636 : dst_blk_y + y_crop_blocks,
637 476 : (JDIMENSION) compptr->v_samp_factor, FALSE);
638 1084 : for (; offset_y < compptr->v_samp_factor; offset_y++)
639 : {
640 608 : memcpy(dst_buffer[offset_y],
641 608 : src_buffer[offset_y] + x_crop_blocks,
642 1824 : nXBlocksToCopy * (DCTSIZE2 * sizeof(JCOEF)));
643 608 : if (nXBlocksToCopy < compptr->width_in_blocks)
644 : {
645 138 : memset(dst_buffer[offset_y] + nXBlocksToCopy, 0,
646 : (compptr->width_in_blocks - nXBlocksToCopy) *
647 276 : (DCTSIZE2 * sizeof(JCOEF)));
648 : }
649 : }
650 : }
651 : }
652 : }
653 :
654 76 : jpeg_finish_compress(&sCInfo);
655 76 : jpeg_destroy_compress(&sCInfo);
656 :
657 76 : VSIFCloseL(fpMEM);
658 :
659 : /* -------------------------------------------------------------------- */
660 : /* Write the JPEG content with libtiff raw API */
661 : /* -------------------------------------------------------------------- */
662 76 : vsi_l_offset nSize = 0;
663 76 : GByte* pabyJPEGData = VSIGetMemFileBuffer(osTmpFilename, &nSize, FALSE);
664 :
665 76 : CPLErr eErr = CE_None;
666 :
667 76 : if ( bIsTiled )
668 : {
669 16 : if ((vsi_l_offset)TIFFWriteRawTile(hTIFF, iX + iY * nXBlocks,
670 : pabyJPEGData, nSize) != nSize)
671 0 : eErr = CE_Failure;
672 : }
673 : else
674 : {
675 60 : if ((vsi_l_offset)TIFFWriteRawStrip(hTIFF, iX + iY * nXBlocks,
676 : pabyJPEGData, nSize) != nSize)
677 0 : eErr = CE_Failure;
678 : }
679 :
680 76 : VSIUnlink(osTmpFilename);
681 :
682 76 : return eErr;
683 : }
684 :
685 : /************************************************************************/
686 : /* GTIFF_CopyFromJPEG() */
687 : /************************************************************************/
688 :
689 16 : CPLErr GTIFF_CopyFromJPEG(GDALDataset* poDS, GDALDataset* poSrcDS,
690 : GDALProgressFunc pfnProgress, void * pProgressData,
691 : int& bShouldFallbackToNormalCopyIfFail)
692 : {
693 16 : bShouldFallbackToNormalCopyIfFail = TRUE;
694 :
695 16 : poSrcDS = GetUnderlyingDataset(poSrcDS);
696 16 : if (poSrcDS == NULL)
697 0 : return CE_Failure;
698 :
699 16 : VSILFILE* fpJPEG = VSIFOpenL(poSrcDS->GetDescription(), "rb");
700 16 : if (fpJPEG == NULL)
701 0 : return CE_Failure;
702 :
703 16 : CPLErr eErr = CE_None;
704 :
705 : /* -------------------------------------------------------------------- */
706 : /* Initialization of the decompressor */
707 : /* -------------------------------------------------------------------- */
708 : struct jpeg_error_mgr sJErr;
709 : struct jpeg_decompress_struct sDInfo;
710 : jmp_buf setjmp_buffer;
711 16 : if (setjmp(setjmp_buffer))
712 : {
713 0 : VSIFCloseL(fpJPEG);
714 0 : return CE_Failure;
715 : }
716 :
717 16 : sDInfo.err = jpeg_std_error( &sJErr );
718 16 : sJErr.error_exit = GTIFF_ErrorExitJPEG;
719 16 : sDInfo.client_data = (void *) &setjmp_buffer;
720 :
721 16 : jpeg_create_decompress(&sDInfo);
722 :
723 : /* This is to address bug related in ticket #1795 */
724 16 : if (CPLGetConfigOption("JPEGMEM", NULL) == NULL)
725 : {
726 : /* If the user doesn't provide a value for JPEGMEM, we want to be sure */
727 : /* that at least 500 MB will be used before creating the temporary file */
728 : sDInfo.mem->max_memory_to_use =
729 16 : MAX(sDInfo.mem->max_memory_to_use, 500 * 1024 * 1024);
730 : }
731 :
732 16 : jpeg_vsiio_src( &sDInfo, fpJPEG );
733 16 : jpeg_read_header( &sDInfo, TRUE );
734 :
735 16 : jvirt_barray_ptr* pSrcCoeffs = jpeg_read_coefficients(&sDInfo);
736 :
737 : /* -------------------------------------------------------------------- */
738 : /* Compute MCU dimensions */
739 : /* -------------------------------------------------------------------- */
740 : int iMCU_sample_width, iMCU_sample_height;
741 16 : if (sDInfo.num_components == 1)
742 : {
743 8 : iMCU_sample_width = 8;
744 8 : iMCU_sample_height = 8;
745 : }
746 : else
747 : {
748 8 : iMCU_sample_width = sDInfo.max_h_samp_factor * 8;
749 8 : iMCU_sample_height = sDInfo.max_v_samp_factor * 8;
750 : }
751 :
752 : /* -------------------------------------------------------------------- */
753 : /* Get raster and block dimensions */
754 : /* -------------------------------------------------------------------- */
755 : int nXSize, nYSize, nBands;
756 : int nBlockXSize, nBlockYSize;
757 :
758 16 : nXSize = poDS->GetRasterXSize();
759 16 : nYSize = poDS->GetRasterYSize();
760 16 : nBands = poDS->GetRasterCount();
761 :
762 : /* We don't use the GDAL block dimensions because of the split-band */
763 : /* mechanism that can expose a pseudo one-line-strip whereas the */
764 : /* real layout is a single big strip */
765 :
766 16 : TIFF* hTIFF = (TIFF*) poDS->GetInternalHandle(NULL);
767 16 : if( TIFFIsTiled(hTIFF) )
768 : {
769 4 : TIFFGetField( hTIFF, TIFFTAG_TILEWIDTH, &(nBlockXSize) );
770 4 : TIFFGetField( hTIFF, TIFFTAG_TILELENGTH, &(nBlockYSize) );
771 : }
772 : else
773 : {
774 : uint32 nRowsPerStrip;
775 12 : if( !TIFFGetField( hTIFF, TIFFTAG_ROWSPERSTRIP,
776 : &(nRowsPerStrip) ) )
777 : {
778 : CPLError( CE_Warning, CPLE_AppDefined,
779 0 : "RowsPerStrip not defined ... assuming all one strip." );
780 0 : nRowsPerStrip = nYSize; /* dummy value */
781 : }
782 :
783 : // If the rows per strip is larger than the file we will get
784 : // confused. libtiff internally will treat the rowsperstrip as
785 : // the image height and it is best if we do too. (#4468)
786 12 : if (nRowsPerStrip > (uint32)nYSize)
787 0 : nRowsPerStrip = nYSize;
788 :
789 12 : nBlockXSize = nXSize;
790 12 : nBlockYSize = nRowsPerStrip;
791 : }
792 :
793 16 : int nXBlocks = (nXSize + nBlockXSize - 1) / nBlockXSize;
794 16 : int nYBlocks = (nYSize + nBlockYSize - 1) / nBlockYSize;
795 :
796 : /* -------------------------------------------------------------------- */
797 : /* Copy blocks. */
798 : /* -------------------------------------------------------------------- */
799 :
800 16 : bShouldFallbackToNormalCopyIfFail = FALSE;
801 :
802 84 : for(int iY=0;iY<nYBlocks && eErr == CE_None;iY++)
803 : {
804 144 : for(int iX=0;iX<nXBlocks && eErr == CE_None;iX++)
805 : {
806 : eErr = GTIFF_CopyBlockFromJPEG( hTIFF,
807 : sDInfo,
808 : iX, iY,
809 : nXBlocks, nYBlocks,
810 : nXSize, nYSize,
811 : nBlockXSize, nBlockYSize,
812 : iMCU_sample_width,
813 : iMCU_sample_height,
814 76 : pSrcCoeffs );
815 :
816 76 : if (!pfnProgress((iY * nXBlocks + iX + 1) * 1.0 /
817 : (nXBlocks * nYBlocks),
818 : NULL, pProgressData ) )
819 0 : eErr = CE_Failure;
820 : }
821 : }
822 :
823 : /* -------------------------------------------------------------------- */
824 : /* Cleanup. */
825 : /* -------------------------------------------------------------------- */
826 :
827 16 : jpeg_finish_decompress( &sDInfo );
828 16 : jpeg_destroy_decompress( &sDInfo );
829 :
830 16 : VSIFCloseL(fpJPEG);
831 :
832 16 : return eErr;
833 : }
834 :
835 : #endif // HAVE_LIBJPEG
|