| Index: third_party/libwebp/enc/analysis.c
|
| diff --git a/third_party/libwebp/enc/analysis.c b/third_party/libwebp/enc/analysis.c
|
| index 4ff3edd2a73c9fadb6ba5702fab66de1119b8e6f..7d4cfdc190fa5723a9b42ec05986b01aaae1dbcf 100644
|
| --- a/third_party/libwebp/enc/analysis.c
|
| +++ b/third_party/libwebp/enc/analysis.c
|
| @@ -19,10 +19,6 @@
|
| #include "./cost.h"
|
| #include "../utils/utils.h"
|
|
|
| -#if defined(__cplusplus) || defined(c_plusplus)
|
| -extern "C" {
|
| -#endif
|
| -
|
| #define MAX_ITERS_K_MEANS 6
|
|
|
| //------------------------------------------------------------------------------
|
| @@ -55,6 +51,7 @@ static void SmoothSegmentMap(VP8Encoder* const enc) {
|
| for (n = 0; n < NUM_MB_SEGMENTS; ++n) {
|
| if (cnt[n] >= majority_cnt_3_x_3_grid) {
|
| majority_seg = n;
|
| + break;
|
| }
|
| }
|
| tmp[x + y * w] = majority_seg;
|
| @@ -153,6 +150,8 @@ static void AssignSegments(VP8Encoder* const enc,
|
| // 'int' type is ok for histo, and won't overflow
|
| int accum[NUM_MB_SEGMENTS], dist_accum[NUM_MB_SEGMENTS];
|
|
|
| + assert(nb >= 1);
|
| +
|
| // bracket the input
|
| for (n = 0; n <= MAX_ALPHA && alphas[n] == 0; ++n) {}
|
| min_a = n;
|
| @@ -161,8 +160,9 @@ static void AssignSegments(VP8Encoder* const enc,
|
| range_a = max_a - min_a;
|
|
|
| // Spread initial centers evenly
|
| - for (n = 1, k = 0; n < 2 * nb; n += 2) {
|
| - centers[k++] = min_a + (n * range_a) / (2 * nb);
|
| + for (k = 0, n = 1; k < nb; ++k, n += 2) {
|
| + assert(n < 2 * nb);
|
| + centers[k] = min_a + (n * range_a) / (2 * nb);
|
| }
|
|
|
| for (k = 0; k < MAX_ITERS_K_MEANS; ++k) { // few iters are enough
|
| @@ -177,7 +177,7 @@ static void AssignSegments(VP8Encoder* const enc,
|
| n = 0; // track the nearest center for current 'a'
|
| for (a = min_a; a <= max_a; ++a) {
|
| if (alphas[a]) {
|
| - while (n < nb - 1 && abs(a - centers[n + 1]) < abs(a - centers[n])) {
|
| + while (n + 1 < nb && abs(a - centers[n + 1]) < abs(a - centers[n])) {
|
| n++;
|
| }
|
| map[a] = n;
|
| @@ -384,38 +384,114 @@ static void ResetAllMBInfo(VP8Encoder* const enc) {
|
| // Default susceptibilities.
|
| enc->dqm_[0].alpha_ = 0;
|
| enc->dqm_[0].beta_ = 0;
|
| - // Note: we can't compute this alpha_ / uv_alpha_.
|
| + // Note: we can't compute this alpha_ / uv_alpha_ -> set to default value.
|
| + enc->alpha_ = 0;
|
| + enc->uv_alpha_ = 0;
|
| WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
|
| }
|
|
|
| +// struct used to collect job result
|
| +typedef struct {
|
| + WebPWorker worker;
|
| + int alphas[MAX_ALPHA + 1];
|
| + int alpha, uv_alpha;
|
| + VP8EncIterator it;
|
| + int delta_progress;
|
| +} SegmentJob;
|
| +
|
| +// main work call
|
| +static int DoSegmentsJob(SegmentJob* const job, VP8EncIterator* const it) {
|
| + int ok = 1;
|
| + if (!VP8IteratorIsDone(it)) {
|
| + uint8_t tmp[32 + ALIGN_CST];
|
| + uint8_t* const scratch = (uint8_t*)DO_ALIGN(tmp);
|
| + do {
|
| + // Let's pretend we have perfect lossless reconstruction.
|
| + VP8IteratorImport(it, scratch);
|
| + MBAnalyze(it, job->alphas, &job->alpha, &job->uv_alpha);
|
| + ok = VP8IteratorProgress(it, job->delta_progress);
|
| + } while (ok && VP8IteratorNext(it));
|
| + }
|
| + return ok;
|
| +}
|
| +
|
| +static void MergeJobs(const SegmentJob* const src, SegmentJob* const dst) {
|
| + int i;
|
| + for (i = 0; i <= MAX_ALPHA; ++i) dst->alphas[i] += src->alphas[i];
|
| + dst->alpha += src->alpha;
|
| + dst->uv_alpha += src->uv_alpha;
|
| +}
|
| +
|
| +// initialize the job struct with some TODOs
|
| +static void InitSegmentJob(VP8Encoder* const enc, SegmentJob* const job,
|
| + int start_row, int end_row) {
|
| + WebPWorkerInit(&job->worker);
|
| + job->worker.data1 = job;
|
| + job->worker.data2 = &job->it;
|
| + job->worker.hook = (WebPWorkerHook)DoSegmentsJob;
|
| + VP8IteratorInit(enc, &job->it);
|
| + VP8IteratorSetRow(&job->it, start_row);
|
| + VP8IteratorSetCountDown(&job->it, (end_row - start_row) * enc->mb_w_);
|
| + memset(job->alphas, 0, sizeof(job->alphas));
|
| + job->alpha = 0;
|
| + job->uv_alpha = 0;
|
| + // only one of both jobs can record the progress, since we don't
|
| + // expect the user's hook to be multi-thread safe
|
| + job->delta_progress = (start_row == 0) ? 20 : 0;
|
| +}
|
| +
|
| +// main entry point
|
| int VP8EncAnalyze(VP8Encoder* const enc) {
|
| int ok = 1;
|
| const int do_segments =
|
| enc->config_->emulate_jpeg_size || // We need the complexity evaluation.
|
| (enc->segment_hdr_.num_segments_ > 1) ||
|
| (enc->method_ == 0); // for method 0, we need preds_[] to be filled.
|
| - enc->alpha_ = 0;
|
| - enc->uv_alpha_ = 0;
|
| if (do_segments) {
|
| - int alphas[MAX_ALPHA + 1] = { 0 };
|
| - VP8EncIterator it;
|
| -
|
| - VP8IteratorInit(enc, &it);
|
| - do {
|
| - VP8IteratorImport(&it);
|
| - MBAnalyze(&it, alphas, &enc->alpha_, &enc->uv_alpha_);
|
| - ok = VP8IteratorProgress(&it, 20);
|
| - // Let's pretend we have perfect lossless reconstruction.
|
| - } while (ok && VP8IteratorNext(&it, it.yuv_in_));
|
| - enc->alpha_ /= enc->mb_w_ * enc->mb_h_;
|
| - enc->uv_alpha_ /= enc->mb_w_ * enc->mb_h_;
|
| - if (ok) AssignSegments(enc, alphas);
|
| + const int last_row = enc->mb_h_;
|
| + // We give a little more than a half work to the main thread.
|
| + const int split_row = (9 * last_row + 15) >> 4;
|
| + const int total_mb = last_row * enc->mb_w_;
|
| +#ifdef WEBP_USE_THREAD
|
| + const int kMinSplitRow = 2; // minimal rows needed for mt to be worth it
|
| + const int do_mt = (enc->thread_level_ > 0) && (split_row >= kMinSplitRow);
|
| +#else
|
| + const int do_mt = 0;
|
| +#endif
|
| + SegmentJob main_job;
|
| + if (do_mt) {
|
| + SegmentJob side_job;
|
| + // Note the use of '&' instead of '&&' because we must call the functions
|
| + // no matter what.
|
| + InitSegmentJob(enc, &main_job, 0, split_row);
|
| + InitSegmentJob(enc, &side_job, split_row, last_row);
|
| + // we don't need to call Reset() on main_job.worker, since we're calling
|
| + // WebPWorkerExecute() on it
|
| + ok &= WebPWorkerReset(&side_job.worker);
|
| + // launch the two jobs in parallel
|
| + if (ok) {
|
| + WebPWorkerLaunch(&side_job.worker);
|
| + WebPWorkerExecute(&main_job.worker);
|
| + ok &= WebPWorkerSync(&side_job.worker);
|
| + ok &= WebPWorkerSync(&main_job.worker);
|
| + }
|
| + WebPWorkerEnd(&side_job.worker);
|
| + if (ok) MergeJobs(&side_job, &main_job); // merge results together
|
| + } else {
|
| + // Even for single-thread case, we use the generic Worker tools.
|
| + InitSegmentJob(enc, &main_job, 0, last_row);
|
| + WebPWorkerExecute(&main_job.worker);
|
| + ok &= WebPWorkerSync(&main_job.worker);
|
| + }
|
| + WebPWorkerEnd(&main_job.worker);
|
| + if (ok) {
|
| + enc->alpha_ = main_job.alpha / total_mb;
|
| + enc->uv_alpha_ = main_job.uv_alpha / total_mb;
|
| + AssignSegments(enc, main_job.alphas);
|
| + }
|
| } else { // Use only one default segment.
|
| ResetAllMBInfo(enc);
|
| }
|
| return ok;
|
| }
|
|
|
| -#if defined(__cplusplus) || defined(c_plusplus)
|
| -} // extern "C"
|
| -#endif
|
|
|