blob: a1e5b732fb0bf4e542d41ba9e869df01d659e354 [file] [log] [blame]
/*
* 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