File indexing completed on 2024-05-12 15:58:29
0001 /* 0002 * SPDX-FileCopyrightText: 2015 Jouni Pentikäinen <joupent@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "kis_onion_skin_compositor.h" 0008 0009 #include "kis_paint_device.h" 0010 #include "kis_painter.h" 0011 #include "KoColor.h" 0012 #include "KoColorSpace.h" 0013 #include "KoCompositeOpRegistry.h" 0014 #include "KoColorSpaceConstants.h" 0015 #include "kis_image_config.h" 0016 #include "kis_raster_keyframe_channel.h" 0017 0018 Q_GLOBAL_STATIC(KisOnionSkinCompositor, s_instance) 0019 0020 struct KisOnionSkinCompositor::Private 0021 { 0022 int numberOfSkins = 0; 0023 int tintFactor = 0; 0024 QColor backwardTintColor; 0025 QColor forwardTintColor; 0026 QVector<int> backwardOpacities; 0027 QVector<int> forwardOpacities; 0028 int configSeqNo = 0; 0029 QSet<int> colorLabelFilter; 0030 0031 int skinOpacity(int offset) 0032 { 0033 const QVector<int> &bo = backwardOpacities; 0034 const QVector<int> &fo = forwardOpacities; 0035 0036 return offset > 0 ? fo[qAbs(offset) - 1] : bo[qAbs(offset) - 1]; 0037 } 0038 0039 KisPaintDeviceSP setUpTintDevice(const QColor &tintColor, const KoColorSpace *colorSpace) 0040 { 0041 KisPaintDeviceSP tintDevice = new KisPaintDevice(colorSpace); 0042 KoColor color = KoColor(tintColor, colorSpace); 0043 tintDevice->setDefaultPixel(color); 0044 return tintDevice; 0045 } 0046 0047 0048 KisRasterKeyframeSP getNextFrameToComposite(KisKeyframeChannel *channel, int &outFrame, bool backwards) // TODO: Double-check this function... outFrame might be weird? 0049 { 0050 while (!channel->keyframeAt(outFrame).isNull()) { 0051 outFrame = backwards ? channel->previousKeyframeTime(outFrame) : channel->nextKeyframeTime(outFrame); 0052 if (colorLabelFilter.isEmpty()) { 0053 return channel->keyframeAt<KisRasterKeyframe>(outFrame); 0054 } else if (channel->keyframeAt<KisRasterKeyframe>(outFrame)) { 0055 if (colorLabelFilter.contains(channel->keyframeAt(outFrame)->colorLabel())) { 0056 return channel->keyframeAt<KisRasterKeyframe>(outFrame); 0057 } 0058 } 0059 } 0060 return channel->keyframeAt<KisRasterKeyframe>(outFrame); 0061 } 0062 0063 void tryCompositeFrame(KisRasterKeyframeSP keyframe, KisPainter &gcFrame, KisPainter &gcDest, KisPaintDeviceSP tintSource, int opacity, const QRect &rect) 0064 { 0065 if (keyframe.isNull() || opacity == OPACITY_TRANSPARENT_U8) return; 0066 0067 keyframe->writeFrameToDevice(gcFrame.device()); 0068 0069 gcFrame.bitBlt(rect.topLeft(), tintSource, rect); 0070 0071 gcDest.setOpacity(opacity); 0072 gcDest.bitBlt(rect.topLeft(), gcFrame.device(), rect); 0073 } 0074 0075 void refreshConfig() 0076 { 0077 KisImageConfig config(true); 0078 0079 numberOfSkins = config.numberOfOnionSkins(); 0080 tintFactor = config.onionSkinTintFactor(); 0081 backwardTintColor = config.onionSkinTintColorBackward(); 0082 forwardTintColor = config.onionSkinTintColorForward(); 0083 0084 backwardOpacities.resize(numberOfSkins); 0085 forwardOpacities.resize(numberOfSkins); 0086 0087 const int mainState = (int) config.onionSkinState(0); 0088 const qreal scaleFactor = mainState * config.onionSkinOpacity(0) / 255.0; 0089 0090 for (int i = 0; i < numberOfSkins; i++) { 0091 int backwardState = (int) config.onionSkinState(-(i + 1)); 0092 int forwardState = (int) config.onionSkinState(i + 1); 0093 0094 backwardOpacities[i] = scaleFactor * backwardState * config.onionSkinOpacity(-(i + 1)); 0095 forwardOpacities[i] = scaleFactor * forwardState * config.onionSkinOpacity(i + 1); 0096 } 0097 0098 configSeqNo++; 0099 } 0100 }; 0101 0102 KisOnionSkinCompositor *KisOnionSkinCompositor::instance() 0103 { 0104 return s_instance; 0105 } 0106 0107 KisOnionSkinCompositor::KisOnionSkinCompositor() 0108 : m_d(new Private) 0109 { 0110 m_d->refreshConfig(); 0111 } 0112 0113 KisOnionSkinCompositor::~KisOnionSkinCompositor() 0114 {} 0115 0116 int KisOnionSkinCompositor::configSeqNo() const 0117 { 0118 return m_d->configSeqNo; 0119 } 0120 0121 void KisOnionSkinCompositor::setColorLabelFilter(QSet<int> colors) 0122 { 0123 m_d->colorLabelFilter = colors; 0124 } 0125 0126 QSet<int> KisOnionSkinCompositor::colorLabelFilter() 0127 { 0128 return m_d->colorLabelFilter; 0129 } 0130 0131 void KisOnionSkinCompositor::composite(const KisPaintDeviceSP sourceDevice, KisPaintDeviceSP targetDevice, const QRect& rect) 0132 { 0133 KisRasterKeyframeChannel *keyframes = sourceDevice->keyframeChannel(); 0134 0135 KisPaintDeviceSP frameDevice = new KisPaintDevice(sourceDevice->colorSpace()); 0136 KisPainter gcFrame(frameDevice); 0137 QBitArray channelFlags = targetDevice->colorSpace()->channelFlags(true, false); 0138 gcFrame.setChannelFlags(channelFlags); 0139 gcFrame.setOpacity(m_d->tintFactor); 0140 0141 KisPaintDeviceSP backwardTintDevice = m_d->setUpTintDevice(m_d->backwardTintColor, sourceDevice->colorSpace()); 0142 KisPaintDeviceSP forwardTintDevice = m_d->setUpTintDevice(m_d->forwardTintColor, sourceDevice->colorSpace()); 0143 0144 KisPainter gcDest(targetDevice); 0145 gcDest.setCompositeOpId(sourceDevice->colorSpace()->compositeOp(COMPOSITE_BEHIND)); 0146 0147 int keyframeTimeBck; 0148 int keyframeTimeFwd; 0149 0150 int time = sourceDevice->defaultBounds()->currentTime(); 0151 0152 if (!keyframes) { // it happens when you try to show onion skins on non-animated layer with opacity keyframes 0153 return; 0154 } 0155 0156 keyframeTimeBck = keyframeTimeFwd = keyframes->activeKeyframeTime(time); 0157 0158 for (int offset = 1; offset <= m_d->numberOfSkins; offset++) { 0159 KisRasterKeyframeSP backKeyframe = m_d->getNextFrameToComposite(keyframes, keyframeTimeBck, true); 0160 KisRasterKeyframeSP forwardKeyframe = m_d->getNextFrameToComposite(keyframes, keyframeTimeFwd, false); 0161 0162 if (!backKeyframe.isNull()) { 0163 m_d->tryCompositeFrame(backKeyframe, gcFrame, gcDest, backwardTintDevice, m_d->skinOpacity(-offset), rect); 0164 } 0165 0166 if (!forwardKeyframe.isNull()) { 0167 m_d->tryCompositeFrame(forwardKeyframe, gcFrame, gcDest, forwardTintDevice, m_d->skinOpacity(offset), rect); 0168 } 0169 } 0170 0171 } 0172 0173 QRect KisOnionSkinCompositor::calculateFullExtent(const KisPaintDeviceSP device) 0174 { 0175 QRect rect; 0176 0177 KisRasterKeyframeChannel *channel = device->keyframeChannel(); 0178 if (!channel) return rect; 0179 0180 int currentKeyTime = channel->firstKeyframeTime(); 0181 0182 while (channel->keyframeAt(currentKeyTime)) { 0183 rect |= channel->frameExtents(channel->keyframeAt(currentKeyTime)); 0184 currentKeyTime = channel->nextKeyframeTime(currentKeyTime); 0185 } 0186 0187 return rect; 0188 } 0189 0190 QRect KisOnionSkinCompositor::calculateExtent(const KisPaintDeviceSP device) 0191 { 0192 QRect rect; 0193 int keyframeTimeBack; 0194 int keyframeTimeFwd; 0195 0196 KisRasterKeyframeChannel *channel = device->keyframeChannel(); //TODO: take in channel instead of device...? 0197 0198 if (!channel) { // it happens when you try to show onion skins on non-animated layer with opacity keyframes 0199 return rect; 0200 } 0201 0202 keyframeTimeBack = keyframeTimeFwd = channel->activeKeyframeTime(); 0203 0204 for (int offset = 1; offset <= m_d->numberOfSkins; offset++) { 0205 if (channel->keyframeAt(keyframeTimeBack)) { 0206 keyframeTimeBack = channel->previousKeyframeTime(keyframeTimeBack); 0207 0208 if (channel->keyframeAt(keyframeTimeBack)) { 0209 rect |= channel->frameExtents(channel->keyframeAt(keyframeTimeBack)); 0210 } 0211 } 0212 0213 if (channel->keyframeAt(keyframeTimeFwd)) { 0214 keyframeTimeFwd = channel->nextKeyframeTime(keyframeTimeFwd); 0215 0216 if (channel->keyframeAt(keyframeTimeFwd)) { 0217 rect |= channel->frameExtents(channel->keyframeAt(keyframeTimeFwd)); 0218 } 0219 } 0220 } 0221 0222 return rect; 0223 } 0224 0225 void KisOnionSkinCompositor::configChanged() 0226 { 0227 m_d->refreshConfig(); 0228 emit sigOnionSkinChanged(); 0229 }