Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
godot
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
community
godot
Commits
93f6a065
Commit
93f6a065
authored
Mar 04, 2019
by
Rémi Verschelde
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
libwebp: Sync with upstream 1.0.2
parent
2bc98194
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
492 additions
and
164 deletions
+492
-164
SCsub
modules/webp/SCsub
+4
-3
README.md
thirdparty/README.md
+1
-1
AUTHORS
thirdparty/libwebp/AUTHORS
+4
-1
vp8i_dec.h
thirdparty/libwebp/src/dec/vp8i_dec.h
+1
-1
demux.c
thirdparty/libwebp/src/demux/demux.c
+1
-1
cost.c
thirdparty/libwebp/src/dsp/cost.c
+6
-0
cost_neon.c
thirdparty/libwebp/src/dsp/cost_neon.c
+122
-0
quant.h
thirdparty/libwebp/src/dsp/quant.h
+70
-0
histogram_enc.c
thirdparty/libwebp/src/enc/histogram_enc.c
+245
-123
predictor_enc.c
thirdparty/libwebp/src/enc/predictor_enc.c
+8
-6
quant_enc.c
thirdparty/libwebp/src/enc/quant_enc.c
+1
-13
vp8i_enc.h
thirdparty/libwebp/src/enc/vp8i_enc.h
+1
-1
vp8l_enc.c
thirdparty/libwebp/src/enc/vp8l_enc.c
+1
-0
muxi.h
thirdparty/libwebp/src/mux/muxi.h
+1
-1
bit_writer_utils.c
thirdparty/libwebp/src/utils/bit_writer_utils.c
+1
-0
utils.h
thirdparty/libwebp/src/utils/utils.h
+13
-13
decode.h
thirdparty/libwebp/src/webp/decode.h
+12
-0
No files found.
modules/webp/SCsub
View file @
93f6a065
...
...
@@ -29,13 +29,11 @@ if env['builtin_libwebp']:
"dsp/cost.c"
,
"dsp/cost_mips32.c"
,
"dsp/cost_mips_dsp_r2.c"
,
"dsp/cost_neon.c"
,
"dsp/cost_sse2.c"
,
"dsp/cpu.c"
,
"dsp/dec.c"
,
"dsp/dec_clip_tables.c"
,
"dsp/ssim.c"
,
"dsp/ssim_sse2.c"
,
"dsp/yuv_neon.c"
,
"dsp/dec_mips32.c"
,
"dsp/dec_mips_dsp_r2.c"
,
"dsp/dec_msa.c"
,
...
...
@@ -72,6 +70,8 @@ if env['builtin_libwebp']:
"dsp/rescaler_msa.c"
,
"dsp/rescaler_neon.c"
,
"dsp/rescaler_sse2.c"
,
"dsp/ssim.c"
,
"dsp/ssim_sse2.c"
,
"dsp/upsampling.c"
,
"dsp/upsampling_mips_dsp_r2.c"
,
"dsp/upsampling_msa.c"
,
...
...
@@ -81,6 +81,7 @@ if env['builtin_libwebp']:
"dsp/yuv.c"
,
"dsp/yuv_mips32.c"
,
"dsp/yuv_mips_dsp_r2.c"
,
"dsp/yuv_neon.c"
,
"dsp/yuv_sse2.c"
,
"dsp/yuv_sse41.c"
,
"enc/alpha_enc.c"
,
...
...
thirdparty/README.md
View file @
93f6a065
...
...
@@ -239,7 +239,7 @@ from the Android NDK r18.
## libwebp
-
Upstream: https://chromium.googlesource.com/webm/libwebp/
-
Version: 1.0.
1
-
Version: 1.0.
2
-
License: BSD-3-Clause
Files extracted from upstream source:
...
...
thirdparty/libwebp/AUTHORS
View file @
93f6a065
Contributors:
- Alan Browning (browning at google dot com)
- Charles Munger (clm at google dot com)
- Christian Duvivier (cduvivier at google dot com)
- Djordje Pesut (djordje dot pesut at imgtec dot com)
...
...
@@ -6,9 +7,10 @@ Contributors:
- James Zern (jzern at google dot com)
- Jan Engelhardt (jengelh at medozas dot de)
- Jehan (jehan at girinstud dot io)
- Johann (johann dot koenig at duck dot com)
- Johann
Koenig
(johann dot koenig at duck dot com)
- Jovan Zelincevic (jovan dot zelincevic at imgtec dot com)
- Jyrki Alakuijala (jyrki at google dot com)
- Konstantin Ivlev (tomskside at gmail dot com)
- Lode Vandevenne (lode at google dot com)
- Lou Quillio (louquillio at google dot com)
- Mans Rullgard (mans at mansr dot com)
...
...
@@ -37,3 +39,4 @@ Contributors:
- Vincent Rabaud (vrabaud at google dot com)
- Vlad Tsyrklevich (vtsyrklevich at chromium dot org)
- Yang Zhang (yang dot zhang at arm dot com)
- Yannis Guyon (yguyon at google dot com)
thirdparty/libwebp/src/dec/vp8i_dec.h
View file @
93f6a065
...
...
@@ -32,7 +32,7 @@ extern "C" {
// version numbers
#define DEC_MAJ_VERSION 1
#define DEC_MIN_VERSION 0
#define DEC_REV_VERSION
1
#define DEC_REV_VERSION
2
// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline).
// Constraints are: We need to store one 16x16 block of luma samples (y),
...
...
thirdparty/libwebp/src/demux/demux.c
View file @
93f6a065
...
...
@@ -25,7 +25,7 @@
#define DMUX_MAJ_VERSION 1
#define DMUX_MIN_VERSION 0
#define DMUX_REV_VERSION
1
#define DMUX_REV_VERSION
2
typedef
struct
{
size_t
start_
;
// start location of the data
...
...
thirdparty/libwebp/src/dsp/cost.c
View file @
93f6a065
...
...
@@ -377,6 +377,7 @@ VP8SetResidualCoeffsFunc VP8SetResidualCoeffs;
extern
void
VP8EncDspCostInitMIPS32
(
void
);
extern
void
VP8EncDspCostInitMIPSdspR2
(
void
);
extern
void
VP8EncDspCostInitSSE2
(
void
);
extern
void
VP8EncDspCostInitNEON
(
void
);
WEBP_DSP_INIT_FUNC
(
VP8EncDspCostInit
)
{
VP8GetResidualCost
=
GetResidualCost_C
;
...
...
@@ -399,6 +400,11 @@ WEBP_DSP_INIT_FUNC(VP8EncDspCostInit) {
VP8EncDspCostInitSSE2
();
}
#endif
#if defined(WEBP_USE_NEON)
if
(
VP8GetCPUInfo
(
kNEON
))
{
VP8EncDspCostInitNEON
();
}
#endif
}
}
...
...
thirdparty/libwebp/src/dsp/cost_neon.c
0 → 100644
View file @
93f6a065
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
// ARM NEON version of cost functions
#include "src/dsp/dsp.h"
#if defined(WEBP_USE_NEON)
#include "src/dsp/neon.h"
#include "src/enc/cost_enc.h"
static
const
uint8_t
position
[
16
]
=
{
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
10
,
11
,
12
,
13
,
14
,
15
,
16
};
static
void
SetResidualCoeffs_NEON
(
const
int16_t
*
const
coeffs
,
VP8Residual
*
const
res
)
{
const
int16x8_t
minus_one
=
vdupq_n_s16
(
-
1
);
const
int16x8_t
coeffs_0
=
vld1q_s16
(
coeffs
);
const
int16x8_t
coeffs_1
=
vld1q_s16
(
coeffs
+
8
);
const
uint16x8_t
eob_0
=
vtstq_s16
(
coeffs_0
,
minus_one
);
const
uint16x8_t
eob_1
=
vtstq_s16
(
coeffs_1
,
minus_one
);
const
uint8x16_t
eob
=
vcombine_u8
(
vqmovn_u16
(
eob_0
),
vqmovn_u16
(
eob_1
));
const
uint8x16_t
masked
=
vandq_u8
(
eob
,
vld1q_u8
(
position
));
#ifdef __aarch64__
res
->
last
=
vmaxvq_u8
(
masked
)
-
1
;
#else
const
uint8x8_t
eob_8x8
=
vmax_u8
(
vget_low_u8
(
masked
),
vget_high_u8
(
masked
));
const
uint16x8_t
eob_16x8
=
vmovl_u8
(
eob_8x8
);
const
uint16x4_t
eob_16x4
=
vmax_u16
(
vget_low_u16
(
eob_16x8
),
vget_high_u16
(
eob_16x8
));
const
uint32x4_t
eob_32x4
=
vmovl_u16
(
eob_16x4
);
uint32x2_t
eob_32x2
=
vmax_u32
(
vget_low_u32
(
eob_32x4
),
vget_high_u32
(
eob_32x4
));
eob_32x2
=
vpmax_u32
(
eob_32x2
,
eob_32x2
);
vst1_lane_s32
(
&
res
->
last
,
vreinterpret_s32_u32
(
eob_32x2
),
0
);
--
res
->
last
;
#endif // __aarch64__
res
->
coeffs
=
coeffs
;
}
static
int
GetResidualCost_NEON
(
int
ctx0
,
const
VP8Residual
*
const
res
)
{
uint8_t
levels
[
16
],
ctxs
[
16
];
uint16_t
abs_levels
[
16
];
int
n
=
res
->
first
;
// should be prob[VP8EncBands[n]], but it's equivalent for n=0 or 1
const
int
p0
=
res
->
prob
[
n
][
ctx0
][
0
];
CostArrayPtr
const
costs
=
res
->
costs
;
const
uint16_t
*
t
=
costs
[
n
][
ctx0
];
// bit_cost(1, p0) is already incorporated in t[] tables, but only if ctx != 0
// (as required by the syntax). For ctx0 == 0, we need to add it here or it'll
// be missing during the loop.
int
cost
=
(
ctx0
==
0
)
?
VP8BitCost
(
1
,
p0
)
:
0
;
if
(
res
->
last
<
0
)
{
return
VP8BitCost
(
0
,
p0
);
}
{
// precompute clamped levels and contexts, packed to 8b.
const
uint8x16_t
kCst2
=
vdupq_n_u8
(
2
);
const
uint8x16_t
kCst67
=
vdupq_n_u8
(
MAX_VARIABLE_LEVEL
);
const
int16x8_t
c0
=
vld1q_s16
(
res
->
coeffs
);
const
int16x8_t
c1
=
vld1q_s16
(
res
->
coeffs
+
8
);
const
uint16x8_t
E0
=
vreinterpretq_u16_s16
(
vabsq_s16
(
c0
));
const
uint16x8_t
E1
=
vreinterpretq_u16_s16
(
vabsq_s16
(
c1
));
const
uint8x16_t
F
=
vcombine_u8
(
vqmovn_u16
(
E0
),
vqmovn_u16
(
E1
));
const
uint8x16_t
G
=
vminq_u8
(
F
,
kCst2
);
// context = 0,1,2
const
uint8x16_t
H
=
vminq_u8
(
F
,
kCst67
);
// clamp_level in [0..67]
vst1q_u8
(
ctxs
,
G
);
vst1q_u8
(
levels
,
H
);
vst1q_u16
(
abs_levels
,
E0
);
vst1q_u16
(
abs_levels
+
8
,
E1
);
}
for
(;
n
<
res
->
last
;
++
n
)
{
const
int
ctx
=
ctxs
[
n
];
const
int
level
=
levels
[
n
];
const
int
flevel
=
abs_levels
[
n
];
// full level
cost
+=
VP8LevelFixedCosts
[
flevel
]
+
t
[
level
];
// simplified VP8LevelCost()
t
=
costs
[
n
+
1
][
ctx
];
}
// Last coefficient is always non-zero
{
const
int
level
=
levels
[
n
];
const
int
flevel
=
abs_levels
[
n
];
assert
(
flevel
!=
0
);
cost
+=
VP8LevelFixedCosts
[
flevel
]
+
t
[
level
];
if
(
n
<
15
)
{
const
int
b
=
VP8EncBands
[
n
+
1
];
const
int
ctx
=
ctxs
[
n
];
const
int
last_p0
=
res
->
prob
[
b
][
ctx
][
0
];
cost
+=
VP8BitCost
(
0
,
last_p0
);
}
}
return
cost
;
}
//------------------------------------------------------------------------------
// Entry point
extern
void
VP8EncDspCostInitNEON
(
void
);
WEBP_TSAN_IGNORE_FUNCTION
void
VP8EncDspCostInitNEON
(
void
)
{
VP8SetResidualCoeffs
=
SetResidualCoeffs_NEON
;
VP8GetResidualCost
=
GetResidualCost_NEON
;
}
#else // !WEBP_USE_NEON
WEBP_DSP_INIT_STUB
(
VP8EncDspCostInitNEON
)
#endif // WEBP_USE_NEON
thirdparty/libwebp/src/dsp/quant.h
0 → 100644
View file @
93f6a065
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
#ifndef WEBP_DSP_QUANT_H_
#define WEBP_DSP_QUANT_H_
#include "src/dsp/dsp.h"
#include "src/webp/types.h"
#if defined(WEBP_USE_NEON) && !defined(WEBP_ANDROID_NEON) && \
!defined(WEBP_HAVE_NEON_RTCD)
#include <arm_neon.h>
#define IsFlat IsFlat_NEON
static
uint32x2_t
horizontal_add_uint32x4
(
const
uint32x4_t
a
)
{
const
uint64x2_t
b
=
vpaddlq_u32
(
a
);
return
vadd_u32
(
vreinterpret_u32_u64
(
vget_low_u64
(
b
)),
vreinterpret_u32_u64
(
vget_high_u64
(
b
)));
}
static
WEBP_INLINE
int
IsFlat
(
const
int16_t
*
levels
,
int
num_blocks
,
int
thresh
)
{
const
int16x8_t
tst_ones
=
vdupq_n_s16
(
-
1
);
uint32x4_t
sum
=
vdupq_n_u32
(
0
);
for
(
int
i
=
0
;
i
<
num_blocks
;
++
i
)
{
// Set DC to zero.
const
int16x8_t
a_0
=
vsetq_lane_s16
(
0
,
vld1q_s16
(
levels
),
0
);
const
int16x8_t
a_1
=
vld1q_s16
(
levels
+
8
);
const
uint16x8_t
b_0
=
vshrq_n_u16
(
vtstq_s16
(
a_0
,
tst_ones
),
15
);
const
uint16x8_t
b_1
=
vshrq_n_u16
(
vtstq_s16
(
a_1
,
tst_ones
),
15
);
sum
=
vpadalq_u16
(
sum
,
b_0
);
sum
=
vpadalq_u16
(
sum
,
b_1
);
levels
+=
16
;
}
return
thresh
>=
(
int32_t
)
vget_lane_u32
(
horizontal_add_uint32x4
(
sum
),
0
);
}
#else
#define IsFlat IsFlat_C
static
WEBP_INLINE
int
IsFlat
(
const
int16_t
*
levels
,
int
num_blocks
,
int
thresh
)
{
int
score
=
0
;
while
(
num_blocks
--
>
0
)
{
// TODO(skal): refine positional scoring?
int
i
;
for
(
i
=
1
;
i
<
16
;
++
i
)
{
// omit DC, we're only interested in AC
score
+=
(
levels
[
i
]
!=
0
);
if
(
score
>
thresh
)
return
0
;
}
levels
+=
16
;
}
return
1
;
}
#endif // defined(WEBP_USE_NEON) && !defined(WEBP_ANDROID_NEON) &&
// !defined(WEBP_HAVE_NEON_RTCD)
#endif // WEBP_DSP_QUANT_H_
thirdparty/libwebp/src/enc/histogram_enc.c
View file @
93f6a065
...
...
@@ -165,7 +165,7 @@ VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits) {
void
VP8LHistogramSetClear
(
VP8LHistogramSet
*
const
set
)
{
int
i
;
const
int
cache_bits
=
set
->
histograms
[
0
]
->
palette_code_bits_
;
const
int
size
=
set
->
size
;
const
int
size
=
set
->
max_
size
;
const
size_t
total_size
=
HistogramSetTotalSize
(
size
,
cache_bits
);
uint8_t
*
memory
=
(
uint8_t
*
)
set
;
...
...
@@ -180,6 +180,20 @@ void VP8LHistogramSetClear(VP8LHistogramSet* const set) {
}
}
// Removes the histogram 'i' from 'set' by setting it to NULL.
static
void
HistogramSetRemoveHistogram
(
VP8LHistogramSet
*
const
set
,
int
i
,
int
*
const
num_used
)
{
assert
(
set
->
histograms
[
i
]
!=
NULL
);
set
->
histograms
[
i
]
=
NULL
;
--*
num_used
;
// If we remove the last valid one, shrink until the next valid one.
if
(
i
==
set
->
size
-
1
)
{
while
(
set
->
size
>=
1
&&
set
->
histograms
[
set
->
size
-
1
]
==
NULL
)
{
--
set
->
size
;
}
}
}
// -----------------------------------------------------------------------------
void
VP8LHistogramAddSinglePixOrCopy
(
VP8LHistogram
*
const
histo
,
...
...
@@ -447,7 +461,9 @@ static double HistogramAddEval(const VP8LHistogram* const a,
static
double
HistogramAddThresh
(
const
VP8LHistogram
*
const
a
,
const
VP8LHistogram
*
const
b
,
double
cost_threshold
)
{
double
cost
=
-
a
->
bit_cost_
;
double
cost
;
assert
(
a
!=
NULL
&&
b
!=
NULL
);
cost
=
-
a
->
bit_cost_
;
GetCombinedHistogramEntropy
(
a
,
b
,
cost_threshold
,
&
cost
);
return
cost
;
}
...
...
@@ -561,14 +577,17 @@ static void HistogramBuild(
}
// Copies the histograms and computes its bit_cost.
static
void
HistogramCopyAndAnalyze
(
VP8LHistogramSet
*
const
orig_histo
,
VP8LHistogramSet
*
const
image_histo
)
{
int
i
;
const
int
histo_size
=
orig_histo
->
size
;
static
const
uint16_t
kInvalidHistogramSymbol
=
(
uint16_t
)(
-
1
);
static
void
HistogramCopyAndAnalyze
(
VP8LHistogramSet
*
const
orig_histo
,
VP8LHistogramSet
*
const
image_histo
,
int
*
const
num_used
,
uint16_t
*
const
histogram_symbols
)
{
int
i
,
cluster_id
;
int
num_used_orig
=
*
num_used
;
VP8LHistogram
**
const
orig_histograms
=
orig_histo
->
histograms
;
VP8LHistogram
**
const
histograms
=
image_histo
->
histograms
;
image_histo
->
size
=
0
;
for
(
i
=
0
;
i
<
histo
_size
;
++
i
)
{
assert
(
image_histo
->
max_size
==
orig_histo
->
max_size
)
;
for
(
cluster_id
=
0
,
i
=
0
;
i
<
orig_histo
->
max
_size
;
++
i
)
{
VP8LHistogram
*
const
histo
=
orig_histograms
[
i
];
UpdateHistogramCost
(
histo
);
...
...
@@ -576,10 +595,19 @@ static void HistogramCopyAndAnalyze(
// with no information (when they are skipped because of LZ77).
if
(
!
histo
->
is_used_
[
0
]
&&
!
histo
->
is_used_
[
1
]
&&
!
histo
->
is_used_
[
2
]
&&
!
histo
->
is_used_
[
3
]
&&
!
histo
->
is_used_
[
4
])
{
continue
;
// The first histogram is always used. If an histogram is empty, we set
// its id to be the same as the previous one: this will improve
// compressibility for later LZ77.
assert
(
i
>
0
);
HistogramSetRemoveHistogram
(
image_histo
,
i
,
num_used
);
HistogramSetRemoveHistogram
(
orig_histo
,
i
,
&
num_used_orig
);
histogram_symbols
[
i
]
=
kInvalidHistogramSymbol
;
}
else
{
// Copy histograms from orig_histo[] to image_histo[].
HistogramCopy
(
histo
,
histograms
[
i
]);
histogram_symbols
[
i
]
=
cluster_id
++
;
assert
(
cluster_id
<=
image_histo
->
max_size
);
}
// Copy histograms from orig_histo[] to image_histo[].
HistogramCopy
(
histo
,
histograms
[
image_histo
->
size
++
]);
}
}
...
...
@@ -596,29 +624,33 @@ static void HistogramAnalyzeEntropyBin(VP8LHistogramSet* const image_histo,
// Analyze the dominant (literal, red and blue) entropy costs.
for
(
i
=
0
;
i
<
histo_size
;
++
i
)
{
if
(
histograms
[
i
]
==
NULL
)
continue
;
UpdateDominantCostRange
(
histograms
[
i
],
&
cost_range
);
}
// bin-hash histograms on three of the dominant (literal, red and blue)
// symbol costs and store the resulting bin_id for each histogram.
for
(
i
=
0
;
i
<
histo_size
;
++
i
)
{
// bin_map[i] is not set to a special value as its use will later be guarded
// by another (histograms[i] == NULL).
if
(
histograms
[
i
]
==
NULL
)
continue
;
bin_map
[
i
]
=
GetHistoBinIndex
(
histograms
[
i
],
&
cost_range
,
low_effort
);
}
}
//
Compact image_histo[] by merging some histograms with same bin_id together if
//
it's advantageous
.
//
Merges some histograms with same bin_id together if it's advantageous.
//
Sets the remaining histograms to NULL
.
static
void
HistogramCombineEntropyBin
(
VP8LHistogramSet
*
const
image_histo
,
int
*
num_used
,
const
uint16_t
*
const
clusters
,
uint16_t
*
const
cluster_mappings
,
VP8LHistogram
*
cur_combo
,
const
uint16_t
*
const
bin_map
,
int
bin_map_size
,
int
num_bins
,
int
num_bins
,
double
combine_cost_factor
,
int
low_effort
)
{
VP8LHistogram
**
const
histograms
=
image_histo
->
histograms
;
int
idx
;
// Work in-place: processed histograms are put at the beginning of
// image_histo[]. At the end, we just have to truncate the array.
int
size
=
0
;
struct
{
int16_t
first
;
// position of the histogram that accumulates all
// histograms with the same bin_id
...
...
@@ -631,16 +663,19 @@ static void HistogramCombineEntropyBin(VP8LHistogramSet* const image_histo,
bin_info
[
idx
].
num_combine_failures
=
0
;
}
for
(
idx
=
0
;
idx
<
bin_map_size
;
++
idx
)
{
const
int
bin_id
=
bin_map
[
idx
];
const
int
first
=
bin_info
[
bin_id
].
first
;
assert
(
size
<=
idx
);
// By default, a cluster matches itself.
for
(
idx
=
0
;
idx
<
*
num_used
;
++
idx
)
cluster_mappings
[
idx
]
=
idx
;
for
(
idx
=
0
;
idx
<
image_histo
->
size
;
++
idx
)
{
int
bin_id
,
first
;
if
(
histograms
[
idx
]
==
NULL
)
continue
;
bin_id
=
bin_map
[
idx
];
first
=
bin_info
[
bin_id
].
first
;
if
(
first
==
-
1
)
{
// just move histogram #idx to its final position
histograms
[
size
]
=
histograms
[
idx
];
bin_info
[
bin_id
].
first
=
size
++
;
bin_info
[
bin_id
].
first
=
idx
;
}
else
if
(
low_effort
)
{
HistogramAdd
(
histograms
[
idx
],
histograms
[
first
],
histograms
[
first
]);
HistogramSetRemoveHistogram
(
image_histo
,
idx
,
num_used
);
cluster_mappings
[
clusters
[
idx
]]
=
clusters
[
first
];
}
else
{
// try to merge #idx into #first (both share the same bin_id)
const
double
bit_cost
=
histograms
[
idx
]
->
bit_cost_
;
...
...
@@ -663,19 +698,18 @@ static void HistogramCombineEntropyBin(VP8LHistogramSet* const image_histo,
bin_info
[
bin_id
].
num_combine_failures
>=
max_combine_failures
)
{
// move the (better) merged histogram to its final slot
HistogramSwap
(
&
cur_combo
,
&
histograms
[
first
]);
HistogramSetRemoveHistogram
(
image_histo
,
idx
,
num_used
);
cluster_mappings
[
clusters
[
idx
]]
=
clusters
[
first
];
}
else
{
histograms
[
size
++
]
=
histograms
[
idx
];
++
bin_info
[
bin_id
].
num_combine_failures
;
}
}
else
{
histograms
[
size
++
]
=
histograms
[
idx
];
}
}
}
image_histo
->
size
=
size
;
if
(
low_effort
)
{
// for low_effort case, update the final cost when everything is merged
for
(
idx
=
0
;
idx
<
size
;
++
idx
)
{
for
(
idx
=
0
;
idx
<
image_histo
->
size
;
++
idx
)
{
if
(
histograms
[
idx
]
==
NULL
)
continue
;
UpdateHistogramCost
(
histograms
[
idx
]);
}
}
...
...
@@ -706,16 +740,9 @@ typedef struct {
int
max_size
;
}
HistoQueue
;
static
int
HistoQueueInit
(
HistoQueue
*
const
histo_queue
,
const
int
max_
index
)
{
static
int
HistoQueueInit
(
HistoQueue
*
const
histo_queue
,
const
int
max_
size
)
{
histo_queue
->
size
=
0
;
// max_index^2 for the queue size is safe. If you look at
// HistogramCombineGreedy, and imagine that UpdateQueueFront always pushes
// data to the queue, you insert at most:
// - max_index*(max_index-1)/2 (the first two for loops)
// - max_index - 1 in the last for loop at the first iteration of the while
// loop, max_index - 2 at the second iteration ... therefore
// max_index*(max_index-1)/2 overall too
histo_queue
->
max_size
=
max_index
*
max_index
;
histo_queue
->
max_size
=
max_size
;
// We allocate max_size + 1 because the last element at index "size" is
// used as temporary data (and it could be up to max_size).
histo_queue
->
queue
=
(
HistogramPair
*
)
WebPSafeMalloc
(
...
...
@@ -778,6 +805,8 @@ static double HistoQueuePush(HistoQueue* const histo_queue,
const
VP8LHistogram
*
h2
;
HistogramPair
pair
;
// Stop here if the queue is full.
if
(
histo_queue
->
size
==
histo_queue
->
max_size
)
return
0
.;
assert
(
threshold
<=
0
.);
if
(
idx1
>
idx2
)
{
const
int
tmp
=
idx2
;
...
...
@@ -794,8 +823,6 @@ static double HistoQueuePush(HistoQueue* const histo_queue,
// Do not even consider the pair if it does not improve the entropy.
if
(
pair
.
cost_diff
>=
threshold
)
return
0
.;
// We cannot add more elements than the capacity.
assert
(
histo_queue
->
size
<
histo_queue
->
max_size
);
histo_queue
->
queue
[
histo_queue
->
size
++
]
=
pair
;
HistoQueueUpdateHead
(
histo_queue
,
&
histo_queue
->
queue
[
histo_queue
->
size
-
1
]);
...
...
@@ -806,42 +833,43 @@ static double HistoQueuePush(HistoQueue* const histo_queue,
// Combines histograms by continuously choosing the one with the highest cost
// reduction.
static
int
HistogramCombineGreedy
(
VP8LHistogramSet
*
const
image_histo
)
{
static
int
HistogramCombineGreedy
(
VP8LHistogramSet
*
const
image_histo
,
int
*
const
num_used
)
{
int
ok
=
0
;
int
image_histo_size
=
image_histo
->
size
;
const
int
image_histo_size
=
image_histo
->
size
;
int
i
,
j
;
VP8LHistogram
**
const
histograms
=
image_histo
->
histograms
;
// Indexes of remaining histograms.
int
*
const
clusters
=
(
int
*
)
WebPSafeMalloc
(
image_histo_size
,
sizeof
(
*
clusters
));
// Priority queue of histogram pairs.
HistoQueue
histo_queue
;
if
(
!
HistoQueueInit
(
&
histo_queue
,
image_histo_size
)
||
clusters
==
NULL
)
{
// image_histo_size^2 for the queue size is safe. If you look at
// HistogramCombineGreedy, and imagine that UpdateQueueFront always pushes
// data to the queue, you insert at most:
// - image_histo_size*(image_histo_size-1)/2 (the first two for loops)
// - image_histo_size - 1 in the last for loop at the first iteration of
// the while loop, image_histo_size - 2 at the second iteration ...
// therefore image_histo_size*(image_histo_size-1)/2 overall too
if
(
!
HistoQueueInit
(
&
histo_queue
,
image_histo_size
*
image_histo_size
))
{
goto
End
;
}
for
(
i
=
0
;
i
<
image_histo_size
;
++
i
)
{
// Initialize clusters indexes.
clusters
[
i
]
=
i
;
if
(
image_histo
->
histograms
[
i
]
==
NULL
)
continue
;
for
(
j
=
i
+
1
;
j
<
image_histo_size
;
++
j
)
{
// Initialize positions array.
// Initialize queue.
if
(
image_histo
->
histograms
[
j
]
==
NULL
)
continue
;
HistoQueuePush
(
&
histo_queue
,
histograms
,
i
,
j
,
0
.);
}
}
while
(
image_histo_size
>
1
&&
histo_queue
.
size
>
0
)
{
while
(
histo_queue
.
size
>
0
)
{
const
int
idx1
=
histo_queue
.
queue
[
0
].
idx1
;
const
int
idx2
=
histo_queue
.
queue
[
0
].
idx2
;
HistogramAdd
(
histograms
[
idx2
],
histograms
[
idx1
],
histograms
[
idx1
]);
histograms
[
idx1
]
->
bit_cost_
=
histo_queue
.
queue
[
0
].
cost_combo
;
// Remove merged histogram.
for
(
i
=
0
;
i
+
1
<
image_histo_size
;
++
i
)
{
if
(
clusters
[
i
]
>=
idx2
)
{
clusters
[
i
]
=
clusters
[
i
+
1
];
}
}
--
image_histo_size
;
HistogramSetRemoveHistogram
(
image_histo
,
idx2
,
num_used
);
// Remove pairs intersecting the just combined best pair.
for
(
i
=
0
;
i
<
histo_queue
.
size
;)
{
...
...
@@ -856,24 +884,15 @@ static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo) {
}
// Push new pairs formed with combined histogram to the queue.
for
(
i
=
0
;
i
<
image_histo_size
;
++
i
)
{
if
(
clusters
[
i
]
!=
idx1
)
{
HistoQueuePush
(
&
histo_queue
,
histograms
,
idx1
,
clusters
[
i
],
0
.);
}
}
}
// Move remaining histograms to the beginning of the array.
for
(
i
=
0
;
i
<
image_histo_size
;
++
i
)
{
if
(
i
!=
clusters
[
i
])
{
// swap the two histograms
HistogramSwap
(
&
histograms
[
i
],
&
histograms
[
clusters
[
i
]]);
for
(
i
=
0
;
i
<
image_histo
->
size
;
++
i
)
{
if
(
i
==
idx1
||
image_histo
->
histograms
[
i
]
==
NULL
)
continue
;
HistoQueuePush
(
&
histo_queue
,
image_histo
->
histograms
,
idx1
,
i
,
0
.);
}
}
image_histo
->
size
=
image_histo_size
;
ok
=
1
;
End:
WebPSafeFree
(
clusters
);
HistoQueueClear
(
&
histo_queue
);
return
ok
;
}
...
...
@@ -881,47 +900,69 @@ static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo) {
// Perform histogram aggregation using a stochastic approach.
// 'do_greedy' is set to 1 if a greedy approach needs to be performed
// afterwards, 0 otherwise.
static
int
PairComparison
(
const
void
*
idx1
,
const
void
*
idx2
)
{
// To be used with bsearch: <0 when *idx1<*idx2, >0 if >, 0 when ==.
return
(
*
(
int
*
)
idx1
-
*
(
int
*
)
idx2
);
}
static
int
HistogramCombineStochastic
(
VP8LHistogramSet
*
const
image_histo
,
int
min_cluster_size
,
int
*
const
num_used
,
int
min_cluster_size
,
int
*
const
do_greedy
)
{
int
iter
;
int
j
,
iter
;
uint32_t
seed
=
1
;
int
tries_with_no_success
=
0
;
int
image_histo_size
=
image_histo
->
size
;
const
int
outer_iters
=
image_histo_size
;
const
int
outer_iters
=
*
num_used
;
const
int
num_tries_no_success
=
outer_iters
/
2
;
VP8LHistogram
**
const
histograms
=
image_histo
->
histograms
;
// Priority queue of histogram pairs. Its size of
"kCostHeapSizeSqrt"^2
// Priority queue of histogram pairs. Its size of
'kHistoQueueSize'
// impacts the quality of the compression and the speed: the smaller the
// faster but the worse for the compression.
HistoQueue
histo_queue
;
const
int
kHistoQueueSize
Sqrt
=
3
;
const
int
kHistoQueueSize
=
9
;
int
ok
=
0
;
// mapping from an index in image_histo with no NULL histogram to the full
// blown image_histo.
int
*
mappings
;
if
(
!
HistoQueueInit
(
&
histo_queue
,
kHistoQueueSizeSqrt
))
{
if
(
*
num_used
<
min_cluster_size
)
{
*
do_greedy
=
1
;
return
1
;
}
mappings
=
(
int
*
)
WebPSafeMalloc
(
*
num_used
,
sizeof
(
*
mappings
));
if
(
mappings
==
NULL
||
!
HistoQueueInit
(
&
histo_queue
,
kHistoQueueSize
))
{
goto
End
;
}
// Fill the initial mapping.
for
(
j
=
0
,
iter
=
0
;
iter
<
image_histo
->
size
;
++
iter
)
{
if
(
histograms
[
iter
]
==
NULL
)
continue
;
mappings
[
j
++
]
=
iter
;
}
assert
(
j
==
*
num_used
);
// Collapse similar histograms in 'image_histo'.
++
min_cluster_size
;
for
(
iter
=
0
;
iter
<
outer_iters
&&
image_histo_size
>=
min_cluster_size
&&
++
tries_with_no_success
<
num_tries_no_success
;
for
(
iter
=
0
;
iter
<
outer_iters
&&
*
num_used
>=
min_cluster_size
&&
++
tries_with_no_success
<
num_tries_no_success
;
++
iter
)
{
int
*
mapping_index
;
double
best_cost
=
(
histo_queue
.
size
==
0
)
?
0
.
:
histo_queue
.
queue
[
0
].
cost_diff
;
int
best_idx1
=
-
1
,
best_idx2
=
1
;
int
j
;
const
uint32_t
rand_range
=
(
image_histo_size
-
1
)
*
image_histo_size
;
// image_histo_size / 2 was chosen empirically. Less means faster but worse
const
uint32_t
rand_range
=
(
*
num_used
-
1
)
*
(
*
num_used
);
// (*num_used) / 2 was chosen empirically. Less means faster but worse
// compression.
const
int
num_tries
=
image_histo_size
/
2
;
const
int
num_tries
=
(
*
num_used
)
/
2
;
for
(
j
=
0
;
j
<
num_tries
;
++
j
)
{
// Pick random samples.
for
(
j
=
0
;
*
num_used
>=
2
&&
j
<
num_tries
;
++
j
)
{
double
curr_cost
;
// Choose two different histograms at random and try to combine them.
const
uint32_t
tmp
=
MyRand
(
&
seed
)
%
rand_range
;
const
uint32_t
idx1
=
tmp
/
(
image_histo_size
-
1
);
uint32_t
idx2
=
tmp
%
(
image_histo_size
-
1
);
uint32_t
idx1
=
tmp
/
(
*
num_used
-
1
);
uint32_t
idx2
=
tmp
%
(
*
num_used
-
1
);
if
(
idx2
>=
idx1
)
++
idx2
;
idx1
=
mappings
[
idx1
];
idx2
=
mappings
[
idx2
];
// Calculate cost reduction on combination.
curr_cost
=
...
...
@@ -934,18 +975,21 @@ static int HistogramCombineStochastic(VP8LHistogramSet* const image_histo,
}
if
(
histo_queue
.
size
==
0
)
continue
;
//
Merge the two
best histograms.
//
Get the
best histograms.
best_idx1
=
histo_queue
.
queue
[
0
].
idx1
;
best_idx2
=
histo_queue
.
queue
[
0
].
idx2
;
assert
(
best_idx1
<
best_idx2
);
HistogramAddEval
(
histograms
[
best_idx1
],
histograms
[
best_idx2
],
histograms
[
best_idx1
],
0
);
// Swap the best_idx2 histogram with the last one (which is now unused).
--
image_histo_size
;
if
(
best_idx2
!=
image_histo_size
)
{
HistogramSwap
(
&
histograms
[
image_histo_size
],
&
histograms
[
best_idx2
]);
}
histograms
[
image_histo_size
]
=
NULL
;
// Pop best_idx2 from mappings.
mapping_index
=
(
int
*
)
bsearch
(
&
best_idx2
,
mappings
,
*
num_used
,
sizeof
(
best_idx2
),
&
PairComparison
);
assert
(
mapping_index
!=
NULL
);
memmove
(
mapping_index
,
mapping_index
+
1
,
sizeof
(
*
mapping_index
)
*
((
*
num_used
)
-
(
mapping_index
-
mappings
)
-
1
));
// Merge the histograms and remove best_idx2 from the queue.
HistogramAdd
(
histograms
[
best_idx2
],
histograms
[
best_idx1
],
histograms
[
best_idx1
]);
histograms
[
best_idx1
]
->
bit_cost_
=
histo_queue
.
queue
[
0
].
cost_combo
;
HistogramSetRemoveHistogram
(
image_histo
,
best_idx2
,
num_used
);
// Parse the queue and update each pair that deals with best_idx1,
// best_idx2 or image_histo_size.
for
(
j
=
0
;
j
<
histo_queue
.
size
;)
{
...
...
@@ -968,12 +1012,6 @@ static int HistogramCombineStochastic(VP8LHistogramSet* const image_histo,
p
->
idx2
=
best_idx1
;
do_eval
=
1
;
}
if
(
p
->
idx2
==
image_histo_size
)
{
// No need to re-evaluate here as it does not involve a pair
// containing best_idx1 or best_idx2.
p
->
idx2
=
best_idx2
;
}
assert
(
p
->
idx2
<
image_histo_size
);
// Make sure the index order is respected.
if
(
p
->
idx1
>
p
->
idx2
)
{
const
int
tmp
=
p
->
idx2
;
...
...
@@ -991,15 +1029,14 @@ static int HistogramCombineStochastic(VP8LHistogramSet* const image_histo,
HistoQueueUpdateHead
(
&
histo_queue
,
p
);
++
j
;
}
tries_with_no_success
=
0
;
}
image_histo
->
size
=
image_histo_size
;
*
do_greedy
=
(
image_histo
->
size
<=
min_cluster_size
);
*
do_greedy
=
(
*
num_used
<=
min_cluster_size
);
ok
=
1
;
End:
HistoQueueClear
(
&
histo_queue
);
WebPSafeFree
(
mappings
);
return
ok
;
}
...
...
@@ -1007,23 +1044,29 @@ End:
// Histogram refinement
// Find the best 'out' histogram for each of the 'in' histograms.
// At call-time, 'out' contains the histograms of the clusters.
// Note: we assume that out[]->bit_cost_ is already up-to-date.
static
void
HistogramRemap
(
const
VP8LHistogramSet
*
const
in
,
const
VP8LHistogramSet
*
const
out
,
VP8LHistogramSet
*
const
out
,
uint16_t
*
const
symbols
)
{
int
i
;
VP8LHistogram
**
const
in_histo
=
in
->
histograms
;
VP8LHistogram
**
const
out_histo
=
out
->
histograms
;
const
int
in_size
=
in
->
size
;
const
int
in_size
=
out
->
max_
size
;
const
int
out_size
=
out
->
size
;
if
(
out_size
>
1
)
{
for
(
i
=
0
;
i
<
in_size
;
++
i
)
{
int
best_out
=
0
;
double
best_bits
=
MAX_COST
;
int
k
;
if
(
in_histo
[
i
]
==
NULL
)
{
// Arbitrarily set to the previous value if unused to help future LZ77.
symbols
[
i
]
=
symbols
[
i
-
1
];
continue
;
}
for
(
k
=
0
;
k
<
out_size
;
++
k
)
{
const
double
cur_bits
=
HistogramAddThresh
(
out_histo
[
k
],
in_histo
[
i
],
best_bits
);
double
cur_bits
;
cur_bits
=
HistogramAddThresh
(
out_histo
[
k
],
in_histo
[
i
],
best_bits
);
if
(
k
==
0
||
cur_bits
<
best_bits
)
{
best_bits
=
cur_bits
;
best_out
=
k
;
...
...
@@ -1039,12 +1082,13 @@ static void HistogramRemap(const VP8LHistogramSet* const in,
}
// Recompute each out based on raw and symbols.
for
(
i
=
0
;
i
<
out_size
;
++
i
)
{
HistogramClear
(
out_histo
[
i
]);
}
VP8LHistogramSetClear
(
out
);
out
->
size
=
out_size
;
for
(
i
=
0
;
i
<
in_size
;
++
i
)
{
const
int
idx
=
symbols
[
i
];
int
idx
;
if
(
in_histo
[
i
]
==
NULL
)
continue
;
idx
=
symbols
[
i
];
HistogramAdd
(
in_histo
[
i
],
out_histo
[
idx
],
out_histo
[
idx
]);
}
}
...
...
@@ -1060,6 +1104,70 @@ static double GetCombineCostFactor(int histo_size, int quality) {
return
combine_cost_factor
;
}
// Given a HistogramSet 'set', the mapping of clusters 'cluster_mapping' and the
// current assignment of the cells in 'symbols', merge the clusters and
// assign the smallest possible clusters values.
static
void
OptimizeHistogramSymbols
(
const
VP8LHistogramSet
*
const
set
,
uint16_t
*
const
cluster_mappings
,
int
num_clusters
,
uint16_t
*
const
cluster_mappings_tmp
,
uint16_t
*
const
symbols
)
{
int
i
,
cluster_max
;
int
do_continue
=
1
;
// First, assign the lowest cluster to each pixel.
while
(
do_continue
)
{
do_continue
=
0
;
for
(
i
=
0
;
i
<
num_clusters
;
++
i
)
{
int
k
;
k
=
cluster_mappings
[
i
];
while
(
k
!=
cluster_mappings
[
k
])
{
cluster_mappings
[
k
]
=
cluster_mappings
[
cluster_mappings
[
k
]];
k
=
cluster_mappings
[
k
];
}
if
(
k
!=
cluster_mappings
[
i
])
{
do_continue
=
1
;
cluster_mappings
[
i
]
=
k
;
}
}
}
// Create a mapping from a cluster id to its minimal version.
cluster_max
=
0
;
memset
(
cluster_mappings_tmp
,
0
,
set
->
max_size
*
sizeof
(
*
cluster_mappings_tmp
));
assert
(
cluster_mappings
[
0
]
==
0
);
// Re-map the ids.
for
(
i
=
0
;
i
<
set
->
max_size
;
++
i
)
{
int
cluster
;
if
(
symbols
[
i
]
==
kInvalidHistogramSymbol
)
continue
;
cluster
=
cluster_mappings
[
symbols
[
i
]];
assert
(
symbols
[
i
]
<
num_clusters
);
if
(
cluster
>
0
&&
cluster_mappings_tmp
[
cluster
]
==
0
)
{
++
cluster_max
;
cluster_mappings_tmp
[
cluster
]
=
cluster_max
;
}
symbols
[
i
]
=
cluster_mappings_tmp
[
cluster
];
}
// Make sure all cluster values are used.
cluster_max
=
0
;
for
(
i
=
0
;
i
<
set
->
max_size
;
++
i
)
{
if
(
symbols
[
i
]
==
kInvalidHistogramSymbol
)
continue
;
if
(
symbols
[
i
]
<=
cluster_max
)
continue
;
++
cluster_max
;
assert
(
symbols
[
i
]
==
cluster_max
);
}
}
static
void
RemoveEmptyHistograms
(
VP8LHistogramSet
*
const
image_histo
)
{
uint32_t
size
;
int
i
;
for
(
i
=
0
,
size
=
0
;
i
<
image_histo
->
size
;
++
i
)
{
if
(
image_histo
->
histograms
[
i
]
==
NULL
)
continue
;
image_histo
->
histograms
[
size
++
]
=
image_histo
->
histograms
[
i
];
}
image_histo
->
size
=
size
;
}
int
VP8LGetHistoImageSymbols
(
int
xsize
,
int
ysize
,
const
VP8LBackwardRefs
*
const
refs
,
int
quality
,
int
low_effort
,
...
...
@@ -1078,27 +1186,36 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize,
// maximum quality q==100 (to preserve the compression gains at that level).
const
int
entropy_combine_num_bins
=
low_effort
?
NUM_PARTITIONS
:
BIN_SIZE
;
int
entropy_combine
;
if
(
orig_histo
==
NULL
)
goto
Error
;
uint16_t
*
const
map_tmp
=
WebPSafeMalloc
(
2
*
image_histo_raw_size
,
sizeof
(
map_tmp
));
uint16_t
*
const
cluster_mappings
=
map_tmp
+
image_histo_raw_size
;
int
num_used
=
image_histo_raw_size
;
if
(
orig_histo
==
NULL
||
map_tmp
==
NULL
)
goto
Error
;
// Construct the histograms from backward references.
HistogramBuild
(
xsize
,
histo_bits
,
refs
,
orig_histo
);
// Copies the histograms and computes its bit_cost.
HistogramCopyAndAnalyze
(
orig_histo
,
image_histo
);
// histogram_symbols is optimized
HistogramCopyAndAnalyze
(
orig_histo
,
image_histo
,
&
num_used
,
histogram_symbols
);
entropy_combine
=
(
image_histo
->
size
>
entropy_combine_num_bins
*
2
)
&&
(
quality
<
100
);
(
num_used
>
entropy_combine_num_bins
*
2
)
&&
(
quality
<
100
);
if
(
entropy_combine
)
{
const
int
bin_map_size
=
image_histo
->
size
;
// Reuse histogram_symbols storage. By definition, it's guaranteed to be ok.
uint16_t
*
const
bin_map
=
histogram_symbols
;
uint16_t
*
const
bin_map
=
map_tmp
;
const
double
combine_cost_factor
=
GetCombineCostFactor
(
image_histo_raw_size
,
quality
);
const
uint32_t
num_clusters
=
num_used
;
HistogramAnalyzeEntropyBin
(
image_histo
,
bin_map
,
low_effort
);
// Collapse histograms with similar entropy.
HistogramCombineEntropyBin
(
image_histo
,
tmp_histo
,
bin_map
,
bin_map_size
,
HistogramCombineEntropyBin
(
image_histo
,
&
num_used
,
histogram_symbols
,
cluster_mappings
,
tmp_histo
,
bin_map
,
entropy_combine_num_bins
,
combine_cost_factor
,
low_effort
);
OptimizeHistogramSymbols
(
image_histo
,
cluster_mappings
,
num_clusters
,
map_tmp
,
histogram_symbols
);
}
// Don't combine the histograms using stochastic and greedy heuristics for
...
...
@@ -1108,21 +1225,26 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize,
// cubic ramp between 1 and MAX_HISTO_GREEDY:
const
int
threshold_size
=
(
int
)(
1
+
(
x
*
x
*
x
)
*
(
MAX_HISTO_GREEDY
-
1
));
int
do_greedy
;
if
(
!
HistogramCombineStochastic
(
image_histo
,
threshold_size
,
&
do_greedy
))
{
if
(
!
HistogramCombineStochastic
(
image_histo
,
&
num_used
,
threshold_size
,
&
do_greedy
))
{
goto
Error
;
}
if
(
do_greedy
&&
!
HistogramCombineGreedy
(
image_histo
))
{
goto
Error
;
if
(
do_greedy
)
{
RemoveEmptyHistograms
(
image_histo
);
if
(
!
HistogramCombineGreedy
(
image_histo
,
&
num_used
))
{
goto
Error
;
}
}
}
// TODO(vrabaud): Optimize HistogramRemap for low-effort compression mode.
// Find the optimal map from original histograms to the final ones.
RemoveEmptyHistograms
(
image_histo
);
HistogramRemap
(
orig_histo
,
image_histo
,
histogram_symbols
);
ok
=
1
;
Error:
VP8LFreeHistogramSet
(
orig_histo
);
WebPSafeFree
(
map_tmp
);
return
ok
;
}
thirdparty/libwebp/src/enc/predictor_enc.c
View file @
93f6a065
...
...
@@ -177,12 +177,15 @@ static uint8_t NearLosslessComponent(uint8_t value, uint8_t predict,
}
}
static
WEBP_INLINE
uint8_t
NearLosslessDiff
(
uint8_t
a
,
uint8_t
b
)
{
return
(
uint8_t
)((((
int
)(
a
)
-
(
int
)(
b
)))
&
0xff
);
}
// Quantize every component of the difference between the actual pixel value and
// its prediction to a multiple of a quantization (a power of 2, not larger than
// max_quantization which is a power of 2, smaller than max_diff). Take care if
// value and predict have undergone subtract green, which means that red and
// blue are represented as offsets from green.
#define NEAR_LOSSLESS_DIFF(a, b) (uint8_t)((((int)(a) - (int)(b))) & 0xff)
static
uint32_t
NearLossless
(
uint32_t
value
,
uint32_t
predict
,
int
max_quantization
,
int
max_diff
,
int
used_subtract_green
)
{
...
...
@@ -199,7 +202,7 @@ static uint32_t NearLossless(uint32_t value, uint32_t predict,
}
if
((
value
>>
24
)
==
0
||
(
value
>>
24
)
==
0xff
)
{
// Preserve transparency of fully transparent or fully opaque pixels.
a
=
N
EAR_LOSSLESS_DIFF
(
value
>>
24
,
predict
>>
24
);
a
=
N
earLosslessDiff
(
value
>>
24
,
predict
>>
24
);
}
else
{
a
=
NearLosslessComponent
(
value
>>
24
,
predict
>>
24
,
0xff
,
quantization
);
}
...
...
@@ -212,16 +215,15 @@ static uint32_t NearLossless(uint32_t value, uint32_t predict,
// The amount by which green has been adjusted during quantization. It is
// subtracted from red and blue for compensation, to avoid accumulating two
// quantization errors in them.
green_diff
=
N
EAR_LOSSLESS_DIFF
(
new_green
,
value
>>
8
);
green_diff
=
N
earLosslessDiff
(
new_green
,
value
>>
8
);
}
r
=
NearLosslessComponent
(
N
EAR_LOSSLESS_DIFF
(
value
>>
16
,
green_diff
),
r
=
NearLosslessComponent
(
N
earLosslessDiff
(
value
>>
16
,
green_diff
),
(
predict
>>
16
)
&
0xff
,
0xff
-
new_green
,
quantization
);
b
=
NearLosslessComponent
(
N
EAR_LOSSLESS_DIFF
(
value
,
green_diff
),
b
=
NearLosslessComponent
(
N
earLosslessDiff
(
value
,
green_diff
),
predict
&
0xff
,
0xff
-
new_green
,
quantization
);
return
((
uint32_t
)
a
<<
24
)
|
((
uint32_t
)
r
<<
16
)
|
((
uint32_t
)
g
<<
8
)
|
b
;
}
#undef NEAR_LOSSLESS_DIFF
#endif // (WEBP_NEAR_LOSSLESS == 1)
// Stores the difference between the pixel and its prediction in "out".
...
...
thirdparty/libwebp/src/enc/quant_enc.c
View file @
93f6a065
...
...
@@ -15,6 +15,7 @@
#include <math.h>
#include <stdlib.h> // for abs()
#include "src/dsp/quant.h"
#include "src/enc/vp8i_enc.h"
#include "src/enc/cost_enc.h"
...
...
@@ -977,19 +978,6 @@ static void SwapOut(VP8EncIterator* const it) {
SwapPtr
(
&
it
->
yuv_out_
,
&
it
->
yuv_out2_
);
}
static
score_t
IsFlat
(
const
int16_t
*
levels
,
int
num_blocks
,
score_t
thresh
)
{
score_t
score
=
0
;
while
(
num_blocks
--
>
0
)
{
// TODO(skal): refine positional scoring?
int
i
;
for
(
i
=
1
;
i
<
16
;
++
i
)
{
// omit DC, we're only interested in AC
score
+=
(
levels
[
i
]
!=
0
);
if
(
score
>
thresh
)
return
0
;
}
levels
+=
16
;
}
return
1
;
}
static
void
PickBestIntra16
(
VP8EncIterator
*
const
it
,
VP8ModeScore
*
rd
)
{
const
int
kNumBlocks
=
16
;
VP8SegmentInfo
*
const
dqm
=
&
it
->
enc_
->
dqm_
[
it
->
mb_
->
segment_
];
...
...
thirdparty/libwebp/src/enc/vp8i_enc.h
View file @
93f6a065
...
...
@@ -32,7 +32,7 @@ extern "C" {
// version numbers
#define ENC_MAJ_VERSION 1
#define ENC_MIN_VERSION 0
#define ENC_REV_VERSION
1
#define ENC_REV_VERSION
2
enum
{
MAX_LF_LEVELS
=
64
,
// Maximum loop filter level
MAX_VARIABLE_LEVEL
=
67
,
// last (inclusive) level with variable cost
...
...
thirdparty/libwebp/src/enc/vp8l_enc.c
View file @
93f6a065
...
...
@@ -462,6 +462,7 @@ static int GetHuffBitLengthsAndCodes(
for
(
i
=
0
;
i
<
histogram_image_size
;
++
i
)
{
const
VP8LHistogram
*
const
histo
=
histogram_image
->
histograms
[
i
];
HuffmanTreeCode
*
const
codes
=
&
huffman_codes
[
5
*
i
];
assert
(
histo
!=
NULL
);
for
(
k
=
0
;
k
<
5
;
++
k
)
{
const
int
num_symbols
=
(
k
==
0
)
?
VP8LHistogramNumCodes
(
histo
->
palette_code_bits_
)
:
...
...
thirdparty/libwebp/src/mux/muxi.h
View file @
93f6a065
...
...
@@ -29,7 +29,7 @@ extern "C" {
#define MUX_MAJ_VERSION 1
#define MUX_MIN_VERSION 0
#define MUX_REV_VERSION
1
#define MUX_REV_VERSION
2
// Chunk object.
typedef
struct
WebPChunk
WebPChunk
;
...
...
thirdparty/libwebp/src/utils/bit_writer_utils.c
View file @
93f6a065
...
...
@@ -248,6 +248,7 @@ int VP8LBitWriterClone(const VP8LBitWriter* const src,
dst
->
bits_
=
src
->
bits_
;
dst
->
used_
=
src
->
used_
;
dst
->
error_
=
src
->
error_
;
dst
->
cur_
=
dst
->
buf_
+
current_size
;
return
1
;
}
...
...
thirdparty/libwebp/src/utils/utils.h
View file @
93f6a065
...
...
@@ -107,19 +107,6 @@ static WEBP_INLINE void PutLE32(uint8_t* const data, uint32_t val) {
PutLE16
(
data
+
2
,
(
int
)(
val
>>
16
));
}
// Returns 31 ^ clz(n) = log2(n). This is the default C-implementation, either
// based on table or not. Can be used as fallback if clz() is not available.
#define WEBP_NEED_LOG_TABLE_8BIT
extern
const
uint8_t
WebPLogTable8bit
[
256
];
static
WEBP_INLINE
int
WebPLog2FloorC
(
uint32_t
n
)
{
int
log_value
=
0
;
while
(
n
>=
256
)
{
log_value
+=
8
;
n
>>=
8
;
}
return
log_value
+
WebPLogTable8bit
[
n
];
}
// Returns (int)floor(log2(n)). n must be > 0.
// use GNU builtins where available.
#if defined(__GNUC__) && \
...
...
@@ -138,6 +125,19 @@ static WEBP_INLINE int BitsLog2Floor(uint32_t n) {
return
first_set_bit
;
}
#else // default: use the C-version.
// Returns 31 ^ clz(n) = log2(n). This is the default C-implementation, either
// based on table or not. Can be used as fallback if clz() is not available.
#define WEBP_NEED_LOG_TABLE_8BIT
extern
const
uint8_t
WebPLogTable8bit
[
256
];
static
WEBP_INLINE
int
WebPLog2FloorC
(
uint32_t
n
)
{
int
log_value
=
0
;
while
(
n
>=
256
)
{
log_value
+=
8
;
n
>>=
8
;
}
return
log_value
+
WebPLogTable8bit
[
n
];
}
static
WEBP_INLINE
int
BitsLog2Floor
(
uint32_t
n
)
{
return
WebPLog2FloorC
(
n
);
}
#endif
...
...
thirdparty/libwebp/src/webp/decode.h
View file @
93f6a065
...
...
@@ -42,6 +42,12 @@ WEBP_EXTERN int WebPGetDecoderVersion(void);
// This function will also validate the header, returning true on success,
// false otherwise. '*width' and '*height' are only valid on successful return.
// Pointers 'width' and 'height' can be passed NULL if deemed irrelevant.
// Note: The following chunk sequences (before the raw VP8/VP8L data) are
// considered valid by this function:
// RIFF + VP8(L)
// RIFF + VP8X + (optional chunks) + VP8(L)
// ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.
// VP8(L) <-- Not a valid WebP format: only allowed for internal purpose.
WEBP_EXTERN
int
WebPGetInfo
(
const
uint8_t
*
data
,
size_t
data_size
,
int
*
width
,
int
*
height
);
...
...
@@ -425,6 +431,12 @@ WEBP_EXTERN VP8StatusCode WebPGetFeaturesInternal(
// Returns VP8_STATUS_OK when the features are successfully retrieved. Returns
// VP8_STATUS_NOT_ENOUGH_DATA when more data is needed to retrieve the
// features from headers. Returns error in other cases.
// Note: The following chunk sequences (before the raw VP8/VP8L data) are
// considered valid by this function:
// RIFF + VP8(L)
// RIFF + VP8X + (optional chunks) + VP8(L)
// ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.
// VP8(L) <-- Not a valid WebP format: only allowed for internal purpose.
static
WEBP_INLINE
VP8StatusCode
WebPGetFeatures
(
const
uint8_t
*
data
,
size_t
data_size
,
WebPBitstreamFeatures
*
features
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment