CGImageRef ImageSource::createFrameAtIndex(size_t index, SubsamplingLevel subsamplingLevel) { if (!initialized()) return 0; RetainPtr<CGImageRef> image = adoptCF(CGImageSourceCreateImageAtIndex(m_decoder, index, imageSourceOptions(SkipMetadata, subsamplingLevel))); #if PLATFORM(IOS) // <rdar://problem/7371198> - CoreGraphics changed the default caching behaviour in iOS 4.0 to kCGImageCachingTransient // which caused a performance regression for us since the images had to be resampled/recreated every time we called // CGContextDrawImage. We now tell CG to cache the drawn images. See also <rdar://problem/14366755> - // CoreGraphics needs to un-deprecate kCGImageCachingTemporary since it's still not the default. #if COMPILER(CLANG) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" #endif CGImageSetCachingFlags(image.get(), kCGImageCachingTemporary); #if COMPILER(CLANG) #pragma clang diagnostic pop #endif #endif // !PLATFORM(IOS) CFStringRef imageUTI = CGImageSourceGetType(m_decoder); static const CFStringRef xbmUTI = CFSTR("public.xbitmap-image"); if (!imageUTI || !CFEqual(imageUTI, xbmUTI)) return image.leakRef(); // If it is an xbm image, mask out all the white areas to render them transparent. const CGFloat maskingColors[6] = {255, 255, 255, 255, 255, 255}; RetainPtr<CGImageRef> maskedImage = adoptCF(CGImageCreateWithMaskingColors(image.get(), maskingColors)); if (!maskedImage) return image.leakRef(); return maskedImage.leakRef(); }
String ImageSource::filenameExtension() const { if (!m_decoder) return String(); CFStringRef imageSourceType = CGImageSourceGetType(m_decoder); return WebCore::preferredExtensionForImageSourceType(imageSourceType); }
static SkImageDecoder::Format get_format_cg(SkStreamRewindable* stream) { CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream); if (NULL == imageSrc) { return SkImageDecoder::kUnknown_Format; } SkAutoTCallVProc<const void, CFRelease> arsrc(imageSrc); const CFStringRef name = CGImageSourceGetType(imageSrc); if (NULL == name) { return SkImageDecoder::kUnknown_Format; } return UTType_to_Format(name); }
CGImageRef ImageSource::createFrameAtIndex(size_t index, float* scale) { UNUSED_PARAM(scale); if (!initialized()) return 0; #if !PLATFORM(IOS) UNUSED_PARAM(scale); RetainPtr<CGImageRef> image = adoptCF(CGImageSourceCreateImageAtIndex(m_decoder, index, imageSourceOptions(SkipMetadata))); #else // Subsampling can be 1, 2 or 3, which means quarter-, sixteenth- and sixty-fourth-size, respectively. // A zero or negative value means no subsampling. int subsampling = scale ? static_cast<int>(log2f(1.0f / std::max(0.1f, std::min(1.0f, *scale)))) : -1; RetainPtr<CGImageRef> image = adoptCF(CGImageSourceCreateImageAtIndex(m_decoder, index, imageSourceOptions(SkipMetadata, subsampling))); // <rdar://problem/7371198> - CoreGraphics changed the default caching behaviour in iOS 4.0 to kCGImageCachingTransient // which caused a performance regression for us since the images had to be resampled/recreated every time we called // CGContextDrawImage. We now tell CG to cache the drawn images. See also <rdar://problem/14366755> - // CoreGraphics needs to un-deprecate kCGImageCachingTemporary since it's still not the default. #if COMPILER(CLANG) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" #endif CGImageSetCachingFlags(image.get(), kCGImageCachingTemporary); #if COMPILER(CLANG) #pragma clang diagnostic pop #endif if (scale) { if (subsampling > 0) *scale = static_cast<float>(CGImageGetWidth(image.get())) / size(DoNotRespectImageOrientation).width(); else { ASSERT(static_cast<int>(CGImageGetWidth(image.get())) == size(DoNotRespectImageOrientation).width()); *scale = 1; } } #endif // !PLATFORM(IOS) CFStringRef imageUTI = CGImageSourceGetType(m_decoder); static const CFStringRef xbmUTI = CFSTR("public.xbitmap-image"); if (!imageUTI || !CFEqual(imageUTI, xbmUTI)) return image.leakRef(); // If it is an xbm image, mask out all the white areas to render them transparent. const CGFloat maskingColors[6] = {255, 255, 255, 255, 255, 255}; RetainPtr<CGImageRef> maskedImage = adoptCF(CGImageCreateWithMaskingColors(image.get(), maskingColors)); if (!maskedImage) return image.leakRef(); return maskedImage.leakRef(); }
bool ImageSource::frameHasAlphaAtIndex(size_t) { if (!m_decoder) return false; CFStringRef imageType = CGImageSourceGetType(m_decoder); // Return false if there is no image type or the image type is JPEG, because // JPEG does not support alpha transparency. if (!imageType || CFEqual(imageType, CFSTR("public.jpeg"))) return false; // FIXME: Could return false for other non-transparent image formats. // FIXME: Could maybe return false for a GIF Frame if we have enough info in the GIF properties dictionary // to determine whether or not a transparent color was defined. return true; }
//----------------------------------------------------------------------------- bool CGBitmap::loadFromImageSource (CGImageSourceRef source) { imageSource = source; if (imageSource) { CFRetain (imageSource); CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex (imageSource, 0, 0); if (properties == 0) { return false; } CFNumberRef value = (CFNumberRef)CFDictionaryGetValue (properties, kCGImagePropertyPixelHeight); if (value) { double fValue = 0; if (CFNumberGetValue (value, kCFNumberDoubleType, &fValue)) size.y = fValue; } value = (CFNumberRef)CFDictionaryGetValue (properties, kCGImagePropertyPixelWidth); if (value) { double fValue = 0; if (CFNumberGetValue (value, kCFNumberDoubleType, &fValue)) size.x = fValue; } CFRetain (imageSource); #if VSTGUI_QUARTZ_WORKAROUND_PNG_DECODE_ON_DRAW_BUG // workaround a bug in Mac OS X 10.6 (32 bit), where PNG bitmaps were decoded all the time when drawn. // we fix this by copying the pixels of the bitmap into our own buffer. CFStringRef imageType = CGImageSourceGetType (imageSource); if (imageType && CFStringCompare (imageType, kUTTypePNG, 0) == kCFCompareEqualTo) { CGContextRef context = createCGContext (); if (context) { dirty = true; CFRelease (context); } } #endif CFRelease (properties); } return (size.x != 0 && size.y != 0); }
CGImageRef ImageSource::createFrameAtIndex(size_t index) { if (!initialized()) return 0; RetainPtr<CGImageRef> image(AdoptCF, CGImageSourceCreateImageAtIndex(m_decoder, index, imageSourceOptions())); CFStringRef imageUTI = CGImageSourceGetType(m_decoder); static const CFStringRef xbmUTI = CFSTR("public.xbitmap-image"); if (!imageUTI || !CFEqual(imageUTI, xbmUTI)) return image.releaseRef(); // If it is an xbm image, mask out all the white areas to render them transparent. const CGFloat maskingColors[6] = {255, 255, 255, 255, 255, 255}; RetainPtr<CGImageRef> maskedImage(AdoptCF, CGImageCreateWithMaskingColors(image.get(), maskingColors)); if (!maskedImage) return image.releaseRef(); return maskedImage.releaseRef(); }
NativeImagePtr ImageDecoder::createFrameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel, DecodingMode decodingMode) const { LOG(Images, "ImageDecoder %p createFrameImageAtIndex %lu", this, index); RetainPtr<CFDictionaryRef> options = imageSourceOptions(subsamplingLevel, decodingMode); RetainPtr<CGImageRef> image; if (decodingMode == DecodingMode::OnDemand) image = adoptCF(CGImageSourceCreateImageAtIndex(m_nativeDecoder.get(), index, options.get())); else image = adoptCF(CGImageSourceCreateThumbnailAtIndex(m_nativeDecoder.get(), index, options.get())); #if PLATFORM(IOS) // <rdar://problem/7371198> - CoreGraphics changed the default caching behaviour in iOS 4.0 to kCGImageCachingTransient // which caused a performance regression for us since the images had to be resampled/recreated every time we called // CGContextDrawImage. We now tell CG to cache the drawn images. See also <rdar://problem/14366755> - // CoreGraphics needs to un-deprecate kCGImageCachingTemporary since it's still not the default. #if COMPILER(CLANG) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" #endif CGImageSetCachingFlags(image.get(), kCGImageCachingTemporary); #if COMPILER(CLANG) #pragma clang diagnostic pop #endif #endif // PLATFORM(IOS) CFStringRef imageUTI = CGImageSourceGetType(m_nativeDecoder.get()); static const CFStringRef xbmUTI = CFSTR("public.xbitmap-image"); if (!imageUTI) return image; if (!CFEqual(imageUTI, xbmUTI)) return image; // If it is an xbm image, mask out all the white areas to render them transparent. const CGFloat maskingColors[6] = {255, 255, 255, 255, 255, 255}; RetainPtr<CGImageRef> maskedImage = adoptCF(CGImageCreateWithMaskingColors(image.get(), maskingColors)); return maskedImage ? maskedImage : image; }
static int Internal_isType(SDL_RWops* rw_ops, CFStringRef uti_string_to_test) { CGImageSourceRef image_source; CFStringRef uti_type; Boolean is_type; CFDictionaryRef hint_dictionary = NULL; hint_dictionary = CreateHintDictionary(uti_string_to_test); image_source = CreateCGImageSourceFromRWops(rw_ops, hint_dictionary); if(hint_dictionary != NULL) { CFRelease(hint_dictionary); } if(NULL == image_source) { return 0; } // This will get the UTI of the container, not the image itself. // Under most cases, this won't be a problem. // But if a person passes an icon file which contains a bmp, // the format will be of the icon file. // But I think the main SDL_image codebase has this same problem so I'm not going to worry about it. uti_type = CGImageSourceGetType(image_source); // CFShow(uti_type); // Unsure if we really want conformance or equality is_type = UTTypeConformsTo(uti_string_to_test, uti_type); CFRelease(image_source); return (int)is_type; }
String ImageDecoder::filenameExtension() const { CFStringRef imageSourceType = CGImageSourceGetType(m_nativeDecoder.get()); return WebCore::preferredExtensionForImageSourceType(imageSourceType); }
bool FLACMetadata::WriteMetadata(CFErrorRef *error) { UInt8 buf [PATH_MAX]; if(!CFURLGetFileSystemRepresentation(mURL, false, buf, PATH_MAX)) return false; auto stream = new TagLib::FileStream(reinterpret_cast<const char *>(buf)); TagLib::FLAC::File file(stream, TagLib::ID3v2::FrameFactory::instance(), false); if(!file.isValid()) { if(error) { CFStringRef description = CFCopyLocalizedString(CFSTR("The file “%@” is not a valid FLAC file."), ""); CFStringRef failureReason = CFCopyLocalizedString(CFSTR("Not a FLAC file"), ""); CFStringRef recoverySuggestion = CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), ""); *error = CreateErrorForURL(AudioMetadataErrorDomain, AudioMetadataInputOutputError, description, mURL, failureReason, recoverySuggestion); CFRelease(description), description = nullptr; CFRelease(failureReason), failureReason = nullptr; CFRelease(recoverySuggestion), recoverySuggestion = nullptr; } return false; } SetXiphCommentFromMetadata(*this, file.xiphComment(), false); // Remove existing cover art file.removePictures(); // Add album art for(auto attachedPicture : GetAttachedPictures()) { CGImageSourceRef imageSource = CGImageSourceCreateWithData(attachedPicture->GetData(), nullptr); if(nullptr == imageSource) { LOGGER_ERR("org.sbooth.AudioEngine.AudioMetadata.FLAC", "Skipping album art (unable to create image)"); continue; } TagLib::FLAC::Picture *picture = new TagLib::FLAC::Picture; picture->setData(TagLib::ByteVector((const char *)CFDataGetBytePtr(attachedPicture->GetData()), (TagLib::uint)CFDataGetLength(attachedPicture->GetData()))); picture->setType(static_cast<TagLib::FLAC::Picture::Type>(attachedPicture->GetType())); if(attachedPicture->GetDescription()) picture->setDescription(TagLib::StringFromCFString(attachedPicture->GetDescription())); // Convert the image's UTI into a MIME type CFStringRef mimeType = UTTypeCopyPreferredTagWithClass(CGImageSourceGetType(imageSource), kUTTagClassMIMEType); if(mimeType) { picture->setMimeType(TagLib::StringFromCFString(mimeType)); CFRelease(mimeType), mimeType = nullptr; } // Flesh out the height, width, and depth CFDictionaryRef imagePropertiesDictionary = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nullptr); if(imagePropertiesDictionary) { CFNumberRef imageWidth = (CFNumberRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyPixelWidth); CFNumberRef imageHeight = (CFNumberRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyPixelHeight); CFNumberRef imageDepth = (CFNumberRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyDepth); int height, width, depth; // Ignore numeric conversion errors CFNumberGetValue(imageWidth, kCFNumberIntType, &width); CFNumberGetValue(imageHeight, kCFNumberIntType, &height); CFNumberGetValue(imageDepth, kCFNumberIntType, &depth); picture->setHeight(height); picture->setWidth(width); picture->setColorDepth(depth); CFRelease(imagePropertiesDictionary), imagePropertiesDictionary = nullptr; } file.addPicture(picture); CFRelease(imageSource), imageSource = nullptr; } if(!file.save()) { if(error) { CFStringRef description = CFCopyLocalizedString(CFSTR("The file “%@” is not a valid FLAC file."), ""); CFStringRef failureReason = CFCopyLocalizedString(CFSTR("Unable to write metadata"), ""); CFStringRef recoverySuggestion = CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), ""); *error = CreateErrorForURL(AudioMetadataErrorDomain, AudioMetadataInputOutputError, description, mURL, failureReason, recoverySuggestion); CFRelease(description), description = nullptr; CFRelease(failureReason), failureReason = nullptr; CFRelease(recoverySuggestion), recoverySuggestion = nullptr; } return false; } MergeChangedMetadataIntoMetadata(); return true; }
bool SFB::Audio::SetID3v2TagFromMetadata(const Metadata& metadata, TagLib::ID3v2::Tag *tag, bool setAlbumArt) { if(nullptr == tag) return false; // Use UTF-8 as the default encoding (TagLib::ID3v2::FrameFactory::instance())->setDefaultTextEncoding(TagLib::String::UTF8); // Album title tag->setAlbum(TagLib::StringFromCFString(metadata.GetAlbumTitle())); // Artist tag->setArtist(TagLib::StringFromCFString(metadata.GetArtist())); // Composer tag->removeFrames("TCOM"); if(metadata.GetComposer()) { auto frame = new TagLib::ID3v2::TextIdentificationFrame("TCOM", TagLib::String::Latin1); frame->setText(TagLib::StringFromCFString(metadata.GetComposer())); tag->addFrame(frame); } // Genre tag->setGenre(TagLib::StringFromCFString(metadata.GetGenre())); // Date #if 1 int year = 0; if(metadata.GetReleaseDate()) year = CFStringGetIntValue(metadata.GetReleaseDate()); tag->setYear((TagLib::uint)year); #else // TODO: Parse the release date into components and set the frame appropriately tag->removeFrames("TDRC"); if(metadata.GetReleaseDate()) { /* The timestamp fields are based on a subset of ISO 8601. When being as precise as possible the format of a time string is yyyy-MM-ddTHH:mm:ss (year, "-", month, "-", day, "T", hour (out of 24), ":", minutes, ":", seconds), but the precision may be reduced by removing as many time indicators as wanted. Hence valid timestamps are yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddTHH, yyyy-MM-ddTHH:mm and yyyy-MM-ddTHH:mm:ss. All time stamps are UTC. For durations, use the slash character as described in 8601, and for multiple non- contiguous dates, use multiple strings, if allowed by the frame definition. */ // year = CFStringGetIntValue(metadata.GetReleaseDate()); auto frame = new TagLib::ID3v2::TextIdentificationFrame("TDRC", TagLib::String::Latin1); frame->setText(""); tag->addFrame(frame); } #endif // Comment tag->setComment(TagLib::StringFromCFString(metadata.GetComment())); // Album artist tag->removeFrames("TPE2"); if(metadata.GetAlbumArtist()) { auto frame = new TagLib::ID3v2::TextIdentificationFrame("TPE2", TagLib::String::Latin1); frame->setText(TagLib::StringFromCFString(metadata.GetAlbumArtist())); tag->addFrame(frame); } // Track title tag->setTitle(TagLib::StringFromCFString(metadata.GetTitle())); // BPM tag->removeFrames("TBPM"); if(metadata.GetBPM()) { SFB::CFString str = CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("%@"), metadata.GetBPM()); auto frame = new TagLib::ID3v2::TextIdentificationFrame("TBPM", TagLib::String::Latin1); frame->setText(TagLib::StringFromCFString(str)); tag->addFrame(frame); } // Rating tag->removeFrames("POPM"); CFNumberRef rating = metadata.GetRating(); if(rating) { TagLib::ID3v2::PopularimeterFrame *frame = new TagLib::ID3v2::PopularimeterFrame(); int i; if(CFNumberGetValue(rating, kCFNumberIntType, &i)) { frame->setRating(i); tag->addFrame(frame); } } // Track number and total tracks tag->removeFrames("TRCK"); CFNumberRef trackNumber = metadata.GetTrackNumber(); CFNumberRef trackTotal = metadata.GetTrackTotal(); if(trackNumber && trackTotal) { SFB::CFString str = CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("%@/%@"), trackNumber, trackTotal); auto frame = new TagLib::ID3v2::TextIdentificationFrame("TRCK", TagLib::String::Latin1); frame->setText(TagLib::StringFromCFString(str)); tag->addFrame(frame); } else if(trackNumber) { SFB::CFString str = CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("%@"), trackNumber); auto frame = new TagLib::ID3v2::TextIdentificationFrame("TRCK", TagLib::String::Latin1); frame->setText(TagLib::StringFromCFString(str)); tag->addFrame(frame); } else if(trackTotal) { SFB::CFString str = CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("/%@"), trackTotal); auto frame = new TagLib::ID3v2::TextIdentificationFrame("TRCK", TagLib::String::Latin1); frame->setText(TagLib::StringFromCFString(str)); tag->addFrame(frame); } // Compilation // iTunes uses the TCMP frame for this, which isn't in the standard, but we'll use it for compatibility tag->removeFrames("TCMP"); if(metadata.GetCompilation()) { auto frame = new TagLib::ID3v2::TextIdentificationFrame("TCMP", TagLib::String::Latin1); frame->setText(CFBooleanGetValue(metadata.GetCompilation()) ? "1" : "0"); tag->addFrame(frame); } // Disc number and total discs tag->removeFrames("TPOS"); CFNumberRef discNumber = metadata.GetDiscNumber(); CFNumberRef discTotal = metadata.GetDiscTotal(); if(discNumber && discTotal) { SFB::CFString str = CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("%@/%@"), discNumber, discTotal); auto frame = new TagLib::ID3v2::TextIdentificationFrame("TPOS", TagLib::String::Latin1); frame->setText(TagLib::StringFromCFString(str)); tag->addFrame(frame); } else if(discNumber) { SFB::CFString str = CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("%@"), discNumber); auto frame = new TagLib::ID3v2::TextIdentificationFrame("TPOS", TagLib::String::Latin1); frame->setText(TagLib::StringFromCFString(str)); tag->addFrame(frame); } else if(discTotal) { SFB::CFString str = CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("/%@"), discTotal); auto frame = new TagLib::ID3v2::TextIdentificationFrame("TPOS", TagLib::String::Latin1); frame->setText(TagLib::StringFromCFString(str)); tag->addFrame(frame); } // Lyrics tag->removeFrames("USLT"); if(metadata.GetLyrics()) { auto frame = new TagLib::ID3v2::UnsynchronizedLyricsFrame(TagLib::String::UTF8); frame->setText(TagLib::StringFromCFString(metadata.GetLyrics())); tag->addFrame(frame); } tag->removeFrames("TSRC"); if(metadata.GetISRC()) { auto frame = new TagLib::ID3v2::TextIdentificationFrame("TSRC", TagLib::String::Latin1); frame->setText(TagLib::StringFromCFString(metadata.GetISRC())); tag->addFrame(frame); } // MusicBrainz auto musicBrainzReleaseIDFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(tag, "MusicBrainz Album Id"); if(nullptr != musicBrainzReleaseIDFrame) tag->removeFrame(musicBrainzReleaseIDFrame); CFStringRef musicBrainzReleaseID = metadata.GetMusicBrainzReleaseID(); if(musicBrainzReleaseID) { auto frame = new TagLib::ID3v2::UserTextIdentificationFrame(); frame->setDescription("MusicBrainz Album Id"); frame->setText(TagLib::StringFromCFString(musicBrainzReleaseID)); tag->addFrame(frame); } auto musicBrainzRecordingIDFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(const_cast<TagLib::ID3v2::Tag *>(tag), "MusicBrainz Track Id"); if(nullptr != musicBrainzRecordingIDFrame) tag->removeFrame(musicBrainzRecordingIDFrame); CFStringRef musicBrainzRecordingID = metadata.GetMusicBrainzRecordingID(); if(musicBrainzRecordingID) { auto frame = new TagLib::ID3v2::UserTextIdentificationFrame(); frame->setDescription("MusicBrainz Track Id"); frame->setText(TagLib::StringFromCFString(musicBrainzRecordingID)); tag->addFrame(frame); } // Sorting and grouping tag->removeFrames("TSOT"); if(metadata.GetTitleSortOrder()) { auto frame = new TagLib::ID3v2::TextIdentificationFrame("TSOT", TagLib::String::UTF8); frame->setText(TagLib::StringFromCFString(metadata.GetTitleSortOrder())); tag->addFrame(frame); } tag->removeFrames("TSOA"); if(metadata.GetAlbumTitleSortOrder()) { auto frame = new TagLib::ID3v2::TextIdentificationFrame("TSOA", TagLib::String::UTF8); frame->setText(TagLib::StringFromCFString(metadata.GetAlbumTitleSortOrder())); tag->addFrame(frame); } tag->removeFrames("TSOP"); if(metadata.GetArtistSortOrder()) { auto frame = new TagLib::ID3v2::TextIdentificationFrame("TSOP", TagLib::String::UTF8); frame->setText(TagLib::StringFromCFString(metadata.GetArtistSortOrder())); tag->addFrame(frame); } tag->removeFrames("TSO2"); if(metadata.GetAlbumArtistSortOrder()) { auto frame = new TagLib::ID3v2::TextIdentificationFrame("TSO2", TagLib::String::UTF8); frame->setText(TagLib::StringFromCFString(metadata.GetAlbumArtistSortOrder())); tag->addFrame(frame); } tag->removeFrames("TSOC"); if(metadata.GetComposerSortOrder()) { auto frame = new TagLib::ID3v2::TextIdentificationFrame("TSOC", TagLib::String::UTF8); frame->setText(TagLib::StringFromCFString(metadata.GetComposerSortOrder())); tag->addFrame(frame); } tag->removeFrames("TIT1"); if(metadata.GetGrouping()) { auto frame = new TagLib::ID3v2::TextIdentificationFrame("TIT1", TagLib::String::UTF8); frame->setText(TagLib::StringFromCFString(metadata.GetGrouping())); tag->addFrame(frame); } // ReplayGain CFNumberRef trackGain = metadata.GetReplayGainTrackGain(); CFNumberRef trackPeak = metadata.GetReplayGainTrackPeak(); CFNumberRef albumGain = metadata.GetReplayGainAlbumGain(); CFNumberRef albumPeak = metadata.GetReplayGainAlbumPeak(); // Write TXXX frames auto trackGainFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(tag, "replaygain_track_gain"); auto trackPeakFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(tag, "replaygain_track_peak"); auto albumGainFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(tag, "replaygain_album_gain"); auto albumPeakFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(tag, "replaygain_album_peak"); if(nullptr != trackGainFrame) tag->removeFrame(trackGainFrame); if(nullptr != trackPeakFrame) tag->removeFrame(trackPeakFrame); if(nullptr != albumGainFrame) tag->removeFrame(albumGainFrame); if(nullptr != albumPeakFrame) tag->removeFrame(albumPeakFrame); if(trackGain) { SFB::CFString str = CreateStringFromNumberWithFormat(trackGain, kCFNumberDoubleType, CFSTR("%+2.2f dB")); auto frame = new TagLib::ID3v2::UserTextIdentificationFrame(); frame->setDescription("replaygain_track_gain"); frame->setText(TagLib::StringFromCFString(str)); tag->addFrame(frame); } if(trackPeak) { SFB::CFString str = CreateStringFromNumberWithFormat(trackPeak, kCFNumberDoubleType, CFSTR("%1.8f dB")); auto frame = new TagLib::ID3v2::UserTextIdentificationFrame(); frame->setDescription("replaygain_track_peak"); frame->setText(TagLib::StringFromCFString(str)); tag->addFrame(frame); } if(albumGain) { SFB::CFString str = CreateStringFromNumberWithFormat(albumGain, kCFNumberDoubleType, CFSTR("%+2.2f dB")); auto frame = new TagLib::ID3v2::UserTextIdentificationFrame(); frame->setDescription("replaygain_album_gain"); frame->setText(TagLib::StringFromCFString(str)); tag->addFrame(frame); } if(albumPeak) { SFB::CFString str = CreateStringFromNumberWithFormat(albumPeak, kCFNumberDoubleType, CFSTR("%1.8f dB")); auto frame = new TagLib::ID3v2::UserTextIdentificationFrame(); frame->setDescription("replaygain_album_peak"); frame->setText(TagLib::StringFromCFString(str)); tag->addFrame(frame); } // Also write the RVA2 frames tag->removeFrames("RVA2"); if(trackGain) { auto relativeVolume = new TagLib::ID3v2::RelativeVolumeFrame(); float f; CFNumberGetValue(trackGain, kCFNumberFloatType, &f); relativeVolume->setIdentification(TagLib::String("track", TagLib::String::Latin1)); relativeVolume->setVolumeAdjustment(f, TagLib::ID3v2::RelativeVolumeFrame::MasterVolume); tag->addFrame(relativeVolume); } if(albumGain) { auto relativeVolume = new TagLib::ID3v2::RelativeVolumeFrame(); float f; CFNumberGetValue(albumGain, kCFNumberFloatType, &f); relativeVolume->setIdentification(TagLib::String("album", TagLib::String::Latin1)); relativeVolume->setVolumeAdjustment(f, TagLib::ID3v2::RelativeVolumeFrame::MasterVolume); tag->addFrame(relativeVolume); } // Album art if(setAlbumArt) { tag->removeFrames("APIC"); for(auto attachedPicture : metadata.GetAttachedPictures()) { SFB::CGImageSource imageSource = CGImageSourceCreateWithData(attachedPicture->GetData(), nullptr); if(!imageSource) continue; TagLib::ID3v2::AttachedPictureFrame *frame = new TagLib::ID3v2::AttachedPictureFrame; // Convert the image's UTI into a MIME type SFB::CFString mimeType = UTTypeCopyPreferredTagWithClass(CGImageSourceGetType(imageSource), kUTTagClassMIMEType); if(mimeType) frame->setMimeType(TagLib::StringFromCFString(mimeType)); frame->setPicture(TagLib::ByteVector((const char *)CFDataGetBytePtr(attachedPicture->GetData()), (TagLib::uint)CFDataGetLength(attachedPicture->GetData()))); frame->setType((TagLib::ID3v2::AttachedPictureFrame::Type)attachedPicture->GetType()); if(attachedPicture->GetDescription()) frame->setDescription(TagLib::StringFromCFString(attachedPicture->GetDescription())); tag->addFrame(frame); } } return true; }
/* the error does not contain the filename as the caller already has it */ CoglBitmap * _cogl_bitmap_from_file (const char *filename, GError **error) { CFURLRef url; CGImageSourceRef image_source; CGImageRef image; int save_errno; CFStringRef type; size_t width, height, rowstride; uint8_t *out_data; CGColorSpaceRef color_space; CGContextRef bitmap_context; CoglBitmap *bmp; _COGL_GET_CONTEXT (ctx, NULL); g_assert (filename != NULL); g_assert (error == NULL || *error == NULL); url = CFURLCreateFromFileSystemRepresentation (NULL, (guchar *) filename, strlen (filename), false); image_source = CGImageSourceCreateWithURL (url, NULL); save_errno = errno; CFRelease (url); if (image_source == NULL) { /* doesn't exist, not readable, etc. */ g_set_error_literal (error, COGL_BITMAP_ERROR, COGL_BITMAP_ERROR_FAILED, g_strerror (save_errno)); return NULL; } /* Unknown images would be cleanly caught as zero width/height below, but try * to provide better error message */ type = CGImageSourceGetType (image_source); if (type == NULL) { CFRelease (image_source); g_set_error_literal (error, COGL_BITMAP_ERROR, COGL_BITMAP_ERROR_UNKNOWN_TYPE, "Unknown image type"); return NULL; } CFRelease (type); image = CGImageSourceCreateImageAtIndex (image_source, 0, NULL); CFRelease (image_source); width = CGImageGetWidth (image); height = CGImageGetHeight (image); if (width == 0 || height == 0) { /* incomplete or corrupt */ CFRelease (image); g_set_error_literal (error, COGL_BITMAP_ERROR, COGL_BITMAP_ERROR_CORRUPT_IMAGE, "Image has zero width or height"); return NULL; } /* allocate buffer big enough to hold pixel data */ bmp = _cogl_bitmap_new_with_malloc_buffer (ctx, width, height, COGL_PIXEL_FORMAT_ARGB_8888); rowstride = cogl_bitmap_get_rowstride (bmp); out_data = _cogl_bitmap_map (bmp, COGL_BUFFER_ACCESS_WRITE, COGL_BUFFER_MAP_HINT_DISCARD); /* render to buffer */ color_space = CGColorSpaceCreateWithName (kCGColorSpaceGenericRGB); bitmap_context = CGBitmapContextCreate (out_data, width, height, 8, rowstride, color_space, kCGImageAlphaPremultipliedFirst); CGColorSpaceRelease (color_space); { const CGRect rect = {{0, 0}, {width, height}}; CGContextDrawImage (bitmap_context, rect, image); } CGImageRelease (image); CGContextRelease (bitmap_context); _cogl_bitmap_unmap (bmp); /* store bitmap info */ return bmp; }
/* the error does not contain the filename as the caller already has it */ gboolean _cogl_bitmap_from_file (CoglBitmap *bmp, const gchar *filename, GError **error) { g_assert (bmp != NULL); g_assert (filename != NULL); g_assert (error == NULL || *error == NULL); CFURLRef url = CFURLCreateFromFileSystemRepresentation (NULL, (guchar*)filename, strlen(filename), false); CGImageSourceRef image_source = CGImageSourceCreateWithURL (url, NULL); int save_errno = errno; CFRelease (url); if (image_source == NULL) { /* doesn't exist, not readable, etc. */ g_set_error (error, COGL_BITMAP_ERROR, COGL_BITMAP_ERROR_FAILED, "%s", g_strerror (save_errno)); return FALSE; } /* Unknown images would be cleanly caught as zero width/height below, but try * to provide better error message */ CFStringRef type = CGImageSourceGetType (image_source); if (type == NULL) { CFRelease (image_source); g_set_error (error, COGL_BITMAP_ERROR, COGL_BITMAP_ERROR_UNKNOWN_TYPE, "Unknown image type"); return FALSE; } CFRelease (type); CGImageRef image = CGImageSourceCreateImageAtIndex (image_source, 0, NULL); CFRelease (image_source); size_t width = CGImageGetWidth (image); size_t height = CGImageGetHeight (image); if (width == 0 || height == 0) { /* incomplete or corrupt */ CFRelease (image); g_set_error (error, COGL_BITMAP_ERROR, COGL_BITMAP_ERROR_CORRUPT_IMAGE, "Image has zero width or height"); return FALSE; } /* allocate buffer big enough to hold pixel data */ size_t rowstride; CGBitmapInfo bitmap_info = CGImageGetBitmapInfo (image); if ((bitmap_info & kCGBitmapAlphaInfoMask) == kCGImageAlphaNone) { bitmap_info = kCGImageAlphaNone; rowstride = 3 * width; } else { bitmap_info = kCGImageAlphaPremultipliedFirst; rowstride = 4 * width; } guint8 *out_data = g_malloc0 (height * rowstride); /* render to buffer */ CGColorSpaceRef color_space = CGColorSpaceCreateWithName (kCGColorSpaceGenericRGB); CGContextRef bitmap_context = CGBitmapContextCreate (out_data, width, height, 8, rowstride, color_space, bitmap_info); CGColorSpaceRelease (color_space); const CGRect rect = {{0, 0}, {width, height}}; CGContextDrawImage (bitmap_context, rect, image); CGImageRelease (image); CGContextRelease (bitmap_context); /* store bitmap info */ bmp->data = out_data; bmp->format = bitmap_info == kCGImageAlphaPremultipliedFirst ? COGL_PIXEL_FORMAT_ARGB_8888 : COGL_PIXEL_FORMAT_RGB_888; bmp->width = width; bmp->height = height; bmp->rowstride = rowstride; return TRUE; }
bool SetAPETagFromMetadata(const AudioMetadata& metadata, TagLib::APE::Tag *tag, bool setAlbumArt) { if(nullptr == tag) return false; // Standard tags SetAPETag(tag, "ALBUM", metadata.GetAlbumTitle()); SetAPETag(tag, "ARTIST", metadata.GetArtist()); SetAPETag(tag, "ALBUMARTIST", metadata.GetAlbumArtist()); SetAPETag(tag, "COMPOSER", metadata.GetComposer()); SetAPETag(tag, "GENRE", metadata.GetGenre()); SetAPETag(tag, "DATE", metadata.GetReleaseDate()); SetAPETag(tag, "DESCRIPTION", metadata.GetComment()); SetAPETag(tag, "TITLE", metadata.GetTitle()); SetAPETagNumber(tag, "TRACKNUMBER", metadata.GetTrackNumber()); SetAPETagNumber(tag, "TRACKTOTAL", metadata.GetTrackTotal()); SetAPETagBoolean(tag, "COMPILATION", metadata.GetCompilation()); SetAPETagNumber(tag, "DISCNUMBER", metadata.GetDiscNumber()); SetAPETagNumber(tag, "DISCTOTAL", metadata.GetDiscTotal()); SetAPETagNumber(tag, "BPM", metadata.GetBPM()); SetAPETagNumber(tag, "RATING", metadata.GetRating()); SetAPETag(tag, "ISRC", metadata.GetISRC()); SetAPETag(tag, "MCN", metadata.GetMCN()); SetAPETag(tag, "TITLESORT", metadata.GetTitleSortOrder()); SetAPETag(tag, "ALBUMTITLESORT", metadata.GetAlbumTitleSortOrder()); SetAPETag(tag, "ARTISTSORT", metadata.GetArtistSortOrder()); SetAPETag(tag, "ALBUMARTISTSORT", metadata.GetAlbumArtistSortOrder()); SetAPETag(tag, "COMPOSERSORT", metadata.GetComposerSortOrder()); SetAPETag(tag, "GROUPING", metadata.GetGrouping()); // Additional metadata CFDictionaryRef additionalMetadata = metadata.GetAdditionalMetadata(); if(nullptr != additionalMetadata) { CFIndex count = CFDictionaryGetCount(additionalMetadata); const void * keys [count]; const void * values [count]; CFDictionaryGetKeysAndValues(additionalMetadata, reinterpret_cast<const void **>(keys), reinterpret_cast<const void **>(values)); for(CFIndex i = 0; i < count; ++i) { CFIndex keySize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(reinterpret_cast<CFStringRef>(keys[i])), kCFStringEncodingASCII); char key [keySize + 1]; if(!CFStringGetCString(reinterpret_cast<CFStringRef>(keys[i]), key, keySize + 1, kCFStringEncodingASCII)) { LOGGER_ERR("org.sbooth.AudioEngine", "CFStringGetCString failed"); continue; } SetAPETag(tag, key, reinterpret_cast<CFStringRef>(values[i])); } } // ReplayGain info SetAPETagDouble(tag, "REPLAYGAIN_REFERENCE_LOUDNESS", metadata.GetReplayGainReferenceLoudness(), CFSTR("%2.1f dB")); SetAPETagDouble(tag, "REPLAYGAIN_TRACK_GAIN", metadata.GetReplayGainTrackGain(), CFSTR("%+2.2f dB")); SetAPETagDouble(tag, "REPLAYGAIN_TRACK_PEAK", metadata.GetReplayGainTrackPeak(), CFSTR("%1.8f")); SetAPETagDouble(tag, "REPLAYGAIN_ALBUM_GAIN", metadata.GetReplayGainAlbumGain(), CFSTR("%+2.2f dB")); SetAPETagDouble(tag, "REPLAYGAIN_ALBUM_PEAK", metadata.GetReplayGainAlbumPeak(), CFSTR("%1.8f")); // Album art if(setAlbumArt) { tag->removeItem("Cover Art (Front)"); tag->removeItem("Cover Art (Back)"); #if 0 tag->removeItem("METADATA_BLOCK_PICTURE"); #endif for(auto attachedPicture : metadata.GetAttachedPictures()) { // APE can handle front and back covers natively if(AttachedPicture::Type::FrontCover == attachedPicture->GetType() || AttachedPicture::Type::FrontCover == attachedPicture->GetType()) { TagLib::ByteVector data; if(attachedPicture->GetDescription()) data.append(TagLib::StringFromCFString(attachedPicture->GetDescription()).data(TagLib::String::UTF8)); data.append('\0'); data.append(TagLib::ByteVector((const char *)CFDataGetBytePtr(attachedPicture->GetData()), (TagLib::uint)CFDataGetLength(attachedPicture->GetData()))); if(AttachedPicture::Type::FrontCover == attachedPicture->GetType()) tag->setData("Cover Art (Front)", data); else if(AttachedPicture::Type::BackCover == attachedPicture->GetType()) tag->setData("Cover Art (Back)", data); } #if 0 else { CGImageSourceRef imageSource = CGImageSourceCreateWithData(attachedPicture->GetData(), nullptr); if(nullptr == imageSource) return false; TagLib::FLAC::Picture picture; picture.setData(TagLib::ByteVector((const char *)CFDataGetBytePtr(attachedPicture->GetData()), (TagLib::uint)CFDataGetLength(attachedPicture->GetData()))); picture.setType(static_cast<TagLib::FLAC::Picture::Type>(attachedPicture->GetType())); if(attachedPicture->GetDescription()) picture.setDescription(TagLib::StringFromCFString(attachedPicture->GetDescription())); // Convert the image's UTI into a MIME type CFStringRef mimeType = UTTypeCopyPreferredTagWithClass(CGImageSourceGetType(imageSource), kUTTagClassMIMEType); if(mimeType) { picture.setMimeType(TagLib::StringFromCFString(mimeType)); CFRelease(mimeType), mimeType = nullptr; } // Flesh out the height, width, and depth CFDictionaryRef imagePropertiesDictionary = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nullptr); if(imagePropertiesDictionary) { CFNumberRef imageWidth = (CFNumberRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyPixelWidth); CFNumberRef imageHeight = (CFNumberRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyPixelHeight); CFNumberRef imageDepth = (CFNumberRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyDepth); int height, width, depth; // Ignore numeric conversion errors CFNumberGetValue(imageWidth, kCFNumberIntType, &width); CFNumberGetValue(imageHeight, kCFNumberIntType, &height); CFNumberGetValue(imageDepth, kCFNumberIntType, &depth); picture.setHeight(height); picture.setWidth(width); picture.setColorDepth(depth); CFRelease(imagePropertiesDictionary), imagePropertiesDictionary = nullptr; } TagLib::ByteVector encodedBlock = TagLib::EncodeBase64(picture.render()); tag->addValue("METADATA_BLOCK_PICTURE", TagLib::String(encodedBlock, TagLib::String::UTF8), false); CFRelease(imageSource), imageSource = nullptr; } #endif } } return true; }