// 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)
// Palette reordering for smaller sum of deltas (and for smaller storage).
staticint PaletteCompareColorsForQsort(constvoid* p1, constvoid* 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); constint 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;
}
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] = {0}; const uint32_t* argb = pic->argb; constint width = pic->width; constint 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;
} elseif (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;
}
// 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. staticint 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.
}
staticvoid 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. if (num_colors > 17) { if (palette[0] == 0) {
--num_colors;
SwapColor(&palette[num_colors], &palette[0]);
}
} 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. staticvoid 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);
}
staticint 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;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.