1 /**************************************************************************\
2  * Copyright (c) Kongsberg Oil & Gas Technologies AS
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of the copyright holder nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32 
33 /*!
34   \class SoGLBigImage include/Inventor/misc/SoGLBigImage.h
35   \brief The SoGLBigImage class is used to handle 2D OpenGL textures of any size.
36 
37   This class is internal. To enable/disable big-image texture handling
38   you should use the SoTextureScalePolicy node.
39 
40   The technique used is the following: split the texture into x*y
41   equal size blocks. All these subtextures are of size 2^n, and are
42   typically quite small (256x256 or smaller).  Each triangle is
43   clipped, based on the texture coordinates, into several smaller
44   triangles. The triangles will then be guaranteed to use only one
45   subtexture. Then the triangles are projected onto the screen, and
46   the maximum projected size for each subtexture is
47   calculated. Subtextures outside the viewport will be culled. Each
48   subtexture is then sampled down to a 2^n value close to the
49   projected size, and a GL texture is created with this size. This GL
50   texture is used when rendering triangles that are clipped into that
51   subtexture.
52 
53   Mipmapping is disabled for SoGLBigImage. Aliasing problems shouldn't
54   occur because the projected size of the texture is calculated on the
55   fly.  When mipmapping is enabled, the amount of texture memory used
56   is doubled, and creating the texture object is much slower, so we
57   avoid this for SoGLBigImage.
58 
59   \COIN_CLASS_EXTENSION
60 
61   \since Coin 2.0
62 */
63 
64 // *************************************************************************
65 
66 #include <Inventor/misc/SoGLBigImage.h>
67 #include "coindefs.h"
68 
69 #include <cstdlib>
70 #include <cstdio>
71 #include <cstring>
72 #include <cassert>
73 
74 #ifdef HAVE_CONFIG_H
75 #include "config.h"
76 #endif // HAVE_CONFIG_H
77 
78 #include <Inventor/C/threads/storage.h>
79 #include <Inventor/SbImage.h>
80 #include <Inventor/elements/SoGLCacheContextElement.h>
81 #include <Inventor/elements/SoGLDisplayList.h>
82 #include <Inventor/errors/SoDebugError.h>
83 #include <Inventor/system/gl.h>
84 
85 #ifdef COIN_THREADSAFE
86 #include <Inventor/threads/SbMutex.h>
87 #endif // COIN_THREADSAFE
88 
89 #include "tidbitsp.h"
90 #include "rendering/SoGL.h"
91 
92 // *************************************************************************
93 
94 // the number of subtextures that can be changed (resized) each frame.
95 // By keeping this number small, we avoid slow updates when zooming in
96 // on an image, as only few textures are changed each frame.
97 static int CHANGELIMIT = 4;
98 
99 // the texturequality limit when linear filtering will be used
100 #define LINEAR_LIMIT 0.1f
101 
102 typedef struct {
103   SbVec2s imagesize;
104   SbVec2s glimagesize;
105   SbVec2s remain;
106   SbVec2f tcmul;
107   SbVec2s dim;
108   SbVec2s currentdim;
109 
110   unsigned char * tmpbuf;
111   int tmpbufsize;
112   SoGLImage ** glimagearray;
113   SbImage ** imagearray;
114   int * glimagediv;
115   uint32_t * glimageage;
116   int changecnt;
117   unsigned int * averagebuf;
118 } SoGLBigImageTls;
119 
120 class SoGLBigImageP {
121 public:
122   SoGLBigImageP(void);
123   ~SoGLBigImageP();
124 
125   static SoType classTypeId;
126 
127   cc_storage * storage;
128 #ifdef COIN_THREADSAFE
129   SbMutex mutex;
130 #endif // !COIN_THREADSAFE
131   unsigned char ** cache;
132   SbVec2s * cachesize;
133   int numcachelevels;
134 
135   // inline for speed
getTls(void)136   inline SoGLBigImageTls * getTls(void) {
137     return (SoGLBigImageTls*) cc_storage_get(this->storage);
138   }
139 
lock(void)140   inline void lock(void) {
141 #ifdef COIN_THREADSAFE
142     this->mutex.lock();
143 #endif // COIN_THREADSAFE
144   }
unlock(void)145   inline void unlock(void) {
146 #ifdef COIN_THREADSAFE
147     this->mutex.unlock();
148 #endif // COIN_THREADSAFE
149   }
150 
151   void copySubImage(SoGLBigImageTls * tls,
152                     const int idx,
153                     const unsigned char * src,
154                     const SbVec2s & fullsize,
155                     const int nc,
156                     unsigned char * dst,
157                     const int div,
158                     const int level);
159   void copyResizeSubImage(SoGLBigImageTls * tls,
160                           const int idx,
161                           const unsigned char * src,
162                           const SbVec2s & fullsize,
163                           const int nc,
164                           unsigned char * dst,
165                           const SbVec2s & targetsize);
166   void resetAllTls(SoState * state);
167   void resetCache(void);
168   static void reset(SoGLBigImageTls * tls, SoState * state = NULL);
169   static void unrefOldDL(SoGLBigImageTls * tls, SoState * state, const uint32_t maxage);
170   void createCache(const unsigned char * bytes, const SbVec2s size, const int nc);
171 };
172 
173 SoType SoGLBigImageP::classTypeId STATIC_SOTYPE_INIT;
174 
soglbigimagep_cleanup(void)175 static void soglbigimagep_cleanup(void)
176 {
177   SoGLBigImageP::classTypeId STATIC_SOTYPE_INIT;
178   CHANGELIMIT = 4;
179 }
180 
181 static void
soglbigimagetls_construct(void * closure)182 soglbigimagetls_construct(void * closure)
183 {
184   SoGLBigImageTls * storage = (SoGLBigImageTls*) closure;
185   storage->imagesize.setValue(0, 0);
186   storage->remain.setValue(0, 0);
187   storage->dim.setValue(0, 0);
188   storage->currentdim.setValue(0, 0);
189   storage->tmpbuf = NULL;
190   storage->tmpbufsize = 0;
191   storage->glimagearray = NULL;
192   storage->imagearray = NULL;
193   storage->glimagediv = NULL;
194   storage->glimageage = NULL;
195   storage->averagebuf = NULL;
196 }
197 
198 static void
soglbigimagetls_destruct(void * closure)199 soglbigimagetls_destruct(void * closure)
200 {
201   SoGLBigImageTls * tls = (SoGLBigImageTls*) closure;
202   SoGLBigImageP::reset(tls, NULL);
203 
204   // these are not destructed in reset()
205   delete[] tls->tmpbuf;
206   delete[] tls->averagebuf;
207 }
208 
209 #define PRIVATE(obj) (obj->pimpl)
210 
211 // *************************************************************************
212 
213 /*!
214   Constructor.
215 */
SoGLBigImage(void)216 SoGLBigImage::SoGLBigImage(void)
217 {
218   PRIVATE(this) = new SoGLBigImageP;
219 }
220 
221 /*!
222   Destructor.
223 */
~SoGLBigImage()224 SoGLBigImage::~SoGLBigImage()
225 {
226   PRIVATE(this)->resetCache();
227   delete PRIVATE(this);
228 }
229 
230 void
unref(SoState * state)231 SoGLBigImage::unref(SoState * state)
232 {
233   PRIVATE(this)->resetAllTls(state);
234   inherited::unref(state);
235 }
236 
237 /*!
238   \COININTERNAL
239 */
240 void
initClass(void)241 SoGLBigImage::initClass(void)
242 {
243   assert(SoGLBigImageP::classTypeId.isBad());
244   SoGLBigImageP::classTypeId =
245     SoType::createType(SoGLImage::getClassTypeId(), SbName("GLBigImage"));
246   coin_atexit((coin_atexit_f*) soglbigimagep_cleanup, CC_ATEXIT_NORMAL);
247 }
248 
249 // Doc in superclass.
250 SoType
getClassTypeId(void)251 SoGLBigImage::getClassTypeId(void)
252 {
253   assert(!SoGLBigImageP::classTypeId.isBad());
254   return SoGLBigImageP::classTypeId;
255 }
256 
257 // Doc in superclass.
258 SoType
getTypeId(void) const259 SoGLBigImage::getTypeId(void) const
260 {
261   return SoGLBigImage::getClassTypeId();
262 }
263 
264 void
setData(const SbImage * image,const Wrap wraps,const Wrap wrapt,const float quality,const int border,SoState * createinstate)265 SoGLBigImage::setData(const SbImage * image,
266                       const Wrap wraps,
267                       const Wrap wrapt,
268                       const float quality,
269                       const int border,
270                       SoState * createinstate)
271 {
272   if (createinstate) {
273     SoDebugError::postWarning("SoGLBigImage::setData",
274                               "createinstate must be NULL for SoGLBigImage");
275   }
276   delete PRIVATE(this);
277   PRIVATE(this) = new SoGLBigImageP;
278   inherited::setData(image, wraps, wrapt, quality, border, NULL);
279 }
280 
281 void
setData(const SbImage * image,const Wrap wraps,const Wrap wrapt,const Wrap wrapr,const float quality,const int border,SoState * createinstate)282 SoGLBigImage::setData(const SbImage * image,
283                       const Wrap wraps,
284                       const Wrap wrapt,
285                       const Wrap wrapr,
286                       const float quality,
287                       const int border,
288                       SoState * createinstate)
289 {
290   if (createinstate) {
291     SoDebugError::postWarning("SoGLBigImage::setData",
292                               "createinstate must be NULL for SoGLBigImage");
293   }
294   delete PRIVATE(this);
295   PRIVATE(this) = new SoGLBigImageP;
296   inherited::setData(image, wraps, wrapt, wrapr, quality, border, NULL);
297 }
298 
299 
300 SoGLDisplayList *
getGLDisplayList(SoState * COIN_UNUSED_ARG (state))301 SoGLBigImage::getGLDisplayList(SoState * COIN_UNUSED_ARG(state))
302 {
303   return NULL;
304 }
305 
306 int
initSubImages(const SbVec2s & subimagesize) const307 SoGLBigImage::initSubImages(const SbVec2s & subimagesize) const
308 {
309   SoGLBigImageTls * tls = PRIVATE(this)->getTls();
310 
311   tls->changecnt = 0;
312   if (subimagesize == tls->imagesize &&
313       tls->dim[0] > 0) return tls->dim[0] * tls->dim[1];
314 
315   tls->imagesize = subimagesize;
316   tls->glimagesize[0] = coin_geq_power_of_two(tls->imagesize[0]);
317   tls->glimagesize[1] = coin_geq_power_of_two(tls->imagesize[1]);
318 
319   // FIXME: hardcoding for maximum 265x256 tiles is a bad strategy, as
320   // it will often give bad performance vs larger tile sizes.
321   //
322   // pederb has the following input on this issue:
323   // ------------8<--------- [snip] --------------------------8<-----
324   // That part of SoGLBigImage should be recoded. We should use a quad
325   // tree instead, so that the number of subtextures depends on the
326   // needed resolution. Right now the number of subtextures is static
327   // for a texture. This can lead to slow rendering when the entire
328   // texture is viewed from a long distance.
329   //
330   // We should also precalculate the triangle clipping done on this
331   // quadtree.  This would lead to much faster rendering on models
332   // with many triangles (the rendering is very slow per triangle
333   // now).
334   //
335   // As a temporary workaround it might be possible to calculate a new
336   // subtexture size, based on the size of the original image. We
337   // could base this on maximum number of subtextures to create or
338   // something.
339   // ------------8<--------- [snip] --------------------------8<-----
340   //
341   // Note also that there's hardcoding for 256x256 in
342   // src/shapenodes/soshape_bigtexture.cpp's beginShape().
343   //
344   // 20050701 mortene.
345 
346   if (tls->glimagesize[0] > tls->imagesize[0] && tls->glimagesize[0] >= 256) {
347     int diff = tls->imagesize[0] - (tls->glimagesize[0]>>1);
348     float ratio = float(diff) / float(tls->glimagesize[0]>>1);
349     if (ratio < 0.3) tls->glimagesize[0] >>= 1;
350   }
351 
352   if (tls->glimagesize[1] > tls->imagesize[1] && tls->glimagesize[1] >= 256) {
353     int diff = tls->imagesize[1] - (tls->glimagesize[1]>>1);
354     float ratio = float(diff) / float(tls->glimagesize[1]>>1);
355     if (ratio < 0.3) tls->glimagesize[1] >>= 1;
356   }
357 
358   SbVec2s size(0,0);
359   int nc = 0;
360 
361   if (this->getImage() != NULL)
362     (void)(this->getImage()->getValue(size, nc));
363 
364   tls->dim[0] = size[0] / subimagesize[0];
365   tls->dim[1] = size[1] / subimagesize[1];
366 
367   tls->remain[0] = size[0] % subimagesize[0];
368   if (tls->remain[0]) tls->dim[0] += 1;
369   tls->remain[1] = size[1] % subimagesize[1];
370   if (tls->remain[1]) tls->dim[1] += 1;
371 
372   tls->tcmul[0] = float(tls->dim[0] * subimagesize[0]) / float(size[0]);
373   tls->tcmul[1] = float(tls->dim[1] * subimagesize[1]) / float(size[1]);
374   return tls->dim[0] * tls->dim[1];
375 }
376 
377 void
handleSubImage(const int idx,SbVec2f & start,SbVec2f & end,SbVec2f & tcmul)378 SoGLBigImage::handleSubImage(const int idx,
379                              SbVec2f & start,
380                              SbVec2f & end,
381                              SbVec2f & tcmul)
382 {
383   SoGLBigImageTls * tls = PRIVATE(this)->getTls();
384 
385   SbVec2s pos(idx % tls->dim[0], idx / tls->dim[0]);
386   start[0] = float(pos[0]) / float(tls->dim[0]);
387   start[1] = float(pos[1]) / float(tls->dim[1]);
388   end[0] = float(pos[0]+1) / float(tls->dim[0]);
389   end[1] = float(pos[1]+1) / float(tls->dim[1]);
390 
391   start[0] *= tls->tcmul[0];
392   start[1] *= tls->tcmul[1];
393   end[0] *= tls->tcmul[0];
394   end[1] *= tls->tcmul[1];
395   tcmul = tls->tcmul;
396 }
397 
398 void
applySubImage(SoState * state,const int idx,const float quality,const SbVec2s & projsize)399 SoGLBigImage::applySubImage(SoState * state, const int idx,
400                             const float quality,
401                             const SbVec2s & projsize)
402 {
403   SbVec2s size;
404   int numcomponents;
405   unsigned char * bytes = this->getImage() ?
406     this->getImage()->getValue(size, numcomponents) : NULL;
407 
408   SoGLBigImageTls * tls = PRIVATE(this)->getTls();
409 
410   if (tls->currentdim != tls->dim) {
411     SoGLBigImageP::reset(tls, state);
412     tls->currentdim = tls->dim;
413     const int numimages = tls->dim[0] * tls->dim[1];
414 
415     tls->glimagediv = new int[numimages];
416     tls->glimagearray = new SoGLImage*[numimages];
417     tls->imagearray = new SbImage*[numimages];
418     tls->glimageage = new uint32_t[numimages];
419     for (int i = 0; i < numimages; i++) {
420       tls->glimagearray[i] = NULL;
421       tls->imagearray[i] = NULL;
422       tls->glimagediv[i] = 1;
423       tls->glimageage[i] = 0;
424     }
425 
426     int numbytes = tls->imagesize[0] * tls->imagesize[1] * numcomponents;
427     tls->averagebuf =
428       new unsigned int[numbytes ? numbytes : 1];
429 
430     // lock before testing/creating cache to avoid race conditions
431     PRIVATE(this)->lock();
432     if (PRIVATE(this)->cache == NULL) {
433       PRIVATE(this)->createCache(bytes, size, numcomponents);
434     }
435     PRIVATE(this)->unlock();
436   }
437 
438   int level = 0;
439   int div = 2;
440   while ((tls->imagesize[0]/div > projsize[0]) &&
441          (tls->imagesize[1]/div > projsize[1])) {
442     div <<= 1;
443     level++;
444   }
445   div >>= 1;
446 
447   if (tls->glimagearray[idx] == NULL ||
448       (tls->glimagediv[idx] != div && tls->changecnt < CHANGELIMIT)) {
449 
450     if (tls->glimagearray[idx] == NULL) {
451       tls->glimagearray[idx] = new SoGLImage();
452       if (tls->imagearray[idx] == NULL) {
453         tls->imagearray[idx] = new SbImage;
454       }
455     }
456     else {
457       tls->changecnt++;
458     }
459     tls->glimagediv[idx] = div;
460 
461     uint32_t flags = this->getFlags();
462     flags |= NO_MIPMAP|INVINCIBLE;
463 
464     if (flags & USE_QUALITY_VALUE) {
465       flags &= ~USE_QUALITY_VALUE;
466       if (quality >= LINEAR_LIMIT) {
467         flags |= LINEAR_MIN_FILTER|LINEAR_MAG_FILTER;
468       }
469     }
470     tls->glimagearray[idx]->setFlags(flags);
471 
472     SbVec2s actualsize(tls->glimagesize[0]/div,
473                        tls->glimagesize[1]/div);
474     if (bytes) {
475       int numbytes = actualsize[0]*actualsize[1]*numcomponents;
476       if (numbytes > tls->tmpbufsize) {
477         delete[] tls->tmpbuf;
478         tls->tmpbuf = new unsigned char[numbytes];
479         tls->tmpbufsize = numbytes;
480       }
481 
482       if (tls->glimagesize == tls->imagesize) {
483         PRIVATE(this)->copySubImage(tls,
484                            idx,
485                            bytes,
486                            size,
487                            numcomponents,
488                            tls->tmpbuf, div, level);
489       }
490       else {
491         PRIVATE(this)->copyResizeSubImage(tls,
492                                  idx,
493                                  bytes,
494                                  size,
495                                  numcomponents,
496                                  tls->tmpbuf,
497                                  actualsize);
498       }
499       tls->imagearray[idx]->setValue(actualsize, numcomponents, tls->tmpbuf);
500     }
501     else tls->imagearray[idx]->setValuePtr(SbVec2s(0,0), 0, NULL);
502 
503     // do not create-in-state, since the same thread might be used to
504     // render into more than one context
505     tls->glimagearray[idx]->setData(tls->imagearray[idx],
506                                     SoGLImage::CLAMP_TO_EDGE,
507                                     SoGLImage::CLAMP_TO_EDGE,
508                                     quality,
509                                     0, NULL);
510   }
511 
512   SoGLDisplayList * dl = tls->glimagearray[idx]->getGLDisplayList(state);
513   assert(dl);
514   tls->glimageage[idx] = 0;
515   SoGLImage::tagImage(state, tls->glimagearray[idx]);
516   this->resetAge();
517   dl->call(state);
518 }
519 
520 /*!
521   To avoid doing too much work in one frame, there is a limit on the
522   number of subtextures that can be changed each frame. If this limit
523   is exceeded, this function will return TRUE, otherwise FALSE.
524 
525   \sa setChangeLimit()
526 */
527 SbBool
exceededChangeLimit(void)528 SoGLBigImage::exceededChangeLimit(void)
529 {
530   return PRIVATE(this)->getTls()->changecnt >= CHANGELIMIT;
531 }
532 
533 /*!
534   Sets the change limit. Returns the old limit.
535 
536   \sa exceededChangeLimit()
537   \since Coin 2.3
538 */
539 int
setChangeLimit(const int limit)540 SoGLBigImage::setChangeLimit(const int limit)
541 {
542   int old = CHANGELIMIT;
543   CHANGELIMIT = limit;
544   return old;
545 }
546 
547 // needed for cc_storage_apply_to_all() callback
548 typedef struct {
549   uint32_t maxage;
550   SoState * state;
551 } soglbigimage_unrefolddl_data;
552 
553 // cc_storage_apply_to_all() callback
554 static void
soglbigimage_unrefolddl_cb(void * tls,void * closure)555 soglbigimage_unrefolddl_cb(void * tls, void * closure)
556 {
557   soglbigimage_unrefolddl_data * data =
558     (soglbigimage_unrefolddl_data *) closure;
559 
560   SoGLBigImageP::unrefOldDL((SoGLBigImageTls*)tls, data->state, data->maxage);
561 }
562 
563 // Documented in superclass. Overridden to handle age on subimages.
564 void
unrefOldDL(SoState * state,const uint32_t maxage)565 SoGLBigImage::unrefOldDL(SoState * state, const uint32_t maxage)
566 {
567   soglbigimage_unrefolddl_data data;
568   data.maxage = maxage;
569   data.state = state;
570   cc_storage_apply_to_all(PRIVATE(this)->storage, soglbigimage_unrefolddl_cb, &data);
571 
572   this->incAge();
573 }
574 
575 #undef PRIVATE
576 
577 #ifndef DOXYGEN_SKIP_THIS
578 
SoGLBigImageP(void)579 SoGLBigImageP::SoGLBigImageP(void) :
580   cache(NULL),
581   cachesize(NULL),
582   numcachelevels(0)
583 {
584   this->storage = cc_storage_construct_etc(sizeof(SoGLBigImageTls),
585                                            soglbigimagetls_construct,
586                                            soglbigimagetls_destruct);
587 }
588 
~SoGLBigImageP()589 SoGLBigImageP::~SoGLBigImageP()
590 {
591   this->resetCache();
592   cc_storage_destruct(this->storage);
593 }
594 
595 //  The method copySubImage() handles the downsampling. It averages
596 //  the full-resolution pixels to create the low resolution image.
597 void
copySubImage(SoGLBigImageTls * tls,const int idx,const unsigned char * src,const SbVec2s & fsize,const int nc,unsigned char * dst,const int div,const int level)598 SoGLBigImageP::copySubImage(SoGLBigImageTls * tls,
599                             const int idx,
600                             const unsigned char * src,
601                             const SbVec2s & fsize,
602                             const int nc,
603                             unsigned char * dst,
604                             const int div,
605                             const int level)
606 {
607   if ((div == 1) || (this->cache && level < this->numcachelevels && this->cache[level])) {
608     SbVec2s pos(idx % tls->dim[0], idx / tls->dim[0]);
609 
610     // FIXME: investigate if it's possible to set the pixel transfer
611     // mode so that we don't have to copy the data into a temporary
612     // image. This is probably fast enough though.  pederb?.
613 
614     int origin[2];
615     int fullsize[2];
616     int w, h;
617     const unsigned char * datasrc;
618 
619     if (div == 1) { // use original image
620       origin[0] = pos[0] * tls->imagesize[0];
621       origin[1] = pos[1] * tls->imagesize[1];
622 
623       fullsize[0] = fsize[0];
624       fullsize[1] = fsize[1];
625       w = tls->imagesize[0];
626       h = tls->imagesize[1];
627       datasrc = src;
628     }
629     else { // use cache image
630       origin[0] = pos[0] * (tls->imagesize[0] >> level);
631       origin[1] = pos[1] * (tls->imagesize[1] >> level);
632       fullsize[0] = this->cachesize[level][0];
633       fullsize[1] = this->cachesize[level][1];
634       w = tls->imagesize[0] >> level;
635       h = tls->imagesize[1] >> level;
636       datasrc = this->cache[level];
637     }
638 
639     assert(fullsize[0] > 0 && fullsize[1] > 0);
640 
641     // check for fast loop (common case)
642     if ((origin[0] + w) < fullsize[0] && (origin[1] + h) < fullsize[1]) {
643       for (int y = 0; y < h; y++) {
644         int tmpyadd = fullsize[0] * (origin[1]+y);
645         for (int x = 0; x < w; x++) {
646           const unsigned char * srcptr =
647             datasrc + nc * (tmpyadd + origin[0]+x);
648           for (int c = 0; c < nc; c++) {
649             *dst++ = srcptr[c];
650           }
651         }
652       }
653     }
654     else { // slower loop (x and y values are clamped)
655       for (int y = 0; y < h; y++) {
656         int tmpyadd = fullsize[0] * SbClamp(origin[1]+y, 0, fullsize[1]-1);
657         for (int x = 0; x < w; x++) {
658           const unsigned char * srcptr =
659             datasrc + nc * (tmpyadd + SbClamp(origin[0]+x, 0, fullsize[0]-1));
660           for (int c = 0; c < nc; c++) {
661             *dst++ = srcptr[c];
662           }
663         }
664       }
665     }
666   }
667   else {
668     SbVec2s pos(idx % tls->dim[0], idx / tls->dim[0]);
669 
670     int origin[2];
671     origin[0] = pos[0] * tls->imagesize[0];
672     origin[1] = pos[1] * tls->imagesize[1];
673 
674     int fullsize[2];
675     fullsize[0] = fsize[0];
676     fullsize[1] = fsize[1];
677 
678     int w = tls->imagesize[0];
679     int h = tls->imagesize[1];
680 
681     unsigned int mask = (unsigned int) div-1;
682 
683     if ((origin[0] + w) > fullsize[0]) {
684       w = fullsize[0] - origin[0];
685       if (w & mask) {
686         w = w - (w & mask);
687       }
688     }
689     if ((origin[1] + h) > fullsize[1]) {
690       h = fullsize[1] - origin[1];
691       if (h & mask) {
692         h = h - (h & mask);
693       }
694     }
695 
696     memset(tls->averagebuf, 0, w*h*nc*sizeof(int)/div);
697     unsigned int * aptr = tls->averagebuf;
698     int y;
699     for (y = 0; y < h; y++) {
700       unsigned int * tmpaptr = aptr;
701       const unsigned char * srcptr =
702         src + (fullsize[0] * (origin[1]+y) + origin[0]) * nc;
703       for (int x = 0; x < w; x++) {
704         for (int c = 0; c < nc; c++) {
705           aptr[c] += srcptr[c];
706         }
707         srcptr += nc;
708         if (!((x+1) & mask)) aptr += nc;
709       }
710       if ((y+1) & mask) aptr = tmpaptr;
711     }
712 
713     aptr = tls->averagebuf;
714     int mydiv = div * div;
715 
716     int lineadd = tls->imagesize[0] - w;
717 
718     lineadd /= div;
719     w /= div;
720     h /= div;
721 
722     for (y = 0; y < h; y++) {
723       for (int x = 0; x < w; x++) {
724         for (int c = 0; c < nc; c++) {
725           dst[c] = (unsigned char) (aptr[c] / mydiv);
726         }
727         dst += nc;
728         aptr += nc;
729       }
730       dst += lineadd*nc;
731     }
732   }
733 }
734 
735 void
copyResizeSubImage(SoGLBigImageTls * tls,const int idx,const unsigned char * src,const SbVec2s & fullsize,const int nc,unsigned char * dst,const SbVec2s & targetsize)736 SoGLBigImageP::copyResizeSubImage(SoGLBigImageTls * tls,
737                                   const int idx,
738                                   const unsigned char * src,
739                                   const SbVec2s & fullsize,
740                                   const int nc,
741                                   unsigned char * dst,
742                                   const SbVec2s & targetsize)
743 {
744   SbVec2s pos(idx % tls->dim[0], idx / tls->dim[0]);
745 
746   SbVec2s origin;
747   origin[0] = pos[0] * tls->imagesize[0];
748   origin[1] = pos[1] * tls->imagesize[1];
749 
750   int incy = ((tls->imagesize[1]<<8) / targetsize[1]);
751   int incx = ((tls->imagesize[0]<<8) / targetsize[0]);
752 
753   const int w = targetsize[0];
754   const int h = targetsize[1];
755 
756   int addy = 0;
757 
758   for (int y = 0; y < h; y++) {
759     int addx = 0;
760     int tmpaddy = ((addy>>8)+origin[1])*fullsize[0]*nc;
761     for (int x  = 0; x < w; x++) {
762       const unsigned char * ptr = src + tmpaddy + ((addx>>8)+origin[0]) * nc;
763       for (int c = 0; c < nc; c++) {
764         *dst++ = *ptr++;
765       }
766       addx += incx;
767     }
768     addy += incy;
769   }
770 }
771 
772 #if 0 // FIXME: Not in use
773 // create a lower resolution image by averaging all pixels in a block
774 // (from the full resolution image) into a new pixel. This is pretty
775 // slow, but yields a higher quality result compared to when each
776 // level is calculated based on the previous level.
777 static  unsigned char *
778 image_downsample(const unsigned char * bytes, const SbVec2s fullsize,
779                  const int nc, const SbVec2s subsize, const int div)
780 {
781   unsigned char * dst = new unsigned char[subsize[0]*subsize[1]*nc];
782   unsigned char * dstptr = dst;
783 
784   int starty = 0;
785   int stopy = div;
786   for (int y = 0; y < subsize[1]; y++) {
787     assert(starty < fullsize[1]);
788 
789     int startx = 0;
790     int stopx = div;
791 
792     for (int x = 0; x < subsize[0]; x++) {
793       assert(startx < fullsize[0]);
794 
795       int avg[4] = {0};
796       int numavg = 0;
797 
798       for (int y2 = starty; y2 < stopy; y2++) {
799         for (int x2 = startx; x2 < stopx; x2++) {
800           const unsigned char * src = bytes + (fullsize[0]*y2 + x2) * nc;
801           for (int c = 0; c < nc; c++) {
802             avg[c] += src[c];
803           }
804           numavg++;
805         }
806       }
807       assert(numavg > 0);
808       for (int c = 0; c < nc; c++) {
809         *dstptr++ = avg[c] / numavg;
810       }
811       startx += div;
812       if (startx >= fullsize[0]) startx = fullsize[0] - 1;
813       stopx += div;
814       if (stopx > fullsize[0]) stopx = fullsize[0];
815     }
816     starty += div;
817     if (starty >= fullsize[1]) starty = fullsize[1]-1;
818     stopy += div;
819     if (stopy > fullsize[1]) stopy = fullsize[1];
820   }
821 
822   return dst;
823 }
824 #endif
825 
826 // create a lower resolution image by averaging four and four pixels
827 // into a new pixel. This is the same technique as the one usually
828 // used when creating OpenGL mipmaps. Each level is calculated based
829 // on the previous level, not on the full-resolution image.
830 static void
image_downsample_fast(const int width,const int height,const int nc,const unsigned char * datain,unsigned char * dataout)831 image_downsample_fast(const int width, const int height, const int nc,
832                       const unsigned char * datain, unsigned char * dataout)
833 {
834   assert(width > 1 || height > 1);
835 
836   int nextrow = width * nc;
837   if (width & 1) nextrow += nc; // in case original image has odd size
838   int newwidth = width >> 1;
839   int newheight = height >> 1;
840   unsigned char * dst = dataout;
841   const unsigned char * src = datain;
842 
843   // check for 1D images
844   if (width == 1 || height == 1) {
845     int n = SbMax(newwidth, newheight);
846     for (int i = 0; i < n; i++) {
847       for (int j = 0; j < nc; j++) {
848         *dst = (src[0] + src[nc]) >> 1;
849         dst++; src++;
850       }
851       src += nc; // skip to next pixel
852     }
853   }
854   else {
855     for (int i = 0; i < newheight; i++) {
856       for (int j = 0; j < newwidth; j++) {
857         for (int c = 0; c < nc; c++) {
858           *dst = (src[0] + src[nc] + src[nextrow] + src[nextrow+nc] + 2) >> 2;
859           dst++; src++;
860         }
861         src += nc; // skip to next pixel
862       }
863       src += nextrow;
864     }
865   }
866 }
867 
868 void
createCache(const unsigned char * bytes,const SbVec2s size,const int nc)869 SoGLBigImageP::createCache(const unsigned char * bytes, const SbVec2s size, const int nc)
870 {
871   int levels = 0;
872 
873   while (((size[0]>>levels) > 0) || ((size[1]>>levels) > 0)) {
874     levels++;
875   }
876   if (levels == 0) return;
877   this->numcachelevels = levels;
878 
879   this->cache = new unsigned char*[levels];
880   this->cachesize = new SbVec2s[levels];
881   // temporarily set first cache to simplify code below
882   this->cache[0] = (unsigned char*) bytes;
883   this->cachesize[0] = size;
884 
885   for (int l = 1; l < levels; l++) {
886 #if 0 // high-quality downsample is too slow, currently disabled
887     int sx = size[0] >> l;
888     if (sx == 0) sx = 1;
889     int sy = size[1] >> l;
890     if (sy == 0) sy = 1;
891 
892     this->cachesize[l] = SbVec2s((short)sx, (short)sy);
893     this->cache[l] = image_downsample(bytes, size, nc, this->cachesize[l], 1<<l);
894 #else // end of high quality downsample
895     short w = size[0]>>l;
896     short h = size[1]>>l;
897     if (w == 0) w = 1;
898     if (h == 0) h = 1;
899     this->cachesize[l] = SbVec2s(w, h);
900     this->cache[l] = new unsigned char[w*h*nc];
901     image_downsample_fast(this->cachesize[l-1][0], this->cachesize[l-1][1], nc,
902                           this->cache[l-1], this->cache[l]);
903 #endif // end of low quality downsample
904   }
905   this->cache[0] = NULL;
906   this->cachesize[0] = SbVec2s(0, 0);
907 }
908 
909 void
resetCache(void)910 SoGLBigImageP::resetCache(void)
911 {
912   for (int i = 0; i < this->numcachelevels; i++) {
913     delete[] this->cache[i];
914   }
915   delete[] this->cache;
916   delete[] this->cachesize;
917   this->cache = NULL;
918   this->cachesize = NULL;
919   this->numcachelevels = 0;
920 }
921 
922 void
reset(SoGLBigImageTls * tls,SoState * state)923 SoGLBigImageP::reset(SoGLBigImageTls * tls, SoState * state)
924 {
925   const int n = tls->currentdim[0] * tls->currentdim[1];
926   for (int i = 0; i < n; i++) {
927     if (tls->glimagearray[i]) {
928       tls->glimagearray[i]->unref(state);
929       tls->glimagearray[i] = NULL;
930     }
931     if (tls->imagearray[i]) {
932       delete tls->imagearray[i];
933       tls->imagearray[i] = NULL;
934     }
935   }
936   delete[] tls->glimagearray;
937   delete[] tls->imagearray;
938   delete[] tls->glimageage;
939   delete[] tls->glimagediv;
940   delete[] tls->averagebuf;
941   tls->glimagearray = NULL;
942   tls->imagearray = NULL;
943   tls->glimageage = NULL;
944   tls->glimagediv = NULL;
945   tls->averagebuf = NULL;
946   tls->currentdim.setValue(0,0);
947 }
948 
949 void
unrefOldDL(SoGLBigImageTls * tls,SoState * state,const uint32_t maxage)950 SoGLBigImageP::unrefOldDL(SoGLBigImageTls * tls, SoState * state, const uint32_t maxage)
951 {
952   const int numimages = tls->currentdim[0] * tls->currentdim[1];
953   for (int i = 0; i < numimages; i++) {
954     if (tls->glimagearray[i]) {
955       if (tls->glimageage[i] >= maxage) {
956 #if COIN_DEBUG && 0 // debug
957         SoDebugError::postInfo("SoGLBigImageP::unrefOldDL",
958                                "Killed image because of old age.");
959 #endif // debug
960         tls->glimagearray[i]->unref(state);
961         tls->glimagearray[i] = NULL;
962       }
963       else tls->glimageage[i] += 1;
964     }
965   }
966 }
967 
968 // cc_storage_apply_to_all callback used by resetAllTls()
969 static void
soglbigimage_resetall_cb(void * tls,void * closure)970 soglbigimage_resetall_cb(void * tls, void * closure)
971 {
972   // simply call SoGLBigImageP::reset()
973   SoGLBigImageP::reset((SoGLBigImageTls*) tls, (SoState*) closure);
974 }
975 
976 void
resetAllTls(SoState * state)977 SoGLBigImageP::resetAllTls(SoState * state)
978 {
979   cc_storage_apply_to_all(this->storage, soglbigimage_resetall_cb, state);
980 }
981 
982 #endif // DOXYGEN_SKIP_THIS
983 
984 #undef LINEAR_LIMIT
985