void CatalogImages::Impl::ProcessFiles(int img_size, int jpeg_compression_level) { const size_t count= files_.size(); counter_ = 0; for (size_t i= 0; i < count; ++i) { if (parentWnd_) ::PostMessage(parentWnd_, MESSAGE, i, IMG_PROCESSING); if (break_) return; const Path& path= files_[i].first; PhotoFactory::CreateFn fn= 0; int id= 0; if (!GetPhotoFactory().MatchPhotoType(path.GetExtension(), fn, id)) { ASSERT(false); continue; } SmartPhotoPtr photo(fn()); try { CatalogImgRecord img; // ExifBlock exif; // exif.clear(); bool has_exif= photo->Scan(path.c_str(), img.exif_, false, nullptr); // scan image to find EXIF data if (!has_exif && readOnlyPhotosWithEXIF_) continue; if (!has_exif || photo->GetDateTime().is_not_a_date_time()) ReadFileTimeStamp(*photo, path); uint64 fileLength= 0; // write to db img.type_ = id; // get file length and last write time { WIN32_FIND_DATA findFileData; HANDLE find= ::FindFirstFile(path.c_str(), &findFileData); if (find != INVALID_HANDLE_VALUE) { VERIFY(::FindClose(find)); fileLength = uint64(findFileData.nFileSizeLow) + (uint64(findFileData.nFileSizeHigh) << 32); img.time_stamp_ = findFileData.ftLastWriteTime; img.access_time_ = findFileData.ftLastAccessTime; img.creation_time_ = findFileData.ftCreationTime; img.file_attribs_ = findFileData.dwFileAttributes; } } // uint64 fileLength= path.GetFileLength(); //photo->dir_visited_ = static_cast<uint32>(dir_visited); photo->SetFileSize(fileLength); photo->SetPhotoName(path.GetFileName()); photo->SetPhysicalPath(path); photo->exif_data_present_ = has_exif; // SetAutoRotationFlag(photo.get()); img.path_ = String2WStr(path); img.make_ = String2WStr(photo->GetMake()); img.model_ = String2WStr(photo->GetModel()); img.orientation_ = photo->OrientationField(); img.file_size_ = fileLength; // find.GetLastWriteTime(&img.time_stamp_); //photo->index_.Serialize(img.buf_index_); // verify this: =========== // img.jpeg_offset_ = photo->jpeg_offset_; // HACK: this is orientation extracted from CRW file; when reading info from // cache CRW is not scanned, so orientation cannot be set img.exif_orientation_ = has_exif ? 0u : photo->OrientationField(); // ========================= // EXIF block img.has_exif_ = has_exif; // img.exif_ifd_offset_ = static_cast<int32>(exif.ifd0Start); // if (exif.is_raw) // img.exif_type_ = exif.bigEndianByteOrder ? // CatalogImgRecord::RAW_MM_EXIF_BLOCK : CatalogImgRecord::RAW_II_EXIF_BLOCK; // else // img.exif_type_ = CatalogImgRecord::JPEG_EXIF_BLOCK; // img.exif_.swap(exif.exif_buffer); // prepare preview Dib bmp; CSize thumbnail_size(img_size, img_size); CImageDecoderPtr decoder= photo->GetDecoder(); bool ycbcr_image= true; ImageStat stat= decoder->DecodeImgToYCbCr(bmp, thumbnail_size, true); if (stat != IS_OK) { ycbcr_image = false; if (stat == IS_OPERATION_NOT_SUPPORTED) stat = decoder->DecodeImg(bmp, thumbnail_size, true); } if (stat != IS_OK || !bmp.IsValid()) { failed_.push_back(path); continue; } // if EXIF indicates rotation and photo is already physically rotated AND exif reset EXIF orientation // flag back to normal to avoid problems later on... PhotoInfo::ImgOrientation orient= photo->GetOrientation(); if ((orient == PhotoInfo::ORIENT_90CCW || orient == PhotoInfo::ORIENT_90CW) && photo->GetWidth() > photo->GetHeight() && bmp.GetWidth() < bmp.GetHeight()) { //photo->orientation_ = 0; std::swap(img.img_width_, img.img_height_); uint32 w= photo->GetWidth(); uint32 h= photo->GetHeight(); photo->SetSize(h, w); } else if ((orient == PhotoInfo::ORIENT_NORMAL || orient == PhotoInfo::ORIENT_NO_INFO || orient == PhotoInfo::ORIENT_UNKNOWN) && photo->GetWidth() > photo->GetHeight() && bmp.GetWidth() < bmp.GetHeight()) { // orientation is 'normal', but reported size and size of decoded image do not agree uint32 w= photo->GetWidth(); uint32 h= photo->GetHeight(); photo->SetSize(h, w); } img.photo_width_ = photo->GetWidth(); img.photo_height_ = photo->GetHeight(); //TODO: convert non-ycbcr from rgb if needed // calculate 'index' using YCbCr image photo->index_.CalcHistogram(bmp); photo->index_.Serialize(img.index_); // now to RGB if (ycbcr_image) bmp.ConvertYCbCr2RGB(); Dib thm; // little thumbnail if (img_size > 160) bmp.ResizeToFit(CSize(160, 160), Dib::RESIZE_HALFTONE, thm); CSize size= bmp.GetSize(); int big= img_size; if (size.cx > big || size.cy > big) bmp.ResizeToFit(CSize(big, big), Dib::RESIZE_HALFTONE); img.img_width_ = bmp.GetWidth(); img.img_height_ = bmp.GetHeight(); // compress preview into JPEG JPEGEncoder enc(jpeg_compression_level, false, false); if (thm.IsValid()) { // we have both little thumbnail image and preview image { CMemoryDataDestination memdest; enc.Encode(memdest, &thm); memdest.SwapJPEG(img.thumbnail_); } { CMemoryDataDestination memdest; enc.Encode(memdest, &bmp); memdest.SwapJPEG(img.preview_); } } else { // only small preview available; // store it in the thumbnail leaving preview field empty CMemoryDataDestination memdest; enc.Encode(memdest, &bmp); memdest.SwapJPEG(img.thumbnail_); img.preview_.clear(); } // IPTC present? //if (photo->IPTC_.get()) // img.iptc_.reset(photo->IPTC_.release()); if (photo->HasMetadata()) { img.iptc_.reset(new IPTCRecord()); photo->GetIPTCInfo(*img.iptc_); } img.description_ = photo->GetExifDescription(); img.marker_index_ = photo->GetFileTypeIndex(); //TODO: store XMP too!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //TODO: // grab tags; // and description; // EliminateNonPrintableChars(photo->photo_desc_); // PostProcessTime(photo.get()); CatalogHeader::DirPtr dir= files_[i].second; img.dir_visited_ = dir->id_; // add/modify record std::vector<uint8> buf; img.Serialize(buf, 1); uint64 record_offset= dbImages_.Append(buf); // examine tags if (!photo->GetTags().empty()) // has tags? { PhotoTags::const_iterator end= photo->GetTags().end(); for (PhotoTags::const_iterator it = photo->GetTags().begin(); it != end; ++it) tags_[*it].push_back(record_offset); } if (dir->records_.empty()) dir->records_.reserve(dir->reserved_capacity_); if (dir->records_.size() < dir->reserved_capacity_) dir->records_.push_back(record_offset); else { ASSERT(false); continue; } ++counter_; } catch (MemPointer::MemPtrException&) { CString msg= _T("Error parsing file: "); msg += path.c_str(); ::ShowMessageBox(msg); } catch (CMemoryException*) { CString msg= _T("Out of memory reading file: "); msg += path.c_str(); ::ShowMessageBox(msg); } catch (JPEGException& ex) { CString msg= _T("Error processing file: "); msg += path.c_str(); msg += _T("\n\n"); msg += ex.GetMessage(); ::ShowMessageBox(msg); } #ifndef _DEBUG catch (...) { // ASSERT(false); } #endif } }
void CTransformationThread::Process(size_t index) { ASSERT(index < files_.size()); ASSERT(files_[index] != 0); ASSERT(index < files_.size()); PhotoInfo& photo= *files_[index]; // output file Path output= GetDestFileName(index); if (transformation_.size() == 1 && transformation_.front().needs_input_file_) { transformation_.front().fn_transform_img_(photo.GetOriginalPath(), output); return; // exclusive transformation done, exit } // start loading SetOperationLabel(IDS_IMG_TRANSFORM_LOADING); CImageDecoderPtr decoder= photo.GetDecoder(); decoder->SetProgressCallback(boost::bind(&CTransformationThread::LinesDecoded, this, _1, _2, _3)); CSize img_size(0, 0); Dib dib; if (ImageStat status= decoder->DecodeImg(dib, img_size, false)) throw ImageStatMsg(status); SetOperationLabel(IDS_IMG_TRANSFORM_APPLYING); for (size_t i= 0; i < transformation_.size(); ++i) { ASSERT(!transformation_[i].needs_input_file_); transformation_[i].fn_transform_bmp_(dib); } // EXIF block std::vector<uint8> exif; if (params_.copy_exif_ && photo.IsExifDataPresent()) { ExifBlock exifBlock; if (photo.ReadExifBlock(exifBlock)) { exifBlock.ModifySizeFields(dib.GetSize(), true); exifBlock.GetExifMarkerBlock(exif); } } // start storing result SetOperationLabel(IDS_IMG_TRANSFORM_STORING); // destination file CFileDataDestination fdest(output.c_str()); // write transformed photo if (!encoder_->Encode(fdest, &dib, &exif)) throw _T("Error encoding JPEG file."); }