kis_basic_histogram_producers.cpp
1/*
2 * Copyright (c) 2005 Bart Coppens <kde@bartcoppens.be>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19#include <tqstring.h>
20#include <tdelocale.h>
21
22#include "config.h"
23
24#ifdef HAVE_OPENEXR
25#include <half.h>
26#endif
27
28#include "kis_global.h"
29#include "kis_basic_histogram_producers.h"
30#include "kis_integer_maths.h"
31#include "kis_channelinfo.h"
32#include "kis_colorspace.h"
33#include "kis_lab_colorspace.h"
34
35KisLabColorSpace* KisGenericLabHistogramProducer::m_labCs = 0;
36
37
38KisBasicHistogramProducer::KisBasicHistogramProducer(const KisID& id, int channels, int nrOfBins, KisColorSpace *cs)
39 : m_channels(channels),
40 m_nrOfBins(nrOfBins),
41 m_colorSpace(cs),
42 m_id(id)
43{
44 m_bins.resize(m_channels);
45 for (int i = 0; i < m_channels; i++)
46 m_bins.at(i).resize(m_nrOfBins);
47 m_outLeft.resize(m_channels);
48 m_outRight.resize(m_channels);
49 m_count = 0;
50 m_from = 0.0;
51 m_width = 1.0;
52}
53
54void KisBasicHistogramProducer::clear() {
55 m_count = 0;
56 for (int i = 0; i < m_channels; i++) {
57 for (int j = 0; j < m_nrOfBins; j++) {
58 m_bins.at(i).at(j) = 0;
59 }
60 m_outRight.at(i) = 0;
61 m_outLeft.at(i) = 0;
62 }
63}
64
65void KisBasicHistogramProducer::makeExternalToInternal() {
66 // This function assumes that the pixel is has no 'gaps'. That is to say: if we start
67 // at byte 0, we can get to the end of the pixel by adding consecutive size()s of
68 // the channels
69 TQValueVector<KisChannelInfo *> c = channels();
70 uint count = c.count();
71 int currentPos = 0;
72
73 for (uint i = 0; i < count; i++) {
74 for (uint j = 0; j < count; j++) {
75 if (c.at(j)->pos() == currentPos) {
76 m_external.append(j);
77 break;
78 }
79 }
80 currentPos += c.at(m_external.at(m_external.count() - 1))->size();
81 }
82}
83
84// ------------ U8 ---------------------
85
86KisBasicU8HistogramProducer::KisBasicU8HistogramProducer(const KisID& id, KisColorSpace *cs)
87 : KisBasicHistogramProducer(id, cs->nChannels(), 256, cs)
88{
89}
90
91TQString KisBasicU8HistogramProducer::positionToString(double pos) const {
92 return TQString("%1").arg(static_cast<TQ_UINT8>(pos * UINT8_MAX));
93}
94
95void KisBasicU8HistogramProducer::addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *cs)
96{
97 if (!pixels) return;
98 if (!cs) return;
99 if (nPixels == 0) return;
100
101 TQ_INT32 pSize = cs->pixelSize();
102
103 if ( selectionMask ) {
104 while (nPixels > 0) {
105 if ( ! (m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT) ) {
106
107 for (int i = 0; i < m_channels; i++) {
108 m_bins.at(i).at(pixels[i])++;
109 }
110 m_count++;
111
112 }
113
114 pixels += pSize;
115 selectionMask++;
116 nPixels--;
117 }
118 }
119 else {
120 while (nPixels > 0) {
121 if ( ! (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT) ) {
122
123 for (int i = 0; i < m_channels; i++) {
124 m_bins.at(i).at(pixels[i])++;
125 }
126 m_count++;
127
128 }
129
130 pixels += pSize;
131 nPixels--;
132 }
133 }
134}
135
136// ------------ U16 ---------------------
137
138KisBasicU16HistogramProducer::KisBasicU16HistogramProducer(const KisID& id, KisColorSpace *cs)
139 : KisBasicHistogramProducer(id, cs->nChannels(), 256, cs)
140{
141}
142
143TQString KisBasicU16HistogramProducer::positionToString(double pos) const
144{
145 return TQString("%1").arg(static_cast<TQ_UINT8>(pos * UINT8_MAX));
146}
147
148double KisBasicU16HistogramProducer::maximalZoom() const
149{
150 return 1.0 / 255.0;
151}
152
153void KisBasicU16HistogramProducer::addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *cs)
154{
155 // The view
156 TQ_UINT16 from = static_cast<TQ_UINT16>(m_from * UINT16_MAX);
157 TQ_UINT16 width = static_cast<TQ_UINT16>(m_width * UINT16_MAX + 0.5); // We include the end
158 TQ_UINT16 to = from + width;
159 double factor = 255.0 / width;
160
161 TQ_INT32 pSize = cs->pixelSize();
162
163 if ( selectionMask ) {
164 TQ_UINT16* pixel = reinterpret_cast<TQ_UINT16*>(pixels);
165 while (nPixels > 0) {
166 if ( ! ((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) ) {
167 for (int i = 0; i < m_channels; i++) {
168 TQ_UINT16 value = pixel[i];
169 if (value > to)
170 m_outRight.at(i)++;
171 else if (value < from)
172 m_outLeft.at(i)++;
173 else
174 m_bins.at(i).at(static_cast<TQ_UINT8>((value - from) * factor))++;
175 }
176 m_count++;
177 }
178 pixels += pSize;
179 selectionMask++;
180 nPixels--;
181 }
182 }
183 else {
184 while (nPixels > 0) {
185 TQ_UINT16* pixel = reinterpret_cast<TQ_UINT16*>(pixels);
186
187 if ( ! (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) {
188 for (int i = 0; i < m_channels; i++) {
189 TQ_UINT16 value = pixel[i];
190 if (value > to)
191 m_outRight.at(i)++;
192 else if (value < from)
193 m_outLeft.at(i)++;
194 else
195 m_bins.at(i).at(static_cast<TQ_UINT8>((value - from) * factor))++;
196 }
197 m_count++;
198 }
199 pixels += pSize;
200 nPixels--;
201
202 }
203 }
204}
205
206// ------------ Float32 ---------------------
207KisBasicF32HistogramProducer::KisBasicF32HistogramProducer(const KisID& id, KisColorSpace *cs)
208 : KisBasicHistogramProducer(id, cs->nChannels(), 256, cs)
209{
210}
211
212TQString KisBasicF32HistogramProducer::positionToString(double pos) const {
213 return TQString("%1").arg(static_cast<float>(pos)); // XXX I doubt this is correct!
214}
215
216double KisBasicF32HistogramProducer::maximalZoom() const {
217 // XXX What _is_ the maximal zoom here? I don't think there is one with floats, so this seems a fine compromis for the moment
218 return 1.0 / 255.0;
219}
220
221void KisBasicF32HistogramProducer::addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *cs) {
222 // The view
223 float from = static_cast<float>(m_from);
224 float width = static_cast<float>(m_width);
225 float to = from + width;
226 float factor = 255.0 / width;
227
228 TQ_INT32 pSize = cs->pixelSize();
229
230 if ( selectionMask ) {
231 while (nPixels > 0) {
232
233 float* pixel = reinterpret_cast<float*>(pixels);
234 if ( !((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) ) {
235 for (int i = 0; i < m_channels; i++) {
236 float value = pixel[i];
237 if (value > to)
238 m_outRight.at(i)++;
239 else if (value < from)
240 m_outLeft.at(i)++;
241 else
242 m_bins.at(i).at(static_cast<TQ_UINT8>((value - from) * factor))++;
243 }
244 m_count++;
245 }
246
247 pixels += pSize;
248 selectionMask++;
249 nPixels--;
250
251 }
252 }
253 else {
254 while (nPixels > 0) {
255
256 float* pixel = reinterpret_cast<float*>(pixels);
257 if ( !(m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) {
258 for (int i = 0; i < m_channels; i++) {
259 float value = pixel[i];
260 if (value > to)
261 m_outRight.at(i)++;
262 else if (value < from)
263 m_outLeft.at(i)++;
264 else
265 m_bins.at(i).at(static_cast<TQ_UINT8>((value - from) * factor))++;
266 }
267 m_count++;
268 }
269
270 pixels += pSize;
271 nPixels--;
272
273 }
274 }
275}
276
277#ifdef HAVE_OPENEXR
278// ------------ Float16 Half ---------------------
279KisBasicF16HalfHistogramProducer::KisBasicF16HalfHistogramProducer(const KisID& id,
280 KisColorSpace *cs)
281 : KisBasicHistogramProducer(id, cs->nChannels(), 256, cs) {
282}
283
284TQString KisBasicF16HalfHistogramProducer::positionToString(double pos) const {
285 return TQString("%1").arg(static_cast<float>(pos)); // XXX I doubt this is correct!
286}
287
288double KisBasicF16HalfHistogramProducer::maximalZoom() const {
289 // XXX What _is_ the maximal zoom here? I don't think there is one with floats, so this seems a fine compromis for the moment
290 return 1.0 / 255.0;
291}
292
293void KisBasicF16HalfHistogramProducer::addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *cs) {
294 // The view
295 float from = static_cast<float>(m_from);
296 float width = static_cast<float>(m_width);
297 float to = from + width;
298 float factor = 255.0 / width;
299
300 TQ_INT32 pSize = cs->pixelSize();
301 if ( selectionMask ) {
302 while (nPixels > 0) {
303 half* pixel = reinterpret_cast<half*>(pixels);
304 if ( !((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) ) {
305 for (int i = 0; i < m_channels; i++) {
306 float value = pixel[i];
307 if (value > to)
308 m_outRight.at(i)++;
309 else if (value < from)
310 m_outLeft.at(i)++;
311 else
312 m_bins.at(i).at(static_cast<TQ_UINT8>((value - from) * factor))++;
313 }
314 m_count++;
315 }
316 pixels += pSize;
317 selectionMask++;
318 nPixels--;
319 }
320 }
321 else {
322 while (nPixels > 0) {
323 half* pixel = reinterpret_cast<half*>(pixels);
324 if ( !(m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) {
325 for (int i = 0; i < m_channels; i++) {
326 float value = pixel[i];
327 if (value > to)
328 m_outRight.at(i)++;
329 else if (value < from)
330 m_outLeft.at(i)++;
331 else
332 m_bins.at(i).at(static_cast<TQ_UINT8>((value - from) * factor))++;
333 }
334 m_count++;
335 }
336 pixels += pSize;
337 nPixels--;
338 }
339 }
340}
341#endif
342
343// ------------ Generic RGB ---------------------
344KisGenericRGBHistogramProducer::KisGenericRGBHistogramProducer()
345 : KisBasicHistogramProducer(KisID("GENRGBHISTO", i18n("Generic RGB Histogram")),
346 3, 256, 0) {
347 /* we set 0 as colorspece, because we are not based on a specific colorspace. This
348 is no problem for the superclass since we override channels() */
349 m_channelsList.append(new KisChannelInfo(i18n("R"), i18n("R"), 0, KisChannelInfo::COLOR, KisChannelInfo::UINT8, 1, TQColor(255,0,0)));
350 m_channelsList.append(new KisChannelInfo(i18n("G"), i18n("G"), 1, KisChannelInfo::COLOR, KisChannelInfo::UINT8, 1, TQColor(0,255,0)));
351 m_channelsList.append(new KisChannelInfo(i18n("B"), i18n("B"), 2, KisChannelInfo::COLOR, KisChannelInfo::UINT8, 1, TQColor(0,0,255)));
352}
353
354TQValueVector<KisChannelInfo *> KisGenericRGBHistogramProducer::channels() {
355 return m_channelsList;
356}
357
358TQString KisGenericRGBHistogramProducer::positionToString(double pos) const {
359 return TQString("%1").arg(static_cast<TQ_UINT8>(pos * UINT8_MAX));
360}
361
362double KisGenericRGBHistogramProducer::maximalZoom() const {
363 return 1.0;
364}
365
366
367void KisGenericRGBHistogramProducer::addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *cs)
368{
369 for (int i = 0; i < m_channels; i++) {
370 m_outRight.at(i) = 0;
371 m_outLeft.at(i) = 0;
372 }
373
374 TQColor c;
375 TQ_INT32 pSize = cs->pixelSize();
376 if (selectionMask) {
377 while (nPixels > 0) {
378 if ( !((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) ) {
379 cs->toTQColor(pixels, &c);
380 m_bins.at(0).at(c.red())++;
381 m_bins.at(1).at(c.green())++;
382 m_bins.at(2).at(c.blue())++;
383
384 m_count++;
385 }
386 pixels += pSize;
387 selectionMask++;
388 nPixels--;
389 }
390
391 }
392 else {
393 while (nPixels > 0) {
394
395 if ( !(m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) {
396 cs->toTQColor(pixels, &c);
397 m_bins.at(0).at(c.red())++;
398 m_bins.at(1).at(c.green())++;
399 m_bins.at(2).at(c.blue())++;
400
401 m_count++;
402 }
403 pixels += pSize;
404 nPixels--;
405 }
406 }
407}
408
409// ------------ Generic L*a*b* ---------------------
410KisGenericLabHistogramProducer::KisGenericLabHistogramProducer()
411 : KisBasicHistogramProducer(KisID("GENLABHISTO", i18n("L*a*b* Histogram")), 3, 256, 0) {
412 /* we set 0 as colorspace, because we are not based on a specific colorspace. This
413 is no problem for the superclass since we override channels() */
414 m_channelsList.append(new KisChannelInfo(i18n("L*"), i18n("L"), 0, KisChannelInfo::COLOR, KisChannelInfo::UINT8));
415 m_channelsList.append(new KisChannelInfo(i18n("a*"), i18n("a"), 1, KisChannelInfo::COLOR, KisChannelInfo::UINT8));
416 m_channelsList.append(new KisChannelInfo(i18n("b*"), i18n("b"), 2, KisChannelInfo::COLOR, KisChannelInfo::UINT8));
417
418 if (!m_labCs) {
419 KisProfile *labProfile = new KisProfile(cmsCreateLabProfile(NULL));
420 m_labCs = new KisLabColorSpace(0, labProfile);
421 }
422 m_colorSpace = m_labCs;
423}
424KisGenericLabHistogramProducer::~KisGenericLabHistogramProducer()
425{
426 delete m_channelsList[0];
427 delete m_channelsList[1];
428 delete m_channelsList[2];
429}
430
431TQValueVector<KisChannelInfo *> KisGenericLabHistogramProducer::channels() {
432 return m_channelsList;
433}
434
435TQString KisGenericLabHistogramProducer::positionToString(double pos) const {
436 return TQString("%1").arg(static_cast<TQ_UINT16>(pos * UINT16_MAX));
437}
438
439double KisGenericLabHistogramProducer::maximalZoom() const {
440 return 1.0;
441}
442
443
444void KisGenericLabHistogramProducer::addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *cs)
445{
446 for (int i = 0; i < m_channels; i++) {
447 m_outRight.at(i) = 0;
448 m_outLeft.at(i) = 0;
449 }
450
451 TQ_UINT8 dst[8];
452 TQ_INT32 pSize = cs->pixelSize();
453
454 if (selectionMask) {
455 while (nPixels > 0) {
456 if ( !((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) ) {
457/*
458 cs->toTQColor(pixels, &c);
459 m_bins.at(0).at(c.red())++;
460*/
461 m_count++;
462 }
463 pixels += pSize;
464 selectionMask++;
465 nPixels--;
466 }
467 }
468 else {
469 while (nPixels > 0) {
470 if ( !(m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) {
471
472 cs->convertPixelsTo(pixels, dst, m_colorSpace, 1);
473 m_bins.at(0).at(m_colorSpace->scaleToU8(dst, 0))++;
474 m_bins.at(1).at(m_colorSpace->scaleToU8(dst, 1))++;
475 m_bins.at(2).at(m_colorSpace->scaleToU8(dst, 2))++;
476
477 m_count++;
478 }
479 pixels += pSize;
480 nPixels--;
481 }
482 }
483}
484
This class gives some basic information about a channel, that is, one of the components that makes up...
A colorspace is the definition of a certain color model in Chalk.
virtual void toTQColor(const TQ_UINT8 *src, TQColor *c, KisProfile *profile=0)=0
The toTQColor methods take a byte array that is at least pixelSize() long and converts the contents t...
virtual TQ_UINT8 getAlpha(const TQ_UINT8 *pixel) const =0
Get the alpha value of the given pixel, downscaled to an 8-bit value.
virtual bool convertPixelsTo(const TQ_UINT8 *src, TQ_UINT8 *dst, KisColorSpace *dstColorSpace, TQ_UINT32 numPixels, TQ_INT32 renderingIntent=INTENT_PERCEPTUAL)=0
Convert a byte array of srcLen pixels *src to the specified color space and put the converted bytes i...
virtual TQ_UINT32 pixelSize() const =0
The size in bytes of a single pixel in this color model.
virtual void addRegionToBin(TQ_UINT8 *pixels, TQ_UINT8 *selectionMask, TQ_UINT32 nPixels, KisColorSpace *colorSpace)
Adds the values from the specified array of pixels to the bins – does not reset anything.
virtual void addRegionToBin(TQ_UINT8 *pixels, TQ_UINT8 *selectionMask, TQ_UINT32 nPixels, KisColorSpace *colorSpace)
Adds the values from the specified array of pixels to the bins – does not reset anything.
Chalk has a large number of extensible resources.
Definition kis_id.h:57