Krita Source Code Documentation
Loading...
Searching...
No Matches
KoBasicHistogramProducers.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2005 Bart Coppens <kde@bartcoppens.be>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-or-later
5 */
6
8
9#include <QString>
10#include <klocalizedstring.h>
11
12#include <KoConfig.h>
13#ifdef HAVE_OPENEXR
14#include <half.h>
15#endif
16
17// #include "Ko_global.h"
18#include "KoIntegerMaths.h"
19#include "KoChannelInfo.h"
20
21static const KoColorSpace* m_labCs = 0;
22
23
24KoBasicHistogramProducer::KoBasicHistogramProducer(const KoID& id, int channelCount, int nrOfBins)
25 : m_channels(channelCount)
26 , m_nrOfBins(nrOfBins)
27 , m_colorSpace(0)
28 , m_id(id)
29{
30 m_bins.resize(m_channels);
31 for (int i = 0; i < m_channels; i++)
32 m_bins[i].resize(m_nrOfBins);
33 m_outLeft.resize(m_channels);
34 m_outRight.resize(m_channels);
35 m_count = 0;
36 m_from = 0.0;
37 m_width = 1.0;
38}
39
41 : m_nrOfBins(nrOfBins),
42 m_colorSpace(cs),
43 m_id(id)
44{
45 Q_ASSERT(cs);
47
48 m_bins.resize(m_channels);
49 for (int i = 0; i < m_channels; i++)
50 m_bins[i].resize(m_nrOfBins);
51 m_outLeft.resize(m_channels);
52 m_outRight.resize(m_channels);
53 m_count = 0;
54 m_from = 0.0;
55 m_width = 1.0;
56}
57
58
60{
61 m_count = 0;
62 for (int i = 0; i < m_channels; i++) {
63 for (int j = 0; j < m_nrOfBins; j++) {
64 m_bins[i][j] = 0;
65 }
66 m_outRight[i] = 0;
67 m_outLeft[i] = 0;
68 }
69}
70
72{
73 // This function assumes that the pixel is has no 'gaps'. That is to say: if we start
74 // at byte 0, we can get to the end of the pixel by adding consecutive size()s of
75 // the channels
77 uint count = c.count();
78 int currentPos = 0;
79
80 for (uint i = 0; i < count; i++) {
81 for (uint j = 0; j < count; j++) {
82 if (c[j]->pos() == currentPos) {
83 m_external.append(j);
84 break;
85 }
86 }
87 currentPos += c.at(m_external.at(m_external.count() - 1))->size();
88 }
89}
90
91// ------------ U8 ---------------------
92
97
99{
100 return QString("%1").arg(static_cast<quint8>(pos * UINT8_MAX));
101}
102
103void KoBasicU8HistogramProducer::addRegionToBin(const quint8 * pixels, const quint8 * selectionMask, quint32 nPixels, const KoColorSpace *cs)
104{
105 quint32 dstPixelSize = m_colorSpace->pixelSize();
106 quint8 *dstPixels = new quint8[nPixels * dstPixelSize];
108
109 if (selectionMask) {
110 quint8 *dst = dstPixels;
111 while (nPixels > 0) {
112 if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) {
113
114 for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) {
115 m_bins[i][m_colorSpace->scaleToU8(dst,i)]++;
116 }
117 m_count++;
118 }
119 dst += dstPixelSize;
120 selectionMask++;
121 nPixels--;
122 }
123 } else {
124 quint8 *dst = dstPixels;
125 while (nPixels > 0) {
126 if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) {
127
128 for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) {
129 m_bins[i][m_colorSpace->scaleToU8(dst,i)]++;
130 }
131 m_count++;
132 }
133 dst += dstPixelSize;
134 nPixels--;
135 }
136 }
137}
138
139// ------------ U16 ---------------------
140
145
147{
148 return QString("%1").arg(static_cast<quint8>(pos * UINT8_MAX));
149}
150
152{
153 return 1.0 / 255.0;
154}
155
156void KoBasicU16HistogramProducer::addRegionToBin(const quint8 * pixels, const quint8 * selectionMask, quint32 nPixels, const KoColorSpace *cs)
157{
158 // The view
159 quint16 from = static_cast<quint16>(m_from * UINT16_MAX);
160 quint16 width = static_cast<quint16>(m_width * UINT16_MAX + 0.5); // We include the end
161 quint16 to = from + width;
162 qreal factor = 255.0 / width;
163
164 quint32 dstPixelSize = m_colorSpace->pixelSize();
165 quint8 *dstPixels = new quint8[nPixels * dstPixelSize];
167 quint8 *dst = dstPixels;
169
170 if (selectionMask) {
171 while (nPixels > 0) {
172 if (!((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8))) {
174 for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) {
175 quint16 value = channels[i]*UINT16_MAX;
176 if (value > to)
177 m_outRight[i]++;
178 else if (value < from)
179 m_outLeft[i]++;
180 else
181 m_bins[i][static_cast<quint8>((value - from) * factor)]++;
182 }
183 m_count++;
184 }
185 dst += dstPixelSize;
186 selectionMask++;
187 nPixels--;
188 }
189 } else {
190 while (nPixels > 0) {
191 if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) {
193 for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) {
194 quint16 value = channels[i]*UINT16_MAX;
195
196 if (value > to)
197 m_outRight[i]++;
198 else if (value < from)
199 m_outLeft[i]++;
200 else
201 m_bins[i][static_cast<quint8>((value - from) * factor)]++;
202 }
203 m_count++;
204 }
205 dst += dstPixelSize;
206 nPixels--;
207 }
208 }
209}
210
211// ------------ Float32 ---------------------
216
218{
219 return QString("%1").arg(static_cast<float>(pos)); // XXX I doubt this is correct!
220}
221
223{
224 // XXX What _is_ the maximal zoom here? I don't think there is one with floats, so this seems a fine compromise for the moment
225 return 1.0 / 255.0;
226}
227
228void KoBasicF32HistogramProducer::addRegionToBin(const quint8 * pixels, const quint8 * selectionMask, quint32 nPixels, const KoColorSpace *cs)
229{
230 // The view
231 float from = static_cast<float>(m_from);
232 float width = static_cast<float>(m_width);
233 float to = from + width;
234 float factor = 255.0 / width;
235
236 quint32 dstPixelSize = m_colorSpace->pixelSize();
237 quint8 *dstPixels = new quint8[nPixels * dstPixelSize];
239 quint8 *dst = dstPixels;
241
242 if (selectionMask) {
243 while (nPixels > 0) {
244 if (!((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8))) {
246 for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) {
247 float value = channels[i];
248 if (value > to)
249 m_outRight[i]++;
250 else if (value < from)
251 m_outLeft[i]++;
252 else
253 m_bins[i][static_cast<quint8>((value - from) * factor)]++;
254 }
255 m_count++;
256 }
257 dst += dstPixelSize;
258 selectionMask++;
259 nPixels--;
260
261 }
262 } else {
263 while (nPixels > 0) {
264 if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) {
266 for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) {
267 float value = channels[i];
268 if (value > to)
269 m_outRight[i]++;
270 else if (value < from)
271 m_outLeft[i]++;
272 else
273 m_bins[i][static_cast<quint8>((value - from) * factor)]++;
274 }
275 m_count++;
276 }
277 dst += dstPixelSize;
278 nPixels--;
279
280 }
281 }
282}
283
284#ifdef HAVE_OPENEXR
285// ------------ Float16 Half ---------------------
286KoBasicF16HalfHistogramProducer::KoBasicF16HalfHistogramProducer(const KoID& id,
287 const KoColorSpace *cs)
288 : KoBasicHistogramProducer(id, 256, cs)
289{
290}
291
292QString KoBasicF16HalfHistogramProducer::positionToString(qreal pos) const
293{
294 return QString("%1").arg(static_cast<float>(pos)); // XXX I doubt this is correct!
295}
296
297qreal KoBasicF16HalfHistogramProducer::maximalZoom() const
298{
299 // XXX What _is_ the maximal zoom here? I don't think there is one with floats, so this seems a fine compromise for the moment
300 return 1.0 / 255.0;
301}
302
303void KoBasicF16HalfHistogramProducer::addRegionToBin(const quint8 * pixels, const quint8 * selectionMask, quint32 nPixels, const KoColorSpace *cs)
304{
305 // The view
306 float from = static_cast<float>(m_from);
307 float width = static_cast<float>(m_width);
308 float to = from + width;
309 float factor = 255.0 / width;
310
311 quint32 dstPixelSize = m_colorSpace->pixelSize();
312 quint8 *dstPixels = new quint8[nPixels * dstPixelSize];
314 quint8 *dst = dstPixels;
315 QVector<float> channels(m_colorSpace->channelCount());
316
317 if (selectionMask) {
318 while (nPixels > 0) {
319 if (!((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8))) {
320 m_colorSpace->normalisedChannelsValue(dst,channels);
321 for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) {
322 float value = channels[i];
323 if (value > to)
324 m_outRight[i]++;
325 else if (value < from)
326 m_outLeft[i]++;
327 else
328 m_bins[i][static_cast<quint8>((value - from) * factor)]++;
329 }
330 m_count++;
331 }
332 dst += dstPixelSize;
333 selectionMask++;
334 nPixels--;
335 }
336 } else {
337 while (nPixels > 0) {
338 if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) {
339 m_colorSpace->normalisedChannelsValue(dst,channels);
340 for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) {
341 float value = channels[i];
342 if (value > to)
343 m_outRight[i]++;
344 else if (value < from)
345 m_outLeft[i]++;
346 else
347 m_bins[i][static_cast<quint8>((value - from) * factor)]++;
348 }
349 m_count++;
350 }
351 dst += dstPixelSize;
352 nPixels--;
353 }
354 }
355}
356#endif
357
358// ------------ Generic RGB ---------------------
360 : KoBasicHistogramProducer(KoID("GENRGBHISTO", i18n("Generic RGB Histogram")), 3, 256)
361{
362 /* we set 0 as colorspace, because we are not based on a specific colorspace. This
363 is no problem for the superclass since we override channels() */
364 m_channelsList.append(new KoChannelInfo(i18n("R"), 0, 0, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(255, 0, 0)));
365 m_channelsList.append(new KoChannelInfo(i18n("G"), 1, 1, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(0, 255, 0)));
366 m_channelsList.append(new KoChannelInfo(i18n("B"), 2, 2, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(0, 0, 255)));
367}
368
373
375{
376 return QString("%1").arg(static_cast<quint8>(pos * UINT8_MAX));
377}
378
380{
381 return 1.0;
382}
383
384
385void KoGenericRGBHistogramProducer::addRegionToBin(const quint8 * pixels, const quint8 * selectionMask, quint32 nPixels, const KoColorSpace *cs)
386{
387 for (int i = 0; i < m_channels; i++) {
388 m_outRight[i] = 0;
389 m_outLeft[i] = 0;
390 }
391
392 QColor c;
393 qint32 pSize = cs->pixelSize();
394 if (selectionMask) {
395 while (nPixels > 0) {
396 if (!((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8))) {
397 cs->toQColor(pixels, &c);
398 m_bins[0][c.red()]++;
399 m_bins[1][c.green()]++;
400 m_bins[2][c.blue()]++;
401
402 m_count++;
403 }
404 pixels += pSize;
405 selectionMask++;
406 nPixels--;
407 }
408
409 } else {
410 while (nPixels > 0) {
411
412 if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) {
413 cs->toQColor(pixels, &c);
414 m_bins[0][c.red()]++;
415 m_bins[1][c.green()]++;
416 m_bins[2][c.blue()]++;
417
418 m_count++;
419 }
420 pixels += pSize;
421 nPixels--;
422 }
423 }
424}
425
430
431// ------------ Generic L*a*b* ---------------------
433 : KoBasicHistogramProducer(KoID("GENLABHISTO", i18n("L*a*b* Histogram")), 3, 256)
434{
435 /* we set 0 as colorspace, because we are not based on a specific colorspace. This
436 is no problem for the superclass since we override channels() */
440
441 if (!m_labCs) {
443 }
445}
452
457
459{
460 return QString("%1").arg(static_cast<quint16>(pos * UINT16_MAX));
461}
462
464{
465 return 1.0;
466}
467
468
469void KoGenericLabHistogramProducer::addRegionToBin(const quint8 *pixels, const quint8 *selectionMask, quint32 nPixels, const KoColorSpace *cs)
470{
471 for (int i = 0; i < m_channels; i++) {
472 m_outRight[i] = 0;
473 m_outLeft[i] = 0;
474 }
475
476 qint32 dstPixelSize = m_colorSpace->pixelSize();
477
478 quint8 *dstPixels = new quint8[nPixels * dstPixelSize];
480
481 qint32 pSize = cs->pixelSize();
482
483 if (selectionMask) {
484 while (nPixels > 0) {
485 if (!((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8))) {
486 m_count++;
487 }
488 pixels += pSize;
489 selectionMask++;
490 nPixels--;
491 }
492 } else {
493 quint8 *dst = dstPixels;
494 while (nPixels > 0) {
495 if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) {
496
497 m_bins[0][m_colorSpace->scaleToU8(dst, 0)]++;
498 m_bins[1][m_colorSpace->scaleToU8(dst, 1)]++;
499 m_bins[2][m_colorSpace->scaleToU8(dst, 2)]++;
500
501 m_count++;
502 }
503 dst+= dstPixelSize;
504 nPixels--;
505 }
506 }
507 delete[] dstPixels;
508}
509
float value(const T *src, size_t ch)
static const KoColorSpace * m_labCs
const quint8 OPACITY_TRANSPARENT_U8
#define UINT16_MAX
unsigned int uint
#define UINT8_MAX
void addRegionToBin(const quint8 *pixels, const quint8 *selectionMask, quint32 nPixels, const KoColorSpace *colorSpace) override
KoBasicF32HistogramProducer(const KoID &id, const KoColorSpace *colorSpace)
QString positionToString(qreal pos) const override
QList< KoChannelInfo * > channels() override
KoBasicHistogramProducer(const KoID &id, int channelCount, int nrOfBins)
void addRegionToBin(const quint8 *pixels, const quint8 *selectionMask, quint32 nPixels, const KoColorSpace *colorSpace) override
KoBasicU16HistogramProducer(const KoID &id, const KoColorSpace *colorSpace)
QString positionToString(qreal pos) const override
KoBasicU8HistogramProducer(const KoID &id, const KoColorSpace *colorSpace)
void addRegionToBin(const quint8 *pixels, const quint8 *selectionMask, quint32 nPixels, const KoColorSpace *colorSpace) override
QString positionToString(qreal pos) const override
@ COLOR
The channel represents a color.
@ UINT8
use this for an unsigned integer 8bits channel
virtual quint32 pixelSize() const =0
virtual void toQColor(const quint8 *src, QColor *c) const =0
virtual quint8 scaleToU8(const quint8 *srcPixel, qint32 channelPos) const =0
virtual quint32 channelCount() const =0
virtual void normalisedChannelsValue(const quint8 *pixel, QVector< float > &channels) const =0
virtual quint8 opacityU8(const quint8 *pixel) const =0
virtual bool convertPixelsTo(const quint8 *src, quint8 *dst, const KoColorSpace *dstColorSpace, quint32 numPixels, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
QList< KoChannelInfo * > channels() override
void addRegionToBin(const quint8 *pixels, const quint8 *selectionMask, quint32 nPixels, const KoColorSpace *colorSpace) override
QString positionToString(qreal pos) const override
QString positionToString(qreal pos) const override
void addRegionToBin(const quint8 *pixels, const quint8 *selectionMask, quint32 nPixels, const KoColorSpace *colorSpace) override
QList< KoChannelInfo * > channels() override
Definition KoID.h:30
const KoColorSpace * lab16(const QString &profileName=QString())
static KoColorSpaceRegistry * instance()