Exemple #1
0
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.");
}