bool CxImageJPG::DecodeExif(CxFile * hFile) { long pos=hFile->Tell(); CxExifInfo exif(&m_exifinfo); exif.DecodeExif(hFile); hFile->Seek(pos,SEEK_SET); return exif.m_exifinfo->IsExif; }
bool ReadJPEGInfo(const char *filename, JPEG_INFO& info) { EXIFINFO exinfo; Exif exif(&exinfo); FILE *fp; bool success = false; memset(&exinfo, 0, sizeof(exinfo)); if ((fp = fopen(filename, "rb")) != NULL) { if (exif.DecodeExif(fp)) { info.CameraMake = exinfo.CameraMake; info.CameraModel = exinfo.CameraModel; info.Width = exinfo.Width; info.Height = exinfo.Height; info.Orientation = exinfo.Orientation; info.bDateValid = false; info.Comments = exinfo.Comments; AString str = exinfo.DateTime; if (str.Valid()) { //printf("File '%s' has date '%s'\n", filename, str.str()); str.Replace(":/-.", " "); ADateTime& dt = info.DateTime; uint_t word = 0; uint16_t year = str.Word(word++); uint8_t month = str.Word(word++); uint8_t day = str.Word(word++); uint8_t hour = str.Word(word++); uint8_t minute = str.Word(word++); uint8_t second = str.Word(word++); if ((year >= 1980) && RANGE(month, 1, 12) && RANGE(day, 1, 31) && (hour <= 23) && (minute <= 59) && (second <= 61)) { dt.Set(day, month, year, hour, minute, second); info.bDateValid = true; } } success = true; } fclose(fp); } return success; }
bool CxImageJPG::GetExifThumbnail(const TCHAR *filename, const TCHAR *outname, int32_t type) { CxIOFile file; if (!file.Open(filename, _T("rb"))) return false; CxExifInfo exif(&info.ExifInfo); exif.DecodeExif(&file); if (info.ExifInfo.IsExif && info.ExifInfo.ThumbnailPointer && info.ExifInfo.ThumbnailSize > 0) { // have a thumbnail - check whether it needs rotating or resizing // TODO: Write a fast routine to read the jpeg header to get the width and height CxImage image(info.ExifInfo.ThumbnailPointer, info.ExifInfo.ThumbnailSize, CXIMAGE_FORMAT_JPG); if (image.IsValid()) { if (image.GetWidth() > 256 || image.GetHeight() > 256) { // resize the image // float amount = 256.0f / max(image.GetWidth(), image.GetHeight()); // image.Resample((int32_t)(image.GetWidth() * amount), (int32_t)(image.GetHeight() * amount), 0); } #if CXIMAGE_SUPPORT_TRANSFORMATION if (info.ExifInfo.Orientation != 1) image.RotateExif(info.ExifInfo.Orientation); #endif #if CXIMAGE_SUPPORT_ENCODE return image.Save(outname, CXIMAGE_FORMAT_JPG); #endif } // nice and fast, but we can't resize :( /* FILE *hFileWrite; if ((hFileWrite=fopen(outname, "wb")) != NULL) { fwrite(m_exifinfo.ThumbnailPointer, m_exifinfo.ThumbnailSize, 1, hFileWrite); fclose(hFileWrite); return true; }*/ } return false; }
void PNG::dump() { // Text chunks png_textp textp = 0; int ntext = 0; png_get_text(mData, mInfo, &textp, &ntext); var text; for (int i=0; i<ntext; i++) text[textp[i].key] = textp[i].text; std::cout << "Text: " << text << std::endl; // EXIF // The library reports "png_get_eXIf does not work; use png_get_eXIf_1" png_bytep exif_data = 0; png_uint_32 num_exif = 0; png_uint_32 pngRet = png_get_eXIf_1(mData, mInfo, &num_exif, &exif_data); if (pngRet) { assert(exif_data); EXIF exif(exif_data, num_exif); exif.dump(); std::cout << "EXIF date: " << exif.date() << std::endl; } }
AReader::Error ReaderJpeg::read( imageCache &cache, const uint8_t* data, unsigned length, QString format ) const{ if( !can_read( data, length, format ) ) return ERROR_TYPE_UNKNOWN; try{ cache.set_info( 1 ); JpegDecompress jpeg( data, length ); jpeg.cinfo.client_data = &cache; //Save application data, we are interested in ICC profiles and EXIF metadata jpeg.saveMarker( ICC_META_TEST ); jpeg.saveMarker( EXIF_META_TEST ); /* READ EVERYTHING! jpeg_save_markers( &jpeg.cinfo, JPEG_COM, 0xFFFF ); for( unsigned i=0; i<16; i++ ) jpeg_save_markers( &jpeg.cinfo, JPEG_APP0+i, 0xFFFF ); //*/ //Read header and set-up image jpeg.readHeader(); jpeg_start_decompress( &jpeg.cinfo ); bool is_gray; switch( jpeg.cinfo.out_color_components ){ case 1: is_gray = true; break; case 3: is_gray = false; break; default: return ERROR_UNSUPPORTED; } QImage frame( jpeg.cinfo.output_width, jpeg.cinfo.output_height, QImage::Format_RGB32 ); //Read image auto buffer = std::make_unique<JSAMPLE[]>( jpeg.bytesPerLine() ); JSAMPLE* arr[1] = { buffer.get() }; while( jpeg.cinfo.output_scanline < jpeg.cinfo.output_height ){ auto out = (QRgb*)frame.scanLine( jpeg.cinfo.output_scanline ); jpeg_read_scanlines( &jpeg.cinfo, arr, 1 ); if( is_gray ) for( unsigned ix=0; ix<jpeg.cinfo.output_width; ix++ ) out[ix] = qRgb( buffer[ix], buffer[ix], buffer[ix] ); else for( unsigned ix=0; ix<jpeg.cinfo.output_width; ix++ ) out[ix] = qRgb( buffer[ix*3+0], buffer[ix*3+1], buffer[ix*3+2] ); } //Check all markers for( auto marker = jpeg.cinfo.marker_list; marker; marker = marker->next ){ //Check for and read ICC profile if( ICC_META_TEST.validate( marker ) ){ cache.set_profile( ColorProfile::fromMem( marker->data + ICC_META_TEST.length , marker->data_length - ICC_META_TEST.length ) ); } if( EXIF_META_TEST.validate( marker ) ){ meta exif( marker->data + EXIF_META_TEST.length , marker->data_length - EXIF_META_TEST.length ); cache.set_orientation( exif.get_orientation() ); //Read thumbnail cache.thumbnail = exif.get_thumbnail(); //TODO: We actually want to read this BEFORE the full image ;) //TODO: Actually do something with this info. Perhaps check for a profile as well! } /* Save data to file for debugging QFile f( "Jpeg marker " + QString::number(marker->marker-JPEG_APP0) + ".bin" ); f.open( QIODevice::WriteOnly ); f.write( (char*)marker->data, marker->data_length ); //*/ } jpeg_finish_decompress( &jpeg.cinfo ); cache.add_frame( frame, 0 ); //Cleanup and return cache.set_fully_loaded(); return ERROR_NONE; } catch( int err_code ){ switch( err_code ){ case JERR_NO_SOI: return ERROR_TYPE_UNKNOWN; default: return ERROR_FILE_BROKEN; }; } }