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 }