Path: blob/master/3rdparty/libwebp/src/enc/predictor_enc.c
16349 views
// Copyright 2016 Google Inc. All Rights Reserved.1//2// Use of this source code is governed by a BSD-style license3// that can be found in the COPYING file in the root of the source4// tree. An additional intellectual property rights grant can be found5// in the file PATENTS. All contributing project authors may6// be found in the AUTHORS file in the root of the source tree.7// -----------------------------------------------------------------------------8//9// Image transform methods for lossless encoder.10//11// Authors: Vikas Arora ([email protected])12// Jyrki Alakuijala ([email protected])13// Urvang Joshi ([email protected])14// Vincent Rabaud ([email protected])1516#include "src/dsp/lossless.h"17#include "src/dsp/lossless_common.h"18#include "src/enc/vp8li_enc.h"1920#define MAX_DIFF_COST (1e30f)2122static const float kSpatialPredictorBias = 15.f;23static const int kPredLowEffort = 11;24static const uint32_t kMaskAlpha = 0xff000000;2526// Mostly used to reduce code size + readability27static WEBP_INLINE int GetMin(int a, int b) { return (a > b) ? b : a; }2829//------------------------------------------------------------------------------30// Methods to calculate Entropy (Shannon).3132static float PredictionCostSpatial(const int counts[256], int weight_0,33double exp_val) {34const int significant_symbols = 256 >> 4;35const double exp_decay_factor = 0.6;36double bits = weight_0 * counts[0];37int i;38for (i = 1; i < significant_symbols; ++i) {39bits += exp_val * (counts[i] + counts[256 - i]);40exp_val *= exp_decay_factor;41}42return (float)(-0.1 * bits);43}4445static float PredictionCostSpatialHistogram(const int accumulated[4][256],46const int tile[4][256]) {47int i;48double retval = 0;49for (i = 0; i < 4; ++i) {50const double kExpValue = 0.94;51retval += PredictionCostSpatial(tile[i], 1, kExpValue);52retval += VP8LCombinedShannonEntropy(tile[i], accumulated[i]);53}54return (float)retval;55}5657static WEBP_INLINE void UpdateHisto(int histo_argb[4][256], uint32_t argb) {58++histo_argb[0][argb >> 24];59++histo_argb[1][(argb >> 16) & 0xff];60++histo_argb[2][(argb >> 8) & 0xff];61++histo_argb[3][argb & 0xff];62}6364//------------------------------------------------------------------------------65// Spatial transform functions.6667static WEBP_INLINE void PredictBatch(int mode, int x_start, int y,68int num_pixels, const uint32_t* current,69const uint32_t* upper, uint32_t* out) {70if (x_start == 0) {71if (y == 0) {72// ARGB_BLACK.73VP8LPredictorsSub[0](current, NULL, 1, out);74} else {75// Top one.76VP8LPredictorsSub[2](current, upper, 1, out);77}78++x_start;79++out;80--num_pixels;81}82if (y == 0) {83// Left one.84VP8LPredictorsSub[1](current + x_start, NULL, num_pixels, out);85} else {86VP8LPredictorsSub[mode](current + x_start, upper + x_start, num_pixels,87out);88}89}9091#if (WEBP_NEAR_LOSSLESS == 1)92static WEBP_INLINE int GetMax(int a, int b) { return (a < b) ? b : a; }9394static int MaxDiffBetweenPixels(uint32_t p1, uint32_t p2) {95const int diff_a = abs((int)(p1 >> 24) - (int)(p2 >> 24));96const int diff_r = abs((int)((p1 >> 16) & 0xff) - (int)((p2 >> 16) & 0xff));97const int diff_g = abs((int)((p1 >> 8) & 0xff) - (int)((p2 >> 8) & 0xff));98const int diff_b = abs((int)(p1 & 0xff) - (int)(p2 & 0xff));99return GetMax(GetMax(diff_a, diff_r), GetMax(diff_g, diff_b));100}101102static int MaxDiffAroundPixel(uint32_t current, uint32_t up, uint32_t down,103uint32_t left, uint32_t right) {104const int diff_up = MaxDiffBetweenPixels(current, up);105const int diff_down = MaxDiffBetweenPixels(current, down);106const int diff_left = MaxDiffBetweenPixels(current, left);107const int diff_right = MaxDiffBetweenPixels(current, right);108return GetMax(GetMax(diff_up, diff_down), GetMax(diff_left, diff_right));109}110111static uint32_t AddGreenToBlueAndRed(uint32_t argb) {112const uint32_t green = (argb >> 8) & 0xff;113uint32_t red_blue = argb & 0x00ff00ffu;114red_blue += (green << 16) | green;115red_blue &= 0x00ff00ffu;116return (argb & 0xff00ff00u) | red_blue;117}118119static void MaxDiffsForRow(int width, int stride, const uint32_t* const argb,120uint8_t* const max_diffs, int used_subtract_green) {121uint32_t current, up, down, left, right;122int x;123if (width <= 2) return;124current = argb[0];125right = argb[1];126if (used_subtract_green) {127current = AddGreenToBlueAndRed(current);128right = AddGreenToBlueAndRed(right);129}130// max_diffs[0] and max_diffs[width - 1] are never used.131for (x = 1; x < width - 1; ++x) {132up = argb[-stride + x];133down = argb[stride + x];134left = current;135current = right;136right = argb[x + 1];137if (used_subtract_green) {138up = AddGreenToBlueAndRed(up);139down = AddGreenToBlueAndRed(down);140right = AddGreenToBlueAndRed(right);141}142max_diffs[x] = MaxDiffAroundPixel(current, up, down, left, right);143}144}145146// Quantize the difference between the actual component value and its prediction147// to a multiple of quantization, working modulo 256, taking care not to cross148// a boundary (inclusive upper limit).149static uint8_t NearLosslessComponent(uint8_t value, uint8_t predict,150uint8_t boundary, int quantization) {151const int residual = (value - predict) & 0xff;152const int boundary_residual = (boundary - predict) & 0xff;153const int lower = residual & ~(quantization - 1);154const int upper = lower + quantization;155// Resolve ties towards a value closer to the prediction (i.e. towards lower156// if value comes after prediction and towards upper otherwise).157const int bias = ((boundary - value) & 0xff) < boundary_residual;158if (residual - lower < upper - residual + bias) {159// lower is closer to residual than upper.160if (residual > boundary_residual && lower <= boundary_residual) {161// Halve quantization step to avoid crossing boundary. This midpoint is162// on the same side of boundary as residual because midpoint >= residual163// (since lower is closer than upper) and residual is above the boundary.164return lower + (quantization >> 1);165}166return lower;167} else {168// upper is closer to residual than lower.169if (residual <= boundary_residual && upper > boundary_residual) {170// Halve quantization step to avoid crossing boundary. This midpoint is171// on the same side of boundary as residual because midpoint <= residual172// (since upper is closer than lower) and residual is below the boundary.173return lower + (quantization >> 1);174}175return upper & 0xff;176}177}178179// Quantize every component of the difference between the actual pixel value and180// its prediction to a multiple of a quantization (a power of 2, not larger than181// max_quantization which is a power of 2, smaller than max_diff). Take care if182// value and predict have undergone subtract green, which means that red and183// blue are represented as offsets from green.184#define NEAR_LOSSLESS_DIFF(a, b) (uint8_t)((((int)(a) - (int)(b))) & 0xff)185static uint32_t NearLossless(uint32_t value, uint32_t predict,186int max_quantization, int max_diff,187int used_subtract_green) {188int quantization;189uint8_t new_green = 0;190uint8_t green_diff = 0;191uint8_t a, r, g, b;192if (max_diff <= 2) {193return VP8LSubPixels(value, predict);194}195quantization = max_quantization;196while (quantization >= max_diff) {197quantization >>= 1;198}199if ((value >> 24) == 0 || (value >> 24) == 0xff) {200// Preserve transparency of fully transparent or fully opaque pixels.201a = NEAR_LOSSLESS_DIFF(value >> 24, predict >> 24);202} else {203a = NearLosslessComponent(value >> 24, predict >> 24, 0xff, quantization);204}205g = NearLosslessComponent((value >> 8) & 0xff, (predict >> 8) & 0xff, 0xff,206quantization);207if (used_subtract_green) {208// The green offset will be added to red and blue components during decoding209// to obtain the actual red and blue values.210new_green = ((predict >> 8) + g) & 0xff;211// The amount by which green has been adjusted during quantization. It is212// subtracted from red and blue for compensation, to avoid accumulating two213// quantization errors in them.214green_diff = NEAR_LOSSLESS_DIFF(new_green, value >> 8);215}216r = NearLosslessComponent(NEAR_LOSSLESS_DIFF(value >> 16, green_diff),217(predict >> 16) & 0xff, 0xff - new_green,218quantization);219b = NearLosslessComponent(NEAR_LOSSLESS_DIFF(value, green_diff),220predict & 0xff, 0xff - new_green, quantization);221return ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;222}223#undef NEAR_LOSSLESS_DIFF224#endif // (WEBP_NEAR_LOSSLESS == 1)225226// Stores the difference between the pixel and its prediction in "out".227// In case of a lossy encoding, updates the source image to avoid propagating228// the deviation further to pixels which depend on the current pixel for their229// predictions.230static WEBP_INLINE void GetResidual(231int width, int height, uint32_t* const upper_row,232uint32_t* const current_row, const uint8_t* const max_diffs, int mode,233int x_start, int x_end, int y, int max_quantization, int exact,234int used_subtract_green, uint32_t* const out) {235if (exact) {236PredictBatch(mode, x_start, y, x_end - x_start, current_row, upper_row,237out);238} else {239const VP8LPredictorFunc pred_func = VP8LPredictors[mode];240int x;241for (x = x_start; x < x_end; ++x) {242uint32_t predict;243uint32_t residual;244if (y == 0) {245predict = (x == 0) ? ARGB_BLACK : current_row[x - 1]; // Left.246} else if (x == 0) {247predict = upper_row[x]; // Top.248} else {249predict = pred_func(current_row[x - 1], upper_row + x);250}251#if (WEBP_NEAR_LOSSLESS == 1)252if (max_quantization == 1 || mode == 0 || y == 0 || y == height - 1 ||253x == 0 || x == width - 1) {254residual = VP8LSubPixels(current_row[x], predict);255} else {256residual = NearLossless(current_row[x], predict, max_quantization,257max_diffs[x], used_subtract_green);258// Update the source image.259current_row[x] = VP8LAddPixels(predict, residual);260// x is never 0 here so we do not need to update upper_row like below.261}262#else263(void)max_diffs;264(void)height;265(void)max_quantization;266(void)used_subtract_green;267residual = VP8LSubPixels(current_row[x], predict);268#endif269if ((current_row[x] & kMaskAlpha) == 0) {270// If alpha is 0, cleanup RGB. We can choose the RGB values of the271// residual for best compression. The prediction of alpha itself can be272// non-zero and must be kept though. We choose RGB of the residual to be273// 0.274residual &= kMaskAlpha;275// Update the source image.276current_row[x] = predict & ~kMaskAlpha;277// The prediction for the rightmost pixel in a row uses the leftmost278// pixel279// in that row as its top-right context pixel. Hence if we change the280// leftmost pixel of current_row, the corresponding change must be281// applied282// to upper_row as well where top-right context is being read from.283if (x == 0 && y != 0) upper_row[width] = current_row[0];284}285out[x - x_start] = residual;286}287}288}289290// Returns best predictor and updates the accumulated histogram.291// If max_quantization > 1, assumes that near lossless processing will be292// applied, quantizing residuals to multiples of quantization levels up to293// max_quantization (the actual quantization level depends on smoothness near294// the given pixel).295static int GetBestPredictorForTile(int width, int height,296int tile_x, int tile_y, int bits,297int accumulated[4][256],298uint32_t* const argb_scratch,299const uint32_t* const argb,300int max_quantization,301int exact, int used_subtract_green,302const uint32_t* const modes) {303const int kNumPredModes = 14;304const int start_x = tile_x << bits;305const int start_y = tile_y << bits;306const int tile_size = 1 << bits;307const int max_y = GetMin(tile_size, height - start_y);308const int max_x = GetMin(tile_size, width - start_x);309// Whether there exist columns just outside the tile.310const int have_left = (start_x > 0);311// Position and size of the strip covering the tile and adjacent columns if312// they exist.313const int context_start_x = start_x - have_left;314#if (WEBP_NEAR_LOSSLESS == 1)315const int context_width = max_x + have_left + (max_x < width - start_x);316#endif317const int tiles_per_row = VP8LSubSampleSize(width, bits);318// Prediction modes of the left and above neighbor tiles.319const int left_mode = (tile_x > 0) ?320(modes[tile_y * tiles_per_row + tile_x - 1] >> 8) & 0xff : 0xff;321const int above_mode = (tile_y > 0) ?322(modes[(tile_y - 1) * tiles_per_row + tile_x] >> 8) & 0xff : 0xff;323// The width of upper_row and current_row is one pixel larger than image width324// to allow the top right pixel to point to the leftmost pixel of the next row325// when at the right edge.326uint32_t* upper_row = argb_scratch;327uint32_t* current_row = upper_row + width + 1;328uint8_t* const max_diffs = (uint8_t*)(current_row + width + 1);329float best_diff = MAX_DIFF_COST;330int best_mode = 0;331int mode;332int histo_stack_1[4][256];333int histo_stack_2[4][256];334// Need pointers to be able to swap arrays.335int (*histo_argb)[256] = histo_stack_1;336int (*best_histo)[256] = histo_stack_2;337int i, j;338uint32_t residuals[1 << MAX_TRANSFORM_BITS];339assert(bits <= MAX_TRANSFORM_BITS);340assert(max_x <= (1 << MAX_TRANSFORM_BITS));341342for (mode = 0; mode < kNumPredModes; ++mode) {343float cur_diff;344int relative_y;345memset(histo_argb, 0, sizeof(histo_stack_1));346if (start_y > 0) {347// Read the row above the tile which will become the first upper_row.348// Include a pixel to the left if it exists; include a pixel to the right349// in all cases (wrapping to the leftmost pixel of the next row if it does350// not exist).351memcpy(current_row + context_start_x,352argb + (start_y - 1) * width + context_start_x,353sizeof(*argb) * (max_x + have_left + 1));354}355for (relative_y = 0; relative_y < max_y; ++relative_y) {356const int y = start_y + relative_y;357int relative_x;358uint32_t* tmp = upper_row;359upper_row = current_row;360current_row = tmp;361// Read current_row. Include a pixel to the left if it exists; include a362// pixel to the right in all cases except at the bottom right corner of363// the image (wrapping to the leftmost pixel of the next row if it does364// not exist in the current row).365memcpy(current_row + context_start_x,366argb + y * width + context_start_x,367sizeof(*argb) * (max_x + have_left + (y + 1 < height)));368#if (WEBP_NEAR_LOSSLESS == 1)369if (max_quantization > 1 && y >= 1 && y + 1 < height) {370MaxDiffsForRow(context_width, width, argb + y * width + context_start_x,371max_diffs + context_start_x, used_subtract_green);372}373#endif374375GetResidual(width, height, upper_row, current_row, max_diffs, mode,376start_x, start_x + max_x, y, max_quantization, exact,377used_subtract_green, residuals);378for (relative_x = 0; relative_x < max_x; ++relative_x) {379UpdateHisto(histo_argb, residuals[relative_x]);380}381}382cur_diff = PredictionCostSpatialHistogram(383(const int (*)[256])accumulated, (const int (*)[256])histo_argb);384// Favor keeping the areas locally similar.385if (mode == left_mode) cur_diff -= kSpatialPredictorBias;386if (mode == above_mode) cur_diff -= kSpatialPredictorBias;387388if (cur_diff < best_diff) {389int (*tmp)[256] = histo_argb;390histo_argb = best_histo;391best_histo = tmp;392best_diff = cur_diff;393best_mode = mode;394}395}396397for (i = 0; i < 4; i++) {398for (j = 0; j < 256; j++) {399accumulated[i][j] += best_histo[i][j];400}401}402403return best_mode;404}405406// Converts pixels of the image to residuals with respect to predictions.407// If max_quantization > 1, applies near lossless processing, quantizing408// residuals to multiples of quantization levels up to max_quantization409// (the actual quantization level depends on smoothness near the given pixel).410static void CopyImageWithPrediction(int width, int height,411int bits, uint32_t* const modes,412uint32_t* const argb_scratch,413uint32_t* const argb,414int low_effort, int max_quantization,415int exact, int used_subtract_green) {416const int tiles_per_row = VP8LSubSampleSize(width, bits);417// The width of upper_row and current_row is one pixel larger than image width418// to allow the top right pixel to point to the leftmost pixel of the next row419// when at the right edge.420uint32_t* upper_row = argb_scratch;421uint32_t* current_row = upper_row + width + 1;422uint8_t* current_max_diffs = (uint8_t*)(current_row + width + 1);423#if (WEBP_NEAR_LOSSLESS == 1)424uint8_t* lower_max_diffs = current_max_diffs + width;425#endif426int y;427428for (y = 0; y < height; ++y) {429int x;430uint32_t* const tmp32 = upper_row;431upper_row = current_row;432current_row = tmp32;433memcpy(current_row, argb + y * width,434sizeof(*argb) * (width + (y + 1 < height)));435436if (low_effort) {437PredictBatch(kPredLowEffort, 0, y, width, current_row, upper_row,438argb + y * width);439} else {440#if (WEBP_NEAR_LOSSLESS == 1)441if (max_quantization > 1) {442// Compute max_diffs for the lower row now, because that needs the443// contents of argb for the current row, which we will overwrite with444// residuals before proceeding with the next row.445uint8_t* const tmp8 = current_max_diffs;446current_max_diffs = lower_max_diffs;447lower_max_diffs = tmp8;448if (y + 2 < height) {449MaxDiffsForRow(width, width, argb + (y + 1) * width, lower_max_diffs,450used_subtract_green);451}452}453#endif454for (x = 0; x < width;) {455const int mode =456(modes[(y >> bits) * tiles_per_row + (x >> bits)] >> 8) & 0xff;457int x_end = x + (1 << bits);458if (x_end > width) x_end = width;459GetResidual(width, height, upper_row, current_row, current_max_diffs,460mode, x, x_end, y, max_quantization, exact,461used_subtract_green, argb + y * width + x);462x = x_end;463}464}465}466}467468// Finds the best predictor for each tile, and converts the image to residuals469// with respect to predictions. If near_lossless_quality < 100, applies470// near lossless processing, shaving off more bits of residuals for lower471// qualities.472void VP8LResidualImage(int width, int height, int bits, int low_effort,473uint32_t* const argb, uint32_t* const argb_scratch,474uint32_t* const image, int near_lossless_quality,475int exact, int used_subtract_green) {476const int tiles_per_row = VP8LSubSampleSize(width, bits);477const int tiles_per_col = VP8LSubSampleSize(height, bits);478int tile_y;479int histo[4][256];480const int max_quantization = 1 << VP8LNearLosslessBits(near_lossless_quality);481if (low_effort) {482int i;483for (i = 0; i < tiles_per_row * tiles_per_col; ++i) {484image[i] = ARGB_BLACK | (kPredLowEffort << 8);485}486} else {487memset(histo, 0, sizeof(histo));488for (tile_y = 0; tile_y < tiles_per_col; ++tile_y) {489int tile_x;490for (tile_x = 0; tile_x < tiles_per_row; ++tile_x) {491const int pred = GetBestPredictorForTile(width, height, tile_x, tile_y,492bits, histo, argb_scratch, argb, max_quantization, exact,493used_subtract_green, image);494image[tile_y * tiles_per_row + tile_x] = ARGB_BLACK | (pred << 8);495}496}497}498499CopyImageWithPrediction(width, height, bits, image, argb_scratch, argb,500low_effort, max_quantization, exact,501used_subtract_green);502}503504//------------------------------------------------------------------------------505// Color transform functions.506507static WEBP_INLINE void MultipliersClear(VP8LMultipliers* const m) {508m->green_to_red_ = 0;509m->green_to_blue_ = 0;510m->red_to_blue_ = 0;511}512513static WEBP_INLINE void ColorCodeToMultipliers(uint32_t color_code,514VP8LMultipliers* const m) {515m->green_to_red_ = (color_code >> 0) & 0xff;516m->green_to_blue_ = (color_code >> 8) & 0xff;517m->red_to_blue_ = (color_code >> 16) & 0xff;518}519520static WEBP_INLINE uint32_t MultipliersToColorCode(521const VP8LMultipliers* const m) {522return 0xff000000u |523((uint32_t)(m->red_to_blue_) << 16) |524((uint32_t)(m->green_to_blue_) << 8) |525m->green_to_red_;526}527528static float PredictionCostCrossColor(const int accumulated[256],529const int counts[256]) {530// Favor low entropy, locally and globally.531// Favor small absolute values for PredictionCostSpatial532static const double kExpValue = 2.4;533return VP8LCombinedShannonEntropy(counts, accumulated) +534PredictionCostSpatial(counts, 3, kExpValue);535}536537static float GetPredictionCostCrossColorRed(538const uint32_t* argb, int stride, int tile_width, int tile_height,539VP8LMultipliers prev_x, VP8LMultipliers prev_y, int green_to_red,540const int accumulated_red_histo[256]) {541int histo[256] = { 0 };542float cur_diff;543544VP8LCollectColorRedTransforms(argb, stride, tile_width, tile_height,545green_to_red, histo);546547cur_diff = PredictionCostCrossColor(accumulated_red_histo, histo);548if ((uint8_t)green_to_red == prev_x.green_to_red_) {549cur_diff -= 3; // favor keeping the areas locally similar550}551if ((uint8_t)green_to_red == prev_y.green_to_red_) {552cur_diff -= 3; // favor keeping the areas locally similar553}554if (green_to_red == 0) {555cur_diff -= 3;556}557return cur_diff;558}559560static void GetBestGreenToRed(561const uint32_t* argb, int stride, int tile_width, int tile_height,562VP8LMultipliers prev_x, VP8LMultipliers prev_y, int quality,563const int accumulated_red_histo[256], VP8LMultipliers* const best_tx) {564const int kMaxIters = 4 + ((7 * quality) >> 8); // in range [4..6]565int green_to_red_best = 0;566int iter, offset;567float best_diff = GetPredictionCostCrossColorRed(568argb, stride, tile_width, tile_height, prev_x, prev_y,569green_to_red_best, accumulated_red_histo);570for (iter = 0; iter < kMaxIters; ++iter) {571// ColorTransformDelta is a 3.5 bit fixed point, so 32 is equal to572// one in color computation. Having initial delta here as 1 is sufficient573// to explore the range of (-2, 2).574const int delta = 32 >> iter;575// Try a negative and a positive delta from the best known value.576for (offset = -delta; offset <= delta; offset += 2 * delta) {577const int green_to_red_cur = offset + green_to_red_best;578const float cur_diff = GetPredictionCostCrossColorRed(579argb, stride, tile_width, tile_height, prev_x, prev_y,580green_to_red_cur, accumulated_red_histo);581if (cur_diff < best_diff) {582best_diff = cur_diff;583green_to_red_best = green_to_red_cur;584}585}586}587best_tx->green_to_red_ = green_to_red_best;588}589590static float GetPredictionCostCrossColorBlue(591const uint32_t* argb, int stride, int tile_width, int tile_height,592VP8LMultipliers prev_x, VP8LMultipliers prev_y,593int green_to_blue, int red_to_blue, const int accumulated_blue_histo[256]) {594int histo[256] = { 0 };595float cur_diff;596597VP8LCollectColorBlueTransforms(argb, stride, tile_width, tile_height,598green_to_blue, red_to_blue, histo);599600cur_diff = PredictionCostCrossColor(accumulated_blue_histo, histo);601if ((uint8_t)green_to_blue == prev_x.green_to_blue_) {602cur_diff -= 3; // favor keeping the areas locally similar603}604if ((uint8_t)green_to_blue == prev_y.green_to_blue_) {605cur_diff -= 3; // favor keeping the areas locally similar606}607if ((uint8_t)red_to_blue == prev_x.red_to_blue_) {608cur_diff -= 3; // favor keeping the areas locally similar609}610if ((uint8_t)red_to_blue == prev_y.red_to_blue_) {611cur_diff -= 3; // favor keeping the areas locally similar612}613if (green_to_blue == 0) {614cur_diff -= 3;615}616if (red_to_blue == 0) {617cur_diff -= 3;618}619return cur_diff;620}621622#define kGreenRedToBlueNumAxis 8623#define kGreenRedToBlueMaxIters 7624static void GetBestGreenRedToBlue(625const uint32_t* argb, int stride, int tile_width, int tile_height,626VP8LMultipliers prev_x, VP8LMultipliers prev_y, int quality,627const int accumulated_blue_histo[256],628VP8LMultipliers* const best_tx) {629const int8_t offset[kGreenRedToBlueNumAxis][2] =630{{0, -1}, {0, 1}, {-1, 0}, {1, 0}, {-1, -1}, {-1, 1}, {1, -1}, {1, 1}};631const int8_t delta_lut[kGreenRedToBlueMaxIters] = { 16, 16, 8, 4, 2, 2, 2 };632const int iters =633(quality < 25) ? 1 : (quality > 50) ? kGreenRedToBlueMaxIters : 4;634int green_to_blue_best = 0;635int red_to_blue_best = 0;636int iter;637// Initial value at origin:638float best_diff = GetPredictionCostCrossColorBlue(639argb, stride, tile_width, tile_height, prev_x, prev_y,640green_to_blue_best, red_to_blue_best, accumulated_blue_histo);641for (iter = 0; iter < iters; ++iter) {642const int delta = delta_lut[iter];643int axis;644for (axis = 0; axis < kGreenRedToBlueNumAxis; ++axis) {645const int green_to_blue_cur =646offset[axis][0] * delta + green_to_blue_best;647const int red_to_blue_cur = offset[axis][1] * delta + red_to_blue_best;648const float cur_diff = GetPredictionCostCrossColorBlue(649argb, stride, tile_width, tile_height, prev_x, prev_y,650green_to_blue_cur, red_to_blue_cur, accumulated_blue_histo);651if (cur_diff < best_diff) {652best_diff = cur_diff;653green_to_blue_best = green_to_blue_cur;654red_to_blue_best = red_to_blue_cur;655}656if (quality < 25 && iter == 4) {657// Only axis aligned diffs for lower quality.658break; // next iter.659}660}661if (delta == 2 && green_to_blue_best == 0 && red_to_blue_best == 0) {662// Further iterations would not help.663break; // out of iter-loop.664}665}666best_tx->green_to_blue_ = green_to_blue_best;667best_tx->red_to_blue_ = red_to_blue_best;668}669#undef kGreenRedToBlueMaxIters670#undef kGreenRedToBlueNumAxis671672static VP8LMultipliers GetBestColorTransformForTile(673int tile_x, int tile_y, int bits,674VP8LMultipliers prev_x,675VP8LMultipliers prev_y,676int quality, int xsize, int ysize,677const int accumulated_red_histo[256],678const int accumulated_blue_histo[256],679const uint32_t* const argb) {680const int max_tile_size = 1 << bits;681const int tile_y_offset = tile_y * max_tile_size;682const int tile_x_offset = tile_x * max_tile_size;683const int all_x_max = GetMin(tile_x_offset + max_tile_size, xsize);684const int all_y_max = GetMin(tile_y_offset + max_tile_size, ysize);685const int tile_width = all_x_max - tile_x_offset;686const int tile_height = all_y_max - tile_y_offset;687const uint32_t* const tile_argb = argb + tile_y_offset * xsize688+ tile_x_offset;689VP8LMultipliers best_tx;690MultipliersClear(&best_tx);691692GetBestGreenToRed(tile_argb, xsize, tile_width, tile_height,693prev_x, prev_y, quality, accumulated_red_histo, &best_tx);694GetBestGreenRedToBlue(tile_argb, xsize, tile_width, tile_height,695prev_x, prev_y, quality, accumulated_blue_histo,696&best_tx);697return best_tx;698}699700static void CopyTileWithColorTransform(int xsize, int ysize,701int tile_x, int tile_y,702int max_tile_size,703VP8LMultipliers color_transform,704uint32_t* argb) {705const int xscan = GetMin(max_tile_size, xsize - tile_x);706int yscan = GetMin(max_tile_size, ysize - tile_y);707argb += tile_y * xsize + tile_x;708while (yscan-- > 0) {709VP8LTransformColor(&color_transform, argb, xscan);710argb += xsize;711}712}713714void VP8LColorSpaceTransform(int width, int height, int bits, int quality,715uint32_t* const argb, uint32_t* image) {716const int max_tile_size = 1 << bits;717const int tile_xsize = VP8LSubSampleSize(width, bits);718const int tile_ysize = VP8LSubSampleSize(height, bits);719int accumulated_red_histo[256] = { 0 };720int accumulated_blue_histo[256] = { 0 };721int tile_x, tile_y;722VP8LMultipliers prev_x, prev_y;723MultipliersClear(&prev_y);724MultipliersClear(&prev_x);725for (tile_y = 0; tile_y < tile_ysize; ++tile_y) {726for (tile_x = 0; tile_x < tile_xsize; ++tile_x) {727int y;728const int tile_x_offset = tile_x * max_tile_size;729const int tile_y_offset = tile_y * max_tile_size;730const int all_x_max = GetMin(tile_x_offset + max_tile_size, width);731const int all_y_max = GetMin(tile_y_offset + max_tile_size, height);732const int offset = tile_y * tile_xsize + tile_x;733if (tile_y != 0) {734ColorCodeToMultipliers(image[offset - tile_xsize], &prev_y);735}736prev_x = GetBestColorTransformForTile(tile_x, tile_y, bits,737prev_x, prev_y,738quality, width, height,739accumulated_red_histo,740accumulated_blue_histo,741argb);742image[offset] = MultipliersToColorCode(&prev_x);743CopyTileWithColorTransform(width, height, tile_x_offset, tile_y_offset,744max_tile_size, prev_x, argb);745746// Gather accumulated histogram data.747for (y = tile_y_offset; y < all_y_max; ++y) {748int ix = y * width + tile_x_offset;749const int ix_end = ix + all_x_max - tile_x_offset;750for (; ix < ix_end; ++ix) {751const uint32_t pix = argb[ix];752if (ix >= 2 &&753pix == argb[ix - 2] &&754pix == argb[ix - 1]) {755continue; // repeated pixels are handled by backward references756}757if (ix >= width + 2 &&758argb[ix - 2] == argb[ix - width - 2] &&759argb[ix - 1] == argb[ix - width - 1] &&760pix == argb[ix - width]) {761continue; // repeated pixels are handled by backward references762}763++accumulated_red_histo[(pix >> 16) & 0xff];764++accumulated_blue_histo[(pix >> 0) & 0xff];765}766}767}768}769}770771772