/////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2003, Industrial Light & Magic, a division of Lucas // Digital Ltd. LLC // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Industrial Light & Magic nor the names of // its contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- // // PhotoRealistic RenderMan display driver that outputs // floating-point image files, using ILM's IlmImf library. // // When you use this display driver for RGBA or Z output, you should // turn RGBA and Z quantization off by adding the following lines to // your RIB file: // // Quantize "rgba" 0 0 0 0 // Quantize "z" 0 0 0 0 // // Like Pixar's Tiff driver, this display driver can output image // channels other than R, G, B and A; for details on RIB file and // shader syntax, see the Renderman Release Notes (New Display // System, RGBAZ Output Images, Arbitrary Output Variables). // // This driver maps Renderman's output variables to image channels // as follows: // // Renderman output image channel image channel // variable name name type // -------------------------------------------------------------- // // "r" "R" HALF // // "g" "G" HALF // // "b" "B" HALF // // "a" "A" HALF // // "z" "Z" FLOAT // // other same as output preferred type // variable name (see below) // // By default, the "preferred" channel type is HALF; the // preferred type can be changed by adding an "exrpixeltype" // argument to the Display command in the RIB file. // For example: // // Declare "exrpixeltype" "string" // // # Store point positions in FLOAT format // Display "gnome.points.exr" "exr" "P" "exrpixeltype" "float" // // The default compression method for the image's pixel data // is defined in ImfHeader.h. You can select a different // compression method by adding an "exrcompression" argument // to the Display command. For example: // // Declare "exrcompression" "string" // // # Store RGBA using run-length encoding // Display "gnome.rgba.exr" "exr" "rgba" "exrcompression" "rle" // // See function DspyImageOpen(), below, for a list of valid // "exrpixeltype" and "exrcompression" values. // //----------------------------------------------------------------------------- #undef NDEBUG // enable assert() #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Imath; using namespace Imf; using namespace std; namespace { typedef map ChannelOffsetMap; typedef vector *> ChannelLuts; // // Define halfFunctions for the identity and piz12 // half halfID( half x ) { return x; } halfFunction id( halfID ); halfFunction piz12( round12log ); class Image { public: Image (const char filename[], const Header &header, ChannelOffsetMap &rmanChannelOffsets, int rmanPixelSize, ChannelLuts &channelLuts); const Header & header () const; void writePixels (int xMin, int xMaxPlusone, int yMin, int yMaxPlusone, int entrySize, const unsigned char *data); private: OutputFile _file; Array _buffer; vector _rmanChannelOffsets; vector _bufferChannelOffsets; int _rmanPixelSize; int _bufferPixelSize; int _bufferXMin; int _bufferNumPixels; int _numPixelsReceived; ChannelLuts _channelLuts; }; Image::Image (const char filename[], const Header &header, ChannelOffsetMap &rmanChannelOffsets, int rmanPixelSize, ChannelLuts &channelLuts ) : _file (filename, header), _rmanPixelSize (rmanPixelSize), _bufferPixelSize (0), _bufferXMin (header.dataWindow().min.x), _bufferNumPixels (header.dataWindow().max.x - _bufferXMin + 1), _numPixelsReceived (0), _channelLuts (channelLuts) { V2i dwSize = header.dataWindow().size(); for (ChannelList::ConstIterator i = header.channels().begin(); i != header.channels().end(); ++i) { switch (i.channel().type) { case HALF: _rmanChannelOffsets.push_back (rmanChannelOffsets[i.name()]); _bufferChannelOffsets.push_back (_bufferPixelSize); _bufferPixelSize += sizeof (float); // Note: to avoid alignment break; // problems when float and half // channels are mixed, halfs case FLOAT: // are not packed densely. _rmanChannelOffsets.push_back (rmanChannelOffsets[i.name()]); _bufferChannelOffsets.push_back (_bufferPixelSize); _bufferPixelSize += sizeof (float); break; default: assert (false); // unsupported channel type break; } } _buffer.resizeErase (_bufferNumPixels * _bufferPixelSize); FrameBuffer fb; int j = 0; int yStride = 0; char *base = &_buffer[0] - _bufferXMin * _bufferPixelSize; for (ChannelList::ConstIterator i = header.channels().begin(); i != header.channels().end(); ++i) { fb.insert (i.name(), Slice (i.channel().type, // type base + _bufferChannelOffsets[j], // base _bufferPixelSize, // xStride yStride, // yStride 1, // xSampling 1)); // ySampling ++j; } _file.setFrameBuffer (fb); } const Header & Image::header () const { return _file.header(); } void Image::writePixels (int xMin, int xMaxPlusone, int yMin, int yMaxPlusone, int entrySize, const unsigned char *data) { // // We can only deal with one scan line at a time. // assert (yMin == yMaxPlusone - 1); const ChannelList &channels = _file.header().channels(); int numPixels = xMaxPlusone - xMin; int j = 0; char *toBase; int toInc; // // Copy the pixels into our internal one-line frame buffer. // toBase = _buffer + _bufferPixelSize * xMin; toInc = _bufferPixelSize; for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { const unsigned char *from = data + _rmanChannelOffsets[j]; const unsigned char *end = from + numPixels * entrySize; char *to = toBase + _bufferChannelOffsets[j]; switch (i.channel().type) { case HALF: { halfFunction &lut = *_channelLuts[j]; while (from < end) { *(half *) to = lut( ( half )( *(float *) from ) ); from += entrySize; to += toInc; } break; } case FLOAT: while (from < end) { *(float *) to = *(float *) from; from += entrySize; to += toInc; } break; default: assert (false); // channel type is not currently supported break; } ++j; } _numPixelsReceived += numPixels; assert (_numPixelsReceived <= _bufferNumPixels); if (_numPixelsReceived == _bufferNumPixels) { // // If our one-line frame buffer is full, then write it to // the output file. // _file.writePixels(); _numPixelsReceived = 0; } } } // namespace extern "C" { PtDspyError DspyImageOpen (PtDspyImageHandle *pvImage, const char *drivername, const char *filename, int width, int height, int paramCount, const UserParameter *parameters, int formatCount, PtDspyDevFormat *format, PtFlagStuff *flagstuff) { try { // // Build an output file header // Header header; ChannelOffsetMap channelOffsets; ChannelLuts channelLuts; int pixelSize = 0; halfFunction *rgbLUT = &id; halfFunction *otherLUT = &id; // // Data window // { Box2i &dw = header.dataWindow(); int n = 2; DspyFindIntsInParamList ("origin", &n, &dw.min.x, paramCount, parameters); assert (n == 2); dw.max.x = dw.min.x + width - 1; dw.max.y = dw.min.y + height - 1; } // // Display window // { Box2i &dw = header.displayWindow(); int n = 2; DspyFindIntsInParamList ("OriginalSize", &n, &dw.max.x, paramCount, parameters); assert (n == 2); dw.min.x = 0; dw.min.y = 0; dw.max.x -= 1; dw.max.y -= 1; } // // Camera parameters // { // // World-to-NDC matrix, world-to-camera matrix, // near and far clipping plane distances // M44f NP, Nl; float near = 0, far = 0; DspyFindMatrixInParamList ("NP", &NP[0][0], paramCount, parameters); DspyFindMatrixInParamList ("Nl", &Nl[0][0], paramCount, parameters); DspyFindFloatInParamList ("near", &near, paramCount, parameters); DspyFindFloatInParamList ("far", &far, paramCount, parameters); // // The matrices reflect the orientation of the camera at // render time. // header.insert ("worldToNDC", M44fAttribute (NP)); header.insert ("worldToCamera", M44fAttribute (Nl)); header.insert ("clipNear", FloatAttribute (near)); header.insert ("clipFar", FloatAttribute (far)); // // Projection matrix // M44f P = Nl.inverse() * NP; // // Derive pixel aspect ratio, screen window width, screen // window center from projection matrix. // Box2f sw (V2f ((-1 - P[3][0] - P[2][0]) / P[0][0], (-1 - P[3][1] - P[2][1]) / P[1][1]), V2f (( 1 - P[3][0] - P[2][0]) / P[0][0], ( 1 - P[3][1] - P[2][1]) / P[1][1])); header.screenWindowWidth() = sw.max.x - sw.min.x; header.screenWindowCenter() = (sw.max + sw.min) / 2; const Box2i &dw = header.displayWindow(); header.pixelAspectRatio() = (sw.max.x - sw.min.x) / (sw.max.y - sw.min.y) * (dw.max.y - dw.min.y + 1) / (dw.max.x - dw.min.x + 1); } // // Line order // header.lineOrder() = INCREASING_Y; flagstuff->flags |= PkDspyFlagsWantsScanLineOrder; // // Compression // { char *comp = 0; DspyFindStringInParamList ("exrcompression", &comp, paramCount, parameters); if (comp) { if (!strcmp (comp, "none")) header.compression() = NO_COMPRESSION; else if (!strcmp (comp, "rle")) header.compression() = RLE_COMPRESSION; else if (!strcmp (comp, "zips")) header.compression() = ZIPS_COMPRESSION; else if (!strcmp (comp, "zip")) header.compression() = ZIP_COMPRESSION; else if (!strcmp (comp, "piz")) header.compression() = PIZ_COMPRESSION; else if (!strcmp (comp, "piz12")) { header.compression() = PIZ_COMPRESSION; rgbLUT = &piz12; } else THROW (Iex::ArgExc, "Invalid exrcompression \"" << comp << "\" " "for image file " << filename << "."); } } // // Channel list // { PixelType pixelType = HALF; char *ptype = 0; DspyFindStringInParamList ("exrpixeltype", &ptype, paramCount, parameters); if (ptype) { if (!strcmp (ptype, "float")) pixelType = FLOAT; else if (!strcmp (ptype, "half")) pixelType = HALF; else THROW (Iex::ArgExc, "Invalid exrpixeltype \"" << ptype << "\" " "for image file " << filename << "."); } ChannelList &channels = header.channels(); for (int i = 0; i < formatCount; ++i) { if (!strcmp (format[i].name, "r")) { channels.insert ("R", Channel (HALF)); channelOffsets["R"] = pixelSize; channelLuts.push_back( rgbLUT ); } else if (!strcmp (format[i].name, "g")) { channels.insert ("G", Channel (HALF)); channelOffsets["G"] = pixelSize; channelLuts.push_back( rgbLUT ); } else if (!strcmp (format[i].name, "b")) { channels.insert ("B", Channel (HALF)); channelOffsets["B"] = pixelSize; channelLuts.push_back( rgbLUT ); } else if (!strcmp (format[i].name, "a")) { channels.insert ("A", Channel (HALF)); channelOffsets["A"] = pixelSize; channelLuts.push_back( otherLUT ); } else if (!strcmp (format[i].name, "z")) { channels.insert ("Z", Channel (FLOAT)); channelOffsets["Z"] = pixelSize; channelLuts.push_back( otherLUT ); } else { // // Unknown channel name; keep its name and store // the channel, unless the name conflicts with // another channel's name. // if (!channels.findChannel (format[i].name)) { channels.insert (format[i].name, Channel (pixelType)); channelOffsets[format[i].name] = pixelSize; channelLuts.push_back( otherLUT ); } } format[i].type = PkDspyFloat32 | PkDspyByteOrderNative; pixelSize += sizeof (float); } } // // Open the output file // Image *image = new Image (filename, header, channelOffsets, pixelSize, channelLuts); *pvImage = (PtDspyImageHandle) image; } catch (const exception &e) { DspyError ("OpenEXR display driver", "%s\n", e.what()); return PkDspyErrorUndefined; } return PkDspyErrorNone; } PtDspyError DspyImageData (PtDspyImageHandle pvImage, int xmin, int xmax_plusone, int ymin, int ymax_plusone, int entrysize, const unsigned char *data) { try { Image *image = (Image *) pvImage; image->writePixels (xmin, xmax_plusone, ymin, ymax_plusone, entrysize, data); } catch (const exception &e) { DspyError ("OpenEXR display driver", "%s\n", e.what()); return PkDspyErrorUndefined; } return PkDspyErrorNone; } PtDspyError DspyImageClose (PtDspyImageHandle pvImage) { try { delete (Image *) pvImage; } catch (const exception &e) { DspyError ("OpenEXR display driver", "%s\n", e.what()); return PkDspyErrorUndefined; } return PkDspyErrorNone; } PtDspyError DspyImageQuery (PtDspyImageHandle pvImage, PtDspyQueryType querytype, int datalen, void *data) { if (datalen > 0 && data) { switch (querytype) { case PkOverwriteQuery: { PtDspyOverwriteInfo overwriteInfo; if (datalen > sizeof(overwriteInfo)) datalen = sizeof(overwriteInfo); overwriteInfo.overwrite = 1; overwriteInfo.interactive = 0; memcpy(data, &overwriteInfo, datalen); } break; case PkSizeQuery: { PtDspySizeInfo sizeInfo; if (datalen > sizeof(sizeInfo)) datalen = sizeof(sizeInfo); const Image *image = (const Image *) pvImage; if (image) { const Box2i &dw = image->header().dataWindow(); sizeInfo.width = dw.max.x - dw.min.x + 1; sizeInfo.height = dw.max.y - dw.min.y + 1; // // Renderman documentation does not specify if // sizeInfo.aspectRatio refers to the pixel or // the image aspect ratio, but sample code in // the documentation suggests pixel aspect ratio. // sizeInfo.aspectRatio = image->header().pixelAspectRatio(); } else { sizeInfo.width = 640; sizeInfo.height = 480; sizeInfo.aspectRatio = 1.0f; } memcpy(data, &sizeInfo, datalen); } break; default: return PkDspyErrorUnsupported; } } else { return PkDspyErrorBadParams; } return PkDspyErrorNone; } } // extern C