Newer
Older
58001
58002
58003
58004
58005
58006
58007
58008
58009
58010
58011
58012
58013
58014
58015
58016
58017
58018
58019
58020
58021
58022
58023
58024
58025
58026
58027
58028
58029
58030
58031
58032
58033
58034
58035
58036
58037
58038
58039
58040
58041
58042
58043
58044
58045
58046
58047
58048
58049
58050
58051
58052
58053
58054
58055
58056
58057
58058
58059
58060
58061
58062
58063
58064
58065
58066
58067
58068
58069
58070
58071
58072
58073
58074
58075
58076
58077
58078
58079
58080
58081
58082
58083
58084
58085
58086
58087
58088
58089
58090
58091
58092
58093
58094
58095
58096
58097
58098
58099
58100
58101
58102
58103
58104
58105
58106
58107
58108
58109
58110
58111
58112
58113
58114
58115
58116
58117
58118
58119
58120
58121
58122
58123
58124
58125
58126
58127
58128
58129
58130
58131
58132
58133
58134
58135
58136
58137
58138
58139
58140
58141
58142
58143
58144
58145
58146
58147
58148
58149
58150
58151
58152
58153
58154
58155
58156
58157
58158
58159
58160
58161
58162
58163
58164
58165
58166
58167
58168
58169
58170
58171
58172
58173
58174
58175
58176
58177
58178
58179
58180
58181
58182
58183
58184
58185
58186
58187
58188
58189
58190
58191
58192
58193
58194
58195
58196
58197
58198
58199
58200
58201
58202
58203
58204
58205
58206
58207
58208
58209
58210
58211
58212
58213
58214
58215
58216
58217
58218
58219
58220
58221
58222
58223
58224
58225
58226
58227
58228
58229
58230
58231
58232
58233
58234
58235
58236
58237
58238
58239
58240
58241
58242
58243
58244
58245
58246
58247
58248
58249
58250
58251
58252
58253
58254
58255
58256
58257
58258
58259
58260
58261
58262
58263
58264
58265
58266
58267
58268
58269
58270
58271
58272
58273
58274
58275
58276
58277
58278
58279
58280
58281
58282
58283
58284
58285
58286
58287
58288
58289
58290
58291
58292
58293
58294
58295
58296
58297
58298
58299
58300
58301
58302
58303
58304
58305
58306
58307
58308
58309
58310
58311
58312
58313
58314
58315
58316
58317
58318
58319
58320
58321
58322
58323
58324
58325
58326
58327
58328
58329
58330
58331
58332
58333
58334
58335
58336
58337
58338
58339
58340
58341
58342
58343
58344
58345
58346
58347
58348
58349
58350
58351
58352
58353
58354
58355
58356
58357
58358
58359
58360
58361
58362
58363
58364
58365
58366
58367
58368
58369
58370
58371
58372
58373
58374
58375
58376
58377
58378
58379
58380
58381
58382
58383
58384
58385
58386
58387
58388
58389
58390
58391
58392
58393
58394
58395
58396
58397
58398
58399
58400
58401
58402
58403
58404
58405
58406
58407
58408
58409
58410
58411
58412
58413
58414
58415
58416
58417
58418
58419
58420
58421
58422
58423
58424
58425
58426
58427
58428
58429
58430
58431
58432
58433
58434
58435
58436
58437
58438
58439
58440
58441
58442
58443
58444
58445
58446
58447
58448
58449
58450
58451
58452
58453
58454
58455
58456
58457
58458
58459
58460
58461
58462
58463
58464
58465
58466
58467
58468
58469
58470
58471
58472
58473
58474
58475
58476
58477
58478
58479
58480
58481
58482
58483
58484
58485
58486
58487
58488
58489
58490
58491
58492
58493
58494
58495
58496
58497
58498
58499
58500
58501
58502
58503
58504
58505
58506
58507
58508
58509
58510
58511
58512
58513
58514
58515
58516
58517
58518
58519
58520
58521
58522
58523
58524
58525
58526
58527
58528
58529
58530
58531
58532
58533
58534
58535
58536
58537
58538
58539
58540
58541
58542
58543
58544
58545
58546
58547
58548
58549
58550
58551
58552
58553
58554
58555
58556
58557
58558
58559
58560
58561
58562
58563
58564
58565
58566
58567
58568
58569
58570
58571
58572
58573
58574
58575
58576
58577
58578
58579
58580
58581
58582
58583
58584
58585
58586
58587
58588
58589
58590
58591
58592
58593
58594
58595
58596
58597
58598
58599
58600
58601
58602
58603
58604
58605
58606
58607
58608
58609
58610
58611
58612
58613
58614
58615
58616
58617
58618
58619
58620
58621
58622
58623
58624
58625
58626
58627
58628
58629
58630
58631
58632
58633
58634
58635
58636
58637
58638
58639
58640
58641
58642
58643
58644
58645
58646
58647
58648
58649
58650
58651
58652
58653
58654
58655
58656
58657
58658
58659
58660
58661
58662
58663
58664
58665
58666
58667
58668
58669
58670
58671
58672
58673
58674
58675
58676
58677
58678
58679
58680
58681
58682
58683
58684
58685
58686
58687
58688
58689
58690
58691
58692
58693
58694
58695
58696
58697
58698
58699
58700
58701
58702
58703
58704
58705
58706
58707
58708
58709
58710
58711
58712
58713
58714
58715
58716
58717
58718
58719
58720
58721
58722
58723
58724
58725
58726
58727
58728
58729
58730
58731
58732
58733
58734
58735
58736
58737
58738
58739
58740
58741
58742
58743
58744
58745
58746
58747
58748
58749
58750
58751
58752
58753
58754
58755
58756
58757
58758
58759
58760
58761
58762
58763
58764
58765
58766
58767
58768
58769
58770
58771
58772
58773
58774
58775
58776
58777
58778
58779
58780
58781
58782
58783
58784
58785
58786
58787
58788
58789
58790
58791
58792
58793
58794
58795
58796
58797
58798
58799
58800
58801
58802
58803
58804
58805
58806
58807
58808
58809
58810
58811
58812
58813
58814
58815
58816
58817
58818
58819
58820
58821
58822
58823
58824
58825
58826
58827
58828
58829
58830
58831
58832
58833
58834
58835
58836
58837
58838
58839
58840
58841
58842
58843
58844
58845
58846
58847
58848
58849
58850
58851
58852
58853
58854
58855
58856
58857
58858
58859
58860
58861
58862
58863
58864
58865
58866
58867
58868
58869
58870
58871
58872
58873
58874
58875
58876
58877
58878
58879
58880
58881
58882
58883
58884
58885
58886
58887
58888
58889
58890
58891
58892
58893
58894
58895
58896
58897
58898
58899
58900
58901
58902
58903
58904
58905
58906
58907
58908
58909
58910
58911
58912
58913
58914
58915
58916
58917
58918
58919
58920
58921
58922
58923
58924
58925
58926
58927
58928
58929
58930
58931
58932
58933
58934
58935
58936
58937
58938
58939
58940
58941
58942
58943
58944
58945
58946
58947
58948
58949
58950
58951
58952
58953
58954
58955
58956
58957
58958
58959
58960
58961
58962
58963
58964
58965
58966
58967
58968
58969
58970
58971
58972
58973
58974
58975
58976
58977
58978
58979
58980
58981
58982
58983
58984
58985
58986
58987
58988
58989
58990
58991
58992
58993
58994
58995
58996
58997
58998
58999
59000
std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
cimglist_for(*this,l) {
CImg<ucharT> YCbCr((*this)[l]);
if (is_rgb) YCbCr.RGBtoYCbCr();
cimg::fwrite(YCbCr._data,(size_t)YCbCr._width*YCbCr._height,nfile);
cimg::fwrite(YCbCr.get_resize(YCbCr._width/2, YCbCr._height/2,1,3,3).data(0,0,0,1),
(size_t)YCbCr._width*YCbCr._height/2,nfile);
}
if (!file) cimg::fclose(nfile);
return *this;
}
//! Save list as a YUV image sequence file.
/**
\param filename Filename to write data to.
\param is_rgb Tells if the RGB to YUV conversion must be done for saving.
**/
const CImgList<T>& save_yuv(const char *const filename=0, const bool is_rgb=true) const {
return _save_yuv(0,filename,is_rgb);
}
//! Save image sequence into a YUV file.
/**
\param file File to write data to.
\param is_rgb Tells if the RGB to YUV conversion must be done for saving.
**/
const CImgList<T>& save_yuv(std::FILE *const file, const bool is_rgb=true) const {
return _save_yuv(file,0,is_rgb);
}
const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename, const bool is_compressed) const {
if (!file && !filename)
throw CImgArgumentException(_cimglist_instance
"save_cimg(): Specified filename is (null).",
cimglist_instance);
#ifndef cimg_use_zlib
if (is_compressed)
cimg::warn(_cimglist_instance
"save_cimg(): Unable to save compressed data in file '%s' unless zlib is enabled, "
"saving them uncompressed.",
cimglist_instance,
filename?filename:"(FILE*)");
#endif
std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
if (std::strstr(ptype,"unsigned")==ptype) std::fprintf(nfile,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype);
else std::fprintf(nfile,"%u %s %s_endian\n",_width,ptype,etype);
cimglist_for(*this,l) {
const CImg<T>& img = _data[l];
std::fprintf(nfile,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
if (img._data) {
CImg<T> tmp;
if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
const CImg<T>& ref = cimg::endianness()?tmp:img;
bool failed_to_compress = true;
if (is_compressed) {
#ifdef cimg_use_zlib
const ulongT siz = sizeof(T)*ref.size();
uLongf csiz = siz + siz/100 + 16;
Bytef *const cbuf = new Bytef[csiz];
if (compress(cbuf,&csiz,(Bytef*)ref._data,siz))
cimg::warn(_cimglist_instance
"save_cimg(): Failed to save compressed data for file '%s', saving them uncompressed.",
cimglist_instance,
filename?filename:"(FILE*)");
else {
std::fprintf(nfile," #%lu\n",csiz);
cimg::fwrite(cbuf,csiz,nfile);
delete[] cbuf;
failed_to_compress = false;
}
#endif
}
if (failed_to_compress) { // Write in a non-compressed way.
std::fputc('\n',nfile);
cimg::fwrite(ref._data,ref.size(),nfile);
}
} else std::fputc('\n',nfile);
}
if (!file) cimg::fclose(nfile);
return *this;
}
//! Save list into a .cimg file.
/**
\param filename Filename to write data to.
\param is_compressed Tells if data compression must be enabled.
**/
const CImgList<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
return _save_cimg(0,filename,is_compressed);
}
//! Save list into a .cimg file.
/**
\param file File to write data to.
\param is_compressed Tells if data compression must be enabled.
**/
const CImgList<T>& save_cimg(std::FILE *file, const bool is_compressed=false) const {
return _save_cimg(file,0,is_compressed);
}
const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename,
const unsigned int n0,
const unsigned int x0, const unsigned int y0,
const unsigned int z0, const unsigned int c0) const {
#define _cimg_save_cimg_case(Ts,Tss) \
if (!saved && !cimg::strcasecmp(Ts,str_pixeltype)) { \
for (unsigned int l = 0; l<lmax; ++l) { \
j = 0; while ((i=std::fgetc(nfile))!='\n') tmp[j++]=(char)i; tmp[j] = 0; \
W = H = D = C = 0; \
if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
throw CImgIOException(_cimglist_instance \
"save_cimg(): Invalid size (%u,%u,%u,%u) of image[%u], for file '%s'.", \
cimglist_instance, \
W,H,D,C,l,filename?filename:"(FILE*)"); \
if (W*H*D*C>0) { \
if (l<n0 || x0>=W || y0>=H || z0>=D || c0>=D) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
else { \
const CImg<T>& img = (*this)[l - n0]; \
const T *ptrs = img._data; \
const unsigned int \
x1 = x0 + img._width - 1, \
y1 = y0 + img._height - 1, \
z1 = z0 + img._depth - 1, \
c1 = c0 + img._spectrum - 1, \
nx1 = x1>=W?W - 1:x1, \
ny1 = y1>=H?H - 1:y1, \
nz1 = z1>=D?D - 1:z1, \
nc1 = c1>=C?C - 1:c1; \
CImg<Tss> raw(1 + nx1 - x0); \
const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \
if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \
for (unsigned int v = 1 + nc1 - c0; v; --v) { \
const unsigned int skipzb = z0*W*H*sizeof(Tss); \
if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \
for (unsigned int z = 1 + nz1 - z0; z; --z) { \
const unsigned int skipyb = y0*W*sizeof(Tss); \
if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \
for (unsigned int y = 1 + ny1 - y0; y; --y) { \
const unsigned int skipxb = x0*sizeof(Tss); \
if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \
raw.assign(ptrs, raw._width); \
ptrs+=img._width; \
if (endian) cimg::invert_endianness(raw._data,raw._width); \
cimg::fwrite(raw._data,raw._width,nfile); \
const unsigned int skipxe = (W - 1 - nx1)*sizeof(Tss); \
if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \
} \
const unsigned int skipye = (H - 1 - ny1)*W*sizeof(Tss); \
if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \
} \
const unsigned int skipze = (D - 1 - nz1)*W*H*sizeof(Tss); \
if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \
} \
const unsigned int skipve = (C - 1 - nc1)*W*H*D*sizeof(Tss); \
if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \
} \
} \
} \
saved = true; \
}
if (!file && !filename)
throw CImgArgumentException(_cimglist_instance
"save_cimg(): Specified filename is (null).",
cimglist_instance);
if (is_empty())
throw CImgInstanceException(_cimglist_instance
"save_cimg(): Empty instance, for file '%s'.",
cimglist_instance,
filename?filename:"(FILE*)");
std::FILE *const nfile = file?file:cimg::fopen(filename,"rb+");
bool saved = false, endian = cimg::endianness();
CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
*tmp = *str_pixeltype = *str_endian = 0;
unsigned int j, N, W, H, D, C;
int i, err;
j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype._data,str_endian._data);
if (err<2) {
if (!file) cimg::fclose(nfile);
throw CImgIOException(_cimglist_instance
"save_cimg(): CImg header not found in file '%s'.",
cimglist_instance,
filename?filename:"(FILE*)");
}
if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
const unsigned int lmax = std::min(N,n0 + _width);
_cimg_save_cimg_case("bool",bool);
_cimg_save_cimg_case("unsigned_char",unsigned char);
_cimg_save_cimg_case("uchar",unsigned char);
_cimg_save_cimg_case("char",char);
_cimg_save_cimg_case("unsigned_short",unsigned short);
_cimg_save_cimg_case("ushort",unsigned short);
_cimg_save_cimg_case("short",short);
_cimg_save_cimg_case("unsigned_int",unsigned int);
_cimg_save_cimg_case("uint",unsigned int);
_cimg_save_cimg_case("int",int);
_cimg_save_cimg_case("unsigned_int64",uint64T);
_cimg_save_cimg_case("uint64",uint64T);
_cimg_save_cimg_case("int64",int64T);
_cimg_save_cimg_case("float",float);
_cimg_save_cimg_case("double",double);
if (!saved) {
if (!file) cimg::fclose(nfile);
throw CImgIOException(_cimglist_instance
"save_cimg(): Unsupported data type '%s' for file '%s'.",
cimglist_instance,
filename?filename:"(FILE*)",str_pixeltype._data);
}
if (!file) cimg::fclose(nfile);
return *this;
}
//! Insert the image instance into into an existing .cimg file, at specified coordinates.
/**
\param filename Filename to write data to.
\param n0 Starting index of images to write.
\param x0 Starting X-coordinates of image regions to write.
\param y0 Starting Y-coordinates of image regions to write.
\param z0 Starting Z-coordinates of image regions to write.
\param c0 Starting C-coordinates of image regions to write.
**/
const CImgList<T>& save_cimg(const char *const filename,
const unsigned int n0,
const unsigned int x0, const unsigned int y0,
const unsigned int z0, const unsigned int c0) const {
return _save_cimg(0,filename,n0,x0,y0,z0,c0);
}
//! Insert the image instance into into an existing .cimg file, at specified coordinates.
/**
\param file File to write data to.
\param n0 Starting index of images to write.
\param x0 Starting X-coordinates of image regions to write.
\param y0 Starting Y-coordinates of image regions to write.
\param z0 Starting Z-coordinates of image regions to write.
\param c0 Starting C-coordinates of image regions to write.
**/
const CImgList<T>& save_cimg(std::FILE *const file,
const unsigned int n0,
const unsigned int x0, const unsigned int y0,
const unsigned int z0, const unsigned int c0) const {
return _save_cimg(file,0,n0,x0,y0,z0,c0);
}
static void _save_empty_cimg(std::FILE *const file, const char *const filename,
const unsigned int nb,
const unsigned int dx, const unsigned int dy,
const unsigned int dz, const unsigned int dc) {
std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
const ulongT siz = (ulongT)dx*dy*dz*dc*sizeof(T);
std::fprintf(nfile,"%u %s\n",nb,pixel_type());
for (unsigned int i=nb; i; --i) {
std::fprintf(nfile,"%u %u %u %u\n",dx,dy,dz,dc);
for (ulongT off = siz; off; --off) std::fputc(0,nfile);
}
if (!file) cimg::fclose(nfile);
}
//! Save empty (non-compressed) .cimg file with specified dimensions.
/**
\param filename Filename to write data to.
\param nb Number of images to write.
\param dx Width of images in the written file.
\param dy Height of images in the written file.
\param dz Depth of images in the written file.
\param dc Spectrum of images in the written file.
**/
static void save_empty_cimg(const char *const filename,
const unsigned int nb,
const unsigned int dx, const unsigned int dy=1,
const unsigned int dz=1, const unsigned int dc=1) {
return _save_empty_cimg(0,filename,nb,dx,dy,dz,dc);
}
//! Save empty .cimg file with specified dimensions.
/**
\param file File to write data to.
\param nb Number of images to write.
\param dx Width of images in the written file.
\param dy Height of images in the written file.
\param dz Depth of images in the written file.
\param dc Spectrum of images in the written file.
**/
static void save_empty_cimg(std::FILE *const file,
const unsigned int nb,
const unsigned int dx, const unsigned int dy=1,
const unsigned int dz=1, const unsigned int dc=1) {
return _save_empty_cimg(file,0,nb,dx,dy,dz,dc);
}
//! Save list as a TIFF file.
/**
\param filename Filename to write data to.
\param compression_type Compression mode used to write data.
**/
const CImgList<T>& save_tiff(const char *const filename, const unsigned int compression_type=0,
const float *const voxel_size=0, const char *const description=0,
const bool use_bigtiff=true) const {
if (!filename)
throw CImgArgumentException(_cimglist_instance
"save_tiff(): Specified filename is (null).",
cimglist_instance);
if (is_empty()) { cimg::fempty(0,filename); return *this; }
#ifndef cimg_use_tiff
if (_width==1) _data[0].save_tiff(filename,compression_type,voxel_size,description,use_bigtiff);
else cimglist_for(*this,l) {
CImg<charT> nfilename(1024);
cimg::number_filename(filename,l,6,nfilename);
_data[l].save_tiff(nfilename,compression_type,voxel_size,description,use_bigtiff);
}
#else
ulongT siz = 0;
cimglist_for(*this,l) siz+=_data[l].size();
const bool _use_bigtiff = use_bigtiff && sizeof(siz)>=8 && siz*sizeof(T)>=1UL<<31; // No bigtiff for small images.
TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4");
if (tif) {
for (unsigned int dir = 0, l = 0; l<_width; ++l) {
const CImg<T>& img = (*this)[l];
cimg_forZ(img,z) img._save_tiff(tif,dir++,z,compression_type,voxel_size,description);
}
TIFFClose(tif);
} else
throw CImgIOException(_cimglist_instance
"save_tiff(): Failed to open stream for file '%s'.",
cimglist_instance,
filename);
#endif
return *this;
}
//! Save list as a gzipped file, using external tool 'gzip'.
/**
\param filename Filename to write data to.
**/
const CImgList<T>& save_gzip_external(const char *const filename) const {
if (!filename)
throw CImgIOException(_cimglist_instance
"save_gzip_external(): Specified filename is (null).",
cimglist_instance);
CImg<charT> command(1024), filename_tmp(256), body(256);
const char
*ext = cimg::split_filename(filename,body),
*ext2 = cimg::split_filename(body,0);
std::FILE *file;
do {
if (!cimg::strcasecmp(ext,"gz")) {
if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
} else {
if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
}
if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
} while (file);
if (is_saveable(body)) {
save(filename_tmp);
cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"",
cimg::gzip_path(),
CImg<charT>::string(filename_tmp)._system_strescape().data(),
CImg<charT>::string(filename)._system_strescape().data());
cimg::system(command);
file = std_fopen(filename,"rb");
if (!file)
throw CImgIOException(_cimglist_instance
"save_gzip_external(): Failed to save file '%s' with external command 'gzip'.",
cimglist_instance,
filename);
else cimg::fclose(file);
std::remove(filename_tmp);
} else {
CImg<charT> nfilename(1024);
cimglist_for(*this,l) {
cimg::number_filename(body,l,6,nfilename);
if (*ext) cimg_sprintf(nfilename._data + std::strlen(nfilename),".%s",ext);
_data[l].save_gzip_external(nfilename);
}
}
return *this;
}
//! Save image sequence, using the OpenCV library.
/**
\param filename Filename to write data to.
\param fps Number of frames per second.
\param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs).
\param keep_open Tells if the video writer associated to the specified filename
must be kept open or not (to allow frames to be added in the same file afterwards).
**/
const CImgList<T>& save_video(const char *const filename, const unsigned int fps=25,
const char *codec=0, const bool keep_open=false) const {
#ifndef cimg_use_opencv
cimg::unused(codec,keep_open);
return save_ffmpeg_external(filename,fps);
#else
static CvVideoWriter *writers[32] = { 0 };
static CImgList<charT> filenames(32);
static CImg<intT> sizes(32,2,1,1,0);
static int last_used_index = -1;
// Detect if a video writer already exists for the specified filename.
cimg::mutex(9);
int index = -1;
if (filename) {
if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) {
index = last_used_index;
} else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) {
index = l; break;
}
} else index = last_used_index;
cimg::mutex(9,0);
// Find empty slot for capturing video stream.
if (index<0) {
if (!filename)
throw CImgArgumentException(_cimglist_instance
"save_video(): No already open video writer found. You must specify a "
"non-(null) filename argument for the first call.",
cimglist_instance);
else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); }
if (index<0)
throw CImgIOException(_cimglist_instance
"save_video(): File '%s', no video writer slots available. "
"You have to release some of your previously opened videos.",
cimglist_instance,filename);
if (is_empty())
throw CImgInstanceException(_cimglist_instance
"save_video(): Instance list is empty.",
cimglist_instance);
const unsigned int W = _data?_data[0]._width:0, H = _data?_data[0]._height:0;
if (!W || !H)
throw CImgInstanceException(_cimglist_instance
"save_video(): Frame [0] is an empty image.",
cimglist_instance);
#define _cimg_docase(x) ((x)>='a'&&(x)<='z'?(x) + 'A' - 'a':(x))
const char
*const _codec = codec && *codec?codec:"mp4v",
codec0 = _cimg_docase(_codec[0]),
codec1 = _codec[0]?_cimg_docase(_codec[1]):0,
codec2 = _codec[1]?_cimg_docase(_codec[2]):0,
codec3 = _codec[2]?_cimg_docase(_codec[3]):0;
cimg::mutex(9);
writers[index] = cvCreateVideoWriter(filename,CV_FOURCC(codec0,codec1,codec2,codec3),
fps,cvSize(W,H));
CImg<charT>::string(filename).move_to(filenames[index]);
sizes(index,0) = W; sizes(index,1) = H;
cimg::mutex(9,0);
if (!writers[index])
throw CImgIOException(_cimglist_instance
"save_video(): File '%s', unable to initialize video writer with codec '%c%c%c%c'.",
cimglist_instance,filename,
codec0,codec1,codec2,codec3);
}
if (!is_empty()) {
const unsigned int W = sizes(index,0), H = sizes(index,1);
cimg::mutex(9);
IplImage *ipl = cvCreateImage(cvSize(W,H),8,3);
cimglist_for(*this,l) {
CImg<T> &src = _data[l];
if (src.is_empty())
cimg::warn(_cimglist_instance
"save_video(): Skip empty frame %d for file '%s'.",
cimglist_instance,l,filename);
if (src._depth>1 || src._spectrum>3)
cimg::warn(_cimglist_instance
"save_video(): Frame %u has incompatible dimension (%u,%u,%u,%u). "
"Some image data may be ignored when writing frame into video file '%s'.",
cimglist_instance,l,src._width,src._height,src._depth,src._spectrum,filename);
if (src._width==W && src._height==H && src._spectrum==3) {
const T *ptr_r = src.data(0,0,0,0), *ptr_g = src.data(0,0,0,1), *ptr_b = src.data(0,0,0,2);
char *ptrd = ipl->imageData;
cimg_forXY(src,x,y) {
*(ptrd++) = (char)*(ptr_b++); *(ptrd++) = (char)*(ptr_g++); *(ptrd++) = (char)*(ptr_r++);
}
} else {
CImg<unsigned char> _src(src,false);
_src.channels(0,std::min(_src._spectrum - 1,2U)).resize(W,H);
_src.resize(W,H,1,3,_src._spectrum==1);
const unsigned char *ptr_r = _src.data(0,0,0,0), *ptr_g = _src.data(0,0,0,1), *ptr_b = _src.data(0,0,0,2);
char *ptrd = ipl->imageData;
cimg_forXY(_src,x,y) {
*(ptrd++) = (char)*(ptr_b++); *(ptrd++) = (char)*(ptr_g++); *(ptrd++) = (char)*(ptr_r++);
}
}
cvWriteFrame(writers[index],ipl);
}
cvReleaseImage(&ipl);
cimg::mutex(9,0);
}
cimg::mutex(9);
if (!keep_open) {
cvReleaseVideoWriter(&writers[index]);
writers[index] = 0;
filenames[index].assign();
sizes(index,0) = sizes(index,1) = 0;
last_used_index = -1;
} else last_used_index = index;
cimg::mutex(9,0);
return *this;
#endif
}
//! Save image sequence, using the external tool 'ffmpeg'.
/**
\param filename Filename to write data to.
\param fps Number of frames per second.
\param codec Type of compression.
\param bitrate Output bitrate
**/
const CImgList<T>& save_ffmpeg_external(const char *const filename, const unsigned int fps=25,
const char *const codec=0, const unsigned int bitrate=2048) const {
if (!filename)
throw CImgArgumentException(_cimglist_instance
"save_ffmpeg_external(): Specified filename is (null).",
cimglist_instance);
if (is_empty()) { cimg::fempty(0,filename); return *this; }
const char
*const ext = cimg::split_filename(filename),
*const _codec = codec?codec:!cimg::strcasecmp(ext,"flv")?"flv":"mpeg2video";
CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
CImgList<charT> filenames;
std::FILE *file = 0;
cimglist_for(*this,l) if (!_data[l].is_sameXYZ(_data[0]))
throw CImgInstanceException(_cimglist_instance
"save_ffmpeg_external(): Invalid instance dimensions for file '%s'.",
cimglist_instance,
filename);
do {
cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data);
if ((file=std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
} while (file);
cimglist_for(*this,l) {
cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,l + 1);
CImg<charT>::string(filename_tmp2).move_to(filenames);
if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save_pnm(filename_tmp2);
else _data[l].save_pnm(filename_tmp2);
}
#if cimg_OS!=2
cimg_snprintf(command,command._width,"%s -i \"%s_%%6d.ppm\" -vcodec %s -b %uk -r %u -y \"%s\" >/dev/null 2>&1",
cimg::ffmpeg_path(),
CImg<charT>::string(filename_tmp)._system_strescape().data(),
_codec,bitrate,fps,
CImg<charT>::string(filename)._system_strescape().data());
#else
cimg_snprintf(command,command._width,"\"%s -i \"%s_%%6d.ppm\" -vcodec %s -b %uk -r %u -y \"%s\"\" >NUL 2>&1",
cimg::ffmpeg_path(),
CImg<charT>::string(filename_tmp)._system_strescape().data(),
_codec,bitrate,fps,
CImg<charT>::string(filename)._system_strescape().data());
#endif
cimg::system(command);
file = std_fopen(filename,"rb");
if (!file)
throw CImgIOException(_cimglist_instance
"save_ffmpeg_external(): Failed to save file '%s' with external command 'ffmpeg'.",
cimglist_instance,
filename);
else cimg::fclose(file);
cimglist_for(*this,l) std::remove(filenames[l]);
return *this;
}
//! Serialize a CImgList<T> instance into a raw CImg<unsigned char> buffer.
/**
\param is_compressed tells if zlib compression must be used for serialization
(this requires 'cimg_use_zlib' been enabled).
**/
CImg<ucharT> get_serialize(const bool is_compressed=false) const {
#ifndef cimg_use_zlib
if (is_compressed)
cimg::warn(_cimglist_instance
"get_serialize(): Unable to compress data unless zlib is enabled, "
"storing them uncompressed.",
cimglist_instance);
#endif
CImgList<ucharT> stream;
CImg<charT> tmpstr(128);
const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
if (std::strstr(ptype,"unsigned")==ptype)
cimg_snprintf(tmpstr,tmpstr._width,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype);
else
cimg_snprintf(tmpstr,tmpstr._width,"%u %s %s_endian\n",_width,ptype,etype);
CImg<ucharT>::string(tmpstr,false).move_to(stream);
cimglist_for(*this,l) {
const CImg<T>& img = _data[l];
cimg_snprintf(tmpstr,tmpstr._width,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
CImg<ucharT>::string(tmpstr,false).move_to(stream);
if (img._data) {
CImg<T> tmp;
if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
const CImg<T>& ref = cimg::endianness()?tmp:img;
bool failed_to_compress = true;
if (is_compressed) {
#ifdef cimg_use_zlib
const ulongT siz = sizeof(T)*ref.size();
uLongf csiz = (ulongT)compressBound(siz);
Bytef *const cbuf = new Bytef[csiz];
if (compress(cbuf,&csiz,(Bytef*)ref._data,siz))
cimg::warn(_cimglist_instance
"get_serialize(): Failed to save compressed data, saving them uncompressed.",
cimglist_instance);
else {
cimg_snprintf(tmpstr,tmpstr._width," #%lu\n",csiz);
CImg<ucharT>::string(tmpstr,false).move_to(stream);
CImg<ucharT>(cbuf,csiz).move_to(stream);
delete[] cbuf;
failed_to_compress = false;
}
#endif
}
if (failed_to_compress) { // Write in a non-compressed way.
CImg<charT>::string("\n",false).move_to(stream);
stream.insert(1);
stream.back().assign((unsigned char*)ref._data,ref.size()*sizeof(T),1,1,1,true);
}
} else CImg<charT>::string("\n",false).move_to(stream);
}
cimglist_apply(stream,unroll)('y');
return stream>'y';
}
//! Unserialize a CImg<unsigned char> serialized buffer into a CImgList<T> list.
template<typename t>
static CImgList<T> get_unserialize(const CImg<t>& buffer) {
#ifdef cimg_use_zlib
#define _cimgz_unserialize_case(Tss) { \
Bytef *cbuf = (Bytef*)stream; \
if (sizeof(t)!=1 || cimg::type<t>::string()==cimg::type<bool>::string()) { \
cbuf = new Bytef[csiz]; Bytef *_cbuf = cbuf; \
for (ulongT i = 0; i<csiz; ++i) *(_cbuf++) = (Bytef)*(stream++); \
is_bytef = false; \
} else { stream+=csiz; is_bytef = true; } \
CImg<Tss> raw(W,H,D,C); \
uLongf destlen = raw.size()*sizeof(Tss); \
uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \
if (!is_bytef) delete[] cbuf; \
if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
raw.move_to(img); \
}
#else
#define _cimgz_unserialize_case(Tss) \
throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unable to unserialize compressed data " \
"unless zlib is enabled.", \
pixel_type());
#endif
#define _cimg_unserialize_case(Ts,Tss) \
if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
for (unsigned int l = 0; l<N; ++l) { \
j = 0; while ((i=(int)*stream)!='\n' && stream<estream && j<255) { ++stream; tmp[j++] = (char)i; } \
++stream; tmp[j] = 0; \
W = H = D = C = 0; csiz = 0; \
if ((err = cimg_sscanf(tmp,"%u %u %u %u #" cimg_fuint64,&W,&H,&D,&C,&csiz))<4) \
throw CImgArgumentException("CImgList<%s>::unserialize(): Invalid specified size (%u,%u,%u,%u) for " \
"image #%u in serialized buffer.", \
pixel_type(),W,H,D,C,l); \
if (W*H*D*C>0) { \
CImg<T> &img = res._data[l]; \
if (err==5) _cimgz_unserialize_case(Tss) \
else { \
if (sizeof(t)!=1) { \
CImg<ucharT> raw(W*sizeof(Tss),H,D,C); \
cimg_for(raw,p,unsigned char) *p = (unsigned char)*(stream++); \
img.assign((Tss*)raw._data,W,H,D,C); \
} else img.assign((Tss*)stream,W,H,D,C); \
if (endian!=cimg::endianness()) cimg::invert_endianness(img._data,img.size()); \
} \
} \
} \
loaded = true; \
}
if (buffer.is_empty())
throw CImgArgumentException("CImgList<%s>::get_unserialize(): Specified serialized buffer is (null).",
pixel_type());
CImgList<T> res;
const t *stream = buffer._data, *const estream = buffer._data + buffer.size();
bool loaded = false, endian = cimg::endianness(), is_bytef = false;
CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
*tmp = *str_pixeltype = *str_endian = 0;
unsigned int j, N = 0, W, H, D, C;
uint64T csiz;
int i, err;
cimg::unused(is_bytef);
do {
j = 0; while ((i=(int)*stream)!='\n' && stream<estream && j<255) { ++stream; tmp[j++] = (char)i; }
++stream; tmp[j] = 0;
} while (*tmp=='#' && stream<estream);
err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",
&N,str_pixeltype._data,str_endian._data);
if (err<2)
throw CImgArgumentException("CImgList<%s>::get_unserialize(): CImg header not found in serialized buffer.",
pixel_type());
if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
res.assign(N);
_cimg_unserialize_case("bool",bool);
_cimg_unserialize_case("unsigned_char",unsigned char);
_cimg_unserialize_case("uchar",unsigned char);
_cimg_unserialize_case("char",char);
_cimg_unserialize_case("unsigned_short",unsigned short);
_cimg_unserialize_case("ushort",unsigned short);
_cimg_unserialize_case("short",short);
_cimg_unserialize_case("unsigned_int",unsigned int);
_cimg_unserialize_case("uint",unsigned int);
_cimg_unserialize_case("int",int);
_cimg_unserialize_case("unsigned_int64",uint64T);
_cimg_unserialize_case("uint64",uint64T);
_cimg_unserialize_case("int64",int64T);
_cimg_unserialize_case("float",float);
_cimg_unserialize_case("double",double);
if (!loaded)
throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unsupported pixel type '%s' defined "
"in serialized buffer.",
pixel_type(),str_pixeltype._data);
return res;
}
//@}
//----------------------------------
//
//! \name Others
//@{
//----------------------------------
//! Crop font along the X-axis.
/**
**/
CImgList<T>& crop_font() {
return get_crop_font().move_to(*this);
}
//! Crop font along the X-axis \newinstance.
/**
**/
CImgList<T> get_crop_font() const {
CImgList<T> res;
cimglist_for(*this,l) {
const CImg<T>& letter = (*this)[l];
int xmin = letter.width(), xmax = 0;
cimg_forXY(letter,x,y) if (letter(x,y)) { if (x<xmin) xmin = x; if (x>xmax) xmax = x; }
if (xmin>xmax) CImg<T>(letter._width,letter._height,1,letter._spectrum,0).move_to(res);
else letter.get_crop(xmin,0,xmax,letter._height - 1).move_to(res);
}
res[' '].resize(res['f']._width,-100,-100,-100,0);
if (' ' + 256<res.size()) res[' ' + 256].resize(res['f']._width,-100,-100,-100,0);
return res;
}
//! Return a CImg pre-defined font with desired size.
/**
\param font_height Height of the desired font (exact match for 13,23,53,103).
\param is_variable_width Decide if the font has a variable (\c true) or fixed (\c false) width.
**/
static const CImgList<ucharT>& font(const unsigned int font_height, const bool is_variable_width=true) {
if (!font_height) return CImgList<ucharT>::const_empty();
cimg::mutex(11);
// Decompress nearest base font data if needed.
static const char *data_fonts[] = { cimg::data_font12x13, cimg::data_font20x23, cimg::data_font47x53, 0 };
static const unsigned int data_widths[] = { 12,20,47,90 }, data_heights[] = { 13,23,53,103 },
data_Ms[] = { 86,79,57,47 };
const unsigned int data_ind = font_height<=13U?0U:font_height<=23U?1U:font_height<=53U?2U:3U;
static CImg<ucharT> base_fonts[4];
CImg<ucharT> &base_font = base_fonts[data_ind];
if (!base_font) {
const unsigned int w = data_widths[data_ind], h = data_heights[data_ind], M = data_Ms[data_ind];
base_font.assign(256*w,h);
const char *data_font = data_fonts[data_ind];
unsigned char *ptrd = base_font;
const unsigned char *const ptrde = base_font.end();
// Special case needed for 90x103 to avoid MS compiler limit with big strings.
CImg<char> data90x103;
if (!data_font) {
((CImg<char>(cimg::_data_font90x103[0],
(unsigned int)std::strlen(cimg::_data_font90x103[0]),1,1,1,true),
CImg<char>(cimg::_data_font90x103[1],
(unsigned int)std::strlen(cimg::_data_font90x103[1]) + 1,1,1,1,true))>'x').
move_to(data90x103);
data_font = data90x103.data();
}
// Uncompress font data (decode RLE).
for (const char *ptrs = data_font; *ptrs; ++ptrs) {
const int c = (int)(*ptrs - M - 32), v = c>=0?255:0, n = c>=0?c:-c;
if (ptrd + n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; }
else { std::memset(ptrd,v,ptrde - ptrd); break; }
}
}
// Find optimal font cache location to return.
static CImgList<ucharT> fonts[16];
static bool is_variable_widths[16] = { 0 };
unsigned int ind = ~0U;
for (int i = 0; i<16; ++i)
if (!fonts[i] || (is_variable_widths[i]==is_variable_width && font_height==fonts[i][0]._height)) {
ind = (unsigned int)i; break; // Found empty slot or cached font.
}
if (ind==~0U) { // No empty slots nor existing font in cache.
fonts->assign();
std::memmove(fonts,fonts + 1,15*sizeof(CImgList<ucharT>));
std::memmove(is_variable_widths,is_variable_widths + 1,15*sizeof(bool));
std::memset(fonts + (ind=15),0,sizeof(CImgList<ucharT>)); // Free a slot in cache for new font.
}
CImgList<ucharT> &font = fonts[ind];
// Render requested font.
if (!font) {
const unsigned int padding_x = font_height<33U?1U:font_height<53U?2U:font_height<103U?3U:4U;
is_variable_widths[ind] = is_variable_width;
font = base_font.get_split('x',256);
if (font_height!=font[0]._height)
cimglist_for(font,l)
font[l].resize(std::max(1U,font[l]._width*font_height/font[l]._height),font_height,-100,-100,
font[0]._height>font_height?2:5);
if (is_variable_width) font.crop_font();
cimglist_for(font,l) font[l].resize(font[l]._width + padding_x,-100,1,1,0,0,0.5);
font.insert(256,0);
cimglist_for_in(font,0,255,l) font[l].assign(font[l + 256]._width,font[l + 256]._height,1,3,1);
}
cimg::mutex(11,0);
return font;
}
//! Compute a 1d Fast Fourier Transform, along specified axis.
/**
\param axis Axis along which the Fourier transform is computed.
\param invert Tells if the direct (\c false) or inverse transform (\c true) is computed.
**/
CImgList<T>& FFT(const char axis, const bool invert=false) {
if (is_empty()) return *this;
if (_width==1) insert(1);
if (_width>2)
cimg::warn(_cimglist_instance
"FFT(): Instance has more than 2 images",
cimglist_instance);
CImg<T>::FFT(_data[0],_data[1],axis,invert);
return *this;
}
//! Compute a 1-D Fast Fourier Transform, along specified axis \newinstance.
CImgList<Tfloat> get_FFT(const char axis, const bool invert=false) const {
return CImgList<Tfloat>(*this,false).FFT(axis,invert);
}
//! Compute a n-d Fast Fourier Transform.
/**
\param invert Tells if the direct (\c false) or inverse transform (\c true) is computed.
**/
CImgList<T>& FFT(const bool invert=false) {
if (is_empty()) return *this;
if (_width==1) insert(1);
if (_width>2)
cimg::warn(_cimglist_instance
"FFT(): Instance has more than 2 images",
cimglist_instance);
CImg<T>::FFT(_data[0],_data[1],invert);
return *this;
}
//! Compute a n-d Fast Fourier Transform \newinstance.
CImgList<Tfloat> get_FFT(const bool invert=false) const {
return CImgList<Tfloat>(*this,false).FFT(invert);
}
//! Reverse primitives orientations of a 3d object.
/**
**/
CImgList<T>& reverse_object3d() {
cimglist_for(*this,l) {
CImg<T>& p = _data[l];
switch (p.size()) {
case 2 : case 3: cimg::swap(p[0],p[1]); break;
case 6 : cimg::swap(p[0],p[1],p[2],p[4],p[3],p[5]); break;
case 9 : cimg::swap(p[0],p[1],p[3],p[5],p[4],p[6]); break;
case 4 : cimg::swap(p[0],p[1],p[2],p[3]); break;
case 12 : cimg::swap(p[0],p[1],p[2],p[3],p[4],p[6],p[5],p[7],p[8],p[10],p[9],p[11]); break;
}
}
return *this;
}
//! Reverse primitives orientations of a 3d object \newinstance.
CImgList<T> get_reverse_object3d() const {
return (+*this).reverse_object3d();
}
//@}
}; // struct CImgList<T> { ...
/*
#---------------------------------------------
#
# Completion of previously declared functions
#
#----------------------------------------------
*/
namespace cimg {
// Functions to return standard streams 'stdin', 'stdout' and 'stderr'.
// (throw a CImgIOException when macro 'cimg_use_r' is defined).
inline FILE* _stdin(const bool throw_exception) {
#ifndef cimg_use_r
cimg::unused(throw_exception);
return stdin;
#else
if (throw_exception) {
cimg::exception_mode(0);
throw CImgIOException("cimg::stdin(): Reference to 'stdin' stream not allowed in R mode "
"('cimg_use_r' is defined).");
}
return 0;
#endif
}
inline FILE* _stdout(const bool throw_exception) {
#ifndef cimg_use_r
cimg::unused(throw_exception);
return stdout;
#else
if (throw_exception) {
cimg::exception_mode(0);
throw CImgIOException("cimg::stdout(): Reference to 'stdout' stream not allowed in R mode "
"('cimg_use_r' is defined).");
}
return 0;
#endif
}
inline FILE* _stderr(const bool throw_exception) {
#ifndef cimg_use_r
cimg::unused(throw_exception);
return stderr;
#else
if (throw_exception) {
cimg::exception_mode(0);
throw CImgIOException("cimg::stderr(): Reference to 'stderr' stream not allowed in R mode "
"('cimg_use_r' is defined).");
}
return 0;
#endif
}
// Open a file (with wide character support on Windows).
inline std::FILE *win_fopen(const char *const path, const char *const mode) {
#if cimg_OS==2
// Convert 'path' to a wide-character string.
int err = MultiByteToWideChar(CP_UTF8,0,path,-1,0,0);
if (!err) return std_fopen(path,mode);
CImg<wchar_t> wpath(err);
err = MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err);
if (!err) return std_fopen(path,mode);
// Convert 'mode' to a wide-character string.
err = MultiByteToWideChar(CP_UTF8,0,mode,-1,0,0);
if (!err) return std_fopen(path,mode);
CImg<wchar_t> wmode(err);
err = MultiByteToWideChar(CP_UTF8,0,mode,-1,wmode,err);
if (!err) return std_fopen(path,mode);
return _wfopen(wpath,wmode);
#else
return std_fopen(path,mode);
#endif
}
//! Get/set path to store temporary files.
/**
\param user_path Specified path, or \c 0 to get the path currently used.
\param reinit_path Force path to be recalculated (may take some time).
\return Path where temporary files can be saved.
**/
inline const char* temporary_path(const char *const user_path, const bool reinit_path) {
#define _cimg_test_temporary_path(p) \
if (!path_found) { \
cimg_snprintf(s_path,s_path.width(),"%s",p); \
cimg_snprintf(tmp,tmp._width,"%s%c%s",s_path.data(),cimg_file_separator,filename_tmp._data); \
if ((file=std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } \