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 |