File indexing completed on 2026-04-09 07:49:44
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021 #include <iostream>
0022 #include <fstream>
0023
0024 #include "SLOG.hh"
0025 #include "SStr.hh"
0026 #include "SColor.hh"
0027 #include "SPPM.hh"
0028
0029 const plog::Severity SPPM::LEVEL = SLOG::EnvLevel("SPPM", "DEBUG") ;
0030
0031 SPPM::SPPM()
0032 :
0033 pixels(NULL),
0034 pwidth(0),
0035 pheight(0),
0036 pscale(0),
0037 yflip(true)
0038 {
0039 }
0040
0041 std::string SPPM::desc() const
0042 {
0043 std::stringstream ss ;
0044 ss << " SPPM "
0045 << " pwidth " << pwidth
0046 << " pheight " << pheight
0047 << " pscale " << pscale
0048 << " yflip " << yflip
0049 ;
0050 return ss.str();
0051 }
0052
0053 void SPPM::resize( int width, int height, int scale )
0054 {
0055 bool changed_size = !(width == pwidth && height == pheight && scale == pscale) ;
0056 if( pixels == NULL || changed_size )
0057 {
0058 delete [] pixels ;
0059 pixels = NULL ;
0060 pwidth = width ;
0061 pheight = height ;
0062 pscale = scale ;
0063 int size = 4 * pwidth * pscale * pheight * pscale ;
0064 pixels = new unsigned char[size];
0065 LOG(LEVEL) << "creating resized pixels buffer " << desc() ;
0066 }
0067 }
0068
0069 void SPPM::save(const char* path)
0070 {
0071 if(path == NULL ) path = "/tmp/SPPM.ppm" ;
0072 save(path, pwidth*pscale, pheight*pscale, pixels, yflip );
0073 LOG(fatal)
0074 << " path " << path
0075 << " desc " << desc()
0076 ;
0077 }
0078
0079 void SPPM::save(const char* path, int width, int height, const unsigned char* image, bool yflip)
0080 {
0081
0082 std::cout << "SPPM::save " << path << std::endl ;
0083
0084 FILE * fp;
0085 fp = fopen(path, "wb");
0086 LOG_IF(fatal, !fp) << "FAILED to open for writing " << path ;
0087 assert(fp);
0088
0089 int ncomp = 4;
0090 fprintf(fp, "P6\n%d %d\n%d\n", width, height, 255);
0091
0092 unsigned size = height*width*3 ;
0093 unsigned char* data = new unsigned char[size] ;
0094
0095 for( int h=0 ; h < height ; h++ )
0096 {
0097 int y = yflip ? height - 1 - h : h ;
0098
0099 for( int x=0; x < width ; ++x )
0100 {
0101 *(data + (y*width+x)*3+0) = image[(h*width+x)*ncomp+0] ;
0102 *(data + (y*width+x)*3+1) = image[(h*width+x)*ncomp+1] ;
0103 *(data + (y*width+x)*3+2) = image[(h*width+x)*ncomp+2] ;
0104 }
0105 }
0106 fwrite(data, sizeof(unsigned char)*size, 1, fp);
0107 fclose(fp);
0108 delete[] data;
0109 }
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122 void SPPM::snap(const char* path)
0123 {
0124 download();
0125 save(path);
0126 }
0127
0128
0129 void SPPM::write( const char* filename, const float* image, int width, int height, int ncomp, bool yflip )
0130 {
0131 std::ofstream out( filename, std::ios::out | std::ios::binary );
0132 if( !out )
0133 {
0134 std::cerr << "Cannot open file " << filename << "'" << std::endl;
0135 return;
0136 }
0137
0138 out << "P6\n" << width << " " << height << "\n255" << std::endl;
0139
0140 for( int h=0; h < height ; h++ )
0141 {
0142 int y = yflip ? height - 1 - h : h ;
0143
0144 for( int x = 0; x < width*ncomp; ++x )
0145 {
0146 float val = image[y*width*ncomp + x];
0147 unsigned char cval = val < 0.0f ? 0u : val > 1.0f ? 255u : static_cast<unsigned char>( val*255.0f );
0148 out.put( cval );
0149 }
0150 }
0151 LOG(LEVEL) << "Wrote file (float*)" << filename ;
0152 }
0153
0154
0155
0156
0157
0158
0159
0160
0161
0162
0163
0164 void SPPM::write( const char* filename, const unsigned char* image, int width, int height, int ncomp, bool yflip )
0165 {
0166 FILE * fp;
0167 fp = fopen(filename, "wb");
0168 LOG_IF(fatal, !fp) << "FAILED to open for writing " << filename ;
0169 assert(fp);
0170 fprintf(fp, "P6\n%d %d\n%d\n", width, height, 255);
0171
0172
0173 unsigned size = height*width*3 ;
0174 unsigned char* data = new unsigned char[size] ;
0175
0176 for( int h=0; h < height ; h++ )
0177 {
0178 int y = yflip ? height - 1 - h : h ;
0179
0180 for( int x=0; x < width ; ++x )
0181 {
0182 *(data + (y*width+x)*3+0) = image[(h*width+x)*ncomp+0] ;
0183 *(data + (y*width+x)*3+1) = image[(h*width+x)*ncomp+1] ;
0184 *(data + (y*width+x)*3+2) = image[(h*width+x)*ncomp+2] ;
0185 }
0186 }
0187
0188 fwrite(data, sizeof(unsigned char)*size, 1, fp);
0189 fclose(fp);
0190 LOG(LEVEL) << "Wrote file (unsigned char*) " << filename ;
0191 delete[] data;
0192 }
0193
0194
0195
0196
0197
0198
0199
0200
0201
0202
0203
0204 void SPPM::dumpHeader( const char* path )
0205 {
0206 unsigned width(0);
0207 unsigned height(0);
0208 unsigned mode(0);
0209 unsigned bits(0);
0210
0211 int rc = readHeader(path, width, height, mode, bits );
0212
0213 LOG(info)
0214 << " path " << path
0215 << " width " << width
0216 << " height " << height
0217 << " mode " << mode
0218 << " bits " << bits
0219 << " rc " << rc
0220 ;
0221 }
0222
0223 int SPPM::readHeader( const char* path, unsigned& width, unsigned& height, unsigned& mode, unsigned& bits )
0224 {
0225 assert(SStr::EndsWith(path, ".ppm"));
0226 std::ifstream f(path, std::ios::binary);
0227 if(f.fail())
0228 {
0229 LOG(fatal) << "Could not open path: " << path ;
0230 return 1 ;
0231 }
0232
0233 mode = 0;
0234 std::string s;
0235 f >> s;
0236 if (s == "P3") mode = 3;
0237 else if (s == "P6") mode = 6;
0238
0239 f >> width ;
0240 f >> height ;
0241 f >> bits;
0242
0243 f.close();
0244 return 0 ;
0245 }
0246
0247
0248
0249
0250
0251
0252
0253
0254
0255
0256
0257
0258
0259
0260
0261
0262
0263
0264
0265
0266
0267
0268
0269
0270
0271
0272
0273
0274
0275
0276
0277
0278 int SPPM::read( const char* path, std::vector<unsigned char>& img, unsigned& width, unsigned& height, const unsigned ncomp, const bool yflip )
0279 {
0280 assert(SStr::EndsWith(path, ".ppm"));
0281
0282 std::ifstream f(path, std::ios::binary);
0283 if(f.fail())
0284 {
0285 std::cout << "Could not open path: " << path << std::endl;
0286 return 1 ;
0287 }
0288
0289 int mode = 0;
0290 std::string s;
0291 f >> s;
0292 if (s == "P3") mode = 3;
0293 else if (s == "P6") mode = 6;
0294
0295 bool expect_mode = mode == 6 ;
0296 if(!expect_mode) std::cerr << "SPPM::read : unexpected mode " << mode << std::endl ;
0297 assert(expect_mode);
0298
0299 f >> width ;
0300 f >> height ;
0301
0302 int bits = 0;
0303 f >> bits;
0304 assert( bits == 255 );
0305 f.get();
0306
0307 unsigned filesize = width*height*3 ;
0308 unsigned arraysize = ncomp == 3 ? filesize : width*height*ncomp ;
0309
0310 img.clear();
0311 img.resize(arraysize);
0312 unsigned char* imgdata = img.data();
0313
0314 if( ncomp == 3 && yflip == false )
0315 {
0316 f.read((char*)imgdata, filesize);
0317 }
0318 else
0319 {
0320
0321
0322
0323 unsigned char* tmp = new unsigned char[filesize] ;
0324 f.read( (char*)tmp, filesize);
0325
0326 for( int h=0; h < int(height) ; h++ )
0327 {
0328 int y = yflip ? height - 1 - h : h ;
0329 for( int x=0; x < int(width) ; ++x )
0330 {
0331 for( int k=0 ; k < int(ncomp) ; k++ )
0332 {
0333 imgdata[ (h*width+x)*ncomp + k] = k < 3 ? tmp[(y*width+x)*3+k] : 0xff ;
0334 }
0335 }
0336 }
0337 delete [] tmp ;
0338 }
0339 f.close();
0340 return 0 ;
0341 }
0342
0343
0344 void SPPM::AddBorder( std::vector<unsigned char>& img, const int width, const int height, const int ncomp, const bool yflip )
0345 {
0346 int size = width*height*ncomp ;
0347 bool expect_size = int(img.size()) == size ;
0348 if(!expect_size) std::cerr << "SPPM::AddBorder unexpected size " << size << std::endl ;
0349 assert( expect_size );
0350 unsigned char* imgdata = img.data();
0351 AddBorder( imgdata, width, height, ncomp, yflip );
0352 }
0353
0354
0355 void SPPM::AddBorder( unsigned char* imgdata, const int width, const int height, const int ncomp, const bool yflip )
0356 {
0357 SColor border_color = SColors::red ;
0358
0359 int margin = 16 ;
0360
0361 for( int h=0; h < height ; h++ )
0362 {
0363 int y = yflip ? height - 1 - h : h ;
0364
0365 bool y_border = y < margin || height - 1 - y < margin ;
0366
0367 for( int x=0; x < width ; ++x )
0368 {
0369 bool x_border = x < margin || width - x < margin ;
0370
0371 if( y_border || x_border )
0372 for( int k=0 ; k < ncomp ; k++ ) imgdata[ (h*width+x)*ncomp + k] = k < 3 ? border_color.get(k) : 0xff ;
0373
0374 }
0375 }
0376 }
0377
0378
0379
0380 void SPPM::AddMidline( std::vector<unsigned char>& img, const int width, const int height, const int ncomp, const bool yflip )
0381 {
0382 int size = width*height*ncomp ;
0383 bool expect_size = int(img.size()) == size ;
0384 if(!expect_size) std::cerr << "SPPM::AddMidline unexpected size " << size << std::endl ;
0385 assert( expect_size );
0386 unsigned char* imgdata = img.data();
0387 AddMidline( imgdata, width, height, ncomp, yflip );
0388 }
0389
0390
0391 void SPPM::AddMidline( unsigned char* imgdata, const int width, const int height, const int ncomp, const bool yflip )
0392 {
0393 SColor midline_color = SColors::green ;
0394
0395 int margin = 8 ;
0396
0397 for( int h=0; h < height ; h++ )
0398 {
0399 int y = yflip ? height - 1 - h : h ;
0400
0401 bool y_mid = std::abs(y - height/2) < margin ;
0402
0403 for( int x=0; x < width ; ++x )
0404 {
0405 bool x_mid = std::abs( x - width/2 ) < margin ;
0406
0407 if( y_mid || x_mid )
0408 for( int k=0 ; k < ncomp ; k++ ) imgdata[ (h*width+x)*ncomp + k] = k < 3 ? midline_color.get(k) : 0xff ;
0409
0410 }
0411 }
0412 }
0413
0414
0415
0416 void SPPM::AddQuadline( std::vector<unsigned char>& img, const int width, const int height, const int ncomp, const bool yflip )
0417 {
0418 int size = width*height*ncomp ;
0419 bool expect_size = int(img.size()) == size ;
0420 if(!expect_size) std::cerr << "SPPM::AddQuadline unexpected size " << size << std::endl ;
0421 assert( expect_size );
0422
0423 unsigned char* imgdata = img.data();
0424 AddQuadline( imgdata, width, height, ncomp, yflip );
0425 }
0426
0427
0428 void SPPM::AddQuadline( unsigned char* imgdata, const int width, const int height, const int ncomp, const bool yflip )
0429 {
0430 SColor quadline_color = SColors::blue ;
0431
0432 int margin = 8 ;
0433
0434 for( int h=0; h < height ; h++ )
0435 {
0436 int y = yflip ? height - 1 - h : h ;
0437
0438 bool y_quad0 = std::abs(y - height/4) < margin ;
0439 bool y_quad1 = std::abs(y - 3*height/4) < margin ;
0440
0441 for( int x=0; x < width ; ++x )
0442 {
0443 bool x_quad0 = std::abs( x - width/4 ) < margin ;
0444 bool x_quad1 = std::abs( x - 3*width/4 ) < margin ;
0445
0446 if( y_quad0 || y_quad1 || x_quad0 || x_quad1 )
0447 for( int k=0 ; k < ncomp ; k++ ) imgdata[ (h*width+x)*ncomp + k] = k < 3 ? quadline_color.get(k) : 0xff ;
0448 }
0449 }
0450 }
0451
0452
0453
0454
0455
0456
0457
0458
0459
0460
0461
0462
0463 unsigned char* SPPM::MakeTestImage(const int width, const int height, const int ncomp, const bool yflip, const char* config)
0464 {
0465 assert( ncomp == 3 );
0466
0467 int size = width*height*ncomp ;
0468 unsigned char* imgdata = new unsigned char[size] ;
0469
0470 for(int i=0 ; i < height ; i++){
0471 for(int j=0 ; j < width ; j++){
0472
0473 unsigned idx = i*width + j ;
0474 unsigned mi = i % 32 ;
0475 unsigned mj = j % 32 ;
0476
0477 float fi = float(i)/float(height) ;
0478 float fj = float(j)/float(width) ;
0479
0480 unsigned char ii = (1.-fi)*255.f ;
0481 unsigned char jj = (1.-fj)*255.f ;
0482
0483 SColor col = SColors::white ;
0484 if( SStr::Contains(config, "checkerboard") )
0485 {
0486 if( mi < 4 ) col = SColors::black ;
0487 else if (mj < 4 ) col = SColors::red ;
0488 else col = { jj, jj, jj } ;
0489 }
0490 else if( SStr::Contains(config, "horizontal_gradient") )
0491 {
0492 col = { jj , jj, jj } ;
0493 }
0494 else if( SStr::Contains(config, "vertical_gradient") )
0495 {
0496 col = { ii , ii, ii } ;
0497 }
0498
0499 imgdata[idx*ncomp+0] = col.r ;
0500 imgdata[idx*ncomp+1] = col.g ;
0501 imgdata[idx*ncomp+2] = col.b ;
0502 }
0503 }
0504
0505
0506 bool add_border = SStr::Contains(config, "add_border") ;
0507 bool add_midline = SStr::Contains(config, "add_midline") ;
0508 bool add_quadline = SStr::Contains(config, "add_quadline") ;
0509
0510 if(add_border) SPPM::AddBorder(imgdata, width, height, ncomp, yflip);
0511 if(add_midline) SPPM::AddMidline(imgdata, width, height, ncomp, yflip);
0512 if(add_quadline) SPPM::AddQuadline(imgdata, width, height, ncomp, yflip);
0513
0514 return imgdata ;
0515 }
0516
0517
0518 unsigned SPPM::ImageCompare(const int width, const int height, const int ncomp, const unsigned char* imgdata, const unsigned char* imgdata2 )
0519 {
0520 unsigned count(0);
0521 unsigned mismatch(0);
0522 for(int h=0 ; h < height ; h++){
0523 for(int w=0 ; w < width ; w++){
0524
0525 unsigned idx = h*width + w ;
0526 assert( idx*ncomp == count );
0527 count += ncomp ;
0528
0529 unsigned r = idx*ncomp+0 ;
0530 unsigned g = idx*ncomp+1 ;
0531 unsigned b = idx*ncomp+2 ;
0532
0533 bool match =
0534 imgdata[r] == imgdata2[r] &&
0535 imgdata[g] == imgdata2[g] &&
0536 imgdata[b] == imgdata2[b] ;
0537
0538 if(!match)
0539 {
0540 mismatch++ ;
0541 std::cout
0542 << " h " << std::setw(3) << h
0543 << " w " << std::setw(3) << w
0544 << " idx " << idx
0545 << " mismatch " << mismatch
0546 << " imgdata[rgb] "
0547 << "("
0548 << std::setw(3) << unsigned(imgdata[r])
0549 << " "
0550 << std::setw(3) << unsigned(imgdata[g])
0551 << " "
0552 << std::setw(3) << unsigned(imgdata[b])
0553 << ")"
0554 << " imgdata2[rgb] "
0555 << "("
0556 << std::setw(3) << unsigned(imgdata2[r])
0557 << " "
0558 << std::setw(3) << unsigned(imgdata2[g])
0559 << " "
0560 << std::setw(3) << unsigned(imgdata2[b])
0561 << ")"
0562 << std::endl
0563 ;
0564 }
0565 }
0566 }
0567 return mismatch ;
0568 }
0569
0570
0571