mirror of
https://github.com/webmproject/libwebp.git
synced 2025-06-02 12:14:22 +02:00
Compare commits
6 Commits
e53e213091
...
7191a602b0
Author | SHA1 | Date | |
---|---|---|---|
|
7191a602b0 | ||
|
19696e0a6f | ||
|
89b01eccca | ||
|
52a430a7b6 | ||
|
f8b360c419 | ||
|
eb4f813761 |
@ -65,6 +65,7 @@ Options:
|
||||
(default: 0 100)
|
||||
-crop <x> <y> <w> <h> .. crop picture with the given rectangle
|
||||
-resize <w> <h> ........ resize picture (*after* any cropping)
|
||||
-resize_mode <string> .. one of: up_only, down_only, always (default)
|
||||
-mt .................... use multi-threading if available
|
||||
-low_memory ............ reduce memory usage (slower encoding)
|
||||
-map <int> ............. print map of extra info
|
||||
|
@ -515,6 +515,37 @@ static int WriteWebPWithMetadata(FILE* const out,
|
||||
return (fwrite(webp, webp_size, 1, out) == 1);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Resize
|
||||
|
||||
enum {
|
||||
RESIZE_MODE_DOWN_ONLY,
|
||||
RESIZE_MODE_UP_ONLY,
|
||||
RESIZE_MODE_ALWAYS,
|
||||
RESIZE_MODE_DEFAULT = RESIZE_MODE_ALWAYS
|
||||
};
|
||||
|
||||
static void ApplyResizeMode(const int resize_mode,
|
||||
const WebPPicture* const pic,
|
||||
int* const resize_w, int* const resize_h) {
|
||||
const int src_w = pic->width;
|
||||
const int src_h = pic->height;
|
||||
const int dst_w = *resize_w;
|
||||
const int dst_h = *resize_h;
|
||||
|
||||
if (resize_mode == RESIZE_MODE_DOWN_ONLY) {
|
||||
if ((dst_w == 0 && src_h <= dst_h) ||
|
||||
(dst_h == 0 && src_w <= dst_w) ||
|
||||
(src_w <= dst_w && src_h <= dst_h)) {
|
||||
*resize_w = *resize_h = 0;
|
||||
}
|
||||
} else if (resize_mode == RESIZE_MODE_UP_ONLY) {
|
||||
if (src_w >= dst_w && src_h >= dst_h) {
|
||||
*resize_w = *resize_h = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static int ProgressReport(int percent, const WebPPicture* const picture) {
|
||||
@ -583,6 +614,8 @@ static void HelpLong(void) {
|
||||
" (default: 0 100)\n");
|
||||
printf(" -crop <x> <y> <w> <h> .. crop picture with the given rectangle\n");
|
||||
printf(" -resize <w> <h> ........ resize picture (*after* any cropping)\n");
|
||||
printf(" -resize_mode <string> .. one of: up_only, down_only,"
|
||||
" always (default)\n");
|
||||
printf(" -mt .................... use multi-threading if available\n");
|
||||
printf(" -low_memory ............ reduce memory usage (slower encoding)\n");
|
||||
printf(" -map <int> ............. print map of extra info\n");
|
||||
@ -670,6 +703,7 @@ int main(int argc, const char* argv[]) {
|
||||
uint32_t background_color = 0xffffffu;
|
||||
int crop = 0, crop_x = 0, crop_y = 0, crop_w = 0, crop_h = 0;
|
||||
int resize_w = 0, resize_h = 0;
|
||||
int resize_mode = RESIZE_MODE_DEFAULT;
|
||||
int lossless_preset = 6;
|
||||
int use_lossless_preset = -1; // -1=unset, 0=don't use, 1=use it
|
||||
int show_progress = 0;
|
||||
@ -837,6 +871,18 @@ int main(int argc, const char* argv[]) {
|
||||
} else if (!strcmp(argv[c], "-resize") && c + 2 < argc) {
|
||||
resize_w = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
resize_h = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
} else if (!strcmp(argv[c], "-resize_mode") && c + 1 < argc) {
|
||||
++c;
|
||||
if (!strcmp(argv[c], "down_only")) {
|
||||
resize_mode = RESIZE_MODE_DOWN_ONLY;
|
||||
} else if (!strcmp(argv[c], "up_only")) {
|
||||
resize_mode = RESIZE_MODE_UP_ONLY;
|
||||
} else if (!strcmp(argv[c], "always")) {
|
||||
resize_mode = RESIZE_MODE_ALWAYS;
|
||||
} else {
|
||||
fprintf(stderr, "Error! Unrecognized resize mode: %s\n", argv[c]);
|
||||
goto Error;
|
||||
}
|
||||
#ifndef WEBP_DLL
|
||||
} else if (!strcmp(argv[c], "-noasm")) {
|
||||
VP8GetCPUInfo = NULL;
|
||||
@ -1057,6 +1103,7 @@ int main(int argc, const char* argv[]) {
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
ApplyResizeMode(resize_mode, &picture, &resize_w, &resize_h);
|
||||
if ((resize_w | resize_h) > 0) {
|
||||
WebPPicture picture_no_alpha;
|
||||
if (config.exact) {
|
||||
|
10
man/cwebp.1
10
man/cwebp.1
@ -1,5 +1,5 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.TH CWEBP 1 "September 17, 2024"
|
||||
.TH CWEBP 1 "April 10, 2025"
|
||||
.SH NAME
|
||||
cwebp \- compress an image file to a WebP file
|
||||
.SH SYNOPSIS
|
||||
@ -102,6 +102,14 @@ If either (but not both) of the \fBwidth\fP or \fBheight\fP parameters is 0,
|
||||
the value will be calculated preserving the aspect\-ratio. Note: scaling
|
||||
is applied \fIafter\fP cropping.
|
||||
.TP
|
||||
.BI \-resize_mode " string
|
||||
Specify the behavior of the \fB\-resize\fP option. Possible values are:
|
||||
\fBdown_only\fP, \fBup_only\fP, \fBalways\fP (default). \fBdown_only\fP will
|
||||
use the values specified by \fB\-resize\fP if \fIeither\fP the input width or
|
||||
height are larger than the given dimensions. Similarly, \fBup_only\fP will only
|
||||
resize if \fIeither\fP the input width or height are smaller than the given
|
||||
dimensions.
|
||||
.TP
|
||||
.B \-mt
|
||||
Use multi\-threading for encoding, if possible.
|
||||
.TP
|
||||
|
@ -29,7 +29,7 @@ static int DispatchAlpha_SSE2(const uint8_t* WEBP_RESTRICT alpha,
|
||||
int i, j;
|
||||
const __m128i zero = _mm_setzero_si128();
|
||||
const __m128i alpha_mask = _mm_set1_epi32((int)0xff); // to preserve A
|
||||
const __m128i all_0xff = _mm_set1_epi8(0xff);
|
||||
const __m128i all_0xff = _mm_set1_epi8((char)0xff);
|
||||
__m128i all_alphas16 = all_0xff;
|
||||
__m128i all_alphas8 = all_0xff;
|
||||
|
||||
|
@ -41,7 +41,7 @@ typedef enum {
|
||||
RED,
|
||||
BLUE,
|
||||
ALPHA,
|
||||
DISTANCE,
|
||||
DISTANCE
|
||||
} HistogramIndex;
|
||||
|
||||
// Return the size of the histogram for a given cache_bits.
|
||||
@ -105,7 +105,8 @@ void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits,
|
||||
if (init_arrays) {
|
||||
HistogramClear(p);
|
||||
} else {
|
||||
p->trivial_symbol = 0;
|
||||
int i;
|
||||
for (i = 0; i < 5; ++i) p->trivial_symbol[i] = VP8L_NON_TRIVIAL_SYM;
|
||||
p->bit_cost = 0;
|
||||
memset(p->costs, 0, sizeof(p->costs));
|
||||
memset(p->is_used, 0, sizeof(p->is_used));
|
||||
@ -317,7 +318,7 @@ static uint64_t FinalHuffmanCost(const VP8LStreaks* const stats) {
|
||||
// Get the symbol entropy for the distribution 'population'.
|
||||
// Set 'trivial_sym', if there's only one symbol present in the distribution.
|
||||
static uint64_t PopulationCost(const uint32_t* const population, int length,
|
||||
uint32_t* const trivial_sym,
|
||||
uint16_t* const trivial_sym,
|
||||
uint8_t* const is_used) {
|
||||
VP8LBitEntropy bit_entropy;
|
||||
VP8LStreaks stats;
|
||||
@ -332,13 +333,41 @@ static uint64_t PopulationCost(const uint32_t* const population, int length,
|
||||
return BitsEntropyRefine(&bit_entropy) + FinalHuffmanCost(&stats);
|
||||
}
|
||||
|
||||
static WEBP_INLINE void GetPopulationInfo(const VP8LHistogram* const histo,
|
||||
HistogramIndex index,
|
||||
const uint32_t** population,
|
||||
int* length) {
|
||||
switch (index) {
|
||||
case LITERAL:
|
||||
*population = histo->literal;
|
||||
*length = VP8LHistogramNumCodes(histo->palette_code_bits);
|
||||
break;
|
||||
case RED:
|
||||
*population = histo->red;
|
||||
*length = NUM_LITERAL_CODES;
|
||||
break;
|
||||
case BLUE:
|
||||
*population = histo->blue;
|
||||
*length = NUM_LITERAL_CODES;
|
||||
break;
|
||||
case ALPHA:
|
||||
*population = histo->alpha;
|
||||
*length = NUM_LITERAL_CODES;
|
||||
break;
|
||||
case DISTANCE:
|
||||
*population = histo->distance;
|
||||
*length = NUM_DISTANCE_CODES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// trivial_at_end is 1 if the two histograms only have one element that is
|
||||
// non-zero: both the zero-th one, or both the last one.
|
||||
// 'index' is the index of the symbol in the histogram (literal, red, blue,
|
||||
// alpha, distance).
|
||||
static WEBP_INLINE uint64_t GetCombinedEntropy(
|
||||
const VP8LHistogram* const histo_X, const VP8LHistogram* const histo_Y,
|
||||
HistogramIndex index, int trivial_at_end) {
|
||||
static WEBP_INLINE uint64_t
|
||||
GetCombinedEntropy(const VP8LHistogram* const histo_X,
|
||||
const VP8LHistogram* const histo_Y, HistogramIndex index) {
|
||||
const uint32_t* X;
|
||||
const uint32_t* Y;
|
||||
int length;
|
||||
@ -346,35 +375,18 @@ static WEBP_INLINE uint64_t GetCombinedEntropy(
|
||||
VP8LBitEntropy bit_entropy;
|
||||
const int is_X_used = histo_X->is_used[index];
|
||||
const int is_Y_used = histo_Y->is_used[index];
|
||||
const int is_trivial =
|
||||
histo_X->trivial_symbol[index] != VP8L_NON_TRIVIAL_SYM &&
|
||||
histo_X->trivial_symbol[index] == histo_Y->trivial_symbol[index];
|
||||
|
||||
if (trivial_at_end || !is_X_used || !is_Y_used) {
|
||||
if (is_trivial || !is_X_used || !is_Y_used) {
|
||||
if (is_X_used) return histo_X->costs[index];
|
||||
return histo_Y->costs[index];
|
||||
}
|
||||
assert(is_X_used && is_Y_used);
|
||||
|
||||
if (index == LITERAL) {
|
||||
X = histo_X->literal;
|
||||
Y = histo_Y->literal;
|
||||
length = VP8LHistogramNumCodes(histo_X->palette_code_bits);
|
||||
} else if (index == RED) {
|
||||
X = histo_X->red;
|
||||
Y = histo_Y->red;
|
||||
length = NUM_LITERAL_CODES;
|
||||
} else if (index == BLUE) {
|
||||
X = histo_X->blue;
|
||||
Y = histo_Y->blue;
|
||||
length = NUM_LITERAL_CODES;
|
||||
} else if (index == ALPHA) {
|
||||
X = histo_X->alpha;
|
||||
Y = histo_Y->alpha;
|
||||
length = NUM_LITERAL_CODES;
|
||||
} else {
|
||||
assert(index == DISTANCE);
|
||||
X = histo_X->distance;
|
||||
Y = histo_Y->distance;
|
||||
length = NUM_DISTANCE_CODES;
|
||||
}
|
||||
GetPopulationInfo(histo_X, index, &X, &length);
|
||||
GetPopulationInfo(histo_Y, index, &Y, &length);
|
||||
VP8LGetCombinedEntropyUnrefined(X, Y, length, &bit_entropy, &stats);
|
||||
return BitsEntropyRefine(&bit_entropy) + FinalHuffmanCost(&stats);
|
||||
}
|
||||
@ -411,38 +423,19 @@ static WEBP_INLINE void SaturateAdd(uint64_t a, int64_t* b) {
|
||||
WEBP_NODISCARD static int GetCombinedHistogramEntropy(
|
||||
const VP8LHistogram* const a, const VP8LHistogram* const b,
|
||||
int64_t cost_threshold_in, uint64_t* cost, uint64_t costs[5]) {
|
||||
int trivial_at_end = 0, i;
|
||||
int i;
|
||||
const uint64_t cost_threshold = (uint64_t)cost_threshold_in;
|
||||
assert(a->palette_code_bits == b->palette_code_bits);
|
||||
if (cost_threshold_in <= 0) return 0;
|
||||
*cost = costs[LITERAL] =
|
||||
GetCombinedEntropy(a, b, LITERAL, /*trivial_at_end=*/0);
|
||||
// No need to add the extra cost for lengths as it is a constant that does not
|
||||
// influence the histograms.
|
||||
if (*cost >= cost_threshold) return 0;
|
||||
*cost = 0;
|
||||
|
||||
if (a->trivial_symbol != VP8L_NON_TRIVIAL_SYM &&
|
||||
a->trivial_symbol == b->trivial_symbol) {
|
||||
// A, R and B are all 0 or 0xff.
|
||||
const uint32_t color_a = (a->trivial_symbol >> 24) & 0xff;
|
||||
const uint32_t color_r = (a->trivial_symbol >> 16) & 0xff;
|
||||
const uint32_t color_b = (a->trivial_symbol >> 0) & 0xff;
|
||||
if ((color_a == 0 || color_a == 0xff) &&
|
||||
(color_r == 0 || color_r == 0xff) &&
|
||||
(color_b == 0 || color_b == 0xff)) {
|
||||
trivial_at_end = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 1; i <= 4; ++i) {
|
||||
costs[i] =
|
||||
GetCombinedEntropy(a, b, (HistogramIndex)i,
|
||||
/*trivial_at_end=*/i <= 3 ? trivial_at_end : 0);
|
||||
// No need to add the extra cost for length and distance as it is a constant
|
||||
// that does not influence the histograms.
|
||||
for (i = 0; i < 5; ++i) {
|
||||
costs[i] = GetCombinedEntropy(a, b, (HistogramIndex)i);
|
||||
*cost += costs[i];
|
||||
if (*cost >= cost_threshold) return 0;
|
||||
}
|
||||
// No need to add the extra cost for distances as it is a constant that does
|
||||
// not influence the histograms.
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -450,10 +443,13 @@ WEBP_NODISCARD static int GetCombinedHistogramEntropy(
|
||||
static WEBP_INLINE void HistogramAdd(const VP8LHistogram* const a,
|
||||
const VP8LHistogram* const b,
|
||||
VP8LHistogram* const out) {
|
||||
int i;
|
||||
VP8LHistogramAdd(a, b, out);
|
||||
out->trivial_symbol = (a->trivial_symbol == b->trivial_symbol)
|
||||
? a->trivial_symbol
|
||||
: VP8L_NON_TRIVIAL_SYM;
|
||||
for (i = 0; i < 5; ++i) {
|
||||
out->trivial_symbol[i] = a->trivial_symbol[i] == b->trivial_symbol[i]
|
||||
? a->trivial_symbol[i]
|
||||
: VP8L_NON_TRIVIAL_SYM;
|
||||
}
|
||||
}
|
||||
|
||||
// Performs out = a + b, computing the cost C(a+b) - C(a) - C(b) while comparing
|
||||
@ -533,28 +529,18 @@ static void UpdateDominantCostRange(
|
||||
}
|
||||
|
||||
static void UpdateHistogramCost(VP8LHistogram* const h) {
|
||||
uint32_t alpha_sym, red_sym, blue_sym;
|
||||
const int num_codes = VP8LHistogramNumCodes(h->palette_code_bits);
|
||||
h->costs[ALPHA] = PopulationCost(h->alpha, NUM_LITERAL_CODES, &alpha_sym,
|
||||
&h->is_used[ALPHA]);
|
||||
// No need to add the extra cost as it is a constant that does not influence
|
||||
// the histograms.
|
||||
h->costs[DISTANCE] = PopulationCost(h->distance, NUM_DISTANCE_CODES, NULL,
|
||||
&h->is_used[DISTANCE]);
|
||||
h->costs[LITERAL] =
|
||||
PopulationCost(h->literal, num_codes, NULL, &h->is_used[LITERAL]);
|
||||
h->costs[RED] =
|
||||
PopulationCost(h->red, NUM_LITERAL_CODES, &red_sym, &h->is_used[RED]);
|
||||
h->costs[BLUE] =
|
||||
PopulationCost(h->blue, NUM_LITERAL_CODES, &blue_sym, &h->is_used[BLUE]);
|
||||
int i;
|
||||
// No need to add the extra cost for length and distance as it is a constant
|
||||
// that does not influence the histograms.
|
||||
for (i = 0; i < 5; ++i) {
|
||||
const uint32_t* population;
|
||||
int length;
|
||||
GetPopulationInfo(h, i, &population, &length);
|
||||
h->costs[i] = PopulationCost(population, length, &h->trivial_symbol[i],
|
||||
&h->is_used[i]);
|
||||
}
|
||||
h->bit_cost = h->costs[LITERAL] + h->costs[RED] + h->costs[BLUE] +
|
||||
h->costs[ALPHA] + h->costs[DISTANCE];
|
||||
if ((alpha_sym | red_sym | blue_sym) == VP8L_NON_TRIVIAL_SYM) {
|
||||
h->trivial_symbol = VP8L_NON_TRIVIAL_SYM;
|
||||
} else {
|
||||
h->trivial_symbol =
|
||||
((uint32_t)alpha_sym << 24) | (red_sym << 16) | (blue_sym << 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int GetBinIdForEntropy(uint64_t min, uint64_t max, uint64_t val) {
|
||||
@ -695,16 +681,26 @@ static void HistogramCombineEntropyBin(VP8LHistogramSet* const image_histo,
|
||||
-DivRound((int64_t)bit_cost * combine_cost_factor, 100);
|
||||
if (HistogramAddEval(histograms[first], histograms[idx], cur_combo,
|
||||
bit_cost_thresh)) {
|
||||
const int max_combine_failures = 32;
|
||||
// Try to merge two histograms only if the combo is a trivial one or
|
||||
// the two candidate histograms are already non-trivial.
|
||||
// For some images, 'try_combine' turns out to be false for a lot of
|
||||
// histogram pairs. In that case, we fallback to combining
|
||||
// histograms as usual to avoid increasing the header size.
|
||||
const int try_combine =
|
||||
(cur_combo->trivial_symbol != VP8L_NON_TRIVIAL_SYM) ||
|
||||
((histograms[idx]->trivial_symbol == VP8L_NON_TRIVIAL_SYM) &&
|
||||
(histograms[first]->trivial_symbol == VP8L_NON_TRIVIAL_SYM));
|
||||
const int max_combine_failures = 32;
|
||||
int try_combine =
|
||||
cur_combo->trivial_symbol[RED] != VP8L_NON_TRIVIAL_SYM &&
|
||||
cur_combo->trivial_symbol[BLUE] != VP8L_NON_TRIVIAL_SYM &&
|
||||
cur_combo->trivial_symbol[ALPHA] != VP8L_NON_TRIVIAL_SYM;
|
||||
if (!try_combine) {
|
||||
try_combine =
|
||||
histograms[idx]->trivial_symbol[RED] == VP8L_NON_TRIVIAL_SYM ||
|
||||
histograms[idx]->trivial_symbol[BLUE] == VP8L_NON_TRIVIAL_SYM ||
|
||||
histograms[idx]->trivial_symbol[ALPHA] == VP8L_NON_TRIVIAL_SYM;
|
||||
try_combine &=
|
||||
histograms[first]->trivial_symbol[RED] == VP8L_NON_TRIVIAL_SYM ||
|
||||
histograms[first]->trivial_symbol[BLUE] == VP8L_NON_TRIVIAL_SYM ||
|
||||
histograms[first]->trivial_symbol[ALPHA] == VP8L_NON_TRIVIAL_SYM;
|
||||
}
|
||||
if (try_combine ||
|
||||
bin_info[bin_id].num_combine_failures >= max_combine_failures) {
|
||||
// move the (better) merged histogram to its final slot
|
||||
|
@ -24,7 +24,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
// Not a trivial literal symbol.
|
||||
#define VP8L_NON_TRIVIAL_SYM (0xffffffff)
|
||||
#define VP8L_NON_TRIVIAL_SYM ((uint16_t)(0xffff))
|
||||
|
||||
// A simple container for histograms of data.
|
||||
typedef struct {
|
||||
@ -37,8 +37,9 @@ typedef struct {
|
||||
// Backward reference prefix-code histogram.
|
||||
uint32_t distance[NUM_DISTANCE_CODES];
|
||||
int palette_code_bits;
|
||||
uint32_t trivial_symbol; // True, if histograms for Red, Blue & Alpha
|
||||
// literal symbols are single valued.
|
||||
// Index of the unique value of a histogram if any, VP8L_NON_TRIVIAL_SYM
|
||||
// otherwise.
|
||||
uint16_t trivial_symbol[5];
|
||||
uint64_t bit_cost; // Cached value of total bit cost.
|
||||
// Cached values of entropy costs: literal, red, blue, alpha, distance
|
||||
uint64_t costs[5];
|
||||
|
Loading…
x
Reference in New Issue
Block a user