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

0001 /*
0002  *  SPDX-FileCopyrightText: 2015 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_layer_projection_plane.h"
0008 
0009 #include <QBitArray>
0010 #include <KoColorSpace.h>
0011 #include <KoChannelInfo.h>
0012 #include <KoCompositeOpRegistry.h>
0013 #include "kis_painter.h"
0014 #include "kis_projection_leaf.h"
0015 #include "kis_cached_paint_device.h"
0016 #include "kis_sequential_iterator.h"
0017 
0018 
0019 struct KisLayerProjectionPlane::Private
0020 {
0021     KisLayer *layer;
0022     KisCachedPaintDevice cachedDevice;
0023 };
0024 
0025 
0026 KisLayerProjectionPlane::KisLayerProjectionPlane(KisLayer *layer)
0027     : m_d(new Private)
0028 {
0029     m_d->layer = layer;
0030 }
0031 
0032 KisLayerProjectionPlane::~KisLayerProjectionPlane()
0033 {
0034 }
0035 
0036 QRect KisLayerProjectionPlane::recalculate(const QRect& rect, KisNodeSP filthyNode)
0037 {
0038     return m_d->layer->updateProjection(rect, filthyNode);
0039 }
0040 
0041 void KisLayerProjectionPlane::applyImpl(KisPainter *painter, const QRect &rect, KritaUtils::ThresholdMode thresholdMode)
0042 {
0043     KisPaintDeviceSP device = m_d->layer->projection();
0044     if (!device) return;
0045 
0046     QRect needRect = rect;
0047 
0048     if (m_d->layer->compositeOpId() != COMPOSITE_COPY &&
0049         m_d->layer->compositeOpId() != COMPOSITE_DESTINATION_IN  &&
0050         m_d->layer->compositeOpId() != COMPOSITE_DESTINATION_ATOP) {
0051 
0052         needRect &= device->extent();
0053     }
0054 
0055     if(needRect.isEmpty()) return;
0056 
0057     QBitArray channelFlags = m_d->layer->projectionLeaf()->channelFlags();
0058 
0059 
0060     // if the color spaces don't match we will have a problem with the channel flags
0061     // because the channel flags from the source layer doesn't match with the colorspace of the projection device
0062     // this leads to the situation that the wrong channels will be enabled/disabled
0063     const KoColorSpace* srcCS = device->colorSpace();
0064     const KoColorSpace* dstCS = painter->device()->colorSpace();
0065 
0066     if (!channelFlags.isEmpty() && srcCS != dstCS) {
0067         bool alphaFlagIsSet        = (srcCS->channelFlags(false,true) & channelFlags) == srcCS->channelFlags(false,true);
0068         bool allColorFlagsAreSet   = (srcCS->channelFlags(true,false) & channelFlags) == srcCS->channelFlags(true,false);
0069         bool allColorFlagsAreUnset = (srcCS->channelFlags(true,false) & channelFlags).count(true) == 0;
0070 
0071         if(allColorFlagsAreSet) {
0072             channelFlags = dstCS->channelFlags(true, alphaFlagIsSet);
0073         } else if(allColorFlagsAreUnset) {
0074             channelFlags = dstCS->channelFlags(false, alphaFlagIsSet);
0075         } else {
0076             //TODO: convert the cannel flags properly
0077             //      for now just the alpha channel bit is copied and the other channels are left alone
0078             for (quint32 i=0; i < dstCS->channelCount(); ++i) {
0079                 if (dstCS->channels()[i]->channelType() == KoChannelInfo::ALPHA) {
0080                     channelFlags.setBit(i, alphaFlagIsSet);
0081                     break;
0082                 }
0083             }
0084         }
0085     }
0086 
0087     QScopedPointer<KisCachedPaintDevice::Guard> d1;
0088 
0089     if (thresholdMode != KritaUtils::ThresholdNone) {
0090         d1.reset(new KisCachedPaintDevice::Guard(device, m_d->cachedDevice));
0091         KisPaintDeviceSP tmp = d1->device();
0092         tmp->makeCloneFromRough(device, needRect);
0093 
0094         KritaUtils::thresholdOpacity(tmp, needRect, thresholdMode);
0095 
0096         device = tmp;
0097     }
0098 
0099     painter->setChannelFlags(channelFlags);
0100     painter->setCompositeOpId(m_d->layer->compositeOpId());
0101     painter->setOpacity(m_d->layer->projectionLeaf()->opacity());
0102     painter->bitBlt(needRect.topLeft(), device, needRect);
0103 }
0104 
0105 void KisLayerProjectionPlane::apply(KisPainter *painter, const QRect &rect)
0106 {
0107     applyImpl(painter, rect, KritaUtils::ThresholdNone);
0108 }
0109 
0110 void KisLayerProjectionPlane::applyMaxOutAlpha(KisPainter *painter, const QRect &rect, KritaUtils::ThresholdMode thresholdMode)
0111 {
0112     applyImpl(painter, rect, thresholdMode);
0113 }
0114 
0115 KisPaintDeviceList KisLayerProjectionPlane::getLodCapableDevices() const
0116 {
0117     return KisPaintDeviceList() << m_d->layer->projection();
0118 }
0119 
0120 QRect KisLayerProjectionPlane::needRect(const QRect &rect, KisLayer::PositionToFilthy pos) const
0121 {
0122     return m_d->layer->needRect(rect, pos);
0123 }
0124 
0125 QRect KisLayerProjectionPlane::changeRect(const QRect &rect, KisLayer::PositionToFilthy pos) const
0126 {
0127     return m_d->layer->changeRect(rect, pos);
0128 }
0129 
0130 QRect KisLayerProjectionPlane::accessRect(const QRect &rect, KisLayer::PositionToFilthy pos) const
0131 {
0132     return m_d->layer->accessRect(rect, pos);
0133 }
0134 
0135 QRect KisLayerProjectionPlane::needRectForOriginal(const QRect &rect) const
0136 {
0137     return m_d->layer->needRectForOriginal(rect);
0138 }
0139 
0140 QRect KisLayerProjectionPlane::tightUserVisibleBounds() const
0141 {
0142     return m_d->layer->tightUserVisibleBounds();
0143 }
0144