diff --git a/core/libs/dimg/dimg.h b/core/libs/dimg/dimg.h index 96139382ff..28be468b55 100644 --- a/core/libs/dimg/dimg.h +++ b/core/libs/dimg/dimg.h @@ -1,724 +1,726 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-06-14 * Description : digiKam 8/16 bits image management API * * Copyright (C) 2005 by Renchi Raju * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2006-2013 by Marcel Wiesweg * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_DIMG_H #define DIGIKAM_DIMG_H // Qt includes #include #include #include #include #include #include #include // Local includes #include "digikam_export.h" #include "drawdecoding.h" #include "dcolor.h" #include "dcolorcomposer.h" #include "historyimageid.h" #include "iccprofile.h" #include "metaengine_data.h" class QImage; class QPixmap; namespace Digikam { class ExposureSettingsContainer; class DImageHistory; class FilterAction; class IccTransform; class DImgLoaderObserver; class DIGIKAM_EXPORT DImg { public: enum FORMAT { // NOTE: Order is important here: // See filesaveoptionbox.cpp which use these values to fill a stack of widgets. NONE = 0, JPEG, PNG, TIFF, JP2K, PGF, HEIF, // Others file formats. RAW, QIMAGE // QImage or ImageMagick }; enum ANGLE { ROT90 = 0, ROT180, ROT270, ROTNONE }; enum FLIP { HORIZONTAL = 0, VERTICAL }; enum COLORMODEL { COLORMODELUNKNOWN = 0, RGB, GRAYSCALE, MONOCHROME, INDEXED, YCBCR, CMYK, CIELAB, COLORMODELRAW }; public: /** Identify file format */ static FORMAT fileFormat(const QString& filePath); static QString formatToMimeType(FORMAT frm); public: class Private; public: /** Create null image */ DImg(); /** Load image using QByteArray as file path */ explicit DImg(const QByteArray& filePath, DImgLoaderObserver* const observer = nullptr, const DRawDecoding& rawDecodingSettings = DRawDecoding()); /** Load image using QString as file path */ explicit DImg(const QString& filePath, DImgLoaderObserver* const observer = nullptr, const DRawDecoding& rawDecodingSettings = DRawDecoding()); /** Copy image: Creates a shallow copy that refers to the same shared data. The two images will be equal. Call detach() or copy() to create deep copies. */ DImg(const DImg& image); /** Copy image: Creates a copy of a QImage object. If the QImage is null, a null DImg will be created. */ explicit DImg(const QImage& image); /** Create image from data. If data is 0, a new buffer will be allocated, otherwise the given data will be used: If copydata is true, the data will be copied to a newly allocated buffer. If copyData is false, this DImg object will take ownership of the data pointer. If there is an alpha channel, the data shall be in non-premultiplied form (unassociated alpha). */ DImg(uint width, uint height, bool sixteenBit, bool alpha = false, uchar* const data = nullptr, bool copyData = true); ~DImg(); /** Equivalent to the copy constructor */ DImg& operator=(const DImg& image); /** Detaches from shared data and makes sure that this image is the only one referring to the data. If multiple images share common data, this image makes a copy of the data and detaches itself from the sharing mechanism. Nothing is done if there is just a single reference. */ void detach(); /** Returns whether two images are equal. Two images are equal if and only if they refer to the same shared data. (Thus, DImg() == DImg() is not true, both instances refer two their own shared data. image == DImg(image) is true.) If two or more images refer to the same data, they have the same image data, bits() returns the same data, they have the same metadata, and a change to one image also affects the others. Call detach() to split one image from the group of equal images. */ bool operator==(const DImg& image) const; /** Replaces image data of this object. Metadata is unchanged. Parameters like constructor above. */ void putImageData(uint width, uint height, bool sixteenBit, bool alpha, uchar* const data, bool copyData = true); /** Overloaded function, provided for convenience, behaves essentially like the function above if data is not 0. Uses current width, height, sixteenBit, and alpha values. If data is 0, the current data is deleted and the image is set to null (But metadata unchanged). */ void putImageData(uchar* const data, bool copyData = true); /** Reset metadata and image data to null image */ void reset(); /** Reset metadata, but do not change image data */ void resetMetaData(); /** Returns the data of this image. Ownership of the buffer is passed to the caller, this image will be null afterwards. */ uchar* stripImageData(); bool load(const QString& filePath, DImgLoaderObserver* const observer = nullptr, const DRawDecoding& rawDecodingSettings = DRawDecoding()); bool load(const QString& filePath, bool loadMetadata, bool loadICCData, bool loadUniqueHash, bool loadHistory, DImgLoaderObserver* const observer = nullptr, const DRawDecoding& rawDecodingSettings = DRawDecoding()); bool load(const QString& filePath, int loadFlags, DImgLoaderObserver* const observer, const DRawDecoding& rawDecodingSettings = DRawDecoding()); bool save(const QString& filePath, FORMAT frm, DImgLoaderObserver* const observer = nullptr); bool save(const QString& filePath, const QString& format, DImgLoaderObserver* const observer = nullptr); /** * It is common that images are not directly saved to the destination path. * For this reason, save() does not call addAsReferredImage(), and the stored * save path may be wrong. * Call this method after save() with the final destination path. * This path will be stored in the image history as well. */ void imageSavedAs(const QString& savePath); /** Loads most parts of the meta information, but never the image data. If loadMetadata is true, the metadata will be available with getComments, getExif, getIptc, getXmp . If loadICCData is true, the ICC profile will be available with getICCProfile. */ bool loadItemInfo(const QString& filePath, bool loadMetadata = true, bool loadICCData = true, bool loadUniqueHash = true, bool loadImageHistory = true); bool isNull() const; uint width() const; uint height() const; QSize size() const; uchar* copyBits() const; uchar* bits() const; uchar* scanLine(uint i) const; bool hasAlpha() const; bool sixteenBit() const; uint numBytes() const; uint numPixels() const; /** Return the number of bytes depth of one pixel : 4 (non sixteenBit) or 8 (sixteen) */ int bytesDepth() const; /** Return the number of bits depth of one color component for one pixel : 8 (non sixteenBit) or 16 (sixteen) */ int bitsDepth() const; /** Returns the file path from which this DImg was originally loaded. * Returns a null string if the DImg was not loaded from a file. */ QString originalFilePath() const; /** Returns the file path to which this DImg was saved. * Returns the file path set with imageSavedAs(), if that was not called, * save(), if that was not called, a null string. */ QString lastSavedFilePath() const; /** Returns the color model in which the image was stored in the file. The color space of the loaded image data is always RGB. */ COLORMODEL originalColorModel() const; /** Returns the bit depth (in bits per channel, e.g. 8 or 16) of the original file. */ int originalBitDepth() const; /** Returns the size of the original file. */ QSize originalSize() const; /** Returns the file format in form of the FORMAT enum that was detected in the load() method. Other than the format attribute which is written by the DImgLoader, this can include the QIMAGE or NONE values. Returns NONE for images that have not been loaded. For unknown image formats, a value of QIMAGE can be returned to indicate that the QImage-based loader will have been used. To find out if this has worked, check the return value you got from load(). */ FORMAT detectedFormat() const; /** Returns the format string as written by the image loader this image was originally loaded from. Format strings used include JPEG, PNG, TIFF, PGF, JP2K, RAW, PPM. For images loaded with the platform QImage loader, the file suffix is used. Returns null if this DImg was not loaded from a file, but created in memory. */ QString format() const; /** Returns the format string of the format that this image was last saved to. An image can be loaded from a file - retrieve that format with fileFormat() and loadedFormat() - and can the multiple times be saved to different formats. Format strings used include JPG, PGF, PNG, TIFF and JP2K. If this file was not save, a null string is returned. */ QString savedFormat() const; /** Returns the DRawDecoding options that this DImg was loaded with. * If this is not a RAW image or no options were specified, returns DRawDecoding(). */ DRawDecoding rawDecodingSettings() const; /** Access a single pixel of the image. These functions add some safety checks and then use the methods from DColor. In optimized code working directly on the data, better use the inline methods from DColor. */ DColor getPixelColor(uint x, uint y) const; void setPixelColor(uint x, uint y, const DColor& color); void prepareSubPixelAccess(); DColor getSubPixelColor(float x, float y) const; DColor getSubPixelColorFast(float x, float y) const; /** If the image has an alpha channel, check if there exist pixels * which actually have non-opaque color, that is alpha < 1.0. * Note that all pixels are scanned to reach a return value of "false". * If hasAlpha() is false, always returns false. */ bool hasTransparentPixels() const; /** Return true if the original image file format cannot be saved. This is depending of DImgLoader::save() implementation. For example RAW file formats are supported by DImg using dcraw than cannot support writing operations. */ bool isReadOnly() const; /** Metadata manipulation methods */ MetaEngineData getMetadata() const; IccProfile getIccProfile() const; void setMetadata(const MetaEngineData& data); void setIccProfile(const IccProfile& profile); void setAttribute(const QString& key, const QVariant& value); QVariant attribute(const QString& key) const; bool hasAttribute(const QString& key) const; void removeAttribute(const QString& key); void setEmbeddedText(const QString& key, const QString& text); QString embeddedText(const QString& key) const; const DImageHistory& getItemHistory() const; DImageHistory& getItemHistory(); void setItemHistory(const DImageHistory& history); bool hasImageHistory() const; DImageHistory getOriginalImageHistory() const; void addFilterAction(const FilterAction& action); /** Sets a step in the history to constitute the beginning of a branch. * Use setHistoryBranch() to take getOriginalImageHistory() and set the first added step as a branch. * Use setHistoryBranchForLastSteps(n) to start the branch before the last n steps in the history. * (Assume the history had 3 steps and you added 2, call setHistoryBranchForLastSteps(2)) * Use setHistoryBranchAfter() if have a copy of the history before branching, * the first added step on top of that history will be made a branch. */ void setHistoryBranchAfter(const DImageHistory& historyBeforeBranch, bool isBranch = true); void setHistoryBranchForLastSteps(int numberOfLastHistorySteps, bool isBranch = true); void setHistoryBranch(bool isBranch = true); /** * When saving, several changes to the image metadata are necessary * before it can safely be written to the new file. * This method updates the stored DMetadata object in preparation to a subsequent * call to save() with the same target file. * 'intendedDestPath' is the finally intended file name. Do not give the temporary * file name if you are going to save() to a temp file. * 'destMimeType' is destination type mime. In some cases, metadata is updated depending on this value. * 'originalFileName' is the original file's name, for simplistic history tracking in metadata. * This is completely independent from the DImageHistory framework. * For the 'flags' see below. * Not all steps are optional and can be controlled with flags. */ enum PrepareMetadataFlag { /// A small preview can be stored in the metadata. /// Remove old preview entries RemoveOldMetadataPreviews = 1 << 0, /// Create a new preview from current image data. CreateNewMetadataPreview = 1 << 1, /// Set the exif orientation tag to "normal" /// Applicable if the image data was rotated according to the tag ResetExifOrientationTag = 1 << 2, /// Creates a new UUID for the image history. /// Applicable if the file was changed. CreateNewImageHistoryUUID = 1 << 3, PrepareMetadataFlagsAll = RemoveOldMetadataPreviews | CreateNewMetadataPreview | ResetExifOrientationTag | CreateNewImageHistoryUUID }; Q_DECLARE_FLAGS(PrepareMetadataFlags, PrepareMetadataFlag) void prepareMetadataToSave(const QString& intendedDestPath, const QString& destMimeType, const QString& originalFileName = QString(), PrepareMetadataFlags flags = PrepareMetadataFlagsAll); /** For convenience: Including all flags, except for ResetExifOrientationTag which can be selected. * Uses originalFilePath() to fill the original file name. */ void prepareMetadataToSave(const QString& intendedDestPath, const QString& destMimeType, bool resetExifOrientationTag); /** Create a HistoryImageId for _this_ image _already_ saved at the given file path.*/ HistoryImageId createHistoryImageId(const QString& filePath, HistoryImageId::Type type) const; /** If you have saved this DImg to filePath, and want to continue using this DImg object * to add further changes to the image history, you can call this method to add to the image history * a reference to the just saved image. * First call updateMetadata(), then call save(), then call addAsReferredImage(). * Do not call this directly after loading, before applying any changes: * The history is correctly initialized when loading. * If you need to insert the referred file to an entry which is not the last entry, * which may happen if the added image was saved after this image's history was created, * you can use insertAsReferredImage. * The added id is returned. */ HistoryImageId addAsReferredImage(const QString& filePath, HistoryImageId::Type type = HistoryImageId::Intermediate); void addAsReferredImage(const HistoryImageId& id); void insertAsReferredImage(int afterHistoryStep, const HistoryImageId& otherImagesId); /** In the history, adjusts the UUID of the ImageHistoryId of the current file. * Call this if you have associated a UUID with this file which is not written to the metadata. * If there is already a UUID present, read from metadata, it will not be replaced. */ void addCurrentUniqueImageId(const QString& uuid); /** * Retrieves the Exif orientation, either from the LoadSaveThread info provider if available, * or from the metadata */ int exifOrientation(const QString& filePath); /** When loaded from a file, some attributes like format and isReadOnly still depend on this originating file. When saving in a different format to a different file, you may wish to switch these attributes to the new file. - fileOriginData() returns the current origin data, bundled in the returned QVariant. - setFileOriginData() takes such a variant and adjusts the properties - lastSavedFileOriginData() returns the origin data as if the image was loaded from the last saved image. - switchOriginToLastSaved is equivalent to setting origin data returned from lastSavedFileOriginData() Example: an image loaded from a RAW and saved to PNG will be read-only and format RAW. After calling switchOriginToLastSaved, it will not be read-only, format will be PNG, and rawDecodingSettings will be null. detectedFormat() will not change. In the history, the last referred image that was added (as intermediate) is made the new Current image. NOTE: Set the saved image path with imageSavedAs() before! */ QVariant fileOriginData() const; void setFileOriginData(const QVariant& data); QVariant lastSavedFileOriginData() const; void switchOriginToLastSaved(); /** Return a deep copy of full image */ DImg copy() const; /** Return a deep copy of the image, but do not include metadata. */ DImg copyImageData() const; /** Return an image that contains a deep copy of this image's metadata and the information associated with the image data (width, height, hasAlpha, sixteenBit), but no image data, i.e. isNull() is true. */ DImg copyMetaData() const; /** Return a region of image */ DImg copy(const QRect& rect) const; DImg copy(const QRectF& relativeRect) const; DImg copy(int x, int y, int w, int h) const; /** Copy a region of pixels from a source image to this image. Parameters: sx|sy Coordinates in the source image of the rectangle to be copied w h Width and height of the rectangle (Default, or when both are -1: whole source image) dx|dy Coordinates in this image of the rectangle in which the region will be copied (Default: 0|0) The bit depth of source and destination must be identical. */ void bitBltImage(const DImg* const src, int dx, int dy); void bitBltImage(const DImg* const src, int sx, int sy, int dx, int dy); void bitBltImage(const DImg* const src, int sx, int sy, int w, int h, int dx, int dy); void bitBltImage(const uchar* const src, int sx, int sy, int w, int h, int dx, int dy, uint swidth, uint sheight, int sdepth); /** Blend src image on this image (this is dest) with the specified composer and multiplication flags. See documentation of DColorComposer for more info. For the other arguments, see documentation of bitBltImage above. */ void bitBlendImage(DColorComposer* const composer, const DImg* const src, int sx, int sy, int w, int h, int dx, int dy, DColorComposer::MultiplicationFlags multiplicationFlags = DColorComposer::NoMultiplication); /** For the specified region, blend this image on the given color with the specified composer and multiplication flags. See documentation of DColorComposer for more info. Note that the result pixel is again written to this image, which is, for the blending, source. */ void bitBlendImageOnColor(DColorComposer* const composer, const DColor& color, int x, int y, int w, int h, DColorComposer::MultiplicationFlags multiplicationFlags = DColorComposer::NoMultiplication); void bitBlendImageOnColor(const DColor& color, int x, int y, int w, int h); void bitBlendImageOnColor(const DColor& color); /** QImage wrapper methods */ QImage copyQImage() const; QImage copyQImage(const QRect& rect) const; QImage copyQImage(const QRectF& relativeRect) const; QImage copyQImage(int x, int y, int w, int h) const; /** Crop image to the specified region */ void crop(const QRect& rect); void crop(int x, int y, int w, int h); /** Set width and height of this image, smoothScale it to the given size */ void resize(int w, int h); /** * If the image has an alpha channel and transparent pixels, * it will be blended on the specified color and the alpha channel will be removed. * This is a no-op if hasTransparentPixels() is false, but this method can be expensive, * therefore it is _not_ checked inside removeAlphaChannel(). * (the trivial hasAlpha() is checked) */ void removeAlphaChannel(const DColor& destColor); void removeAlphaChannel(); /** Return a version of this image scaled to the specified size with the specified mode. See QSize documentation for information on available modes */ DImg smoothScale(int width, int height, Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio) const; DImg smoothScale(const QSize& destSize, Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio) const; /** Executes the same scaling as smoothScale(width, height), but from the result of this call, * returns only the section specified by clipx, clipy, clipwidth, clipheight. * This is thus equivalent to calling * Dimg scaled = smoothScale(width, height); scaled.crop(clipx, clipy, clipwidth, clipheight); * but potentially much faster. * In smoothScaleSection, you specify the source region, here, the result region. * It will often not be possible to find _integer_ source coordinates for a result region! */ DImg smoothScaleClipped(int width, int height, int clipx, int clipy, int clipwidth, int clipheight) const; DImg smoothScaleClipped(const QSize& destSize, const QRect& clip) const; /** Take the region specified by the rectangle sx|sy, width and height sw * sh, and scale it to an image with size dw * dh */ DImg smoothScaleSection(int sx, int sy, int sw, int sh, int dw, int dh) const; DImg smoothScaleSection(const QRect& sourceRect, const QSize& destSize) const; void rotate(ANGLE angle); void flip(FLIP direction); /** Rotates and/or flip the DImg according to the given DMetadata::Orientation, * so that the current state is orientation and the resulting step is normal orientation. * Returns true if the image was actually rotated or flipped (e.g. if ORIENTATION_NORMAL * is given, returns false, because no action is taken). */ bool rotateAndFlip(int orientation); /** Reverses the previous function. */ bool reverseRotateAndFlip(int orientation); /** * Utility to make sure that an image is rotated according to Exif tag. * Detects if an image has previously already been rotated: You can * call this method more than one time on the same image. * Returns true if the image has actually been rotated or flipped. * Returns false if a rotation was not needed. */ bool wasExifRotated(); bool exifRotate(const QString& filePath); /** * Reverses the previous function */ bool reverseExifRotate(const QString& filePath); /** Rotates and/or flip the DImg according to the given transform action, * which is a MetaEngineRotation::TransformAction. * Returns true if the image was actually rotated or flipped. */ bool transform(int transformAction); QPixmap convertToPixmap() const; QPixmap convertToPixmap(IccTransform& monitorICCtrans) const; /** Return a mask image where pure white and pure black pixels are over-colored. This way is used to identify over and under exposed pixels. */ QImage pureColorMask(ExposureSettingsContainer* const expoSettings) const; /** Convert depth of image. Depth is bytesDepth * bitsDepth. If depth is 32, converts to 8 bits, if depth is 64, converts to 16 bits. */ void convertDepth(int depth); /** Wrapper methods for convertDepth */ void convertToSixteenBit(); void convertToEightBit(); void convertToDepthOfImage(const DImg* const otherImage); /** Fill whole image with specified color. The bit depth of the color must be identical to the depth of this image. */ void fill(const DColor& color); /** This methods return a 128-bit MD5 hex digest which is meant to uniquely identify the file. The hash is calculated on parts of the file and the file metadata. It cannot be used to find similar images. It is not calculated from the image data. The hash will be returned as a 32-byte hexadecimal string. If you already have a DImg object of the file, use the member method. The object does not need to have the full image data loaded, but it shall at least have been loaded with loadItemInfo with loadMetadata = true, or have the metadata set later with setComments, setExif, setIptc, setXmp. If the object does not have the metadata loaded, a non-null, but invalid hash will be returned! In this case, use the static method. If the image has been loaded with loadUniqueHash = true, the hash can be retrieved with the member method. You do not need a DImg object of the file to retrieve the unique hash; Use the static method and pass just the file path. */ QByteArray getUniqueHash() const; static QByteArray getUniqueHash(const QString& filePath); /** This methods return a 128-bit MD5 hex digest which is meant to uniquely identify the file. The hash is calculated on parts of the file. It cannot be used to find similar images. It is not calculated from the image data. The hash will be returned as a 32-byte hexadecimal string. If you already have a DImg object loaded from the file, use the member method. If the image has been loaded with loadUniqueHash = true, the hash will already be available. You do not need a DImg object of the file to retrieve the unique hash; Use the static method and pass just the file path. */ QByteArray getUniqueHashV2() const; static QByteArray getUniqueHashV2(const QString& filePath); /** This method creates a new 256-bit UUID meant to be globally unique. * The UUID will be returned as a 64-byte hexadecimal string. * At least 128bits of the UUID will be created by the platform random number * generator. The rest may be created from a content-based hash similar to the uniqueHash, see above. * This method only generates a new UUID for this image without in any way changing this image object * or saving the UUID anywhere. */ QByteArray createImageUniqueId() const; /** * Helper method to translate enum values to user presentable strings */ static QString colorModelToString(COLORMODEL colorModel); /** Return true if image file is an animation, as GIFa or NMG */ static bool isAnimatedImage(const QString& filePath); private: DImg(const DImg& image, int w, int h); void copyMetaData(const QExplicitlySharedDataPointer& src); void copyImageData(const QExplicitlySharedDataPointer& src); void setImageData(bool null, uint width, uint height, bool sixteenBit, bool alpha); void setImageDimension(uint width, uint height); size_t allocateData(); + bool clipped(int& x, int& y, int& w, int& h, uint width, uint height) const; + QDateTime creationDateFromFilesystem(const QFileInfo& fileInfo) const; static QByteArray createUniqueHash(const QString& filePath, const QByteArray& ba); static QByteArray createUniqueHashV2(const QString& filePath); - static void bitBlt(const uchar* const src, uchar* const dest, - int sx, int sy, int w, int h, int dx, int dy, - uint swidth, uint sheight, uint dwidth, uint dheight, - bool sixteenBit, int sdepth, int ddepth); - static void bitBlend(DColorComposer* const composer, uchar* const src, uchar* const dest, - int sx, int sy, int w, int h, int dx, int dy, - uint swidth, uint sheight, uint dwidth, uint dheight, - bool sixteenBit, int sdepth, int ddepth, + void bitBlt(const uchar* const src, uchar* const dest, + int sx, int sy, int w, int h, int dx, int dy, + uint swidth, uint sheight, uint dwidth, uint dheight, + bool sixteenBit, int sdepth, int ddepth); + void bitBlend(DColorComposer* const composer, uchar* const src, uchar* const dest, + int sx, int sy, int w, int h, int dx, int dy, + uint swidth, uint sheight, uint dwidth, uint dheight, + bool sixteenBit, int sdepth, int ddepth, + DColorComposer::MultiplicationFlags multiplicationFlags); + void bitBlendOnColor(DColorComposer* const composer, const DColor& color, + uchar* data, int x, int y, int w, int h, + uint width, uint height, bool sixteenBit, int depth, DColorComposer::MultiplicationFlags multiplicationFlags); - static void bitBlendOnColor(DColorComposer* const composer, const DColor& color, - uchar* data, int x, int y, int w, int h, - uint width, uint height, bool sixteenBit, int depth, - DColorComposer::MultiplicationFlags multiplicationFlags); - static bool normalizeRegionArguments(int& sx, int& sy, int& w, int& h, int& dx, int& dy, - uint swidth, uint sheight, uint dwidth, uint dheight); + bool normalizeRegionArguments(int& sx, int& sy, int& w, int& h, int& dx, int& dy, + uint swidth, uint sheight, uint dwidth, uint dheight); private: QExplicitlySharedDataPointer m_priv; friend class DImgLoader; }; } // namespace Digikam Q_DECLARE_OPERATORS_FOR_FLAGS(Digikam::DImg::PrepareMetadataFlags) #endif // DIGIKAM_DIMG_H diff --git a/core/libs/dimg/dimg_copy.cpp b/core/libs/dimg/dimg_copy.cpp index 936e4b7d2d..396bdb0b3f 100644 --- a/core/libs/dimg/dimg_copy.cpp +++ b/core/libs/dimg/dimg_copy.cpp @@ -1,102 +1,126 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-06-14 * Description : digiKam 8/16 bits image management API. * Copying operations. * * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2006-2013 by Marcel Wiesweg * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "dimg_p.h" namespace Digikam { DImg DImg::copy() const { DImg img(*this); img.detach(); return img; } DImg DImg::copyImageData() const { DImg img(width(), height(), sixteenBit(), hasAlpha(), bits(), true); return img; } DImg DImg::copyMetaData() const { DImg img; // copy width, height, alpha, sixteenBit, null img.copyImageData(m_priv); // deeply copy metadata img.copyMetaData(m_priv); // set image to null img.m_priv->null = true; return img; } DImg DImg::copy(const QRect& rect) const { return copy(rect.x(), rect.y(), rect.width(), rect.height()); } DImg DImg::copy(const QRectF& rel) const { if (isNull() || !rel.isValid()) { return DImg(); } return copy(QRectF(rel.x() * m_priv->width, rel.y() * m_priv->height, rel.width() * m_priv->width, rel.height() * m_priv->height) .toRect()); } DImg DImg::copy(int x, int y, int w, int h) const { if (isNull() || w <= 0 || h <= 0) { qCDebug(DIGIKAM_DIMG_LOG) << " : return null image! (" << isNull() << ", " << w << ", " << h << ")"; return DImg(); } - if (!Private::clipped(x, y, w, h, m_priv->width, m_priv->height)) + if (clipped(x, y, w, h, m_priv->width, m_priv->height)) { return DImg(); } DImg image(*this, w, h); image.bitBltImage(this, x, y, w, h, 0, 0); return image; } +/** +* x,y, w x h is a section of the image. The image size is width x height. +* Clips the section to the bounds of the image. +* Returns if the (clipped) section is a valid rectangle. +*/ +bool DImg::clipped(int& x, int& y, int& w, int& h, uint width, uint height) const +{ + QRect inner(x, y, w, h); + QRect outer(0, 0, width, height); + + if (!outer.contains(inner)) + { + QRect pt = inner.intersected(outer); + x = pt.x(); + y = pt.y(); + w = pt.width(); + h = pt.height(); + + return pt.isValid(); + } + + return inner.isValid(); +} + } // namespace Digikam diff --git a/core/libs/dimg/dimg_p.h b/core/libs/dimg/dimg_p.h index 7a28d84444..5cc2dea4f1 100644 --- a/core/libs/dimg/dimg_p.h +++ b/core/libs/dimg/dimg_p.h @@ -1,295 +1,271 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-06-15 * Description : digiKam 8/16 bits image management API. * Private data container. * * Copyright (C) 2005 by Renchi Raju * Copyright (C) 2005-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_DIMG_PRIVATE_H #define DIGIKAM_DIMG_PRIVATE_H #include "digikam_config.h" // C ANSI includes #ifndef Q_OS_WIN extern "C" { #endif #include #ifndef Q_OS_WIN #include } #endif // C++ includes #include // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include // Local includes #include "dimg.h" #include "dplugindimg.h" #include "digikam_export.h" #include "digikam_debug.h" #include "dmetadata.h" #include "dimagehistory.h" #include "iccprofile.h" #include "metaengine_rotation.h" #include "drawdecoder.h" #include "loadsavethread.h" #include "filereadwritelock.h" #include "iccmanager.h" #include "icctransform.h" #include "exposurecontainer.h" #include "dimgloaderobserver.h" #include "randomnumbergenerator.h" /** * Lanczos kernel is precomputed in a table with this resolution * The value below seems to be enough for HQ upscaling up to eight times */ #define LANCZOS_TABLE_RES 256 /** * A support of 3 gives an overall sharper looking image, but * it is a) slower b) gives more sharpening artifacts */ #define LANCZOS_SUPPORT 2 /** * Define this to use a floating-point implementation of Lanczos interpolation. * The integer implementation is a little bit less accurate, but MUCH faster * (even on machines with FPU - ~2.5 times faster on Core2); besides, it will * run a hell lot faster on computers without a FPU (e.g. PDAs). */ //#define LANCZOS_DATA_FLOAT #ifdef LANCZOS_DATA_FLOAT # define LANCZOS_DATA_TYPE float # define LANCZOS_DATA_ONE 1.0 #else # define LANCZOS_DATA_TYPE int # define LANCZOS_DATA_ONE 4096 #endif typedef uint64_t ullong; // krazy:exclude=typedefs typedef int64_t llong; // krazy:exclude=typedefs namespace Digikam { class DIGIKAM_EXPORT DImg::Private : public QSharedData { public: explicit Private() { null = true; width = 0; height = 0; data = nullptr; lanczos_func = nullptr; alpha = false; sixteenBit = false; } ~Private() { delete [] data; delete [] lanczos_func; } static QList pluginsForFile(const QFileInfo& fileInfo, bool magic) { QMap pluginMap; foreach (DPlugin* const p, DPluginLoader::instance()->allPlugins()) { int prio; DPluginDImg* const plug = dynamic_cast(p); if (plug && ((prio = plug->canRead(fileInfo, magic)) > 0)) { /* qCDebug(DIGIKAM_DIMG_LOG) << "File path:" << filePath << "Priority:" << prio << "Loader:" << plug->loaderName(); */ pluginMap.insertMulti(prio, plug); } } return pluginMap.values(); } static DPluginDImg* pluginForFormat(const QString& format) { QMap pluginMap; if (!format.isNull()) { foreach (DPlugin* const p, DPluginLoader::instance()->allPlugins()) { int prio; DPluginDImg* const plug = dynamic_cast(p); if (plug && ((prio = plug->canWrite(format)) > 0)) { pluginMap.insertMulti(prio, plug); } } } if (!pluginMap.isEmpty()) { return pluginMap.first(); } return nullptr; } static DImg::FORMAT loaderNameToFormat(const QString& name) { if (name.isNull()) { return DImg::NONE; } else if (name == QLatin1String("JPEG")) { return DImg::JPEG; } else if (name == QLatin1String("PNG")) { return DImg::PNG; } else if (name == QLatin1String("TIFF")) { return DImg::TIFF; } else if (name == QLatin1String("RAW")) { return DImg::RAW; } else if (name == QLatin1String("JPEG2000")) { return DImg::JP2K; } else if (name == QLatin1String("PGF")) { return DImg::PGF; } else if (name == QLatin1String("HEIF")) { return DImg::HEIF; } // In others cases, ImageMagick or QImage will be used to try to open file. return DImg::QIMAGE; } static QStringList fileOriginAttributes() { QStringList list; list << QLatin1String("format") << QLatin1String("isReadOnly") << QLatin1String("originalFilePath") << QLatin1String("originalSize") << QLatin1String("originalImageHistory") << QLatin1String("rawDecodingSettings") << QLatin1String("rawDecodingFilterAction") << QLatin1String("uniqueHash") << QLatin1String("uniqueHashV2"); return list; } - /** - * x,y, w x h is a section of the image. The image size is width x height. - * Clips the section to the bounds of the image. - * Returns if the (clipped) section is a valid rectangle. - */ - static bool clipped(int& x, int& y, int& w, int& h, uint width, uint height) - { - QRect inner(x, y, w, h); - QRect outer(0, 0, width, height); - - if (!outer.contains(inner)) - { - QRect pt = inner.intersected(outer); - x = pt.x(); - y = pt.y(); - w = pt.width(); - h = pt.height(); - - return pt.isValid(); - } - - return inner.isValid(); - } - public: bool null; bool alpha; bool sixteenBit; unsigned int width; unsigned int height; unsigned char* data; LANCZOS_DATA_TYPE* lanczos_func; QMutex mutex; MetaEngineData metaData; QMap attributes; QMap embeddedText; IccProfile iccProfile; DImageHistory imageHistory; }; } // namespace Digikam #endif // DIGIKAM_DIMG_PRIVATE_H diff --git a/core/libs/dimg/dimg_scale.cpp b/core/libs/dimg/dimg_scale.cpp index f7012a3dce..05253eb2b3 100644 --- a/core/libs/dimg/dimg_scale.cpp +++ b/core/libs/dimg/dimg_scale.cpp @@ -1,2465 +1,2465 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-06-14 * Description : digiKam 8/16 bits image management API. * Description : Smoothscale method based on Imlib2's implementations. * https://git.enlightenment.org/legacy/imlib2.git/tree/src/lib/scale.c * Ported to C++ and QImage * Add smoothScaleSection to scale only an image section. * Add 16 bits color depth image support. * * Copyright (C) 1999-2004 by Carsten Haitzler * Copyright (C) 2004 by Willem Monsuwe * Copyright (C) 2005 by Renchi Raju * Copyright (C) 2005 by Daniel M. Duley * Copyright (C) 2006-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ // C ANSI includes extern "C" { #include } // C++ includes #include #include #include // Local includes #include "digikam_debug.h" #include "dimg.h" #include "dimg_p.h" typedef uint64_t ullong; // krazy:exclude=typedefs typedef int64_t llong; // krazy:exclude=typedefs namespace Digikam { namespace DImgScale { class Q_DECL_HIDDEN DImgScaleInfo { public: DImgScaleInfo() { xpoints = nullptr; ypoints = nullptr; ypoints16 = nullptr; xapoints = nullptr; yapoints = nullptr; xup_yup = 0; } ~DImgScaleInfo() { delete [] xpoints; delete [] ypoints; delete [] ypoints16; delete [] xapoints; delete [] yapoints; } int* xpoints; uint** ypoints; ullong** ypoints16; int* xapoints; int* yapoints; int xup_yup; }; uint** dimgCalcYPoints(uint* const src, int sw, int sh, int dh); ullong** dimgCalcYPoints16(ullong* const src, int sw, int sh, int dh); int* dimgCalcXPoints(int sw, int dw); int* dimgCalcApoints(int s, int d, int up); DImgScaleInfo* dimgCalcScaleInfo(const DImg& img, int sw, int sh, int dw, int dh, bool sixteenBit, bool aa); // 8 bit, not smoothed void dimgSampleRGBA(DImgScaleInfo* const isi, uint* const dest, int dxx, int dyy, int dw, int dh, int dow); void dimgSampleRGBA(DImgScaleInfo* const isi, uint* const dest, int dxx, int dyy, int dw, int dh, int dow, int clip_dx, int clip_dy, int clip_dw, int clip_dh); // 16 bit, not smoothed void dimgSampleRGBA16(DImgScaleInfo* const isi, ullong* const dest, int dxx, int dyy, int dw, int dh, int dow); void dimgSampleRGBA16(DImgScaleInfo* const isi, ullong* const dest, int dxx, int dyy, int dw, int dh, int dow, int clip_dx, int clip_dy, int clip_dw, int clip_dh); // 8 bit, RGBA void dimgScaleAARGBA(DImgScaleInfo* const isi, uint* const dest, int dxx, int dyy, int dw, int dh, int dow, int sow); void dimgScaleAARGBA(DImgScaleInfo* const isi, uint* const dest, int dxx, int dyy, int dw, int dh, int dow, int sow, int clip_dx, int clip_dy, int clip_dw, int clip_dh); // 8 bit, RGB void dimgScaleAARGB(DImgScaleInfo* const isi, uint* const dest, int dxx, int dyy, int dw, int dh, int dow, int sow); void dimgScaleAARGB(DImgScaleInfo* const isi, uint* const dest, int dxx, int dyy, int dw, int dh, int dow, int sow, int clip_dx, int clip_dy, int clip_dw, int clip_dh); // 16 bit, RGBA void dimgScaleAARGBA16(DImgScaleInfo* const isi, ullong* const dest, int dxx, int dyy, int dw, int dh, int dow, int sow); void dimgScaleAARGBA16(DImgScaleInfo* const isi, ullong* const dest, int dxx, int dyy, int dw, int dh, int dow, int sow, int clip_dx, int clip_dy, int clip_dw, int clip_dh); // 16 bit, RGB void dimgScaleAARGB16(DImgScaleInfo* const isi, ullong* const dest, int dxx, int dyy, int dw, int dh, int dow, int sow); void dimgScaleAARGB16(DImgScaleInfo* const isi, ullong* const dest, int dxx, int dyy, int dw, int dh, int dow, int sow, int clip_dx, int clip_dy, int clip_dw, int clip_dh); } using namespace DImgScale; /* #define CLIP(x, y, w, h, xx, yy, ww, hh) \ if (x < (xx)) {w += (x - (xx)); x = (xx);} \ if (y < (yy)) {h += (y - (yy)); y = (yy);} \ if ((x + w) > ((xx) + (ww))) {w = (ww) - (x - xx);} \ if ((y + h) > ((yy) + (hh))) {h = (hh) - (y - yy);} */ DImg DImg::smoothScale(const QSize& destSize, Qt::AspectRatioMode aspectRatioMode) const { QSize scaleSize = size(); scaleSize.scale(destSize, aspectRatioMode); if (scaleSize.isEmpty()) { return DImg(); } return smoothScaleClipped(scaleSize, QRect(QPoint(0, 0), scaleSize)); } DImg DImg::smoothScale(int dw, int dh, Qt::AspectRatioMode aspectRatioMode) const { return smoothScale(QSize(dw, dh), aspectRatioMode); } DImg DImg::smoothScaleClipped(const QSize& destSize, const QRect& clip) const { return DImg::smoothScaleClipped(destSize.width(), destSize.height(), clip.x(), clip.y(), clip.width(), clip.height()); } DImg DImg::smoothScaleClipped(int dw, int dh, int clipx, int clipy, int clipw, int cliph) const { if (dw <= 0 || dh <= 0 || clipw <= 0 || cliph <= 0 || isNull()) { return DImg(); } uint w = width(); uint h = height(); if (w <= 0 || h <= 0) { return DImg(); } // ensure clip is valid - if (!Private::clipped(clipx, clipy, clipw, cliph, dw, dh)) + if (!clipped(clipx, clipy, clipw, cliph, dw, dh)) { return DImg(); } // do we actually need to scale? if ((w == (uint)dw) && (h == (uint)dh)) { if (clipw == dw && cliph == dh) { return copy(); } else { return copy(clipx, clipy, clipw, cliph); } } DImgScaleInfo* scaleinfo = dimgCalcScaleInfo(*this, w, h, dw, dh, sixteenBit(), true); DImg buffer(*this, clipw, cliph); if (sixteenBit()) { if (hasAlpha()) { dimgScaleAARGBA16(scaleinfo, reinterpret_cast(buffer.bits()), 0, 0, dw, dh, clipw, w, clipx, clipy, clipw, cliph); } else { dimgScaleAARGB16(scaleinfo, reinterpret_cast(buffer.bits()), 0, 0, dw, dh, clipw, w, clipx, clipy, clipw, cliph); } } else { if (hasAlpha()) { dimgScaleAARGBA(scaleinfo, reinterpret_cast(buffer.bits()), 0, 0, dw, dh, clipw, w, clipx, clipy, clipw, cliph); } else { dimgScaleAARGB(scaleinfo, reinterpret_cast(buffer.bits()), 0, 0, dw, dh, clipw, w, clipx, clipy, clipw, cliph); } } delete scaleinfo; return buffer; } DImg DImg::smoothScaleSection(const QRect& sourceRect, const QSize& destSize) const { return smoothScaleSection(sourceRect.x(), sourceRect.y(), sourceRect.width(), sourceRect.height(), destSize.width(), destSize.height()); } DImg DImg::smoothScaleSection(int sx, int sy, int sw, int sh, int dw, int dh) const { uint w = width(); uint h = height(); // sanity checks if ((dw <= 0) || (dh <= 0)) { return DImg(); } if ((sw <= 0) || (sh <= 0)) { return DImg(); } // clip the source rect to be within the actual image int /*psx, psy,*/ psw, psh; // psx = sx; // psy = sy; psw = sw; psh = sh; - if (!Private::clipped(sx, sy, sw, sh, w, h)) + if (!clipped(sx, sy, sw, sh, w, h)) { return DImg(); } // clip output coords to clipped input coords if (psw != sw) { dw = (dw * sw) / psw; } if (psh != sh) { dh = (dh * sh) / psh; } // do a second check to see if we now have invalid coords // do not do anything if we have a 0 width or height image to render if ((dw <= 0) || (dh <= 0)) { return DImg(); } // if the input rect size < 0 do not render either if ((sw <= 0) || (sh <= 0)) { return DImg(); } // do we actually need to scale? if ((sw == dw) && (sh == dh)) { return copy(sx, sy, sw, sh); } // calculate scaleinfo DImgScaleInfo* scaleinfo = dimgCalcScaleInfo(*this, sw, sh, dw, dh, sixteenBit(), true); DImg buffer(*this, dw, dh); if (sixteenBit()) { if (hasAlpha()) { dimgScaleAARGBA16(scaleinfo, reinterpret_cast(buffer.bits()), ((sx * dw) / sw), ((sy * dh) / sh), dw, dh, dw, w); } else { dimgScaleAARGB16(scaleinfo, reinterpret_cast(buffer.bits()), ((sx * dw) / sw), ((sy * dh) / sh), dw, dh, dw, w); } } else { if (hasAlpha()) { dimgScaleAARGBA(scaleinfo, reinterpret_cast(buffer.bits()), ((sx * dw) / sw), ((sy * dh) / sh), dw, dh, dw, w); } else { dimgScaleAARGB(scaleinfo, reinterpret_cast(buffer.bits()), ((sx * dw) / sw), ((sy * dh) / sh), dw, dh, dw, w); } } delete scaleinfo; return buffer; } // // Code ported from Imlib2... // // FIXME: replace with mRed, etc... These work on pointers to pixels, not // pixel values #define A_VAL(p) ((unsigned char*)(p))[3] #define R_VAL(p) ((unsigned char*)(p))[2] #define G_VAL(p) ((unsigned char*)(p))[1] #define B_VAL(p) ((unsigned char*)(p))[0] #define INV_XAP (256 - xapoints[x]) #define XAP (xapoints[x]) #define INV_YAP (256 - yapoints[dyy + y]) #define YAP (yapoints[dyy + y]) uint** DImgScale::dimgCalcYPoints(uint* const src, int sw, int sh, int dh) { uint** p = nullptr; (void)p; // To prevent cppcheck warnings. int i, j = 0; ullong val, inc; p = new uint* [dh+1]; val = 0; inc = (((ullong)sh) << 16) / dh; for (i = 0 ; i < dh ; ++i) { p[j++] = src + ((val >> 16) * sw); val += inc; } return(p); } ullong** DImgScale::dimgCalcYPoints16(ullong* const src, int sw, int sh, int dh) { ullong** p = nullptr; (void)p; // To prevent cppcheck warnings. int i, j = 0; ullong val, inc; p = new ullong*[(dh+1)]; val = 0; inc = (((ullong)sh) << 16) / dh; for (i = 0 ; i < dh ; ++i) { p[j++] = src + ((val >> 16) * sw); val += inc; } return p; } int* DImgScale::dimgCalcXPoints(int sw, int dw) { int* p = nullptr, i, j = 0; (void)p; // To prevent cppcheck warnings. ullong val, inc; p = new int[dw+1]; val = 0; inc = (((ullong)sw) << 16) / dw; for (i = 0 ; i < dw ; ++i) { p[j++] = (val >> 16); val += inc; } return(p); } int* DImgScale::dimgCalcApoints(int s, int d, int up) { int* p = nullptr, i, j = 0; (void)p; // To prevent cppcheck warnings. p = new int[d]; /* scaling up */ if (up) { ullong val, inc; val = 0; inc = (((ullong)s) << 16) / d; for (i = 0 ; i < d ; ++i) { p[j++] = (val >> 8) - ((val >> 8) & 0xffffff00); if ((int)(val >> 16) >= (s - 1)) { p[j - 1] = 0; } val += inc; } } /* scaling down */ else { ullong val, inc; int ap, Cp; val = 0; inc = (((ullong)s) << 16) / d; Cp = ((d << 14) / s) + 1; for (i = 0 ; i < d ; ++i) { ap = ((0x100 - ((val >> 8) & 0xff)) * Cp) >> 8; p[j] = ap | (Cp << 16); ++j; val += inc; } } return(p); } DImgScaleInfo* DImgScale::dimgCalcScaleInfo(const DImg& img, int sw, int sh, int dw, int dh, bool /*sixteenBit*/, bool aa) { DImgScaleInfo* isi = new DImgScaleInfo; int scw, sch; scw = dw * img.width() / sw; sch = dh * img.height() / sh; isi->xup_yup = (abs(dw) >= sw) + ((abs(dh) >= sh) << 1); isi->xpoints = dimgCalcXPoints(img.width(), scw); if (img.sixteenBit()) { isi->ypoints = nullptr; isi->ypoints16 = dimgCalcYPoints16(reinterpret_cast(img.bits()), img.width(), img.height(), sch); } else { isi->ypoints16 = nullptr; isi->ypoints = dimgCalcYPoints(reinterpret_cast(img.bits()), img.width(), img.height(), sch); } if (aa) { isi->xapoints = dimgCalcApoints(img.width(), scw, isi->xup_yup & 1); isi->yapoints = dimgCalcApoints(img.height(), sch, isi->xup_yup & 2); } else { isi->xapoints = new int[scw]; for (int i = 0; i < scw; ++i) { isi->xapoints[i] = 0; } isi->yapoints = new int[sch]; for (int i = 0; i < sch; ++i) { isi->yapoints[i] = 0; } } return isi; } /** scale by pixel sampling only */ void DImgScale::dimgSampleRGBA(DImgScaleInfo* const isi, uint* const dest, int dxx, int dyy, int dw, int dh, int dow) { dimgSampleRGBA(isi, dest, dxx, dyy, dw, dh, dow, 0, 0, dw, dh); } void DImgScale::dimgSampleRGBA(DImgScaleInfo* const isi, uint* const dest, int dxx, int dyy, int dw, int dh, int dow, int clip_dx, int clip_dy, int clip_dw, int clip_dh) { Q_UNUSED(dw); Q_UNUSED(dh); uint* sptr = nullptr; uint* dptr = nullptr; int x, y; uint** ypoints = isi->ypoints; int* xpoints = isi->xpoints; const int x_begin = dxx + clip_dx; // no clip set = dxx const int x_end = x_begin + clip_dw; // no clip set = dxx + dw const int y_begin = clip_dy; // no clip set = 0 const int y_end = clip_dy + clip_dh; // no clip set = dh /* go through every scanline in the output buffer */ for (y = y_begin; y < y_end; ++y) { /* get the pointer to the start of the destination scanline */ dptr = dest + (y - y_begin) * dow; /* calculate the source line we'll scan from */ sptr = ypoints[dyy + y]; /* go through the scanline and copy across */ for (x = x_begin; x < x_end; ++x) { *dptr++ = sptr[xpoints[x]]; } } } void DImgScale::dimgSampleRGBA16(DImgScaleInfo* const isi, ullong* const dest, int dxx, int dyy, int dw, int dh, int dow) { dimgSampleRGBA16(isi, dest, dxx, dyy, dw, dh, dow, 0, 0, dw, dh); } void DImgScale::dimgSampleRGBA16(DImgScaleInfo* const isi, ullong* const dest, int dxx, int dyy, int dw, int dh, int dow, int clip_dx, int clip_dy, int clip_dw, int clip_dh) { Q_UNUSED(dw); Q_UNUSED(dh); ullong* sptr = nullptr; ullong* dptr = nullptr; int x, y; ullong** ypoints = isi->ypoints16; int* xpoints = isi->xpoints; const int x_begin = dxx + clip_dx; // no clip set = dxx const int x_end = x_begin + clip_dw; // no clip set = dxx + dw const int y_begin = clip_dy; // no clip set = 0 const int y_end = clip_dy + clip_dh; // no clip set = dh /* go through every scanline in the output buffer */ for (y = y_begin; y < y_end; ++y) { /* get the pointer to the start of the destination scanline */ dptr = dest + (y - y_begin) * dow; /* calculate the source line we'll scan from */ sptr = ypoints[dyy + y]; /* go through the scanline and copy across */ for (x = x_begin; x < x_end; ++x) { *dptr++ = sptr[xpoints[x]]; } } } /* FIXME: NEED to optimize ScaleAARGBA - currently its "ok" but needs work*/ /** dimgScaleAARGBA : scale by area sampling. Arguments: DImgScaleInfo* isi, // scaleinfo uint* dest, // destination img data int dxx, // destination x location corresponding to start x of src section int dyy, // destination y location corresponding to start y of src section int dw, // destination width int dh, // destination height int dow, // destination scanline width int sow); // src scanline width */ void DImgScale::dimgScaleAARGBA(DImgScaleInfo* const isi, uint* const dest, int dxx, int dyy, int dw, int dh, int dow, int sow) { dimgScaleAARGBA(isi, dest, dxx, dyy, dw, dh, dow, sow, 0, 0, dw, dh); } void DImgScale::dimgScaleAARGBA(DImgScaleInfo* const isi, uint* const dest, int dxx, int dyy, int dw, int dh, int dow, int sow, int clip_dx, int clip_dy, int clip_dw, int clip_dh) { Q_UNUSED(dw); Q_UNUSED(dh); uint* sptr = nullptr; uint* dptr = nullptr; int x, y; uint** ypoints = isi->ypoints; int* xpoints = isi->xpoints; int* xapoints = isi->xapoints; int* yapoints = isi->yapoints; const int x_begin = dxx + clip_dx; // no clip set = dxx const int x_end = x_begin + clip_dw; // no clip set = dxx + dw const int y_begin = clip_dy; // no clip set = 0 const int y_end = clip_dy + clip_dh; // no clip set = dh /* scaling up both ways */ if (isi->xup_yup == 3) { /* go through every scanline in the output buffer */ for (y = y_begin; y < y_end; ++y) { /* calculate the source line we'll scan from */ dptr = dest + (y - y_begin) * dow; sptr = ypoints[dyy + y]; if (YAP > 0) { for (x = x_begin; x < x_end; ++x) { int r, g, b, a; uint* pix = nullptr; if (XAP > 0) { int rr, gg, bb, aa; pix = ypoints[dyy + y] + xpoints[x]; r = R_VAL(pix) * INV_XAP; g = G_VAL(pix) * INV_XAP; b = B_VAL(pix) * INV_XAP; a = A_VAL(pix) * INV_XAP; ++pix; r += R_VAL(pix) * XAP; g += G_VAL(pix) * XAP; b += B_VAL(pix) * XAP; a += A_VAL(pix) * XAP; pix += sow; rr = R_VAL(pix) * XAP; gg = G_VAL(pix) * XAP; bb = B_VAL(pix) * XAP; aa = A_VAL(pix) * XAP; --pix; rr += R_VAL(pix) * INV_XAP; gg += G_VAL(pix) * INV_XAP; bb += B_VAL(pix) * INV_XAP; aa += A_VAL(pix) * INV_XAP; r = ((rr * YAP) + (r * INV_YAP)) >> 16; g = ((gg * YAP) + (g * INV_YAP)) >> 16; b = ((bb * YAP) + (b * INV_YAP)) >> 16; a = ((aa * YAP) + (a * INV_YAP)) >> 16; A_VAL(dptr) = a; R_VAL(dptr) = r; G_VAL(dptr) = g; B_VAL(dptr) = b; ++dptr; } else { pix = ypoints[dyy + y] + xpoints[x]; r = R_VAL(pix) * INV_YAP; g = G_VAL(pix) * INV_YAP; b = B_VAL(pix) * INV_YAP; a = A_VAL(pix) * INV_YAP; pix += sow; r += R_VAL(pix) * YAP; g += G_VAL(pix) * YAP; b += B_VAL(pix) * YAP; a += A_VAL(pix) * YAP; r >>= 8; g >>= 8; b >>= 8; a >>= 8; A_VAL(dptr) = a; R_VAL(dptr) = r; G_VAL(dptr) = g; B_VAL(dptr) = b; ++dptr; } } } else { for (x = x_begin; x < x_end; ++x) { uint* pix = nullptr; if (XAP > 0) { int r, g, b, a; pix = ypoints[dyy + y] + xpoints[x]; r = R_VAL(pix) * INV_XAP; g = G_VAL(pix) * INV_XAP; b = B_VAL(pix) * INV_XAP; a = A_VAL(pix) * INV_XAP; ++pix; r += R_VAL(pix) * XAP; g += G_VAL(pix) * XAP; b += B_VAL(pix) * XAP; a += A_VAL(pix) * XAP; r >>= 8; g >>= 8; b >>= 8; a >>= 8; A_VAL(dptr) = a; R_VAL(dptr) = r; G_VAL(dptr) = g; B_VAL(dptr) = b; ++dptr; } else { *dptr++ = sptr[xpoints[x] ]; } } } } } /* if we're scaling down vertically */ else if (isi->xup_yup == 1) { /*\ 'Correct' version, with math units prepared for MMXification \*/ int Cy, j; uint* pix = nullptr; int r, g, b, a, rr, gg, bb, aa; int yap; /* go through every scanline in the output buffer */ for (y = y_begin; y < y_end; ++y) { Cy = YAP >> 16; yap = YAP & 0xffff; dptr = dest + (y - y_begin) * dow; for (x = x_begin; x < x_end; ++x) { pix = ypoints[dyy + y] + xpoints[x]; r = (R_VAL(pix) * yap) >> 10; g = (G_VAL(pix) * yap) >> 10; b = (B_VAL(pix) * yap) >> 10; a = (A_VAL(pix) * yap) >> 10; for (j = (1 << 14) - yap; j > Cy; j -= Cy) { pix += sow; r += (R_VAL(pix) * Cy) >> 10; g += (G_VAL(pix) * Cy) >> 10; b += (B_VAL(pix) * Cy) >> 10; a += (A_VAL(pix) * Cy) >> 10; } if (j > 0) { pix += sow; r += (R_VAL(pix) * j) >> 10; g += (G_VAL(pix) * j) >> 10; b += (B_VAL(pix) * j) >> 10; a += (A_VAL(pix) * j) >> 10; } if (XAP > 0) { pix = ypoints[dyy + y] + xpoints[x] + 1; rr = (R_VAL(pix) * yap) >> 10; gg = (G_VAL(pix) * yap) >> 10; bb = (B_VAL(pix) * yap) >> 10; aa = (A_VAL(pix) * yap) >> 10; for (j = (1 << 14) - yap; j > Cy; j -= Cy) { pix += sow; rr += (R_VAL(pix) * Cy) >> 10; gg += (G_VAL(pix) * Cy) >> 10; bb += (B_VAL(pix) * Cy) >> 10; aa += (A_VAL(pix) * Cy) >> 10; } if (j > 0) { pix += sow; rr += (R_VAL(pix) * j) >> 10; gg += (G_VAL(pix) * j) >> 10; bb += (B_VAL(pix) * j) >> 10; aa += (A_VAL(pix) * j) >> 10; } r = r * INV_XAP; g = g * INV_XAP; b = b * INV_XAP; a = a * INV_XAP; r = (r + ((rr * XAP))) >> 12; g = (g + ((gg * XAP))) >> 12; b = (b + ((bb * XAP))) >> 12; a = (a + ((aa * XAP))) >> 12; } else { r >>= 4; g >>= 4; b >>= 4; a >>= 4; } A_VAL(dptr) = a; R_VAL(dptr) = r; G_VAL(dptr) = g; B_VAL(dptr) = b; ++dptr; } } } /* if we're scaling down horizontally */ else if (isi->xup_yup == 2) { /*\ 'Correct' version, with math units prepared for MMXification \*/ int Cx, j; uint* pix = nullptr; int r, g, b, a, rr, gg, bb, aa; int xap; /* go through every scanline in the output buffer */ for (y = y_begin; y < y_end; ++y) { dptr = dest + (y - y_begin) * dow; for (x = x_begin; x < x_end; ++x) { Cx = XAP >> 16; xap = XAP & 0xffff; pix = ypoints[dyy + y] + xpoints[x]; r = (R_VAL(pix) * xap) >> 10; g = (G_VAL(pix) * xap) >> 10; b = (B_VAL(pix) * xap) >> 10; a = (A_VAL(pix) * xap) >> 10; for (j = (1 << 14) - xap; j > Cx; j -= Cx) { ++pix; r += (R_VAL(pix) * Cx) >> 10; g += (G_VAL(pix) * Cx) >> 10; b += (B_VAL(pix) * Cx) >> 10; a += (A_VAL(pix) * Cx) >> 10; } if (j > 0) { ++pix; r += (R_VAL(pix) * j) >> 10; g += (G_VAL(pix) * j) >> 10; b += (B_VAL(pix) * j) >> 10; a += (A_VAL(pix) * j) >> 10; } if (YAP > 0) { pix = ypoints[dyy + y] + xpoints[x] + sow; rr = (R_VAL(pix) * xap) >> 10; gg = (G_VAL(pix) * xap) >> 10; bb = (B_VAL(pix) * xap) >> 10; aa = (A_VAL(pix) * xap) >> 10; for (j = (1 << 14) - xap; j > Cx; j -= Cx) { ++pix; rr += (R_VAL(pix) * Cx) >> 10; gg += (G_VAL(pix) * Cx) >> 10; bb += (B_VAL(pix) * Cx) >> 10; aa += (A_VAL(pix) * Cx) >> 10; } if (j > 0) { ++pix; rr += (R_VAL(pix) * j) >> 10; gg += (G_VAL(pix) * j) >> 10; bb += (B_VAL(pix) * j) >> 10; aa += (A_VAL(pix) * j) >> 10; } r = r * INV_YAP; g = g * INV_YAP; b = b * INV_YAP; a = a * INV_YAP; r = (r + ((rr * YAP))) >> 12; g = (g + ((gg * YAP))) >> 12; b = (b + ((bb * YAP))) >> 12; a = (a + ((aa * YAP))) >> 12; } else { r >>= 4; g >>= 4; b >>= 4; a >>= 4; } A_VAL(dptr) = a; R_VAL(dptr) = r; G_VAL(dptr) = g; B_VAL(dptr) = b; ++dptr; } } } /* if we're scaling down horizontally & vertically */ else { /*\ 'Correct' version, with math units prepared for MMXification: |*| The operation 'b = (b * c) >> 16' translates to pmulhw, |*| so the operation 'b = (b * c) >> d' would translate to |*| psllw (16 - d), %mmb; pmulh %mmc, %mmb \*/ int Cx, Cy, i, j; uint* pix = nullptr; int a, r, g, b, ax, rx, gx, bx; int xap, yap; for (y = y_begin; y < y_end; ++y) { Cy = YAP >> 16; yap = YAP & 0xffff; dptr = dest + (y - y_begin) * dow; for (x = x_begin; x < x_end; ++x) { Cx = XAP >> 16; xap = XAP & 0xffff; if (ypoints && xpoints) { sptr = ypoints[dyy + y] + xpoints[x]; pix = sptr; sptr += sow; rx = (R_VAL(pix) * xap) >> 9; gx = (G_VAL(pix) * xap) >> 9; bx = (B_VAL(pix) * xap) >> 9; ax = (A_VAL(pix) * xap) >> 9; ++pix; for (i = (1 << 14) - xap; i > Cx; i -= Cx) { rx += (R_VAL(pix) * Cx) >> 9; gx += (G_VAL(pix) * Cx) >> 9; bx += (B_VAL(pix) * Cx) >> 9; ax += (A_VAL(pix) * Cx) >> 9; ++pix; } if (i > 0) { rx += (R_VAL(pix) * i) >> 9; gx += (G_VAL(pix) * i) >> 9; bx += (B_VAL(pix) * i) >> 9; ax += (A_VAL(pix) * i) >> 9; } r = (rx * yap) >> 14; g = (gx * yap) >> 14; b = (bx * yap) >> 14; a = (ax * yap) >> 14; for (j = (1 << 14) - yap; j > Cy; j -= Cy) { pix = sptr; sptr += sow; rx = (R_VAL(pix) * xap) >> 9; gx = (G_VAL(pix) * xap) >> 9; bx = (B_VAL(pix) * xap) >> 9; ax = (A_VAL(pix) * xap) >> 9; ++pix; for (i = (1 << 14) - xap; i > Cx; i -= Cx) { rx += (R_VAL(pix) * Cx) >> 9; gx += (G_VAL(pix) * Cx) >> 9; bx += (B_VAL(pix) * Cx) >> 9; ax += (A_VAL(pix) * Cx) >> 9; ++pix; } if (i > 0) { rx += (R_VAL(pix) * i) >> 9; gx += (G_VAL(pix) * i) >> 9; bx += (B_VAL(pix) * i) >> 9; ax += (A_VAL(pix) * i) >> 9; } r += (rx * Cy) >> 14; g += (gx * Cy) >> 14; b += (bx * Cy) >> 14; a += (ax * Cy) >> 14; } if (j > 0) { pix = sptr; sptr += sow; (void)sptr; // disable clang warning. rx = (R_VAL(pix) * xap) >> 9; gx = (G_VAL(pix) * xap) >> 9; bx = (B_VAL(pix) * xap) >> 9; ax = (A_VAL(pix) * xap) >> 9; ++pix; for (i = (1 << 14) - xap; i > Cx; i -= Cx) { rx += (R_VAL(pix) * Cx) >> 9; gx += (G_VAL(pix) * Cx) >> 9; bx += (B_VAL(pix) * Cx) >> 9; ax += (A_VAL(pix) * Cx) >> 9; ++pix; } if (i > 0) { rx += (R_VAL(pix) * i) >> 9; gx += (G_VAL(pix) * i) >> 9; bx += (B_VAL(pix) * i) >> 9; ax += (A_VAL(pix) * i) >> 9; } r += (rx * j) >> 14; g += (gx * j) >> 14; b += (bx * j) >> 14; a += (ax * j) >> 14; } R_VAL(dptr) = r >> 5; G_VAL(dptr) = g >> 5; B_VAL(dptr) = b >> 5; A_VAL(dptr) = a >> 5; ++dptr; } } } } } void DImgScale::dimgScaleAARGB(DImgScaleInfo* const isi, uint* const dest, int dxx, int dyy, int dw, int dh, int dow, int sow) { dimgScaleAARGB(isi, dest, dxx, dyy, dw, dh, dow, sow, 0, 0, dw, dh); } /** scale by area sampling - IGNORE the ALPHA byte */ void DImgScale::dimgScaleAARGB(DImgScaleInfo* const isi, uint* const dest, int dxx, int dyy, int dw, int dh, int dow, int sow, int clip_dx, int clip_dy, int clip_dw, int clip_dh) { Q_UNUSED(dw); Q_UNUSED(dh); uint* sptr = nullptr; uint* dptr = nullptr; int x, y; uint** ypoints = isi->ypoints; int* xpoints = isi->xpoints; int* xapoints = isi->xapoints; int* yapoints = isi->yapoints; const int x_begin = dxx + clip_dx; // no clip set = dxx const int x_end = x_begin + clip_dw; // no clip set = dxx + dw const int y_begin = clip_dy; // no clip set = 0 const int y_end = clip_dy + clip_dh; // no clip set = dh /* scaling up both ways */ if (isi->xup_yup == 3) { /* go through every scanline in the output buffer */ for (y = y_begin; y < y_end; ++y) { /* calculate the source line we'll scan from */ dptr = dest + (y - y_begin) * dow; sptr = ypoints[dyy + y]; if (YAP > 0) { for (x = x_begin; x < x_end; ++x) { int r = 0, g = 0, b = 0; uint* pix = nullptr; if (XAP > 0) { int rr = 0, gg = 0, bb = 0; pix = ypoints[dyy + y] + xpoints[x]; r = R_VAL(pix) * INV_XAP; g = G_VAL(pix) * INV_XAP; b = B_VAL(pix) * INV_XAP; ++pix; r += R_VAL(pix) * XAP; g += G_VAL(pix) * XAP; b += B_VAL(pix) * XAP; pix += sow; rr = R_VAL(pix) * XAP; gg = G_VAL(pix) * XAP; bb = B_VAL(pix) * XAP; pix--; rr += R_VAL(pix) * INV_XAP; gg += G_VAL(pix) * INV_XAP; bb += B_VAL(pix) * INV_XAP; r = ((rr * YAP) + (r * INV_YAP)) >> 16; g = ((gg * YAP) + (g * INV_YAP)) >> 16; b = ((bb * YAP) + (b * INV_YAP)) >> 16; R_VAL(dptr) = r; G_VAL(dptr) = g; B_VAL(dptr) = b; A_VAL(dptr) = 0xFF; ++dptr; } else { pix = ypoints[dyy + y] + xpoints[x]; r = R_VAL(pix) * INV_YAP; g = G_VAL(pix) * INV_YAP; b = B_VAL(pix) * INV_YAP; pix += sow; r += R_VAL(pix) * YAP; g += G_VAL(pix) * YAP; b += B_VAL(pix) * YAP; r >>= 8; g >>= 8; b >>= 8; R_VAL(dptr) = r; G_VAL(dptr) = g; B_VAL(dptr) = b; A_VAL(dptr) = 0xFF; ++dptr; } } } else { for (x = x_begin; x < x_end; ++x) { uint* pix = nullptr; if (XAP > 0) { int r = 0, g = 0, b = 0; pix = ypoints[dyy + y] + xpoints[x]; r = R_VAL(pix) * INV_XAP; g = G_VAL(pix) * INV_XAP; b = B_VAL(pix) * INV_XAP; ++pix; r += R_VAL(pix) * XAP; g += G_VAL(pix) * XAP; b += B_VAL(pix) * XAP; r >>= 8; g >>= 8; b >>= 8; R_VAL(dptr) = r; G_VAL(dptr) = g; B_VAL(dptr) = b; A_VAL(dptr) = 0xFF; ++dptr; } else { *dptr++ = sptr[xpoints[x] ]; } } } } } /* if we're scaling down vertically */ else if (isi->xup_yup == 1) { /*\ 'Correct' version, with math units prepared for MMXification \*/ int Cy, j; uint* pix = nullptr; int r, g, b, rr, gg, bb; int yap; /* go through every scanline in the output buffer */ for (y = y_begin; y < y_end; ++y) { Cy = YAP >> 16; yap = YAP & 0xffff; dptr = dest + (y - y_begin) * dow; for (x = x_begin; x < x_end; ++x) { pix = ypoints[dyy + y] + xpoints[x]; r = (R_VAL(pix) * yap) >> 10; g = (G_VAL(pix) * yap) >> 10; b = (B_VAL(pix) * yap) >> 10; pix += sow; for (j = (1 << 14) - yap; j > Cy; j -= Cy) { r += (R_VAL(pix) * Cy) >> 10; g += (G_VAL(pix) * Cy) >> 10; b += (B_VAL(pix) * Cy) >> 10; pix += sow; } if (j > 0) { r += (R_VAL(pix) * j) >> 10; g += (G_VAL(pix) * j) >> 10; b += (B_VAL(pix) * j) >> 10; } if (XAP > 0) { pix = ypoints[dyy + y] + xpoints[x] + 1; rr = (R_VAL(pix) * yap) >> 10; gg = (G_VAL(pix) * yap) >> 10; bb = (B_VAL(pix) * yap) >> 10; pix += sow; for (j = (1 << 14) - yap; j > Cy; j -= Cy) { rr += (R_VAL(pix) * Cy) >> 10; gg += (G_VAL(pix) * Cy) >> 10; bb += (B_VAL(pix) * Cy) >> 10; pix += sow; } if (j > 0) { rr += (R_VAL(pix) * j) >> 10; gg += (G_VAL(pix) * j) >> 10; bb += (B_VAL(pix) * j) >> 10; } r = r * INV_XAP; g = g * INV_XAP; b = b * INV_XAP; r = (r + ((rr * XAP))) >> 12; g = (g + ((gg * XAP))) >> 12; b = (b + ((bb * XAP))) >> 12; } else { r >>= 4; g >>= 4; b >>= 4; } R_VAL(dptr) = r; G_VAL(dptr) = g; B_VAL(dptr) = b; A_VAL(dptr) = 0xFF; ++dptr; } } } /* if we're scaling down horizontally */ else if (isi->xup_yup == 2) { /*\ 'Correct' version, with math units prepared for MMXification \*/ int Cx, j; uint* pix = nullptr; int r, g, b, rr, gg, bb; int xap; /* go through every scanline in the output buffer */ for (y = y_begin; y < y_end; ++y) { dptr = dest + (y - y_begin) * dow; for (x = x_begin; x < x_end; ++x) { Cx = XAP >> 16; xap = XAP & 0xffff; pix = ypoints[dyy + y] + xpoints[x]; r = (R_VAL(pix) * xap) >> 10; g = (G_VAL(pix) * xap) >> 10; b = (B_VAL(pix) * xap) >> 10; ++pix; for (j = (1 << 14) - xap; j > Cx; j -= Cx) { r += (R_VAL(pix) * Cx) >> 10; g += (G_VAL(pix) * Cx) >> 10; b += (B_VAL(pix) * Cx) >> 10; ++pix; } if (j > 0) { r += (R_VAL(pix) * j) >> 10; g += (G_VAL(pix) * j) >> 10; b += (B_VAL(pix) * j) >> 10; } if (YAP > 0) { pix = ypoints[dyy + y] + xpoints[x] + sow; rr = (R_VAL(pix) * xap) >> 10; gg = (G_VAL(pix) * xap) >> 10; bb = (B_VAL(pix) * xap) >> 10; ++pix; for (j = (1 << 14) - xap; j > Cx; j -= Cx) { rr += (R_VAL(pix) * Cx) >> 10; gg += (G_VAL(pix) * Cx) >> 10; bb += (B_VAL(pix) * Cx) >> 10; ++pix; } if (j > 0) { rr += (R_VAL(pix) * j) >> 10; gg += (G_VAL(pix) * j) >> 10; bb += (B_VAL(pix) * j) >> 10; } r = r * INV_YAP; g = g * INV_YAP; b = b * INV_YAP; r = (r + ((rr * YAP))) >> 12; g = (g + ((gg * YAP))) >> 12; b = (b + ((bb * YAP))) >> 12; } else { r >>= 4; g >>= 4; b >>= 4; } R_VAL(dptr) = r; G_VAL(dptr) = g; B_VAL(dptr) = b; A_VAL(dptr) = 0xFF; ++dptr; } } } /* fully optimized (i think) - only change of algorithm can help */ /* if we're scaling down horizontally & vertically */ else { /*\ 'Correct' version, with math units prepared for MMXification \*/ int Cx, Cy, i, j; uint* pix = nullptr; int r, g, b, rx, gx, bx; int xap, yap; for (y = y_begin; y < y_end; ++y) { Cy = YAP >> 16; yap = YAP & 0xffff; dptr = dest + (y - y_begin) * dow; for (x = x_begin; x < x_end; ++x) { Cx = XAP >> 16; xap = XAP & 0xffff; if (ypoints && xpoints) { sptr = ypoints[dyy + y] + xpoints[x]; pix = sptr; sptr += sow; rx = (R_VAL(pix) * xap) >> 9; gx = (G_VAL(pix) * xap) >> 9; bx = (B_VAL(pix) * xap) >> 9; ++pix; for (i = (1 << 14) - xap; i > Cx; i -= Cx) { rx += (R_VAL(pix) * Cx) >> 9; gx += (G_VAL(pix) * Cx) >> 9; bx += (B_VAL(pix) * Cx) >> 9; ++pix; } if (i > 0) { rx += (R_VAL(pix) * i) >> 9; gx += (G_VAL(pix) * i) >> 9; bx += (B_VAL(pix) * i) >> 9; } r = (rx * yap) >> 14; g = (gx * yap) >> 14; b = (bx * yap) >> 14; for (j = (1 << 14) - yap; j > Cy; j -= Cy) { pix = sptr; sptr += sow; rx = (R_VAL(pix) * xap) >> 9; gx = (G_VAL(pix) * xap) >> 9; bx = (B_VAL(pix) * xap) >> 9; ++pix; for (i = (1 << 14) - xap; i > Cx; i -= Cx) { rx += (R_VAL(pix) * Cx) >> 9; gx += (G_VAL(pix) * Cx) >> 9; bx += (B_VAL(pix) * Cx) >> 9; ++pix; } if (i > 0) { rx += (R_VAL(pix) * i) >> 9; gx += (G_VAL(pix) * i) >> 9; bx += (B_VAL(pix) * i) >> 9; } r += (rx * Cy) >> 14; g += (gx * Cy) >> 14; b += (bx * Cy) >> 14; } if (j > 0) { pix = sptr; sptr += sow; (void)sptr; // disable clang warning. rx = (R_VAL(pix) * xap) >> 9; gx = (G_VAL(pix) * xap) >> 9; bx = (B_VAL(pix) * xap) >> 9; ++pix; for (i = (1 << 14) - xap; i > Cx; i -= Cx) { rx += (R_VAL(pix) * Cx) >> 9; gx += (G_VAL(pix) * Cx) >> 9; bx += (B_VAL(pix) * Cx) >> 9; ++pix; } if (i > 0) { rx += (R_VAL(pix) * i) >> 9; gx += (G_VAL(pix) * i) >> 9; bx += (B_VAL(pix) * i) >> 9; } r += (rx * j) >> 14; g += (gx * j) >> 14; b += (bx * j) >> 14; } R_VAL(dptr) = r >> 5; G_VAL(dptr) = g >> 5; B_VAL(dptr) = b >> 5; A_VAL(dptr) = 0xFF; ++dptr; } } } } } #define A_VAL16(p) ((ushort*)(p))[3] #define R_VAL16(p) ((ushort*)(p))[2] #define G_VAL16(p) ((ushort*)(p))[1] #define B_VAL16(p) ((ushort*)(p))[0] void DImgScale::dimgScaleAARGB16(DImgScaleInfo* const isi, ullong* const dest, int dxx, int dyy, int dw, int dh, int dow, int sow) { dimgScaleAARGB16(isi, dest, dxx, dyy, dw, dh, dow, sow, 0, 0, dw, dh); } /** scale by area sampling - IGNORE the ALPHA byte*/ void DImgScale::dimgScaleAARGB16(DImgScaleInfo* const isi, ullong* const dest, int dxx, int dyy, int dw, int dh, int dow, int sow, int clip_dx, int clip_dy, int clip_dw, int clip_dh) { Q_UNUSED(dw); Q_UNUSED(dh); ullong* sptr = nullptr; ullong* dptr = nullptr; int x, y; ullong** ypoints = isi->ypoints16; int* xpoints = isi->xpoints; int* xapoints = isi->xapoints; int* yapoints = isi->yapoints; const int x_begin = dxx + clip_dx; // no clip set = dxx const int x_end = x_begin + clip_dw; // no clip set = dxx + dw const int y_begin = clip_dy; // no clip set = 0 const int y_end = clip_dy + clip_dh; // no clip set = dh // scaling up both ways if (isi->xup_yup == 3) { // go through every scanline in the output buffer for (y = y_begin; y < y_end; ++y) { // calculate the source line we'll scan from dptr = dest + (y - y_begin) * dow; sptr = ypoints[dyy + y]; if (YAP > 0) { for (x = x_begin; x < x_end; ++x) { llong r = 0, g = 0, b = 0; ullong* pix = nullptr; if (XAP > 0) { llong rr = 0, gg = 0, bb = 0; pix = ypoints[dyy + y] + xpoints[x]; r = R_VAL16(pix) * INV_XAP; g = G_VAL16(pix) * INV_XAP; b = B_VAL16(pix) * INV_XAP; ++pix; r += R_VAL16(pix) * XAP; g += G_VAL16(pix) * XAP; b += B_VAL16(pix) * XAP; pix += sow; rr = R_VAL16(pix) * XAP; gg = G_VAL16(pix) * XAP; bb = B_VAL16(pix) * XAP; pix--; rr += R_VAL16(pix) * INV_XAP; gg += G_VAL16(pix) * INV_XAP; bb += B_VAL16(pix) * INV_XAP; r = ((rr * YAP) + (r * INV_YAP)) >> 16; g = ((gg * YAP) + (g * INV_YAP)) >> 16; b = ((bb * YAP) + (b * INV_YAP)) >> 16; R_VAL16(dptr) = r; G_VAL16(dptr) = g; B_VAL16(dptr) = b; A_VAL16(dptr) = 0xFFFF; ++dptr; } else { pix = ypoints[dyy + y] + xpoints[x]; r = R_VAL16(pix) * INV_YAP; g = G_VAL16(pix) * INV_YAP; b = B_VAL16(pix) * INV_YAP; pix += sow; r += R_VAL16(pix) * YAP; g += G_VAL16(pix) * YAP; b += B_VAL16(pix) * YAP; r >>= 8; g >>= 8; b >>= 8; R_VAL16(dptr) = r; G_VAL16(dptr) = g; B_VAL16(dptr) = b; A_VAL16(dptr) = 0xFFFF; ++dptr; } } } else { for (x = x_begin; x < x_end; ++x) { ullong* pix = nullptr; if (XAP > 0) { llong r = 0, g = 0, b = 0; pix = ypoints[dyy + y] + xpoints[x]; r = R_VAL16(pix) * INV_XAP; g = G_VAL16(pix) * INV_XAP; b = B_VAL16(pix) * INV_XAP; ++pix; r += R_VAL16(pix) * XAP; g += G_VAL16(pix) * XAP; b += B_VAL16(pix) * XAP; r >>= 8; g >>= 8; b >>= 8; R_VAL16(dptr) = r; G_VAL16(dptr) = g; B_VAL16(dptr) = b; A_VAL16(dptr) = 0xFFFF; ++dptr; } else { *dptr++ = sptr[xpoints[x] ]; } } } } } // if we're scaling down vertically else if (isi->xup_yup == 1) { // 'Correct' version, with math units prepared for MMXification int Cy, j; ullong* pix = nullptr; llong r, g, b, rr, gg, bb; int yap; // go through every scanline in the output buffer for (y = y_begin; y < y_end; ++y) { Cy = YAP >> 16; yap = YAP & 0xffff; dptr = dest + (y - y_begin) * dow; for (x = x_begin; x < x_end; ++x) { pix = ypoints[dyy + y] + xpoints[x]; r = (R_VAL16(pix) * yap) >> 10; g = (G_VAL16(pix) * yap) >> 10; b = (B_VAL16(pix) * yap) >> 10; pix += sow; for (j = (1 << 14) - yap; j > Cy; j -= Cy) { r += (R_VAL16(pix) * Cy) >> 10; g += (G_VAL16(pix) * Cy) >> 10; b += (B_VAL16(pix) * Cy) >> 10; pix += sow; } if (j > 0) { r += (R_VAL16(pix) * j) >> 10; g += (G_VAL16(pix) * j) >> 10; b += (B_VAL16(pix) * j) >> 10; } if (XAP > 0) { pix = ypoints[dyy + y] + xpoints[x] + 1; rr = (R_VAL16(pix) * yap) >> 10; gg = (G_VAL16(pix) * yap) >> 10; bb = (B_VAL16(pix) * yap) >> 10; pix += sow; for (j = (1 << 14) - yap; j > Cy; j -= Cy) { rr += (R_VAL16(pix) * Cy) >> 10; gg += (G_VAL16(pix) * Cy) >> 10; bb += (B_VAL16(pix) * Cy) >> 10; pix += sow; } if (j > 0) { rr += (R_VAL16(pix) * j) >> 10; gg += (G_VAL16(pix) * j) >> 10; bb += (B_VAL16(pix) * j) >> 10; } r = r * INV_XAP; g = g * INV_XAP; b = b * INV_XAP; r = (r + ((rr * XAP))) >> 12; g = (g + ((gg * XAP))) >> 12; b = (b + ((bb * XAP))) >> 12; } else { r >>= 4; g >>= 4; b >>= 4; } R_VAL16(dptr) = r; G_VAL16(dptr) = g; B_VAL16(dptr) = b; A_VAL16(dptr) = 0xFFFF; ++dptr; } } } // if we're scaling down horizontally else if (isi->xup_yup == 2) { // 'Correct' version, with math units prepared for MMXification int Cx, j; ullong* pix = nullptr; llong r, g, b, rr, gg, bb; int xap; // go through every scanline in the output buffer for (y = y_begin; y < y_end; ++y) { dptr = dest + (y - y_begin) * dow; for (x = x_begin; x < x_end; ++x) { Cx = XAP >> 16; xap = XAP & 0xffff; pix = ypoints[dyy + y] + xpoints[x]; r = (R_VAL16(pix) * xap) >> 10; g = (G_VAL16(pix) * xap) >> 10; b = (B_VAL16(pix) * xap) >> 10; ++pix; for (j = (1 << 14) - xap; j > Cx; j -= Cx) { r += (R_VAL16(pix) * Cx) >> 10; g += (G_VAL16(pix) * Cx) >> 10; b += (B_VAL16(pix) * Cx) >> 10; ++pix; } if (j > 0) { r += (R_VAL16(pix) * j) >> 10; g += (G_VAL16(pix) * j) >> 10; b += (B_VAL16(pix) * j) >> 10; } if (YAP > 0) { pix = ypoints[dyy + y] + xpoints[x] + sow; rr = (R_VAL16(pix) * xap) >> 10; gg = (G_VAL16(pix) * xap) >> 10; bb = (B_VAL16(pix) * xap) >> 10; ++pix; for (j = (1 << 14) - xap; j > Cx; j -= Cx) { rr += (R_VAL16(pix) * Cx) >> 10; gg += (G_VAL16(pix) * Cx) >> 10; bb += (B_VAL16(pix) * Cx) >> 10; ++pix; } if (j > 0) { rr += (R_VAL16(pix) * j) >> 10; gg += (G_VAL16(pix) * j) >> 10; bb += (B_VAL16(pix) * j) >> 10; } r = r * INV_YAP; g = g * INV_YAP; b = b * INV_YAP; r = (r + ((rr * YAP))) >> 12; g = (g + ((gg * YAP))) >> 12; b = (b + ((bb * YAP))) >> 12; } else { r >>= 4; g >>= 4; b >>= 4; } R_VAL16(dptr) = r; G_VAL16(dptr) = g; B_VAL16(dptr) = b; A_VAL16(dptr) = 0xFFFF; ++dptr; } } } // fully optimized (i think) - only change of algorithm can help // if we're scaling down horizontally & vertically else { // 'Correct' version, with math units prepared for MMXification int Cx, Cy, i, j; ullong* pix = nullptr; llong r, g, b, rx, gx, bx; int xap, yap; for (y = y_begin; y < y_end; ++y) { Cy = YAP >> 16; yap = YAP & 0xffff; dptr = dest + (y - y_begin) * dow; for (x = x_begin; x < x_end; ++x) { Cx = XAP >> 16; xap = XAP & 0xffff; if (ypoints && xpoints) { sptr = ypoints[dyy + y] + xpoints[x]; pix = sptr; sptr += sow; rx = (R_VAL16(pix) * xap) >> 9; gx = (G_VAL16(pix) * xap) >> 9; bx = (B_VAL16(pix) * xap) >> 9; ++pix; for (i = (1 << 14) - xap; i > Cx; i -= Cx) { rx += (R_VAL16(pix) * Cx) >> 9; gx += (G_VAL16(pix) * Cx) >> 9; bx += (B_VAL16(pix) * Cx) >> 9; ++pix; } if (i > 0) { rx += (R_VAL16(pix) * i) >> 9; gx += (G_VAL16(pix) * i) >> 9; bx += (B_VAL16(pix) * i) >> 9; } r = (rx * yap) >> 14; g = (gx * yap) >> 14; b = (bx * yap) >> 14; for (j = (1 << 14) - yap; j > Cy; j -= Cy) { pix = sptr; sptr += sow; rx = (R_VAL16(pix) * xap) >> 9; gx = (G_VAL16(pix) * xap) >> 9; bx = (B_VAL16(pix) * xap) >> 9; ++pix; for (i = (1 << 14) - xap; i > Cx; i -= Cx) { rx += (R_VAL16(pix) * Cx) >> 9; gx += (G_VAL16(pix) * Cx) >> 9; bx += (B_VAL16(pix) * Cx) >> 9; ++pix; } if (i > 0) { rx += (R_VAL16(pix) * i) >> 9; gx += (G_VAL16(pix) * i) >> 9; bx += (B_VAL16(pix) * i) >> 9; } r += (rx * Cy) >> 14; g += (gx * Cy) >> 14; b += (bx * Cy) >> 14; } if (j > 0) { pix = sptr; sptr += sow; (void)sptr; // disable clang warning. rx = (R_VAL16(pix) * xap) >> 9; gx = (G_VAL16(pix) * xap) >> 9; bx = (B_VAL16(pix) * xap) >> 9; ++pix; for (i = (1 << 14) - xap; i > Cx; i -= Cx) { rx += (R_VAL16(pix) * Cx) >> 9; gx += (G_VAL16(pix) * Cx) >> 9; bx += (B_VAL16(pix) * Cx) >> 9; ++pix; } if (i > 0) { rx += (R_VAL16(pix) * i) >> 9; gx += (G_VAL16(pix) * i) >> 9; bx += (B_VAL16(pix) * i) >> 9; } r += (rx * j) >> 14; g += (gx * j) >> 14; b += (bx * j) >> 14; } R_VAL16(dptr) = r >> 5; G_VAL16(dptr) = g >> 5; B_VAL16(dptr) = b >> 5; A_VAL16(dptr) = 0xFFFF; ++dptr; } } } } } void DImgScale::dimgScaleAARGBA16(DImgScaleInfo* const isi, ullong* const dest, int dxx, int dyy, int dw, int dh, int dow, int sow ) { dimgScaleAARGBA16(isi, dest, dxx, dyy, dw, dh, dow, sow, 0, 0, dw, dh); } /* scale by area sampling */ void DImgScale::dimgScaleAARGBA16(DImgScaleInfo* const isi, ullong* const dest, int dxx, int dyy, int dw, int dh, int dow, int sow, int clip_dx, int clip_dy, int clip_dw, int clip_dh) { Q_UNUSED(dw); Q_UNUSED(dh); ullong* sptr = nullptr; ullong* dptr = nullptr; int x, y; ullong** ypoints = isi->ypoints16; int* xpoints = isi->xpoints; int* xapoints = isi->xapoints; int* yapoints = isi->yapoints; const int x_begin = dxx + clip_dx; // no clip set = dxx const int x_end = x_begin + clip_dw; // no clip set = dxx + dw const int y_begin = clip_dy; // no clip set = 0 const int y_end = clip_dy + clip_dh; // no clip set = dh /* scaling up both ways */ if (isi->xup_yup == 3) { /* go through every scanline in the output buffer */ for (y = y_begin; y < y_end; ++y) { /* calculate the source line we'll scan from */ dptr = dest + (y - y_begin) * dow; sptr = ypoints[dyy + y]; if (YAP > 0) { for (x = x_begin; x < x_end; ++x) { llong r, g, b, a; llong rr, gg, bb, aa; ullong* pix = nullptr; if (XAP > 0) { pix = ypoints[dyy + y] + xpoints[x]; r = R_VAL16(pix) * INV_XAP; g = G_VAL16(pix) * INV_XAP; b = B_VAL16(pix) * INV_XAP; a = A_VAL16(pix) * INV_XAP; ++pix; r += R_VAL16(pix) * XAP; g += G_VAL16(pix) * XAP; b += B_VAL16(pix) * XAP; a += A_VAL16(pix) * XAP; pix += sow; rr = R_VAL16(pix) * XAP; gg = G_VAL16(pix) * XAP; bb = B_VAL16(pix) * XAP; aa = A_VAL16(pix) * XAP; --pix; rr += R_VAL16(pix) * INV_XAP; gg += G_VAL16(pix) * INV_XAP; bb += B_VAL16(pix) * INV_XAP; aa += A_VAL16(pix) * INV_XAP; r = ((rr * YAP) + (r * INV_YAP)) >> 16; g = ((gg * YAP) + (g * INV_YAP)) >> 16; b = ((bb * YAP) + (b * INV_YAP)) >> 16; a = ((aa * YAP) + (a * INV_YAP)) >> 16; R_VAL16(dptr) = r; G_VAL16(dptr) = g; B_VAL16(dptr) = b; A_VAL16(dptr) = a; ++dptr; } else { pix = ypoints[dyy + y] + xpoints[x]; r = R_VAL16(pix) * INV_YAP; g = G_VAL16(pix) * INV_YAP; b = B_VAL16(pix) * INV_YAP; a = A_VAL16(pix) * INV_YAP; pix += sow; r += R_VAL16(pix) * YAP; g += G_VAL16(pix) * YAP; b += B_VAL16(pix) * YAP; a += A_VAL16(pix) * YAP; r >>= 8; g >>= 8; b >>= 8; a >>= 8; R_VAL16(dptr) = r; G_VAL16(dptr) = g; B_VAL16(dptr) = b; A_VAL16(dptr) = a; ++dptr; } } } else { for (x = x_begin; x < x_end; ++x) { llong r, g, b, a; ullong* pix = nullptr; if (XAP > 0) { pix = ypoints[dyy + y] + xpoints[x]; r = R_VAL16(pix) * INV_XAP; g = G_VAL16(pix) * INV_XAP; b = B_VAL16(pix) * INV_XAP; a = A_VAL16(pix) * INV_XAP; ++pix; r += R_VAL16(pix) * XAP; g += G_VAL16(pix) * XAP; b += B_VAL16(pix) * XAP; a += A_VAL16(pix) * XAP; r >>= 8; g >>= 8; b >>= 8; a >>= 8; R_VAL16(dptr) = r; G_VAL16(dptr) = g; B_VAL16(dptr) = b; A_VAL16(dptr) = a; ++dptr; } else { *dptr++ = sptr[xpoints[x] ]; } } } } } /* if we're scaling down vertically */ else if (isi->xup_yup == 1) { /*\ 'Correct' version, with math units prepared for MMXification \*/ int Cy, j; ullong* pix = nullptr; llong r, g, b, a, rr, gg, bb, aa; int yap; /* go through every scanline in the output buffer */ for (y = y_begin; y < y_end; ++y) { Cy = YAP >> 16; yap = YAP & 0xffff; dptr = dest + (y - y_begin) * dow; for (x = x_begin; x < x_end; ++x) { pix = ypoints[dyy + y] + xpoints[x]; r = (R_VAL16(pix) * yap) >> 10; g = (G_VAL16(pix) * yap) >> 10; b = (B_VAL16(pix) * yap) >> 10; a = (A_VAL16(pix) * yap) >> 10; for (j = (1 << 14) - yap; j > Cy; j -= Cy) { pix += sow; r += (R_VAL16(pix) * Cy) >> 10; g += (G_VAL16(pix) * Cy) >> 10; b += (B_VAL16(pix) * Cy) >> 10; a += (A_VAL16(pix) * Cy) >> 10; } if (j > 0) { pix += sow; r += (R_VAL16(pix) * j) >> 10; g += (G_VAL16(pix) * j) >> 10; b += (B_VAL16(pix) * j) >> 10; a += (A_VAL16(pix) * j) >> 10; } if (XAP > 0) { pix = ypoints[dyy + y] + xpoints[x] + 1; rr = (R_VAL16(pix) * yap) >> 10; gg = (G_VAL16(pix) * yap) >> 10; bb = (B_VAL16(pix) * yap) >> 10; aa = (A_VAL16(pix) * yap) >> 10; for (j = (1 << 14) - yap; j > Cy; j -= Cy) { pix += sow; rr += (R_VAL16(pix) * Cy) >> 10; gg += (G_VAL16(pix) * Cy) >> 10; bb += (B_VAL16(pix) * Cy) >> 10; aa += (A_VAL16(pix) * Cy) >> 10; } if (j > 0) { pix += sow; rr += (R_VAL16(pix) * j) >> 10; gg += (G_VAL16(pix) * j) >> 10; bb += (B_VAL16(pix) * j) >> 10; aa += (A_VAL16(pix) * j) >> 10; } r = r * INV_XAP; g = g * INV_XAP; b = b * INV_XAP; a = a * INV_XAP; r = (r + ((rr * XAP))) >> 12; g = (g + ((gg * XAP))) >> 12; b = (b + ((bb * XAP))) >> 12; a = (a + ((aa * XAP))) >> 12; } else { r >>= 4; g >>= 4; b >>= 4; a >>= 4; } R_VAL16(dptr) = r; G_VAL16(dptr) = g; B_VAL16(dptr) = b; A_VAL16(dptr) = a; ++dptr; } } } /* if we're scaling down horizontally */ else if (isi->xup_yup == 2) { /*\ 'Correct' version, with math units prepared for MMXification \*/ int Cx, j; ullong* pix = nullptr; llong r, g, b, a, rr, gg, bb, aa; int xap; /* go through every scanline in the output buffer */ for (y = y_begin; y < y_end; ++y) { dptr = dest + (y - y_begin) * dow; for (x = x_begin; x < x_end; ++x) { Cx = XAP >> 16; xap = XAP & 0xffff; pix = ypoints[dyy + y] + xpoints[x]; r = (R_VAL16(pix) * xap) >> 10; g = (G_VAL16(pix) * xap) >> 10; b = (B_VAL16(pix) * xap) >> 10; a = (A_VAL16(pix) * xap) >> 10; for (j = (1 << 14) - xap; j > Cx; j -= Cx) { ++pix; r += (R_VAL16(pix) * Cx) >> 10; g += (G_VAL16(pix) * Cx) >> 10; b += (B_VAL16(pix) * Cx) >> 10; a += (A_VAL16(pix) * Cx) >> 10; } if (j > 0) { ++pix; r += (R_VAL16(pix) * j) >> 10; g += (G_VAL16(pix) * j) >> 10; b += (B_VAL16(pix) * j) >> 10; a += (A_VAL16(pix) * j) >> 10; } if (YAP > 0) { pix = ypoints[dyy + y] + xpoints[x] + sow; rr = (R_VAL16(pix) * xap) >> 10; gg = (G_VAL16(pix) * xap) >> 10; bb = (B_VAL16(pix) * xap) >> 10; aa = (A_VAL16(pix) * xap) >> 10; for (j = (1 << 14) - xap; j > Cx; j -= Cx) { ++pix; rr += (R_VAL16(pix) * Cx) >> 10; gg += (G_VAL16(pix) * Cx) >> 10; bb += (B_VAL16(pix) * Cx) >> 10; aa += (A_VAL16(pix) * Cx) >> 10; } if (j > 0) { ++pix; rr += (R_VAL16(pix) * j) >> 10; gg += (G_VAL16(pix) * j) >> 10; bb += (B_VAL16(pix) * j) >> 10; aa += (A_VAL16(pix) * j) >> 10; } r = r * INV_YAP; g = g * INV_YAP; b = b * INV_YAP; a = a * INV_YAP; r = (r + ((rr * YAP))) >> 12; g = (g + ((gg * YAP))) >> 12; b = (b + ((bb * YAP))) >> 12; a = (a + ((aa * YAP))) >> 12; } else { r >>= 4; g >>= 4; b >>= 4; a >>= 4; } R_VAL16(dptr) = r; G_VAL16(dptr) = g; B_VAL16(dptr) = b; A_VAL16(dptr) = a; ++dptr; } } } /* if we're scaling down horizontally & vertically */ else { /*\ 'Correct' version, with math units prepared for MMXification: |*| The operation 'b = (b * c) >> 16' translates to pmulhw, |*| so the operation 'b = (b * c) >> d' would translate to |*| psllw (16 - d), %mmb; pmulh %mmc, %mmb \*/ int Cx, Cy, i, j; ullong* pix = nullptr; llong a, r, g, b, ax, rx, gx, bx; int xap, yap; for (y = y_begin; y < y_end; ++y) { Cy = YAP >> 16; yap = YAP & 0xffff; dptr = dest + (y - y_begin) * dow; for (x = x_begin; x < x_end; ++x) { Cx = XAP >> 16; xap = XAP & 0xffff; if (ypoints && xpoints) { sptr = ypoints[dyy + y] + xpoints[x]; pix = sptr; sptr += sow; rx = (R_VAL16(pix) * xap) >> 9; gx = (G_VAL16(pix) * xap) >> 9; bx = (B_VAL16(pix) * xap) >> 9; ax = (A_VAL16(pix) * xap) >> 9; ++pix; for (i = (1 << 14) - xap; i > Cx; i -= Cx) { rx += (R_VAL16(pix) * Cx) >> 9; gx += (G_VAL16(pix) * Cx) >> 9; bx += (B_VAL16(pix) * Cx) >> 9; ax += (A_VAL16(pix) * Cx) >> 9; ++pix; } if (i > 0) { rx += (R_VAL16(pix) * i) >> 9; gx += (G_VAL16(pix) * i) >> 9; bx += (B_VAL16(pix) * i) >> 9; ax += (A_VAL16(pix) * i) >> 9; } r = (rx * yap) >> 14; g = (gx * yap) >> 14; b = (bx * yap) >> 14; a = (ax * yap) >> 14; for (j = (1 << 14) - yap; j > Cy; j -= Cy) { pix = sptr; sptr += sow; rx = (R_VAL16(pix) * xap) >> 9; gx = (G_VAL16(pix) * xap) >> 9; bx = (B_VAL16(pix) * xap) >> 9; ax = (A_VAL16(pix) * xap) >> 9; ++pix; for (i = (1 << 14) - xap; i > Cx; i -= Cx) { rx += (R_VAL16(pix) * Cx) >> 9; gx += (G_VAL16(pix) * Cx) >> 9; bx += (B_VAL16(pix) * Cx) >> 9; ax += (A_VAL16(pix) * Cx) >> 9; ++pix; } if (i > 0) { rx += (R_VAL16(pix) * i) >> 9; gx += (G_VAL16(pix) * i) >> 9; bx += (B_VAL16(pix) * i) >> 9; ax += (A_VAL16(pix) * i) >> 9; } r += (rx * Cy) >> 14; g += (gx * Cy) >> 14; b += (bx * Cy) >> 14; a += (ax * Cy) >> 14; } if (j > 0) { pix = sptr; sptr += sow; (void)sptr; // disable clang warning. rx = (R_VAL16(pix) * xap) >> 9; gx = (G_VAL16(pix) * xap) >> 9; bx = (B_VAL16(pix) * xap) >> 9; ax = (A_VAL16(pix) * xap) >> 9; ++pix; for (i = (1 << 14) - xap; i > Cx; i -= Cx) { rx += (R_VAL16(pix) * Cx) >> 9; gx += (G_VAL16(pix) * Cx) >> 9; bx += (B_VAL16(pix) * Cx) >> 9; ax += (A_VAL16(pix) * Cx) >> 9; ++pix; } if (i > 0) { rx += (R_VAL16(pix) * i) >> 9; gx += (G_VAL16(pix) * i) >> 9; bx += (B_VAL16(pix) * i) >> 9; ax += (A_VAL16(pix) * i) >> 9; } r += (rx * j) >> 14; g += (gx * j) >> 14; b += (bx * j) >> 14; a += (ax * j) >> 14; } R_VAL16(dptr) = r >> 5; G_VAL16(dptr) = g >> 5; B_VAL16(dptr) = b >> 5; A_VAL16(dptr) = a >> 5; ++dptr; } } } } } } // namespace Digikam