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 SoSFImage SoSFImage.h Inventor/fields/SoSFImage.h
35   \brief The SoSFImage class is used to store pixel images.
36 
37   \ingroup fields
38 
39   The SoSFImage class provides storage for inline 2D image
40   maps. Images in Coin are mainly used for texture mapping support.
41 
42   SoSFImage instances can be exported and imported as any other field
43   class in Coin.
44 
45   The components of an SoSFImage is: its image dimensions (width and
46   height), the number of bytes used for describing each pixel (number
47   of components) and an associated pixel buffer. The size of the pixel
48   buffer will be width*height*components.
49 
50   For texture maps, the components / bytes-per-pixel setting
51   translates as follows: use 1 for a grayscale imagemap, 2 for
52   grayscale + opacity (i.e. alpha value), 3 for RGB (1 byte each for
53   red, green and blue) and 4 components means 3 bytes for RGB + 1 byte
54   opacity value (aka RGBA).
55 
56   This field is serializable into the Inventor / Coin file format in
57   the following manner:
58 
59   \code
60   FIELDNAME X Y C 0xRRGGBBAA 0xRRGGBBAA ...
61   \endcode
62 
63   "X" and "Y" are the image dimensions along the given axes, "C" is
64   the number of components in the image. The number of 0xRRGGBBAA
65   pixel color specifications needs to equal the exact number of
66   pixels, which of course is given by X*Y. Each part of the pixel
67   color value is in the range 0x00 to 0xff (hexadecimal, 0 to 255
68   decimal).
69 
70   For 3-component images, the pixel-format is 0xXXRRGGBB, where the
71   byte in the pixel color value marked as "XX" is ignored and can be
72   left out.
73 
74   For 2-component images, the pixel-format is 0xXXXXGGAA, where the
75   bytes in the pixel color values marked as "XX" are ignored and can
76   be left out. "GG" is the part which gives a grayscale value and "AA"
77   is for opacity.
78 
79   For 1-component images, the pixel-format is 0xXXXXXXGG, where the
80   bytes in the pixel color values marked as "XX" are ignored and can
81   be left out.
82 
83   The pixels are read as being ordered in rows along X (width) and
84   columns along Y (height, bottom to top).
85 
86   Here's a simple example of the file format serialization, for a 2x2
87   RGB-image inside an SoTexture2 node, as mapped onto an SoCube:
88 
89   \code
90   Complexity { textureQuality 0.1 }   # set low to avoid smoothing
91 
92   Texture2 {
93      image 2 2 4
94 
95      0xffffffff 0x00ff0088   # white   semi-transparent green
96      0xff0000ff 0xffff00ff   #  red    yellow
97   }
98 
99   Cube { }
100   \endcode
101 
102   The mini-scenegraph above results in the following mapping on the
103   cube:<br>
104 
105   <center>
106   \image html sosfimage.png "Rendering of Example Scenegraph"
107   </center>
108 
109   The cube has only been \e slightly rotated, so as you can see from
110   the snapshot, the Y-rows are mapped from bottom to top, while the
111   X-column pixels are mapped onto the cube from left to right.
112 
113   \sa SoTexture2, SoSFImage3
114 */
115 
116 // *************************************************************************
117 
118 #include <Inventor/fields/SoSFImage.h>
119 
120 #include "coindefs.h"
121 
122 #include <cstdlib> // free()
123 
124 #include <Inventor/SoInput.h>
125 #include <Inventor/SoOutput.h>
126 #include <Inventor/SbImage.h>
127 #include <Inventor/errors/SoReadError.h>
128 #include <Inventor/errors/SoDebugError.h>
129 
130 #include "fields/SoSubFieldP.h"
131 
132 // *************************************************************************
133 
134 class SoSFImageP {
135 public:
SoSFImageP(void)136   SoSFImageP(void) {
137     this->image = new SbImage;
138     this->freeimage = NULL;
139     this->deleteimage = NULL;
140   }
~SoSFImageP()141   ~SoSFImageP() {
142     delete this->image;
143     if (this->freeimage) free(this->freeimage);
144     delete[] this->deleteimage;
145   }
146   SbImage * image;
147   unsigned char * deleteimage; // free this data using delete[]
148   unsigned char * freeimage; // free this data using free()
149 };
150 
151 #define PRIVATE(p) ((p)->pimpl)
152 
153 // *************************************************************************
154 
155 PRIVATE_TYPEID_SOURCE(SoSFImage);
156 PRIVATE_EQUALITY_SOURCE(SoSFImage);
157 
158 // *************************************************************************
159 
160 // (Declarations hidden in macro in SoSFImage.h, so don't use Doxygen
161 // commenting.)
162 #ifndef DOXYGEN_SKIP_THIS
163 
164 /* Constructor, initializes fields to represent an empty image. */
SoSFImage(void)165 SoSFImage::SoSFImage(void)
166 {
167   PRIVATE(this) = new SoSFImageP;
168 }
169 
170 /* Free all resources associated with the image. */
~SoSFImage()171 SoSFImage::~SoSFImage()
172 {
173   delete PRIVATE(this);
174 }
175 
176 /* Copy the image of \a field into this field. */
177 const SoSFImage &
operator =(const SoSFImage & field)178 SoSFImage::operator=(const SoSFImage & field)
179 {
180   int nc = 0;
181   SbVec2s size(0,0);
182   unsigned char * bytes = PRIVATE(&field)->image->getValue(size, nc);
183 
184   this->setValue(size, nc, bytes);
185   return *this;
186 }
187 
188 #endif // DOXYGEN_SKIP_THIS
189 
190 
191 // Override from parent class.
192 void
initClass(void)193 SoSFImage::initClass(void)
194 {
195   SO_SFIELD_INTERNAL_INIT_CLASS(SoSFImage);
196 }
197 
198 SbBool
readValue(SoInput * in)199 SoSFImage::readValue(SoInput * in)
200 {
201   SbVec2s size;
202   int nc;
203   if (!in->read(size[0]) || !in->read(size[1]) ||
204       !in->read(nc)) {
205     SoReadError::post(in, "Premature end of file");
206     return FALSE;
207   }
208 
209   // Note: empty images (dimensions 0x0x0) are allowed.
210 
211   if (size[0] < 0 || size[1] < 0 || nc < 0 || nc > 4) {
212     SoReadError::post(in, "Invalid image specification %dx%dx%d",
213                       size[0], size[1], nc);
214     return FALSE;
215   }
216 
217   int buffersize = int(size[0]) * int(size[1]) * nc;
218 
219   if (buffersize == 0 &&
220       (size[0] != 0 || size[1] != 0 || nc != 0)) {
221     SoReadError::post(in, "Invalid image specification %dx%dx%d",
222                       size[0], size[1], nc);
223     return FALSE;
224   }
225 
226 #if COIN_DEBUG && 0 // debug
227   SoDebugError::postInfo("SoSFImage::readValue", "image dimensions: %dx%dx%d",
228                          size[0], size[1], nc);
229 #endif // debug
230 
231   if (!buffersize) {
232     PRIVATE(this)->image->setValue(SbVec2s(0,0), 0, NULL);
233     return TRUE;
234   }
235 
236   // allocate image data and get new pointer back
237   PRIVATE(this)->image->setValue(size, nc, NULL);
238   unsigned char * pixblock = PRIVATE(this)->image->getValue(size, nc);
239 
240   // The binary image format of 2.1 and later tries to be less
241   // wasteful when storing images.
242   if (in->isBinary() && in->getIVVersion() >= 2.1f) {
243     if (!in->readBinaryArray(pixblock, buffersize)) {
244       SoReadError::post(in, "Premature end of file");
245       return FALSE;
246     }
247     // images are padded to a 4-byte alignment
248     int padsize = ((buffersize + 3) / 4) * 4 - buffersize;
249     if (padsize) {
250       unsigned char pads[3]; // pad is at most 3 bytes
251       if (!in->readBinaryArray(pads, padsize)) {
252         SoReadError::post(in, "Premature end of file");
253         return FALSE;
254       }
255     }
256   }
257   else {
258     int byte = 0;
259     int numpixels = int(size[0]) * int(size[1]);
260     for (int i = 0; i < numpixels; i++) {
261       unsigned int l;
262       if (!in->read(l)) {
263         SoReadError::post(in, "Premature end of file");
264         return FALSE;
265       }
266       for (int j = 0; j < nc; j++) {
267         pixblock[byte++] =
268           static_cast<unsigned char>((l >> (8 * (nc-j-1))) & 0xFF);
269       }
270     }
271   }
272   return TRUE;
273 }
274 
275 void
writeValue(SoOutput * out) const276 SoSFImage::writeValue(SoOutput * out) const
277 {
278   int nc;
279   SbVec2s size;
280   unsigned char * pixblock = PRIVATE(this)->image->getValue(size, nc);
281 
282   out->write(size[0]);
283   if (!out->isBinary()) out->write(' ');
284   out->write(size[1]);
285   if (!out->isBinary()) out->write(' ');
286   out->write(nc);
287 
288   if (out->isBinary()) {
289     int buffersize = int(size[0]) * int(size[1]) * nc;
290     if (buffersize) { // in case of an empty image
291       out->writeBinaryArray(pixblock, buffersize);
292       int padsize = ((buffersize + 3) / 4) * 4 - buffersize;
293       if (padsize) {
294         unsigned char pads[3] = {'\0','\0','\0'};
295         out->writeBinaryArray(pads, padsize);
296       }
297     }
298   }
299   else {
300     out->write('\n');
301     out->indent();
302 
303     int numpixels = int(size[0]) * int(size[1]);
304     for (int i = 0; i < numpixels; i++) {
305       unsigned int data = 0;
306       for (int j = 0; j < nc; j++) {
307         if (j) data <<= 8;
308         data |= static_cast<uint32_t>(pixblock[i * nc + j]);
309       }
310       out->write(data);
311       if (((i+1)%8 == 0) && (i+1 != numpixels)) {
312         out->write('\n');
313         out->indent();
314       }
315       else {
316         out->write(' ');
317       }
318     }
319   }
320 }
321 
322 
323 /*!
324   \fn int SoSFImage::operator!=(const SoSFImage & field) const
325   Compare image of \a field with the image in this field and
326   return \c FALSE if they are equal.
327 */
328 
329 /*!
330   Compare image of \a field with the image in this field and
331   return \c TRUE if they are equal.
332 */
333 int
operator ==(const SoSFImage & field) const334 SoSFImage::operator==(const SoSFImage & field) const
335 {
336   return (*PRIVATE(this)->image) == (*(PRIVATE(&field)->image));
337 }
338 
339 
340 /*!
341   Return pixel buffer, set \a size to contain the image dimensions and
342   \a nc to the number of components in the image.
343 */
344 const unsigned char *
getValue(SbVec2s & size,int & nc) const345 SoSFImage::getValue(SbVec2s & size, int & nc) const
346 {
347   this->evaluate();
348   return PRIVATE(this)->image->getValue(size, nc);
349 }
350 
351 /*!
352   \retval SbImage contained by this SoSFImage
353 */
354 const SbImage &
getValue() const355 SoSFImage::getValue() const
356 {
357   this->evaluate();
358   return *PRIVATE(this)->image;
359 }
360 
361 /*!
362   Initialize this field to \a size and \a nc.
363 
364   If \a pixels is not \c NULL, the image data is copied from \a pixels
365   into this field.  If \a pixels is \c NULL, the image data is cleared
366   by setting all bytes to 0 (note that the behavior on passing a \c
367   NULL pointer is specific for Coin, Open Inventor will crash if you
368   try it).
369 
370   The image dimensions is given by the \a size argument, and the \a nc
371   argument specifies the number of bytes-pr-pixel. A 24-bit RGB image
372   would for instance have an \a nc equal to 3.
373 
374   The \a copypolicy argument makes it possible to share image data
375   with SoSFImage without the data being copied (thereby using less
376   memory resources). The default is to copy image data from the \a
377   pixels source into an internal copy.
378 
379   \e Important \e note: if you call this with \a copypolicy as either
380   \c NO_COPY_AND_DELETE or \c NO_COPY_AND_FREE, and your application
381   is running on Mirosoft Windows, be aware that you will get
382   mysterious crashes if your application is not using the same C
383   library run-time as the Coin library.
384 
385   The cause of this is that a memory block would then be allocated by
386   the application on the memory heap of one C library run-time (say,
387   for instance \c MSVCRT.LIB), but attempted deallocated in the memory
388   heap of another C library run-time (e.g. \c MSVCRTD.LIB), which
389   typically leads to hard-to-debug crashes.
390 
391   \since The CopyPolicy argument was added in Coin 2.0.
392   \since CopyPolicy was added to TGS Inventor 3.0.
393 */
394 void
setValue(const SbVec2s & size,const int nc,const unsigned char * pixels,SoSFImage::CopyPolicy copypolicy)395 SoSFImage::setValue(const SbVec2s & size, const int nc,
396                     const unsigned char * pixels,
397                     SoSFImage::CopyPolicy copypolicy)
398 {
399   // free old data
400   if (PRIVATE(this)->freeimage) {
401     free(PRIVATE(this)->freeimage);
402     PRIVATE(this)->freeimage = NULL;
403   }
404   delete[] PRIVATE(this)->deleteimage;
405   PRIVATE(this)->deleteimage = NULL;
406   // set new data
407   switch (copypolicy) {
408   default:
409     assert(0 && "unknown copy policy");
410   case COPY:
411     PRIVATE(this)->image->setValue(size, nc, pixels);
412     break;
413   case NO_COPY:
414     PRIVATE(this)->image->setValuePtr(size, nc, pixels);
415     break;
416 
417     // FIXME: as for the "multiple C run-times" problem mentioned in
418     // the API docs above, would it be possible to put in a check for
419     // whether or not the memory block is within the same C library
420     // heap as for the Coin library itself? I seem to remember that
421     // there is such a function in the Win32 API.  20050518 mortene.
422     //
423     // UPDATE 20050518 mortene: _CrtIsValidHeapPointer() is mentioned
424     // in the MSDN docs, and seems to be a step in the right
425     // direction. This may however be a macro, which uses some
426     // underlying Win32 API call -- check that.
427 
428   case NO_COPY_AND_DELETE:
429     PRIVATE(this)->image->setValuePtr(size, nc, pixels);
430     PRIVATE(this)->deleteimage = const_cast<unsigned char *>(pixels);
431     break;
432   case NO_COPY_AND_FREE:
433     PRIVATE(this)->image->setValuePtr(size, nc, pixels);
434     PRIVATE(this)->freeimage = const_cast<unsigned char *>(pixels);
435     break;
436   }
437   this->valueChanged();
438 }
439 
440 /*!
441   Return pixel buffer. Return the image size and components in
442   \a size and \a nc.
443 
444   You can not use this method to set a new image size. Use setValue()
445   to change the size of the image buffer.
446 
447   The field's container will not be notified about the changes
448   until you call finishEditing().
449 */
450 unsigned char *
startEditing(SbVec2s & size,int & nc)451 SoSFImage::startEditing(SbVec2s & size, int & nc)
452 {
453   return PRIVATE(this)->image->getValue(size, nc);
454 }
455 
456 /*!
457   Notify the field's auditors that the image data has been
458   modified.
459 */
460 void
finishEditing(void)461 SoSFImage::finishEditing(void)
462 {
463   this->valueChanged();
464 }
465 
466 /*!
467   Not yet implemented for Coin. Get in touch if you need this method.
468 
469   \since Coin 2.0
470   \since TGS Inventor 3.0
471  */
472 void
setSubValue(const SbVec2s & COIN_UNUSED_ARG (dims),const SbVec2s & COIN_UNUSED_ARG (offset),unsigned char * COIN_UNUSED_ARG (pixels))473 SoSFImage::setSubValue(
474                      const SbVec2s & COIN_UNUSED_ARG(dims),
475                      const SbVec2s & COIN_UNUSED_ARG(offset),
476                      unsigned char * COIN_UNUSED_ARG(pixels)
477                      )
478 {
479   // FIXME: unimplemented yet. 20030226 mortene.
480   SoDebugError::postWarning("SoSFImage::setSubValue",
481                             "Not yet implemented for Coin. "
482                             "Get in touch if you need this functionality.");
483 }
484 
485 /*!
486   Not yet implemented for Coin. Get in touch if you need this method.
487 
488   \since Coin 2.0
489   \since TGS Inventor 3.0
490  */
491 void
setSubValues(const SbVec2s * COIN_UNUSED_ARG (dims),const SbVec2s * COIN_UNUSED_ARG (offsets),int COIN_UNUSED_ARG (num),unsigned char ** COIN_UNUSED_ARG (pixelblocks))492 SoSFImage::setSubValues(
493                      const SbVec2s * COIN_UNUSED_ARG(dims),
494                      const SbVec2s * COIN_UNUSED_ARG(offsets),
495                      int COIN_UNUSED_ARG(num),
496                      unsigned char ** COIN_UNUSED_ARG(pixelblocks)
497                      )
498 {
499   // FIXME: unimplemented yet. 20030226 mortene.
500   SoDebugError::postWarning("SoSFImage::setSubValues",
501                             "Not yet implemented for Coin. "
502                             "Get in touch if you need this functionality.");
503 }
504 
505 /*!
506   Not yet implemented for Coin. Get in touch if you need this method.
507 
508   \since Coin 2.0
509   \since TGS Inventor 3.0
510  */
511 unsigned char *
getSubTexture(int COIN_UNUSED_ARG (idx),SbVec2s & COIN_UNUSED_ARG (dims),SbVec2s & COIN_UNUSED_ARG (offset)) const512 SoSFImage::getSubTexture(
513                       int COIN_UNUSED_ARG(idx),
514                       SbVec2s & COIN_UNUSED_ARG(dims),
515                       SbVec2s & COIN_UNUSED_ARG(offset)
516                       ) const
517 {
518   // FIXME: unimplemented yet. 20030226 mortene.
519   SoDebugError::postWarning("SoSFImage::getSubTexture",
520                             "Not yet implemented for Coin. "
521                             "Get in touch if you need this functionality.");
522   return NULL;
523 }
524 
525 /*!
526   Returns whether or not sub textures was set up for this field.
527 
528   If \c TRUE is returned, the \a numsubtextures argument will be set
529   to the number of sub textures in this image. This number can be used
530   for iterating over all textures with the SoSFImage::getSubTextures()
531   method.
532 
533   \since Coin 2.0
534   \since TGS Inventor 3.0
535  */
536 SbBool
hasSubTextures(int & numsubtextures)537 SoSFImage::hasSubTextures(int & numsubtextures)
538 {
539   // FIXME: unimplemented yet. 20030226 mortene.
540   numsubtextures = 0;
541   return FALSE;
542 }
543 
544 /*!
545   Set this flag to true to avoid writing out the texture to file. This
546   can save a lot on file size.
547 
548   Default value is \c FALSE (i.e. write texture data to file.)
549 
550   (Note: yet unimplemented for Coin.)
551 
552   \since Coin 2.0
553   \since TGS Inventor ?.?
554  */
555 void
setNeverWrite(SbBool COIN_UNUSED_ARG (flag))556 SoSFImage::setNeverWrite(SbBool COIN_UNUSED_ARG(flag))
557 {
558   // FIXME: unimplemented yet. 20030226 mortene.
559   SoDebugError::postWarning("SoSFImage::setNeverWrite",
560                             "Not yet implemented for Coin. "
561                             "Get in touch if you need this functionality.");
562 }
563 
564 /*!
565   Returns value of "never write texture data" flag.
566 
567   \sa SoSFImage::setNeverWrite()
568 
569   \since Coin 2.0
570   \since TGS Inventor ?.?
571  */
572 SbBool
isNeverWrite(void) const573 SoSFImage::isNeverWrite(void) const
574 {
575   // FIXME: unimplemented yet. 20030226 mortene.
576   return FALSE;
577 }
578 
579 /*!
580   Returns \c TRUE if at least one pixel of the image in this field is
581   not completely opaque, otherwise \c FALSE.
582 
583   \since Coin 2.0
584   \since TGS Inventor ?.?
585  */
586 SbBool
hasTransparency(void) const587 SoSFImage::hasTransparency(void) const
588 {
589   // FIXME: unimplemented yet. 20030226 mortene.
590   SoDebugError::postWarning("SoSFImage::hasTransparency",
591                             "Not yet implemented for Coin. "
592                             "Get in touch if you need this functionality.");
593   return TRUE;
594 }
595 
596 #undef PRIVATE
597 
598 #ifdef COIN_TEST_SUITE
599 
BOOST_AUTO_TEST_CASE(initialized)600 BOOST_AUTO_TEST_CASE(initialized)
601 {
602   SoSFImage field;
603   BOOST_CHECK_MESSAGE(SoSFImage::getClassTypeId() != SoType::badType(),
604                       "SoSFImage class not initialized");
605   BOOST_CHECK_MESSAGE(field.getTypeId() != SoType::badType(),
606                       "missing class initialization");
607 }
608 
609 #endif // COIN_TEST_SUITE
610