File indexing completed on 2024-05-19 04:26:24
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 QRect updateExtentOnFrameChange(KisRasterKeyframeChannel *channel, 0102 int prevActiveTime, int prevIgnoredTime, 0103 int nowActiveTime, int nowIgnoredTime); 0104 0105 }; 0106 0107 KisOnionSkinCompositor *KisOnionSkinCompositor::instance() 0108 { 0109 return s_instance; 0110 } 0111 0112 KisOnionSkinCompositor::KisOnionSkinCompositor() 0113 : m_d(new Private) 0114 { 0115 m_d->refreshConfig(); 0116 } 0117 0118 KisOnionSkinCompositor::~KisOnionSkinCompositor() 0119 {} 0120 0121 int KisOnionSkinCompositor::configSeqNo() const 0122 { 0123 return m_d->configSeqNo; 0124 } 0125 0126 void KisOnionSkinCompositor::setColorLabelFilter(QSet<int> colors) 0127 { 0128 m_d->colorLabelFilter = colors; 0129 } 0130 0131 QSet<int> KisOnionSkinCompositor::colorLabelFilter() 0132 { 0133 return m_d->colorLabelFilter; 0134 } 0135 0136 void KisOnionSkinCompositor::composite(const KisPaintDeviceSP sourceDevice, KisPaintDeviceSP targetDevice, const QRect& rect) 0137 { 0138 KisRasterKeyframeChannel *keyframes = sourceDevice->keyframeChannel(); 0139 0140 KisPaintDeviceSP frameDevice = new KisPaintDevice(sourceDevice->colorSpace()); 0141 KisPainter gcFrame(frameDevice); 0142 QBitArray channelFlags = sourceDevice->colorSpace()->channelFlags(true, false); 0143 gcFrame.setChannelFlags(channelFlags); 0144 gcFrame.setOpacity(m_d->tintFactor); 0145 0146 KisPaintDeviceSP backwardTintDevice = m_d->setUpTintDevice(m_d->backwardTintColor, sourceDevice->colorSpace()); 0147 KisPaintDeviceSP forwardTintDevice = m_d->setUpTintDevice(m_d->forwardTintColor, sourceDevice->colorSpace()); 0148 0149 KisPainter gcDest(targetDevice); 0150 gcDest.setCompositeOpId(sourceDevice->colorSpace()->compositeOp(COMPOSITE_BEHIND)); 0151 0152 int keyframeTimeBck; 0153 int keyframeTimeFwd; 0154 0155 int time = sourceDevice->defaultBounds()->currentTime(); 0156 0157 if (!keyframes) { // it happens when you try to show onion skins on non-animated layer with opacity keyframes 0158 return; 0159 } 0160 0161 keyframeTimeBck = keyframeTimeFwd = keyframes->activeKeyframeTime(time); 0162 0163 for (int offset = 1; offset <= m_d->numberOfSkins; offset++) { 0164 KisRasterKeyframeSP backKeyframe = m_d->getNextFrameToComposite(keyframes, keyframeTimeBck, true); 0165 KisRasterKeyframeSP forwardKeyframe = m_d->getNextFrameToComposite(keyframes, keyframeTimeFwd, false); 0166 0167 if (!backKeyframe.isNull()) { 0168 m_d->tryCompositeFrame(backKeyframe, gcFrame, gcDest, backwardTintDevice, m_d->skinOpacity(-offset), rect); 0169 } 0170 0171 if (!forwardKeyframe.isNull()) { 0172 m_d->tryCompositeFrame(forwardKeyframe, gcFrame, gcDest, forwardTintDevice, m_d->skinOpacity(offset), rect); 0173 } 0174 } 0175 0176 } 0177 0178 QRect KisOnionSkinCompositor::calculateFullExtent(const KisPaintDeviceSP device) 0179 { 0180 QRect rect; 0181 0182 KisRasterKeyframeChannel *channel = device->keyframeChannel(); 0183 if (!channel) return rect; 0184 0185 int currentKeyTime = channel->firstKeyframeTime(); 0186 0187 while (channel->keyframeAt(currentKeyTime)) { 0188 rect |= channel->frameExtents(channel->keyframeAt(currentKeyTime)); 0189 currentKeyTime = channel->nextKeyframeTime(currentKeyTime); 0190 } 0191 0192 return rect; 0193 } 0194 0195 QRect KisOnionSkinCompositor::calculateExtent(const KisPaintDeviceSP device, int time) 0196 { 0197 QRect rect; 0198 int keyframeTimeBack; 0199 int keyframeTimeFwd; 0200 0201 KisRasterKeyframeChannel *channel = device->keyframeChannel(); //TODO: take in channel instead of device...? 0202 0203 if (!channel) { // it happens when you try to show onion skins on non-animated layer with opacity keyframes 0204 return rect; 0205 } 0206 0207 keyframeTimeBack = keyframeTimeFwd = time; 0208 0209 for (int offset = 1; offset <= m_d->numberOfSkins; offset++) { 0210 if (channel->keyframeAt(keyframeTimeBack)) { 0211 keyframeTimeBack = channel->previousKeyframeTime(keyframeTimeBack); 0212 0213 if (channel->keyframeAt(keyframeTimeBack)) { 0214 rect |= channel->frameExtents(channel->keyframeAt(keyframeTimeBack)); 0215 } 0216 } 0217 0218 if (channel->keyframeAt(keyframeTimeFwd)) { 0219 keyframeTimeFwd = channel->nextKeyframeTime(keyframeTimeFwd); 0220 0221 if (channel->keyframeAt(keyframeTimeFwd)) { 0222 rect |= channel->frameExtents(channel->keyframeAt(keyframeTimeFwd)); 0223 } 0224 } 0225 } 0226 0227 return rect; 0228 } 0229 0230 QRect KisOnionSkinCompositor::calculateExtent(const KisPaintDeviceSP device) 0231 { 0232 KisRasterKeyframeChannel *channel = device->keyframeChannel(); //TODO: take in channel instead of device...? 0233 0234 if (!channel) { // it happens when you try to show onion skins on non-animated layer with opacity keyframes 0235 return QRect(); 0236 } 0237 0238 return calculateExtent(device, channel->activeKeyframeTime()); 0239 } 0240 0241 0242 QRect KisOnionSkinCompositor::Private::updateExtentOnFrameChange(KisRasterKeyframeChannel *channel, 0243 int prevActiveTime, int prevIgnoredTime, 0244 int nowActiveTime, int nowIgnoredTime) 0245 { 0246 QRect rect; 0247 0248 std::vector<int> skinsBefore; 0249 std::vector<int> skinsAfter; 0250 0251 auto fetchSkins = [this] (KisRasterKeyframeChannel *channel, const int activeTime, int ignoredFrame) { 0252 std::vector<int> skinsLeft; 0253 std::vector<int> skinsRight; 0254 0255 int keyframeTimeBck = activeTime; 0256 int keyframeTimeFwd = activeTime; 0257 0258 auto addNextFrame = [channel, this, ignoredFrame] (int offset, int &startTime, std::vector<int> &skins, bool backwards) { 0259 KisRasterKeyframeSP keyframe; 0260 do { 0261 keyframe = getNextFrameToComposite(channel, startTime, backwards); 0262 } while (keyframe && startTime == ignoredFrame); 0263 0264 if (keyframe && skinOpacity(-offset) != OPACITY_TRANSPARENT_U8) { 0265 skins.push_back(startTime); 0266 } 0267 }; 0268 0269 for (int offset = 1; offset <= numberOfSkins; offset++) { 0270 addNextFrame(offset, keyframeTimeBck, skinsLeft, true); 0271 addNextFrame(offset, keyframeTimeFwd, skinsRight, false); 0272 } 0273 0274 std::reverse(skinsLeft.begin(), skinsLeft.end()); 0275 skinsLeft.reserve(skinsLeft.size() + skinsRight.size()); 0276 std::copy(skinsRight.begin(), skinsRight.end(), std::back_inserter(skinsLeft)); 0277 0278 return skinsLeft; 0279 }; 0280 0281 skinsBefore = fetchSkins(channel, prevActiveTime, prevIgnoredTime); 0282 skinsAfter = fetchSkins(channel, nowActiveTime, nowIgnoredTime); 0283 0284 std::vector<int> changedSkins; 0285 0286 std::set_symmetric_difference(skinsBefore.begin(), skinsBefore.end(), 0287 skinsAfter.begin(), skinsAfter.end(), 0288 std::back_inserter(changedSkins)); 0289 0290 // ENTER_FUNCTION() << ppVar(skinsBefore); 0291 // ENTER_FUNCTION() << ppVar(skinsAfter); 0292 // ENTER_FUNCTION() << ppVar(changedSkins); 0293 0294 for (auto it = changedSkins.begin(); it != changedSkins.end(); ++it) { 0295 KIS_SAFE_ASSERT_RECOVER(channel->keyframeAt(*it)) { continue; } 0296 rect |= channel->frameExtents(channel->keyframeAt(*it)); 0297 } 0298 0299 return rect; 0300 } 0301 0302 QRect KisOnionSkinCompositor::updateExtentOnAddition(const KisPaintDeviceSP device, int addedTime) 0303 { 0304 QRect rect; 0305 0306 KisRasterKeyframeChannel *channel = device->keyframeChannel(); //TODO: take in channel instead of device...? 0307 0308 if (!channel) { // it happens when you try to show onion skins on non-animated layer with opacity keyframes 0309 return rect; 0310 } 0311 0312 int currentActiveTime = channel->activeKeyframeTime(); 0313 int prevActiveTime = -1; 0314 0315 if (currentActiveTime == addedTime) { 0316 prevActiveTime = channel->previousKeyframeTime(currentActiveTime); 0317 } else { 0318 prevActiveTime = currentActiveTime; 0319 } 0320 0321 rect = m_d->updateExtentOnFrameChange(channel, 0322 prevActiveTime, addedTime, 0323 currentActiveTime, -1); 0324 0325 return rect; 0326 } 0327 0328 void KisOnionSkinCompositor::configChanged() 0329 { 0330 m_d->refreshConfig(); 0331 emit sigOnionSkinChanged(); 0332 }