1 /******************************************************************************
2  *
3  * Project:  jpip read driver
4  * Purpose:  GDAL bindings for JPIP.
5  * Author:   Norman Barker, ITT VIS, norman.barker@gmail.com
6  *
7  ******************************************************************************
8  * ITT Visual Information Systems grants you use of this code, under the
9  * following license:
10  *
11  * Copyright (c) 2000-2007, ITT Visual Information Solutions
12  *
13  * Permission is hereby granted, free of charge, to any person obtaining a
14  * copy of this software and associated documentation files (the "Software"),
15  * to deal in the Software without restriction, including without limitation
16  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17  * and/or sell copies of the Software, and to permit persons to whom the
18  * Software is furnished to do so, subject to the following conditions:
19  * The above copyright notice and this permission notice shall be included
20  * in all copies or substantial portions of the Software.
21 
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29 **/
30 
31 #ifdef DEBUG_BOOL
32 #define DO_NOT_USE_DEBUG_BOOL
33 #endif
34 
35 #include "gdal_frmts.h"
36 #include "jpipkakdataset.h"
37 
38 CPL_CVSID("$Id: jpipkakdataset.cpp cee3d5d3864c1d7f493306700a19ef22f0fecbee 2021-03-08 22:53:56 +0100 Even Rouault $")
39 
40 /*
41 ** The following are for testing premature stream termination support.
42 ** This is a mechanism to test handling of failed or incomplete reads
43 ** from the server, and is not normally active.  For this reason we
44 ** don't worry about the non-threadsafe nature of the debug support
45 ** variables below.
46 */
47 
48 #ifdef DEBUG
49 #  define PST_DEBUG 1
50 #endif
51 
52 #ifdef PST_DEBUG
53 static int nPSTTargetInstance = -1;
54 static int nPSTThisInstance = -1;
55 static int nPSTTargetOffset = -1;
56 #endif
57 
58 static bool kakadu_initialized = false;
59 
60 /************************************************************************/
61 /* ==================================================================== */
62 /*                     Set up messaging services                        */
63 /* ==================================================================== */
64 /************************************************************************/
65 
66 class jpipkak_kdu_cpl_error_message : public kdu_message
67 {
68 public: // Member classes
69     using kdu_message::put_text;
70 
jpipkak_kdu_cpl_error_message(CPLErr eErrClass)71     explicit jpipkak_kdu_cpl_error_message( CPLErr eErrClass ) :
72         m_eErrClass ( eErrClass ),
73         m_pszError ( nullptr )
74     {}
75 
put_text(const char * string)76     void put_text(const char *string) override
77     {
78         if( m_pszError == nullptr )
79             m_pszError = CPLStrdup( string );
80         else
81         {
82             m_pszError = (char *)
83                 CPLRealloc(m_pszError, strlen(m_pszError) + strlen(string)+1 );
84             strcat( m_pszError, string );
85         }
86     }
87 
88     class JP2KAKException
89     {
90     };
91 
flush(bool end_of_message=false)92     void flush(bool end_of_message=false) override
93     {
94         if( m_pszError == nullptr )
95             return;
96         if( m_pszError[strlen(m_pszError)-1] == '\n' )
97             m_pszError[strlen(m_pszError)-1] = '\0';
98 
99         CPLError( m_eErrClass, CPLE_AppDefined, "%s", m_pszError );
100         CPLFree( m_pszError );
101         m_pszError = nullptr;
102 
103         if( end_of_message && m_eErrClass == CE_Failure )
104         {
105             throw new JP2KAKException();
106         }
107     }
108 
109 private:
110     CPLErr m_eErrClass;
111     char *m_pszError;
112 };
113 
114 /************************************************************************/
115 /* ==================================================================== */
116 /*                            JPIPKAKRasterBand                         */
117 /* ==================================================================== */
118 /************************************************************************/
119 
120 /************************************************************************/
121 /*                         JPIPKAKRasterBand()                          */
122 /************************************************************************/
123 
JPIPKAKRasterBand(int nBandIn,int nDiscardLevelsIn,kdu_codestream * oCodeStreamIn,int nResCount,JPIPKAKDataset * poBaseDSIn)124 JPIPKAKRasterBand::JPIPKAKRasterBand( int nBandIn, int nDiscardLevelsIn,
125                                       kdu_codestream *oCodeStreamIn,
126                                       int nResCount,
127                                       JPIPKAKDataset *poBaseDSIn )
128 
129 {
130     this->nBand = nBandIn;
131     poBaseDS = poBaseDSIn;
132 
133     eDataType = poBaseDSIn->eDT;
134 
135     this->nDiscardLevels = nDiscardLevelsIn;
136     this->oCodeStream = oCodeStreamIn;
137 
138     oCodeStream->apply_input_restrictions( 0, 0, nDiscardLevels, 0, nullptr );
139     oCodeStream->get_dims( 0, band_dims );
140 
141     nRasterXSize = band_dims.size.x;
142     nRasterYSize = band_dims.size.y;
143 
144 /* -------------------------------------------------------------------- */
145 /*      Use a 2048x128 "virtual" block size unless the file is small.    */
146 /* -------------------------------------------------------------------- */
147     if( nRasterXSize >= 2048 )
148         nBlockXSize = 2048;
149     else
150         nBlockXSize = nRasterXSize;
151 
152     if( nRasterYSize >= 256 )
153         nBlockYSize = 128;
154     else
155         nBlockYSize = nRasterYSize;
156 
157 /* -------------------------------------------------------------------- */
158 /*      Figure out the color interpretation for this band.              */
159 /* -------------------------------------------------------------------- */
160 
161     eInterp = GCI_Undefined;
162 
163 /* -------------------------------------------------------------------- */
164 /*      Do we have any overviews?  Only check if we are the full res    */
165 /*      image.                                                          */
166 /* -------------------------------------------------------------------- */
167     nOverviewCount = 0;
168     papoOverviewBand = nullptr;
169 
170     if( nDiscardLevels == 0 )
171     {
172         int nXSize = nRasterXSize;
173         int nYSize = nRasterYSize;
174 
175         for( int nDiscard = 1; nDiscard < nResCount; nDiscard++ )
176         {
177 
178             nXSize = (nXSize + 1) / 2;
179             nYSize = (nYSize + 1) / 2;
180 
181             if( (nXSize+nYSize) < 128 || nXSize < 4 || nYSize < 4 )
182                 continue; /* skip super reduced resolution layers */
183 
184             oCodeStream->apply_input_restrictions( 0, 0, nDiscard, 0, nullptr );
185             kdu_dims dims;
186             oCodeStream->get_dims( 0, dims );
187 
188             if( (dims.size.x == nXSize || dims.size.x == nXSize-1)
189                 && (dims.size.y == nYSize || dims.size.y == nYSize-1) )
190             {
191                 nOverviewCount++;
192                 papoOverviewBand = (JPIPKAKRasterBand **)
193                     CPLRealloc( papoOverviewBand,
194                                 sizeof(void*) * nOverviewCount );
195                 papoOverviewBand[nOverviewCount-1] =
196                     new JPIPKAKRasterBand( nBand, nDiscard, oCodeStream, 0,
197                                            poBaseDS );
198             }
199             else
200             {
201                 CPLDebug( "GDAL", "Discard %dx%d JPEG2000 overview layer,\n"
202                           "expected %dx%d.",
203                           dims.size.x, dims.size.y, nXSize, nYSize );
204             }
205         }
206     }
207 }
208 
209 /************************************************************************/
210 /*                         ~JPIPKAKRasterBand()                          */
211 /************************************************************************/
212 
~JPIPKAKRasterBand()213 JPIPKAKRasterBand::~JPIPKAKRasterBand()
214 
215 {
216     for( int i = 0; i < nOverviewCount; i++ )
217         delete papoOverviewBand[i];
218 
219     CPLFree( papoOverviewBand );
220 }
221 
222 /************************************************************************/
223 /*                          GetOverviewCount()                          */
224 /************************************************************************/
225 
GetOverviewCount()226 int JPIPKAKRasterBand::GetOverviewCount()
227 
228 {
229     return nOverviewCount;
230 }
231 
232 /************************************************************************/
233 /*                            GetOverview()                             */
234 /************************************************************************/
235 
GetOverview(int iOverviewIndex)236 GDALRasterBand *JPIPKAKRasterBand::GetOverview( int iOverviewIndex )
237 
238 {
239     if( iOverviewIndex < 0 || iOverviewIndex >= nOverviewCount )
240         return nullptr;
241     else
242         return papoOverviewBand[iOverviewIndex];
243 }
244 
245 /************************************************************************/
246 /*                             IReadBlock()                             */
247 /************************************************************************/
248 
IReadBlock(int nBlockXOff,int nBlockYOff,void * pImage)249 CPLErr JPIPKAKRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
250                                       void * pImage )
251 {
252     CPLDebug( "JPIPKAK", "IReadBlock(%d,%d) on band %d.",
253               nBlockXOff, nBlockYOff, nBand );
254 
255 /* -------------------------------------------------------------------- */
256 /*      Fix the buffer layer.                                           */
257 /* -------------------------------------------------------------------- */
258     int nPixelSpace = GDALGetDataTypeSize(eDataType) / 8;
259     int nLineSpace = nPixelSpace * nBlockXSize;
260     int nBandSpace = nLineSpace * nBlockYSize;
261 
262 /* -------------------------------------------------------------------- */
263 /*      Zoom up file window based on overview level so we are           */
264 /*      referring to the full res image.                                */
265 /* -------------------------------------------------------------------- */
266     int nZoom = 1 << nDiscardLevels;
267 
268     int xOff = nBlockXOff * nBlockXSize * nZoom;
269     int yOff = nBlockYOff * nBlockYSize * nZoom;
270     int xSize = nBlockXSize * nZoom;
271     int ySize = nBlockYSize * nZoom;
272 
273     int nBufXSize = nBlockXSize;
274     int nBufYSize = nBlockYSize;
275 
276 /* -------------------------------------------------------------------- */
277 /*      Make adjustments for partial blocks on right and bottom.        */
278 /* -------------------------------------------------------------------- */
279     if( xOff + xSize > poBaseDS->GetRasterXSize() )
280     {
281         xSize = poBaseDS->GetRasterXSize() - xOff;
282         nBufXSize= MAX(xSize/nZoom,1);
283     }
284 
285     if( yOff + ySize > poBaseDS->GetRasterYSize() )
286     {
287         ySize = poBaseDS->GetRasterYSize() - yOff;
288         nBufYSize = MAX(ySize/nZoom,1);
289     }
290 
291 /* -------------------------------------------------------------------- */
292 /*      Start the reader and run till complete.                         */
293 /* -------------------------------------------------------------------- */
294     GDALAsyncReader* ario = poBaseDS->
295         BeginAsyncReader(xOff, yOff, xSize, ySize,
296                          pImage, nBufXSize,nBufYSize,
297                          eDataType, 1, &nBand,
298                          nPixelSpace, nLineSpace, nBandSpace, nullptr);
299 
300     if( ario == nullptr )
301         return CE_Failure;
302 
303     int nXBufOff; // absolute x image offset
304     int nYBufOff; // absolute y image offset
305     int nXBufSize;
306     int nYBufSize;
307 
308     GDALAsyncStatusType status;
309 
310     do
311     {
312         status = ario->GetNextUpdatedRegion(-1.0,
313                                             &nXBufOff, &nYBufOff,
314                                             &nXBufSize, &nYBufSize );
315     } while (status != GARIO_ERROR && status != GARIO_COMPLETE );
316 
317     poBaseDS->EndAsyncReader(ario);
318 
319     if (status == GARIO_ERROR)
320         return CE_Failure;
321     else
322         return CE_None;
323 }
324 
325 /************************************************************************/
326 /*                             IRasterIO()                              */
327 /************************************************************************/
328 
329 CPLErr
IRasterIO(GDALRWFlag eRWFlag,int nXOff,int nYOff,int nXSize,int nYSize,void * pData,int nBufXSize,int nBufYSize,GDALDataType eBufType,GSpacing nPixelSpace,GSpacing nLineSpace,GDALRasterIOExtraArg * psExtraArg)330 JPIPKAKRasterBand::IRasterIO( GDALRWFlag eRWFlag,
331                               int nXOff, int nYOff, int nXSize, int nYSize,
332                               void * pData, int nBufXSize, int nBufYSize,
333                               GDALDataType eBufType,
334                               GSpacing nPixelSpace, GSpacing nLineSpace,
335                               GDALRasterIOExtraArg* psExtraArg)
336 
337 {
338 /* -------------------------------------------------------------------- */
339 /*      We need various criteria to skip out to block based methods.    */
340 /* -------------------------------------------------------------------- */
341     if( poBaseDS->TestUseBlockIO( nXOff, nYOff, nXSize, nYSize,
342                                   nBufXSize, nBufYSize,
343                                   eBufType, 1, &nBand ) )
344         return GDALPamRasterBand::IRasterIO(
345             eRWFlag, nXOff, nYOff, nXSize, nYSize,
346             pData, nBufXSize, nBufYSize, eBufType,
347             nPixelSpace, nLineSpace, psExtraArg );
348 
349 /* -------------------------------------------------------------------- */
350 /*      Otherwise do this as a single uncached async rasterio.          */
351 /* -------------------------------------------------------------------- */
352     GDALAsyncReader* ario =
353         poBaseDS->BeginAsyncReader(nXOff, nYOff, nXSize, nYSize,
354                                    pData, nBufXSize, nBufYSize, eBufType,
355                                    1, &nBand,
356                                    static_cast<int>(nPixelSpace),
357                                    static_cast<int>(nLineSpace), 0, nullptr);
358 
359     if( ario == nullptr )
360         return CE_Failure;
361 
362     GDALAsyncStatusType status;
363 
364     do
365     {
366         int nXBufOff,nYBufOff,nXBufSize,nYBufSize;
367 
368         status = ario->GetNextUpdatedRegion(-1.0,
369                                             &nXBufOff, &nYBufOff,
370                                             &nXBufSize, &nYBufSize );
371     } while (status != GARIO_ERROR && status != GARIO_COMPLETE );
372 
373     poBaseDS->EndAsyncReader(ario);
374 
375     if (status == GARIO_ERROR)
376         return CE_Failure;
377     else
378         return CE_None;
379 }
380 
381 /*****************************************/
382 /*         JPIPKAKDataset()              */
383 /*****************************************/
JPIPKAKDataset()384 JPIPKAKDataset::JPIPKAKDataset()
385 {
386     adfGeoTransform[0] = 0.0;
387     adfGeoTransform[1] = 1.0;
388     adfGeoTransform[2] = 0.0;
389     adfGeoTransform[3] = 0.0;
390     adfGeoTransform[4] = 0.0;
391     adfGeoTransform[5] = 1.0;
392 
393     pGlobalMutex = CPLCreateMutex();
394     CPLReleaseMutex(pGlobalMutex);
395 }
396 
397 /*****************************************/
398 /*         ~JPIPKAKDataset()             */
399 /*****************************************/
~JPIPKAKDataset()400 JPIPKAKDataset::~JPIPKAKDataset()
401 {
402     char** papszOptions = nullptr;
403     papszOptions = CSLSetNameValue(papszOptions,
404                         "CLOSE_PERSISTENT", CPLSPrintf("JPIPKAK:%p", this));
405     CPLHTTPDestroyResult(CPLHTTPFetch("", papszOptions));
406     CSLDestroy(papszOptions);
407 
408     Deinitialize();
409 
410     CPLFree(pszProjection);
411     pszProjection = nullptr;
412 
413     CPLFree(pszPath);
414 
415     if (nGCPCount > 0 )
416     {
417         GDALDeinitGCPs( nGCPCount, pasGCPList );
418         CPLFree( pasGCPList );
419     }
420 }
421 
422 /************************************************************************/
423 /*                            Deinitialize()                            */
424 /*                                                                      */
425 /*      Cleanup stuff that we will rebuild during a                     */
426 /*      reinitialization.                                               */
427 /************************************************************************/
428 
Deinitialize()429 void JPIPKAKDataset::Deinitialize()
430 
431 {
432     CPLFree(pszCid);
433     pszCid = nullptr;
434 
435     // frees decompressor as well
436     if (poCodestream)
437     {
438         poCodestream->destroy();
439         delete poCodestream;
440         poCodestream = nullptr;
441     }
442 
443     delete poDecompressor;
444     poDecompressor = nullptr;
445 
446     delete poCache;
447     poCache = nullptr;
448 
449     bNeedReinitialize = TRUE;
450 }
451 
452 /************************************************************************/
453 /*                          KakaduInitialize()                          */
454 /************************************************************************/
455 
KakaduInitialize()456 void JPIPKAKDataset::KakaduInitialize()
457 
458 {
459 /* -------------------------------------------------------------------- */
460 /*      Initialize Kakadu warning/error reporting subsystem.            */
461 /* -------------------------------------------------------------------- */
462     if( !kakadu_initialized )
463     {
464         kakadu_initialized = true;
465 
466         jpipkak_kdu_cpl_error_message oErrHandler( CE_Failure );
467         jpipkak_kdu_cpl_error_message oWarningHandler( CE_Warning );
468         CPL_IGNORE_RET_VAL(oErrHandler);
469         CPL_IGNORE_RET_VAL(oWarningHandler);
470 
471         kdu_customize_warnings(new jpipkak_kdu_cpl_error_message( CE_Warning ) );
472         kdu_customize_errors(new jpipkak_kdu_cpl_error_message( CE_Failure ) );
473     }
474 }
475 
476 /*****************************************/
477 /*         Initialize()                  */
478 /*****************************************/
Initialize(const char * pszDatasetName,int bReinitializing)479 int JPIPKAKDataset::Initialize(const char* pszDatasetName, int bReinitializing )
480 {
481     KakaduInitialize();
482 
483     // create necessary http headers
484     CPLString osHeaders = "HEADERS=Accept: jpp-stream";
485     CPLString osPersistent;
486 
487     osPersistent.Printf( "PERSISTENT=JPIPKAK:%p", this );
488 
489     char *apszOptions[] = {
490         (char *) osHeaders.c_str(),
491         (char *) osPersistent.c_str(),
492         nullptr
493     };
494 
495     // Setup url to have http in place of jpip protocol indicator.
496     CPLString osURL = "http";
497     osURL += (pszDatasetName + 4);
498 
499     CPLAssert( STARTS_WITH(pszDatasetName, "jpip") );
500 
501     // make initial request to the server for a session, we are going to
502     // assume that the jpip communication is stateful, rather than one-shot
503     // stateless requests append pszUrl with jpip request parameters for a
504     // stateful session (multi-shot communications)
505     // "cnew=http&type=jpp-stream&stream=0&tid=0&len="
506     CPLString osRequest;
507     osRequest.Printf("%s?%s%i", osURL.c_str(),
508                      "cnew=http&type=jpp-stream&stream=0&tid=0&len=", 2000);
509 
510     CPLHTTPResult *psResult = CPLHTTPFetch(osRequest, apszOptions);
511 
512     if ( psResult == nullptr)
513         return FALSE;
514 
515     if( psResult->nDataLen == 0 || psResult->pabyData == nullptr  )
516     {
517 
518         CPLError(CE_Failure, CPLE_AppDefined,
519                  "No data was returned from the given URL" );
520         CPLHTTPDestroyResult( psResult );
521         return FALSE;
522     }
523 
524     if (psResult->nStatus != 0)
525     {
526         CPLError(CE_Failure, CPLE_AppDefined,
527                  "Curl reports error: %d: %s", psResult->nStatus, psResult->pszErrBuf );
528         CPLHTTPDestroyResult( psResult );
529         return FALSE;
530     }
531 
532     // parse the response headers, and the initial data until we get to the
533     // codestream definition
534     char** pszHdrs = psResult->papszHeaders;
535     const char* pszCnew = CSLFetchNameValue(pszHdrs, "JPIP-cnew");
536 
537     if( pszCnew == nullptr )
538     {
539         if( psResult->pszContentType != nullptr
540             && STARTS_WITH_CI(psResult->pszContentType, "text/html") )
541             CPLDebug( "JPIPKAK", "%s",
542                       psResult->pabyData );
543 
544         CPLHTTPDestroyResult( psResult );
545         CPLError(CE_Failure, CPLE_AppDefined,
546                  "Unable to parse required cnew and tid response headers" );
547 
548         return FALSE;
549     }
550 
551     // parse cnew response
552     // JPIP-cnew:
553     // cid=DC69DF980A641A4BBDEB50E484A66578,path=MyPath,transport=http
554     char **papszTokens = CSLTokenizeString2( pszCnew, ",", CSLT_HONOURSTRINGS);
555     for (int i = 0; i < CSLCount(papszTokens); i++)
556     {
557         // looking for cid, path
558         if (STARTS_WITH_CI(papszTokens[i], "cid"))
559         {
560             char *pszKey = nullptr;
561             const char *pszValue = CPLParseNameValue(papszTokens[i], &pszKey );
562             pszCid = CPLStrdup(pszValue);
563             CPLFree( pszKey );
564         }
565 
566         if (STARTS_WITH_CI(papszTokens[i], "path"))
567         {
568             char *pszKey = nullptr;
569             const char *pszValue = CPLParseNameValue(papszTokens[i], &pszKey );
570             pszPath = CPLStrdup(pszValue);
571             CPLFree( pszKey );
572         }
573     }
574 
575     CSLDestroy(papszTokens);
576 
577     if( pszPath == nullptr || pszCid == nullptr )
578     {
579         CPLHTTPDestroyResult(psResult);
580         CPLError(CE_Failure, CPLE_AppDefined, "Error parsing path and cid from cnew - %s", pszCnew);
581         return FALSE;
582     }
583 
584     // Okay, good to go with JPIP, get to the codestream before returning
585     // successful initialization of the driver
586     try
587     {
588         poCache = new kdu_cache();
589         poCodestream = new kdu_codestream();
590         poDecompressor = new kdu_region_decompressor();
591 
592         int bFinished = FALSE;
593         int bError = FALSE;
594         bFinished = ReadFromInput(psResult->pabyData, psResult->nDataLen,
595                                   bError );
596         CPLHTTPDestroyResult(psResult);
597 
598         // continue making requests in the main thread to get all the available
599         // metadata for data bin 0, and reach the codestream
600 
601         // format the new request
602         // and set as pszRequestUrl;
603         // get the protocol from the original request
604         size_t found = osRequest.find_first_of("/");
605         CPLString osProtocol = osRequest.substr(0, found + 2);
606         osRequest.erase(0, found + 2);
607         // find context path
608         found = osRequest.find_first_of("/");
609         osRequest.erase(found);
610 
611         osRequestUrl.Printf("%s%s/%s?cid=%s&stream=0&len=%i", osProtocol.c_str(), osRequest.c_str(), pszPath, pszCid, 2000);
612 
613         while (!bFinished && !bError )
614         {
615             psResult = CPLHTTPFetch(osRequestUrl, apszOptions);
616             bFinished = ReadFromInput(psResult->pabyData, psResult->nDataLen,
617                                       bError );
618             CPLHTTPDestroyResult(psResult);
619         }
620 
621         if( bError )
622             return FALSE;
623 
624         // clean up osRequest, remove variable len= parameter
625         size_t pos = osRequestUrl.find_last_of("&");
626         osRequestUrl.erase(pos);
627 
628         // create codestream
629         poCache->set_read_scope(KDU_MAIN_HEADER_DATABIN, 0, 0);
630         poCodestream->create(poCache);
631         poCodestream->set_persistent();
632 
633 /* -------------------------------------------------------------------- */
634 /*      If this is a reinitialization then we can hop out at this       */
635 /*      point.  The rest of the stuff was already done, and             */
636 /*      hopefully the configuration is unchanged.                       */
637 /* -------------------------------------------------------------------- */
638         if( bReinitializing )
639         {
640             bNeedReinitialize = FALSE;
641             return TRUE;
642         }
643 
644 /* -------------------------------------------------------------------- */
645 /*      Collect GDAL raster configuration information.                  */
646 /* -------------------------------------------------------------------- */
647         kdu_channel_mapping oChannels;
648         oChannels.configure(*poCodestream);
649         kdu_coords* ref_expansion = new kdu_coords(1, 1);
650 
651         // get available resolutions, image width / height etc.
652         kdu_dims view_dims = poDecompressor->
653             get_rendered_image_dims(*poCodestream, &oChannels, -1, 0,
654                                     *ref_expansion, *ref_expansion,
655                                     KDU_WANT_OUTPUT_COMPONENTS);
656 
657         nRasterXSize = view_dims.size.x;
658         nRasterYSize = view_dims.size.y;
659 
660         // Establish the datatype - we will use the same datatype for
661         // all bands based on the first.  This really doesn't do something
662         // great for >16 bit images.
663         if( poCodestream->get_bit_depth(0) > 8
664             && poCodestream->get_signed(0) )
665         {
666             eDT = GDT_Int16;
667         }
668         else if( poCodestream->get_bit_depth(0) > 8
669                  && !poCodestream->get_signed(0) )
670         {
671             eDT = GDT_UInt16;
672         }
673         else
674             eDT = GDT_Byte;
675 
676         if( (poCodestream->get_bit_depth(0) % 8) != 0
677             && poCodestream->get_bit_depth(0) < 16 )
678             SetMetadataItem(
679                 "NBITS",
680                 CPLString().Printf("%d",poCodestream->get_bit_depth(0)),
681                 "IMAGE_STRUCTURE" );
682 
683         // TODO add color interpretation
684 
685         // calculate overviews
686         siz_params* siz_in = poCodestream->access_siz();
687         kdu_params* cod_in = siz_in->access_cluster("COD");
688 
689         delete ref_expansion;
690 
691         siz_in->get("Scomponents", 0, 0, nComps);
692         siz_in->get("Sprecision", 0, 0, nBitDepth);
693 
694         cod_in->get("Clayers", 0, 0, nQualityLayers);
695         cod_in->get("Clevels", 0, 0, nResLevels);
696 
697         bYCC=TRUE;
698         cod_in->get("Cycc", 0, 0, bYCC);
699     }
700     catch(...)
701     {
702         CPLError(CE_Failure, CPLE_AppDefined,
703                  "Trapped Kakadu exception attempting to initialize JPIP access." );
704         return FALSE;
705     }
706 
707 /* -------------------------------------------------------------------- */
708 /*      YCC images are always processed as 3 bands.                     */
709 /* -------------------------------------------------------------------- */
710     if( bYCC )
711         nBands = 3;
712     else
713         nBands = nComps;
714 
715 /* -------------------------------------------------------------------- */
716 /*      Setup band objects.                                             */
717 /* -------------------------------------------------------------------- */
718     int iBand;
719 
720     for( iBand = 1; iBand <= nBands; iBand++ )
721     {
722         JPIPKAKRasterBand *poBand =
723             new JPIPKAKRasterBand(iBand,0,poCodestream,nResLevels,
724                                   this );
725 
726         SetBand( iBand, poBand );
727     }
728 
729     // set specific metadata items
730     CPLString osNQualityLayers;
731     osNQualityLayers.Printf("%i", nQualityLayers);
732     CPLString osNResolutionLevels;
733     osNResolutionLevels.Printf("%i", nResLevels);
734     CPLString osNComps;
735     osNComps.Printf("%i", nComps);
736     CPLString osBitDepth;
737     osBitDepth.Printf("%i", nBitDepth);
738 
739     SetMetadataItem("JPIP_NQUALITYLAYERS", osNQualityLayers.c_str(), "JPIP");
740     SetMetadataItem("JPIP_NRESOLUTIONLEVELS", osNResolutionLevels.c_str(), "JPIP");
741     SetMetadataItem("JPIP_NCOMPS", osNComps.c_str(), "JPIP");
742     SetMetadataItem("JPIP_SPRECISION", osBitDepth.c_str(), "JPIP");
743 
744     if( bYCC )
745         SetMetadataItem("JPIP_YCC", "YES", "JPIP");
746     else
747         SetMetadataItem("JPIP_YCC", "NO", "JPIP");
748 
749 /* ==================================================================== */
750 /*      Parse geojp2, or gmljp2, we will assume that the core           */
751 /*      metadata  of gml or a geojp2 uuid have been sent in the         */
752 /*      initial metadata response.                                      */
753 /*      If the server has used placeholder boxes for this               */
754 /*      information then the image will be interpreted as x,y           */
755 /* ==================================================================== */
756     GDALJP2Metadata oJP2Geo;
757     int nLen = poCache->get_databin_length(KDU_META_DATABIN, nCodestream, 0);
758 
759     if( nLen == 0 )
760     {
761         CPLError(CE_Failure, CPLE_AppDefined,
762                  "Unable to open stream to parse metadata boxes" );
763         return FALSE;
764     }
765 
766     // create in memory file using vsimem
767     CPLString osFileBoxName;
768     osFileBoxName.Printf("/vsimem/jpip/%s.dat", pszCid);
769     VSILFILE *fpLL = VSIFOpenL(osFileBoxName.c_str(), "w+");
770     poCache->set_read_scope(KDU_META_DATABIN, nCodestream, 0);
771     kdu_byte* pabyBuffer = (kdu_byte *)CPLMalloc(nLen);
772     poCache->read(pabyBuffer, nLen);
773     VSIFWriteL(pabyBuffer, nLen, 1, fpLL);
774     CPLFree( pabyBuffer );
775 
776     VSIFFlushL(fpLL);
777     VSIFSeekL(fpLL, 0, SEEK_SET);
778 
779     nPamFlags |= GPF_NOSAVE;
780 
781     try
782     {
783         oJP2Geo.ReadBoxes(fpLL);
784         // parse gml first, followed by geojp2 as a fallback
785         if (oJP2Geo.ParseGMLCoverageDesc() || oJP2Geo.ParseJP2GeoTIFF())
786         {
787             pszProjection = CPLStrdup(oJP2Geo.pszProjection);
788             bGeoTransformValid = TRUE;
789 
790             memcpy(adfGeoTransform, oJP2Geo.adfGeoTransform,
791                    sizeof(double) * 6 );
792             nGCPCount = oJP2Geo.nGCPCount;
793             pasGCPList = oJP2Geo.pasGCPList;
794 
795             oJP2Geo.pasGCPList = nullptr;
796             oJP2Geo.nGCPCount = 0;
797 
798             int iBox;
799 
800             for( iBox = 0;
801                  oJP2Geo.papszGMLMetadata
802                      && oJP2Geo.papszGMLMetadata[iBox] != nullptr;
803                  iBox++ )
804             {
805                 char *pszName = nullptr;
806                 const char *pszXML =
807                     CPLParseNameValue( oJP2Geo.papszGMLMetadata[iBox],
808                                        &pszName );
809                 CPLString osDomain;
810                 char *apszMDList[2];
811 
812                 osDomain.Printf( "xml:%s", pszName );
813                 apszMDList[0] = (char *) pszXML;
814                 apszMDList[1] = nullptr;
815 
816                 GDALPamDataset::SetMetadata( apszMDList, osDomain );
817                 CPLFree( pszName );
818             }
819         }
820         else
821         {
822             // treat as Cartesian, no geo metadata
823             CPLError(CE_Warning, CPLE_AppDefined,
824                      "Parsed metadata boxes from jpip stream, geographic metadata not found - is the server using placeholders for this data?" );
825         }
826     }
827     catch(...)
828     {
829         CPLError(CE_Failure, CPLE_AppDefined,
830                  "Unable to parse geographic metadata boxes from jpip stream" );
831     }
832 
833     VSIFCloseL(fpLL);
834     VSIUnlink( osFileBoxName.c_str());
835 
836     bNeedReinitialize = FALSE;
837 
838     return TRUE;
839 }
840 
841 /******************************************/
842 /*           ReadVBAS()                   */
843 /******************************************/
ReadVBAS(GByte * pabyData,int nLen)844 long JPIPKAKDataset::ReadVBAS(GByte* pabyData, int nLen )
845 {
846     int c = -1;
847     long val = 0;
848     nVBASLen = 0;
849 
850     while ((c & 0x80) != 0)
851     {
852         if (nVBASLen >= 9)
853         {
854             CPLError(CE_Failure, CPLE_AppDefined,
855                      "VBAS Length not supported");
856             return -1;
857         }
858 
859         if (nPos > nLen)
860         {
861             CPLError(CE_Failure, CPLE_AppDefined,
862                      "EOF reached before completing VBAS");
863             return -1;
864         }
865 
866 #ifdef PST_DEBUG
867         if( nPSTThisInstance == nPSTTargetInstance
868             && nPos >= nPSTTargetOffset )
869         {
870             CPLError(CE_Failure, CPLE_AppDefined,
871                      "Artificial PST EOF reached before completing VBAS");
872             return -1;
873         }
874 #endif
875 
876         c = pabyData[nPos];
877         nPos++;
878 
879         val = (val << 7) | (long)(c & 0x7F);
880 
881         if (nVBASLen == 0)
882             nVBASFirstByte = c;
883 
884         nVBASLen++;
885     }
886 
887     return val;
888 }
889 
890 /******************************************/
891 /*            ReadSegment()               */
892 /******************************************/
ReadSegment(GByte * pabyData,int nLen,int & bError)893 JPIPDataSegment* JPIPKAKDataset::ReadSegment(GByte* pabyData, int nLen,
894                                              int& bError )
895 {
896     long nId = ReadVBAS(pabyData, nLen);
897     bError = FALSE;
898 
899     if (nId < 0)
900     {
901         bError = TRUE;
902         return nullptr;
903     }
904     else
905     {
906         JPIPDataSegment* segment = new JPIPDataSegment();
907         segment->SetId(nId);
908 
909         if (nVBASFirstByte == 0)
910         {
911             segment->SetEOR(TRUE);
912             segment->SetId(pabyData[nPos]);
913         }
914         else
915         {
916             segment->SetEOR(FALSE);
917             nId &= ~(0x70 << ((nVBASLen -1) * 7));
918             segment->SetId(nId);
919             segment->SetFinal((nVBASFirstByte & 0x10) != 0);
920 
921             int m = (nVBASFirstByte & 0x7F) >> 5;
922 
923             if (m == 0)
924             {
925                 CPLError(CE_Failure, CPLE_AppDefined,
926                          "Invalid Bin-ID value format");
927                 bError = TRUE;
928                 delete segment;
929                 return nullptr;
930             }
931             else if (m >= 2) {
932                 nClassId = static_cast<int>(ReadVBAS(pabyData, nLen));
933                 if (m > 2)
934                 {
935                     nCodestream = static_cast<int>(ReadVBAS(pabyData, nLen));
936                     if( nCodestream < 0 )
937                     {
938                         bError = TRUE;
939                         delete segment;
940                         return nullptr;
941                     }
942                 }
943             }
944 
945             long nNextVal;
946 
947             segment->SetClassId(nClassId);
948             segment->SetCodestreamIdx(nCodestream);
949 
950             nNextVal = ReadVBAS(pabyData, nLen);
951             if( nNextVal == -1 )
952             {
953                 bError = TRUE;
954                 delete segment;
955                 return nullptr;
956             }
957             else
958                 segment->SetOffset( nNextVal );
959 
960             nNextVal = ReadVBAS(pabyData, nLen);
961             if( nNextVal == -1 )
962             {
963                 bError = TRUE;
964                 delete segment;
965                 return nullptr;
966             }
967             else
968                 segment->SetLen(nNextVal);
969         }
970 
971         if ((segment->GetLen() > 0) && (!segment->IsEOR()))
972         {
973             GByte* pabyDataSegment = (GByte *) CPLCalloc(1,segment->GetLen());
974 
975             // copy data from input array pabyData to the data segment
976             memcpy(pabyDataSegment,
977                    pabyData + nPos,
978                    segment->GetLen());
979 
980             segment->SetData(pabyDataSegment);
981         }
982 
983         nPos += segment->GetLen();
984 
985         if (!segment->IsEOR())
986             nDatabins++;
987 
988         if ((segment->GetId() == JPIPKAKDataset::JPIP_EOR_WINDOW_DONE) && (segment->IsEOR()))
989             bWindowDone = TRUE;
990 
991         return segment;
992     }
993 }
994 
995 /******************************************/
996 /*           KakaduClassId()              */
997 /******************************************/
KakaduClassId(int nClassIdIn)998 int JPIPKAKDataset::KakaduClassId(int nClassIdIn)
999 {
1000     if (nClassIdIn == 0)
1001         return KDU_PRECINCT_DATABIN;
1002     else if (nClassIdIn == 2)
1003         return KDU_TILE_HEADER_DATABIN;
1004     else if (nClassIdIn == 6)
1005         return KDU_MAIN_HEADER_DATABIN;
1006     else if (nClassIdIn == 8)
1007         return KDU_META_DATABIN;
1008     else if (nClassIdIn == 4)
1009         return KDU_TILE_DATABIN;
1010     else
1011         return -1;
1012 }
1013 
1014 /******************************************/
1015 /*            ReadFromInput()             */
1016 /******************************************/
ReadFromInput(GByte * pabyData,int nLen,int & bError)1017 int JPIPKAKDataset::ReadFromInput(GByte* pabyData, int nLen, int &bError )
1018 {
1019     int res = FALSE;
1020     bError = FALSE;
1021 
1022     if (nLen <= 0 )
1023         return FALSE;
1024 
1025 #ifdef PST_DEBUG
1026     nPSTThisInstance++;
1027     if( CPLGetConfigOption( "PST_OFFSET", nullptr ) != nullptr )
1028     {
1029         nPSTTargetOffset = atoi(CPLGetConfigOption("PST_OFFSET","0"));
1030         nPSTTargetInstance = 0;
1031     }
1032     if( CPLGetConfigOption( "PST_INSTANCE", nullptr ) != nullptr )
1033     {
1034         nPSTTargetInstance = atoi(CPLGetConfigOption("PST_INSTANCE","0"));
1035     }
1036 
1037     if( nPSTTargetOffset != -1 && nPSTThisInstance == 0 )
1038     {
1039         CPLDebug( "JPIPKAK", "Premature Stream Termination Activated, PST_OFFSET=%d, PST_INSTANCE=%d",
1040                   nPSTTargetOffset, nPSTTargetInstance );
1041     }
1042     if( nPSTTargetOffset != -1
1043         && nPSTThisInstance == nPSTTargetInstance )
1044     {
1045         CPLDebug( "JPIPKAK", "Premature Stream Termination in force for this input instance, PST_OFFSET=%d, data length=%d",
1046                   nPSTTargetOffset, nLen );
1047     }
1048 #endif // def PST_DEBUG
1049 
1050     // parse the data stream, reading the vbas and adding to the kakadu cache
1051     // we could parse all the boxes by hand, and just add data to the kakadu cache
1052     // we will do it the easy way and retrieve the metadata through the kakadu query api
1053 
1054     nPos = 0;
1055     JPIPDataSegment* pSegment = nullptr;
1056 
1057     while ((pSegment = ReadSegment(pabyData, nLen, bError)) != nullptr)
1058     {
1059         if (pSegment->IsEOR())
1060         {
1061             if ((pSegment->GetId() == JPIPKAKDataset::JPIP_EOR_IMAGE_DONE) ||
1062                 (pSegment->GetId() == JPIPKAKDataset::JPIP_EOR_WINDOW_DONE))
1063                 res = TRUE;
1064 
1065             delete pSegment;
1066             break;
1067         }
1068         else
1069         {
1070             // add data to kakadu
1071             //CPLDebug("JPIPKAK", "Parsed JPIP Segment class=%i stream=%i id=%i offset=%i len=%i isFinal=%i isEOR=%i", pSegment->GetClassId(), pSegment->GetCodestreamIdx(), pSegment->GetId(), pSegment->GetOffset(), pSegment->GetLen(), pSegment->IsFinal(), pSegment->IsEOR());
1072             poCache->add_to_databin(KakaduClassId(static_cast<int>(pSegment->GetClassId())), pSegment->GetCodestreamIdx(),
1073                                     pSegment->GetId(), pSegment->GetData(),
1074                                     static_cast<int>(pSegment->GetOffset()),
1075                                     static_cast<int>(pSegment->GetLen()), pSegment->IsFinal());
1076 
1077             delete pSegment;
1078         }
1079     }
1080 
1081     return res;
1082 }
1083 
1084 /************************************************************************/
1085 /*                          GetProjectionRef()                          */
1086 /************************************************************************/
1087 
_GetProjectionRef()1088 const char *JPIPKAKDataset::_GetProjectionRef()
1089 
1090 {
1091     if( pszProjection && *pszProjection )
1092         return pszProjection;
1093     else
1094         return GDALPamDataset::_GetProjectionRef();
1095 }
1096 
1097 /************************************************************************/
1098 /*                          GetGeoTransform()                           */
1099 /************************************************************************/
1100 
GetGeoTransform(double * padfTransform)1101 CPLErr JPIPKAKDataset::GetGeoTransform( double * padfTransform )
1102 
1103 {
1104     if( bGeoTransformValid )
1105     {
1106         memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 );
1107 
1108         return CE_None;
1109     }
1110     else
1111         return GDALPamDataset::GetGeoTransform( padfTransform );
1112 }
1113 
1114 /************************************************************************/
1115 /*                            GetGCPCount()                             */
1116 /************************************************************************/
1117 
GetGCPCount()1118 int JPIPKAKDataset::GetGCPCount()
1119 
1120 {
1121     return nGCPCount;
1122 }
1123 
1124 /************************************************************************/
1125 /*                          GetGCPProjection()                          */
1126 /************************************************************************/
1127 
_GetGCPProjection()1128 const char *JPIPKAKDataset::_GetGCPProjection()
1129 
1130 {
1131     if( nGCPCount > 0 )
1132         return pszProjection;
1133     else
1134         return "";
1135 }
1136 
1137 /************************************************************************/
1138 /*                               GetGCP()                               */
1139 /************************************************************************/
1140 
GetGCPs()1141 const GDAL_GCP *JPIPKAKDataset::GetGCPs()
1142 
1143 {
1144     return pasGCPList;
1145 }
1146 
1147 /************************************************************************/
1148 /*                             IRasterIO()                              */
1149 /************************************************************************/
1150 
IRasterIO(GDALRWFlag eRWFlag,int nXOff,int nYOff,int nXSize,int nYSize,void * pData,int nBufXSize,int nBufYSize,GDALDataType eBufType,int nBandCount,int * panBandMap,GSpacing nPixelSpace,GSpacing nLineSpace,GSpacing nBandSpace,GDALRasterIOExtraArg * psExtraArg)1151 CPLErr JPIPKAKDataset::IRasterIO( GDALRWFlag eRWFlag,
1152                                   int nXOff, int nYOff, int nXSize, int nYSize,
1153                                   void * pData, int nBufXSize, int nBufYSize,
1154                                   GDALDataType eBufType,
1155                                   int nBandCount, int *panBandMap,
1156                                   GSpacing nPixelSpace, GSpacing nLineSpace,
1157                                   GSpacing nBandSpace,
1158                                   GDALRasterIOExtraArg* psExtraArg)
1159 
1160 {
1161 /* -------------------------------------------------------------------- */
1162 /*      We need various criteria to skip out to block based methods.    */
1163 /* -------------------------------------------------------------------- */
1164     if( TestUseBlockIO( nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize,
1165                         eBufType, nBandCount, panBandMap ) )
1166         return GDALPamDataset::IRasterIO(
1167             eRWFlag, nXOff, nYOff, nXSize, nYSize,
1168             pData, nBufXSize, nBufYSize, eBufType,
1169             nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace, psExtraArg );
1170 
1171 /* -------------------------------------------------------------------- */
1172 /*      Otherwise do this as a single uncached async rasterio.          */
1173 /* -------------------------------------------------------------------- */
1174     GDALAsyncReader* ario =
1175         BeginAsyncReader(nXOff, nYOff, nXSize, nYSize,
1176                          pData, nBufXSize, nBufYSize, eBufType,
1177                          nBandCount, panBandMap,
1178                          static_cast<int>(nPixelSpace),
1179                          static_cast<int>(nLineSpace),
1180                          static_cast<int>(nBandSpace), nullptr);
1181 
1182     if( ario == nullptr )
1183         return CE_Failure;
1184 
1185     GDALAsyncStatusType status;
1186 
1187     do
1188     {
1189         int nXBufOff,nYBufOff,nXBufSize,nYBufSize;
1190 
1191         status = ario->GetNextUpdatedRegion(-1.0,
1192                                             &nXBufOff, &nYBufOff,
1193                                             &nXBufSize, &nYBufSize );
1194     } while (status != GARIO_ERROR && status != GARIO_COMPLETE );
1195 
1196     EndAsyncReader(ario);
1197 
1198     if (status == GARIO_ERROR)
1199         return CE_Failure;
1200     else
1201         return CE_None;
1202 }
1203 
1204 /************************************************************************/
1205 /*                           TestUseBlockIO()                           */
1206 /************************************************************************/
1207 
1208 int
TestUseBlockIO(CPL_UNUSED int nXOff,CPL_UNUSED int nYOff,int nXSize,int nYSize,int nBufXSize,int nBufYSize,CPL_UNUSED GDALDataType eDataType,int nBandCount,int * panBandList) const1209 JPIPKAKDataset::TestUseBlockIO( CPL_UNUSED int nXOff, CPL_UNUSED int nYOff,
1210                                 int nXSize, int nYSize,
1211                                 int nBufXSize, int nBufYSize,
1212                                 CPL_UNUSED GDALDataType eDataType,
1213                                 int nBandCount, int *panBandList ) const
1214 
1215 {
1216 /* -------------------------------------------------------------------- */
1217 /*      Due to limitations in DirectRasterIO() we can only handle       */
1218 /*      it no duplicates in the band list.                              */
1219 /* -------------------------------------------------------------------- */
1220     int i, j;
1221 
1222     for( i = 0; i < nBandCount; i++ )
1223     {
1224         for( j = i+1; j < nBandCount; j++ )
1225             if( panBandList[j] == panBandList[i] )
1226                 return TRUE;
1227     }
1228 
1229 /* -------------------------------------------------------------------- */
1230 /*      The rest of the rules are io strategy stuff, and use            */
1231 /*      configuration checks.                                           */
1232 /* -------------------------------------------------------------------- */
1233     int bUseBlockedIO = bForceCachedIO;
1234 
1235     if( nYSize == 1 || nXSize * ((double) nYSize) < 100.0 )
1236         bUseBlockedIO = TRUE;
1237 
1238     if( nBufYSize == 1 || nBufXSize * ((double) nBufYSize) < 100.0 )
1239         bUseBlockedIO = TRUE;
1240 
1241     if( bUseBlockedIO
1242         && CPLTestBool( CPLGetConfigOption( "GDAL_ONE_BIG_READ", "NO") ) )
1243         bUseBlockedIO = FALSE;
1244 
1245     return bUseBlockedIO;
1246 }
1247 
1248 /*************************************************************************/
1249 /*                     BeginAsyncReader()                              */
1250 /*************************************************************************/
1251 
1252 GDALAsyncReader*
BeginAsyncReader(int xOff,int yOff,int xSize,int ySize,void * pBuf,int bufXSize,int bufYSize,GDALDataType bufType,int nBandCount,int * pBandMap,int nPixelSpace,int nLineSpace,int nBandSpace,char ** papszOptions)1253 JPIPKAKDataset::BeginAsyncReader(int xOff, int yOff,
1254                                    int xSize, int ySize,
1255                                    void *pBuf,
1256                                    int bufXSize, int bufYSize,
1257                                    GDALDataType bufType,
1258                                    int nBandCount, int* pBandMap,
1259                                    int nPixelSpace, int nLineSpace,
1260                                    int nBandSpace,
1261                                    char **papszOptions)
1262 {
1263     CPLDebug( "JPIP", "BeginAsyncReadeR(%d,%d,%d,%d -> %dx%d)",
1264               xOff, yOff, xSize, ySize, bufXSize, bufYSize );
1265 
1266 /* -------------------------------------------------------------------- */
1267 /*      Recreate the code stream access if needed.                      */
1268 /* -------------------------------------------------------------------- */
1269     if( bNeedReinitialize )
1270     {
1271         CPLDebug( "JPIPKAK", "\n\nReinitializing after error! ******\n" );
1272 
1273         Deinitialize();
1274         if( !Initialize(GetDescription(),TRUE) )
1275             return nullptr;
1276     }
1277 
1278 /* -------------------------------------------------------------------- */
1279 /*      Provide default packing if needed.                              */
1280 /* -------------------------------------------------------------------- */
1281     if( nPixelSpace == 0 )
1282         nPixelSpace = GDALGetDataTypeSize(bufType) / 8;
1283     if( nLineSpace == 0 )
1284         nLineSpace = nPixelSpace * bufXSize;
1285     if( nBandSpace == 0 )
1286         nBandSpace = nLineSpace * bufYSize;
1287 
1288 /* -------------------------------------------------------------------- */
1289 /*      check we have sensible values for windowing.                    */
1290 /* -------------------------------------------------------------------- */
1291     if (xOff > GetRasterXSize()
1292         || yOff > GetRasterYSize()
1293         || (xOff + xSize) > GetRasterXSize()
1294         || (yOff + ySize) > GetRasterYSize() )
1295     {
1296         CPLError( CE_Failure, CPLE_AppDefined,
1297                   "Requested window (%d,%d %dx%d) off dataset.",
1298                   xOff, yOff, xSize, ySize );
1299         return nullptr;
1300     }
1301 
1302 /* -------------------------------------------------------------------- */
1303 /*      Record request information.                                     */
1304 /* -------------------------------------------------------------------- */
1305     JPIPKAKAsyncReader* ario = new JPIPKAKAsyncReader();
1306     ario->poDS = this;
1307     ario->nBufXSize = bufXSize;
1308     ario->nBufYSize = bufYSize;
1309     ario->eBufType = bufType;
1310     ario->nBandCount = nBandCount;
1311     ario->nXOff = xOff;
1312     ario->nYOff = yOff;
1313     ario->nXSize = xSize;
1314     ario->nYSize = ySize;
1315 
1316     ario->panBandMap = new int[nBandCount];
1317     if (pBandMap)
1318     {
1319         for (int i = 0; i < nBandCount; i++)
1320             ario->panBandMap[i] = pBandMap[i];
1321     }
1322     else
1323     {
1324         for (int i = 0; i < nBandCount; i++)
1325             ario->panBandMap[i] = i+1;
1326     }
1327 
1328 /* -------------------------------------------------------------------- */
1329 /*      If the buffer type is of other than images type, we need to     */
1330 /*      allocate a private buffer the same type as the image which      */
1331 /*      will be converted later.                                        */
1332 /* -------------------------------------------------------------------- */
1333     if( bufType != eDT )
1334     {
1335         ario->nPixelSpace = GDALGetDataTypeSize(eDT) / 8;
1336         ario->nLineSpace = ario->nPixelSpace * bufXSize;
1337         ario->nBandSpace = ario->nLineSpace * bufYSize;
1338 
1339         ario->nAppPixelSpace = nPixelSpace;
1340         ario->nAppLineSpace = nLineSpace;
1341         ario->nAppBandSpace = nBandSpace;
1342 
1343         ario->pBuf = VSI_MALLOC3_VERBOSE(bufXSize,bufYSize,ario->nPixelSpace*nBandCount);
1344         if( ario->pBuf == nullptr )
1345         {
1346             delete ario;
1347             return nullptr;
1348         }
1349 
1350         ario->pAppBuf = pBuf;
1351     }
1352     else
1353     {
1354         ario->pBuf = pBuf;
1355         ario->pAppBuf = pBuf;
1356 
1357         ario->nAppPixelSpace = nPixelSpace;
1358         ario->nPixelSpace = nPixelSpace;
1359 
1360         ario->nAppLineSpace = nLineSpace;
1361         ario->nLineSpace = nLineSpace;
1362 
1363         ario->nAppBandSpace = nBandSpace;
1364         ario->nBandSpace = nBandSpace;
1365     }
1366 
1367     // parse options
1368     const char* pszLevel = CSLFetchNameValue(papszOptions, "LEVEL");
1369     const char* pszLayers = CSLFetchNameValue(papszOptions, "LAYERS");
1370     const char* pszPriority = CSLFetchNameValue(papszOptions, "PRIORITY");
1371 
1372     if (pszLayers)
1373         ario->nQualityLayers = atoi(pszLayers);
1374     else
1375         ario->nQualityLayers = nQualityLayers;
1376 
1377     if (pszPriority)
1378     {
1379         if (EQUAL(pszPriority, "0"))
1380             ario->bHighPriority = 0;
1381         else
1382             ario->bHighPriority = 1;
1383     }
1384     else
1385         ario->bHighPriority = 1;
1386 
1387 /* -------------------------------------------------------------------- */
1388 /*      Select an appropriate level based on the ratio of buffer        */
1389 /*      size to full resolution image.  We aim for the next             */
1390 /*      resolution *lower* than we might expect for the target          */
1391 /*      buffer unless it falls on a power of two.  This is because      */
1392 /*      the region decompressor only seems to support upsampling        */
1393 /*      via the numerator/denominator magic.                            */
1394 /* -------------------------------------------------------------------- */
1395     if (pszLevel)
1396         ario->nLevel = atoi(pszLevel);
1397     else
1398     {
1399         int nRXSize = xSize;
1400         int nRYSize = ySize;
1401         ario->nLevel = 0;
1402 
1403         while( ario->nLevel < nResLevels
1404                && (nRXSize > bufXSize || nRYSize > bufYSize) )
1405         {
1406             nRXSize = (nRXSize+1) / 2;
1407             nRYSize = (nRYSize+1) / 2;
1408             ario->nLevel++;
1409         }
1410     }
1411 
1412     ario->Start();
1413 
1414     return ario;
1415 }
1416 
1417 /************************************************************************/
1418 /*                  EndAsyncReader()                                  */
1419 /************************************************************************/
1420 
EndAsyncReader(GDALAsyncReader * poARIO)1421 void JPIPKAKDataset::EndAsyncReader(GDALAsyncReader *poARIO)
1422 {
1423     delete poARIO;
1424 }
1425 
1426 /*****************************************/
1427 /*             Open()                    */
1428 /*****************************************/
Open(GDALOpenInfo * poOpenInfo)1429 GDALDataset *JPIPKAKDataset::Open(GDALOpenInfo * poOpenInfo)
1430 {
1431     // test jpip and jpips, assuming jpip is using http as the transport layer
1432     // jpip = http, jpips = https (note SSL is allowed, but jpips is not in the ISO spec)
1433     if (STARTS_WITH_CI(poOpenInfo->pszFilename, "jpip://")
1434         || STARTS_WITH_CI(poOpenInfo->pszFilename, "jpips://"))
1435     {
1436         // perform the initial connection
1437         // using cpl_http for the connection
1438         if  (CPLHTTPEnabled() == TRUE)
1439         {
1440             JPIPKAKDataset *poDS = new JPIPKAKDataset();
1441             if (poDS->Initialize(poOpenInfo->pszFilename,FALSE))
1442             {
1443                 poDS->SetDescription( poOpenInfo->pszFilename );
1444                 return poDS;
1445             }
1446             else
1447             {
1448                 delete poDS;
1449                 return nullptr;
1450             }
1451         }
1452         else
1453         {
1454             CPLError( CE_Failure, CPLE_OpenFailed,
1455                       "Failed to open %s within JPIPKAK driver CPL HTTP not enabled.\n",
1456                       poOpenInfo->pszFilename );
1457             return nullptr;
1458         }
1459     }
1460     else
1461         return nullptr;
1462 }
1463 
1464 /************************************************************************/
1465 /*                        GDALRegister_JPIPKAK()                        */
1466 /************************************************************************/
1467 
GDALRegister_JPIPKAK()1468 void GDALRegister_JPIPKAK()
1469 {
1470     if( !GDAL_CHECK_VERSION( "JPIPKAK driver" ) )
1471         return;
1472 
1473     if( GDALGetDriverByName( "JPIPKAK" ) != nullptr )
1474         return;
1475 
1476     GDALDriver *poDriver = new GDALDriver();
1477 
1478     poDriver->SetDescription( "JPIPKAK" );
1479     poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
1480     poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, "JPIP (based on Kakadu)" );
1481     poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "drivers/raster/jpipkak.html" );
1482     poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/jpp-stream" );
1483 
1484     poDriver->pfnOpen = JPIPKAKDataset::Open;
1485     GetGDALDriverManager()->RegisterDriver( poDriver );
1486 }
1487 
1488 /************************************************************************/
1489 /*                         JPIPKAKAsyncReader                         */
1490 /************************************************************************/
JPIPKAKAsyncReader()1491 JPIPKAKAsyncReader::JPIPKAKAsyncReader()
1492 {
1493     panBandMap = nullptr;
1494     pAppBuf = nullptr;
1495     pBuf = nullptr;
1496     nDataRead = 0;
1497     nAppPixelSpace = 0;
1498     nAppLineSpace = 0;
1499     nAppBandSpace = 0;
1500     nLevel = 0;
1501     nQualityLayers = 0;
1502     bHighPriority = FALSE;
1503     bComplete = FALSE;
1504 }
1505 
1506 /************************************************************************/
1507 /*                        ~JPIPKAKAsyncReader                         */
1508 /************************************************************************/
~JPIPKAKAsyncReader()1509 JPIPKAKAsyncReader::~JPIPKAKAsyncReader()
1510 {
1511     Stop();
1512 
1513     // don't own the buffer
1514     delete [] panBandMap;
1515 
1516     if( pAppBuf != pBuf )
1517         CPLFree( pBuf );
1518 }
1519 
1520 /************************************************************************/
1521 /*                               Start()                                */
1522 /************************************************************************/
1523 
Start()1524 void JPIPKAKAsyncReader::Start()
1525 {
1526     JPIPKAKDataset *poJDS = (JPIPKAKDataset*)poDS;
1527 
1528     // stop the currently running thread
1529     // start making requests to the server to the server
1530     nDataRead = 0;
1531     bComplete = 0;
1532 
1533     // check a thread is not already running
1534     if (((bHighPriority) && (poJDS->bHighThreadRunning)) ||
1535         ((!bHighPriority) && (poJDS->bLowThreadRunning)))
1536         CPLError(CE_Failure, CPLE_AppDefined, "JPIPKAKAsyncReader supports at most two concurrent server communication threads");
1537     else
1538     {
1539         // Ensure we are working against full res
1540         ((JPIPKAKDataset*)poDS)->poCodestream->
1541             apply_input_restrictions( 0, 0, 0, 0, nullptr );
1542 
1543         // calculate the url the worker function is going to retrieve
1544         // calculate the kakadu adjust image size
1545         channels.configure(*(((JPIPKAKDataset*)poDS)->poCodestream));
1546 
1547         // find current canvas width and height in the cache and check we don't
1548         // exceed this in our process request
1549         kdu_dims view_dims;
1550         kdu_coords ref_expansion;
1551         ref_expansion.x = 1;
1552         ref_expansion.y = 1;
1553 
1554         view_dims = ((JPIPKAKDataset*)poDS)->poDecompressor->
1555             get_rendered_image_dims(*((JPIPKAKDataset*)poDS)->poCodestream, &channels,
1556                                     -1, nLevel,
1557                                     ref_expansion );
1558 
1559         kdu_coords* view_siz = view_dims.access_size();
1560 
1561         // Establish the decimation implied by our resolution level.
1562         int nRes = 1;
1563         if (nLevel > 0)
1564             nRes = 2 << (nLevel - 1);
1565 
1566         // setup expansion to account for the difference between
1567         // the selected level and the buffer resolution.
1568         exp_numerator.x = nBufXSize;
1569         exp_numerator.y = nBufYSize;
1570 
1571         exp_denominator.x = (int) ceil(nXSize / (double) nRes);
1572         exp_denominator.y = (int) ceil(nYSize / (double) nRes);
1573 
1574         // formulate jpip parameters and adjust offsets for current level
1575         int fx = view_siz->x / nRes;
1576         int fy = view_siz->y / nRes;
1577 
1578         rr_win.pos.x = (int) ceil(nXOff / (1.0 * nRes)); // roffx
1579         rr_win.pos.y = (int) ceil(nYOff / (1.0 * nRes)); // roffy
1580         rr_win.size.x = (int) ceil(nXSize / (1.0 * nRes)); // rsizx
1581         rr_win.size.y = (int) ceil(nYSize / (1.0 * nRes)); // rsizy
1582 
1583         if ( rr_win.pos.x + rr_win.size.x > fx)
1584             rr_win.size.x = fx - rr_win.pos.x;
1585         if ( rr_win.pos.y + rr_win.size.y > fy)
1586             rr_win.size.y = fy - rr_win.pos.y;
1587 
1588         CPLString jpipUrl;
1589         CPLString comps;
1590 
1591         if( poJDS->bYCC )
1592         {
1593             comps = "0,1,2";
1594         }
1595         else
1596         {
1597             for (int i = 0; i < nBandCount; i++)
1598                 comps.Printf("%s%i,", comps.c_str(), panBandMap[i]-1);
1599 
1600             comps.erase(comps.length() -1);
1601         }
1602 
1603         jpipUrl.Printf("%s&type=jpp-stream&roff=%i,%i&rsiz=%i,%i&fsiz=%i,%i,closest&quality=%i&comps=%s",
1604                        ((JPIPKAKDataset*)poDS)->osRequestUrl.c_str(),
1605                        rr_win.pos.x, rr_win.pos.y,
1606                        rr_win.size.x, rr_win.size.y,
1607                        fx, fy,
1608                        nQualityLayers, comps.c_str());
1609 
1610         JPIPRequest* pRequest = new JPIPRequest();
1611         pRequest->bPriority = bHighPriority;
1612         pRequest->osRequest = jpipUrl;
1613         pRequest->poARIO = this;
1614 
1615         if( bHighPriority )
1616             poJDS->bHighThreadFinished = 0;
1617         else
1618             poJDS->bLowThreadFinished = 0;
1619 
1620         //CPLDebug("JPIPKAKAsyncReader", "THREADING TURNED OFF");
1621         if (CPLCreateThread(JPIPWorkerFunc, pRequest) == -1)
1622             CPLError(CE_Failure, CPLE_AppDefined,
1623                      "Unable to create worker jpip  thread" );
1624         // run in main thread as a test
1625         //JPIPWorkerFunc(pRequest);
1626     }
1627 }
1628 
1629 /************************************************************************/
1630 /*                                Stop()                                */
1631 /************************************************************************/
Stop()1632 void JPIPKAKAsyncReader::Stop()
1633 {
1634     JPIPKAKDataset *poJDS = (JPIPKAKDataset*)poDS;
1635 
1636     bComplete = 1;
1637     if (poJDS->pGlobalMutex)
1638     {
1639         if (((bHighPriority) && (!poJDS->bHighThreadFinished)) ||
1640             ((!bHighPriority) && (!poJDS->bLowThreadFinished)))
1641         {
1642             CPLDebug( "JPIPKAK", "JPIPKAKAsyncReader::Stop() requested." );
1643 
1644             // stop the thread
1645             if (bHighPriority)
1646             {
1647                 CPLAcquireMutex(poJDS->pGlobalMutex, 100.0);
1648                 poJDS->bHighThreadRunning = 0;
1649                 CPLReleaseMutex(poJDS->pGlobalMutex);
1650 
1651                 while (!poJDS->bHighThreadFinished)
1652                     CPLSleep(0.1);
1653             }
1654             else
1655             {
1656                 CPLAcquireMutex(poJDS->pGlobalMutex, 100.0);
1657                 poJDS->bLowThreadRunning = 0;
1658                 CPLReleaseMutex(poJDS->pGlobalMutex);
1659 
1660                 while (!poJDS->bLowThreadFinished)
1661                 {
1662                     CPLSleep(0.1);
1663                 }
1664             }
1665             CPLDebug( "JPIPKAK", "JPIPKAKAsyncReader::Stop() confirmed." );
1666         }
1667     }
1668 }
1669 
1670 /************************************************************************/
1671 /*                        GetNextUpdatedRegion()                        */
1672 /************************************************************************/
1673 
1674 GDALAsyncStatusType
GetNextUpdatedRegion(double dfTimeout,int * pnxbufoff,int * pnybufoff,int * pnxbufsize,int * pnybufsize)1675 JPIPKAKAsyncReader::GetNextUpdatedRegion(double dfTimeout,
1676                                          int* pnxbufoff,
1677                                          int* pnybufoff,
1678                                          int* pnxbufsize,
1679                                          int* pnybufsize)
1680 {
1681     GDALAsyncStatusType result = GARIO_ERROR;
1682     JPIPKAKDataset *poJDS = (JPIPKAKDataset*)poDS;
1683 
1684     long nSize = 0;
1685     // take a snapshot of the volatile variables
1686     if (bHighPriority)
1687     {
1688         const long s = poJDS->nHighThreadByteCount - nDataRead;
1689         nSize = s;
1690     }
1691     else
1692     {
1693         const long s = poJDS->nLowThreadByteCount - nDataRead;
1694         nSize = s;
1695     }
1696 
1697 /* -------------------------------------------------------------------- */
1698 /*      Wait for new data to return if required.                        */
1699 /* -------------------------------------------------------------------- */
1700     if ((nSize == 0) && dfTimeout != 0 )
1701     {
1702         // poll for change in cache size
1703         clock_t end_wait = 0;
1704 
1705         end_wait = clock() + (int) (dfTimeout * CLOCKS_PER_SEC);
1706         while ((nSize == 0) && ((bHighPriority && poJDS->bHighThreadRunning) ||
1707                                 (!bHighPriority && poJDS->bLowThreadRunning)))
1708         {
1709             if (end_wait)
1710                 if (clock() > end_wait && dfTimeout >= 0 )
1711                     break;
1712 
1713             CPLSleep(0.1);
1714 
1715             if (bHighPriority)
1716             {
1717                 const long s = poJDS->nHighThreadByteCount - nDataRead;
1718                 nSize = s;
1719             }
1720             else
1721             {
1722                 const long s = poJDS->nLowThreadByteCount - nDataRead;
1723                 nSize = s;
1724             }
1725         }
1726     }
1727 
1728     // if there is no pending data and we don't want to wait.
1729     if( nSize == 0 )
1730     {
1731         *pnxbufoff = 0;
1732         *pnybufoff = 0;
1733         *pnxbufsize = 0;
1734         *pnybufsize = 0;
1735 
1736         // Indicate an error if the thread finished prematurely
1737         if( (bHighPriority
1738              && !poJDS->bHighThreadRunning
1739              && poJDS->bHighThreadFinished)
1740             || (!bHighPriority
1741                 && !poJDS->bLowThreadRunning
1742                 && poJDS->bLowThreadFinished) )
1743         {
1744             if( osErrorMsg != "" )
1745                 CPLError( CE_Failure, CPLE_AppDefined,
1746                           "%s", osErrorMsg.c_str() );
1747             else
1748                 CPLError( CE_Failure, CPLE_AppDefined,
1749                           "Working thread failed without complete data. (%d,%d,%d)",
1750                           bHighPriority,
1751                           poJDS->bHighThreadRunning,
1752                           poJDS->bHighThreadFinished );
1753             poJDS->bNeedReinitialize = TRUE;
1754             return GARIO_ERROR;
1755         }
1756 
1757         // Otherwise there is still pending data to wait for.
1758         return GARIO_PENDING;
1759     }
1760 
1761 /* -------------------------------------------------------------------- */
1762 /*      Establish the canvas region with the expansion factor           */
1763 /*      applied, and compute region from the original window cut        */
1764 /*      down to the target canvas.                                      */
1765 /* -------------------------------------------------------------------- */
1766     kdu_dims view_dims, region;
1767     int nBytesPerPixel = GDALGetDataTypeSize(poJDS->eDT) / 8;
1768     int nPrecision = 0;
1769 
1770     try
1771     {
1772         // Ensure we are working against full res
1773         poJDS->poCodestream->apply_input_restrictions( 0, 0, 0, 0, nullptr );
1774 
1775         view_dims = poJDS->poDecompressor->get_rendered_image_dims(
1776             *poJDS->poCodestream, &channels,
1777             -1, nLevel, exp_numerator, exp_denominator );
1778 
1779         double x_ratio, y_ratio;
1780         x_ratio = view_dims.size.x / (double) poDS->GetRasterXSize();
1781         y_ratio = view_dims.size.y / (double) poDS->GetRasterYSize();
1782 
1783         region = rr_win;
1784 
1785         region.pos.x = (int) ceil(region.pos.x * x_ratio);
1786         region.pos.y = (int) ceil(region.pos.y * y_ratio);
1787         region.size.x = (int) ceil(region.size.x * x_ratio);
1788         region.size.y = (int) ceil(region.size.y * y_ratio);
1789 
1790         region.size.x = MIN(region.size.x,nBufXSize);
1791         region.size.y = MIN(region.size.y,nBufYSize);
1792 
1793         if( region.pos.x + region.size.x > view_dims.size.x )
1794             region.size.x = view_dims.size.x - region.pos.x;
1795         if( region.pos.y + region.size.y > view_dims.size.y )
1796             region.size.y = view_dims.size.y - region.pos.y;
1797 
1798         region.pos += view_dims.pos;
1799 
1800         CPLAssert( nBytesPerPixel == 1 || nBytesPerPixel == 2  );
1801 
1802         if( poJDS->poCodestream->get_bit_depth(0) > 16 )
1803             nPrecision = 16;
1804     }
1805     catch(...)
1806     {
1807         // The error handler should already have posted an error message.
1808         return GARIO_ERROR;
1809     }
1810 
1811 /* ==================================================================== */
1812 /*      Now we process the available cached jpeg2000 data into          */
1813 /*      imagery.  The kdu_region_decompressor only seems to support     */
1814 /*      reading back one or three components at a time, we may need     */
1815 /*      to do several processing passes to get the bands we             */
1816 /*      want. We try to do groups of three were possible, and handle    */
1817 /*      the rest one band at a time.                                    */
1818 /* ==================================================================== */
1819     int nBandsCompleted = 0;
1820 
1821     while( nBandsCompleted < nBandCount )
1822     {
1823 /* -------------------------------------------------------------------- */
1824 /*      Set up channel list requested.                                  */
1825 /* -------------------------------------------------------------------- */
1826         std::vector<int> component_indices;
1827         unsigned int i;
1828 
1829         if( nBandCount - nBandsCompleted >= 3 )
1830         {
1831             CPLDebug( "JPIPKAK", "process bands %d,%d,%d",
1832                       panBandMap[nBandsCompleted],
1833                       panBandMap[nBandsCompleted+1],
1834                       panBandMap[nBandsCompleted+2] );
1835 
1836             component_indices.push_back( panBandMap[nBandsCompleted++]-1 );
1837             component_indices.push_back( panBandMap[nBandsCompleted++]-1 );
1838             component_indices.push_back( panBandMap[nBandsCompleted++]-1 );
1839         }
1840         else
1841         {
1842             CPLDebug( "JPIPKAK", "process band %d",
1843                       panBandMap[nBandsCompleted] );
1844             component_indices.push_back( panBandMap[nBandsCompleted++]-1 );
1845         }
1846 
1847 /* -------------------------------------------------------------------- */
1848 /*      Apply region, channel and overview level restrictions.          */
1849 /* -------------------------------------------------------------------- */
1850         kdu_dims region_pass = region;
1851 
1852         poJDS->poCodestream->apply_input_restrictions(
1853             static_cast<int>(component_indices.size()), &(component_indices[0]),
1854             nLevel, nQualityLayers, &region_pass,
1855             KDU_WANT_CODESTREAM_COMPONENTS);
1856 
1857         channels.configure(*(poJDS->poCodestream));
1858 
1859         for( i=0; i < component_indices.size(); i++ )
1860             channels.source_components[i] = component_indices[i];
1861 
1862         kdu_dims incomplete_region = region_pass;
1863         kdu_coords origin = region_pass.pos;
1864 
1865         int bIsDecompressing = FALSE;
1866 
1867         CPLAcquireMutex(poJDS->pGlobalMutex, 100.0);
1868 
1869         try
1870         {
1871 
1872             bIsDecompressing = poJDS->poDecompressor->start(
1873                 *(poJDS->poCodestream),
1874                 &channels, -1, nLevel, nQualityLayers,
1875                 region_pass, exp_numerator, exp_denominator, TRUE);
1876 
1877             *pnxbufoff = 0;
1878             *pnybufoff = 0;
1879             *pnxbufsize = region_pass.access_size()->x;
1880             *pnybufsize = region_pass.access_size()->y;
1881 
1882             // Setup channel buffers
1883             std::vector<kdu_byte*> channel_bufs;
1884 
1885             for( i=0; i < component_indices.size(); i++ )
1886                 channel_bufs.push_back(
1887                     ((kdu_byte *) pBuf)
1888                     + (i+nBandsCompleted-component_indices.size()) * nBandSpace );
1889 
1890             int pixel_gap = nPixelSpace / nBytesPerPixel;
1891             int row_gap = nLineSpace / nBytesPerPixel;
1892 
1893             while ((bIsDecompressing == 1) || (incomplete_region.area() != 0))
1894             {
1895                 if( nBytesPerPixel == 1 )
1896                 {
1897                     bIsDecompressing = poJDS->poDecompressor->
1898                         process(&(channel_bufs[0]), false,
1899                                 pixel_gap, origin, row_gap, 1000000, 0,
1900                                 incomplete_region, region_pass,
1901                                 0, false );
1902                 }
1903                 else if( nBytesPerPixel == 2 )
1904                 {
1905                     bIsDecompressing = poJDS->poDecompressor->
1906                         process((kdu_uint16**) &(channel_bufs[0]), false,
1907                                 pixel_gap, origin, row_gap, 1000000, 0,
1908                                 incomplete_region, region_pass,
1909                                 nPrecision, false );
1910                 }
1911 
1912                 CPLDebug( "JPIPKAK",
1913                           "processed=%d,%d %dx%d   - incomplete=%d,%d %dx%d",
1914                           region_pass.pos.x, region_pass.pos.y,
1915                           region_pass.size.x, region_pass.size.y,
1916                           incomplete_region.pos.x, incomplete_region.pos.y,
1917                           incomplete_region.size.x, incomplete_region.size.y );
1918             }
1919 
1920             poJDS->poDecompressor->finish();
1921             CPLReleaseMutex(poJDS->pGlobalMutex);
1922         }
1923         catch(...)
1924         {
1925             poJDS->poDecompressor->finish();
1926             CPLReleaseMutex(poJDS->pGlobalMutex);
1927             // The error handler should already have posted an error message.
1928             return GARIO_ERROR;
1929         }
1930     } // nBandsCompleted < nBandCount
1931 
1932 /* -------------------------------------------------------------------- */
1933 /*      If the application buffer is of a different type than our       */
1934 /*      band we need to copy into the application buffer at this        */
1935 /*      point.                                                          */
1936 /*                                                                      */
1937 /*      We could optimize to only update affected area, but that is     */
1938 /*      always the whole area for the JPIP driver it seems.             */
1939 /* -------------------------------------------------------------------- */
1940     if( pAppBuf != pBuf )
1941     {
1942         int iY, iBand;
1943         GByte *pabySrc = (GByte *) pBuf;
1944         GByte *pabyDst = (GByte *) pAppBuf;
1945 
1946         for( iBand = 0; iBand < nBandCount; iBand++ )
1947         {
1948             for( iY = 0; iY < nBufYSize; iY++ )
1949             {
1950                 GDALCopyWords( pabySrc + nLineSpace * iY + nBandSpace * iBand,
1951                                poJDS->eDT, nPixelSpace,
1952                                pabyDst + nAppLineSpace*iY + nAppBandSpace*iBand,
1953                                eBufType, nAppPixelSpace,
1954                                nBufXSize );
1955             }
1956         }
1957     }
1958 
1959 /* -------------------------------------------------------------------- */
1960 /*      has there been any more data read while were have been processing?*/
1961 /* -------------------------------------------------------------------- */
1962     long size = 0;
1963     if (bHighPriority)
1964     {
1965         const long x = poJDS->nHighThreadByteCount - nDataRead;
1966         size = x;
1967     }
1968     else
1969     {
1970         const long x = poJDS->nLowThreadByteCount - nDataRead;
1971         size = x;
1972     }
1973 
1974     if ((bComplete) && (nSize == size))
1975         result = GARIO_COMPLETE;
1976     else
1977         result = GARIO_UPDATE;
1978 
1979     nDataRead += nSize;
1980 
1981     if( result == GARIO_ERROR )
1982         poJDS->bNeedReinitialize = TRUE;
1983 
1984     return result;
1985 }
1986 
1987 /************************************************************************/
1988 /*                          JPIPDataSegment()                           */
1989 /************************************************************************/
JPIPDataSegment()1990 JPIPDataSegment::JPIPDataSegment()
1991 {
1992     nId = 0;
1993     nAux = 0;
1994     nClassId = 0;
1995     nCodestream = 0;
1996     nOffset = 0;
1997     nLen = 0;
1998     pabyData = nullptr;
1999     bIsFinal = FALSE;
2000     bIsEOR = FALSE;
2001 }
2002 
2003 /************************************************************************/
2004 /*                          ~JPIPDataSegment()                          */
2005 /************************************************************************/
~JPIPDataSegment()2006 JPIPDataSegment::~JPIPDataSegment()
2007 {
2008     CPLFree(pabyData);
2009 }
2010 
2011 /************************************************************************/
2012 /*                           JPIPWorkerFunc()                           */
2013 /************************************************************************/
JPIPWorkerFunc(void * req)2014 static void JPIPWorkerFunc(void *req)
2015 {
2016     int nCurrentTransmissionLength = 2000;
2017     int nMinimumTransmissionLength = 2000;
2018 
2019     JPIPRequest *pRequest = (JPIPRequest *)req;
2020     JPIPKAKDataset *poJDS =
2021         (JPIPKAKDataset*)(pRequest->poARIO->GetGDALDataset());
2022 
2023     int bPriority = pRequest->bPriority;
2024 
2025     CPLAcquireMutex(poJDS->pGlobalMutex, 100.0);
2026 
2027     CPLDebug( "JPIPKAK", "working thread starting." );
2028     // set the running status
2029     if (bPriority)
2030     {
2031         poJDS->bHighThreadRunning = 1;
2032         poJDS->bHighThreadFinished = 0;
2033     }
2034     else
2035     {
2036         poJDS->bLowThreadRunning = 1;
2037         poJDS->bLowThreadFinished = 0;
2038     }
2039 
2040     CPLReleaseMutex(poJDS->pGlobalMutex);
2041 
2042     CPLString osHeaders = "HEADERS=";
2043     osHeaders += "Accept: jpp-stream";
2044     CPLString osPersistent;
2045 
2046     osPersistent.Printf( "PERSISTENT=JPIPKAK:%p", poJDS );
2047 
2048     char *apszOptions[] = {
2049         (char *) osHeaders.c_str(),
2050         (char *) osPersistent.c_str(),
2051         nullptr
2052     };
2053 
2054     while( true )
2055     {
2056         // modulate the len= parameter to use the currently available bandwidth
2057         long nStart = clock();
2058 
2059         if (((bPriority) && (!poJDS->bHighThreadRunning)) || ((!bPriority) && (!poJDS->bLowThreadRunning)))
2060             break;
2061 
2062         // make jpip requests
2063         // adjust transmission length
2064         CPLString osCurrentRequest;
2065         osCurrentRequest.Printf("%s&len=%i", pRequest->osRequest.c_str(), nCurrentTransmissionLength);
2066         CPLHTTPResult *psResult = CPLHTTPFetch(osCurrentRequest, apszOptions);
2067         if (psResult->nDataLen == 0)
2068         {
2069             CPLAcquireMutex(poJDS->pGlobalMutex, 100.0);
2070             if( psResult->pszErrBuf )
2071                 pRequest->poARIO->
2072                     osErrorMsg.Printf( "zero data returned from server, timeout?\n%s", psResult->pszErrBuf );
2073             else
2074                 pRequest->poARIO->
2075                     osErrorMsg =  "zero data returned from server, timeout?";
2076 
2077             // status is not being set, always zero in cpl_http
2078             CPLDebug("JPIPWorkerFunc", "zero data returned from server");
2079             CPLReleaseMutex(poJDS->pGlobalMutex);
2080             CPLHTTPDestroyResult(psResult);
2081             break;
2082         }
2083 
2084         if( psResult->pszContentType != nullptr )
2085             CPLDebug( "JPIPKAK", "Content-type: %s", psResult->pszContentType );
2086 
2087         if( psResult->pszContentType != nullptr
2088             && strstr(psResult->pszContentType,"html") != nullptr )
2089         {
2090             CPLDebug( "JPIPKAK", "%s", psResult->pabyData );
2091         }
2092 
2093         int bytes = psResult->nDataLen;
2094         long nEnd = clock();
2095 
2096         if ((nEnd - nStart) > 0)
2097             nCurrentTransmissionLength = (int) MAX(bytes / ((1.0 * (nEnd - nStart)) / CLOCKS_PER_SEC), nMinimumTransmissionLength);
2098 
2099         CPLAcquireMutex(poJDS->pGlobalMutex, 100.0);
2100 
2101         int bError;
2102         int bComplete = ((JPIPKAKDataset*)pRequest->poARIO->GetGDALDataset())->ReadFromInput(psResult->pabyData, psResult->nDataLen, bError);
2103         if (bPriority)
2104             poJDS->nHighThreadByteCount += psResult->nDataLen;
2105         else
2106             poJDS->nLowThreadByteCount += psResult->nDataLen;
2107 
2108         pRequest->poARIO->SetComplete(bComplete);
2109 
2110         CPLReleaseMutex(poJDS->pGlobalMutex);
2111         CPLHTTPDestroyResult(psResult);
2112 
2113         if (bComplete || bError )
2114             break;
2115     }
2116 
2117     CPLAcquireMutex(poJDS->pGlobalMutex, 100.0);
2118 
2119     CPLDebug( "JPIPKAK", "Worker shutting down." );
2120 
2121     if (bPriority)
2122     {
2123         poJDS->bHighThreadRunning = 0;
2124         poJDS->bHighThreadFinished = 1;
2125     }
2126     else
2127     {
2128         poJDS->bLowThreadRunning = 0;
2129         poJDS->bLowThreadFinished = 1;
2130     }
2131 
2132     CPLReleaseMutex(poJDS->pGlobalMutex);
2133 
2134     // end of thread
2135     delete pRequest;
2136 }
2137