1 /*M/////////////////////////////////////////////////////////////////////////////////////// 2 // 3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. 4 // 5 // By downloading, copying, installing or using the software you agree to this license. 6 // If you do not agree to this license, do not download, install, 7 // copy or use the software. 8 // 9 // 10 // Intel License Agreement 11 // For Open Source Computer Vision Library 12 // 13 // Copyright (C) 2000, Intel Corporation, all rights reserved. 14 // Third party copyrights are property of their respective owners. 15 // 16 // Redistribution and use in source and binary forms, with or without modification, 17 // are permitted provided that the following conditions are met: 18 // 19 // * Redistribution's of source code must retain the above copyright notice, 20 // this list of conditions and the following disclaimer. 21 // 22 // * Redistribution's in binary form must reproduce the above copyright notice, 23 // this list of conditions and the following disclaimer in the documentation 24 // and/or other materials provided with the distribution. 25 // 26 // * The name of Intel Corporation may not be used to endorse or promote products 27 // derived from this software without specific prior written permission. 28 // 29 // This software is provided by the copyright holders and contributors "as is" and 30 // any express or implied warranties, including, but not limited to, the implied 31 // warranties of merchantability and fitness for a particular purpose are disclaimed. 32 // In no event shall the Intel Corporation or contributors be liable for any direct, 33 // indirect, incidental, special, exemplary, or consequential damages 34 // (including, but not limited to, procurement of substitute goods or services; 35 // loss of use, data, or profits; or business interruption) however caused 36 // and on any theory of liability, whether in contract, strict liability, 37 // or tort (including negligence or otherwise) arising in any way out of 38 // the use of this software, even if advised of the possibility of such damage. 39 // 40 //M*/ 41 42 #include "_highgui.h" 43 44 #ifdef HAVE_ILMIMF 45 46 #include <OpenEXR/ImfHeader.h> 47 #include <OpenEXR/ImfInputFile.h> 48 #include <OpenEXR/ImfOutputFile.h> 49 #include <OpenEXR/ImfChannelList.h> 50 #include <OpenEXR/ImfStandardAttributes.h> 51 #include <OpenEXR/half.h> 52 #include "grfmt_exr.h" 53 54 #if defined _MSC_VER && _MSC_VER >= 1200 55 #pragma comment(lib, "Half.lib") 56 #pragma comment(lib, "Iex.lib") 57 #pragma comment(lib, "IlmImf.lib") 58 #pragma comment(lib, "IlmThread.lib") 59 #pragma comment(lib, "Imath.lib") 60 61 #undef UINT 62 #define UINT ((Imf::PixelType)0) 63 #undef HALF 64 #define HALF ((Imf::PixelType)1) 65 #undef FLOAT 66 #define FLOAT ((Imf::PixelType)2) 67 #undef uint 68 #define uint unsigned 69 70 #endif 71 72 // Exr Filter Factory 73 GrFmtExr::GrFmtExr() 74 { 75 m_sign_len = 4; 76 m_signature = "\x76\x2f\x31\x01"; 77 m_description = "OpenEXR Image files (*.exr)"; 78 } 79 80 81 GrFmtExr::~GrFmtExr() 82 { 83 } 84 85 86 GrFmtReader* GrFmtExr::NewReader( const char* filename ) 87 { 88 return new GrFmtExrReader( filename ); 89 } 90 91 92 GrFmtWriter* GrFmtExr::NewWriter( const char* filename ) 93 { 94 return new GrFmtExrWriter( filename ); 95 } 96 97 98 /////////////////////// GrFmtExrReader /////////////////// 99 100 GrFmtExrReader::GrFmtExrReader( const char* filename ) : GrFmtReader( filename ) 101 { 102 m_file = new InputFile( filename ); 103 m_red = m_green = m_blue = 0; 104 } 105 106 107 GrFmtExrReader::~GrFmtExrReader() 108 { 109 Close(); 110 } 111 112 113 void GrFmtExrReader::Close() 114 { 115 if( m_file ) 116 { 117 delete m_file; 118 m_file = 0; 119 } 120 121 GrFmtReader::Close(); 122 } 123 124 bool GrFmtExrReader::ReadHeader() 125 { 126 bool result = false; 127 128 if( !m_file ) // probably paranoid 129 return false; 130 131 m_datawindow = m_file->header().dataWindow(); 132 m_width = m_datawindow.max.x - m_datawindow.min.x + 1; 133 m_height = m_datawindow.max.y - m_datawindow.min.y + 1; 134 135 // the type HALF is converted to 32 bit float 136 // and the other types supported by OpenEXR are 32 bit anyway 137 m_bit_depth = 32; 138 139 if( hasChromaticities( m_file->header() )) 140 m_chroma = chromaticities( m_file->header() ); 141 142 const ChannelList &channels = m_file->header().channels(); 143 m_red = channels.findChannel( "R" ); 144 m_green = channels.findChannel( "G" ); 145 m_blue = channels.findChannel( "B" ); 146 if( m_red || m_green || m_blue ) 147 { 148 m_iscolor = true; 149 m_ischroma = false; 150 result = true; 151 } 152 else 153 { 154 m_green = channels.findChannel( "Y" ); 155 if( m_green ) 156 { 157 m_ischroma = true; 158 m_red = channels.findChannel( "RY" ); 159 m_blue = channels.findChannel( "BY" ); 160 m_iscolor = (m_blue || m_red); 161 result = true; 162 } 163 else 164 result = false; 165 } 166 167 if( result ) 168 { 169 int uintcnt = 0; 170 int chcnt = 0; 171 if( m_red ) 172 { 173 chcnt++; 174 uintcnt += ( m_red->type == UINT ); 175 } 176 if( m_green ) 177 { 178 chcnt++; 179 uintcnt += ( m_green->type == UINT ); 180 } 181 if( m_blue ) 182 { 183 chcnt++; 184 uintcnt += ( m_blue->type == UINT ); 185 } 186 m_type = (chcnt == uintcnt) ? UINT : FLOAT; 187 188 m_isfloat = (m_type == FLOAT); 189 } 190 191 if( !result ) 192 Close(); 193 194 return result; 195 } 196 197 198 bool GrFmtExrReader::ReadData( uchar* data, int step, int color ) 199 { 200 bool justcopy = m_native_depth; 201 bool chromatorgb = false; 202 bool rgbtogray = false; 203 bool result = true; 204 FrameBuffer frame; 205 int xsample[3] = {1, 1, 1}; 206 char *buffer; 207 int xstep; 208 int ystep; 209 210 xstep = m_native_depth ? 4 : 1; 211 212 if( !m_native_depth || (!color && m_iscolor )) 213 { 214 buffer = (char *)new float[ m_width * 3 ]; 215 ystep = 0; 216 } 217 else 218 { 219 buffer = (char *)data; 220 ystep = step; 221 } 222 223 if( m_ischroma ) 224 { 225 if( color ) 226 { 227 if( m_iscolor ) 228 { 229 if( m_blue ) 230 { 231 frame.insert( "BY", Slice( m_type, 232 buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep, 233 12, ystep, m_blue->xSampling, m_blue->ySampling, 0.0 )); 234 xsample[0] = m_blue->ySampling; 235 } 236 if( m_green ) 237 { 238 frame.insert( "Y", Slice( m_type, 239 buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 4, 240 12, ystep, m_green->xSampling, m_green->ySampling, 0.0 )); 241 xsample[1] = m_green->ySampling; 242 } 243 if( m_red ) 244 { 245 frame.insert( "RY", Slice( m_type, 246 buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 8, 247 12, ystep, m_red->xSampling, m_red->ySampling, 0.0 )); 248 xsample[2] = m_red->ySampling; 249 } 250 chromatorgb = true; 251 } 252 else 253 { 254 frame.insert( "Y", Slice( m_type, 255 buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep, 256 12, ystep, m_green->xSampling, m_green->ySampling, 0.0 )); 257 frame.insert( "Y", Slice( m_type, 258 buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 4, 259 12, ystep, m_green->xSampling, m_green->ySampling, 0.0 )); 260 frame.insert( "Y", Slice( m_type, 261 buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 8, 262 12, ystep, m_green->xSampling, m_green->ySampling, 0.0 )); 263 xsample[0] = m_green->ySampling; 264 xsample[1] = m_green->ySampling; 265 xsample[2] = m_green->ySampling; 266 } 267 } 268 else 269 { 270 frame.insert( "Y", Slice( m_type, 271 buffer - m_datawindow.min.x * 4 - m_datawindow.min.y * ystep, 272 4, ystep, m_green->xSampling, m_green->ySampling, 0.0 )); 273 xsample[0] = m_green->ySampling; 274 } 275 } 276 else 277 { 278 if( m_blue ) 279 { 280 frame.insert( "B", Slice( m_type, 281 buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep, 282 12, ystep, m_blue->xSampling, m_blue->ySampling, 0.0 )); 283 xsample[0] = m_blue->ySampling; 284 } 285 if( m_green ) 286 { 287 frame.insert( "G", Slice( m_type, 288 buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 4, 289 12, ystep, m_green->xSampling, m_green->ySampling, 0.0 )); 290 xsample[1] = m_green->ySampling; 291 } 292 if( m_red ) 293 { 294 frame.insert( "R", Slice( m_type, 295 buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 8, 296 12, ystep, m_red->xSampling, m_red->ySampling, 0.0 )); 297 xsample[2] = m_red->ySampling; 298 } 299 if(color == 0) 300 { 301 rgbtogray = true; 302 justcopy = false; 303 } 304 } 305 306 m_file->setFrameBuffer( frame ); 307 if( justcopy ) 308 { 309 m_file->readPixels( m_datawindow.min.y, m_datawindow.max.y ); 310 311 if( color ) 312 { 313 if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) ) 314 UpSample( data, 3, step / xstep, xsample[0], m_blue->ySampling ); 315 if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) ) 316 UpSample( data + xstep, 3, step / xstep, xsample[1], m_green->ySampling ); 317 if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) ) 318 UpSample( data + 2 * xstep, 3, step / xstep, xsample[2], m_red->ySampling ); 319 } 320 else if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) ) 321 UpSample( data, 1, step / xstep, xsample[0], m_green->ySampling ); 322 } 323 else 324 { 325 uchar *out = data; 326 int x, y; 327 for( y = m_datawindow.min.y; y <= m_datawindow.max.y; y++ ) 328 { 329 m_file->readPixels( y, y ); 330 331 if( rgbtogray ) 332 { 333 if( xsample[0] != 1 ) 334 UpSampleX( (float *)buffer, 3, xsample[0] ); 335 if( xsample[1] != 1 ) 336 UpSampleX( (float *)buffer + 4, 3, xsample[1] ); 337 if( xsample[2] != 1 ) 338 UpSampleX( (float *)buffer + 8, 3, xsample[2] ); 339 340 RGBToGray( (float *)buffer, (float *)out ); 341 } 342 else 343 { 344 if( xsample[0] != 1 ) 345 UpSampleX( (float *)buffer, 3, xsample[0] ); 346 if( xsample[1] != 1 ) 347 UpSampleX( (float *)(buffer + 4), 3, xsample[1] ); 348 if( xsample[2] != 1 ) 349 UpSampleX( (float *)(buffer + 8), 3, xsample[2] ); 350 351 if( chromatorgb ) 352 ChromaToBGR( (float *)buffer, 1, step ); 353 354 if( m_type == FLOAT ) 355 { 356 float *fi = (float *)buffer; 357 for( x = 0; x < m_width * 3; x++) 358 { 359 int t = cvRound(fi[x]*5); 360 out[x] = CV_CAST_8U(t); 361 } 362 } 363 else 364 { 365 uint *ui = (uint *)buffer; 366 for( x = 0; x < m_width * 3; x++) 367 { 368 uint t = ui[x]; 369 out[x] = CV_CAST_8U(t); 370 } 371 } 372 } 373 374 out += step; 375 } 376 if( color ) 377 { 378 if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) ) 379 UpSampleY( data, 3, step / xstep, m_blue->ySampling ); 380 if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) ) 381 UpSampleY( data + xstep, 3, step / xstep, m_green->ySampling ); 382 if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) ) 383 UpSampleY( data + 2 * xstep, 3, step / xstep, m_red->ySampling ); 384 } 385 else if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) ) 386 UpSampleY( data, 1, step / xstep, m_green->ySampling ); 387 } 388 389 if( chromatorgb ) 390 ChromaToBGR( (float *)data, m_height, step / xstep ); 391 392 Close(); 393 394 return result; 395 } 396 397 /** 398 // on entry pixel values are stored packed in the upper left corner of the image 399 // this functions expands them by duplication to cover the whole image 400 */ 401 void GrFmtExrReader::UpSample( uchar *data, int xstep, int ystep, int xsample, int ysample ) 402 { 403 for( int y = (m_height - 1) / ysample, yre = m_height - ysample; y >= 0; y--, yre -= ysample ) 404 { 405 for( int x = (m_width - 1) / xsample, xre = m_width - xsample; x >= 0; x--, xre -= xsample ) 406 { 407 for( int i = 0; i < ysample; i++ ) 408 { 409 for( int n = 0; n < xsample; n++ ) 410 { 411 if( !m_native_depth ) 412 data[(yre + i) * ystep + (xre + n) * xstep] = data[y * ystep + x * xstep]; 413 else if( m_type == FLOAT ) 414 ((float *)data)[(yre + i) * ystep + (xre + n) * xstep] = ((float *)data)[y * ystep + x * xstep]; 415 else 416 ((uint *)data)[(yre + i) * ystep + (xre + n) * xstep] = ((uint *)data)[y * ystep + x * xstep]; 417 } 418 } 419 } 420 } 421 } 422 423 /** 424 // on entry pixel values are stored packed in the upper left corner of the image 425 // this functions expands them by duplication to cover the whole image 426 */ 427 void GrFmtExrReader::UpSampleX( float *data, int xstep, int xsample ) 428 { 429 for( int x = (m_width - 1) / xsample, xre = m_width - xsample; x >= 0; x--, xre -= xsample ) 430 { 431 for( int n = 0; n < xsample; n++ ) 432 { 433 if( m_type == FLOAT ) 434 ((float *)data)[(xre + n) * xstep] = ((float *)data)[x * xstep]; 435 else 436 ((uint *)data)[(xre + n) * xstep] = ((uint *)data)[x * xstep]; 437 } 438 } 439 } 440 441 /** 442 // on entry pixel values are stored packed in the upper left corner of the image 443 // this functions expands them by duplication to cover the whole image 444 */ 445 void GrFmtExrReader::UpSampleY( uchar *data, int xstep, int ystep, int ysample ) 446 { 447 for( int y = m_height - ysample, yre = m_height - ysample; y >= 0; y -= ysample, yre -= ysample ) 448 { 449 for( int x = 0; x < m_width; x++ ) 450 { 451 for( int i = 1; i < ysample; i++ ) 452 { 453 if( !m_native_depth ) 454 data[(yre + i) * ystep + x * xstep] = data[y * ystep + x * xstep]; 455 else if( m_type == FLOAT ) 456 ((float *)data)[(yre + i) * ystep + x * xstep] = ((float *)data)[y * ystep + x * xstep]; 457 else 458 ((uint *)data)[(yre + i) * ystep + x * xstep] = ((uint *)data)[y * ystep + x * xstep]; 459 } 460 } 461 } 462 } 463 464 /** 465 // algorithm from ImfRgbaYca.cpp 466 */ 467 void GrFmtExrReader::ChromaToBGR( float *data, int numlines, int step ) 468 { 469 int x, y, t; 470 471 for( y = 0; y < numlines; y++ ) 472 { 473 for( x = 0; x < m_width; x++ ) 474 { 475 double b, Y, r; 476 if( !m_native_depth ) 477 { 478 b = ((uchar *)data)[y * step + x * 3]; 479 Y = ((uchar *)data)[y * step + x * 3 + 1]; 480 r = ((uchar *)data)[y * step + x * 3 + 2]; 481 } 482 else if( m_type == FLOAT ) 483 { 484 b = data[y * step + x * 3]; 485 Y = data[y * step + x * 3 + 1]; 486 r = data[y * step + x * 3 + 2]; 487 } 488 else 489 { 490 b = ((uint *)data)[y * step + x * 3]; 491 Y = ((uint *)data)[y * step + x * 3 + 1]; 492 r = ((uint *)data)[y * step + x * 3 + 2]; 493 } 494 r = (r + 1) * Y; 495 b = (b + 1) * Y; 496 Y = (Y - b * m_chroma.blue[1] - r * m_chroma.red[1]) / m_chroma.green[1]; 497 498 if( !m_native_depth ) 499 { 500 int t = cvRound(b); 501 ((uchar *)data)[y * step + x * 3] = CV_CAST_8U(t); 502 t = cvRound(Y); 503 ((uchar *)data)[y * step + x * 3 + 1] = CV_CAST_8U(t); 504 t = cvRound(r); 505 ((uchar *)data)[y * step + x * 3 + 2] = CV_CAST_8U(t); 506 } 507 else if( m_type == FLOAT ) 508 { 509 data[y * step + x * 3] = (float)b; 510 data[y * step + x * 3 + 1] = (float)Y; 511 data[y * step + x * 3 + 2] = (float)r; 512 } 513 else 514 { 515 int t = cvRound(b); 516 ((uint *)data)[y * step + x * 3] = (uint)MAX(t,0); 517 t = cvRound(Y); 518 ((uint *)data)[y * step + x * 3 + 1] = (uint)MAX(t,0); 519 t = cvRound(r); 520 ((uint *)data)[y * step + x * 3 + 2] = (uint)MAX(t,0); 521 } 522 } 523 } 524 } 525 526 527 /** 528 // convert one row to gray 529 */ 530 void GrFmtExrReader::RGBToGray( float *in, float *out ) 531 { 532 if( m_type == FLOAT ) 533 { 534 if( m_native_depth ) 535 { 536 for( int i = 0, n = 0; i < m_width; i++, n += 3 ) 537 out[i] = in[n] * m_chroma.blue[0] + in[n + 1] * m_chroma.green[0] + in[n + 2] * m_chroma.red[0]; 538 } 539 else 540 { 541 uchar *o = (uchar *)out; 542 for( int i = 0, n = 0; i < m_width; i++, n += 3 ) 543 o[i] = (uchar) (in[n] * m_chroma.blue[0] + in[n + 1] * m_chroma.green[0] + in[n + 2] * m_chroma.red[0]); 544 } 545 } 546 else // UINT 547 { 548 if( m_native_depth ) 549 { 550 uint *ui = (uint *)in; 551 for( int i = 0; i < m_width * 3; i++ ) 552 ui[i] -= 0x80000000; 553 int *si = (int *)in; 554 for( int i = 0, n = 0; i < m_width; i++, n += 3 ) 555 ((int *)out)[i] = int(si[n] * m_chroma.blue[0] + si[n + 1] * m_chroma.green[0] + si[n + 2] * m_chroma.red[0]); 556 } 557 else // how to best convert float to uchar? 558 { 559 uint *ui = (uint *)in; 560 for( int i = 0, n = 0; i < m_width; i++, n += 3 ) 561 ((uchar *)out)[i] = uchar((ui[n] * m_chroma.blue[0] + ui[n + 1] * m_chroma.green[0] + ui[n + 2] * m_chroma.red[0]) * (256.0 / 4294967296.0)); 562 } 563 } 564 } 565 566 /////////////////////// GrFmtExrWriter /////////////////// 567 568 569 GrFmtExrWriter::GrFmtExrWriter( const char* filename ) : GrFmtWriter( filename ) 570 { 571 } 572 573 574 GrFmtExrWriter::~GrFmtExrWriter() 575 { 576 } 577 578 579 bool GrFmtExrWriter::IsFormatSupported( int depth ) 580 { 581 return depth == IPL_DEPTH_8U || depth == IPL_DEPTH_8S || 582 depth == IPL_DEPTH_16U || depth == IPL_DEPTH_16S || 583 depth == IPL_DEPTH_32S || depth == IPL_DEPTH_32F; 584 // TODO: do (or should) we support 64f? 585 } 586 587 588 // TODO scale appropriately 589 bool GrFmtExrWriter::WriteImage( const uchar* data, int step, 590 int width, int height, int depth, int channels ) 591 { 592 bool result = false; 593 594 Header header( width, height ); 595 PixelType type; 596 bool issigned = depth < 0; 597 bool isfloat = depth == IPL_DEPTH_32F || depth == IPL_DEPTH_64F; 598 599 if(depth == IPL_DEPTH_8U || depth == IPL_DEPTH_8S) 600 type = HALF; 601 else if(isfloat) 602 type = FLOAT; 603 else 604 type = UINT; 605 606 depth &= 255; 607 608 if( channels == 3 ) 609 { 610 header.channels().insert( "R", Channel( type )); 611 header.channels().insert( "G", Channel( type )); 612 header.channels().insert( "B", Channel( type )); 613 //printf("bunt\n"); 614 } 615 else 616 { 617 header.channels().insert( "Y", Channel( type )); 618 //printf("gray\n"); 619 } 620 621 OutputFile file( m_filename, header ); 622 623 FrameBuffer frame; 624 625 char *buffer; 626 int bufferstep; 627 int size; 628 if( type == FLOAT && depth == 32 ) 629 { 630 buffer = (char *)const_cast<uchar *>(data); 631 bufferstep = step; 632 size = 4; 633 } 634 else if( depth > 16 || type == UINT ) 635 { 636 buffer = (char *)new uint[width * channels]; 637 bufferstep = 0; 638 size = 4; 639 } 640 else 641 { 642 buffer = (char *)new half[width * channels]; 643 bufferstep = 0; 644 size = 2; 645 } 646 647 //printf("depth %d %s\n", depth, types[type]); 648 649 if( channels == 3 ) 650 { 651 frame.insert( "B", Slice( type, buffer, size * 3, bufferstep )); 652 frame.insert( "G", Slice( type, buffer + size, size * 3, bufferstep )); 653 frame.insert( "R", Slice( type, buffer + size * 2, size * 3, bufferstep )); 654 } 655 else 656 frame.insert( "Y", Slice( type, buffer, size, bufferstep )); 657 658 file.setFrameBuffer( frame ); 659 660 int offset = issigned ? 1 << (depth - 1) : 0; 661 662 result = true; 663 if( type == FLOAT && depth == 32 ) 664 { 665 try 666 { 667 file.writePixels( height ); 668 } 669 catch(...) 670 { 671 result = false; 672 } 673 } 674 else 675 { 676 // int scale = 1 << (32 - depth); 677 // printf("scale %d\n", scale); 678 for(int line = 0; line < height; line++) 679 { 680 if(type == UINT) 681 { 682 uint *buf = (uint *)buffer; // FIXME 64-bit problems 683 684 if( depth <= 8 ) 685 { 686 for(int i = 0; i < width * channels; i++) 687 buf[i] = data[i] + offset; 688 } 689 else if( depth <= 16 ) 690 { 691 unsigned short *sd = (unsigned short *)data; 692 for(int i = 0; i < width * channels; i++) 693 buf[i] = sd[i] + offset; 694 } 695 else 696 { 697 int *sd = (int *)data; // FIXME 64-bit problems 698 for(int i = 0; i < width * channels; i++) 699 buf[i] = (uint) sd[i] + offset; 700 } 701 } 702 else 703 { 704 half *buf = (half *)buffer; 705 706 if( depth <= 8 ) 707 { 708 for(int i = 0; i < width * channels; i++) 709 buf[i] = data[i]; 710 } 711 else if( depth <= 16 ) 712 { 713 unsigned short *sd = (unsigned short *)data; 714 for(int i = 0; i < width * channels; i++) 715 buf[i] = sd[i]; 716 } 717 } 718 try 719 { 720 file.writePixels( 1 ); 721 } 722 catch(...) 723 { 724 result = false; 725 break; 726 } 727 data += step; 728 } 729 delete buffer; 730 } 731 732 return result; 733 } 734 735 #endif 736 737 /* End of file. */ 738