File indexing completed on 2024-06-16 04:16:35
0001 /* 0002 * This file is part of Krita 0003 * 0004 * SPDX-FileCopyrightText: 2005 Michael Thaler <michael.thaler@physik.tu-muenchen.de> 0005 * 0006 * ported from Gimp, SPDX-FileCopyrightText: 1997 Eiichi Takamori <taka@ma1.seikyou.ne.jp> 0007 * original pixelize.c for GIMP 0.54 by Tracy Scott 0008 * 0009 * SPDX-License-Identifier: GPL-2.0-or-later 0010 */ 0011 0012 #include "kis_channel_separator.h" 0013 0014 #include <limits.h> 0015 #include <stdlib.h> 0016 #include <vector> 0017 0018 #include <QStandardPaths> 0019 0020 #include <klocalizedstring.h> 0021 #include <kis_debug.h> 0022 #include <kpluginfactory.h> 0023 0024 0025 #include <KisImportExportManager.h> 0026 #include <KoUpdater.h> 0027 #include <KoFileDialog.h> 0028 #include <KoColorSpace.h> 0029 #include <KoColorSpaceRegistry.h> 0030 #include <KoChannelInfo.h> 0031 #include <KoColorModelStandardIds.h> 0032 0033 #include <KisDocument.h> 0034 #include <kis_image.h> 0035 #include <kis_layer.h> 0036 #include <kis_paint_layer.h> 0037 #include <kis_group_layer.h> 0038 #include <kis_transaction.h> 0039 #include <kis_undo_adapter.h> 0040 #include <kis_global.h> 0041 #include <kis_types.h> 0042 #include "kis_iterator_ng.h" 0043 #include <KisPart.h> 0044 #include <KisViewManager.h> 0045 #include <kis_paint_device.h> 0046 #include <kis_node_manager.h> 0047 #include <kis_node_commands_adapter.h> 0048 #include <KisMimeDatabase.h> 0049 #include "KisImageBarrierLock.h" 0050 0051 KisChannelSeparator::KisChannelSeparator(KisViewManager * view) 0052 : m_viewManager(view) 0053 { 0054 } 0055 0056 void KisChannelSeparator::separate(KoUpdater * progressUpdater, enumSepAlphaOptions alphaOps, enumSepSource sourceOps, bool downscale, bool toColor, bool activateCurrentChannel) 0057 { 0058 KisImageSP image = m_viewManager->image(); 0059 if (!image) return; 0060 0061 KisPaintDeviceSP src; 0062 0063 // Use the flattened image, if required 0064 switch (sourceOps) { 0065 case ALL_LAYERS: 0066 // the content will be locked later 0067 src = image->projection(); 0068 break; 0069 case CURRENT_LAYER: 0070 src = m_viewManager->activeDevice(); 0071 break; 0072 default: 0073 break; 0074 } 0075 0076 if (!src) return; 0077 0078 progressUpdater->setProgress(1); 0079 0080 const KoColorSpace * dstCs = 0; 0081 0082 quint32 numberOfChannels = src->channelCount(); 0083 const KoColorSpace * srcCs = src->colorSpace(); 0084 const QList<KoChannelInfo *> channels = srcCs->channels(); 0085 vKisPaintDeviceSP paintDevices; 0086 0087 /** 0088 * We should process the entire image, even when its pixels are 0089 * transparent, because we might be pulling colors from under a 0090 * zero-alpha channel. 0091 */ 0092 const QRect rect = image->bounds(); 0093 0094 KisImageBarrierLock lock(image); 0095 int i = 0; 0096 for (QList<KoChannelInfo *>::const_iterator it = channels.constBegin(); it != channels.constEnd(); ++it) { 0097 0098 0099 KoChannelInfo *ch = (*it); 0100 0101 bool channelToColor = (toColor && ch->channelType() != KoChannelInfo::ALPHA); 0102 0103 if (ch->channelType() == KoChannelInfo::ALPHA && alphaOps != CREATE_ALPHA_SEPARATION) { 0104 continue; 0105 } 0106 0107 qint32 channelSize = ch->size(); 0108 qint32 channelPos = ch->pos(); 0109 qint32 destSize = 1; 0110 0111 KisPaintDeviceSP dev; 0112 if (channelToColor) { 0113 // We don't downscale if we separate to color channels 0114 dev = new KisPaintDevice(srcCs); 0115 } else { 0116 if (channelSize == 1 || downscale) { 0117 dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), 0)); 0118 } else { 0119 dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer16BitsColorDepthID.id(), 0)); 0120 destSize = 2; 0121 } 0122 } 0123 0124 dstCs = dev->colorSpace(); 0125 0126 paintDevices.push_back(dev); 0127 0128 KisHLineConstIteratorSP srcIt = src->createHLineConstIteratorNG(rect.x(), rect.y(), rect.width()); 0129 KisHLineIteratorSP dstIt = dev->createHLineIteratorNG(rect.x(), rect.y(), rect.width()); 0130 0131 for (qint32 row = 0; row < rect.height(); ++row) { 0132 do { 0133 if (channelToColor) { 0134 dstCs->singleChannelPixel(dstIt->rawData(), srcIt->oldRawData(), channelPos); 0135 0136 if (alphaOps == COPY_ALPHA_TO_SEPARATIONS) { 0137 dstCs->setOpacity(dstIt->rawData(), srcCs->opacityU8(srcIt->oldRawData()), 1); 0138 } else { 0139 dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1); 0140 } 0141 } else { 0142 0143 // To grayscale 0144 0145 // Decide whether we need downscaling 0146 if (channelSize == 1 && destSize == 1) { 0147 0148 // Both 8-bit channels 0149 dstIt->rawData()[0] = srcIt->oldRawData()[channelPos]; 0150 0151 if (alphaOps == COPY_ALPHA_TO_SEPARATIONS) { 0152 dstCs->setOpacity(dstIt->rawData(), srcCs->opacityU8(srcIt->oldRawData()), 1); 0153 } else { 0154 dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1); 0155 } 0156 } else if (channelSize == 2 && destSize == 2) { 0157 0158 // Both 16-bit 0159 dstIt->rawData()[0] = srcIt->oldRawData()[channelPos]; 0160 dstIt->rawData()[1] = srcIt->oldRawData()[channelPos + 1]; 0161 0162 if (alphaOps == COPY_ALPHA_TO_SEPARATIONS) { 0163 dstCs->setOpacity(dstIt->rawData(), srcCs->opacityU8(srcIt->oldRawData()), 1); 0164 } else { 0165 dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1); 0166 } 0167 } else if (channelSize != 1 && destSize == 1) { 0168 // Downscale 0169 memset(dstIt->rawData(), srcCs->scaleToU8(srcIt->oldRawData(), channelPos), 1); 0170 0171 dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1); 0172 } else if (channelSize != 2 && destSize == 2) { 0173 // Upscale 0174 dstIt->rawData()[0] = srcCs->scaleToU8(srcIt->oldRawData(), channelPos); 0175 0176 dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1); 0177 0178 } 0179 } 0180 0181 } while (dstIt->nextPixel() && srcIt->nextPixel()); 0182 dstIt->nextRow(); 0183 srcIt->nextRow(); 0184 } 0185 ++i; 0186 0187 progressUpdater->setProgress((i * 100) / numberOfChannels); 0188 if (progressUpdater->interrupted()) { 0189 break; 0190 } 0191 } 0192 0193 vKisPaintDeviceSP::const_iterator paintDeviceIterator = paintDevices.cbegin(); 0194 0195 if (!progressUpdater->interrupted()) { 0196 KisNodeCommandsAdapter adapter(m_viewManager); 0197 adapter.beginMacro(kundo2_i18n("Separate Image")); 0198 0199 for (QList<KoChannelInfo *>::const_iterator it = channels.constBegin(); it != channels.constEnd(); ++it) { 0200 0201 KoChannelInfo *ch = (*it); 0202 if (ch->channelType() == KoChannelInfo::ALPHA && alphaOps != CREATE_ALPHA_SEPARATION) { 0203 // Don't make an separate separation of the alpha channel if the user didn't ask for it. 0204 continue; 0205 } 0206 0207 KisPaintLayerSP l = new KisPaintLayer(image, ch->name(), OPACITY_OPAQUE_U8, *paintDeviceIterator); 0208 0209 if (toColor && ch->channelType() != KoChannelInfo::ALPHA && activateCurrentChannel) { 0210 QBitArray channelFlags(channels.count()); 0211 int i = 0; 0212 for (QList<KoChannelInfo *>::const_iterator it2 = channels.constBegin(); it2 != channels.constEnd(); ++it2) { 0213 channelFlags.setBit(i, (it == it2)); 0214 i++; 0215 } 0216 l->setChannelFlags(channelFlags); 0217 } 0218 0219 adapter.addNode(l.data(), image->rootLayer(), 0); 0220 ++paintDeviceIterator; 0221 } 0222 0223 adapter.endMacro(); 0224 } 0225 0226 progressUpdater->setProgress(100); 0227 0228 0229 }