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