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