2 * This file is part of RawTherapee.
4 * Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
6 * RawTherapee is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * RawTherapee is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
19 #include <thumbnail.h>
29 #include <imagedata.h>
30 #include <glib/gstdio.h>
33 extern jmp_buf jpeg_jmp_buf
;
34 extern GLOBAL(struct jpeg_error_mgr
*)
35 my_jpeg_std_error (struct jpeg_error_mgr
* err
);
37 my_jpeg_stdio_src (j_decompress_ptr cinfo
, FILE * infile
);
40 #include <profilestore.h>
42 using namespace rtengine::procparams
;
43 using namespace rtengine
;
45 Thumbnail::Thumbnail (CacheManager
* cm
, const Glib::ustring
& fname
, CacheFileStruct
* cf
, int fpos
)
46 : cachemgr(cm
), cfs(cf
), tImgData(NULL
), tw(-1), th(-1), pparamsValid(false), filePos(fpos
), fname(fname
),
47 lastImg(NULL
), ref(1), enqueueNumber(0), tpp(NULL
), needsReProcessing(true) {
49 mutex
= new Glib::Mutex ();
52 if (options
.liveThumbnails
!=cfs
->thumbProcessed
) {
53 cfs
->thumbProcessed
= options
.liveThumbnails
;
54 generateThumbnailImage (false);
56 if (!tImgData
|| !tpp
)
57 loadThumbnail (options
.thumbCacheMemPolicy
==MP_Memory
);
60 Thumbnail::Thumbnail (CacheManager
* cm
, const Glib::ustring
& fname
, const std::string
& md5
, CacheFileStruct
* cf
, int fpos
)
61 : cachemgr(cm
), cfs(cf
), pparamsValid(false), filePos(fpos
), lastImg(NULL
), tImgData(NULL
), fname(fname
),
62 ref(1), enqueueNumber(0), tpp(NULL
), needsReProcessing(true) {
64 mutex
= new Glib::Mutex ();
66 memcpy (cfs
->MD5
, md5
.c_str(), 32);
72 cfs
->thumbnail
= FT_Invalid
;
73 cfs
->recentlySaved
= false;
74 cfs
->thumbProcessed
= options
.liveThumbnails
;
76 generateThumbnailImage (false);
79 void Thumbnail::generateThumbnailImage (bool useLock
) {
83 if (cfs
->thumbProcessed
)
84 generateProcessedThumbnailImage (useLock
);
86 generatePreviewThumbnailImage (useLock
);
91 void Thumbnail::generatePreviewThumbnailImage (bool useLock
) {
93 // delete everything loaded into memory
99 th
= options
.maxThumbnailHeight
;
103 // generate thumbnail image
104 int lastdot
= fname
.find_last_of ('.');
105 if (lastdot
==Glib::ustring::npos
)
108 cfs
->thumbnail
= (int) options
.thumbnailFormat
;
109 Glib::ustring ext
= fname
.substr (lastdot
);
110 cfs
->supported
= false;
113 if (ext
.lowercase()==".jpg") {
114 // try to load it as jpeg
116 int res
= loadJPEGThumbnail (0, tImgData
, tw
, th
);
118 cfs
->format
= (int) FT_Jpeg
;
119 infoFromImage (fname
);
120 cfs
->supported
= true;
123 // try to let it load by dcraw
124 rtengine::RawMetaDataLocation ri
;
125 res
= getRawFileBasicInfo (fname
, ri
, cfs
->rotate
, thumbW
, thumbH
, cfs
->thumbOffset
, cfs
->thumbImgType
);
127 cfs
->format
= (int) FT_Raw
;
128 infoFromImage (fname
, &ri
);
129 cfs
->supported
= true;
130 if (cfs
->thumbImgType
== 1)
131 loadJPEGThumbnail (cfs
->thumbOffset
, tImgData
, tw
, th
, cfs
->rotate
);
132 else if (cfs
->thumbImgType
== 2)
133 loadPPMThumbnail (cfs
->thumbOffset
, thumbW
, thumbH
, tImgData
, tw
, th
, cfs
->rotate
);
135 cfs
->thumbnail
= (int) FT_None
;
136 tw
= th
* thumbW
/ thumbH
;
142 else if (ext
.lowercase()==".tif" || ext
.lowercase()==".tiff") {
143 // try to detect it as raw file first
145 rtengine::RawMetaDataLocation ri
;
146 if (!getRawFileBasicInfo (fname
, ri
, cfs
->rotate
, thumbW
, thumbH
, cfs
->thumbOffset
, cfs
->thumbImgType
)) {
147 cfs
->format
= (int) FT_Raw
;
148 infoFromImage (fname
, &ri
);
149 cfs
->supported
= true;
150 if (cfs
->thumbImgType
== 1)
151 loadJPEGThumbnail (cfs
->thumbOffset
, tImgData
, tw
, th
, cfs
->rotate
);
152 else if (cfs
->thumbImgType
== 2)
153 loadPPMThumbnail (cfs
->thumbOffset
, thumbW
, thumbH
, tImgData
, tw
, th
, cfs
->rotate
);
155 cfs
->thumbnail
= (int) FT_None
;
156 tw
= th
* thumbW
/ thumbH
;
159 else if (!loadTIFFThumbnail (tImgData
, tw
, th
)) {
160 cfs
->format
= (int) FT_Tiff
;
161 infoFromImage (fname
);
162 cfs
->supported
= true;
165 else if (ext
.lowercase()==".png") {
166 int res
= loadPNGThumbnail (tImgData
, tw
, th
);
168 cfs
->format
= (int) FT_Png
;
169 cfs
->supported
= true;
174 rtengine::RawMetaDataLocation ri
;
175 if (!getRawFileBasicInfo (fname
, ri
, cfs
->rotate
, thumbW
, thumbH
, cfs
->thumbOffset
, cfs
->thumbImgType
)) {
176 cfs
->format
= (int) FT_Raw
;
177 infoFromImage (fname
, &ri
);
178 cfs
->supported
= true;
179 if (cfs
->thumbImgType
== 1)
180 loadJPEGThumbnail (cfs
->thumbOffset
, tImgData
, tw
, th
, cfs
->rotate
);
181 else if (cfs
->thumbImgType
== 2)
182 loadPPMThumbnail (cfs
->thumbOffset
, thumbW
, thumbH
, tImgData
, tw
, th
, cfs
->rotate
);
184 cfs
->thumbnail
= (int) FT_None
;
185 tw
= th
* thumbW
/ thumbH
;
190 cfs
->thumbProcessed
= 0;
191 // save thumbnail image to cache
192 if (cfs
->supported
) {
194 if (options
.thumbCacheMemPolicy
==MP_Memory
) {
199 updateCache (true, true, useLock
);
202 void Thumbnail::generateProcessedThumbnailImage (bool useLock
) {
204 // delete everything loaded into memory
210 th
= options
.maxThumbnailHeight
;
214 // generate thumbnail image
215 int lastdot
= fname
.find_last_of ('.');
216 if (lastdot
==Glib::ustring::npos
)
219 cfs
->thumbnail
= (int) options
.thumbnailFormat
;
220 Glib::ustring ext
= fname
.substr (lastdot
);
221 cfs
->supported
= false;
227 if (ext
.lowercase()==".jpg" || ext
.lowercase()==".tif" || ext
.lowercase()==".tiff" || ext
.lowercase()==".png")
228 tpp
= ThumbnailProcessingParameters::loadFromImage (fname
, tw
, th
, 1);
231 if (ext
.lowercase()==".jpg") {
232 cfs
->format
= (int) FT_Jpeg
;
233 infoFromImage (fname
);
235 else if (ext
.lowercase()==".tif" || ext
.lowercase()==".tiff") {
236 cfs
->format
= (int) FT_Tiff
;
237 infoFromImage (fname
);
239 else if (ext
.lowercase()==".png")
240 cfs
->format
= (int) FT_Png
;
243 rtengine::RawMetaDataLocation ri
;
244 tpp
= ThumbnailProcessingParameters::loadFromRaw (fname
, ri
, tw
, th
, 1);
246 cfs
->format
= (int) FT_Raw
;
247 infoFromImage (fname
, &ri
);
251 cfs
->supported
= true;
252 cfs
->camwbRed
= tpp
->camwbRed
;
253 cfs
->camwbGreen
= tpp
->camwbGreen
;
254 cfs
->camwbBlue
= tpp
->camwbBlue
;
255 cfs
->autowbTemp
= tpp
->autowbTemp
;
256 cfs
->autowbGreen
= tpp
->autowbGreen
;
257 cfs
->aeHistCompression
= tpp
->aeHistCompression
;
258 cfs
->embProfileLength
= tpp
->embProfileLength
;
259 cfs
->redMultiplier
= tpp
->redMultiplier
;
260 cfs
->greenMultiplier
= tpp
->greenMultiplier
;
261 cfs
->blueMultiplier
= tpp
->blueMultiplier
;
262 cfs
->scale
= tpp
->scale
;
263 cfs
->defGain
= tpp
->defGain
;
264 cfs
->scaleForSave
= tpp
->scaleForSave
;
265 cfs
->gammaCorrected
= tpp
->gammaCorrected
;
266 memcpy (cfs
->colorMatrix
, tpp
->colorMatrix
, sizeof (cfs
->colorMatrix
));
267 /* IImage8* res = tpp->processImage (getProcParams ());
268 tw = res->getWidth ();
269 th = res->getHeight ();
270 tImgData = new unsigned char [tw*th*3];
271 memcpy (tImgData, res->getData (), tw*th*3);
275 cfs
->thumbProcessed
= 1;
276 // save thumbnail image to cache
277 if (cfs
->supported
) {
279 if (options
.thumbCacheMemPolicy
==MP_Memory
) {
282 // delete [] tImgData;
287 updateCache (true, true, useLock
);
288 needsReProcessing
= true;
291 bool Thumbnail::isSupported () {
293 return cfs
->supported
;
296 const ProcParams
& Thumbnail::getProcParams () {
301 rtengine::procparams::ProcParams
* pp
= profileStore
.getDefaultProcParams (getType()==FT_Raw
);
305 return pparams
; // there is no valid pp to return, but we have to return something
308 void Thumbnail::loadProcParams () {
310 pparamsValid
= false;
311 if (options
.paramsLoadLocation
==PLL_Input
) {
312 // try to load it from pp2 file next to the image file
313 int ppres
= pparams
.load (removeExtension(fname
) + ".pp2");
314 pparamsValid
= !ppres
&& pparams
.version
>=220;
315 // if no success, load it from the cache
317 pparamsValid
= !pparams
.load (getCacheFileName ("profiles")+".pp2");
320 // try to load it from cache
321 pparamsValid
= !pparams
.load (getCacheFileName ("profiles")+".pp2");
322 // if no success, load it from pp2 file next to the image file
324 int ppres
= pparams
.load (removeExtension(fname
) + ".pp2");
325 pparamsValid
= !ppres
&& pparams
.version
>=220;
330 void Thumbnail::clearProcParams () {
332 cfs
->recentlySaved
= false;
334 pparamsValid
= false;
335 needsReProcessing
= true;
336 // remove pp2 file from cache
337 Glib::ustring fname_
= getCacheFileName ("profiles")+".pp2";
338 if (Glib::file_test (fname_
, Glib::FILE_TEST_EXISTS
))
339 ::g_remove (fname_
.c_str());
340 // remove pp2 file located next to the file
341 fname_
= removeExtension(fname
) + ".pp2";
342 if (Glib::file_test (fname_
, Glib::FILE_TEST_EXISTS
))
343 ::g_remove (fname_
.c_str());
346 bool Thumbnail::hasProcParams () {
351 void Thumbnail::setProcParams (const ProcParams
& pp
, bool updateCacheNow
) {
354 cfs
->recentlySaved
= false;
358 needsReProcessing
= true;
360 pparams
.save (getCacheFileName ("profiles")+".pp2");
363 bool Thumbnail::isRecentlySaved () {
365 return cfs
->recentlySaved
;
368 void Thumbnail::imageDeveloped () {
370 cfs
->recentlySaved
= true;
371 updateCache (false, false);
374 void Thumbnail::imageEnqueued () {
379 void Thumbnail::imageRemovedFromQueue () {
384 bool Thumbnail::isEnqueued () {
386 return enqueueNumber
> 0;
389 void Thumbnail::increaseRef () { ref
++; }
390 void Thumbnail::decreaseRef () { ref
--; if (!ref
) cachemgr
->closeThumbnail (this); }
392 void Thumbnail::getThumbnailSize (int &w
, int &h
, int fixwh
) { // fixwh = 0: fix w and calculate h, =1: fix h and calculate w
404 unsigned char* Thumbnail::getThumbnailImage (int &w
, int &h
, int fixwh
) { // fixwh = 0: fix w and calculate h, =1: fix h and calculate w
406 getThumbnailSize (w
, h
, fixwh
);
410 // if the result of last query fits the needs, we just have to return it
411 if (lastImg
&& lastW
==w
&& lastH
==h
&& !(cfs
->thumbProcessed
&& needsReProcessing
)) {
418 if (!cfs
->thumbProcessed
) {
419 // if thumbnail not loaed yet, load it
433 lastImg
= new unsigned char [lastW
*lastH
*3];
434 thumbInterp (tImgData
, tw
, th
, lastImg
, w
, h
);
439 IImage8
* res
= tpp
->processImage (getProcParams (), h
, TI_Bilinear
);
440 lastW
= w
= res
->getWidth ();
441 lastH
= h
= res
->getHeight ();
442 lastImg
= new unsigned char [lastW
*lastH
*3];
443 memcpy (lastImg
, res
->getData(), lastW
*lastH
*3);
445 needsReProcessing
= false;
448 // if we have to spare with memory, delete full res thumb image from memory
449 if (options
.thumbCacheMemPolicy
==MP_Memory
) {
460 Glib::ustring
Thumbnail::getExifString () {
465 std::ostringstream s
;
466 s
<< "f/" << ImageData::apertureToString(cfs
->fnumber
) << " ";
467 s
<< ImageData::shutterToString(cfs
->shutter
) << "s ";
468 s
<< "ISO" << cfs
->iso
;
473 Glib::ustring
Thumbnail::getDateTimeString () {
477 std::string dateFormat
= options
.dateFormat
;
478 std::ostringstream ostr
;
480 for (int i
=0; i
<dateFormat
.size(); i
++)
481 if (spec
&& dateFormat
[i
]=='y') {
485 else if (spec
&& dateFormat
[i
]=='m') {
486 ostr
<< (int)cfs
->month
;
489 else if (spec
&& dateFormat
[i
]=='d') {
490 ostr
<< (int)cfs
->day
;
493 else if (dateFormat
[i
]=='%')
496 ostr
<< (char)dateFormat
[i
];
499 ostr
<< " " << (int)cfs
->hour
<< ":" << (int)cfs
->min
<< ":" << (int)cfs
->sec
;
503 ThFileType
Thumbnail::getType () {
505 return (ThFileType
) cfs
->format
;
508 void Thumbnail::infoFromImage (const Glib::ustring
& fname
, rtengine::RawMetaDataLocation
* rml
) {
510 ImageMetaData
* idata
= ImageMetaData::fromFile (fname
, rml
);
513 cfs
->timeValid
= false;
514 cfs
->exifValid
= false;
515 if (idata
->hasExif()) {
516 cfs
->shutter
= idata
->getShutterSpeed ();
517 cfs
->fnumber
= idata
->getFNumber ();
518 cfs
->focalLen
= idata
->getFocalLen ();
519 cfs
->iso
= idata
->getISOSpeed ();
520 cfs
->year
= 1900 + idata
->getDateTime().tm_year
;
521 cfs
->month
= idata
->getDateTime().tm_mon
+ 1;
522 cfs
->day
= idata
->getDateTime().tm_mday
;
523 cfs
->hour
= idata
->getDateTime().tm_hour
;
524 cfs
->min
= idata
->getDateTime().tm_min
;
525 cfs
->sec
= idata
->getDateTime().tm_sec
;
526 cfs
->timeValid
= true;
527 cfs
->exifValid
= true;
528 cfs
->lens
= idata
->getLens();
529 cfs
->camera
= idata
->getMake() + " " + idata
->getModel();
534 void Thumbnail::loadThumbnail (bool sizeOnly
) {
542 needsReProcessing
= true;
544 if (cfs
->thumbnail
== FT_None
)
547 unsigned char* data
= NULL
;
548 Glib::ustring fname
= getCacheFileName ("images");
550 if (Glib::file_test (fname
+".cust", Glib::FILE_TEST_EXISTS
))
552 else if (Glib::file_test (fname
+".jpg", Glib::FILE_TEST_EXISTS
))
556 FILE* f
= g_fopen (fname
.c_str(), "rb");
557 if (!f
|| !imgType
) {
561 generateThumbnailImage ();
563 loadThumbnail (sizeOnly
);
567 else if (imgType
==1) {
568 fread (&tw
, 1, sizeof (int), f
);
569 fread (&th
, 1, sizeof (int), f
);
571 data
= new unsigned char [tw
*th
*3];
572 fread (data
, tw
*th
, 3, f
);
576 else if (imgType
==2) {
577 struct jpeg_decompress_struct cinfo
;
578 struct jpeg_error_mgr jerr
;
579 if (!setjmp(jpeg_jmp_buf
)) {
580 cinfo
.err
= my_jpeg_std_error (&jerr
);
581 jpeg_create_decompress (&cinfo
);
582 my_jpeg_stdio_src (&cinfo
,f
);
583 jpeg_read_header (&cinfo
, TRUE
);
584 tw
= cinfo
.image_width
;
585 th
= cinfo
.image_height
;
587 cinfo
.dct_method
= JDCT_FASTEST
;
588 cinfo
.do_fancy_upsampling
= 1;
589 jpeg_start_decompress(&cinfo
);
590 data
= new unsigned char [tw
*th
*3];
591 while (cinfo
.output_scanline
< cinfo
.output_height
) {
592 unsigned char* row
= data
+ cinfo
.output_scanline
*tw
*3;
593 jpeg_read_scanlines (&cinfo
, &row
, 1);
595 jpeg_finish_decompress (&cinfo
);
597 jpeg_destroy_decompress (&cinfo
);
606 if (!cfs
->thumbProcessed
) {
609 else if (!sizeOnly
) {
610 tpp
= new ThumbnailProcessingParameters ();
614 tpp
->camwbRed
= cfs
->camwbRed
;
615 tpp
->camwbGreen
= cfs
->camwbGreen
;
616 tpp
->camwbBlue
= cfs
->camwbBlue
;
617 tpp
->autowbTemp
= cfs
->autowbTemp
;
618 tpp
->autowbGreen
= cfs
->autowbGreen
;
619 tpp
->aeHistCompression
= cfs
->aeHistCompression
;
620 FILE* f
= fopen (getCacheFileName ("aehistograms").c_str(), "rb");
622 tpp
->aeHistogram
= NULL
;
624 tpp
->aeHistogram
= new int[65536>>tpp
->aeHistCompression
];
625 fread (tpp
->aeHistogram
, 1, (65536>>tpp
->aeHistCompression
)*sizeof(int), f
);
628 tpp
->embProfileLength
= cfs
->embProfileLength
;
629 Glib::ustring pfName
= getCacheFileName ("embprofiles")+".icc";
630 f
= fopen (pfName
.c_str(), "rb");
632 tpp
->embProfileData
= NULL
;
633 tpp
->embProfile
= NULL
;
636 tpp
->embProfileData
= new unsigned char[tpp
->embProfileLength
];
637 fread (tpp
->embProfileData
, 1, tpp
->embProfileLength
, f
);
639 tpp
->embProfile
= cmsOpenProfileFromMem (tpp
->embProfileData
, tpp
->embProfileLength
);
641 tpp
->redMultiplier
= cfs
->redMultiplier
;
642 tpp
->greenMultiplier
= cfs
->greenMultiplier
;
643 tpp
->blueMultiplier
= cfs
->blueMultiplier
;
644 tpp
->scale
= cfs
->scale
;
645 tpp
->defGain
= cfs
->defGain
;
646 tpp
->scaleForSave
= cfs
->scaleForSave
;
647 tpp
->gammaCorrected
= cfs
->gammaCorrected
;
648 tpp
->isRaw
= (cfs
->format
== (int) FT_Raw
);
649 memcpy (tpp
->colorMatrix
, cfs
->colorMatrix
, sizeof (tpp
->colorMatrix
));
655 void Thumbnail::saveThumbnail () {
657 if (!tImgData
&& !tpp
)
660 unsigned char* data
= tImgData
;
663 if (cfs
->thumbProcessed
&& tpp
) {
670 if (cfs
->thumbnail
== FT_Custom
) {
671 Glib::ustring fname
= getCacheFileName ("images")+".cust";
672 FILE* f
= g_fopen (fname
.c_str(), "wb");
675 fwrite (&w
, 1, sizeof (int), f
);
676 fwrite (&h
, 1, sizeof (int), f
);
677 fwrite (data
, w
*h
, 3, f
);
680 else if (cfs
->thumbnail
== FT_Jpeg
) {
681 Glib::ustring fname
= getCacheFileName ("images")+".jpg";
682 FILE* f
= g_fopen (fname
.c_str(), "wb");
685 jpeg_compress_struct cinfo
;
687 cinfo
.err
= jpeg_std_error (&jerr
);
688 jpeg_create_compress (&cinfo
);
689 jpeg_stdio_dest (&cinfo
, f
);
690 cinfo
.image_width
= w
;
691 cinfo
.image_height
= h
;
692 cinfo
.in_color_space
= JCS_RGB
;
693 cinfo
.input_components
= 3;
694 jpeg_set_defaults (&cinfo
);
695 cinfo
.write_JFIF_header
= FALSE
;
696 jpeg_set_quality (&cinfo
, 85, true);
697 jpeg_start_compress(&cinfo
, TRUE
);
699 while (cinfo
.next_scanline
< cinfo
.image_height
) {
700 unsigned char* row
= data
+ cinfo
.next_scanline
*w
*3;
701 if (jpeg_write_scanlines (&cinfo
, &row
, 1) < 1) {
702 jpeg_finish_compress (&cinfo
);
703 jpeg_destroy_compress (&cinfo
);
708 jpeg_finish_compress (&cinfo
);
709 jpeg_destroy_compress (&cinfo
);
716 if (tpp
->aeHistogram
) {
717 FILE* f
= fopen (getCacheFileName ("aehistograms").c_str(), "wb");
719 fwrite (tpp
->aeHistogram
, 1, (65536>>tpp
->aeHistCompression
)*sizeof(int), f
);
723 // save embedded profile
724 if (tpp
->embProfileLength
) {
725 Glib::ustring pfName
= getCacheFileName ("embprofiles")+".icc";
726 FILE* f
= fopen (pfName
.c_str(), "wb");
728 fwrite (tpp
->embProfileData
, 1, tpp
->embProfileLength
, f
);
735 void Thumbnail::updateCache (bool savePparams
, bool saveThumbImg
, bool useLock
) {
744 if (pparamsValid
&& savePparams
) {
745 if (options
.saveParamsCache
)
746 pparams
.save (getCacheFileName ("profiles"));
747 if (options
.saveParamsFile
)
748 pparams
.save (removeExtension(fname
) + ".pp2");
751 cachemgr
->saveThumbnail (this, useLock
);
755 Thumbnail::~Thumbnail () {
761 Glib::ustring
Thumbnail::getCacheFileName (Glib::ustring subdir
) {
763 Glib::ustring cfn
= cachemgr
->getBaseDir() + "/" + subdir
+ "/";
764 for (int i
=0; i
<32; i
++)
769 int Thumbnail::loadJPEGThumbnail (int offset
, unsigned char* &data
, int &width
, int &height
, int degree
, int fixwh
) {
771 struct jpeg_decompress_struct cinfo
;
772 struct jpeg_error_mgr jerr
;
776 FILE* f
= g_fopen (fname
.c_str(), "rb");
780 fseek (f
, offset
, SEEK_SET
);
782 if (!setjmp(jpeg_jmp_buf
)) {
784 cinfo
.err
= my_jpeg_std_error (&jerr
);
785 jpeg_create_decompress (&cinfo
);
786 my_jpeg_stdio_src (&cinfo
,f
);
787 jpeg_read_header (&cinfo
, TRUE
);
788 cinfo
.dct_method
= JDCT_FASTEST
;
792 if (degree
==0 || degree
==180)
793 width
= height
* cinfo
.image_width
/ cinfo
.image_height
;
795 width
= height
* cinfo
.image_height
/ cinfo
.image_width
;
798 if (degree
==0 || degree
==180)
799 height
= width
* cinfo
.image_height
/ cinfo
.image_width
;
801 height
= width
* cinfo
.image_width
/ cinfo
.image_height
;
804 if (cinfo
.image_width
> 8*width
&& cinfo
.image_height
> 8*height
)
805 cinfo
.scale_denom
= 8;
806 else if (cinfo
.image_width
> 4*width
&& cinfo
.image_height
> 4*height
)
807 cinfo
.scale_denom
= 4;
808 else if (cinfo
.image_width
> 2*width
&& cinfo
.image_height
> 2*height
)
809 cinfo
.scale_denom
= 2;
811 cinfo
.scale_denom
= 1;
813 cinfo
.do_fancy_upsampling
= 1;
814 jpeg_start_decompress(&cinfo
);
815 unsigned char* fullData
= new unsigned char [3*cinfo
.output_height
*cinfo
.output_width
];
816 unsigned char* row
= fullData
;
817 for (int i
=0; i
<cinfo
.output_height
; i
++, row
+= 3*cinfo
.output_width
)
818 jpeg_read_scanlines (&cinfo
, &row
, 1);
820 unsigned char* tdata
= new unsigned char [3*width
*height
];
822 thumbInterp (fullData
, cinfo
.output_width
, cinfo
.output_height
, tdata
, width
, height
);
823 else if (degree
==90 || degree
==270) {
824 thumbInterp (fullData
, cinfo
.output_width
, cinfo
.output_height
, tdata
, height
, width
);
825 rotate (tdata
, height
, width
, degree
);
827 else if (degree
==180) {
828 thumbInterp (fullData
, cinfo
.output_width
, cinfo
.output_height
, tdata
, width
, height
);
829 rotate (tdata
, width
, height
, degree
);
833 jpeg_finish_decompress(&cinfo
);
834 jpeg_destroy_decompress(&cinfo
);
847 int Thumbnail::loadPPMThumbnail (int offset
, int iw
, int ih
, unsigned char* &data
, int &width
, int &height
, int degree
, int fixwh
) {
851 FILE* f
= g_fopen (fname
.c_str(), "rb");
855 fseek (f
, offset
, SEEK_SET
);
858 if (degree
==0 || degree
==180)
859 width
= height
* iw
/ ih
;
861 width
= height
* ih
/ iw
;
864 if (degree
==0 || degree
==180)
865 height
= width
* ih
/ iw
;
867 height
= width
* iw
/ ih
;
870 unsigned char* fullData
= new unsigned char [3*iw
*ih
];
871 fread (fullData
, 1, iw
*ih
*3, f
);
872 unsigned char* tdata
= new unsigned char [3*width
*height
];
874 thumbInterp (fullData
, iw
, ih
, tdata
, width
, height
);
875 else if (degree
==90 || degree
==270) {
876 thumbInterp (fullData
, iw
, ih
, tdata
, height
, width
);
877 rotate (tdata
, height
, width
, degree
);
879 else if (degree
==180) {
880 thumbInterp (fullData
, iw
, iw
, tdata
, width
, height
);
881 rotate (tdata
, width
, height
, degree
);
890 void png_read_data(png_structp png_ptr
, png_bytep data
, png_size_t length
) {
891 /* fread() returns 0 on error, so it is OK to store this in a png_size_t
892 * instead of an int, which is what fread() actually returns.
894 png_size_t check
= (png_size_t
)fread(data
, (png_size_t
)1, length
, (FILE *)png_ptr
->io_ptr
);
896 png_error(png_ptr
, "Read Error");
899 int Thumbnail::loadPNGThumbnail (unsigned char* &data
, int &width
, int &height
, int fixwh
) {
904 FILE *file
=g_fopen(fname
.c_str(),"rb");
908 unsigned char header
[8];
909 fread(header
,1,8,file
);
910 if (!png_check_sig(header
,8)) {
914 //initializing main structures
915 png_structp png
= png_create_read_struct(PNG_LIBPNG_VER_STRING
,0,0,0);
920 png_infop info
= png_create_info_struct(png
);
921 png_infop end_info
= png_create_info_struct(png
);
922 if (!end_info
|| !info
) {
923 png_destroy_read_struct(&png
,&info
,&end_info
);
928 if (setjmp(png_jmpbuf(png
))) {
929 png_destroy_read_struct(&png
,&info
,&end_info
);
935 png_set_read_fn(png
, file
, png_read_data
);
936 png_set_sig_bytes(png
,8);
938 png_read_info(png
,info
);
940 //retrieving image information
941 unsigned long fwidth
, fheight
;
942 int bit_depth
,color_type
,interlace_type
,compression_type
,filter_method
;
943 png_get_IHDR(png
,info
,&fwidth
,&fheight
,&bit_depth
,&color_type
,&interlace_type
,
944 &compression_type
, &filter_method
);
946 //converting to 32bpp format
947 if (color_type
==PNG_COLOR_TYPE_PALETTE
) png_set_palette_to_rgb(png
);
949 if (color_type
==PNG_COLOR_TYPE_GRAY
|| color_type
==PNG_COLOR_TYPE_GRAY_ALPHA
)
950 png_set_gray_to_rgb(png
);
952 if (png_get_valid(png
,info
,PNG_INFO_tRNS
)) png_set_tRNS_to_alpha(png
);
954 if (interlace_type
!=PNG_INTERLACE_NONE
) {
959 if (color_type
& PNG_COLOR_MASK_ALPHA
)
960 png_set_strip_alpha(png
);
963 png_set_strip_16(png
);
967 if (png_get_gAMA(png
,info
,&gamma
))
968 png_set_gamma(png
, 2.0, gamma
);
970 png_set_gamma(png
, 1.0, 1.0);
972 //updating png info struct
973 png_read_update_info(png
,info
);
974 png_get_IHDR(png
,info
,&fwidth
,&fheight
,&bit_depth
,&color_type
,&interlace_type
,
975 &compression_type
, &filter_method
);
977 if (color_type
& PNG_COLOR_MASK_ALPHA
)
978 png_set_strip_alpha(png
);
980 png_set_strip_16(png
);
982 png_read_update_info(png
,info
);
983 png_get_IHDR(png
,info
,&fwidth
,&fheight
,&bit_depth
,&color_type
,&interlace_type
,
984 &compression_type
, &filter_method
);
987 cfs
->timeValid
= png_get_tIME (png
, info
, &time
);
989 cfs
->year
= time
->year
;
990 cfs
->month
= time
->month
;
991 cfs
->day
= time
->day
;
992 cfs
->hour
= time
->hour
;
993 cfs
->min
= time
->minute
;
994 cfs
->sec
= time
->second
;
997 width
= height
* fwidth
/ fheight
;
999 height
= width
* fheight
/ fwidth
;
1001 unsigned char* fullData
= new unsigned char [3*fheight
*fwidth
];
1002 unsigned char* row
= fullData
;
1003 for (int i
=0; i
<fheight
; i
++, row
+= 3*fwidth
)
1004 png_read_row (png
, (png_byte
*)row
, NULL
);
1006 unsigned char* tdata
= new unsigned char [3*width
*height
];
1007 thumbInterp (fullData
, fwidth
, fheight
, tdata
, width
, height
);
1010 png_read_end(png
,0);
1011 png_destroy_read_struct(&png
,&info
,&end_info
);
1018 int Thumbnail::loadTIFFThumbnail (unsigned char* &data
, int &width
, int &height
, int fixwh
) {
1022 wchar_t *wfilename
= (wchar_t*)g_utf8_to_utf16 (fname
.c_str(), -1, NULL
, NULL
, NULL
);
1023 TIFF
* in
= TIFFOpenW (wfilename
, "r");
1026 TIFF
* in
= TIFFOpen(fname
.c_str(), "r");
1030 int fwidth
, fheight
;
1031 TIFFGetField(in
, TIFFTAG_IMAGEWIDTH
, &fwidth
);
1032 TIFFGetField(in
, TIFFTAG_IMAGELENGTH
, &fheight
);
1033 uint16 bitspersample
, samplesperpixel
;
1034 TIFFGetField(in
, TIFFTAG_BITSPERSAMPLE
, &bitspersample
);
1035 TIFFGetField(in
, TIFFTAG_SAMPLESPERPIXEL
, &samplesperpixel
);
1037 if (!TIFFGetField(in
, TIFFTAG_PHOTOMETRIC
, &photometric
) || photometric
!= PHOTOMETRIC_RGB
|| samplesperpixel
< 3) {
1042 TIFFGetField(in
, TIFFTAG_PLANARCONFIG
, &config
);
1043 if (config
!= PLANARCONFIG_CONTIG
) {
1049 width
= height
* fwidth
/ fheight
;
1051 height
= width
* fheight
/ fwidth
;
1053 unsigned char* fullData
= new unsigned char [3*fheight
*fwidth
];
1054 unsigned char* row
= fullData
;
1055 if (bitspersample
== 16) {
1056 unsigned short* linebuffer
= new unsigned short[TIFFScanlineSize(in
)];
1058 for (int i
=0; i
<fheight
; i
++) {
1059 TIFFReadScanline(in
, linebuffer
, i
, 0);
1060 for (int j
=0; j
<3*fwidth
; j
++)
1061 fullData
[ix
++] = linebuffer
[j
] >> 8;
1064 delete [] linebuffer
;
1066 else if (bitspersample
== 8) {
1067 for (int i
=0; i
<fheight
; i
++, row
+= 3*fwidth
)
1068 TIFFReadScanline(in
, row
, i
, 0);
1074 unsigned char* tdata
= new unsigned char [3*width
*height
];
1075 thumbInterp (fullData
, fwidth
, fheight
, tdata
, width
, height
);