Refactor palette sorting computation.

This will ease the integration of new methods.

Change-Id: Icec3eddaa4ab9030d28ccfe579eb6dc13ded2f02
This commit is contained in:
Vincent Rabaud 2023-07-26 09:16:07 +02:00
parent af7fbfd2d9
commit 1432ebbadb
3 changed files with 66 additions and 44 deletions

View File

@ -267,7 +267,7 @@ typedef struct {
// +2 because we add a palette sorting configuration for kPalette and // +2 because we add a palette sorting configuration for kPalette and
// kPaletteAndSpatial. // kPaletteAndSpatial.
#define CRUNCH_CONFIGS_MAX (kNumEntropyIx + 2) #define CRUNCH_CONFIGS_MAX (kNumEntropyIx + 2 * kPaletteSortingNum)
static int EncoderAnalyze(VP8LEncoder* const enc, static int EncoderAnalyze(VP8LEncoder* const enc,
CrunchConfig crunch_configs[CRUNCH_CONFIGS_MAX], CrunchConfig crunch_configs[CRUNCH_CONFIGS_MAX],
@ -324,20 +324,28 @@ static int EncoderAnalyze(VP8LEncoder* const enc,
// a palette. // a palette.
if ((i != kPalette && i != kPaletteAndSpatial) || use_palette) { if ((i != kPalette && i != kPaletteAndSpatial) || use_palette) {
assert(*crunch_configs_size < CRUNCH_CONFIGS_MAX); assert(*crunch_configs_size < CRUNCH_CONFIGS_MAX);
crunch_configs[(*crunch_configs_size)].entropy_idx_ = i;
if (use_palette && (i == kPalette || i == kPaletteAndSpatial)) { if (use_palette && (i == kPalette || i == kPaletteAndSpatial)) {
crunch_configs[(*crunch_configs_size)].palette_sorting_type_ = for (int sorting_method = 0; sorting_method < kPaletteSortingNum;
kMinimizeDelta; ++sorting_method) {
++*crunch_configs_size; const PaletteSorting typed_sorting_method =
// Also add modified Zeng's method. (PaletteSorting)sorting_method;
// TODO(vrabaud) kSortedDefault should be tested. It is omitted
// for now for backward compatibility.
if (typed_sorting_method == kUnusedPalette ||
typed_sorting_method == kSortedDefault) {
continue;
}
crunch_configs[(*crunch_configs_size)].entropy_idx_ = i;
crunch_configs[(*crunch_configs_size)].palette_sorting_type_ =
typed_sorting_method;
++*crunch_configs_size;
}
} else {
crunch_configs[(*crunch_configs_size)].entropy_idx_ = i; crunch_configs[(*crunch_configs_size)].entropy_idx_ = i;
crunch_configs[(*crunch_configs_size)].palette_sorting_type_ =
kModifiedZeng;
} else {
crunch_configs[(*crunch_configs_size)].palette_sorting_type_ = crunch_configs[(*crunch_configs_size)].palette_sorting_type_ =
kUnusedPalette; kUnusedPalette;
++*crunch_configs_size;
} }
++*crunch_configs_size;
} }
} }
} else { } else {
@ -1544,20 +1552,11 @@ static int EncodeStreamHook(void* input, void* data2) {
// Encode palette // Encode palette
if (enc->use_palette_) { if (enc->use_palette_) {
if (crunch_configs[idx].palette_sorting_type_ == kSortedDefault) { if (!PaletteSort(crunch_configs[idx].palette_sorting_type_, enc->pic_,
// Nothing to do, we have already sorted the palette. enc->palette_sorted_, enc->palette_size_,
memcpy(enc->palette_, enc->palette_sorted_, enc->palette_)) {
enc->palette_size_ * sizeof(*enc->palette_)); WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
} else if (crunch_configs[idx].palette_sorting_type_ == kMinimizeDelta) { goto Error;
PaletteSortMinimizeDeltas(enc->palette_sorted_, enc->palette_size_,
enc->palette_);
} else {
assert(crunch_configs[idx].palette_sorting_type_ == kModifiedZeng);
if (!PaletteSortModifiedZeng(enc->pic_, enc->palette_sorted_,
enc->palette_size_, enc->palette_)) {
WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
goto Error;
}
} }
percent_range = remaining_percent / 4; percent_range = remaining_percent / 4;
if (!EncodePalette(bw, low_effort, enc, percent_range, &percent)) { if (!EncodePalette(bw, low_effort, enc, percent_range, &percent)) {

View File

@ -182,8 +182,8 @@ static int PaletteHasNonMonotonousDeltas(const uint32_t* const palette,
return (sign_found & (sign_found << 1)) != 0; // two consequent signs. return (sign_found & (sign_found << 1)) != 0; // two consequent signs.
} }
void PaletteSortMinimizeDeltas(const uint32_t* const palette_sorted, static void PaletteSortMinimizeDeltas(const uint32_t* const palette_sorted,
int num_colors, uint32_t* const palette) { int num_colors, uint32_t* const palette) {
uint32_t predict = 0x00000000; uint32_t predict = 0x00000000;
int i, k; int i, k;
memcpy(palette, palette_sorted, num_colors * sizeof(*palette)); memcpy(palette, palette_sorted, num_colors * sizeof(*palette));
@ -293,9 +293,10 @@ struct Sum {
uint32_t sum; uint32_t sum;
}; };
int PaletteSortModifiedZeng(const WebPPicture* const pic, static int PaletteSortModifiedZeng(const WebPPicture* const pic,
const uint32_t* const palette_in, const uint32_t* const palette_in,
uint32_t num_colors, uint32_t* const palette) { uint32_t num_colors,
uint32_t* const palette) {
uint32_t i, j, ind; uint32_t i, j, ind;
uint8_t remapping[MAX_PALETTE_SIZE]; uint8_t remapping[MAX_PALETTE_SIZE];
uint32_t* cooccurrence; uint32_t* cooccurrence;
@ -375,3 +376,27 @@ int PaletteSortModifiedZeng(const WebPPicture* const pic,
} }
return 1; return 1;
} }
// -----------------------------------------------------------------------------
int PaletteSort(PaletteSorting method, const struct WebPPicture* const pic,
const uint32_t* const palette_sorted, uint32_t num_colors,
uint32_t* const palette) {
switch (method) {
case kSortedDefault:
// Nothing to do, we have already sorted the palette.
memcpy(palette, palette_sorted, num_colors * sizeof(*palette));
return 1;
case kMinimizeDelta:
PaletteSortMinimizeDeltas(palette_sorted, num_colors, palette);
return 1;
case kModifiedZeng:
return PaletteSortModifiedZeng(pic, palette_sorted, num_colors, palette);
case kUnusedPalette:
case kPaletteSortingNum:
break;
}
assert(0);
return 0;
}

View File

@ -21,9 +21,15 @@ struct WebPPicture;
// The different ways a palette can be sorted. // The different ways a palette can be sorted.
typedef enum PaletteSorting { typedef enum PaletteSorting {
kSortedDefault = 0, kSortedDefault = 0,
// Sorts by minimizing L1 deltas between consecutive colors, giving more
// weight to RGB colors.
kMinimizeDelta = 1, kMinimizeDelta = 1,
// Implements the modified Zeng method from "A Survey on Palette Reordering
// Methods for Improving the Compression of Color-Indexed Images" by Armando
// J. Pinho and Antonio J. R. Neves.
kModifiedZeng = 2, kModifiedZeng = 2,
kUnusedPalette = 3 kUnusedPalette = 3,
kPaletteSortingNum = 4
} PaletteSorting; } PaletteSorting;
// Returns the index of 'color' in the sorted palette 'sorted' of size // Returns the index of 'color' in the sorted palette 'sorted' of size
@ -44,19 +50,11 @@ void PrepareMapToPalette(const uint32_t palette[], uint32_t num_colors,
int GetColorPalette(const struct WebPPicture* const pic, int GetColorPalette(const struct WebPPicture* const pic,
uint32_t* const palette); uint32_t* const palette);
// Sorts the color sorted palette in 'palette_sorted'/'num_colors' by // Sorts the palette according to the criterion defined by 'method'.
// minimizing deltas between consecutive colors and stores it in 'palette'. // 'palette_sorted' is the input palette sorted lexicographically, as done in
void PaletteSortMinimizeDeltas(const uint32_t* const palette_sorted, // PrepareMapToPalette. Returns 0 on memory allocation error.
int num_colors, uint32_t* const palette); int PaletteSort(PaletteSorting method, const struct WebPPicture* pic,
const uint32_t* palette_sorted, uint32_t num_colors,
// Implements the modified Zeng method from "A Survey on Palette Reordering uint32_t* palette);
// Methods for Improving the Compression of Color-Indexed Images" by Armando J.
// Pinho and Antonio J. R. Neves. The palette defined by 'palette_in' and
// 'num_colors' is sorted using information from 'pic' and output in
// 'palette'.
// Returns 0 on memory allocation error.
int PaletteSortModifiedZeng(const struct WebPPicture* const pic,
const uint32_t* const palette_in,
uint32_t num_colors, uint32_t* const palette);
#endif // WEBP_UTILS_PALETTE_H_ #endif // WEBP_UTILS_PALETTE_H_