Grok 20.3.2
GrkImage.h
Go to the documentation of this file.
1/*
2 * Copyright (C) 2016-2026 Grok Image Compression Inc.
3 *
4 * This source code is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License, version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This source code is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Affero General Public License for more details.
12 *
13 * You should have received a copy of the GNU Affero General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18#pragma once
19
20#include <set>
21#include <algorithm>
22#include <cmath>
23
24#define CMS_NO_REGISTER_KEYWORD 1
25#include "lcms2.h"
26
27#include "packer.h"
28#include "GrkImageMeta.h"
29#include "GrkImageSIMD.h"
30
31namespace grk
32{
33struct Tile;
34struct CodingParams;
35struct TileComponent;
36
37const uint32_t singleTileRowsPerStrip = 32;
38
39constexpr uint32_t GRK_CIE_DAY = ((((uint32_t)'C') << 24) + (((uint32_t)'T') << 16));
40constexpr uint32_t GRK_CIE_D50 = ((uint32_t)0x00443530);
41constexpr uint32_t GRK_CIE_D65 = ((uint32_t)0x00443635);
42constexpr uint32_t GRK_CIE_D75 = ((uint32_t)0x00443735);
43constexpr uint32_t GRK_CIE_SA = ((uint32_t)0x00005341);
44constexpr uint32_t GRK_CIE_SC = ((uint32_t)0x00005343);
45constexpr uint32_t GRK_CIE_F2 = ((uint32_t)0x00004632);
46constexpr uint32_t GRK_CIE_F7 = ((uint32_t)0x00004637);
47constexpr uint32_t GRK_CIE_F11 = ((uint32_t)0x00463131);
48
53class GrkImage : public grk_image
54{
56
57public:
61 GrkImage();
73 static GrkImage* create(grk_image* src, uint16_t numcmpts, grk_image_comp* cmptparms,
74 GRK_COLOR_SPACE clrspc, bool doAllocation);
83 static bool allocData(grk_image_comp* imageComp, bool clear);
84
92 static bool allocData(grk_image_comp* imageComp);
93
99 bool allocCompositeData(void);
100
107 void copyHeaderTo(GrkImage* dest) const;
112 void transferDataTo(GrkImage* dest);
113 void filterComponents(const std::vector<uint16_t>& compsToKeep);
114 GrkImage* extractFrom(const Tile* tile_src) const;
115 bool composite(const GrkImage* src);
116 bool greyToRGB(void);
117 template<typename T>
118 bool applyColourManagement(void);
119 bool validateICC(void);
120 void all_components_data_free(void);
122 void validateColourSpace(void);
123 bool isSubsampled() const;
124
125 bool check_color(uint16_t signalledNumComps);
126 void apply_channel_definition(void);
128 bool needsChannelDefinitionSwap(void) const;
129 void allocPalette(uint8_t num_channels, uint16_t num_entries);
130 uint32_t width(void) const;
131 uint32_t height(void) const;
132 void print(void) const;
133 bool componentsEqual(bool checkPrecision) const;
134 bool componentsEqual(uint16_t firstNComponents, bool checkPrecision) const;
135 static void setDataToNull(grk_image_comp* comp);
136
142 Rect32 getBounds(void) const;
143
150 bool subsampleAndReduce(uint8_t reduce);
151
152 bool compositeInterleaved(const Tile* src, uint32_t yBegin, uint32_t yEnd)
153 {
154 switch(comps[0].data_type) // assume all comps same type
155 {
156 case GRK_INT_32:
157 return compositeInterleaved_T<int32_t>(src, yBegin, yEnd);
158 case GRK_INT_16:
159 return compositeInterleaved_T<int16_t>(src, yBegin, yEnd);
160 // case GRK_INT_8:
161 // return compositeInterleaved_T<int8_t>(src,yBegin,yEnd);
162 // case GRK_FLOAT:
163 // return compositeInterleaved_T<float>(src,yBegin,yEnd);
164 // case GRK_DOUBLE:
165 // return compositeInterleaved_T<double>(src,yBegin,yEnd);
166 default:
167 return false;
168 }
169 }
170 bool postProcess(void)
171 {
172 switch(comps[0].data_type)
173 {
174 case GRK_INT_32:
175 return postProcess_T<int32_t>();
176 case GRK_INT_16:
177 return postProcess_T<int16_t>();
178 // case GRK_INT_8:
179 // return postProcess_T<int8_t>();
180 // case GRK_FLOAT:
181 // return postProcess_T<float>();
182 // case GRK_DOUBLE:
183 // return postProcess_T<double>();
184 default:
185 return false;
186 }
187 }
188
189 bool applyColour(void)
190 {
191 switch(comps[0].data_type)
192 {
193 case GRK_INT_32:
194 return applyColour_T<int32_t>();
195 case GRK_INT_16:
196 return applyColour_T<int16_t>();
197 // case GRK_INT_8:
198 // return applyColour_T<int8_t>();
199 // case GRK_FLOAT:
200 // return applyColour_T<float>();
201 // case GRK_DOUBLE:
202 // return applyColour_T<double>();
203 default:
204 return false;
205 }
206 }
207
208 void transferDataFrom(const Tile* tile_src_data);
209
216 bool isPostProcessNoOp(void) const;
217
218private:
224 ~GrkImage();
225
226 template<typename T>
227 bool applyColour_T(void);
228
229 template<typename T>
230 bool convertToRGB_T();
231
232 template<typename T>
233 bool compositeInterleaved_T(const Tile* src, uint32_t yBegin, uint32_t yEnd);
234
235 template<typename T>
236 bool postProcess_T(void);
237
238 static size_t sizeOfDataType(grk_data_type type);
240 std::string getColourSpaceString(void) const;
241 std::string getICCColourSpaceString(cmsColorSpaceSignature color_space) const;
242 bool isValidICCColourSpace(uint32_t signature) const;
243 bool needsConversionToRGB(void) const;
244 bool isOpacity(uint16_t compno) const;
245 bool generateCompositeBounds(const grk_image_comp* srcComp, uint16_t destCompno, Rect32* destWin);
246 bool generateCompositeBounds(const Rect32& src, uint16_t destCompno, Rect32* destWin);
247 bool allComponentsSanityCheck(bool equalPrecision) const;
248 grk_image* createRGB(uint16_t numcmpts, uint32_t w, uint32_t h, uint8_t prec);
249 bool componentsEqual(grk_image_comp* src, grk_image_comp* dest, bool checkPrecision) const;
250 static void copyComponent(grk_image_comp* src, grk_image_comp* dest);
251
252public:
253 template<typename T>
254 void convertPrecision(void);
255
256 template<typename T>
257 bool apply_palette_clr();
258
259 template<typename T>
260 bool execUpsample(void);
261
262 template<typename T>
263 void transferDataFrom_T(const Tile* tile_src_data);
264
272 template<typename T>
273 bool compositeInterleaved(uint16_t srcNumComps, grk_image_comp* srcComps);
274
275 /*#define DEBUG_PROFILE*/
276 template<typename T>
277 bool applyICC(void);
278
279private:
280 template<typename T>
281 void scaleComponent(grk_image_comp* component, uint8_t precision);
282
289 template<typename T>
290 bool compositePlanar(uint16_t srcNumComps, grk_image_comp* srcComps);
291 /*--------------------------------------------------------
292 Matrix for sYCC, Amendment 1 to IEC 61966-2-1
293
294 Y | 0.299 0.587 0.114 | R
295 Cb | -0.1687 -0.3312 0.5 | x G
296 Cr | 0.5 -0.4187 -0.0812 | B
297
298 Inverse:
299
300 R |1 -3.68213e-05 1.40199 | Y
301 G = |1.00003 -0.344125 -0.714128 | x Cb - 2^(prec - 1)
302 B |0.999823 1.77204 -8.04142e-06 | Cr - 2^(prec - 1)
303
304 -----------------------------------------------------------*/
305 template<typename T>
306 void sycc_to_rgb(T offset, T upb, T y, T cb, T cr, T* out_r, T* out_g, T* out_b);
307
308 template<typename T>
309 bool sycc444_to_rgb(void);
310
311 template<typename T>
312 bool sycc422_to_rgb(bool oddFirstX);
313
314 template<typename T>
315 bool sycc420_to_rgb(bool oddFirstX, bool oddFirstY);
316
317 template<typename T>
318 bool color_sycc_to_rgb(bool oddFirstX, bool oddFirstY);
319
320 template<typename T>
321 bool color_cmyk_to_rgb(void);
322
323 // assuming unsigned data !
324 template<typename T>
325 bool color_esycc_to_rgb(void);
326
327 // transform LAB colour space to sRGB @ 16 bit precision
328 template<typename T>
329 bool cieLabToRGB(void);
330};
331
332template<typename T>
334{
335 // sanity checks
336 if(numcomps == 0 || !allComponentsSanityCheck(true))
337 return false;
338 if(numcomps < 3)
339 {
340 grklog.warn("cieLabToRGB: there must be at least three components");
341 return false;
342 }
343 if(numcomps > 3)
344 grklog.warn("cieLabToRGB: there are more than three components : extra components will be "
345 "ignored.");
346 if(!meta)
347 return false;
348 if(comps[0].sgnd || comps[1].sgnd || comps[2].sgnd)
349 {
350 grklog.warn("cieLabToRGB: components must be unsigned");
351 return false;
352 }
353 size_t i;
354 for(i = 1U; i < numcomps; ++i)
355 {
356 auto comp0 = comps;
357 auto compi = comps + i;
358
359 if(comp0->stride != compi->stride)
360 break;
361
362 if(comp0->w != compi->w)
363 break;
364
365 if(comp0->h != compi->h)
366 break;
367 }
368 if(i != numcomps)
369 {
370 grklog.warn("cieLabToRGB: all components must have same dimensions, precision and sign");
371 return false;
372 }
373
374 auto row = (uint32_t*)meta->color.icc_profile_buf;
375 auto enumcs = (GRK_ENUM_COLOUR_SPACE)row[0];
376 if(enumcs != GRK_ENUM_CLRSPC_CIE)
377 { /* CIELab */
378 grklog.warn("enumCS %d not handled. Ignoring.", enumcs);
379 return false;
380 }
381
382 bool defaultType = true;
383 color_space = GRK_CLRSPC_SRGB;
384 defaultType = row[1] == GRK_DEFAULT_CIELAB_SPACE;
385 T *L, *a, *b, *red, *green, *blue;
386 // range, offset and precision for L,a and b coordinates
387 double r_L, o_L, r_a, o_a, r_b, o_b, prec_L, prec_a, prec_b;
388 double minL, maxL, mina, maxa, minb, maxb;
389 cmsUInt16Number RGB[3];
390 prec_L = (double)comps[0].prec;
391 prec_a = (double)comps[1].prec;
392 prec_b = (double)comps[2].prec;
393
394 uint32_t illuminant = GRK_CIE_D50;
395 if(defaultType)
396 { // default Lab space
397 r_L = 100;
398 r_a = 170;
399 r_b = 200;
400 o_L = 0;
401 o_a = pow(2, prec_a - 1); // 2 ^ (prec_b - 1)
402 o_b = 3 * pow(2, prec_b - 3); // 0.75 * 2 ^ (prec_b - 1)
403 }
404 else
405 {
406 r_L = row[2];
407 r_a = row[4];
408 r_b = row[6];
409 o_L = row[3];
410 o_a = row[5];
411 o_b = row[7];
412 illuminant = row[8];
413 }
414 cmsCIExyY WhitePoint;
415 switch(illuminant)
416 {
417 case GRK_CIE_D50:
418 break;
419 case GRK_CIE_D65:
420 cmsWhitePointFromTemp(&WhitePoint, 6504);
421 break;
422 case GRK_CIE_D75:
423 cmsWhitePointFromTemp(&WhitePoint, 7500);
424 break;
425 case GRK_CIE_SA:
426 cmsWhitePointFromTemp(&WhitePoint, 2856);
427 break;
428 case GRK_CIE_SC:
429 cmsWhitePointFromTemp(&WhitePoint, 6774);
430 break;
431 case GRK_CIE_F2:
432 cmsWhitePointFromTemp(&WhitePoint, 4100);
433 break;
434 case GRK_CIE_F7:
435 cmsWhitePointFromTemp(&WhitePoint, 6500);
436 break;
437 case GRK_CIE_F11:
438 cmsWhitePointFromTemp(&WhitePoint, 4000);
439 break;
440 default:
441 grklog.warn("Unrecognized illuminant %d in CIELab colour space. "
442 "Setting to default Daylight50",
443 illuminant);
444 illuminant = GRK_CIE_D50;
445 break;
446 }
447
448 // Lab input profile
449 auto in = cmsCreateLab4Profile(illuminant == GRK_CIE_D50 ? nullptr : &WhitePoint);
450 // sRGB output profile
451 auto out = cmsCreate_sRGBProfile();
452 auto transform = cmsCreateTransform(in, TYPE_Lab_DBL, out, TYPE_RGB_16, INTENT_PERCEPTUAL, 0);
453
454 cmsCloseProfile(in);
455 cmsCloseProfile(out);
456 if(transform == nullptr)
457 return false;
458
459 L = (T*)comps[0].data;
460 a = (T*)comps[1].data;
461 b = (T*)comps[2].data;
462
463 if(!L || !a || !b)
464 {
465 grklog.warn("color_cielab_to_rgb: null L*a*b component");
466 return false;
467 }
468
469 auto dest_img = createRGB(3, comps[0].w, comps[0].h, comps[0].prec);
470 if(!dest_img)
471 return false;
472
473 red = (T*)dest_img->comps[0].data;
474 green = (T*)dest_img->comps[1].data;
475 blue = (T*)dest_img->comps[2].data;
476
477 uint32_t src_stride_diff = comps[0].stride - comps[0].w;
478 uint32_t dest_stride_diff = dest_img->comps[0].stride - dest_img->comps[0].w;
479
480 minL = -(r_L * o_L) / (pow(2, prec_L) - 1);
481 maxL = minL + r_L;
482
483 mina = -(r_a * o_a) / (pow(2, prec_a) - 1);
484 maxa = mina + r_a;
485
486 minb = -(r_b * o_b) / (pow(2, prec_b) - 1);
487 maxb = minb + r_b;
488
489 size_t dest_index = 0;
490 for(uint32_t j = 0; j < comps[0].h; ++j)
491 {
492 for(uint32_t k = 0; k < comps[0].w; ++k)
493 {
494 cmsCIELab Lab;
495 Lab.L = minL + (double)(*L) * (maxL - minL) / (pow(2, prec_L) - 1);
496 ++L;
497 Lab.a = mina + (double)(*a) * (maxa - mina) / (pow(2, prec_a) - 1);
498 ++a;
499 Lab.b = minb + (double)(*b) * (maxb - minb) / (pow(2, prec_b) - 1);
500 ++b;
501
502 cmsDoTransform(transform, &Lab, RGB, 1);
503
504 red[dest_index] = (T)RGB[0];
505 green[dest_index] = (T)RGB[1];
506 blue[dest_index] = (T)RGB[2];
507 dest_index++;
508 }
509 dest_index += dest_stride_diff;
510 L += src_stride_diff;
511 a += src_stride_diff;
512 b += src_stride_diff;
513 }
514 cmsDeleteTransform(transform);
515
516 for(i = 0; i < numcomps; ++i)
518
519 numcomps = 3;
520 for(i = 0; i < numcomps; ++i)
521 {
522 auto srcComp = comps + i;
523 auto destComp = dest_img->comps + i;
524
525 srcComp->prec = 16;
526 srcComp->stride = destComp->stride;
527 srcComp->data = destComp->data;
528 }
529 // clean up dest image
530 setDataToNull(dest_img->comps);
531 setDataToNull(dest_img->comps + 1);
532 setDataToNull(dest_img->comps + 2);
533 grk_unref(dest_img);
534
535 color_space = GRK_CLRSPC_SRGB;
536
537 return true;
538}
539
540template<typename T>
541void clip(grk_image_comp* component, uint8_t precision)
542{
543 uint32_t stride_diff = component->stride - component->w;
544 assert(precision <= GRK_MAX_SUPPORTED_IMAGE_PRECISION);
545 auto data = (T*)component->data;
546 size_t index = 0;
547
548 // Define clamping bounds based on type and signedness
549 T minimum, maximum;
550 if constexpr(std::is_floating_point_v<T>)
551 {
552 // For floating-point types, use normalized ranges
553 if(component->sgnd)
554 {
555 minimum = -1.0f;
556 maximum = 1.0f;
557 }
558 else
559 {
560 minimum = 0.0f;
561 maximum = 1.0f;
562 }
563 }
564 else
565 {
566 // For integer types, compute bounds based on precision
567 if(component->sgnd)
568 {
569 minimum = static_cast<T>(-(1LL << (precision - 1)));
570 maximum = static_cast<T>((1LL << (precision - 1)) - 1);
571 }
572 else
573 {
574 minimum = static_cast<T>(0);
575 maximum = static_cast<T>((1ULL << precision) - 1);
576 }
577 }
578
579 // Clip the data
580 if constexpr(std::is_same_v<T, int32_t>)
581 {
582 hwy_clip_i32(data, component->w, component->h, component->stride, (int32_t)minimum,
583 (int32_t)maximum);
584 }
585 else
586 {
587 for(uint32_t j = 0; j < component->h; ++j)
588 {
589 for(uint32_t i = 0; i < component->w; ++i)
590 {
591 data[index] = std::clamp<T>(data[index], minimum, maximum);
592 index++;
593 }
594 index += stride_diff;
595 }
596 }
597 component->prec = precision;
598}
599
600template<typename T>
602{
603 if(precision)
604 {
605 for(uint16_t compno = 0; compno < numcomps; ++compno)
606 {
607 uint32_t precisionno = compno;
608 if(precisionno >= num_precision)
609 precisionno = num_precision - 1U;
610 uint8_t prec = precision[precisionno].prec;
611 auto comp = comps + compno;
612 if(prec == 0)
613 prec = comp->prec;
614 switch(precision[precisionno].mode)
615 {
617 clip<T>(comp, prec);
618 break;
620 scaleComponent<T>(comp, prec);
621 break;
622 default:
623 break;
624 }
625 }
626 }
627 if(decompress_fmt == GRK_FMT_JPG)
628 {
629 uint8_t prec = comps[0].prec;
630 if(prec < 8 && numcomps > 1)
631 { /* GRAY_ALPHA, RGB, RGB_ALPHA */
632 for(uint16_t i = 0; i < numcomps; ++i)
633 scaleComponent<T>(comps + i, 8);
634 }
635 else if((prec > 1) && (prec < 8) && ((prec == 6) || ((prec & 1) == 1)))
636 { /* GRAY with non native precision */
637 if((prec == 5) || (prec == 6))
638 prec = 8;
639 else
640 prec++;
641 for(uint16_t i = 0; i < numcomps; ++i)
642 scaleComponent<T>(comps + i, prec);
643 }
644 }
645 else if(decompress_fmt == GRK_FMT_PNG)
646 {
647 uint16_t nr_comp = numcomps;
648 if(nr_comp > 4)
649 {
650 grklog.warn("PNG: number of components %d is "
651 "greater than 4. Truncating to 4",
652 nr_comp);
653 nr_comp = 4;
654 }
655 uint8_t prec = comps[0].prec;
656 if(prec > 8 && prec < 16)
657 {
658 prec = 16;
659 }
660 else if(prec < 8 && nr_comp > 1)
661 { /* GRAY_ALPHA, RGB, RGB_ALPHA */
662 prec = 8;
663 }
664 else if((prec > 1) && (prec < 8) && ((prec == 6) || ((prec & 1) == 1)))
665 { /* GRAY with non native precision */
666 if((prec == 5) || (prec == 6))
667 prec = 8;
668 else
669 prec++;
670 }
671 for(uint16_t i = 0; i < nr_comp; ++i)
672 scaleComponent<T>(comps + i, prec);
673 }
674}
675
676// assuming unsigned data !
677template<typename T>
679{
680 if((numcomps < 3) || !allComponentsSanityCheck(true))
681 return false;
682 if(comps[0].sgnd || comps[1].sgnd || comps[2].sgnd)
683 {
684 grklog.warn("color_esycc_to_rgb: components must be unsigned");
685 return false;
686 }
687
688 T flip_value = (T)((1ULL << (comps[0].prec - 1)));
689 T max_value = (T)((1ULL << comps[0].prec) - 1);
690
691 uint32_t w = comps[0].w;
692 uint32_t h = comps[0].h;
693
694 bool sign1 = comps[1].sgnd;
695 bool sign2 = comps[2].sgnd;
696
697 uint32_t stride_diff = comps[0].stride - w;
698 size_t dest_index = 0;
699 auto yd = (T*)comps[0].data;
700 auto bd = (T*)comps[1].data;
701 auto rd = (T*)comps[2].data;
702
703 if constexpr(std::is_same_v<T, int32_t>)
704 {
705 hwy_esycc_to_rgb_i32(yd, bd, rd, w, h, comps[0].stride, max_value, flip_value, sign1, sign2);
706 }
707 else
708 {
709 for(uint32_t j = 0; j < h; ++j)
710 {
711 for(uint32_t i = 0; i < w; ++i)
712 {
713 T y = yd[dest_index];
714 T cb = bd[dest_index];
715 T cr = rd[dest_index];
716
717 if(!sign1)
718 cb -= flip_value;
719 if(!sign2)
720 cr -= flip_value;
721
722 T val = (T)(y - 0.0000368 * cb + 1.40199 * cr + 0.5);
723
724 if(val > max_value)
725 val = max_value;
726 else if(val < 0)
727 val = 0;
728 yd[dest_index] = val;
729
730 val = (T)(1.0003 * y - 0.344125 * cb - 0.7141128 * cr + 0.5);
731
732 if(val > max_value)
733 val = max_value;
734 else if(val < 0)
735 val = 0;
736 bd[dest_index] = val;
737
738 val = (T)(0.999823 * y + 1.77204 * cb - 0.000008 * cr + 0.5);
739
740 if(val > max_value)
741 val = max_value;
742 else if(val < 0)
743 val = 0;
744 rd[dest_index] = val;
745 dest_index++;
746 }
747 dest_index += stride_diff;
748 }
749 }
750 color_space = GRK_CLRSPC_SRGB;
751
752 return true;
753
754} /* color_esycc_to_rgb() */
755
756template<typename T>
758{
759 uint32_t w = comps[0].w;
760 uint32_t h = comps[0].h;
761
762 if((numcomps < 4) || !allComponentsSanityCheck(true))
763 return false;
764 if(comps[0].sgnd || comps[1].sgnd || comps[2].sgnd || comps[3].sgnd)
765 {
766 grklog.warn("color_cmyk_to_rgb: components must be unsigned");
767 return false;
768 }
769
770 float sC = 1.0F / (float)((1ULL << comps[0].prec) - 1);
771 float sM = 1.0F / (float)((1ULL << comps[1].prec) - 1);
772 float sY = 1.0F / (float)((1ULL << comps[2].prec) - 1);
773 float sK = 1.0F / (float)((1ULL << comps[3].prec) - 1);
774
775 uint32_t stride_diff = comps[0].stride - w;
776 size_t dest_index = 0;
777 auto cd = (T*)comps[0].data;
778 auto md = (T*)comps[1].data;
779 auto yd = (T*)comps[2].data;
780 auto kd = (T*)comps[3].data;
781
782 for(uint32_t j = 0; j < h; ++j)
783 {
784 for(uint32_t i = 0; i < w; ++i)
785 {
786 /* CMYK values from 0 to 1 */
787 float C = std::clamp((float)(cd[dest_index]) * sC, 0.0f, 1.0f);
788 float M = std::clamp((float)(md[dest_index]) * sM, 0.0f, 1.0f);
789 float Y = std::clamp((float)(yd[dest_index]) * sY, 0.0f, 1.0f);
790 float K = std::clamp((float)(kd[dest_index]) * sK, 0.0f, 1.0f);
791
792 /* Invert all CMYK values */
793 C = 1.0F - C;
794 M = 1.0F - M;
795 Y = 1.0F - Y;
796 K = 1.0F - K;
797
798 /* CMYK -> RGB : RGB results from 0 to 255 */
799 cd[dest_index] = (T)(255.0F * C * K); /* R */
800 md[dest_index] = (T)(255.0F * M * K); /* G */
801 yd[dest_index] = (T)(255.0F * Y * K); /* B */
802 dest_index++;
803 }
804 dest_index += stride_diff;
805 }
806
808 comps[0].prec = 8;
809 comps[1].prec = 8;
810 comps[2].prec = 8;
811 numcomps = (uint16_t)(numcomps - 1U);
812 color_space = GRK_CLRSPC_SRGB;
813
814 for(uint16_t i = 3; i < numcomps; ++i)
815 memcpy(&(comps[i]), &(comps[i + 1]), sizeof(comps[i]));
816
817 return true;
818
819} /* color_cmyk_to_rgb() */
820
821template<typename T>
822bool GrkImage::color_sycc_to_rgb(bool oddFirstX, bool oddFirstY)
823{
824 if(numcomps != 3)
825 {
826 grklog.warn("color_sycc_to_rgb: number of components %d is not equal to 3."
827 " Unable to convert",
828 numcomps);
829 return false;
830 }
831 if(comps[0].sgnd || comps[1].sgnd || comps[2].sgnd)
832 {
833 grklog.warn("color_sycc_to_rgb: components must be unsigned");
834 return false;
835 }
836
837 bool rc;
838
839 if((comps[0].dx == 1) && (comps[1].dx == 2) && (comps[2].dx == 2) && (comps[0].dy == 1) &&
840 (comps[1].dy == 2) && (comps[2].dy == 2))
841 { /* horizontal and vertical sub-sample */
842 rc = sycc420_to_rgb<T>(oddFirstX, oddFirstY);
843 }
844 else if((comps[0].dx == 1) && (comps[1].dx == 2) && (comps[2].dx == 2) && (comps[0].dy == 1) &&
845 (comps[1].dy == 1) && (comps[2].dy == 1))
846 { /* horizontal sub-sample only */
847 rc = sycc422_to_rgb<T>(oddFirstX);
848 }
849 else if((comps[0].dx == 1) && (comps[1].dx == 1) && (comps[2].dx == 1) && (comps[0].dy == 1) &&
850 (comps[1].dy == 1) && (comps[2].dy == 1))
851 { /* no sub-sample */
852 rc = sycc444_to_rgb<T>();
853 }
854 else
855 {
856 grklog.warn("color_sycc_to_rgb: Invalid sub-sampling: (%d,%d), (%d,%d), (%d,%d)."
857 " Unable to convert.",
858 comps[0].dx, comps[0].dy, comps[1].dx, comps[1].dy, comps[2].dx, comps[2].dy);
859 rc = false;
860 }
861 if(rc)
862 color_space = GRK_CLRSPC_SRGB;
863
864 return rc;
865
866} /* color_sycc_to_rgb() */
867
868template<typename T>
869void GrkImage::scaleComponent(grk_image_comp* component, uint8_t precision)
870{
871 if(component->prec == precision)
872 return;
873 uint32_t diff =
874 (precision > component->prec) ? (precision - component->prec) : (component->prec - precision);
875 if(diff >= 64) // prevent overflow
876 {
877 grklog.error("scaleComponent: precision difference %u too large", diff);
878 return;
879 }
880 uint32_t stride_diff = component->stride - component->w;
881 auto data = (T*)component->data;
882 if(component->prec < precision)
883 {
884 T scale = (T)(1ULL << diff);
885 if constexpr(std::is_same_v<T, int32_t>)
886 {
887 hwy_scale_mul_i32(data, component->w, component->h, component->stride, scale);
888 }
889 else
890 {
891 size_t index = 0;
892 for(uint32_t j = 0; j < component->h; ++j)
893 {
894 for(uint32_t i = 0; i < component->w; ++i)
895 data[index++] *= scale;
896 index += stride_diff;
897 }
898 }
899 }
900 else
901 {
902 T scale = (T)(1ULL << diff);
903 if constexpr(std::is_same_v<T, int32_t>)
904 {
905 hwy_scale_div_i32(data, component->w, component->h, component->stride, scale);
906 }
907 else
908 {
909 size_t index = 0;
910 for(uint32_t j = 0; j < component->h; ++j)
911 {
912 for(uint32_t i = 0; i < component->w; ++i)
913 data[index++] /= scale;
914 index += stride_diff;
915 }
916 }
917 }
918 component->prec = precision;
919}
920
927template<typename T>
928bool GrkImage::compositePlanar(uint16_t srcNumComps, grk_image_comp* srcComps)
929{
930 for(uint16_t compno = 0; compno < srcNumComps; compno++)
931 {
932 auto destComp = comps + compno;
933 if(!destComp->data)
934 continue;
935 Rect32 destWin;
936 auto srcComp = srcComps + compno;
937 if(!generateCompositeBounds(srcComp, compno, &destWin))
938 {
939 grklog.warn("GrkImage::compositePlanar: cannot generate composite bounds for component %u",
940 compno);
941 continue;
942 }
943 if(!srcComp->data)
944 {
945 grklog.warn("GrkImage::compositePlanar: null data for source component %u", compno);
946 continue;
947 }
948 size_t srcIndex = 0;
949 auto destIndex = (size_t)destWin.x0 + (size_t)destWin.y0 * destComp->stride;
950 size_t destLineOffset = (size_t)destComp->stride - (size_t)destWin.width();
951 uint32_t srcLineOffset = srcComp->stride - srcComp->w;
952
953 // Source and dest have matching element types: fast memcpy path
954 if(srcComp->data_type == destComp->data_type)
955 {
956 auto src_ptr = (T*)srcComp->data;
957 for(uint32_t j = 0; j < destWin.height(); ++j)
958 {
959 memcpy((T*)destComp->data + destIndex, src_ptr + srcIndex,
960 (size_t)destWin.width() * sizeof(T));
961 destIndex += destLineOffset + destWin.width();
962 srcIndex += srcLineOffset + destWin.width();
963 }
964 }
965 // Source is int16 but dest is int32: widen during composite
966 else if(srcComp->data_type == GRK_INT_16 && destComp->data_type == GRK_INT_32)
967 {
968 auto src16 = (int16_t*)srcComp->data;
969 auto dest32 = (int32_t*)destComp->data;
970 for(uint32_t j = 0; j < destWin.height(); ++j)
971 {
972 for(uint32_t x = 0; x < destWin.width(); ++x)
973 dest32[destIndex + x] = src16[srcIndex + x];
974 destIndex += destLineOffset + destWin.width();
975 srcIndex += srcLineOffset + destWin.width();
976 }
977 }
978 else
979 {
980 auto src_ptr = (T*)srcComp->data;
981 for(uint32_t j = 0; j < destWin.height(); ++j)
982 {
983 memcpy((T*)destComp->data + destIndex, src_ptr + srcIndex,
984 (size_t)destWin.width() * sizeof(T));
985 destIndex += destLineOffset + destWin.width();
986 srcIndex += srcLineOffset + destWin.width();
987 }
988 }
989 }
990
991 return true;
992}
993
994/*--------------------------------------------------------
995Matrix for sYCC, Amendment 1 to IEC 61966-2-1
996
997Y | 0.299 0.587 0.114 | R
998Cb | -0.1687 -0.3312 0.5 | x G
999Cr | 0.5 -0.4187 -0.0812 | B
1000
1001Inverse:
1002
1003R |1 -3.68213e-05 1.40199 | Y
1004G = |1.00003 -0.344125 -0.714128 | x Cb - 2^(prec - 1)
1005B |0.999823 1.77204 -8.04142e-06 | Cr - 2^(prec - 1)
1006
1007-----------------------------------------------------------*/
1008template<typename T>
1009void GrkImage::sycc_to_rgb(T offset, T upb, T y, T cb, T cr, T* out_r, T* out_g, T* out_b)
1010{
1011 T r, g, b;
1012
1013 cb -= offset;
1014 cr -= offset;
1015 r = y + (T)(1.402 * cr);
1016 if(r < 0)
1017 r = 0;
1018 else if(r > upb)
1019 r = upb;
1020 *out_r = r;
1021
1022 g = y - (T)(0.344 * cb + 0.714 * cr);
1023 if(g < 0)
1024 g = 0;
1025 else if(g > upb)
1026 g = upb;
1027 *out_g = g;
1028
1029 b = y + (T)(1.772 * cb);
1030 if(b < 0)
1031 b = 0;
1032 else if(b > upb)
1033 b = upb;
1034 *out_b = b;
1035}
1036
1037template<typename T>
1039{
1040 T *d0, *d1, *d2, *r, *g, *b;
1041 auto dst = createRGB(3, comps[0].w, comps[0].h, comps[0].prec);
1042 if(!dst)
1043 return false;
1044
1045 T offset = (T)(1ULL << (comps[0].prec - 1));
1046 T upb = (T)((1ULL << comps[0].prec) - 1);
1047
1048 uint32_t w = comps[0].w;
1049 uint32_t src_stride_diff = comps[0].stride - w;
1050 uint32_t dst_stride_diff = dst->comps[0].stride - dst->comps[0].w;
1051 uint32_t h = comps[0].h;
1052
1053 auto y = (T*)comps[0].data;
1054 auto cb = (T*)comps[1].data;
1055 auto cr = (T*)comps[2].data;
1056
1057 d0 = r = (T*)dst->comps[0].data;
1058 d1 = g = (T*)dst->comps[1].data;
1059 d2 = b = (T*)dst->comps[2].data;
1060
1061 dst->comps[0].data = nullptr;
1062 dst->comps[1].data = nullptr;
1063 dst->comps[2].data = nullptr;
1064
1065 if constexpr(std::is_same_v<T, int32_t>)
1066 {
1067 hwy_sycc444_to_rgb_i32(y, cb, cr, r, g, b, w, h, comps[0].stride, dst->comps[0].stride, offset,
1068 upb);
1069 }
1070 else
1071 {
1072 for(uint32_t j = 0; j < h; ++j)
1073 {
1074 for(uint32_t i = 0; i < w; ++i)
1075 sycc_to_rgb<T>(offset, upb, *y++, *cb++, *cr++, r++, g++, b++);
1076 y += src_stride_diff;
1077 cb += src_stride_diff;
1078 cr += src_stride_diff;
1079 r += dst_stride_diff;
1080 g += dst_stride_diff;
1081 b += dst_stride_diff;
1082 }
1083 }
1084
1086 comps[0].data = d0;
1087 comps[1].data = d1;
1088 comps[2].data = d2;
1089 color_space = GRK_CLRSPC_SRGB;
1090
1091 for(uint16_t i = 0; i < numcomps; ++i)
1092 {
1093 comps[i].stride = dst->comps[i].stride;
1094 comps[i].owns_data = true;
1095 }
1096 grk_unref(dst);
1097
1098 return true;
1099} /* sycc444_to_rgb() */
1100
1101template<typename T>
1102bool GrkImage::sycc422_to_rgb(bool oddFirstX)
1103{
1104 /* if img->x0 is odd, then first column shall use Cb/Cr = 0 */
1105 uint32_t w = comps[0].w;
1106 uint32_t h = comps[0].h;
1107 uint32_t loopWidth = w;
1108 if(oddFirstX)
1109 loopWidth--;
1110 // sanity check
1111 if((loopWidth + 1) / 2 != comps[1].w)
1112 {
1113 grklog.warn("incorrect subsampled width %u", comps[1].w);
1114 return false;
1115 }
1116
1117 auto dst = createRGB(3, w, h, comps[0].prec);
1118 if(!dst)
1119 return false;
1120
1121 T offset = (T)(1ULL << (comps[0].prec - 1));
1122 T upb = (T)((1ULL << comps[0].prec) - 1);
1123
1124 uint32_t dst_stride_diff = dst->comps[0].stride - dst->comps[0].w;
1125 uint32_t src_stride_diff = comps[0].stride - w;
1126 uint32_t src_stride_diff_chroma = comps[1].stride - comps[1].w;
1127
1128 T *d0, *d1, *d2, *r, *g, *b;
1129
1130 auto y = (T*)comps[0].data;
1131 if(!y)
1132 {
1133 grklog.warn("sycc422_to_rgb: null luma channel");
1134 return false;
1135 }
1136 auto cb = (T*)comps[1].data;
1137 auto cr = (T*)comps[2].data;
1138 if(!cb || !cr)
1139 {
1140 grklog.warn("sycc422_to_rgb: null chroma channel");
1141 return false;
1142 }
1143
1144 d0 = r = (T*)dst->comps[0].data;
1145 d1 = g = (T*)dst->comps[1].data;
1146 d2 = b = (T*)dst->comps[2].data;
1147
1148 dst->comps[0].data = nullptr;
1149 dst->comps[1].data = nullptr;
1150 dst->comps[2].data = nullptr;
1151
1152 for(uint32_t i = 0U; i < h; ++i)
1153 {
1154 if(oddFirstX)
1155 sycc_to_rgb<T>(offset, upb, *y++, 0, 0, r++, g++, b++);
1156
1157 uint32_t j;
1158 for(j = 0U; j < (loopWidth & ~(size_t)1U); j += 2U)
1159 {
1160 sycc_to_rgb<T>(offset, upb, *y++, *cb, *cr, r++, g++, b++);
1161 sycc_to_rgb<T>(offset, upb, *y++, *cb++, *cr++, r++, g++, b++);
1162 }
1163 if(j < loopWidth)
1164 sycc_to_rgb<T>(offset, upb, *y++, *cb++, *cr++, r++, g++, b++);
1165
1166 y += src_stride_diff;
1167 cb += src_stride_diff_chroma;
1168 cr += src_stride_diff_chroma;
1169 r += dst_stride_diff;
1170 g += dst_stride_diff;
1171 b += dst_stride_diff;
1172 }
1174
1175 comps[0].data = d0;
1176 comps[1].data = d1;
1177 comps[2].data = d2;
1178
1179 comps[1].w = comps[2].w = w;
1180 comps[1].h = comps[2].h = h;
1181 comps[1].dx = comps[2].dx = comps[0].dx;
1182 comps[1].dy = comps[2].dy = comps[0].dy;
1183 color_space = GRK_CLRSPC_SRGB;
1184
1185 for(uint32_t i = 0; i < numcomps; ++i)
1186 {
1187 comps[i].stride = dst->comps[i].stride;
1188 comps[i].owns_data = true;
1189 }
1190 grk_unref(dst);
1191
1192 return true;
1193
1194} /* sycc422_to_rgb() */
1195
1196template<typename T>
1197bool GrkImage::sycc420_to_rgb(bool oddFirstX, bool oddFirstY)
1198{
1199 uint32_t w = comps[0].w;
1200 uint32_t h = comps[0].h;
1201 uint32_t loopWidth = w;
1202 // if img->x0 is odd, then first column shall use Cb/Cr = 0
1203 // this is handled in the loop below
1204 if(oddFirstX)
1205 loopWidth--;
1206 uint32_t loopHeight = h;
1207 // if img->y0 is odd, then first line shall use Cb/Cr = 0
1208 if(oddFirstY)
1209 loopHeight--;
1210
1211 // sanity check
1212 if((loopWidth + 1) / 2 != comps[1].w)
1213 {
1214 grklog.warn("incorrect subsampled width %u", comps[1].w);
1215 return false;
1216 }
1217 if((loopHeight + 1) / 2 != comps[1].h)
1218 {
1219 grklog.warn("incorrect subsampled height %u", comps[1].h);
1220 return false;
1221 }
1222
1223 auto rgbImg = createRGB(3, w, h, comps[0].prec);
1224 if(!rgbImg)
1225 return false;
1226
1227 T offset = (T)(1ULL << (comps[0].prec - 1));
1228 T upb = (T)((1ULL << comps[0].prec) - 1);
1229
1230 uint32_t stride_src[3];
1231 uint32_t stride_src_diff[3];
1232
1233 uint32_t stride_dest = rgbImg->comps[0].stride;
1234 uint32_t stride_dest_diff = rgbImg->comps[0].stride - w;
1235
1236 T* src[3];
1237 T* dest_ptr[3];
1238 for(uint32_t i = 0; i < 3; ++i)
1239 {
1240 auto srcComp = comps + i;
1241 src[i] = (T*)srcComp->data;
1242 stride_src[i] = srcComp->stride;
1243 stride_src_diff[i] = srcComp->stride - srcComp->w;
1244
1245 dest_ptr[i] = (T*)rgbImg->comps[i].data;
1246 }
1247 // if img->y0 is odd, then first line shall use Cb/Cr = 0
1248 if(oddFirstY)
1249 {
1250 for(size_t j = 0U; j < w; ++j)
1251 sycc_to_rgb<T>(offset, upb, *src[0]++, 0, 0, dest_ptr[0]++, dest_ptr[1]++, dest_ptr[2]++);
1252 src[0] += stride_src_diff[0];
1253 for(uint32_t i = 0; i < 3; ++i)
1254 dest_ptr[i] += stride_dest_diff;
1255 }
1256
1257 size_t i;
1258 for(i = 0U; i < (loopHeight & ~(size_t)1U); i += 2U)
1259 {
1260 auto nextY = src[0] + stride_src[0];
1261 auto nextRed = dest_ptr[0] + stride_dest;
1262 auto nextGreen = dest_ptr[1] + stride_dest;
1263 auto nextBlue = dest_ptr[2] + stride_dest;
1264 // if img->x0 is odd, then first column shall use Cb/Cr = 0
1265 if(oddFirstX)
1266 {
1267 sycc_to_rgb<T>(offset, upb, *src[0]++, 0, 0, dest_ptr[0]++, dest_ptr[1]++, dest_ptr[2]++);
1268 sycc_to_rgb<T>(offset, upb, *nextY++, *src[1], *src[2], nextRed++, nextGreen++, nextBlue++);
1269 }
1270 uint32_t j;
1271 for(j = 0U; j < (loopWidth & ~(size_t)1U); j += 2U)
1272 {
1273 sycc_to_rgb<T>(offset, upb, *src[0]++, *src[1], *src[2], dest_ptr[0]++, dest_ptr[1]++,
1274 dest_ptr[2]++);
1275 sycc_to_rgb<T>(offset, upb, *nextY++, *src[1], *src[2], nextRed++, nextGreen++, nextBlue++);
1276
1277 sycc_to_rgb<T>(offset, upb, *src[0]++, *src[1], *src[2], dest_ptr[0]++, dest_ptr[1]++,
1278 dest_ptr[2]++);
1279 sycc_to_rgb<T>(offset, upb, *nextY++, *src[1]++, *src[2]++, nextRed++, nextGreen++,
1280 nextBlue++);
1281 }
1282 if(j < loopWidth)
1283 {
1284 sycc_to_rgb<T>(offset, upb, *src[0]++, *src[1], *src[2], dest_ptr[0]++, dest_ptr[1]++,
1285 dest_ptr[2]++);
1286 sycc_to_rgb<T>(offset, upb, *nextY++, *src[1]++, *src[2]++, nextRed++, nextGreen++,
1287 nextBlue++);
1288 }
1289 for(uint32_t k = 0; k < 3; ++k)
1290 {
1291 dest_ptr[k] += stride_dest_diff + stride_dest;
1292 src[k] += stride_src_diff[k];
1293 }
1294 src[0] += stride_src[0];
1295 }
1296 // final odd row has no sub-sampling
1297 if(i < loopHeight)
1298 {
1299 // if img->x0 is odd, then first column shall use Cb/Cr = 0
1300 if(oddFirstX)
1301 sycc_to_rgb<T>(offset, upb, *src[0]++, 0, 0, dest_ptr[0]++, dest_ptr[1]++, dest_ptr[2]++);
1302 uint32_t j;
1303 for(j = 0U; j < (loopWidth & ~(size_t)1U); j += 2U)
1304 {
1305 sycc_to_rgb<T>(offset, upb, *src[0]++, *src[1], *src[2], dest_ptr[0]++, dest_ptr[1]++,
1306 dest_ptr[2]++);
1307 sycc_to_rgb<T>(offset, upb, *src[0]++, *src[1]++, *src[2]++, dest_ptr[0]++, dest_ptr[1]++,
1308 dest_ptr[2]++);
1309 }
1310 if(j < loopWidth)
1311 sycc_to_rgb<T>(offset, upb, *src[0], *src[1], *src[2], dest_ptr[0], dest_ptr[1], dest_ptr[2]);
1312 }
1313
1315 for(uint32_t k = 0; k < 3; ++k)
1316 {
1317 comps[k].data = rgbImg->comps[k].data;
1318 comps[k].stride = rgbImg->comps[k].stride;
1319 comps[k].owns_data = true;
1320 rgbImg->comps[k].data = nullptr;
1321 }
1322 grk_unref(rgbImg);
1323
1324 comps[1].w = comps[2].w = comps[0].w;
1325 comps[1].h = comps[2].h = comps[0].h;
1326 comps[1].dx = comps[2].dx = comps[0].dx;
1327 comps[1].dy = comps[2].dy = comps[0].dy;
1328 color_space = GRK_CLRSPC_SRGB;
1329
1330 return true;
1331
1332} /* sycc420_to_rgb() */
1333
1334template<typename T>
1336{
1337 if(palette_applied)
1338 return true;
1339
1340 auto clr = &meta->color;
1341 auto pal = clr->palette;
1342 auto channel_prec = pal->channel_prec;
1343 auto channel_sign = pal->channel_sign;
1344 auto lut = pal->lut;
1345 auto component_mapping = pal->component_mapping;
1346 uint16_t num_channels = pal->num_channels;
1347
1348 // sanity check on component mapping
1349 for(uint16_t channel = 0; channel < num_channels; ++channel)
1350 {
1351 auto mapping = component_mapping + channel;
1352 uint16_t compno = mapping->component;
1353 auto comp = comps + compno;
1354 if(compno >= numcomps)
1355 {
1356 grklog.error("apply_palette_clr: component mapping component number %u for channel %u "
1357 "must be less than number of image components %u",
1358 compno, channel, numcomps);
1359 return false;
1360 }
1361 if(comp->data == nullptr)
1362 {
1363 grklog.error("comps[%u].data == nullptr"
1364 " in apply_palette_clr().",
1365 compno);
1366 return false;
1367 }
1368 if((1ULL << comp->prec) > pal->num_entries)
1369 {
1370 grklog.error("Precision %u of component %u implies max value greater than "
1371 "number of palette entries %u",
1372 comp->prec, compno, pal->num_entries);
1373 return false;
1374 }
1375 uint16_t paletteColumn = mapping->palette_column;
1376 switch(mapping->mapping_type)
1377 {
1378 case 0:
1379 if(paletteColumn != 0)
1380 {
1381 grklog.error("apply_palette_clr: channel %u with direct component mapping: "
1382 "non-zero palette column %u not allowed",
1383 channel, paletteColumn);
1384 return false;
1385 }
1386 break;
1387 case 1:
1388 if(comp->sgnd)
1389 {
1390 grklog.error("apply_palette_clr: channel %u with non-direct component mapping: "
1391 "cannot be signed",
1392 channel);
1393 return false;
1394 }
1395 break;
1396 }
1397 }
1398
1399 // check for unique palette columns in mappings
1400 std::set<uint16_t> usedColumns;
1401 for(uint16_t channel = 0; channel < num_channels; ++channel)
1402 {
1403 auto mapping = component_mapping + channel;
1404 if(mapping->mapping_type == 1)
1405 {
1406 uint16_t paletteColumn = mapping->palette_column;
1407 if(usedColumns.count(paletteColumn))
1408 {
1409 grklog.error("apply_palette_clr: duplicate palette column %u in mappings", paletteColumn);
1410 return false;
1411 }
1412 usedColumns.insert(paletteColumn);
1413 }
1414 }
1415
1416 auto oldComps = comps;
1417 auto newComps = new grk_image_comp[num_channels];
1418 memset(newComps, 0, num_channels * sizeof(grk_image_comp));
1419 for(uint16_t channel = 0; channel < num_channels; ++channel)
1420 {
1421 auto mapping = component_mapping + channel;
1422 uint16_t compno = mapping->component;
1423 newComps[channel] = oldComps[compno];
1424 newComps[channel].data = nullptr; // null out to avoid double-free
1425
1426 if(!GrkImage::allocData(newComps + channel))
1427 {
1428 for(uint16_t ch = 0; ch < channel; ++ch)
1429 {
1430 grk_aligned_free(newComps[ch].data);
1431 }
1432 delete[] newComps;
1433 grklog.error("Memory allocation failure in apply_palette_clr().");
1434 return false;
1435 }
1436 newComps[channel].prec = channel_prec[channel];
1437 newComps[channel].sgnd = channel_sign[channel];
1438 }
1439 uint32_t top_k = pal->num_entries - 1;
1440 for(uint16_t channel = 0; channel < num_channels; ++channel)
1441 {
1442 /* Palette mapping: */
1443 auto mapping = component_mapping + channel;
1444 uint16_t compno = mapping->component;
1445 auto src = (T*)oldComps[compno].data;
1446 auto dst = (T*)newComps[channel].data;
1447 size_t num_pixels = (size_t)newComps[channel].stride * newComps[channel].h;
1448 uint32_t diff = (uint32_t)(newComps[channel].stride - newComps[channel].w);
1449 size_t ind = 0;
1450
1451 switch(mapping->mapping_type)
1452 {
1453 case 0: {
1454 memcpy(dst, src, num_pixels * sizeof(T));
1455 }
1456 break;
1457 case 1: {
1458 uint16_t palette_column = mapping->palette_column;
1459 // note: 1 <= n <= 255
1460 for(uint32_t n = 0; n < newComps[channel].h; ++n)
1461 {
1462 for(uint32_t m = 0; m < newComps[channel].w; ++m)
1463 {
1464 uint32_t k = static_cast<uint32_t>(src[ind]); // unsigned cast to avoid signed issues
1465 if(k > top_k)
1466 k = top_k;
1467 dst[ind++] = (T)lut[k * num_channels + palette_column];
1468 }
1469 ind += diff;
1470 }
1471 }
1472 break;
1473 }
1474 }
1475 for(uint16_t i = 0; i < numcomps; ++i)
1476 single_component_data_free(oldComps + i);
1477 delete[] oldComps;
1478 comps = newComps;
1479 numcomps = num_channels;
1480 palette_applied = true;
1481
1482 return true;
1483}
1484
1485template<typename T>
1487{
1488 if(!upsample)
1489 return true;
1490
1491 if(!comps)
1492 return false;
1493
1494 grk_image_comp* new_components = nullptr;
1495 bool upsampleNeeded = false;
1496
1497 for(uint16_t compno = 0U; compno < numcomps; ++compno)
1498 {
1499 if((comps[compno].dx > 1U) || (comps[compno].dy > 1U))
1500 {
1501 upsampleNeeded = true;
1502 break;
1503 }
1504 }
1505 if(!upsampleNeeded)
1506 return true;
1507
1508 new_components = new grk_image_comp[numcomps];
1509 memset(new_components, 0, numcomps * sizeof(grk_image_comp));
1510 for(uint16_t compno = 0U; compno < numcomps; ++compno)
1511 {
1512 auto new_cmp = new_components + compno;
1513 copyComponent(comps + compno, new_cmp);
1514 new_cmp->dx = 1;
1515 new_cmp->dy = 1;
1516 new_cmp->w = x1 - x0;
1517 new_cmp->h = y1 - y0;
1518 if(!allocData(new_cmp))
1519 {
1520 delete[] new_components;
1521 return false;
1522 }
1523 }
1524 for(uint16_t compno = 0U; compno < numcomps; ++compno)
1525 {
1526 auto new_cmp = new_components + compno;
1527 auto org_cmp = comps + compno;
1528 if((org_cmp->dx > 1U) || (org_cmp->dy > 1U))
1529 {
1530 auto src = (T*)org_cmp->data;
1531 auto dst = (T*)new_cmp->data;
1532
1533 /* need to take into account dx & dy */
1534 uint32_t xoff = org_cmp->dx * org_cmp->x0 - x0;
1535 uint32_t yoff = org_cmp->dy * org_cmp->y0 - y0;
1536 if((xoff >= org_cmp->dx) || (yoff >= org_cmp->dy))
1537 {
1538 grklog.error("upsample: Invalid image/component parameters found when upsampling");
1539 delete[] new_components;
1540 return false;
1541 }
1542
1543 uint32_t y;
1544 for(y = 0U; y < yoff; ++y)
1545 {
1546 memset(dst, 0U, (size_t)new_cmp->w * sizeof(T));
1547 dst += new_cmp->stride;
1548 }
1549
1550 if(new_cmp->h > (org_cmp->dy - 1U))
1551 { /* check subtraction overflow for really small images */
1552 for(; y < new_cmp->h - (org_cmp->dy - 1U); y += org_cmp->dy)
1553 {
1554 uint32_t x, dy;
1555 uint32_t xorg = 0;
1556 for(x = 0U; x < xoff; ++x)
1557 dst[x] = 0;
1558
1559 if(new_cmp->w > (org_cmp->dx - 1U))
1560 { /* check subtraction overflow for really small images */
1561 for(; x < new_cmp->w - (org_cmp->dx - 1U); x += org_cmp->dx, ++xorg)
1562 {
1563 for(uint32_t dx = 0U; dx < org_cmp->dx; ++dx)
1564 dst[x + dx] = src[xorg];
1565 }
1566 }
1567 for(; x < new_cmp->w; ++x)
1568 dst[x] = src[xorg];
1569 dst += new_cmp->stride;
1570
1571 for(dy = 1U; dy < org_cmp->dy; ++dy)
1572 {
1573 memcpy(dst, dst - new_cmp->stride, (size_t)new_cmp->w * sizeof(T));
1574 dst += new_cmp->stride;
1575 }
1576 src += org_cmp->stride;
1577 }
1578 }
1579 if(y < new_cmp->h)
1580 {
1581 uint32_t x;
1582 uint32_t xorg = 0;
1583 for(x = 0U; x < xoff; ++x)
1584 dst[x] = 0;
1585
1586 if(new_cmp->w > (org_cmp->dx - 1U))
1587 { /* check subtraction overflow for really small images */
1588 for(; x < new_cmp->w - (org_cmp->dx - 1U); x += org_cmp->dx, ++xorg)
1589 {
1590 for(uint32_t dx = 0U; dx < org_cmp->dx; ++dx)
1591 dst[x + dx] = src[xorg];
1592 }
1593 }
1594 for(; x < new_cmp->w; ++x)
1595 dst[x] = src[xorg];
1596 dst += new_cmp->stride;
1597 ++y;
1598 for(; y < new_cmp->h; ++y)
1599 {
1600 memcpy(dst, dst - new_cmp->stride, (size_t)new_cmp->w * sizeof(T));
1601 dst += new_cmp->stride;
1602 }
1603 }
1604 }
1605 else
1606 {
1607 memcpy(new_cmp->data, org_cmp->data, (size_t)org_cmp->stride * org_cmp->h * sizeof(T));
1608 }
1609 }
1611 delete[] comps;
1612 comps = new_components;
1613
1614 return true;
1615}
1616
1624template<typename T>
1625bool GrkImage::compositeInterleaved(uint16_t srcNumComps, grk_image_comp* srcComps)
1626{
1627 auto srcComp = srcComps;
1628 auto destComp = comps;
1629 Rect32 destWin;
1630 for(uint16_t i = 0; i < srcNumComps; ++i)
1631 {
1632 if(!(srcComps + i)->data)
1633 {
1634 grklog.warn("GrkImage::compositeInterleaved: null data for source component %u", i);
1635 return true;
1636 }
1637 }
1638 if(!generateCompositeBounds(srcComp, 0, &destWin))
1639 {
1640 grklog.warn("GrkImage::compositeInterleaved: cannot generate composite bounds");
1641 return false;
1642 }
1643 uint8_t prec = destComp->prec;
1644 switch(decompress_fmt)
1645 {
1646 case GRK_FMT_TIF:
1647 break;
1648 case GRK_FMT_PXM:
1649 prec = prec > 8 ? 16 : 8;
1650 break;
1651 default:
1652 return false;
1653 break;
1654 }
1655 auto destStride = grk::PlanarToInterleaved<T>::getPackedBytes(srcNumComps, destComp->w, prec);
1656 auto destx0 = grk::PlanarToInterleaved<T>::getPackedBytes(srcNumComps, destWin.x0, prec);
1657 auto destIndex = (uint64_t)destWin.y0 * destStride + (uint64_t)destx0;
1658 auto iter = InterleaverFactory<T>::makeInterleaver(
1659 prec == 16 && decompress_fmt != GRK_FMT_TIF ? packer16BitBE : prec);
1660 if(!iter)
1661 return false;
1662 auto planes = std::make_unique<T*[]>(numcomps);
1663 for(uint16_t i = 0; i < srcNumComps; ++i)
1664 planes[i] = (T*)(srcComps + i)->data;
1665 iter->interleave(const_cast<T**>(planes.get()), srcNumComps, interleaved_data.data + destIndex,
1666 destWin.width(), srcComp->stride, destStride, destWin.height(), 0);
1667 delete iter;
1668
1669 return true;
1670}
1671
1672/*#define DEBUG_PROFILE*/
1673/*#define DEBUG_PROFILE*/
1674template<typename T>
1676{
1677 cmsUInt32Number out_space;
1678 cmsUInt32Number intent = 0;
1679 cmsHTRANSFORM transform = nullptr;
1680 cmsHPROFILE in_prof = nullptr;
1681 cmsHPROFILE out_prof = nullptr;
1682 cmsUInt32Number in_type, out_type;
1683 size_t nr_samples, componentSize;
1684 uint32_t prec, w, stride_diff, h;
1685 GRK_COLOR_SPACE oldspace;
1686 bool rc = false;
1687
1688 if(!validateICC())
1689 return false;
1690
1691 if(numcomps == 0 || !allComponentsSanityCheck(true))
1692 return false;
1693 for(uint16_t i = 0; i < numcomps; ++i)
1694 {
1695 if(comps[i].sgnd)
1696 {
1697 grklog.warn("applyICC: components must be unsigned");
1698 return false;
1699 }
1700 }
1701 if(!meta || !meta->color.icc_profile_buf || !meta->color.icc_profile_len)
1702 return false;
1703 in_prof = cmsOpenProfileFromMem(meta->color.icc_profile_buf, meta->color.icc_profile_len);
1704 if(!in_prof)
1705 goto cleanup;
1706
1707 // auto in_space = cmsGetPCS(in_prof);
1708 out_space = cmsGetColorSpace(in_prof);
1709 intent = cmsGetHeaderRenderingIntent(in_prof);
1710
1711 w = comps[0].w;
1712 stride_diff = comps[0].stride - w;
1713 h = comps[0].h;
1714 if(!w || !h)
1715 goto cleanup;
1716 componentSize = (size_t)w * h;
1717
1718 prec = comps[0].prec;
1719 oldspace = color_space;
1720
1721 if(out_space == cmsSigRgbData)
1722 { /* enumCS 16 */
1723 uint16_t i, nr_comp = numcomps;
1724 if(nr_comp > 4)
1725 nr_comp = 4;
1726
1727 for(i = 1; i < nr_comp; ++i)
1728 {
1729 if(comps[0].dx != comps[i].dx)
1730 break;
1731 if(comps[0].dy != comps[i].dy)
1732 break;
1733 if(comps[0].prec != comps[i].prec)
1734 break;
1735 if(comps[0].sgnd != comps[i].sgnd)
1736 break;
1737 }
1738 if(i != nr_comp)
1739 goto cleanup;
1740
1741 if(prec <= 8)
1742 {
1743 in_type = TYPE_RGB_8;
1744 out_type = TYPE_RGB_8;
1745 }
1746 else
1747 {
1748 in_type = TYPE_RGB_16;
1749 out_type = TYPE_RGB_16;
1750 }
1751 out_prof = cmsCreate_sRGBProfile();
1752 color_space = GRK_CLRSPC_SRGB;
1753 }
1754 else if(out_space == cmsSigGrayData)
1755 { /* enumCS 17 */
1756 if(prec <= 8)
1757 {
1758 in_type = TYPE_GRAY_8;
1759 out_type = TYPE_RGB_8;
1760 }
1761 else
1762 {
1763 in_type = TYPE_GRAY_16;
1764 out_type = TYPE_RGB_16;
1765 }
1766 out_prof = cmsCreate_sRGBProfile();
1767 if(force_rgb)
1768 color_space = GRK_CLRSPC_SRGB;
1769 else
1770 color_space = GRK_CLRSPC_GRAY;
1771 }
1772 else if(out_space == cmsSigYCbCrData)
1773 { /* enumCS 18 */
1774 if(prec <= 8)
1775 {
1776 in_type = TYPE_YCbCr_8;
1777 out_type = TYPE_RGB_8;
1778 }
1779 else
1780 {
1781 in_type = TYPE_YCbCr_16;
1782 out_type = TYPE_RGB_16;
1783 }
1784 out_prof = cmsCreate_sRGBProfile();
1785 color_space = GRK_CLRSPC_SRGB;
1786 }
1787 else
1788 {
1789 grklog.warn("Apply ICC profile has unknown "
1790 "output color space (%#x)\nICC profile ignored.",
1791 out_space);
1792 goto cleanup;
1793 }
1794 transform = cmsCreateTransform(in_prof, in_type, out_prof, out_type, intent, 0);
1795 if(!transform)
1796 {
1797 color_space = oldspace;
1798 goto cleanup;
1799 }
1800
1801 if(numcomps > 2)
1802 { /* RGB, RGBA */
1803 if(prec <= 8)
1804 {
1805 nr_samples = componentSize * 3U;
1806 if(nr_samples / 3U != componentSize) // overflow check
1807 {
1808 grklog.error("nr_samples overflow in applyICC");
1809 goto cleanup;
1810 }
1811 if(w > UINT32_MAX / 3)
1812 {
1813 grklog.error("Image width of {} converted to sample size 3 will overflow.", w);
1814 goto cleanup;
1815 }
1816 auto inbuf = new uint8_t[nr_samples];
1817 auto outbuf = new uint8_t[nr_samples];
1818
1819 auto r = (T*)comps[0].data;
1820 auto g = (T*)comps[1].data;
1821 auto b = (T*)comps[2].data;
1822
1823 if constexpr(std::is_same_v<T, int32_t>)
1824 {
1825 hwy_planar_to_packed_8(r, g, b, inbuf, w, h, comps[0].stride);
1826 }
1827 else
1828 {
1829 size_t src_index = 0;
1830 size_t dest_index = 0;
1831 for(uint32_t j = 0; j < h; ++j)
1832 {
1833 for(uint32_t i = 0; i < w; ++i)
1834 {
1835 inbuf[dest_index++] = (uint8_t)r[src_index];
1836 inbuf[dest_index++] = (uint8_t)g[src_index];
1837 inbuf[dest_index++] = (uint8_t)b[src_index];
1838 src_index++;
1839 }
1840 src_index += stride_diff;
1841 }
1842 }
1843
1844 cmsDoTransformLineStride(transform, inbuf, outbuf, w, h, 3 * w, 3 * w, 0, 0);
1845
1846 if constexpr(std::is_same_v<T, int32_t>)
1847 {
1848 hwy_packed_to_planar_8(outbuf, r, g, b, w, h, comps[0].stride);
1849 }
1850 else
1851 {
1852 size_t src_index = 0;
1853 size_t dest_index = 0;
1854 for(uint32_t j = 0; j < h; ++j)
1855 {
1856 for(uint32_t i = 0; i < w; ++i)
1857 {
1858 r[dest_index] = (T)outbuf[src_index++];
1859 g[dest_index] = (T)outbuf[src_index++];
1860 b[dest_index] = (T)outbuf[src_index++];
1861 dest_index++;
1862 }
1863 dest_index += stride_diff;
1864 }
1865 }
1866 delete[] inbuf;
1867 delete[] outbuf;
1868 }
1869 else
1870 {
1871 if(componentSize > (SIZE_MAX / 3) / sizeof(uint16_t)) // overflow check
1872 {
1873 grklog.error("nr_samples overflow in applyICC");
1874 goto cleanup;
1875 }
1876 if(w > UINT32_MAX / (3 * sizeof(uint16_t)))
1877 {
1878 grklog.error("Image width of {} converted to sample size 3 @ 16 bits will overflow.", w);
1879 goto cleanup;
1880 }
1881 nr_samples = componentSize * 3U * sizeof(uint16_t);
1882 auto inbuf = new uint16_t[nr_samples / sizeof(uint16_t)];
1883 auto outbuf = new uint16_t[nr_samples / sizeof(uint16_t)];
1884
1885 auto r = (T*)comps[0].data;
1886 auto g = (T*)comps[1].data;
1887 auto b = (T*)comps[2].data;
1888
1889 if constexpr(std::is_same_v<T, int32_t>)
1890 {
1891 hwy_planar_to_packed_16(r, g, b, inbuf, w, h, comps[0].stride);
1892 }
1893 else
1894 {
1895 size_t src_index = 0;
1896 size_t dest_index = 0;
1897 for(uint32_t j = 0; j < h; ++j)
1898 {
1899 for(uint32_t i = 0; i < w; ++i)
1900 {
1901 inbuf[dest_index++] = (uint16_t)r[src_index];
1902 inbuf[dest_index++] = (uint16_t)g[src_index];
1903 inbuf[dest_index++] = (uint16_t)b[src_index];
1904 src_index++;
1905 }
1906 src_index += stride_diff;
1907 }
1908 }
1909
1910 cmsDoTransformLineStride(transform, inbuf, outbuf, w, h, 3 * w * sizeof(uint16_t),
1911 3 * w * sizeof(uint16_t), 0, 0);
1912 if constexpr(std::is_same_v<T, int32_t>)
1913 {
1914 hwy_packed_to_planar_16(outbuf, r, g, b, w, h, comps[0].stride);
1915 }
1916 else
1917 {
1918 size_t src_index = 0;
1919 size_t dest_index = 0;
1920 for(uint32_t j = 0; j < h; ++j)
1921 {
1922 for(uint32_t i = 0; i < w; ++i)
1923 {
1924 r[dest_index] = (T)outbuf[src_index++];
1925 g[dest_index] = (T)outbuf[src_index++];
1926 b[dest_index] = (T)outbuf[src_index++];
1927 dest_index++;
1928 }
1929 dest_index += stride_diff;
1930 }
1931 }
1932 delete[] inbuf;
1933 delete[] outbuf;
1934 }
1935 }
1936 else
1937 { /* GRAY, GRAYA */
1938 nr_samples = componentSize * 3U;
1939 if(nr_samples / 3U != componentSize) // overflow check
1940 {
1941 grklog.error("nr_samples overflow in applyICC");
1942 goto cleanup;
1943 }
1944 auto newComps = new grk_image_comp[numcomps + 2U];
1945 if(!newComps)
1946 {
1947 grklog.error("Memory allocation failure in applyICC");
1948 goto cleanup;
1949 }
1950 for(uint16_t i = 0; i < numcomps + 2U; ++i)
1951 {
1952 if(i < numcomps)
1953 newComps[i] = comps[i];
1954 else
1955 memset(newComps + i, 0, sizeof(grk_image_comp));
1956 }
1957 delete[] comps;
1958 comps = newComps;
1959 if(prec <= 8)
1960 {
1961 if(w > UINT32_MAX / 3)
1962 {
1963 grklog.error("Image width of {} converted to sample size 3 will overflow.", w);
1964 goto cleanup;
1965 }
1966 auto inbuf = new uint8_t[componentSize];
1967 auto outbuf = new uint8_t[nr_samples];
1968
1969 auto r = (T*)comps[0].data;
1970 size_t src_index = 0;
1971 size_t dest_index = 0;
1972 for(uint32_t j = 0; j < h; ++j)
1973 {
1974 for(uint32_t i = 0; i < w; ++i)
1975 inbuf[dest_index++] = (uint8_t)r[src_index++];
1976 src_index += stride_diff;
1977 }
1978 cmsDoTransformLineStride(transform, inbuf, outbuf, w, h, w, 3 * w, 0, 0);
1979 T *g = nullptr, *b = nullptr;
1980 if(force_rgb)
1981 {
1982 if(numcomps == 2)
1983 comps[3] = comps[1];
1984 comps[1] = comps[0];
1985 setDataToNull(comps + 1);
1986 allocData(comps + 1);
1987 comps[2] = comps[0];
1988 setDataToNull(comps + 2);
1989 allocData(comps + 2);
1990 numcomps = (uint16_t)(2 + numcomps);
1991 g = (T*)comps[1].data;
1992 b = (T*)comps[2].data;
1993 }
1994 src_index = 0;
1995 dest_index = 0;
1996 for(uint32_t j = 0; j < h; ++j)
1997 {
1998 for(uint32_t i = 0; i < w; ++i)
1999 {
2000 r[dest_index] = (T)outbuf[src_index++];
2001 if(force_rgb)
2002 {
2003 g[dest_index] = (T)outbuf[src_index++];
2004 b[dest_index] = (T)outbuf[src_index++];
2005 }
2006 else
2007 {
2008 src_index += 2;
2009 }
2010 dest_index++;
2011 }
2012 dest_index += stride_diff;
2013 }
2014 delete[] inbuf;
2015 delete[] outbuf;
2016 }
2017 else
2018 {
2019 if(componentSize > SIZE_MAX / sizeof(uint16_t))
2020 {
2021 grklog.error("componentSize overflow in applyICC");
2022 goto cleanup;
2023 }
2024 if(componentSize * 3U > SIZE_MAX / sizeof(uint16_t))
2025 {
2026 grklog.error("nr_samples overflow in applyICC");
2027 goto cleanup;
2028 }
2029 if(w > UINT32_MAX / (3 * sizeof(uint16_t)))
2030 {
2031 grklog.error("Image width of {} converted to sample size 3 @ 16 bits will overflow.", w);
2032 goto cleanup;
2033 }
2034 auto inbuf = new uint16_t[componentSize];
2035 auto outbuf = new uint16_t[componentSize * 3U];
2036
2037 auto r = (T*)comps[0].data;
2038 size_t src_index = 0;
2039 size_t dest_index = 0;
2040 for(uint32_t j = 0; j < h; ++j)
2041 {
2042 for(uint32_t i = 0; i < w; ++i)
2043 inbuf[dest_index++] = (uint16_t)r[src_index++];
2044 src_index += stride_diff;
2045 }
2046 cmsDoTransformLineStride(transform, inbuf, outbuf, w, h, w * sizeof(uint16_t),
2047 3 * w * sizeof(uint16_t), 0, 0);
2048 T *g = nullptr, *b = nullptr;
2049 if(force_rgb)
2050 {
2051 if(numcomps == 2)
2052 comps[3] = comps[1];
2053 comps[1] = comps[0];
2054 setDataToNull(comps + 1);
2055 allocData(comps + 1);
2056 comps[2] = comps[0];
2057 setDataToNull(comps + 2);
2058 allocData(comps + 2);
2059 numcomps = (uint16_t)(2 + numcomps);
2060 g = (T*)comps[1].data;
2061 b = (T*)comps[2].data;
2062 }
2063 src_index = 0;
2064 dest_index = 0;
2065 for(uint32_t j = 0; j < h; ++j)
2066 {
2067 for(uint32_t i = 0; i < w; ++i)
2068 {
2069 r[dest_index] = (T)outbuf[src_index++];
2070 if(force_rgb)
2071 {
2072 g[dest_index] = (T)outbuf[src_index++];
2073 b[dest_index] = (T)outbuf[src_index++];
2074 }
2075 else
2076 {
2077 src_index += 2;
2078 }
2079 dest_index++;
2080 }
2081 dest_index += stride_diff;
2082 }
2083 delete[] inbuf;
2084 delete[] outbuf;
2085 }
2086 } /* if(image->numcomps */
2087 rc = true;
2088 delete[] meta->color.icc_profile_buf;
2089 meta->color.icc_profile_buf = nullptr;
2090 meta->color.icc_profile_len = 0;
2091cleanup:
2092 if(in_prof)
2093 cmsCloseProfile(in_prof);
2094 if(out_prof)
2095 cmsCloseProfile(out_prof);
2096 if(transform)
2097 cmsDeleteTransform(transform);
2098
2099 return rc;
2100} /* applyICC() */
2101
2102template<typename T>
2104{
2105 bool convert = needsConversionToRGB();
2106 switch(color_space)
2107 {
2108 case GRK_CLRSPC_SYCC:
2109 if(numcomps != 3)
2110 {
2111 grklog.error("grk_decompress: YCC: number of components %d "
2112 "not equal to 3 ",
2113 numcomps);
2114 return false;
2115 }
2116 if(convert)
2117 {
2118 bool oddFirstX = x0 & 1;
2119 bool oddFirstY = y0 & 1;
2120 // todo: region decode should force region to even REGION x0 and y0 coordinates in order
2121 // to get correct sycc to rgb conversion. Image x0 and y0 parity shouldn't matter, but
2122 // rather the REGION x0 and y0 parity.
2123 // if(!wholeTileDecompress)
2124 // {
2125 // oddFirstX = false;
2126 // oddFirstY = false;
2127 // }
2128 if(!color_sycc_to_rgb<T>(oddFirstX, oddFirstY))
2129 grklog.warn("grk_decompress: sYCC to RGB colour conversion failed");
2130 }
2131 break;
2132 case GRK_CLRSPC_EYCC:
2133 if(numcomps != 3)
2134 {
2135 grklog.error("grk_decompress: YCC: number of components %d "
2136 "not equal to 3 ",
2137 numcomps);
2138 return false;
2139 }
2140 if(convert && !color_esycc_to_rgb<T>())
2141 grklog.warn("grk_decompress: eYCC to RGB colour conversion failed");
2142 break;
2143 case GRK_CLRSPC_CMYK:
2144 if(numcomps != 4)
2145 {
2146 grklog.error("grk_decompress: CMYK: number of components %d "
2147 "not equal to 4 ",
2148 numcomps);
2149 return false;
2150 }
2151 if(convert && !color_cmyk_to_rgb<T>())
2152 grklog.warn("grk_decompress: CMYK to RGB colour conversion failed");
2153 break;
2154 default:
2155 break;
2156 }
2157
2158 return true;
2159}
2160
2161template<typename T>
2163{
2164 if(!meta || !meta->color.icc_profile_buf)
2165 return true;
2166
2167 bool isTiff = decompress_fmt == GRK_FMT_TIF;
2168 bool canStoreCIE = isTiff && color_space == GRK_CLRSPC_DEFAULT_CIE;
2169 bool isCIE = color_space == GRK_CLRSPC_DEFAULT_CIE || color_space == GRK_CLRSPC_CUSTOM_CIE;
2170 bool canStoreICC = (decompress_fmt == GRK_FMT_TIF || decompress_fmt == GRK_FMT_PNG ||
2171 decompress_fmt == GRK_FMT_JPG || decompress_fmt == GRK_FMT_BMP);
2172
2173 bool shouldApplyColourManagement =
2174 force_rgb || (decompress_fmt != GRK_FMT_UNK && meta->color.icc_profile_buf &&
2175 ((isCIE && !canStoreCIE) || !canStoreICC));
2176 if(!shouldApplyColourManagement)
2177 return true;
2178
2179 if(isCIE)
2180 {
2181 if(!force_rgb)
2182 grklog.warn(" Input image is in CIE colour space,\n"
2183 "but the codec is unable to store this information in the "
2184 "output file .\n"
2185 "The output image will therefore be converted to sRGB before saving.");
2186 if(!cieLabToRGB<T>())
2187 {
2188 grklog.error("Unable to convert L*a*b image to sRGB");
2189 return false;
2190 }
2191 }
2192 else
2193 {
2194 if(validateICC())
2195 {
2196 if(!force_rgb)
2197 {
2198 grklog.warn("");
2199 grklog.warn("The input image contains an ICC profile");
2200 grklog.warn("but the codec is unable to store this profile"
2201 " in the output file.");
2202 grklog.warn("The profile will therefore be applied to the output"
2203 " image before saving.");
2204 grklog.warn("");
2205 }
2206 if(!applyICC<T>())
2207 {
2208 grklog.warn("Unable to apply ICC profile");
2209 return false;
2210 }
2211 }
2212 }
2213
2214 return true;
2215}
2216
2217template<typename T>
2219{
2220 if(!applyColour())
2221 return false;
2223 if(!convertToRGB_T<T>())
2224 return false;
2225 if(!greyToRGB())
2226 return false;
2228
2229 return execUpsample<T>();
2230}
2231
2232template<typename T>
2234{
2235 if(!meta)
2236 return false;
2237 if(meta->color.palette)
2238 {
2239 /* Part 1, I.5.3.4: Either both or none : */
2240 if(!meta->color.palette->component_mapping)
2241 ((GrkImageMeta*)meta)->releaseColorPalatte();
2242 else if(!apply_palette_clr<T>())
2243 return false;
2244 }
2245 if(meta->color.channel_definition)
2247
2248 return true;
2249}
2250
2251} // namespace grk
#define SIZE_MAX
Definition MemManager.h:37
bool cieLabToRGB(void)
Definition GrkImage.h:333
bool subsampleAndReduce(uint8_t reduce)
Generates subsampled and reduced bounds for components.
Definition GrkImage.cpp:258
bool sycc444_to_rgb(void)
Definition GrkImage.h:1038
bool compositePlanar(uint16_t srcNumComps, grk_image_comp *srcComps)
Copy planar image data to planar composite image.
Definition GrkImage.h:928
void apply_channel_definition(void)
Definition GrkImage.cpp:647
void convertPrecision(void)
Definition GrkImage.h:601
GrkImage()
Constructs a GrkImage.
Definition GrkImage.cpp:55
bool postProcess_T(void)
Definition GrkImage.h:2218
bool apply_palette_clr()
Definition GrkImage.h:1335
bool sycc420_to_rgb(bool oddFirstX, bool oddFirstY)
Definition GrkImage.h:1197
static GrkImage * create(grk_image *src, uint16_t numcmpts, grk_image_comp *cmptparms, GRK_COLOR_SPACE clrspc, bool doAllocation)
Creates a GrkImage.
Definition GrkImage.cpp:158
std::string getColourSpaceString(void) const
Definition GrkImage.cpp:1127
bool needsChannelDefinitionSwap(void) const
Definition GrkImage.cpp:609
bool convertToRGB_T()
Definition GrkImage.h:2103
void applyChannelDefinitionTypes(void)
Definition GrkImage.cpp:632
bool isValidICCColourSpace(uint32_t signature) const
Definition GrkImage.cpp:1190
void print(void) const
Definition GrkImage.cpp:85
void scaleComponent(grk_image_comp *component, uint8_t precision)
Definition GrkImage.h:869
void transferDataFrom_T(const Tile *tile_src_data)
Definition GrkImage.cpp:1360
bool color_cmyk_to_rgb(void)
Definition GrkImage.h:757
bool validateICC(void)
Definition GrkImage.cpp:1215
static size_t sizeOfDataType(grk_data_type type)
Definition GrkImage.cpp:95
bool sycc422_to_rgb(bool oddFirstX)
Definition GrkImage.h:1102
bool allocCompositeData(void)
Allocate data for tile compositing.
Definition GrkImage.cpp:859
bool allComponentsSanityCheck(bool equalPrecision) const
return false if :
Definition GrkImage.cpp:1040
bool greyToRGB(void)
Convert to sRGB.
Definition GrkImage.cpp:1317
GrkImage * extractFrom(const Tile *tile_src) const
Create new image and transfer tile buffer data.
Definition GrkImage.cpp:951
bool compositeInterleaved_T(const Tile *src, uint32_t yBegin, uint32_t yEnd)
bool color_sycc_to_rgb(bool oddFirstX, bool oddFirstY)
Definition GrkImage.h:822
bool isPostProcessNoOp(void) const
Check if postProcess would be a no-op for the current image.
Definition GrkImage.cpp:447
void validateColourSpace(void)
Definition GrkImage.cpp:429
static void copyComponent(grk_image_comp *src, grk_image_comp *dest)
Definition GrkImage.cpp:114
static void setDataToNull(grk_image_comp *comp)
Definition GrkImage.cpp:314
void sycc_to_rgb(T offset, T upb, T y, T cb, T cr, T *out_r, T *out_g, T *out_b)
Definition GrkImage.h:1009
bool generateCompositeBounds(const grk_image_comp *srcComp, uint16_t destCompno, Rect32 *destWin)
Definition GrkImage.cpp:1010
void copyHeaderTo(GrkImage *dest) const
Copies only header of image and its component header.
Definition GrkImage.cpp:329
uint32_t height(void) const
Definition GrkImage.cpp:75
Rect32 getBounds(void) const
Gets unreduced, non-subsampled image bounds.
Definition GrkImage.cpp:80
bool applyICC(void)
Definition GrkImage.h:1675
bool applyColourManagement(void)
Definition GrkImage.h:2162
void transferDataFrom(const Tile *tile_src_data)
Definition GrkImage.cpp:1378
static bool allocData(grk_image_comp *imageComp, bool clear)
Allocate data for single image component.
Definition GrkImage.cpp:395
bool postProcess(void)
Definition GrkImage.h:170
void filterComponents(const std::vector< uint16_t > &compsToKeep)
Definition GrkImage.cpp:920
bool applyColour_T(void)
Definition GrkImage.h:2233
bool applyColour(void)
Definition GrkImage.h:189
bool componentsEqual(bool checkPrecision) const
Definition GrkImage.cpp:144
bool isSubsampled() const
Definition GrkImage.cpp:419
void postReadHeader(CodingParams *cp)
Definition GrkImage.cpp:510
bool check_color(uint16_t signalledNumComps)
Definition GrkImage.cpp:703
~GrkImage()
Destroys a GrkImage.
Definition GrkImage.cpp:61
void transferDataTo(GrkImage *dest)
Transfer data to dest for each component, and null out "this" data.
Definition GrkImage.cpp:893
bool isOpacity(uint16_t compno) const
Definition GrkImage.cpp:438
std::string getICCColourSpaceString(cmsColorSpaceSignature color_space) const
Definition GrkImage.cpp:1163
bool composite(const GrkImage *src)
Definition GrkImage.cpp:978
static void single_component_data_free(grk_image_comp *comp)
Definition GrkImage.cpp:1018
uint32_t width(void) const
Definition GrkImage.cpp:71
bool compositeInterleaved(const Tile *src, uint32_t yBegin, uint32_t yEnd)
Definition GrkImage.h:152
void all_components_data_free(void)
Definition GrkImage.cpp:239
void allocPalette(uint8_t num_channels, uint16_t num_entries)
Definition GrkImage.cpp:604
bool needsConversionToRGB(void) const
Definition GrkImage.cpp:250
grk_image * createRGB(uint16_t numcmpts, uint32_t w, uint32_t h, uint8_t prec)
Definition GrkImage.cpp:1105
bool execUpsample(void)
Definition GrkImage.h:1486
bool color_esycc_to_rgb(void)
Definition GrkImage.h:678
Definition GrkImageMeta.h:24
Definition GrkObjectWrapper.h:31
enum _grk_data_type grk_data_type
Grok Data types Used to specify the actual data type of Grok image components.
@ GRK_FMT_BMP
Definition grok.h:307
@ GRK_FMT_PNG
Definition grok.h:310
@ GRK_FMT_UNK
Definition grok.h:301
@ GRK_FMT_TIF
Definition grok.h:308
@ GRK_FMT_JPG
Definition grok.h:312
@ GRK_FMT_PXM
Definition grok.h:304
enum _GRK_ENUM_COLOUR_SPACE GRK_ENUM_COLOUR_SPACE
JPEG 2000 enumerated color spaces.
#define GRK_MAX_SUPPORTED_IMAGE_PRECISION
maximum Grok supported precision
Definition grok.h:150
@ GRK_INT_32
Definition grok.h:768
@ GRK_INT_16
Definition grok.h:769
@ GRK_CLRSPC_SRGB
unknown
Definition grok.h:90
@ GRK_CLRSPC_EYCC
standard YCC (YUV)
Definition grok.h:93
@ GRK_CLRSPC_SYCC
grayscale
Definition grok.h:92
@ GRK_CLRSPC_DEFAULT_CIE
CMYK.
Definition grok.h:95
@ GRK_CLRSPC_CMYK
extended YCC
Definition grok.h:94
@ GRK_CLRSPC_GRAY
sRGB
Definition grok.h:91
@ GRK_CLRSPC_CUSTOM_CIE
default CIE LAB
Definition grok.h:96
@ GRK_PREC_MODE_SCALE
Definition grok.h:435
@ GRK_PREC_MODE_CLIP
Definition grok.h:434
enum _GRK_COLOR_SPACE GRK_COLOR_SPACE
Grok supported color spaces.
#define GRK_DEFAULT_CIELAB_SPACE
Definition grok.h:2092
@ GRK_ENUM_CLRSPC_CIE
Definition grok.h:114
ResWindow.
Definition CompressedChunkCache.h:36
T clip(int64_t val)
Definition geometry.h:70
constexpr uint32_t GRK_CIE_D75
Definition GrkImage.h:42
void hwy_scale_mul_i32(int32_t *data, uint32_t w, uint32_t h, uint32_t stride, int32_t scale)
constexpr uint32_t GRK_CIE_D50
Definition GrkImage.h:40
void hwy_planar_to_packed_8(const int32_t *r, const int32_t *g, const int32_t *b, uint8_t *out, uint32_t w, uint32_t h, uint32_t src_stride)
constexpr uint32_t GRK_CIE_F11
Definition GrkImage.h:47
ILogger & grklog
Definition Logger.cpp:24
void hwy_planar_to_packed_16(const int32_t *r, const int32_t *g, const int32_t *b, uint16_t *out, uint32_t w, uint32_t h, uint32_t src_stride)
void hwy_sycc444_to_rgb_i32(const int32_t *y, const int32_t *cb, const int32_t *cr, int32_t *r, int32_t *g, int32_t *b, uint32_t w, uint32_t h, uint32_t src_stride, uint32_t dst_stride, int32_t offset, int32_t upb)
void hwy_packed_to_planar_8(const uint8_t *in, int32_t *r, int32_t *g, int32_t *b, uint32_t w, uint32_t h, uint32_t dst_stride)
constexpr uint32_t GRK_CIE_D65
Definition GrkImage.h:41
constexpr uint32_t GRK_CIE_F2
Definition GrkImage.h:45
void hwy_packed_to_planar_16(const uint16_t *in, int32_t *r, int32_t *g, int32_t *b, uint32_t w, uint32_t h, uint32_t dst_stride)
const double scale
Definition RateControl.cpp:167
void hwy_clip_i32(int32_t *data, uint32_t w, uint32_t h, uint32_t stride, int32_t minVal, int32_t maxVal)
Rect< uint32_t > Rect32
Definition geometry.h:64
T * grk_unref(T *w)
Definition RefCounted.h:31
constexpr uint32_t GRK_CIE_F7
Definition GrkImage.h:46
const uint32_t singleTileRowsPerStrip
Definition GrkImage.h:37
constexpr uint32_t GRK_CIE_DAY
Definition GrkImage.h:39
constexpr uint32_t GRK_CIE_SA
Definition GrkImage.h:43
void hwy_esycc_to_rgb_i32(int32_t *yd, int32_t *bd, int32_t *rd, uint32_t w, uint32_t h, uint32_t stride, int32_t max_value, int32_t flip_value, bool sign1, bool sign2)
constexpr uint32_t GRK_CIE_SC
Definition GrkImage.h:44
void hwy_scale_div_i32(int32_t *data, uint32_t w, uint32_t h, uint32_t stride, int32_t scale)
void grk_aligned_free(void *ptr)
Definition MemManager.h:324
Coding parameters.
Definition CodingParams.h:402
T width() const
Definition geometry.h:411
T height() const
Definition geometry.h:415
T x0
Definition geometry.h:192
T y0
Definition geometry.h:192
Definition Tile.h:39
Image component.
Grok image Note: do not directly create a grk_image object.