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[[nodiscard]] QPair<QString, QString> getPrefixSuffix(const QString &formatString)
180{
181 // QString::arg() support for %L2, %5 etc which translators might expect:
182 const auto list = formatString //
183 .arg(QStringLiteral("%1")) //
184 .split(QStringLiteral("%1"));
185 if (list.count() == 2) {
186 return QPair<QString, QString>(list.at(0), list.at(1));
187 }
188 return QPair<QString, QString>(QString(), QString());
189}
190
191/** @internal
192 *
193 * @brief Icon from theme.
194 *
195 * @param names List of names, preferred names first. The system’s icon
196 * themes are searched for this.
197 * @param fallback If the system icon themes do not provide an icon, use
198 * this fallback icon from the built-in resources.
199 * @param type Type of widget color scheme for which the fallback icon (if
200 * used) should be suitable.
201 *
202 * @returns An icon from the system icons and or a fallback icons. If none is
203 * available, an empty icon. */
204QIcon qIconFromTheme(const QStringList &names, const QString &fallback, ColorSchemeType type)
205{
206#ifdef PERCEPTUALCOLORINTERNAL
207 Q_UNUSED(names)
208#else
209 // Try to find icon in theme
210 for (auto const &name : std::as_const(names)) {
211 const QIcon myIcon = QIcon::fromTheme(name);
212 if (!myIcon.isNull()) {
213 return myIcon;
214 }
215 }
216#endif
217
218 // Return fallback icon
219 initializeLibraryResources();
220 QString path = QStringLiteral( //
221 ":/PerceptualColor/icons/lighttheme/%1.svg");
222 if (type == ColorSchemeType::Dark) {
223 path = QStringLiteral( //
224 ":/PerceptualColor/icons/darktheme/%1.svg");
225 }
226 return QIcon(path.arg(fallback));
227}
228
229/** @internal
230 *
231 * @brief Converts text with mnemonics to rich text rendering the mnemonics.
232 *
233 * At some places in Qt, mnemonics are used. For example, setting
234 * <tt>QPushButton::setText()</tt> to "T&est" will make appear the text
235 * "Test". If mnemonic support is enabled in the current platform theme,
236 * the "e" is underlined.
237 *
238 * At some other places in Qt, rich text is used. For example, setting
239 * <tt>QWidget::setToolTip()</tt> to "T<u>e</u>st" will make appear the text
240 * "Test", but with the "e" underlined.
241 *
242 * @param mnemonicText A text that might contain mnemonics
243 *
244 * @returns A rich text that will render in rich-text-functions with the same
245 * rendering as if the mnemonic text would have been
246 * used in mnemonic-functions: If currently in the platform theme,
247 * auto-mnemonics are enabled, the mnemonics are underlined. Otherwise,
248 * the mnemonics are not underlined nor is the “&” character visible
249 *
250 * @note This function mimics Qt’s algorithm form mnemonic rendering quite
251 * well, but there might be subtile differences in corner cases. Like Qt,
252 * this function accepts multiple occurrences of "&" in the same string, even
253 * before different characters, and underlines all of them, though
254 * <tt>QKeySequence::mnemonic()</tt> will return only one of them as
255 * shortcut. */
256QString fromMnemonicToRichText(const QString &mnemonicText)
257{
258 QString result;
259
260 const bool doUnderline = !QKeySequence::mnemonic(mnemonicText).isEmpty();
261 const auto underlineStart = doUnderline ? QStringLiteral("<u>") : QString();
262 const auto underlineStop = doUnderline ? QStringLiteral("</u>") : QString();
263
264 bool underlineNextChar = false;
265 for (int i = 0; i < mnemonicText.length(); ++i) {
266 if (mnemonicText[i] == QStringLiteral("&")) {
267 const auto nextChar = //
268 (i + 1 < mnemonicText.length()) //
269 ? mnemonicText[i + 1]
270 : QChar();
271 if (nextChar == QStringLiteral("&")) {
272 // Double ampersand: Escape the "&"
273 result.append(QStringLiteral("&"));
274 i++; // Skip the second "&"
275 } else {
276 // Single ampersand: Start underline
277 underlineNextChar = true;
278 }
279 } else {
280 if (underlineNextChar) {
281 // End underline
282 result.append(underlineStart);
283 result.append(mnemonicText[i]);
284 result.append(underlineStop);
285 underlineNextChar = false;
286 } else {
287 result.append(mnemonicText[i]);
288 }
289 }
290 }
291
292 return result;
293}
294
295/** @internal
296 *
297 * @brief Guess the actual @ref ColorSchemeType of a given widget.
298 *
299 * It guesses the color scheme type actually used by the current widget style,
300 * and not the type of the current color palette. This makes a difference
301 * for example on the Windows Vista style, which might ignore the palette and
302 * use always a light theme instead.
303 *
304 * @param widget The widget to evaluate
305 *
306 * @returns The guessed scheme.
307 *
308 * @note The exact implementation of the guess might change over time.
309 *
310 * @internal
311 *
312 * The current implementation creates a QLabel as child widget of the current
313 * widget, than takes a screenshot of the QLabel and calculates
314 * the average lightness of this screenshot and determines the color schema
315 * type accordingly.
316 *
317 * @note With Qt 6.5, there is
318 * <a href="https://www.qt.io/blog/dark-mode-on-windows-11-with-qt-6.5">
319 * better access to color themes</a>. Apparently, the Windows Vista style
320 * now seems to polish the widgets by setting a light color palette, so
321 * also on Windows Vista style we could simply rely on the color palette
322 * and test if the text color is lighter or darker than the background color
323 * to determine the color scheme type. This would also give us more reliable
324 * results with color schemes that have background colors around 50% lightness,
325 * which our currently implementation has problems to get right. But on
326 * the other hand, other styles like Kvantum might still chose to ignore
327 * the color palette, so it seems safer to stay with the current
328 * implementation. */
329ColorSchemeType guessColorSchemeTypeFromWidget(QWidget *widget)
330{
331 if (widget == nullptr) {
333 }
334
335 // Create a QLabel
336 QScopedPointer<QLabel> label{new QLabel(widget)};
337 label->setText(QStringLiteral("abc"));
338 label->resize(label->sizeHint()); // Smaller size means faster guess.
339
340 // Take a screenshot of the QLabel
341 const QImage screenshot = label->grab().toImage();
342 if (screenshot.size().isEmpty()) {
344 }
345
346 // Calculate the average lightness of the screenshot
347 QColorFloatType lightnessSum = 0;
348 for (int y = 0; y < screenshot.height(); ++y) {
349 for (int x = 0; x < screenshot.width(); ++x) {
350 lightnessSum += QColor(screenshot.pixel(x, y)).lightnessF();
351 }
352 }
353 const auto pixelCount = screenshot.width() * screenshot.height();
354 constexpr QColorFloatType threeshold = 0.5;
355 const bool isDark = //
356 (lightnessSum / static_cast<QColorFloatType>(pixelCount)) < threeshold;
357 if (isDark) {
359 }
361}
362
363/** @brief Swatch grid derived from the basic colors as by WCS (World color
364 * survey).
365 *
366 * The swatch grid contains various tints and shades of the
367 * basic colors. The choice of the basic colors is based on the
368 * <a href="https://en.wikipedia.org/wiki/Basic_Color_Terms:_Their_Universality_and_Evolution">
369 * study by Brent Berlin and Paul Kay</a>, who suggest that the
370 * basic color terms in almost all languages on earth follow a universal
371 * pattern. They propose that there are eleven basic color terms that appear
372 * in this order during the evolution of a language:
373 *
374 * 1. black, white
375 * 2. red
376 * 3. green, yellow
377 * 4. blue
378 * 5. brown
379 * 6. purple, pink, orange, gray
380 *
381 * Additionally, people worldwide seem to agree quite well on the typical
382 * values of each of these color terms. This theory is a fascinating one
383 * and forms a good basis for choosing basic colors for this swatch grid.
384 *
385 * This widget's colors have been arranged largely according to the color
386 * wheel of the perceptually uniform color space. We start with the saturated
387 * basic colors: red, orange, yellow, green, blue, and purple in order of
388 * their hue angles. Next, we have pink and brown, which have roughly the
389 * same hue as red or orange but are less saturated. These are simply the
390 * less chromatic parts of this hue but are nevertheless perceived by humans as
391 * independent colors. For each of these basic colors, there are five variants
392 * in the order of <a href="https://en.wikipedia.org/wiki/Tints_and_shades">
393 * tint, pure color, and shade</a>. Following the saturated colors and
394 * eventually the less saturated ones, the gray axis comes in last place.
395 *
396 * What exact colors are used? What exactly is a typical “red” or a
397 * “green”? Doesn’t every human have a slightly different
398 * feeling what a “typical” red or a “typical” blue is? We
399 * need a <em>focal color</em>, which is, according to
400 * <a href="https://www.oxfordreference.com/display/10.1093/oi/authority.20110803095825870">
401 * Oxford Reference</a>:
402 *
403 * > “A colour that is a prototypical instance of a particular colour name,
404 * > such as a shade of red that a majority of viewers consider to be the
405 * > best example of a red colour.”
406 *
407 * The <a href="https://www1.icsi.berkeley.edu/wcs/">World Color Survey</a>
408 * (WCS) is a significant study about focal colors of speakers of different
409 * languages across the world. The data from this survey is available online,
410 * and while it does not provide direct values for focal colors, various
411 * studies have used this data to determine focal colors for some
412 * <em>basic color terms</em> and a naming centroid for others.
413 *
414 * The table below shows the WCS grid coordinates for the basic color terms
415 * along with the corresponding Cielab values for the focal color (where
416 * available) or the naming centroid (where focal color data is unavailable).
417 *
418 * |Basic color term|WCS grid coordinates|Cielab³ L|Cielab³ a|Cielab³ b|
419 * | :--------------| -----------------: | ------: | ------: | ------: |
420 * | white¹ | A0 | 96.00 | -0.06 | 0.06 |
421 * | black¹ | J0 | 15.60 | -0.02 | 0.02 |
422 * | red¹ | G1 | 41.22 | 61.40 | 17.92 |
423 * | yellow¹ | C9 | 81.35 | 7.28 | 109.12 |
424 * | green¹ | F17 | 51.57 | -63.28 | 28.95 |
425 * | blue¹ | F29 | 51.57 | -3.41 | -48.08 |
426 * | brown³ | G7 | 41.22 | 17.04 | 45.95 |
427 * | purple³ | G34 | 41.22 | 33.08 | -30.50 |
428 * | pink³ | E1 | 61.70 | 49.42 | 18.23 |
429 * | orange³ | E6 | 61.70| 29.38 | 64.40 |
430 * | gray | not available | | | |
431 *
432 * ¹ Focal color as proposed by
433 * <a href="https://www.pnas.org/doi/10.1073/pnas.0503281102">Focal colors
434 * are universal after all</a>.
435 *
436 * ² Raw estimation of the naming centroid based on Fig. 4 of
437 * <a href="https://sites.socsci.uci.edu/~kjameson/ECST/Kay_Cook_WorldColorSurvey.pdf">
438 * this document</a>. (Fig. 5 would be the better choice, as it gives the
439 * focal color instead of the naming centroid, but unfortunately contains
440 * only red, yellow, green and blue, for which we have yet direct data.)
441 *
442 * ³ <a href="https://www1.icsi.berkeley.edu/wcs/data/cnum-maps/cnum-vhcm-lab-new.txt">
443 * Lookup table providing Lab values for WCS grid coordinates</a> and the
444 * <a href="https://www1.icsi.berkeley.edu/wcs/data/cnum-maps/cnum-vhcm-lab-new-README.txt">
445 * corresponding explanation</a>.
446 *
447 * From this data, the colors in our swatch grid have been derived as follows:
448 * - The gray axis has been defined manually, ignoring the WCS data. Chroma
449 * is 0. The lightness is 100% for white, 0% for black, and 75%, 50%,
450 * and 25% for the intermediate grays.
451 * - The other columns for chromatic colors use the WCS data for the swatch in
452 * the middle. Tints and shades are calculated by adding or reducing chroma
453 * and lightness within the Oklab color space. If the resulting color falls
454 * outside the color space, a nearby in-gamut color is chosen instead.
455 *
456 * @param colorSpace The color space in which the return value is calculated.
457 *
458 * @returns Swatch grid derived from the basic colors. Provides as a list of
459 * basic colors (in this order: red, orange, yellow, green, blue, purple, pink,
460 * brown, gray axis). Each basic color is a list of 5 swatches (starting with
461 * the lightest and finishing with the darkest: 2 tints, the tone itself,
462 * 2 shades).
463 *
464 * @note The RGB value is rounded to full integers in the range [0, 255]. */
466{
467 constexpr GenericColor red{41.22, 61.40, 17.92};
468 constexpr GenericColor orange{61.70, 29.38, 64.40};
469 constexpr GenericColor yellow{81.35, 07.28, 109.12};
470 constexpr GenericColor green{51.57, -63.28, 28.95};
471 constexpr GenericColor blue{51.57, -03.41, -48.08};
472 constexpr GenericColor purple{41.22, 33.08, -30.50};
473 constexpr GenericColor pink{61.70, 49.42, 18.23};
474 constexpr GenericColor brown{41.22, 17.04, 45.95};
475 constexpr std::array<GenericColor, 8> chromaticCielabColors //
476 {{red, orange, yellow, green, blue, purple, pink, brown}};
477
478 // Lowest common denominator of QList‘s and std::array’s size types:
479 using MySizeType = quint8;
480
481 constexpr MySizeType columnCount = //
482 chromaticCielabColors.size() + 1; // + 1 for gray axis
483 constexpr auto rowCount = 5;
484 Swatches wcsSwatches{columnCount, rowCount};
485
486 // Chromatic colors
487 constexpr double strongTint = 0.46;
488 constexpr double weakTint = 0.23;
489 constexpr double weakShade = 0.18;
490 constexpr double strongShade = 0.36;
491 std::array<GenericColor, rowCount> tintsAndShades;
492 for (MySizeType i = 0; i < chromaticCielabColors.size(); ++i) { //
493 const auto oklch = AbsoluteColor::convert( //
495 chromaticCielabColors.at(i),
497 )
498 .value_or(GenericColor());
499 tintsAndShades[0] = GenericColor //
500 {oklch.first + (1 - oklch.first) * strongTint, //
501 oklch.second * (1 - strongTint), //
502 oklch.third};
503 tintsAndShades[1] = GenericColor //
504 {oklch.first + (1 - oklch.first) * weakTint, //
505 oklch.second * (1 - weakTint), //
506 oklch.third};
507 tintsAndShades[2] = oklch;
508 tintsAndShades[3] = GenericColor //
509 {oklch.first * (1 - weakShade), //
510 oklch.second * (1 - weakShade), //
511 oklch.third};
512 tintsAndShades[4] = GenericColor //
513 {oklch.first * (1 - strongShade), //
514 oklch.second * (1 - strongShade), //
515 oklch.third};
516 for (MySizeType j = 0; j < rowCount; ++j) {
517 const auto variationCielchD50 = AbsoluteColor::convert( //
519 tintsAndShades.at(j), //
521 )
522 .value_or(GenericColor());
523 const auto variationRgb = colorSpace->fromCielchD50ToQRgbBound( //
524 variationCielchD50);
525 wcsSwatches.setValue(i, //
526 j,
527 variationRgb);
528 }
529 }
530
531 // Gray axis
532 QList<double> lightnesses{1, 0.75, 0.5, 0.25, 0};
533 for (int j = 0; j < lightnesses.count(); ++j) {
534 const GenericColor myOklab{lightnesses.at(j), 0, 0};
535 const auto cielchD50 = AbsoluteColor::convert( //
537 myOklab, //
539 )
540 .value_or(GenericColor());
541 const auto rgb = colorSpace->fromCielchD50ToQRgbBound(cielchD50);
542 wcsSwatches.setValue(columnCount - 1, j, rgb);
543 }
544
545 return wcsSwatches;
546}
547
548/**
549 * @brief The rendering intents supported by the LittleCMS library.
550 *
551 * Contains all rendering intents supported by the LittleCMS library
552 * against which this we are currently linking (or by one of the
553 * LittleCMS library plugigs). Each entry contains the code and the
554 * description (english-language, might be empty) just as
555 * provided by LittleCMS’ <tt>cmsGetSupportedIntents()</tt>.
556 *
557 * Note that LittleCMS supports as built-in intents the four official
558 * ICC intents and also some other, non-ICC intents. Furthermore,
559 * LittleCMS plugins can provide even more intents. As of LittleCMS 2.13
560 * the following built-in intents are available:
561 *
562 * | Type | Macro name | Code |
563 * | :------ | :-------------------------------------------- | ---: |
564 * | ICC | INTENT_PERCEPTUAL | 0 |
565 * | ICC | INTENT_RELATIVE_COLORIMETRIC | 1 |
566 * | ICC | INTENT_SATURATION | 2 |
567 * | ICC | INTENT_ABSOLUTE_COLORIMETRIC | 3 |
568 * | Non-ICC | INTENT_PRESERVE_K_ONLY_PERCEPTUAL | 10 |
569 * | Non-ICC | INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC | 11 |
570 * | Non-ICC | INTENT_PRESERVE_K_ONLY_SATURATION | 12 |
571 * | Non-ICC | INTENT_PRESERVE_K_PLANE_PERCEPTUAL | 13 |
572 * | Non-ICC | INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC | 14 |
573 * | Non-ICC | INTENT_PRESERVE_K_PLANE_SATURATION | 15 |
574 *
575 * @returns A QMap of key-value pairs. The key is the code representing the
576 * rendering intent. The value is a QString with the description.
577 */
579{
580 // Thread-Safe Lazy Initialization: Starting with C++11, function-local
581 // static variables are guaranteed to be thread-safe during initialization.
582 static const QMap<cmsUInt32Number, QString> result = []() {
584 const cmsUInt32Number intentCount = //
585 cmsGetSupportedIntents(0, nullptr, nullptr);
586 cmsUInt32Number *codeArray = new cmsUInt32Number[intentCount];
587 char **descriptionArray = new char *[intentCount];
588 cmsGetSupportedIntents(intentCount, codeArray, descriptionArray);
589 for (cmsUInt32Number i = 0; i < intentCount; ++i) {
590 lambdaResult.insert(codeArray[i], QString::fromUtf8(descriptionArray[i]));
591 }
592 delete[] codeArray;
593 delete[] descriptionArray;
594 return lambdaResult;
595 }();
596 return result;
597}
598
599} // 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:465
@ 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:578
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 Apr 25 2025 12:03:13 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.