| /* |
| * Copyright (C) 2020 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "ImageUtilities.h" |
| |
| #include "Logging.h" |
| #include "MIMETypeRegistry.h" |
| #include "UTIUtilities.h" |
| #include <ImageIO/ImageIO.h> |
| #include <wtf/FileSystem.h> |
| #include <wtf/text/CString.h> |
| |
| namespace WebCore { |
| |
| WorkQueue& sharedImageTranscodingQueue() |
| { |
| static NeverDestroyed<Ref<WorkQueue>> queue(WorkQueue::create("com.apple.WebKit.ImageTranscoding")); |
| return queue.get(); |
| } |
| |
| static String transcodeImage(const String& path, const String& destinationUTI, const String& destinationExtension) |
| { |
| auto sourceURL = adoptCF(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path.createCFString().get(), kCFURLPOSIXPathStyle, false)); |
| auto source = adoptCF(CGImageSourceCreateWithURL(sourceURL.get(), nullptr)); |
| if (!source) |
| return nullString(); |
| |
| auto sourceUTI = String(CGImageSourceGetType(source.get())); |
| if (sourceUTI == destinationUTI) |
| return nullString(); |
| |
| #if !HAVE(IMAGEIO_FIX_FOR_RADAR_59589723) |
| auto sourceMIMEType = MIMETypeFromUTI(sourceUTI); |
| if (sourceMIMEType == "image/heif"_s || sourceMIMEType == "image/heic"_s) { |
| static std::once_flag onceFlag; |
| std::call_once(onceFlag, [&] { |
| // This call will force ImageIO to load the symbols of the HEIF reader. This |
| // bug is already fixed in ImageIO of macOS Big Sur <rdar://problem/59589723>. |
| CGImageSourceGetCount(source.get()); |
| }); |
| } |
| #endif |
| |
| // It is important to add the appropriate file extension to the temporary file path. |
| // The File object depends solely on the extension to know the MIME type of the file. |
| auto suffix = makeString('.', destinationExtension); |
| |
| FileSystem::PlatformFileHandle destinationFileHandle; |
| String destinationPath = FileSystem::openTemporaryFile("tempImage"_s, destinationFileHandle, suffix); |
| if (destinationFileHandle == FileSystem::invalidPlatformFileHandle) { |
| RELEASE_LOG_ERROR(Images, "transcodeImage: Destination image could not be created: %s %s\n", path.utf8().data(), destinationUTI.utf8().data()); |
| return nullString(); |
| } |
| |
| CGDataConsumerCallbacks callbacks = { |
| [](void* info, const void* buffer, size_t count) -> size_t { |
| auto handle = *static_cast<FileSystem::PlatformFileHandle*>(info); |
| return FileSystem::writeToFile(handle, buffer, count); |
| }, |
| nullptr |
| }; |
| |
| auto consumer = adoptCF(CGDataConsumerCreate(&destinationFileHandle, &callbacks)); |
| auto destination = adoptCF(CGImageDestinationCreateWithDataConsumer(consumer.get(), destinationUTI.createCFString().get(), 1, nullptr)); |
| |
| CGImageDestinationAddImageFromSource(destination.get(), source.get(), 0, nullptr); |
| |
| if (!CGImageDestinationFinalize(destination.get())) { |
| RELEASE_LOG_ERROR(Images, "transcodeImage: Image transcoding fails: %s %s\n", path.utf8().data(), destinationUTI.utf8().data()); |
| FileSystem::closeFile(destinationFileHandle); |
| FileSystem::deleteFile(destinationPath); |
| return nullString(); |
| } |
| |
| FileSystem::closeFile(destinationFileHandle); |
| return destinationPath; |
| } |
| |
| Vector<String> findImagesForTranscoding(const Vector<String>& paths, const Vector<String>& allowedMIMETypes) |
| { |
| Vector<String> transcodingPaths; |
| transcodingPaths.reserveInitialCapacity(paths.size()); |
| |
| bool needsTranscoding = false; |
| |
| for (auto& path : paths) { |
| // Append a path of the image which needs transcoding. Otherwise append a null string. |
| if (!allowedMIMETypes.contains(WebCore::MIMETypeRegistry::mimeTypeForPath(path))) { |
| transcodingPaths.uncheckedAppend(path); |
| needsTranscoding = true; |
| } else |
| transcodingPaths.uncheckedAppend(nullString()); |
| } |
| |
| // If none of the files needs image transcoding, return an empty Vector. |
| return needsTranscoding ? transcodingPaths : Vector<String>(); |
| } |
| |
| Vector<String> transcodeImages(const Vector<String>& paths, const String& destinationUTI, const String& destinationExtension) |
| { |
| ASSERT(!destinationUTI.isNull()); |
| ASSERT(!destinationExtension.isNull()); |
| |
| Vector<String> transcodedPaths; |
| transcodedPaths.reserveInitialCapacity(paths.size()); |
| |
| for (auto& path : paths) { |
| // Append the transcoded path if the image needs transcoding. Otherwise append a null string. |
| if (!path.isNull()) |
| transcodedPaths.uncheckedAppend(transcodeImage(path, destinationUTI, destinationExtension)); |
| else |
| transcodedPaths.uncheckedAppend(nullString()); |
| } |
| |
| return transcodedPaths; |
| } |
| |
| } // namespace WebCore |