Changeset View
Changeset View
Standalone View
Standalone View
plugins/dockers/channeldocker/channelmodel.cpp
Context not available. | |||||
16 | */ | 16 | */ | ||
---|---|---|---|---|---|
17 | 17 | | |||
18 | #include "channelmodel.h" | 18 | #include "channelmodel.h" | ||
19 | 19 | #include <QImage> | |||
20 | #include <KoColorSpace.h> | 20 | #include <KoColorSpace.h> | ||
21 | #include <KoChannelInfo.h> | 21 | #include <KoChannelInfo.h> | ||
22 | #include <kis_layer.h> | 22 | #include <kis_painter.h> | ||
23 | #include <kis_paint_layer.h> | 23 | | ||
24 | #include <kis_group_layer.h> | ||||
25 | #include <kis_paint_device.h> | ||||
26 | #include <kis_iterator_ng.h> | ||||
27 | #include <kis_default_bounds.h> | ||||
28 | | ||||
29 | #include <kis_canvas2.h> | ||||
24 | 30 | | |||
25 | ChannelModel::ChannelModel(QObject* parent): QAbstractTableModel(parent), m_currentLayer(0) | 31 | ChannelModel::ChannelModel(QObject* parent): | ||
32 | QAbstractTableModel(parent), | ||||
33 | m_canvas(nullptr), m_oversampleRatio(2), m_channelCount(0) | ||||
26 | { | 34 | { | ||
35 | setThumbnailSizeLimit(QSize(64, 64)); | ||||
27 | } | 36 | } | ||
28 | 37 | | |||
29 | ChannelModel::~ChannelModel() | 38 | ChannelModel::~ChannelModel() | ||
Context not available. | |||||
32 | 41 | | |||
33 | QVariant ChannelModel::data(const QModelIndex& index, int role) const | 42 | QVariant ChannelModel::data(const QModelIndex& index, int role) const | ||
34 | { | 43 | { | ||
35 | if (m_currentLayer.isValid() && index.isValid()) | 44 | if (m_canvas && index.isValid()) { | ||
36 | { | 45 | KisGroupLayerSP rootLayer = m_canvas->image()->rootLayer(); | ||
37 | QList<KoChannelInfo*> channels = m_currentLayer->colorSpace()->channels(); | 46 | const KoColorSpace* cs = rootLayer->colorSpace(); | ||
38 | int channelIndex = KoChannelInfo::displayPositionToChannelIndex(index.row(), channels); | 47 | QList<KoChannelInfo*> channels = cs->channels(); | ||
48 | | ||||
49 | int channelIndex = index.row(); | ||||
39 | 50 | | |||
40 | switch (role) { | 51 | switch (role) { | ||
41 | case Qt::DisplayRole: | 52 | case Qt::DisplayRole: { | ||
42 | { | 53 | if (index.column() == 2) { | ||
43 | return channels.at(channelIndex)->name(); | 54 | return channels.at(channelIndex)->name(); | ||
44 | } | 55 | } | ||
56 | return QVariant(); | ||||
57 | } | ||||
58 | case Qt::DecorationRole: { | ||||
59 | if (index.column() == 1) { | ||||
60 | Q_ASSERT(m_thumbnails.count() > index.row()); | ||||
61 | return QVariant(m_thumbnails.at(index.row())); | ||||
62 | } | ||||
63 | return QVariant(); | ||||
64 | } | ||||
45 | case Qt::CheckStateRole: { | 65 | case Qt::CheckStateRole: { | ||
46 | Q_ASSERT(index.row() < rowCount()); | 66 | Q_ASSERT(index.row() < rowCount()); | ||
47 | Q_ASSERT(index.column() < columnCount()); | 67 | Q_ASSERT(index.column() < columnCount()); | ||
48 | 68 | | |||
49 | if (index.column() == 0) { | 69 | if (index.column() == 0) { | ||
50 | QBitArray flags = m_currentLayer->channelFlags(); | 70 | QBitArray flags = rootLayer->channelFlags(); | ||
51 | return (flags.isEmpty() || flags.testBit(channelIndex)) ? Qt::Checked : Qt::Unchecked; | 71 | return (flags.isEmpty() || flags.testBit(channelIndex)) ? Qt::Checked : Qt::Unchecked; | ||
52 | } | 72 | } | ||
53 | 73 | return QVariant(); | |||
54 | QBitArray flags = dynamic_cast<const KisPaintLayer*>(m_currentLayer.data())->channelLockFlags(); | | |||
55 | return (flags.isEmpty() || flags.testBit(channelIndex)) ? Qt::Unchecked : Qt::Checked; | | |||
56 | } | 74 | } | ||
57 | } | 75 | } | ||
58 | } | 76 | } | ||
Context not available. | |||||
61 | 79 | | |||
62 | QVariant ChannelModel::headerData(int section, Qt::Orientation orientation, int role) const | 80 | QVariant ChannelModel::headerData(int section, Qt::Orientation orientation, int role) const | ||
63 | { | 81 | { | ||
64 | if(orientation == Qt::Horizontal && role == Qt::DisplayRole) { | 82 | Q_UNUSED(section); Q_UNUSED(orientation); Q_UNUSED(role); | ||
65 | if(section == 0) | 83 | return QVariant(); | ||
66 | return i18n("Enabled"); | | |||
67 | | ||||
68 | return i18n("Locked"); | | |||
69 | } | | |||
70 | | ||||
71 | return QAbstractItemModel::headerData(section, orientation, role); | | |||
72 | } | 84 | } | ||
73 | 85 | | |||
74 | 86 | | |||
75 | int ChannelModel::rowCount(const QModelIndex& /*parent*/) const | 87 | int ChannelModel::rowCount(const QModelIndex& /*parent*/) const | ||
76 | { | 88 | { | ||
77 | if (!m_currentLayer) return 0; | 89 | if (!m_canvas) return 0; | ||
78 | 90 | | |||
79 | return m_currentLayer.isValid() ? m_currentLayer->colorSpace()->channelCount() : 0; | 91 | return m_channelCount; | ||
80 | } | 92 | } | ||
81 | 93 | | |||
82 | int ChannelModel::columnCount(const QModelIndex& /*parent*/) const | 94 | int ChannelModel::columnCount(const QModelIndex& /*parent*/) const | ||
83 | { | 95 | { | ||
84 | if (!m_currentLayer) return 0; | 96 | if (!m_canvas) return 0; | ||
85 | 97 | | |||
86 | return dynamic_cast<const KisPaintLayer*>(m_currentLayer.data()) ? 2 : 1; | 98 | //columns are: checkbox, thumbnail, channel name | ||
99 | return 3; | ||||
87 | } | 100 | } | ||
88 | 101 | | |||
89 | 102 | | |||
90 | bool ChannelModel::setData(const QModelIndex& index, const QVariant& value, int role) | 103 | bool ChannelModel::setData(const QModelIndex& index, const QVariant& value, int role) | ||
91 | { | 104 | { | ||
92 | if (m_currentLayer.isValid() && index.isValid()) | 105 | if (m_canvas && m_canvas->image()) { | ||
93 | { | 106 | KisGroupLayerSP rootLayer = m_canvas->image()->rootLayer(); | ||
94 | QList<KoChannelInfo*> channels = m_currentLayer->colorSpace()->channels(); | 107 | const KoColorSpace* cs = rootLayer->colorSpace(); | ||
95 | int channelIndex = KoChannelInfo::displayPositionToChannelIndex(index.row(), channels); | 108 | QList<KoChannelInfo*> channels = cs->channels(); | ||
96 | 109 | Q_ASSERT(index.row() <= channels.count()); | |||
110 | | ||||
111 | int channelIndex = index.row(); | ||||
112 | | ||||
97 | if (role == Qt::CheckStateRole) { | 113 | if (role == Qt::CheckStateRole) { | ||
98 | Q_ASSERT(index.row() < rowCount()); | 114 | QBitArray flags = rootLayer->channelFlags(); | ||
99 | Q_ASSERT(index.column() < columnCount()); | 115 | flags = flags.isEmpty() ? cs->channelFlags(true, true) : flags; | ||
100 | 116 | Q_ASSERT(!flags.isEmpty()); | |||
101 | if (index.column() == 0) { | 117 | | ||
102 | QBitArray flags = m_currentLayer->channelFlags(); | 118 | flags.setBit(channelIndex, value.toInt() == Qt::Checked); | ||
119 | rootLayer->setChannelFlags(flags); | ||||
103 | 120 | | |||
104 | flags = flags.isEmpty() ? m_currentLayer->colorSpace()->channelFlags(true, true) : flags; | | |||
105 | flags.setBit(channelIndex, value.toInt() == Qt::Checked); | | |||
106 | m_currentLayer->setChannelFlags(flags); | | |||
107 | } | | |||
108 | else { //if (index.column() == 1) | | |||
109 | KisPaintLayer* paintLayer = dynamic_cast<KisPaintLayer*>(m_currentLayer.data()); | | |||
110 | QBitArray flags = paintLayer->channelLockFlags(); | | |||
111 | flags = flags.isEmpty() ? m_currentLayer->colorSpace()->channelFlags(true, true) : flags; | | |||
112 | flags.setBit(channelIndex, value.toInt() == Qt::Unchecked); | | |||
113 | paintLayer->setChannelLockFlags(flags); | | |||
114 | } | | |||
115 | | ||||
116 | emit channelFlagsChanged(); | 121 | emit channelFlagsChanged(); | ||
122 | emit dataChanged(this->index(0, 0), this->index(channels.count(), 0)); | ||||
117 | return true; | 123 | return true; | ||
118 | } | 124 | } | ||
119 | } | 125 | } | ||
120 | return false; | 126 | return false; | ||
121 | } | 127 | } | ||
122 | 128 | | |||
129 | | ||||
130 | //User double clicked on a row (but on channel checkbox) | ||||
131 | //we select this channel, and deselect all other channels (except alpha, which we don't touch) | ||||
132 | //this makes it fast to select single color channel | ||||
133 | void ChannelModel::rowActivated(const QModelIndex &index) | ||||
134 | { | ||||
135 | if (m_canvas && m_canvas->image()) { | ||||
136 | KisGroupLayerWSP rootLayer = m_canvas->image()->rootLayer(); | ||||
137 | const KoColorSpace* cs = rootLayer->colorSpace(); | ||||
138 | QList<KoChannelInfo*> channels = cs->channels(); | ||||
139 | Q_ASSERT(index.row() <= channels.count()); | ||||
140 | | ||||
141 | int channelIndex = index.row(); | ||||
142 | | ||||
143 | QBitArray flags = rootLayer->channelFlags(); | ||||
144 | flags = flags.isEmpty() ? cs->channelFlags(true, true) : flags; | ||||
145 | Q_ASSERT(!flags.isEmpty()); | ||||
146 | | ||||
147 | for (int i = 0; i < channels.count(); ++i) { | ||||
148 | if (channels[i]->channelType() != KoChannelInfo::ALPHA) { | ||||
149 | flags.setBit(i, (i == channelIndex)); | ||||
150 | } | ||||
151 | } | ||||
152 | | ||||
153 | rootLayer->setChannelFlags(flags); | ||||
154 | | ||||
155 | emit channelFlagsChanged(); | ||||
156 | emit dataChanged(this->index(0, 0), this->index(channels.count(), 0)); | ||||
157 | } | ||||
158 | } | ||||
159 | | ||||
160 | | ||||
123 | Qt::ItemFlags ChannelModel::flags(const QModelIndex& /*index*/) const | 161 | Qt::ItemFlags ChannelModel::flags(const QModelIndex& /*index*/) const | ||
124 | { | 162 | { | ||
125 | Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; | 163 | Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; | ||
126 | return flags; | 164 | return flags; | ||
127 | } | 165 | } | ||
128 | 166 | | |||
129 | void ChannelModel::slotLayerActivated(KisLayerSP layer) | 167 | void ChannelModel::unsetCanvas() | ||
130 | { | 168 | { | ||
131 | beginResetModel(); | 169 | m_canvas = 0; | ||
132 | m_currentLayer = layer; | 170 | } | ||
133 | endResetModel(); | 171 | | ||
172 | void ChannelModel::setThumbnailSizeLimit(QSize size) | ||||
173 | { | ||||
174 | m_thumbnailSizeLimit = size; | ||||
175 | updateData(m_canvas); | ||||
176 | } | ||||
177 | | ||||
178 | void ChannelModel::slotSetCanvas(KisCanvas2 *canvas) | ||||
179 | { | ||||
180 | if (m_canvas != canvas) { | ||||
181 | beginResetModel(); | ||||
182 | m_canvas = canvas; | ||||
183 | if (m_canvas && m_canvas->image()) { | ||||
184 | m_channelCount = m_canvas->image()->colorSpace()->channelCount(); | ||||
185 | updateThumbnails(); | ||||
186 | } else { | ||||
187 | m_channelCount = 0; | ||||
188 | } | ||||
189 | endResetModel(); | ||||
190 | } | ||||
134 | } | 191 | } | ||
135 | 192 | | |||
136 | void ChannelModel::slotColorSpaceChanged(const KoColorSpace *colorSpace) | 193 | void ChannelModel::slotColorSpaceChanged(const KoColorSpace *colorSpace) | ||
137 | { | 194 | { | ||
138 | Q_UNUSED(colorSpace); | 195 | Q_UNUSED(colorSpace); | ||
139 | slotLayerActivated(m_currentLayer); | 196 | beginResetModel(); | ||
197 | updateThumbnails(); | ||||
198 | endResetModel(); | ||||
199 | } | ||||
200 | | ||||
201 | void ChannelModel::updateData(KisCanvas2 *canvas) | ||||
202 | { | ||||
203 | beginResetModel(); | ||||
204 | m_canvas = canvas; | ||||
205 | m_channelCount = (m_canvas) ? m_canvas->image()->colorSpace()->channelCount() : 0; | ||||
206 | updateThumbnails(); | ||||
207 | endResetModel(); | ||||
208 | } | ||||
209 | | ||||
210 | | ||||
211 | | ||||
212 | //Create thumbnails from full image. | ||||
213 | //Assumptions: thumbnail size is small compared to the original image and thumbnail quality | ||||
214 | //doesn't need to be high, so we use fast but not very accurate algorithm. | ||||
215 | void ChannelModel::updateThumbnails(void) | ||||
216 | { | ||||
217 | | ||||
218 | if (m_canvas && m_canvas->image()) { | ||||
219 | KisImageSP canvas_image = m_canvas->image(); | ||||
220 | const KoColorSpace* cs = canvas_image->colorSpace(); | ||||
221 | m_channelCount = cs->channelCount(); | ||||
222 | | ||||
223 | KisPaintDeviceSP dev = canvas_image->projection(); | ||||
224 | | ||||
225 | //make sure thumbnail maintains aspect ratio of the original image | ||||
226 | QSize thumbnailSize(canvas_image->bounds().size()); | ||||
227 | thumbnailSize.scale(m_thumbnailSizeLimit, Qt::KeepAspectRatio); | ||||
228 | | ||||
229 | KisPaintDeviceSP thumbnailDev = dev->createThumbnailDeviceOversampled(thumbnailSize.width(), thumbnailSize.height(), | ||||
230 | m_oversampleRatio, canvas_image->bounds()); | ||||
231 | | ||||
232 | m_thumbnails.resize(m_channelCount); | ||||
233 | | ||||
234 | for (quint32 i = 0; i < m_channelCount; ++i) { | ||||
235 | m_thumbnails[i] = QImage(thumbnailSize, QImage::Format_Grayscale8); | ||||
236 | } | ||||
237 | | ||||
238 | KisSequentialConstIterator it(thumbnailDev, QRect(0, 0, thumbnailSize.width(), thumbnailSize.height())); | ||||
239 | | ||||
240 | for (int y = 0; y < thumbnailSize.height(); y++) { | ||||
241 | for (int x = 0; x < thumbnailSize.width(); x++) { | ||||
242 | const quint8* pixel = it.rawDataConst(); | ||||
243 | for (int chan = 0; chan < m_channelCount; ++chan) { | ||||
244 | QImage &img = m_thumbnails[chan]; | ||||
245 | *(img.scanLine(y) + x) = cs->scaleToU8(pixel, chan); | ||||
246 | } | ||||
247 | it.nextPixel(); | ||||
248 | } | ||||
249 | } | ||||
250 | } else { | ||||
251 | m_channelCount = 0; | ||||
252 | } | ||||
140 | } | 253 | } | ||
141 | 254 | | |||
142 | #include "moc_channelmodel.cpp" | 255 | #include "moc_channelmodel.cpp" | ||
Context not available. |