Proper check for rawzor libraries.
[rawtherapee-fixes.git] / rtgui / thumbnail_old.cc
blobd3735785da5b85111ecf79f8c9e36d6b99e4fbb9
1 /*
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>
20 #include <sstream>
21 #include <options.h>
22 #include <mytime.h>
23 #include <png.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <tiff.h>
27 #include <tiffio.h>
28 #include <glibmm.h>
29 #include <imagedata.h>
30 #include <glib/gstdio.h>
31 extern "C" {
32 #include <jpeglib.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);
36 extern GLOBAL(void)
37 my_jpeg_stdio_src (j_decompress_ptr cinfo, FILE * infile);
39 #include <guiutils.h>
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 ();
51 loadProcParams ();
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);
67 cfs->ppformat = 0;
68 cfs->preinterp = 0;
69 cfs->accessed = -1;
70 cfs->rank = 0;
71 cfs->stage = 0;
72 cfs->thumbnail = FT_Invalid;
73 cfs->recentlySaved = false;
74 cfs->thumbProcessed = options.liveThumbnails;
76 generateThumbnailImage (false);
79 void Thumbnail::generateThumbnailImage (bool useLock) {
81 mutex->lock ();
83 if (cfs->thumbProcessed)
84 generateProcessedThumbnailImage (useLock);
85 else
86 generatePreviewThumbnailImage (useLock);
88 mutex->unlock ();
91 void Thumbnail::generatePreviewThumbnailImage (bool useLock) {
93 // delete everything loaded into memory
94 delete tpp;
95 tpp = NULL;
96 delete [] lastImg;
97 lastImg = NULL;
98 tw = -1;
99 th = options.maxThumbnailHeight;
100 delete [] tImgData;
101 tImgData = NULL;
103 // generate thumbnail image
104 int lastdot = fname.find_last_of ('.');
105 if (lastdot==Glib::ustring::npos)
106 return;
108 cfs->thumbnail = (int) options.thumbnailFormat;
109 Glib::ustring ext = fname.substr (lastdot);
110 cfs->supported = false;
111 cfs->exifValid = 0;
112 cfs->timeValid = 0;
113 if (ext.lowercase()==".jpg") {
114 // try to load it as jpeg
115 int thumbW, thumbH;
116 int res = loadJPEGThumbnail (0, tImgData, tw, th);
117 if (!res) {
118 cfs->format = (int) FT_Jpeg;
119 infoFromImage (fname);
120 cfs->supported = true;
122 else {
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);
126 if (!res) {
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);
134 else {
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
144 int thumbW, thumbH;
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);
154 else {
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);
167 if (!res) {
168 cfs->format = (int) FT_Png;
169 cfs->supported = true;
172 else {
173 int thumbW, thumbH;
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);
183 else {
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) {
193 saveThumbnail ();
194 if (options.thumbCacheMemPolicy==MP_Memory) {
195 delete [] tImgData;
196 tImgData = NULL;
199 updateCache (true, true, useLock);
202 void Thumbnail::generateProcessedThumbnailImage (bool useLock) {
204 // delete everything loaded into memory
205 delete tpp;
206 tpp = NULL;
207 delete [] lastImg;
208 lastImg = NULL;
209 tw = -1;
210 th = options.maxThumbnailHeight;
211 delete [] tImgData;
212 tImgData = NULL;
214 // generate thumbnail image
215 int lastdot = fname.find_last_of ('.');
216 if (lastdot==Glib::ustring::npos)
217 return;
219 cfs->thumbnail = (int) options.thumbnailFormat;
220 Glib::ustring ext = fname.substr (lastdot);
221 cfs->supported = false;
222 cfs->exifValid = 0;
223 cfs->timeValid = 0;
225 delete tpp;
226 tpp = NULL;
227 if (ext.lowercase()==".jpg" || ext.lowercase()==".tif" || ext.lowercase()==".tiff" || ext.lowercase()==".png")
228 tpp = ThumbnailProcessingParameters::loadFromImage (fname, tw, th, 1);
230 if (tpp) {
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;
242 else {
243 rtengine::RawMetaDataLocation ri;
244 tpp = ThumbnailProcessingParameters::loadFromRaw (fname, ri, tw, th, 1);
245 if (tpp) {
246 cfs->format = (int) FT_Raw;
247 infoFromImage (fname, &ri);
250 if (tpp) {
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);
272 res->free ();*/
275 cfs->thumbProcessed = 1;
276 // save thumbnail image to cache
277 if (cfs->supported) {
278 saveThumbnail ();
279 if (options.thumbCacheMemPolicy==MP_Memory) {
280 delete tpp;
281 tpp = NULL;
282 // delete [] tImgData;
283 // tImgData = NULL;
287 updateCache (true, true, useLock);
288 needsReProcessing = true;
291 bool Thumbnail::isSupported () {
293 return cfs->supported;
296 const ProcParams& Thumbnail::getProcParams () {
298 if (pparamsValid)
299 return pparams;
300 else {
301 rtengine::procparams::ProcParams* pp = profileStore.getDefaultProcParams (getType()==FT_Raw);
302 if (pp)
303 return *pp;
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
316 if (!pparamsValid)
317 pparamsValid = !pparams.load (getCacheFileName ("profiles")+".pp2");
319 else {
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
323 if (!pparamsValid) {
324 int ppres = pparams.load (removeExtension(fname) + ".pp2");
325 pparamsValid = !ppres && pparams.version>=220;
330 void Thumbnail::clearProcParams () {
332 cfs->recentlySaved = false;
333 cfs->ppformat = 0;
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 () {
348 return pparamsValid;
351 void Thumbnail::setProcParams (const ProcParams& pp, bool updateCacheNow) {
353 if (pparams!=pp)
354 cfs->recentlySaved = false;
356 pparams = pp;
357 pparamsValid = true;
358 needsReProcessing = true;
359 if (updateCacheNow)
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 () {
376 enqueueNumber++;
379 void Thumbnail::imageRemovedFromQueue () {
381 enqueueNumber--;
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
394 mutex->lock ();
396 if (fixwh==1)
397 w = tw * h / th;
398 else if (fixwh==0)
399 h = th * w / tw;
401 mutex->unlock ();
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);
408 mutex->lock ();
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)) {
412 mutex->unlock ();
413 return lastImg;
415 else
416 delete [] lastImg;
418 if (!cfs->thumbProcessed) {
419 // if thumbnail not loaed yet, load it
420 if (!tImgData) {
421 loadThumbnail ();
422 if (!tImgData) {
423 if (fixwh==1)
424 w = tw * h / th;
425 else if (fixwh==0)
426 h = th * w / tw;
427 mutex->unlock ();
428 return NULL;
431 lastW = w;
432 lastH = h;
433 lastImg = new unsigned char [lastW*lastH*3];
434 thumbInterp (tImgData, tw, th, lastImg, w, h);
436 else {
437 if (!tpp)
438 loadThumbnail ();
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);
444 res->free ();
445 needsReProcessing = false;
448 // if we have to spare with memory, delete full res thumb image from memory
449 if (options.thumbCacheMemPolicy==MP_Memory) {
450 delete [] tImgData;
451 delete tpp;
452 tpp = NULL;
453 tImgData = NULL;
456 mutex->unlock ();
457 return lastImg;
460 Glib::ustring Thumbnail::getExifString () {
462 if (!cfs->exifValid)
463 return "";
465 std::ostringstream s;
466 s << "f/" << ImageData::apertureToString(cfs->fnumber) << " ";
467 s << ImageData::shutterToString(cfs->shutter) << "s ";
468 s << "ISO" << cfs->iso;
470 return s.str();
473 Glib::ustring Thumbnail::getDateTimeString () {
475 if (!cfs->timeValid)
476 return "";
477 std::string dateFormat = options.dateFormat;
478 std::ostringstream ostr;
479 bool spec = false;
480 for (int i=0; i<dateFormat.size(); i++)
481 if (spec && dateFormat[i]=='y') {
482 ostr << cfs->year;
483 spec = false;
485 else if (spec && dateFormat[i]=='m') {
486 ostr << (int)cfs->month;
487 spec = false;
489 else if (spec && dateFormat[i]=='d') {
490 ostr << (int)cfs->day;
491 spec = false;
493 else if (dateFormat[i]=='%')
494 spec = true;
495 else {
496 ostr << (char)dateFormat[i];
497 spec = false;
499 ostr << " " << (int)cfs->hour << ":" << (int)cfs->min << ":" << (int)cfs->sec;
500 return ostr.str ();
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);
511 if (!idata)
512 return;
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();
531 delete idata;
534 void Thumbnail::loadThumbnail (bool sizeOnly) {
536 mutex->lock ();
538 delete tImgData;
539 delete tpp;
540 tImgData = NULL;
541 tpp = NULL;
542 needsReProcessing = true;
544 if (cfs->thumbnail == FT_None)
545 return;
547 unsigned char* data = NULL;
548 Glib::ustring fname = getCacheFileName ("images");
549 int imgType = 0;
550 if (Glib::file_test (fname+".cust", Glib::FILE_TEST_EXISTS))
551 imgType = 1;
552 else if (Glib::file_test (fname+".jpg", Glib::FILE_TEST_EXISTS))
553 imgType = 2;
556 FILE* f = g_fopen (fname.c_str(), "rb");
557 if (!f || !imgType) {
558 if (f)
559 fclose(f);
560 mutex->unlock ();
561 generateThumbnailImage ();
562 if (cfs->supported)
563 loadThumbnail (sizeOnly);
564 else
565 return;
567 else if (imgType==1) {
568 fread (&tw, 1, sizeof (int), f);
569 fread (&th, 1, sizeof (int), f);
570 if (!sizeOnly) {
571 data = new unsigned char [tw*th*3];
572 fread (data, tw*th, 3, f);
574 fclose (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;
586 if (!sizeOnly) {
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);
598 fclose (f);
600 else {
601 fclose (f);
602 mutex->unlock ();
603 return;
606 if (!cfs->thumbProcessed) {
607 tImgData = data;
609 else if (!sizeOnly) {
610 tpp = new ThumbnailProcessingParameters ();
611 tpp->data = data;
612 tpp->width = tw;
613 tpp->height = th;
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");
621 if (!f)
622 tpp->aeHistogram = NULL;
623 else {
624 tpp->aeHistogram = new int[65536>>tpp->aeHistCompression];
625 fread (tpp->aeHistogram, 1, (65536>>tpp->aeHistCompression)*sizeof(int), f);
626 fclose (f);
628 tpp->embProfileLength = cfs->embProfileLength;
629 Glib::ustring pfName = getCacheFileName ("embprofiles")+".icc";
630 f = fopen (pfName.c_str(), "rb");
631 if (!f) {
632 tpp->embProfileData = NULL;
633 tpp->embProfile = NULL;
635 else {
636 tpp->embProfileData = new unsigned char[tpp->embProfileLength];
637 fread (tpp->embProfileData, 1, tpp->embProfileLength, f);
638 fclose (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));
650 tpp->init ();
652 mutex->unlock ();
655 void Thumbnail::saveThumbnail () {
657 if (!tImgData && !tpp)
658 return;
660 unsigned char* data = tImgData;
661 int w = tw, h = th;
663 if (cfs->thumbProcessed && tpp) {
664 data = tpp->data;
665 w = tpp->width;
666 h = tpp->height;
669 if (data) {
670 if (cfs->thumbnail == FT_Custom) {
671 Glib::ustring fname = getCacheFileName ("images")+".cust";
672 FILE* f = g_fopen (fname.c_str(), "wb");
673 if (!f)
674 return;
675 fwrite (&w, 1, sizeof (int), f);
676 fwrite (&h, 1, sizeof (int), f);
677 fwrite (data, w*h, 3, f);
678 fclose (f);
680 else if (cfs->thumbnail == FT_Jpeg) {
681 Glib::ustring fname = getCacheFileName ("images")+".jpg";
682 FILE* f = g_fopen (fname.c_str(), "wb");
683 if (!f)
684 return;
685 jpeg_compress_struct cinfo;
686 jpeg_error_mgr jerr;
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);
698 int rowlen = w*3;
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);
704 fclose (f);
705 return;
708 jpeg_finish_compress (&cinfo);
709 jpeg_destroy_compress (&cinfo);
710 fclose (f);
714 if (tpp) {
715 // save aehistogram
716 if (tpp->aeHistogram) {
717 FILE* f = fopen (getCacheFileName ("aehistograms").c_str(), "wb");
718 if (f) {
719 fwrite (tpp->aeHistogram, 1, (65536>>tpp->aeHistCompression)*sizeof(int), f);
720 fclose (f);
723 // save embedded profile
724 if (tpp->embProfileLength) {
725 Glib::ustring pfName = getCacheFileName ("embprofiles")+".icc";
726 FILE* f = fopen (pfName.c_str(), "wb");
727 if (f) {
728 fwrite (tpp->embProfileData, 1, tpp->embProfileLength, f);
729 fclose (f);
735 void Thumbnail::updateCache (bool savePparams, bool saveThumbImg, bool useLock) {
737 if (filePos>=0) {
739 if (pparamsValid)
740 cfs->ppformat = 1;
741 else
742 cfs->ppformat = 0;
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 () {
757 delete [] lastImg;
758 delete [] tImgData;
761 Glib::ustring Thumbnail::getCacheFileName (Glib::ustring subdir) {
763 Glib::ustring cfn = cachemgr->getBaseDir() + "/" + subdir + "/";
764 for (int i=0; i<32; i++)
765 cfn += cfs->MD5[i];
766 return cfn;
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;
774 data = NULL;
776 FILE* f = g_fopen (fname.c_str(), "rb");
778 if (!f)
779 return 2;
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;
789 cinfo.scale_num = 1;
791 if (fixwh==1) {
792 if (degree==0 || degree==180)
793 width = height * cinfo.image_width / cinfo.image_height;
794 else
795 width = height * cinfo.image_height / cinfo.image_width;
797 else {
798 if (degree==0 || degree==180)
799 height = width * cinfo.image_height / cinfo.image_width;
800 else
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;
810 else
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];
821 if (degree==0)
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);
831 delete [] fullData;
833 jpeg_finish_decompress(&cinfo);
834 jpeg_destroy_decompress(&cinfo);
835 fclose(f);
837 data = tdata;
838 return 0;
840 else {
841 fclose(f);
842 return 1;
847 int Thumbnail::loadPPMThumbnail (int offset, int iw, int ih, unsigned char* &data, int &width, int &height, int degree, int fixwh) {
849 data = NULL;
851 FILE* f = g_fopen (fname.c_str(), "rb");
852 if (!f)
853 return 2;
855 fseek (f, offset, SEEK_SET);
857 if (fixwh==1) {
858 if (degree==0 || degree==180)
859 width = height * iw / ih;
860 else
861 width = height * ih / iw;
863 else {
864 if (degree==0 || degree==180)
865 height = width * ih / iw;
866 else
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];
873 if (degree==0)
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);
883 delete [] fullData;
884 fclose(f);
885 data = tdata;
886 return 0;
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);
895 if (check != length)
896 png_error(png_ptr, "Read Error");
899 int Thumbnail::loadPNGThumbnail (unsigned char* &data, int &width, int &height, int fixwh) {
902 data = NULL;
904 FILE *file=g_fopen(fname.c_str(),"rb");
905 if (!file) return 2;
907 //reading PNG header
908 unsigned char header[8];
909 fread(header,1,8,file);
910 if (!png_check_sig(header,8)) {
911 fclose(file);
912 return 3;
914 //initializing main structures
915 png_structp png= png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0);
916 if (!png) {
917 fclose(file);
918 return 4;
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);
924 fclose(file);
925 return 5;
928 if (setjmp(png_jmpbuf(png))) {
929 png_destroy_read_struct(&png,&info,&end_info);
930 fclose(file);
931 return 3;
934 //set up png read
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) {
955 fclose (file);
956 return 6;
959 if (color_type & PNG_COLOR_MASK_ALPHA)
960 png_set_strip_alpha(png);
962 if (bit_depth==16)
963 png_set_strip_16(png);
965 //setting gamma
966 double gamma;
967 if (png_get_gAMA(png,info,&gamma))
968 png_set_gamma(png, 2.0, gamma);
969 else
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);
979 if (bit_depth==16)
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);
986 png_timep time;
987 cfs->timeValid = png_get_tIME (png, info, &time);
988 cfs->exifValid = 0;
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;
996 if (fixwh==1)
997 width = height * fwidth / fheight;
998 else
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);
1008 delete [] fullData;
1010 png_read_end(png,0);
1011 png_destroy_read_struct(&png,&info,&end_info);
1013 fclose(file);
1014 data = tdata;
1015 return 0;
1018 int Thumbnail::loadTIFFThumbnail (unsigned char* &data, int &width, int &height, int fixwh) {
1020 data = NULL;
1021 #ifdef WIN32
1022 wchar_t *wfilename = (wchar_t*)g_utf8_to_utf16 (fname.c_str(), -1, NULL, NULL, NULL);
1023 TIFF* in = TIFFOpenW (wfilename, "r");
1024 g_free (wfilename);
1025 #else
1026 TIFF* in = TIFFOpen(fname.c_str(), "r");
1027 #endif
1028 if (in == NULL)
1029 return 1;
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);
1036 uint16 photometric;
1037 if (!TIFFGetField(in, TIFFTAG_PHOTOMETRIC, &photometric) || photometric != PHOTOMETRIC_RGB || samplesperpixel < 3) {
1038 TIFFClose(in);
1039 return 2;
1041 uint16 config;
1042 TIFFGetField(in, TIFFTAG_PLANARCONFIG, &config);
1043 if (config != PLANARCONFIG_CONTIG) {
1044 TIFFClose(in);
1045 return 3;
1048 if (fixwh==1)
1049 width = height * fwidth / fheight;
1050 else
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)];
1057 int ix = 0;
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;
1063 TIFFClose(in);
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);
1070 else {
1071 TIFFClose(in);
1072 return 4;
1074 unsigned char* tdata = new unsigned char [3*width*height];
1075 thumbInterp (fullData, fwidth, fheight, tdata, width, height);
1076 delete [] fullData;
1077 data = tdata;
1078 return 0;