EVOLUTION-MANAGER
Edit File: gt_jpeg_copy.cpp
/****************************************************************************** * $Id: gt_jpeg_copy.cpp 27899 2014-10-23 13:36:59Z rouault $ * * Project: GeoTIFF Driver * Purpose: Specialized copy of JPEG content into TIFF. * Author: Even Rouault, <even dot rouault at mines dash paris dot org> * ****************************************************************************** * Copyright (c) 2012, Even Rouault <even dot rouault at mines-paris dot org> * * 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 "cpl_vsi.h" #include "gt_jpeg_copy.h" /* Note: JPEG_DIRECT_COPY is not defined by default, because it is mainly */ /* usefull for debugging purposes */ CPL_CVSID("$Id: gt_jpeg_copy.cpp 27899 2014-10-23 13:36:59Z rouault $"); #if defined(JPEG_DIRECT_COPY) || defined(HAVE_LIBJPEG) #include "vrt/vrtdataset.h" /************************************************************************/ /* GetUnderlyingDataset() */ /************************************************************************/ static GDALDataset* GetUnderlyingDataset(GDALDataset* poSrcDS) { /* Test if we can directly copy original JPEG content */ /* if available */ if (poSrcDS->GetDriver() != NULL && poSrcDS->GetDriver() == GDALGetDriverByName("VRT")) { VRTDataset* poVRTDS = (VRTDataset* )poSrcDS; poSrcDS = poVRTDS->GetSingleSimpleSource(); } return poSrcDS; } #endif // defined(JPEG_DIRECT_COPY) || defined(HAVE_LIBJPEG) #ifdef JPEG_DIRECT_COPY /************************************************************************/ /* IsBaselineDCTJPEG() */ /************************************************************************/ static int IsBaselineDCTJPEG(VSILFILE* fp) { GByte abyBuf[4]; if (VSIFReadL(abyBuf, 1, 2, fp) != 2 || abyBuf[0] != 0xff || abyBuf[1] != 0xd8 ) { CPLError(CE_Failure, CPLE_AppDefined, "Not a valid JPEG file"); return FALSE; } int nOffset = 2; while(TRUE) { VSIFSeekL(fp, nOffset, SEEK_SET); if (VSIFReadL(abyBuf, 1, 4, fp) != 4 || abyBuf[0] != 0xFF) { CPLError(CE_Failure, CPLE_AppDefined, "Not a valid JPEG file"); return FALSE; } int nMarker = abyBuf[1]; if (nMarker == 0xC0 /* Start of Frame 0 = Baseline DCT */) return TRUE; if (nMarker == 0xD9) return FALSE; if (nMarker == 0xF7 /* JPEG Extension 7, JPEG-LS */ || nMarker == 0xF8 /* JPEG Extension 8, JPEG-LS Extension */ || (nMarker >= 0xC1 && nMarker <= 0xCF) /* Other Start of Frames that we don't want to support */) { CPLError(CE_Failure, CPLE_AppDefined, "Unsupported type of JPEG file for JPEG_DIRECT_COPY mode"); return FALSE; } nOffset += 2 + abyBuf[2] * 256 + abyBuf[3]; } } /************************************************************************/ /* GTIFF_CanDirectCopyFromJPEG() */ /************************************************************************/ int GTIFF_CanDirectCopyFromJPEG(GDALDataset* poSrcDS, char** &papszCreateOptions) { poSrcDS = GetUnderlyingDataset(poSrcDS); if (poSrcDS == NULL) return FALSE; if (poSrcDS->GetDriver() == NULL) return FALSE; if (!EQUAL(GDALGetDriverShortName(poSrcDS->GetDriver()), "JPEG")) return FALSE; const char* pszCompress = CSLFetchNameValue(papszCreateOptions, "COMPRESS"); if (pszCompress != NULL && !EQUAL(pszCompress, "JPEG")) return FALSE; const char* pszSrcColorSpace = poSrcDS->GetMetadataItem("SOURCE_COLOR_SPACE", "IMAGE_STRUCTURE"); if (pszSrcColorSpace != NULL && (EQUAL(pszSrcColorSpace, "CMYK") || EQUAL(pszSrcColorSpace, "YCbCrK"))) return FALSE; int bJPEGDirectCopy = FALSE; VSILFILE* fpJPEG = VSIFOpenL(poSrcDS->GetDescription(), "rb"); if (fpJPEG && IsBaselineDCTJPEG(fpJPEG)) { bJPEGDirectCopy = TRUE; if (pszCompress == NULL) papszCreateOptions = CSLSetNameValue(papszCreateOptions, "COMPRESS", "JPEG"); papszCreateOptions = CSLSetNameValue(papszCreateOptions, "BLOCKXSIZE", NULL); papszCreateOptions = CSLSetNameValue(papszCreateOptions, "BLOCKYSIZE", CPLSPrintf("%d", poSrcDS->GetRasterYSize())); if (pszSrcColorSpace != NULL && EQUAL(pszSrcColorSpace, "YCbCr")) papszCreateOptions = CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", "YCBCR"); else papszCreateOptions = CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", NULL); if (poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte) papszCreateOptions = CSLSetNameValue(papszCreateOptions, "NBITS", "12"); else papszCreateOptions = CSLSetNameValue(papszCreateOptions, "NBITS", NULL); papszCreateOptions = CSLSetNameValue(papszCreateOptions, "TILED", NULL); papszCreateOptions = CSLSetNameValue(papszCreateOptions, "JPEG_QUALITY", NULL); } if (fpJPEG) VSIFCloseL(fpJPEG); return bJPEGDirectCopy; } /************************************************************************/ /* GTIFF_DirectCopyFromJPEG() */ /************************************************************************/ CPLErr GTIFF_DirectCopyFromJPEG(GDALDataset* poDS, GDALDataset* poSrcDS, GDALProgressFunc pfnProgress, void * pProgressData, int& bShouldFallbackToNormalCopyIfFail) { bShouldFallbackToNormalCopyIfFail = TRUE; poSrcDS = GetUnderlyingDataset(poSrcDS); if (poSrcDS == NULL) return CE_Failure; VSILFILE* fpJPEG = VSIFOpenL(poSrcDS->GetDescription(), "rb"); if (fpJPEG == NULL) return CE_Failure; CPLErr eErr = CE_None; VSIFSeekL(fpJPEG, 0, SEEK_END); tmsize_t nSize = (tmsize_t) VSIFTellL(fpJPEG); VSIFSeekL(fpJPEG, 0, SEEK_SET); void* pabyJPEGData = VSIMalloc(nSize); if (pabyJPEGData == NULL) { VSIFCloseL(fpJPEG); return CE_Failure; } if (pabyJPEGData != NULL && (tmsize_t)VSIFReadL(pabyJPEGData, 1, nSize, fpJPEG) == nSize) { bShouldFallbackToNormalCopyIfFail = FALSE; TIFF* hTIFF = (TIFF*) poDS->GetInternalHandle(NULL); if (TIFFWriteRawStrip(hTIFF, 0, pabyJPEGData, nSize) != nSize) eErr = CE_Failure; if( !pfnProgress( 1.0, NULL, pProgressData ) ) eErr = CE_Failure; } else { eErr = CE_Failure; } VSIFree(pabyJPEGData); VSIFCloseL(fpJPEG); return eErr; } #endif // JPEG_DIRECT_COPY #ifdef HAVE_LIBJPEG #include "vsidataio.h" #include <setjmp.h> /* * We are using width_in_blocks which is supposed to be private to * libjpeg. Unfortunately, the libjpeg delivered with Cygwin has * renamed this member to width_in_data_units. Since the header has * also renamed a define, use that unique define name in order to * detect the problem header and adjust to suit. */ #if defined(D_MAX_DATA_UNITS_IN_MCU) #define width_in_blocks width_in_data_units #endif /************************************************************************/ /* GTIFF_CanCopyFromJPEG() */ /************************************************************************/ int GTIFF_CanCopyFromJPEG(GDALDataset* poSrcDS, char** &papszCreateOptions) { poSrcDS = GetUnderlyingDataset(poSrcDS); if (poSrcDS == NULL) return FALSE; if (poSrcDS->GetDriver() == NULL) return FALSE; if (!EQUAL(GDALGetDriverShortName(poSrcDS->GetDriver()), "JPEG")) return FALSE; const char* pszCompress = CSLFetchNameValue(papszCreateOptions, "COMPRESS"); if (pszCompress == NULL || !EQUAL(pszCompress, "JPEG")) return FALSE; int nBlockXSize = atoi(CSLFetchNameValueDef(papszCreateOptions, "BLOCKXSIZE", "0")); int nBlockYSize = atoi(CSLFetchNameValueDef(papszCreateOptions, "BLOCKYSIZE", "0")); int nMCUSize = 8; const char* pszSrcColorSpace = poSrcDS->GetMetadataItem("SOURCE_COLOR_SPACE", "IMAGE_STRUCTURE"); if (pszSrcColorSpace != NULL && EQUAL(pszSrcColorSpace, "YCbCr")) nMCUSize = 16; int nXSize = poSrcDS->GetRasterXSize(); int nYSize = poSrcDS->GetRasterYSize(); int nBands = poSrcDS->GetRasterCount(); const char* pszPhotometric = CSLFetchNameValue(papszCreateOptions, "PHOTOMETRIC"); int bCompatiblePhotometric = ( pszPhotometric == NULL || (nMCUSize == 16 && EQUAL(pszPhotometric, "YCbCr")) || (nMCUSize == 8 && nBands == 4 && poSrcDS->GetRasterBand(1)->GetColorInterpretation() == GCI_CyanBand && poSrcDS->GetRasterBand(2)->GetColorInterpretation() == GCI_MagentaBand && poSrcDS->GetRasterBand(3)->GetColorInterpretation() == GCI_YellowBand && poSrcDS->GetRasterBand(4)->GetColorInterpretation() == GCI_BlackBand) || (nMCUSize == 8 && EQUAL(pszPhotometric, "RGB") && nBands == 3) || (nMCUSize == 8 && EQUAL(pszPhotometric, "MINISBLACK") && nBands == 1) ); if (!bCompatiblePhotometric) return FALSE; if ( nBands == 4 && pszPhotometric == NULL && poSrcDS->GetRasterBand(1)->GetColorInterpretation() == GCI_CyanBand && poSrcDS->GetRasterBand(2)->GetColorInterpretation() == GCI_MagentaBand && poSrcDS->GetRasterBand(3)->GetColorInterpretation() == GCI_YellowBand && poSrcDS->GetRasterBand(4)->GetColorInterpretation() == GCI_BlackBand ) { papszCreateOptions = CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", "CMYK"); } const char* pszInterleave = CSLFetchNameValue(papszCreateOptions, "INTERLEAVE"); int bCompatibleInterleave = ( pszInterleave == NULL || (nBands > 1 && EQUAL(pszInterleave, "PIXEL")) || nBands == 1 ); if( !bCompatibleInterleave ) return FALSE; if ( (nBlockXSize == nXSize || (nBlockXSize % nMCUSize) == 0) && (nBlockYSize == nYSize || (nBlockYSize % nMCUSize) == 0) && poSrcDS->GetRasterBand(1)->GetRasterDataType() == GDT_Byte && CSLFetchNameValue(papszCreateOptions, "NBITS") == NULL && CSLFetchNameValue(papszCreateOptions, "JPEG_QUALITY") == NULL ) { if (nMCUSize == 16 && pszPhotometric == NULL) papszCreateOptions = CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", "YCBCR"); return TRUE; } else { return FALSE; } } /************************************************************************/ /* GTIFF_ErrorExitJPEG() */ /************************************************************************/ static void GTIFF_ErrorExitJPEG(j_common_ptr cinfo) { jmp_buf *setjmp_buffer = (jmp_buf *) cinfo->client_data; char buffer[JMSG_LENGTH_MAX]; /* Create the message */ (*cinfo->err->format_message) (cinfo, buffer); CPLError( CE_Failure, CPLE_AppDefined, "libjpeg: %s", buffer ); /* Return control to the setjmp point */ longjmp(*setjmp_buffer, 1); } /************************************************************************/ /* GTIFF_Set_TIFFTAG_JPEGTABLES() */ /************************************************************************/ void GTIFF_Set_TIFFTAG_JPEGTABLES(TIFF* hTIFF, jpeg_decompress_struct& sDInfo, jpeg_compress_struct& sCInfo) { char szTmpFilename[128]; sprintf(szTmpFilename, "/vsimem/tables_%p", &sDInfo); VSILFILE* fpTABLES = VSIFOpenL(szTmpFilename, "wb+"); jpeg_vsiio_dest( &sCInfo, fpTABLES ); jpeg_write_tables( &sCInfo ); VSIFCloseL(fpTABLES); vsi_l_offset nSizeTables = 0; GByte* pabyJPEGTablesData = VSIGetMemFileBuffer(szTmpFilename, &nSizeTables, FALSE); TIFFSetField(hTIFF, TIFFTAG_JPEGTABLES, (int)nSizeTables, pabyJPEGTablesData); VSIUnlink(szTmpFilename); } /************************************************************************/ /* GTIFF_CopyFromJPEG_WriteAdditionalTags() */ /************************************************************************/ CPLErr GTIFF_CopyFromJPEG_WriteAdditionalTags(TIFF* hTIFF, GDALDataset* poSrcDS) { poSrcDS = GetUnderlyingDataset(poSrcDS); if (poSrcDS == NULL) return CE_Failure; /* -------------------------------------------------------------------- */ /* Write TIFFTAG_JPEGTABLES */ /* -------------------------------------------------------------------- */ VSILFILE* fpJPEG = VSIFOpenL(poSrcDS->GetDescription(), "rb"); if (fpJPEG == NULL) return CE_Failure; struct jpeg_error_mgr sJErr; struct jpeg_decompress_struct sDInfo; jmp_buf setjmp_buffer; if (setjmp(setjmp_buffer)) { VSIFCloseL(fpJPEG); return CE_Failure; } sDInfo.err = jpeg_std_error( &sJErr ); sJErr.error_exit = GTIFF_ErrorExitJPEG; sDInfo.client_data = (void *) &setjmp_buffer; jpeg_create_decompress(&sDInfo); jpeg_vsiio_src( &sDInfo, fpJPEG ); jpeg_read_header( &sDInfo, TRUE ); struct jpeg_compress_struct sCInfo; sCInfo.err = jpeg_std_error( &sJErr ); sJErr.error_exit = GTIFF_ErrorExitJPEG; sCInfo.client_data = (void *) &setjmp_buffer; jpeg_create_compress(&sCInfo); jpeg_copy_critical_parameters(&sDInfo, &sCInfo); GTIFF_Set_TIFFTAG_JPEGTABLES(hTIFF, sDInfo, sCInfo); jpeg_abort_compress(&sCInfo); jpeg_destroy_compress(&sCInfo); /* -------------------------------------------------------------------- */ /* Write TIFFTAG_REFERENCEBLACKWHITE if needed. */ /* -------------------------------------------------------------------- */ uint16 nPhotometric; if( !TIFFGetField( hTIFF, TIFFTAG_PHOTOMETRIC, &(nPhotometric) ) ) nPhotometric = PHOTOMETRIC_MINISBLACK; uint16 nBitsPerSample; if( !TIFFGetField(hTIFF, TIFFTAG_BITSPERSAMPLE, &(nBitsPerSample)) ) nBitsPerSample = 1; if ( nPhotometric == PHOTOMETRIC_YCBCR ) { /* * A ReferenceBlackWhite field *must* be present since the * default value is inappropriate for YCbCr. Fill in the * proper value if application didn't set it. */ float *ref; if (!TIFFGetField(hTIFF, TIFFTAG_REFERENCEBLACKWHITE, &ref)) { float refbw[6]; long top = 1L << nBitsPerSample; refbw[0] = 0; refbw[1] = (float)(top-1L); refbw[2] = (float)(top>>1); refbw[3] = refbw[1]; refbw[4] = refbw[2]; refbw[5] = refbw[1]; TIFFSetField(hTIFF, TIFFTAG_REFERENCEBLACKWHITE, refbw); } } /* -------------------------------------------------------------------- */ /* Write TIFFTAG_YCBCRSUBSAMPLING if needed. */ /* -------------------------------------------------------------------- */ if ( nPhotometric == PHOTOMETRIC_YCBCR && sDInfo.num_components == 3 ) { if ((sDInfo.comp_info[0].h_samp_factor == 1 || sDInfo.comp_info[0].h_samp_factor == 2) && (sDInfo.comp_info[0].v_samp_factor == 1 || sDInfo.comp_info[0].v_samp_factor == 2) && sDInfo.comp_info[1].h_samp_factor == 1 && sDInfo.comp_info[1].v_samp_factor == 1 && sDInfo.comp_info[2].h_samp_factor == 1 && sDInfo.comp_info[2].v_samp_factor == 1) { TIFFSetField(hTIFF, TIFFTAG_YCBCRSUBSAMPLING, sDInfo.comp_info[0].h_samp_factor, sDInfo.comp_info[0].v_samp_factor); } else { CPLDebug("GTiff", "Unusual sampling factors. TIFFTAG_YCBCRSUBSAMPLING not written."); } } /* -------------------------------------------------------------------- */ /* Cleanup. */ /* -------------------------------------------------------------------- */ jpeg_abort_decompress( &sDInfo ); jpeg_destroy_decompress( &sDInfo ); VSIFCloseL(fpJPEG); return CE_None; } /************************************************************************/ /* GTIFF_CopyBlockFromJPEG() */ /************************************************************************/ static CPLErr GTIFF_CopyBlockFromJPEG(TIFF* hTIFF, jpeg_decompress_struct& sDInfo, int iX, int iY, int nXBlocks, CPL_UNUSED int nYBlocks, int nXSize, int nYSize, int nBlockXSize, int nBlockYSize, int iMCU_sample_width, int iMCU_sample_height, jvirt_barray_ptr *pSrcCoeffs) { CPLString osTmpFilename(CPLSPrintf("/vsimem/%p", &sDInfo)); VSILFILE* fpMEM = VSIFOpenL(osTmpFilename, "wb+"); /* -------------------------------------------------------------------- */ /* Initialization of the compressor */ /* -------------------------------------------------------------------- */ struct jpeg_error_mgr sJErr; struct jpeg_compress_struct sCInfo; jmp_buf setjmp_buffer; if (setjmp(setjmp_buffer)) { VSIFCloseL(fpMEM); VSIUnlink(osTmpFilename); return CE_Failure; } sCInfo.err = jpeg_std_error( &sJErr ); sJErr.error_exit = GTIFF_ErrorExitJPEG; sCInfo.client_data = (void *) &setjmp_buffer; /* Initialize destination compression parameters from source values */ jpeg_create_compress(&sCInfo); jpeg_copy_critical_parameters(&sDInfo, &sCInfo); /* ensure libjpeg won't write any extraneous markers */ sCInfo.write_JFIF_header = FALSE; sCInfo.write_Adobe_marker = FALSE; /* -------------------------------------------------------------------- */ /* Allocated destination coefficient array */ /* -------------------------------------------------------------------- */ int bIsTiled = TIFFIsTiled(hTIFF); int nJPEGWidth, nJPEGHeight; if (bIsTiled) { nJPEGWidth = nBlockXSize; nJPEGHeight = nBlockYSize; } else { nJPEGWidth = MIN(nBlockXSize, nXSize - iX * nBlockXSize); nJPEGHeight = MIN(nBlockYSize, nYSize - iY * nBlockYSize); } /* Code partially derived from libjpeg transupp.c */ /* Correct the destination's image dimensions as necessary */ #if JPEG_LIB_VERSION >= 70 sCInfo.jpeg_width = nJPEGWidth; sCInfo.jpeg_height = nJPEGHeight; #else sCInfo.image_width = nJPEGWidth; sCInfo.image_height = nJPEGHeight; #endif /* Save x/y offsets measured in iMCUs */ int x_crop_offset = (iX * nBlockXSize) / iMCU_sample_width; int y_crop_offset = (iY * nBlockYSize) / iMCU_sample_height; jvirt_barray_ptr* pDstCoeffs = (jvirt_barray_ptr *) (*sCInfo.mem->alloc_small) ((j_common_ptr) &sCInfo, JPOOL_IMAGE, sizeof(jvirt_barray_ptr) * sCInfo.num_components); int ci; for (ci = 0; ci < sCInfo.num_components; ci++) { jpeg_component_info *compptr = sCInfo.comp_info + ci; int h_samp_factor, v_samp_factor; if (sCInfo.num_components == 1) { /* we're going to force samp factors to 1x1 in this case */ h_samp_factor = v_samp_factor = 1; } else { h_samp_factor = compptr->h_samp_factor; v_samp_factor = compptr->v_samp_factor; } int width_in_iMCUs = (nJPEGWidth + iMCU_sample_width - 1) / iMCU_sample_width; int height_in_iMCUs = (nJPEGHeight + iMCU_sample_height - 1) / iMCU_sample_height; int nWidth_in_blocks = width_in_iMCUs * h_samp_factor; int nHeight_in_blocks = height_in_iMCUs * v_samp_factor; pDstCoeffs[ci] = (*sCInfo.mem->request_virt_barray) ((j_common_ptr) &sCInfo, JPOOL_IMAGE, FALSE, nWidth_in_blocks, nHeight_in_blocks, (JDIMENSION) v_samp_factor); } jpeg_vsiio_dest( &sCInfo, fpMEM ); /* Start compressor (note no image data is actually written here) */ jpeg_write_coefficients(&sCInfo, pDstCoeffs); jpeg_suppress_tables( &sCInfo, TRUE ); /* We simply have to copy the right amount of data (the destination's * image size) starting at the given X and Y offsets in the source. */ for (ci = 0; ci < sCInfo.num_components; ci++) { jpeg_component_info *compptr = sCInfo.comp_info + ci; int x_crop_blocks = x_crop_offset * compptr->h_samp_factor; int y_crop_blocks = y_crop_offset * compptr->v_samp_factor; JDIMENSION nSrcWidthInBlocks = sDInfo.comp_info[ci].width_in_blocks; JDIMENSION nSrcHeightInBlocks = sDInfo.comp_info[ci].height_in_blocks; JDIMENSION nXBlocksToCopy = compptr->width_in_blocks; if (x_crop_blocks + compptr->width_in_blocks > nSrcWidthInBlocks) nXBlocksToCopy = nSrcWidthInBlocks - x_crop_blocks; for (JDIMENSION dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; dst_blk_y += compptr->v_samp_factor) { JBLOCKARRAY dst_buffer = (*sDInfo.mem->access_virt_barray) ((j_common_ptr) &sDInfo, pDstCoeffs[ci], dst_blk_y, (JDIMENSION) compptr->v_samp_factor, TRUE); int offset_y = 0; int nYBlocks = compptr->v_samp_factor; if( bIsTiled && dst_blk_y + y_crop_blocks + compptr->v_samp_factor > nSrcHeightInBlocks) { nYBlocks = nSrcHeightInBlocks - (dst_blk_y + y_crop_blocks); if (nYBlocks > 0) { JBLOCKARRAY src_buffer = (*sDInfo.mem->access_virt_barray) ((j_common_ptr) &sDInfo, pSrcCoeffs[ci], dst_blk_y + y_crop_blocks, (JDIMENSION) 1, FALSE); for (; offset_y < nYBlocks; offset_y++) { memcpy(dst_buffer[offset_y], src_buffer[offset_y] + x_crop_blocks, nXBlocksToCopy * (DCTSIZE2 * sizeof(JCOEF))); if (nXBlocksToCopy < compptr->width_in_blocks) { memset(dst_buffer[offset_y] + nXBlocksToCopy, 0, (compptr->width_in_blocks - nXBlocksToCopy) * (DCTSIZE2 * sizeof(JCOEF))); } } } for (; offset_y < compptr->v_samp_factor; offset_y++) { memset(dst_buffer[offset_y], 0, compptr->width_in_blocks * (DCTSIZE2 * sizeof(JCOEF))); } } else { JBLOCKARRAY src_buffer = (*sDInfo.mem->access_virt_barray) ((j_common_ptr) &sDInfo, pSrcCoeffs[ci], dst_blk_y + y_crop_blocks, (JDIMENSION) compptr->v_samp_factor, FALSE); for (; offset_y < compptr->v_samp_factor; offset_y++) { memcpy(dst_buffer[offset_y], src_buffer[offset_y] + x_crop_blocks, nXBlocksToCopy * (DCTSIZE2 * sizeof(JCOEF))); if (nXBlocksToCopy < compptr->width_in_blocks) { memset(dst_buffer[offset_y] + nXBlocksToCopy, 0, (compptr->width_in_blocks - nXBlocksToCopy) * (DCTSIZE2 * sizeof(JCOEF))); } } } } } jpeg_finish_compress(&sCInfo); jpeg_destroy_compress(&sCInfo); VSIFCloseL(fpMEM); /* -------------------------------------------------------------------- */ /* Write the JPEG content with libtiff raw API */ /* -------------------------------------------------------------------- */ vsi_l_offset nSize = 0; GByte* pabyJPEGData = VSIGetMemFileBuffer(osTmpFilename, &nSize, FALSE); CPLErr eErr = CE_None; if ( bIsTiled ) { if ((vsi_l_offset)TIFFWriteRawTile(hTIFF, iX + iY * nXBlocks, pabyJPEGData, nSize) != nSize) eErr = CE_Failure; } else { if ((vsi_l_offset)TIFFWriteRawStrip(hTIFF, iX + iY * nXBlocks, pabyJPEGData, nSize) != nSize) eErr = CE_Failure; } VSIUnlink(osTmpFilename); return eErr; } /************************************************************************/ /* GTIFF_CopyFromJPEG() */ /************************************************************************/ CPLErr GTIFF_CopyFromJPEG(GDALDataset* poDS, GDALDataset* poSrcDS, GDALProgressFunc pfnProgress, void * pProgressData, int& bShouldFallbackToNormalCopyIfFail) { bShouldFallbackToNormalCopyIfFail = TRUE; poSrcDS = GetUnderlyingDataset(poSrcDS); if (poSrcDS == NULL) return CE_Failure; VSILFILE* fpJPEG = VSIFOpenL(poSrcDS->GetDescription(), "rb"); if (fpJPEG == NULL) return CE_Failure; CPLErr eErr = CE_None; /* -------------------------------------------------------------------- */ /* Initialization of the decompressor */ /* -------------------------------------------------------------------- */ struct jpeg_error_mgr sJErr; struct jpeg_decompress_struct sDInfo; jmp_buf setjmp_buffer; if (setjmp(setjmp_buffer)) { VSIFCloseL(fpJPEG); return CE_Failure; } sDInfo.err = jpeg_std_error( &sJErr ); sJErr.error_exit = GTIFF_ErrorExitJPEG; sDInfo.client_data = (void *) &setjmp_buffer; jpeg_create_decompress(&sDInfo); /* This is to address bug related in ticket #1795 */ if (CPLGetConfigOption("JPEGMEM", NULL) == NULL) { /* If the user doesn't provide a value for JPEGMEM, we want to be sure */ /* that at least 500 MB will be used before creating the temporary file */ sDInfo.mem->max_memory_to_use = MAX(sDInfo.mem->max_memory_to_use, 500 * 1024 * 1024); } jpeg_vsiio_src( &sDInfo, fpJPEG ); jpeg_read_header( &sDInfo, TRUE ); jvirt_barray_ptr* pSrcCoeffs = jpeg_read_coefficients(&sDInfo); /* -------------------------------------------------------------------- */ /* Compute MCU dimensions */ /* -------------------------------------------------------------------- */ int iMCU_sample_width, iMCU_sample_height; if (sDInfo.num_components == 1) { iMCU_sample_width = 8; iMCU_sample_height = 8; } else { iMCU_sample_width = sDInfo.max_h_samp_factor * 8; iMCU_sample_height = sDInfo.max_v_samp_factor * 8; } /* -------------------------------------------------------------------- */ /* Get raster and block dimensions */ /* -------------------------------------------------------------------- */ int nXSize, nYSize /* , nBands */; int nBlockXSize, nBlockYSize; nXSize = poDS->GetRasterXSize(); nYSize = poDS->GetRasterYSize(); /* nBands = poDS->GetRasterCount(); */ /* We don't use the GDAL block dimensions because of the split-band */ /* mechanism that can expose a pseudo one-line-strip whereas the */ /* real layout is a single big strip */ TIFF* hTIFF = (TIFF*) poDS->GetInternalHandle(NULL); if( TIFFIsTiled(hTIFF) ) { TIFFGetField( hTIFF, TIFFTAG_TILEWIDTH, &(nBlockXSize) ); TIFFGetField( hTIFF, TIFFTAG_TILELENGTH, &(nBlockYSize) ); } else { uint32 nRowsPerStrip; if( !TIFFGetField( hTIFF, TIFFTAG_ROWSPERSTRIP, &(nRowsPerStrip) ) ) { CPLError( CE_Warning, CPLE_AppDefined, "RowsPerStrip not defined ... assuming all one strip." ); nRowsPerStrip = nYSize; /* dummy value */ } // If the rows per strip is larger than the file we will get // confused. libtiff internally will treat the rowsperstrip as // the image height and it is best if we do too. (#4468) if (nRowsPerStrip > (uint32)nYSize) nRowsPerStrip = nYSize; nBlockXSize = nXSize; nBlockYSize = nRowsPerStrip; } int nXBlocks = (nXSize + nBlockXSize - 1) / nBlockXSize; int nYBlocks = (nYSize + nBlockYSize - 1) / nBlockYSize; /* -------------------------------------------------------------------- */ /* Copy blocks. */ /* -------------------------------------------------------------------- */ bShouldFallbackToNormalCopyIfFail = FALSE; for(int iY=0;iY<nYBlocks && eErr == CE_None;iY++) { for(int iX=0;iX<nXBlocks && eErr == CE_None;iX++) { eErr = GTIFF_CopyBlockFromJPEG( hTIFF, sDInfo, iX, iY, nXBlocks, nYBlocks, nXSize, nYSize, nBlockXSize, nBlockYSize, iMCU_sample_width, iMCU_sample_height, pSrcCoeffs ); if (!pfnProgress((iY * nXBlocks + iX + 1) * 1.0 / (nXBlocks * nYBlocks), NULL, pProgressData ) ) eErr = CE_Failure; } } /* -------------------------------------------------------------------- */ /* Cleanup. */ /* -------------------------------------------------------------------- */ jpeg_finish_decompress( &sDInfo ); jpeg_destroy_decompress( &sDInfo ); VSIFCloseL(fpJPEG); return eErr; } #endif // HAVE_LIBJPEG