// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/jpegli/quant.h"
#include <algorithm>
#include <cmath>
#include <vector>
#include "lib/jpegli/adaptive_quantization.h"
#include "lib/jpegli/common.h"
#include "lib/jpegli/encode_internal.h"
#include "lib/jpegli/error.h"
#include "lib/jpegli/memory_manager.h"
#include "lib/jxl/base/byte_order.h"
#include "lib/jxl/base/status.h"
namespace jpegli {
namespace {
// Global scale is chosen in a way that butteraugli 3-norm matches libjpeg
// with the same quality setting. Fitted for quality 90 on jyrki31 corpus.
constexpr float kGlobalScaleXYB = 1 .43951668 f;
constexpr float kGlobalScaleYCbCr = 1 .73966010 f;
constexpr float kBaseQuantMatrixXYB[] = {
// c = 0
7 .5629935265 f,
19 .8247814178 f,
22 .5724945068 f,
20 .6706695557 f,
22 .6864585876 f,
23 .5696277618 f,
25 .8129081726 f,
36 .3307571411 f,
19 .8247814178 f,
21 .5503177643 f,
19 .9372234344 f,
20 .5424213409 f,
21 .8645496368 f,
23 .9041385651 f,
28 .2844066620 f,
32 .6609764099 f,
22 .5724945068 f,
19 .9372234344 f,
21 .9017257690 f,
19 .1223449707 f,
21 .7515811920 f,
24 .6724700928 f,
25 .4249649048 f,
32 .6653823853 f,
20 .6706695557 f,
20 .5424213409 f,
19 .1223449707 f,
20 .1610221863 f,
25 .3719692230 f,
25 .9668903351 f,
30 .9804954529 f,
31 .3406009674 f,
22 .6864585876 f,
21 .8645496368 f,
21 .7515811920 f,
25 .3719692230 f,
26 .2431850433 f,
40 .5992202759 f,
43 .2624626160 f,
63 .3010940552 f,
23 .5696277618 f,
23 .9041385651 f,
24 .6724700928 f,
25 .9668903351 f,
40 .5992202759 f,
48 .3026771545 f,
34 .0964355469 f,
61 .9852142334 f,
25 .8129081726 f,
28 .2844066620 f,
25 .4249649048 f,
30 .9804954529 f,
43 .2624626160 f,
34 .0964355469 f,
34 .4937438965 f,
66 .9702758789 f,
36 .3307571411 f,
32 .6609764099 f,
32 .6653823853 f,
31 .3406009674 f,
63 .3010940552 f,
61 .9852142334 f,
66 .9702758789 f,
39 .9652709961 f,
// c = 1
1 .6262000799 f,
3 .2199242115 f,
3 .4903779030 f,
3 .9148359299 f,
4 .8337211609 f,
4 .9108843803 f,
5 .3137121201 f,
6 .1676793098 f,
3 .2199242115 f,
3 .4547898769 f,
3 .6036829948 f,
4 .2652835846 f,
4 .8368387222 f,
4 .8226222992 f,
5 .6120514870 f,
6 .3431472778 f,
3 .4903779030 f,
3 .6036829948 f,
3 .9044559002 f,
4 .3374395370 f,
4 .8435096741 f,
5 .4057979584 f,
5 .6066360474 f,
6 .1075134277 f,
3 .9148359299 f,
4 .2652835846 f,
4 .3374395370 f,
4 .6064834595 f,
5 .1751475334 f,
5 .4013924599 f,
6 .0399808884 f,
6 .7825231552 f,
4 .8337211609 f,
4 .8368387222 f,
4 .8435096741 f,
5 .1751475334 f,
5 .3748049736 f,
6 .1410837173 f,
7 .6529307365 f,
7 .5235214233 f,
4 .9108843803 f,
4 .8226222992 f,
5 .4057979584 f,
5 .4013924599 f,
6 .1410837173 f,
6 .3431472778 f,
7 .1083049774 f,
7 .6008300781 f,
5 .3137121201 f,
5 .6120514870 f,
5 .6066360474 f,
6 .0399808884 f,
7 .6529307365 f,
7 .1083049774 f,
7 .0943155289 f,
7 .0478363037 f,
6 .1676793098 f,
6 .3431472778 f,
6 .1075134277 f,
6 .7825231552 f,
7 .5235214233 f,
7 .6008300781 f,
7 .0478363037 f,
6 .9186143875 f,
// c = 2
3 .3038473129 f,
10 .0689258575 f,
12 .2785224915 f,
14 .6041173935 f,
16 .2107315063 f,
19 .2314529419 f,
28 .0129547119 f,
55 .6682891846 f,
10 .0689258575 f,
11 .4085016251 f,
11 .3871345520 f,
15 .4934167862 f,
16 .5364933014 f,
14 .9153423309 f,
26 .3748722076 f,
40 .8614425659 f,
12 .2785224915 f,
11 .3871345520 f,
17 .0886878967 f,
13 .9500350952 f,
16 .0003223419 f,
28 .5660629272 f,
26 .2124195099 f,
30 .1260128021 f,
14 .6041173935 f,
15 .4934167862 f,
13 .9500350952 f,
21 .1235027313 f,
26 .1579780579 f,
25 .5579223633 f,
40 .6859359741 f,
33 .8056335449 f,
16 .2107315063 f,
16 .5364933014 f,
16 .0003223419 f,
26 .1579780579 f,
26 .8042831421 f,
26 .1587715149 f,
35 .7343978882 f,
43 .6857032776 f,
19 .2314529419 f,
14 .9153423309 f,
28 .5660629272 f,
25 .5579223633 f,
26 .1587715149 f,
34 .5418128967 f,
41 .3197937012 f,
48 .7867660522 f,
28 .0129547119 f,
26 .3748722076 f,
26 .2124195099 f,
40 .6859359741 f,
35 .7343978882 f,
41 .3197937012 f,
47 .6329460144 f,
55 .3498458862 f,
55 .6682891846 f,
40 .8614425659 f,
30 .1260128021 f,
33 .8056335449 f,
43 .6857032776 f,
48 .7867660522 f,
55 .3498458862 f,
63 .6065597534 f,
};
const float kBaseQuantMatrixYCbCr[] = {
// c = 0
1 .2397409345866273 f, //
1 .7227115097630963 f, //
2 .9212167156636855 f, //
2 .812737435286529 f, //
3 .339819711906184 f, //
3 .463603762596166 f, //
3 .840915217993518 f, //
3 .86956 f, //
1 .7227115097630963 f, //
2 .0928894413636874 f, //
2 .8456760904429297 f, //
2 .704506820909662 f, //
3 .4407673520905337 f, //
3 .166232352090534 f, //
4 .025208741558432 f, //
4 .035324490952577 f, //
2 .9212167156636855 f, //
2 .8456760904429297 f, //
2 .9587403520905338 f, //
3 .3862948970669273 f, //
3 .619523781336757 f, //
3 .9046279999999998 f, //
3 .757835838431854 f, //
4 .237447515714274 f, //
2 .812737435286529 f, //
2 .704506820909662 f, //
3 .3862948970669273 f, //
3 .380058821812233 f, //
4 .1679867415584315 f, //
4 .805510627261856 f, //
4 .784259 f, //
4 .605934 f, //
3 .339819711906184 f, //
3 .4407673520905337 f, //
3 .619523781336757 f, //
4 .1679867415584315 f, //
4 .579851258441568 f, //
4 .923237 f, //
5 .574107 f, //
5 .48533336146308 f, //
3 .463603762596166 f, //
3 .166232352090534 f, //
3 .9046279999999998 f, //
4 .805510627261856 f, //
4 .923237 f, //
5 .43936 f, //
5 .093895741558431 f, //
6 .0872254423617225 f, //
3 .840915217993518 f, //
4 .025208741558432 f, //
3 .757835838431854 f, //
4 .784259 f, //
5 .574107 f, //
5 .093895741558431 f, //
5 .438461 f, //
5 .4037359493250845 f, //
3 .86956 f, //
4 .035324490952577 f, //
4 .237447515714274 f, //
4 .605934 f, //
5 .48533336146308 f, //
6 .0872254423617225 f, //
5 .4037359493250845 f, //
4 .37787101190424 f,
// c = 1
2 .8236197786377537 f, //
6 .495639358561486 f, //
9 .310489207538302 f, //
10 .64747864717083 f, //
11 .07419143098738 f, //
17 .146390223910462 f, //
18 .463982229408998 f, //
29 .087001644203088 f, //
6 .495639358561486 f, //
8 .890103846667353 f, //
8 .976895794294748 f, //
13 .666270550318826 f, //
16 .547071905624193 f, //
16 .63871382827686 f, //
26 .778396930893695 f, //
21 .33034294694781 f, //
9 .310489207538302 f, //
8 .976895794294748 f, //
11 .08737706005991 f, //
18 .20548239870446 f, //
19 .752481654011646 f, //
23 .985660533114896 f, //
102 .6457378402362 f, //
24 .450989 f, //
10 .64747864717083 f, //
13 .666270550318826 f, //
18 .20548239870446 f, //
18 .628012327860365 f, //
16 .042509519487183 f, //
25 .04918273242625 f, //
25 .017140189353015 f, //
35 .79788782635831 f, //
11 .07419143098738 f, //
16 .547071905624193 f, //
19 .752481654011646 f, //
16 .042509519487183 f, //
19 .373482748612577 f, //
14 .677529999999999 f, //
19 .94695960400931 f, //
51 .094112 f, //
17 .146390223910462 f, //
16 .63871382827686 f, //
23 .985660533114896 f, //
25 .04918273242625 f, //
14 .677529999999999 f, //
31 .320412426835304 f, //
46 .357234000000005 f, //
67 .48111451705412 f, //
18 .463982229408998 f, //
26 .778396930893695 f, //
102 .6457378402362 f, //
25 .017140189353015 f, //
19 .94695960400931 f, //
46 .357234000000005 f, //
61 .315764694388044 f, //
88 .34665293823721 f, //
29 .087001644203088 f, //
21 .33034294694781 f, //
24 .450989 f, //
35 .79788782635831 f, //
51 .094112 f, //
67 .48111451705412 f, //
88 .34665293823721 f, //
112 .16099098350989 f,
// c = 2
2 .9217254961255255 f, //
4 .497681013199305 f, //
7 .356344520940414 f, //
6 .583891506504051 f, //
8 .535608740100237 f, //
8 .799434353234647 f, //
9 .188341534163023 f, //
9 .482700481227672 f, //
4 .497681013199305 f, //
6 .309548851989123 f, //
7 .024608962670982 f, //
7 .156445324163424 f, //
8 .049059218663244 f, //
7 .0124290657218555 f, //
6 .711923184393611 f, //
8 .380307846134853 f, //
7 .356344520940414 f, //
7 .024608962670982 f, //
6 .892101177327445 f, //
6 .882819916277163 f, //
8 .782226090078568 f, //
6 .8774750000000004 f, //
7 .8858175969577955 f, //
8 .67909 f, //
6 .583891506504051 f, //
7 .156445324163424 f, //
6 .882819916277163 f, //
7 .003072944847055 f, //
7 .7223464701024875 f, //
7 .955425720217421 f, //
7 .4734110000000005 f, //
8 .362933242943903 f, //
8 .535608740100237 f, //
8 .049059218663244 f, //
8 .782226090078568 f, //
7 .7223464701024875 f, //
6 .778005927001542 f, //
9 .484922741558432 f, //
9 .043702663686046 f, //
8 .053178199770173 f, //
8 .799434353234647 f, //
7 .0124290657218555 f, //
6 .8774750000000004 f, //
7 .955425720217421 f, //
9 .484922741558432 f, //
8 .607606527385098 f, //
9 .922697394370815 f, //
64 .25135180237939 f, //
9 .188341534163023 f, //
6 .711923184393611 f, //
7 .8858175969577955 f, //
7 .4734110000000005 f, //
9 .043702663686046 f, //
9 .922697394370815 f, //
63 .184936549738225 f, //
83 .35294340273799 f, //
9 .482700481227672 f, //
8 .380307846134853 f, //
8 .67909 f, //
8 .362933242943903 f, //
8 .053178199770173 f, //
64 .25135180237939 f, //
83 .35294340273799 f, //
114 .89202448569779 f, //
};
const float k420GlobalScale = 1 .22 ;
const float k420Rescale[64 ] = {
0 .4093 , 0 .3209 , 0 .3477 , 0 .3333 , 0 .3144 , 0 .2823 , 0 .3214 , 0 .3354 , //
0 .3209 , 0 .3111 , 0 .3489 , 0 .2801 , 0 .3059 , 0 .3119 , 0 .4135 , 0 .3445 , //
0 .3477 , 0 .3489 , 0 .3586 , 0 .3257 , 0 .2727 , 0 .3754 , 0 .3369 , 0 .3484 , //
0 .3333 , 0 .2801 , 0 .3257 , 0 .3020 , 0 .3515 , 0 .3410 , 0 .3971 , 0 .3839 , //
0 .3144 , 0 .3059 , 0 .2727 , 0 .3515 , 0 .3105 , 0 .3397 , 0 .2716 , 0 .3836 , //
0 .2823 , 0 .3119 , 0 .3754 , 0 .3410 , 0 .3397 , 0 .3212 , 0 .3203 , 0 .0726 , //
0 .3214 , 0 .4135 , 0 .3369 , 0 .3971 , 0 .2716 , 0 .3203 , 0 .0798 , 0 .0553 , //
0 .3354 , 0 .3445 , 0 .3484 , 0 .3839 , 0 .3836 , 0 .0726 , 0 .0553 , 0 .3368 , //
};
const float kBaseQuantMatrixStd[] = {
// c = 0
16 .0 f, 11 .0 f, 10 .0 f, 16 .0 f, 24 .0 f, 40 .0 f, 51 .0 f, 61 .0 f, //
12 .0 f, 12 .0 f, 14 .0 f, 19 .0 f, 26 .0 f, 58 .0 f, 60 .0 f, 55 .0 f, //
14 .0 f, 13 .0 f, 16 .0 f, 24 .0 f, 40 .0 f, 57 .0 f, 69 .0 f, 56 .0 f, //
14 .0 f, 17 .0 f, 22 .0 f, 29 .0 f, 51 .0 f, 87 .0 f, 80 .0 f, 62 .0 f, //
18 .0 f, 22 .0 f, 37 .0 f, 56 .0 f, 68 .0 f, 109 .0 f, 103 .0 f, 77 .0 f, //
24 .0 f, 35 .0 f, 55 .0 f, 64 .0 f, 81 .0 f, 104 .0 f, 113 .0 f, 92 .0 f, //
49 .0 f, 64 .0 f, 78 .0 f, 87 .0 f, 103 .0 f, 121 .0 f, 120 .0 f, 101 .0 f, //
72 .0 f, 92 .0 f, 95 .0 f, 98 .0 f, 112 .0 f, 100 .0 f, 103 .0 f, 99 .0 f, //
// c = 1
17 .0 f, 18 .0 f, 24 .0 f, 47 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, //
18 .0 f, 21 .0 f, 26 .0 f, 66 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, //
24 .0 f, 26 .0 f, 56 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, //
47 .0 f, 66 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, //
99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, //
99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, //
99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, //
99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, 99 .0 f, //
};
const float kZeroBiasMulYCbCrLQ[] = {
// c = 0
0 .0000 f, 0 .0568 f, 0 .3880 f, 0 .6190 f, 0 .6190 f, 0 .4490 f, 0 .4490 f, 0 .6187 f, //
0 .0568 f, 0 .5829 f, 0 .6189 f, 0 .6190 f, 0 .6190 f, 0 .7190 f, 0 .6190 f, 0 .6189 f, //
0 .3880 f, 0 .6189 f, 0 .6190 f, 0 .6190 f, 0 .6190 f, 0 .6190 f, 0 .6187 f, 0 .6100 f, //
0 .6190 f, 0 .6190 f, 0 .6190 f, 0 .6190 f, 0 .5890 f, 0 .3839 f, 0 .7160 f, 0 .6190 f, //
0 .6190 f, 0 .6190 f, 0 .6190 f, 0 .5890 f, 0 .6190 f, 0 .3880 f, 0 .5860 f, 0 .4790 f, //
0 .4490 f, 0 .7190 f, 0 .6190 f, 0 .3839 f, 0 .3880 f, 0 .6190 f, 0 .6190 f, 0 .6190 f, //
0 .4490 f, 0 .6190 f, 0 .6187 f, 0 .7160 f, 0 .5860 f, 0 .6190 f, 0 .6204 f, 0 .6190 f, //
0 .6187 f, 0 .6189 f, 0 .6100 f, 0 .6190 f, 0 .4790 f, 0 .6190 f, 0 .6190 f, 0 .3480 f, //
// c = 1
0 .0000 f, 1 .1640 f, 0 .9373 f, 1 .1319 f, 0 .8016 f, 0 .9136 f, 1 .1530 f, 0 .9430 f, //
1 .1640 f, 0 .9188 f, 0 .9160 f, 1 .1980 f, 1 .1830 f, 0 .9758 f, 0 .9430 f, 0 .9430 f, //
0 .9373 f, 0 .9160 f, 0 .8430 f, 1 .1720 f, 0 .7083 f, 0 .9430 f, 0 .9430 f, 0 .9430 f, //
1 .1319 f, 1 .1980 f, 1 .1720 f, 1 .1490 f, 0 .8547 f, 0 .9430 f, 0 .9430 f, 0 .9430 f, //
0 .8016 f, 1 .1830 f, 0 .7083 f, 0 .8547 f, 0 .9430 f, 0 .9430 f, 0 .9430 f, 0 .9430 f, //
0 .9136 f, 0 .9758 f, 0 .9430 f, 0 .9430 f, 0 .9430 f, 0 .9430 f, 0 .9430 f, 0 .9430 f, //
1 .1530 f, 0 .9430 f, 0 .9430 f, 0 .9430 f, 0 .9430 f, 0 .9430 f, 0 .9430 f, 0 .9480 f, //
0 .9430 f, 0 .9430 f, 0 .9430 f, 0 .9430 f, 0 .9430 f, 0 .9430 f, 0 .9480 f, 0 .9430 f, //
// c = 2
0 .0000 f, 1 .3190 f, 0 .4308 f, 0 .4460 f, 0 .0661 f, 0 .0660 f, 0 .2660 f, 0 .2960 f, //
1 .3190 f, 0 .3280 f, 0 .3093 f, 0 .0750 f, 0 .0505 f, 0 .1594 f, 0 .3060 f, 0 .2113 f, //
0 .4308 f, 0 .3093 f, 0 .3060 f, 0 .1182 f, 0 .0500 f, 0 .3060 f, 0 .3915 f, 0 .2426 f, //
0 .4460 f, 0 .0750 f, 0 .1182 f, 0 .0512 f, 0 .0500 f, 0 .2130 f, 0 .3930 f, 0 .1590 f, //
0 .0661 f, 0 .0505 f, 0 .0500 f, 0 .0500 f, 0 .3055 f, 0 .3360 f, 0 .5148 f, 0 .5403 f, //
0 .0660 f, 0 .1594 f, 0 .3060 f, 0 .2130 f, 0 .3360 f, 0 .5060 f, 0 .5874 f, 0 .3060 f, //
0 .2660 f, 0 .3060 f, 0 .3915 f, 0 .3930 f, 0 .5148 f, 0 .5874 f, 0 .3060 f, 0 .3060 f, //
0 .2960 f, 0 .2113 f, 0 .2426 f, 0 .1590 f, 0 .5403 f, 0 .3060 f, 0 .3060 f, 0 .3060 f, //
};
const float kZeroBiasMulYCbCrHQ[] = {
// c = 0
0 .0000 f, 0 .0044 f, 0 .2521 f, 0 .6547 f, 0 .8161 f, 0 .6130 f, 0 .8841 f, 0 .8155 f, //
0 .0044 f, 0 .6831 f, 0 .6553 f, 0 .6295 f, 0 .7848 f, 0 .7843 f, 0 .8474 f, 0 .7836 f, //
0 .2521 f, 0 .6553 f, 0 .7834 f, 0 .7829 f, 0 .8161 f, 0 .8072 f, 0 .7743 f, 0 .9242 f, //
0 .6547 f, 0 .6295 f, 0 .7829 f, 0 .8654 f, 0 .7829 f, 0 .6986 f, 0 .7818 f, 0 .7726 f, //
0 .8161 f, 0 .7848 f, 0 .8161 f, 0 .7829 f, 0 .7471 f, 0 .7827 f, 0 .7843 f, 0 .7653 f, //
0 .6130 f, 0 .7843 f, 0 .8072 f, 0 .6986 f, 0 .7827 f, 0 .7848 f, 0 .9508 f, 0 .7653 f, //
0 .8841 f, 0 .8474 f, 0 .7743 f, 0 .7818 f, 0 .7843 f, 0 .9508 f, 0 .7839 f, 0 .8437 f, //
0 .8155 f, 0 .7836 f, 0 .9242 f, 0 .7726 f, 0 .7653 f, 0 .7653 f, 0 .8437 f, 0 .7819 f, //
// c = 1
0 .0000 f, 1 .0816 f, 1 .0556 f, 1 .2876 f, 1 .1554 f, 1 .1567 f, 1 .8851 f, 0 .5488 f, //
1 .0816 f, 1 .1537 f, 1 .1850 f, 1 .0712 f, 1 .1671 f, 2 .0719 f, 1 .0544 f, 1 .4764 f, //
1 .0556 f, 1 .1850 f, 1 .2870 f, 1 .1981 f, 1 .8181 f, 1 .2618 f, 1 .0564 f, 1 .1191 f, //
1 .2876 f, 1 .0712 f, 1 .1981 f, 1 .4753 f, 2 .0609 f, 1 .0564 f, 1 .2645 f, 1 .0564 f, //
1 .1554 f, 1 .1671 f, 1 .8181 f, 2 .0609 f, 0 .7324 f, 1 .1163 f, 0 .8464 f, 1 .0564 f, //
1 .1567 f, 2 .0719 f, 1 .2618 f, 1 .0564 f, 1 .1163 f, 1 .0040 f, 1 .0564 f, 1 .0564 f, //
1 .8851 f, 1 .0544 f, 1 .0564 f, 1 .2645 f, 0 .8464 f, 1 .0564 f, 1 .0564 f, 1 .0564 f, //
0 .5488 f, 1 .4764 f, 1 .1191 f, 1 .0564 f, 1 .0564 f, 1 .0564 f, 1 .0564 f, 1 .0564 f, //
// c = 2
0 .0000 f, 0 .5392 f, 0 .6659 f, 0 .8968 f, 0 .6829 f, 0 .6328 f, 0 .5802 f, 0 .4836 f, //
0 .5392 f, 0 .6746 f, 0 .6760 f, 0 .6102 f, 0 .6015 f, 0 .6958 f, 0 .7327 f, 0 .4897 f, //
0 .6659 f, 0 .6760 f, 0 .6957 f, 0 .6543 f, 0 .4396 f, 0 .6330 f, 0 .7081 f, 0 .2583 f, //
0 .8968 f, 0 .6102 f, 0 .6543 f, 0 .5913 f, 0 .6457 f, 0 .5828 f, 0 .5139 f, 0 .3565 f, //
0 .6829 f, 0 .6015 f, 0 .4396 f, 0 .6457 f, 0 .5633 f, 0 .4263 f, 0 .6371 f, 0 .5949 f, //
0 .6328 f, 0 .6958 f, 0 .6330 f, 0 .5828 f, 0 .4263 f, 0 .2847 f, 0 .2909 f, 0 .6629 f, //
0 .5802 f, 0 .7327 f, 0 .7081 f, 0 .5139 f, 0 .6371 f, 0 .2909 f, 0 .6644 f, 0 .6644 f, //
0 .4836 f, 0 .4897 f, 0 .2583 f, 0 .3565 f, 0 .5949 f, 0 .6629 f, 0 .6644 f, 0 .6644 f, //
};
const float kZeroBiasOffsetYCbCrDC[] = {0 .0 f, 0 .0 f, 0 .0 f};
const float kZeroBiasOffsetYCbCrAC[] = {
0 .59082 f,
0 .58146 f,
0 .57988 f,
};
constexpr uint8_t kTransferFunctionPQ = 16 ;
constexpr uint8_t kTransferFunctionHLG = 18 ;
float DistanceToLinearQuality(float distance) {
if (distance <= 0 .1 f) {
return 1 .0 f;
} else if (distance <= 4 .6 f) {
return (200 .0 f / 9 .0 f) * (distance - 0 .1 f);
} else if (distance <= 6 .4 f) {
return 5000 .0 f / (100 .0 f - (distance - 0 .1 f) / 0 .09 f);
} else if (distance < 25 .0 f) {
return 530000 .0 f /
(3450 .0 f -
300 .0 f * std::sqrt((848 .0 f * distance - 5330 .0 f) / 120 .0 f));
} else {
return 5000 .0 f;
}
}
constexpr float kExponent[DCTSIZE2] = {
1 .00 f, 0 .51 f, 0 .67 f, 0 .74 f, 1 .00 f, 1 .00 f, 1 .00 f, 1 .00 f, //
0 .51 f, 0 .66 f, 0 .69 f, 0 .87 f, 1 .00 f, 1 .00 f, 1 .00 f, 1 .00 f, //
0 .67 f, 0 .69 f, 0 .84 f, 0 .83 f, 0 .96 f, 1 .00 f, 1 .00 f, 1 .00 f, //
0 .74 f, 0 .87 f, 0 .83 f, 1 .00 f, 1 .00 f, 0 .91 f, 0 .91 f, 1 .00 f, //
1 .00 f, 1 .00 f, 0 .96 f, 1 .00 f, 1 .00 f, 1 .00 f, 1 .00 f, 1 .00 f, //
1 .00 f, 1 .00 f, 1 .00 f, 0 .91 f, 1 .00 f, 1 .00 f, 1 .00 f, 1 .00 f, //
1 .00 f, 1 .00 f, 1 .00 f, 0 .91 f, 1 .00 f, 1 .00 f, 1 .00 f, 1 .00 f, //
1 .00 f, 1 .00 f, 1 .00 f, 1 .00 f, 1 .00 f, 1 .00 f, 1 .00 f, 1 .00 f, //
};
constexpr float kDist0 = 1 .5 f; // distance where non-linearity kicks in.
float DistanceToScale(float distance, int k) {
if (distance < kDist0) {
return distance;
}
const float exp = kExponent[k];
const float mul = std::pow(kDist0, 1 .0 - exp);
return std::max<float >(0 .5 f * distance, mul * std::pow(distance, exp));
}
float ScaleToDistance(float scale, int k) {
if (scale < kDist0) {
return scale;
}
const float exp = 1 .0 / kExponent[k];
const float mul = std::pow(kDist0, 1 .0 - exp);
return std::min<float >(2 .0 f * scale, mul * std::pow(scale, exp));
}
float QuantValsToDistance(j_compress_ptr cinfo) {
jpeg_comp_master* m = cinfo->master;
float global_scale = kGlobalScaleYCbCr;
if (m->cicp_transfer_function == kTransferFunctionPQ) {
global_scale *= .4 f;
} else if (m->cicp_transfer_function == kTransferFunctionHLG) {
global_scale *= .5 f;
}
int quant_max = m->force_baseline ? 255 : 32767 U;
static const float kDistMax = 10000 .0 f;
float dist_min = 0 .0 f;
float dist_max = kDistMax;
for (int c = 0 ; c < cinfo->num_components; ++c) {
int quant_idx = cinfo->comp_info[c].quant_tbl_no;
uint16_t* quantval = cinfo->quant_tbl_ptrs[quant_idx]->quantval;
const float * base_qm = &kBaseQuantMatrixYCbCr[quant_idx * DCTSIZE2];
for (int k = 0 ; k < DCTSIZE2; ++k) {
float dmin = 0 .0 ;
float dmax = kDistMax;
float invq = 1 .0 f / base_qm[k] / global_scale;
int qval = quantval[k];
if (qval > 1 ) {
float scale_min = (qval - 0 .5 f) * invq;
dmin = ScaleToDistance(scale_min, k);
}
if (qval < quant_max) {
float scale_max = (qval + 0 .5 f) * invq;
dmax = ScaleToDistance(scale_max, k);
}
if (dmin <= dist_max) {
dist_min = std::max(dmin, dist_min);
}
if (dmax >= dist_min) {
dist_max = std::min(dist_max, dmax);
}
}
}
float distance;
if (dist_min == 0 ) {
distance = dist_max;
} else if (dist_max == kDistMax) {
distance = dist_min;
} else {
distance = 0 .5 f * (dist_min + dist_max);
}
return distance;
}
bool IsYUV420(j_compress_ptr cinfo) {
return (cinfo->jpeg_color_space == JCS_YCbCr &&
cinfo->comp_info[0 ].h_samp_factor == 2 &&
cinfo->comp_info[0 ].v_samp_factor == 2 &&
cinfo->comp_info[1 ].h_samp_factor == 1 &&
cinfo->comp_info[1 ].v_samp_factor == 1 &&
cinfo->comp_info[2 ].h_samp_factor == 1 &&
cinfo->comp_info[2 ].v_samp_factor == 1 );
}
} // namespace
void SetQuantMatrices(j_compress_ptr cinfo, float distances[NUM_QUANT_TBLS],
bool add_two_chroma_tables) {
jpeg_comp_master* m = cinfo->master;
const bool xyb = m->xyb_mode && cinfo->jpeg_color_space == JCS_RGB;
const bool is_yuv420 = IsYUV420(cinfo);
float global_scale;
bool non_linear_scaling = true ;
const float * base_quant_matrix[NUM_QUANT_TBLS];
int num_base_tables;
if (xyb) {
global_scale = kGlobalScaleXYB;
num_base_tables = 3 ;
base_quant_matrix[0 ] = kBaseQuantMatrixXYB;
base_quant_matrix[1 ] = kBaseQuantMatrixXYB + DCTSIZE2;
base_quant_matrix[2 ] = kBaseQuantMatrixXYB + 2 * DCTSIZE2;
} else if (cinfo->jpeg_color_space == JCS_YCbCr && !m->use_std_tables) {
global_scale = kGlobalScaleYCbCr;
if (m->cicp_transfer_function == kTransferFunctionPQ) {
global_scale *= .4 f;
} else if (m->cicp_transfer_function == kTransferFunctionHLG) {
global_scale *= .5 f;
}
if (is_yuv420) {
global_scale *= k420GlobalScale;
}
if (add_two_chroma_tables) {
cinfo->comp_info[2 ].quant_tbl_no = 2 ;
num_base_tables = 3 ;
base_quant_matrix[0 ] = kBaseQuantMatrixYCbCr;
base_quant_matrix[1 ] = kBaseQuantMatrixYCbCr + DCTSIZE2;
base_quant_matrix[2 ] = kBaseQuantMatrixYCbCr + 2 * DCTSIZE2;
} else {
num_base_tables = 2 ;
base_quant_matrix[0 ] = kBaseQuantMatrixYCbCr;
// Use the Cr table for both Cb and Cr.
base_quant_matrix[1 ] = kBaseQuantMatrixYCbCr + 2 * DCTSIZE2;
}
} else {
global_scale = 0 .01 f;
non_linear_scaling = false ;
num_base_tables = 2 ;
base_quant_matrix[0 ] = kBaseQuantMatrixStd;
base_quant_matrix[1 ] = kBaseQuantMatrixStd + DCTSIZE2;
}
int quant_max = m->force_baseline ? 255 : 32767 U;
for (int quant_idx = 0 ; quant_idx < num_base_tables; ++quant_idx) {
const float * base_qm = base_quant_matrix[quant_idx];
JQUANT_TBL** qtable = &cinfo->quant_tbl_ptrs[quant_idx];
if (*qtable == nullptr) {
*qtable = jpegli_alloc_quant_table(reinterpret_cast <j_common_ptr>(cinfo));
}
for (int k = 0 ; k < DCTSIZE2; ++k) {
float scale = global_scale;
if (non_linear_scaling) {
scale *= DistanceToScale(distances[quant_idx], k);
if (is_yuv420 && quant_idx > 0 ) {
scale *= k420Rescale[k];
}
} else {
scale *= DistanceToLinearQuality(distances[quant_idx]);
}
int qval = std::round(scale * base_qm[k]);
(*qtable)->quantval[k] = std::max(1 , std::min(qval, quant_max));
}
(*qtable)->sent_table = FALSE ;
}
}
void InitQuantizer(j_compress_ptr cinfo, QuantPass pass) {
jpeg_comp_master* m = cinfo->master;
// Compute quantization multupliers from the quant table values.
for (int c = 0 ; c < cinfo->num_components; ++c) {
int quant_idx = cinfo->comp_info[c].quant_tbl_no;
JQUANT_TBL* quant_table = cinfo->quant_tbl_ptrs[quant_idx];
if (!quant_table) {
JPEGLI_ERROR("Missing quantization table %d for component %d" , quant_idx,
c);
}
for (size_t k = 0 ; k < DCTSIZE2; k++) {
int val = quant_table->quantval[k];
if (val == 0 ) {
JPEGLI_ERROR("Invalid quantval 0." );
}
switch (pass) {
case QuantPass::NO_SEARCH:
m->quant_mul[c][k] = 8 .0 f / val;
break ;
case QuantPass::SEARCH_FIRST_PASS:
m->quant_mul[c][k] = 128 .0 f;
break ;
case QuantPass::SEARCH_SECOND_PASS:
m->quant_mul[c][kJPEGZigZagOrder[k]] = 1 .0 f / (16 * val);
break ;
}
}
}
if (m->use_adaptive_quantization) {
for (int c = 0 ; c < cinfo->num_components; ++c) {
for (int k = 0 ; k < DCTSIZE2; ++k) {
m->zero_bias_mul[c][k] = k == 0 ? 0 .0 f : 0 .5 f;
m->zero_bias_offset[c][k] = k == 0 ? 0 .0 f : 0 .5 f;
}
}
if (cinfo->jpeg_color_space == JCS_YCbCr) {
float distance = QuantValsToDistance(cinfo);
static const float kDistHQ = 1 .0 f;
static const float kDistLQ = 3 .0 f;
float mix0 = (distance - kDistHQ) / (kDistLQ - kDistHQ);
mix0 = std::max(0 .0 f, std::min(1 .0 f, mix0));
float mix1 = 1 .0 f - mix0;
for (int c = 0 ; c < cinfo->num_components; ++c) {
for (int k = 0 ; k < DCTSIZE2; ++k) {
float mul0 = kZeroBiasMulYCbCrLQ[c * DCTSIZE2 + k];
float mul1 = kZeroBiasMulYCbCrHQ[c * DCTSIZE2 + k];
m->zero_bias_mul[c][k] = mix0 * mul0 + mix1 * mul1;
m->zero_bias_offset[c][k] =
k == 0 ? kZeroBiasOffsetYCbCrDC[c] : kZeroBiasOffsetYCbCrAC[c];
}
}
}
} else if (cinfo->jpeg_color_space == JCS_YCbCr) {
for (int c = 0 ; c < cinfo->num_components; ++c) {
for (int k = 0 ; k < DCTSIZE2; ++k) {
m->zero_bias_offset[c][k] =
k == 0 ? kZeroBiasOffsetYCbCrDC[c] : kZeroBiasOffsetYCbCrAC[c];
}
}
}
}
} // namespace jpegli
Messung V0.5 in Prozent C=88 H=96 G=91
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-06-04)
¤
*© Formatika GbR, Deutschland