mirror of
https://github.com/webmproject/libwebp.git
synced 2024-11-20 04:18:26 +01:00
Have the palette code be in its own file.
Change-Id: I099a342effedd9f451c94d00a14aead27079e6cc
This commit is contained in:
parent
e2c85878f6
commit
eac3bd5c53
@ -164,6 +164,7 @@ utils_dec_srcs := \
|
|||||||
src/utils/color_cache_utils.c \
|
src/utils/color_cache_utils.c \
|
||||||
src/utils/filters_utils.c \
|
src/utils/filters_utils.c \
|
||||||
src/utils/huffman_utils.c \
|
src/utils/huffman_utils.c \
|
||||||
|
src/utils/palette.c \
|
||||||
src/utils/quant_levels_dec_utils.c \
|
src/utils/quant_levels_dec_utils.c \
|
||||||
src/utils/random_utils.c \
|
src/utils/random_utils.c \
|
||||||
src/utils/rescaler_utils.c \
|
src/utils/rescaler_utils.c \
|
||||||
|
@ -336,6 +336,7 @@ UTILS_DEC_OBJS = \
|
|||||||
$(DIROBJ)\utils\color_cache_utils.obj \
|
$(DIROBJ)\utils\color_cache_utils.obj \
|
||||||
$(DIROBJ)\utils\filters_utils.obj \
|
$(DIROBJ)\utils\filters_utils.obj \
|
||||||
$(DIROBJ)\utils\huffman_utils.obj \
|
$(DIROBJ)\utils\huffman_utils.obj \
|
||||||
|
$(DIROBJ)\utils\palette.obj \
|
||||||
$(DIROBJ)\utils\quant_levels_dec_utils.obj \
|
$(DIROBJ)\utils\quant_levels_dec_utils.obj \
|
||||||
$(DIROBJ)\utils\rescaler_utils.obj \
|
$(DIROBJ)\utils\rescaler_utils.obj \
|
||||||
$(DIROBJ)\utils\random_utils.obj \
|
$(DIROBJ)\utils\random_utils.obj \
|
||||||
|
@ -173,6 +173,7 @@ model {
|
|||||||
include "color_cache_utils.c"
|
include "color_cache_utils.c"
|
||||||
include "filters_utils.c"
|
include "filters_utils.c"
|
||||||
include "huffman_utils.c"
|
include "huffman_utils.c"
|
||||||
|
include "palette.c"
|
||||||
include "quant_levels_dec_utils.c"
|
include "quant_levels_dec_utils.c"
|
||||||
include "random_utils.c"
|
include "random_utils.c"
|
||||||
include "rescaler_utils.c"
|
include "rescaler_utils.c"
|
||||||
|
@ -276,6 +276,7 @@ UTILS_DEC_OBJS = \
|
|||||||
src/utils/color_cache_utils.o \
|
src/utils/color_cache_utils.o \
|
||||||
src/utils/filters_utils.o \
|
src/utils/filters_utils.o \
|
||||||
src/utils/huffman_utils.o \
|
src/utils/huffman_utils.o \
|
||||||
|
src/utils/palette.o \
|
||||||
src/utils/quant_levels_dec_utils.o \
|
src/utils/quant_levels_dec_utils.o \
|
||||||
src/utils/random_utils.o \
|
src/utils/random_utils.o \
|
||||||
src/utils/rescaler_utils.o \
|
src/utils/rescaler_utils.o \
|
||||||
@ -343,6 +344,7 @@ HDRS = \
|
|||||||
src/utils/filters_utils.h \
|
src/utils/filters_utils.h \
|
||||||
src/utils/huffman_utils.h \
|
src/utils/huffman_utils.h \
|
||||||
src/utils/huffman_encode_utils.h \
|
src/utils/huffman_encode_utils.h \
|
||||||
|
src/utils/palette.h \
|
||||||
src/utils/quant_levels_utils.h \
|
src/utils/quant_levels_utils.h \
|
||||||
src/utils/quant_levels_dec_utils.h \
|
src/utils/quant_levels_dec_utils.h \
|
||||||
src/utils/random_utils.h \
|
src/utils/random_utils.h \
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
#ifndef WEBP_DSP_LOSSLESS_COMMON_H_
|
#ifndef WEBP_DSP_LOSSLESS_COMMON_H_
|
||||||
#define WEBP_DSP_LOSSLESS_COMMON_H_
|
#define WEBP_DSP_LOSSLESS_COMMON_H_
|
||||||
|
|
||||||
#include "src/webp/types.h"
|
#include "src/dsp/cpu.h"
|
||||||
|
|
||||||
#include "src/utils/utils.h"
|
#include "src/utils/utils.h"
|
||||||
|
#include "src/webp/types.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -12,10 +12,10 @@
|
|||||||
// Author: Skal (pascal.massimino@gmail.com)
|
// Author: Skal (pascal.massimino@gmail.com)
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "src/enc/vp8i_enc.h"
|
#include "src/enc/vp8i_enc.h"
|
||||||
#include "src/dsp/dsp.h"
|
|
||||||
#include "src/utils/utils.h"
|
#include "src/utils/utils.h"
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "src/enc/vp8li_enc.h"
|
#include "src/enc/vp8li_enc.h"
|
||||||
#include "src/utils/bit_writer_utils.h"
|
#include "src/utils/bit_writer_utils.h"
|
||||||
#include "src/utils/huffman_encode_utils.h"
|
#include "src/utils/huffman_encode_utils.h"
|
||||||
|
#include "src/utils/palette.h"
|
||||||
#include "src/utils/utils.h"
|
#include "src/utils/utils.h"
|
||||||
#include "src/webp/encode.h"
|
#include "src/webp/encode.h"
|
||||||
#include "src/webp/format_constants.h"
|
#include "src/webp/format_constants.h"
|
||||||
@ -30,298 +31,6 @@
|
|||||||
// Maximum number of histogram images (sub-blocks).
|
// Maximum number of histogram images (sub-blocks).
|
||||||
#define MAX_HUFF_IMAGE_SIZE 2600
|
#define MAX_HUFF_IMAGE_SIZE 2600
|
||||||
|
|
||||||
// Palette reordering for smaller sum of deltas (and for smaller storage).
|
|
||||||
|
|
||||||
static int PaletteCompareColorsForQsort(const void* p1, const void* p2) {
|
|
||||||
const uint32_t a = WebPMemToUint32((uint8_t*)p1);
|
|
||||||
const uint32_t b = WebPMemToUint32((uint8_t*)p2);
|
|
||||||
assert(a != b);
|
|
||||||
return (a < b) ? -1 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static WEBP_INLINE uint32_t PaletteComponentDistance(uint32_t v) {
|
|
||||||
return (v <= 128) ? v : (256 - v);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Computes a value that is related to the entropy created by the
|
|
||||||
// palette entry diff.
|
|
||||||
//
|
|
||||||
// Note that the last & 0xff is a no-operation in the next statement, but
|
|
||||||
// removed by most compilers and is here only for regularity of the code.
|
|
||||||
static WEBP_INLINE uint32_t PaletteColorDistance(uint32_t col1, uint32_t col2) {
|
|
||||||
const uint32_t diff = VP8LSubPixels(col1, col2);
|
|
||||||
const int kMoreWeightForRGBThanForAlpha = 9;
|
|
||||||
uint32_t score;
|
|
||||||
score = PaletteComponentDistance((diff >> 0) & 0xff);
|
|
||||||
score += PaletteComponentDistance((diff >> 8) & 0xff);
|
|
||||||
score += PaletteComponentDistance((diff >> 16) & 0xff);
|
|
||||||
score *= kMoreWeightForRGBThanForAlpha;
|
|
||||||
score += PaletteComponentDistance((diff >> 24) & 0xff);
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
static WEBP_INLINE void SwapColor(uint32_t* const col1, uint32_t* const col2) {
|
|
||||||
const uint32_t tmp = *col1;
|
|
||||||
*col1 = *col2;
|
|
||||||
*col2 = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
static WEBP_INLINE int SearchColorNoIdx(const uint32_t sorted[], uint32_t color,
|
|
||||||
int num_colors) {
|
|
||||||
int low = 0, hi = num_colors;
|
|
||||||
if (sorted[low] == color) return low; // loop invariant: sorted[low] != color
|
|
||||||
while (1) {
|
|
||||||
const int mid = (low + hi) >> 1;
|
|
||||||
if (sorted[mid] == color) {
|
|
||||||
return mid;
|
|
||||||
} else if (sorted[mid] < color) {
|
|
||||||
low = mid;
|
|
||||||
} else {
|
|
||||||
hi = mid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The palette has been sorted by alpha. This function checks if the other
|
|
||||||
// components of the palette have a monotonic development with regards to
|
|
||||||
// position in the palette. If all have monotonic development, there is
|
|
||||||
// no benefit to re-organize them greedily. A monotonic development
|
|
||||||
// would be spotted in green-only situations (like lossy alpha) or gray-scale
|
|
||||||
// images.
|
|
||||||
static int PaletteHasNonMonotonousDeltas(const uint32_t* const palette,
|
|
||||||
int num_colors) {
|
|
||||||
uint32_t predict = 0x000000;
|
|
||||||
int i;
|
|
||||||
uint8_t sign_found = 0x00;
|
|
||||||
for (i = 0; i < num_colors; ++i) {
|
|
||||||
const uint32_t diff = VP8LSubPixels(palette[i], predict);
|
|
||||||
const uint8_t rd = (diff >> 16) & 0xff;
|
|
||||||
const uint8_t gd = (diff >> 8) & 0xff;
|
|
||||||
const uint8_t bd = (diff >> 0) & 0xff;
|
|
||||||
if (rd != 0x00) {
|
|
||||||
sign_found |= (rd < 0x80) ? 1 : 2;
|
|
||||||
}
|
|
||||||
if (gd != 0x00) {
|
|
||||||
sign_found |= (gd < 0x80) ? 8 : 16;
|
|
||||||
}
|
|
||||||
if (bd != 0x00) {
|
|
||||||
sign_found |= (bd < 0x80) ? 64 : 128;
|
|
||||||
}
|
|
||||||
predict = palette[i];
|
|
||||||
}
|
|
||||||
return (sign_found & (sign_found << 1)) != 0; // two consequent signs.
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PaletteSortMinimizeDeltas(const uint32_t* const palette_sorted,
|
|
||||||
int num_colors, uint32_t* const palette) {
|
|
||||||
uint32_t predict = 0x00000000;
|
|
||||||
int i, k;
|
|
||||||
memcpy(palette, palette_sorted, num_colors * sizeof(*palette));
|
|
||||||
if (!PaletteHasNonMonotonousDeltas(palette_sorted, num_colors)) return;
|
|
||||||
// Find greedily always the closest color of the predicted color to minimize
|
|
||||||
// deltas in the palette. This reduces storage needs since the
|
|
||||||
// palette is stored with delta encoding.
|
|
||||||
for (i = 0; i < num_colors; ++i) {
|
|
||||||
int best_ix = i;
|
|
||||||
uint32_t best_score = ~0U;
|
|
||||||
for (k = i; k < num_colors; ++k) {
|
|
||||||
const uint32_t cur_score = PaletteColorDistance(palette[k], predict);
|
|
||||||
if (best_score > cur_score) {
|
|
||||||
best_score = cur_score;
|
|
||||||
best_ix = k;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SwapColor(&palette[best_ix], &palette[i]);
|
|
||||||
predict = palette[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort palette in increasing order and prepare an inverse mapping array.
|
|
||||||
static void PrepareMapToPalette(const uint32_t palette[], uint32_t num_colors,
|
|
||||||
uint32_t sorted[], uint32_t idx_map[]) {
|
|
||||||
uint32_t i;
|
|
||||||
memcpy(sorted, palette, num_colors * sizeof(*sorted));
|
|
||||||
qsort(sorted, num_colors, sizeof(*sorted), PaletteCompareColorsForQsort);
|
|
||||||
for (i = 0; i < num_colors; ++i) {
|
|
||||||
idx_map[SearchColorNoIdx(sorted, palette[i], num_colors)] = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
// Finds the biggest cooccurrence in the matrix.
|
|
||||||
static void CoOccurrenceFindMax(const uint32_t* const cooccurrence,
|
|
||||||
uint32_t num_colors, uint8_t* const c1,
|
|
||||||
uint8_t* const c2) {
|
|
||||||
// Find the index that is most frequently located adjacent to other
|
|
||||||
// (different) indexes.
|
|
||||||
uint32_t best_sum = 0u;
|
|
||||||
uint32_t i, j, best_cooccurrence;
|
|
||||||
*c1 = 0u;
|
|
||||||
for (i = 0; i < num_colors; ++i) {
|
|
||||||
uint32_t sum = 0;
|
|
||||||
for (j = 0; j < num_colors; ++j) sum += cooccurrence[i * num_colors + j];
|
|
||||||
if (sum > best_sum) {
|
|
||||||
best_sum = sum;
|
|
||||||
*c1 = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Find the index that is most frequently found adjacent to *c1.
|
|
||||||
*c2 = 0u;
|
|
||||||
best_cooccurrence = 0u;
|
|
||||||
for (i = 0; i < num_colors; ++i) {
|
|
||||||
if (cooccurrence[*c1 * num_colors + i] > best_cooccurrence) {
|
|
||||||
best_cooccurrence = cooccurrence[*c1 * num_colors + i];
|
|
||||||
*c2 = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(*c1 != *c2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Builds the cooccurrence matrix
|
|
||||||
static int CoOccurrenceBuild(const WebPPicture* const pic,
|
|
||||||
const uint32_t* const palette, uint32_t num_colors,
|
|
||||||
uint32_t* cooccurrence) {
|
|
||||||
uint32_t *lines, *line_top, *line_current, *line_tmp;
|
|
||||||
int x, y;
|
|
||||||
const uint32_t* src = pic->argb;
|
|
||||||
uint32_t prev_pix = ~src[0];
|
|
||||||
uint32_t prev_idx = 0u;
|
|
||||||
uint32_t idx_map[MAX_PALETTE_SIZE] = {0};
|
|
||||||
uint32_t palette_sorted[MAX_PALETTE_SIZE];
|
|
||||||
lines = (uint32_t*)WebPSafeMalloc(2 * pic->width, sizeof(*lines));
|
|
||||||
if (lines == NULL) {
|
|
||||||
return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
|
|
||||||
}
|
|
||||||
line_top = &lines[0];
|
|
||||||
line_current = &lines[pic->width];
|
|
||||||
PrepareMapToPalette(palette, num_colors, palette_sorted, idx_map);
|
|
||||||
for (y = 0; y < pic->height; ++y) {
|
|
||||||
for (x = 0; x < pic->width; ++x) {
|
|
||||||
const uint32_t pix = src[x];
|
|
||||||
if (pix != prev_pix) {
|
|
||||||
prev_idx = idx_map[SearchColorNoIdx(palette_sorted, pix, num_colors)];
|
|
||||||
prev_pix = pix;
|
|
||||||
}
|
|
||||||
line_current[x] = prev_idx;
|
|
||||||
// 4-connectivity is what works best as mentioned in "On the relation
|
|
||||||
// between Memon's and the modified Zeng's palette reordering methods".
|
|
||||||
if (x > 0 && prev_idx != line_current[x - 1]) {
|
|
||||||
const uint32_t left_idx = line_current[x - 1];
|
|
||||||
++cooccurrence[prev_idx * num_colors + left_idx];
|
|
||||||
++cooccurrence[left_idx * num_colors + prev_idx];
|
|
||||||
}
|
|
||||||
if (y > 0 && prev_idx != line_top[x]) {
|
|
||||||
const uint32_t top_idx = line_top[x];
|
|
||||||
++cooccurrence[prev_idx * num_colors + top_idx];
|
|
||||||
++cooccurrence[top_idx * num_colors + prev_idx];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
line_tmp = line_top;
|
|
||||||
line_top = line_current;
|
|
||||||
line_current = line_tmp;
|
|
||||||
src += pic->argb_stride;
|
|
||||||
}
|
|
||||||
WebPSafeFree(lines);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Sum {
|
|
||||||
uint8_t index;
|
|
||||||
uint32_t sum;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
static int PaletteSortModifiedZeng(
|
|
||||||
const WebPPicture* const pic, const uint32_t* const palette_sorted,
|
|
||||||
uint32_t num_colors, uint32_t* const palette) {
|
|
||||||
uint32_t i, j, ind;
|
|
||||||
uint8_t remapping[MAX_PALETTE_SIZE];
|
|
||||||
uint32_t* cooccurrence;
|
|
||||||
struct Sum sums[MAX_PALETTE_SIZE];
|
|
||||||
uint32_t first, last;
|
|
||||||
uint32_t num_sums;
|
|
||||||
// TODO(vrabaud) check whether one color images should use palette or not.
|
|
||||||
if (num_colors <= 1) return 1;
|
|
||||||
// Build the co-occurrence matrix.
|
|
||||||
cooccurrence =
|
|
||||||
(uint32_t*)WebPSafeCalloc(num_colors * num_colors, sizeof(*cooccurrence));
|
|
||||||
if (cooccurrence == NULL) {
|
|
||||||
return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
|
|
||||||
}
|
|
||||||
if (!CoOccurrenceBuild(pic, palette_sorted, num_colors, cooccurrence)) {
|
|
||||||
WebPSafeFree(cooccurrence);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the mapping list with the two best indices.
|
|
||||||
CoOccurrenceFindMax(cooccurrence, num_colors, &remapping[0], &remapping[1]);
|
|
||||||
|
|
||||||
// We need to append and prepend to the list of remapping. To this end, we
|
|
||||||
// actually define the next start/end of the list as indices in a vector (with
|
|
||||||
// a wrap around when the end is reached).
|
|
||||||
first = 0;
|
|
||||||
last = 1;
|
|
||||||
num_sums = num_colors - 2; // -2 because we know the first two values
|
|
||||||
if (num_sums > 0) {
|
|
||||||
// Initialize the sums with the first two remappings and find the best one
|
|
||||||
struct Sum* best_sum = &sums[0];
|
|
||||||
best_sum->index = 0u;
|
|
||||||
best_sum->sum = 0u;
|
|
||||||
for (i = 0, j = 0; i < num_colors; ++i) {
|
|
||||||
if (i == remapping[0] || i == remapping[1]) continue;
|
|
||||||
sums[j].index = i;
|
|
||||||
sums[j].sum = cooccurrence[i * num_colors + remapping[0]] +
|
|
||||||
cooccurrence[i * num_colors + remapping[1]];
|
|
||||||
if (sums[j].sum > best_sum->sum) best_sum = &sums[j];
|
|
||||||
++j;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (num_sums > 0) {
|
|
||||||
const uint8_t best_index = best_sum->index;
|
|
||||||
// Compute delta to know if we need to prepend or append the best index.
|
|
||||||
int32_t delta = 0;
|
|
||||||
const int32_t n = num_colors - num_sums;
|
|
||||||
for (ind = first, j = 0; (ind + j) % num_colors != last + 1; ++j) {
|
|
||||||
const uint16_t l_j = remapping[(ind + j) % num_colors];
|
|
||||||
delta += (n - 1 - 2 * (int32_t)j) *
|
|
||||||
(int32_t)cooccurrence[best_index * num_colors + l_j];
|
|
||||||
}
|
|
||||||
if (delta > 0) {
|
|
||||||
first = (first == 0) ? num_colors - 1 : first - 1;
|
|
||||||
remapping[first] = best_index;
|
|
||||||
} else {
|
|
||||||
++last;
|
|
||||||
remapping[last] = best_index;
|
|
||||||
}
|
|
||||||
// Remove best_sum from sums.
|
|
||||||
*best_sum = sums[num_sums - 1];
|
|
||||||
--num_sums;
|
|
||||||
// Update all the sums and find the best one.
|
|
||||||
best_sum = &sums[0];
|
|
||||||
for (i = 0; i < num_sums; ++i) {
|
|
||||||
sums[i].sum += cooccurrence[best_index * num_colors + sums[i].index];
|
|
||||||
if (sums[i].sum > best_sum->sum) best_sum = &sums[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert((last + 1) % num_colors == first);
|
|
||||||
WebPSafeFree(cooccurrence);
|
|
||||||
|
|
||||||
// Re-map the palette.
|
|
||||||
for (i = 0; i < num_colors; ++i) {
|
|
||||||
palette[i] = palette_sorted[remapping[(first + i) % num_colors]];
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Palette
|
// Palette
|
||||||
|
|
||||||
@ -336,13 +45,6 @@ typedef enum {
|
|||||||
kNumEntropyIx = 6
|
kNumEntropyIx = 6
|
||||||
} EntropyIx;
|
} EntropyIx;
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
kSortedDefault = 0,
|
|
||||||
kMinimizeDelta = 1,
|
|
||||||
kModifiedZeng = 2,
|
|
||||||
kUnusedPalette = 3,
|
|
||||||
} PaletteSorting;
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
kHistoAlpha = 0,
|
kHistoAlpha = 0,
|
||||||
kHistoAlphaPred,
|
kHistoAlphaPred,
|
||||||
@ -586,13 +288,10 @@ static int EncoderAnalyze(VP8LEncoder* const enc,
|
|||||||
assert(pic != NULL && pic->argb != NULL);
|
assert(pic != NULL && pic->argb != NULL);
|
||||||
|
|
||||||
// Check whether a palette is possible.
|
// Check whether a palette is possible.
|
||||||
enc->palette_size_ = WebPGetColorPalette(pic, enc->palette_sorted_);
|
enc->palette_size_ = GetColorPalette(pic, enc->palette_sorted_);
|
||||||
use_palette = (enc->palette_size_ <= MAX_PALETTE_SIZE);
|
use_palette = (enc->palette_size_ <= MAX_PALETTE_SIZE);
|
||||||
if (!use_palette) {
|
if (!use_palette) {
|
||||||
enc->palette_size_ = 0;
|
enc->palette_size_ = 0;
|
||||||
} else {
|
|
||||||
qsort(enc->palette_sorted_, enc->palette_size_,
|
|
||||||
sizeof(*enc->palette_sorted_), PaletteCompareColorsForQsort);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empirical bit sizes.
|
// Empirical bit sizes.
|
||||||
@ -1855,7 +1554,8 @@ static int EncodeStreamHook(void* input, void* data2) {
|
|||||||
} else {
|
} else {
|
||||||
assert(crunch_configs[idx].palette_sorting_type_ == kModifiedZeng);
|
assert(crunch_configs[idx].palette_sorting_type_ == kModifiedZeng);
|
||||||
if (!PaletteSortModifiedZeng(enc->pic_, enc->palette_sorted_,
|
if (!PaletteSortModifiedZeng(enc->pic_, enc->palette_sorted_,
|
||||||
enc->palette_size_, enc->palette_)) {
|
enc->palette_size_, enc->palette_)) {
|
||||||
|
WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,8 @@ COMMON_SOURCES += filters_utils.c
|
|||||||
COMMON_SOURCES += filters_utils.h
|
COMMON_SOURCES += filters_utils.h
|
||||||
COMMON_SOURCES += huffman_utils.c
|
COMMON_SOURCES += huffman_utils.c
|
||||||
COMMON_SOURCES += huffman_utils.h
|
COMMON_SOURCES += huffman_utils.h
|
||||||
|
COMMON_SOURCES += palette.c
|
||||||
|
COMMON_SOURCES += palette.h
|
||||||
COMMON_SOURCES += quant_levels_dec_utils.c
|
COMMON_SOURCES += quant_levels_dec_utils.c
|
||||||
COMMON_SOURCES += quant_levels_dec_utils.h
|
COMMON_SOURCES += quant_levels_dec_utils.h
|
||||||
COMMON_SOURCES += rescaler_utils.c
|
COMMON_SOURCES += rescaler_utils.c
|
||||||
|
377
src/utils/palette.c
Normal file
377
src/utils/palette.c
Normal file
@ -0,0 +1,377 @@
|
|||||||
|
// Copyright 2023 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.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Utilities for palette analysis.
|
||||||
|
//
|
||||||
|
// Author: Vincent Rabaud (vrabaud@google.com)
|
||||||
|
|
||||||
|
#include "src/utils/palette.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "src/dsp/lossless_common.h"
|
||||||
|
#include "src/utils/color_cache_utils.h"
|
||||||
|
#include "src/utils/utils.h"
|
||||||
|
#include "src/webp/encode.h"
|
||||||
|
#include "src/webp/format_constants.h"
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Palette reordering for smaller sum of deltas (and for smaller storage).
|
||||||
|
|
||||||
|
static int PaletteCompareColorsForQsort(const void* p1, const void* p2) {
|
||||||
|
const uint32_t a = WebPMemToUint32((uint8_t*)p1);
|
||||||
|
const uint32_t b = WebPMemToUint32((uint8_t*)p2);
|
||||||
|
assert(a != b);
|
||||||
|
return (a < b) ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static WEBP_INLINE uint32_t PaletteComponentDistance(uint32_t v) {
|
||||||
|
return (v <= 128) ? v : (256 - v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes a value that is related to the entropy created by the
|
||||||
|
// palette entry diff.
|
||||||
|
//
|
||||||
|
// Note that the last & 0xff is a no-operation in the next statement, but
|
||||||
|
// removed by most compilers and is here only for regularity of the code.
|
||||||
|
static WEBP_INLINE uint32_t PaletteColorDistance(uint32_t col1, uint32_t col2) {
|
||||||
|
const uint32_t diff = VP8LSubPixels(col1, col2);
|
||||||
|
const int kMoreWeightForRGBThanForAlpha = 9;
|
||||||
|
uint32_t score;
|
||||||
|
score = PaletteComponentDistance((diff >> 0) & 0xff);
|
||||||
|
score += PaletteComponentDistance((diff >> 8) & 0xff);
|
||||||
|
score += PaletteComponentDistance((diff >> 16) & 0xff);
|
||||||
|
score *= kMoreWeightForRGBThanForAlpha;
|
||||||
|
score += PaletteComponentDistance((diff >> 24) & 0xff);
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
static WEBP_INLINE void SwapColor(uint32_t* const col1, uint32_t* const col2) {
|
||||||
|
const uint32_t tmp = *col1;
|
||||||
|
*col1 = *col2;
|
||||||
|
*col2 = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SearchColorNoIdx(const uint32_t sorted[], uint32_t color, int num_colors) {
|
||||||
|
int low = 0, hi = num_colors;
|
||||||
|
if (sorted[low] == color) return low; // loop invariant: sorted[low] != color
|
||||||
|
while (1) {
|
||||||
|
const int mid = (low + hi) >> 1;
|
||||||
|
if (sorted[mid] == color) {
|
||||||
|
return mid;
|
||||||
|
} else if (sorted[mid] < color) {
|
||||||
|
low = mid;
|
||||||
|
} else {
|
||||||
|
hi = mid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrepareMapToPalette(const uint32_t palette[], uint32_t num_colors,
|
||||||
|
uint32_t sorted[], uint32_t idx_map[]) {
|
||||||
|
uint32_t i;
|
||||||
|
memcpy(sorted, palette, num_colors * sizeof(*sorted));
|
||||||
|
qsort(sorted, num_colors, sizeof(*sorted), PaletteCompareColorsForQsort);
|
||||||
|
for (i = 0; i < num_colors; ++i) {
|
||||||
|
idx_map[SearchColorNoIdx(sorted, palette[i], num_colors)] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#define COLOR_HASH_SIZE (MAX_PALETTE_SIZE * 4)
|
||||||
|
#define COLOR_HASH_RIGHT_SHIFT 22 // 32 - log2(COLOR_HASH_SIZE).
|
||||||
|
|
||||||
|
int GetColorPalette(const WebPPicture* const pic, uint32_t* const palette) {
|
||||||
|
int i;
|
||||||
|
int x, y;
|
||||||
|
int num_colors = 0;
|
||||||
|
uint8_t in_use[COLOR_HASH_SIZE] = {0};
|
||||||
|
uint32_t colors[COLOR_HASH_SIZE];
|
||||||
|
const uint32_t* argb = pic->argb;
|
||||||
|
const int width = pic->width;
|
||||||
|
const int height = pic->height;
|
||||||
|
uint32_t last_pix = ~argb[0]; // so we're sure that last_pix != argb[0]
|
||||||
|
assert(pic != NULL);
|
||||||
|
assert(pic->use_argb);
|
||||||
|
|
||||||
|
for (y = 0; y < height; ++y) {
|
||||||
|
for (x = 0; x < width; ++x) {
|
||||||
|
int key;
|
||||||
|
if (argb[x] == last_pix) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
last_pix = argb[x];
|
||||||
|
key = VP8LHashPix(last_pix, COLOR_HASH_RIGHT_SHIFT);
|
||||||
|
while (1) {
|
||||||
|
if (!in_use[key]) {
|
||||||
|
colors[key] = last_pix;
|
||||||
|
in_use[key] = 1;
|
||||||
|
++num_colors;
|
||||||
|
if (num_colors > MAX_PALETTE_SIZE) {
|
||||||
|
return MAX_PALETTE_SIZE + 1; // Exact count not needed.
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else if (colors[key] == last_pix) {
|
||||||
|
break; // The color is already there.
|
||||||
|
} else {
|
||||||
|
// Some other color sits here, so do linear conflict resolution.
|
||||||
|
++key;
|
||||||
|
key &= (COLOR_HASH_SIZE - 1); // Key mask.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argb += pic->argb_stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (palette != NULL) { // Fill the colors into palette.
|
||||||
|
num_colors = 0;
|
||||||
|
for (i = 0; i < COLOR_HASH_SIZE; ++i) {
|
||||||
|
if (in_use[i]) {
|
||||||
|
palette[num_colors] = colors[i];
|
||||||
|
++num_colors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qsort(palette, num_colors, sizeof(*palette), PaletteCompareColorsForQsort);
|
||||||
|
}
|
||||||
|
return num_colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef COLOR_HASH_SIZE
|
||||||
|
#undef COLOR_HASH_RIGHT_SHIFT
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// The palette has been sorted by alpha. This function checks if the other
|
||||||
|
// components of the palette have a monotonic development with regards to
|
||||||
|
// position in the palette. If all have monotonic development, there is
|
||||||
|
// no benefit to re-organize them greedily. A monotonic development
|
||||||
|
// would be spotted in green-only situations (like lossy alpha) or gray-scale
|
||||||
|
// images.
|
||||||
|
static int PaletteHasNonMonotonousDeltas(const uint32_t* const palette,
|
||||||
|
int num_colors) {
|
||||||
|
uint32_t predict = 0x000000;
|
||||||
|
int i;
|
||||||
|
uint8_t sign_found = 0x00;
|
||||||
|
for (i = 0; i < num_colors; ++i) {
|
||||||
|
const uint32_t diff = VP8LSubPixels(palette[i], predict);
|
||||||
|
const uint8_t rd = (diff >> 16) & 0xff;
|
||||||
|
const uint8_t gd = (diff >> 8) & 0xff;
|
||||||
|
const uint8_t bd = (diff >> 0) & 0xff;
|
||||||
|
if (rd != 0x00) {
|
||||||
|
sign_found |= (rd < 0x80) ? 1 : 2;
|
||||||
|
}
|
||||||
|
if (gd != 0x00) {
|
||||||
|
sign_found |= (gd < 0x80) ? 8 : 16;
|
||||||
|
}
|
||||||
|
if (bd != 0x00) {
|
||||||
|
sign_found |= (bd < 0x80) ? 64 : 128;
|
||||||
|
}
|
||||||
|
predict = palette[i];
|
||||||
|
}
|
||||||
|
return (sign_found & (sign_found << 1)) != 0; // two consequent signs.
|
||||||
|
}
|
||||||
|
|
||||||
|
void PaletteSortMinimizeDeltas(const uint32_t* const palette_sorted,
|
||||||
|
int num_colors, uint32_t* const palette) {
|
||||||
|
uint32_t predict = 0x00000000;
|
||||||
|
int i, k;
|
||||||
|
memcpy(palette, palette_sorted, num_colors * sizeof(*palette));
|
||||||
|
if (!PaletteHasNonMonotonousDeltas(palette_sorted, num_colors)) return;
|
||||||
|
// Find greedily always the closest color of the predicted color to minimize
|
||||||
|
// deltas in the palette. This reduces storage needs since the
|
||||||
|
// palette is stored with delta encoding.
|
||||||
|
for (i = 0; i < num_colors; ++i) {
|
||||||
|
int best_ix = i;
|
||||||
|
uint32_t best_score = ~0U;
|
||||||
|
for (k = i; k < num_colors; ++k) {
|
||||||
|
const uint32_t cur_score = PaletteColorDistance(palette[k], predict);
|
||||||
|
if (best_score > cur_score) {
|
||||||
|
best_score = cur_score;
|
||||||
|
best_ix = k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SwapColor(&palette[best_ix], &palette[i]);
|
||||||
|
predict = palette[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Finds the biggest cooccurrence in the matrix.
|
||||||
|
static void CoOccurrenceFindMax(const uint32_t* const cooccurrence,
|
||||||
|
uint32_t num_colors, uint8_t* const c1,
|
||||||
|
uint8_t* const c2) {
|
||||||
|
// Find the index that is most frequently located adjacent to other
|
||||||
|
// (different) indexes.
|
||||||
|
uint32_t best_sum = 0u;
|
||||||
|
uint32_t i, j, best_cooccurrence;
|
||||||
|
*c1 = 0u;
|
||||||
|
for (i = 0; i < num_colors; ++i) {
|
||||||
|
uint32_t sum = 0;
|
||||||
|
for (j = 0; j < num_colors; ++j) sum += cooccurrence[i * num_colors + j];
|
||||||
|
if (sum > best_sum) {
|
||||||
|
best_sum = sum;
|
||||||
|
*c1 = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Find the index that is most frequently found adjacent to *c1.
|
||||||
|
*c2 = 0u;
|
||||||
|
best_cooccurrence = 0u;
|
||||||
|
for (i = 0; i < num_colors; ++i) {
|
||||||
|
if (cooccurrence[*c1 * num_colors + i] > best_cooccurrence) {
|
||||||
|
best_cooccurrence = cooccurrence[*c1 * num_colors + i];
|
||||||
|
*c2 = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(*c1 != *c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builds the cooccurrence matrix
|
||||||
|
static int CoOccurrenceBuild(const WebPPicture* const pic,
|
||||||
|
const uint32_t* const palette, uint32_t num_colors,
|
||||||
|
uint32_t* cooccurrence) {
|
||||||
|
uint32_t *lines, *line_top, *line_current, *line_tmp;
|
||||||
|
int x, y;
|
||||||
|
const uint32_t* src = pic->argb;
|
||||||
|
uint32_t prev_pix = ~src[0];
|
||||||
|
uint32_t prev_idx = 0u;
|
||||||
|
uint32_t idx_map[MAX_PALETTE_SIZE] = {0};
|
||||||
|
uint32_t palette_sorted[MAX_PALETTE_SIZE];
|
||||||
|
lines = (uint32_t*)WebPSafeMalloc(2 * pic->width, sizeof(*lines));
|
||||||
|
if (lines == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
line_top = &lines[0];
|
||||||
|
line_current = &lines[pic->width];
|
||||||
|
PrepareMapToPalette(palette, num_colors, palette_sorted, idx_map);
|
||||||
|
for (y = 0; y < pic->height; ++y) {
|
||||||
|
for (x = 0; x < pic->width; ++x) {
|
||||||
|
const uint32_t pix = src[x];
|
||||||
|
if (pix != prev_pix) {
|
||||||
|
prev_idx = idx_map[SearchColorNoIdx(palette_sorted, pix, num_colors)];
|
||||||
|
prev_pix = pix;
|
||||||
|
}
|
||||||
|
line_current[x] = prev_idx;
|
||||||
|
// 4-connectivity is what works best as mentioned in "On the relation
|
||||||
|
// between Memon's and the modified Zeng's palette reordering methods".
|
||||||
|
if (x > 0 && prev_idx != line_current[x - 1]) {
|
||||||
|
const uint32_t left_idx = line_current[x - 1];
|
||||||
|
++cooccurrence[prev_idx * num_colors + left_idx];
|
||||||
|
++cooccurrence[left_idx * num_colors + prev_idx];
|
||||||
|
}
|
||||||
|
if (y > 0 && prev_idx != line_top[x]) {
|
||||||
|
const uint32_t top_idx = line_top[x];
|
||||||
|
++cooccurrence[prev_idx * num_colors + top_idx];
|
||||||
|
++cooccurrence[top_idx * num_colors + prev_idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line_tmp = line_top;
|
||||||
|
line_top = line_current;
|
||||||
|
line_current = line_tmp;
|
||||||
|
src += pic->argb_stride;
|
||||||
|
}
|
||||||
|
WebPSafeFree(lines);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Sum {
|
||||||
|
uint8_t index;
|
||||||
|
uint32_t sum;
|
||||||
|
};
|
||||||
|
|
||||||
|
int PaletteSortModifiedZeng(const WebPPicture* const pic,
|
||||||
|
const uint32_t* const palette_in,
|
||||||
|
uint32_t num_colors, uint32_t* const palette) {
|
||||||
|
uint32_t i, j, ind;
|
||||||
|
uint8_t remapping[MAX_PALETTE_SIZE];
|
||||||
|
uint32_t* cooccurrence;
|
||||||
|
struct Sum sums[MAX_PALETTE_SIZE];
|
||||||
|
uint32_t first, last;
|
||||||
|
uint32_t num_sums;
|
||||||
|
// TODO(vrabaud) check whether one color images should use palette or not.
|
||||||
|
if (num_colors <= 1) return 1;
|
||||||
|
// Build the co-occurrence matrix.
|
||||||
|
cooccurrence =
|
||||||
|
(uint32_t*)WebPSafeCalloc(num_colors * num_colors, sizeof(*cooccurrence));
|
||||||
|
if (cooccurrence == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!CoOccurrenceBuild(pic, palette_in, num_colors, cooccurrence)) {
|
||||||
|
WebPSafeFree(cooccurrence);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the mapping list with the two best indices.
|
||||||
|
CoOccurrenceFindMax(cooccurrence, num_colors, &remapping[0], &remapping[1]);
|
||||||
|
|
||||||
|
// We need to append and prepend to the list of remapping. To this end, we
|
||||||
|
// actually define the next start/end of the list as indices in a vector (with
|
||||||
|
// a wrap around when the end is reached).
|
||||||
|
first = 0;
|
||||||
|
last = 1;
|
||||||
|
num_sums = num_colors - 2; // -2 because we know the first two values
|
||||||
|
if (num_sums > 0) {
|
||||||
|
// Initialize the sums with the first two remappings and find the best one
|
||||||
|
struct Sum* best_sum = &sums[0];
|
||||||
|
best_sum->index = 0u;
|
||||||
|
best_sum->sum = 0u;
|
||||||
|
for (i = 0, j = 0; i < num_colors; ++i) {
|
||||||
|
if (i == remapping[0] || i == remapping[1]) continue;
|
||||||
|
sums[j].index = i;
|
||||||
|
sums[j].sum = cooccurrence[i * num_colors + remapping[0]] +
|
||||||
|
cooccurrence[i * num_colors + remapping[1]];
|
||||||
|
if (sums[j].sum > best_sum->sum) best_sum = &sums[j];
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (num_sums > 0) {
|
||||||
|
const uint8_t best_index = best_sum->index;
|
||||||
|
// Compute delta to know if we need to prepend or append the best index.
|
||||||
|
int32_t delta = 0;
|
||||||
|
const int32_t n = num_colors - num_sums;
|
||||||
|
for (ind = first, j = 0; (ind + j) % num_colors != last + 1; ++j) {
|
||||||
|
const uint16_t l_j = remapping[(ind + j) % num_colors];
|
||||||
|
delta += (n - 1 - 2 * (int32_t)j) *
|
||||||
|
(int32_t)cooccurrence[best_index * num_colors + l_j];
|
||||||
|
}
|
||||||
|
if (delta > 0) {
|
||||||
|
first = (first == 0) ? num_colors - 1 : first - 1;
|
||||||
|
remapping[first] = best_index;
|
||||||
|
} else {
|
||||||
|
++last;
|
||||||
|
remapping[last] = best_index;
|
||||||
|
}
|
||||||
|
// Remove best_sum from sums.
|
||||||
|
*best_sum = sums[num_sums - 1];
|
||||||
|
--num_sums;
|
||||||
|
// Update all the sums and find the best one.
|
||||||
|
best_sum = &sums[0];
|
||||||
|
for (i = 0; i < num_sums; ++i) {
|
||||||
|
sums[i].sum += cooccurrence[best_index * num_colors + sums[i].index];
|
||||||
|
if (sums[i].sum > best_sum->sum) best_sum = &sums[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert((last + 1) % num_colors == first);
|
||||||
|
WebPSafeFree(cooccurrence);
|
||||||
|
|
||||||
|
// Re-map the palette.
|
||||||
|
for (i = 0; i < num_colors; ++i) {
|
||||||
|
palette[i] = palette_in[remapping[(first + i) % num_colors]];
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
62
src/utils/palette.h
Normal file
62
src/utils/palette.h
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
// Copyright 2023 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.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Utilities for palette analysis.
|
||||||
|
//
|
||||||
|
// Author: Vincent Rabaud (vrabaud@google.com)
|
||||||
|
|
||||||
|
#ifndef WEBP_UTILS_PALETTE_H_
|
||||||
|
#define WEBP_UTILS_PALETTE_H_
|
||||||
|
|
||||||
|
#include "src/webp/types.h"
|
||||||
|
|
||||||
|
struct WebPPicture;
|
||||||
|
|
||||||
|
// The different ways a palette can be sorted.
|
||||||
|
typedef enum PaletteSorting {
|
||||||
|
kSortedDefault = 0,
|
||||||
|
kMinimizeDelta = 1,
|
||||||
|
kModifiedZeng = 2,
|
||||||
|
kUnusedPalette = 3
|
||||||
|
} PaletteSorting;
|
||||||
|
|
||||||
|
// Returns the index of 'color' in the sorted palette 'sorted' of size
|
||||||
|
// 'num_colors'.
|
||||||
|
int SearchColorNoIdx(const uint32_t sorted[], uint32_t color, int num_colors);
|
||||||
|
|
||||||
|
// Sort palette in increasing order and prepare an inverse mapping array.
|
||||||
|
void PrepareMapToPalette(const uint32_t palette[], uint32_t num_colors,
|
||||||
|
uint32_t sorted[], uint32_t idx_map[]);
|
||||||
|
|
||||||
|
// Returns count of unique colors in 'pic', assuming pic->use_argb is true.
|
||||||
|
// If the unique color count is more than MAX_PALETTE_SIZE, returns
|
||||||
|
// MAX_PALETTE_SIZE+1.
|
||||||
|
// If 'palette' is not NULL and the number of unique colors is less than or
|
||||||
|
// equal to MAX_PALETTE_SIZE, also outputs the actual unique colors into
|
||||||
|
// 'palette' in a sorted order. Note: 'palette' is assumed to be an array
|
||||||
|
// already allocated with at least MAX_PALETTE_SIZE elements.
|
||||||
|
int GetColorPalette(const struct WebPPicture* const pic,
|
||||||
|
uint32_t* const palette);
|
||||||
|
|
||||||
|
// Sorts the color sorted palette in 'palette_sorted'/'num_colors' by
|
||||||
|
// minimizing deltas between consecutive colors and stores it in 'palette'.
|
||||||
|
void PaletteSortMinimizeDeltas(const uint32_t* const palette_sorted,
|
||||||
|
int num_colors, uint32_t* const palette);
|
||||||
|
|
||||||
|
// 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. 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_
|
@ -11,13 +11,13 @@
|
|||||||
//
|
//
|
||||||
// Author: Skal (pascal.massimino@gmail.com)
|
// Author: Skal (pascal.massimino@gmail.com)
|
||||||
|
|
||||||
|
#include "src/utils/utils.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h> // for memcpy()
|
#include <string.h> // for memcpy()
|
||||||
#include "src/webp/decode.h"
|
|
||||||
|
#include "src/utils/palette.h"
|
||||||
#include "src/webp/encode.h"
|
#include "src/webp/encode.h"
|
||||||
#include "src/webp/format_constants.h" // for MAX_PALETTE_SIZE
|
|
||||||
#include "src/utils/color_cache_utils.h"
|
|
||||||
#include "src/utils/utils.h"
|
|
||||||
|
|
||||||
// If PRINT_MEM_INFO is defined, extra info (like total memory used, number of
|
// If PRINT_MEM_INFO is defined, extra info (like total memory used, number of
|
||||||
// alloc/free etc) is printed. For debugging/tuning purpose only (it's slow,
|
// alloc/free etc) is printed. For debugging/tuning purpose only (it's slow,
|
||||||
@ -252,66 +252,10 @@ void WebPCopyPixels(const WebPPicture* const src, WebPPicture* const dst) {
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
#define COLOR_HASH_SIZE (MAX_PALETTE_SIZE * 4)
|
|
||||||
#define COLOR_HASH_RIGHT_SHIFT 22 // 32 - log2(COLOR_HASH_SIZE).
|
|
||||||
|
|
||||||
int WebPGetColorPalette(const WebPPicture* const pic, uint32_t* const palette) {
|
int WebPGetColorPalette(const WebPPicture* const pic, uint32_t* const palette) {
|
||||||
int i;
|
return GetColorPalette(pic, palette);
|
||||||
int x, y;
|
|
||||||
int num_colors = 0;
|
|
||||||
uint8_t in_use[COLOR_HASH_SIZE] = { 0 };
|
|
||||||
uint32_t colors[COLOR_HASH_SIZE];
|
|
||||||
const uint32_t* argb = pic->argb;
|
|
||||||
const int width = pic->width;
|
|
||||||
const int height = pic->height;
|
|
||||||
uint32_t last_pix = ~argb[0]; // so we're sure that last_pix != argb[0]
|
|
||||||
assert(pic != NULL);
|
|
||||||
assert(pic->use_argb);
|
|
||||||
|
|
||||||
for (y = 0; y < height; ++y) {
|
|
||||||
for (x = 0; x < width; ++x) {
|
|
||||||
int key;
|
|
||||||
if (argb[x] == last_pix) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
last_pix = argb[x];
|
|
||||||
key = VP8LHashPix(last_pix, COLOR_HASH_RIGHT_SHIFT);
|
|
||||||
while (1) {
|
|
||||||
if (!in_use[key]) {
|
|
||||||
colors[key] = last_pix;
|
|
||||||
in_use[key] = 1;
|
|
||||||
++num_colors;
|
|
||||||
if (num_colors > MAX_PALETTE_SIZE) {
|
|
||||||
return MAX_PALETTE_SIZE + 1; // Exact count not needed.
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} else if (colors[key] == last_pix) {
|
|
||||||
break; // The color is already there.
|
|
||||||
} else {
|
|
||||||
// Some other color sits here, so do linear conflict resolution.
|
|
||||||
++key;
|
|
||||||
key &= (COLOR_HASH_SIZE - 1); // Key mask.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
argb += pic->argb_stride;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (palette != NULL) { // Fill the colors into palette.
|
|
||||||
num_colors = 0;
|
|
||||||
for (i = 0; i < COLOR_HASH_SIZE; ++i) {
|
|
||||||
if (in_use[i]) {
|
|
||||||
palette[num_colors] = colors[i];
|
|
||||||
++num_colors;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return num_colors;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef COLOR_HASH_SIZE
|
|
||||||
#undef COLOR_HASH_RIGHT_SHIFT
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
#if defined(WEBP_NEED_LOG_TABLE_8BIT)
|
#if defined(WEBP_NEED_LOG_TABLE_8BIT)
|
||||||
|
@ -20,9 +20,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <limits.h>
|
|
||||||
|
|
||||||
#include "src/dsp/dsp.h"
|
|
||||||
#include "src/webp/types.h"
|
#include "src/webp/types.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@ -198,6 +196,7 @@ WEBP_EXTERN void WebPCopyPixels(const struct WebPPicture* const src,
|
|||||||
// MAX_PALETTE_SIZE, also outputs the actual unique colors into 'palette'.
|
// MAX_PALETTE_SIZE, also outputs the actual unique colors into 'palette'.
|
||||||
// Note: 'palette' is assumed to be an array already allocated with at least
|
// Note: 'palette' is assumed to be an array already allocated with at least
|
||||||
// MAX_PALETTE_SIZE elements.
|
// MAX_PALETTE_SIZE elements.
|
||||||
|
// TODO(vrabaud) remove whenever we can break the ABI.
|
||||||
WEBP_EXTERN int WebPGetColorPalette(const struct WebPPicture* const pic,
|
WEBP_EXTERN int WebPGetColorPalette(const struct WebPPicture* const pic,
|
||||||
uint32_t* const palette);
|
uint32_t* const palette);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user