File indexing completed on 2024-10-13 04:16:23

0001 // SPDX-FileCopyrightText: Lukas Sommer <sommerluk@gmail.com>
0002 // SPDX-License-Identifier: BSD-2-Clause OR MIT
0003 
0004 // Own headers
0005 // First the interface, which forces the header to be self-contained.
0006 #include "iohandlerfactory.h"
0007 
0008 #include "helpermath.h"
0009 #include <lcms2.h>
0010 #include <lcms2_plugin.h>
0011 #include <limits>
0012 #include <qdebug.h>
0013 #include <qfile.h>
0014 #include <qglobal.h>
0015 #include <qiodevice.h>
0016 #include <qstringliteral.h>
0017 
0018 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0019 #include <qiodevicebase.h>
0020 #endif
0021 
0022 namespace PerceptualColor
0023 {
0024 /** @internal
0025  *
0026  * @brief Read from file.
0027  *
0028  * @param iohandler The <tt>cmsIOHANDLER</tt> on which to operate
0029  * @param Buffer Pointer to the buffer to which the data should be loaded
0030  * @param size Size of the chucks that should be loaded
0031  * @param count Number of elements that should be loaded
0032  * @returns On success, <tt>count</tt> is returned. If failing (either
0033  * because less elements have been read or because zero elements have
0034  * been read), <tt>0</tt> is returned. */
0035 cmsUInt32Number IOHandlerFactory::read(cmsIOHANDLER *iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count)
0036 {
0037     QFile *const myFile = static_cast<QFile *>(iohandler->stream);
0038     const cmsUInt32Number numberOfBytesRequested = size * count;
0039     const qint64 numberOfBytesRead = myFile->read( //
0040         static_cast<char *>(Buffer),
0041         size * count);
0042     if (numberOfBytesRead != numberOfBytesRequested) {
0043         return 0;
0044     }
0045     return count;
0046 }
0047 
0048 /** @internal
0049  *
0050  * @brief Sets the current position within the file.
0051  *
0052  * @param iohandler The <tt>cmsIOHANDLER</tt> on which to operate
0053  * @param offset Set the current position to this position
0054  * @returns <tt>true</tt> on success, or <tt>false</tt> if an error
0055  * occurred. */
0056 cmsBool IOHandlerFactory::seek(cmsIOHANDLER *iohandler, cmsUInt32Number offset)
0057 {
0058     QFile *const myFile = static_cast<QFile *>(iohandler->stream);
0059     const bool seekSucceeded = myFile->seek(offset);
0060     if (!seekSucceeded) {
0061         qDebug() << QStringLiteral("Seek error; probably corrupted file");
0062         return false;
0063     }
0064     return true;
0065 }
0066 
0067 /** @internal
0068  *
0069  * @brief The position that data is written to or read from.
0070  * @param iohandler The <tt>cmsIOHANDLER</tt> on which to operate
0071  * @returns The position that data is written to or read from. */
0072 cmsUInt32Number IOHandlerFactory::tell(cmsIOHANDLER *iohandler)
0073 {
0074     const QFile *const myFile = static_cast<QFile *>(iohandler->stream);
0075     return static_cast<cmsUInt32Number>(myFile->pos());
0076 }
0077 
0078 /** @internal
0079  *
0080  * @brief Writes data to stream.
0081  *
0082  * Also keeps used space for further reference.
0083  *
0084  * @param iohandler The <tt>cmsIOHANDLER</tt> on which to operate
0085  * @param size The size of the buffer that should be written
0086  * @param Buffer The buffer that should be written
0087  * @returns Returns <tt>true</tt> on success, <tt>false</tt> on error.
0088  *
0089  * @note Because @ref IOHandlerFactory only provides support for read-only
0090  * handlers, this function does nothing and returns always <tt>false</tt>. */
0091 cmsBool IOHandlerFactory::write(cmsIOHANDLER *iohandler, cmsUInt32Number size, const void *Buffer)
0092 {
0093     Q_UNUSED(iohandler)
0094     Q_UNUSED(size)
0095     Q_UNUSED(Buffer)
0096     return false;
0097 }
0098 
0099 /** @internal
0100  *
0101  * @brief Closes the file and deletes the file handler.
0102  * @param iohandler The <tt>cmsIOHANDLER</tt> on which to operate
0103  * @returns <tt>true</tt> on success. */
0104 cmsBool IOHandlerFactory::close(cmsIOHANDLER *iohandler)
0105 {
0106     QFile *const myFile = static_cast<QFile *>(iohandler->stream);
0107     delete myFile; // This will also close the file.
0108     iohandler->stream = nullptr;
0109     _cmsFree(iohandler->ContextID, iohandler);
0110     return true;
0111 }
0112 
0113 /** @brief Create a read-only LittleCMS IO handler for a file.
0114  *
0115  * The handler has to be deleted with <tt>cmsCloseIOhandler</tt>
0116  * to free memory once it is not used anymore.
0117  *
0118  * @param ContextID Handle to user-defined context, or <tt>nullptr</tt> for
0119  * the global context
0120  * @param fileName Name of the file. See QFile::setFileName() for
0121  * the valid format. This format is portable, has standardized directory
0122  * separators and supports Unicode file names on all platforms.
0123  * @returns On success, a pointer to a new IO handler. On fail,
0124  * <tt>nullptr</tt>. The function might fail when the file does not
0125  * exist or cannot be opened for reading.
0126  *
0127  * @internal
0128  *
0129  * @note The type of the return value is not fully defined
0130  * in <tt>lcms2.h</tt> but only in <tt>lcms2_plugin.h</tt>. This is
0131  * the expected behaviour for an opaque handle. */
0132 cmsIOHANDLER *IOHandlerFactory::createReadOnly(cmsContext ContextID, const QString &fileName)
0133 {
0134     cmsIOHANDLER *const result = static_cast<cmsIOHANDLER *>( //
0135         _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER)) //
0136     );
0137     if (result == nullptr) {
0138         return nullptr;
0139     }
0140 
0141     QFile *const fileObject = new QFile(fileName);
0142     if (fileObject == nullptr) {
0143         return nullptr;
0144     }
0145 
0146     const bool openSucceeded = fileObject->open(QIODevice::ReadOnly);
0147     const qint64 fileSize = fileObject->size();
0148     // Check if the size is not negative (this might be an error indicator)
0149     // neither too big for LittleCMS’s data types:
0150     const bool isFileSizeOkay = PerceptualColor::isInRange<qint64>( //
0151         0,
0152         fileSize,
0153         std::numeric_limits<cmsInt32Number>::max());
0154     if ((!openSucceeded) || (!isFileSizeOkay)) {
0155         delete fileObject;
0156         _cmsFree(ContextID, result);
0157         return nullptr;
0158     }
0159 
0160     // Initialize data members
0161     result->ContextID = ContextID;
0162     // static_cast loses integer precision: 'qint64' to 'cmsInt32Number'.
0163     // This is okay because we have tested yet that the file is not that big.
0164     result->ReportedSize = static_cast<cmsUInt32Number>(fileSize);
0165     result->stream = static_cast<void *>(fileObject);
0166     result->UsedSpace = 0;
0167     result->PhysicalFile[0] = 0;
0168 
0169     // Initialize function pointers
0170     result->Read = read;
0171     result->Seek = seek;
0172     result->Close = close;
0173     result->Tell = tell;
0174     result->Write = write;
0175 
0176     return result;
0177 }
0178 
0179 } // namespace PerceptualColor