File indexing completed on 2024-05-12 15:58:16

0001 /*
0002  *  SPDX-FileCopyrightText: 2009 Boudewijn Rempt <boud@valdyas.org>
0003  *  SPDX-FileCopyrightText: 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 #include "kis_fixed_paint_device.h"
0008 
0009 #include <KoColorSpaceRegistry.h>
0010 #include <KoColor.h>
0011 #include <KoColorModelStandardIds.h>
0012 #include "kis_debug.h"
0013 
0014 KisFixedPaintDevice::KisFixedPaintDevice(const KoColorSpace* colorSpace, KisOptimizedByteArray::MemoryAllocatorSP allocator)
0015         : m_colorSpace(colorSpace),
0016           m_data(allocator)
0017 {
0018 }
0019 
0020 KisFixedPaintDevice::~KisFixedPaintDevice()
0021 {
0022 }
0023 
0024 KisFixedPaintDevice::KisFixedPaintDevice(const KisFixedPaintDevice& rhs)
0025         : KisShared()
0026 {
0027     m_bounds = rhs.m_bounds;
0028     m_colorSpace = rhs.m_colorSpace;
0029     m_data = rhs.m_data;
0030 }
0031 
0032 KisFixedPaintDevice& KisFixedPaintDevice::operator=(const KisFixedPaintDevice& rhs)
0033 {
0034     m_bounds = rhs.m_bounds;
0035     m_colorSpace = rhs.m_colorSpace;
0036 
0037 
0038     const int referenceSize = m_bounds.height() * m_bounds.width() * pixelSize();
0039 
0040     if (m_data.size() >= referenceSize) {
0041         memcpy(m_data.data(), rhs.m_data.constData(), referenceSize);
0042     } else {
0043         m_data = rhs.m_data;
0044     }
0045 
0046     return *this;
0047 }
0048 
0049 void KisFixedPaintDevice::setRect(const QRect& rc)
0050 {
0051     m_bounds = rc;
0052 }
0053 
0054 void KisFixedPaintDevice::setColorSpace(const KoColorSpace *cs)
0055 {
0056     m_colorSpace = cs;
0057 }
0058 
0059 QRect KisFixedPaintDevice::bounds() const
0060 {
0061     return m_bounds;
0062 }
0063 
0064 
0065 int KisFixedPaintDevice::allocatedPixels() const
0066 {
0067     return m_data.size() / m_colorSpace->pixelSize();
0068 }
0069 
0070 
0071 quint32 KisFixedPaintDevice::pixelSize() const
0072 {
0073     return m_colorSpace->pixelSize();
0074 }
0075 
0076 bool KisFixedPaintDevice::initialize(quint8 defaultValue)
0077 {
0078     m_data.fill(defaultValue, m_bounds.height() * m_bounds.width() * pixelSize());
0079 
0080     return true;
0081 }
0082 
0083 void KisFixedPaintDevice::reallocateBufferWithoutInitialization()
0084 {
0085     const int referenceSize = m_bounds.height() * m_bounds.width() * pixelSize();
0086 
0087     if (referenceSize != m_data.size()) {
0088         m_data.resize(m_bounds.height() * m_bounds.width() * pixelSize());
0089     }
0090 }
0091 
0092 void KisFixedPaintDevice::lazyGrowBufferWithoutInitialization()
0093 {
0094     const int referenceSize = m_bounds.height() * m_bounds.width() * pixelSize();
0095 
0096     if (m_data.size() < referenceSize) {
0097         m_data.resize(referenceSize);
0098     }
0099 }
0100 
0101 quint8* KisFixedPaintDevice::data()
0102 {
0103     return (quint8*) m_data.data();
0104 }
0105 
0106 const quint8 *KisFixedPaintDevice::constData() const
0107 {
0108     return (const quint8*) m_data.constData();
0109 }
0110 
0111 quint8* KisFixedPaintDevice::data() const
0112 {
0113     return const_cast<quint8*>(m_data.constData());
0114 }
0115 
0116 void KisFixedPaintDevice::convertTo(const KoColorSpace* dstColorSpace,
0117                                     KoColorConversionTransformation::Intent renderingIntent,
0118                                     KoColorConversionTransformation::ConversionFlags conversionFlags)
0119 {
0120     if (*m_colorSpace == *dstColorSpace) {
0121         return;
0122     }
0123     quint32 size = m_bounds.width() * m_bounds.height();
0124     KisOptimizedByteArray dstData(m_data.customMemoryAllocator());
0125 
0126     // make sure that we are not initializing the destination pixels!
0127     dstData.resize(size * dstColorSpace->pixelSize());
0128 
0129     m_colorSpace->convertPixelsTo(constData(), (quint8*)dstData.data(),
0130                                   dstColorSpace,
0131                                   size,
0132                                   renderingIntent,
0133                                   conversionFlags);
0134 
0135     m_colorSpace = dstColorSpace;
0136     m_data = dstData;
0137 }
0138 
0139 void KisFixedPaintDevice::setProfile(const KoColorProfile *profile)
0140 {
0141     KIS_SAFE_ASSERT_RECOVER_RETURN(profile);
0142 
0143     const KoColorSpace *dstColorSpace =
0144         KoColorSpaceRegistry::instance()->colorSpace(
0145                 colorSpace()->colorModelId().id(),
0146                 colorSpace()->colorDepthId().id(),
0147                 profile);
0148 
0149     KIS_SAFE_ASSERT_RECOVER_RETURN(dstColorSpace);
0150 
0151     m_colorSpace = dstColorSpace;
0152 }
0153 
0154 void KisFixedPaintDevice::convertFromQImage(const QImage& _image, const QString &srcProfileName)
0155 {
0156     QImage image = _image;
0157 
0158     if (image.format() != QImage::Format_ARGB32) {
0159         image = image.convertToFormat(QImage::Format_ARGB32);
0160     }
0161     setRect(image.rect());
0162     lazyGrowBufferWithoutInitialization();
0163 
0164     // Don't convert if not no profile is given and both paint dev and qimage are rgba.
0165     if (srcProfileName.isEmpty() && colorSpace()->id() == "RGBA") {
0166 #if QT_VERSION >= QT_VERSION_CHECK(5,10,0)
0167         memcpy(data(), image.constBits(), image.sizeInBytes());
0168 #else
0169         memcpy(data(), image.constBits(), image.byteCount());
0170 #endif
0171     } else {
0172         KoColorSpaceRegistry::instance()
0173             ->colorSpace( RGBAColorModelID.id(), Integer8BitsColorDepthID.id(), srcProfileName)
0174             ->convertPixelsTo(image.constBits(), data(), colorSpace(), image.width() * image.height(),
0175                               KoColorConversionTransformation::internalRenderingIntent(),
0176                               KoColorConversionTransformation::internalConversionFlags());
0177     }
0178 }
0179 
0180 QImage KisFixedPaintDevice::convertToQImage(const KoColorProfile *  dstProfile, KoColorConversionTransformation::Intent intent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
0181 {
0182     qint32 x1;
0183     qint32 y1;
0184     qint32 w;
0185     qint32 h;
0186 
0187     x1 = m_bounds.x();
0188     y1 = m_bounds.y();
0189     w = m_bounds.width();
0190     h = m_bounds.height();
0191 
0192     return convertToQImage(dstProfile, x1, y1, w, h, intent, conversionFlags);
0193 }
0194 
0195 QImage KisFixedPaintDevice::convertToQImage(const KoColorProfile *  dstProfile, qint32 x1, qint32 y1, qint32 w, qint32 h, KoColorConversionTransformation::Intent intent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
0196 {
0197     Q_ASSERT( m_bounds.contains(QRect(x1,y1,w,h)) );
0198 
0199     if (w < 0)
0200         return QImage();
0201 
0202     if (h < 0)
0203         return QImage();
0204 
0205     if (QRect(x1, y1, w, h) == m_bounds) {
0206         return colorSpace()->convertToQImage(constData(), w, h, dstProfile,
0207                                              intent, conversionFlags);
0208     } else {
0209         try {
0210             // XXX: fill the image row by row!
0211             const int pSize = pixelSize();
0212             const int deviceWidth = m_bounds.width();
0213             quint8* newData = new quint8[w * h * pSize];
0214             const quint8* srcPtr = constData() + x1 * pSize + y1 * deviceWidth * pSize;
0215             quint8* dstPtr = newData;
0216             // copy the right area out of the paint device into data
0217             for (int row = 0; row < h; row++) {
0218                 memcpy(dstPtr, srcPtr, w * pSize);
0219                 srcPtr += deviceWidth * pSize;
0220                 dstPtr += w * pSize;
0221             }
0222             QImage image = colorSpace()->convertToQImage(newData, w, h, dstProfile, intent, conversionFlags);
0223             return image;
0224         }
0225         catch(const std::bad_alloc&) {
0226             return QImage();
0227         }
0228     }
0229 }
0230 
0231 void KisFixedPaintDevice::clear(const QRect & rc)
0232 {
0233     KoColor c(Qt::black, m_colorSpace);
0234     quint8* black = new quint8[pixelSize()];
0235     memcpy(black, c.data(), m_colorSpace->pixelSize());
0236     m_colorSpace->setOpacity(black, OPACITY_TRANSPARENT_U8, 1);
0237     fill(rc.x(), rc.y(), rc.width(), rc.height(), black);
0238     delete[] black;
0239 }
0240 
0241 void KisFixedPaintDevice::fill(const QRect &rc, const KoColor &color)
0242 {
0243     KoColor realColor(color);
0244     realColor.convertTo(colorSpace());
0245     fill(rc.x(), rc.y(), rc.width(), rc.height(), realColor.data());
0246 }
0247 
0248 void KisFixedPaintDevice::fill(qint32 x, qint32 y, qint32 w, qint32 h, const quint8 *fillPixel)
0249 {
0250     if (m_data.isEmpty() || m_bounds.isEmpty()) {
0251         setRect(QRect(x, y, w, h));
0252         reallocateBufferWithoutInitialization();
0253     }
0254 
0255     QRect rc(x, y, w, h);
0256     if (!m_bounds.contains(rc)) {
0257         rc = m_bounds;
0258     }
0259 
0260     quint8 pixelSize = m_colorSpace->pixelSize();
0261     quint8* dabPointer = data();
0262 
0263     if (rc.contains(m_bounds)) {
0264         for (int i = 0; i < w * h ; ++i) {
0265             memcpy(dabPointer, fillPixel, pixelSize);
0266             dabPointer += pixelSize;
0267         }
0268 
0269     } else {
0270         int deviceWidth = bounds().width();
0271         quint8* rowPointer = dabPointer + ((y - bounds().y()) * deviceWidth + (x - bounds().x())) * pixelSize;
0272         for (int row = 0; row < h; row++) {
0273             for (int col = 0; col < w; col++) {
0274                 memcpy(rowPointer + col * pixelSize , fillPixel, pixelSize);
0275             }
0276             rowPointer += deviceWidth * pixelSize;
0277         }
0278     }
0279 }
0280 
0281 
0282 void KisFixedPaintDevice::readBytes(quint8* dstData, qint32 x, qint32 y, qint32 w, qint32 h) const
0283 {
0284     if (m_data.isEmpty() || m_bounds.isEmpty()) {
0285         return;
0286     }
0287 
0288     QRect rc(x, y, w, h);
0289     if (!m_bounds.contains(rc)){
0290         return;
0291     }
0292 
0293     const int pixelSize = m_colorSpace->pixelSize();
0294     const quint8* dabPointer = constData();
0295 
0296     if (rc == m_bounds) {
0297         memcpy(dstData, dabPointer, pixelSize * w * h);
0298     } else {
0299         int deviceWidth = m_bounds.width();
0300         const quint8* rowPointer = dabPointer + ((y - bounds().y()) * deviceWidth + (x - bounds().x())) * pixelSize;
0301         for (int row = 0; row < h; row++) {
0302             memcpy(dstData, rowPointer, w * pixelSize);
0303             rowPointer += deviceWidth * pixelSize;
0304             dstData += w * pixelSize;
0305         }
0306     }
0307 }
0308 
0309 void KisFixedPaintDevice::mirror(bool horizontal, bool vertical)
0310 {
0311     if (!horizontal && !vertical){
0312         return;
0313     }
0314 
0315     int pixelSize = m_colorSpace->pixelSize();
0316     int w = m_bounds.width();
0317     int h = m_bounds.height();
0318 
0319     if (horizontal){
0320         int rowSize = pixelSize * w;
0321 
0322         quint8 * dabPointer = data();
0323         quint8 * row = new quint8[ rowSize ];
0324         quint8 * mirror = 0;
0325 
0326         for (int y = 0; y < h ; y++){
0327             // TODO: implement better flipping of the data
0328 
0329             memcpy(row, dabPointer, rowSize);
0330             mirror = row;
0331             mirror += (w-1) * pixelSize;
0332             for (int x = 0; x < w; x++){
0333                 memcpy(dabPointer,mirror,pixelSize);
0334                 dabPointer += pixelSize;
0335                 mirror -= pixelSize;
0336             }
0337         }
0338 
0339         delete [] row;
0340     }
0341 
0342     if (vertical){
0343         int rowsToMove = h / 2;
0344         int rowSize = pixelSize * w;
0345 
0346         quint8 * startRow = data();
0347         quint8 * endRow = data() + (h-1) * w * pixelSize;
0348         quint8 * row = new quint8[ rowSize ];
0349 
0350         for (int y = 0; y < rowsToMove; y++){
0351             memcpy(row, startRow, rowSize);
0352             memcpy(startRow, endRow, rowSize);
0353             memcpy(endRow, row, rowSize);
0354 
0355             startRow += rowSize;
0356             endRow -= rowSize;
0357         }
0358 
0359         delete [] row;
0360     }
0361 
0362 }