KImgIO
exr.cpp
Go to the documentation of this file.
00001 // -*- C++;indent-tabs-mode: t; tab-width: 4; c-basic-offset: 4; -*- 00002 00011 #include "exr.h" 00012 00013 #include <config.h> 00014 00015 #include <ImfRgbaFile.h> 00016 #include <ImfStandardAttributes.h> 00017 #include <ImathBox.h> 00018 #include <ImfInputFile.h> 00019 #include <ImfBoxAttribute.h> 00020 #include <ImfChannelListAttribute.h> 00021 #include <ImfCompressionAttribute.h> 00022 #include <ImfFloatAttribute.h> 00023 #include <ImfIntAttribute.h> 00024 #include <ImfLineOrderAttribute.h> 00025 #include <ImfStringAttribute.h> 00026 #include <ImfVecAttribute.h> 00027 #include <ImfArray.h> 00028 #include <ImfConvert.h> 00029 #include <ImfVersion.h> 00030 #include <IexThrowErrnoExc.h> 00031 00032 #include <iostream> 00033 00034 #include <kdebug.h> 00035 00036 00037 #include <QImage> 00038 #include <QDataStream> 00039 #include <QImageIOPlugin> 00040 00041 class K_IStream: public Imf::IStream 00042 { 00043 public: 00044 K_IStream( QIODevice *dev, const QByteArray& fileName ): 00045 IStream( fileName.data() ), m_dev ( dev ) 00046 {} 00047 00048 virtual bool read( char c[], int n ); 00049 virtual Imf::Int64 tellg( ); 00050 virtual void seekg( Imf::Int64 pos ); 00051 virtual void clear( ); 00052 00053 private: 00054 QIODevice *m_dev; 00055 }; 00056 00057 bool K_IStream::read( char c[], int n ) 00058 { 00059 qint64 result = m_dev->read( c, n ); 00060 if ( result > 0 ) { 00061 return true; 00062 } else if ( result == 0 ) { 00063 throw Iex::InputExc( "Unexpected end of file" ); 00064 } else // negative value { 00065 Iex::throwErrnoExc( "Error in read", result ); 00066 return false; 00067 } 00068 00069 Imf::Int64 K_IStream::tellg( ) 00070 { 00071 return m_dev->pos(); 00072 } 00073 00074 void K_IStream::seekg( Imf::Int64 pos ) 00075 { 00076 m_dev->seek( pos ); 00077 } 00078 00079 void K_IStream::clear( ) 00080 { 00081 // TODO 00082 } 00083 00084 /* this does a conversion from the ILM Half (equal to Nvidia Half) 00085 * format into the normal 32 bit pixel format. Process is from the 00086 * ILM code. 00087 */ 00088 QRgb RgbaToQrgba(struct Imf::Rgba imagePixel) 00089 { 00090 float r,g,b,a; 00091 00092 // 1) Compensate for fogging by subtracting defog 00093 // from the raw pixel values. 00094 // Response: We work with defog of 0.0, so this is a no-op 00095 00096 // 2) Multiply the defogged pixel values by 00097 // 2^(exposure + 2.47393). 00098 // Response: We work with exposure of 0.0. 00099 // (2^2.47393) is 5.55555 00100 r = imagePixel.r * 5.55555; 00101 g = imagePixel.g * 5.55555; 00102 b = imagePixel.b * 5.55555; 00103 a = imagePixel.a * 5.55555; 00104 00105 // 3) Values, which are now 1.0, are called "middle gray". 00106 // If defog and exposure are both set to 0.0, then 00107 // middle gray corresponds to a raw pixel value of 0.18. 00108 // In step 6, middle gray values will be mapped to an 00109 // intensity 3.5 f-stops below the display's maximum 00110 // intensity. 00111 // Response: no apparent content. 00112 00113 // 4) Apply a knee function. The knee function has two 00114 // parameters, kneeLow and kneeHigh. Pixel values 00115 // below 2^kneeLow are not changed by the knee 00116 // function. Pixel values above kneeLow are lowered 00117 // according to a logarithmic curve, such that the 00118 // value 2^kneeHigh is mapped to 2^3.5 (in step 6, 00119 // this value will be mapped to the display's 00120 // maximum intensity). 00121 // Response: kneeLow = 0.0 (2^0.0 => 1); kneeHigh = 5.0 (2^5 =>32) 00122 if (r > 1.0) 00123 r = 1.0 + Imath::Math<float>::log ((r-1.0) * 0.184874 + 1) / 0.184874; 00124 if (g > 1.0) 00125 g = 1.0 + Imath::Math<float>::log ((g-1.0) * 0.184874 + 1) / 0.184874; 00126 if (b > 1.0) 00127 b = 1.0 + Imath::Math<float>::log ((b-1.0) * 0.184874 + 1) / 0.184874; 00128 if (a > 1.0) 00129 a = 1.0 + Imath::Math<float>::log ((a-1.0) * 0.184874 + 1) / 0.184874; 00130 // 00131 // 5) Gamma-correct the pixel values, assuming that the 00132 // screen's gamma is 0.4545 (or 1/2.2). 00133 r = Imath::Math<float>::pow (r, 0.4545); 00134 g = Imath::Math<float>::pow (g, 0.4545); 00135 b = Imath::Math<float>::pow (b, 0.4545); 00136 a = Imath::Math<float>::pow (a, 0.4545); 00137 00138 // 6) Scale the values such that pixels middle gray 00139 // pixels are mapped to 84.66 (or 3.5 f-stops below 00140 // the display's maximum intensity). 00141 // 00142 // 7) Clamp the values to [0, 255]. 00143 return qRgba( (unsigned char) (Imath::clamp ( r * 84.66f, 0.f, 255.f ) ), 00144 (unsigned char) (Imath::clamp ( g * 84.66f, 0.f, 255.f ) ), 00145 (unsigned char) (Imath::clamp ( b * 84.66f, 0.f, 255.f ) ), 00146 (unsigned char) (Imath::clamp ( a * 84.66f, 0.f, 255.f ) ) ); 00147 } 00148 00149 EXRHandler::EXRHandler() 00150 { 00151 } 00152 00153 bool EXRHandler::canRead() const 00154 { 00155 if (canRead(device())) { 00156 setFormat("exr"); 00157 return true; 00158 } 00159 return false; 00160 } 00161 00162 QByteArray EXRHandler::name() const 00163 { 00164 // TODO 00165 return QByteArray("exr"); 00166 } 00167 00168 bool EXRHandler::read( QImage *outImage ) 00169 { 00170 try 00171 { 00172 int width, height; 00173 00174 K_IStream istr( device(), QByteArray() ); 00175 Imf::RgbaInputFile file( istr ); 00176 Imath::Box2i dw = file.dataWindow(); 00177 00178 width = dw.max.x - dw.min.x + 1; 00179 height = dw.max.y - dw.min.y + 1; 00180 00181 Imf::Array2D<Imf::Rgba> pixels; 00182 pixels.resizeErase (height, width); 00183 00184 file.setFrameBuffer (&pixels[0][0] - dw.min.x - dw.min.y * width, 1, width); 00185 file.readPixels (dw.min.y, dw.max.y); 00186 00187 QImage image(width, height, QImage::Format_RGB32); 00188 if( image.isNull()) 00189 return false; 00190 00191 // somehow copy pixels into image 00192 for ( int y=0; y < height; y++ ) { 00193 for ( int x=0; x < width; x++ ) { 00194 // copy pixels(x,y) into image(x,y) 00195 image.setPixel( x, y, RgbaToQrgba( pixels[y][x] ) ); 00196 } 00197 } 00198 00199 *outImage = image; 00200 00201 return true; 00202 } 00203 catch (const std::exception &exc) 00204 { 00205 kDebug() << exc.what(); 00206 return false; 00207 } 00208 } 00209 00210 00211 bool EXRHandler::write( const QImage &image ) 00212 { 00213 // TODO: stub 00214 Q_UNUSED( image ); 00215 return false; 00216 } 00217 00218 00219 bool EXRHandler::canRead(QIODevice *device) 00220 { 00221 if (!device) { 00222 qWarning("EXRHandler::canRead() called with no device"); 00223 return false; 00224 } 00225 00226 qint64 oldPos = device->pos(); 00227 00228 const QByteArray head = device->readLine(4); 00229 int readBytes = head.size(); 00230 if (device->isSequential()) { 00231 while (readBytes > 0) 00232 device->ungetChar(head[readBytes-- - 1]); 00233 } else { 00234 device->seek(oldPos); 00235 } 00236 00237 return Imf::isImfMagic( head.data() ); 00238 } 00239 00240 00241 /* --- Plugin --- */ 00242 00243 QStringList EXRPlugin::keys() const 00244 { 00245 return QStringList() << "exr" << "EXR"; 00246 } 00247 00248 00249 QImageIOPlugin::Capabilities EXRPlugin::capabilities(QIODevice *device, const QByteArray &format) const 00250 { 00251 if ( format == "exr" || format == "EXR" ) 00252 return Capabilities(CanRead); 00253 if ( !format.isEmpty() ) 00254 return 0; 00255 if ( !device->isOpen() ) 00256 return 0; 00257 00258 Capabilities cap; 00259 if (device->isReadable() && EXRHandler::canRead(device)) 00260 cap |= CanRead; 00261 return cap; 00262 } 00263 00264 QImageIOHandler *EXRPlugin::create(QIODevice *device, const QByteArray &format) const 00265 { 00266 QImageIOHandler *handler = new EXRHandler; 00267 handler->setDevice(device); 00268 handler->setFormat(format); 00269 return handler; 00270 } 00271 00272 Q_EXPORT_STATIC_PLUGIN( EXRPlugin ) 00273 Q_EXPORT_PLUGIN2( exr, EXRPlugin )
KDE 4.6 API Reference