start using API style in openJPIP library
[openjpeg.git] / applications / jpip / libopenjpip / jpip_parser.c
1 /*
2  * $Id$
3  *
4  * Copyright (c) 2002-2011, Communications and Remote Sensing Laboratory, Universite catholique de Louvain (UCL), Belgium
5  * Copyright (c) 2002-2011, Professor Benoit Macq
6  * Copyright (c) 2010-2011, Kaori Hagihara 
7  * Copyright (c) 2011,      Lucian Corlaciu, GSoC
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include <stdio.h>
33 #include <string.h>
34 #include <math.h>
35 #include "jpip_parser.h"
36 #include "channel_manager.h"
37 #include "imgreg_manager.h"
38
39 #ifdef SERVER
40 #include "fcgi_stdio.h"
41 #define logstream FCGI_stdout
42 #else
43 #define FCGI_stdout stdout
44 #define FCGI_stderr stderr
45 #define logstream stderr
46 #endif //SERVER
47
48
49 bool identify_target( query_param_t query_param, targetlist_param_t *targetlist, target_param_t **target)
50 {
51   if( query_param.tid[0] !='\0' && strcmp( query_param.tid, "0") != 0 ){
52     if( query_param.cid[0] != '\0'){
53       fprintf( FCGI_stdout, "Reason: Target can not be specified both through tid and cid\r\n");
54       fprintf( FCGI_stdout, "Status: 400\r\n");
55       return false;
56     }
57     if( ( *target = search_targetBytid( query_param.tid, targetlist)))
58       return true;
59   }
60
61   if( query_param.target[0] !='\0')
62     if( !( *target = search_target( query_param.target, targetlist)))
63       if(!( *target = gene_target( targetlist, query_param.target)))
64         return false;
65
66   if( *target){
67     fprintf( FCGI_stdout, "JPIP-tid: %s\r\n", (*target)->tid);
68     return true;
69   }
70   else{
71     fprintf( FCGI_stdout, "Reason: target not found\r\n");
72     fprintf( FCGI_stdout, "Status: 400\r\n");
73     return false;
74   }
75 }
76
77 bool associate_channel( query_param_t    query_param, 
78                         sessionlist_param_t *sessionlist,
79                         session_param_t **cursession, 
80                         channel_param_t **curchannel)
81 {
82   if( search_session_and_channel( query_param.cid, sessionlist, cursession, curchannel)){
83     
84     if( !query_param.cnew)
85       set_channel_variable_param( query_param, *curchannel);
86   }
87   else{
88     fprintf( FCGI_stderr, "Error: process canceled\n");
89     return false;
90   }
91   return true;
92 }
93
94 bool open_channel( query_param_t query_param, 
95                    sessionlist_param_t *sessionlist,
96                    target_param_t *target,
97                    session_param_t **cursession, 
98                    channel_param_t **curchannel)
99 {
100   cachemodel_param_t *cachemodel = NULL;
101
102   if( target){
103     if( !(*cursession))
104       *cursession = gene_session( sessionlist);
105     if( !( cachemodel = search_cachemodel( target, (*cursession)->cachemodellist)))
106       if( !(cachemodel = gene_cachemodel( (*cursession)->cachemodellist, target, query_param.return_type==JPPstream)))
107         return false;
108   }
109   else
110     if( *curchannel)
111       cachemodel = (*curchannel)->cachemodel;
112
113   *curchannel = gene_channel( query_param, cachemodel, (*cursession)->channellist);
114   if( *curchannel == NULL)
115     return false;
116
117   return true;
118 }
119
120 bool close_channel( query_param_t query_param, 
121                     sessionlist_param_t *sessionlist,
122                     session_param_t **cursession, 
123                     channel_param_t **curchannel)
124 {
125   if( query_param.cclose[0][0] =='*'){
126 #ifndef SERVER
127     fprintf( logstream, "local log: close all\n");
128 #endif
129     // all channels associatd with the session will be closed
130     if( !delete_session( cursession, sessionlist))
131       return false;
132   }
133   else{
134     // check if all entry belonging to the same session
135     int i=0;
136     while( query_param.cclose[i][0] !='\0'){
137       
138       // In case of the first entry of close cid
139       if( *cursession == NULL){
140         if( !search_session_and_channel( query_param.cclose[i], sessionlist, cursession, curchannel))
141           return false;
142       }
143       else // second or more entry of close cid
144         if( !(*curchannel=search_channel( query_param.cclose[i], (*cursession)->channellist))){
145           fprintf( FCGI_stdout, "Reason: Cclose id %s is from another session\r\n", query_param.cclose[i]); 
146           return false;
147         }
148       i++;
149     }
150     // delete channels
151     i=0;
152     while( query_param.cclose[i][0] !='\0'){
153       
154       *curchannel = search_channel( query_param.cclose[i], (*cursession)->channellist);
155       delete_channel( curchannel, (*cursession)->channellist);
156       i++;
157     }
158     
159     if( (*cursession)->channellist->first == NULL || (*cursession)->channellist->last == NULL)
160       // In case of empty session
161       delete_session( cursession, sessionlist);
162   }
163   return true;
164 }
165
166
167 /**
168  * enqueue tiles or precincts into the message queue
169  *
170  * @param[in] query_param structured query
171  * @param[in] msgqueue    message queue pointer  
172  */
173 void enqueue_imagedata( query_param_t query_param, msgqueue_param_t *msgqueue);
174
175 /**
176  * enqueue metadata bins into the message queue
177  *
178  * @param[in]     query_param  structured query
179  * @param[in]     metadatalist pointer to metadata bin list
180  * @param[in,out] msgqueue     message queue pointer  
181  */
182 void enqueue_metabins( query_param_t query_param, metadatalist_param_t *metadatalist, msgqueue_param_t *msgqueue);
183
184
185 bool gene_JPIPstream( query_param_t query_param,
186                       target_param_t *target,
187                       session_param_t *cursession, 
188                       channel_param_t *curchannel,
189                       msgqueue_param_t **msgqueue)
190 {
191   index_param_t *codeidx;
192   cachemodel_param_t *cachemodel;
193   
194   if( !cursession || !curchannel){ // stateless
195     if( !target)
196       return false;
197     if( !(cachemodel = gene_cachemodel( NULL, target, query_param.return_type==JPPstream)))
198       return false;
199     *msgqueue = gene_msgqueue( true, cachemodel);
200   }
201   else{ // session
202     cachemodel  = curchannel->cachemodel;
203     target = cachemodel->target;
204     *msgqueue = gene_msgqueue( false, cachemodel);
205   }
206   
207   codeidx = target->codeidx;
208
209   if( cachemodel->jppstream)
210     fprintf( FCGI_stdout, "Content-type: image/jpp-stream\r\n");
211   else
212     fprintf( FCGI_stdout, "Content-type: image/jpt-stream\r\n");
213   
214   if( query_param.layers != -1){
215     if( query_param.layers  > codeidx->COD.numOflayers){
216       fprintf( FCGI_stdout, "JPIP-layers: %d\r\n", codeidx->COD.numOflayers);
217       query_param.layers = codeidx->COD.numOflayers;
218     }
219   }
220
221   //meta
222   if( query_param.box_type[0][0] != 0  && query_param.len != 0)
223     enqueue_metabins( query_param, codeidx->metadatalist, *msgqueue); 
224
225   // image codestream
226   if( query_param.fx > 0 && query_param.fy > 0){
227     if( !cachemodel->mhead_model && query_param.len != 0)
228       enqueue_mainheader( *msgqueue);
229     enqueue_imagedata( query_param, *msgqueue);
230   }
231   return true;
232 }
233
234
235 /**
236  * enqueue precinct data-bins into the queue
237  *
238  * @param[in] xmin      min x coordinate in the tile at the decomposition level
239  * @param[in] xmax      max x coordinate in the tile at the decomposition level
240  * @param[in] ymin      min y coordinate in the tile at the decomposition level
241  * @param[in] ymax      max y coordinate in the tile at the decomposition level
242  * @param[in] tile_id   tile index
243  * @param[in] level     decomposition level
244  * @param[in] lastcomp  last component number
245  * @param[in] comps     pointer to the array that stores the requested components
246  * @param[in] layers    number of quality layers
247  * @param[in] msgqueue  message queue
248  * @return
249  */
250 void enqueue_precincts( int xmin, int xmax, int ymin, int ymax, int tile_id, int level, int lastcomp, bool *comps, int layers, msgqueue_param_t *msgqueue);
251
252 /**
253  * enqueue all precincts inside a tile into the queue
254  *
255  * @param[in] tile_id   tile index
256  * @param[in] level     decomposition level
257  * @param[in] lastcomp  last component number
258  * @param[in] comps     pointer to the array that stores the requested components
259  * @param[in] layers    number of quality layers
260  * @param[in] msgqueue  message queue
261  * @return
262  */
263 void enqueue_allprecincts( int tile_id, int level, int lastcomp, bool *comps, int layers, msgqueue_param_t *msgqueue);
264
265 void enqueue_imagedata( query_param_t query_param, msgqueue_param_t *msgqueue)
266 {
267   index_param_t *codeidx;
268   imgreg_param_t imgreg;
269   range_param_t tile_Xrange, tile_Yrange;
270   int u, v, tile_id;
271   int xmin, xmax, ymin, ymax;
272   int numOfreslev;
273
274   codeidx = msgqueue->cachemodel->target->codeidx;
275
276   if( !(msgqueue->cachemodel->jppstream)  &&  get_nmax( codeidx->tilepart) == 1) // normally not the case
277     numOfreslev = 1;
278   else
279     numOfreslev = codeidx->COD.numOfdecomp+1;
280
281   imgreg  = map_viewin2imgreg( query_param.fx, query_param.fy, 
282                                query_param.rx, query_param.ry, query_param.rw, query_param.rh,
283                                codeidx->SIZ.XOsiz, codeidx->SIZ.YOsiz, codeidx->SIZ.Xsiz, codeidx->SIZ.Ysiz, 
284                                numOfreslev );
285
286   if( query_param.len == 0)
287     return;
288
289   for( u=0, tile_id=0; u<codeidx->SIZ.YTnum; u++){
290     tile_Yrange = get_tile_Yrange( codeidx->SIZ, tile_id, imgreg.level);
291     
292     for( v=0; v<codeidx->SIZ.XTnum; v++, tile_id++){
293       tile_Xrange = get_tile_Xrange( codeidx->SIZ, tile_id, imgreg.level);
294         
295       if( tile_Xrange.minvalue < tile_Xrange.maxvalue && tile_Yrange.minvalue < tile_Yrange.maxvalue){
296         if( tile_Xrange.maxvalue <= imgreg.xosiz + imgreg.ox || 
297             tile_Xrange.minvalue >= imgreg.xosiz + imgreg.ox + imgreg.sx ||
298             tile_Yrange.maxvalue <= imgreg.yosiz + imgreg.oy || 
299             tile_Yrange.minvalue >= imgreg.yosiz + imgreg.oy + imgreg.sy) {
300           //printf("Tile completely excluded from view-window %d\n", tile_id);
301           // Tile completely excluded from view-window
302         }
303         else if( tile_Xrange.minvalue >= imgreg.xosiz + imgreg.ox && 
304                  tile_Xrange.maxvalue <= imgreg.xosiz + imgreg.ox + imgreg.sx && 
305                  tile_Yrange.minvalue >= imgreg.yosiz + imgreg.oy && 
306                  tile_Yrange.maxvalue <= imgreg.yosiz + imgreg.oy + imgreg.sy) {
307           // Tile completely contained within view-window
308           // high priority
309           //printf("Tile completely contained within view-window %d\n", tile_id);
310           if( msgqueue->cachemodel->jppstream){
311             enqueue_tileheader( tile_id, msgqueue);
312             enqueue_allprecincts( tile_id, imgreg.level, query_param.lastcomp, query_param.comps, query_param.layers, msgqueue);
313           }
314           else
315             enqueue_tile( tile_id, imgreg.level, msgqueue);
316         }
317         else{
318           // Tile partially overlaps view-window
319           // low priority
320           //printf("Tile partially overlaps view-window %d\n", tile_id);
321           if( msgqueue->cachemodel->jppstream){
322             enqueue_tileheader( tile_id, msgqueue);
323
324             xmin = tile_Xrange.minvalue >= imgreg.xosiz + imgreg.ox ? 0 : imgreg.xosiz + imgreg.ox - tile_Xrange.minvalue;
325             xmax = tile_Xrange.maxvalue <= imgreg.xosiz + imgreg.ox + imgreg.sx ? tile_Xrange.maxvalue - tile_Xrange.minvalue -1 : imgreg.xosiz + imgreg.ox + imgreg.sx - tile_Xrange.minvalue -1;
326             ymin = tile_Yrange.minvalue >= imgreg.yosiz + imgreg.oy ? 0 : imgreg.yosiz + imgreg.oy - tile_Yrange.minvalue;
327             ymax = tile_Yrange.maxvalue <= imgreg.yosiz + imgreg.oy + imgreg.sy ? tile_Yrange.maxvalue - tile_Yrange.minvalue -1 : imgreg.yosiz + imgreg.oy + imgreg.sy - tile_Yrange.minvalue -1;
328             enqueue_precincts( xmin, xmax, ymin, ymax, tile_id, imgreg.level, query_param.lastcomp, query_param.comps, query_param.layers, msgqueue);
329           }
330           else
331             enqueue_tile( tile_id, imgreg.level, msgqueue);
332         }
333       }
334     }
335   }
336 }
337
338
339 void enqueue_precincts( int xmin, int xmax, int ymin, int ymax, int tile_id, int level, int lastcomp, bool *comps, int layers, msgqueue_param_t *msgqueue)
340 {
341   index_param_t *codeidx;
342   int c, u, v, res_lev, dec_lev;
343   int seq_id;
344   Byte4_t XTsiz, YTsiz;
345   Byte4_t XPsiz, YPsiz;
346   Byte4_t xminP, xmaxP, yminP, ymaxP;
347
348   codeidx  = msgqueue->cachemodel->target->codeidx;
349
350   for( c=0; c<codeidx->SIZ.Csiz; c++)
351     if( lastcomp == -1 /*all*/ || ( c<=lastcomp && comps[c])){
352       seq_id = 0;
353       for( res_lev=0, dec_lev=codeidx->COD.numOfdecomp; dec_lev>=level; res_lev++, dec_lev--){
354         
355         XTsiz = get_tile_XSiz( codeidx->SIZ, tile_id, dec_lev);
356         YTsiz = get_tile_YSiz( codeidx->SIZ, tile_id, dec_lev);
357         
358         XPsiz = ( codeidx->COD.Scod & 0x01) ? codeidx->COD.XPsiz[ res_lev] : XTsiz;
359         YPsiz = ( codeidx->COD.Scod & 0x01) ? codeidx->COD.YPsiz[ res_lev] : YTsiz;
360           
361         for( u=0; u<ceil((double)YTsiz/(double)YPsiz); u++){
362           yminP = u*YPsiz;
363           ymaxP = (u+1)*YPsiz-1;
364           if( YTsiz <= ymaxP)
365             ymaxP = YTsiz-1;
366           
367           for( v=0; v<ceil((double)XTsiz/(double)XPsiz); v++, seq_id++){
368             xminP = v*XPsiz;
369             xmaxP = (v+1)*XPsiz-1;
370             if( XTsiz <= xmaxP)
371               xmaxP = XTsiz-1;
372             
373             if( xmaxP < xmin || xminP > xmax || ymaxP < ymin || yminP > ymax){
374               // Precinct completely excluded from view-window
375             }
376             else if( xminP >= xmin && xmaxP <= xmax && yminP >= ymin && ymaxP <= ymax){
377               // Precinct completely contained within view-window
378               // high priority
379               enqueue_precinct( seq_id, tile_id, c, (dec_lev>level)?-1:layers, msgqueue);
380             }
381             else{
382               // Precinct partially overlaps view-window
383               // low priority
384               enqueue_precinct( seq_id, tile_id, c, (dec_lev>level)?-1:layers, msgqueue);
385             }
386           }
387         }
388       }
389     }
390 }
391
392 void enqueue_allprecincts( int tile_id, int level, int lastcomp, bool *comps, int layers, msgqueue_param_t *msgqueue)
393 {
394   index_param_t *codeidx;
395   int c, i, res_lev, dec_lev;
396   int seq_id;
397   Byte4_t XTsiz, YTsiz;
398   Byte4_t XPsiz, YPsiz;
399
400   codeidx  = msgqueue->cachemodel->target->codeidx;
401
402   for( c=0; c<codeidx->SIZ.Csiz; c++)
403     if( lastcomp == -1 /*all*/ || ( c<=lastcomp && comps[c])){
404       seq_id = 0;
405       for( res_lev=0, dec_lev=codeidx->COD.numOfdecomp; dec_lev>=level; res_lev++, dec_lev--){
406         
407         XTsiz = get_tile_XSiz( codeidx->SIZ, tile_id, dec_lev);
408         YTsiz = get_tile_YSiz( codeidx->SIZ, tile_id, dec_lev);
409         
410         XPsiz = ( codeidx->COD.Scod & 0x01) ? codeidx->COD.XPsiz[ res_lev] : XTsiz;
411         YPsiz = ( codeidx->COD.Scod & 0x01) ? codeidx->COD.YPsiz[ res_lev] : YTsiz;
412         
413         for( i=0; i<ceil((double)YTsiz/(double)YPsiz)*ceil((double)XTsiz/(double)XPsiz); i++, seq_id++)
414           enqueue_precinct( seq_id, tile_id, c, (dec_lev>level)?-1:layers, msgqueue);
415       }
416     }
417 }
418
419 void enqueue_metabins( query_param_t query_param, metadatalist_param_t *metadatalist, msgqueue_param_t *msgqueue)
420 {
421   int i;
422   for( i=0; query_param.box_type[i][0]!=0 && i<MAX_NUMOFBOX; i++){
423     if( query_param.box_type[i][0] == '*'){
424       // not implemented
425     }
426     else{
427       int idx = search_metadataidx( query_param.box_type[i], metadatalist);
428
429       if( idx != -1)
430         enqueue_metadata( idx, msgqueue);
431     }
432   }
433 }