Perceptual Color

helper.cpp
1// SPDX-FileCopyrightText: Lukas Sommer <sommerluk@gmail.com>
2// SPDX-License-Identifier: BSD-2-Clause OR MIT
3
4// Own header
5#include "helper.h"
6
7#include "absolutecolor.h"
8#include "genericcolor.h"
9#include "helperconversion.h"
10#include "initializelibraryresources.h"
11#include "rgbcolorspace.h"
12#include <array>
13#include <lcms2.h>
14#include <qchar.h>
15#include <qcolor.h>
16#include <qevent.h>
17#include <qkeysequence.h>
18#include <qlabel.h>
19#include <qpainter.h>
20#include <qpixmap.h>
21#include <qpoint.h>
22#include <qsize.h>
23#include <qstringliteral.h>
24#include <qstyle.h>
25#include <qstyleoption.h>
26#include <qwidget.h>
27
28#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
29#include <qlist.h>
30#else
31#include <qstringlist.h>
32#endif
33
34#ifndef PERCEPTUALCOLORINTERNAL
35#include <type_traits>
36#include <utility>
37#endif
38
39namespace PerceptualColor
40{
41/** @internal
42 *
43 * @brief Number of vertical <em>standard</em> wheel steps done by a
44 * wheel event
45 *
46 * As the QWheelEvent documentation explains, there is a common physical
47 * standard wheel step size for mouse wheels: 15°. But there are some
48 * mouse models which use non-standard physical wheel step sizes for
49 * their mouse wheel, for example because they have a higher wheel
50 * resolution.
51 *
52 * This function converts the values in a QMouseEvent to the
53 * <em>standard</em> wheel step count.
54 *
55 * @param event the QWheelEvent
56 * @returns the count of vertical <em>standard</em> wheel steps done
57 * within this mouse event. The value is positive for up-steps and
58 * negative for down-steps. On a standard mouse wheel, moving the wheel
59 * one physical step up will return the value 1. On a non-standard,
60 * higher resolution mouse wheel, moving the wheel one physical step up
61 * will return a smaller value, for example 0.7 */
62qreal standardWheelStepCount(QWheelEvent *event)
63{
64 // QWheelEvent::angleDelta() returns 8 units for each degree.
65 // The standard wheel step is 15°. So on a standard
66 // mouse, one wheel step results in (8 × 15) units.
67 return event->angleDelta().y() / static_cast<qreal>(8 * 15);
68}
69
70/** @internal
71 *
72 * @brief Background for semi-transparent colors.
73 *
74 * When showing a semi-transparent color, there has to be a background
75 * on which it is shown. This function provides a suitable background
76 * for showcasing a color.
77 *
78 * @param devicePixelRatioF The desired device-pixel ratio. Must be ≥ 1.
79 *
80 * @returns An image of a mosaic of neutral gray rectangles of different
81 * lightness. You can use this as tiles to paint a background, starting from
82 * the top-left corner. This image is made for LTR layouts. If you have an
83 * RTL layout, you should horizontally mirror your paint buffer after painting
84 * the tiles. The image has its device pixel ratio set to the value that was
85 * given in the parameter.
86 *
87 * @note The image is considering the given device-pixel ratio to deliver
88 * sharp (and correctly scaled) images also for HiDPI devices.
89 * The painting does not use floating point drawing, but rounds
90 * to full integers. Therefore, the result is always a sharp image.
91 * This function takes care that each square has the same pixel size,
92 * without scaling errors or anti-aliasing errors.
93 *
94 * @sa @ref AbstractDiagram::transparencyBackground()
95 *
96 * @todo Provide color management support! Currently, we use the same
97 * value for red, green and blue, this might <em>not</em> be perfectly
98 * neutral gray depending on the color profile of the monitor… And: We
99 * should make sure that transparent colors are not applied by Qt on top
100 * of this image. Instead, add a parameter to this function to get the
101 * transparent color to paint above, and do color-managed overlay of the
102 * transparent color, in Lch space. For each Lab (not Lch!) channel:
103 * result = opacity * foreground + (100% - opacity) * background. */
104QImage transparencyBackground(qreal devicePixelRatioF)
105{
106 // The valid lightness range is [0, 255]. The median is 127/128.
107 // We use two color with equal distance to this median to get a
108 // neutral gray.
109 constexpr int lightnessDistance = 15;
110 constexpr int lightnessOne = 127 - lightnessDistance;
111 constexpr int lightnessTwo = 128 + lightnessDistance;
112 constexpr int squareSizeInLogicalPixel = 10;
113 const int squareSize = qRound(squareSizeInLogicalPixel * devicePixelRatioF);
114
115 QImage temp(squareSize * 2, squareSize * 2, QImage::Format_RGB32);
116 temp.fill(QColor(lightnessOne, lightnessOne, lightnessOne));
117 QPainter painter(&temp);
118 QColor foregroundColor(lightnessTwo, lightnessTwo, lightnessTwo);
119 painter.fillRect(0, 0, squareSize, squareSize, foregroundColor);
120 painter.fillRect(squareSize, squareSize, squareSize, squareSize, foregroundColor);
121 temp.setDevicePixelRatio(devicePixelRatioF);
122 return temp;
123}
124
125/** @internal
126 *
127 * @brief Draws a QWidget respecting Qt Style Sheets.
128 *
129 * When subclassing QWidget-derived classes, the Qt Style Sheets are
130 * considered automatically. But when subclassing QWidget itself, the
131 * Qt Style Sheets are <em>not</em> considered automatically. Also,
132 * calling <tt>QWidget::paintEvent()</tt> from the subclass’s paint
133 * event does not help. Instead, call this function from within your
134 * subclass’s paint event. It uses some special code as documented
135 * in the <em>Qt Style Sheets Reference</em> in the section about QWidget.
136 *
137 * @warning This function creates a QPainter for the widget. As there
138 * should be not more than one QPainter at the same time for a given
139 * paint device, you may not call this function while a QPainter
140 * exists for the widget. Therefore, it is best to call this
141 * function as very first statement in your paintEvent() implementation,
142 * before initializing any QPainter.
143 *
144 * @param widget the widget */
145void drawQWidgetStyleSheetAware(QWidget *widget)
146{
147 QStyleOption opt;
148 opt.initFrom(widget);
149 QPainter p(widget);
150 widget->style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, widget);
151}
152
153/** @internal
154 *
155 * @brief Provides prefix and suffix of a value from a given format string.
156 *
157 * A typical use case: You want to put a percent value into a spinbox. The
158 * simple approach would be:
159 * @snippet testhelper.cpp percentTraditional
160 * It could be improved like this:
161 * @snippet testhelper.cpp percentImproved
162 * However, in some languages, the position of the prefix and suffix may be
163 * reversed compared to English. Example: In English, you write 50\%, but in
164 * Turkish, you write \%50. Qt does not offer an out-of-the-box solution for
165 * this. This helper now provides complete internationalization for prefixes
166 * and suffixes of spin boxes, allowing to do this easily in a fail-safe way:
167 * @snippet testhelper.cpp percentFullyInternationalized
168 *
169 * @param formatString A translated string in the format "prefix%1suffix". It
170 * should contain exactly <em>one</em> place marker as described in
171 * <tt>QString::arg()</tt> like <tt>\%1</tt> or <tt>\%L2</tt>. This place
172 * marker represents the value. Example: “Prefix\%1Suffix”. Prefix and suffix
173 * may be empty.
174 *
175 * @returns If the <tt>formatString</tt> parameter has the correct format,
176 * the prefix will be returned at <tt>QPair::first</tt> and the suffix will
177 * be returned at <tt>QPair::second</tt>. Otherwise, they will be set to an
178 * empty string.
179 *
180 * @note The functionality implemented here served as the inspiration for
181 * <a href="https://api.kde.org/frameworks/ki18n/html/namespaceKLocalization.html">
182 * KLocalization::setupSpinBoxFormatString()</a>. However, there are key
183 * differences.
184 * <a href="https://api.kde.org/frameworks/ki18n/html/namespaceKLocalization.html">
185 * KLocalization::setupSpinBoxFormatString()</a> extends this approach to
186 * include support for plural handling, specifically for integers (but not
187 * floating-point numbers). It uses %v as a placeholder instead of %1.
188 * Because %v is not compatible with the translation system, this design allows
189 * the string to be translated while enabling the selection of the appropriate
190 * plural form by passing the relevant integer – without actually replacing the
191 * placeholder with an actual value. Despite its broader functionality, it is
192 * limited to handling QSpinBox and QDoubleSpinBox. Since this project does
193 * not link to KLocalization, and our implementation already meets our specific
194 * requirements, we continue to use our own approach.
195 */
196[[nodiscard]] QPair<QString, QString> getPrefixSuffix(const QString &formatString)
197{
198 // QString::arg() support for %L2, %5 etc which translators might expect:
199 const auto list = formatString //
200 .arg(QStringLiteral("%1")) //
201 .split(QStringLiteral("%1"));
202 if (list.count() == 2) {
203 return QPair<QString, QString>(list.at(0), list.at(1));
204 }
205 return QPair<QString, QString>(QString(), QString());
206}
207
208/** @internal
209 *
210 * @brief Icon from theme.
211 *
212 * @param names List of names, preferred names first. The system’s icon
213 * themes are searched for this.
214 * @param fallback If the system icon themes do not provide an icon, use
215 * this fallback icon from the built-in resources.
216 * @param type Type of widget color scheme for which the fallback icon (if
217 * used) should be suitable.
218 *
219 * @returns An icon from the system icons or a fallback icons. If none is
220 * available, an empty icon.
221 *
222 * @internal
223 *
224 * @note Defining PERCEPTUALCOLORINTERNAL bypasses the platform's
225 * icon theme, relying exclusively on the fallback. This behavior
226 * is primarily intended to ensure that utils/generatescreenshots.cpp,
227 * used for generating documentation screenshots, produces results
228 * that are independent of the platform.
229 */
230QIcon qIconFromTheme(const QStringList &names, const QString &fallback, ColorSchemeType type)
231{
232#ifdef PERCEPTUALCOLORINTERNAL
233 Q_UNUSED(names)
234#else
235 // Try to find icon in theme
236 for (auto const &name : std::as_const(names)) {
237 const QIcon myIcon = QIcon::fromTheme(name);
238 if (!myIcon.isNull()) {
239 return myIcon;
240 }
241 }
242#endif
243
244 // Return fallback icon
245 initializeLibraryResources();
246 QString path = QStringLiteral( //
247 ":/PerceptualColor/icons/lighttheme/%1.svg");
248 if (type == ColorSchemeType::Dark) {
249 path = QStringLiteral( //
250 ":/PerceptualColor/icons/darktheme/%1.svg");
251 }
252 return QIcon(path.arg(fallback));
253}
254
255/** @internal
256 *
257 * @brief Converts text with mnemonics to rich text rendering the mnemonics.
258 *
259 * At some places in Qt, mnemonics are used. For example, setting
260 * <tt>QPushButton::setText()</tt> to "T&est" will make appear the text
261 * "Test". If mnemonic support is enabled in the current platform theme,
262 * the "e" is underlined.
263 *
264 * At some other places in Qt, rich text is used. For example, setting
265 * <tt>QWidget::setToolTip()</tt> to "T<u>e</u>st" will make appear the text
266 * "Test", but with the "e" underlined.
267 *
268 * @param mnemonicText A text that might contain mnemonics
269 *
270 * @returns A rich text that will render in rich-text-functions with the same
271 * rendering as if the mnemonic text would have been
272 * used in mnemonic-functions: If currently in the platform theme,
273 * auto-mnemonics are enabled, the mnemonics are underlined. Otherwise,
274 * the mnemonics are not underlined nor is the “&” character visible
275 *
276 * @note This function mimics Qt’s algorithm form mnemonic rendering quite
277 * well, but there might be subtile differences in corner cases. Like Qt,
278 * this function accepts multiple occurrences of "&" in the same string, even
279 * before different characters, and underlines all of them, though
280 * <tt>QKeySequence::mnemonic()</tt> will return only one of them as
281 * shortcut. */
282QString fromMnemonicToRichText(const QString &mnemonicText)
283{
284 QString result;
285
286 const bool doUnderline = !QKeySequence::mnemonic(mnemonicText).isEmpty();
287 const auto underlineStart = doUnderline ? QStringLiteral("<u>") : QString();
288 const auto underlineStop = doUnderline ? QStringLiteral("</u>") : QString();
289
290 bool underlineNextChar = false;
291 for (int i = 0; i < mnemonicText.length(); ++i) {
292 if (mnemonicText[i] == QStringLiteral("&")) {
293 const auto nextChar = //
294 (i + 1 < mnemonicText.length()) //
295 ? mnemonicText[i + 1]
296 : QChar();
297 if (nextChar == QStringLiteral("&")) {
298 // Double ampersand: Escape the "&"
299 result.append(QStringLiteral("&"));
300 i++; // Skip the second "&"
301 } else {
302 // Single ampersand: Start underline
303 underlineNextChar = true;
304 }
305 } else {
306 if (underlineNextChar) {
307 // End underline
308 result.append(underlineStart);
309 result.append(mnemonicText[i]);
310 result.append(underlineStop);
311 underlineNextChar = false;
312 } else {
313 result.append(mnemonicText[i]);
314 }
315 }
316 }
317
318 return result;
319}
320
321/** @internal
322 *
323 * @brief Guess the actual @ref ColorSchemeType of a given widget.
324 *
325 * It guesses the color scheme type actually used by the current widget style,
326 * and not the type of the current color palette. This makes a difference
327 * for example on the Windows Vista style, which might ignore the palette and
328 * use always a light theme instead.
329 *
330 * @param widget The widget to evaluate
331 *
332 * @returns The guessed scheme.
333 *
334 * @note The exact implementation of the guess might change over time.
335 *
336 * @internal
337 *
338 * The current implementation creates a QLabel as child widget of the current
339 * widget, than takes a screenshot of the QLabel and calculates
340 * the average lightness of this screenshot and determines the color schema
341 * type accordingly.
342 *
343 * @note With Qt 6.5, there is
344 * <a href="https://www.qt.io/blog/dark-mode-on-windows-11-with-qt-6.5">
345 * better access to color themes</a>. Apparently, the Windows Vista style
346 * now seems to polish the widgets by setting a light color palette, so
347 * also on Windows Vista style we could simply rely on the color palette
348 * and test if the text color is lighter or darker than the background color
349 * to determine the color scheme type. This would also give us more reliable
350 * results with color schemes that have background colors around 50% lightness,
351 * which our currently implementation has problems to get right. But on
352 * the other hand, other styles like Kvantum might still chose to ignore
353 * the color palette, so it seems safer to stay with the current
354 * implementation. */
355ColorSchemeType guessColorSchemeTypeFromWidget(QWidget *widget)
356{
357 if (widget == nullptr) {
359 }
360
361 // Create a QLabel
362 QScopedPointer<QLabel> label{new QLabel(widget)};
363 label->setText(QStringLiteral("abc"));
364 label->resize(label->sizeHint()); // Smaller size means faster guess.
365
366 // Take a screenshot of the QLabel
367 const QImage screenshot = label->grab().toImage();
368 if (screenshot.size().isEmpty()) {
370 }
371
372 // Calculate the average lightness of the screenshot
373 QColorFloatType lightnessSum = 0;
374 for (int y = 0; y < screenshot.height(); ++y) {
375 for (int x = 0; x < screenshot.width(); ++x) {
376 lightnessSum += QColor(screenshot.pixel(x, y)).lightnessF();
377 }
378 }
379 const auto pixelCount = screenshot.width() * screenshot.height();
380 constexpr QColorFloatType threeshold = 0.5;
381 const bool isDark = //
382 (lightnessSum / static_cast<QColorFloatType>(pixelCount)) < threeshold;
383 if (isDark) {
385 }
387}
388
389/** @brief Swatch grid derived from the basic colors as by WCS (World color
390 * survey).
391 *
392 * The swatch grid contains various tints and shades of the
393 * basic colors. The choice of the basic colors is based on the
394 * <a href="https://en.wikipedia.org/wiki/Basic_Color_Terms:_Their_Universality_and_Evolution">
395 * study by Brent Berlin and Paul Kay</a>, who suggest that the
396 * basic color terms in almost all languages on earth follow a universal
397 * pattern. They propose that there are eleven basic color terms that appear
398 * in this order during the evolution of a language:
399 *
400 * 1. black, white
401 * 2. red
402 * 3. green, yellow
403 * 4. blue
404 * 5. brown
405 * 6. purple, pink, orange, gray
406 *
407 * Additionally, people worldwide seem to agree quite well on the typical
408 * values of each of these color terms. This theory is a fascinating one
409 * and forms a good basis for choosing basic colors for this swatch grid.
410 *
411 * This widget's colors have been arranged largely according to the color
412 * wheel of the perceptually uniform color space. We start with the saturated
413 * basic colors: red, orange, yellow, green, blue, and purple in order of
414 * their hue angles. Next, we have pink and brown, which have roughly the
415 * same hue as red or orange but are less saturated. These are simply the
416 * less chromatic parts of this hue but are nevertheless perceived by humans as
417 * independent colors. For each of these basic colors, there are five variants
418 * in the order of <a href="https://en.wikipedia.org/wiki/Tints_and_shades">
419 * tint, pure color, and shade</a>. Following the saturated colors and
420 * eventually the less saturated ones, the gray axis comes in last place.
421 *
422 * What exact colors are used? What exactly is a typical “red” or a
423 * “green”? Doesn’t every human have a slightly different
424 * feeling what a “typical” red or a “typical” blue is? We
425 * need a <em>focal color</em>, which is, according to
426 * <a href="https://www.oxfordreference.com/display/10.1093/oi/authority.20110803095825870">
427 * Oxford Reference</a>:
428 *
429 * > “A colour that is a prototypical instance of a particular colour name,
430 * > such as a shade of red that a majority of viewers consider to be the
431 * > best example of a red colour.”
432 *
433 * The <a href="https://www1.icsi.berkeley.edu/wcs/">World Color Survey</a>
434 * (WCS) is a significant study about focal colors of speakers of different
435 * languages across the world. The data from this survey is available online,
436 * and while it does not provide direct values for focal colors, various
437 * studies have used this data to determine focal colors for some
438 * <em>basic color terms</em> and a naming centroid for others.
439 *
440 * The table below shows the WCS grid coordinates for the basic color terms
441 * along with the corresponding Cielab values for the focal color (where
442 * available) or the naming centroid (where focal color data is unavailable).
443 *
444 * |Basic color term|WCS grid coordinates|Cielab³ L|Cielab³ a|Cielab³ b|
445 * | :--------------| -----------------: | ------: | ------: | ------: |
446 * | white¹ | A0 | 96.00 | -0.06 | 0.06 |
447 * | black¹ | J0 | 15.60 | -0.02 | 0.02 |
448 * | red¹ | G1 | 41.22 | 61.40 | 17.92 |
449 * | yellow¹ | C9 | 81.35 | 7.28 | 109.12 |
450 * | green¹ | F17 | 51.57 | -63.28 | 28.95 |
451 * | blue¹ | F29 | 51.57 | -3.41 | -48.08 |
452 * | brown³ | G7 | 41.22 | 17.04 | 45.95 |
453 * | purple³ | G34 | 41.22 | 33.08 | -30.50 |
454 * | pink³ | E1 | 61.70 | 49.42 | 18.23 |
455 * | orange³ | E6 | 61.70| 29.38 | 64.40 |
456 * | gray | not available | | | |
457 *
458 * ¹ Focal color as proposed by
459 * <a href="https://www.pnas.org/doi/10.1073/pnas.0503281102">Focal colors
460 * are universal after all</a>.
461 *
462 * ² Raw estimation of the naming centroid based on Fig. 4 of
463 * <a href="https://sites.socsci.uci.edu/~kjameson/ECST/Kay_Cook_WorldColorSurvey.pdf">
464 * this document</a>. (Fig. 5 would be the better choice, as it gives the
465 * focal color instead of the naming centroid, but unfortunately contains
466 * only red, yellow, green and blue, for which we have yet direct data.)
467 *
468 * ³ <a href="https://www1.icsi.berkeley.edu/wcs/data/cnum-maps/cnum-vhcm-lab-new.txt">
469 * Lookup table providing Lab values for WCS grid coordinates</a> and the
470 * <a href="https://www1.icsi.berkeley.edu/wcs/data/cnum-maps/cnum-vhcm-lab-new-README.txt">
471 * corresponding explanation</a>.
472 *
473 * From this data, the colors in our swatch grid have been derived as follows:
474 * - The gray axis has been defined manually, ignoring the WCS data. Chroma
475 * is 0. The lightness is 100% for white, 0% for black, and 75%, 50%,
476 * and 25% for the intermediate grays.
477 * - The other columns for chromatic colors use the WCS data for the swatch in
478 * the middle. Tints and shades are calculated by adding or reducing chroma
479 * and lightness within the Oklab color space. If the resulting color falls
480 * outside the color space, a nearby in-gamut color is chosen instead.
481 *
482 * @param colorSpace The color space in which the return value is calculated.
483 *
484 * @returns Swatch grid derived from the basic colors. Provides as a list of
485 * basic colors (in this order: red, orange, yellow, green, blue, purple, pink,
486 * brown, gray axis). Each basic color is a list of 5 swatches (starting with
487 * the lightest and finishing with the darkest: 2 tints, the tone itself,
488 * 2 shades).
489 *
490 * @note The RGB value is rounded to full integers in the range [0, 255]. */
492{
493 constexpr GenericColor red{41.22, 61.40, 17.92};
494 constexpr GenericColor orange{61.70, 29.38, 64.40};
495 constexpr GenericColor yellow{81.35, 07.28, 109.12};
496 constexpr GenericColor green{51.57, -63.28, 28.95};
497 constexpr GenericColor blue{51.57, -03.41, -48.08};
498 constexpr GenericColor purple{41.22, 33.08, -30.50};
499 constexpr GenericColor pink{61.70, 49.42, 18.23};
500 constexpr GenericColor brown{41.22, 17.04, 45.95};
501 constexpr std::array<GenericColor, 8> chromaticCielabColors //
502 {{red, orange, yellow, green, blue, purple, pink, brown}};
503
504 // Lowest common denominator of QList‘s and std::array’s size types:
505 using MySizeType = quint8;
506
507 constexpr MySizeType columnCount = //
508 chromaticCielabColors.size() + 1; // + 1 for gray axis
509 constexpr auto rowCount = 5;
510 Swatches wcsSwatches{columnCount, rowCount};
511
512 // Chromatic colors
513 constexpr double strongTint = 0.46;
514 constexpr double weakTint = 0.23;
515 constexpr double weakShade = 0.18;
516 constexpr double strongShade = 0.36;
517 std::array<GenericColor, rowCount> tintsAndShades;
518 for (MySizeType i = 0; i < chromaticCielabColors.size(); ++i) { //
519 const auto oklch = AbsoluteColor::convert( //
521 chromaticCielabColors.at(i),
523 )
524 .value_or(GenericColor());
525 tintsAndShades[0] = GenericColor //
526 {oklch.first + (1 - oklch.first) * strongTint, //
527 oklch.second * (1 - strongTint), //
528 oklch.third};
529 tintsAndShades[1] = GenericColor //
530 {oklch.first + (1 - oklch.first) * weakTint, //
531 oklch.second * (1 - weakTint), //
532 oklch.third};
533 tintsAndShades[2] = oklch;
534 tintsAndShades[3] = GenericColor //
535 {oklch.first * (1 - weakShade), //
536 oklch.second * (1 - weakShade), //
537 oklch.third};
538 tintsAndShades[4] = GenericColor //
539 {oklch.first * (1 - strongShade), //
540 oklch.second * (1 - strongShade), //
541 oklch.third};
542 for (MySizeType j = 0; j < rowCount; ++j) {
543 const auto variationCielchD50 = AbsoluteColor::convert( //
545 tintsAndShades.at(j), //
547 )
548 .value_or(GenericColor());
549 const auto variationRgb = colorSpace->fromCielchD50ToQRgbBound( //
550 variationCielchD50);
551 wcsSwatches.setValue(i, //
552 j,
553 variationRgb);
554 }
555 }
556
557 // Gray axis
558 QList<double> lightnesses{1, 0.75, 0.5, 0.25, 0};
559 for (int j = 0; j < lightnesses.count(); ++j) {
560 const GenericColor myOklab{lightnesses.at(j), 0, 0};
561 const auto cielchD50 = AbsoluteColor::convert( //
563 myOklab, //
565 )
566 .value_or(GenericColor());
567 const auto rgb = colorSpace->fromCielchD50ToQRgbBound(cielchD50);
568 wcsSwatches.setValue(columnCount - 1, j, rgb);
569 }
570
571 return wcsSwatches;
572}
573
574/**
575 * @brief The rendering intents supported by the LittleCMS library.
576 *
577 * Contains all rendering intents supported by the LittleCMS library
578 * against which this we are currently linking (or by one of the
579 * LittleCMS library plugigs). Each entry contains the code and the
580 * description (english-language, might be empty) just as
581 * provided by LittleCMS’ <tt>cmsGetSupportedIntents()</tt>.
582 *
583 * Note that LittleCMS supports as built-in intents the four official
584 * ICC intents and also some other, non-ICC intents. Furthermore,
585 * LittleCMS plugins can provide even more intents. As of LittleCMS 2.13
586 * the following built-in intents are available:
587 *
588 * | Type | Macro name | Code |
589 * | :------ | :-------------------------------------------- | ---: |
590 * | ICC | INTENT_PERCEPTUAL | 0 |
591 * | ICC | INTENT_RELATIVE_COLORIMETRIC | 1 |
592 * | ICC | INTENT_SATURATION | 2 |
593 * | ICC | INTENT_ABSOLUTE_COLORIMETRIC | 3 |
594 * | Non-ICC | INTENT_PRESERVE_K_ONLY_PERCEPTUAL | 10 |
595 * | Non-ICC | INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC | 11 |
596 * | Non-ICC | INTENT_PRESERVE_K_ONLY_SATURATION | 12 |
597 * | Non-ICC | INTENT_PRESERVE_K_PLANE_PERCEPTUAL | 13 |
598 * | Non-ICC | INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC | 14 |
599 * | Non-ICC | INTENT_PRESERVE_K_PLANE_SATURATION | 15 |
600 *
601 * @returns A QMap of key-value pairs. The key is the code representing the
602 * rendering intent. The value is a QString with the description.
603 */
605{
606 // Thread-Safe Lazy Initialization: Starting with C++11, function-local
607 // static variables are guaranteed to be thread-safe during initialization.
608 static const QMap<cmsUInt32Number, QString> result = []() {
610 const cmsUInt32Number intentCount = //
611 cmsGetSupportedIntents(0, nullptr, nullptr);
612 cmsUInt32Number *codeArray = new cmsUInt32Number[intentCount];
613 char **descriptionArray = new char *[intentCount];
614 cmsGetSupportedIntents(intentCount, codeArray, descriptionArray);
615 for (cmsUInt32Number i = 0; i < intentCount; ++i) {
616 lambdaResult.insert(codeArray[i], QString::fromUtf8(descriptionArray[i]));
617 }
618 delete[] codeArray;
619 delete[] descriptionArray;
620 return lambdaResult;
621 }();
622 return result;
623}
624
625} // namespace PerceptualColor
QString path(const QString &relativePath)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QString label(StandardShortcut id)
The namespace of this library.
Array2D< QColor > Swatches
Swatches organized in a grid.
Definition helper.h:262
ColorSchemeType
Represents the appearance of a theme.
Definition helper.h:41
Swatches wcsBasicColors(const QSharedPointer< PerceptualColor::RgbColorSpace > &colorSpace)
Swatch grid derived from the basic colors as by WCS (World color survey).
Definition helper.cpp:491
@ OklabD65
The absolute Oklab color space, which by definition always and exclusively uses a D65 illuminant.
@ CielabD50
The absolute Cielab color space using a D50 illuminant.
@ OklchD65
The absolute Oklch color space, which by definition always and exclusively uses a D65 illuminant.
@ CielchD50
The absolute Cielch color space using a D50 illuminant.
QMap< cmsUInt32Number, QString > lcmsIntentList()
The rendering intents supported by the LittleCMS library.
Definition helper.cpp:604
QIcon fromTheme(const QString &name)
bool isNull() const const
int height() const const
QRgb pixel(const QPoint &position) const const
QSize size() const const
int width() const const
bool isEmpty() const const
QKeySequence mnemonic(const QString &text)
const_reference at(qsizetype i) const const
qsizetype count() const const
iterator insert(const Key &key, const T &value)
bool isEmpty() const const
QString & append(QChar ch)
QString arg(Args &&... args) const const
QString fromUtf8(QByteArrayView str)
qsizetype length() const const
void resize(qsizetype newSize, QChar fillChar)
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
virtual void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const const=0
void initFrom(const QWidget *widget)
QStyle * style() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri May 2 2025 12:04:47 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.