Import Cobalt 24.lts.3.1032501
diff --git a/base/sys_info_starboard.cc b/base/sys_info_starboard.cc
index b5aae9c..138a14c 100644
--- a/base/sys_info_starboard.cc
+++ b/base/sys_info_starboard.cc
@@ -15,9 +15,11 @@
 #include "base/sys_info.h"
 
 #include "base/logging.h"
+#include "starboard/common/system_property.h"
 #include "starboard/system.h"
 
 namespace base {
+ using starboard::kSystemPropertyMaxLength;
 
 // static
 int SysInfo::NumberOfProcessors() {
@@ -56,4 +58,17 @@
   return AmountOfPhysicalMemoryImpl();
 }
 
+// static
+std::string SysInfo::OperatingSystemName() {
+  char value[kSystemPropertyMaxLength];
+  SbSystemGetProperty(kSbSystemPropertyPlatformName, value,
+                      kSystemPropertyMaxLength);
+  return value;
+}
+
+// static
+std::string SysInfo::OperatingSystemVersion() {
+  return SysInfo::OperatingSystemName();
+}
+
 }  // namespace base
diff --git a/build/toolchain/gcc_toolchain.gni b/build/toolchain/gcc_toolchain.gni
index df72296..89b26d3 100644
--- a/build/toolchain/gcc_toolchain.gni
+++ b/build/toolchain/gcc_toolchain.gni
@@ -457,14 +457,7 @@
       # .TOC file, overwrite it, otherwise, don't change it.
       tocfile = sofile + ".TOC"
 
-      # TODO(b/206642994): see if we can remove this condition. It's needed for
-      # now because we use the ld.lld linker for evergreen platforms and need to
-      # pass options as `option` instead of `-Wl,option`.
-      if (is_starboard && sb_is_evergreen && !is_starboard_toolchain) {
-        link_command = "$ld -shared -soname=\"$soname\" {{ldflags}}${extra_ldflags} -o \"$unstripped_sofile\" @\"$rspfile\""
-      } else {
-        link_command = "$ld -shared -Wl,-soname=\"$soname\" {{ldflags}}${extra_ldflags} -o \"$unstripped_sofile\" @\"$rspfile\""
-      }
+      link_command = "$ld -shared -Wl,-soname=\"$soname\" {{ldflags}}${extra_ldflags} -o \"$unstripped_sofile\" @\"$rspfile\""
 
       # Generate a map file to be used for binary size analysis.
       # Map file adds ~10% to the link time on a z620.
@@ -491,11 +484,6 @@
 
       if (target_cpu == "mipsel" && is_component_build && is_android) {
         rspfile_content = "-Wl,--start-group -Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}} -Wl,--end-group$tail_lib_dependencies"
-        # TODO(b/206642994): see if we can remove this condition. It's needed for
-        # now because we use the ld.lld linker for evergreen platforms and need
-        # to pass options as `option` instead of `-Wl,option`.
-      } else if (is_starboard && sb_is_evergreen && !is_starboard_toolchain) {
-        rspfile_content = "--whole-archive {{inputs}} {{solibs}} --no-whole-archive {{libs}}$tail_lib_dependencies"
       } else {
         rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}}$tail_lib_dependencies"
       }
diff --git a/cobalt/browser/application.cc b/cobalt/browser/application.cc
index 9f81b5f..e6febfe 100644
--- a/cobalt/browser/application.cc
+++ b/cobalt/browser/application.cc
@@ -1512,9 +1512,9 @@
   // it's enabled or upload interval.
   bool is_metrics_enabled = persistent_settings_->GetPersistentSettingAsBool(
       metrics::kMetricEnabledSettingName, false);
-  metrics_services_manager_->SetUploadInterval(
-      persistent_settings_->GetPersistentSettingAsInt(
-          metrics::kMetricEventIntervalSettingName, 300));
+  auto metric_event_interval = persistent_settings_->GetPersistentSettingAsInt(
+      metrics::kMetricEventIntervalSettingName, 300);
+  metrics_services_manager_->SetUploadInterval(metric_event_interval);
   metrics_services_manager_->ToggleMetricsEnabled(is_metrics_enabled);
   // Metric recording state initialization _must_ happen before we bootstrap
   // otherwise we crash.
@@ -1523,6 +1523,10 @@
   // UpdateUploadPermissions bootstraps the whole metric reporting, scheduling,
   // and uploading cycle.
   metrics_services_manager_->UpdateUploadPermissions(is_metrics_enabled);
+  LOG(INFO)
+      << "Cobalt Telemetry initialized with settings: is_metrics_enabled: "
+      << is_metrics_enabled
+      << ", metric_event_interval: " << metric_event_interval;
 }
 
 }  // namespace browser
diff --git a/cobalt/browser/metrics/cobalt_metrics_log_uploader.cc b/cobalt/browser/metrics/cobalt_metrics_log_uploader.cc
index 1d3050d..49f1c81 100644
--- a/cobalt/browser/metrics/cobalt_metrics_log_uploader.cc
+++ b/cobalt/browser/metrics/cobalt_metrics_log_uploader.cc
@@ -14,6 +14,7 @@
 
 #include "cobalt/browser/metrics/cobalt_metrics_log_uploader.h"
 
+#include "base/base64url.h"
 #include "base/logging.h"
 #include "cobalt/browser/metrics/cobalt_metrics_uploader_callback.h"
 #include "cobalt/h5vcc/h5vcc_metric_type.h"
@@ -59,9 +60,15 @@
       PopulateCobaltUmaEvent(uma_event, reporting_info, cobalt_uma_event);
       LOG(INFO) << "Publishing Cobalt metrics upload event. Type: "
                 << h5vcc::H5vccMetricType::kH5vccMetricTypeCobaltUma;
-      // Publish the trimmed Cobalt UMA proto.
+      std::string base64_encoded_proto;
+      // Base64 encode the payload as web client's can't consume it without
+      // corrupting the data (see b/293431381). Also, use a URL/web safe
+      // encoding so it can be safely included in any web network request.
+      base::Base64UrlEncode(cobalt_uma_event.SerializeAsString(),
+                            base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+                            &base64_encoded_proto);
       upload_handler_->Run(h5vcc::H5vccMetricType::kH5vccMetricTypeCobaltUma,
-                           cobalt_uma_event.SerializeAsString());
+                           base64_encoded_proto);
     }
   }
 
diff --git a/cobalt/browser/metrics/cobalt_metrics_log_uploader_test.cc b/cobalt/browser/metrics/cobalt_metrics_log_uploader_test.cc
index 5adb841..78e7d18 100644
--- a/cobalt/browser/metrics/cobalt_metrics_log_uploader_test.cc
+++ b/cobalt/browser/metrics/cobalt_metrics_log_uploader_test.cc
@@ -16,6 +16,7 @@
 
 #include <memory>
 
+#include "base/base64url.h"
 #include "base/test/mock_callback.h"
 #include "cobalt/browser/metrics/cobalt_metrics_uploader_callback.h"
 #include "cobalt/h5vcc/h5vcc_metrics.h"
@@ -82,9 +83,13 @@
 
   std::string compressed_message;
   compression::GzipCompress(uma_log.SerializeAsString(), &compressed_message);
+  std::string base64_encoded_proto;
+  base::Base64UrlEncode(cobalt_event.SerializeAsString(),
+                        base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+                        &base64_encoded_proto);
   EXPECT_CALL(mock_upload_handler,
               Run(Eq(h5vcc::H5vccMetricType::kH5vccMetricTypeCobaltUma),
-                  StrEq(cobalt_event.SerializeAsString())))
+                  StrEq(base64_encoded_proto)))
       .Times(1);
   uploader_->UploadLog(compressed_message, "fake_hash", dummy_reporting_info);
   ASSERT_EQ(callback_count_, 1);
@@ -99,9 +104,13 @@
   cobalt_event2.mutable_reporting_info()->CopyFrom(dummy_reporting_info);
   std::string compressed_message2;
   compression::GzipCompress(uma_log2.SerializeAsString(), &compressed_message2);
+  std::string base64_encoded_proto2;
+  base::Base64UrlEncode(cobalt_event2.SerializeAsString(),
+                        base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+                        &base64_encoded_proto2);
   EXPECT_CALL(mock_upload_handler,
               Run(Eq(h5vcc::H5vccMetricType::kH5vccMetricTypeCobaltUma),
-                  StrEq(cobalt_event2.SerializeAsString())))
+                  StrEq(base64_encoded_proto2)))
       .Times(1);
   uploader_->UploadLog(compressed_message2, "fake_hash", dummy_reporting_info);
   ASSERT_EQ(callback_count_, 2);
diff --git a/cobalt/browser/metrics/cobalt_metrics_services_manager.cc b/cobalt/browser/metrics/cobalt_metrics_services_manager.cc
index 3874d08..acb5d8b 100644
--- a/cobalt/browser/metrics/cobalt_metrics_services_manager.cc
+++ b/cobalt/browser/metrics/cobalt_metrics_services_manager.cc
@@ -16,6 +16,7 @@
 
 #include <memory>
 
+#include "base/logging.h"
 #include "cobalt/browser/metrics/cobalt_metrics_service_client.h"
 #include "cobalt/browser/metrics/cobalt_metrics_services_manager_client.h"
 #include "components/metrics_services_manager/metrics_services_manager.h"
@@ -56,6 +57,7 @@
       static_cast<CobaltMetricsServiceClient*>(GetMetricsServiceClient());
   DCHECK(client);
   client->SetOnUploadHandler(uploader_callback);
+  LOG(INFO) << "New Cobalt Telemetry metric upload handler bound.";
 }
 
 void CobaltMetricsServicesManager::ToggleMetricsEnabled(bool is_enabled) {
@@ -73,6 +75,7 @@
   client->GetEnabledStateProvider()->SetConsentGiven(is_enabled);
   client->GetEnabledStateProvider()->SetReportingEnabled(is_enabled);
   UpdateUploadPermissions(is_enabled);
+  LOG(INFO) << "Cobalt Telemetry enabled state toggled to: " << is_enabled;
 }
 
 void CobaltMetricsServicesManager::SetUploadInterval(
@@ -90,6 +93,8 @@
           GetMetricsServiceClient());
   DCHECK(client);
   client->SetUploadInterval(interval_seconds);
+  LOG(INFO) << "Cobalt Telemetry metric upload interval changed to: "
+            << interval_seconds;
 }
 
 
diff --git a/cobalt/browser/web_module.cc b/cobalt/browser/web_module.cc
index 33ca0be..7e5f31b 100644
--- a/cobalt/browser/web_module.cc
+++ b/cobalt/browser/web_module.cc
@@ -537,6 +537,7 @@
       data.options.loader_thread_priority));
 
   animated_image_tracker_.reset(new loader::image::AnimatedImageTracker(
+      web_context_->name().c_str(),
       data.options.animated_image_decode_thread_priority));
 
   DCHECK_LE(0, data.options.image_cache_capacity);
@@ -975,6 +976,7 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   web_module_stat_tracker_->OnRenderTreeRasterized(produced_time,
                                                    rasterized_time);
+  animated_image_tracker_->OnRenderTreeRasterized();
   if (produced_time >= last_render_tree_produced_time_) {
     is_render_tree_rasterization_pending_ = false;
   }
diff --git a/cobalt/build/cobalt_configuration.py b/cobalt/build/cobalt_configuration.py
index 91b90a6..e1320ab 100644
--- a/cobalt/build/cobalt_configuration.py
+++ b/cobalt/build/cobalt_configuration.py
@@ -105,6 +105,7 @@
     ]
     return filters
 
+  # TODO(b/292007482): Replace static list with gn query.
   def GetTestTargets(self):
     return [
         'audio_test',
@@ -112,38 +113,48 @@
         'base_unittests',
         'bindings_test',
         'browser_test',
+        'components_metrics_tests',
+        'crypto_impl_test',
         'crypto_unittests',
         'csp_test',
-        'css_parser_test',
         'cssom_test',
+        'css_parser_test',
+        'cwrappers_test',
         'dom_parser_test',
         'dom_test',
         'extension_test',
+        # TODO(b/292127297): This target is not built for all platforms.
+        # 'ffmpeg_demuxer_test',
         'graphics_system_test',
         'layout_test',
         'layout_tests',
-        'cwrappers_test',
         'loader_test',
         'math_test',
         'media_capture_test',
         'media_session_test',
         'media_stream_test',
+        # TODO(b/292030213): Crashes on evergreen
+        # 'media_test',
         'memory_store_test',
         'metrics_test',
         'nb_test',
         'net_unittests',
         'network_test',
+        'overlay_info_test',
         'persistent_settings_test',
+        'png_utils_test',
         'poem_unittests',
-        'render_tree_test',
         'renderer_test',
+        'render_tree_test',
         'scroll_engine_tests',
+        'speech_test',
         'storage_test',
         'text_encoding_test',
-        'web_test',
+        'watchdog_test',
         'web_animations_test',
         'webdriver_test',
         'websocket_test',
+        'web_test',
         'worker_test',
         'xhr_test',
         'zip_unittests',
diff --git a/cobalt/demos/content/animated-webp-performance-demo/10_turtle.webp b/cobalt/demos/content/animated-webp-performance-demo/10_turtle.webp
new file mode 100644
index 0000000..800df18
--- /dev/null
+++ b/cobalt/demos/content/animated-webp-performance-demo/10_turtle.webp
Binary files differ
diff --git a/cobalt/demos/content/animated-webp-performance-demo/1_fan.webp b/cobalt/demos/content/animated-webp-performance-demo/1_fan.webp
new file mode 100644
index 0000000..f982fd8
--- /dev/null
+++ b/cobalt/demos/content/animated-webp-performance-demo/1_fan.webp
Binary files differ
diff --git a/cobalt/demos/content/animated-webp-performance-demo/2_heart.webp b/cobalt/demos/content/animated-webp-performance-demo/2_heart.webp
new file mode 100644
index 0000000..79cdd39
--- /dev/null
+++ b/cobalt/demos/content/animated-webp-performance-demo/2_heart.webp
Binary files differ
diff --git a/cobalt/demos/content/animated-webp-performance-demo/3_cry.webp b/cobalt/demos/content/animated-webp-performance-demo/3_cry.webp
new file mode 100644
index 0000000..c045e21
--- /dev/null
+++ b/cobalt/demos/content/animated-webp-performance-demo/3_cry.webp
Binary files differ
diff --git a/cobalt/demos/content/animated-webp-performance-demo/4_clap.webp b/cobalt/demos/content/animated-webp-performance-demo/4_clap.webp
new file mode 100644
index 0000000..194a34c
--- /dev/null
+++ b/cobalt/demos/content/animated-webp-performance-demo/4_clap.webp
Binary files differ
diff --git a/cobalt/demos/content/animated-webp-performance-demo/5_egg.webp b/cobalt/demos/content/animated-webp-performance-demo/5_egg.webp
new file mode 100644
index 0000000..995a4cb
--- /dev/null
+++ b/cobalt/demos/content/animated-webp-performance-demo/5_egg.webp
Binary files differ
diff --git a/cobalt/demos/content/animated-webp-performance-demo/6_party.webp b/cobalt/demos/content/animated-webp-performance-demo/6_party.webp
new file mode 100644
index 0000000..d5dfb16
--- /dev/null
+++ b/cobalt/demos/content/animated-webp-performance-demo/6_party.webp
Binary files differ
diff --git a/cobalt/demos/content/animated-webp-performance-demo/7_rofl.webp b/cobalt/demos/content/animated-webp-performance-demo/7_rofl.webp
new file mode 100644
index 0000000..4c6c375
--- /dev/null
+++ b/cobalt/demos/content/animated-webp-performance-demo/7_rofl.webp
Binary files differ
diff --git a/cobalt/demos/content/animated-webp-performance-demo/8_melt.webp b/cobalt/demos/content/animated-webp-performance-demo/8_melt.webp
new file mode 100644
index 0000000..3feba61
--- /dev/null
+++ b/cobalt/demos/content/animated-webp-performance-demo/8_melt.webp
Binary files differ
diff --git a/cobalt/demos/content/animated-webp-performance-demo/9_explode.webp b/cobalt/demos/content/animated-webp-performance-demo/9_explode.webp
new file mode 100644
index 0000000..3c548ce
--- /dev/null
+++ b/cobalt/demos/content/animated-webp-performance-demo/9_explode.webp
Binary files differ
diff --git a/cobalt/demos/content/animated-webp-performance-demo/index.html b/cobalt/demos/content/animated-webp-performance-demo/index.html
new file mode 100644
index 0000000..5613cc8
--- /dev/null
+++ b/cobalt/demos/content/animated-webp-performance-demo/index.html
@@ -0,0 +1,166 @@
+<!DOCTYPE html>
+<!--
+ | Demo to help test animated webp performance metrics.
+ | We set up decoding speed tracking on a regular interval, and make a
+ | customizeable page with varying number of decoded images.
+ -->
+<html>
+
+<head>
+  <title>WebP performance test</title>
+  <style type="text/css">
+
+    body {
+      background-color: white;
+    }
+    .image {
+      width: 100px;
+      height: 100px;
+      background-size: contain;
+      display: inline-block;
+    }
+    .highlight {
+      background-color: rgb(0, 255, 0);
+    }
+  </style>
+  <script type="text/javascript">
+    window.onload = () => {
+      var size = 100; // Default pixel size of rendered images
+      var search = window.location.search;
+      var num = 1; // Default number of decoded images
+
+      // Grab URL search args
+      var matches = search.matchAll(/num=([0-9]+)/gm);
+      for (const match of matches) {
+        num = match[1]
+      }
+      var matches = search.matchAll(/size=([0-9]+)/gm);
+      for (const match of matches) {
+        size = match[1]
+      }
+
+      // Insert images into DOM tree
+      var layer = document.getElementById('layer');
+      function addImage(img, size) {
+        var el = document.createElement('span');
+        console.log(`adding ${img}`);
+        el.classList.add('image');
+        el.style['backgroundImage'] = `url(${img}.webp)`;
+        el.style['width'] = `${size}px`;
+        el.style['height'] = `${size}px`;
+        layer.appendChild(el)
+        console.log("Added image");
+      }
+      var images = [
+        "1_fan","2_heart","3_cry","4_clap", "5_egg",
+        "6_party","7_rofl","8_melt","9_explode","10_turtle"
+      ]
+      for(i = 0; i< num; i++)  {
+        (function(i) {
+          var img = images[i % 10];
+          window.setTimeout( () => {  addImage(img,size) } , 1 + i*40);
+        })(i)
+      }
+
+      // Set up function retrieve CVals.
+      var getCVal = function(name) { return 0 }
+      if (typeof h5vcc != "undefined" && typeof h5vcc.cVal != "undefined") {
+        getCVal = function(name) {
+          return h5vcc.cVal.getValue(name);
+        }
+      }
+
+      // Set up periodic stats tracking loop.
+      var last_frames = 0;
+      var last_time = performance.now();
+      function updateStats() {
+        var current_time = performance.now();
+        var time_delta_milliseconds = current_time - last_time;
+        var decoded_frames =
+          getCVal('Count.MainWebModule.AnimatedImage.DecodedFrames');
+        var underruns = getCVal('Count.MainWebModule.AnimatedImage.DecodingUnderruns');
+        var overruns = getCVal('Count.MainWebModule.AnimatedImage.DecodingOverruns');
+        var newly_decoded_frames = decoded_frames - last_frames;
+        var fps = parseInt( newly_decoded_frames * 100 / (time_delta_milliseconds / 1000.0)) / 100;
+        document.getElementById('Active').textContent =
+          getCVal('Count.MainWebModule.AnimatedImage.Active');
+        document.getElementById('DecodedFrames').textContent = decoded_frames;
+        document.getElementById('DecodingUnderruns').textContent = underruns
+        document.getElementById('DecodingOverruns').textContent = overruns;
+        document.getElementById('DecodedFPS').textContent = fps;
+        document.getElementById('UnderrunPercent').textContent =  parseInt(underruns / decoded_frames * 100.0);
+        document.getElementById('OverrunPercent').textContent =  parseInt(overruns / decoded_frames * 100.0);
+
+        last_frames = decoded_frames;
+        last_time = current_time;
+      }
+      window.setInterval(updateStats, 1000);
+
+      // Set up keyboard menu nav
+      var menu = document.getElementById('menu').children;
+      var index = 0;
+      function refresh() {
+        var textBox = document.getElementById('nav');
+        for (let i = 0; i < menu.length; i++) {
+          if (i == index) {
+            menu[i].classList.add('highlight');
+          } else {
+            menu[i].classList.remove('highlight');
+          }
+        }
+      }
+      document.addEventListener('keydown', function (e) {
+        // left, up, android left, up
+        if ([37, 38, 32782, 32780].includes(e.keyCode)) {
+          index -= 1;
+        //right, down, android right, down
+        } else if ([39, 40, 32781, 32783].includes(e.keyCode)) {
+          index += 1;
+        // enter, android enter
+        } else if ([13, 32768].includes(e.keyCode)) {
+          var el = document.getElementById('menu').children[index];
+          window.location = el.firstChild.href;
+        }
+        index = (index + menu.length) % menu.length;
+        refresh();
+      });
+      refresh();
+    }
+  </script>
+</head>
+
+<body>
+  <div class="background"></div>
+  <div id="menu">
+    <span><a href="?size=100&amp;num=1">1 small image</a></span>
+    <span><a href="?size=300&amp;num=1">1 large image</a></span>
+    <span><a href="?size=100&amp;num=2">2 small images</a></span>
+    <span><a href="?size=300&amp;num=2">2 large image</a></span>
+    <span><a href="?size=100&amp;num=3">3 small images</a></span>
+    <span><a href="?size=300&amp;num=3">3 large images</a></span>
+    <span><a href="?size=100&amp;num=4">4 small images</a></span>
+    <span><a href="?size=300&amp;num=4">4 large images</a></span>
+    <div></div>
+    <span><a href="?size=100&amp;num=4">5 small images</a></span>
+    <span><a href="?size=300&amp;num=4">5 large images</a></span>
+    <span><a href="?size=100&amp;num=6">6 small images</a></span>
+    <span><a href="?size=300&amp;num=6">6 large images</a></span>
+    <span><a href="?size=100&amp;num=8">8 small images</a></span>
+    <span><a href="?size=300&amp;num=8">8 large images</a></span>
+    <span><a href="?size=100&amp;num=10">10 small images</a></span>
+    <span><a href="?size=300&amp;num=10">10 large images</a></span>
+  </div>
+  <div id="metrics">
+    <div><span>Total images playing:</span><span id="Active"></span></div>
+    <div><span>Total frames decoded:</span><span id="DecodedFrames"></span></div>
+    <div><span>Total frames underrun:</span><span id="DecodingUnderruns"></span></div>
+    <div><span>Total frames overrun:</span><span id="DecodingOverruns"></span></div>
+    <div><span>Frames decoded per second:</span><span id="DecodedFPS"></span></div>
+    <div><span>Underrun %:</span><span id="UnderrunPercent"></span>
+      <span>Overrun %:</span><span id="OverrunPercent"></span></div>
+  </div>
+
+  <div class="layer" id="layer"></div>
+</body>
+
+</html>
diff --git a/cobalt/demos/content/video-background-demo/dash-audio.mp4 b/cobalt/demos/content/video-background-demo/dash-audio.mp4
new file mode 100644
index 0000000..b26721c
--- /dev/null
+++ b/cobalt/demos/content/video-background-demo/dash-audio.mp4
Binary files differ
diff --git a/cobalt/demos/content/video-background-demo/sddefault.jpg b/cobalt/demos/content/video-background-demo/sddefault.jpg
new file mode 100644
index 0000000..ef2dae5
--- /dev/null
+++ b/cobalt/demos/content/video-background-demo/sddefault.jpg
Binary files differ
diff --git a/cobalt/demos/content/video-background-demo/video-background-demo.html b/cobalt/demos/content/video-background-demo/video-background-demo.html
new file mode 100644
index 0000000..c68d4da
--- /dev/null
+++ b/cobalt/demos/content/video-background-demo/video-background-demo.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<!--
+ | Copyright 2023 The Cobalt Authors. All Rights Reserved.
+ |
+ | Licensed under the Apache License, Version 2.0 (the "License");
+ | you may not use this file except in compliance with the License.
+ | You may obtain a copy of the License at
+ |
+ |     http://www.apache.org/licenses/LICENSE-2.0
+ |
+ | Unless required by applicable law or agreed to in writing, software
+ | distributed under the License is distributed on an "AS IS" BASIS,
+ | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ | See the License for the specific language governing permissions and
+ | limitations under the License.
+ -->
+
+<html>
+  <head>
+    <title>Video Elements With Background</title>
+    <style>
+        body {
+          margin: 0;
+        }
+
+        video {
+          width: 100%;
+          height: 100%;
+        }
+
+        #ui-layer {
+          display: flex;
+          justify-content: space-between;
+          position: absolute;
+          top: 15%;
+          height: 85%;
+          width: 100%;
+          background-color: rgba(33, 33, 33, .75);
+          padding: 24px;
+          margin: auto;
+        }
+
+        .item {
+          width: 320px;
+          height: 240px;
+          display: inline-block;
+          margin: 24px;
+          vertical-align: middle;
+        }
+    </style>
+  </head>
+  <body>
+    <div id="ui-layer">
+      <div class="item">
+        <video id="video1" muted="1" style="background-color: #4285F4">
+        </video>
+      </div>
+      <div class="item">
+        <video id="video2" muted="1"
+           style="background-image: url(sddefault.jpg);
+                  background-size: 320px 240px;">
+        </video>
+      </div>
+      <div class="item">
+        <video id="video3" muted="1" style="background-color: #4285F4">
+        </video>
+      </div>
+      <div class="item">
+        <video id="video4" muted="1"
+           style="background-image: url(sddefault.jpg);
+                  background-size: 320px 240px;">
+        </video>
+      </div>
+    </div>
+    <script src="video-background-demo.js"></script>
+  </body>
+</html>
diff --git a/cobalt/demos/content/video-background-demo/video-background-demo.js b/cobalt/demos/content/video-background-demo/video-background-demo.js
new file mode 100644
index 0000000..1c50180
--- /dev/null
+++ b/cobalt/demos/content/video-background-demo/video-background-demo.js
@@ -0,0 +1,106 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+var nextVideoElementIndex = 0;
+var audioData;
+var videoData;
+
+function downloadMediaData(downloadedCallback) {
+  var xhr = new XMLHttpRequest;
+
+  xhr.onload = function() {
+    audioData = xhr.response;
+    console.log("Downloaded " + audioData.byteLength + " of audio data.");
+
+    xhr.onload = function() {
+      videoData = xhr.response;
+      console.log("Downloaded " + videoData.byteLength + " of video data.");
+      downloadedCallback();
+    }
+
+    xhr.open("GET", "vp9-720p.webm", true);
+    xhr.send();
+  }
+
+  xhr.open("GET", "dash-audio.mp4", true);
+  xhr.responseType = "arraybuffer";
+  xhr.send();
+}
+
+function playVideoOn(videoElement) {
+  var ms = new MediaSource;
+  ms.addEventListener('sourceopen', function() {
+    console.log("Creating SourceBuffer objects.");
+    var audioBuffer = ms.addSourceBuffer('audio/mp4; codecs="mp4a.40.2"');
+    var videoBuffer = ms.addSourceBuffer('video/webm; codecs="vp9"; decode-to-texture=true');
+    audioBuffer.addEventListener("updateend", function() {
+      audioBuffer.abort();
+      videoBuffer.addEventListener("updateend", function() {
+        setTimeout(function() {
+          videoBuffer.addEventListener("updateend", function() {
+            videoBuffer.abort();
+            ms.endOfStream();
+            videoElement.ontimeupdate = function() {
+              if (videoElement.currentTime > 10) {
+                console.log("Stop playback.");
+                videoElement.src = '';
+                videoElement.load();
+                videoElement.ontimeupdate = null;
+              }
+            }
+            console.log("Start playback.");
+            videoElement.play();
+          });
+          videoBuffer.appendBuffer(videoData.slice(1024));
+        }, 5000);
+      });
+      videoBuffer.appendBuffer(videoData.slice(0, 1024));
+    });
+    audioBuffer.appendBuffer(audioData);
+  });
+
+  console.log("Attaching MediaSource to video element.");
+  videoElement.src = URL.createObjectURL(ms);
+}
+
+function setupKeyHandler() {
+  document.onkeydown = function() {
+    videoElements = document.getElementsByTagName('video');
+    for(let i = 0; i < videoElements.length; i++) {
+      if (videoElements[i].playing) {
+        console.log("Ignore key press as a video is still playing.");
+        return;
+      }
+    }
+
+    nextVideoElementIndex = nextVideoElementIndex % videoElements.length;
+
+    console.log("Trying to play next video at index " + nextVideoElementIndex);
+
+    var currentVideoElement = videoElements[nextVideoElementIndex];
+    if (currentVideoElement.setMaxVideoCapabilities) {
+      if (nextVideoElementIndex < videoElements.length / 2) {
+        currentVideoElement.setMaxVideoCapabilities("");
+      } else {
+        currentVideoElement.setMaxVideoCapabilities("width=1920; height=1080");
+      }
+    }
+
+    nextVideoElementIndex++;
+
+    playVideoOn(currentVideoElement);
+  };
+}
+
+downloadMediaData(setupKeyHandler);
diff --git a/cobalt/demos/content/video-background-demo/vp9-720p.webm b/cobalt/demos/content/video-background-demo/vp9-720p.webm
new file mode 100644
index 0000000..08a670e
--- /dev/null
+++ b/cobalt/demos/content/video-background-demo/vp9-720p.webm
Binary files differ
diff --git a/cobalt/doc/cvals.md b/cobalt/doc/cvals.md
index 6accd8f..68317e4 100644
--- a/cobalt/doc/cvals.md
+++ b/cobalt/doc/cvals.md
@@ -119,6 +119,18 @@
     compatibility violations encountered.
 *   **Count.XHR** - The total number of xhr::XMLHttpRequest in existence
     globally.
+*   **Count.MainWebModule.AnimatedImage.Active** - The total number of currently
+    active animated image decoders. Same image from a single URL source rendered
+    multiple times across the content counts as one decoder.
+*   **Count.MainWebModule.AnimatedImage.DecodedFrames** - Total number of decoded
+    frames across all active image decoders. This number resets only when
+    WebModule gets re-created - e.g. page reload, navigation.
+*   **Count.MainWebModule.AnimatedImage.DecodingUnderruns** - Total number of
+    frames when animated image decoding has fallen behind real-time, as defined
+    by image frame exposure times. This indicates a CPU bottleneck.
+*   **Count.MainWebModule.AnimatedImage.DecodingOverruns** - Total number of
+    frames when animated image decoding has been attempted too early, before
+    next frame exposure time. This indicates a timing issue in platform.
 
 ### Event
 
diff --git a/cobalt/dom/html_video_element.cc b/cobalt/dom/html_video_element.cc
index a973f3e..75b3f9f 100644
--- a/cobalt/dom/html_video_element.cc
+++ b/cobalt/dom/html_video_element.cc
@@ -18,6 +18,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/trace_event/trace_event.h"
 #include "cobalt/dom/dom_settings.h"
+#include "cobalt/dom/media_settings.h"
 #include "cobalt/dom/performance.h"
 #include "cobalt/dom/window.h"
 #include "cobalt/math/size_f.h"
@@ -30,6 +31,15 @@
 
 const char HTMLVideoElement::kTagName[] = "video";
 
+const MediaSettings& GetMediaSettings(web::EnvironmentSettings* settings) {
+  DCHECK(settings);
+  DCHECK(settings->context());
+  DCHECK(settings->context()->web_settings());
+
+  const auto& web_settings = settings->context()->web_settings();
+  return web_settings->media_settings();
+}
+
 HTMLVideoElement::HTMLVideoElement(Document* document)
     : HTMLMediaElement(document, base::Token(kTagName)) {}
 
@@ -98,9 +108,15 @@
   }
 }
 
-scoped_refptr<DecodeTargetProvider>
-HTMLVideoElement::GetDecodeTargetProvider() {
+scoped_refptr<DecodeTargetProvider> HTMLVideoElement::GetDecodeTargetProvider(
+    bool* paint_to_black) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(paint_to_black);
+
+  *paint_to_black = GetMediaSettings(environment_settings())
+                        .IsPaintingVideoBackgroundToBlack()
+                        .value_or(false);
+
   return player() ? player()->GetDecodeTargetProvider() : NULL;
 }
 
diff --git a/cobalt/dom/html_video_element.h b/cobalt/dom/html_video_element.h
index b98b6f6..914a69e 100644
--- a/cobalt/dom/html_video_element.h
+++ b/cobalt/dom/html_video_element.h
@@ -52,7 +52,10 @@
   // From HTMLElement
   scoped_refptr<HTMLVideoElement> AsHTMLVideoElement() override { return this; }
 
-  scoped_refptr<DecodeTargetProvider> GetDecodeTargetProvider();
+  // When the return value is nullptr, and |paint_to_black| is set to true, the
+  // caller is expected to paint the area covered by the video to black.
+  scoped_refptr<DecodeTargetProvider> GetDecodeTargetProvider(
+      bool* paint_to_black);
 
   WebMediaPlayer::SetBoundsCB GetSetBoundsCB();
 
diff --git a/cobalt/dom/media_settings.cc b/cobalt/dom/media_settings.cc
index 2915df3..ed39dbe 100644
--- a/cobalt/dom/media_settings.cc
+++ b/cobalt/dom/media_settings.cc
@@ -77,6 +77,12 @@
       LOG(INFO) << name << ": set to " << value;
       return true;
     }
+  } else if (name == "MediaElement.PaintingVideoBackgroundToBlack") {
+    if (value == 0 || value == 1) {
+      is_painting_video_background_to_black_ = value != 0;
+      LOG(INFO) << name << ": set to " << value;
+      return true;
+    }
   } else {
     LOG(WARNING) << "Ignore unknown setting with name \"" << name << "\"";
     return false;
diff --git a/cobalt/dom/media_settings.h b/cobalt/dom/media_settings.h
index eace022..9f1a6f4 100644
--- a/cobalt/dom/media_settings.h
+++ b/cobalt/dom/media_settings.h
@@ -41,6 +41,7 @@
 
   virtual base::Optional<int>
   GetMediaElementTimeupdateEventIntervalInMilliseconds() const = 0;
+  virtual base::Optional<bool> IsPaintingVideoBackgroundToBlack() const = 0;
 
  protected:
   MediaSettings() = default;
@@ -89,6 +90,9 @@
       const override {
     return media_element_timeupdate_event_interval_in_milliseconds_;
   }
+  base::Optional<bool> IsPaintingVideoBackgroundToBlack() const override {
+    return is_painting_video_background_to_black_;
+  }
 
   // Returns true when the setting associated with `name` is set to `value`.
   // Returns false when `name` is not associated with any settings, or if
@@ -106,6 +110,8 @@
   base::Optional<int> max_source_buffer_append_size_in_bytes_;
 
   base::Optional<int> media_element_timeupdate_event_interval_in_milliseconds_;
+
+  base::Optional<bool> is_painting_video_background_to_black_;
 };
 
 }  // namespace dom
diff --git a/cobalt/dom/media_settings_test.cc b/cobalt/dom/media_settings_test.cc
index 8850f72..e4addfe 100644
--- a/cobalt/dom/media_settings_test.cc
+++ b/cobalt/dom/media_settings_test.cc
@@ -31,6 +31,7 @@
   EXPECT_FALSE(impl.GetMaxSizeForImmediateJob());
   EXPECT_FALSE(impl.GetMaxSourceBufferAppendSizeInBytes());
   EXPECT_FALSE(impl.GetMediaElementTimeupdateEventIntervalInMilliseconds());
+  EXPECT_FALSE(impl.IsPaintingVideoBackgroundToBlack());
 }
 
 TEST(MediaSettingsImplTest, SunnyDay) {
@@ -46,6 +47,7 @@
   ASSERT_TRUE(impl.Set("MediaSource.MaxSourceBufferAppendSizeInBytes", 100000));
   ASSERT_TRUE(
       impl.Set("MediaElement.TimeupdateEventIntervalInMilliseconds", 100001));
+  ASSERT_TRUE(impl.Set("MediaElement.PaintingVideoBackgroundToBlack", 1));
 
   EXPECT_EQ(impl.GetSourceBufferEvictExtraInBytes().value(), 100);
   EXPECT_EQ(impl.GetMinimumProcessorCountToOffloadAlgorithm().value(), 101);
@@ -56,6 +58,7 @@
   EXPECT_EQ(impl.GetMaxSourceBufferAppendSizeInBytes().value(), 100000);
   EXPECT_EQ(impl.GetMediaElementTimeupdateEventIntervalInMilliseconds().value(),
             100001);
+  EXPECT_TRUE(impl.IsPaintingVideoBackgroundToBlack().value());
 }
 
 TEST(MediaSettingsImplTest, RainyDay) {
@@ -71,6 +74,7 @@
   ASSERT_FALSE(impl.Set("MediaSource.MaxSourceBufferAppendSizeInBytes", 0));
   ASSERT_FALSE(
       impl.Set("MediaElement.TimeupdateEventIntervalInMilliseconds", 0));
+  ASSERT_FALSE(impl.Set("MediaElement.PaintingVideoBackgroundToBlack", 2));
 
   EXPECT_FALSE(impl.GetSourceBufferEvictExtraInBytes());
   EXPECT_FALSE(impl.GetMinimumProcessorCountToOffloadAlgorithm());
@@ -80,6 +84,7 @@
   EXPECT_FALSE(impl.GetMaxSizeForImmediateJob());
   EXPECT_FALSE(impl.GetMaxSourceBufferAppendSizeInBytes());
   EXPECT_FALSE(impl.GetMediaElementTimeupdateEventIntervalInMilliseconds());
+  EXPECT_FALSE(impl.IsPaintingVideoBackgroundToBlack());
 }
 
 TEST(MediaSettingsImplTest, ZeroValuesWork) {
@@ -95,6 +100,7 @@
   // O is an invalid value for "MediaSource.MaxSourceBufferAppendSizeInBytes".
   // O is an invalid value for
   // "MediaElement.TimeupdateEventIntervalInMilliseconds".
+  ASSERT_TRUE(impl.Set("MediaElement.PaintingVideoBackgroundToBlack", 0));
 
   EXPECT_EQ(impl.GetSourceBufferEvictExtraInBytes().value(), 0);
   EXPECT_EQ(impl.GetMinimumProcessorCountToOffloadAlgorithm().value(), 0);
@@ -102,6 +108,7 @@
   EXPECT_FALSE(impl.IsAvoidCopyingArrayBufferEnabled().value());
   EXPECT_FALSE(impl.IsCallingEndedWhenClosedEnabled().value());
   EXPECT_EQ(impl.GetMaxSizeForImmediateJob().value(), 0);
+  EXPECT_FALSE(impl.IsPaintingVideoBackgroundToBlack().value());
 }
 
 TEST(MediaSettingsImplTest, Updatable) {
@@ -117,6 +124,7 @@
   ASSERT_TRUE(impl.Set("MediaSource.MaxSourceBufferAppendSizeInBytes", 1));
   ASSERT_TRUE(
       impl.Set("MediaElement.TimeupdateEventIntervalInMilliseconds", 1));
+  ASSERT_TRUE(impl.Set("MediaElement.PaintingVideoBackgroundToBlack", 0));
 
   ASSERT_TRUE(impl.Set("MediaSource.SourceBufferEvictExtraInBytes", 1));
   ASSERT_TRUE(
@@ -128,6 +136,7 @@
   ASSERT_TRUE(impl.Set("MediaSource.MaxSourceBufferAppendSizeInBytes", 2));
   ASSERT_TRUE(
       impl.Set("MediaElement.TimeupdateEventIntervalInMilliseconds", 2));
+  ASSERT_TRUE(impl.Set("MediaElement.PaintingVideoBackgroundToBlack", 1));
 
   EXPECT_EQ(impl.GetSourceBufferEvictExtraInBytes().value(), 1);
   EXPECT_EQ(impl.GetMinimumProcessorCountToOffloadAlgorithm().value(), 1);
@@ -138,6 +147,7 @@
   EXPECT_EQ(impl.GetMaxSourceBufferAppendSizeInBytes().value(), 2);
   EXPECT_EQ(impl.GetMediaElementTimeupdateEventIntervalInMilliseconds().value(),
             2);
+  EXPECT_TRUE(impl.IsPaintingVideoBackgroundToBlack().value());
 }
 
 TEST(MediaSettingsImplTest, InvalidSettingNames) {
diff --git a/cobalt/h5vcc/h5vcc_metric_type.idl b/cobalt/h5vcc/h5vcc_metric_type.idl
index 762cc9e..79bf952 100644
--- a/cobalt/h5vcc/h5vcc_metric_type.idl
+++ b/cobalt/h5vcc/h5vcc_metric_type.idl
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 // The various metric types we support to be published through the H5vccMetrics
-// API.
+// API. The proto will be serialized and then base64 encoded.
 enum H5vccMetricType {
   // //third_party/metrics_proto/chrome_user_metrics_extension.proto
   // ChromeUserMetricsExtension proto.
diff --git a/cobalt/h5vcc/h5vcc_metrics.idl b/cobalt/h5vcc/h5vcc_metrics.idl
index c9f62a9..1d7292c 100644
--- a/cobalt/h5vcc/h5vcc_metrics.idl
+++ b/cobalt/h5vcc/h5vcc_metrics.idl
@@ -61,7 +61,8 @@
 };
 
 // Callback invoked when a new metric payload is ready to be published. The
-// payload is a serialized protobuf and the metric type should give the consumer
-// a hint on how to deserialize it. See h5vcc_metric_type.idl for more info.
+// payload is a base64 encoded serialized protobuf and the metric type should
+// give the consumer a hint on how to deserialize it. See h5vcc_metric_type.idl
+// for more info.
 callback H5vccMetricEventHandler =
     void(H5vccMetricType metricType, DOMString metricPayload);
diff --git a/cobalt/h5vcc/h5vcc_settings.cc b/cobalt/h5vcc/h5vcc_settings.cc
index 4babfd9..e6c7b26 100644
--- a/cobalt/h5vcc/h5vcc_settings.cc
+++ b/cobalt/h5vcc/h5vcc_settings.cc
@@ -106,10 +106,16 @@
   }
 
   if (name.compare(kQUIC) == 0) {
-    if (!network_module_) {
+    if (!persistent_settings_) {
       return false;
     } else {
-      network_module_->SetEnableQuic(value != 0);
+      persistent_settings_->SetPersistentSetting(
+          network::kQuicEnabledPersistentSettingsKey,
+          std::make_unique<base::Value>(value != 0));
+      // Tell NetworkModule (if exists) to re-query persistent settings.
+      if (network_module_) {
+        network_module_->SetEnableQuicFromPersistentSettings();
+      }
       return true;
     }
   }
diff --git a/cobalt/layout/box_generator.cc b/cobalt/layout/box_generator.cc
index 6eee421..b279f0c 100644
--- a/cobalt/layout/box_generator.cc
+++ b/cobalt/layout/box_generator.cc
@@ -339,22 +339,31 @@
   // If the optional is disengaged, then we don't know if punch out is enabled
   // or not.
   base::Optional<ReplacedBox::ReplacedBoxMode> replaced_box_mode;
-  if (video_element->GetDecodeTargetProvider()) {
+  bool paint_to_black = false;
+  auto decode_target_provider =
+      video_element->GetDecodeTargetProvider(&paint_to_black);
+  if (decode_target_provider) {
     DecodeTargetProvider::OutputMode output_mode =
-        video_element->GetDecodeTargetProvider()->GetOutputMode();
-    if (output_mode != DecodeTargetProvider::kOutputModeInvalid) {
+        decode_target_provider->GetOutputMode();
+    if (output_mode == DecodeTargetProvider::kOutputModeInvalid) {
+      if (paint_to_black) {
+        replaced_box_mode = ReplacedBox::ReplacedBoxMode::kPaintToBlack;
+      }
+    } else {
       replaced_box_mode =
           (output_mode == DecodeTargetProvider::kOutputModePunchOut)
               ? ReplacedBox::ReplacedBoxMode::kPunchOutVideo
-              : ReplacedBox::ReplacedBoxMode::kVideo;
+              : ReplacedBox::ReplacedBoxMode::kDecodeToTextureVideo;
     }
+  } else {
+    // ReplacedBox won't paint anything when |decode_target_provider| is
+    // nullptr, as |replace_image_cb_| is also null in this case.
   }
 
   ReplacedBoxGenerator replaced_box_generator(
       video_element->css_computed_style_declaration(),
-      video_element->GetDecodeTargetProvider()
-          ? base::Bind(GetVideoFrame, video_element->GetDecodeTargetProvider(),
-                       resource_provider)
+      decode_target_provider
+          ? base::Bind(GetVideoFrame, decode_target_provider, resource_provider)
           : ReplacedBox::ReplaceImageCB(),
       video_element->GetSetBoundsCB(), *paragraph_, text_position,
       base::nullopt, base::nullopt, base::nullopt, context_, replaced_box_mode,
diff --git a/cobalt/layout/replaced_box.cc b/cobalt/layout/replaced_box.cc
index 21eba9a..a31407e 100644
--- a/cobalt/layout/replaced_box.cc
+++ b/cobalt/layout/replaced_box.cc
@@ -316,9 +316,14 @@
     return;
   }
 
-  if (replaced_box_mode_ == base::nullopt) {
-    // If we don't have a data stream associated with this video [yet], then
-    // we don't yet know if it is punched out or not, and so render black.
+  if (!replaced_box_mode_.has_value()) {
+    // Don't render anything, so any background color or image will be visible.
+    return;
+  }
+
+  if (replaced_box_mode_ == ReplacedBoxMode::kPaintToBlack) {
+    // Explicitly render black if we don't have a data stream associated with
+    // this video [yet], this is the same as the previous behavior.
     border_node_builder->AddChild(new RectNode(
         math::RectF(content_box_size()),
         std::unique_ptr<render_tree::Brush>(new render_tree::SolidColorBrush(
@@ -327,7 +332,7 @@
     return;
   }
 
-  if (*replaced_box_mode_ == ReplacedBox::ReplacedBoxMode::kLottie) {
+  if (*replaced_box_mode_ == ReplacedBoxMode::kLottie) {
     AnimateNode::Builder animate_node_builder;
     scoped_refptr<LottieNode> lottie_node =
         new LottieNode(nullptr, math::RectF());
@@ -347,7 +352,7 @@
   // Map-to-mesh is only supported with decode-to-texture videos.
   const bool supports_mtm =
       replaced_box_mode_ &&
-      *replaced_box_mode_ == ReplacedBox::ReplacedBoxMode::kVideo;
+      *replaced_box_mode_ == ReplacedBoxMode::kDecodeToTextureVideo;
 
   if (supports_mtm && mtm_filter_function &&
       mtm_filter_function->mesh_spec().mesh_type() !=
@@ -759,7 +764,7 @@
   scoped_refptr<CompositionNode> composition_node =
       new CompositionNode(composition_node_builder);
 
-  if (*replaced_box_mode_ == ReplacedBox::ReplacedBoxMode::kPunchOutVideo) {
+  if (*replaced_box_mode_ == ReplacedBoxMode::kPunchOutVideo) {
     LetterboxDimensions letterbox_dims =
         GetLetterboxDimensions(content_size_, content_box_size());
     AddLetterboxedPunchThroughVideoNodeToRenderTree(
diff --git a/cobalt/layout/replaced_box.h b/cobalt/layout/replaced_box.h
index f4384e5..3837b7c 100644
--- a/cobalt/layout/replaced_box.h
+++ b/cobalt/layout/replaced_box.h
@@ -43,7 +43,12 @@
   typedef base::Callback<scoped_refptr<render_tree::Image>()> ReplaceImageCB;
   typedef render_tree::PunchThroughVideoNode::SetBoundsCB SetBoundsCB;
 
-  enum class ReplacedBoxMode { kVideo, kPunchOutVideo, kLottie };
+  enum class ReplacedBoxMode {
+    kPaintToBlack,  // Paint a black rectangle
+    kDecodeToTextureVideo,
+    kPunchOutVideo,
+    kLottie
+  };
 
   ReplacedBox(const scoped_refptr<cssom::CSSComputedStyleDeclaration>&
                   css_computed_style_declaration,
diff --git a/cobalt/loader/image/animated_image_tracker.cc b/cobalt/loader/image/animated_image_tracker.cc
index 98dadca..c7a3e73 100644
--- a/cobalt/loader/image/animated_image_tracker.cc
+++ b/cobalt/loader/image/animated_image_tracker.cc
@@ -15,7 +15,9 @@
 #include "cobalt/loader/image/animated_image_tracker.h"
 
 #include <algorithm>
+#include <utility>
 
+#include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event.h"
 #include "cobalt/base/polymorphic_downcast.h"
 
@@ -24,8 +26,22 @@
 namespace image {
 
 AnimatedImageTracker::AnimatedImageTracker(
+    const char* name,
     base::ThreadPriority animated_image_decode_thread_priority)
-    : animated_image_decode_thread_("AnimatedImage") {
+    : animated_image_decode_thread_("AnimatedImage"),
+      name_(name),
+      count_animated_images_active(
+          base::StringPrintf("Count.%s.AnimatedImage.Active", name), 0,
+          "Total number of active animated image decoders."),
+      count_animated_frames_decoded(
+          base::StringPrintf("Count.%s.AnimatedImage.DecodedFrames", name), 0,
+          "Total number of decoded animated image frames."),
+      count_animated_frames_decoding_underrun(
+          base::StringPrintf("Count.%s.AnimatedImage.DecodingUnderruns", name),
+          0, "Number of underruns from decoding animated images"),
+      count_animated_frames_decoding_overrun(
+          base::StringPrintf("Count.%s.AnimatedImage.DecodingOverruns", name),
+          0, "Number of overruns from decoding animated images") {
   TRACE_EVENT0("cobalt::loader::image", "AnimatedImageTracker::RecordImage()");
   base::Thread::Options options(base::MessageLoop::TYPE_DEFAULT,
                                 0 /* default stack size */);
@@ -123,6 +139,17 @@
   playing_urls_.clear();
 }
 
+void AnimatedImageTracker::OnRenderTreeRasterized() {
+  count_animated_images_active = playing_urls_.size();
+  for (const auto& playing_url : playing_urls_) {
+    auto image = image_map_[playing_url.first].get();
+    auto stats = image->GetFrameDeltaStats();
+    count_animated_frames_decoded += stats.frames_decoded;
+    count_animated_frames_decoding_underrun += stats.frames_underrun;
+    count_animated_frames_decoding_overrun += stats.frames_overrun;
+  }
+}
+
 }  // namespace image
 }  // namespace loader
 }  // namespace cobalt
diff --git a/cobalt/loader/image/animated_image_tracker.h b/cobalt/loader/image/animated_image_tracker.h
index f6f38e4..6df920b 100644
--- a/cobalt/loader/image/animated_image_tracker.h
+++ b/cobalt/loader/image/animated_image_tracker.h
@@ -20,6 +20,7 @@
 
 #include "base/containers/small_map.h"
 #include "base/threading/thread.h"
+#include "cobalt/base/c_val.h"
 #include "cobalt/base/unused.h"
 #include "cobalt/loader/image/image.h"
 #include "url/gurl.h"
@@ -33,7 +34,8 @@
 // playing status is updated hence decoding is turned on / off for it.
 class AnimatedImageTracker {
  public:
-  explicit AnimatedImageTracker(
+  AnimatedImageTracker(
+      const char* name,
       base::ThreadPriority animated_image_decode_thread_priority);
   ~AnimatedImageTracker();
 
@@ -54,6 +56,9 @@
   // animations.
   void Reset();
 
+  // Called from WebModule to compute image animation related stats.
+  void OnRenderTreeRasterized();
+
  private:
   void OnDisplayStart(loader::image::AnimatedImage* image);
   void OnDisplayEnd(loader::image::AnimatedImage* image);
@@ -72,6 +77,15 @@
   URLToIntMap current_url_counts_;
   URLSet playing_urls_;
 
+  // The name of the WebModule this AnimatedImage tracker belongs to, for CVals.
+  const std::string name_;
+
+  // Animated image counters
+  base::CVal<int, base::CValPublic> count_animated_images_active;
+  base::CVal<int, base::CValPublic> count_animated_frames_decoded;
+  base::CVal<int, base::CValPublic> count_animated_frames_decoding_underrun;
+  base::CVal<int, base::CValPublic> count_animated_frames_decoding_overrun;
+
   // Used to ensure that all AnimatedImageTracker methods are called on the
   // same thread (*not* |animated_image_decode_thread_|), the thread that we
   // were constructed on.
diff --git a/cobalt/loader/image/animated_webp_image.cc b/cobalt/loader/image/animated_webp_image.cc
index cab2ca0..312098d 100644
--- a/cobalt/loader/image/animated_webp_image.cc
+++ b/cobalt/loader/image/animated_webp_image.cc
@@ -146,6 +146,8 @@
     return;
   }
   is_playing_ = true;
+  current_stats.frames_underrun = 0;
+  current_stats.frames_overrun = 0;
 
   if (received_first_frame_) {
     StartDecoding();
@@ -169,7 +171,8 @@
 void AnimatedWebPImage::StartDecoding() {
   TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::StartDecoding()");
   lock_.AssertAcquired();
-  current_frame_time_ = base::TimeTicks::Now();
+  decoding_start_time_ = current_frame_time_ = base::TimeTicks::Now();
+  current_stats.frames_decoded = 0;
   if (task_runner_->BelongsToCurrentThread()) {
     DecodeFrames();
   } else {
@@ -265,6 +268,7 @@
       LOG(ERROR) << "Failed to decode WebP image frame.";
       return false;
     }
+    current_stats.frames_decoded++;
   }
 
   // Alpha blend the current frame on top of the buffer.
@@ -349,6 +353,7 @@
   // Always wait for a consumer to consume the previous frame before moving
   // forward with decoding the next frame.
   if (!frame_provider_->FrameConsumed()) {
+    current_stats.frames_overrun++;
     return false;
   }
 
@@ -381,6 +386,7 @@
   if (next_frame_time_ < current_time) {
     // Don't let the animation fall back for more than a frame.
     next_frame_time_ = current_time;
+    current_stats.frames_underrun++;
   }
 
   return true;
@@ -426,6 +432,25 @@
   return target_canvas;
 }
 
+AnimatedImage::AnimatedImageDecodingStats
+AnimatedWebPImage::GetFrameDeltaStats() {
+  AnimatedImageDecodingStats result;
+  if (current_stats.frames_decoded >= last_stats.frames_decoded) {
+    result.frames_decoded =
+        current_stats.frames_decoded - last_stats.frames_decoded;
+    result.frames_underrun =
+        current_stats.frames_underrun - last_stats.frames_underrun;
+    result.frames_overrun =
+        current_stats.frames_overrun - last_stats.frames_overrun;
+  } else {
+    // There was a reset somewhere
+    // Simply return total, this discards any overflow data we might have had.
+    result = current_stats;
+  }
+  last_stats = current_stats;
+  return result;
+}
+
 }  // namespace image
 }  // namespace loader
 }  // namespace cobalt
diff --git a/cobalt/loader/image/animated_webp_image.h b/cobalt/loader/image/animated_webp_image.h
index 135bf6a..4d1be04 100644
--- a/cobalt/loader/image/animated_webp_image.h
+++ b/cobalt/loader/image/animated_webp_image.h
@@ -66,6 +66,8 @@
   // Returns the render image of the frame for debugging
   scoped_refptr<render_tree::Image> GetFrameForDebugging(int target_frame);
 
+  AnimatedImageDecodingStats GetFrameDeltaStats() override;
+
  private:
   ~AnimatedWebPImage() override;
 
@@ -119,12 +121,17 @@
   base::CancelableClosure decode_closure_;
   base::TimeTicks current_frame_time_;
   base::Optional<base::TimeTicks> next_frame_time_;
+
   // The original encoded data.
   std::vector<uint8> data_buffer_;
   scoped_refptr<render_tree::Image> current_canvas_;
   scoped_refptr<FrameProvider> frame_provider_;
   base::Lock lock_;
 
+  base::TimeTicks decoding_start_time_;
+  AnimatedImageDecodingStats current_stats;
+  AnimatedImageDecodingStats last_stats;
+
   // Makes sure that the thread that sets the task_runner is always consistent.
   // This is the thread sending Play()/Stop() calls, and is not necessarily
   // the same thread that the task_runner itself is running on.
diff --git a/cobalt/loader/image/image.h b/cobalt/loader/image/image.h
index af99363..bac9849 100644
--- a/cobalt/loader/image/image.h
+++ b/cobalt/loader/image/image.h
@@ -145,6 +145,16 @@
     image_node_builder->destination_rect = destination_rect;
     image_node_builder->local_transform = local_transform;
   }
+
+  // Frame counters for decoding.
+  struct AnimatedImageDecodingStats {
+    unsigned int frames_decoded = 0;
+    unsigned int frames_underrun = 0;
+    unsigned int frames_overrun = 0;
+  };
+
+  // Returns decoded frame stats since the last call, as a delta.
+  virtual AnimatedImageDecodingStats GetFrameDeltaStats() = 0;
 };
 
 }  // namespace image
diff --git a/cobalt/media/base/sbplayer_bridge.cc b/cobalt/media/base/sbplayer_bridge.cc
index fad7fda..92dc4da 100644
--- a/cobalt/media/base/sbplayer_bridge.cc
+++ b/cobalt/media/base/sbplayer_bridge.cc
@@ -707,7 +707,11 @@
     // a method of querying that texture.
     decode_target_provider_->SetGetCurrentSbDecodeTargetFunction(base::Bind(
         &SbPlayerBridge::GetCurrentSbDecodeTarget, base::Unretained(this)));
+    SB_LOG(INFO) << "Playing in decode-to-texture mode.";
+  } else {
+    SB_LOG(INFO) << "Playing in punch-out mode.";
   }
+
   decode_target_provider_->SetOutputMode(
       ToVideoFrameProviderOutputMode(output_mode_));
 
@@ -773,7 +777,11 @@
     // a method of querying that texture.
     decode_target_provider_->SetGetCurrentSbDecodeTargetFunction(base::Bind(
         &SbPlayerBridge::GetCurrentSbDecodeTarget, base::Unretained(this)));
+    SB_LOG(INFO) << "Playing in decode-to-texture mode.";
+  } else {
+    SB_LOG(INFO) << "Playing in punch-out mode.";
   }
+
   decode_target_provider_->SetOutputMode(
       ToVideoFrameProviderOutputMode(output_mode_));
   set_bounds_helper_->SetPlayerBridge(this);
diff --git a/cobalt/media/file_data_source_test.cc b/cobalt/media/file_data_source_test.cc
index 4fe7fe7..34989f8 100644
--- a/cobalt/media/file_data_source_test.cc
+++ b/cobalt/media/file_data_source_test.cc
@@ -31,6 +31,9 @@
 }
 
 TEST(FileDataSourceTest, SunnyDay) {
+  // TODO(b/292134341): Test fails on all platforms.
+  GTEST_SKIP();
+
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   FileDataSource data_source(
       GURL("file:///cobalt/media/testing/data/"
diff --git a/cobalt/network/network_module.cc b/cobalt/network/network_module.cc
index b7caed4..8154b7c 100644
--- a/cobalt/network/network_module.cc
+++ b/cobalt/network/network_module.cc
@@ -98,11 +98,16 @@
                             custom_proxy_rules));
 }
 
-void NetworkModule::SetEnableQuic(bool enable_quic) {
-  task_runner()->PostTask(
-      FROM_HERE,
-      base::Bind(&URLRequestContext::SetEnableQuic,
-                 base::Unretained(url_request_context_.get()), enable_quic));
+void NetworkModule::SetEnableQuicFromPersistentSettings() {
+  // Called on initialization and when the persistent setting is changed.
+  if (options_.persistent_settings != nullptr) {
+    bool enable_quic = options_.persistent_settings->GetPersistentSettingAsBool(
+        kQuicEnabledPersistentSettingsKey, false);
+    task_runner()->PostTask(
+        FROM_HERE,
+        base::Bind(&URLRequestContext::SetEnableQuic,
+                   base::Unretained(url_request_context_.get()), enable_quic));
+  }
 }
 
 void NetworkModule::SetEnableClientHintHeadersFlagsFromPersistentSettings() {
@@ -193,6 +198,8 @@
   DCHECK(url_request_context_);
   url_request_context_getter_ = new network::URLRequestContextGetter(
       url_request_context_.get(), thread_.get());
+
+  SetEnableQuicFromPersistentSettings();
 }
 
 void NetworkModule::OnCreate(base::WaitableEvent* creation_event) {
diff --git a/cobalt/network/network_module.h b/cobalt/network/network_module.h
index 6066324..b6c8bd2 100644
--- a/cobalt/network/network_module.h
+++ b/cobalt/network/network_module.h
@@ -59,6 +59,8 @@
   kCallTypeXHR = (1u << 5),
 };
 
+const char kQuicEnabledPersistentSettingsKey[] = "QUICEnabled";
+
 // Holds bit mask flag, read into |enable_client_hint_headers_flags_|.
 const char kClientHintHeadersEnabledPersistentSettingsKey[] =
     "clientHintHeadersEnabled";
@@ -128,7 +130,7 @@
 #endif
   void SetProxy(const std::string& custom_proxy_rules);
 
-  void SetEnableQuic(bool enable_quic);
+  void SetEnableQuicFromPersistentSettings();
 
   // Checks persistent settings to determine if Client Hint Headers are enabled.
   void SetEnableClientHintHeadersFlagsFromPersistentSettings();
diff --git a/cobalt/persistent_storage/persistent_settings.cc b/cobalt/persistent_storage/persistent_settings.cc
index ce64f97..52bc864 100644
--- a/cobalt/persistent_storage/persistent_settings.cc
+++ b/cobalt/persistent_storage/persistent_settings.cc
@@ -19,7 +19,6 @@
 
 #include "base/bind.h"
 #include "components/prefs/json_pref_store.h"
-#include "components/prefs/json_read_only_pref_store.h"
 #include "starboard/common/file.h"
 #include "starboard/common/log.h"
 #include "starboard/configuration_constants.h"
@@ -51,37 +50,23 @@
       std::string(storage_dir.data()) + kSbFileSepString + file_name;
   LOG(INFO) << "Persistent settings file path: " << persistent_settings_file_;
 
-  // Initialize pref_store_ with a JSONReadOnlyPrefStore, Used for
-  // synchronous PersistentSettings::Get calls made before the asynchronous
-  // InitializeWriteablePrefStore initializes pref_store_ with a writable
-  // instance.
-  {
-    base::AutoLock auto_lock(pref_store_lock_);
-    pref_store_ = base::MakeRefCounted<JsonReadOnlyPrefStore>(
-        base::FilePath(persistent_settings_file_));
-    pref_store_->ReadPrefs();
-  }
-
   message_loop()->task_runner()->PostTask(
-      FROM_HERE, base::Bind(&PersistentSettings::InitializeWriteablePrefStore,
+      FROM_HERE, base::Bind(&PersistentSettings::InitializePrefStore,
                             base::Unretained(this)));
+  pref_store_initialized_.Wait();
+  destruction_observer_added_.Wait();
 }
 
 PersistentSettings::~PersistentSettings() {
   DCHECK(message_loop());
   DCHECK(thread_.IsRunning());
 
-  // Ensure that the destruction observer got added and the pref store was
-  // initialized before stopping the thread. Stop the thread. This will cause
-  // the destruction observer to be notified.
-  writeable_pref_store_initialized_.Wait();
-  destruction_observer_added_.Wait();
   // Wait for all previously posted tasks to finish.
   thread_.message_loop()->task_runner()->WaitForFence();
   thread_.Stop();
 }
 
-void PersistentSettings::InitializeWriteablePrefStore() {
+void PersistentSettings::InitializePrefStore() {
   DCHECK_EQ(base::MessageLoop::current(), message_loop());
   // Register as a destruction observer to shut down the thread once all
   // pending tasks have been executed and the message loop is about to be
@@ -94,9 +79,7 @@
     pref_store_ = base::MakeRefCounted<JsonPrefStore>(
         base::FilePath(persistent_settings_file_));
     pref_store_->ReadPrefs();
-    // PersistentSettings Set and Remove Helper methods will now be able to
-    // access the pref_store_ initialized from the dedicated thread_.
-    writeable_pref_store_initialized_.Signal();
+    pref_store_initialized_.Signal();
   }
   validated_initial_settings_ = GetPersistentSettingAsBool(kValidated, false);
   if (!validated_initial_settings_) {
@@ -116,10 +99,9 @@
   DCHECK_EQ(base::MessageLoop::current(), message_loop());
   if (!validated_initial_settings_) {
     base::AutoLock auto_lock(pref_store_lock_);
-    writeable_pref_store()->SetValue(
-        kValidated, std::make_unique<base::Value>(true),
-        WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
-    writeable_pref_store()->CommitPendingWrite();
+    pref_store_->SetValue(kValidated, std::make_unique<base::Value>(true),
+                          WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+    CommitPendingWrite(false);
     validated_initial_settings_ = true;
   }
 }
@@ -190,7 +172,7 @@
 
 void PersistentSettings::SetPersistentSetting(
     const std::string& key, std::unique_ptr<base::Value> value,
-    base::OnceClosure closure) {
+    base::OnceClosure closure, bool blocking) {
   if (key == kValidated) {
     LOG(ERROR) << "Cannot set protected persistent setting: " << key;
     return;
@@ -198,21 +180,20 @@
   message_loop()->task_runner()->PostTask(
       FROM_HERE, base::BindOnce(&PersistentSettings::SetPersistentSettingHelper,
                                 base::Unretained(this), key, std::move(value),
-                                std::move(closure)));
+                                std::move(closure), blocking));
 }
 
 void PersistentSettings::SetPersistentSettingHelper(
     const std::string& key, std::unique_ptr<base::Value> value,
-    base::OnceClosure closure) {
+    base::OnceClosure closure, bool blocking) {
   DCHECK_EQ(base::MessageLoop::current(), message_loop());
   if (validated_initial_settings_) {
     base::AutoLock auto_lock(pref_store_lock_);
-    writeable_pref_store()->SetValue(
-        kValidated, std::make_unique<base::Value>(false),
-        WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
-    writeable_pref_store()->SetValue(
-        key, std::move(value), WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
-    writeable_pref_store()->CommitPendingWrite();
+    pref_store_->SetValue(kValidated, std::make_unique<base::Value>(false),
+                          WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+    pref_store_->SetValue(key, std::move(value),
+                          WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+    CommitPendingWrite(blocking);
   } else {
     LOG(ERROR) << "Cannot set persistent setting while unvalidated: " << key;
   }
@@ -220,24 +201,24 @@
 }
 
 void PersistentSettings::RemovePersistentSetting(const std::string& key,
-                                                 base::OnceClosure closure) {
+                                                 base::OnceClosure closure,
+                                                 bool blocking) {
   message_loop()->task_runner()->PostTask(
       FROM_HERE,
       base::BindOnce(&PersistentSettings::RemovePersistentSettingHelper,
-                     base::Unretained(this), key, std::move(closure)));
+                     base::Unretained(this), key, std::move(closure),
+                     blocking));
 }
 
 void PersistentSettings::RemovePersistentSettingHelper(
-    const std::string& key, base::OnceClosure closure) {
+    const std::string& key, base::OnceClosure closure, bool blocking) {
   DCHECK_EQ(base::MessageLoop::current(), message_loop());
   if (validated_initial_settings_) {
     base::AutoLock auto_lock(pref_store_lock_);
-    writeable_pref_store()->SetValue(
-        kValidated, std::make_unique<base::Value>(false),
-        WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
-    writeable_pref_store()->RemoveValue(
-        key, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
-    writeable_pref_store()->CommitPendingWrite();
+    pref_store_->SetValue(kValidated, std::make_unique<base::Value>(false),
+                          WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+    pref_store_->RemoveValue(key, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+    CommitPendingWrite(blocking);
   } else {
     LOG(ERROR) << "Cannot remove persistent setting while unvalidated: " << key;
   }
@@ -257,12 +238,24 @@
   if (validated_initial_settings_) {
     starboard::SbFileDeleteRecursive(persistent_settings_file_.c_str(), true);
     base::AutoLock auto_lock(pref_store_lock_);
-    writeable_pref_store()->ReadPrefs();
+    pref_store_->ReadPrefs();
   } else {
     LOG(ERROR) << "Cannot delete persistent setting while unvalidated.";
   }
   std::move(closure).Run();
 }
 
+void PersistentSettings::CommitPendingWrite(bool blocking) {
+  if (blocking) {
+    base::WaitableEvent written;
+    pref_store_->CommitPendingWrite(
+        base::OnceClosure(),
+        base::BindOnce(&base::WaitableEvent::Signal, Unretained(&written)));
+    written.Wait();
+  } else {
+    pref_store_->CommitPendingWrite();
+  }
+}
+
 }  // namespace persistent_storage
 }  // namespace cobalt
diff --git a/cobalt/persistent_storage/persistent_settings.h b/cobalt/persistent_storage/persistent_settings.h
index 0cf789f..6c2c157 100644
--- a/cobalt/persistent_storage/persistent_settings.h
+++ b/cobalt/persistent_storage/persistent_settings.h
@@ -71,35 +71,34 @@
 
   void SetPersistentSetting(
       const std::string& key, std::unique_ptr<base::Value> value,
-      base::OnceClosure closure = std::move(base::DoNothing()));
+      base::OnceClosure closure = std::move(base::DoNothing()),
+      bool blocking = false);
 
   void RemovePersistentSetting(
       const std::string& key,
-      base::OnceClosure closure = std::move(base::DoNothing()));
+      base::OnceClosure closure = std::move(base::DoNothing()),
+      bool blocking = false);
 
   void DeletePersistentSettings(
       base::OnceClosure closure = std::move(base::DoNothing()));
 
  private:
   // Called by the constructor to initialize pref_store_ from
-  // the dedicated thread_ as a writeable JSONPrefStore.
-  void InitializeWriteablePrefStore();
+  // the dedicated thread_ as a JSONPrefStore.
+  void InitializePrefStore();
 
   void ValidatePersistentSettingsHelper();
 
   void SetPersistentSettingHelper(const std::string& key,
                                   std::unique_ptr<base::Value> value,
-                                  base::OnceClosure closure);
+                                  base::OnceClosure closure, bool blocking);
 
   void RemovePersistentSettingHelper(const std::string& key,
-                                     base::OnceClosure closure);
+                                     base::OnceClosure closure, bool blocking);
 
   void DeletePersistentSettingsHelper(base::OnceClosure closure);
 
-  scoped_refptr<PersistentPrefStore> writeable_pref_store() {
-    writeable_pref_store_initialized_.Wait();
-    return pref_store_;
-  }
+  void CommitPendingWrite(bool blocking);
 
   // Persistent settings file path.
   std::string persistent_settings_file_;
@@ -117,7 +116,7 @@
 
   // This event is used to signal when Initialize has been called and
   // pref_store_ mutations can now occur.
-  base::WaitableEvent writeable_pref_store_initialized_ = {
+  base::WaitableEvent pref_store_initialized_ = {
       base::WaitableEvent::ResetPolicy::MANUAL,
       base::WaitableEvent::InitialState::NOT_SIGNALED};
 
diff --git a/cobalt/persistent_storage/persistent_settings_test.cc b/cobalt/persistent_storage/persistent_settings_test.cc
index 3f88397..91b2bce 100644
--- a/cobalt/persistent_storage/persistent_settings_test.cc
+++ b/cobalt/persistent_storage/persistent_settings_test.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "cobalt/persistent_storage/persistent_settings.h"
+
 #include <utility>
 #include <vector>
 
@@ -20,9 +22,7 @@
 #include "base/callback_forward.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/test/scoped_task_environment.h"
-#include "base/threading/platform_thread.h"
 #include "base/values.h"
-#include "cobalt/persistent_storage/persistent_settings.h"
 #include "starboard/common/file.h"
 #include "starboard/common/log.h"
 #include "starboard/configuration_constants.h"
@@ -81,13 +81,13 @@
   base::OnceClosure closure = base::BindOnce(
       [](PersistentSettings* persistent_settings,
          base::WaitableEvent* test_done) {
-        ASSERT_TRUE(
+        EXPECT_TRUE(
             persistent_settings->GetPersistentSettingAsInt("key", true));
         test_done->Signal();
       },
       persistent_settings.get(), &test_done_);
   persistent_settings->SetPersistentSetting(
-      "key", std::make_unique<base::Value>(4.2), std::move(closure));
+      "key", std::make_unique<base::Value>(4.2), std::move(closure), true);
   test_done_.Wait();
 }
 
@@ -99,32 +99,32 @@
   base::OnceClosure closure = base::BindOnce(
       [](PersistentSettings* persistent_settings,
          base::WaitableEvent* test_done) {
-        ASSERT_TRUE(
+        EXPECT_TRUE(
             persistent_settings->GetPersistentSettingAsBool("key", true));
-        ASSERT_TRUE(
+        EXPECT_TRUE(
             persistent_settings->GetPersistentSettingAsBool("key", false));
         test_done->Signal();
       },
       persistent_settings.get(), &test_done_);
 
   persistent_settings->SetPersistentSetting(
-      "key", std::make_unique<base::Value>(true), std::move(closure));
+      "key", std::make_unique<base::Value>(true), std::move(closure), true);
   test_done_.Wait();
   test_done_.Reset();
 
   closure = base::BindOnce(
       [](PersistentSettings* persistent_settings,
          base::WaitableEvent* test_done) {
-        ASSERT_FALSE(
+        EXPECT_FALSE(
             persistent_settings->GetPersistentSettingAsBool("key", true));
-        ASSERT_FALSE(
+        EXPECT_FALSE(
             persistent_settings->GetPersistentSettingAsBool("key", false));
         test_done->Signal();
       },
       persistent_settings.get(), &test_done_);
 
   persistent_settings->SetPersistentSetting(
-      "key", std::make_unique<base::Value>(false), std::move(closure));
+      "key", std::make_unique<base::Value>(false), std::move(closure), true);
 
   test_done_.Wait();
 }
@@ -143,13 +143,13 @@
   base::OnceClosure closure = base::BindOnce(
       [](PersistentSettings* persistent_settings,
          base::WaitableEvent* test_done) {
-        ASSERT_EQ(8, persistent_settings->GetPersistentSettingAsInt("key", 8));
+        EXPECT_EQ(8, persistent_settings->GetPersistentSettingAsInt("key", 8));
         test_done->Signal();
       },
       persistent_settings.get(), &test_done_);
 
   persistent_settings->SetPersistentSetting(
-      "key", std::make_unique<base::Value>(4.2), std::move(closure));
+      "key", std::make_unique<base::Value>(4.2), std::move(closure), true);
   test_done_.Wait();
 }
 
@@ -161,36 +161,36 @@
   base::OnceClosure closure = base::BindOnce(
       [](PersistentSettings* persistent_settings,
          base::WaitableEvent* test_done) {
-        ASSERT_EQ(-1, persistent_settings->GetPersistentSettingAsInt("key", 8));
+        EXPECT_EQ(-1, persistent_settings->GetPersistentSettingAsInt("key", 8));
         test_done->Signal();
       },
       persistent_settings.get(), &test_done_);
   persistent_settings->SetPersistentSetting(
-      "key", std::make_unique<base::Value>(-1), std::move(closure));
+      "key", std::make_unique<base::Value>(-1), std::move(closure), true);
   test_done_.Wait();
   test_done_.Reset();
 
   closure = base::BindOnce(
       [](PersistentSettings* persistent_settings,
          base::WaitableEvent* test_done) {
-        ASSERT_EQ(0, persistent_settings->GetPersistentSettingAsInt("key", 8));
+        EXPECT_EQ(0, persistent_settings->GetPersistentSettingAsInt("key", 8));
         test_done->Signal();
       },
       persistent_settings.get(), &test_done_);
   persistent_settings->SetPersistentSetting(
-      "key", std::make_unique<base::Value>(0), std::move(closure));
+      "key", std::make_unique<base::Value>(0), std::move(closure), true);
   test_done_.Wait();
   test_done_.Reset();
 
   closure = base::BindOnce(
       [](PersistentSettings* persistent_settings,
          base::WaitableEvent* test_done) {
-        ASSERT_EQ(42, persistent_settings->GetPersistentSettingAsInt("key", 8));
+        EXPECT_EQ(42, persistent_settings->GetPersistentSettingAsInt("key", 8));
         test_done->Signal();
       },
       persistent_settings.get(), &test_done_);
   persistent_settings->SetPersistentSetting(
-      "key", std::make_unique<base::Value>(42), std::move(closure));
+      "key", std::make_unique<base::Value>(42), std::move(closure), true);
   test_done_.Wait();
 }
 
@@ -214,13 +214,13 @@
   base::OnceClosure closure = base::BindOnce(
       [](PersistentSettings* persistent_settings,
          base::WaitableEvent* test_done) {
-        ASSERT_EQ("hello", persistent_settings->GetPersistentSettingAsString(
+        EXPECT_EQ("hello", persistent_settings->GetPersistentSettingAsString(
                                "key", "hello"));
         test_done->Signal();
       },
       persistent_settings.get(), &test_done_);
   persistent_settings->SetPersistentSetting(
-      "key", std::make_unique<base::Value>(4.2), std::move(closure));
+      "key", std::make_unique<base::Value>(4.2), std::move(closure), true);
   test_done_.Wait();
 }
 
@@ -232,21 +232,21 @@
   base::OnceClosure closure = base::BindOnce(
       [](PersistentSettings* persistent_settings,
          base::WaitableEvent* test_done) {
-        ASSERT_EQ("", persistent_settings->GetPersistentSettingAsString(
+        EXPECT_EQ("", persistent_settings->GetPersistentSettingAsString(
                           "key", "hello"));
         test_done->Signal();
       },
       persistent_settings.get(), &test_done_);
 
   persistent_settings->SetPersistentSetting(
-      "key", std::make_unique<base::Value>(""), std::move(closure));
+      "key", std::make_unique<base::Value>(""), std::move(closure), true);
   test_done_.Wait();
   test_done_.Reset();
 
   closure = base::BindOnce(
       [](PersistentSettings* persistent_settings,
          base::WaitableEvent* test_done) {
-        ASSERT_EQ(
+        EXPECT_EQ(
             "hello there",
             persistent_settings->GetPersistentSettingAsString("key", "hello"));
         test_done->Signal();
@@ -254,46 +254,47 @@
       persistent_settings.get(), &test_done_);
 
   persistent_settings->SetPersistentSetting(
-      "key", std::make_unique<base::Value>("hello there"), std::move(closure));
+      "key", std::make_unique<base::Value>("hello there"), std::move(closure),
+      true);
   test_done_.Wait();
   test_done_.Reset();
 
   closure = base::BindOnce(
       [](PersistentSettings* persistent_settings,
          base::WaitableEvent* test_done) {
-        ASSERT_EQ("42", persistent_settings->GetPersistentSettingAsString(
+        EXPECT_EQ("42", persistent_settings->GetPersistentSettingAsString(
                             "key", "hello"));
         test_done->Signal();
       },
       persistent_settings.get(), &test_done_);
   persistent_settings->SetPersistentSetting(
-      "key", std::make_unique<base::Value>("42"), std::move(closure));
+      "key", std::make_unique<base::Value>("42"), std::move(closure), true);
   test_done_.Wait();
   test_done_.Reset();
 
   closure = base::BindOnce(
       [](PersistentSettings* persistent_settings,
          base::WaitableEvent* test_done) {
-        ASSERT_EQ("\n", persistent_settings->GetPersistentSettingAsString(
+        EXPECT_EQ("\n", persistent_settings->GetPersistentSettingAsString(
                             "key", "hello"));
         test_done->Signal();
       },
       persistent_settings.get(), &test_done_);
   persistent_settings->SetPersistentSetting(
-      "key", std::make_unique<base::Value>("\n"), std::move(closure));
+      "key", std::make_unique<base::Value>("\n"), std::move(closure), true);
   test_done_.Wait();
   test_done_.Reset();
 
   closure = base::BindOnce(
       [](PersistentSettings* persistent_settings,
          base::WaitableEvent* test_done) {
-        ASSERT_EQ("\\n", persistent_settings->GetPersistentSettingAsString(
+        EXPECT_EQ("\\n", persistent_settings->GetPersistentSettingAsString(
                              "key", "hello"));
         test_done->Signal();
       },
       persistent_settings.get(), &test_done_);
   persistent_settings->SetPersistentSetting(
-      "key", std::make_unique<base::Value>("\\n"), std::move(closure));
+      "key", std::make_unique<base::Value>("\\n"), std::move(closure), true);
   test_done_.Wait();
   test_done_.Reset();
 }
@@ -307,10 +308,10 @@
       [](PersistentSettings* persistent_settings,
          base::WaitableEvent* test_done) {
         auto test_list = persistent_settings->GetPersistentSettingAsList("key");
-        ASSERT_FALSE(test_list.empty());
-        ASSERT_EQ(1, test_list.size());
-        ASSERT_TRUE(test_list[0].is_string());
-        ASSERT_EQ("hello", test_list[0].GetString());
+        EXPECT_FALSE(test_list.empty());
+        EXPECT_EQ(1, test_list.size());
+        EXPECT_TRUE(test_list[0].is_string());
+        EXPECT_EQ("hello", test_list[0].GetString());
         test_done->Signal();
       },
       persistent_settings.get(), &test_done_);
@@ -318,7 +319,7 @@
   std::vector<base::Value> list;
   list.emplace_back("hello");
   persistent_settings->SetPersistentSetting(
-      "key", std::make_unique<base::Value>(list), std::move(closure));
+      "key", std::make_unique<base::Value>(list), std::move(closure), true);
   test_done_.Wait();
   test_done_.Reset();
 
@@ -326,19 +327,19 @@
       [](PersistentSettings* persistent_settings,
          base::WaitableEvent* test_done) {
         auto test_list = persistent_settings->GetPersistentSettingAsList("key");
-        ASSERT_FALSE(test_list.empty());
-        ASSERT_EQ(2, test_list.size());
-        ASSERT_TRUE(test_list[0].is_string());
-        ASSERT_EQ("hello", test_list[0].GetString());
-        ASSERT_TRUE(test_list[1].is_string());
-        ASSERT_EQ("there", test_list[1].GetString());
+        EXPECT_FALSE(test_list.empty());
+        EXPECT_EQ(2, test_list.size());
+        EXPECT_TRUE(test_list[0].is_string());
+        EXPECT_EQ("hello", test_list[0].GetString());
+        EXPECT_TRUE(test_list[1].is_string());
+        EXPECT_EQ("there", test_list[1].GetString());
         test_done->Signal();
       },
       persistent_settings.get(), &test_done_);
 
   list.emplace_back("there");
   persistent_settings->SetPersistentSetting(
-      "key", std::make_unique<base::Value>(list), std::move(closure));
+      "key", std::make_unique<base::Value>(list), std::move(closure), true);
   test_done_.Wait();
   test_done_.Reset();
 
@@ -346,21 +347,21 @@
       [](PersistentSettings* persistent_settings,
          base::WaitableEvent* test_done) {
         auto test_list = persistent_settings->GetPersistentSettingAsList("key");
-        ASSERT_FALSE(test_list.empty());
-        ASSERT_EQ(3, test_list.size());
-        ASSERT_TRUE(test_list[0].is_string());
-        ASSERT_EQ("hello", test_list[0].GetString());
-        ASSERT_TRUE(test_list[1].is_string());
-        ASSERT_EQ("there", test_list[1].GetString());
-        ASSERT_TRUE(test_list[2].is_int());
-        ASSERT_EQ(42, test_list[2].GetInt());
+        EXPECT_FALSE(test_list.empty());
+        EXPECT_EQ(3, test_list.size());
+        EXPECT_TRUE(test_list[0].is_string());
+        EXPECT_EQ("hello", test_list[0].GetString());
+        EXPECT_TRUE(test_list[1].is_string());
+        EXPECT_EQ("there", test_list[1].GetString());
+        EXPECT_TRUE(test_list[2].is_int());
+        EXPECT_EQ(42, test_list[2].GetInt());
         test_done->Signal();
       },
       persistent_settings.get(), &test_done_);
 
   list.emplace_back(42);
   persistent_settings->SetPersistentSetting(
-      "key", std::make_unique<base::Value>(list), std::move(closure));
+      "key", std::make_unique<base::Value>(list), std::move(closure), true);
   test_done_.Wait();
   test_done_.Reset();
 }
@@ -375,10 +376,10 @@
          base::WaitableEvent* test_done) {
         auto test_dict =
             persistent_settings->GetPersistentSettingAsDictionary("key");
-        ASSERT_FALSE(test_dict.empty());
-        ASSERT_EQ(1, test_dict.size());
-        ASSERT_TRUE(test_dict["key_string"]->is_string());
-        ASSERT_EQ("hello", test_dict["key_string"]->GetString());
+        EXPECT_FALSE(test_dict.empty());
+        EXPECT_EQ(1, test_dict.size());
+        EXPECT_TRUE(test_dict["key_string"]->is_string());
+        EXPECT_EQ("hello", test_dict["key_string"]->GetString());
         test_done->Signal();
       },
       persistent_settings.get(), &test_done_);
@@ -386,7 +387,7 @@
   base::flat_map<std::string, std::unique_ptr<base::Value>> dict;
   dict.try_emplace("key_string", std::make_unique<base::Value>("hello"));
   persistent_settings->SetPersistentSetting(
-      "key", std::make_unique<base::Value>(dict), std::move(closure));
+      "key", std::make_unique<base::Value>(dict), std::move(closure), true);
   test_done_.Wait();
   test_done_.Reset();
 
@@ -395,19 +396,19 @@
          base::WaitableEvent* test_done) {
         auto test_dict =
             persistent_settings->GetPersistentSettingAsDictionary("key");
-        ASSERT_FALSE(test_dict.empty());
-        ASSERT_EQ(2, test_dict.size());
-        ASSERT_TRUE(test_dict["key_string"]->is_string());
-        ASSERT_EQ("hello", test_dict["key_string"]->GetString());
-        ASSERT_TRUE(test_dict["key_int"]->is_int());
-        ASSERT_EQ(42, test_dict["key_int"]->GetInt());
+        EXPECT_FALSE(test_dict.empty());
+        EXPECT_EQ(2, test_dict.size());
+        EXPECT_TRUE(test_dict["key_string"]->is_string());
+        EXPECT_EQ("hello", test_dict["key_string"]->GetString());
+        EXPECT_TRUE(test_dict["key_int"]->is_int());
+        EXPECT_EQ(42, test_dict["key_int"]->GetInt());
         test_done->Signal();
       },
       persistent_settings.get(), &test_done_);
 
   dict.try_emplace("key_int", std::make_unique<base::Value>(42));
   persistent_settings->SetPersistentSetting(
-      "key", std::make_unique<base::Value>(dict), std::move(closure));
+      "key", std::make_unique<base::Value>(dict), std::move(closure), true);
   test_done_.Wait();
   test_done_.Reset();
 }
@@ -425,10 +426,10 @@
          base::WaitableEvent* test_done) {
         auto test_dict = persistent_settings->GetPersistentSettingAsDictionary(
             "http://127.0.0.1:45019/");
-        ASSERT_FALSE(test_dict.empty());
-        ASSERT_EQ(1, test_dict.size());
-        ASSERT_TRUE(test_dict["http://127.0.0.1:45019/"]->is_string());
-        ASSERT_EQ("Dictionary URL Key Works!",
+        EXPECT_FALSE(test_dict.empty());
+        EXPECT_EQ(1, test_dict.size());
+        EXPECT_TRUE(test_dict["http://127.0.0.1:45019/"]->is_string());
+        EXPECT_EQ("Dictionary URL Key Works!",
                   test_dict["http://127.0.0.1:45019/"]->GetString());
         test_done->Signal();
       },
@@ -441,7 +442,7 @@
                    std::make_unique<base::Value>("Dictionary URL Key Works!"));
   persistent_settings->SetPersistentSetting("http://127.0.0.1:45019/",
                                             std::make_unique<base::Value>(dict),
-                                            std::move(closure));
+                                            std::move(closure), true);
   test_done_.Wait();
   test_done_.Reset();
 
@@ -450,12 +451,12 @@
          base::WaitableEvent* test_done) {
         auto test_dict = persistent_settings->GetPersistentSettingAsDictionary(
             "http://127.0.0.1:45019/");
-        ASSERT_TRUE(test_dict.empty());
+        EXPECT_TRUE(test_dict.empty());
         test_done->Signal();
       },
       persistent_settings.get(), &test_done_);
   persistent_settings->RemovePersistentSetting("http://127.0.0.1:45019/",
-                                               std::move(closure));
+                                               std::move(closure), true);
   test_done_.Wait();
   test_done_.Reset();
 }
@@ -471,29 +472,29 @@
   base::OnceClosure closure = base::BindOnce(
       [](PersistentSettings* persistent_settings,
          base::WaitableEvent* test_done) {
-        ASSERT_TRUE(
+        EXPECT_TRUE(
             persistent_settings->GetPersistentSettingAsBool("key", true));
-        ASSERT_TRUE(
+        EXPECT_TRUE(
             persistent_settings->GetPersistentSettingAsBool("key", false));
         test_done->Signal();
       },
       persistent_settings.get(), &test_done_);
   persistent_settings->SetPersistentSetting(
-      "key", std::make_unique<base::Value>(true), std::move(closure));
+      "key", std::make_unique<base::Value>(true), std::move(closure), true);
   test_done_.Wait();
   test_done_.Reset();
 
   closure = base::BindOnce(
       [](PersistentSettings* persistent_settings,
          base::WaitableEvent* test_done) {
-        ASSERT_TRUE(
+        EXPECT_TRUE(
             persistent_settings->GetPersistentSettingAsBool("key", true));
-        ASSERT_FALSE(
+        EXPECT_FALSE(
             persistent_settings->GetPersistentSettingAsBool("key", false));
         test_done->Signal();
       },
       persistent_settings.get(), &test_done_);
-  persistent_settings->RemovePersistentSetting("key", std::move(closure));
+  persistent_settings->RemovePersistentSetting("key", std::move(closure), true);
   test_done_.Wait();
   test_done_.Reset();
 }
@@ -509,24 +510,24 @@
   base::OnceClosure closure = base::BindOnce(
       [](PersistentSettings* persistent_settings,
          base::WaitableEvent* test_done) {
-        ASSERT_TRUE(
+        EXPECT_TRUE(
             persistent_settings->GetPersistentSettingAsBool("key", true));
-        ASSERT_TRUE(
+        EXPECT_TRUE(
             persistent_settings->GetPersistentSettingAsBool("key", false));
         test_done->Signal();
       },
       persistent_settings.get(), &test_done_);
   persistent_settings->SetPersistentSetting(
-      "key", std::make_unique<base::Value>(true), std::move(closure));
+      "key", std::make_unique<base::Value>(true), std::move(closure), true);
   test_done_.Wait();
   test_done_.Reset();
 
   closure = base::BindOnce(
       [](PersistentSettings* persistent_settings,
          base::WaitableEvent* test_done) {
-        ASSERT_TRUE(
+        EXPECT_TRUE(
             persistent_settings->GetPersistentSettingAsBool("key", true));
-        ASSERT_FALSE(
+        EXPECT_FALSE(
             persistent_settings->GetPersistentSettingAsBool("key", false));
         test_done->Signal();
       },
@@ -547,22 +548,18 @@
   base::OnceClosure closure = base::BindOnce(
       [](PersistentSettings* persistent_settings,
          base::WaitableEvent* test_done) {
-        ASSERT_TRUE(
+        EXPECT_TRUE(
             persistent_settings->GetPersistentSettingAsBool("key", true));
-        ASSERT_TRUE(
+        EXPECT_TRUE(
             persistent_settings->GetPersistentSettingAsBool("key", false));
         test_done->Signal();
       },
       persistent_settings.get(), &test_done_);
   persistent_settings->SetPersistentSetting(
-      "key", std::make_unique<base::Value>(true), std::move(closure));
+      "key", std::make_unique<base::Value>(true), std::move(closure), true);
   test_done_.Wait();
   test_done_.Reset();
 
-  // Sleep for one second to allow for the previous persistent_setting's
-  // JsonPrefStore instance time to write to disk before creating a new
-  // persistent_settings and JsonPrefStore instance.
-  base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
   persistent_settings =
       std::make_unique<PersistentSettings>(kPersistentSettingsJson);
   ASSERT_TRUE(persistent_settings->GetPersistentSettingAsBool("key", true));
@@ -571,15 +568,15 @@
   closure = base::BindOnce(
       [](PersistentSettings* persistent_settings,
          base::WaitableEvent* test_done) {
-        ASSERT_TRUE(
+        EXPECT_TRUE(
             persistent_settings->GetPersistentSettingAsBool("key", true));
-        ASSERT_TRUE(
+        EXPECT_TRUE(
             persistent_settings->GetPersistentSettingAsBool("key", false));
         test_done->Signal();
       },
       persistent_settings.get(), &test_done_);
   persistent_settings->SetPersistentSetting(
-      "key", std::make_unique<base::Value>(false), std::move(closure));
+      "key", std::make_unique<base::Value>(false), std::move(closure), true);
   test_done_.Wait();
   test_done_.Reset();
 
diff --git a/cobalt/renderer/test/png_utils/BUILD.gn b/cobalt/renderer/test/png_utils/BUILD.gn
index 28e8ac1..2e8135e 100644
--- a/cobalt/renderer/test/png_utils/BUILD.gn
+++ b/cobalt/renderer/test/png_utils/BUILD.gn
@@ -40,15 +40,18 @@
 
   deps = [
     ":png_utils",
-    ":png_utils_copy_test_data",
     "//cobalt/base",
     "//cobalt/test:run_all_unittests",
     "//testing/gmock",
     "//testing/gtest",
   ]
+
+  data_deps = [ ":png_utils_copy_test_data" ]
 }
 
 copy("png_utils_copy_test_data") {
+  install_content = true
+
   sources = [
     "png_benchmark_image.png",
     "png_premultiplied_alpha_test_image.png",
diff --git a/cobalt/script/v8c/v8c_global_environment.cc b/cobalt/script/v8c/v8c_global_environment.cc
index 80a27d2..9ec5c07 100644
--- a/cobalt/script/v8c/v8c_global_environment.cc
+++ b/cobalt/script/v8c/v8c_global_environment.cc
@@ -97,8 +97,8 @@
   isolate_->SetData(kIsolateDataIndex, this);
   DCHECK(isolate_->GetData(kIsolateDataIndex) == this);
 
-  isolate_->SetAllowCodeGenerationFromStringsCallback(
-      AllowCodeGenerationFromStringsCallback);
+  isolate_->SetModifyCodeGenerationFromStringsCallback(
+      ModifyCodeGenerationFromStringsCallback);
 
   isolate_->SetAllowWasmCodeGenerationCallback(
       [](v8::Local<v8::Context> context, v8::Local<v8::String> source) {
@@ -371,8 +371,10 @@
 }
 
 // static
-bool V8cGlobalEnvironment::AllowCodeGenerationFromStringsCallback(
-    v8::Local<v8::Context> context, v8::Local<v8::String> source) {
+v8::ModifyCodeGenerationFromStringsResult
+V8cGlobalEnvironment::ModifyCodeGenerationFromStringsCallback(
+    v8::Local<v8::Context> context, v8::Local<v8::Value> source,
+    bool is_code_like) {
   V8cGlobalEnvironment* global_environment =
       V8cGlobalEnvironment::GetFromIsolate(context->GetIsolate());
   DCHECK(global_environment);
@@ -385,7 +387,7 @@
   // WebAssembly callback has not been explicitly set, however we *have* set
   // one.
   DCHECK_EQ(context->IsCodeGenerationFromStringsAllowed(), false);
-  return context->IsCodeGenerationFromStringsAllowed();
+  return {context->IsCodeGenerationFromStringsAllowed(), {}};
 }
 
 // static
@@ -394,14 +396,14 @@
   v8::Isolate* isolate = v8::Isolate::GetCurrent();
   V8cGlobalEnvironment* global_environment =
       V8cGlobalEnvironment::GetFromIsolate(isolate);
-  if (isolate->GetEnteredContext().IsEmpty()) {
+  if (isolate->GetEnteredOrMicrotaskContext().IsEmpty()) {
     return;
   }
   if (message->ErrorLevel() != v8::Isolate::kMessageError) {
     return;
   }
 
-  v8::Local<v8::Context> context = isolate->GetEnteredContext();
+  v8::Local<v8::Context> context = isolate->GetEnteredOrMicrotaskContext();
   ErrorReport error_report;
   error_report.message = *v8::String::Utf8Value(isolate, message->Get());
   error_report.filename =
diff --git a/cobalt/script/v8c/v8c_global_environment.h b/cobalt/script/v8c/v8c_global_environment.h
index 265c2e1..6996e62 100644
--- a/cobalt/script/v8c/v8c_global_environment.h
+++ b/cobalt/script/v8c/v8c_global_environment.h
@@ -151,8 +151,10 @@
     v8::Isolate* isolate_;
   };
 
-  static bool AllowCodeGenerationFromStringsCallback(
-      v8::Local<v8::Context> context, v8::Local<v8::String> source);
+  static v8::ModifyCodeGenerationFromStringsResult
+  ModifyCodeGenerationFromStringsCallback(v8::Local<v8::Context> context,
+                                          v8::Local<v8::Value> source,
+                                          bool is_code_like);
 
   static void MessageHandler(v8::Local<v8::Message> message,
                              v8::Local<v8::Value> data);
diff --git a/cobalt/site/docs/gen/cobalt/doc/cvals.md b/cobalt/site/docs/gen/cobalt/doc/cvals.md
index 752fc7f..665c6a1 100644
--- a/cobalt/site/docs/gen/cobalt/doc/cvals.md
+++ b/cobalt/site/docs/gen/cobalt/doc/cvals.md
@@ -79,7 +79,7 @@
 
 #### PublicCVals
 
-*   **Count.DOM.EventListeners** - The total number of EventListeners in
+*   **Count.WEB.EventListeners** - The total number of EventListeners in
     existence globally. This includes ones that are pending garbage collection.
 *   **Count.DOM.Nodes** - The total number of Nodes in existence globally. This
     includes ones that are pending garbage collection.
@@ -95,7 +95,7 @@
 
 #### DebugCVals
 
-*   **Count.DOM.ActiveJavaScriptEvents** - The number of JavaScript events that
+*   **Count.WEB.ActiveJavaScriptEvents** - The number of JavaScript events that
     are currently running.
 *   **Count.DOM.Attrs** - The total number of Attrs in existence globally. This
     includes ones that are pending garbage collection.
diff --git a/cobalt/site/docs/reference/starboard/configuration-public.md b/cobalt/site/docs/reference/starboard/configuration-public.md
index 285f90e..44659a2 100644
--- a/cobalt/site/docs/reference/starboard/configuration-public.md
+++ b/cobalt/site/docs/reference/starboard/configuration-public.md
@@ -50,7 +50,6 @@
 
 | Properties |
 | :--- |
-| **`SB_HAS_BILINEAR_FILTERING_SUPPORT`**<br><br>Indicates whether or not the given platform supports bilinear filtering. This can be checked to enable/disable renderer tests that verify that this is working properly.<br><br>The default value in the Stub implementation is `1` |
 | **`SB_HAS_NV12_TEXTURE_SUPPORT`**<br><br>Indicates whether or not the given platform supports rendering of NV12 textures. These textures typically originate from video decoders.<br><br>The default value in the Stub implementation is `0` |
 | **`SB_HAS_VIRTUAL_REALITY`**<br><br>The default value in the Stub implementation is `1` |
 
diff --git a/cobalt/site/docs/reference/starboard/modules/13/speech_recognizer.md b/cobalt/site/docs/reference/starboard/modules/13/speech_recognizer.md
deleted file mode 100644
index 2a40f19..0000000
--- a/cobalt/site/docs/reference/starboard/modules/13/speech_recognizer.md
+++ /dev/null
@@ -1,268 +0,0 @@
----
-layout: doc
-title: "Starboard Module Reference: speech_recognizer.h"
----
-
-Defines a streaming speech recognizer API. It provides access to the platform
-speech recognition service.
-
-Note that there can be only one speech recognizer. Attempting to create a second
-speech recognizer without destroying the first one will result in undefined
-behavior.
-
-`SbSpeechRecognizerCreate`, `SbSpeechRecognizerStart`, `SbSpeechRecognizerStop`,
-`SbSpeechRecognizerCancel` and `SbSpeechRecognizerDestroy` should be called from
-a single thread. Callbacks defined in `SbSpeechRecognizerHandler` will happen on
-another thread, so calls back into the SbSpeechRecognizer from the callback
-thread are disallowed.
-
-## Macros ##
-
-### kSbSpeechRecognizerInvalid ###
-
-Well-defined value for an invalid speech recognizer handle.
-
-## Enums ##
-
-### SbSpeechRecognizerError ###
-
-Indicates what has gone wrong with the recognition.
-
-#### Values ####
-
-*   `kSbNoSpeechError`
-
-    No speech was detected. Speech timed out.
-*   `kSbAborted`
-
-    Speech input was aborted somehow.
-*   `kSbAudioCaptureError`
-
-    Audio capture failed.
-*   `kSbNetworkError`
-
-    Some network communication that was required to complete the recognition
-    failed.
-*   `kSbNotAllowed`
-
-    The implementation is not allowing any speech input to occur for reasons of
-    security, privacy or user preference.
-*   `kSbServiceNotAllowed`
-
-    The implementation is not allowing the application requested speech service,
-    but would allow some speech service, to be used either because the
-    implementation doesn't support the selected one or because of reasons of
-    security, privacy or user preference.
-*   `kSbBadGrammar`
-
-    There was an error in the speech recognition grammar or semantic tags, or
-    the grammar format or semantic tag format is supported.
-*   `kSbLanguageNotSupported`
-
-    The language was not supported.
-
-## Typedefs ##
-
-### SbSpeechRecognizer ###
-
-An opaque handle to an implementation-private structure that represents a speech
-recognizer.
-
-#### Definition ####
-
-```
-typedef struct SbSpeechRecognizerPrivate* SbSpeechRecognizer
-```
-
-### SbSpeechRecognizerErrorFunction ###
-
-A function to notify that a speech recognition error occurred. `error`: The
-occurred speech recognition error.
-
-#### Definition ####
-
-```
-typedef void(* SbSpeechRecognizerErrorFunction) (void *context, SbSpeechRecognizerError error)
-```
-
-### SbSpeechRecognizerResultsFunction ###
-
-A function to notify that the recognition results are ready. `results`: the list
-of recognition results. `results_size`: the number of `results`. `is_final`:
-indicates if the `results` is final.
-
-#### Definition ####
-
-```
-typedef void(* SbSpeechRecognizerResultsFunction) (void *context, SbSpeechResult *results, int results_size, bool is_final)
-```
-
-### SbSpeechRecognizerSpeechDetectedFunction ###
-
-A function to notify that the user has started to speak or stops speaking.
-`detected`: true if the user has started to speak, and false if the user stops
-speaking.
-
-#### Definition ####
-
-```
-typedef void(* SbSpeechRecognizerSpeechDetectedFunction) (void *context, bool detected)
-```
-
-## Structs ##
-
-### SbSpeechConfiguration ###
-
-#### Members ####
-
-*   `bool continuous`
-
-    When the continuous value is set to false, the implementation MUST return no
-    more than one final result in response to starting recognition. When the
-    continuous attribute is set to true, the implementation MUST return zero or
-    more final results representing multiple consecutive recognitions in
-    response to starting recognition. This attribute setting does not affect
-    interim results.
-*   `bool interim_results`
-
-    Controls whether interim results are returned. When set to true, interim
-    results SHOULD be returned. When set to false, interim results MUST NOT be
-    returned. This value setting does not affect final results.
-*   `int max_alternatives`
-
-    This sets the maximum number of SbSpeechResult in
-    `SbSpeechRecognizerOnResults` callback.
-
-### SbSpeechRecognizerHandler ###
-
-Allows receiving notifications from the device when recognition related events
-occur.
-
-The void* context is passed to every function.
-
-#### Members ####
-
-*   `SbSpeechRecognizerSpeechDetectedFunction on_speech_detected`
-
-    Function to notify the beginning/end of the speech.
-*   `SbSpeechRecognizerErrorFunction on_error`
-
-    Function to notify the speech error.
-*   `SbSpeechRecognizerResultsFunction on_results`
-
-    Function to notify that the recognition results are available.
-*   `void * context`
-
-    This is passed to handler functions as first argument.
-
-### SbSpeechResult ###
-
-The recognition response that is received from the recognizer.
-
-#### Members ####
-
-*   `char * transcript`
-
-    The raw words that the user spoke.
-*   `float confidence`
-
-    A numeric estimate between 0 and 1 of how confident the recognition system
-    is that the recognition is correct. A higher number means the system is more
-    confident. NaN represents an unavailable confidence score.
-
-## Functions ##
-
-### SbSpeechRecognizerCancel ###
-
-Cancels speech recognition. The speech recognizer stops listening to audio and
-does not return any information. When `SbSpeechRecognizerCancel` is called, the
-implementation MUST NOT collect additional audio, MUST NOT continue to listen to
-the user, and MUST stop recognizing. This is important for privacy reasons. If
-`SbSpeechRecognizerCancel` is called on a speech recognizer which is already
-stopped or cancelled, the implementation MUST ignore the call.
-
-#### Declaration ####
-
-```
-void SbSpeechRecognizerCancel(SbSpeechRecognizer recognizer)
-```
-
-### SbSpeechRecognizerCreate ###
-
-Creates a speech recognizer with a speech recognizer handler.
-
-If the system has a speech recognition service available, this function returns
-the newly created handle.
-
-If no speech recognition service is available on the device, this function
-returns `kSbSpeechRecognizerInvalid`.
-
-`SbSpeechRecognizerCreate` does not expect the passed SbSpeechRecognizerHandler
-structure to live after `SbSpeechRecognizerCreate` is called, so the
-implementation must copy the contents if necessary.
-
-#### Declaration ####
-
-```
-SbSpeechRecognizer SbSpeechRecognizerCreate(const SbSpeechRecognizerHandler *handler)
-```
-
-### SbSpeechRecognizerDestroy ###
-
-Destroys the given speech recognizer. If the speech recognizer is in the started
-state, it is first stopped and then destroyed.
-
-#### Declaration ####
-
-```
-void SbSpeechRecognizerDestroy(SbSpeechRecognizer recognizer)
-```
-
-### SbSpeechRecognizerIsSupported ###
-
-Returns whether the platform supports SbSpeechRecognizer.
-
-#### Declaration ####
-
-```
-bool SbSpeechRecognizerIsSupported()
-```
-
-### SbSpeechRecognizerIsValid ###
-
-Indicates whether the given speech recognizer is valid.
-
-#### Declaration ####
-
-```
-static bool SbSpeechRecognizerIsValid(SbSpeechRecognizer recognizer)
-```
-
-### SbSpeechRecognizerStart ###
-
-Starts listening to audio and recognizing speech with the specified speech
-configuration. If `SbSpeechRecognizerStart` is called on an already started
-speech recognizer, the implementation MUST ignore the call and return false.
-
-Returns whether the speech recognizer is started successfully.
-
-#### Declaration ####
-
-```
-bool SbSpeechRecognizerStart(SbSpeechRecognizer recognizer, const SbSpeechConfiguration *configuration)
-```
-
-### SbSpeechRecognizerStop ###
-
-Stops listening to audio and returns a result using just the audio that it has
-already received. Once `SbSpeechRecognizerStop` is called, the implementation
-MUST NOT collect additional audio and MUST NOT continue to listen to the user.
-This is important for privacy reasons. If `SbSpeechRecognizerStop` is called on
-a speech recognizer which is already stopped or being stopped, the
-implementation MUST ignore the call.
-
-#### Declaration ####
-
-```
-void SbSpeechRecognizerStop(SbSpeechRecognizer recognizer)
-```
diff --git a/cobalt/site/docs/reference/starboard/modules/14/speech_recognizer.md b/cobalt/site/docs/reference/starboard/modules/14/speech_recognizer.md
deleted file mode 100644
index 2a40f19..0000000
--- a/cobalt/site/docs/reference/starboard/modules/14/speech_recognizer.md
+++ /dev/null
@@ -1,268 +0,0 @@
----
-layout: doc
-title: "Starboard Module Reference: speech_recognizer.h"
----
-
-Defines a streaming speech recognizer API. It provides access to the platform
-speech recognition service.
-
-Note that there can be only one speech recognizer. Attempting to create a second
-speech recognizer without destroying the first one will result in undefined
-behavior.
-
-`SbSpeechRecognizerCreate`, `SbSpeechRecognizerStart`, `SbSpeechRecognizerStop`,
-`SbSpeechRecognizerCancel` and `SbSpeechRecognizerDestroy` should be called from
-a single thread. Callbacks defined in `SbSpeechRecognizerHandler` will happen on
-another thread, so calls back into the SbSpeechRecognizer from the callback
-thread are disallowed.
-
-## Macros ##
-
-### kSbSpeechRecognizerInvalid ###
-
-Well-defined value for an invalid speech recognizer handle.
-
-## Enums ##
-
-### SbSpeechRecognizerError ###
-
-Indicates what has gone wrong with the recognition.
-
-#### Values ####
-
-*   `kSbNoSpeechError`
-
-    No speech was detected. Speech timed out.
-*   `kSbAborted`
-
-    Speech input was aborted somehow.
-*   `kSbAudioCaptureError`
-
-    Audio capture failed.
-*   `kSbNetworkError`
-
-    Some network communication that was required to complete the recognition
-    failed.
-*   `kSbNotAllowed`
-
-    The implementation is not allowing any speech input to occur for reasons of
-    security, privacy or user preference.
-*   `kSbServiceNotAllowed`
-
-    The implementation is not allowing the application requested speech service,
-    but would allow some speech service, to be used either because the
-    implementation doesn't support the selected one or because of reasons of
-    security, privacy or user preference.
-*   `kSbBadGrammar`
-
-    There was an error in the speech recognition grammar or semantic tags, or
-    the grammar format or semantic tag format is supported.
-*   `kSbLanguageNotSupported`
-
-    The language was not supported.
-
-## Typedefs ##
-
-### SbSpeechRecognizer ###
-
-An opaque handle to an implementation-private structure that represents a speech
-recognizer.
-
-#### Definition ####
-
-```
-typedef struct SbSpeechRecognizerPrivate* SbSpeechRecognizer
-```
-
-### SbSpeechRecognizerErrorFunction ###
-
-A function to notify that a speech recognition error occurred. `error`: The
-occurred speech recognition error.
-
-#### Definition ####
-
-```
-typedef void(* SbSpeechRecognizerErrorFunction) (void *context, SbSpeechRecognizerError error)
-```
-
-### SbSpeechRecognizerResultsFunction ###
-
-A function to notify that the recognition results are ready. `results`: the list
-of recognition results. `results_size`: the number of `results`. `is_final`:
-indicates if the `results` is final.
-
-#### Definition ####
-
-```
-typedef void(* SbSpeechRecognizerResultsFunction) (void *context, SbSpeechResult *results, int results_size, bool is_final)
-```
-
-### SbSpeechRecognizerSpeechDetectedFunction ###
-
-A function to notify that the user has started to speak or stops speaking.
-`detected`: true if the user has started to speak, and false if the user stops
-speaking.
-
-#### Definition ####
-
-```
-typedef void(* SbSpeechRecognizerSpeechDetectedFunction) (void *context, bool detected)
-```
-
-## Structs ##
-
-### SbSpeechConfiguration ###
-
-#### Members ####
-
-*   `bool continuous`
-
-    When the continuous value is set to false, the implementation MUST return no
-    more than one final result in response to starting recognition. When the
-    continuous attribute is set to true, the implementation MUST return zero or
-    more final results representing multiple consecutive recognitions in
-    response to starting recognition. This attribute setting does not affect
-    interim results.
-*   `bool interim_results`
-
-    Controls whether interim results are returned. When set to true, interim
-    results SHOULD be returned. When set to false, interim results MUST NOT be
-    returned. This value setting does not affect final results.
-*   `int max_alternatives`
-
-    This sets the maximum number of SbSpeechResult in
-    `SbSpeechRecognizerOnResults` callback.
-
-### SbSpeechRecognizerHandler ###
-
-Allows receiving notifications from the device when recognition related events
-occur.
-
-The void* context is passed to every function.
-
-#### Members ####
-
-*   `SbSpeechRecognizerSpeechDetectedFunction on_speech_detected`
-
-    Function to notify the beginning/end of the speech.
-*   `SbSpeechRecognizerErrorFunction on_error`
-
-    Function to notify the speech error.
-*   `SbSpeechRecognizerResultsFunction on_results`
-
-    Function to notify that the recognition results are available.
-*   `void * context`
-
-    This is passed to handler functions as first argument.
-
-### SbSpeechResult ###
-
-The recognition response that is received from the recognizer.
-
-#### Members ####
-
-*   `char * transcript`
-
-    The raw words that the user spoke.
-*   `float confidence`
-
-    A numeric estimate between 0 and 1 of how confident the recognition system
-    is that the recognition is correct. A higher number means the system is more
-    confident. NaN represents an unavailable confidence score.
-
-## Functions ##
-
-### SbSpeechRecognizerCancel ###
-
-Cancels speech recognition. The speech recognizer stops listening to audio and
-does not return any information. When `SbSpeechRecognizerCancel` is called, the
-implementation MUST NOT collect additional audio, MUST NOT continue to listen to
-the user, and MUST stop recognizing. This is important for privacy reasons. If
-`SbSpeechRecognizerCancel` is called on a speech recognizer which is already
-stopped or cancelled, the implementation MUST ignore the call.
-
-#### Declaration ####
-
-```
-void SbSpeechRecognizerCancel(SbSpeechRecognizer recognizer)
-```
-
-### SbSpeechRecognizerCreate ###
-
-Creates a speech recognizer with a speech recognizer handler.
-
-If the system has a speech recognition service available, this function returns
-the newly created handle.
-
-If no speech recognition service is available on the device, this function
-returns `kSbSpeechRecognizerInvalid`.
-
-`SbSpeechRecognizerCreate` does not expect the passed SbSpeechRecognizerHandler
-structure to live after `SbSpeechRecognizerCreate` is called, so the
-implementation must copy the contents if necessary.
-
-#### Declaration ####
-
-```
-SbSpeechRecognizer SbSpeechRecognizerCreate(const SbSpeechRecognizerHandler *handler)
-```
-
-### SbSpeechRecognizerDestroy ###
-
-Destroys the given speech recognizer. If the speech recognizer is in the started
-state, it is first stopped and then destroyed.
-
-#### Declaration ####
-
-```
-void SbSpeechRecognizerDestroy(SbSpeechRecognizer recognizer)
-```
-
-### SbSpeechRecognizerIsSupported ###
-
-Returns whether the platform supports SbSpeechRecognizer.
-
-#### Declaration ####
-
-```
-bool SbSpeechRecognizerIsSupported()
-```
-
-### SbSpeechRecognizerIsValid ###
-
-Indicates whether the given speech recognizer is valid.
-
-#### Declaration ####
-
-```
-static bool SbSpeechRecognizerIsValid(SbSpeechRecognizer recognizer)
-```
-
-### SbSpeechRecognizerStart ###
-
-Starts listening to audio and recognizing speech with the specified speech
-configuration. If `SbSpeechRecognizerStart` is called on an already started
-speech recognizer, the implementation MUST ignore the call and return false.
-
-Returns whether the speech recognizer is started successfully.
-
-#### Declaration ####
-
-```
-bool SbSpeechRecognizerStart(SbSpeechRecognizer recognizer, const SbSpeechConfiguration *configuration)
-```
-
-### SbSpeechRecognizerStop ###
-
-Stops listening to audio and returns a result using just the audio that it has
-already received. Once `SbSpeechRecognizerStop` is called, the implementation
-MUST NOT collect additional audio and MUST NOT continue to listen to the user.
-This is important for privacy reasons. If `SbSpeechRecognizerStop` is called on
-a speech recognizer which is already stopped or being stopped, the
-implementation MUST ignore the call.
-
-#### Declaration ####
-
-```
-void SbSpeechRecognizerStop(SbSpeechRecognizer recognizer)
-```
diff --git a/cobalt/version.h b/cobalt/version.h
index b0b7424..6eb86c4 100644
--- a/cobalt/version.h
+++ b/cobalt/version.h
@@ -35,6 +35,6 @@
 //                  release is cut.
 //.
 
-#define COBALT_VERSION "24.lts.2"
+#define COBALT_VERSION "24.lts.3"
 
 #endif  // COBALT_VERSION_H_
diff --git a/components/metrics/BUILD.gn b/components/metrics/BUILD.gn
index b7c04f3..53e47b4 100644
--- a/components/metrics/BUILD.gn
+++ b/components/metrics/BUILD.gn
@@ -120,6 +120,11 @@
 
   if (use_cobalt_customizations) {
     sources -= [
+      # Allows removal of unused sampled_profile.proto, which adds ~200KB to
+      # the binary. See b/290819695.
+      "call_stack_profile_metrics_provider.cc",
+      "call_stack_profile_metrics_provider.h",
+
       # All code should be OS agnostic above SB.
       "drive_metrics_provider_android.cc",
       "drive_metrics_provider_ios.mm",
@@ -130,7 +135,7 @@
       # MemoryMapped files, etc).
       "file_metrics_provider.cc",
       "file_metrics_provider.h",
-       # All code should be OS agnostic above SB.
+      # All code should be OS agnostic above SB.
       "machine_id_provider_win.cc",
       "system_memory_stats_recorder_linux.cc",
       "system_memory_stats_recorder_win.cc",
@@ -279,7 +284,6 @@
       "//components/metrics/public/interfaces:single_sample_metrics_mojo_bindings",
     ]
   }
-  
 
   source_set("call_stack_profile_params") {
     public = [
@@ -380,7 +384,6 @@
   }
 }
 
-# TODO(b/283275474): Re-enable as many of these tests as possible.
 if (!use_cobalt_customizations) {
   source_set("unit_tests") {
     testonly = true
@@ -469,3 +472,34 @@
     ]
   }
 }
+
+if (use_cobalt_customizations) {
+  target(gtest_target_type, "components_metrics_tests") {
+    testonly = true
+    has_pedantic_warnings = true
+    sources = [
+      "histogram_encoder_unittest.cc",
+      "metrics_log_manager_unittest.cc",
+      "metrics_log_store_unittest.cc",
+      "metrics_log_unittest.cc",
+      "metrics_service_unittest.cc",
+      "metrics_state_manager_unittest.cc",
+      "reporting_service_unittest.cc",
+      "stability_metrics_helper_unittest.cc",
+      "stability_metrics_provider_unittest.cc",
+    ]
+
+    deps = [
+      ":metrics",
+      ":test_support",
+      "//base",
+      "//base/test:test_support",
+      "//cobalt/test:run_all_unittests",
+      "//components/prefs:test_support",
+      "//components/variations",
+      "//extensions/buildflags",
+      "//testing/gtest",
+      "//third_party/zlib/google:compression_utils",
+    ]
+  }
+}
diff --git a/components/metrics/metrics_log.cc b/components/metrics/metrics_log.cc
index 39ad9b1..8fe32c7 100644
--- a/components/metrics/metrics_log.cc
+++ b/components/metrics/metrics_log.cc
@@ -169,11 +169,8 @@
 #endif
 
   metrics::SystemProfileProto::OS* os = system_profile->mutable_os();
-// TODO(b/283256747): Remove when base::SysInfo is Starboardized.
-#if !defined(STARBOARD)
   os->set_name(base::SysInfo::OperatingSystemName());
   os->set_version(base::SysInfo::OperatingSystemVersion());
-#endif
 #if defined(OS_CHROMEOS)
   os->set_kernel_version(base::SysInfo::KernelVersion());
 #elif defined(OS_ANDROID)
@@ -332,7 +329,8 @@
         internal::kUserActionEventLimit,
         uma_proto_.user_action_event_size() - internal::kUserActionEventLimit);
   }
-
+// Omnibox proto removed for binary size reasons: b/290819695.
+#if !defined(USE_COBALT_CUSTOMIZATIONS)
   if (uma_proto_.omnibox_event_size() > internal::kOmniboxEventLimit) {
     UMA_HISTOGRAM_COUNTS_100000("UMA.TruncatedEvents.Omnibox",
                                 uma_proto_.omnibox_event_size());
@@ -340,6 +338,7 @@
         internal::kOmniboxEventLimit,
         uma_proto_.omnibox_event_size() - internal::kOmniboxEventLimit);
   }
+#endif
 }
 
 void MetricsLog::GetEncodedLog(std::string* encoded_log) {
diff --git a/components/metrics/metrics_log_unittest.cc b/components/metrics/metrics_log_unittest.cc
index 8af49a8..bddc0fc 100644
--- a/components/metrics/metrics_log_unittest.cc
+++ b/components/metrics/metrics_log_unittest.cc
@@ -142,7 +142,7 @@
 #endif
   metrics::SystemProfileProto::Hardware* hardware =
       system_profile->mutable_hardware();
-#if !defined(OS_IOS)
+#if !defined(OS_IOS) && !defined(STARBOARD)
   hardware->set_cpu_architecture(base::SysInfo::OperatingSystemArchitecture());
 #endif
   hardware->set_system_ram_mb(base::SysInfo::AmountOfPhysicalMemoryMB());
@@ -229,6 +229,8 @@
   EXPECT_EQ(12, histogram_proto.bucket(4).max());
 }
 
+// TODO(b/283255893): Remove when base::CPU is Starboardized.
+#if !defined(STARBOARD)
 TEST_F(MetricsLogTest, RecordEnvironment) {
   TestMetricsServiceClient client;
   TestMetricsLog log(kClientId, kSessionId, MetricsLog::ONGOING_LOG, &client);
@@ -238,6 +240,7 @@
   // Check that the system profile on the log has the correct values set.
   CheckSystemProfile(log.system_profile());
 }
+#endif
 
 TEST_F(MetricsLogTest, RecordEnvironmentEnableDefault) {
   TestMetricsServiceClient client;
@@ -343,6 +346,8 @@
     log.RecordUserAction("BasicAction");
     EXPECT_EQ(i + 1, log.uma_proto().user_action_event_size());
   }
+// Omnibox proto removed for binary size reasons: b/290819695.
+#if !defined(USE_COBALT_CUSTOMIZATIONS)
   for (int i = 0; i < internal::kOmniboxEventLimit * 2; ++i) {
     // Add an empty omnibox event. Not fully realistic since these are normally
     // supplied by a metrics provider.
@@ -355,6 +360,7 @@
   EXPECT_EQ(internal::kUserActionEventLimit,
             log.uma_proto().user_action_event_size());
   EXPECT_EQ(internal::kOmniboxEventLimit, log.uma_proto().omnibox_event_size());
+#endif
 }
 
 }  // namespace metrics
diff --git a/components/metrics/metrics_service_unittest.cc b/components/metrics/metrics_service_unittest.cc
index 4350238..f10b79a 100644
--- a/components/metrics/metrics_service_unittest.cc
+++ b/components/metrics/metrics_service_unittest.cc
@@ -244,7 +244,10 @@
   EXPECT_TRUE(uma_log.has_session_id());
   EXPECT_TRUE(uma_log.has_system_profile());
   EXPECT_EQ(0, uma_log.user_action_event_size());
+// Omnibox proto removed for binary size reasons: b/290819695.
+#if !defined(USE_COBALT_CUSTOMIZATIONS)
   EXPECT_EQ(0, uma_log.omnibox_event_size());
+#endif
   EXPECT_EQ(0, uma_log.perf_data_size());
   CheckForNonStabilityHistograms(uma_log);
 
@@ -311,7 +314,10 @@
   EXPECT_TRUE(uma_log.has_session_id());
   EXPECT_TRUE(uma_log.has_system_profile());
   EXPECT_EQ(0, uma_log.user_action_event_size());
+// Omnibox proto removed for binary size reasons: b/290819695.
+#if !defined(USE_COBALT_CUSTOMIZATIONS)
   EXPECT_EQ(0, uma_log.omnibox_event_size());
+#endif
   EXPECT_EQ(0, uma_log.perf_data_size());
   CheckForNonStabilityHistograms(uma_log);
 
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
index 3b89398..28772bf 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
@@ -38,6 +38,7 @@
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.Locale;
+import java.util.Optional;
 
 /** A wrapper of the MediaCodec class. */
 @SuppressWarnings("unused")
@@ -82,7 +83,6 @@
   private boolean mFlushed;
   private long mLastPresentationTimeUs;
   private final String mMime;
-  private boolean mAdaptivePlaybackSupported;
   private double mPlaybackRate = 1.0;
   private int mFps = 30;
 
@@ -269,6 +269,7 @@
     private int mStatus;
     // May be null if mStatus is not MEDIA_CODEC_OK.
     private MediaFormat mFormat;
+    private Optional<Boolean> mFormatHasCropValues = Optional.empty();
 
     @SuppressWarnings("unused")
     @UsedByNative
@@ -277,18 +278,16 @@
       mFormat = null;
     }
 
-    @SuppressWarnings("unused")
-    @UsedByNative
-    private GetOutputFormatResult(int status, MediaFormat format) {
-      mStatus = status;
-      mFormat = format;
-    }
-
     private boolean formatHasCropValues() {
-      return mFormat.containsKey(KEY_CROP_RIGHT)
-          && mFormat.containsKey(KEY_CROP_LEFT)
-          && mFormat.containsKey(KEY_CROP_BOTTOM)
-          && mFormat.containsKey(KEY_CROP_TOP);
+      if (!mFormatHasCropValues.isPresent()) {
+        boolean hasCropValues =
+            mFormat.containsKey(KEY_CROP_RIGHT)
+                && mFormat.containsKey(KEY_CROP_LEFT)
+                && mFormat.containsKey(KEY_CROP_BOTTOM)
+                && mFormat.containsKey(KEY_CROP_TOP);
+        mFormatHasCropValues = Optional.of(hasCropValues);
+      }
+      return mFormatHasCropValues.get();
     }
 
     @SuppressWarnings("unused")
@@ -299,18 +298,38 @@
 
     @SuppressWarnings("unused")
     @UsedByNative
-    private int width() {
-      return formatHasCropValues()
-          ? mFormat.getInteger(KEY_CROP_RIGHT) - mFormat.getInteger(KEY_CROP_LEFT) + 1
-          : mFormat.getInteger(MediaFormat.KEY_WIDTH);
+    private int textureWidth() {
+      return mFormat.getInteger(MediaFormat.KEY_WIDTH);
     }
 
     @SuppressWarnings("unused")
     @UsedByNative
-    private int height() {
-      return formatHasCropValues()
-          ? mFormat.getInteger(KEY_CROP_BOTTOM) - mFormat.getInteger(KEY_CROP_TOP) + 1
-          : mFormat.getInteger(MediaFormat.KEY_HEIGHT);
+    private int textureHeight() {
+      return mFormat.getInteger(MediaFormat.KEY_HEIGHT);
+    }
+
+    @SuppressWarnings("unused")
+    @UsedByNative
+    private int cropLeft() {
+      return formatHasCropValues() ? mFormat.getInteger(KEY_CROP_LEFT) : -1;
+    }
+
+    @SuppressWarnings("unused")
+    @UsedByNative
+    private int cropTop() {
+      return formatHasCropValues() ? mFormat.getInteger(KEY_CROP_TOP) : -1;
+    }
+
+    @SuppressWarnings("unused")
+    @UsedByNative
+    private int cropRight() {
+      return formatHasCropValues() ? mFormat.getInteger(KEY_CROP_RIGHT) : -1;
+    }
+
+    @SuppressWarnings("unused")
+    @UsedByNative
+    private int cropBottom() {
+      return formatHasCropValues() ? mFormat.getInteger(KEY_CROP_BOTTOM) : -1;
     }
 
     @SuppressWarnings("unused")
@@ -427,7 +446,6 @@
       long nativeMediaCodecBridge,
       MediaCodec mediaCodec,
       String mime,
-      boolean adaptivePlaybackSupported,
       BitrateAdjustmentTypes bitrateAdjustmentType,
       int tunnelModeAudioSessionId) {
     if (mediaCodec == null) {
@@ -438,7 +456,6 @@
     mMime = mime; // TODO: Delete the unused mMime field
     mLastPresentationTimeUs = 0;
     mFlushed = true;
-    mAdaptivePlaybackSupported = adaptivePlaybackSupported;
     mBitrateAdjustmentType = bitrateAdjustmentType;
     mCallback =
         new MediaCodec.Callback() {
@@ -552,12 +569,7 @@
     }
     MediaCodecBridge bridge =
         new MediaCodecBridge(
-            nativeMediaCodecBridge,
-            mediaCodec,
-            mime,
-            true,
-            BitrateAdjustmentTypes.NO_ADJUSTMENT,
-            -1);
+            nativeMediaCodecBridge, mediaCodec, mime, BitrateAdjustmentTypes.NO_ADJUSTMENT, -1);
 
     MediaFormat mediaFormat = createAudioFormat(mime, sampleRate, channelCount);
 
@@ -591,8 +603,10 @@
       long nativeMediaCodecBridge,
       String mime,
       String decoderName,
-      int width,
-      int height,
+      // `widthHint` and `heightHint` are used to create the Android video format, which don't have
+      // to be directly related to the resolution of the video.
+      int widthHint,
+      int heightHint,
       int fps,
       int maxWidth,
       int maxHeight,
@@ -652,10 +666,10 @@
             nativeMediaCodecBridge,
             mediaCodec,
             mime,
-            true,
             BitrateAdjustmentTypes.NO_ADJUSTMENT,
             tunnelModeAudioSessionId);
-    MediaFormat mediaFormat = createVideoDecoderFormat(mime, width, height, videoCapabilities);
+    MediaFormat mediaFormat =
+        createVideoDecoderFormat(mime, widthHint, heightHint, videoCapabilities);
 
     boolean shouldConfigureHdr =
         colorInfo != null && MediaCodecUtil.isHdrCapableVideoDecoder(mime, codecCapabilities);
@@ -759,14 +773,7 @@
     }
 
     if (!bridge.configureVideo(
-        mediaFormat,
-        surface,
-        crypto,
-        0,
-        true,
-        maxWidth,
-        maxHeight,
-        outCreateMediaCodecBridgeResult)) {
+        mediaFormat, surface, crypto, 0, maxWidth, maxHeight, outCreateMediaCodecBridgeResult)) {
       Log.e(TAG, "Failed to configure video codec.");
       bridge.release();
       // outCreateMediaCodecBridgeResult.mErrorMessage is set inside configureVideo() on error.
@@ -853,7 +860,7 @@
     try {
       mFlushed = true;
       mMediaCodec.flush();
-    } catch (IllegalStateException e) {
+    } catch (Exception e) {
       Log.e(TAG, "Failed to flush MediaCodec", e);
       return MEDIA_CODEC_ERROR;
     }
@@ -1019,6 +1026,9 @@
           "Failed to queue secure input buffer, CryptoException with error code "
               + e.getErrorCode());
       return MEDIA_CODEC_ERROR;
+    } catch (IllegalArgumentException e) {
+      Log.e(TAG, "Failed to queue secure input buffer, IllegalArgumentException " + e);
+      return MEDIA_CODEC_ERROR;
     } catch (IllegalStateException e) {
       Log.e(TAG, "Failed to queue secure input buffer, IllegalStateException " + e);
       return MEDIA_CODEC_ERROR;
@@ -1055,37 +1065,25 @@
       Surface surface,
       MediaCrypto crypto,
       int flags,
-      boolean allowAdaptivePlayback,
       int maxSupportedWidth,
       int maxSupportedHeight,
       CreateMediaCodecBridgeResult outCreateMediaCodecBridgeResult) {
     try {
-      // If adaptive playback is turned off by request, then treat it as
-      // not supported.  Note that configureVideo is only called once
-      // during creation, else this would prevent re-enabling adaptive
-      // playback later.
-      if (!allowAdaptivePlayback) {
-        mAdaptivePlaybackSupported = false;
+      // Since we haven't passed the properties of the stream we're playing down to this level, from
+      // our perspective, we could potentially adapt up to 8k at any point. We thus request 8k
+      // buffers up front, unless the decoder claims to not be able to do 8k, in which case we're
+      // ok, since we would've rejected a 8k stream when canPlayType was called, and then use those
+      // decoder values instead. We only support 8k for API level 29 and above.
+      if (Build.VERSION.SDK_INT > 28) {
+        format.setInteger(MediaFormat.KEY_MAX_WIDTH, Math.min(7680, maxSupportedWidth));
+        format.setInteger(MediaFormat.KEY_MAX_HEIGHT, Math.min(4320, maxSupportedHeight));
+      } else {
+        // Android 5.0/5.1 seems not support 8K. Fallback to 4K until we get a
+        // better way to get maximum supported resolution.
+        format.setInteger(MediaFormat.KEY_MAX_WIDTH, Math.min(3840, maxSupportedWidth));
+        format.setInteger(MediaFormat.KEY_MAX_HEIGHT, Math.min(2160, maxSupportedHeight));
       }
 
-      if (mAdaptivePlaybackSupported) {
-        // Since we haven't passed the properties of the stream we're playing
-        // down to this level, from our perspective, we could potentially
-        // adapt up to 8k at any point. We thus request 8k buffers up front,
-        // unless the decoder claims to not be able to do 8k, in which case
-        // we're ok, since we would've rejected a 8k stream when canPlayType
-        // was called, and then use those decoder values instead. We only
-        // support 8k for API level 29 and above.
-        if (Build.VERSION.SDK_INT > 28) {
-          format.setInteger(MediaFormat.KEY_MAX_WIDTH, Math.min(7680, maxSupportedWidth));
-          format.setInteger(MediaFormat.KEY_MAX_HEIGHT, Math.min(4320, maxSupportedHeight));
-        } else {
-          // Android 5.0/5.1 seems not support 8K. Fallback to 4K until we get a
-          // better way to get maximum supported resolution.
-          format.setInteger(MediaFormat.KEY_MAX_WIDTH, Math.min(3840, maxSupportedWidth));
-          format.setInteger(MediaFormat.KEY_MAX_HEIGHT, Math.min(2160, maxSupportedHeight));
-        }
-      }
       maybeSetMaxInputSize(format);
       mMediaCodec.configure(format, surface, crypto, flags);
       mFrameRateEstimator = new FrameRateEstimator();
@@ -1115,11 +1113,11 @@
   }
 
   private static MediaFormat createVideoDecoderFormat(
-      String mime, int width, int height, VideoCapabilities videoCapabilities) {
+      String mime, int widthHint, int heightHint, VideoCapabilities videoCapabilities) {
     return MediaFormat.createVideoFormat(
         mime,
-        alignDimension(width, videoCapabilities.getWidthAlignment()),
-        alignDimension(height, videoCapabilities.getHeightAlignment()));
+        alignDimension(widthHint, videoCapabilities.getWidthAlignment()),
+        alignDimension(heightHint, videoCapabilities.getHeightAlignment()));
   }
 
   private static int alignDimension(int size, int alignment) {
@@ -1145,11 +1143,11 @@
       return;
     }
     int maxHeight = format.getInteger(MediaFormat.KEY_HEIGHT);
-    if (mAdaptivePlaybackSupported && format.containsKey(MediaFormat.KEY_MAX_HEIGHT)) {
+    if (format.containsKey(MediaFormat.KEY_MAX_HEIGHT)) {
       maxHeight = Math.max(maxHeight, format.getInteger(MediaFormat.KEY_MAX_HEIGHT));
     }
     int maxWidth = format.getInteger(MediaFormat.KEY_WIDTH);
-    if (mAdaptivePlaybackSupported && format.containsKey(MediaFormat.KEY_MAX_WIDTH)) {
+    if (format.containsKey(MediaFormat.KEY_MAX_WIDTH)) {
       maxWidth = Math.max(maxHeight, format.getInteger(MediaFormat.KEY_MAX_WIDTH));
     }
     int maxPixels;
@@ -1198,14 +1196,6 @@
 
   @SuppressWarnings("unused")
   @UsedByNative
-  private boolean isAdaptivePlaybackSupported(int width, int height) {
-    // If media codec has adaptive playback supported, then the max sizes
-    // used during creation are only hints.
-    return mAdaptivePlaybackSupported;
-  }
-
-  @SuppressWarnings("unused")
-  @UsedByNative
   private static void setCodecSpecificData(MediaFormat format, int index, byte[] bytes) {
     // Codec Specific Data is set in the MediaFormat as ByteBuffer entries with keys csd-0,
     // csd-1, and so on. See: http://developer.android.com/reference/android/media/MediaCodec.html
diff --git a/starboard/android/shared/BUILD.gn b/starboard/android/shared/BUILD.gn
index 17d91c7..b07982a 100644
--- a/starboard/android/shared/BUILD.gn
+++ b/starboard/android/shared/BUILD.gn
@@ -476,8 +476,11 @@
 
   sources = media_tests_sources + player_tests_sources + [
               "//starboard/common/test_main.cc",
+              "//starboard/shared/starboard/drm/drm_test_helpers.cc",
+              "//starboard/shared/starboard/drm/drm_test_helpers.h",
               "jni_env_ext_test.cc",
               "model_year_test.cc",
+              "player_get_preferred_output_mode_test.cc",
             ]
 
   configs += [ "//starboard/build/config:starboard_implementation" ]
diff --git a/starboard/android/shared/audio_renderer_passthrough.cc b/starboard/android/shared/audio_renderer_passthrough.cc
index c43fac3..610baaa 100644
--- a/starboard/android/shared/audio_renderer_passthrough.cc
+++ b/starboard/android/shared/audio_renderer_passthrough.cc
@@ -282,6 +282,7 @@
   stop_called_ = false;
   playback_head_position_when_stopped_ = 0;
   stopped_at_ = 0;
+  first_audio_timestamp_ = -1;
   if (!seek_to_time_set) {
     seek_to_time_ = seek_to_time;
   }
@@ -312,9 +313,17 @@
     return seek_to_time_;
   }
 
+  SbTime audio_start_time;
+  if (first_audio_timestamp_ > -1) {
+    audio_start_time = first_audio_timestamp_;
+  } else {
+    audio_start_time = seek_to_time_;
+  }
+
+  SbTime playback_time;
   if (stop_called_) {
     // When AudioTrackBridge::Stop() is called, the playback will continue until
-    // all the frames written are played, as the AudioTrack in created in
+    // all the frames written are played, as the AudioTrack is created in
     // MODE_STREAM.
     auto now = SbTimeGetMonotonicNow();
     SB_DCHECK(now >= stopped_at_);
@@ -324,8 +333,10 @@
     int64_t total_frames_played =
         frames_played + playback_head_position_when_stopped_;
     total_frames_played = std::min(total_frames_played, total_frames_written_);
-    return seek_to_time_ + total_frames_played * kSbTimeSecond /
+    playback_time =
+        audio_start_time + total_frames_played * kSbTimeSecond /
                                audio_stream_info_.samples_per_second;
+    return std::max(playback_time, seek_to_time_);
   }
 
   SbTime updated_at;
@@ -334,14 +345,13 @@
   if (playback_head_position <= 0) {
     // The playback is warming up, don't adjust the media time by the monotonic
     // system time.
-    return seek_to_time_;
+    return std::max(audio_start_time, seek_to_time_);
   }
 
   // TODO: This may cause time regression, because the unadjusted time will be
   //       returned on pause, after an adjusted time has been returned.
-  SbTime playback_time =
-      seek_to_time_ + playback_head_position * kSbTimeSecond /
-                          audio_stream_info_.samples_per_second;
+  playback_time = audio_start_time + playback_head_position * kSbTimeSecond /
+                                         audio_stream_info_.samples_per_second;
 
   // When underlying AudioTrack is paused, we use returned playback time
   // directly. Note that we should not use |paused_| or |playback_rate_| here.
@@ -352,7 +362,7 @@
   // before calling AudioTrack.Play(), the returned playback time and last frame
   // consumed time would be the same as at when we pause the video.
   if (audio_track_paused_) {
-    return playback_time;
+    return std::max(playback_time, seek_to_time_);
   }
 
   // TODO: Cap this to the maximum frames written to the AudioTrack.
@@ -366,7 +376,7 @@
 
   playback_time += std::max<SbTime>(now - updated_at, 0);
 
-  return playback_time;
+  return std::max(playback_time, seek_to_time_);
 }
 
 void AudioRendererPassthrough::CreateAudioTrackAndStartProcessing() {
@@ -542,6 +552,11 @@
         audio_track_bridge_->PauseAndFlush();
         return;
       }
+
+      if (first_audio_timestamp_ < 0) {
+        first_audio_timestamp_ = sync_time;
+      }
+
       decoded_audio_writing_offset_ += samples_written;
 
       if (decoded_audio_writing_offset_ ==
diff --git a/starboard/android/shared/audio_renderer_passthrough.h b/starboard/android/shared/audio_renderer_passthrough.h
index d4063df..7d9a1a3 100644
--- a/starboard/android/shared/audio_renderer_passthrough.h
+++ b/starboard/android/shared/audio_renderer_passthrough.h
@@ -130,6 +130,7 @@
   int64_t playback_head_position_when_stopped_ = 0;
   SbTimeMonotonic stopped_at_ = 0;
   SbTime seek_to_time_ = 0;
+  SbTime first_audio_timestamp_ = -1;
   double volume_ = 1.0;
   bool paused_ = true;
   double playback_rate_ = 1.0;
diff --git a/starboard/android/shared/media_codec_bridge.cc b/starboard/android/shared/media_codec_bridge.cc
index 298ea0c..fe97038 100644
--- a/starboard/android/shared/media_codec_bridge.cc
+++ b/starboard/android/shared/media_codec_bridge.cc
@@ -215,8 +215,8 @@
 // static
 scoped_ptr<MediaCodecBridge> MediaCodecBridge::CreateVideoMediaCodecBridge(
     SbMediaVideoCodec video_codec,
-    int width,
-    int height,
+    int width_hint,
+    int height_hint,
     int fps,
     optional<int> max_width,
     optional<int> max_height,
@@ -322,9 +322,10 @@
       "Ldev/cobalt/media/MediaCodecBridge$CreateMediaCodecBridgeResult;)"
       "V",
       reinterpret_cast<jlong>(native_media_codec_bridge.get()), j_mime.Get(),
-      j_decoder_name.Get(), width, height, fps, max_width.value_or(-1),
-      max_height.value_or(-1), j_surface, j_media_crypto, j_color_info.Get(),
-      tunnel_mode_audio_session_id, j_create_media_codec_bridge_result.Get());
+      j_decoder_name.Get(), width_hint, height_hint, fps,
+      max_width.value_or(-1), max_height.value_or(-1), j_surface,
+      j_media_crypto, j_color_info.Get(), tunnel_mode_audio_session_id,
+      j_create_media_codec_bridge_result.Get());
 
   jobject j_media_codec_bridge = env->CallObjectMethodOrAbort(
       j_create_media_codec_bridge_result.Get(), "mediaCodecBridge",
@@ -451,16 +452,25 @@
                                                 "()I");
 }
 
-SurfaceDimensions MediaCodecBridge::GetOutputDimensions() {
+FrameSize MediaCodecBridge::GetOutputSize() {
   JniEnvExt* env = JniEnvExt::Get();
   env->CallVoidMethodOrAbort(
       j_media_codec_bridge_, "getOutputFormat",
       "(Ldev/cobalt/media/MediaCodecBridge$GetOutputFormatResult;)V",
       j_reused_get_output_format_result_);
-  return {env->CallIntMethodOrAbort(j_reused_get_output_format_result_, "width",
-                                    "()I"),
-          env->CallIntMethodOrAbort(j_reused_get_output_format_result_,
-                                    "height", "()I")};
+
+  auto call_int_method = [env, this](const char* name) {
+    return env->CallIntMethodOrAbort(j_reused_get_output_format_result_, name,
+                                     "()I");
+  };
+
+  FrameSize size = {
+      call_int_method("textureWidth"), call_int_method("textureHeight"),
+      call_int_method("cropLeft"),     call_int_method("cropTop"),
+      call_int_method("cropRight"),    call_int_method("cropBottom")};
+
+  size.DCheckValid();
+  return size;
 }
 
 AudioOutputFormatResult MediaCodecBridge::GetAudioOutputFormat() {
diff --git a/starboard/android/shared/media_codec_bridge.h b/starboard/android/shared/media_codec_bridge.h
index 1490b75..bf395d5 100644
--- a/starboard/android/shared/media_codec_bridge.h
+++ b/starboard/android/shared/media_codec_bridge.h
@@ -63,9 +63,53 @@
   jint num_bytes;
 };
 
-struct SurfaceDimensions {
-  jint width;
-  jint height;
+struct FrameSize {
+  jint texture_width;
+  jint texture_height;
+
+  // Crop values can be set to -1 when they are not provided by the platform
+  jint crop_left = -1;
+  jint crop_top = -1;
+  jint crop_right = -1;
+  jint crop_bottom = -1;
+
+  bool has_crop_values() const {
+    return crop_left >= 0 && crop_top >= 0 && crop_right >= 0 &&
+           crop_bottom >= 0;
+  }
+
+  jint display_width() const {
+    if (has_crop_values()) {
+      return crop_right - crop_left + 1;
+    }
+
+    return texture_width;
+  }
+
+  jint display_height() const {
+    if (has_crop_values()) {
+      return crop_bottom - crop_top + 1;
+    }
+
+    return texture_height;
+  }
+
+  void DCheckValid() const {
+    SB_DCHECK(texture_width >= 0) << texture_width;
+    SB_DCHECK(texture_height >= 0) << texture_height;
+
+    if (crop_left >= 0 || crop_top >= 0 || crop_right >= 0 ||
+        crop_bottom >= 0) {
+      // If there is at least one crop value set, all of them should be set.
+      SB_DCHECK(crop_left >= 0) << crop_left;
+      SB_DCHECK(crop_top >= 0) << crop_top;
+      SB_DCHECK(crop_right >= 0) << crop_right;
+      SB_DCHECK(crop_bottom >= 0) << crop_bottom;
+      SB_DCHECK(has_crop_values());
+      SB_DCHECK(display_width() >= 0) << display_width();
+      SB_DCHECK(display_height() >= 0) << display_height();
+    }
+  }
 };
 
 struct AudioOutputFormatResult {
@@ -113,8 +157,11 @@
   // them without the other), which will be checked in the function.
   static scoped_ptr<MediaCodecBridge> CreateVideoMediaCodecBridge(
       SbMediaVideoCodec video_codec,
-      int width,
-      int height,
+      // `width_hint` and `height_hint` are used to create the Android video
+      // format, which don't have to be directly related to the resolution of
+      // the video.
+      int width_hint,
+      int height_hint,
       int fps,
       optional<int> max_width,
       optional<int> max_height,
@@ -152,7 +199,7 @@
 
   void SetPlaybackRate(double playback_rate);
   jint Flush();
-  SurfaceDimensions GetOutputDimensions();
+  FrameSize GetOutputSize();
   AudioOutputFormatResult GetAudioOutputFormat();
 
   void OnMediaCodecError(bool is_recoverable,
diff --git a/starboard/android/shared/media_decoder.cc b/starboard/android/shared/media_decoder.cc
index a44acf8..721680e 100644
--- a/starboard/android/shared/media_decoder.cc
+++ b/starboard/android/shared/media_decoder.cc
@@ -60,7 +60,7 @@
     case MEDIA_CODEC_ERROR:
       return "MEDIA_CODEC_ERROR";
     default:
-      SB_NOTREACHED();
+      SB_NOTREACHED() << "Unknown status value: " << status;
       return "MEDIA_CODEC_ERROR_UNKNOWN";
   }
 }
@@ -103,8 +103,8 @@
 
 MediaDecoder::MediaDecoder(Host* host,
                            SbMediaVideoCodec video_codec,
-                           int width,
-                           int height,
+                           int width_hint,
+                           int height_hint,
                            optional<int> max_width,
                            optional<int> max_height,
                            int fps,
@@ -130,7 +130,7 @@
       drm_system_ && drm_system_->require_secured_decoder();
   SB_DCHECK(!drm_system_ || j_media_crypto);
   media_codec_bridge_ = MediaCodecBridge::CreateVideoMediaCodecBridge(
-      video_codec, width, height, fps, max_width, max_height, this,
+      video_codec, width_hint, height_hint, fps, max_width, max_height, this,
       j_output_surface, j_media_crypto, color_metadata, require_secured_decoder,
       require_software_codec, tunnel_mode_audio_session_id,
       force_big_endian_hdr_metadata, force_improved_support_check,
diff --git a/starboard/android/shared/media_decoder.h b/starboard/android/shared/media_decoder.h
index 350e611..7c7a2eb 100644
--- a/starboard/android/shared/media_decoder.h
+++ b/starboard/android/shared/media_decoder.h
@@ -82,8 +82,11 @@
                SbDrmSystem drm_system);
   MediaDecoder(Host* host,
                SbMediaVideoCodec video_codec,
-               int width,
-               int height,
+               // `width_hint` and `height_hint` are used to create the Android
+               // video format, which don't have to be directly related to the
+               // resolution of the video.
+               int width_hint,
+               int height_hint,
                optional<int> max_width,
                optional<int> max_height,
                int fps,
diff --git a/starboard/android/shared/player_components_factory.h b/starboard/android/shared/player_components_factory.h
index eb85c45..18db0ba 100644
--- a/starboard/android/shared/player_components_factory.h
+++ b/starboard/android/shared/player_components_factory.h
@@ -515,8 +515,8 @@
     }
 
     scoped_ptr<VideoDecoder> video_decoder(new VideoDecoder(
-        creation_parameters.video_codec(), creation_parameters.drm_system(),
-        creation_parameters.output_mode(),
+        creation_parameters.video_stream_info(),
+        creation_parameters.drm_system(), creation_parameters.output_mode(),
         creation_parameters.decode_target_graphics_context_provider(),
         creation_parameters.max_video_capabilities(),
         tunnel_mode_audio_session_id, force_secure_pipeline_under_tunnel_mode,
diff --git a/starboard/android/shared/player_get_preferred_output_mode.cc b/starboard/android/shared/player_get_preferred_output_mode.cc
index 4639159..c7485e3 100644
--- a/starboard/android/shared/player_get_preferred_output_mode.cc
+++ b/starboard/android/shared/player_get_preferred_output_mode.cc
@@ -17,6 +17,7 @@
 #include <algorithm>
 
 #include "starboard/configuration.h"
+#include "starboard/shared/starboard/media/media_util.h"
 #include "starboard/shared/starboard/player/filter/player_components.h"
 #include "starboard/string.h"
 
@@ -64,35 +65,49 @@
   auto drm_system = creation_param->drm_system;
   auto max_video_capabilities = video_stream_info.max_video_capabilities;
 
-  // Sub players must use decode-to-texture on Android.
+  bool is_sdr = true;
+  if (codec != kSbMediaVideoCodecNone) {
+    const auto& color_metadata = video_stream_info.color_metadata;
+    is_sdr = starboard::shared::starboard::media::IsSDRVideo(
+        color_metadata.bits_per_channel, color_metadata.primaries,
+        color_metadata.transfer, color_metadata.matrix);
+  }
+
   if (max_video_capabilities && strlen(max_video_capabilities) > 0) {
+    // Sub players must use "decode-to-texture" on Android.
+    // Since hdr videos are not supported under "decode-to-texture" mode, reject
+    // it for sub players.
     if (PlayerComponents::Factory::OutputModeSupported(
-            kSbPlayerOutputModeDecodeToTexture, codec, drm_system)) {
+            kSbPlayerOutputModeDecodeToTexture, codec, drm_system) &&
+        is_sdr) {
       return kSbPlayerOutputModeDecodeToTexture;
     }
-    SB_NOTREACHED();
     return kSbPlayerOutputModeInvalid;
   }
 
-  // The main player may use any output mode.
-  SbPlayerOutputMode output_modes_to_check[] = {
-      kSbPlayerOutputModePunchOut,
-      kSbPlayerOutputModeDecodeToTexture,
-  };
+  SbPlayerOutputMode output_modes_to_check[2] = {kSbPlayerOutputModeInvalid,
+                                                 kSbPlayerOutputModeInvalid};
+  int number_of_output_modes_to_check = 2;
 
-  // Check |kSbPlayerOutputModeDecodeToTexture| first if the caller prefers it.
-  if (creation_param->output_mode == kSbPlayerOutputModeDecodeToTexture) {
-    std::swap(output_modes_to_check[0], output_modes_to_check[1]);
+  if (is_sdr) {
+    if (creation_param->output_mode == kSbPlayerOutputModeDecodeToTexture) {
+      output_modes_to_check[0] = kSbPlayerOutputModeDecodeToTexture;
+      output_modes_to_check[1] = kSbPlayerOutputModePunchOut;
+    } else {
+      output_modes_to_check[0] = kSbPlayerOutputModePunchOut;
+      output_modes_to_check[1] = kSbPlayerOutputModeDecodeToTexture;
+    }
+  } else {
+    // HDR videos require "punch-out".
+    output_modes_to_check[0] = kSbPlayerOutputModePunchOut;
+    number_of_output_modes_to_check = 1;
   }
 
-  if (PlayerComponents::Factory::OutputModeSupported(output_modes_to_check[0],
-                                                     codec, drm_system)) {
-    return output_modes_to_check[0];
-  }
-
-  if (PlayerComponents::Factory::OutputModeSupported(output_modes_to_check[1],
-                                                     codec, drm_system)) {
-    return output_modes_to_check[1];
+  for (int i = 0; i < number_of_output_modes_to_check; ++i) {
+    if (PlayerComponents::Factory::OutputModeSupported(output_modes_to_check[i],
+                                                       codec, drm_system)) {
+      return output_modes_to_check[i];
+    }
   }
 
   SB_LOG(WARNING) << "creation_param->video_stream_info.codec ("
diff --git a/starboard/android/shared/player_get_preferred_output_mode_test.cc b/starboard/android/shared/player_get_preferred_output_mode_test.cc
new file mode 100644
index 0000000..775d76c
--- /dev/null
+++ b/starboard/android/shared/player_get_preferred_output_mode_test.cc
@@ -0,0 +1,149 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+
+#include "starboard/drm.h"
+#include "starboard/media.h"
+#include "starboard/player.h"
+#include "starboard/shared/starboard/drm/drm_test_helpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// The Android TV `SbPlayerGetPreferredOutputMode()` implementation has special
+// constraints that requires it own tests.
+
+namespace starboard {
+namespace android {
+namespace shared {
+namespace {
+
+using ::starboard::shared::starboard::drm::CreateDummyDrmSystem;
+
+SbPlayerCreationParam GetDefaultPlaybackParam() {
+  SbPlayerCreationParam creation_param = {
+      kSbDrmSystemInvalid,
+      {
+          kSbMediaAudioCodecAac,
+      },
+      {
+          kSbMediaVideoCodecVp9,
+      },
+  };
+
+  creation_param.audio_stream_info.mime = "";
+  creation_param.audio_stream_info.number_of_channels = 2;
+  creation_param.audio_stream_info.samples_per_second = 48000;
+  creation_param.audio_stream_info.bits_per_sample = 16;
+
+  auto& video_stream_info = creation_param.video_stream_info;
+
+  video_stream_info.mime = "";
+  video_stream_info.max_video_capabilities = "";
+  video_stream_info.frame_width = 640;
+  video_stream_info.frame_height = 480;
+
+  video_stream_info.color_metadata.bits_per_channel = 8;
+  video_stream_info.color_metadata.primaries = kSbMediaPrimaryIdBt709;
+  video_stream_info.color_metadata.transfer = kSbMediaTransferIdBt709;
+  video_stream_info.color_metadata.matrix = kSbMediaMatrixIdBt709;
+
+  return creation_param;
+}
+
+SbPlayerCreationParam GetSdrPlaybackParam() {
+  return GetDefaultPlaybackParam();
+}
+
+SbPlayerCreationParam GetHdrPlaybackParam() {
+  SbPlayerCreationParam creation_param = GetDefaultPlaybackParam();
+  auto& video_stream_info = creation_param.video_stream_info;
+
+  video_stream_info.color_metadata.bits_per_channel = 10;
+  video_stream_info.color_metadata.primaries = kSbMediaPrimaryIdBt2020;
+  video_stream_info.color_metadata.transfer = kSbMediaTransferId10BitBt2020;
+  video_stream_info.color_metadata.matrix =
+      kSbMediaMatrixIdBt2020ConstantLuminance;
+
+  return creation_param;
+}
+
+TEST(SbPlayerGetPreferredOutputModeTest, HdrPlaybackRequiresPunchOut) {
+  SbPlayerCreationParam creation_param = GetHdrPlaybackParam();
+
+  creation_param.output_mode = kSbPlayerOutputModeDecodeToTexture;
+  auto preferred_output_mode = SbPlayerGetPreferredOutputMode(&creation_param);
+  EXPECT_EQ(preferred_output_mode, kSbPlayerOutputModePunchOut);
+}
+
+TEST(SbPlayerGetPreferredOutputModeTest,
+     SecondaryPlayerRequiresDecodeToTexture) {
+  SbPlayerCreationParam creation_param = GetSdrPlaybackParam();
+
+  creation_param.video_stream_info.max_video_capabilities =
+      "width=640; height=480";
+  creation_param.output_mode = kSbPlayerOutputModePunchOut;
+  auto preferred_output_mode = SbPlayerGetPreferredOutputMode(&creation_param);
+  EXPECT_EQ(preferred_output_mode, kSbPlayerOutputModeDecodeToTexture);
+}
+
+TEST(SbPlayerGetPreferredOutputModeTest, SecondaryPlayerCannotBeHdr) {
+  SbPlayerCreationParam creation_param = GetHdrPlaybackParam();
+
+  creation_param.video_stream_info.max_video_capabilities =
+      "width=640; height=480";
+  creation_param.output_mode = kSbPlayerOutputModeDecodeToTexture;
+  auto preferred_output_mode = SbPlayerGetPreferredOutputMode(&creation_param);
+  EXPECT_EQ(preferred_output_mode, kSbPlayerOutputModeInvalid);
+}
+
+TEST(SbPlayerGetPreferredOutputModeTest, SecuredDrmRequiresPunchOut) {
+  SbPlayerCreationParam creation_param = GetSdrPlaybackParam();
+
+  SbDrmSystem drm_system = CreateDummyDrmSystem("com.widevine");
+  ASSERT_TRUE(SbDrmSystemIsValid(drm_system));
+
+  creation_param.drm_system = drm_system;
+  creation_param.output_mode = kSbPlayerOutputModeDecodeToTexture;
+  auto preferred_output_mode = SbPlayerGetPreferredOutputMode(&creation_param);
+  EXPECT_EQ(preferred_output_mode, kSbPlayerOutputModePunchOut);
+
+  SbDrmDestroySystem(drm_system);
+}
+
+TEST(SbPlayerGetPreferredOutputModeTest,
+     SecuredDrmWithDecodeToTextureIsInvalid) {
+  SbPlayerCreationParam creation_param = GetSdrPlaybackParam();
+
+  SbDrmSystem drm_system = CreateDummyDrmSystem("com.widevine");
+  ASSERT_TRUE(SbDrmSystemIsValid(drm_system));
+
+  creation_param.drm_system = drm_system;
+  creation_param.video_stream_info.max_video_capabilities =
+      "width=640; height=480";
+
+  creation_param.output_mode = kSbPlayerOutputModePunchOut;
+  auto preferred_output_mode = SbPlayerGetPreferredOutputMode(&creation_param);
+  EXPECT_EQ(preferred_output_mode, kSbPlayerOutputModeInvalid);
+
+  creation_param.output_mode = kSbPlayerOutputModeDecodeToTexture;
+  preferred_output_mode = SbPlayerGetPreferredOutputMode(&creation_param);
+  EXPECT_EQ(preferred_output_mode, kSbPlayerOutputModeInvalid);
+
+  SbDrmDestroySystem(drm_system);
+}
+
+}  // namespace
+}  // namespace shared
+}  // namespace android
+}  // namespace starboard
diff --git a/starboard/android/shared/test_filters.py b/starboard/android/shared/test_filters.py
index b2a269b..f4840c1 100644
--- a/starboard/android/shared/test_filters.py
+++ b/starboard/android/shared/test_filters.py
@@ -85,6 +85,15 @@
 
         # TODO: Filter this test on a per-device basis.
         'SbMediaCanPlayMimeAndKeySystem.MinimumSupport',
+
+        # TODO: b/289281412 Make this test work on lab devices consistently.
+        'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.PartialAudio/*',
+
+        # TODO: b/292319097 Make this test work on lab devices consistently.
+        'SbPlayerTest.MaxVideoCapabilities',
+
+        # TODO: b/292409536 Make this test fork on lab devices consistently.
+        'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.PartialAudioDiscardAll/*',
     ],
 }
 # pylint: enable=line-too-long
diff --git a/starboard/android/shared/video_decoder.cc b/starboard/android/shared/video_decoder.cc
index e906faf..89ea05d 100644
--- a/starboard/android/shared/video_decoder.cc
+++ b/starboard/android/shared/video_decoder.cc
@@ -102,12 +102,12 @@
 }
 
 void ParseMaxResolution(const std::string& max_video_capabilities,
-                        int window_width,
-                        int window_height,
+                        int frame_width,
+                        int frame_height,
                         optional<int>* max_width,
                         optional<int>* max_height) {
-  SB_DCHECK(window_width > 0);
-  SB_DCHECK(window_height > 0);
+  SB_DCHECK(frame_width > 0);
+  SB_DCHECK(frame_height > 0);
   SB_DCHECK(max_width);
   SB_DCHECK(max_height);
 
@@ -148,31 +148,31 @@
     return;
   }
 
-  if (window_width <= 0 || window_height <= 0) {
+  if (frame_width <= 0 || frame_height <= 0) {
     // We DCHECK() above, but just be safe.
     SB_LOG(WARNING)
-        << "Failed to parse max resolutions due to invalid window resolutions ("
-        << window_width << ", " << window_height << ").";
+        << "Failed to parse max resolutions due to invalid frame resolutions ("
+        << frame_width << ", " << frame_height << ").";
     return;
   }
 
   if (width > 0) {
     *max_width = width;
-    *max_height = max_width->value() * window_height / window_width;
+    *max_height = max_width->value() * frame_height / frame_width;
     SB_LOG(INFO) << "Inferred max height (" << *max_height
                  << ") from max_width (" << *max_width
-                 << ") and window resolution @ (" << window_width << ", "
-                 << window_height << ").";
+                 << ") and frame resolution @ (" << frame_width << ", "
+                 << frame_height << ").";
     return;
   }
 
   if (height > 0) {
     *max_height = height;
-    *max_width = max_height->value() * window_width / window_height;
+    *max_width = max_height->value() * frame_width / frame_height;
     SB_LOG(INFO) << "Inferred max width (" << *max_width
                  << ") from max_height (" << *max_height
-                 << ") and window resolution @ (" << window_width << ", "
-                 << window_height << ").";
+                 << ") and frame resolution @ (" << frame_width << ", "
+                 << frame_height << ").";
   }
 }
 
@@ -344,7 +344,7 @@
   bool rendered_;
 };
 
-VideoDecoder::VideoDecoder(SbMediaVideoCodec video_codec,
+VideoDecoder::VideoDecoder(const VideoStreamInfo& video_stream_info,
                            SbDrmSystem drm_system,
                            SbPlayerOutputMode output_mode,
                            SbDecodeTargetGraphicsContextProvider*
@@ -356,7 +356,7 @@
                            bool force_big_endian_hdr_metadata,
                            bool force_improved_support_check,
                            std::string* error_message)
-    : video_codec_(video_codec),
+    : video_codec_(video_stream_info.codec),
       drm_system_(static_cast<DrmSystem*>(drm_system)),
       output_mode_(output_mode),
       decode_target_graphics_context_provider_(
@@ -390,7 +390,7 @@
   }
 
   if (video_codec_ != kSbMediaVideoCodecAv1) {
-    if (!InitializeCodec(error_message)) {
+    if (!InitializeCodec(video_stream_info, error_message)) {
       *error_message =
           "Failed to initialize video decoder with error: " + *error_message;
       SB_LOG(ERROR) << *error_message;
@@ -490,7 +490,8 @@
     // because we need to change the color metadata.
     if (video_codec_ != kSbMediaVideoCodecAv1 && media_decoder_ == NULL) {
       std::string error_message;
-      if (!InitializeCodec(&error_message)) {
+      if (!InitializeCodec(input_buffers.front()->video_stream_info(),
+                           &error_message)) {
         error_message =
             "Failed to reinitialize codec with error: " + error_message;
         SB_LOG(ERROR) << error_message;
@@ -519,7 +520,8 @@
       return;
     }
     std::string error_message;
-    if (!InitializeCodec(&error_message)) {
+    if (!InitializeCodec(pending_input_buffers_.front()->video_stream_info(),
+                         &error_message)) {
       error_message =
           "Failed to reinitialize codec with error: " + error_message;
       SB_LOG(ERROR) << error_message;
@@ -556,7 +558,8 @@
     SB_DCHECK(pending_input_buffers_.size() == input_buffer_written_);
 
     std::string error_message;
-    if (!InitializeCodec(&error_message)) {
+    if (!InitializeCodec(pending_input_buffers_.front()->video_stream_info(),
+                         &error_message)) {
       error_message =
           "Failed to reinitialize codec with error: " + error_message;
       SB_LOG(ERROR) << error_message;
@@ -601,11 +604,12 @@
   //       it depends on the behavior of the video renderer.
 }
 
-bool VideoDecoder::InitializeCodec(std::string* error_message) {
+bool VideoDecoder::InitializeCodec(const VideoStreamInfo& video_stream_info,
+                                   std::string* error_message) {
   SB_DCHECK(BelongsToCurrentThread());
   SB_DCHECK(error_message);
 
-  if (video_codec_ == kSbMediaVideoCodecAv1) {
+  if (video_stream_info.codec == kSbMediaVideoCodecAv1) {
     SB_DCHECK(pending_input_buffers_.size() > 0);
 
     // Guesstimate the video fps.
@@ -679,17 +683,9 @@
     return false;
   }
 
-  int window_width, window_height;
-  if (!GetVideoWindowSize(&window_width, &window_height)) {
-    *error_message =
-        "Can't initialize the codec since we don't have a video window.";
-    SB_LOG(ERROR) << *error_message;
-    return false;
-  }
-
   jobject j_media_crypto = drm_system_ ? drm_system_->GetMediaCrypto() : NULL;
   SB_DCHECK(!drm_system_ || j_media_crypto);
-  if (video_codec_ == kSbMediaVideoCodecAv1) {
+  if (video_stream_info.codec == kSbMediaVideoCodecAv1) {
     SB_DCHECK(video_fps_ > 0);
   } else {
     SB_DCHECK(video_fps_ == 0);
@@ -698,12 +694,13 @@
   optional<int> max_width, max_height;
   // TODO(b/281431214): Evaluate if we should also parse the fps from
   //                    `max_video_capabilities_` and pass to MediaDecoder ctor.
-  ParseMaxResolution(max_video_capabilities_, window_width, window_height,
-                     &max_width, &max_height);
+  ParseMaxResolution(max_video_capabilities_, video_stream_info.frame_width,
+                     video_stream_info.frame_height, &max_width, &max_height);
 
   media_decoder_.reset(new MediaDecoder(
-      this, video_codec_, window_width, window_height, max_width, max_height,
-      video_fps_, j_output_surface, drm_system_,
+      this, video_stream_info.codec, video_stream_info.frame_width,
+      video_stream_info.frame_height, max_width, max_height, video_fps_,
+      j_output_surface, drm_system_,
       color_metadata_ ? &*color_metadata_ : nullptr, require_software_codec_,
       std::bind(&VideoDecoder::OnTunnelModeFrameRendered, this, _1),
       tunnel_mode_audio_session_id_, force_big_endian_hdr_metadata_,
@@ -715,7 +712,7 @@
     }
     media_decoder_->SetPlaybackRate(playback_rate_);
 
-    if (video_codec_ == kSbMediaVideoCodecAv1) {
+    if (video_stream_info.codec == kSbMediaVideoCodecAv1) {
       SB_DCHECK(!pending_input_buffers_.empty());
     } else {
       SB_DCHECK(pending_input_buffers_.empty());
@@ -889,11 +886,8 @@
   SB_DLOG(INFO) << "Output format changed, trying to dequeue again.";
 
   ScopedLock lock(decode_target_mutex_);
-  // Record the latest width/height of the decoded input.
-  SurfaceDimensions output_dimensions =
-      media_codec_bridge->GetOutputDimensions();
-  frame_width_ = output_dimensions.width;
-  frame_height_ = output_dimensions.height;
+  // Record the latest dimensions of the decoded input.
+  frame_sizes_.push_back(media_codec_bridge->GetOutputSize());
 
   if (tunnel_mode_audio_session_id_ != -1) {
     return;
@@ -906,9 +900,9 @@
     output_format_ = starboard::nullopt;
     return;
   }
-  output_format_ = VideoOutputFormat(video_codec_, output_dimensions.width,
-                                     output_dimensions.height,
-                                     (color_metadata_ ? true : false));
+  output_format_ = VideoOutputFormat(
+      video_codec_, frame_sizes_.back().display_width(),
+      frame_sizes_.back().display_height(), (color_metadata_ ? true : false));
   first_output_format_changed_ = true;
   auto max_output_buffers =
       MaxMediaCodecOutputBuffersLookupTable::GetInstance()
@@ -931,6 +925,7 @@
 }
 
 namespace {
+
 void updateTexImage(jobject surface_texture) {
   JniEnvExt* env = JniEnvExt::Get();
   env->CallVoidMethodOrAbort(surface_texture, "updateTexImage", "()V");
@@ -1013,6 +1008,7 @@
   content_region->top = extent_y * height;
   content_region->bottom = origin_y * height;
 }
+
 }  // namespace
 
 // When in decode-to-texture mode, this returns the current decoded video frame.
@@ -1025,17 +1021,7 @@
     bool has_new_texture = has_new_texture_available_.exchange(false);
     if (has_new_texture) {
       updateTexImage(decode_target_->data->surface_texture);
-
-      decode_target_->data->info.planes[0].width = frame_width_;
-      decode_target_->data->info.planes[0].height = frame_height_;
-      decode_target_->data->info.width = frame_width_;
-      decode_target_->data->info.height = frame_height_;
-
-      float matrix4x4[16];
-      getTransformMatrix(decode_target_->data->surface_texture, matrix4x4);
-      SetDecodeTargetContentRegionFromMatrix(
-          &decode_target_->data->info.planes[0].content_region, frame_width_,
-          frame_height_, matrix4x4);
+      UpdateDecodeTargetSizeAndContentRegion_Locked();
 
       if (!first_texture_received_) {
         first_texture_received_ = true;
@@ -1051,6 +1037,97 @@
   return kSbDecodeTargetInvalid;
 }
 
+void VideoDecoder::UpdateDecodeTargetSizeAndContentRegion_Locked() {
+  decode_target_mutex_.DCheckAcquired();
+
+  SB_DCHECK(!frame_sizes_.empty());
+
+  while (!frame_sizes_.empty()) {
+    const auto& frame_size = frame_sizes_.front();
+    if (frame_size.has_crop_values()) {
+      decode_target_->data->info.planes[0].width = frame_size.texture_width;
+      decode_target_->data->info.planes[0].height = frame_size.texture_height;
+      decode_target_->data->info.width = frame_size.texture_width;
+      decode_target_->data->info.height = frame_size.texture_height;
+
+      float matrix4x4[16];
+      getTransformMatrix(decode_target_->data->surface_texture, matrix4x4);
+
+      auto& content_region =
+          decode_target_->data->info.planes[0].content_region;
+      SetDecodeTargetContentRegionFromMatrix(
+          &content_region, frame_size.texture_width, frame_size.texture_height,
+          matrix4x4);
+
+      // Now we have two crop rectangles, one from the MediaFormat, one from the
+      // transform of the surface texture.  Their sizes should match.
+      // Note that we cannot compare individual corners directly, as the values
+      // retrieving from the surface texture can be flipped.
+      int content_region_width =
+          std::abs(content_region.left - content_region.right) + 1;
+      int content_region_height =
+          std::abs(content_region.bottom - content_region.top) + 1;
+      // Using 2 as epsilon, as the texture may get clipped by one pixel from
+      // each side.
+      bool are_crop_values_matching =
+          std::abs(content_region_width - frame_size.display_width()) <= 2 &&
+          std::abs(content_region_height - frame_size.display_height()) <= 2;
+      if (are_crop_values_matching) {
+        return;
+      }
+
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+      // If we failed to find any matching clip regions, the crop values
+      // returned from the platform may be inconsistent.
+      // Crash in non-gold mode, and fallback to the old logic in gold mode to
+      // avoid terminating the app in production.
+      SB_CHECK(frame_sizes_.size() > 1)
+          << frame_size.texture_width << "x" << frame_size.texture_height
+          << " - (" << content_region.left << ", " << content_region.top << ", "
+          << content_region.right << ", " << content_region.bottom << "), ("
+          << frame_size.crop_left << "), (" << frame_size.crop_top << "), ("
+          << frame_size.crop_right << "), (" << frame_size.crop_bottom << ")";
+#endif  // !defined(COBALT_BUILD_TYPE_GOLD)
+    } else {
+      SB_LOG(WARNING) << "Crop values not set.";
+    }
+
+    if (frame_sizes_.size() == 1) {
+      SB_LOG(WARNING) << "Setting content region frame width/height failed,"
+                      << " fallback to the legacy logic.";
+      break;
+    }
+
+    frame_sizes_.erase(frame_sizes_.begin());
+  }
+
+  SB_DCHECK(!frame_sizes_.empty());
+  if (frame_sizes_.empty()) {
+    // This should never happen.  Appending a default value so it aligns to the
+    // legacy behavior, where a single value (instead of an std::vector<>) is
+    // used.
+    frame_sizes_.resize(1);
+  }
+
+  // The legacy logic works when the crop rectangle has the same aspect ratio as
+  // the video texture, which is true for most of the playbacks.
+  // Leaving the legacy logic in place in case the new logic above doesn't work
+  // on some devices, so at least the majority of playbacks still work.
+  decode_target_->data->info.planes[0].width =
+      frame_sizes_.back().display_width();
+  decode_target_->data->info.planes[0].height =
+      frame_sizes_.back().display_height();
+  decode_target_->data->info.width = frame_sizes_.back().display_width();
+  decode_target_->data->info.height = frame_sizes_.back().display_height();
+
+  float matrix4x4[16];
+  getTransformMatrix(decode_target_->data->surface_texture, matrix4x4);
+  SetDecodeTargetContentRegionFromMatrix(
+      &decode_target_->data->info.planes[0].content_region,
+      frame_sizes_.back().display_width(), frame_sizes_.back().display_height(),
+      matrix4x4);
+}
+
 void VideoDecoder::SetPlaybackRate(double playback_rate) {
   playback_rate_ = playback_rate;
   if (media_decoder_) {
diff --git a/starboard/android/shared/video_decoder.h b/starboard/android/shared/video_decoder.h
index d8eafed..232db16 100644
--- a/starboard/android/shared/video_decoder.h
+++ b/starboard/android/shared/video_decoder.h
@@ -34,6 +34,7 @@
 #include "starboard/media.h"
 #include "starboard/player.h"
 #include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/media/media_util.h"
 #include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
 #include "starboard/shared/starboard/player/filter/video_render_algorithm.h"
 #include "starboard/shared/starboard/player/filter/video_renderer_sink.h"
@@ -50,6 +51,8 @@
       private ::starboard::shared::starboard::player::JobQueue::JobOwner,
       private VideoSurfaceHolder {
  public:
+  typedef ::starboard::shared::starboard::media::VideoStreamInfo
+      VideoStreamInfo;
   typedef ::starboard::shared::starboard::player::filter::VideoRenderAlgorithm
       VideoRenderAlgorithm;
   typedef ::starboard::shared::starboard::player::filter::VideoRendererSink
@@ -57,7 +60,7 @@
 
   class Sink;
 
-  VideoDecoder(SbMediaVideoCodec video_codec,
+  VideoDecoder(const VideoStreamInfo& video_stream_info,
                SbDrmSystem drm_system,
                SbPlayerOutputMode output_mode,
                SbDecodeTargetGraphicsContextProvider*
@@ -90,6 +93,7 @@
   void Reset() override;
   SbDecodeTarget GetCurrentDecodeTarget() override;
 
+  void UpdateDecodeTargetSizeAndContentRegion_Locked();
   void SetPlaybackRate(double playback_rate);
 
   void OnNewTextureAvailable();
@@ -99,7 +103,8 @@
  private:
   // Attempt to initialize the codec.  Returns whether initialization was
   // successful.
-  bool InitializeCodec(std::string* error_message);
+  bool InitializeCodec(const VideoStreamInfo& video_stream_info,
+                       std::string* error_message);
   void TeardownCodec();
 
   void WriteInputBuffersInternal(const InputBuffers& input_buffers);
@@ -168,9 +173,12 @@
   // and we do so through this mutex.
   Mutex decode_target_mutex_;
 
-  // The width and height of the latest decoded frame.
-  int32_t frame_width_ = 0;
-  int32_t frame_height_ = 0;
+  // The size infos of the frames in use, i.e. the frames being displayed, and
+  // the frames recently decoded frames and pending display.
+  // They are the same at most of the time, but they can be different when there
+  // is a format change. For example, when the newly decoded frames are at
+  // 1080p, and the frames being played are still at 480p.
+  std::vector<FrameSize> frame_sizes_;
 
   double playback_rate_ = 1.0;
 
diff --git a/starboard/android/shared/video_render_algorithm.cc b/starboard/android/shared/video_render_algorithm.cc
index 74ed628..5edf96f 100644
--- a/starboard/android/shared/video_render_algorithm.cc
+++ b/starboard/android/shared/video_render_algorithm.cc
@@ -26,7 +26,7 @@
 
 namespace {
 
-const SbTimeMonotonic kBufferTooLateThreshold = -30 * kSbTimeMillisecond;
+const SbTimeMonotonic kBufferTooLateThreshold = -32 * kSbTimeMillisecond;
 const SbTimeMonotonic kBufferReadyThreshold = 50 * kSbTimeMillisecond;
 
 jlong GetSystemNanoTime() {
diff --git a/starboard/build/config/BUILDCONFIG.gn b/starboard/build/config/BUILDCONFIG.gn
index e57c265..64bd0f8 100644
--- a/starboard/build/config/BUILDCONFIG.gn
+++ b/starboard/build/config/BUILDCONFIG.gn
@@ -90,13 +90,16 @@
 # TODO(b/272790873): Set host_cpu to x86 for 32 bit builds.
 if (target_cpu == "x86" || target_cpu == "arm") {
   _host_toolchain_cpu = "x86"
+} else if (target_cpu == "arm64") {
+  # TODO(b/292107465): Set host_cpu to x64 for arm64 builds.
+  _host_toolchain_cpu = "x64"
 } else {
   _host_toolchain_cpu = host_cpu
 }
 host_toolchain = "//starboard/build/toolchain/$host_os:$_host_toolchain_cpu"
 
 if (build_with_separate_cobalt_toolchain) {
-  cobalt_toolchain = "//starboard/build/toolchain/linux:clang_lld"
+  cobalt_toolchain = "//starboard/build/toolchain:clang"
   starboard_toolchain = "//$starboard_path/toolchain:starboard"
 } else {
   cobalt_toolchain = "//$starboard_path/toolchain:target"
diff --git a/starboard/build/platform_configuration.py b/starboard/build/platform_configuration.py
index c5bac3d..af3fa2c 100644
--- a/starboard/build/platform_configuration.py
+++ b/starboard/build/platform_configuration.py
@@ -165,13 +165,19 @@
     Returns:
       A list of strings of test target names.
     """
+    # TODO(b/292007482): Replace static list with gn query.
     return [
         'app_key_files_test',
         'app_key_test',
+        'common_test',
+        'cwrappers_test',
         'drain_file_test',
         'elf_loader_test',
+        'eztime_test',
         'installation_manager_test',
         'nplb',
+        # TODO(b/292138589): Fails on various linux configs.
+        # 'nplb_evergreen_compat_tests',
         'player_filter_tests',
         'reset_evergreen_update_test',
         'slot_management_test',
diff --git a/starboard/build/toolchain/linux/clang_lld_toolchain.gni b/starboard/build/toolchain/BUILD.gn
similarity index 65%
rename from starboard/build/toolchain/linux/clang_lld_toolchain.gni
rename to starboard/build/toolchain/BUILD.gn
index f9de0de..2bfec6e 100644
--- a/starboard/build/toolchain/linux/clang_lld_toolchain.gni
+++ b/starboard/build/toolchain/BUILD.gn
@@ -12,21 +12,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//build/config/clang/clang.gni")
 import("//build/toolchain/gcc_toolchain.gni")
 
-template("clang_lld_toolchain") {
-  not_needed(invoker, "*")
-
-  gcc_toolchain(target_name) {
-    prefix = rebase_path("$clang_base_path/bin", root_build_dir)
-    cc = "$prefix/clang"
-    cxx = "$prefix/clang++"
-    ld = "$prefix/ld.lld"
-    ar = "$prefix/llvm-ar"
-    nm = "nm"
-
-    toolchain_args = {
-      is_clang = true
-    }
-  }
+clang_toolchain("clang") {
+  clang_base_path = clang_base_path
 }
diff --git a/starboard/build/toolchain/linux/BUILD.gn b/starboard/build/toolchain/linux/BUILD.gn
index 97a5b53..24da4aa 100644
--- a/starboard/build/toolchain/linux/BUILD.gn
+++ b/starboard/build/toolchain/linux/BUILD.gn
@@ -14,7 +14,6 @@
 
 import("//build/toolchain/gcc_toolchain.gni")
 import("//starboard/build/platform_path.gni")
-import("//starboard/build/toolchain/linux/clang_lld_toolchain.gni")
 
 clang_toolchain("x64") {
   clang_base_path = clang_base_path
@@ -37,6 +36,3 @@
     is_clang_16 = true
   }
 }
-
-clang_lld_toolchain("clang_lld") {
-}
diff --git a/starboard/build/toolchain/mac/BUILD.gn b/starboard/build/toolchain/mac/BUILD.gn
index 780c648..7e11b90 100644
--- a/starboard/build/toolchain/mac/BUILD.gn
+++ b/starboard/build/toolchain/mac/BUILD.gn
@@ -24,3 +24,13 @@
     toolchain_config_path = "//starboard/build/config/mac:host"
   }
 }
+
+apple_toolchain("arm64") {
+  bin_path = apple_clang_base_path
+  toolchain_args = {
+    current_os = "mac"
+    current_cpu = "arm64"
+    use_xcode_clang = true
+    toolchain_config_path = "//starboard/build/config/mac:host"
+  }
+}
diff --git a/starboard/doc/evergreen/cobalt_evergreen_overview.md b/starboard/doc/evergreen/cobalt_evergreen_overview.md
index e614373..8e2591c 100644
--- a/starboard/doc/evergreen/cobalt_evergreen_overview.md
+++ b/starboard/doc/evergreen/cobalt_evergreen_overview.md
@@ -131,7 +131,7 @@
 
 *   `kSbSystemPathStorageDirectory`
     *   Dedicated location for storing Cobalt Evergreen-related binaries
-    *   This path must be writable and have at least 96MB of reserved space for
+    *   This path must be writable and have at least 64MB of reserved space for
         Evergreen updates. Please see the “Platforms Requirements” section below
         for more details.
 *   `kSbMemoryMapProtectExec`
@@ -195,12 +195,12 @@
 
 *   V8
 
-Additional reserved storage (96MB) is required for Evergreen binaries. We expect
+Additional reserved storage (64MB) is required for Evergreen binaries. We expect
 Evergreen implementations to have an initial Cobalt preloaded on the device and
 an additional reserved space for additional Cobalt update storage.
 
 *   Initial Cobalt binary deployment - 64MB
-*   Additional Cobalt update storage - 96MB
+*   Additional Cobalt update storage - 64MB
     *   Required for 2 update slots under `kSbSystemPathStorageDirectory`
 
 As Cobalt Evergreen is intended to be updated from Google Cloud architecture
diff --git a/starboard/evergreen/arm/hardfp/platform_configuration/BUILD.gn b/starboard/evergreen/arm/hardfp/platform_configuration/BUILD.gn
index 2dcac4f..c4403ea 100644
--- a/starboard/evergreen/arm/hardfp/platform_configuration/BUILD.gn
+++ b/starboard/evergreen/arm/hardfp/platform_configuration/BUILD.gn
@@ -28,7 +28,7 @@
     "//starboard/evergreen/arm/shared/platform_configuration",
   ]
   ldflags = [
-    "-m",
-    "armelf",
+    "-Wl,-m",
+    "-Wl,armelf",
   ]
 }
diff --git a/starboard/evergreen/arm/hardfp/toolchain/BUILD.gn b/starboard/evergreen/arm/hardfp/toolchain/BUILD.gn
index 5bc6339..8f50b01 100644
--- a/starboard/evergreen/arm/hardfp/toolchain/BUILD.gn
+++ b/starboard/evergreen/arm/hardfp/toolchain/BUILD.gn
@@ -12,7 +12,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import("//starboard/build/toolchain/linux/clang_lld_toolchain.gni")
+import("//build/config/clang/clang.gni")
+import("//build/toolchain/gcc_toolchain.gni")
 
-clang_lld_toolchain("target") {
+clang_toolchain("target") {
+  clang_base_path = clang_base_path
 }
diff --git a/starboard/evergreen/arm/softfp/platform_configuration/BUILD.gn b/starboard/evergreen/arm/softfp/platform_configuration/BUILD.gn
index 0de964f..907d14e 100644
--- a/starboard/evergreen/arm/softfp/platform_configuration/BUILD.gn
+++ b/starboard/evergreen/arm/softfp/platform_configuration/BUILD.gn
@@ -28,7 +28,7 @@
     "//starboard/evergreen/arm/shared/platform_configuration",
   ]
   ldflags = [
-    "-m",
-    "armelf",
+    "-Wl,-m",
+    "-Wl,armelf",
   ]
 }
diff --git a/starboard/evergreen/arm/softfp/toolchain/BUILD.gn b/starboard/evergreen/arm/softfp/toolchain/BUILD.gn
index 5bc6339..8f50b01 100644
--- a/starboard/evergreen/arm/softfp/toolchain/BUILD.gn
+++ b/starboard/evergreen/arm/softfp/toolchain/BUILD.gn
@@ -12,7 +12,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import("//starboard/build/toolchain/linux/clang_lld_toolchain.gni")
+import("//build/config/clang/clang.gni")
+import("//build/toolchain/gcc_toolchain.gni")
 
-clang_lld_toolchain("target") {
+clang_toolchain("target") {
+  clang_base_path = clang_base_path
 }
diff --git a/starboard/evergreen/arm64/platform_configuration/BUILD.gn b/starboard/evergreen/arm64/platform_configuration/BUILD.gn
index ce00c13..20a22bb 100644
--- a/starboard/evergreen/arm64/platform_configuration/BUILD.gn
+++ b/starboard/evergreen/arm64/platform_configuration/BUILD.gn
@@ -31,8 +31,8 @@
   ]
 
   ldflags = [
-    "-m",
-    "aarch64elf",
+    "-Wl,-m",
+    "-Wl,aarch64elf",
   ]
   cflags = [ "-isystem" +
              rebase_path("//third_party/musl/arch/aarch64", root_build_dir) ]
diff --git a/starboard/evergreen/arm64/toolchain/BUILD.gn b/starboard/evergreen/arm64/toolchain/BUILD.gn
index 5bc6339..8f50b01 100644
--- a/starboard/evergreen/arm64/toolchain/BUILD.gn
+++ b/starboard/evergreen/arm64/toolchain/BUILD.gn
@@ -12,7 +12,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import("//starboard/build/toolchain/linux/clang_lld_toolchain.gni")
+import("//build/config/clang/clang.gni")
+import("//build/toolchain/gcc_toolchain.gni")
 
-clang_lld_toolchain("target") {
+clang_toolchain("target") {
+  clang_base_path = clang_base_path
 }
diff --git a/starboard/evergreen/shared/platform_configuration/BUILD.gn b/starboard/evergreen/shared/platform_configuration/BUILD.gn
index 6b154a0..94f7208 100644
--- a/starboard/evergreen/shared/platform_configuration/BUILD.gn
+++ b/starboard/evergreen/shared/platform_configuration/BUILD.gn
@@ -14,20 +14,19 @@
 
 config("platform_configuration") {
   ldflags = [
-    "--build-id",
-    "--gc-sections",
-    "-X",
-    "-v",
-    "-eh-frame-hdr",
-    "--fini=__cxa_finalize",
-    "-shared",
-    "-L$clang_base_path",
-    "-L/usr/lib",
-    "-L/lib",
-    "-nostdlib",
-    "--whole-archive",
-    "--no-whole-archive",
-    "-u GetEvergreenSabiString",
+    "-fuse-ld=lld",
+    "-Wl,--build-id",
+    "-Wl,--gc-sections",
+    "-Wl,-X",
+    "-Wl,-v",
+    "-Wl,-eh-frame-hdr",
+    "-Wl,--fini=__cxa_finalize",
+    "-Wl,-shared",
+    "-Wl,-L$clang_base_path",
+    "-Wl,-L/usr/lib",
+    "-Wl,-L/lib",
+    "-Wl,-nostdlib",
+    "-Wl,-u GetEvergreenSabiString",
   ]
 
   cflags = [
@@ -201,7 +200,7 @@
 config("size") {
   cflags = [ "-Os" ]
   if (is_qa || is_gold) {
-    ldflags = [ "--icf=safe" ]
+    ldflags = [ "-Wl,--icf=safe" ]
   }
 }
 
diff --git a/starboard/evergreen/x64/platform_configuration/BUILD.gn b/starboard/evergreen/x64/platform_configuration/BUILD.gn
index 47d7e22..d49869b 100644
--- a/starboard/evergreen/x64/platform_configuration/BUILD.gn
+++ b/starboard/evergreen/x64/platform_configuration/BUILD.gn
@@ -28,8 +28,8 @@
   ]
 
   ldflags = [
-    "-m",
-    "elf_x86_64",
+    "-Wl,-m",
+    "-Wl,elf_x86_64",
   ]
   cflags = [ "-isystem" +
              rebase_path("//third_party/musl/arch/x86_64", root_build_dir) ]
diff --git a/starboard/evergreen/x64/toolchain/BUILD.gn b/starboard/evergreen/x64/toolchain/BUILD.gn
index 5bc6339..8f50b01 100644
--- a/starboard/evergreen/x64/toolchain/BUILD.gn
+++ b/starboard/evergreen/x64/toolchain/BUILD.gn
@@ -12,7 +12,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import("//starboard/build/toolchain/linux/clang_lld_toolchain.gni")
+import("//build/config/clang/clang.gni")
+import("//build/toolchain/gcc_toolchain.gni")
 
-clang_lld_toolchain("target") {
+clang_toolchain("target") {
+  clang_base_path = clang_base_path
 }
diff --git a/starboard/evergreen/x86/platform_configuration/BUILD.gn b/starboard/evergreen/x86/platform_configuration/BUILD.gn
index 633a354..d1bcbc0 100644
--- a/starboard/evergreen/x86/platform_configuration/BUILD.gn
+++ b/starboard/evergreen/x86/platform_configuration/BUILD.gn
@@ -27,6 +27,10 @@
     "//starboard/evergreen/shared/platform_configuration",
   ]
 
+  ldflags = [
+    "-Wl,-m",
+    "-Wl,elf_i386",
+  ]
   cflags = [ "-isystem" +
              rebase_path("//third_party/musl/arch/i386", root_build_dir) ]
 }
diff --git a/starboard/evergreen/x86/toolchain/BUILD.gn b/starboard/evergreen/x86/toolchain/BUILD.gn
index 5bc6339..8f50b01 100644
--- a/starboard/evergreen/x86/toolchain/BUILD.gn
+++ b/starboard/evergreen/x86/toolchain/BUILD.gn
@@ -12,7 +12,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import("//starboard/build/toolchain/linux/clang_lld_toolchain.gni")
+import("//build/config/clang/clang.gni")
+import("//build/toolchain/gcc_toolchain.gni")
 
-clang_lld_toolchain("target") {
+clang_toolchain("target") {
+  clang_base_path = clang_base_path
 }
diff --git a/starboard/nplb/BUILD.gn b/starboard/nplb/BUILD.gn
index 7cfa816..08e415a 100644
--- a/starboard/nplb/BUILD.gn
+++ b/starboard/nplb/BUILD.gn
@@ -23,6 +23,8 @@
     "//starboard/nplb/sabi/signedness_of_char_test.cc",
     "//starboard/nplb/sabi/size_test.cc",
     "//starboard/nplb/sabi/struct_alignment_test.cc",
+    "//starboard/shared/starboard/drm/drm_test_helpers.cc",
+    "//starboard/shared/starboard/drm/drm_test_helpers.h",
     "//starboard/testing/fake_graphics_context_provider.cc",
     "//starboard/testing/fake_graphics_context_provider.h",
     "accessibility_test.cc",
@@ -55,7 +57,6 @@
     "directory_get_next_test.cc",
     "directory_open_test.cc",
     "drm_get_metrics_test.cc",
-    "drm_helpers.cc",
     "drm_helpers.h",
     "drm_is_server_certificate_updatable_test.cc",
     "drm_session_test.cc",
diff --git a/starboard/nplb/drm_helpers.h b/starboard/nplb/drm_helpers.h
index 6717fcc..1d64f49 100644
--- a/starboard/nplb/drm_helpers.h
+++ b/starboard/nplb/drm_helpers.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Cobalt Authors. All Rights Reserved.
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -15,136 +15,24 @@
 #ifndef STARBOARD_NPLB_DRM_HELPERS_H_
 #define STARBOARD_NPLB_DRM_HELPERS_H_
 
-#include "starboard/drm.h"
+#include "starboard/shared/starboard/drm/drm_test_helpers.h"
 
 namespace starboard {
 namespace nplb {
 
-void DummySessionUpdateRequestFunc(SbDrmSystem drm_system,
-                                   void* context,
-                                   int ticket,
-                                   SbDrmStatus status,
-                                   SbDrmSessionRequestType type,
-                                   const char* error_message,
-                                   const void* session_id,
-                                   int session_id_size,
-                                   const void* content,
-                                   int content_size,
-                                   const char* url);
+using ::starboard::shared::starboard::drm::DummyServerCertificateUpdatedFunc;
+using ::starboard::shared::starboard::drm::DummySessionClosedFunc;
+using ::starboard::shared::starboard::drm::DummySessionKeyStatusesChangedFunc;
+using ::starboard::shared::starboard::drm::DummySessionUpdatedFunc;
+using ::starboard::shared::starboard::drm::DummySessionUpdateRequestFunc;
 
-void DummySessionUpdatedFunc(SbDrmSystem drm_system,
-                             void* context,
-                             int ticket,
-                             SbDrmStatus status,
-                             const char* error_message,
-                             const void* session_id,
-                             int session_id_size);
+using ::starboard::shared::starboard::drm::CreateDummyDrmSystem;
 
-void DummyServerCertificateUpdatedFunc(SbDrmSystem drm_system,
-                                       void* context,
-                                       int ticket,
-                                       SbDrmStatus status,
-                                       const char* error_message);
+using ::starboard::shared::starboard::drm::kEncryptionSchemes;
+using ::starboard::shared::starboard::drm::kKeySystems;
+using ::starboard::shared::starboard::drm::kWidevineCertificate;
 
-void DummySessionKeyStatusesChangedFunc(SbDrmSystem drm_system,
-                                        void* context,
-                                        const void* session_id,
-                                        int session_id_size,
-                                        int number_of_keys,
-                                        const SbDrmKeyId* key_ids,
-                                        const SbDrmKeyStatus* key_statuses);
-
-void DummySessionClosedFunc(SbDrmSystem drm_system,
-                            void* context,
-                            const void* session_id,
-                            int session_id_size);
-
-SbDrmSystem CreateDummyDrmSystem(const char* key_system);
-
-static const char* kKeySystems[] = {
-    "com.widevine",
-    "com.widevine.alpha",
-    "com.youtube.playready",
-    "com.youtube.fairplay",
-};
-
-static const char* kEncryptionSchemes[] = {
-    "cenc",
-    "cbcs",
-    "cbcs-1-9",
-};
-
-static constexpr uint8_t kWidevineCertificate[] = {
-    0x0a, 0xc1, 0x02, 0x08, 0x03, 0x12, 0x10, 0x17, 0x05, 0xb9, 0x17, 0xcc,
-    0x12, 0x04, 0x86, 0x8b, 0x06, 0x33, 0x3a, 0x2f, 0x77, 0x2a, 0x8c, 0x18,
-    0x82, 0xb4, 0x82, 0x92, 0x05, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a,
-    0x02, 0x82, 0x01, 0x01, 0x00, 0x99, 0xed, 0x5b, 0x3b, 0x32, 0x7d, 0xab,
-    0x5e, 0x24, 0xef, 0xc3, 0xb6, 0x2a, 0x95, 0xb5, 0x98, 0x52, 0x0a, 0xd5,
-    0xbc, 0xcb, 0x37, 0x50, 0x3e, 0x06, 0x45, 0xb8, 0x14, 0xd8, 0x76, 0xb8,
-    0xdf, 0x40, 0x51, 0x04, 0x41, 0xad, 0x8c, 0xe3, 0xad, 0xb1, 0x1b, 0xb8,
-    0x8c, 0x4e, 0x72, 0x5a, 0x5e, 0x4a, 0x9e, 0x07, 0x95, 0x29, 0x1d, 0x58,
-    0x58, 0x40, 0x23, 0xa7, 0xe1, 0xaf, 0x0e, 0x38, 0xa9, 0x12, 0x79, 0x39,
-    0x30, 0x08, 0x61, 0x0b, 0x6f, 0x15, 0x8c, 0x87, 0x8c, 0x7e, 0x21, 0xbf,
-    0xfb, 0xfe, 0xea, 0x77, 0xe1, 0x01, 0x9e, 0x1e, 0x57, 0x81, 0xe8, 0xa4,
-    0x5f, 0x46, 0x26, 0x3d, 0x14, 0xe6, 0x0e, 0x80, 0x58, 0xa8, 0x60, 0x7a,
-    0xdc, 0xe0, 0x4f, 0xac, 0x84, 0x57, 0xb1, 0x37, 0xa8, 0xd6, 0x7c, 0xcd,
-    0xeb, 0x33, 0x70, 0x5d, 0x98, 0x3a, 0x21, 0xfb, 0x4e, 0xec, 0xbd, 0x4a,
-    0x10, 0xca, 0x47, 0x49, 0x0c, 0xa4, 0x7e, 0xaa, 0x5d, 0x43, 0x82, 0x18,
-    0xdd, 0xba, 0xf1, 0xca, 0xde, 0x33, 0x92, 0xf1, 0x3d, 0x6f, 0xfb, 0x64,
-    0x42, 0xfd, 0x31, 0xe1, 0xbf, 0x40, 0xb0, 0xc6, 0x04, 0xd1, 0xc4, 0xba,
-    0x4c, 0x95, 0x20, 0xa4, 0xbf, 0x97, 0xee, 0xbd, 0x60, 0x92, 0x9a, 0xfc,
-    0xee, 0xf5, 0x5b, 0xba, 0xf5, 0x64, 0xe2, 0xd0, 0xe7, 0x6c, 0xd7, 0xc5,
-    0x5c, 0x73, 0xa0, 0x82, 0xb9, 0x96, 0x12, 0x0b, 0x83, 0x59, 0xed, 0xce,
-    0x24, 0x70, 0x70, 0x82, 0x68, 0x0d, 0x6f, 0x67, 0xc6, 0xd8, 0x2c, 0x4a,
-    0xc5, 0xf3, 0x13, 0x44, 0x90, 0xa7, 0x4e, 0xec, 0x37, 0xaf, 0x4b, 0x2f,
-    0x01, 0x0c, 0x59, 0xe8, 0x28, 0x43, 0xe2, 0x58, 0x2f, 0x0b, 0x6b, 0x9f,
-    0x5d, 0xb0, 0xfc, 0x5e, 0x6e, 0xdf, 0x64, 0xfb, 0xd3, 0x08, 0xb4, 0x71,
-    0x1b, 0xcf, 0x12, 0x50, 0x01, 0x9c, 0x9f, 0x5a, 0x09, 0x02, 0x03, 0x01,
-    0x00, 0x01, 0x3a, 0x14, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x2e,
-    0x77, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
-    0x12, 0x80, 0x03, 0xae, 0x34, 0x73, 0x14, 0xb5, 0xa8, 0x35, 0x29, 0x7f,
-    0x27, 0x13, 0x88, 0xfb, 0x7b, 0xb8, 0xcb, 0x52, 0x77, 0xd2, 0x49, 0x82,
-    0x3c, 0xdd, 0xd1, 0xda, 0x30, 0xb9, 0x33, 0x39, 0x51, 0x1e, 0xb3, 0xcc,
-    0xbd, 0xea, 0x04, 0xb9, 0x44, 0xb9, 0x27, 0xc1, 0x21, 0x34, 0x6e, 0xfd,
-    0xbd, 0xea, 0xc9, 0xd4, 0x13, 0x91, 0x7e, 0x6e, 0xc1, 0x76, 0xa1, 0x04,
-    0x38, 0x46, 0x0a, 0x50, 0x3b, 0xc1, 0x95, 0x2b, 0x9b, 0xa4, 0xe4, 0xce,
-    0x0f, 0xc4, 0xbf, 0xc2, 0x0a, 0x98, 0x08, 0xaa, 0xaf, 0x4b, 0xfc, 0xd1,
-    0x9c, 0x1d, 0xcf, 0xcd, 0xf5, 0x74, 0xcc, 0xac, 0x28, 0xd1, 0xb4, 0x10,
-    0x41, 0x6c, 0xf9, 0xde, 0x88, 0x04, 0x30, 0x1c, 0xbd, 0xb3, 0x34, 0xca,
-    0xfc, 0xd0, 0xd4, 0x09, 0x78, 0x42, 0x3a, 0x64, 0x2e, 0x54, 0x61, 0x3d,
-    0xf0, 0xaf, 0xcf, 0x96, 0xca, 0x4a, 0x92, 0x49, 0xd8, 0x55, 0xe4, 0x2b,
-    0x3a, 0x70, 0x3e, 0xf1, 0x76, 0x7f, 0x6a, 0x9b, 0xd3, 0x6d, 0x6b, 0xf8,
-    0x2b, 0xe7, 0x6b, 0xbf, 0x0c, 0xba, 0x4f, 0xde, 0x59, 0xd2, 0xab, 0xcc,
-    0x76, 0xfe, 0xb6, 0x42, 0x47, 0xb8, 0x5c, 0x43, 0x1f, 0xbc, 0xa5, 0x22,
-    0x66, 0xb6, 0x19, 0xfc, 0x36, 0x97, 0x95, 0x43, 0xfc, 0xa9, 0xcb, 0xbd,
-    0xbb, 0xfa, 0xfa, 0x0e, 0x1a, 0x55, 0xe7, 0x55, 0xa3, 0xc7, 0xbc, 0xe6,
-    0x55, 0xf9, 0x64, 0x6f, 0x58, 0x2a, 0xb9, 0xcf, 0x70, 0xaa, 0x08, 0xb9,
-    0x79, 0xf8, 0x67, 0xf6, 0x3a, 0x0b, 0x2b, 0x7f, 0xdb, 0x36, 0x2c, 0x5b,
-    0xc4, 0xec, 0xd5, 0x55, 0xd8, 0x5b, 0xca, 0xa9, 0xc5, 0x93, 0xc3, 0x83,
-    0xc8, 0x57, 0xd4, 0x9d, 0xaa, 0xb7, 0x7e, 0x40, 0xb7, 0x85, 0x1d, 0xdf,
-    0xd2, 0x49, 0x98, 0x80, 0x8e, 0x35, 0xb2, 0x58, 0xe7, 0x5d, 0x78, 0xea,
-    0xc0, 0xca, 0x16, 0xf7, 0x04, 0x73, 0x04, 0xc2, 0x0d, 0x93, 0xed, 0xe4,
-    0xe8, 0xff, 0x1c, 0x6f, 0x17, 0xe6, 0x24, 0x3e, 0x3f, 0x3d, 0xa8, 0xfc,
-    0x17, 0x09, 0x87, 0x0e, 0xc4, 0x5f, 0xba, 0x82, 0x3a, 0x26, 0x3f, 0x0c,
-    0xef, 0xa1, 0xf7, 0x09, 0x3b, 0x19, 0x09, 0x92, 0x83, 0x26, 0x33, 0x37,
-    0x05, 0x04, 0x3a, 0x29, 0xbd, 0xa6, 0xf9, 0xb4, 0x34, 0x2c, 0xc8, 0xdf,
-    0x54, 0x3c, 0xb1, 0xa1, 0x18, 0x2f, 0x7c, 0x5f, 0xff, 0x33, 0xf1, 0x04,
-    0x90, 0xfa, 0xca, 0x5b, 0x25, 0x36, 0x0b, 0x76, 0x01, 0x5e, 0x9c, 0x5a,
-    0x06, 0xab, 0x8e, 0xe0, 0x2f, 0x00, 0xd2, 0xe8, 0xd5, 0x98, 0x61, 0x04,
-    0xaa, 0xcc, 0x4d, 0xd4, 0x75, 0xfd, 0x96, 0xee, 0x9c, 0xe4, 0xe3, 0x26,
-    0xf2, 0x1b, 0x83, 0xc7, 0x05, 0x85, 0x77, 0xb3, 0x87, 0x32, 0xcd, 0xda,
-    0xbc, 0x6a, 0x6b, 0xed, 0x13, 0xfb, 0x0d, 0x49, 0xd3, 0x8a, 0x45, 0xeb,
-    0x87, 0xa5, 0xf4};
-
-// Widevine-specific CENC Initialization data.
-// https://www.w3.org/TR/eme-stream-mp4/
-// https://www.w3.org/TR/eme-initdata-cenc/#common-system
-static constexpr uint8_t kCencInitData[] = {
-    0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68, 0x00, 0x00, 0x00,
-    0x00, 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, 0xa3, 0xc8,
-    0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed, 0x00, 0x00, 0x00, 0x14, 0x08,
-    0x01, 0x12, 0x10, 0x31, 0xfd, 0x5b, 0x66, 0x19, 0xfc, 0x5e, 0xad,
-    0x86, 0x7c, 0xff, 0xb5, 0x84, 0xed, 0x4c, 0x19};
+using ::starboard::shared::starboard::drm::kCencInitData;
 
 }  // namespace nplb
 }  // namespace starboard
diff --git a/starboard/nplb/nplb_evergreen_compat_tests/storage_test.cc b/starboard/nplb/nplb_evergreen_compat_tests/storage_test.cc
index 218cfb3..2445fd4 100644
--- a/starboard/nplb/nplb_evergreen_compat_tests/storage_test.cc
+++ b/starboard/nplb/nplb_evergreen_compat_tests/storage_test.cc
@@ -32,7 +32,7 @@
 namespace {
 
 const char kFileName[] = "test_file.data";
-const size_t kBufSize = 96 * 1024 * 1024;  // 96 MB
+const size_t kBufSize = 64 * 1024 * 1024;  // 64 MB
 
 class StorageTest : public ::testing::Test {
  protected:
diff --git a/starboard/nplb/drm_helpers.cc b/starboard/shared/starboard/drm/drm_test_helpers.cc
similarity index 93%
rename from starboard/nplb/drm_helpers.cc
rename to starboard/shared/starboard/drm/drm_test_helpers.cc
index cac39fa..f010f4c 100644
--- a/starboard/nplb/drm_helpers.cc
+++ b/starboard/shared/starboard/drm/drm_test_helpers.cc
@@ -12,12 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/nplb/drm_helpers.h"
+#include "starboard/shared/starboard/drm/drm_test_helpers.h"
 
 #include "starboard/drm.h"
 
 namespace starboard {
-namespace nplb {
+namespace shared {
+namespace starboard {
+namespace drm {
 
 void DummySessionUpdateRequestFunc(SbDrmSystem drm_system,
                                    void* context,
@@ -65,5 +67,7 @@
       DummyServerCertificateUpdatedFunc, DummySessionClosedFunc);
 }
 
-}  // namespace nplb
+}  // namespace drm
+}  // namespace starboard
+}  // namespace shared
 }  // namespace starboard
diff --git a/starboard/shared/starboard/drm/drm_test_helpers.h b/starboard/shared/starboard/drm/drm_test_helpers.h
new file mode 100644
index 0000000..4ed08b8
--- /dev/null
+++ b/starboard/shared/starboard/drm/drm_test_helpers.h
@@ -0,0 +1,158 @@
+// Copyright 2018 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_STARBOARD_DRM_DRM_TEST_HELPERS_H_
+#define STARBOARD_SHARED_STARBOARD_DRM_DRM_TEST_HELPERS_H_
+
+#include "starboard/drm.h"
+
+// Helpers to be used by tests
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace drm {
+
+void DummySessionUpdateRequestFunc(SbDrmSystem drm_system,
+                                   void* context,
+                                   int ticket,
+                                   SbDrmStatus status,
+                                   SbDrmSessionRequestType type,
+                                   const char* error_message,
+                                   const void* session_id,
+                                   int session_id_size,
+                                   const void* content,
+                                   int content_size,
+                                   const char* url);
+
+void DummySessionUpdatedFunc(SbDrmSystem drm_system,
+                             void* context,
+                             int ticket,
+                             SbDrmStatus status,
+                             const char* error_message,
+                             const void* session_id,
+                             int session_id_size);
+
+void DummyServerCertificateUpdatedFunc(SbDrmSystem drm_system,
+                                       void* context,
+                                       int ticket,
+                                       SbDrmStatus status,
+                                       const char* error_message);
+
+void DummySessionKeyStatusesChangedFunc(SbDrmSystem drm_system,
+                                        void* context,
+                                        const void* session_id,
+                                        int session_id_size,
+                                        int number_of_keys,
+                                        const SbDrmKeyId* key_ids,
+                                        const SbDrmKeyStatus* key_statuses);
+
+void DummySessionClosedFunc(SbDrmSystem drm_system,
+                            void* context,
+                            const void* session_id,
+                            int session_id_size);
+
+SbDrmSystem CreateDummyDrmSystem(const char* key_system);
+
+static const char* kKeySystems[] = {
+    "com.widevine",
+    "com.widevine.alpha",
+    "com.youtube.playready",
+    "com.youtube.fairplay",
+};
+
+static const char* kEncryptionSchemes[] = {
+    "cenc",
+    "cbcs",
+    "cbcs-1-9",
+};
+
+static constexpr uint8_t kWidevineCertificate[] = {
+    0x0a, 0xc1, 0x02, 0x08, 0x03, 0x12, 0x10, 0x17, 0x05, 0xb9, 0x17, 0xcc,
+    0x12, 0x04, 0x86, 0x8b, 0x06, 0x33, 0x3a, 0x2f, 0x77, 0x2a, 0x8c, 0x18,
+    0x82, 0xb4, 0x82, 0x92, 0x05, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a,
+    0x02, 0x82, 0x01, 0x01, 0x00, 0x99, 0xed, 0x5b, 0x3b, 0x32, 0x7d, 0xab,
+    0x5e, 0x24, 0xef, 0xc3, 0xb6, 0x2a, 0x95, 0xb5, 0x98, 0x52, 0x0a, 0xd5,
+    0xbc, 0xcb, 0x37, 0x50, 0x3e, 0x06, 0x45, 0xb8, 0x14, 0xd8, 0x76, 0xb8,
+    0xdf, 0x40, 0x51, 0x04, 0x41, 0xad, 0x8c, 0xe3, 0xad, 0xb1, 0x1b, 0xb8,
+    0x8c, 0x4e, 0x72, 0x5a, 0x5e, 0x4a, 0x9e, 0x07, 0x95, 0x29, 0x1d, 0x58,
+    0x58, 0x40, 0x23, 0xa7, 0xe1, 0xaf, 0x0e, 0x38, 0xa9, 0x12, 0x79, 0x39,
+    0x30, 0x08, 0x61, 0x0b, 0x6f, 0x15, 0x8c, 0x87, 0x8c, 0x7e, 0x21, 0xbf,
+    0xfb, 0xfe, 0xea, 0x77, 0xe1, 0x01, 0x9e, 0x1e, 0x57, 0x81, 0xe8, 0xa4,
+    0x5f, 0x46, 0x26, 0x3d, 0x14, 0xe6, 0x0e, 0x80, 0x58, 0xa8, 0x60, 0x7a,
+    0xdc, 0xe0, 0x4f, 0xac, 0x84, 0x57, 0xb1, 0x37, 0xa8, 0xd6, 0x7c, 0xcd,
+    0xeb, 0x33, 0x70, 0x5d, 0x98, 0x3a, 0x21, 0xfb, 0x4e, 0xec, 0xbd, 0x4a,
+    0x10, 0xca, 0x47, 0x49, 0x0c, 0xa4, 0x7e, 0xaa, 0x5d, 0x43, 0x82, 0x18,
+    0xdd, 0xba, 0xf1, 0xca, 0xde, 0x33, 0x92, 0xf1, 0x3d, 0x6f, 0xfb, 0x64,
+    0x42, 0xfd, 0x31, 0xe1, 0xbf, 0x40, 0xb0, 0xc6, 0x04, 0xd1, 0xc4, 0xba,
+    0x4c, 0x95, 0x20, 0xa4, 0xbf, 0x97, 0xee, 0xbd, 0x60, 0x92, 0x9a, 0xfc,
+    0xee, 0xf5, 0x5b, 0xba, 0xf5, 0x64, 0xe2, 0xd0, 0xe7, 0x6c, 0xd7, 0xc5,
+    0x5c, 0x73, 0xa0, 0x82, 0xb9, 0x96, 0x12, 0x0b, 0x83, 0x59, 0xed, 0xce,
+    0x24, 0x70, 0x70, 0x82, 0x68, 0x0d, 0x6f, 0x67, 0xc6, 0xd8, 0x2c, 0x4a,
+    0xc5, 0xf3, 0x13, 0x44, 0x90, 0xa7, 0x4e, 0xec, 0x37, 0xaf, 0x4b, 0x2f,
+    0x01, 0x0c, 0x59, 0xe8, 0x28, 0x43, 0xe2, 0x58, 0x2f, 0x0b, 0x6b, 0x9f,
+    0x5d, 0xb0, 0xfc, 0x5e, 0x6e, 0xdf, 0x64, 0xfb, 0xd3, 0x08, 0xb4, 0x71,
+    0x1b, 0xcf, 0x12, 0x50, 0x01, 0x9c, 0x9f, 0x5a, 0x09, 0x02, 0x03, 0x01,
+    0x00, 0x01, 0x3a, 0x14, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x2e,
+    0x77, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+    0x12, 0x80, 0x03, 0xae, 0x34, 0x73, 0x14, 0xb5, 0xa8, 0x35, 0x29, 0x7f,
+    0x27, 0x13, 0x88, 0xfb, 0x7b, 0xb8, 0xcb, 0x52, 0x77, 0xd2, 0x49, 0x82,
+    0x3c, 0xdd, 0xd1, 0xda, 0x30, 0xb9, 0x33, 0x39, 0x51, 0x1e, 0xb3, 0xcc,
+    0xbd, 0xea, 0x04, 0xb9, 0x44, 0xb9, 0x27, 0xc1, 0x21, 0x34, 0x6e, 0xfd,
+    0xbd, 0xea, 0xc9, 0xd4, 0x13, 0x91, 0x7e, 0x6e, 0xc1, 0x76, 0xa1, 0x04,
+    0x38, 0x46, 0x0a, 0x50, 0x3b, 0xc1, 0x95, 0x2b, 0x9b, 0xa4, 0xe4, 0xce,
+    0x0f, 0xc4, 0xbf, 0xc2, 0x0a, 0x98, 0x08, 0xaa, 0xaf, 0x4b, 0xfc, 0xd1,
+    0x9c, 0x1d, 0xcf, 0xcd, 0xf5, 0x74, 0xcc, 0xac, 0x28, 0xd1, 0xb4, 0x10,
+    0x41, 0x6c, 0xf9, 0xde, 0x88, 0x04, 0x30, 0x1c, 0xbd, 0xb3, 0x34, 0xca,
+    0xfc, 0xd0, 0xd4, 0x09, 0x78, 0x42, 0x3a, 0x64, 0x2e, 0x54, 0x61, 0x3d,
+    0xf0, 0xaf, 0xcf, 0x96, 0xca, 0x4a, 0x92, 0x49, 0xd8, 0x55, 0xe4, 0x2b,
+    0x3a, 0x70, 0x3e, 0xf1, 0x76, 0x7f, 0x6a, 0x9b, 0xd3, 0x6d, 0x6b, 0xf8,
+    0x2b, 0xe7, 0x6b, 0xbf, 0x0c, 0xba, 0x4f, 0xde, 0x59, 0xd2, 0xab, 0xcc,
+    0x76, 0xfe, 0xb6, 0x42, 0x47, 0xb8, 0x5c, 0x43, 0x1f, 0xbc, 0xa5, 0x22,
+    0x66, 0xb6, 0x19, 0xfc, 0x36, 0x97, 0x95, 0x43, 0xfc, 0xa9, 0xcb, 0xbd,
+    0xbb, 0xfa, 0xfa, 0x0e, 0x1a, 0x55, 0xe7, 0x55, 0xa3, 0xc7, 0xbc, 0xe6,
+    0x55, 0xf9, 0x64, 0x6f, 0x58, 0x2a, 0xb9, 0xcf, 0x70, 0xaa, 0x08, 0xb9,
+    0x79, 0xf8, 0x67, 0xf6, 0x3a, 0x0b, 0x2b, 0x7f, 0xdb, 0x36, 0x2c, 0x5b,
+    0xc4, 0xec, 0xd5, 0x55, 0xd8, 0x5b, 0xca, 0xa9, 0xc5, 0x93, 0xc3, 0x83,
+    0xc8, 0x57, 0xd4, 0x9d, 0xaa, 0xb7, 0x7e, 0x40, 0xb7, 0x85, 0x1d, 0xdf,
+    0xd2, 0x49, 0x98, 0x80, 0x8e, 0x35, 0xb2, 0x58, 0xe7, 0x5d, 0x78, 0xea,
+    0xc0, 0xca, 0x16, 0xf7, 0x04, 0x73, 0x04, 0xc2, 0x0d, 0x93, 0xed, 0xe4,
+    0xe8, 0xff, 0x1c, 0x6f, 0x17, 0xe6, 0x24, 0x3e, 0x3f, 0x3d, 0xa8, 0xfc,
+    0x17, 0x09, 0x87, 0x0e, 0xc4, 0x5f, 0xba, 0x82, 0x3a, 0x26, 0x3f, 0x0c,
+    0xef, 0xa1, 0xf7, 0x09, 0x3b, 0x19, 0x09, 0x92, 0x83, 0x26, 0x33, 0x37,
+    0x05, 0x04, 0x3a, 0x29, 0xbd, 0xa6, 0xf9, 0xb4, 0x34, 0x2c, 0xc8, 0xdf,
+    0x54, 0x3c, 0xb1, 0xa1, 0x18, 0x2f, 0x7c, 0x5f, 0xff, 0x33, 0xf1, 0x04,
+    0x90, 0xfa, 0xca, 0x5b, 0x25, 0x36, 0x0b, 0x76, 0x01, 0x5e, 0x9c, 0x5a,
+    0x06, 0xab, 0x8e, 0xe0, 0x2f, 0x00, 0xd2, 0xe8, 0xd5, 0x98, 0x61, 0x04,
+    0xaa, 0xcc, 0x4d, 0xd4, 0x75, 0xfd, 0x96, 0xee, 0x9c, 0xe4, 0xe3, 0x26,
+    0xf2, 0x1b, 0x83, 0xc7, 0x05, 0x85, 0x77, 0xb3, 0x87, 0x32, 0xcd, 0xda,
+    0xbc, 0x6a, 0x6b, 0xed, 0x13, 0xfb, 0x0d, 0x49, 0xd3, 0x8a, 0x45, 0xeb,
+    0x87, 0xa5, 0xf4};
+
+// Widevine-specific CENC Initialization data.
+// https://www.w3.org/TR/eme-stream-mp4/
+// https://www.w3.org/TR/eme-initdata-cenc/#common-system
+static constexpr uint8_t kCencInitData[] = {
+    0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68, 0x00, 0x00, 0x00,
+    0x00, 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, 0xa3, 0xc8,
+    0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed, 0x00, 0x00, 0x00, 0x14, 0x08,
+    0x01, 0x12, 0x10, 0x31, 0xfd, 0x5b, 0x66, 0x19, 0xfc, 0x5e, 0xad,
+    0x86, 0x7c, 0xff, 0xb5, 0x84, 0xed, 0x4c, 0x19};
+
+}  // namespace drm
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_STARBOARD_DRM_DRM_TEST_HELPERS_H_
diff --git a/starboard/win/win32/args.gn b/starboard/win/win32/args.gn
index 5dba6c9..ca2004d 100644
--- a/starboard/win/win32/args.gn
+++ b/starboard/win/win32/args.gn
@@ -16,4 +16,3 @@
 target_os = "win"
 target_cpu = "x64"
 is_clang = false
-is_internal_build = true
diff --git a/starboard/win/win32/cobalt/configuration.py b/starboard/win/win32/cobalt/configuration.py
index 0128815..1fff087 100644
--- a/starboard/win/win32/cobalt/configuration.py
+++ b/starboard/win/win32/cobalt/configuration.py
@@ -52,4 +52,13 @@
           # Flaky test to be re-enabled after b/271006511 is fixed.
           'CookieMonsterTest.PredicateSeesAllCookies',
       ],
+      'watchdog_test': [
+          # TODO(b/292027298): Enable once test failure is fixed.
+          'WatchdogTest.FrequentConsecutiveViolationsShouldNotWrite'
+          'WatchdogTest.PingInfosAreEvictedAfterMax',
+          'WatchdogTest.RedundantViolationsShouldStack',
+          'WatchdogTest.ViolationsAreEvictedAfterMax',
+          'WatchdogTest.ViolationsJsonShouldPersistAndBeValid',
+          'WatchdogTest.ViolationsShouldResetAfterFetch',
+      ],
   }
diff --git a/third_party/googletest/src/googletest/src/gtest.cc b/third_party/googletest/src/googletest/src/gtest.cc
index 76a0f72..ce395bc 100644
--- a/third_party/googletest/src/googletest/src/gtest.cc
+++ b/third_party/googletest/src/googletest/src/gtest.cc
@@ -1152,7 +1152,10 @@
 // Timer instead.
 TimeInMillis GetTimeInMillis() {
 #if GTEST_OS_STARBOARD
-  return SbTimeGetNow() / kSbTimeMillisecond;
+  // Use EzTime to get millis from posix epoch.
+  EzTimeValue time_value;
+  EzTimeValueGetNow(&time_value, NULL);
+  return time_value.tv_sec * 1000 + time_value.tv_usec / 1000;
 #else
   return std::chrono::duration_cast<std::chrono::milliseconds>(
              std::chrono::system_clock::now() -
diff --git a/third_party/libevent/evdns.c b/third_party/libevent/evdns.c
index d72fdaa..780d6b1 100644
--- a/third_party/libevent/evdns.c
+++ b/third_party/libevent/evdns.c
@@ -2496,9 +2496,12 @@
 static char *
 search_make_new(const struct search_state *const state, int n, const char *const base_name) {
 	const int base_len = strlen(base_name);
-	const char need_to_append_dot = base_name[base_len - 1] == '.' ? 0 : 1;
+	char need_to_append_dot;
 	struct search_domain *dom;
 
+	if (!base_len) return NULL;
+	need_to_append_dot = base_name[base_len - 1] == '.' ? 0 : 1;
+
 	for (dom = state->head; dom; dom = dom->next) {
 		if (!n--) {
 			/* this is the postfix we want */
diff --git a/third_party/libwebp/.cmake-format.py b/third_party/libwebp/.cmake-format.py
new file mode 100644
index 0000000..71043af
--- /dev/null
+++ b/third_party/libwebp/.cmake-format.py
@@ -0,0 +1,240 @@
+# ----------------------------------
+# Options affecting listfile parsing
+# ----------------------------------
+with section("parse"):
+
+  # Specify structure for custom cmake functions
+  additional_commands = { 'foo': { 'flags': ['BAR', 'BAZ'],
+             'kwargs': {'DEPENDS': '*', 'HEADERS': '*', 'SOURCES': '*'}}}
+
+  # Override configurations per-command where available
+  override_spec = {}
+
+  # Specify variable tags.
+  vartags = []
+
+  # Specify property tags.
+  proptags = []
+
+# -----------------------------
+# Options affecting formatting.
+# -----------------------------
+with section("format"):
+
+  # Disable formatting entirely, making cmake-format a no-op
+  disable = False
+
+  # How wide to allow formatted cmake files
+  line_width = 80
+
+  # How many spaces to tab for indent
+  tab_size = 2
+
+  # If true, lines are indented using tab characters (utf-8 0x09) instead of
+  # <tab_size> space characters (utf-8 0x20). In cases where the layout would
+  # require a fractional tab character, the behavior of the  fractional
+  # indentation is governed by <fractional_tab_policy>
+  use_tabchars = False
+
+  # If <use_tabchars> is True, then the value of this variable indicates how
+  # fractional indentions are handled during whitespace replacement. If set to
+  # 'use-space', fractional indentation is left as spaces (utf-8 0x20). If set
+  # to `round-up` fractional indentation is replaced with a single tab character
+  # (utf-8 0x09) effectively shifting the column to the next tabstop
+  fractional_tab_policy = 'use-space'
+
+  # If an argument group contains more than this many sub-groups (parg or kwarg
+  # groups) then force it to a vertical layout.
+  max_subgroups_hwrap = 3
+
+  # If a positional argument group contains more than this many arguments, then
+  # force it to a vertical layout.
+  max_pargs_hwrap = 6
+
+  # If a cmdline positional group consumes more than this many lines without
+  # nesting, then invalidate the layout (and nest)
+  max_rows_cmdline = 2
+
+  # If true, separate flow control names from their parentheses with a space
+  separate_ctrl_name_with_space = False
+
+  # If true, separate function names from parentheses with a space
+  separate_fn_name_with_space = False
+
+  # If a statement is wrapped to more than one line, than dangle the closing
+  # parenthesis on its own line.
+  dangle_parens = False
+
+  # If the trailing parenthesis must be 'dangled' on its on line, then align it
+  # to this reference: `prefix`: the start of the statement,  `prefix-indent`:
+  # the start of the statement, plus one indentation  level, `child`: align to
+  # the column of the arguments
+  dangle_align = 'prefix'
+
+  # If the statement spelling length (including space and parenthesis) is
+  # smaller than this amount, then force reject nested layouts.
+  min_prefix_chars = 4
+
+  # If the statement spelling length (including space and parenthesis) is larger
+  # than the tab width by more than this amount, then force reject un-nested
+  # layouts.
+  max_prefix_chars = 10
+
+  # If a candidate layout is wrapped horizontally but it exceeds this many
+  # lines, then reject the layout.
+  max_lines_hwrap = 2
+
+  # What style line endings to use in the output.
+  line_ending = 'unix'
+
+  # Format command names consistently as 'lower' or 'upper' case
+  command_case = 'canonical'
+
+  # Format keywords consistently as 'lower' or 'upper' case
+  keyword_case = 'unchanged'
+
+  # A list of command names which should always be wrapped
+  always_wrap = []
+
+  # If true, the argument lists which are known to be sortable will be sorted
+  # lexicographicall
+  enable_sort = True
+
+  # If true, the parsers may infer whether or not an argument list is sortable
+  # (without annotation).
+  autosort = False
+
+  # By default, if cmake-format cannot successfully fit everything into the
+  # desired linewidth it will apply the last, most agressive attempt that it
+  # made. If this flag is True, however, cmake-format will print error, exit
+  # with non-zero status code, and write-out nothing
+  require_valid_layout = False
+
+  # A dictionary mapping layout nodes to a list of wrap decisions. See the
+  # documentation for more information.
+  layout_passes = {}
+
+# ------------------------------------------------
+# Options affecting comment reflow and formatting.
+# ------------------------------------------------
+with section("markup"):
+
+  # What character to use for bulleted lists
+  bullet_char = '*'
+
+  # What character to use as punctuation after numerals in an enumerated list
+  enum_char = '.'
+
+  # If comment markup is enabled, don't reflow the first comment block in each
+  # listfile. Use this to preserve formatting of your copyright/license
+  # statements.
+  first_comment_is_literal = True
+
+  # If comment markup is enabled, don't reflow any comment block which matches
+  # this (regex) pattern. Default is `None` (disabled).
+  literal_comment_pattern = None
+
+  # Regular expression to match preformat fences in comments default=
+  # ``r'^\s*([`~]{3}[`~]*)(.*)$'``
+  fence_pattern = '^\\s*([`~]{3}[`~]*)(.*)$'
+
+  # Regular expression to match rulers in comments default=
+  # ``r'^\s*[^\w\s]{3}.*[^\w\s]{3}$'``
+  ruler_pattern = '^\\s*[^\\w\\s]{3}.*[^\\w\\s]{3}$'
+
+  # If a comment line matches starts with this pattern then it is explicitly a
+  # trailing comment for the preceeding argument. Default is '#<'
+  explicit_trailing_pattern = '#<'
+
+  # If a comment line starts with at least this many consecutive hash
+  # characters, then don't lstrip() them off. This allows for lazy hash rulers
+  # where the first hash char is not separated by space
+  hashruler_min_length = 10
+
+  # If true, then insert a space between the first hash char and remaining hash
+  # chars in a hash ruler, and normalize its length to fill the column
+  canonicalize_hashrulers = True
+
+  # enable comment markup parsing and reflow
+  enable_markup = True
+
+# ----------------------------
+# Options affecting the linter
+# ----------------------------
+with section("lint"):
+
+  # a list of lint codes to disable
+  disabled_codes = []
+
+  # regular expression pattern describing valid function names
+  function_pattern = '[0-9a-z_]+'
+
+  # regular expression pattern describing valid macro names
+  macro_pattern = '[0-9A-Z_]+'
+
+  # regular expression pattern describing valid names for variables with global
+  # (cache) scope
+  global_var_pattern = '[A-Z][0-9A-Z_]+'
+
+  # regular expression pattern describing valid names for variables with global
+  # scope (but internal semantic)
+  internal_var_pattern = '_[A-Z][0-9A-Z_]+'
+
+  # regular expression pattern describing valid names for variables with local
+  # scope
+  local_var_pattern = '[a-z][a-z0-9_]+'
+
+  # regular expression pattern describing valid names for privatedirectory
+  # variables
+  private_var_pattern = '_[0-9a-z_]+'
+
+  # regular expression pattern describing valid names for public directory
+  # variables
+  public_var_pattern = '[A-Z][0-9A-Z_]+'
+
+  # regular expression pattern describing valid names for function/macro
+  # arguments and loop variables.
+  argument_var_pattern = '[a-z][a-z0-9_]+'
+
+  # regular expression pattern describing valid names for keywords used in
+  # functions or macros
+  keyword_pattern = '[A-Z][0-9A-Z_]+'
+
+  # In the heuristic for C0201, how many conditionals to match within a loop in
+  # before considering the loop a parser.
+  max_conditionals_custom_parser = 2
+
+  # Require at least this many newlines between statements
+  min_statement_spacing = 1
+
+  # Require no more than this many newlines between statements
+  max_statement_spacing = 2
+  max_returns = 6
+  max_branches = 12
+  max_arguments = 5
+  max_localvars = 15
+  max_statements = 50
+
+# -------------------------------
+# Options affecting file encoding
+# -------------------------------
+with section("encode"):
+
+  # If true, emit the unicode byte-order mark (BOM) at the start of the file
+  emit_byteorder_mark = False
+
+  # Specify the encoding of the input file. Defaults to utf-8
+  input_encoding = 'utf-8'
+
+  # Specify the encoding of the output file. Defaults to utf-8. Note that cmake
+  # only claims to support utf-8 so be careful when using anything else
+  output_encoding = 'utf-8'
+
+# -------------------------------------
+# Miscellaneous configurations options.
+# -------------------------------------
+with section("misc"):
+
+  # A dictionary containing any per-command configuration overrides. Currently
+  # only `command_case` is supported.
+  per_command = {}
diff --git a/third_party/libwebp/.pylintrc b/third_party/libwebp/.pylintrc
new file mode 100644
index 0000000..4658b84
--- /dev/null
+++ b/third_party/libwebp/.pylintrc
@@ -0,0 +1,441 @@
+# This Pylint rcfile contains a best-effort configuration to uphold the
+# best-practices and style described in the Google Python style guide:
+#   https://google.github.io/styleguide/pyguide.html
+#
+# Its canonical open-source location is:
+#   https://google.github.io/styleguide/pylintrc
+
+[MASTER]
+
+# Files or directories to be skipped. They should be base names, not paths.
+ignore=third_party
+
+# Files or directories matching the regex patterns are skipped. The regex
+# matches against base names, not paths.
+ignore-patterns=
+
+# Pickle collected data for later comparisons.
+persistent=no
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+# Use multiple processes to speed up Pylint.
+jobs=4
+
+# Allow loading of arbitrary C extensions. Extensions are imported into the
+# active Python interpreter and may run arbitrary code.
+unsafe-load-any-extension=no
+
+
+[MESSAGES CONTROL]
+
+# Only show warnings with the listed confidence levels. Leave empty to show
+# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
+confidence=
+
+# Enable the message, report, category or checker with the given id(s). You can
+# either give multiple identifier separated by comma (,) or put this option
+# multiple time (only on the command line, not in the configuration file where
+# it should appear only once). See also the "--disable" option for examples.
+#enable=
+
+# Disable the message, report, category or checker with the given id(s). You
+# can either give multiple identifiers separated by comma (,) or put this
+# option multiple times (only on the command line, not in the configuration
+# file where it should appear only once).You can also use "--disable=all" to
+# disable everything first and then reenable specific checks. For example, if
+# you want to run only the similarities checker, you can use "--disable=all
+# --enable=similarities". If you want to run only the classes checker, but have
+# no Warning level messages displayed, use"--disable=all --enable=classes
+# --disable=W"
+disable=abstract-method,
+        apply-builtin,
+        arguments-differ,
+        attribute-defined-outside-init,
+        backtick,
+        bad-option-value,
+        basestring-builtin,
+        buffer-builtin,
+        c-extension-no-member,
+        consider-using-enumerate,
+        cmp-builtin,
+        cmp-method,
+        coerce-builtin,
+        coerce-method,
+        delslice-method,
+        div-method,
+        duplicate-code,
+        eq-without-hash,
+        execfile-builtin,
+        file-builtin,
+        filter-builtin-not-iterating,
+        fixme,
+        getslice-method,
+        global-statement,
+        hex-method,
+        idiv-method,
+        implicit-str-concat-in-sequence,
+        import-error,
+        import-self,
+        import-star-module-level,
+        inconsistent-return-statements,
+        input-builtin,
+        intern-builtin,
+        invalid-str-codec,
+        locally-disabled,
+        long-builtin,
+        long-suffix,
+        map-builtin-not-iterating,
+        misplaced-comparison-constant,
+        missing-function-docstring,
+        metaclass-assignment,
+        next-method-called,
+        next-method-defined,
+        no-absolute-import,
+        no-else-break,
+        no-else-continue,
+        no-else-raise,
+        no-else-return,
+        no-init,  # added
+        no-member,
+        no-name-in-module,
+        no-self-use,
+        nonzero-method,
+        oct-method,
+        old-division,
+        old-ne-operator,
+        old-octal-literal,
+        old-raise-syntax,
+        parameter-unpacking,
+        print-statement,
+        raising-string,
+        range-builtin-not-iterating,
+        raw_input-builtin,
+        rdiv-method,
+        reduce-builtin,
+        relative-import,
+        reload-builtin,
+        round-builtin,
+        setslice-method,
+        signature-differs,
+        standarderror-builtin,
+        suppressed-message,
+        sys-max-int,
+        too-few-public-methods,
+        too-many-ancestors,
+        too-many-arguments,
+        too-many-boolean-expressions,
+        too-many-branches,
+        too-many-instance-attributes,
+        too-many-locals,
+        too-many-nested-blocks,
+        too-many-public-methods,
+        too-many-return-statements,
+        too-many-statements,
+        trailing-newlines,
+        unichr-builtin,
+        unicode-builtin,
+        unnecessary-pass,
+        unpacking-in-except,
+        useless-else-on-loop,
+        useless-object-inheritance,
+        useless-suppression,
+        using-cmp-argument,
+        wrong-import-order,
+        xrange-builtin,
+        zip-builtin-not-iterating,
+
+
+[REPORTS]
+
+# Set the output format. Available formats are text, parseable, colorized, msvs
+# (visual studio) and html. You can also give a reporter class, eg
+# mypackage.mymodule.MyReporterClass.
+output-format=text
+
+# Put messages in a separate file for each module / package specified on the
+# command line instead of printing them on stdout. Reports (if any) will be
+# written in a file name "pylint_global.[txt|html]". This option is deprecated
+# and it will be removed in Pylint 2.0.
+files-output=no
+
+# Tells whether to display a full report or only the messages
+reports=no
+
+# Python expression which should return a note less than 10 (10 is the highest
+# note). You have access to the variables errors warning, statement which
+# respectively contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (RP0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Template used to display messages. This is a python new-style format string
+# used to format the message information. See doc for all details
+#msg-template=
+
+
+[BASIC]
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=main,_,PRESUBMIT
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=
+
+# Colon-delimited sets of names that determine each other's naming style when
+# the name regexes allow several styles.
+name-group=
+
+# Include a hint for the correct naming format with invalid-name
+include-naming-hint=no
+
+# List of decorators that produce properties, such as abc.abstractproperty. Add
+# to this list to register other decorators that produce valid properties.
+property-classes=abc.abstractproperty,cached_property.cached_property,cached_property.threaded_cached_property,cached_property.cached_property_with_ttl,cached_property.threaded_cached_property_with_ttl
+
+# Regular expression matching correct function names
+function-rgx=^(?:(?P<exempt>setUp|tearDown|setUpModule|tearDownModule)|(?P<camel_case>_?[A-Z][a-zA-Z0-9]*)|(?P<snake_case>_?[a-z][a-z0-9_]*))$
+
+# Regular expression matching correct variable names
+variable-rgx=^[a-z][a-z0-9_]*$
+
+# Regular expression matching correct constant names
+const-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$
+
+# Regular expression matching correct attribute names
+attr-rgx=^_{0,2}[a-z][a-z0-9_]*$
+
+# Regular expression matching correct argument names
+argument-rgx=^[a-z][a-z0-9_]*$
+
+# Regular expression matching correct class attribute names
+class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$
+
+# Regular expression matching correct inline iteration names
+inlinevar-rgx=^[a-z][a-z0-9_]*$
+
+# Regular expression matching correct class names
+class-rgx=^_?[A-Z][a-zA-Z0-9]*$
+
+# Regular expression matching correct module names
+module-rgx=^(_?[a-z][a-z0-9_]*|__init__)$
+
+# Regular expression matching correct method names
+method-rgx=(?x)^(?:(?P<exempt>_[a-z0-9_]+__|runTest|setUp|tearDown|setUpTestCase|tearDownTestCase|setupSelf|tearDownClass|setUpClass|(test|assert)_*[A-Z0-9][a-zA-Z0-9_]*|next)|(?P<camel_case>_{0,2}[A-Z][a-zA-Z0-9_]*)|(?P<snake_case>_{0,2}[a-z][a-z0-9_]*))$
+
+# Regular expression which should only match function or class names that do
+# not require a docstring.
+no-docstring-rgx=(__.*__|main|test.*|.*test|.*Test)$
+
+# Minimum line length for functions/classes that require docstrings, shorter
+# ones are exempt.
+docstring-min-length=10
+
+
+[TYPECHECK]
+
+# List of decorators that produce context managers, such as
+# contextlib.contextmanager. Add to this list to register other decorators that
+# produce valid context managers.
+contextmanager-decorators=contextlib.contextmanager,contextlib2.contextmanager
+
+# Tells whether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# List of module names for which member attributes should not be checked
+# (useful for modules/projects where namespaces are manipulated during runtime
+# and thus existing member attributes cannot be deduced by static analysis. It
+# supports qualified module names, as well as Unix pattern matching.
+ignored-modules=
+
+# List of class names for which member attributes should not be checked (useful
+# for classes with dynamically set attributes). This supports the use of
+# qualified names.
+ignored-classes=optparse.Values,thread._local,_thread._local
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E1101 when accessed. Python regular
+# expressions are accepted.
+generated-members=
+
+
+[FORMAT]
+
+# Maximum number of characters on a single line.
+max-line-length=80
+
+# TODO(https://github.com/PyCQA/pylint/issues/3352): Direct pylint to exempt
+# lines made too long by directives to pytype.
+
+# Regexp for a line that is allowed to be longer than the limit.
+ignore-long-lines=(?x)(
+  ^\s*(\#\ )?<?https?://\S+>?$|
+  ^\s*(from\s+\S+\s+)?import\s+.+$)
+
+# Allow the body of an if to be on the same line as the test if there is no
+# else.
+single-line-if-stmt=yes
+
+# List of optional constructs for which whitespace checking is disabled. `dict-
+# separator` is used to allow tabulation in dicts, etc.: {1  : 1,\n222: 2}.
+# `trailing-comma` allows a space between comma and closing bracket: (a, ).
+# `empty-line` allows space-only lines.
+no-space-check=
+
+# Maximum number of lines in a module
+max-module-lines=99999
+
+# String used as indentation unit.  The internal Google style guide mandates 2
+# spaces.  Google's externaly-published style guide says 4, consistent with
+# PEP 8.  Here, we use 2 spaces, for conformity with many open-sourced Google
+# projects (like TensorFlow).
+indent-string='  '
+
+# Number of spaces of indent required inside a hanging  or continued line.
+indent-after-paren=4
+
+# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
+expected-line-ending-format=
+
+
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=TODO
+
+
+[STRING]
+
+# This flag controls whether inconsistent-quotes generates a warning when the
+# character used as a quote delimiter is used inconsistently within a module.
+check-quote-consistency=yes
+
+
+[VARIABLES]
+
+# Tells whether we should check for unused import in __init__ files.
+init-import=no
+
+# A regular expression matching the name of dummy variables (i.e. expectedly
+# not used).
+dummy-variables-rgx=^\*{0,2}(_$|unused_|dummy_)
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+# List of strings which can identify a callback function by name. A callback
+# name must start or end with one of those strings.
+callbacks=cb_,_cb
+
+# List of qualified module names which can have objects that can redefine
+# builtins.
+redefining-builtins-modules=six,six.moves,past.builtins,future.builtins,functools
+
+
+[LOGGING]
+
+# Logging modules to check that the string format arguments are in logging
+# function parameter format
+logging-modules=logging,absl.logging,tensorflow.io.logging
+
+
+[SIMILARITIES]
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+# Ignore imports when computing similarities.
+ignore-imports=no
+
+
+[SPELLING]
+
+# Spelling dictionary name. Available dictionaries: none. To make it working
+# install python-enchant package.
+spelling-dict=
+
+# List of comma separated words that should not be checked.
+spelling-ignore-words=
+
+# A path to a file that contains private dictionary; one word per line.
+spelling-private-dict-file=
+
+# Tells whether to store unknown words to indicated private dictionary in
+# --spelling-private-dict-file option instead of raising a message.
+spelling-store-unknown-words=no
+
+
+[IMPORTS]
+
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=regsub,
+                   TERMIOS,
+                   Bastion,
+                   rexec,
+                   sets
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report RP0402 must not be disabled)
+import-graph=
+
+# Create a graph of external dependencies in the given file (report RP0402 must
+# not be disabled)
+ext-import-graph=
+
+# Create a graph of internal dependencies in the given file (report RP0402 must
+# not be disabled)
+int-import-graph=
+
+# Force import order to recognize a module as part of the standard
+# compatibility libraries.
+known-standard-library=
+
+# Force import order to recognize a module as part of a third party library.
+known-third-party=enchant, absl
+
+# Analyse import fallback blocks. This can be used to support both Python 2 and
+# 3 compatible code, which means that the block might have code that exists
+# only in one or another interpreter, leading to false positives when analysed.
+analyse-fallback-blocks=no
+
+
+[CLASSES]
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,
+                      __new__,
+                      setUp
+
+# List of member names, which should be excluded from the protected access
+# warning.
+exclude-protected=_asdict,
+                  _fields,
+                  _replace,
+                  _source,
+                  _make
+
+# List of valid names for the first argument in a class method.
+valid-classmethod-first-arg=cls,
+                            class_
+
+# List of valid names for the first argument in a metaclass class method.
+valid-metaclass-classmethod-first-arg=mcs
+
+
+[EXCEPTIONS]
+
+# Exceptions that will emit a warning when being caught. Defaults to
+# "Exception"
+overgeneral-exceptions=StandardError,
+                       Exception,
+                       BaseException
diff --git a/third_party/libwebp/.style.yapf b/third_party/libwebp/.style.yapf
new file mode 100644
index 0000000..0be981a
--- /dev/null
+++ b/third_party/libwebp/.style.yapf
@@ -0,0 +1,2 @@
+[style]
+based_on_style = yapf
\ No newline at end of file
diff --git a/third_party/libwebp/AUTHORS b/third_party/libwebp/AUTHORS
index 83c7b9c..8359b20 100644
--- a/third_party/libwebp/AUTHORS
+++ b/third_party/libwebp/AUTHORS
@@ -1,28 +1,45 @@
 Contributors:
+- Aidan O'Loan (aidanol at gmail dot com)
+- Alan Browning (browning at google dot com)
+- Alexandru Ardelean (ardeleanalex at gmail dot com)
+- Brian Ledger (brianpl at google dot com)
 - Charles Munger (clm at google dot com)
+- Cheng Yi (cyi at google dot com)
 - Christian Duvivier (cduvivier at google dot com)
+- Christopher Degawa (ccom at randomderp dot com)
+- Clement Courbet (courbet at google dot com)
 - Djordje Pesut (djordje dot pesut at imgtec dot com)
+- Frank Barchard (fbarchard at google dot com)
 - Hui Su (huisu at google dot com)
+- H. Vetinari (h dot vetinari at gmx dot com)
+- Ilya Kurdyukov (jpegqs at gmail dot com)
+- Ingvar Stepanyan (rreverser at google dot com)
 - James Zern (jzern at google dot com)
 - Jan Engelhardt (jengelh at medozas dot de)
 - Jehan (jehan at girinstud dot io)
-- Johann (johann dot koenig at duck dot com)
+- Jeremy Maitin-Shepard (jbms at google dot com)
+- Johann Koenig (johann dot koenig at duck dot com)
 - Jovan Zelincevic (jovan dot zelincevic at imgtec dot com)
 - Jyrki Alakuijala (jyrki at google dot com)
+- Konstantin Ivlev (tomskside at gmail dot com)
 - Lode Vandevenne (lode at google dot com)
 - Lou Quillio (louquillio at google dot com)
 - Mans Rullgard (mans at mansr dot com)
 - Marcin Kowalczyk (qrczak at google dot com)
 - Martin Olsson (mnemo at minimum dot se)
+- Maryla Ustarroz-Calonge (maryla at google dot com)
 - Mikołaj Zalewski (mikolajz at google dot com)
 - Mislav Bradac (mislavm at google dot com)
 - Nico Weber (thakis at chromium dot org)
 - Noel Chromium (noel at chromium dot org)
+- Nozomi Isozaki (nontan at pixiv dot co dot jp)
+- Oliver Wolff (oliver dot wolff at qt dot io)
 - Owen Rodley (orodley at google dot com)
 - Parag Salasakar (img dot mips1 at gmail dot com)
 - Pascal Massimino (pascal dot massimino at gmail dot com)
 - Paweł Hajdan, Jr (phajdan dot jr at chromium dot org)
 - Pierre Joye (pierre dot php at gmail dot com)
+- Roberto Alanis (alanisbaez at google dot com)
 - Sam Clegg (sbc at chromium dot org)
 - Scott Hancher (seh at google dot com)
 - Scott LaVarnway (slavarnway at google dot com)
@@ -31,9 +48,13 @@
 - Somnath Banerjee (somnath dot banerjee at gmail dot com)
 - Sriraman Tallam (tmsriram at google dot com)
 - Tamar Levy (tamar dot levy at intel dot com)
+- Thiago Perrotta (tperrotta at google dot com)
 - Timothy Gu (timothygu99 at gmail dot com)
 - Urvang Joshi (urvang at google dot com)
 - Vikas Arora (vikasa at google dot com)
 - Vincent Rabaud (vrabaud at google dot com)
 - Vlad Tsyrklevich (vtsyrklevich at chromium dot org)
+- Wan-Teh Chang (wtc at google dot com)
 - Yang Zhang (yang dot zhang at arm dot com)
+- Yannis Guyon (yguyon at google dot com)
+- Zhi An Ng (zhin at chromium dot org)
diff --git a/third_party/libwebp/Android.mk b/third_party/libwebp/Android.mk
index 6752f77..9d16f08 100644
--- a/third_party/libwebp/Android.mk
+++ b/third_party/libwebp/Android.mk
@@ -1,3 +1,5 @@
+# Ignore this file during non-NDK builds.
+ifdef NDK_ROOT
 LOCAL_PATH := $(call my-dir)
 
 WEBP_CFLAGS := -Wall -DANDROID -DHAVE_MALLOC_H -DHAVE_PTHREAD -DWEBP_USE_THREAD
@@ -33,6 +35,15 @@
   NEON := c
 endif
 
+sharpyuv_srcs := \
+    sharpyuv/sharpyuv.c \
+    sharpyuv/sharpyuv_cpu.c \
+    sharpyuv/sharpyuv_csp.c \
+    sharpyuv/sharpyuv_dsp.c \
+    sharpyuv/sharpyuv_gamma.c \
+    sharpyuv/sharpyuv_neon.$(NEON) \
+    sharpyuv/sharpyuv_sse2.c \
+
 dec_srcs := \
     src/dec/alpha_dec.c \
     src/dec/buffer_dec.c \
@@ -74,6 +85,7 @@
     src/dsp/lossless_msa.c \
     src/dsp/lossless_neon.$(NEON) \
     src/dsp/lossless_sse2.c \
+    src/dsp/lossless_sse41.c \
     src/dsp/rescaler.c \
     src/dsp/rescaler_mips32.c \
     src/dsp/rescaler_mips_dsp_r2.c \
@@ -97,9 +109,9 @@
     src/dsp/cost.c \
     src/dsp/cost_mips32.c \
     src/dsp/cost_mips_dsp_r2.c \
+    src/dsp/cost_neon.$(NEON) \
     src/dsp/cost_sse2.c \
     src/dsp/enc.c \
-    src/dsp/enc_avx2.c \
     src/dsp/enc_mips32.c \
     src/dsp/enc_mips_dsp_r2.c \
     src/dsp/enc_msa.c \
@@ -174,7 +186,7 @@
     $(utils_dec_srcs) \
 
 LOCAL_CFLAGS := $(WEBP_CFLAGS)
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
+LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)/src
 
 # prefer arm over thumb mode for performance gains
 LOCAL_ARM_MODE := arm
@@ -203,12 +215,13 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := \
+    $(sharpyuv_srcs) \
     $(dsp_enc_srcs) \
     $(enc_srcs) \
     $(utils_enc_srcs) \
 
 LOCAL_CFLAGS := $(WEBP_CFLAGS)
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
+LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)/src $(LOCAL_PATH)
 
 # prefer arm over thumb mode for performance gains
 LOCAL_ARM_MODE := arm
@@ -231,7 +244,7 @@
 LOCAL_SRC_FILES := $(demux_srcs)
 
 LOCAL_CFLAGS := $(WEBP_CFLAGS)
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
+LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)/src
 
 # prefer arm over thumb mode for performance gains
 LOCAL_ARM_MODE := arm
@@ -254,7 +267,7 @@
 LOCAL_SRC_FILES := $(mux_srcs)
 
 LOCAL_CFLAGS := $(WEBP_CFLAGS)
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
+LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)/src
 
 # prefer arm over thumb mode for performance gains
 LOCAL_ARM_MODE := arm
@@ -278,3 +291,4 @@
 ifeq ($(USE_CPUFEATURES),yes)
   $(call import-module,android/cpufeatures)
 endif
+endif  # NDK_ROOT
diff --git a/third_party/libwebp/BUILD.gn b/third_party/libwebp/BUILD.gn
index 8b6c476..537325c 100644
--- a/third_party/libwebp/BUILD.gn
+++ b/third_party/libwebp/BUILD.gn
@@ -25,6 +25,14 @@
       "//starboard/build/config:speed",
       ":libwebp_direct_config",
     ]
+    if (is_win) {
+      cflags_c = [ "/wd4312" ]  # warning C4312: 'type cast': conversion from 'int' to 'int *'
+    } else {
+      cflags_c = [
+        "-Wno-implicit-function-declaration",
+        "-Wno-int-to-pointer-cast",
+      ]
+    }
   }
 }
 
@@ -83,6 +91,11 @@
   sources = libwebp_enc_sources
 }
 
+libwebp_lib("libwebp_sharpyuv") {
+  sources = libwebp_sharpyuv_sources
+  deps = [ ":libwebp_dsp_dec_common" ]
+}
+
 libwebp_lib("libwebp_dsp_enc") {
   sources = libwebp_dsp_enc_sources
 }
@@ -93,7 +106,7 @@
 
 config("libwebp_direct_config") {
   include_dirs = [ "." ]
-  if (is_starboard && !is_win) {
+  if (is_starboard && (target_os == "android" || target_os == "linux")) {
     if (current_cpu == "x64" || current_cpu == "x86") {
       cflags = [
         "-msse2",
@@ -113,8 +126,7 @@
       ]
       defines += [ "WEBP_HAVE_AVX2" ]
     }
-    if ((current_cpu == "arm" || current_cpu == "arm64") && arm_use_neon &&
-        (target_os == "android" || target_os == "linux")) {
+    if ((current_cpu == "arm" || current_cpu == "arm64") && arm_use_neon ) {
       defines = [ "WEBP_HAVE_NEON" ]
       cflags = [ "-mfpu=neon" ]
     }
@@ -129,6 +141,7 @@
     ":libwebp_dsp_dec_common",
     ":libwebp_dsp_enc",
     ":libwebp_enc",
+    ":libwebp_sharpyuv",
     ":libwebp_utils_dec",
     ":libwebp_utils_enc",
   ]
diff --git a/third_party/libwebp/CMakeLists.txt b/third_party/libwebp/CMakeLists.txt
index ea263b3..ad5e14c 100644
--- a/third_party/libwebp/CMakeLists.txt
+++ b/third_party/libwebp/CMakeLists.txt
@@ -1,79 +1,219 @@
-cmake_minimum_required(VERSION 2.8.7)
+#  Copyright (c) 2020 Google LLC.
+#
+#  Use of this source code is governed by a BSD-style license
+#  that can be found in the LICENSE 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.
 
-project(libwebp C)
-
-# Options for coder / decoder executables.
-option(WEBP_ENABLE_SIMD "Enable any SIMD optimization." ON)
-option(WEBP_BUILD_CWEBP "Build the cwebp command line tool." OFF)
-option(WEBP_BUILD_DWEBP "Build the dwebp command line tool." OFF)
-option(WEBP_BUILD_GIF2WEBP "Build the gif2webp conversion tool." OFF)
-option(WEBP_BUILD_IMG2WEBP "Build the img2webp animation tool." OFF)
-option(WEBP_BUILD_WEBPINFO "Build the webpinfo command line tool." OFF)
-option(WEBP_BUILD_WEBP_JS "Emscripten build of webp.js." OFF)
-option(WEBP_NEAR_LOSSLESS "Enable near-lossless encoding" ON)
-option(WEBP_ENABLE_SWAP_16BIT_CSP "Enable byte swap for 16 bit colorspaces." OFF)
-
-if(WEBP_BUILD_WEBP_JS)
-  set(WEBP_ENABLE_SIMD OFF)
+if(APPLE)
+  cmake_minimum_required(VERSION 3.17)
+else()
+  cmake_minimum_required(VERSION 3.7)
 endif()
 
+if(POLICY CMP0072)
+  cmake_policy(SET CMP0072 NEW)
+endif()
+
+project(WebP C)
+
+# Options for coder / decoder executables.
+if(BUILD_SHARED_LIBS)
+  set(WEBP_LINK_STATIC_DEFAULT OFF)
+else()
+  set(WEBP_LINK_STATIC_DEFAULT ON)
+endif()
+option(WEBP_LINK_STATIC
+       "Link using static libraries. If OFF, use dynamic libraries."
+       ${WEBP_LINK_STATIC_DEFAULT})
+if(NOT EMSCRIPTEN)
+  # Disable SIMD on Emscripten by default, as it's a new unstable Wasm feature.
+  # Users can still explicitly opt-in to make a SIMD-enabled build.
+  set(WEBP_ENABLE_SIMD_DEFAULT ON)
+endif()
+option(WEBP_ENABLE_SIMD "Enable any SIMD optimization."
+       ${WEBP_ENABLE_SIMD_DEFAULT})
+option(WEBP_BUILD_ANIM_UTILS "Build animation utilities." ON)
+option(WEBP_BUILD_CWEBP "Build the cwebp command line tool." ON)
+option(WEBP_BUILD_DWEBP "Build the dwebp command line tool." ON)
+option(WEBP_BUILD_GIF2WEBP "Build the gif2webp conversion tool." ON)
+option(WEBP_BUILD_IMG2WEBP "Build the img2webp animation tool." ON)
+option(WEBP_BUILD_VWEBP "Build the vwebp viewer tool." ON)
+option(WEBP_BUILD_WEBPINFO "Build the webpinfo command line tool." ON)
+option(WEBP_BUILD_LIBWEBPMUX "Build the libwebpmux library." ON)
+option(WEBP_BUILD_WEBPMUX "Build the webpmux command line tool." ON)
+option(WEBP_BUILD_EXTRAS "Build extras." ON)
+option(WEBP_BUILD_WEBP_JS "Emscripten build of webp.js." OFF)
+option(WEBP_USE_THREAD "Enable threading support" ON)
+option(WEBP_NEAR_LOSSLESS "Enable near-lossless encoding" ON)
+option(WEBP_ENABLE_SWAP_16BIT_CSP "Enable byte swap for 16 bit colorspaces."
+       OFF)
+set(WEBP_BITTRACE "0" CACHE STRING "Bit trace mode (0=none, 1=bit, 2=bytes)")
+set_property(CACHE WEBP_BITTRACE PROPERTY STRINGS 0 1 2)
+
+if(WEBP_LINK_STATIC)
+  if(WIN32)
+    set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
+  else()
+    set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
+  endif()
+  set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+  # vwebp does not compile on Ubuntu with static libraries so disabling it for
+  # now.
+  set(WEBP_BUILD_VWEBP OFF)
+endif()
+
+# Option needed for handling Unicode file names on Windows.
+if(WIN32)
+  option(WEBP_UNICODE "Build Unicode executables." ON)
+endif()
+
+if(WEBP_BUILD_WEBP_JS)
+  set(WEBP_BUILD_ANIM_UTILS OFF)
+  set(WEBP_BUILD_CWEBP OFF)
+  set(WEBP_BUILD_DWEBP OFF)
+  set(WEBP_BUILD_GIF2WEBP OFF)
+  set(WEBP_BUILD_IMG2WEBP OFF)
+  set(WEBP_BUILD_VWEBP OFF)
+  set(WEBP_BUILD_WEBPINFO OFF)
+  set(WEBP_BUILD_WEBPMUX OFF)
+  set(WEBP_BUILD_EXTRAS OFF)
+  set(WEBP_USE_THREAD OFF)
+
+  if(WEBP_ENABLE_SIMD)
+    message(NOTICE
+            "wasm2js does not support SIMD, disabling webp.js generation.")
+  endif()
+endif()
+
+set(SHARPYUV_DEP_LIBRARIES)
+set(SHARPYUV_DEP_INCLUDE_DIRS)
 set(WEBP_DEP_LIBRARIES)
 set(WEBP_DEP_INCLUDE_DIRS)
 
 if(NOT CMAKE_BUILD_TYPE)
-  set(CMAKE_BUILD_TYPE "Release" CACHE
-    "Build type: Release, Debug or RelWithDebInfo" STRING FORCE
-  )
+  set(CMAKE_BUILD_TYPE "Release"
+      CACHE STRING "Build type: Release, Debug, MinSizeRel or RelWithDebInfo"
+            FORCE)
 endif()
 
 # Include dependencies.
+if(WEBP_BUILD_ANIM_UTILS
+   OR WEBP_BUILD_CWEBP
+   OR WEBP_BUILD_DWEBP
+   OR WEBP_BUILD_EXTRAS
+   OR WEBP_BUILD_GIF2WEBP
+   OR WEBP_BUILD_IMG2WEBP)
+  set(WEBP_FIND_IMG_LIBS TRUE)
+else()
+  set(WEBP_FIND_IMG_LIBS FALSE)
+endif()
 include(cmake/deps.cmake)
+include(GNUInstallDirs)
 
-################################################################################
+if(BUILD_SHARED_LIBS AND NOT DEFINED CMAKE_INSTALL_RPATH)
+  # Set the rpath to match autoconf/libtool behavior. Note this must be set
+  # before target creation.
+  set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
+endif()
+
+# ##############################################################################
 # Options.
 if(WEBP_ENABLE_SWAP_16BIT_CSP)
   add_definitions(-DWEBP_SWAP_16BIT_CSP=1)
 endif()
 
-################################################################################
+if(NOT WEBP_BITTRACE STREQUAL "0")
+  add_definitions(-DBITTRACE=${WEBP_BITTRACE})
+endif()
+
+if(WEBP_UNICODE)
+  # Windows recommends setting both UNICODE and _UNICODE.
+  add_definitions(-DUNICODE -D_UNICODE)
+endif()
+
+if(MSVC AND BUILD_SHARED_LIBS)
+  add_definitions(-DWEBP_DLL)
+endif()
+
+# pkg-config variables used by *.pc.in.
+set(prefix ${CMAKE_INSTALL_PREFIX})
+set(exec_prefix "\${prefix}")
+if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}")
+  set(libdir "${CMAKE_INSTALL_LIBDIR}")
+else()
+  set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
+endif()
+if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}")
+  set(includedir "${CMAKE_INSTALL_INCLUDEDIR}")
+else()
+  set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
+endif()
+set(PTHREAD_LIBS ${CMAKE_THREAD_LIBS_INIT})
+set(INSTALLED_LIBRARIES)
+
+if(MSVC)
+  # match the naming convention used by nmake
+  set(webp_libname_prefix "lib")
+  set(CMAKE_SHARED_LIBRARY_PREFIX "${webp_libname_prefix}")
+  set(CMAKE_IMPORT_LIBRARY_PREFIX "${webp_libname_prefix}")
+  set(CMAKE_STATIC_LIBRARY_PREFIX "${webp_libname_prefix}")
+endif()
+
+set(CMAKE_C_VISIBILITY_PRESET hidden)
+
+# ##############################################################################
 # Android only.
 if(ANDROID)
   include_directories(${ANDROID_NDK}/sources/android/cpufeatures)
-  add_library(cpufeatures STATIC
-    ${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c
-  )
-  target_link_libraries(cpufeatures dl)
-  set(WEBP_DEP_LIBRARIES ${WEBP_DEP_LIBRARIES} cpufeatures)
-  set(WEBP_DEP_INCLUDE_DIRS ${WEBP_DEP_INCLUDE_DIRS}
-    ${ANDROID_NDK}/sources/android/cpufeatures
-  )
+  add_library(cpufeatures-webp STATIC
+              ${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c)
+  list(APPEND INSTALLED_LIBRARIES cpufeatures-webp)
+  target_link_libraries(cpufeatures-webp dl)
+  set(SHARPYUV_DEP_LIBRARIES ${SHARPYUV_DEP_LIBRARIES} cpufeatures-webp)
+  set(WEBP_DEP_LIBRARIES ${WEBP_DEP_LIBRARIES} cpufeatures-webp)
+  set(cpufeatures_include_dir ${ANDROID_NDK}/sources/android/cpufeatures)
+  set(SHARPYUV_DEP_INCLUDE_DIRS ${SHARPYUV_DEP_INCLUDE_DIRS}
+                                ${cpufeatures_include_dir})
+  set(WEBP_DEP_INCLUDE_DIRS ${WEBP_DEP_INCLUDE_DIRS} ${cpufeatures_include_dir})
   add_definitions(-DHAVE_CPU_FEATURES_H=1)
   set(HAVE_CPU_FEATURES_H 1)
 else()
   set(HAVE_CPU_FEATURES_H 0)
 endif()
 
-################################################################################
-# WebP source files.
-# Read the Makefile.am to get the source files.
+function(configure_pkg_config FILE)
+  configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${FILE}.in"
+                 "${CMAKE_CURRENT_BINARY_DIR}/${FILE}" @ONLY)
 
-# We expect the Makefiles to define the sources as defined in
-# the first regex. E.g.:
-# libimagedec_la_SOURCES  = image_dec.c image_dec.h
+  if(HAVE_MATH_LIBRARY)
+    # MSVC doesn't have libm
+    file(READ ${CMAKE_CURRENT_BINARY_DIR}/${FILE} data)
+    string(REPLACE "-lm" "" data ${data})
+    file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${FILE} ${data})
+  endif()
+
+  install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${FILE}"
+          DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
+endfunction()
+
+# ##############################################################################
+# WebP source files. Read the Makefile.am to get the source files.
+
+# We expect the Makefiles to define the sources as defined in the first regex.
+# E.g.: libimagedec_la_SOURCES  = image_dec.c image_dec.h
 function(parse_Makefile_am FOLDER VAR SRC_REGEX)
   file(READ ${FOLDER}/Makefile.am MAKEFILE_AM)
   string(REGEX MATCHALL "${SRC_REGEX}_SOURCES[ ]*\\+?=[ ]+[0-9a-z\\._ ]*"
-    FILES_PER_LINE ${MAKEFILE_AM}
-  )
+               FILES_PER_LINE ${MAKEFILE_AM})
   set(SRCS ${${VAR}})
   foreach(FILES ${FILES_PER_LINE})
     string(FIND ${FILES} "=" OFFSET)
     math(EXPR OFFSET "${OFFSET} + 2")
     string(SUBSTRING ${FILES} ${OFFSET} -1 FILES)
     if(FILES)
-      string(REGEX MATCHALL "[0-9a-z\\._]+"
-        FILES ${FILES}
-      )
+      string(REGEX MATCHALL "[0-9a-z\\._]+" FILES ${FILES})
       foreach(FILE ${FILES})
         list(APPEND SRCS ${FOLDER}/${FILE})
       endforeach()
@@ -83,16 +223,16 @@
 endfunction()
 
 set(WEBP_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
-parse_Makefile_am(${WEBP_SRC_DIR}/dec "WEBP_DEC_SRCS" "")
-parse_Makefile_am(${WEBP_SRC_DIR}/demux "WEBP_DEMUX_SRCS" "")
-parse_Makefile_am(${WEBP_SRC_DIR}/dsp "WEBP_DSP_COMMON_SRCS" "COMMON")
-parse_Makefile_am(${WEBP_SRC_DIR}/dsp "WEBP_DSP_ENC_SRCS" "ENC")
-parse_Makefile_am(${WEBP_SRC_DIR}/dsp "WEBP_DSP_ENC_SRCS" "dsp_[^ ]*")
-parse_Makefile_am(${WEBP_SRC_DIR}/dsp "WEBP_DSP_DEC_SRCS" "decode_[^ ]*")
-parse_Makefile_am(${WEBP_SRC_DIR}/enc "WEBP_ENC_SRCS" "")
-parse_Makefile_am(${WEBP_SRC_DIR}/utils "WEBP_UTILS_COMMON_SRCS" "COMMON")
-parse_Makefile_am(${WEBP_SRC_DIR}/utils "WEBP_UTILS_ENC_SRCS" "ENC")
-parse_Makefile_am(${WEBP_SRC_DIR}/utils "WEBP_UTILS_DEC_SRCS" "decode_[^ ]*")
+parse_makefile_am(${WEBP_SRC_DIR}/dec "WEBP_DEC_SRCS" "")
+parse_makefile_am(${WEBP_SRC_DIR}/demux "WEBP_DEMUX_SRCS" "")
+parse_makefile_am(${WEBP_SRC_DIR}/dsp "WEBP_DSP_COMMON_SRCS" "COMMON")
+parse_makefile_am(${WEBP_SRC_DIR}/dsp "WEBP_DSP_ENC_SRCS" "ENC")
+parse_makefile_am(${WEBP_SRC_DIR}/dsp "WEBP_DSP_ENC_SRCS" "dsp_[^ ]*")
+parse_makefile_am(${WEBP_SRC_DIR}/dsp "WEBP_DSP_DEC_SRCS" "decode_[^ ]*")
+parse_makefile_am(${WEBP_SRC_DIR}/enc "WEBP_ENC_SRCS" "")
+parse_makefile_am(${WEBP_SRC_DIR}/utils "WEBP_UTILS_COMMON_SRCS" "COMMON")
+parse_makefile_am(${WEBP_SRC_DIR}/utils "WEBP_UTILS_ENC_SRCS" "ENC")
+parse_makefile_am(${WEBP_SRC_DIR}/utils "WEBP_UTILS_DEC_SRCS" "decode_[^ ]*")
 
 # Remove the files specific to SIMD we don't use.
 foreach(FILE ${WEBP_SIMD_FILES_NOT_TO_INCLUDE})
@@ -100,262 +240,555 @@
   list(REMOVE_ITEM WEBP_DSP_DEC_SRCS ${FILE})
 endforeach()
 
-### Define the mandatory libraries.
+# Generate the config.h file.
+configure_file(${CMAKE_CURRENT_LIST_DIR}/cmake/config.h.in
+               ${CMAKE_CURRENT_BINARY_DIR}/src/webp/config.h @ONLY)
+add_definitions(-DHAVE_CONFIG_H)
+
+# Set the version numbers.
+macro(set_version FILE TARGET_NAME NAME_IN_MAKEFILE)
+  file(READ ${CMAKE_CURRENT_SOURCE_DIR}/${FILE} SOURCE_FILE)
+  string(REGEX MATCH
+               "${NAME_IN_MAKEFILE}_la_LDFLAGS[^\n]* -version-info [0-9:]+" TMP
+               ${SOURCE_FILE})
+  string(REGEX MATCH "[0-9:]+" TMP ${TMP})
+  string(REGEX REPLACE ":" " " LT_VERSION ${TMP})
+
+  # See the libtool docs for more information:
+  # https://www.gnu.org/software/libtool/manual/libtool.html#Updating-version-info
+  #
+  # c=<current>, a=<age>, r=<revision>
+  #
+  # libtool generates a .so file as .so.[c-a].a.r, while -version-info c:r:a is
+  # passed to libtool.
+  #
+  # We set FULL = [c-a].a.r and MAJOR = [c-a].
+  separate_arguments(LT_VERSION)
+  list(GET LT_VERSION 0 LT_CURRENT)
+  list(GET LT_VERSION 1 LT_REVISION)
+  list(GET LT_VERSION 2 LT_AGE)
+  math(EXPR LT_CURRENT_MINUS_AGE "${LT_CURRENT} - ${LT_AGE}")
+
+  set_target_properties(
+    ${TARGET_NAME}
+    PROPERTIES VERSION ${LT_CURRENT_MINUS_AGE}.${LT_AGE}.${LT_REVISION}
+               SOVERSION ${LT_CURRENT_MINUS_AGE})
+  if(APPLE)
+    # For compatibility, set MACHO_COMPATIBILITY_VERSION and
+    # MACHO_CURRENT_VERSION to match libtool. These properties were introduced
+    # in 3.17:
+    # https://cmake.org/cmake/help/latest/prop_tgt/MACHO_COMPATIBILITY_VERSION.html
+    math(EXPR LIBWEBP_MACHO_COMPATIBILITY_VERSION "${LT_CURRENT} + 1")
+    set_target_properties(
+      ${TARGET_NAME}
+      PROPERTIES MACHO_COMPATIBILITY_VERSION
+                 ${LIBWEBP_MACHO_COMPATIBILITY_VERSION}
+                 MACHO_CURRENT_VERSION
+                 ${LIBWEBP_MACHO_COMPATIBILITY_VERSION}.${LT_REVISION})
+  endif()
+endmacro()
+
+# ##############################################################################
 # Build the webpdecoder library.
+
+# Creates a source file with an unused stub function in $CMAKE_BINARY_DIR and
+# adds it to the specified target. Currently used only with Xcode.
+#
+# See also:
+# https://cmake.org/cmake/help/v3.18/command/add_library.html#object-libraries
+# "Some native build systems (such as Xcode) may not like targets that have only
+# object files, so consider adding at least one real source file to any target
+# that references $<TARGET_OBJECTS:objlib>."
+function(libwebp_add_stub_file TARGET)
+  set(stub_source_dir "${CMAKE_BINARY_DIR}")
+  set(stub_source_file "${stub_source_dir}/libwebp_${TARGET}_stub.c")
+  set(stub_source_code
+      "// Generated file. DO NOT EDIT!\n"
+      "// C source file created for target ${TARGET}.\n"
+      "void libwebp_${TARGET}_stub_function(void)\;\n"
+      "void libwebp_${TARGET}_stub_function(void) {}\n")
+  file(WRITE "${stub_source_file}" ${stub_source_code})
+
+  target_sources(${TARGET} PRIVATE ${stub_source_file})
+endfunction()
+
+parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/sharpyuv "WEBP_SHARPYUV_SRCS" "")
+add_library(sharpyuv ${WEBP_SHARPYUV_SRCS})
+target_link_libraries(sharpyuv ${SHARPYUV_DEP_LIBRARIES})
+set_version(sharpyuv/Makefile.am sharpyuv sharpyuv)
+target_include_directories(
+  sharpyuv PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
+                   ${CMAKE_CURRENT_SOURCE_DIR}/src)
+set_target_properties(
+  sharpyuv
+  PROPERTIES PUBLIC_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/sharpyuv/sharpyuv.h;\
+${CMAKE_CURRENT_SOURCE_DIR}/sharpyuv/sharpyuv_csp.h")
+configure_pkg_config("sharpyuv/libsharpyuv.pc")
+install(
+  TARGETS sharpyuv
+  EXPORT ${PROJECT_NAME}Targets
+  PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/webp/sharpyuv
+  INCLUDES
+  DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+  ${CMAKE_INSTALL_INCLUDEDIR}/webp
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+
 if(MSVC)
   # avoid security warnings for e.g., fopen() used in the examples.
   add_definitions(-D_CRT_SECURE_NO_WARNINGS)
 else()
-  add_definitions(-Wall)
+  add_compile_options(-Wall)
 endif()
-include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${WEBP_DEP_INCLUDE_DIRS})
+include_directories(${WEBP_DEP_INCLUDE_DIRS})
 add_library(webpdecode OBJECT ${WEBP_DEC_SRCS})
+target_include_directories(webpdecode PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
+                                              ${CMAKE_CURRENT_SOURCE_DIR})
 add_library(webpdspdecode OBJECT ${WEBP_DSP_COMMON_SRCS} ${WEBP_DSP_DEC_SRCS})
+target_include_directories(webpdspdecode PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
+                                                 ${CMAKE_CURRENT_SOURCE_DIR})
 add_library(webputilsdecode OBJECT ${WEBP_UTILS_COMMON_SRCS}
-  ${WEBP_UTILS_DEC_SRCS})
-add_library(webpdecoder $<TARGET_OBJECTS:webpdecode>
-  $<TARGET_OBJECTS:webpdspdecode> $<TARGET_OBJECTS:webputilsdecode>)
+                                   ${WEBP_UTILS_DEC_SRCS})
+target_include_directories(webputilsdecode PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
+                                                   ${CMAKE_CURRENT_SOURCE_DIR})
+add_library(
+  webpdecoder $<TARGET_OBJECTS:webpdecode> $<TARGET_OBJECTS:webpdspdecode>
+              $<TARGET_OBJECTS:webputilsdecode>)
+if(XCODE)
+  libwebp_add_stub_file(webpdecoder)
+endif()
 target_link_libraries(webpdecoder ${WEBP_DEP_LIBRARIES})
+target_include_directories(
+  webpdecoder PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
+  INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+            $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
+set_target_properties(
+  webpdecoder
+  PROPERTIES PUBLIC_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/src/webp/decode.h;\
+${CMAKE_CURRENT_SOURCE_DIR}/src/webp/types.h")
+
+configure_pkg_config("src/libwebpdecoder.pc")
 
 # Build the webp library.
 add_library(webpencode OBJECT ${WEBP_ENC_SRCS})
+target_include_directories(
+  webpencode PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
+                     ${CMAKE_CURRENT_SOURCE_DIR}/src)
 add_library(webpdsp OBJECT ${WEBP_DSP_COMMON_SRCS} ${WEBP_DSP_DEC_SRCS}
-  ${WEBP_DSP_ENC_SRCS})
+                           ${WEBP_DSP_ENC_SRCS})
+target_include_directories(webpdsp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
+                                           ${CMAKE_CURRENT_SOURCE_DIR})
 add_library(webputils OBJECT ${WEBP_UTILS_COMMON_SRCS} ${WEBP_UTILS_DEC_SRCS}
-  ${WEBP_UTILS_ENC_SRCS})
+                             ${WEBP_UTILS_ENC_SRCS})
+target_include_directories(webputils PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
+                                             ${CMAKE_CURRENT_SOURCE_DIR})
 add_library(webp $<TARGET_OBJECTS:webpdecode> $<TARGET_OBJECTS:webpdsp>
-  $<TARGET_OBJECTS:webpencode> $<TARGET_OBJECTS:webputils>)
+                 $<TARGET_OBJECTS:webpencode> $<TARGET_OBJECTS:webputils>)
+target_link_libraries(webp sharpyuv)
+if(XCODE)
+  libwebp_add_stub_file(webp)
+endif()
 target_link_libraries(webp ${WEBP_DEP_LIBRARIES})
+target_include_directories(
+  webp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}
+  PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
+         $<INSTALL_INTERFACE:include>)
+set_target_properties(
+  webp
+  PROPERTIES PUBLIC_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/src/webp/decode.h;\
+${CMAKE_CURRENT_SOURCE_DIR}/src/webp/encode.h;\
+${CMAKE_CURRENT_SOURCE_DIR}/src/webp/types.h")
 
-# Make sure the OBJECT libraries are built with position independent code
-# (it is not ON by default).
-set_target_properties(webpdecode webpdspdecode webputilsdecode
-  webpencode webpdsp webputils PROPERTIES POSITION_INDEPENDENT_CODE ON)
+# Make sure the OBJECT libraries are built with position independent code (it is
+# not ON by default).
+set_target_properties(webpdecode webpdspdecode webputilsdecode webpencode
+                      webpdsp webputils PROPERTIES POSITION_INDEPENDENT_CODE ON)
+configure_pkg_config("src/libwebp.pc")
 
 # Build the webp demux library.
 add_library(webpdemux ${WEBP_DEMUX_SRCS})
 target_link_libraries(webpdemux webp)
+target_include_directories(
+  webpdemux PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}
+  PUBLIC $<INSTALL_INTERFACE:include>)
+set_target_properties(
+  webpdemux
+  PROPERTIES
+    PUBLIC_HEADER
+    "${CMAKE_CURRENT_SOURCE_DIR}/src/webp/decode.h;\
+${CMAKE_CURRENT_SOURCE_DIR}/src/webp/demux.h;\
+${CMAKE_CURRENT_SOURCE_DIR}/src/webp/mux_types.h;\
+${CMAKE_CURRENT_SOURCE_DIR}/src/webp/types.h")
 
-# Set the version numbers.
-function(parse_version FILE NAME VAR)
-  file(READ ${CMAKE_CURRENT_SOURCE_DIR}/src/${FILE} SOURCE_FILE)
-  string(REGEX MATCH "${NAME}_la_LDFLAGS[^\n]* -version-info [0-9:]+" TMP
-    ${SOURCE_FILE})
-  string(REGEX MATCH "[0-9:]+" TMP ${TMP})
-  string(REGEX REPLACE ":" "." VERSION ${TMP})
-  set(${VAR} "${VERSION}" PARENT_SCOPE)
-endfunction()
-parse_version(Makefile.am webp WEBP_WEBP_SOVERSION)
-set_target_properties(webp PROPERTIES VERSION ${PACKAGE_VERSION}
-  SOVERSION ${WEBP_WEBP_SOVERSION})
-parse_version(Makefile.am webpdecoder WEBP_DECODER_SOVERSION)
-set_target_properties(webpdecoder PROPERTIES VERSION ${PACKAGE_VERSION}
-  SOVERSION ${WEBP_DECODER_SOVERSION})
-parse_version(demux/Makefile.am webpdemux WEBP_DEMUX_SOVERSION)
-set_target_properties(webpdemux PROPERTIES VERSION ${PACKAGE_VERSION}
-  SOVERSION ${WEBP_DEMUX_SOVERSION})
+configure_pkg_config("src/demux/libwebpdemux.pc")
+
+set_version(src/Makefile.am webp webp)
+set_version(src/Makefile.am webpdecoder webpdecoder)
+set_version(src/demux/Makefile.am webpdemux webpdemux)
+file(READ ${CMAKE_CURRENT_SOURCE_DIR}/configure.ac CONFIGURE_FILE)
+string(REGEX MATCH "AC_INIT\\([^\n]*\\[[0-9\\.]+\\]" TMP ${CONFIGURE_FILE})
+string(REGEX MATCH "[0-9\\.]+" PROJECT_VERSION ${TMP})
 
 # Define the libraries to install.
-set(INSTALLED_LIBRARIES webpdecoder webp webpdemux)
+list(APPEND INSTALLED_LIBRARIES webpdecoder webp webpdemux)
 
-### Deal with SIMD.
-# Change the compile flags for SIMD files we use.
+# Deal with SIMD. Change the compile flags for SIMD files we use.
 list(LENGTH WEBP_SIMD_FILES_TO_INCLUDE WEBP_SIMD_FILES_TO_INCLUDE_LENGTH)
 math(EXPR WEBP_SIMD_FILES_TO_INCLUDE_RANGE
-  "${WEBP_SIMD_FILES_TO_INCLUDE_LENGTH}-1"
-)
+     "${WEBP_SIMD_FILES_TO_INCLUDE_LENGTH}-1")
 
 foreach(I_FILE RANGE ${WEBP_SIMD_FILES_TO_INCLUDE_RANGE})
   list(GET WEBP_SIMD_FILES_TO_INCLUDE ${I_FILE} FILE)
   list(GET WEBP_SIMD_FLAGS_TO_INCLUDE ${I_FILE} SIMD_COMPILE_FLAG)
-  set_source_files_properties(${FILE} PROPERTIES
-    COMPILE_FLAGS ${SIMD_COMPILE_FLAG}
-  )
+  set_source_files_properties(${FILE} PROPERTIES COMPILE_FLAGS
+                                                 ${SIMD_COMPILE_FLAG})
 endforeach()
 
-# Build the executables if asked for.
-if(WEBP_BUILD_CWEBP OR WEBP_BUILD_DWEBP OR
-   WEBP_BUILD_GIF2WEBP OR WEBP_BUILD_IMG2WEBP OR WEBP_BUILD_WEBP_JS)
-  # Example utility library.
-  parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "EXAMPLEUTIL_SRCS"
-    "example_util_[^ ]*")
-  list(APPEND EXAMPLEUTIL_SRCS
-    ${CMAKE_CURRENT_SOURCE_DIR}/examples/stopwatch.h)
-  add_library(exampleutil ${EXAMPLEUTIL_SRCS})
+if(NOT WEBP_BUILD_LIBWEBPMUX)
+  set(WEBP_BUILD_GIF2WEBP OFF)
+  set(WEBP_BUILD_IMG2WEBP OFF)
+  set(WEBP_BUILD_WEBPMUX OFF)
+endif()
 
-  parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/imageio "IMAGEIOUTILS_SRCS"
-    "imageio_util_[^ ]*")
-  add_library(imageioutil ${IMAGEIOUTILS_SRCS})
+if(WEBP_BUILD_GIF2WEBP AND NOT GIF_FOUND)
+  set(WEBP_BUILD_GIF2WEBP OFF)
+endif()
+
+if(WEBP_BUILD_ANIM_UTILS AND NOT GIF_FOUND)
+  set(WEBP_BUILD_ANIM_UTILS OFF)
+endif()
+
+# Build the executables if asked for.
+if(WEBP_BUILD_ANIM_UTILS
+   OR WEBP_BUILD_CWEBP
+   OR WEBP_BUILD_DWEBP
+   OR WEBP_BUILD_GIF2WEBP
+   OR WEBP_BUILD_IMG2WEBP
+   OR WEBP_BUILD_VWEBP
+   OR WEBP_BUILD_WEBPMUX
+   OR WEBP_BUILD_WEBPINFO)
+  # Example utility library.
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "EXAMPLEUTIL_SRCS"
+                    "example_util_[^ ]*")
+  list(APPEND EXAMPLEUTIL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/examples/stopwatch.h)
+  add_library(exampleutil STATIC ${EXAMPLEUTIL_SRCS})
+  target_include_directories(
+    exampleutil PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>)
+
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/imageio "IMAGEIOUTILS_SRCS"
+                    "imageio_util_[^ ]*")
+  add_library(imageioutil STATIC ${IMAGEIOUTILS_SRCS})
   target_link_libraries(imageioutil webp)
+  target_link_libraries(exampleutil imageioutil)
 
   # Image-decoding utility library.
-  parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/imageio "IMAGEDEC_SRCS"
-    "imagedec_[^ ]*")
-  add_library(imagedec ${IMAGEDEC_SRCS})
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/imageio "IMAGEDEC_SRCS"
+                    "imagedec_[^ ]*")
+  add_library(imagedec STATIC ${IMAGEDEC_SRCS})
   target_link_libraries(imagedec imageioutil webpdemux webp
-    ${WEBP_DEP_IMG_LIBRARIES})
+                        ${WEBP_DEP_IMG_LIBRARIES})
 
   # Image-encoding utility library.
-  parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/imageio "IMAGEENC_SRCS"
-    "imageenc_[^ ]*")
-  add_library(imageenc ${IMAGEENC_SRCS})
-  target_link_libraries(imageenc webp)
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/imageio "IMAGEENC_SRCS"
+                    "imageenc_[^ ]*")
+  add_library(imageenc STATIC ${IMAGEENC_SRCS})
+  target_link_libraries(imageenc imageioutil webp)
 
-  set_property(TARGET exampleutil imageioutil imagedec imageenc
-    PROPERTY INCLUDE_DIRECTORIES
-    ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_BINARY_DIR}/src)
+  set_property(
+    TARGET exampleutil imageioutil imagedec imageenc
+    PROPERTY INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/src
+             ${CMAKE_CURRENT_BINARY_DIR}/src)
 endif()
 
 if(WEBP_BUILD_DWEBP)
   # dwebp
-  include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS})
-  parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "DWEBP_SRCS"
-    "dwebp")
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "DWEBP_SRCS" "dwebp")
   add_executable(dwebp ${DWEBP_SRCS})
-  target_link_libraries(dwebp exampleutil imagedec imageenc webpdecoder)
-  install(TARGETS dwebp RUNTIME DESTINATION bin)
-  set_property(TARGET dwebp PROPERTY INCLUDE_DIRECTORIES
-    ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_BINARY_DIR}/src)
+  target_link_libraries(dwebp exampleutil imagedec imageenc)
+  target_include_directories(dwebp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
+  install(TARGETS dwebp RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
 endif()
 
 if(WEBP_BUILD_CWEBP)
   # cwebp
-  include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS})
-  parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "CWEBP_SRCS"
-    "cwebp")
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "CWEBP_SRCS" "cwebp")
   add_executable(cwebp ${CWEBP_SRCS})
   target_link_libraries(cwebp exampleutil imagedec webp)
-  install(TARGETS cwebp RUNTIME DESTINATION bin)
-  set_property(TARGET cwebp PROPERTY INCLUDE_DIRECTORIES
-    ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_BINARY_DIR}/src)
+  target_include_directories(cwebp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src
+                                           ${CMAKE_CURRENT_SOURCE_DIR})
+  install(TARGETS cwebp RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
 endif()
 
-if(WEBP_BUILD_GIF2WEBP AND NOT GIF_FOUND)
-  unset(WEBP_BUILD_GIF2WEBP CACHE)
-endif()
-
-if(WEBP_BUILD_GIF2WEBP OR WEBP_BUILD_IMG2WEBP)
-  parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/src/mux "WEBP_MUX_SRCS"
-    "")
-  add_library(webpmux ${WEBP_MUX_SRCS})
-  target_link_libraries(webpmux webp)
-  parse_version(mux/Makefile.am webpmux WEBP_MUX_SOVERSION)
-  set_target_properties(webpmux PROPERTIES VERSION ${PACKAGE_VERSION}
-    SOVERSION ${WEBP_MUX_SOVERSION})
-  list(APPEND INSTALLED_LIBRARIES webpmux)
+if(WEBP_BUILD_LIBWEBPMUX)
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/src/mux "WEBP_MUX_SRCS" "")
+  add_library(libwebpmux ${WEBP_MUX_SRCS})
+  target_link_libraries(libwebpmux webp)
+  target_include_directories(libwebpmux PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
+                                                ${CMAKE_CURRENT_SOURCE_DIR})
+  set_version(src/mux/Makefile.am libwebpmux webpmux)
+  set_target_properties(
+    libwebpmux
+    PROPERTIES PUBLIC_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/src/webp/mux.h;\
+${CMAKE_CURRENT_SOURCE_DIR}/src/webp/mux_types.h;\
+${CMAKE_CURRENT_SOURCE_DIR}/src/webp/types.h;")
+  set_target_properties(libwebpmux PROPERTIES OUTPUT_NAME webpmux)
+  list(APPEND INSTALLED_LIBRARIES libwebpmux)
+  configure_pkg_config("src/mux/libwebpmux.pc")
 endif()
 
 if(WEBP_BUILD_GIF2WEBP)
   # gif2webp
   include_directories(${WEBP_DEP_GIF_INCLUDE_DIRS})
-  parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "GIF2WEBP_SRCS"
-    "gif2webp")
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "GIF2WEBP_SRCS"
+                    "gif2webp")
   add_executable(gif2webp ${GIF2WEBP_SRCS})
-  target_link_libraries(gif2webp exampleutil imageioutil webp webpmux
-    ${WEBP_DEP_GIF_LIBRARIES})
-  install(TARGETS gif2webp RUNTIME DESTINATION bin)
-  set_property(TARGET gif2webp PROPERTY INCLUDE_DIRECTORIES
-    ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_BINARY_DIR}/src)
+  target_link_libraries(gif2webp exampleutil imageioutil webp libwebpmux
+                        ${WEBP_DEP_GIF_LIBRARIES})
+  target_include_directories(gif2webp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
+  install(TARGETS gif2webp RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
 endif()
 
 if(WEBP_BUILD_IMG2WEBP)
   # img2webp
   include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS})
-  parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "IMG2WEBP_SRCS"
-    "img2webp")
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "IMG2WEBP_SRCS"
+                    "img2webp")
   add_executable(img2webp ${IMG2WEBP_SRCS})
-  target_link_libraries(img2webp exampleutil imagedec imageioutil webp webpmux)
-  install(TARGETS img2webp RUNTIME DESTINATION bin)
-  set_property(TARGET img2webp PROPERTY INCLUDE_DIRECTORIES
-    ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_BINARY_DIR}/src)
+  target_link_libraries(img2webp exampleutil imagedec imageioutil webp
+                        libwebpmux)
+  target_include_directories(img2webp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src
+                                              ${CMAKE_CURRENT_SOURCE_DIR})
+  install(TARGETS img2webp RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
 endif()
 
-if (WEBP_BUILD_WEBPINFO)
+if(WEBP_BUILD_VWEBP)
+  # vwebp
+  find_package(GLUT)
+  if(GLUT_FOUND)
+    include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS})
+    parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "VWEBP_SRCS" "vwebp")
+    add_executable(vwebp ${VWEBP_SRCS})
+    target_link_libraries(
+      vwebp
+      ${OPENGL_LIBRARIES}
+      exampleutil
+      GLUT::GLUT
+      imageioutil
+      webp
+      webpdemux)
+    target_include_directories(
+      vwebp PRIVATE ${GLUT_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/src
+                    ${OPENGL_INCLUDE_DIR})
+    install(TARGETS vwebp RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+    if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
+      check_c_compiler_flag("-Wno-deprecated-declarations" HAS_NO_DEPRECATED)
+      if(HAS_NO_DEPRECATED)
+        target_compile_options(vwebp PRIVATE "-Wno-deprecated-declarations")
+      endif()
+    endif()
+  endif()
+endif()
+
+if(WEBP_BUILD_WEBPINFO)
   # webpinfo
   include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS})
-  parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "WEBPINFO_SRCS"
-    "webpinfo")
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "WEBPINFO_SRCS"
+                    "webpinfo")
   add_executable(webpinfo ${WEBPINFO_SRCS})
   target_link_libraries(webpinfo exampleutil imageioutil)
-  install(TARGETS webpinfo RUNTIME DESTINATION bin)
-  set_property(TARGET webpinfo PROPERTY INCLUDE_DIRECTORIES
-    ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_BINARY_DIR}/src)
+  target_include_directories(webpinfo PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src
+                                              ${CMAKE_CURRENT_SOURCE_DIR}/src)
+  install(TARGETS webpinfo RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+endif()
+
+if(WEBP_BUILD_WEBPMUX)
+  # webpmux
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "WEBPMUX_SRCS"
+                    "webpmux")
+  add_executable(webpmux ${WEBPMUX_SRCS})
+  target_link_libraries(webpmux exampleutil imageioutil libwebpmux webp)
+  target_include_directories(webpmux PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
+  install(TARGETS webpmux RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+endif()
+
+if(WEBP_BUILD_EXTRAS)
+  set(EXTRAS_MAKEFILE "${CMAKE_CURRENT_SOURCE_DIR}/extras")
+  parse_makefile_am(${EXTRAS_MAKEFILE} "WEBP_EXTRAS_SRCS" "libwebpextras_la")
+  parse_makefile_am(${EXTRAS_MAKEFILE} "GET_DISTO_SRCS" "get_disto")
+  parse_makefile_am(${EXTRAS_MAKEFILE} "WEBP_QUALITY_SRCS" "webp_quality")
+  parse_makefile_am(${EXTRAS_MAKEFILE} "VWEBP_SDL_SRCS" "vwebp_sdl")
+
+  # libextras
+  add_library(extras STATIC ${WEBP_EXTRAS_SRCS})
+  target_include_directories(
+    extras PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
+                   ${CMAKE_CURRENT_SOURCE_DIR}/src)
+
+  # get_disto
+  add_executable(get_disto ${GET_DISTO_SRCS})
+  target_link_libraries(get_disto imagedec)
+  target_include_directories(get_disto PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+                                               ${CMAKE_CURRENT_BINARY_DIR}/src)
+
+  # webp_quality
+  add_executable(webp_quality ${WEBP_QUALITY_SRCS})
+  target_link_libraries(webp_quality exampleutil imagedec extras)
+  target_include_directories(webp_quality PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+                                                  ${CMAKE_CURRENT_BINARY_DIR})
+
+  # vwebp_sdl
+  find_package(SDL)
+  if(WEBP_BUILD_VWEBP AND SDL_FOUND)
+    add_executable(vwebp_sdl ${VWEBP_SDL_SRCS})
+    target_link_libraries(vwebp_sdl ${SDL_LIBRARY} imageioutil webp)
+    target_include_directories(
+      vwebp_sdl PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}
+                        ${CMAKE_CURRENT_BINARY_DIR}/src ${SDL_INCLUDE_DIR})
+    set(WEBP_HAVE_SDL 1)
+    target_compile_definitions(vwebp_sdl PUBLIC WEBP_HAVE_SDL)
+  endif()
 endif()
 
 if(WEBP_BUILD_WEBP_JS)
-  # JavaScript version
-  add_executable(webp_js
-                 ${CMAKE_CURRENT_SOURCE_DIR}/extras/webp_to_sdl.c)
-  target_link_libraries(webp_js webpdecoder SDL)
-  set(WEBP_HAVE_SDL 1)
-  set_target_properties(webp_js PROPERTIES LINK_FLAGS
-      "-s EXPORTED_FUNCTIONS='[\"_WebpToSDL\"]' -s INVOKE_RUN=0 \
-       -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"cwrap\"]'")
-  set_target_properties(webp_js PROPERTIES OUTPUT_NAME webp)
-  target_compile_definitions(webp_js PUBLIC EMSCRIPTEN WEBP_HAVE_SDL)
+  # The default stack size changed from 5MB to 64KB in 3.1.27. See
+  # https://crbug.com/webp/614.
+  if(EMSCRIPTEN_VERSION VERSION_GREATER_EQUAL "3.1.27")
+    # TOTAL_STACK size was renamed to STACK_SIZE in 3.1.27. The old name was
+    # kept for compatibility, but prefer the new one in case it is removed in
+    # the future.
+    set(emscripten_stack_size "-sSTACK_SIZE=5MB")
+  else()
+    set(emscripten_stack_size "-sTOTAL_STACK=5MB")
+  endif()
+  # wasm2js does not support SIMD.
+  if(NOT WEBP_ENABLE_SIMD)
+    # JavaScript version
+    add_executable(webp_js ${CMAKE_CURRENT_SOURCE_DIR}/extras/webp_to_sdl.c)
+    target_link_libraries(webp_js webpdecoder SDL)
+    target_include_directories(webp_js PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
+    set(WEBP_HAVE_SDL 1)
+    set_target_properties(
+      webp_js
+      PROPERTIES LINK_FLAGS "-sWASM=0 ${emscripten_stack_size} \
+         -sEXPORTED_FUNCTIONS=_WebPToSDL -sINVOKE_RUN=0 \
+         -sEXPORTED_RUNTIME_METHODS=cwrap")
+    set_target_properties(webp_js PROPERTIES OUTPUT_NAME webp)
+    target_compile_definitions(webp_js PUBLIC EMSCRIPTEN WEBP_HAVE_SDL)
+  endif()
 
   # WASM version
-  add_executable(webp_wasm
-                 ${CMAKE_CURRENT_SOURCE_DIR}/extras/webp_to_sdl.c)
+  add_executable(webp_wasm ${CMAKE_CURRENT_SOURCE_DIR}/extras/webp_to_sdl.c)
   target_link_libraries(webp_wasm webpdecoder SDL)
-  set_target_properties(webp_wasm PROPERTIES LINK_FLAGS
-      "-s WASM=1 -s 'BINARYEN_METHOD=\"native-wasm\"' \
-       -s EXPORTED_FUNCTIONS='[\"_WebpToSDL\"]' -s INVOKE_RUN=0 \
-       -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"cwrap\"]'")
+  target_include_directories(webp_wasm PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
+  set_target_properties(
+    webp_wasm
+    PROPERTIES LINK_FLAGS "-sWASM=1 ${emscripten_stack_size} \
+       -sEXPORTED_FUNCTIONS=_WebPToSDL -sINVOKE_RUN=0 \
+       -sEXPORTED_RUNTIME_METHODS=cwrap")
   target_compile_definitions(webp_wasm PUBLIC EMSCRIPTEN WEBP_HAVE_SDL)
 
-  target_compile_definitions(webpdecoder PUBLIC EMSCRIPTEN)
+  target_compile_definitions(webpdspdecode PUBLIC EMSCRIPTEN)
 endif()
 
-# Generate the config.h file.
-configure_file(${CMAKE_CURRENT_LIST_DIR}/cmake/config.h.in
-  ${CMAKE_CURRENT_BINARY_DIR}/src/webp/config.h)
-add_definitions(-DHAVE_CONFIG_H)
-# The webp folder is included as we reference config.h as
-# ../webp/config.h or webp/config.h
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
+if(WEBP_BUILD_ANIM_UTILS)
+  # anim_diff
+  include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS} ${WEBP_DEP_GIF_INCLUDE_DIRS})
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "ANIM_DIFF_SRCS"
+                    "anim_diff")
+  add_executable(anim_diff ${ANIM_DIFF_SRCS})
+  target_link_libraries(
+    anim_diff
+    exampleutil
+    imagedec
+    imageenc
+    imageioutil
+    webp
+    webpdemux
+    ${WEBP_DEP_GIF_LIBRARIES})
+  target_include_directories(anim_diff PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
+
+  # anim_dump
+  include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS} ${WEBP_DEP_GIF_INCLUDE_DIRS})
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "ANIM_DUMP_SRCS"
+                    "anim_dump")
+  add_executable(anim_dump ${ANIM_DUMP_SRCS})
+  target_link_libraries(
+    anim_dump
+    exampleutil
+    imagedec
+    imageenc
+    imageioutil
+    webp
+    webpdemux
+    ${WEBP_DEP_GIF_LIBRARIES})
+  target_include_directories(anim_dump PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
+endif()
 
 # Install the different headers and libraries.
-install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/webp/decode.h
-              ${CMAKE_CURRENT_SOURCE_DIR}/src/webp/demux.h
-              ${CMAKE_CURRENT_SOURCE_DIR}/src/webp/encode.h
-              ${CMAKE_CURRENT_SOURCE_DIR}/src/webp/mux.h
-              ${CMAKE_CURRENT_SOURCE_DIR}/src/webp/mux_types.h
-              ${CMAKE_CURRENT_SOURCE_DIR}/src/webp/types.h
-        DESTINATION include/webp)
-install(TARGETS ${INSTALLED_LIBRARIES}
-        LIBRARY DESTINATION lib
-        ARCHIVE DESTINATION lib)
+install(
+  TARGETS ${INSTALLED_LIBRARIES}
+  EXPORT ${PROJECT_NAME}Targets
+  PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/webp
+  INCLUDES
+  DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+set(ConfigPackageLocation ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/cmake/)
+install(EXPORT ${PROJECT_NAME}Targets NAMESPACE ${PROJECT_NAME}::
+        DESTINATION ${ConfigPackageLocation})
 
 # Create the CMake version file.
 include(CMakePackageConfigHelpers)
 write_basic_package_version_file(
   "${CMAKE_CURRENT_BINARY_DIR}/WebPConfigVersion.cmake"
-  VERSION ${PACKAGE_VERSION}
-  COMPATIBILITY AnyNewerVersion
-)
+  VERSION ${PACKAGE_VERSION} COMPATIBILITY AnyNewerVersion)
 
 # Create the Config file.
 include(CMakePackageConfigHelpers)
-set(ConfigPackageLocation share/WebP/cmake/)
+# Fix libwebpmux reference. The target name libwebpmux is used for compatibility
+# purposes, but the library mentioned in WebPConfig.cmake should be the
+# unprefixed version. Note string(...) can be replaced with list(TRANSFORM ...)
+# if cmake_minimum_required is >= 3.12.
+string(REGEX REPLACE "libwebpmux" "webpmux" INSTALLED_LIBRARIES
+                     "${INSTALLED_LIBRARIES}")
+
+if(MSVC)
+  # For compatibility with nmake, MSVC builds use a custom prefix (lib) that
+  # needs to be included in the library name.
+  string(REGEX REPLACE "[A-Za-z0-9_]+" "${CMAKE_STATIC_LIBRARY_PREFIX}\\0"
+                       INSTALLED_LIBRARIES "${INSTALLED_LIBRARIES}")
+endif()
+
 configure_package_config_file(
   ${CMAKE_CURRENT_SOURCE_DIR}/cmake/WebPConfig.cmake.in
   ${CMAKE_CURRENT_BINARY_DIR}/WebPConfig.cmake
   INSTALL_DESTINATION ${ConfigPackageLocation}
-)
+  PATH_VARS CMAKE_INSTALL_INCLUDEDIR)
 
 # Install the generated CMake files.
-install(
-  FILES "${CMAKE_CURRENT_BINARY_DIR}/WebPConfigVersion.cmake"
-        "${CMAKE_CURRENT_BINARY_DIR}/WebPConfig.cmake"
-  DESTINATION ${ConfigPackageLocation}
-)
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/WebPConfigVersion.cmake"
+              "${CMAKE_CURRENT_BINARY_DIR}/WebPConfig.cmake"
+        DESTINATION ${ConfigPackageLocation})
 
 # Install the man pages.
-set(MAN_PAGES cwebp.1 dwebp.1 gif2webp.1 img2webp.1 vwebp.1 webpmux.1
-  webpinfo.1)
-set(EXEC_BUILDS "CWEBP" "DWEBP" "GIF2WEBP" "IMG2WEBP" "VWEBP" "WEBPMUX"
-  "WEBPINFO")
+set(MAN_PAGES
+    cwebp.1
+    dwebp.1
+    gif2webp.1
+    img2webp.1
+    vwebp.1
+    webpmux.1
+    webpinfo.1)
+set(EXEC_BUILDS
+    "CWEBP"
+    "DWEBP"
+    "GIF2WEBP"
+    "IMG2WEBP"
+    "VWEBP"
+    "WEBPMUX"
+    "WEBPINFO")
 list(LENGTH MAN_PAGES MAN_PAGES_LENGTH)
 math(EXPR MAN_PAGES_RANGE "${MAN_PAGES_LENGTH} - 1")
 
@@ -364,8 +797,6 @@
   if(WEBP_BUILD_${EXEC_BUILD})
     list(GET MAN_PAGES ${I_MAN} MAN_PAGE)
     install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/man/${MAN_PAGE}
-      DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1
-      COMPONENT doc
-    )
+            DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 COMPONENT doc)
   endif()
 endforeach()
diff --git a/third_party/libwebp/CONTRIBUTING.md b/third_party/libwebp/CONTRIBUTING.md
new file mode 100644
index 0000000..9540f34
--- /dev/null
+++ b/third_party/libwebp/CONTRIBUTING.md
@@ -0,0 +1,78 @@
+# How to Contribute
+
+We'd love to accept your patches and contributions to this project. There are
+just a few small guidelines you need to follow.
+
+## Contributor License Agreement
+
+Contributions to this project must be accompanied by a Contributor License
+Agreement. You (or your employer) retain the copyright to your contribution;
+this simply gives us permission to use and redistribute your contributions as
+part of the project. Head over to <https://cla.developers.google.com/> to see
+your current agreements on file or to sign a new one.
+
+You generally only need to submit a CLA once, so if you've already submitted one
+(even if it was for a different project), you probably don't need to do it
+again.
+
+## Code reviews
+
+All submissions, including submissions by project members, require review. We
+use a [Gerrit](https://www.gerritcodereview.com) instance hosted at
+https://chromium-review.googlesource.com for this purpose.
+
+## Sending patches
+
+The basic git workflow for modifying libwebp code and sending for review is:
+
+1.  Get the latest version of the repository locally:
+
+    ```sh
+    git clone https://chromium.googlesource.com/webm/libwebp && cd libwebp
+    ```
+
+2.  Copy the commit-msg script into ./git/hooks (this will add an ID to all of
+    your commits):
+
+    ```sh
+    curl -Lo .git/hooks/commit-msg https://chromium-review.googlesource.com/tools/hooks/commit-msg && chmod u+x .git/hooks/commit-msg
+    ```
+
+3.  Modify the local copy of libwebp. Make sure the code
+    [builds successfully](https://chromium.googlesource.com/webm/libwebp/+/HEAD/doc/building.md#cmake).
+
+4.  Choose a short and representative commit message:
+
+    ```sh
+    git commit -a -m "Set commit message here"
+    ```
+
+5.  Send the patch for review:
+
+    ```sh
+    git push https://chromium-review.googlesource.com/webm/libwebp HEAD:refs/for/main
+    ```
+
+    Go to https://chromium-review.googlesource.com to view your patch and
+    request a review from the maintainers.
+
+See the
+[WebM Project page](https://www.webmproject.org/code/contribute/submitting-patches/)
+for additional details.
+
+## Code Style
+
+The C code style is based on the
+[Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) and
+`clang-format --style=Google`, though this project doesn't use the tool to
+enforce the formatting.
+
+CMake files are formatted with
+[cmake-format](https://cmake-format.readthedocs.io/en/latest/). `cmake-format
+-i` can be used to format individual files, it will use the settings from
+`.cmake-format.py`.
+
+## Community Guidelines
+
+This project follows
+[Google's Open Source Community Guidelines](https://opensource.google.com/conduct/).
diff --git a/third_party/libwebp/ChangeLog b/third_party/libwebp/ChangeLog
index 9fd9acf..5e85875 100644
--- a/third_party/libwebp/ChangeLog
+++ b/third_party/libwebp/ChangeLog
@@ -1,23 +1,793 @@
+e1adea50 update NEWS
+43393320 enc/*: normalize WebPEncodingSetError() calls
+287fdefe enc/*: add missing WebPEncodingSetError() calls
+c3bd7cff EncodeAlphaInternal: add missing error check
+d49cfbb3 vp8l_enc,WriteImage: add missing error check
+2e5a9ec3 muxread,MuxImageParse: add missing error checks
+ebb6f949 cmake,emscripten: explicitly set stack size
+59a2b1f9 WebPDecodeYUV: check u/v/stride/uv_stride ptrs
+8e965ccb Call png_get_channels() to see if image has alpha
+7f0a3419 update ChangeLog (tag: v1.3.1-rc1)
+bab7efbe update NEWS
+7138bf8f bump version to 1.3.1
+435b4ded update AUTHORS
+47351229 update .mailmap
+ff6c7f4e CONTRIBUTING.md: add C style / cmake-format notes
+dd530437 add .cmake-format.py
+adbe2cb1 cmake,cosmetics: apply cmake-format
+15b36508 doc/webp-container-spec: rm future codec comment
+c369c4bf doc/webp-lossless-bitstream-spec: improve link text
+1de35f47 doc/webp-container-spec: don't use 'currently'
+bb06a16e doc/webp-container-spec: prefer present tense
+9f38b71e doc/webp-lossless-bitstream-spec: prefer present tense
+7acb6b82 doc/webp-container-spec: avoid i.e. & e.g.
+4967e7cd doc/webp-lossless-bitstream-spec: avoid i.e. & e.g.
+e3366659 Merge "Do not find_package image libraries if not needed." into main
+428588ef clarify single leaf node trees and use of canonical prefix coding
+709ec152 Do not find_package image libraries if not needed.
+8dd80ef8 fuzz_utils.h: lower kFuzzPxLimit w/ASan
+8f187b9f Clean message calls in CMake
+cba30078 WebPConfig.cmake.in: use calculated include path
+6cf9a76a Merge "webp-lossless-bitstream-spec: remove use of 'dynamics'" into main
+740943b2 Merge "Specialize and optimize ITransform_SSE2 using do_two" into main
+2d547e24 Compare kFuzzPxLimit to max_num_operations
+ac42dde1 Specialize and optimize ITransform_SSE2 using do_two
+17e0ef1d webp-lossless-bitstream-spec: remove use of 'dynamics'
+ed274371 neon.h,cosmetics: clear a couple lint warnings
+3fb82947 cpu.h,cosmetics: segment defines
+0c496a4f cpu.h: add WEBP_AARCH64
+8151f388 move VP8GetCPUInfo declaration to cpu.c
+916548c2 Make kFuzzPxLimit sanitizer dependent
+4070b271 advanced_api_fuzzer: reduce scaling limit
+761f49c3 Merge "webp-lossless-bitstream-spec: add missing bits to ABNF" into main
+84d04c48 webp-lossless-bitstream-spec: add missing bits to ABNF
+0696e1a7 advanced_api_fuzzer: reduce scaling limit
+93d88aa2 Merge "deps.cmake: remove unneeded header checks" into main
+118e0035 deps.cmake: remove unneeded header checks
+4c3d7018 webp-lossless-bitstream-spec: condense normal-prefix-code
+a6a09b32 webp-lossless-bitstream-spec: fix 2 code typos
+50ac4f7c Merge "cpu.h: enable NEON w/_M_ARM64EC" into main
+4b7d7b4f Add contribution instructions
+0afbd97b cpu.h: enable NEON w/_M_ARM64EC
+349f4353 Merge changes Ibd89e56b,Ic57e7f84,I89096614 into main
+8f7513b7 upsampling_neon.c: fix WEBP_SWAP_16BIT_CSP check
+cbf624b5 advanced_api_fuzzer: reduce scaling limit
+89edfdd1 Skip slow scaling in libwebp advanced_api_fuzzer
+859f19f7 Reduce libwebp advanced_api_fuzzer threshold
+a4f04835 Merge changes Ic389aaa2,I329ccd79 into main
+1275fac8 Makefile.vc: fix img2webp link w/dynamic cfg
+2fe27bb9 img2webp: normalize help output
+24bed3d9 cwebp: reflow -near_lossless help text
+0825faa4 img2webp: add -sharp_yuv/-near_lossless
+d64e6d7d Merge "PaletteSortModifiedZeng: fix leak on error" into main
+0e12a22d Merge "EncodeAlphaInternal: clear result->bw on error" into main
+0edbb6ea PaletteSortModifiedZeng: fix leak on error
+41ffe04e Merge "Update yapf style from "chromium" to "yapf"" into main
+2d9d9265 Update yapf style from "chromium" to "yapf"
+a486d800 EncodeAlphaInternal: clear result->bw on error
+1347a32d Skip big scaled advanced_api_fuzzer
+52b6f067 Fix scaling limit in advanced_api_fuzzer.c
+73618428 Limit scaling in libwebp advanced_api_fuzzer.c
+b54d21a0 Merge "CMakeLists.txt: allow CMAKE_INSTALL_RPATH to be set empty" into main
+31c28db5 libwebp{,demux,mux}.pc.in: Requires -> Requires.private
+d9a505ff CMakeLists.txt: allow CMAKE_INSTALL_RPATH to be set empty
+bdf33d03 Merge tag 'v1.3.0'
+b5577769 update ChangeLog (tag: v1.3.0-rc1, tag: v1.3.0)
+0ba77244 update NEWS
+e763eb1e bump version to 1.3.0
+2a8686fc update AUTHORS
+106a57c1 Merge "*/Android.mk: add a check for NDK_ROOT" into main
+c5e841c4 Merge "extras: WebpToSDL -> WebPToSDL" into main
+dbc30715 Merge "xcframeworkbuild.sh: bump MACOSX_CATALYST_MIN_VERSION" into main
+6fc1a9f9 */Android.mk: add a check for NDK_ROOT
+d3e151fc doc/api.md,webp_js/README.md: Webp -> WebP
+ed92a626 extras: WebpToSDL -> WebPToSDL
+6eb0189b xcframeworkbuild.sh: bump MACOSX_CATALYST_MIN_VERSION
+1d58575b CMake: align .pc variables with autoconf
+e5fe2cfc webp-lossless-bitstream-spec,cosmetics: reflow paragraphs
+0ceeeab9 webp-lossless-bitstream-spec: add amendment note
+607611cd Merge "webp-container-spec: normalize section title case" into main
+f853685e lossless: SUBTRACT_GREEN -> SUBTRACT_GREEN_TRANSFORM
+786497e4 webp-lossless-bitstream-spec: fix inv color txfm description
+c6ac672d webp-lossless-bitstream-spec: fix num_code_lengths check
+b5700efb webp-lossless-bitstream-spec,cosmetics: grammar/capitalization
+d8ed8c11 webp-container-spec: normalize section title case
+52ec0b8f Merge changes Ie975dbb5,Ifc8c93af,I6ca7c5d6,I2e8d66f5,I152477b8 into main
+5097ef62 webp-container-spec,cosmetics: grammar/capitalization
+e3ba2b1f webp-lossless-bitstream-spec,cosmetics: reflow abstract
+1e8e3ded webp-lossless-bitstream-spec: reword abstract re alpha
+017cb6fa webp-container-spec,cosmetics: normalize range syntax
+f6a4684b webp-lossless-bitstream-spec,cosmetics: normalize range syntax
+54ebd5a3 webp-lossless-bitstream-spec: limit dist map lut to 69 cols
+44741f9c webp-lossless-bitstream-spec: fix dist mapping example
+fad0ece7 pnmdec.c: use snprintf instead of sprintf
+3f73e8f7 sharpyuv: add SharpYuvGetVersion()
+ce2f2d66 SharpYuvConvert: fix a race on SharpYuvGetCPUInfo
+a458e308 sharpyuv_dsp.h: restore sharpyuv_cpu.h include
+9ba800a7 Merge changes Id72fbf3b,Ic59d23a2 into main
+979c0ebb sharpyuv: add SharpYuvGetCPUInfo
+8bab09a4 Merge "*.pc.in: rename lib_prefix to webp_libname_prefix" into main
+769387c5 cpu.c,cosmetics: fix a typo
+a02978c2 sharpyuv/Makefile.am+cmake: add missing -lm
+28aedcb9 *.pc.in: rename lib_prefix to webp_libname_prefix
+c42e6d5a configure.ac: export an empty lib_prefix variable
+dfc843aa Merge "*.pc.in: add lib prefix to lib names w/MSVC" into main
+2498209b *.pc.in: add lib prefix to lib names w/MSVC
+ac252b61 Merge "analysis_enc.c: fix a dead store warning" into main
+56944762 analysis_enc.c: fix a dead store warning
+d34f9b99 Merge "webp-lossless-bitstream-spec: convert BNF to ABNF" into main
+dc05b4db Merge changes I96bc063c,I45880467,If9e18e5a,I6ee938e4,I0a410b28, ... into main
+83270c7f webp-container-spec: add prose for rendering process
+73b19b64 webp-container-spec: note reserved fields MUST be ignored
+57101d3f webp-lossless-bitstream-spec: improve 'small' color table stmt
+dfd32e45 webp-container-spec: remove redundant sentence
+8a6185dd doc/webp-*: fix some punctuation, grammar
+72776530 webp-lossless-bitstream-spec: convert BNF to ABNF
+d992bb08 cmake: rename cpufeatures target to cpufeatures-webp
+3ed2b275 webp-container-spec: clarify background color note
+951c292d webp-container-spec: come too late -> out of order
+902dd787 webp-container-spec: prefer hex literals
+a8f6b5ee webp-container-spec: change SHOULD to MUST w/ANIM chunk
+1dc59435 webp-container-spec: add unknown fields MUST be ignored
+280a810f webp-container-spec: make padding byte=0 a MUST
+41f0bf68 webp-container-spec: update note on trailing data
+6bdd36db webp-container-spec: clarify Chunk Size is in bytes
+87e36c48 Merge "webp_js/README.md,cosmetics: reflow some lines" into main
+5b01f321 Merge "Update Windows makefile to build libsharpyuv library." into main
+19b1a71c webp_js/README.md,cosmetics: reflow some lines
+780db756 Update Windows makefile to build libsharpyuv library.
+e407d4b3 CMakeLists.txt: replace GLUT_glut_LIBRARY w/GLUT::GLUT
+abf73d62 Merge "WebPConfig.cmake.in: add find_dependency(Threads)" into main
+25807fb4 Merge "cmake: restore compatibility with cmake < 3.12" into main
+5dbc4bfa WebPConfig.cmake.in: add find_dependency(Threads)
+b2a175dd Merge "Update wasm instructions." into main
+cb90f76b Update wasm instructions.
+02d15258 cmake: restore compatibility with cmake < 3.12
+5ba046e2 CMake: add_definitions -> add_compile_options
+e68765af dsp,neon: use vaddv in a few more places
+e8f83de2 Set libsharpyuv include dir to 'webp' subdirectory.
+15a91ab1 cmake,cosmetics: apply cmake-format
+0dd49d1a CMakeLists.txt: set @ONLY in configure_file() calls
+62b1bfe8 Merge changes I2877e7bb,I777cad70,I15af7d1a,I686e6740,If10538a9, ... into main
+95c8fe5f Merge changes Iecea3603,I9dc228ab into main
+e7c805cf picture_csp_enc.c: remove SafeInitSharpYuv
+6af8845a sharpyuv: prefer webp/types.h
+639619ce cmake: fix dll exports
+782ed48c sharpyuv,SharpYuvInit: add mutex protection when available
+cad0d5ad sharyuv_{neon,sse2}.c: merge WEBP_USE_* sections
+ef70ee06 add a few missing <stddef.h> includes for NULL
+f0f9eda4 sharpyuv.h: remove <inttypes.h>
+9b902cba Merge "picture_csp_enc.c,CheckNonOpaque: rm unneeded local" into main
+9c1d457c cmake/cpu.cmake: remove unused variable
+9ac25bcb CMakeLists.txt,win32: match naming convention used by nmake
+76c353ba picture_csp_enc.c,CheckNonOpaque: rm unneeded local
+5000de54 Merge "cwebp: fix WebPPictureHasTransparency call" into main
+e1729309 Merge "WebPPictureHasTransparency: add missing pointer check" into main
+00ff988a vp8l_enc,AddSingleSubGreen: clear int sanitizer warnings
+e2fecc22 dsp/lossless_enc.c: clear int sanitizer warnings
+129cf9e9 dsp/lossless.c: clear int sanitizer warnings
+ad7d1753 dsp/lossless_enc.c: clear int sanitizer warnings
+5037220e VP8LSubtractGreenFromBlueAndRed_C: clear int sanitizer warnings
+2ee786c7 upsampling_sse2.c: clear int sanitizer warnings
+4cc157d4 ParseOptionalChunks: clear int sanitizer warning
+892cf033 BuildHuffmanTable: clear int sanitizer warning
+3a9a4d45 VP8GetSigned: clear int sanitizer warnings
+704a3d0a dsp/lossless.c: quiet int sanitizer warnings
+1a6c109c WebPPictureHasTransparency: add missing pointer check
+c626e7d5 cwebp: fix WebPPictureHasTransparency call
+866e349c Merge tag 'v1.2.4'
+c170df38 Merge "Create libsharpyuv.a in makefile.unix." into main
+9d7ff74a Create libsharpyuv.a in makefile.unix.
+0d1f1254 update ChangeLog (tag: v1.2.4)
+fcbc2d78 Merge "doc/*.txt: restrict code to 69 columns" into main
+4ad0e189 Merge "webp-container-spec.txt: normalize fourcc spelling" into main
+980d2488 update NEWS
+9fde8127 bump version to 1.2.4
+7a0a9935 doc/*.txt: restrict code to 69 columns
+c040a615 webp-container-spec.txt: normalize fourcc spelling
+aff1c546 dsp,x86: normalize types w/_mm_cvtsi128_si32 calls
+ab540ae0 dsp,x86: normalize types w/_mm_cvtsi32_si128 calls
+8980362e dsp,x86: normalize types w/_mm_set* calls (2)
+e626925c lossless: fix crunch mode w/WEBP_REDUCE_SIZE
+83539239 dsp,x86: normalize types w/_mm_set* calls
+8a4576ce webp-container-spec.txt: replace &amp; with &
+db870881 Merge "webp-container-spec.txt: make reserved 0 values a MUST" into main
+01d7d378 webp-lossless-bitstream-spec: number all sections
+337cf69f webp-lossless-bitstream-spec: mv Nomenclature after Intro
+79be856e Merge changes I7111d1f7,I872cd62c into main
+5b87983a webp-container-spec.txt: make reserved 0 values a MUST
+bd939123 Merge changes I7a25b1a6,I51b2c2a0,I87d0cbcf,I6ec60af6,I0a3fe9dc into main
+04764b56 libwebp.pc: add libsharpyuv to requires
+7deee810 libsharpyuv: add pkg-config file
+1a64a7e6 webp-container-spec.txt: clarify some SHOULDs
+bec2c88a webp-container-spec.txt: move ChunkHeader to terminology
+c9359332 webp-container-spec.txt: clarify 'VP8 '/'XMP ' fourccs
+70fe3063 webp-container-spec.txt: rightsize table entries
+ddbf3f3f webp-container-spec.txt: update 'key words' text
+c151e95b utils.h,WEBP_ALIGN: make bitmask unsigned
+748e92bb add WebPInt32ToMem
+3fe15b67 Merge "Build libsharpyuv as a full installable library." into main
+4f402f34 add WebPMemToInt32
+a3b68c19 Build libsharpyuv as a full installable library.
+b4994eaa CMake: set rpath for shared objects
+94cd7117 Merge "CMake: fix dylib versioning" into main
+e91451b6 Fix the lossless specs a bit more.
+231bdfb7 CMake: fix dylib versioning
+bfad7ab5 CMakeLists.txt: correct libwebpmux name in WebPConfig.cmake
+c2e3fd30 Revert "cmake: fix webpmux lib name for cmake linking"
+7366f7f3 Merge "lossless: fix crunch mode w/WEBP_REDUCE_SIZE" into main
+84163d9d lossless: fix crunch mode w/WEBP_REDUCE_SIZE
+d01c1eb3 webp-lossless-bitstream-spec,cosmetics: normalize capitalization
+8813ca8e Merge tag 'v1.2.3'
+3c4a0fbf update ChangeLog (tag: v1.2.3)
+56a480e8 dsp/cpu.h: add missing extern "C"
+62b45bdd update ChangeLog (tag: v1.2.3-rc1)
+8764ec7a Merge changes Idb037953,Id582e395 into 1.2.3
+bcb872c3 vwebp: fix file name display in windows unicode build
+67c44ac5 webpmux: fix -frame option in windows unicode build
+8278825a makefile.unix: add sharpyuv objects to clean target
+14a49e01 update NEWS
+34b1dc33 bump version to 1.2.3
+0b397fda update AUTHORS
+c16488ac update .mailmap
+5a2d929c Merge "unicode.h: set console mode before using wprintf" into main
+169f867f unicode.h: set console mode before using wprintf
+a94b855c Merge "libsharpyuv: add version defines" into main
+f83bdb52 libsharpyuv: add version defines
+bef0d797 unicode_gif.h: fix -Wdeclaration-after-statement
+404c1622 Rename Huffman coding to prefix coding in the bitstream spec
+8895f8a3 Merge "run_static_analysis.sh: fix scan-build archive path" into main
+92a673d2 Merge "Add -fvisibility=hidden flag in CMakeLists." into main
+67c1d722 Merge "add WEBP_MSAN" into main
+1124ff66 Add -fvisibility=hidden flag in CMakeLists.
+e15b3560 add WEBP_MSAN
+ec9e782a sharpyuv: remove minimum image size from sharpyuv library
+7bd07f3b run_static_analysis.sh: fix scan-build archive path
+5ecee06f Merge "sharpyuv: increase precision of gamma<->linear conversion" into main
+f81dd7d6 Merge changes I3d17d529,I53026880,I1bd61639,I6bd4b25d,Icfec8fba into main
+2d607ee6 sharpyuv: increase precision of gamma<->linear conversion
+266cbbc5 sharpyuv: add 32bit version of SharpYuvFilterRow.
+9fc12274 CMake: add src to webpinfo includes
+7d18f40a CMake: add WEBP_BUILD_WEBPINFO to list of checks for exampleutil
+11309aa5 CMake: add WEBP_BUILD_WEBPMUX to list of checks for exampleutil
+4bc762f7 CMake: link imageioutil to exampleutil after defined
+0d1b9bc4 WEBP_DEP_LIBRARIES: use Threads::Threads
+20ef48f0 Merge "sharpyuv: add support for 10/12/16 bit rgb and 10/12 bit yuv." into main
+93c54371 sharpyuv: add support for 10/12/16 bit rgb and 10/12 bit yuv.
+53cf2b49 normalize WebPValidatePicture declaration w/definition
+d3006f4b sharpyuv: slightly improve precision
+ea967098 Merge changes Ia01bd397,Ibf3771af into main
+11bc8410 Merge changes I2d317c4b,I9e77f6db into main
+30453ea4 Add an internal WebPValidatePicture.
+6c43219a Some renamings for consistency.
+4f59fa73 update .mailmap
+e74f8a62 webp-lossless-bitstream-spec,cosmetics: normalize range syntax
+5a709ec0 webp-lossless-bitstream-spec,cosmetics: fix code typo
+a2093acc webp-lossless-bitstream-spec: add amendment note
+86c66930 webp-lossless-bitstream-spec: fix BNF
+232f22da webp-lossless-bitstream-spec: fix 'simple code' snippet
+44dd765d webp-lossless-bitstream-spec: fix ColorTransform impl
+7a7e33e9 webp-lossless-bitstream-spec: fix TR-pixel right border note
+86f94ee0 Update lossless spec with Huffman codes.
+a3927cc8 sharpyuv.c,cosmetics: fix indent
+6c45cef7 Make sure the stride has a minimum value in the importer.
+0c8b0e67 sharpyuv: cleanup/cosmetic changes
+dc3841e0 {histogram,predictor}_enc: quiet int -> float warnings
+a19a25bb Replace doubles by floats in lossless misc cost estimations.
+42888f6c Add an option to enable static builds.
+7efcf3cc Merge "Fix typo in color constants: Marix -> Matrix" into main
+8f4b5c62 Fix typo in color constants: Marix -> Matrix
+90084d84 Merge "demux,IsValidExtendedFormat: remove unused variable" into main
+ed643f61 Merge changes I452d2485,Ic6d75475 into main
+8fa053d1 Rename SharpYUV to SharpYuv for consistency.
+99a87562 SharpYuvComputeConversionMatrix: quiet int->float warnings
+deb426be Makefile.vc: add sharpyuv_csp.obj to SHARPYUV_OBJS
+779597d4 demux,IsValidExtendedFormat: remove unused variable
+40e8aa57 Merge "libsharpyuv: add colorspace utilities" into main
+01a05de1 libsharpyuv: add colorspace utilities
+2de4b05a Merge changes Id9890a60,I376d81e6,I1c958838 into main
+b8bca81f Merge "configure.ac: use LT_INIT if available" into main
+e8e77b9c Merge changes I479bc487,I39864691,I5d486c2c,I186d13be into main
+7e7d5d50 Merge ".gitignore: add Android Studio & VS code dirs" into main
+10c50848 normalize label indent
+89f774e6 mux{edit,internal}: fix leaks on error
+2d3293ad ExUtilInitCommandLineArguments: fix leak on error
+ec34fd70 anim_util: fix leaks on error
+e4717287 gif2webp: fix segfault on OOM
+e3cfafaf GetBackwardReferences: fail on alloc error
+a828a59b BackwardReferencesHashChainDistanceOnly: fix segfault on OOM
+fe153fae VP8LEncodeStream: fix segfault on OOM
+919acc0e .gitignore: add Android Studio & VS code dirs
+efa0731b configure.ac: use LT_INIT if available
+0957fd69 tiffdec: add grayscale support
+e685feef Merge "Make libsharpyuv self-contained by removing dependency on cpu.c" into main
+841960b6 Make libsharpyuv self-contained by removing dependency on cpu.c
+617cf036 image_dec: add WebPGetEnabledInputFileFormats()
+7a68afaa Let SharpArgbToYuv caller pass in an RGB>YUV conversion matrix.
+34bb332c man/cwebp.1: add note about crop/resize order
+f0e9351c webp-lossless-bitstream-spec,cosmetics: fix some typos
+5ccbd6ed vp8l_dec.c,cosmetics: fix a few typos
+c3d0c2d7 fix ios build scripts after sharpyuv dep added
+d0d2292e Merge "Make libwebp depend on libsharpyuv." into main
+03d12190 alpha_processing_neon.c: fix 0x01... typo
+d55d447c Make libwebp depend on libsharpyuv.
+e4cbcdd2 Fix lossless encoding for MIPS.
+924e7ca6 alpha_processing_neon.c: fix Dispatch/ExtractAlpha_NEON
+0fa0ea54 Makefile.vc: use /MANIFEST:EMBED
+29cc95ce Basic version of libsharpyuv in libwebp, in C.
+a30f2190 examples/webpmux.c: fix a couple of typos
+66b3ce23 Fix bad overflow check in ReadTIFF()
+54e61a38 Markdownify libwebp docs and reorganize them.
+b4533deb CMakeLists.txt,cosmetics: break long line
+b9d2f9cd quant_enc.c: use WEBP_RESTRICT qualifier
+ec178f2c Add progress hook granularity in lossless
+26139c73 Rename MAX_COST to MAX_BIT_COST in histogram_enc.c
+13b82816 cmake: fix webpmux lib name for cmake linking
+88b6a396 webp-container-spec.txt,cosmetics: normalize formatting
+6f496540 Merge tag 'v1.2.2'
+4074acf8 dsp.h: bump msvc arm64 version requirement to 16.6
+b0a86089 update ChangeLog (tag: v1.2.2)
+6db8248c libwebp: Fix VP8EncTokenLoop() progress
+827a307f BMP enc: fix the transparency case
+db25f1b4 libwebp: Fix VP8EncTokenLoop() progress
+286e7fce libwebp: do not destroy jpeg codec twice on error
+6e8a4126 libwebp: do not destroy jpeg codec twice on error
+faf21968 Merge "BMP enc: fix the transparency case" into main
+480cd51d BMP enc: fix the transparency case
+9195ea05 update ChangeLog (tag: v1.2.2-rc2)
+4acae017 update NEWS
+883f0633 man/img2webp.1: update date
+567e1f44 Reword img2webp synopsis command line
+1b0c15db man/img2webp.1: update date
+17bade38 Merge "Reword img2webp synopsis command line" into main
+a80954a1 Reword img2webp synopsis command line
+f084244d anim_decode: fix alpha blending with big-endian
+b217b4ff webpinfo: fix fourcc comparison w/big-endian
+ec497b75 Merge "anim_decode: fix alpha blending with big-endian" into main
+e4886716 anim_decode: fix alpha blending with big-endian
+e3cb052c webpinfo: fix fourcc comparison w/big-endian
+a510fedb patch-check: detect duplicated files
+f035d2e4 update ChangeLog (tag: v1.2.2-rc1)
+7031946a update NEWS
+973390b6 bump version to 1.2.2
+abd6664f update AUTHORS
+5b7e7930 Merge "add missing USE_{MSA,NEON} checks in headers" into main
+02ca04c3 add missing USE_{MSA,NEON} checks in headers
+e94716e2 xcframeworkbuild.sh: place headers in a subdir
+c846efd8 patch-check: commit subject length check
+b6f756e8 update http links
+8f5cb4c1 update rfc links
+8ea81561 change VP8LPredictorFunc signature to avoid reading 'left'
+6b1d18c3 webpmux: fix the -bgcolor description
+3368d876 Merge "webpmux: add "-set bgcolor A,R,G,B"" into main
+f213abf6 webpinfo: print the number of warnings
+50c97c30 webpmux: add "-set bgcolor A,R,G,B"
+2c206aaf Remove CMakeLists.txt check in compile.sh
+96e3dfef Merge "infra/common.sh: add shard_should_run()" into main
+0e0f74b7 infra/common.sh: add shard_should_run()
+35b7436a Jenkins scripts port: update shell function comments
+21d24b4c webp-container-spec.txt: remove 'experimental' markers
+cdcf8902 Merge "Port Jenkins script: compile" into main
+dc683cde Jenkins scripts port: static analysis
+0858494e Port Jenkins script: compile
+c2cf6a93 Jenkins scripts port: android compilation
+df0e808f presubmit: Add pylint-2.7 and .pylintrc
+676c57db patch-check: shfmt
+7bb7f747 patch-check: Add shellcheck
+abcd1797 Reformat docstrings and imports
+edaf0895 Port Jenkins scripts: compile js
+b9622063 Set CheckPatchFormatted flags to fail on diffs
+e23cd548 dsp.h: enable NEON w/VS2019+ ARM64 targets
+3875c7de CMakeLists.txt: set minimum version to 3.7
+1a8f0d45 Have a hard-coded value for memset in TrellisQuantizeBlock.
+93480160 Speed up TrellisQuantizeBlock
+45eaacc9 Convert deprecated uint32 to uint32_t.
+42592af8 webp,cmake: Remove unnecessary include dirs
+e298e05f Add patch-check steps in PRESUBMIT.py
+29148919 Merge tag 'v1.2.1'
+9ce5843d update ChangeLog (tag: v1.2.1)
+d9191588 fuzzer/*: normalize src/ includes
+c5bc3624 fuzzer/*: normalize src/ includes
+53b6f762 fix indent
+d2caaba4 fix indent
+731246ba update ChangeLog (tag: v1.2.1-rc2)
+d250f01d dsp/*: use WEBP_HAVE_* to determine Init availability
+1fe31625 dsp/*: use WEBP_HAVE_* to determine Init availability
+3a4d3ecd update NEWS
+b2bc8093 bump version to 1.2.1
+e542fc7a update AUTHORS
+e0241154 Merge "libwebp/CMake: Add <BUILD_INTERFACE> to webp incl" into main
+edea6444 libwebp/CMake: Add <BUILD_INTERFACE> to webp incl
+ece18e55 dsp.h: respect --disable-sse2/sse4.1/neon
+a89a3230 wicdec: support alpha from WebP WIC decoder
+26f4aa01 Merge "alpha_processing: fix visual studio warnings" into main
+8f594663 alpha_processing: fix visual studio warnings
+46d844e6 Merge "cpu.cmake: fix compiler flag detection w/3.17.0+" into main
+298d26ea Merge changes I593adf92,If20675e7,Ifac68eac into main
+a1e5dae0 alpha_processing*: use WEBP_RESTRICT qualifier
+327ef24f cpu.cmake: fix compiler flag detection w/3.17.0+
+f70819de configure: enable libwebpmux by default
+dc7e2b42 configure: add informational notices when disabling binaries
+9df23ddd configure: move lib flag checks before binaries
+a2e18f10 Merge "WebPConfig.config.in: correct WEBP_INCLUDE_DIRS" into main
+e1a8d4f3 Merge "bit_reader_inl_utils: uniformly apply WEBP_RESTRICT" into main
+4de35f43 rescaler.c: fix alignment
+0f13eec7 bit_reader_inl_utils: uniformly apply WEBP_RESTRICT
+277d3074 Fix size_t overflow in  WebPRescalerInit
+97adbba5 WebPConfig.config.in: correct WEBP_INCLUDE_DIRS
+b60d4603 advanced_api_fuzzer: add extreme config value coverage
+72fe52f6 anim_encode.c,cosmetics: normalize indent
+116d235c anim_encode: Fix encoded_frames_[] overflow
+6f445b3e CMake: set CMP0072 to NEW
+b1cf887f define WEBP_RESTRICT for MSVC
+3e265136 Add WEBP_RESTRICT & use it in VP8BitReader
+f6d29247 vp8l_dec::ProcessRows: fix int overflow in multiply
+de3b4ba8 CMake: add WEBP_BUILD_LIBWEBPMUX
+7f09d3d1 CMakeLists.txt: rm libwebpmux dep from anim_{diff,dump}
+4edea4a6 Init{RGB,YUV}Rescaler: fix a few more int overflows
+c9e26bdb rescaler_utils: set max valid scaled w/h to INT_MAX/2
+28d488e6 utils.h: add SizeOverflow()
+695bdaa2 Export/EmitRescaledRowsRGBA: fix pointer offset int overflow
+685d073e Init{RGB,YUV}Rescaler: fix int overflows in multiplication
+d38bd0dd WebPFlipBuffer: fix integer overflow
+109ff0f1 utils: allow MALLOC_LIMIT to indicate a max
+a2fce867 WebPRescalerImportRowExpand_C: promote some vals before multiply
+776983d4 AllocateBuffer: fix int multiplication overflow check
+315abbd6 Merge "Revert "Do not use a palette for one color images.""
+eae815d0 Merge changes Ica3bbf75,I82f82954
+afbca5a1 Require Emscripten 2.0.18
+3320416b CMakeLists,emscripten: use EXPORTED_RUNTIME_METHODS
+29145ed6 Update README instructions for using Emscripten
+1f579139 cosmetics: remove use of 'sanity' / 'master'
+29b6129c WebPAnimEncoderNewInternal: remove some unnecessary inits
+b60869a1 Revert "Do not use a palette for one color images."
+6fb4cddc demux: move padded size calc post unpadded validation
+05b72d42 vp8l_enc.c: normalize index types
+b6513fba Do not use a palette for one color images.
+98bbe35b Fix multi-threading with palettes.
+b1674240 Add modified Zeng's method to palette sorting.
+88c90c45 add CONTRIBUTING.md
+6a9916d7 WebPRescalerInit: add missing int64_t promotion
+b6cf52d5 WebPIoInitFromOptions: treat use_scaling as a bool
+3b12b7f4 WebPIoInitFromOptions: treat use_cropping as a bool
+595fa13f add WebPCheckCropDimensions()
+8fdaecb0 Disable cross-color when palette is used.
+8933bac2 WebPIoInitFromOptions: respect incoming bypass_filtering val
+7d416ff0 webpdec,cosmetics: match error text to function call
+ec6cfeb5 Fix typo on WebPPictureAlloc() in README
+7e58a1a2 *.cmake: add license header
+5651a6b2 cmake: fix .so versioning
+25ae67b3 xcframeworkbuild.sh: add arm64 simulator target
+5d4ee4c3 cosmetics: remove use of the term 'dummy'
+01b38ee1 faster CollectColorXXXTransforms_SSE41
+652aa344 Merge "Use BitCtz for FastSLog2Slow_C"
+0320e1e3 add the missing default BitsCtz() code
+8886f620 Use BitCtz for FastSLog2Slow_C
+fae41617 faster CombinedShannonEntropy_SSE2
+5bd2704e Introduce the BitCtz() function.
+fee64287 Merge "wicdec,icc: treat unsupported op as non-fatal"
+33ddb894 lossless_sse{2,41}: remove some unneeded includes
+b27ea852 wicdec,icc: treat unsupported op as non-fatal
+b78494a9 Merge "Fix undefined signed shift."
+e79974cd Fix undefined signed shift.
+a8853394 SSE4.1 versions of BGRA to RGB/BGR color-space conversions
+a09a6472 SSE4.1 version of TransformColorInverse
+401da22b Merge "pngdec: check version before using png_get_chunk_malloc_max"
+26907822 pngdec: check version before using png_get_chunk_malloc_max
+06c1e72e Code cleanup
+8f0d41aa Merge changes Id135bbf4,I99e59797
+373eb170 gif2webp: don't store loop-count if there's only 1 frame
+759b9d5a cmake: add WEBP_USE_THREAD option
+926ce921 cmake: don't install binaries from extras/
+9c367bc6 WebPAnimDecoderNewInternal: validate bitstream before alloc
+47f64f6e filters_sse2: import Chromium change
+cc3577e9 fuzzer/*: use src/ based include paths
+004d77ff Merge tag 'v1.2.0'
+fedac6cc update ChangeLog (tag: v1.2.0-rc3, tag: v1.2.0)
+170a8712 Fix check_c_source_compiles with pthread.
+ceddb5fc Fix check_c_source_compiles with pthread.
+85995719 disable CombinedShannonEntropy_SSE2 on x86
+289757fe TiffDec: enforce stricter mem/dimension limit on tiles
+8af7436f Merge "{ios,xcframework}build.sh: make min version(s) more visible" into 1.2.0
+e56c3c5b pngdec: raise memory limit if needed
+8696147d pngdec: raise memory limit if needed
+13b8e9fe {ios,xcframework}build.sh: make min version(s) more visible
+a9225410 animdecoder_fuzzer: fix memory leak
+d6c2285d update gradle to 6.1.1
+8df77fb1 animdecoder_fuzzer: fix memory leak
+52ce6333 update NEWS
+28c49820 bump version to 1.2.0
+7363dff2 webp/encode.h: restore WEBP_ENCODER_ABI_VERSION to v1.1.0
+826aafa5 update AUTHORS
+63258823 animdecoder_fuzzer: validate canvas size
+9eb26381 CMake: remove duplicate "include(GNUInstallDirs)"
+2e7bed79 WebPPicture: clarify the ownership of user-owned data.
+cccf5e33 webpmux: add an '-set loop <value>' option
+c9a3f6a1 Merge changes Ie29f9867,I289c54c4
+319f56f1 iosbuild.sh: sync some aspects of xcframeworkbuild.sh
+e8e8db98 add xcframeworkbuild.sh
+ae545534 dsp.h: allow config.h to override MSVC SIMD autodetection
+fef789f3 Merge "cmake: fix per-file assembly flags"
+fc14fc03 Have C encoding predictors use decoding predictors.
+7656f0b3 README,cosmetics: fix a couple typos
+d2e245ea cmake: disable webp.js if WEBP_ENABLE_SIMD=1
+96099a79 cmake: fix per-file assembly flags
+5abb5582 Merge "cmake: fix compilation w/Xcode generator"
+8484a120 cmake: fix compilation w/Xcode generator
+d7bf01c9 Merge changes Ifcae0f38,Iee2d7401
+36c81ff6 WASM-SIMD: port 2 patches from rreverser@'s tree
+988b02ab Merge "Couple of fixes to allow SIMD on Emscripten"
+26faf770 wicdec: fail with animated images
+ab2d08a8 [cd]webp: document lack of animated webp support
+52273943 Couple of fixes to allow SIMD on Emscripten
+8870ba7f Fix skia bug #10952
+4b3c6953 Detect if StoreFrame read more than anmf_payload_size bytes
+17fd4ba8 webp/decode.h,cosmetics: normalize 'flip' comment
+411d3677 remove some unreachable break statements
+3700ffd7 WebPPictureHasTransparency: remove unreachable return
+83604bf3 {animencoder,enc_dec}_fuzzer: convert some abort()s to returns
+eb44119c Merge changes I8ae09473,I678c8b1e
+9f6055fc fuzz_utils.h: rename max() to Max()
+695788e7 fuzz_utils.h: make functions WEBP_INLINE
+906c1fcd make ImgIoUtilReadFile use WebPMalloc instead of malloc
+8cb7e536 rename demux_api_fuzzer.c -> mux_demux_api_fuzzer.c
+443db47d add animdecoder_fuzzer.cc
+36a6eea3 Merge "import fuzzers from oss-fuzz/chromium"
+ec5f12c1 Makefile.vc: remove deprecated /Gm option
+64425a08 picture_tools_enc: fix windows build warning
+bd94090a import fuzzers from oss-fuzz/chromium
+cf847cba use WEBP_DSP_INIT_FUNC for Init{GammaTables*,GetCoeffs}
+55a080e5 Add WebPReplaceTransparentPixels() in dsp
+84739717 GetBackgroundColorGIF: promote to uint32_t before << 24
+def64e92 cwebp: Fix -print_psnr for near_lossless
+cf2f88b3 Add palette and spatial for q >= 75 and -m 5
+f0110bae Add no-color cache configuration to the cruncher
+749a8b99 Better estimate of the cache cost.
+4f9f00cc Use spatial predictors on top of palette no matter what.
+7658c686 Add spatial prediction on top of palette in cruncher.
+133ff0e3 webp_js: force WASM=0 option explicitly
+e3c259a2 Fix integer overflow in EmitFancyRGB.
+b3ff0bde man/{gif2,img2}webp,webpmux: normalize some wording
+f9b30586 fix ABI breakage introduced by 6a0ff358
+1d58dcfc README.webp_js: update note about emscripten version
+44070266 README.webp_js: s/fastcomp/upstream/
+2565fa8f README.webp_js: update cmake command
+47309ef5 webp: WEBP_OFFSET_PTR()
+687ab00e DC{4,8,16}_NEON: replace vmovl w/vaddl
+1b92fe75 DC16_NEON,aarch64: use vaddlv
+53f3d8cf dec_neon,DC8_NEON: use vaddlv instead of movl+vaddv
+27d08240 Fix integer overflow in WebPAnimDecoderGetNext()
+69776e38 Merge "remove call to MBAnalyzeBestIntra4Mode for method >= 5"
+a99078c1 remove call to MBAnalyzeBestIntra4Mode for method >= 5
+22e404cc CMakeLists.txt: fix set(CACHE) argument order
+71690b52 fix MSVC warning
+6a0ff358 Enc: add a qmin / qmax range for quality factor
+0fa56f30 Merge tag 'v1.1.0'
+6cf504d0 PNM decoding: handle max_value != 255
+d7844e97 update ChangeLog (tag: v1.1.0-rc2, tag: v1.1.0)
+7f006436 Makefile.vc: fix webp_quality.exe link
+cf047e83 Makefile.vc: fix webp_quality.exe link
+c074c653 update NEWS
+30f09551 bump version to 1.1.0
+a76694a1 update AUTHORS
+6e3ef7b3 extras: fix WEBP_SWAP_16BIT_CSP check
+47178dbd extras: add WebPUnmultiplyARGB() convenience function
+22cbae33 idec_dec: fix 0 offset of NULL pointer
+290dd0b4 muxread: fix 0 offset of NULL pointer
+0df474ac Merge "lossless_(enc_|)sse2: avoid offsetting a NULL pointer"
+c6b75a19 lossless_(enc_|)sse2: avoid offsetting a NULL pointer
+295e5e38 fix UBSAN warning
+e2575e05 DC8_NEON,aarch64: use vaddv
+b0e09e34 dec_neon: Fix build failure under some toolchains
+cf0e903c dsp/lossless: Fix non gcc ARM builds
+bb7bc40b Remove ubsan errors.
+78881b76 CMake: fix GLUT library link
+9f750f7a cmake: fix BUILD_SHARED_LIBS build on mac
+17850e74 libwebp: Remove char-subscripts warning in pnmdec.c
+2fa2552d Merge "Expose WebPMalloc() in addition to WebPFree()"
+a4df4aae Expose WebPMalloc() in addition to WebPFree()
+853ea3d8 imageio/tiff: Return error before allocating bad tile size
+af650c0b Fix a Wxor-used-as-pow false positive
+601ef17c libwebp.py: update to swig 3.0.12
+0e48d889 bugfix: last alpha rows were incorrectly decoded
+24d2ccb4 webp: Fix imageio ReadPNM() TUPLTYPE
+fab8f9cf cosmetics: normalize '*' association
+94138e0e update .gitignore
+0fe1a89d update ChangeLog (tag: v1.0.3-rc1, tag: v1.0.3)
+2ad0916d update NEWS
+1287362b bump version to 1.0.3
+7b968cc2 update AUTHORS
+9d6988f4 Fix the oscillating prediction problem at low quality
+312f74d0 makefile.unix: allow *_LIBS to be overridden w/EXTRA_LIBS
+92dbf237 filters_sse2,cosmetics: shorten some long lines
+a277d197 filters_sse2.c: quiet integer sanitizer warnings
+804540f1 Fix cpufeatures in CMake.
+bf00c15b Add CMake option for bittrace.
+a788b498 filters_sse2.c: quiet integer sanitizer warnings
+e6a92c5e filters.c: quiet integer sanitizer warnings
+ec1cc40a lossless.c: remove U32 -> S8 conversion warnings
+1106478f remove conversion U32 -> S8 warnings
+812a6b49 lossless_enc: fix some conversion warning
+4627c1c9 lossless_enc,TransformColorBlue: quiet uint32_t conv warning
+c84673a6 lossless_enc_sse{2,41}: quiet signed conv warnings
+776a7757 dec_sse2: quiet signed conv warnings
+bd39c063 Merge "thread_utils: release mutex before signaling"
+0550576f Merge "(alpha_processing,enc}_sse2: quiet signed conv warnings"
+6682f2c4 thread_utils: release mutex before signaling
+e78dea75 (alpha_processing,enc}_sse2: quiet signed conv warnings
+9acf18ba iosbuild.sh: add WebP{Demux,Mux}.framework
+b9be7e65 vwebp: remove the -fit option (and make it default)
+1394a2bb Merge "README.webp_js: update Emscripten.cmake note"
+dd3e7f8a README.webp_js: update Emscripten.cmake note
+32cf8801 predictor_enc,GetBestGreenRedToBlue: quiet implicit conv warnings
+e1c8acb5 Merge "vwebp: add a -fit option"
+cbd23dd5 vwebp: add a -fit option
+2e672351 bit_writer_utils,Flush: quiet implicit conversion warnings
+1326988d swig: update libwebp_python_wrap.c
+0e7f8548 update generated swig files
+17ed1438 Merge "PutLE{16,24}: quiet implicit conversion warnings"
+24686538 PutLE{16,24}: quiet implicit conversion warnings
+153bb3a0 fix some clang-7 warnings:
+ab2dc893 Rescaler: fix rounding error
+aa65f89a HistogramCombineStochastic: fix free of uninit value
+af0bac64 Merge "encode.h: mention 'exact' default in WebPEncodeLossless*"
+6d2e11ec encode.h: mention 'exact' default in WebPEncodeLossless*
+8c3f04fe AndroidCPUInfo: reorder terms in conditional
+fcfd9c71 BitTrace: if BITTRACE is > 0, record and print syntax bits used
+067031ea Speedups for unused Huffman groups.
+01ac46ba libwebp: Display "libjpeg error:" in imageio/jpegdec
+d9a662e1 WebPRescalerGetScaledDimensions: round scaled dimension up
+62eb3f08 libwebp: Fix missing '{' in README
+e05f785a Merge "unicode,INIT_WARGV: add missing cast"
+63c9a69f tag the VP8LHashPix() function for potential uint roll-over
+2b7214ab unicode,INIT_WARGV: add missing cast
+bf424b46 tag the GetPixPairHash64() function for potential uint roll-over
+7d05d6ca Have the color cache computation be u32-bit only.
+6bcf8769 Remove BINARYEN_METHOD in wasm settings.
+2b98df90 update ChangeLog (tag: v1.0.2-rc1, tag: v1.0.2)
+61e372b7 update NEWS
+7ae658a0 bump version to 1.0.2
+51c4907d update AUTHORS
+666bd6c6 man/cwebp.1: refine near-lossless text
+561cdce5 Clarify the doc about GetFeatures.
+aec2cf02 near_lossless: fix fuzzing-detected integer overflow
+928a75de webp: Fix VP8LBitWriterClone() bug
+5173d4ee neon IsFlat
+5b081219 IsFlat: inline when possible
+381b7b54 IsFlat: use int for thresh
+6ed15ea1 fix unprobable leak in webp_sdl.c
+22bbb24e Merge "IsFlat: return int"
+8b3fb238 Merge tag 'v1.0.1'
+f435de95 IsFlat: return int
+41521aed utils.h: only define WEBP_NEED_LOG_TABLE_8BIT when needed
+9f4d4a3f neon: GetResidualCost
+0fd7514b neon: SetResidualCoeffs
+f95a996c Simpler histogram clustering.
+e85d3313 update ChangeLog (tag: v1.0.1-rc2, tag: v1.0.1)
+fa8210e4 Fix pair update in stochastic entropy merging.
+fd198f73 add codereview.settings
+825389ac README.mux: add a reference to the AnimDecoder API
+3be698c3 CMake: fix webp_js compilation
+485ff86f Fix pair update in stochastic entropy merging.
+4cd0582d CMake: fix webp_js compilation
+4cbb4caf update NEWS
+f5a5918d bump version to 1.0.1
+d61385db Speed-up: Make sure we only initialize histograms when needed.
+6752904b Speed-up: Make sure we only initialize histograms when needed.
+0c570316 update AUTHORS
+301a2dda img2webp: add help note about arguments from a file
+f0abab92 Speedups for empty histograms.
+f2dfd925 Split HistogramAdd to only have the high level logic in C.
+06b7bc7d Fix compilation on windows and clang-cl+ninja.
+b6284d82 img2webp: add help note about arguments from a file
+decf6f6b Speedups for empty histograms.
+dea3e899 Split HistogramAdd to only have the high level logic in C.
+632798ae Merge "Fix compilation on windows and clang-cl+ninja."
+dc1a9518 Merge "libwebp: Unicode command tools on Windows"
+9cf9841b libwebp: Unicode command tools on Windows
+98179495 remove some minor TODOs
+a376e7b9 Fix compilation on windows and clang-cl+ninja.
+cbf82cc0 Remove AVX2 files.
+5030e902 Merge "TIFF decoder: remove unused KINV definition"
+ac543311 Remove a few more useless #defines
+123d3306 TIFF decoder: remove unused KINV definition
+ef1094b0 Merge "- install pkg-config files during the CMake build"
+b911fbc9 libwebp: Remove duplicate GIFDisplayError in anim_util
+eee00b66 - install pkg-config files during the CMake build
+ac3ec8c9 Merge "Clean-up the common sources in dsp."
+3e13da7b Clean-up the common sources in dsp.
+5c395f1d libwebp: cmake-format all
+e7a69729 libwebp: Add extras targets in CMakeLists.txt
+e52485d6 libwebp: Rename macros in webpmux.c
+92dc0f09 clean-up MakeInputImageCopy()
+39952de2 VP8IteratorImport: add missing 'const'
+382af7a2 clean-up WebPBlendAlpha
+14d020f6 libwebp: Use ExUtilGet*() in anim_diff
+0d92ff25 libwebp: remove useless variable in gif2webp
+556cb1b4 Merge "CMake: Set WEBP_BUILD_GIF2WEBP to off"
+da26ee49 CMake: Set WEBP_BUILD_GIF2WEBP to off
+b2a867c0 cwebp: Don't premultiply during -resize if -exact
+637141bc pngdec: fix build w/libpng < 1.4.x
+bc5092b1 pngdec: set memory functions
+50d8345a Fix CMake math library.
+6aa3e8aa Fix math library on Visual Studio.
+d71df4e2 Fix math library finding in CMake.
+de08d727 cosmetics: normalize include guard comment
+009562b4 vwebp: Fix bug when Dispose then NoBlend frames
+423f2579 Fix up CMake to create targets.
+907208f9 Wait for all threads to be done in DecodeRemaining.
+4649b3c4 vwebp: Add background color display option
+78ad57a3 Fix bad glClearColor parameters
+da96d8d9 Allow for a non-initialized alpha decompressor in DoRemap.
+2563db47 fix rescaling rounding inaccuracy
+211f37ee fix endian problems in pattern copy
+5f0f5c07 Make sure partition #0 is read before VP8 data in IDecode.
+de98732b fix GetColorf() bug
+4338cd36 misc fixes in libwebpmux
+e00af13e fix signatures after a9ceda7ff1
+a9ceda7f Speed-up chunk list operations.
+2281bbf6 Merge "Better handling of bogus Huffman codes."
+39cb9aad Better handling of bogus Huffman codes.
+89cc9d37 Merge "fix read-overflow while parsing VP8X chunk"
+95fd6507 fix read-overflow while parsing VP8X chunk
+9e729fe1 Fix VP8IoTeardownHook being called twice on worker sync failure
+29fb8562 Merge "muxread,anmf: fail on multiple image chunks"
+eb82ce76 muxread,anmf: fail on multiple image chunks
+1344a2e9 fix alpha-filtering crash when image width is larger than radius
+be738c6d muxread,ChunkVerifyAndAssign: validate chunk_size
+2c70ad76 muxread,CreateInternal: fix riff size checks
+569001f1 Fix for thread race heap-use-after-free
+c56a02d9 Android.mk: use LOCAL_EXPORT_C_INCLUDES w/public libs
+15795596 CMakeLists.txt,cosmetics: normalize if() formatting
+1a44c233 Merge "cmake: add support for webpmux"
+e9569ad7 Merge "configure,*am,cosmetics: s/WANT_/BUILD_/"
+35c7de6f cmake: add support for webpmux
+0f25e61c WebpToSDL(): fix the return value in case of error
+5d8985de configure,*am,cosmetics: s/WANT_/BUILD_/
+895fd28f Merge "man/Makefile.am: add img2webp.1"
+5cf3e2af man/Makefile.am: add img2webp.1
+2a9de5b9 Add build rules for anim_diff & anim_dump utils.
+71ed73cf fix invalid check for buffer size
+af0e4fbb gif2webp: fix transcode of loop count=65535
+dce5d764 Limit memory allocation when reading invalid Huffman codes.
+f9df0081 Merge "cmake: quiet glut deprecation warnings on OS X"
+dc39b16f webpmux.1: correct grammar
+c7aa1264 cwebp.c: fix a missing \n
+53aa51e9 Merge tag 'v1.0.0'
+698b8844 update ChangeLog (tag: v1.0.0)
 8d510751 webp-container-spec: correct frame duration=0 note
 e6b2164e vwebp: Copy Chrome's behavior w/frame duration == 0
+094b3b28 cmake: quiet glut deprecation warnings on OS X
+71c39a06 webp-container-spec: correct frame duration=0 note
+fd3d5756 vwebp: Copy Chrome's behavior w/frame duration == 0
+b0c966fb Build vwebp from CMake.
 d20b7707 update ChangeLog (tag: v1.0.0-rc3)
 0d5fad46 add WEBP_DSP_INIT / WEBP_DSP_INIT_FUNC
+d77bf512 add WEBP_DSP_INIT / WEBP_DSP_INIT_FUNC
 c1cb86af fix 16b overflow in SSE2
 e577feb7 makefile.unix: add DEBUG flag for compiling w/ debug-symbol
 99be34b3 cwebp,get_disto: fix bpp output
+e122e511 cwebp,get_disto: fix bpp output
 f5565ca8 cmake: Make sure we use near-lossless by default.
 d898dc14 fix bug in WebPImport565: alpha value was not set
+1c8f358d Fix CMake with WASM.
+a0215fb7 webp_js: fix webp_js demo html
 882784b0 update ChangeLog (tag: v1.0.0-rc2)
 2f930e08 Revert "Use proper targets for CMake."
 8165e8fb Use proper targets for CMake.
 3f157dd5 Remove some very hard TODOs.
+abb47760 Merge "Use proper targets for CMake."
 cd758a17 {de,}mux/Makefile.am: add missing headers
+e155dda0 Use proper targets for CMake.
 b892b8ba makefile.unix,dist: use ascii for text output
 64a57d05 add -version option to anim_dump,anim_diff and img2webp
+994be82d Merge "Remove some very hard TODOs."
+4033e1d7 Remove some very hard TODOs.
 fc1b8e3a webp_js: fix webp_js demo html
 15aa48d9 update ChangeLog (tag: v1.0.0-rc1)
 e607dabc update AUTHORS
 38410c08 [CFI] Remove function pointer casts
+978eec25 [CFI] Remove function pointer casts
 c57b2736 bump version to 1.0.0
 cba28853 update NEWS
 c909d531 Merge "remove some deprecation warning on MacOSX"
@@ -362,7 +1132,7 @@
 6524fcd6 vwebp_sdl: simple viewer based on SDL
 6cf24a24 get_disto: fix reference file read
 43d472aa Merge tag 'v0.6.0'
-50d1a848 update ChangeLog (tag: v0.6.0, origin/0.6.0, 0.6.0)
+50d1a848 update ChangeLog (tag: v0.6.0, origin/0.6.0)
 20a7fea0 extras/Makefile.am: fix libwebpextras.la reference
 415f3ffe update ChangeLog (tag: v0.6.0-rc3)
 3c6d1224 update NEWS
@@ -439,7 +1209,7 @@
 f04eb376 Merge tag 'v0.5.2'
 341d711c NEON: 5% faster conversion to RGB565 and RGBA4444
 abb54827 remove Clang warnings with unused arch arguments.
-ece9684f update ChangeLog (tag: v0.5.2-rc2, tag: v0.5.2, origin/0.5.2, 0.5.2)
+ece9684f update ChangeLog (tag: v0.5.2-rc2, tag: v0.5.2, origin/0.5.2)
 aa7744ca anim_util: quiet implicit conv warnings in 32-bit
 d9120271 jpegdec: correct ContextFill signature
 24eb3940 Remove some errors when compiling the code as C++.
@@ -726,7 +1496,7 @@
 c0991a14 io,EmitRescaledAlphaYUV: factor out a common expr
 48bf5ed1 build.gradle: remove tab
 bfef6c9f Merge tag 'v0.5.1'
-3d97bb75 update ChangeLog (tag: v0.5.1, origin/0.5.1, 0.5.1)
+3d97bb75 update ChangeLog (tag: v0.5.1, origin/0.5.1)
 deb54d91 Clarify the expected 'config' lifespan in WebPIDecode()
 435308e0 Add MSA optimized encoder transform functions
 dce64bfa Add MSA optimized alpha filter functions
@@ -920,7 +1690,7 @@
 6c1d7631 avoid Yoda style for comparison
 8ce975ac SSE optimization for vector mismatch.
 7db53831 Merge tag 'v0.5.0'
-37f04949 update ChangeLog (tag: v0.5.0-rc1, tag: v0.5.0, origin/0.5.0, 0.5.0)
+37f04949 update ChangeLog (tag: v0.5.0-rc1, tag: v0.5.0, origin/0.5.0)
 7e7b6ccc faster rgb565/rgb4444/argb output
 4c7f565f update NEWS
 1f62b6b2 update AUTHORS
@@ -1704,7 +2474,7 @@
 0524d9e5 dsp: detect mips64 & disable mips32 code
 d3485d96 cwebp.1: fix quality description placement
 29a9fe22 Merge tag 'v0.4.1'
-8af27718 update ChangeLog (tag: v0.4.1, origin/0.4.1, 0.4.1)
+8af27718 update ChangeLog (tag: v0.4.1, origin/0.4.1)
 e09e9ff6 Record & log the image pre-processing time.
 f59c0b4b iosbuild.sh: specify optimization flags
 8d34ea3e update ChangeLog (tag: v0.4.1-rc1)
@@ -2089,7 +2859,7 @@
 effcb0fd Merge tag 'v0.4.0'
 7c76255d autoconf: update ax_pthread.m4
 fff2a11b make -short work with -print_ssim, -print_psnr, etc.
-68e7901d update ChangeLog (tag: v0.4.0-rc1, tag: v0.4.0, origin/0.4.0, 0.4.0)
+68e7901d update ChangeLog (tag: v0.4.0-rc1, tag: v0.4.0, origin/0.4.0)
 256e4333 update NEWS description with new general features
 29625340 Merge "gif2webp: don't use C99 %zu" into 0.4.0
 3b9f9dd0 gif2webp: don't use C99 %zu
@@ -2865,7 +3635,7 @@
 a0770727 mux struct naming
 6c66dde8 Merge "Tune Lossless encoder"
 ab5ea217 Tune Lossless encoder
-74fefc8c Update ChangeLog (tag: v0.2.1, origin/0.2.0, 0.2.0)
+74fefc8c Update ChangeLog (tag: v0.2.1, origin/0.2.0)
 92f8059c Rename some chunks:
 3bb4bbeb Merge "Mux API change:"
 d0c79f05 Mux API change:
diff --git a/third_party/libwebp/METADATA b/third_party/libwebp/METADATA
index 51d741c..28bfad1 100644
--- a/third_party/libwebp/METADATA
+++ b/third_party/libwebp/METADATA
@@ -11,11 +11,11 @@
     type: GIT
     value: "https://chromium.googlesource.com/webm/libwebp"
   }
-  version: "698b8844e38a0c5ca50bb20f866e71291bfc3b36"
+  version: "v1.3.1"
   last_upgrade_date {
-    year: 2018
-    month: 4
-    day: 21
+    year: 2023
+    month: 7
+    day: 6
   }
   license_type: NOTICE
 }
diff --git a/third_party/libwebp/Makefile.am b/third_party/libwebp/Makefile.am
index 3f73b13..e1c1dd4 100644
--- a/third_party/libwebp/Makefile.am
+++ b/third_party/libwebp/Makefile.am
@@ -1,8 +1,8 @@
 ACLOCAL_AMFLAGS = -I m4
-SUBDIRS = src imageio man
+SUBDIRS = sharpyuv src imageio man
 EXTRA_DIST = COPYING autogen.sh
 
-if WANT_EXTRAS
+if BUILD_EXTRAS
   SUBDIRS += extras
 endif
 
diff --git a/third_party/libwebp/Makefile.vc b/third_party/libwebp/Makefile.vc
index 5d1bf86..cb6b7a9 100644
--- a/third_party/libwebp/Makefile.vc
+++ b/third_party/libwebp/Makefile.vc
@@ -5,6 +5,7 @@
 LIBWEBP_BASENAME = libwebp
 LIBWEBPMUX_BASENAME = libwebpmux
 LIBWEBPDEMUX_BASENAME = libwebpdemux
+LIBSHARPYUV_BASENAME = libsharpyuv
 
 !IFNDEF ARCH
 !IF ! [ cl 2>&1 | find "x86" > NUL ]
@@ -28,15 +29,14 @@
 
 NOLOGO     = /nologo
 CCNODBG    = cl.exe $(NOLOGO) /O2 /DNDEBUG
-CCDEBUG    = cl.exe $(NOLOGO) /Od /Gm /Zi /D_DEBUG /RTC1
+CCDEBUG    = cl.exe $(NOLOGO) /Od /Zi /D_DEBUG /RTC1
 CFLAGS     = /I. /Isrc $(NOLOGO) /W3 /EHsc /c
 CFLAGS     = $(CFLAGS) /DWIN32 /D_CRT_SECURE_NO_WARNINGS /DWIN32_LEAN_AND_MEAN
-LDFLAGS    = /LARGEADDRESSAWARE /MANIFEST /NXCOMPAT /DYNAMICBASE
+LDFLAGS    = /LARGEADDRESSAWARE /MANIFEST:EMBED /NXCOMPAT /DYNAMICBASE
 LDFLAGS    = $(LDFLAGS) $(PLATFORM_LDFLAGS)
 LNKDLL     = link.exe /DLL $(NOLOGO)
 LNKEXE     = link.exe $(NOLOGO)
 LNKLIB     = lib.exe $(NOLOGO)
-MT         = mt.exe $(NOLOGO)
 RCNODBG    = rc.exe $(NOLOGO) /l"0x0409"  # 0x409 = U.S. English
 RCDEBUG    = $(RCNODBG) /D_DEBUG
 
@@ -53,11 +53,6 @@
 OUTDIR = $(OBJDIR)
 !ENDIF
 
-!IF "$(HAVE_AVX2)" == "1"
-CFLAGS = $(CFLAGS) /DWEBP_HAVE_AVX2
-AVX2_FLAGS = /arch:AVX2
-!ENDIF
-
 ##############################################################
 # Runtime library configuration
 !IF "$(RTLIBCFG)" == "static"
@@ -87,6 +82,7 @@
               $(DIROBJ)\extras \
               $(DIROBJ)\imageio \
               $(DIROBJ)\mux \
+              $(DIROBJ)\sharpyuv \
               $(DIROBJ)\utils \
 
 # Target configuration
@@ -101,6 +97,7 @@
 LIBWEBP_BASENAME = $(LIBWEBP_BASENAME)_debug
 LIBWEBPMUX_BASENAME = $(LIBWEBPMUX_BASENAME)_debug
 LIBWEBPDEMUX_BASENAME = $(LIBWEBPDEMUX_BASENAME)_debug
+LIBSHARPYUV_BASENAME = $(LIBSHARPYUV_BASENAME)_debug
 !ELSE IF "$(CFG)" == "release-dynamic"
 CC        = $(CCNODBG)
 RC        = $(RCNODBG)
@@ -114,6 +111,7 @@
 LIBWEBP_BASENAME = $(LIBWEBP_BASENAME)_debug
 LIBWEBPMUX_BASENAME = $(LIBWEBPMUX_BASENAME)_debug
 LIBWEBPDEMUX_BASENAME = $(LIBWEBPDEMUX_BASENAME)_debug
+LIBSHARPYUV_BASENAME = $(LIBSHARPYUV_BASENAME)_debug
 !ENDIF
 
 !IF "$(STATICLIBBUILD)" == "TRUE"
@@ -123,23 +121,28 @@
 LIBWEBP = $(DIRLIB)\$(LIBWEBP_BASENAME).lib
 LIBWEBPMUX = $(DIRLIB)\$(LIBWEBPMUX_BASENAME).lib
 LIBWEBPDEMUX = $(DIRLIB)\$(LIBWEBPDEMUX_BASENAME).lib
+LIBSHARPYUV = $(DIRLIB)\$(LIBSHARPYUV_BASENAME).lib
 !ELSE IF "$(DLLBUILD)" == "TRUE"
-DLLINC = webp_dll.h
-CC     = $(CC) /I$(DIROBJ) /FI$(DLLINC) $(RTLIB) /DWEBP_DLL
+CC     = $(CC) /I$(DIROBJ) $(RTLIB) /DWEBP_DLL
 LIBWEBPDECODER = $(DIRLIB)\$(LIBWEBPDECODER_BASENAME)_dll.lib
 LIBWEBP = $(DIRLIB)\$(LIBWEBP_BASENAME)_dll.lib
 LIBWEBPMUX = $(DIRLIB)\$(LIBWEBPMUX_BASENAME)_dll.lib
 LIBWEBPDEMUX = $(DIRLIB)\$(LIBWEBPDEMUX_BASENAME)_dll.lib
+LIBSHARPYUV = $(DIRLIB)\$(LIBSHARPYUV_BASENAME)_dll.lib
 LIBWEBP_PDBNAME = $(DIROBJ)\$(LIBWEBP_BASENAME)_dll.pdb
 CFGSET = TRUE
 !ENDIF
 
+!IF "$(UNICODE)" == "1"
+CFLAGS = $(CFLAGS) /D_UNICODE /DUNICODE
+!ENDIF
+
 #######################
 # Usage
 #
 !IF "$(CFGSET)" == "FALSE"
 !MESSAGE Usage: nmake /f Makefile.vc [CFG=<config>]
-!MESSAGE .          [OBJDIR=<path>] [RTLIBCFG=<rtlib>] [<target>]
+!MESSAGE .          [OBJDIR=<path>] [RTLIBCFG=<rtlib>] [UNICODE=1] [<target>]
 !MESSAGE
 !MESSAGE where <config> is one of:
 !MESSAGE -  release-static                - release static library
@@ -175,6 +178,15 @@
 # A config was provided, so the library can be built.
 #
 
+SHARPYUV_OBJS = \
+    $(DIROBJ)\sharpyuv\sharpyuv.obj \
+    $(DIROBJ)\sharpyuv\sharpyuv_cpu.obj \
+    $(DIROBJ)\sharpyuv\sharpyuv_csp.obj \
+    $(DIROBJ)\sharpyuv\sharpyuv_dsp.obj \
+    $(DIROBJ)\sharpyuv\sharpyuv_gamma.obj \
+    $(DIROBJ)\sharpyuv\sharpyuv_neon.obj \
+    $(DIROBJ)\sharpyuv\sharpyuv_sse2.obj \
+
 DEC_OBJS = \
     $(DIROBJ)\dec\alpha_dec.obj \
     $(DIROBJ)\dec\buffer_dec.obj \
@@ -216,6 +228,7 @@
     $(DIROBJ)\dsp\lossless_msa.obj \
     $(DIROBJ)\dsp\lossless_neon.obj \
     $(DIROBJ)\dsp\lossless_sse2.obj \
+    $(DIROBJ)\dsp\lossless_sse41.obj \
     $(DIROBJ)\dsp\rescaler.obj \
     $(DIROBJ)\dsp\rescaler_mips32.obj \
     $(DIROBJ)\dsp\rescaler_mips_dsp_r2.obj \
@@ -239,9 +252,9 @@
     $(DIROBJ)\dsp\cost.obj \
     $(DIROBJ)\dsp\cost_mips32.obj \
     $(DIROBJ)\dsp\cost_mips_dsp_r2.obj \
+    $(DIROBJ)\dsp\cost_neon.obj \
     $(DIROBJ)\dsp\cost_sse2.obj \
     $(DIROBJ)\dsp\enc.obj \
-    $(DIROBJ)\dsp\enc_avx2.obj \
     $(DIROBJ)\dsp\enc_mips32.obj \
     $(DIROBJ)\dsp\enc_mips_dsp_r2.obj \
     $(DIROBJ)\dsp\enc_msa.obj \
@@ -335,12 +348,13 @@
     $(DIROBJ)\utils\quant_levels_utils.obj \
 
 LIBWEBPDECODER_OBJS = $(DEC_OBJS) $(DSP_DEC_OBJS) $(UTILS_DEC_OBJS)
-LIBWEBP_OBJS = $(LIBWEBPDECODER_OBJS) $(ENC_OBJS) $(DSP_ENC_OBJS) \
-               $(UTILS_ENC_OBJS) $(DLL_OBJS)
+LIBWEBP_OBJS = $(LIBWEBPDECODER_OBJS) $(ENC_OBJS) \
+               $(DSP_ENC_OBJS) $(UTILS_ENC_OBJS) $(DLL_OBJS)
 LIBWEBPMUX_OBJS = $(MUX_OBJS) $(LIBWEBPMUX_OBJS)
 LIBWEBPDEMUX_OBJS = $(DEMUX_OBJS) $(LIBWEBPDEMUX_OBJS)
+LIBSHARPYUV_OBJS = $(SHARPYUV_OBJS)
 
-OUT_LIBS = $(LIBWEBPDECODER) $(LIBWEBP)
+OUT_LIBS = $(LIBWEBPDECODER) $(LIBWEBP) $(LIBSHARPYUV)
 !IF "$(ARCH)" == "ARM"
 ex: $(OUT_LIBS)
 all: ex
@@ -368,7 +382,7 @@
 $(DIRBIN)\anim_dump.exe: $(IMAGEIO_ENC_OBJS)
 $(DIRBIN)\cwebp.exe: $(DIROBJ)\examples\cwebp.obj $(IMAGEIO_DEC_OBJS)
 $(DIRBIN)\cwebp.exe: $(IMAGEIO_UTIL_OBJS)
-$(DIRBIN)\cwebp.exe: $(LIBWEBPDEMUX)
+$(DIRBIN)\cwebp.exe: $(LIBWEBPDEMUX) $(LIBSHARPYUV)
 $(DIRBIN)\dwebp.exe: $(DIROBJ)\examples\dwebp.obj $(IMAGEIO_DEC_OBJS)
 $(DIRBIN)\dwebp.exe: $(IMAGEIO_ENC_OBJS)
 $(DIRBIN)\dwebp.exe: $(IMAGEIO_UTIL_OBJS)
@@ -386,13 +400,19 @@
 $(DIRBIN)\img2webp.exe: $(DIROBJ)\examples\img2webp.obj $(LIBWEBPMUX)
 $(DIRBIN)\img2webp.exe: $(IMAGEIO_DEC_OBJS)
 $(DIRBIN)\img2webp.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS)
-$(DIRBIN)\img2webp.exe: $(LIBWEBPDEMUX) $(LIBWEBP)
+$(DIRBIN)\img2webp.exe: $(LIBWEBPDEMUX) $(LIBWEBP) $(LIBSHARPYUV)
 $(DIRBIN)\get_disto.exe: $(DIROBJ)\extras\get_disto.obj
 $(DIRBIN)\get_disto.exe: $(IMAGEIO_DEC_OBJS) $(IMAGEIO_UTIL_OBJS)
 $(DIRBIN)\get_disto.exe: $(LIBWEBPDEMUX) $(LIBWEBP)
 $(DIRBIN)\webp_quality.exe: $(DIROBJ)\extras\webp_quality.obj
 $(DIRBIN)\webp_quality.exe: $(IMAGEIO_UTIL_OBJS)
-$(DIRBIN)\webp_quality.exe: $(EXTRAS_OBJS) $(LIBWEBP)
+$(DIRBIN)\webp_quality.exe: $(EXTRAS_OBJS)
+# EXTRA_OBJS requires private symbols from dsp. Explicitly add those when
+# building libwebp as a dll.
+!IF "$(DLLBUILD)" == "TRUE"
+$(DIRBIN)\webp_quality.exe: $(DSP_DEC_OBJS)
+!ENDIF
+$(DIRBIN)\webp_quality.exe: $(LIBWEBP)
 $(DIRBIN)\webpinfo.exe: $(DIROBJ)\examples\webpinfo.obj
 $(DIRBIN)\webpinfo.exe: $(IMAGEIO_DEC_OBJS)
 $(DIRBIN)\webpinfo.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS)
@@ -403,17 +423,16 @@
 $(IMAGEIO_DEC_OBJS) $(IMAGEIO_ENC_OBJS) $(EXTRAS_OBJS): $(OUTPUT_DIRS)
 !ENDIF  # ARCH == ARM
 
+$(LIBSHARPYUV): $(LIBSHARPYUV_OBJS)
 $(LIBWEBPDECODER): $(LIBWEBPDECODER_OBJS)
-$(LIBWEBP): $(LIBWEBP_OBJS)
+$(LIBWEBP): $(LIBWEBP_OBJS) $(LIBSHARPYUV)
 $(LIBWEBPMUX): $(LIBWEBPMUX_OBJS)
 $(LIBWEBPDEMUX): $(LIBWEBPDEMUX_OBJS)
 
-$(LIBWEBP_OBJS) $(LIBWEBPMUX_OBJS) $(LIBWEBPDEMUX_OBJS): $(OUTPUT_DIRS)
+$(LIBWEBP_OBJS) $(LIBWEBPMUX_OBJS) $(LIBWEBPDEMUX_OBJS) $(LIBSHARPYUV_OBJS): \
+    $(OUTPUT_DIRS)
 
 !IF "$(DLLBUILD)" == "TRUE"
-$(LIBWEBP_OBJS) $(LIBWEBPMUX_OBJS) $(LIBWEBPDEMUX_OBJS): \
-    $(DIROBJ)\$(DLLINC)
-
 {$(DIROBJ)}.c{$(DIROBJ)}.obj:
 	$(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$@  $<
 
@@ -423,20 +442,20 @@
 	$(RC) /fo$@ $<
 {src\mux}.rc{$(DIROBJ)\mux}.res:
 	$(RC) /fo$@ $<
+{sharpyuv}.rc{$(DIROBJ)\sharpyuv}.res:
+	$(RC) /fo$@ $<
 
-$(LIBWEBP): $(DIROBJ)\$(LIBWEBP_BASENAME:_debug=).res
+$(LIBSHARPYUV): $(DIROBJ)\sharpyuv\$(LIBSHARPYUV_BASENAME:_debug=).res
+$(LIBWEBP): $(LIBSHARPYUV) $(DIROBJ)\$(LIBWEBP_BASENAME:_debug=).res
 $(LIBWEBPDECODER): $(DIROBJ)\$(LIBWEBPDECODER_BASENAME:_debug=).res
 $(LIBWEBPMUX): $(LIBWEBP) $(DIROBJ)\mux\$(LIBWEBPMUX_BASENAME:_debug=).res
 $(LIBWEBPDEMUX): $(LIBWEBP) $(DIROBJ)\demux\$(LIBWEBPDEMUX_BASENAME:_debug=).res
 
-$(LIBWEBPDECODER) $(LIBWEBP) $(LIBWEBPMUX) $(LIBWEBPDEMUX):
+$(LIBWEBPDECODER) $(LIBWEBP) $(LIBWEBPMUX) $(LIBWEBPDEMUX) $(LIBSHARPYUV):
 	$(LNKDLL) /out:$(DIRBIN)\$(@B:_dll=.dll) /implib:$@ $(LFLAGS) $**
 	-xcopy $(DIROBJ)\*.pdb $(DIRLIB) /y
-
-clean::
-	@-erase /s $(DIROBJ)\$(DLLINC) 2> NUL
 !ELSE
-$(LIBWEBPDECODER) $(LIBWEBP) $(LIBWEBPMUX) $(LIBWEBPDEMUX):
+$(LIBWEBPDECODER) $(LIBWEBP) $(LIBWEBPMUX) $(LIBWEBPDEMUX) $(LIBSHARPYUV):
 	$(LNKLIB) /out:$@ $**
 	-xcopy $(DIROBJ)\*.pdb $(DIRLIB) /y
 !ENDIF
@@ -444,19 +463,9 @@
 $(OUTPUT_DIRS):
 	@if not exist "$(@)" mkdir "$(@)"
 
-# generate a helper include to define WEBP_EXTERN suitable for the DLL build
-$(DIROBJ)\$(DLLINC):
-	@echo #ifndef WEBP_DLL_H_ > $@
-	@echo #define WEBP_DLL_H_ >> $@
-	@echo #define WEBP_EXTERN __declspec(dllexport) >> $@
-	@echo #endif  /* WEBP_DLL_H_ */ >> $@
-
 .SUFFIXES: .c .obj .res .exe
 # File-specific flag builds. Note batch rules take precedence over wildcards,
 # so for now name each file individually.
-$(DIROBJ)\dsp\enc_avx2.obj: src\dsp\enc_avx2.c
-	$(CC) $(CFLAGS) $(AVX2_FLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\dsp\ \
-	  src\dsp\$(@B).c
 $(DIROBJ)\examples\anim_diff.obj: examples\anim_diff.c
 	$(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \
 	  /Fo$(DIROBJ)\examples\ examples\$(@B).c
@@ -479,6 +488,8 @@
 	$(CC) $(CFLAGS) /Fd$(DIROBJ)\extras\ /Fo$(DIROBJ)\extras\ $<
 {imageio}.c{$(DIROBJ)\imageio}.obj::
 	$(CC) $(CFLAGS) /Fd$(DIROBJ)\imageio\ /Fo$(DIROBJ)\imageio\ $<
+{sharpyuv}.c{$(DIROBJ)\sharpyuv}.obj::
+	$(CC) $(CFLAGS) /Fd$(DIROBJ)\sharpyuv\ /Fo$(DIROBJ)\sharpyuv\ $<
 {src\dec}.c{$(DIROBJ)\dec}.obj::
 	$(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\dec\ $<
 {src\demux}.c{$(DIROBJ)\demux}.obj::
@@ -492,17 +503,16 @@
 {src\utils}.c{$(DIROBJ)\utils}.obj::
 	$(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\utils\ $<
 
+LNKLIBS     = ole32.lib windowscodecs.lib shlwapi.lib
+!IF "$(UNICODE)" == "1"
+LNKLIBS     = $(LNKLIBS) Shell32.lib
+!ENDIF
+
 {$(DIROBJ)\examples}.obj{$(DIRBIN)}.exe:
-	$(LNKEXE) $(LDFLAGS) /OUT:$@ $** \
-	    ole32.lib windowscodecs.lib shlwapi.lib
-	$(MT) -manifest $@.manifest -outputresource:$@;1
-	del $@.manifest
+	$(LNKEXE) $(LDFLAGS) /OUT:$@ $** $(LNKLIBS)
 
 {$(DIROBJ)\extras}.obj{$(DIRBIN)}.exe:
-	$(LNKEXE) $(LDFLAGS) /OUT:$@ $** \
-	    ole32.lib windowscodecs.lib shlwapi.lib
-	$(MT) -manifest $@.manifest -outputresource:$@;1
-	del $@.manifest
+	$(LNKEXE) $(LDFLAGS) /OUT:$@ $** $(LNKLIBS)
 
 clean::
 	@-erase /s $(DIROBJ)\*.dll 2> NUL
diff --git a/third_party/libwebp/NEWS b/third_party/libwebp/NEWS
index 480cb7d..2111d33 100644
--- a/third_party/libwebp/NEWS
+++ b/third_party/libwebp/NEWS
@@ -1,3 +1,115 @@
+- 6/23/2023: version 1.3.1
+  This is a binary compatible release.
+  * security fixes for lossless encoder (#603, chromium: #1420107, #1455619,
+    CVE-2023-1999)
+  * improve error reporting through WebPPicture error codes
+  * fix upsampling for RGB565 and RGBA4444 in NEON builds
+  * img2webp: add -sharp_yuv & -near_lossless
+  * Windows builds:
+    - fix compatibility with clang-cl (#607)
+    - improve Arm64 performance with cl.exe
+    - add Arm64EC support
+  * fix webp_js with emcc >= 3.1.27 (stack size change, #614)
+  * CMake fixes (#592, #610, #612)
+  * further updates to the container and lossless bitstream docs (#581, #611)
+
+- 12/16/2022: version 1.3.0
+  This is a binary compatible release.
+  * add libsharpyuv, which exposes -sharp_yuv/config.use_sharp_yuv
+    functionality to other libraries; libwebp now depends on this library
+  * major updates to the container and lossless bitstream docs (#448, #546,
+    #551)
+  * miscellaneous warning, bug & build fixes (#576, #583, #584)
+
+- 8/4/2022: version 1.2.4
+  This is a binary compatible release.
+  * restore CMake libwebpmux target name for compatibility with 1.2.2 (#575)
+  * fix lossless crunch mode encoding with WEBP_REDUCE_SIZE
+    (chromium: #1345547, #1345595, #1345772, #1345804)
+
+- 6/30/2022: version 1.2.3
+  This is a binary compatible release.
+  * security fix for lossless encoder (#565, chromium:1313709)
+  * improved progress granularity in WebPReportProgress() when using lossless
+  * improved precision in Sharp YUV (-sharp_yuv) conversion
+  * many corrections to webp-lossless-bitstream-spec.txt (#551)
+  * crash/leak fixes on error/OOM and other bug fixes (#558, #563, #569, #573)
+
+- 1/11/2022: version 1.2.2
+  This is a binary compatible release.
+  * webpmux: add "-set bgcolor A,R,G,B"
+  * add ARM64 NEON support for MSVC builds (#539)
+  * fix duplicate include error in Xcode when using multiple XCFrameworks in a
+    project (#542)
+  * doc updates and bug fixes (#538, #544, #548, #550)
+
+- 7/20/2021: version 1.2.1
+  This is a binary compatible release.
+  * minor lossless encoder improvements and x86 color conversion speed up
+  * add ARM64 simulator support to xcframeworkbuild.sh (#510)
+  * further security related hardening in libwebp & examples
+    (issues: #497, #508, #518)
+    (chromium: #1196480, #1196773, #1196775, #1196777, #1196778, #1196850)
+    (oss-fuzz: #28658, #28978)
+  * toolchain updates and bug fixes (#498, #501, #502, #504, #505, #506, #509,
+                                     #533)
+  * use more inclusive language within the source (#507)
+
+- 12/23/2020: version 1.2.0
+  * API changes:
+    - libwebp:
+      encode.h: add a qmin / qmax range for quality factor (cwebp adds -qrange)
+  * lossless encoder improvements
+  * SIMD support for Wasm builds
+  * add xcframeworkbuild.sh, supports Mac Catalyst builds
+  * import fuzzers from oss-fuzz & chromium (#409)
+  * webpmux: add an '-set loop <value>' option (#494)
+  * toolchain updates and bug fixes (#449, #463, #470, #475, #477, #478, #479,
+    #488, #491)
+
+- 12/18/2019: version 1.1.0
+  * API changes:
+    - libwebp:
+      WebPMalloc (issue #442)
+    - extras:
+      WebPUnmultiplyARGB
+  * alpha decode fix (issue #439)
+  * toolchain updates and bug fixes
+    (chromium: #1026858, #1027136, #1027409, #1028620, #1028716, #995200)
+    (oss-fuzz: #19430, #19447)
+
+- 7/4/2019: version 1.0.3
+  This is a binary compatible release.
+  * resize fixes for Nx1 sizes and the addition of non-opaque alpha values for
+    odd sizes (issues #418, #434)
+  * lossless encode/decode performance improvements
+  * lossy compression performance improvement at low quality levels with flat
+    content (issue #432)
+  * python swig files updated to support python 3
+  Tool updates:
+    vwebp will now preserve the aspect ratio of images that exceed monitor
+    resolution by scaling the image to fit (issue #433)
+
+- 1/14/2019: version 1.0.2
+  This is a binary compatible release.
+  * (Windows) unicode file support in the tools (linux and mac already had
+    support, issue #398)
+  * lossless encoder speedups
+  * lossy encoder speedup on ARM
+  * lossless multi-threaded security fix (chromium:917029)
+
+- 11/2/2018: version 1.0.1
+  This is a binary compatible release.
+  * lossless encoder speedups
+  * big-endian fix for alpha decoding (issue #393)
+  * gif2webp fix for loop count=65535 transcode (issue #382)
+  * further security related hardening in libwebp & libwebpmux
+    (issues #383, #385, #386, #387, #388, #391)
+    (oss-fuzz #9099, #9100, #9105, #9106, #9111, #9112, #9119, #9123, #9170,
+              #9178, #9179, #9183, #9186, #9191, #9364, #9417, #9496, #10349,
+              #10423, #10634, #10700, #10838, #10922, #11021, #11088, #11152)
+  * miscellaneous bug & build fixes (issues #381, #394, #396, #397, #400)
+
 - 4/2/2018: version 1.0.0
   This is a binary compatible release.
   * lossy encoder improvements to avoid chroma shifts in various circumstances
diff --git a/third_party/libwebp/PRESUBMIT.py b/third_party/libwebp/PRESUBMIT.py
new file mode 100644
index 0000000..91ad12e
--- /dev/null
+++ b/third_party/libwebp/PRESUBMIT.py
@@ -0,0 +1,245 @@
+# Copyright (c) 2021, Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#   * Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#
+#   * Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in
+#     the documentation and/or other materials provided with the
+#     distribution.
+#
+#   * Neither the name of Google nor the names of its contributors may
+#     be used to endorse or promote products derived from this software
+#     without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Top-level presubmit script for libwebp.
+
+See https://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for
+details on the presubmit API built into depot_tools.
+"""
+
+import re
+import subprocess2
+
+USE_PYTHON3 = True
+_BASH_INDENTATION = "2"
+_GIT_COMMIT_SUBJECT_LENGTH = 65
+_INCLUDE_BASH_FILES_ONLY = [r".*\.sh$"]
+_INCLUDE_MAN_FILES_ONLY = [r"man/.+\.1$"]
+_INCLUDE_SOURCE_FILES_ONLY = [r".*\.[ch]$"]
+_LIBWEBP_MAX_LINE_LENGTH = 80
+
+
+def _CheckCommitSubjectLength(input_api, output_api):
+  """Ensures commit's subject length is no longer than 65 chars."""
+  name = "git-commit subject"
+  cmd = ["git", "log", "-1", "--pretty=%s"]
+  start = input_api.time.time()
+  proc = subprocess2.Popen(
+      cmd,
+      stderr=subprocess2.PIPE,
+      stdout=subprocess2.PIPE,
+      universal_newlines=True)
+
+  stdout, _ = proc.communicate()
+  duration = input_api.time.time() - start
+
+  if not re.match(r"^Revert",
+                  stdout) and (len(stdout) - 1) > _GIT_COMMIT_SUBJECT_LENGTH:
+    failure_msg = (
+        "The commit subject: %s is too long (%d chars)\n"
+        "Try to keep this to 50 or less (up to 65 is permitted for "
+        "non-reverts).\n"
+        "https://www.git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-"
+        "Project#_commit_guidelines") % (stdout, len(stdout) - 1)
+    return output_api.PresubmitError("%s\n (%4.2fs) failed\n%s" %
+                                     (name, duration, failure_msg))
+
+  return output_api.PresubmitResult("%s\n (%4.2fs) success" % (name, duration))
+
+
+def _CheckDuplicateFiles(input_api, output_api):
+  """Ensures there are not repeated filenames."""
+  all_files = []
+  for f in input_api.change.AllFiles():
+    for include_file in _INCLUDE_SOURCE_FILES_ONLY:
+      if re.match(include_file, f):
+        all_files.append(f)
+        break
+
+  basename_to_path = {}
+  for f in all_files:
+    basename_file = input_api.basename(f)
+    if basename_file in basename_to_path:
+      basename_to_path[basename_file].append(f)
+    else:
+      basename_to_path[basename_file] = [f]
+
+  dupes = []
+  for files in basename_to_path.values():
+    if len(files) > 1:
+      dupes.extend(files)
+
+  if dupes:
+    return output_api.PresubmitError(
+        "Duplicate source files, rebase or rename some to make them unique:\n%s"
+        % dupes)
+  return output_api.PresubmitResult("No duplicates, success\n")
+
+
+def _GetFilesToSkip(input_api):
+  return list(input_api.DEFAULT_FILES_TO_SKIP) + [
+      r"swig/.*\.py$",
+      r"\.pylintrc$",
+  ]
+
+
+def _RunManCmd(input_api, output_api, man_file):
+  """man command wrapper."""
+  cmd = ["man", "--warnings", "-EUTF-8", "-l", "-Tutf8", "-Z", man_file]
+  name = "Check %s file." % man_file
+  start = input_api.time.time()
+  output, _ = subprocess2.communicate(
+      cmd, stdout=None, stderr=subprocess2.PIPE, universal_newlines=True)
+  duration = input_api.time.time() - start
+  if output[1]:
+    return output_api.PresubmitError("%s\n%s (%4.2fs) failed\n%s" %
+                                     (name, " ".join(cmd), duration, output[1]))
+  return output_api.PresubmitResult("%s\n%s (%4.2fs)\n" %
+                                    (name, " ".join(cmd), duration))
+
+
+def _RunShellCheckCmd(input_api, output_api, bash_file):
+  """shellcheck command wrapper."""
+  cmd = ["shellcheck", "-x", "-oall", "-sbash", bash_file]
+  name = "Check %s file." % bash_file
+  start = input_api.time.time()
+  output, rc = subprocess2.communicate(
+      cmd, stdout=None, stderr=subprocess2.PIPE, universal_newlines=True)
+  duration = input_api.time.time() - start
+  if rc == 0:
+    return output_api.PresubmitResult("%s\n%s (%4.2fs)\n" %
+                                      (name, " ".join(cmd), duration))
+  return output_api.PresubmitError("%s\n%s (%4.2fs) failed\n%s" %
+                                   (name, " ".join(cmd), duration, output[1]))
+
+
+def _RunShfmtCheckCmd(input_api, output_api, bash_file):
+  """shfmt command wrapper."""
+  cmd = [
+      "shfmt", "-i", _BASH_INDENTATION, "-bn", "-ci", "-sr", "-kp", "-d",
+      bash_file
+  ]
+  name = "Check %s file." % bash_file
+  start = input_api.time.time()
+  output, rc = subprocess2.communicate(
+      cmd, stdout=None, stderr=subprocess2.PIPE, universal_newlines=True)
+  duration = input_api.time.time() - start
+  if rc == 0:
+    return output_api.PresubmitResult("%s\n%s (%4.2fs)\n" %
+                                      (name, " ".join(cmd), duration))
+  return output_api.PresubmitError("%s\n%s (%4.2fs) failed\n%s" %
+                                   (name, " ".join(cmd), duration, output[1]))
+
+
+def _RunCmdOnCheckedFiles(input_api, output_api, run_cmd, files_to_check):
+  """Ensure that libwebp/ files are clean."""
+  file_filter = lambda x: input_api.FilterSourceFile(
+      x, files_to_check=files_to_check, files_to_skip=None)
+
+  affected_files = input_api.change.AffectedFiles(file_filter=file_filter)
+  results = [
+      run_cmd(input_api, output_api, f.AbsoluteLocalPath())
+      for f in affected_files
+  ]
+  return results
+
+
+def _CommonChecks(input_api, output_api):
+  """Ensures this patch does not have trailing spaces, extra EOLs,
+     or long lines.
+  """
+  results = []
+  results.extend(
+      input_api.canned_checks.CheckChangeHasNoCrAndHasOnlyOneEol(
+          input_api, output_api))
+  results.extend(
+      input_api.canned_checks.CheckChangeHasNoTabs(input_api, output_api))
+  results.extend(
+      input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
+          input_api, output_api))
+  results.append(_CheckCommitSubjectLength(input_api, output_api))
+  results.append(_CheckDuplicateFiles(input_api, output_api))
+
+  source_file_filter = lambda x: input_api.FilterSourceFile(
+      x, files_to_skip=_GetFilesToSkip(input_api))
+  results.extend(
+      input_api.canned_checks.CheckLongLines(
+          input_api,
+          output_api,
+          maxlen=_LIBWEBP_MAX_LINE_LENGTH,
+          source_file_filter=source_file_filter))
+
+  results.extend(
+      input_api.canned_checks.CheckPatchFormatted(
+          input_api,
+          output_api,
+          check_clang_format=False,
+          check_python=True,
+          result_factory=output_api.PresubmitError))
+  results.extend(
+      _RunCmdOnCheckedFiles(input_api, output_api, _RunManCmd,
+                            _INCLUDE_MAN_FILES_ONLY))
+  # Run pylint.
+  results.extend(
+      input_api.canned_checks.RunPylint(
+          input_api,
+          output_api,
+          files_to_skip=_GetFilesToSkip(input_api),
+          pylintrc=".pylintrc",
+          version="2.7"))
+
+  # Binaries shellcheck and shfmt are not installed in depot_tools.
+  # Installation is needed
+  try:
+    subprocess2.communicate(["shellcheck", "--version"])
+    results.extend(
+        _RunCmdOnCheckedFiles(input_api, output_api, _RunShellCheckCmd,
+                              _INCLUDE_BASH_FILES_ONLY))
+    print("shfmt")
+    subprocess2.communicate(["shfmt", "-version"])
+    results.extend(
+        _RunCmdOnCheckedFiles(input_api, output_api, _RunShfmtCheckCmd,
+                              _INCLUDE_BASH_FILES_ONLY))
+  except OSError as os_error:
+    results.append(
+        output_api.PresubmitPromptWarning(
+            "%s\nPlease install missing binaries locally." % os_error.args[0]))
+  return results
+
+
+def CheckChangeOnUpload(input_api, output_api):
+  results = []
+  results.extend(_CommonChecks(input_api, output_api))
+  return results
+
+
+def CheckChangeOnCommit(input_api, output_api):
+  results = []
+  results.extend(_CommonChecks(input_api, output_api))
+  return results
diff --git a/third_party/libwebp/README b/third_party/libwebp/README
deleted file mode 100644
index a76b378..0000000
--- a/third_party/libwebp/README
+++ /dev/null
@@ -1,782 +0,0 @@
-          __   __  ____  ____  ____
-         /  \\/  \/  _ \/  _ )/  _ \
-         \       /   __/  _  \   __/
-          \__\__/\____/\_____/__/ ____  ___
-                / _/ /    \    \ /  _ \/ _/
-               /  \_/   / /   \ \   __/  \__
-               \____/____/\_____/_____/____/v1.0.0
-
-Description:
-============
-
-WebP codec: library to encode and decode images in WebP format. This package
-contains the library that can be used in other programs to add WebP support,
-as well as the command line tools 'cwebp' and 'dwebp'.
-
-See http://developers.google.com/speed/webp
-
-The latest source tree is available at
-https://chromium.googlesource.com/webm/libwebp
-
-It is released under the same license as the WebM project.
-See http://www.webmproject.org/license/software/ or the
-"COPYING" file for details. An additional intellectual
-property rights grant can be found in the file PATENTS.
-
-Building:
-=========
-
-Windows build:
---------------
-
-By running:
-
-  nmake /f Makefile.vc CFG=release-static RTLIBCFG=static OBJDIR=output
-
-the directory output\release-static\(x64|x86)\bin will contain the tools
-cwebp.exe and dwebp.exe. The directory output\release-static\(x64|x86)\lib will
-contain the libwebp static library.
-The target architecture (x86/x64) is detected by Makefile.vc from the Visual
-Studio compiler (cl.exe) available in the system path.
-
-Unix build using makefile.unix:
--------------------------------
-
-On platforms with GNU tools installed (gcc and make), running
-
-  make -f makefile.unix
-
-will build the binaries examples/cwebp and examples/dwebp, along
-with the static library src/libwebp.a. No system-wide installation
-is supplied, as this is a simple alternative to the full installation
-system based on the autoconf tools (see below).
-Please refer to makefile.unix for additional details and customizations.
-
-Using autoconf tools:
----------------------
-Prerequisites:
-A compiler (e.g., gcc), make, autoconf, automake, libtool.
-On a Debian-like system the following should install everything you need for a
-minimal build:
-$ sudo apt-get install gcc make autoconf automake libtool
-
-When building from git sources, you will need to run autogen.sh to generate the
-configure script.
-
-./configure
-make
-make install
-
-should be all you need to have the following files
-
-/usr/local/include/webp/decode.h
-/usr/local/include/webp/encode.h
-/usr/local/include/webp/types.h
-/usr/local/lib/libwebp.*
-/usr/local/bin/cwebp
-/usr/local/bin/dwebp
-
-installed.
-
-Note: A decode-only library, libwebpdecoder, is available using the
-'--enable-libwebpdecoder' flag. The encode library is built separately and can
-be installed independently using a minor modification in the corresponding
-Makefile.am configure files (see comments there). See './configure --help' for
-more options.
-
-Building for MIPS Linux:
-------------------------
-MIPS Linux toolchain stable available releases can be found at:
-https://community.imgtec.com/developers/mips/tools/codescape-mips-sdk/available-releases/
-
-# Add toolchain to PATH
-export PATH=$PATH:/path/to/toolchain/bin
-
-# 32-bit build for mips32r5 (p5600)
-HOST=mips-mti-linux-gnu
-MIPS_CFLAGS="-O3 -mips32r5 -mabi=32 -mtune=p5600 -mmsa -mfp64 \
-  -msched-weight -mload-store-pairs -fPIE"
-MIPS_LDFLAGS="-mips32r5 -mabi=32 -mmsa -mfp64 -pie"
-
-# 64-bit build for mips64r6 (i6400)
-HOST=mips-img-linux-gnu
-MIPS_CFLAGS="-O3 -mips64r6 -mabi=64 -mtune=i6400 -mmsa -mfp64 \
-  -msched-weight -mload-store-pairs -fPIE"
-MIPS_LDFLAGS="-mips64r6 -mabi=64 -mmsa -mfp64 -pie"
-
-./configure --host=${HOST} --build=`config.guess` \
-  CC="${HOST}-gcc -EL" \
-  CFLAGS="$MIPS_CFLAGS" \
-  LDFLAGS="$MIPS_LDFLAGS"
-make
-make install
-
-CMake:
-------
-With CMake, you can compile libwebp, cwebp, dwebp, gif2web, img2webp, webpinfo
-and the JS bindings.
-
-Prerequisites:
-A compiler (e.g., gcc with autotools) and CMake.
-On a Debian-like system the following should install everything you need for a
-minimal build:
-$ sudo apt-get install build-essential cmake
-
-When building from git sources, you will need to run cmake to generate the
-makefiles.
-
-mkdir build && cd build && cmake ../
-make
-make install
-
-If you also want any of the executables, you will need to enable them through
-CMake, e.g.:
-
-cmake -DWEBP_BUILD_CWEBP=ON -DWEBP_BUILD_DWEBP=ON ../
-
-or through your favorite interface (like ccmake or cmake-qt-gui).
-
-Finally, once installed, you can also use WebP in your CMake project by doing:
-
-find_package(WebP)
-
-which will define the CMake variables WebP_INCLUDE_DIRS and WebP_LIBRARIES.
-
-Gradle:
--------
-The support for Gradle is minimal: it only helps you compile libwebp, cwebp and
-dwebp and webpmux_example.
-
-Prerequisites:
-A compiler (e.g., gcc with autotools) and gradle.
-On a Debian-like system the following should install everything you need for a
-minimal build:
-$ sudo apt-get install build-essential gradle
-
-When building from git sources, you will need to run the Gradle wrapper with the
-appropriate target, e.g. :
-
-./gradlew buildAllExecutables
-
-SWIG bindings:
---------------
-
-To generate language bindings from swig/libwebp.swig at least swig-1.3
-(http://www.swig.org) is required.
-
-Currently the following functions are mapped:
-Decode:
-  WebPGetDecoderVersion
-  WebPGetInfo
-  WebPDecodeRGBA
-  WebPDecodeARGB
-  WebPDecodeBGRA
-  WebPDecodeBGR
-  WebPDecodeRGB
-
-Encode:
-  WebPGetEncoderVersion
-  WebPEncodeRGBA
-  WebPEncodeBGRA
-  WebPEncodeRGB
-  WebPEncodeBGR
-  WebPEncodeLosslessRGBA
-  WebPEncodeLosslessBGRA
-  WebPEncodeLosslessRGB
-  WebPEncodeLosslessBGR
-
-See swig/README for more detailed build instructions.
-
-Java bindings:
-
-To build the swig-generated JNI wrapper code at least JDK-1.5 (or equivalent)
-is necessary for enum support. The output is intended to be a shared object /
-DLL that can be loaded via System.loadLibrary("webp_jni").
-
-Python bindings:
-
-To build the swig-generated Python extension code at least Python 2.6 is
-required. Python < 2.6 may build with some minor changes to libwebp.swig or the
-generated code, but is untested.
-
-Encoding tool:
-==============
-
-The examples/ directory contains tools for encoding (cwebp) and
-decoding (dwebp) images.
-
-The easiest use should look like:
-  cwebp input.png -q 80 -o output.webp
-which will convert the input file to a WebP file using a quality factor of 80
-on a 0->100 scale (0 being the lowest quality, 100 being the best. Default
-value is 75).
-You might want to try the -lossless flag too, which will compress the source
-(in RGBA format) without any loss. The -q quality parameter will in this case
-control the amount of processing time spent trying to make the output file as
-small as possible.
-
-A longer list of options is available using the -longhelp command line flag:
-
-> cwebp -longhelp
-Usage:
- cwebp [-preset <...>] [options] in_file [-o out_file]
-
-If input size (-s) for an image is not specified, it is
-assumed to be a PNG, JPEG, TIFF or WebP file.
-
-Options:
-  -h / -help ............. short help
-  -H / -longhelp ......... long help
-  -q <float> ............. quality factor (0:small..100:big), default=75
-  -alpha_q <int> ......... transparency-compression quality (0..100),
-                           default=100
-  -preset <string> ....... preset setting, one of:
-                            default, photo, picture,
-                            drawing, icon, text
-     -preset must come first, as it overwrites other parameters
-  -z <int> ............... activates lossless preset with given
-                           level in [0:fast, ..., 9:slowest]
-
-  -m <int> ............... compression method (0=fast, 6=slowest), default=4
-  -segments <int> ........ number of segments to use (1..4), default=4
-  -size <int> ............ target size (in bytes)
-  -psnr <float> .......... target PSNR (in dB. typically: 42)
-
-  -s <int> <int> ......... input size (width x height) for YUV
-  -sns <int> ............. spatial noise shaping (0:off, 100:max), default=50
-  -f <int> ............... filter strength (0=off..100), default=60
-  -sharpness <int> ....... filter sharpness (0:most .. 7:least sharp), default=0
-  -strong ................ use strong filter instead of simple (default)
-  -nostrong .............. use simple filter instead of strong
-  -sharp_yuv ............. use sharper (and slower) RGB->YUV conversion
-  -partition_limit <int> . limit quality to fit the 512k limit on
-                           the first partition (0=no degradation ... 100=full)
-  -pass <int> ............ analysis pass number (1..10)
-  -crop <x> <y> <w> <h> .. crop picture with the given rectangle
-  -resize <w> <h> ........ resize picture (after any cropping)
-  -mt .................... use multi-threading if available
-  -low_memory ............ reduce memory usage (slower encoding)
-  -map <int> ............. print map of extra info
-  -print_psnr ............ prints averaged PSNR distortion
-  -print_ssim ............ prints averaged SSIM distortion
-  -print_lsim ............ prints local-similarity distortion
-  -d <file.pgm> .......... dump the compressed output (PGM file)
-  -alpha_method <int> .... transparency-compression method (0..1), default=1
-  -alpha_filter <string> . predictive filtering for alpha plane,
-                           one of: none, fast (default) or best
-  -exact ................. preserve RGB values in transparent area, default=off
-  -blend_alpha <hex> ..... blend colors against background color
-                           expressed as RGB values written in
-                           hexadecimal, e.g. 0xc0e0d0 for red=0xc0
-                           green=0xe0 and blue=0xd0
-  -noalpha ............... discard any transparency information
-  -lossless .............. encode image losslessly, default=off
-  -near_lossless <int> ... use near-lossless image
-                           preprocessing (0..100=off), default=100
-  -hint <string> ......... specify image characteristics hint,
-                           one of: photo, picture or graph
-
-  -metadata <string> ..... comma separated list of metadata to
-                           copy from the input to the output if present.
-                           Valid values: all, none (default), exif, icc, xmp
-
-  -short ................. condense printed message
-  -quiet ................. don't print anything
-  -version ............... print version number and exit
-  -noasm ................. disable all assembly optimizations
-  -v ..................... verbose, e.g. print encoding/decoding times
-  -progress .............. report encoding progress
-
-Experimental Options:
-  -jpeg_like ............. roughly match expected JPEG size
-  -af .................... auto-adjust filter strength
-  -pre <int> ............. pre-processing filter
-
-The main options you might want to try in order to further tune the
-visual quality are:
- -preset
- -sns
- -f
- -m
-
-Namely:
-  * 'preset' will set up a default encoding configuration targeting a
-     particular type of input. It should appear first in the list of options,
-     so that subsequent options can take effect on top of this preset.
-     Default value is 'default'.
-  * 'sns' will progressively turn on (when going from 0 to 100) some additional
-     visual optimizations (like: segmentation map re-enforcement). This option
-     will balance the bit allocation differently. It tries to take bits from the
-     "easy" parts of the picture and use them in the "difficult" ones instead.
-     Usually, raising the sns value (at fixed -q value) leads to larger files,
-     but with better quality.
-     Typical value is around '75'.
-  * 'f' option directly links to the filtering strength used by the codec's
-     in-loop processing. The higher the value, the smoother the
-     highly-compressed area will look. This is particularly useful when aiming
-     at very small files. Typical values are around 20-30. Note that using the
-     option -strong/-nostrong will change the type of filtering. Use "-f 0" to
-     turn filtering off.
-  * 'm' controls the trade-off between encoding speed and quality. Default is 4.
-     You can try -m 5 or -m 6 to explore more (time-consuming) encoding
-     possibilities. A lower value will result in faster encoding at the expense
-     of quality.
-
-Decoding tool:
-==============
-
-There is a decoding sample in examples/dwebp.c which will take
-a .webp file and decode it to a PNG image file (amongst other formats).
-This is simply to demonstrate the use of the API. You can verify the
-file test.webp decodes to exactly the same as test_ref.ppm by using:
-
- cd examples
- ./dwebp test.webp -ppm -o test.ppm
- diff test.ppm test_ref.ppm
-
-The full list of options is available using -h:
-
-> dwebp -h
-Usage: dwebp in_file [options] [-o out_file]
-
-Decodes the WebP image file to PNG format [Default]
-Use following options to convert into alternate image formats:
-  -pam ......... save the raw RGBA samples as a color PAM
-  -ppm ......... save the raw RGB samples as a color PPM
-  -bmp ......... save as uncompressed BMP format
-  -tiff ........ save as uncompressed TIFF format
-  -pgm ......... save the raw YUV samples as a grayscale PGM
-                 file with IMC4 layout
-  -yuv ......... save the raw YUV samples in flat layout
-
- Other options are:
-  -version ..... print version number and exit
-  -nofancy ..... don't use the fancy YUV420 upscaler
-  -nofilter .... disable in-loop filtering
-  -nodither .... disable dithering
-  -dither <d> .. dithering strength (in 0..100)
-  -alpha_dither  use alpha-plane dithering if needed
-  -mt .......... use multi-threading
-  -crop <x> <y> <w> <h> ... crop output with the given rectangle
-  -resize <w> <h> ......... scale the output (*after* any cropping)
-  -flip ........ flip the output vertically
-  -alpha ....... only save the alpha plane
-  -incremental . use incremental decoding (useful for tests)
-  -h ........... this help message
-  -v ........... verbose (e.g. print encoding/decoding times)
-  -quiet ....... quiet mode, don't print anything
-  -noasm ....... disable all assembly optimizations
-
-WebP file analysis tool:
-========================
-
-'webpinfo' can be used to print out the chunk level structure and bitstream
-header information of WebP files. It can also check if the files are of valid
-WebP format.
-
-Usage: webpinfo [options] in_files
-Note: there could be multiple input files;
-      options must come before input files.
-Options:
-  -version ........... Print version number and exit.
-  -quiet ............. Do not show chunk parsing information.
-  -diag .............. Show parsing error diagnosis.
-  -summary ........... Show chunk stats summary.
-  -bitstream_info .... Parse bitstream header.
-
-Visualization tool:
-===================
-
-There's a little self-serve visualization tool called 'vwebp' under the
-examples/ directory. It uses OpenGL to open a simple drawing window and show
-a decoded WebP file. It's not yet integrated in the automake build system, but
-you can try to manually compile it using the recommendations below.
-
-Usage: vwebp in_file [options]
-
-Decodes the WebP image file and visualize it using OpenGL
-Options are:
-  -version ..... print version number and exit
-  -noicc ....... don't use the icc profile if present
-  -nofancy ..... don't use the fancy YUV420 upscaler
-  -nofilter .... disable in-loop filtering
-  -dither <int>  dithering strength (0..100), default=50
-  -noalphadither disable alpha plane dithering
-  -mt .......... use multi-threading
-  -info ........ print info
-  -h ........... this help message
-
-Keyboard shortcuts:
-  'c' ................ toggle use of color profile
-  'i' ................ overlay file information
-  'd' ................ disable blending & disposal (debug)
-  'q' / 'Q' / ESC .... quit
-
-Building:
----------
-
-Prerequisites:
-1) OpenGL & OpenGL Utility Toolkit (GLUT)
-  Linux:
-    $ sudo apt-get install freeglut3-dev mesa-common-dev
-  Mac + XCode:
-    - These libraries should be available in the OpenGL / GLUT frameworks.
-  Windows:
-    http://freeglut.sourceforge.net/index.php#download
-
-2) (Optional) qcms (Quick Color Management System)
-  i. Download qcms from Mozilla / Chromium:
-    http://hg.mozilla.org/mozilla-central/file/0e7639e3bdfb/gfx/qcms
-    http://src.chromium.org/viewvc/chrome/trunk/src/third_party/qcms
-  ii. Build and archive the source files as libqcms.a / qcms.lib
-  iii. Update makefile.unix / Makefile.vc
-    a) Define WEBP_HAVE_QCMS
-    b) Update include / library paths to reference the qcms directory.
-
-Build using makefile.unix / Makefile.vc:
-$ make -f makefile.unix examples/vwebp
-> nmake /f Makefile.vc CFG=release-static \
-    ../obj/x64/release-static/bin/vwebp.exe
-
-Animation creation tool:
-========================
-The utility 'img2webp' can turn a sequence of input images (PNG, JPEG, ...)
-into an animated WebP file. It offers fine control over duration, encoding
-modes, etc.
-
-Usage:
-
-  img2webp [file-level options] [image files...] [per-frame options...]
-
-File-level options (only used at the start of compression):
- -min_size ............ minimize size
- -loop <int> .......... loop count (default: 0, = infinite loop)
- -kmax <int> .......... maximum number of frame between key-frames
-                        (0=only keyframes)
- -kmin <int> .......... minimum number of frame between key-frames
-                        (0=disable key-frames altogether)
- -mixed ............... use mixed lossy/lossless automatic mode
- -v ................... verbose mode
- -h ................... this help
- -version ............. print version number and exit
-
-Per-frame options (only used for subsequent images input):
- -d <int> ............. frame duration in ms (default: 100)
- -lossless  ........... use lossless mode (default)
- -lossy ... ........... use lossy mode
- -q <float> ........... quality
- -m <int> ............. method to use
-
-example: img2webp -loop 2 in0.png -lossy in1.jpg
-                  -d 80 in2.tiff -o out.webp
-
-Animated GIF conversion:
-========================
-Animated GIF files can be converted to WebP files with animation using the
-gif2webp utility available under examples/. The files can then be viewed using
-vwebp.
-
-Usage:
- gif2webp [options] gif_file -o webp_file
-Options:
-  -h / -help ............. this help
-  -lossy ................. encode image using lossy compression
-  -mixed ................. for each frame in the image, pick lossy
-                           or lossless compression heuristically
-  -q <float> ............. quality factor (0:small..100:big)
-  -m <int> ............... compression method (0=fast, 6=slowest)
-  -min_size .............. minimize output size (default:off)
-                           lossless compression by default; can be
-                           combined with -q, -m, -lossy or -mixed
-                           options
-  -kmin <int> ............ min distance between key frames
-  -kmax <int> ............ max distance between key frames
-  -f <int> ............... filter strength (0=off..100)
-  -metadata <string> ..... comma separated list of metadata to
-                           copy from the input to the output if present
-                           Valid values: all, none, icc, xmp (default)
-  -loop_compatibility .... use compatibility mode for Chrome
-                           version prior to M62 (inclusive)
-  -mt .................... use multi-threading if available
-
-  -version ............... print version number and exit
-  -v ..................... verbose
-  -quiet ................. don't print anything
-
-Building:
----------
-With the libgif development files installed, gif2webp can be built using
-makefile.unix:
-$ make -f makefile.unix examples/gif2webp
-
-or using autoconf:
-$ ./configure --enable-everything
-$ make
-
-Comparison of animated images:
-==============================
-Test utility anim_diff under examples/ can be used to compare two animated
-images (each can be GIF or WebP).
-
-Usage: anim_diff <image1> <image2> [options]
-
-Options:
-  -dump_frames <folder> dump decoded frames in PAM format
-  -min_psnr <float> ... minimum per-frame PSNR
-  -raw_comparison ..... if this flag is not used, RGB is
-                        premultiplied before comparison
-  -max_diff <int> ..... maximum allowed difference per channel
-                        between corresponding pixels in subsequent
-                        frames
-  -h .................. this help
-  -version ............ print version number and exit
-
-Building:
----------
-With the libgif development files and a C++ compiler installed, anim_diff can
-be built using makefile.unix:
-$ make -f makefile.unix examples/anim_diff
-
-or using autoconf:
-$ ./configure --enable-everything
-$ make
-
-Encoding API:
-=============
-
-The main encoding functions are available in the header src/webp/encode.h
-The ready-to-use ones are:
-size_t WebPEncodeRGB(const uint8_t* rgb, int width, int height, int stride,
-                     float quality_factor, uint8_t** output);
-size_t WebPEncodeBGR(const uint8_t* bgr, int width, int height, int stride,
-                     float quality_factor, uint8_t** output);
-size_t WebPEncodeRGBA(const uint8_t* rgba, int width, int height, int stride,
-                      float quality_factor, uint8_t** output);
-size_t WebPEncodeBGRA(const uint8_t* bgra, int width, int height, int stride,
-                      float quality_factor, uint8_t** output);
-
-They will convert raw RGB samples to a WebP data. The only control supplied
-is the quality factor.
-
-There are some variants for using the lossless format:
-
-size_t WebPEncodeLosslessRGB(const uint8_t* rgb, int width, int height,
-                             int stride, uint8_t** output);
-size_t WebPEncodeLosslessBGR(const uint8_t* bgr, int width, int height,
-                             int stride, uint8_t** output);
-size_t WebPEncodeLosslessRGBA(const uint8_t* rgba, int width, int height,
-                              int stride, uint8_t** output);
-size_t WebPEncodeLosslessBGRA(const uint8_t* bgra, int width, int height,
-                              int stride, uint8_t** output);
-
-Of course in this case, no quality factor is needed since the compression
-occurs without loss of the input values, at the expense of larger output sizes.
-
-Advanced encoding API:
-----------------------
-
-A more advanced API is based on the WebPConfig and WebPPicture structures.
-
-WebPConfig contains the encoding settings and is not tied to a particular
-picture.
-WebPPicture contains input data, on which some WebPConfig will be used for
-compression.
-The encoding flow looks like:
-
--------------------------------------- BEGIN PSEUDO EXAMPLE
-
-#include <webp/encode.h>
-
-  // Setup a config, starting form a preset and tuning some additional
-  // parameters
-  WebPConfig config;
-  if (!WebPConfigPreset(&config, WEBP_PRESET_PHOTO, quality_factor))
-    return 0;   // version error
-  }
-  // ... additional tuning
-  config.sns_strength = 90;
-  config.filter_sharpness = 6;
-  config_error = WebPValidateConfig(&config);  // not mandatory, but useful
-
-  // Setup the input data
-  WebPPicture pic;
-  if (!WebPPictureInit(&pic)) {
-    return 0;  // version error
-  }
-  pic.width = width;
-  pic.height = height;
-  // allocated picture of dimension width x height
-  if (!WebPPictureAllocate(&pic)) {
-    return 0;   // memory error
-  }
-  // at this point, 'pic' has been initialized as a container,
-  // and can receive the Y/U/V samples.
-  // Alternatively, one could use ready-made import functions like
-  // WebPPictureImportRGB(), which will take care of memory allocation.
-  // In any case, past this point, one will have to call
-  // WebPPictureFree(&pic) to reclaim memory.
-
-  // Set up a byte-output write method. WebPMemoryWriter, for instance.
-  WebPMemoryWriter wrt;
-  WebPMemoryWriterInit(&wrt);     // initialize 'wrt'
-
-  pic.writer = MyFileWriter;
-  pic.custom_ptr = my_opaque_structure_to_make_MyFileWriter_work;
-
-  // Compress!
-  int ok = WebPEncode(&config, &pic);   // ok = 0 => error occurred!
-  WebPPictureFree(&pic);  // must be called independently of the 'ok' result.
-
-  // output data should have been handled by the writer at that point.
-  // -> compressed data is the memory buffer described by wrt.mem / wrt.size
-
-  // deallocate the memory used by compressed data
-  WebPMemoryWriterClear(&wrt);
-
--------------------------------------- END PSEUDO EXAMPLE
-
-Decoding API:
-=============
-
-This is mainly just one function to call:
-
-#include "webp/decode.h"
-uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size,
-                       int* width, int* height);
-
-Please have a look at the file src/webp/decode.h for the details.
-There are variants for decoding in BGR/RGBA/ARGB/BGRA order, along with
-decoding to raw Y'CbCr samples. One can also decode the image directly into a
-pre-allocated buffer.
-
-To detect a WebP file and gather the picture's dimensions, the function:
-  int WebPGetInfo(const uint8_t* data, size_t data_size,
-                  int* width, int* height);
-is supplied. No decoding is involved when using it.
-
-Incremental decoding API:
-=========================
-
-In the case when data is being progressively transmitted, pictures can still
-be incrementally decoded using a slightly more complicated API. Decoder state
-is stored into an instance of the WebPIDecoder object. This object can be
-created with the purpose of decoding either RGB or Y'CbCr samples.
-For instance:
-
-  WebPDecBuffer buffer;
-  WebPInitDecBuffer(&buffer);
-  buffer.colorspace = MODE_BGR;
-  ...
-  WebPIDecoder* idec = WebPINewDecoder(&buffer);
-
-As data is made progressively available, this incremental-decoder object
-can be used to decode the picture further. There are two (mutually exclusive)
-ways to pass freshly arrived data:
-
-either by appending the fresh bytes:
-
-  WebPIAppend(idec, fresh_data, size_of_fresh_data);
-
-or by just mentioning the new size of the transmitted data:
-
-  WebPIUpdate(idec, buffer, size_of_transmitted_buffer);
-
-Note that 'buffer' can be modified between each call to WebPIUpdate, in
-particular when the buffer is resized to accommodate larger data.
-
-These functions will return the decoding status: either VP8_STATUS_SUSPENDED if
-decoding is not finished yet or VP8_STATUS_OK when decoding is done. Any other
-status is an error condition.
-
-The 'idec' object must always be released (even upon an error condition) by
-calling: WebPDelete(idec).
-
-To retrieve partially decoded picture samples, one must use the corresponding
-method: WebPIDecGetRGB or WebPIDecGetYUVA.
-It will return the last displayable pixel row.
-
-Lastly, note that decoding can also be performed into a pre-allocated pixel
-buffer. This buffer must be passed when creating a WebPIDecoder, calling
-WebPINewRGB() or WebPINewYUVA().
-
-Please have a look at the src/webp/decode.h header for further details.
-
-Advanced Decoding API:
-======================
-
-WebP decoding supports an advanced API which provides on-the-fly cropping and
-rescaling, something of great usefulness on memory-constrained environments like
-mobile phones. Basically, the memory usage will scale with the output's size,
-not the input's, when one only needs a quick preview or a zoomed in portion of
-an otherwise too-large picture. Some CPU can be saved too, incidentally.
-
--------------------------------------- BEGIN PSEUDO EXAMPLE
-     // A) Init a configuration object
-     WebPDecoderConfig config;
-     CHECK(WebPInitDecoderConfig(&config));
-
-     // B) optional: retrieve the bitstream's features.
-     CHECK(WebPGetFeatures(data, data_size, &config.input) == VP8_STATUS_OK);
-
-     // C) Adjust 'config' options, if needed
-     config.options.no_fancy_upsampling = 1;
-     config.options.use_scaling = 1;
-     config.options.scaled_width = scaledWidth();
-     config.options.scaled_height = scaledHeight();
-     // etc.
-
-     // D) Specify 'config' output options for specifying output colorspace.
-     // Optionally the external image decode buffer can also be specified.
-     config.output.colorspace = MODE_BGRA;
-     // Optionally, the config.output can be pointed to an external buffer as
-     // well for decoding the image. This externally supplied memory buffer
-     // should be big enough to store the decoded picture.
-     config.output.u.RGBA.rgba = (uint8_t*) memory_buffer;
-     config.output.u.RGBA.stride = scanline_stride;
-     config.output.u.RGBA.size = total_size_of_the_memory_buffer;
-     config.output.is_external_memory = 1;
-
-     // E) Decode the WebP image. There are two variants w.r.t decoding image.
-     // The first one (E.1) decodes the full image and the second one (E.2) is
-     // used to incrementally decode the image using small input buffers.
-     // Any one of these steps can be used to decode the WebP image.
-
-     // E.1) Decode full image.
-     CHECK(WebPDecode(data, data_size, &config) == VP8_STATUS_OK);
-
-     // E.2) Decode image incrementally.
-     WebPIDecoder* const idec = WebPIDecode(NULL, NULL, &config);
-     CHECK(idec != NULL);
-     while (bytes_remaining > 0) {
-       VP8StatusCode status = WebPIAppend(idec, input, bytes_read);
-       if (status == VP8_STATUS_OK || status == VP8_STATUS_SUSPENDED) {
-         bytes_remaining -= bytes_read;
-       } else {
-         break;
-       }
-     }
-     WebPIDelete(idec);
-
-     // F) Decoded image is now in config.output (and config.output.u.RGBA).
-     // It can be saved, displayed or otherwise processed.
-
-     // G) Reclaim memory allocated in config's object. It's safe to call
-     // this function even if the memory is external and wasn't allocated
-     // by WebPDecode().
-     WebPFreeDecBuffer(&config.output);
-
--------------------------------------- END PSEUDO EXAMPLE
-
-Bugs:
-=====
-
-Please report all bugs to the issue tracker:
-    https://bugs.chromium.org/p/webp
-Patches welcome! See this page to get started:
-    http://www.webmproject.org/code/contribute/submitting-patches/
-
-Discuss:
-========
-
-Email: webp-discuss@webmproject.org
-Web: http://groups.google.com/a/webmproject.org/group/webp-discuss
diff --git a/third_party/libwebp/README.md b/third_party/libwebp/README.md
new file mode 100644
index 0000000..a877320
--- /dev/null
+++ b/third_party/libwebp/README.md
@@ -0,0 +1,53 @@
+# WebP Codec
+
+```
+      __   __  ____  ____  ____
+     /  \\/  \/  _ \/  _ )/  _ \
+     \       /   __/  _  \   __/
+      \__\__/\____/\_____/__/ ____  ___
+            / _/ /    \    \ /  _ \/ _/
+           /  \_/   / /   \ \   __/  \__
+           \____/____/\_____/_____/____/v1.3.1
+```
+
+WebP codec is a library to encode and decode images in WebP format. This package
+contains the library that can be used in other programs to add WebP support, as
+well as the command line tools 'cwebp' and 'dwebp' to compress and decompress
+images respectively.
+
+See https://developers.google.com/speed/webp for details on the image format.
+
+The latest source tree is available at
+https://chromium.googlesource.com/webm/libwebp
+
+It is released under the same license as the WebM project. See
+https://www.webmproject.org/license/software/ or the "COPYING" file for details.
+An additional intellectual property rights grant can be found in the file
+PATENTS.
+
+## Building
+
+See the [building documentation](doc/building.md).
+
+## Encoding and Decoding Tools
+
+The examples/ directory contains tools to encode and decode images and
+animations, view information about WebP images, and more. See the
+[tools documentation](doc/tools.md).
+
+## APIs
+
+See the [APIs documentation](doc/api.md), and API usage examples in the
+`examples/` directory.
+
+## Bugs
+
+Please report all bugs to the issue tracker: https://bugs.chromium.org/p/webp
+
+Patches welcome! See [how to contribute](CONTRIBUTING.md).
+
+## Discuss
+
+Email: webp-discuss@webmproject.org
+
+Web: https://groups.google.com/a/webmproject.org/group/webp-discuss
diff --git a/third_party/libwebp/README.mux b/third_party/libwebp/README.mux
deleted file mode 100644
index bd4f92f..0000000
--- a/third_party/libwebp/README.mux
+++ /dev/null
@@ -1,227 +0,0 @@
-          __   __  ____  ____  ____  __ __  _     __ __
-         /  \\/  \/  _ \/  _ \/  _ \/  \  \/ \___/_ / _\
-         \       /   __/  _  \   __/      /  /  (_/  /__
-          \__\__/\_____/_____/__/  \__//_/\_____/__/___/v1.0.0
-
-
-Description:
-============
-
-WebPMux: set of two libraries 'Mux' and 'Demux' for creation, extraction and
-manipulation of an extended format WebP file, which can have features like
-color profile, metadata and animation. Reference command-line tools 'webpmux'
-and 'vwebp' as well as the WebP container specification
-'doc/webp-container-spec.txt' are also provided in this package.
-
-WebP Mux tool:
-==============
-
-The examples/ directory contains a tool (webpmux) for manipulating WebP
-files. The webpmux tool can be used to create an extended format WebP file and
-also to extract or strip relevant data from such a file.
-
-A list of options is available using the -help command line flag:
-
-> webpmux -help
-Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT
-       webpmux -set SET_OPTIONS INPUT -o OUTPUT
-       webpmux -duration DURATION_OPTIONS [-duration ...]
-               INPUT -o OUTPUT
-       webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT
-       webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]
-               [-bgcolor BACKGROUND_COLOR] -o OUTPUT
-       webpmux -info INPUT
-       webpmux [-h|-help]
-       webpmux -version
-       webpmux argument_file_name
-
-GET_OPTIONS:
- Extract relevant data:
-   icc       get ICC profile
-   exif      get EXIF metadata
-   xmp       get XMP metadata
-   frame n   get nth frame
-
-SET_OPTIONS:
- Set color profile/metadata:
-   icc  file.icc     set ICC profile
-   exif file.exif    set EXIF metadata
-   xmp  file.xmp     set XMP metadata
-   where:    'file.icc' contains the ICC profile to be set,
-             'file.exif' contains the EXIF metadata to be set
-             'file.xmp' contains the XMP metadata to be set
-
-DURATION_OPTIONS:
- Set duration of selected frames:
-   duration            set duration for each frames
-   duration,frame      set duration of a particular frame
-   duration,start,end  set duration of frames in the
-                        interval [start,end])
-   where: 'duration' is the duration in milliseconds
-          'start' is the start frame index
-          'end' is the inclusive end frame index
-           The special 'end' value '0' means: last frame.
-
-STRIP_OPTIONS:
- Strip color profile/metadata:
-   icc       strip ICC profile
-   exif      strip EXIF metadata
-   xmp       strip XMP metadata
-
-FRAME_OPTIONS(i):
- Create animation:
-   file_i +di+[xi+yi[+mi[bi]]]
-   where:    'file_i' is the i'th animation frame (WebP format),
-             'di' is the pause duration before next frame,
-             'xi','yi' specify the image offset for this frame,
-             'mi' is the dispose method for this frame (0 or 1),
-             'bi' is the blending method for this frame (+b or -b)
-
-LOOP_COUNT:
- Number of times to repeat the animation.
- Valid range is 0 to 65535 [Default: 0 (infinite)].
-
-BACKGROUND_COLOR:
- Background color of the canvas.
-  A,R,G,B
-  where:    'A', 'R', 'G' and 'B' are integers in the range 0 to 255 specifying
-            the Alpha, Red, Green and Blue component values respectively
-            [Default: 255,255,255,255]
-
-INPUT & OUTPUT are in WebP format.
-
-Note: The nature of EXIF, XMP and ICC data is not checked and is assumed to be
-valid.
-
-Note: if a single file name is passed as the argument, the arguments will be
-tokenized from this file. The file name must not start with the character '-'.
-
-Visualization tool:
-===================
-
-The examples/ directory also contains a tool (vwebp) for viewing WebP files.
-It decodes the image and visualizes it using OpenGL. See the libwebp README
-for details on building and running this program.
-
-Mux API:
-========
-The Mux API contains methods for adding data to and reading data from WebP
-files. This API currently supports XMP/EXIF metadata, ICC profile and animation.
-Other features may be added in subsequent releases.
-
-Example#1 (pseudo code): Creating a WebPMux object with image data, color
-profile and XMP metadata.
-
-  int copy_data = 0;
-  WebPMux* mux = WebPMuxNew();
-  // ... (Prepare image data).
-  WebPMuxSetImage(mux, &image, copy_data);
-  // ... (Prepare ICC profile data).
-  WebPMuxSetChunk(mux, "ICCP", &icc_profile, copy_data);
-  // ... (Prepare XMP metadata).
-  WebPMuxSetChunk(mux, "XMP ", &xmp, copy_data);
-  // Get data from mux in WebP RIFF format.
-  WebPMuxAssemble(mux, &output_data);
-  WebPMuxDelete(mux);
-  // ... (Consume output_data; e.g. write output_data.bytes to file).
-  WebPDataClear(&output_data);
-
-
-Example#2 (pseudo code): Get image and color profile data from a WebP file.
-
-  int copy_data = 0;
-  // ... (Read data from file).
-  WebPMux* mux = WebPMuxCreate(&data, copy_data);
-  WebPMuxGetFrame(mux, 1, &image);
-  // ... (Consume image; e.g. call WebPDecode() to decode the data).
-  WebPMuxGetChunk(mux, "ICCP", &icc_profile);
-  // ... (Consume icc_profile).
-  WebPMuxDelete(mux);
-  free(data);
-
-
-For a detailed Mux API reference, please refer to the header file
-(src/webp/mux.h).
-
-Demux API:
-==========
-The Demux API enables extraction of images and extended format data from
-WebP files. This API currently supports reading of XMP/EXIF metadata, ICC
-profile and animated images. Other features may be added in subsequent
-releases.
-
-Code example: Demuxing WebP data to extract all the frames, ICC profile
-and EXIF/XMP metadata.
-
-  WebPDemuxer* demux = WebPDemux(&webp_data);
-  uint32_t width = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH);
-  uint32_t height = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT);
-  // ... (Get information about the features present in the WebP file).
-  uint32_t flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS);
-
-  // ... (Iterate over all frames).
-  WebPIterator iter;
-  if (WebPDemuxGetFrame(demux, 1, &iter)) {
-    do {
-      // ... (Consume 'iter'; e.g. Decode 'iter.fragment' with WebPDecode(),
-      // ... and get other frame properties like width, height, offsets etc.
-      // ... see 'struct WebPIterator' below for more info).
-    } while (WebPDemuxNextFrame(&iter));
-    WebPDemuxReleaseIterator(&iter);
-  }
-
-  // ... (Extract metadata).
-  WebPChunkIterator chunk_iter;
-  if (flags & ICCP_FLAG) WebPDemuxGetChunk(demux, "ICCP", 1, &chunk_iter);
-  // ... (Consume the ICC profile in 'chunk_iter.chunk').
-  WebPDemuxReleaseChunkIterator(&chunk_iter);
-  if (flags & EXIF_FLAG) WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter);
-  // ... (Consume the EXIF metadata in 'chunk_iter.chunk').
-  WebPDemuxReleaseChunkIterator(&chunk_iter);
-  if (flags & XMP_FLAG) WebPDemuxGetChunk(demux, "XMP ", 1, &chunk_iter);
-  // ... (Consume the XMP metadata in 'chunk_iter.chunk').
-  WebPDemuxReleaseChunkIterator(&chunk_iter);
-  WebPDemuxDelete(demux);
-
-
-For a detailed Demux API reference, please refer to the header file
-(src/webp/demux.h).
-
-AnimEncoder API:
-================
-The AnimEncoder API can be used to create animated WebP images.
-
-Code example:
-
-  WebPAnimEncoderOptions enc_options;
-  WebPAnimEncoderOptionsInit(&enc_options);
-  // ... (Tune 'enc_options' as needed).
-  WebPAnimEncoder* enc = WebPAnimEncoderNew(width, height, &enc_options);
-  while(<there are more frames>) {
-    WebPConfig config;
-    WebPConfigInit(&config);
-    // ... (Tune 'config' as needed).
-    WebPAnimEncoderAdd(enc, frame, duration, &config);
-  }
-  WebPAnimEncoderAssemble(enc, webp_data);
-  WebPAnimEncoderDelete(enc);
-  // ... (Write the 'webp_data' to a file, or re-mux it further).
-
-
-For a detailed AnimEncoder API reference, please refer to the header file
-(src/webp/mux.h).
-
-
-Bugs:
-=====
-
-Please report all bugs to the issue tracker:
-    https://bugs.chromium.org/p/webp
-Patches welcome! See this page to get started:
-    http://www.webmproject.org/code/contribute/submitting-patches/
-
-Discuss:
-========
-
-Email: webp-discuss@webmproject.org
-Web: http://groups.google.com/a/webmproject.org/group/webp-discuss
diff --git a/third_party/libwebp/README.webp_js b/third_party/libwebp/README.webp_js
deleted file mode 100644
index 2805354..0000000
--- a/third_party/libwebp/README.webp_js
+++ /dev/null
@@ -1,76 +0,0 @@
-     __   __ ____ ____ ____     __  ____
-    /  \\/  \  _ \  _ \  _ \   (__)/  __\
-    \       /  __/ _  \  __/   _)  \_   \
-     \__\__/_____/____/_/     /____/____/
-
-Description:
-============
-
-This file describes the compilation of libwebp into a JavaScript decoder
-using Emscripten and CMake.
-
- - install the Emscripten SDK following the procedure described at:
-   https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html
-   After installation, you should have some global variable positioned to the
-   location of the SDK. In particular, $EMSCRIPTEN should point to the
-   top-level directory containing Emscripten tools.
-
- - make sure the file $EMSCRIPTEN/cmake/Modules/Platform/Emscripten.cmake is
-   accessible. This is the toolchain file used by CMake to invoke Emscripten.
-
- - configure the project 'WEBP_JS' with CMake using:
-
- cd webp_js && \
- cmake -DWEBP_BUILD_WEBP_JS=ON \
-       -DEMSCRIPTEN_GENERATE_BITCODE_STATIC_LIBRARIES=1 \
-       -DCMAKE_TOOLCHAIN_FILE=$EMSCRIPTEN/cmake/Modules/Platform/Emscripten.cmake \
-       ../
-
- - compile webp.js using 'make'.
-
- - that's it! Upon completion, you should have the webp.js and
-   webp.js.mem files generated.
-
-The callable JavaScript function is WebPToSDL(), which decodes a raw WebP
-bitstream into a canvas. See webp_js/index.html for a simple usage sample
-(see below for instructions).
-
-Demo HTML page:
-===============
-
-   The HTML page webp_js/index.html requires an HTTP server to serve the WebP
-   image example. It's easy to just use Python for that.
-
-cd webp_js && python -m SimpleHTTPServer 8080
-
-and then navigate to http://localhost:8080 in your favorite browser.
-
-
-Web-Assembly (WASM) version:
-============================
-
-  CMakeLists.txt is configured to build the WASM version when using
-  the option WEBP_BUILD_WEBP_JS=ON. The compilation step will assemble
-  the files 'webp_wasm.js', 'webp_wasm.wasm' in the webp_js/ directory.
-  See webp_js/index_wasm.html for a simple demo page using the WASM version
-  of the library.
-
-  You will need a fairly recent version of Emscripten (at least 1.37.8) and of
-  your WASM-enabled browser to run this version. Consider it very experimental!
-
-Caveat:
-=======
-
-  - First decoding using the library is usually slower, due to just-in-time
-    compilation.
-
-  - Some versions of llvm produce the following compile error when SSE2 is
-    enabled.
-
-"Unsupported:   %516 = bitcast <8 x i16> %481 to i128
- LLVM ERROR: BitCast Instruction not yet supported for integer types larger than 64 bits"
-
-    The corresponding Emscripten bug is at:
-    https://github.com/kripken/emscripten/issues/3788
-
-    Therefore, SSE2 optimization is currently disabled in CMakeLists.txt.
diff --git a/third_party/libwebp/build.gradle b/third_party/libwebp/build.gradle
index 88ad129..c25314c 100644
--- a/third_party/libwebp/build.gradle
+++ b/third_party/libwebp/build.gradle
@@ -105,6 +105,14 @@
       sources {
         c {
           source {
+            srcDir "sharpyuv"
+            include "sharpyuv.c"
+            include "sharpyuv_cpu.c"
+            include "sharpyuv_csp.c"
+            include "sharpyuv_dsp.c"
+            include "sharpyuv_gamma.c"
+            include "sharpyuv_neon.c"
+            include "sharpyuv_sse2.c"
             srcDir "src/dec"
             include "alpha_dec.c"
             include "buffer_dec.c"
@@ -141,6 +149,7 @@
             include "lossless_msa.c"
             include "lossless_neon.$NEON"
             include "lossless_sse2.c"
+            include "lossless_sse41.c"
             include "rescaler.c"
             include "rescaler_mips32.c"
             include "rescaler_mips_dsp_r2.c"
@@ -173,9 +182,9 @@
             include "cost.c"
             include "cost_mips32.c"
             include "cost_mips_dsp_r2.c"
+            include "cost_neon.$NEON"
             include "cost_sse2.c"
             include "enc.c"
-            include "enc_avx2.c"
             include "enc_mips32.c"
             include "enc_mips_dsp_r2.c"
             include "enc_msa.c"
@@ -432,8 +441,3 @@
     }
   }
 }
-
-// Task to generate the wrapper.
-task wrapper(type: Wrapper) {
-  gradleVersion = '2.13'
-}
diff --git a/third_party/libwebp/cmake/WebPConfig.cmake.in b/third_party/libwebp/cmake/WebPConfig.cmake.in
index ef3df2f..8c883fe 100644
--- a/third_party/libwebp/cmake/WebPConfig.cmake.in
+++ b/third_party/libwebp/cmake/WebPConfig.cmake.in
@@ -1,6 +1,18 @@
+set(WebP_VERSION @PROJECT_VERSION@)
+set(WEBP_VERSION ${WebP_VERSION})
+
 @PACKAGE_INIT@
 
-set(WebP_INCLUDE_DIRS "webp")
+if(@WEBP_USE_THREAD@)
+  include(CMakeFindDependencyMacro)
+  find_dependency(Threads REQUIRED)
+endif()
+
+include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
+
+set_and_check(WebP_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@")
 set(WEBP_INCLUDE_DIRS ${WebP_INCLUDE_DIRS})
 set(WebP_LIBRARIES "@INSTALLED_LIBRARIES@")
 set(WEBP_LIBRARIES "${WebP_LIBRARIES}")
+
+check_required_components(WebP)
diff --git a/third_party/libwebp/cmake/config.h.in b/third_party/libwebp/cmake/config.h.in
index ec84acd..fe1c53a 100644
--- a/third_party/libwebp/cmake/config.h.in
+++ b/third_party/libwebp/cmake/config.h.in
@@ -16,48 +16,18 @@
 /* Define to 1 if you have the <cpu-features.h> header file. */
 #cmakedefine HAVE_CPU_FEATURES_H 1
 
-/* Define to 1 if you have the <dlfcn.h> header file. */
-#cmakedefine HAVE_DLFCN_H 1
-
 /* Define to 1 if you have the <GLUT/glut.h> header file. */
 #cmakedefine HAVE_GLUT_GLUT_H 1
 
 /* Define to 1 if you have the <GL/glut.h> header file. */
 #cmakedefine HAVE_GL_GLUT_H 1
 
-/* Define to 1 if you have the <inttypes.h> header file. */
-#cmakedefine HAVE_INTTYPES_H 1
-
-/* Define to 1 if you have the <memory.h> header file. */
-#cmakedefine HAVE_MEMORY_H 1
-
 /* Define to 1 if you have the <OpenGL/glut.h> header file. */
 #cmakedefine HAVE_OPENGL_GLUT_H 1
 
-/* Have PTHREAD_PRIO_INHERIT. */
-#cmakedefine HAVE_PTHREAD_PRIO_INHERIT @HAVE_PTHREAD_PRIO_INHERIT@
-
 /* Define to 1 if you have the <shlwapi.h> header file. */
 #cmakedefine HAVE_SHLWAPI_H 1
 
-/* Define to 1 if you have the <stdint.h> header file. */
-#cmakedefine HAVE_STDINT_H 1
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#cmakedefine HAVE_STDLIB_H 1
-
-/* Define to 1 if you have the <strings.h> header file. */
-#cmakedefine HAVE_STRINGS_H 1
-
-/* Define to 1 if you have the <string.h> header file. */
-#cmakedefine HAVE_STRING_H 1
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#cmakedefine HAVE_SYS_STAT_H 1
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#cmakedefine HAVE_SYS_TYPES_H 1
-
 /* Define to 1 if you have the <unistd.h> header file. */
 #cmakedefine HAVE_UNISTD_H 1
 
@@ -93,19 +63,9 @@
 /* Define to the version of this package. */
 #cmakedefine PACKAGE_VERSION "@PACKAGE_VERSION@"
 
-/* Define to necessary symbol if this constant uses a non-standard name on
-   your system. */
-#cmakedefine PTHREAD_CREATE_JOINABLE 1
-
-/* Define to 1 if you have the ANSI C header files. */
-#cmakedefine STDC_HEADERS 1
-
 /* Version number of package */
 #cmakedefine VERSION "@VERSION@"
 
-/* Set to 1 if AVX2 is supported */
-#cmakedefine WEBP_HAVE_AVX2 1
-
 /* Set to 1 if GIF library is installed */
 #cmakedefine WEBP_HAVE_GIF 1
 
diff --git a/third_party/libwebp/cmake/cpu.cmake b/third_party/libwebp/cmake/cpu.cmake
index 5aa1bfd..7513ca8 100644
--- a/third_party/libwebp/cmake/cpu.cmake
+++ b/third_party/libwebp/cmake/cpu.cmake
@@ -1,4 +1,12 @@
-## Check for SIMD extensions.
+#  Copyright (c) 2021 Google LLC.
+#
+#  Use of this source code is governed by a BSD-style license
+#  that can be found in the LICENSE 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.
+
+# Check for SIMD extensions.
 include(CMakePushCheckState)
 
 function(webp_check_compiler_flag WEBP_SIMD_FLAG ENABLE_SIMD)
@@ -10,7 +18,8 @@
   unset(WEBP_HAVE_FLAG_${WEBP_SIMD_FLAG} CACHE)
   cmake_push_check_state()
   set(CMAKE_REQUIRED_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR})
-  check_c_source_compiles("
+  check_c_source_compiles(
+    "
       #include \"${CMAKE_CURRENT_LIST_DIR}/../src/dsp/dsp.h\"
       int main(void) {
         #if !defined(WEBP_USE_${WEBP_SIMD_FLAG})
@@ -18,8 +27,8 @@
         #endif
         return 0;
       }
-    " WEBP_HAVE_FLAG_${WEBP_SIMD_FLAG}
-  )
+    "
+    WEBP_HAVE_FLAG_${WEBP_SIMD_FLAG})
   cmake_pop_check_state()
   if(WEBP_HAVE_FLAG_${WEBP_SIMD_FLAG})
     set(WEBP_HAVE_${WEBP_SIMD_FLAG} 1 PARENT_SCOPE)
@@ -29,28 +38,34 @@
 endfunction()
 
 # those are included in the names of WEBP_USE_* in c++ code.
-set(WEBP_SIMD_FLAGS "SSE2;SSE41;AVX2;MIPS32;MIPS_DSP_R2;NEON;MSA")
-set(WEBP_SIMD_FILE_EXTENSIONS "_sse2.c;_sse41.c;_avx2.c;_mips32.c;_mips_dsp_r2.c;_neon.c;_msa.c")
-if(MSVC)
-  # MSVC does not have a SSE4 flag but AVX2 support implies
-  # SSE4 support.
-  set(SIMD_ENABLE_FLAGS "/arch:SSE2;/arch:AVX2;/arch:AVX2;;;;")
+set(WEBP_SIMD_FLAGS "SSE41;SSE2;MIPS32;MIPS_DSP_R2;NEON;MSA")
+set(WEBP_SIMD_FILE_EXTENSIONS
+    "_sse41.c;_sse2.c;_mips32.c;_mips_dsp_r2.c;_neon.c;_msa.c")
+if(MSVC AND CMAKE_C_COMPILER_ID STREQUAL "MSVC")
+  # With at least Visual Studio 12 (2013)+ /arch is not necessary to build SSE2
+  # or SSE4 code unless a lesser /arch is forced. MSVC does not have a SSE4
+  # flag, but an AVX one. Using that with SSE4 code risks generating illegal
+  # instructions when used on machines with SSE4 only. The flags are left for
+  # older (untested) versions to avoid any potential compatibility issues.
+  if(MSVC_VERSION GREATER_EQUAL 1800 AND NOT CMAKE_C_FLAGS MATCHES "/arch:")
+    set(SIMD_ENABLE_FLAGS)
+  else()
+    set(SIMD_ENABLE_FLAGS "/arch:AVX;/arch:SSE2;;;;")
+  endif()
   set(SIMD_DISABLE_FLAGS)
 else()
-  set(SIMD_ENABLE_FLAGS "-msse2;-msse4.1;-mavx2;-mips32;-mdspr2;-mfpu=neon;-mmsa")
-  set(SIMD_DISABLE_FLAGS "-mno-sse2;-mno-sse4.1;-mno-avx2;;-mno-dspr2;;-mno-msa")
+  set(SIMD_ENABLE_FLAGS "-msse4.1;-msse2;-mips32;-mdspr2;-mfpu=neon;-mmsa")
+  set(SIMD_DISABLE_FLAGS "-mno-sse4.1;-mno-sse2;;-mno-dspr2;;-mno-msa")
 endif()
 
-set(WEBP_SIMD_FILES_TO_NOT_INCLUDE)
 set(WEBP_SIMD_FILES_TO_INCLUDE)
 set(WEBP_SIMD_FLAGS_TO_INCLUDE)
 
 if(${ANDROID})
   if(${ANDROID_ABI} STREQUAL "armeabi-v7a")
-    # This is because Android studio uses the configuration
-    # "-march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16"
-    # that does not trigger neon optimizations but should
-    # (as this configuration does not exist anymore).
+    # This is because Android studio uses the configuration "-march=armv7-a
+    # -mfloat-abi=softfp -mfpu=vfpv3-d16" that does not trigger neon
+    # optimizations but should (as this configuration does not exist anymore).
     set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon ")
   endif()
 endif()
@@ -59,6 +74,12 @@
 math(EXPR WEBP_SIMD_FLAGS_RANGE "${WEBP_SIMD_FLAGS_LENGTH} - 1")
 
 foreach(I_SIMD RANGE ${WEBP_SIMD_FLAGS_RANGE})
+  # With Emscripten 2.0.9 -msimd128 -mfpu=neon will enable NEON, but the source
+  # will fail to compile.
+  if(EMSCRIPTEN AND ${I_SIMD} GREATER_EQUAL 2)
+    break()
+  endif()
+
   list(GET WEBP_SIMD_FLAGS ${I_SIMD} WEBP_SIMD_FLAG)
 
   # First try with no extra flag added as the compiler might have default flags
@@ -69,16 +90,24 @@
   webp_check_compiler_flag(${WEBP_SIMD_FLAG} ${WEBP_ENABLE_SIMD})
   if(NOT WEBP_HAVE_${WEBP_SIMD_FLAG})
     list(GET SIMD_ENABLE_FLAGS ${I_SIMD} SIMD_COMPILE_FLAG)
+    if(EMSCRIPTEN)
+      set(SIMD_COMPILE_FLAG "-msimd128 ${SIMD_COMPILE_FLAG}")
+    endif()
     set(CMAKE_REQUIRED_FLAGS ${SIMD_COMPILE_FLAG})
     webp_check_compiler_flag(${WEBP_SIMD_FLAG} ${WEBP_ENABLE_SIMD})
   else()
-    set(SIMD_COMPILE_FLAG " ")
+    if(MSVC AND SIMD_ENABLE_FLAGS)
+      # The detection for SSE2/SSE4 support under MSVC is based on the compiler
+      # version so e.g., clang-cl will require flags to enable the assembly.
+      list(GET SIMD_ENABLE_FLAGS ${I_SIMD} SIMD_COMPILE_FLAG)
+    else()
+      set(SIMD_COMPILE_FLAG " ")
+    endif()
   endif()
   # Check which files we should include or not.
   list(GET WEBP_SIMD_FILE_EXTENSIONS ${I_SIMD} WEBP_SIMD_FILE_EXTENSION)
   file(GLOB SIMD_FILES "${CMAKE_CURRENT_LIST_DIR}/../"
-    "src/dsp/*${WEBP_SIMD_FILE_EXTENSION}"
-  )
+       "src/dsp/*${WEBP_SIMD_FILE_EXTENSION}")
   if(WEBP_HAVE_${WEBP_SIMD_FLAG})
     # Memorize the file and flags.
     foreach(FILE ${SIMD_FILES})
@@ -95,6 +124,12 @@
       list(GET SIMD_DISABLE_FLAGS ${I_SIMD} SIMD_COMPILE_FLAG)
       include(CheckCCompilerFlag)
       if(SIMD_COMPILE_FLAG)
+        # Between 3.17.0 and 3.18.2 check_cxx_compiler_flag() sets a normal
+        # variable at parent scope while check_cxx_source_compiles() continues
+        # to set an internal cache variable, so we unset both to avoid the
+        # failure / success state persisting between checks. See
+        # https://gitlab.kitware.com/cmake/cmake/-/issues/21207.
+        unset(HAS_COMPILE_FLAG)
         unset(HAS_COMPILE_FLAG CACHE)
         check_c_compiler_flag(${SIMD_COMPILE_FLAG} HAS_COMPILE_FLAG)
         if(HAS_COMPILE_FLAG)
@@ -106,12 +141,11 @@
             set(COMMON_PATTERNS)
           endif()
           set(CMAKE_REQUIRED_DEFINITIONS ${SIMD_COMPILE_FLAG})
-          check_c_source_compiles("int main(void) {return 0;}"
-            FLAG_${SIMD_COMPILE_FLAG}
-            FAIL_REGEX "warning: argument unused during compilation:"
-            ${COMMON_PATTERNS}
-          )
+          check_c_source_compiles(
+            "int main(void) {return 0;}" FLAG_${SIMD_COMPILE_FLAG} FAIL_REGEX
+            "warning: argument unused during compilation:" ${COMMON_PATTERNS})
           if(NOT FLAG_${SIMD_COMPILE_FLAG})
+            unset(HAS_COMPILE_FLAG)
             unset(HAS_COMPILE_FLAG CACHE)
           endif()
         endif()
diff --git a/third_party/libwebp/cmake/deps.cmake b/third_party/libwebp/cmake/deps.cmake
index 3d5d10a..0760ba9 100644
--- a/third_party/libwebp/cmake/deps.cmake
+++ b/third_party/libwebp/cmake/deps.cmake
@@ -1,130 +1,137 @@
+#  Copyright (c) 2021 Google LLC.
+#
+#  Use of this source code is governed by a BSD-style license
+#  that can be found in the LICENSE 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.
+
 # Generate the config.h to compile with specific intrinsics / libs.
 
-## Check for compiler options.
+# Check for compiler options.
 include(CheckCSourceCompiles)
-check_c_source_compiles("
+check_c_source_compiles(
+  "
     int main(void) {
       (void)__builtin_bswap16(0);
       return 0;
     }
   "
-  HAVE_BUILTIN_BSWAP16
-)
-check_c_source_compiles("
+  HAVE_BUILTIN_BSWAP16)
+check_c_source_compiles(
+  "
     int main(void) {
       (void)__builtin_bswap32(0);
       return 0;
     }
   "
-  HAVE_BUILTIN_BSWAP32
-)
-check_c_source_compiles("
+  HAVE_BUILTIN_BSWAP32)
+check_c_source_compiles(
+  "
     int main(void) {
       (void)__builtin_bswap64(0);
       return 0;
     }
   "
-  HAVE_BUILTIN_BSWAP64
-)
+  HAVE_BUILTIN_BSWAP64)
 
-## Check for libraries.
-find_package(Threads)
-if(Threads_FOUND)
-  if(CMAKE_USE_PTHREADS_INIT)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread")
+# Check for libraries.
+if(WEBP_USE_THREAD)
+  find_package(Threads)
+  if(Threads_FOUND)
+    # work around cmake bug on QNX (https://cmake.org/Bug/view.php?id=11333)
+    if(CMAKE_USE_PTHREADS_INIT AND NOT CMAKE_SYSTEM_NAME STREQUAL "QNX")
+      set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread")
+    endif()
+    list(APPEND WEBP_DEP_LIBRARIES Threads::Threads)
   endif()
-  foreach(PTHREAD_TEST HAVE_PTHREAD_PRIO_INHERIT PTHREAD_CREATE_UNDETACHED)
-    check_c_source_compiles("
-        #include <pthread.h>
-        int main (void) {
-          int attr = ${PTHREAD_TEST};
-          return attr;
-        }
-      " ${PTHREAD_TEST}
-    )
-  endforeach()
-  list(APPEND WEBP_DEP_LIBRARIES ${CMAKE_THREAD_LIBS_INIT})
+  set(WEBP_USE_THREAD ${Threads_FOUND})
 endif()
-set(WEBP_USE_THREAD ${Threads_FOUND})
 
 # TODO: this seems unused, check with autotools.
 set(LT_OBJDIR ".libs/")
 
 # Only useful for vwebp, so useless for now.
-# find_package(OpenGL)
-# set(WEBP_HAVE_GL ${OPENGL_FOUND})
-# set(WEBP_DEP_INCLUDE_DIRS ${WEBP_DEP_INCLUDE_DIRS} ${OPENGL_INCLUDE_DIRS})
-# set(WEBP_DEP_LIBRARIES ${WEBP_DEP_LIBRARIES} ${OPENGL_LIBRARIES})
+find_package(OpenGL)
+set(WEBP_HAVE_GL ${OPENGL_FOUND})
 
-# Find the standard C math library.
-find_library(MATH_LIBRARY NAMES m)
-if(MATH_LIBRARY)
-  list(APPEND WEBP_DEP_LIBRARIES ${MATH_LIBRARY})
+# Check if we need to link to the C math library. We do not look for it as it is
+# not found when cross-compiling, while it is here.
+check_c_source_compiles(
+  "
+    #include <math.h>
+    int main(int argc, char** argv) {
+      return (int)pow(argc, 2.5);
+    }
+  "
+  HAVE_MATH_LIBRARY)
+if(NOT HAVE_MATH_LIBRARY)
+  message(STATUS "Adding -lm flag.")
+  list(APPEND SHARPYUV_DEP_LIBRARIES m)
+  list(APPEND WEBP_DEP_LIBRARIES m)
 endif()
 
 # Find the standard image libraries.
 set(WEBP_DEP_IMG_LIBRARIES)
 set(WEBP_DEP_IMG_INCLUDE_DIRS)
-foreach(I_LIB PNG JPEG TIFF)
-  find_package(${I_LIB})
-  set(WEBP_HAVE_${I_LIB} ${${I_LIB}_FOUND})
-  if(${I_LIB}_FOUND)
-    list(APPEND WEBP_DEP_IMG_LIBRARIES ${${I_LIB}_LIBRARIES})
-    list(APPEND WEBP_DEP_IMG_INCLUDE_DIRS
-         ${${I_LIB}_INCLUDE_DIR} ${${I_LIB}_INCLUDE_DIRS})
+if(WEBP_FIND_IMG_LIBS)
+  foreach(I_LIB PNG JPEG TIFF)
+    # Disable tiff when compiling in static mode as it is failing on Ubuntu.
+    if(WEBP_LINK_STATIC AND ${I_LIB} STREQUAL "TIFF")
+      message(STATUS "TIFF is disabled when statically linking.")
+      continue()
+    endif()
+    find_package(${I_LIB})
+    set(WEBP_HAVE_${I_LIB} ${${I_LIB}_FOUND})
+    if(${I_LIB}_FOUND)
+      list(APPEND WEBP_DEP_IMG_LIBRARIES ${${I_LIB}_LIBRARIES})
+      list(APPEND WEBP_DEP_IMG_INCLUDE_DIRS ${${I_LIB}_INCLUDE_DIR}
+           ${${I_LIB}_INCLUDE_DIRS})
+    endif()
+  endforeach()
+  if(WEBP_DEP_IMG_INCLUDE_DIRS)
+    list(REMOVE_DUPLICATES WEBP_DEP_IMG_INCLUDE_DIRS)
   endif()
-endforeach()
-if(WEBP_DEP_IMG_INCLUDE_DIRS)
-  list(REMOVE_DUPLICATES WEBP_DEP_IMG_INCLUDE_DIRS)
-endif()
 
-# GIF detection, gifdec isn't part of the imageio lib.
-include(CMakePushCheckState)
-set(WEBP_DEP_GIF_LIBRARIES)
-set(WEBP_DEP_GIF_INCLUDE_DIRS)
-find_package(GIF)
-set(WEBP_HAVE_GIF ${GIF_FOUND})
-if(GIF_FOUND)
-  # GIF find_package only locates the header and library, it doesn't fail
-  # compile tests when detecting the version, but falls back to 3 (as of at
-  # least cmake 3.7.2). Make sure the library links to avoid incorrect
-  # detection when cross compiling.
-  cmake_push_check_state()
-  set(CMAKE_REQUIRED_LIBRARIES ${GIF_LIBRARIES})
-  set(CMAKE_REQUIRED_INCLUDES ${GIF_INCLUDE_DIR})
-  check_c_source_compiles("
+  # GIF detection, gifdec isn't part of the imageio lib.
+  include(CMakePushCheckState)
+  set(WEBP_DEP_GIF_LIBRARIES)
+  set(WEBP_DEP_GIF_INCLUDE_DIRS)
+  find_package(GIF)
+  set(WEBP_HAVE_GIF ${GIF_FOUND})
+  if(GIF_FOUND)
+    # GIF find_package only locates the header and library, it doesn't fail
+    # compile tests when detecting the version, but falls back to 3 (as of at
+    # least cmake 3.7.2). Make sure the library links to avoid incorrect
+    # detection when cross compiling.
+    cmake_push_check_state()
+    set(CMAKE_REQUIRED_LIBRARIES ${GIF_LIBRARIES})
+    set(CMAKE_REQUIRED_INCLUDES ${GIF_INCLUDE_DIR})
+    check_c_source_compiles(
+      "
       #include <gif_lib.h>
       int main(void) {
         (void)DGifOpenFileHandle;
         return 0;
       }
-      " GIF_COMPILES
-  )
-  cmake_pop_check_state()
-  if(GIF_COMPILES)
-    list(APPEND WEBP_DEP_GIF_LIBRARIES ${GIF_LIBRARIES})
-    list(APPEND WEBP_DEP_GIF_INCLUDE_DIRS ${GIF_INCLUDE_DIR})
-  else()
-    unset(GIF_FOUND)
+      "
+      GIF_COMPILES)
+    cmake_pop_check_state()
+    if(GIF_COMPILES)
+      list(APPEND WEBP_DEP_GIF_LIBRARIES ${GIF_LIBRARIES})
+      list(APPEND WEBP_DEP_GIF_INCLUDE_DIRS ${GIF_INCLUDE_DIR})
+    else()
+      unset(GIF_FOUND)
+    endif()
   endif()
 endif()
 
-## Check for specific headers.
+# Check for specific headers.
 include(CheckIncludeFiles)
-check_include_files("stdlib.h;stdarg.h;string.h;float.h" STDC_HEADERS)
-check_include_files(dlfcn.h HAVE_DLFCN_H)
 check_include_files(GLUT/glut.h HAVE_GLUT_GLUT_H)
 check_include_files(GL/glut.h HAVE_GL_GLUT_H)
-check_include_files(inttypes.h HAVE_INTTYPES_H)
-check_include_files(memory.h HAVE_MEMORY_H)
 check_include_files(OpenGL/glut.h HAVE_OPENGL_GLUT_H)
 check_include_files(shlwapi.h HAVE_SHLWAPI_H)
-check_include_files(stdint.h HAVE_STDINT_H)
-check_include_files(stdlib.h HAVE_STDLIB_H)
-check_include_files(strings.h HAVE_STRINGS_H)
-check_include_files(string.h HAVE_STRING_H)
-check_include_files(sys/stat.h HAVE_SYS_STAT_H)
-check_include_files(sys/types.h HAVE_SYS_TYPES_H)
 check_include_files(unistd.h HAVE_UNISTD_H)
 check_include_files(wincodec.h HAVE_WINCODEC_H)
 check_include_files(windows.h HAVE_WINDOWS_H)
@@ -134,18 +141,17 @@
   list(APPEND WEBP_DEP_LIBRARIES shlwapi ole32 windowscodecs)
 endif()
 
-## Check for SIMD extensions.
+# Check for SIMD extensions.
 include(${CMAKE_CURRENT_LIST_DIR}/cpu.cmake)
 
-## Define extra info.
+# Define extra info.
 set(PACKAGE ${PROJECT_NAME})
 set(PACKAGE_NAME ${PROJECT_NAME})
 
 # Read from configure.ac.
 file(READ ${CMAKE_CURRENT_SOURCE_DIR}/configure.ac CONFIGURE_AC)
-string(REGEX MATCHALL "\\[([0-9a-z\\.:/]*)\\]"
-  CONFIGURE_AC_PACKAGE_INFO ${CONFIGURE_AC}
-)
+string(REGEX MATCHALL "\\[([0-9a-z\\.:/]*)\\]" CONFIGURE_AC_PACKAGE_INFO
+             ${CONFIGURE_AC})
 function(strip_bracket VAR)
   string(LENGTH ${${VAR}} TMP_LEN)
   math(EXPR TMP_LEN ${TMP_LEN}-2)
diff --git a/third_party/libwebp/configure.ac b/third_party/libwebp/configure.ac
index 896e5ff..a999e0c 100644
--- a/third_party/libwebp/configure.ac
+++ b/third_party/libwebp/configure.ac
@@ -1,6 +1,6 @@
-AC_INIT([libwebp], [1.0.0],
+AC_INIT([libwebp], [1.3.1],
         [https://bugs.chromium.org/p/webp],,
-        [http://developers.google.com/speed/webp])
+        [https://developers.google.com/speed/webp])
 AC_CANONICAL_HOST
 AC_PREREQ([2.60])
 AM_INIT_AUTOMAKE([-Wall foreign subdir-objects])
@@ -9,7 +9,8 @@
 dnl === it must occur before LT_INIT (AC_PROG_LIBTOOL).
 m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
 
-AC_PROG_LIBTOOL
+dnl === AC_PROG_LIBTOOL is deprecated.
+m4_ifdef([LT_INIT], [LT_INIT], [AC_PROG_LIBTOOL])
 AC_PROG_SED
 AM_PROG_CC_C_O
 
@@ -27,11 +28,46 @@
               AS_HELP_STRING([--enable-everything],
                              [Enable all optional targets. These can still be
                               disabled with --disable-target]),
-              [SET_IF_UNSET([enable_libwebpdecoder], [$enableval])
+              [SET_IF_UNSET([enable_libsharpyuv], [$enableval])
+               SET_IF_UNSET([enable_libwebpdecoder], [$enableval])
                SET_IF_UNSET([enable_libwebpdemux], [$enableval])
                SET_IF_UNSET([enable_libwebpextras], [$enableval])
                SET_IF_UNSET([enable_libwebpmux], [$enableval])])
 
+dnl === Check whether libwebpmux should be built
+AC_MSG_CHECKING(whether libwebpmux is to be built)
+AC_ARG_ENABLE([libwebpmux],
+              AS_HELP_STRING([--disable-libwebpmux],
+                             [Disable libwebpmux @<:@default=no@:>@]),
+              [], [enable_libwebpmux=yes])
+AC_MSG_RESULT(${enable_libwebpmux-no})
+AM_CONDITIONAL([BUILD_MUX], [test "$enable_libwebpmux" = "yes"])
+
+dnl === Check whether libwebpdemux should be built
+AC_MSG_CHECKING(whether libwebpdemux is to be built)
+AC_ARG_ENABLE([libwebpdemux],
+              AS_HELP_STRING([--disable-libwebpdemux],
+                             [Disable libwebpdemux @<:@default=no@:>@]),
+              [], [enable_libwebpdemux=yes])
+AC_MSG_RESULT(${enable_libwebpdemux-no})
+AM_CONDITIONAL([BUILD_DEMUX], [test "$enable_libwebpdemux" = "yes"])
+
+dnl === Check whether decoder library should be built.
+AC_MSG_CHECKING(whether decoder library is to be built)
+AC_ARG_ENABLE([libwebpdecoder],
+              AS_HELP_STRING([--enable-libwebpdecoder],
+                             [Build libwebpdecoder @<:@default=no@:>@]))
+AC_MSG_RESULT(${enable_libwebpdecoder-no})
+AM_CONDITIONAL([BUILD_LIBWEBPDECODER], [test "$enable_libwebpdecoder" = "yes"])
+
+dnl === Check whether libwebpextras should be built
+AC_MSG_CHECKING(whether libwebpextras is to be built)
+AC_ARG_ENABLE([libwebpextras],
+              AS_HELP_STRING([--enable-libwebpextras],
+                             [Build libwebpextras @<:@default=no@:>@]))
+AC_MSG_RESULT(${enable_libwebpextras-no})
+AM_CONDITIONAL([BUILD_EXTRAS], [test "$enable_libwebpextras" = "yes"])
+
 dnl === If --enable-asserts is not defined, define NDEBUG
 
 AC_MSG_CHECKING(whether asserts are enabled)
@@ -80,6 +116,7 @@
 TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wshadow])
 TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wshorten-64-to-32])
 TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wundef])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunreachable-code-aggressive])
 TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunreachable-code])
 TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunused-but-set-variable])
 TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunused])
@@ -122,31 +159,6 @@
 AC_SUBST([AM_CFLAGS])
 
 dnl === Check for machine specific flags
-AC_ARG_ENABLE([avx2],
-              AS_HELP_STRING([--disable-avx2],
-                             [Disable detection of AVX2 support
-                              @<:@default=auto@:>@]))
-
-AS_IF([test "x$enable_avx2" != "xno" -a "x$enable_sse4_1" != "xno" \
-         -a "x$enable_sse2" != "xno"], [
-  AVX2_CFLAGS="$INTRINSICS_CFLAGS $AVX2_FLAGS"
-  TEST_AND_ADD_CFLAGS([AVX2_FLAGS], [-mavx2])
-  AS_IF([test -n "$AVX2_FLAGS"], [
-    SAVED_CFLAGS=$CFLAGS
-    CFLAGS="$CFLAGS $AVX2_FLAGS"
-    AC_CHECK_HEADER([immintrin.h],
-                    [AC_DEFINE(WEBP_HAVE_AVX2, [1],
-                     [Set to 1 if AVX2 is supported])],
-                    [AVX2_FLAGS=""],
-                    dnl it's illegal to directly include avx2intrin.h, but it's
-                    dnl included conditionally in immintrin.h, tricky!
-                    [#ifndef __AVX2__
-                     #error avx2 is not enabled
-                     #endif
-                    ])
-    CFLAGS=$SAVED_CFLAGS])
-  AC_SUBST([AVX2_FLAGS])])
-
 AC_ARG_ENABLE([sse4.1],
               AS_HELP_STRING([--disable-sse4.1],
                              [Disable detection of SSE4.1 support
@@ -249,6 +261,9 @@
         *android*) AC_CHECK_HEADERS([cpu-features.h]) ;;
       esac
       ;;
+    aarch64*|arm64*)
+      AC_DEFINE(WEBP_HAVE_NEON, [1], [Set to 1 if NEON is supported])
+      ;;
   esac
   AC_SUBST([NEON_FLAGS])])
 
@@ -433,6 +448,10 @@
 
   if test "$glut_support" = "yes" -a "$enable_libwebpdemux" = "yes"; then
     build_vwebp=yes
+  else
+    AC_MSG_NOTICE(
+      m4_normalize([Not building vwebp.
+                    OpenGL libraries and --enable-libwebpdemux are required.]))
   fi
 ])
 AM_CONDITIONAL([BUILD_VWEBP], [test "$build_vwebp" = "yes"])
@@ -492,7 +511,7 @@
   if test x"$sdl_support" = "xyes"; then
     build_vwebp_sdl=yes
   else
-    AC_MSG_WARN(Optional SDL library not found)
+    AC_MSG_NOTICE([Not building vwebp-sdl. SDL library is required.])
   fi
 ])
 
@@ -616,11 +635,17 @@
   if test "$gif_support" = "yes" -a \
           "$enable_libwebpdemux" = "yes"; then
     build_anim_diff=yes
+  else
+    AC_MSG_NOTICE(
+      [Not building anim_diff. libgif and --enable-libwebpdemux are required.])
   fi
 
   if test "$gif_support" = "yes" -a \
           "$enable_libwebpmux" = "yes"; then
     build_gif2webp=yes
+  else
+    AC_MSG_NOTICE(
+      [Not building gif2webp. libgif and --enable-libwebpmux are required.])
   fi
 ])
 AM_CONDITIONAL([BUILD_ANIMDIFF], [test "${build_anim_diff}" = "yes"])
@@ -628,11 +653,17 @@
 
 if test "$enable_libwebpdemux" = "yes" -a "$enable_libwebpmux" = "yes"; then
   build_img2webp=yes
+else
+  AC_MSG_NOTICE(
+    m4_normalize([Not building img2webp.
+                  --enable-libwebpdemux & --enable-libwebpmux are required.]))
 fi
 AM_CONDITIONAL([BUILD_IMG2WEBP], [test "${build_img2webp}" = "yes"])
 
 if test "$enable_libwebpmux" = "yes"; then
   build_webpinfo=yes
+else
+  AC_MSG_NOTICE([Not building webpinfo. --enable-libwebpdemux is required.])
 fi
 AM_CONDITIONAL([BUILD_WEBPINFO], [test "${build_webpinfo}" = "yes"])
 
@@ -716,45 +747,15 @@
   AC_MSG_RESULT([no])
 fi
 
-dnl === Check whether libwebpmux should be built
-AC_MSG_CHECKING(whether libwebpmux is to be built)
-AC_ARG_ENABLE([libwebpmux],
-              AS_HELP_STRING([--enable-libwebpmux],
-                             [Build libwebpmux @<:@default=no@:>@]))
-AC_MSG_RESULT(${enable_libwebpmux-no})
-AM_CONDITIONAL([WANT_MUX], [test "$enable_libwebpmux" = "yes"])
-
-dnl === Check whether libwebpdemux should be built
-AC_MSG_CHECKING(whether libwebpdemux is to be built)
-AC_ARG_ENABLE([libwebpdemux],
-              AS_HELP_STRING([--disable-libwebpdemux],
-                             [Disable libwebpdemux @<:@default=no@:>@]),
-              [], [enable_libwebpdemux=yes])
-AC_MSG_RESULT(${enable_libwebpdemux-no})
-AM_CONDITIONAL([WANT_DEMUX], [test "$enable_libwebpdemux" = "yes"])
-
-dnl === Check whether decoder library should be built.
-AC_MSG_CHECKING(whether decoder library is to be built)
-AC_ARG_ENABLE([libwebpdecoder],
-              AS_HELP_STRING([--enable-libwebpdecoder],
-                             [Build libwebpdecoder @<:@default=no@:>@]))
-AC_MSG_RESULT(${enable_libwebpdecoder-no})
-AM_CONDITIONAL([BUILD_LIBWEBPDECODER], [test "$enable_libwebpdecoder" = "yes"])
-
-dnl === Check whether libwebpextras should be built
-AC_MSG_CHECKING(whether libwebpextras is to be built)
-AC_ARG_ENABLE([libwebpextras],
-              AS_HELP_STRING([--enable-libwebpextras],
-                             [Build libwebpextras @<:@default=no@:>@]))
-AC_MSG_RESULT(${enable_libwebpextras-no})
-AM_CONDITIONAL([WANT_EXTRAS], [test "$enable_libwebpextras" = "yes"])
-
 dnl =========================
 
+dnl Add an empty webp_libname_prefix variable for use in *.pc.in.
+AC_SUBST([webp_libname_prefix])
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_HEADERS([src/webp/config.h])
 AC_CONFIG_FILES([Makefile src/Makefile man/Makefile \
                  examples/Makefile extras/Makefile imageio/Makefile \
+                 sharpyuv/Makefile sharpyuv/libsharpyuv.pc \
                  src/dec/Makefile src/enc/Makefile src/dsp/Makefile \
                  src/demux/Makefile src/mux/Makefile \
                  src/utils/Makefile \
diff --git a/third_party/libwebp/doc/README b/third_party/libwebp/doc/README
deleted file mode 100644
index 7f23e0c..0000000
--- a/third_party/libwebp/doc/README
+++ /dev/null
@@ -1,29 +0,0 @@
-
-Generate libwebp Container Spec Docs from Text Source
-=====================================================
-
-HTML generation requires kramdown [1], easily installed as a
-rubygem [2].  Rubygems installation should satisfy dependencies
-automatically.
-
-[1]: http://kramdown.rubyforge.org/
-[2]: http://rubygems.org/
-
-HTML generation can then be done from the project root:
-
-$ kramdown doc/webp-container-spec.txt --template doc/template.html > \
-  doc/output/webp-container-spec.html
-
-kramdown can optionally syntax highlight code blocks, using CodeRay [3],
-a dependency of kramdown that rubygems will install automatically.  The
-following will apply inline CSS styling; an external stylesheet is not
-needed.
-
-$ kramdown doc/webp-lossless-bitstream-spec.txt --template \
-  doc/template.html --coderay-css style --coderay-line-numbers ' ' \
-  --coderay-default-lang c > \
-  doc/output/webp-lossless-bitstream-spec.html
-
-Optimally, use kramdown 0.13.7 or newer if syntax highlighting desired.
-
-[3]: http://coderay.rubychan.de/
diff --git a/third_party/libwebp/doc/api.md b/third_party/libwebp/doc/api.md
new file mode 100644
index 0000000..c613ed3
--- /dev/null
+++ b/third_party/libwebp/doc/api.md
@@ -0,0 +1,385 @@
+# WebP APIs
+
+## Encoding API
+
+The main encoding functions are available in the header src/webp/encode.h
+
+The ready-to-use ones are:
+
+```c
+size_t WebPEncodeRGB(const uint8_t* rgb, int width, int height, int stride,
+                     float quality_factor, uint8_t** output);
+size_t WebPEncodeBGR(const uint8_t* bgr, int width, int height, int stride,
+                     float quality_factor, uint8_t** output);
+size_t WebPEncodeRGBA(const uint8_t* rgba, int width, int height, int stride,
+                      float quality_factor, uint8_t** output);
+size_t WebPEncodeBGRA(const uint8_t* bgra, int width, int height, int stride,
+                      float quality_factor, uint8_t** output);
+```
+
+They will convert raw RGB samples to a WebP data. The only control supplied is
+the quality factor.
+
+There are some variants for using the lossless format:
+
+```c
+size_t WebPEncodeLosslessRGB(const uint8_t* rgb, int width, int height,
+                             int stride, uint8_t** output);
+size_t WebPEncodeLosslessBGR(const uint8_t* bgr, int width, int height,
+                             int stride, uint8_t** output);
+size_t WebPEncodeLosslessRGBA(const uint8_t* rgba, int width, int height,
+                              int stride, uint8_t** output);
+size_t WebPEncodeLosslessBGRA(const uint8_t* bgra, int width, int height,
+                              int stride, uint8_t** output);
+```
+
+Of course in this case, no quality factor is needed since the compression occurs
+without loss of the input values, at the expense of larger output sizes.
+
+### Advanced encoding API
+
+A more advanced API is based on the WebPConfig and WebPPicture structures.
+
+WebPConfig contains the encoding settings and is not tied to a particular
+picture. WebPPicture contains input data, on which some WebPConfig will be used
+for compression. The encoding flow looks like:
+
+```c
+#include <webp/encode.h>
+
+// Setup a config, starting form a preset and tuning some additional
+// parameters
+WebPConfig config;
+if (!WebPConfigPreset(&config, WEBP_PRESET_PHOTO, quality_factor)) {
+  return 0;   // version error
+}
+// ... additional tuning
+config.sns_strength = 90;
+config.filter_sharpness = 6;
+config_error = WebPValidateConfig(&config);  // not mandatory, but useful
+
+// Setup the input data
+WebPPicture pic;
+if (!WebPPictureInit(&pic)) {
+  return 0;  // version error
+}
+pic.width = width;
+pic.height = height;
+// allocated picture of dimension width x height
+if (!WebPPictureAlloc(&pic)) {
+  return 0;   // memory error
+}
+// at this point, 'pic' has been initialized as a container,
+// and can receive the Y/U/V samples.
+// Alternatively, one could use ready-made import functions like
+// WebPPictureImportRGB(), which will take care of memory allocation.
+// In any case, past this point, one will have to call
+// WebPPictureFree(&pic) to reclaim memory.
+
+// Set up a byte-output write method. WebPMemoryWriter, for instance.
+WebPMemoryWriter wrt;
+WebPMemoryWriterInit(&wrt);     // initialize 'wrt'
+
+pic.writer = MyFileWriter;
+pic.custom_ptr = my_opaque_structure_to_make_MyFileWriter_work;
+
+// Compress!
+int ok = WebPEncode(&config, &pic);   // ok = 0 => error occurred!
+WebPPictureFree(&pic);  // must be called independently of the 'ok' result.
+
+// output data should have been handled by the writer at that point.
+// -> compressed data is the memory buffer described by wrt.mem / wrt.size
+
+// deallocate the memory used by compressed data
+WebPMemoryWriterClear(&wrt);
+```
+
+## Decoding API
+
+This is mainly just one function to call:
+
+```c
+#include "webp/decode.h"
+uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size,
+                       int* width, int* height);
+```
+
+Please have a look at the file src/webp/decode.h for the details. There are
+variants for decoding in BGR/RGBA/ARGB/BGRA order, along with decoding to raw
+Y'CbCr samples. One can also decode the image directly into a pre-allocated
+buffer.
+
+To detect a WebP file and gather the picture's dimensions, the function:
+
+```c
+int WebPGetInfo(const uint8_t* data, size_t data_size,
+                int* width, int* height);
+```
+
+is supplied. No decoding is involved when using it.
+
+### Incremental decoding API
+
+In the case when data is being progressively transmitted, pictures can still be
+incrementally decoded using a slightly more complicated API. Decoder state is
+stored into an instance of the WebPIDecoder object. This object can be created
+with the purpose of decoding either RGB or Y'CbCr samples. For instance:
+
+```c
+WebPDecBuffer buffer;
+WebPInitDecBuffer(&buffer);
+buffer.colorspace = MODE_BGR;
+...
+WebPIDecoder* idec = WebPINewDecoder(&buffer);
+```
+
+As data is made progressively available, this incremental-decoder object can be
+used to decode the picture further. There are two (mutually exclusive) ways to
+pass freshly arrived data:
+
+either by appending the fresh bytes:
+
+```c
+WebPIAppend(idec, fresh_data, size_of_fresh_data);
+```
+
+or by just mentioning the new size of the transmitted data:
+
+```c
+WebPIUpdate(idec, buffer, size_of_transmitted_buffer);
+```
+
+Note that 'buffer' can be modified between each call to WebPIUpdate, in
+particular when the buffer is resized to accommodate larger data.
+
+These functions will return the decoding status: either VP8_STATUS_SUSPENDED if
+decoding is not finished yet or VP8_STATUS_OK when decoding is done. Any other
+status is an error condition.
+
+The 'idec' object must always be released (even upon an error condition) by
+calling: WebPDelete(idec).
+
+To retrieve partially decoded picture samples, one must use the corresponding
+method: WebPIDecGetRGB or WebPIDecGetYUVA. It will return the last displayable
+pixel row.
+
+Lastly, note that decoding can also be performed into a pre-allocated pixel
+buffer. This buffer must be passed when creating a WebPIDecoder, calling
+WebPINewRGB() or WebPINewYUVA().
+
+Please have a look at the src/webp/decode.h header for further details.
+
+### Advanced Decoding API
+
+WebP decoding supports an advanced API which provides on-the-fly cropping and
+rescaling, something of great usefulness on memory-constrained environments like
+mobile phones. Basically, the memory usage will scale with the output's size,
+not the input's, when one only needs a quick preview or a zoomed in portion of
+an otherwise too-large picture. Some CPU can be saved too, incidentally.
+
+```c
+// A) Init a configuration object
+WebPDecoderConfig config;
+CHECK(WebPInitDecoderConfig(&config));
+
+// B) optional: retrieve the bitstream's features.
+CHECK(WebPGetFeatures(data, data_size, &config.input) == VP8_STATUS_OK);
+
+// C) Adjust 'config' options, if needed
+config.options.no_fancy_upsampling = 1;
+config.options.use_scaling = 1;
+config.options.scaled_width = scaledWidth();
+config.options.scaled_height = scaledHeight();
+// etc.
+
+// D) Specify 'config' output options for specifying output colorspace.
+// Optionally the external image decode buffer can also be specified.
+config.output.colorspace = MODE_BGRA;
+// Optionally, the config.output can be pointed to an external buffer as
+// well for decoding the image. This externally supplied memory buffer
+// should be big enough to store the decoded picture.
+config.output.u.RGBA.rgba = (uint8_t*) memory_buffer;
+config.output.u.RGBA.stride = scanline_stride;
+config.output.u.RGBA.size = total_size_of_the_memory_buffer;
+config.output.is_external_memory = 1;
+
+// E) Decode the WebP image. There are two variants w.r.t decoding image.
+// The first one (E.1) decodes the full image and the second one (E.2) is
+// used to incrementally decode the image using small input buffers.
+// Any one of these steps can be used to decode the WebP image.
+
+// E.1) Decode full image.
+CHECK(WebPDecode(data, data_size, &config) == VP8_STATUS_OK);
+
+// E.2) Decode image incrementally.
+WebPIDecoder* const idec = WebPIDecode(NULL, NULL, &config);
+CHECK(idec != NULL);
+while (bytes_remaining > 0) {
+  VP8StatusCode status = WebPIAppend(idec, input, bytes_read);
+  if (status == VP8_STATUS_OK || status == VP8_STATUS_SUSPENDED) {
+    bytes_remaining -= bytes_read;
+  } else {
+    break;
+  }
+}
+WebPIDelete(idec);
+
+// F) Decoded image is now in config.output (and config.output.u.RGBA).
+// It can be saved, displayed or otherwise processed.
+
+// G) Reclaim memory allocated in config's object. It's safe to call
+// this function even if the memory is external and wasn't allocated
+// by WebPDecode().
+WebPFreeDecBuffer(&config.output);
+```
+
+## WebP Mux
+
+WebPMux is a set of two libraries 'Mux' and 'Demux' for creation, extraction and
+manipulation of an extended format WebP file, which can have features like color
+profile, metadata and animation. Reference command-line tools `webpmux` and
+`vwebp` as well as the WebP container specification
+'doc/webp-container-spec.txt' are also provided in this package, see the
+[tools documentation](tools.md).
+
+### Mux API
+
+The Mux API contains methods for adding data to and reading data from WebP
+files. This API currently supports XMP/EXIF metadata, ICC profile and animation.
+Other features may be added in subsequent releases.
+
+Example#1 (pseudo code): Creating a WebPMux object with image data, color
+profile and XMP metadata.
+
+```c
+int copy_data = 0;
+WebPMux* mux = WebPMuxNew();
+// ... (Prepare image data).
+WebPMuxSetImage(mux, &image, copy_data);
+// ... (Prepare ICC profile data).
+WebPMuxSetChunk(mux, "ICCP", &icc_profile, copy_data);
+// ... (Prepare XMP metadata).
+WebPMuxSetChunk(mux, "XMP ", &xmp, copy_data);
+// Get data from mux in WebP RIFF format.
+WebPMuxAssemble(mux, &output_data);
+WebPMuxDelete(mux);
+// ... (Consume output_data; e.g. write output_data.bytes to file).
+WebPDataClear(&output_data);
+```
+
+Example#2 (pseudo code): Get image and color profile data from a WebP file.
+
+```c
+int copy_data = 0;
+// ... (Read data from file).
+WebPMux* mux = WebPMuxCreate(&data, copy_data);
+WebPMuxGetFrame(mux, 1, &image);
+// ... (Consume image; e.g. call WebPDecode() to decode the data).
+WebPMuxGetChunk(mux, "ICCP", &icc_profile);
+// ... (Consume icc_profile).
+WebPMuxDelete(mux);
+free(data);
+```
+
+For a detailed Mux API reference, please refer to the header file
+(src/webp/mux.h).
+
+### Demux API
+
+The Demux API enables extraction of images and extended format data from WebP
+files. This API currently supports reading of XMP/EXIF metadata, ICC profile and
+animated images. Other features may be added in subsequent releases.
+
+Code example: Demuxing WebP data to extract all the frames, ICC profile and
+EXIF/XMP metadata.
+
+```c
+WebPDemuxer* demux = WebPDemux(&webp_data);
+uint32_t width = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH);
+uint32_t height = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT);
+// ... (Get information about the features present in the WebP file).
+uint32_t flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS);
+
+// ... (Iterate over all frames).
+WebPIterator iter;
+if (WebPDemuxGetFrame(demux, 1, &iter)) {
+  do {
+    // ... (Consume 'iter'; e.g. Decode 'iter.fragment' with WebPDecode(),
+    // ... and get other frame properties like width, height, offsets etc.
+    // ... see 'struct WebPIterator' below for more info).
+  } while (WebPDemuxNextFrame(&iter));
+  WebPDemuxReleaseIterator(&iter);
+}
+
+// ... (Extract metadata).
+WebPChunkIterator chunk_iter;
+if (flags & ICCP_FLAG) WebPDemuxGetChunk(demux, "ICCP", 1, &chunk_iter);
+// ... (Consume the ICC profile in 'chunk_iter.chunk').
+WebPDemuxReleaseChunkIterator(&chunk_iter);
+if (flags & EXIF_FLAG) WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter);
+// ... (Consume the EXIF metadata in 'chunk_iter.chunk').
+WebPDemuxReleaseChunkIterator(&chunk_iter);
+if (flags & XMP_FLAG) WebPDemuxGetChunk(demux, "XMP ", 1, &chunk_iter);
+// ... (Consume the XMP metadata in 'chunk_iter.chunk').
+WebPDemuxReleaseChunkIterator(&chunk_iter);
+WebPDemuxDelete(demux);
+```
+
+For a detailed Demux API reference, please refer to the header file
+(src/webp/demux.h).
+
+## AnimEncoder API
+
+The AnimEncoder API can be used to create animated WebP images.
+
+Code example:
+
+```c
+WebPAnimEncoderOptions enc_options;
+WebPAnimEncoderOptionsInit(&enc_options);
+// ... (Tune 'enc_options' as needed).
+WebPAnimEncoder* enc = WebPAnimEncoderNew(width, height, &enc_options);
+while(<there are more frames>) {
+  WebPConfig config;
+  WebPConfigInit(&config);
+  // ... (Tune 'config' as needed).
+  WebPAnimEncoderAdd(enc, frame, duration, &config);
+}
+WebPAnimEncoderAssemble(enc, webp_data);
+WebPAnimEncoderDelete(enc);
+// ... (Write the 'webp_data' to a file, or re-mux it further).
+```
+
+For a detailed AnimEncoder API reference, please refer to the header file
+(src/webp/mux.h).
+
+## AnimDecoder API
+
+This AnimDecoder API allows decoding (possibly) animated WebP images.
+
+Code Example:
+
+```c
+WebPAnimDecoderOptions dec_options;
+WebPAnimDecoderOptionsInit(&dec_options);
+// Tune 'dec_options' as needed.
+WebPAnimDecoder* dec = WebPAnimDecoderNew(webp_data, &dec_options);
+WebPAnimInfo anim_info;
+WebPAnimDecoderGetInfo(dec, &anim_info);
+for (uint32_t i = 0; i < anim_info.loop_count; ++i) {
+  while (WebPAnimDecoderHasMoreFrames(dec)) {
+    uint8_t* buf;
+    int timestamp;
+    WebPAnimDecoderGetNext(dec, &buf, &timestamp);
+    // ... (Render 'buf' based on 'timestamp').
+    // ... (Do NOT free 'buf', as it is owned by 'dec').
+  }
+  WebPAnimDecoderReset(dec);
+}
+const WebPDemuxer* demuxer = WebPAnimDecoderGetDemuxer(dec);
+// ... (Do something using 'demuxer'; e.g. get EXIF/XMP/ICC data).
+WebPAnimDecoderDelete(dec);
+```
+
+For a detailed AnimDecoder API reference, please refer to the header file
+(src/webp/demux.h).
diff --git a/third_party/libwebp/doc/building.md b/third_party/libwebp/doc/building.md
new file mode 100644
index 0000000..5efeab9
--- /dev/null
+++ b/third_party/libwebp/doc/building.md
@@ -0,0 +1,213 @@
+# Building
+
+## Windows build
+
+By running:
+
+```batch
+nmake /f Makefile.vc CFG=release-static RTLIBCFG=static OBJDIR=output
+```
+
+the directory `output\release-static\(x64|x86)\bin` will contain the tools
+cwebp.exe and dwebp.exe. The directory `output\release-static\(x64|x86)\lib`
+will contain the libwebp static library. The target architecture (x86/x64) is
+detected by Makefile.vc from the Visual Studio compiler (cl.exe) available in
+the system path.
+
+## Unix build using makefile.unix
+
+On platforms with GNU tools installed (gcc and make), running
+
+```shell
+make -f makefile.unix
+```
+
+will build the binaries examples/cwebp and examples/dwebp, along with the static
+library src/libwebp.a. No system-wide installation is supplied, as this is a
+simple alternative to the full installation system based on the autoconf tools
+(see below). Please refer to makefile.unix for additional details and
+customizations.
+
+## Using autoconf tools
+
+Prerequisites: a compiler (e.g., gcc), make, autoconf, automake, libtool.
+
+On a Debian-like system the following should install everything you need for a
+minimal build:
+
+```shell
+$ sudo apt-get install gcc make autoconf automake libtool
+```
+
+When building from git sources, you will need to run autogen.sh to generate the
+configure script.
+
+```shell
+./configure
+make
+make install
+```
+
+should be all you need to have the following files
+
+```
+/usr/local/include/webp/decode.h
+/usr/local/include/webp/encode.h
+/usr/local/include/webp/types.h
+/usr/local/lib/libwebp.*
+/usr/local/bin/cwebp
+/usr/local/bin/dwebp
+```
+
+installed.
+
+Note: A decode-only library, libwebpdecoder, is available using the
+`--enable-libwebpdecoder` flag. The encode library is built separately and can
+be installed independently using a minor modification in the corresponding
+Makefile.am configure files (see comments there). See `./configure --help` for
+more options.
+
+## Building for MIPS Linux
+
+MIPS Linux toolchain stable available releases can be found at:
+https://community.imgtec.com/developers/mips/tools/codescape-mips-sdk/available-releases/
+
+```shell
+# Add toolchain to PATH
+export PATH=$PATH:/path/to/toolchain/bin
+
+# 32-bit build for mips32r5 (p5600)
+HOST=mips-mti-linux-gnu
+MIPS_CFLAGS="-O3 -mips32r5 -mabi=32 -mtune=p5600 -mmsa -mfp64 \
+  -msched-weight -mload-store-pairs -fPIE"
+MIPS_LDFLAGS="-mips32r5 -mabi=32 -mmsa -mfp64 -pie"
+
+# 64-bit build for mips64r6 (i6400)
+HOST=mips-img-linux-gnu
+MIPS_CFLAGS="-O3 -mips64r6 -mabi=64 -mtune=i6400 -mmsa -mfp64 \
+  -msched-weight -mload-store-pairs -fPIE"
+MIPS_LDFLAGS="-mips64r6 -mabi=64 -mmsa -mfp64 -pie"
+
+./configure --host=${HOST} --build=`config.guess` \
+  CC="${HOST}-gcc -EL" \
+  CFLAGS="$MIPS_CFLAGS" \
+  LDFLAGS="$MIPS_LDFLAGS"
+make
+make install
+```
+
+## CMake
+
+With CMake, you can compile libwebp, cwebp, dwebp, gif2webp, img2webp, webpinfo
+and the JS bindings.
+
+Prerequisites: a compiler (e.g., gcc with autotools) and CMake.
+
+On a Debian-like system the following should install everything you need for a
+minimal build:
+
+```shell
+$ sudo apt-get install build-essential cmake
+```
+
+When building from git sources, you will need to run cmake to generate the
+makefiles.
+
+```shell
+mkdir build && cd build && cmake ../
+make
+make install
+```
+
+If you also want any of the executables, you will need to enable them through
+CMake, e.g.:
+
+```shell
+cmake -DWEBP_BUILD_CWEBP=ON -DWEBP_BUILD_DWEBP=ON ../
+```
+
+or through your favorite interface (like ccmake or cmake-qt-gui).
+
+Use option `-DWEBP_UNICODE=ON` for Unicode support on Windows (with chcp 65001).
+
+Finally, once installed, you can also use WebP in your CMake project by doing:
+
+```cmake
+find_package(WebP)
+```
+
+which will define the CMake variables WebP_INCLUDE_DIRS and WebP_LIBRARIES.
+
+## Gradle
+
+The support for Gradle is minimal: it only helps you compile libwebp, cwebp and
+dwebp and webpmux_example.
+
+Prerequisites: a compiler (e.g., gcc with autotools) and gradle.
+
+On a Debian-like system the following should install everything you need for a
+minimal build:
+
+```shell
+$ sudo apt-get install build-essential gradle
+```
+
+When building from git sources, you will need to run the Gradle wrapper with the
+appropriate target, e.g. :
+
+```shell
+./gradlew buildAllExecutables
+```
+
+## SWIG bindings
+
+To generate language bindings from swig/libwebp.swig at least swig-1.3
+(http://www.swig.org) is required.
+
+Currently the following functions are mapped:
+
+Decode:
+
+```
+WebPGetDecoderVersion
+WebPGetInfo
+WebPDecodeRGBA
+WebPDecodeARGB
+WebPDecodeBGRA
+WebPDecodeBGR
+WebPDecodeRGB
+```
+
+Encode:
+
+```
+WebPGetEncoderVersion
+WebPEncodeRGBA
+WebPEncodeBGRA
+WebPEncodeRGB
+WebPEncodeBGR
+WebPEncodeLosslessRGBA
+WebPEncodeLosslessBGRA
+WebPEncodeLosslessRGB
+WebPEncodeLosslessBGR
+```
+
+See also the [swig documentation](../swig/README.md) for more detailed build
+instructions and usage examples.
+
+### Java bindings
+
+To build the swig-generated JNI wrapper code at least JDK-1.5 (or equivalent) is
+necessary for enum support. The output is intended to be a shared object / DLL
+that can be loaded via `System.loadLibrary("webp_jni")`.
+
+### Python bindings
+
+To build the swig-generated Python extension code at least Python 2.6 is
+required. Python < 2.6 may build with some minor changes to libwebp.swig or the
+generated code, but is untested.
+
+## Javascript decoder
+
+Libwebp can be compiled into a JavaScript decoder using Emscripten and CMake.
+See the [corresponding documentation](../README.md)
diff --git a/third_party/libwebp/doc/specs_generation.md b/third_party/libwebp/doc/specs_generation.md
new file mode 100644
index 0000000..0380d66
--- /dev/null
+++ b/third_party/libwebp/doc/specs_generation.md
@@ -0,0 +1,26 @@
+# Generate libwebp Container Spec Docs from Text Source
+
+HTML generation requires [kramdown](https://kramdown.gettalong.org/), easily
+installed as a [rubygem](https://rubygems.org/). Rubygems installation should
+satisfy dependencies automatically.
+
+HTML generation can then be done from the project root:
+
+```shell
+$ kramdown doc/webp-container-spec.txt --template doc/template.html > \
+  doc/output/webp-container-spec.html
+```
+
+kramdown can optionally syntax highlight code blocks, using
+[CodeRay](https://github.com/rubychan/coderay), a dependency of kramdown that
+rubygems will install automatically. The following will apply inline CSS
+styling; an external stylesheet is not needed.
+
+```shell
+$ kramdown doc/webp-lossless-bitstream-spec.txt --template \
+  doc/template.html --coderay-css style --coderay-line-numbers ' ' \
+  --coderay-default-lang c > \
+  doc/output/webp-lossless-bitstream-spec.html
+```
+
+Optimally, use kramdown 0.13.7 or newer if syntax highlighting desired.
diff --git a/third_party/libwebp/doc/tools.md b/third_party/libwebp/doc/tools.md
new file mode 100644
index 0000000..bf49274
--- /dev/null
+++ b/third_party/libwebp/doc/tools.md
@@ -0,0 +1,516 @@
+# WebP tools
+
+## Encoding tool
+
+The examples/ directory contains tools for encoding (cwebp) and decoding (dwebp)
+images.
+
+The easiest use should look like:
+
+```shell
+cwebp input.png -q 80 -o output.webp
+```
+
+which will convert the input file to a WebP file using a quality factor of 80 on
+a 0->100 scale (0 being the lowest quality, 100 being the best. Default value is
+75).
+
+You might want to try the `-lossless` flag too, which will compress the source
+(in RGBA format) without any loss. The `-q` quality parameter will in this case
+control the amount of processing time spent trying to make the output file as
+small as possible.
+
+A longer list of options is available using the `-longhelp` command line flag:
+
+```shell
+> cwebp -longhelp
+Usage:
+ cwebp [-preset <...>] [options] in_file [-o out_file]
+```
+
+If input size (-s) for an image is not specified, it is assumed to be a PNG,
+JPEG, TIFF or WebP file. Note: Animated PNG and WebP files are not supported.
+
+Options:
+
+```
+-h / -help ............. short help
+-H / -longhelp ......... long help
+-q <float> ............. quality factor (0:small..100:big), default=75
+-alpha_q <int> ......... transparency-compression quality (0..100),
+                         default=100
+-preset <string> ....... preset setting, one of:
+                          default, photo, picture,
+                          drawing, icon, text
+   -preset must come first, as it overwrites other parameters
+-z <int> ............... activates lossless preset with given
+                         level in [0:fast, ..., 9:slowest]
+
+-m <int> ............... compression method (0=fast, 6=slowest), default=4
+-segments <int> ........ number of segments to use (1..4), default=4
+-size <int> ............ target size (in bytes)
+-psnr <float> .......... target PSNR (in dB. typically: 42)
+
+-s <int> <int> ......... input size (width x height) for YUV
+-sns <int> ............. spatial noise shaping (0:off, 100:max), default=50
+-f <int> ............... filter strength (0=off..100), default=60
+-sharpness <int> ....... filter sharpness (0:most .. 7:least sharp), default=0
+-strong ................ use strong filter instead of simple (default)
+-nostrong .............. use simple filter instead of strong
+-sharp_yuv ............. use sharper (and slower) RGB->YUV conversion
+-partition_limit <int> . limit quality to fit the 512k limit on
+                         the first partition (0=no degradation ... 100=full)
+-pass <int> ............ analysis pass number (1..10)
+-qrange <min> <max> .... specifies the permissible quality range
+                         (default: 0 100)
+-crop <x> <y> <w> <h> .. crop picture with the given rectangle
+-resize <w> <h> ........ resize picture (*after* any cropping)
+-mt .................... use multi-threading if available
+-low_memory ............ reduce memory usage (slower encoding)
+-map <int> ............. print map of extra info
+-print_psnr ............ prints averaged PSNR distortion
+-print_ssim ............ prints averaged SSIM distortion
+-print_lsim ............ prints local-similarity distortion
+-d <file.pgm> .......... dump the compressed output (PGM file)
+-alpha_method <int> .... transparency-compression method (0..1), default=1
+-alpha_filter <string> . predictive filtering for alpha plane,
+                         one of: none, fast (default) or best
+-exact ................. preserve RGB values in transparent area, default=off
+-blend_alpha <hex> ..... blend colors against background color
+                         expressed as RGB values written in
+                         hexadecimal, e.g. 0xc0e0d0 for red=0xc0
+                         green=0xe0 and blue=0xd0
+-noalpha ............... discard any transparency information
+-lossless .............. encode image losslessly, default=off
+-near_lossless <int> ... use near-lossless image preprocessing
+                         (0..100=off), default=100
+-hint <string> ......... specify image characteristics hint,
+                         one of: photo, picture or graph
+
+-metadata <string> ..... comma separated list of metadata to
+                         copy from the input to the output if present.
+                         Valid values: all, none (default), exif, icc, xmp
+
+-short ................. condense printed message
+-quiet ................. don't print anything
+-version ............... print version number and exit
+-noasm ................. disable all assembly optimizations
+-v ..................... verbose, e.g. print encoding/decoding times
+-progress .............. report encoding progress
+```
+
+Experimental Options:
+
+```
+-jpeg_like ............. roughly match expected JPEG size
+-af .................... auto-adjust filter strength
+-pre <int> ............. pre-processing filter
+```
+
+The main options you might want to try in order to further tune the visual
+quality are:
+
+-preset -sns -f -m
+
+Namely:
+
+*   `preset` will set up a default encoding configuration targeting a particular
+    type of input. It should appear first in the list of options, so that
+    subsequent options can take effect on top of this preset. Default value is
+    'default'.
+*   `sns` will progressively turn on (when going from 0 to 100) some additional
+    visual optimizations (like: segmentation map re-enforcement). This option
+    will balance the bit allocation differently. It tries to take bits from the
+    "easy" parts of the picture and use them in the "difficult" ones instead.
+    Usually, raising the sns value (at fixed -q value) leads to larger files,
+    but with better quality. Typical value is around '75'.
+*   `f` option directly links to the filtering strength used by the codec's
+    in-loop processing. The higher the value, the smoother the highly-compressed
+    area will look. This is particularly useful when aiming at very small files.
+    Typical values are around 20-30. Note that using the option
+    -strong/-nostrong will change the type of filtering. Use "-f 0" to turn
+    filtering off.
+*   `m` controls the trade-off between encoding speed and quality. Default is 4.
+    You can try -m 5 or -m 6 to explore more (time-consuming) encoding
+    possibilities. A lower value will result in faster encoding at the expense
+    of quality.
+
+## Decoding tool
+
+There is a decoding sample in examples/dwebp.c which will take a .webp file and
+decode it to a PNG image file (amongst other formats). This is simply to
+demonstrate the use of the API. You can verify the file test.webp decodes to
+exactly the same as test_ref.ppm by using:
+
+```shell
+cd examples
+./dwebp test.webp -ppm -o test.ppm
+diff test.ppm test_ref.ppm
+```
+
+The full list of options is available using -h:
+
+```shell
+> dwebp -h
+Usage: dwebp in_file [options] [-o out_file]
+```
+
+Decodes the WebP image file to PNG format [Default]. Note: Animated WebP files
+are not supported.
+
+Use following options to convert into alternate image formats:
+
+```
+-pam ......... save the raw RGBA samples as a color PAM
+-ppm ......... save the raw RGB samples as a color PPM
+-bmp ......... save as uncompressed BMP format
+-tiff ........ save as uncompressed TIFF format
+-pgm ......... save the raw YUV samples as a grayscale PGM
+               file with IMC4 layout
+-yuv ......... save the raw YUV samples in flat layout
+```
+
+Other options are:
+
+```
+-version ..... print version number and exit
+-nofancy ..... don't use the fancy YUV420 upscaler
+-nofilter .... disable in-loop filtering
+-nodither .... disable dithering
+-dither <d> .. dithering strength (in 0..100)
+-alpha_dither  use alpha-plane dithering if needed
+-mt .......... use multi-threading
+-crop <x> <y> <w> <h> ... crop output with the given rectangle
+-resize <w> <h> ......... resize output (*after* any cropping)
+-flip ........ flip the output vertically
+-alpha ....... only save the alpha plane
+-incremental . use incremental decoding (useful for tests)
+-h ........... this help message
+-v ........... verbose (e.g. print encoding/decoding times)
+-quiet ....... quiet mode, don't print anything
+-noasm ....... disable all assembly optimizations
+```
+
+## WebP file analysis tool
+
+`webpinfo` can be used to print out the chunk level structure and bitstream
+header information of WebP files. It can also check if the files are of valid
+WebP format.
+
+Usage:
+
+```shell
+webpinfo [options] in_files
+```
+
+Note: there could be multiple input files; options must come before input files.
+
+Options:
+
+```
+-version ........... Print version number and exit.
+-quiet ............. Do not show chunk parsing information.
+-diag .............. Show parsing error diagnosis.
+-summary ........... Show chunk stats summary.
+-bitstream_info .... Parse bitstream header.
+```
+
+## Visualization tool
+
+There's a little self-serve visualization tool called 'vwebp' under the
+examples/ directory. It uses OpenGL to open a simple drawing window and show a
+decoded WebP file. It's not yet integrated in the automake build system, but you
+can try to manually compile it using the recommendations below.
+
+Usage:
+
+```shell
+vwebp in_file [options]
+```
+
+Decodes the WebP image file and visualize it using OpenGL
+
+Options are:
+
+```
+-version ..... print version number and exit
+-noicc ....... don't use the icc profile if present
+-nofancy ..... don't use the fancy YUV420 upscaler
+-nofilter .... disable in-loop filtering
+-dither <int>  dithering strength (0..100), default=50
+-noalphadither disable alpha plane dithering
+-usebgcolor .. display background color
+-mt .......... use multi-threading
+-info ........ print info
+-h ........... this help message
+```
+
+Keyboard shortcuts:
+
+```
+'c' ................ toggle use of color profile
+'b' ................ toggle background color display
+'i' ................ overlay file information
+'d' ................ disable blending & disposal (debug)
+'q' / 'Q' / ESC .... quit
+```
+
+### Building
+
+Prerequisites:
+
+1.  OpenGL & OpenGL Utility Toolkit (GLUT)
+
+    Linux: `sudo apt-get install freeglut3-dev mesa-common-dev`
+
+    Mac + Xcode: These libraries should be available in the OpenGL / GLUT
+    frameworks.
+
+    Windows: http://freeglut.sourceforge.net/index.php#download
+
+2.  (Optional) qcms (Quick Color Management System)
+
+    1.  Download qcms from Mozilla / Chromium:
+        https://hg.mozilla.org/mozilla-central/file/0e7639e3bdfb/gfx/qcms
+        https://source.chromium.org/chromium/chromium/src/+/main:third_party/qcms/;drc=d4a2f8e1ed461d8fc05ed88d1ae2dc94c9773825
+    2.  Build and archive the source files as libqcms.a / qcms.lib
+    3.  Update makefile.unix / Makefile.vc
+        1.  Define WEBP_HAVE_QCMS
+        2.  Update include / library paths to reference the qcms directory.
+
+Build using makefile.unix / Makefile.vc:
+
+```shell
+$ make -f makefile.unix examples/vwebp
+> nmake /f Makefile.vc CFG=release-static \
+    ../obj/x64/release-static/bin/vwebp.exe
+```
+
+## Animation creation tool
+
+The utility `img2webp` can turn a sequence of input images (PNG, JPEG, ...) into
+an animated WebP file. It offers fine control over duration, encoding modes,
+etc.
+
+Usage:
+
+```shell
+img2webp [file_options] [[frame_options] frame_file]... [-o webp_file]
+```
+
+File-level options (only used at the start of compression):
+
+```
+-min_size ............ minimize size
+-kmax <int> .......... maximum number of frame between key-frames
+                        (0=only keyframes)
+-kmin <int> .......... minimum number of frame between key-frames
+                        (0=disable key-frames altogether)
+-mixed ............... use mixed lossy/lossless automatic mode
+-near_lossless <int> . use near-lossless image preprocessing
+                       (0..100=off), default=100
+-sharp_yuv ........... use sharper (and slower) RGB->YUV conversion
+                       (lossy only)
+-loop <int> .......... loop count (default: 0, = infinite loop)
+-v ................... verbose mode
+-h ................... this help
+-version ............. print version number and exit
+```
+
+Per-frame options (only used for subsequent images input):
+
+```
+-d <int> ............. frame duration in ms (default: 100)
+-lossless  ........... use lossless mode (default)
+-lossy ... ........... use lossy mode
+-q <float> ........... quality
+-m <int> ............. method to use
+```
+
+example: `img2webp -loop 2 in0.png -lossy in1.jpg -d 80 in2.tiff -o out.webp`
+
+Note: if a single file name is passed as the argument, the arguments will be
+tokenized from this file. The file name must not start with the character '-'.
+
+## Animated GIF conversion
+
+Animated GIF files can be converted to WebP files with animation using the
+gif2webp utility available under examples/. The files can then be viewed using
+vwebp.
+
+Usage:
+
+```shell
+gif2webp [options] gif_file -o webp_file
+```
+
+Options:
+
+```
+-h / -help ............. this help
+-lossy ................. encode image using lossy compression
+-mixed ................. for each frame in the image, pick lossy
+                         or lossless compression heuristically
+-q <float> ............. quality factor (0:small..100:big)
+-m <int> ............... compression method (0=fast, 6=slowest)
+-min_size .............. minimize output size (default:off)
+                         lossless compression by default; can be
+                         combined with -q, -m, -lossy or -mixed
+                         options
+-kmin <int> ............ min distance between key frames
+-kmax <int> ............ max distance between key frames
+-f <int> ............... filter strength (0=off..100)
+-metadata <string> ..... comma separated list of metadata to
+                         copy from the input to the output if present
+                         Valid values: all, none, icc, xmp (default)
+-loop_compatibility .... use compatibility mode for Chrome
+                         version prior to M62 (inclusive)
+-mt .................... use multi-threading if available
+
+-version ............... print version number and exit
+-v ..................... verbose
+-quiet ................. don't print anything
+```
+
+### Building
+
+With the libgif development files installed, gif2webp can be built using
+makefile.unix:
+
+```shell
+$ make -f makefile.unix examples/gif2webp
+```
+
+or using autoconf:
+
+```shell
+$ ./configure --enable-everything
+$ make
+```
+
+## Comparison of animated images
+
+Test utility anim_diff under examples/ can be used to compare two animated
+images (each can be GIF or WebP).
+
+Usage:
+
+```shell
+anim_diff <image1> <image2> [options]
+```
+
+Options:
+
+```
+-dump_frames <folder> dump decoded frames in PAM format
+-min_psnr <float> ... minimum per-frame PSNR
+-raw_comparison ..... if this flag is not used, RGB is
+                      premultiplied before comparison
+-max_diff <int> ..... maximum allowed difference per channel
+                      between corresponding pixels in subsequent
+                      frames
+-h .................. this help
+-version ............ print version number and exit
+```
+
+### Building
+
+With the libgif development files installed, anim_diff can be built using
+makefile.unix:
+
+```shell
+$ make -f makefile.unix examples/anim_diff
+```
+
+or using autoconf:
+
+```shell
+$ ./configure --enable-everything
+$ make
+```
+
+## WebP Mux tool
+
+The examples/ directory contains a tool (webpmux) for manipulating WebP files.
+The webpmux tool can be used to create an extended format WebP file and also to
+extract or strip relevant data from such a file.
+
+A list of options is available using the -help command line flag:
+
+```shell
+> webpmux -help
+Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT
+       webpmux -set SET_OPTIONS INPUT -o OUTPUT
+       webpmux -duration DURATION_OPTIONS [-duration ...]
+               INPUT -o OUTPUT
+       webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT
+       webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]
+               [-bgcolor BACKGROUND_COLOR] -o OUTPUT
+       webpmux -info INPUT
+       webpmux [-h|-help]
+       webpmux -version
+       webpmux argument_file_name
+
+GET_OPTIONS:
+ Extract relevant data:
+   icc       get ICC profile
+   exif      get EXIF metadata
+   xmp       get XMP metadata
+   frame n   get nth frame
+
+SET_OPTIONS:
+ Set color profile/metadata/parameters:
+   loop LOOP_COUNT            set the loop count
+   bgcolor BACKGROUND_COLOR   set the animation background color
+   icc  file.icc              set ICC profile
+   exif file.exif             set EXIF metadata
+   xmp  file.xmp              set XMP metadata
+   where:    'file.icc' contains the ICC profile to be set,
+             'file.exif' contains the EXIF metadata to be set
+             'file.xmp' contains the XMP metadata to be set
+
+DURATION_OPTIONS:
+ Set duration of selected frames:
+   duration            set duration for all frames
+   duration,frame      set duration of a particular frame
+   duration,start,end  set duration of frames in the
+                        interval [start,end])
+   where: 'duration' is the duration in milliseconds
+          'start' is the start frame index
+          'end' is the inclusive end frame index
+           The special 'end' value '0' means: last frame.
+
+STRIP_OPTIONS:
+ Strip color profile/metadata:
+   icc       strip ICC profile
+   exif      strip EXIF metadata
+   xmp       strip XMP metadata
+
+FRAME_OPTIONS(i):
+ Create animation:
+   file_i +di[+xi+yi[+mi[bi]]]
+   where:    'file_i' is the i'th animation frame (WebP format),
+             'di' is the pause duration before next frame,
+             'xi','yi' specify the image offset for this frame,
+             'mi' is the dispose method for this frame (0 or 1),
+             'bi' is the blending method for this frame (+b or -b)
+
+LOOP_COUNT:
+ Number of times to repeat the animation.
+ Valid range is 0 to 65535 [Default: 0 (infinite)].
+
+BACKGROUND_COLOR:
+ Background color of the canvas.
+  A,R,G,B
+  where:    'A', 'R', 'G' and 'B' are integers in the range 0 to 255 specifying
+            the Alpha, Red, Green and Blue component values respectively
+            [Default: 255,255,255,255]
+
+INPUT & OUTPUT are in WebP format.
+
+Note: The nature of EXIF, XMP and ICC data is not checked and is assumed to be
+valid.
+
+Note: if a single file name is passed as the argument, the arguments will be
+tokenized from this file. The file name must not start with the character '-'.
+```
diff --git a/third_party/libwebp/doc/webp-container-spec.txt b/third_party/libwebp/doc/webp-container-spec.txt
index 94a7ca2..3e66526 100644
--- a/third_party/libwebp/doc/webp-container-spec.txt
+++ b/third_party/libwebp/doc/webp-container-spec.txt
@@ -2,10 +2,10 @@
 
 Although you may be viewing an alternate representation, this document
 is sourced in Markdown, a light-duty markup scheme, and is optimized for
-the [kramdown](http://kramdown.rubyforge.org/) transformer.
+the [kramdown](https://kramdown.gettalong.org/) transformer.
 
-See the accompanying README. External link targets are referenced at the
-end of this file.
+See the accompanying specs_generation.md. External link targets are referenced
+at the end of this file.
 
 -->
 
@@ -20,25 +20,25 @@
 Introduction
 ------------
 
-WebP is an image format that uses either (i) the VP8 key frame encoding
-to compress image data in a lossy way, or (ii) the WebP lossless encoding
-(and possibly other encodings in the future). These encoding schemes should
-make it more efficient than currently used formats. It is optimized for fast
-image transfer over the network (e.g., for websites). The WebP format has
-feature parity (color profile, metadata, animation etc) with other formats as
-well. This document describes the structure of a WebP file.
+WebP is an image format that uses either (i) the VP8 key frame encoding to
+compress image data in a lossy way, or (ii) the WebP lossless encoding. These
+encoding schemes should make it more efficient than older formats such as JPEG,
+GIF and PNG. It is optimized for fast image transfer over the network (for
+example, for websites). The WebP format has feature parity (color profile,
+metadata, animation, etc.) with other formats as well. This document describes
+the structure of a WebP file.
 
-The WebP container (i.e., RIFF container for WebP) allows feature support over
-and above the basic use case of WebP (i.e., a file containing a single image
-encoded as a VP8 key frame). The WebP container provides additional support
-for:
+The WebP container (that is, the RIFF container for WebP) allows feature support
+over and above the basic use case of WebP (that is, a file containing a single
+image encoded as a VP8 key frame). The WebP container provides additional
+support for:
 
   * **Lossless compression.** An image can be losslessly compressed, using the
     WebP Lossless Format.
 
-  * **Metadata.** An image may have metadata stored in EXIF or XMP formats.
+  * **Metadata.** An image may have metadata stored in Exif or XMP formats.
 
-  * **Transparency.** An image may have transparency, i.e., an alpha channel.
+  * **Transparency.** An image may have transparency, that is, an alpha channel.
 
   * **Color Profile.** An image may have an embedded ICC profile as described
     by the [International Color Consortium][iccspec].
@@ -46,24 +46,21 @@
   * **Animation.** An image may have multiple frames with pauses between them,
     making it an animation.
 
-The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
-"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
-document are to be interpreted as described in [RFC 2119][].
+The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
+"SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this
+document are to be interpreted as described in BCP 14 [RFC 2119][] [RFC 8174][]
+when, and only when, they appear in all capitals, as shown here.
 
 Bit numbering in chunk diagrams starts at `0` for the most significant bit
 ('MSB 0') as described in [RFC 1166][].
 
-**Note:** Out of the features mentioned above, lossy compression, lossless
-compression, transparency, metadata, color profile and animation are finalized
-and are to be considered stable.
+Terminology & Basics
+--------------------
 
-Terminology &amp; Basics
-------------------------
-
-A WebP file contains either a still image (i.e., an encoded matrix of pixels)
+A WebP file contains either a still image (that is, an encoded matrix of pixels)
 or an [animation](#animation). Optionally, it can also contain transparency
-information, color profile and metadata. In case we need to refer only to the
-matrix of pixels, we will call it the _canvas_ of the image.
+information, color profile and metadata. We refer to the matrix of pixels as the
+_canvas_ of the image.
 
 Below are additional terms used throughout this document:
 
@@ -87,18 +84,25 @@
 _FourCC_
 
 : A _FourCC_ (four-character code) is a _uint32_ created by concatenating four
-  ASCII characters in little-endian order.
+  ASCII characters in little-endian order. This means 'aaaa' (0x61616161) and
+ 'AAAA' (0x41414141) are treated as different _FourCCs_.
 
 _1-based_
 
-: An unsigned integer field storing values offset by `-1`. e.g., Such a field
-  would store value _25_ as _24_.
+: An unsigned integer field storing values offset by `-1`, for example, such a
+  field would store value _25_ as _24_.
+
+_ChunkHeader('ABCD')_
+
+: This is used to describe the _FourCC_ and _Chunk Size_ header of individual
+  chunks, where 'ABCD' is the FourCC for the chunk. This element's size is 8
+  bytes.
 
 
 RIFF File Format
 ----------------
 
-The WebP file format is based on the RIFF (resource interchange file format)
+The WebP file format is based on the RIFF (Resource Interchange File Format)
 document format.
 
 The basic element of a RIFF file is a _chunk_. It consists of:
@@ -110,7 +114,7 @@
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                          Chunk Size                           |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |                         Chunk Payload                         |
+    :                         Chunk Payload                         :
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
 Chunk FourCC: 32 bits
@@ -119,19 +123,13 @@
 
 Chunk Size: 32 bits (_uint32_)
 
-: The size of the chunk not including this field, the chunk identifier or
-  padding.
+: The size of the chunk in bytes, not including this field, the chunk
+  identifier or padding.
 
 Chunk Payload: _Chunk Size_ bytes
 
-: The data payload. If _Chunk Size_ is odd, a single padding byte -- that
-  SHOULD be `0` -- is added.
-
-_ChunkHeader('ABCD')_
-
-: This is used to describe the _FourCC_ and _Chunk Size_ header of individual
-  chunks, where 'ABCD' is the FourCC for the chunk. This element's
-  size is 8 bytes.
+: The data payload. If _Chunk Size_ is odd, a single padding byte -- that MUST
+  be `0` to conform with RIFF -- is added.
 
 **Note:** RIFF has a convention that all-uppercase chunk FourCCs are standard
 chunks that apply to any RIFF file format, while FourCCs specific to a file
@@ -167,9 +165,11 @@
 
 A WebP file MUST begin with a RIFF header with the FourCC 'WEBP'. The file size
 in the header is the total size of the chunks that follow plus `4` bytes for
-the 'WEBP' FourCC. The file SHOULD NOT contain anything after it. As the size
-of any chunk is even, the size given by the RIFF header is also even. The
-contents of individual chunks will be described in the following sections.
+the 'WEBP' FourCC. The file SHOULD NOT contain any data after the data
+specified by _File Size_. Readers MAY parse such files, ignoring the trailing
+data. As the size of any chunk is even, the size given by the RIFF header is
+also even. The contents of individual chunks are described in the following
+sections.
 
 
 Simple File Format (Lossy)
@@ -184,9 +184,11 @@
      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                                                               |
     |                    WebP file header (12 bytes)                |
+    |                                                               |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |                          VP8 chunk                            |
+    :                          VP8 chunk                            :
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
 VP8 chunk:
@@ -195,20 +197,24 @@
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                      ChunkHeader('VP8 ')                      |
+    |                                                               |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |                           VP8 data                            |
+    :                           VP8 data                            :
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
 VP8 data: _Chunk Size_ bytes
 
 : VP8 bitstream data.
 
+Note the fourth character in the 'VP8 ' FourCC is an ASCII space (0x20).
+
 The VP8 bitstream format specification can be found at [VP8 Data Format and
 Decoding Guide][vp8spec]. Note that the VP8 frame header contains the VP8 frame
 width and height. That is assumed to be the width and height of the canvas.
 
-The VP8 specification describes how to decode the image into Y'CbCr
-format. To convert to RGB, Rec. 601 SHOULD be used.
+The VP8 specification describes how to decode the image into Y'CbCr format. To
+convert to RGB, Rec. 601 SHOULD be used. Applications MAY use another
+conversion method, but visual results may differ among decoders.
 
 
 Simple File Format (Lossless)
@@ -225,9 +231,11 @@
      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                                                               |
     |                    WebP file header (12 bytes)                |
+    |                                                               |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |                          VP8L chunk                           |
+    :                          VP8L chunk                           :
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
 VP8L chunk:
@@ -236,8 +244,9 @@
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                      ChunkHeader('VP8L')                      |
+    |                                                               |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |                           VP8L data                           |
+    :                           VP8L data                           :
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
 VP8L data: _Chunk Size_ bytes
@@ -265,11 +274,11 @@
 
   * Image data.
 
-  * An optional 'EXIF' chunk with EXIF metadata.
+  * An optional 'EXIF' chunk with Exif metadata.
 
   * An optional 'XMP ' chunk with XMP metadata.
 
-  * An optional list of [unknown chunks](#unknown-chunks). _\[status: experimental\]_
+  * An optional list of [unknown chunks](#unknown-chunks).
 
 For a _still image_, the _image data_ consists of a single frame, which is made
 up of:
@@ -283,7 +292,7 @@
 
 All chunks SHOULD be placed in the same order as listed above. If a chunk
 appears in the wrong place, the file is invalid, but readers MAY parse the
-file, ignoring the chunks that come too late.
+file, ignoring the chunks that are out of order.
 
 **Rationale:** Setting the order of chunks should allow quicker file
 parsing. For example, if an 'ALPH' chunk does not appear in its required
@@ -297,9 +306,12 @@
      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                                                               |
     |                   WebP file header (12 bytes)                 |
+    |                                                               |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                      ChunkHeader('VP8X')                      |
+    |                                                               |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |Rsv|I|L|E|X|A|R|                   Reserved                    |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -310,7 +322,7 @@
 
 Reserved (Rsv): 2 bits
 
-: SHOULD be `0`.
+: MUST be `0`. Readers MUST ignore this field.
 
 ICC profile (I): 1 bit
 
@@ -321,9 +333,9 @@
 : Set if any of the frames of the image contain transparency information
   ("alpha").
 
-EXIF metadata (E): 1 bit
+Exif metadata (E): 1 bit
 
-: Set if the file contains EXIF metadata.
+: Set if the file contains Exif metadata.
 
 XMP metadata (X): 1 bit
 
@@ -336,25 +348,25 @@
 
 Reserved (R): 1 bit
 
-: SHOULD be `0`.
+: MUST be `0`. Readers MUST ignore this field.
 
 Reserved: 24 bits
 
-: SHOULD be `0`.
+: MUST be `0`. Readers MUST ignore this field.
 
 Canvas Width Minus One: 24 bits
 
 : _1-based_ width of the canvas in pixels.
-  The actual canvas width is '1 + Canvas Width Minus One'
+  The actual canvas width is `1 + Canvas Width Minus One`.
 
 Canvas Height Minus One: 24 bits
 
 : _1-based_ height of the canvas in pixels.
-  The actual canvas height is '1 + Canvas Height Minus One'
+  The actual canvas height is `1 + Canvas Height Minus One`.
 
 The product of _Canvas Width_ and _Canvas Height_ MUST be at most `2^32 - 1`.
 
-Future specifications MAY add more fields.
+Future specifications may add more fields. Unknown fields MUST be ignored.
 
 ### Chunks
 
@@ -372,6 +384,7 @@
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                      ChunkHeader('ANIM')                      |
+    |                                                               |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                       Background Color                        |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -387,8 +400,8 @@
 
 **Note**:
 
-  * Background color MAY contain a transparency value (alpha), even if the
-    _Alpha_ flag in [VP8X chunk](#extended_header) is unset.
+  * Background color MAY contain a non-opaque alpha value, even if the _Alpha_
+    flag in [VP8X chunk](#extended_header) is unset.
 
   * Viewer applications SHOULD treat the background color value as a hint, and
     are not required to use it.
@@ -401,8 +414,8 @@
 : The number of times to loop the animation. `0` means infinitely.
 
 This chunk MUST appear if the _Animation_ flag in the VP8X chunk is set.
-If the _Animation_ flag is not set and this chunk is present, it
-SHOULD be ignored.
+If the _Animation_ flag is not set and this chunk is present, it MUST be
+ignored.
 
 ANMF chunk:
 
@@ -413,6 +426,7 @@
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                      ChunkHeader('ANMF')                      |
+    |                                                               |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                        Frame X                |             ...
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -422,26 +436,26 @@
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                 Frame Duration                |  Reserved |B|D|
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |                         Frame Data                            |
+    :                         Frame Data                            :
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
 Frame X: 24 bits (_uint24_)
 
-: The X coordinate of the upper left corner of the frame is `Frame X * 2`
+: The X coordinate of the upper left corner of the frame is `Frame X * 2`.
 
 Frame Y: 24 bits (_uint24_)
 
-: The Y coordinate of the upper left corner of the frame is `Frame Y * 2`
+: The Y coordinate of the upper left corner of the frame is `Frame Y * 2`.
 
 Frame Width Minus One: 24 bits (_uint24_)
 
 : The _1-based_ width of the frame.
-  The frame width is `1 + Frame Width Minus One`
+  The frame width is `1 + Frame Width Minus One`.
 
 Frame Height Minus One: 24 bits (_uint24_)
 
 : The _1-based_ height of the frame.
-  The frame height is `1 + Frame Height Minus One`
+  The frame height is `1 + Frame Height Minus One`.
 
 Frame Duration: 24 bits (_uint24_)
 
@@ -452,7 +466,7 @@
 
 Reserved: 6 bits
 
-: SHOULD be 0.
+: MUST be `0`. Readers MUST ignore this field.
 
 Blending method (B): 1 bit
 
@@ -497,8 +511,9 @@
     if blend.A = 0 then
       blend.RGB = 0
     else
-      blend.RGB = (src.RGB * src.A +
-                   dst.RGB * dst.A * (1 - src.A / 255)) / blend.A
+      blend.RGB =
+          (src.RGB * src.A +
+           dst.RGB * dst.A * (1 - src.A / 255)) / blend.A
 ~~~~~
 
   * Alpha-blending SHOULD be done in linear color space, by taking into account
@@ -525,22 +540,23 @@
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                      ChunkHeader('ALPH')                      |
+    |                                                               |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |Rsv| P | F | C |     Alpha Bitstream...                        |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
 Reserved (Rsv): 2 bits
 
-: SHOULD be `0`.
+: MUST be `0`. Readers MUST ignore this field.
 
 Pre-processing (P): 2 bits
 
-: These INFORMATIVE bits are used to signal the pre-processing that has
+: These _informative_ bits are used to signal the pre-processing that has
   been performed during compression. The decoder can use this information to
-  e.g. dither the values or smooth the gradients prior to display.
+  for example, dither the values or smooth the gradients prior to display.
 
-    * `0`: no pre-processing
-    * `1`: level reduction
+    * `0`: No pre-processing.
+    * `1`: Level reduction.
 
 Filtering method (F): 2 bits
 
@@ -573,14 +589,14 @@
   * v    otherwise
 
 The final value is derived by adding the decompressed value `X` to the
-predictor and using modulo-256 arithmetic to wrap the \[256-511\] range
-into the \[0-255\] one:
+predictor and using modulo-256 arithmetic to wrap the \[256..511\] range
+into the \[0..255\] one:
 
 `alpha = (predictor + X) % 256`
 
-There are special cases for left-most and top-most pixel positions:
+There are special cases for the left-most and top-most pixel positions:
 
-  * Top-left value at location (0,0) uses 0 as predictor value. Otherwise,
+  * The top-left value at location (0, 0) uses 0 as predictor value. Otherwise,
   * For horizontal or gradient filtering methods, the left-most pixels at
     location (0, y) are predicted using the location (0, y-1) just above.
   * For vertical or gradient filtering methods, the top-most pixels at
@@ -642,14 +658,15 @@
 [Simple File Format (Lossy)](#simple-file-format-lossy)
 and [Simple File Format (Lossless)](#simple-file-format-lossless) respectively.
 
-#### Color profile
+#### Color Profile
 
      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                      ChunkHeader('ICCP')                      |
+    |                                                               |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |                       Color Profile                           |
+    :                       Color Profile                           :
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
 Color Profile: _Chunk Size_ bytes
@@ -669,8 +686,7 @@
 Metadata can be stored in 'EXIF' or 'XMP ' chunks.
 
 There SHOULD be at most one chunk of each type ('EXIF' and 'XMP '). If there
-are more such chunks, readers MAY ignore all except the first one. Also, a file
-may possibly contain both 'EXIF' and 'XMP ' chunks.
+are more such chunks, readers MAY ignore all except the first one.
 
 The chunks are defined as follows:
 
@@ -680,13 +696,14 @@
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                      ChunkHeader('EXIF')                      |
+    |                                                               |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |                        EXIF Metadata                          |
+    :                        Exif Metadata                          :
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
-EXIF Metadata: _Chunk Size_ bytes
+Exif Metadata: _Chunk Size_ bytes
 
-: image metadata in EXIF format.
+: Image metadata in Exif format.
 
 XMP chunk:
 
@@ -694,18 +711,21 @@
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                      ChunkHeader('XMP ')                      |
+    |                                                               |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |                        XMP Metadata                           |
+    :                        XMP Metadata                           :
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
 XMP Metadata: _Chunk Size_ bytes
 
-: image metadata in XMP format.
+: Image metadata in XMP format.
+
+Note the fourth character in the 'XMP ' FourCC is an ASCII space (0x20).
 
 Additional guidance about handling metadata can be found in the
 Metadata Working Group's [Guidelines for Handling Metadata][metadata].
 
-#### Unknown Chunks _\[status: experimental\]_
+#### Unknown Chunks
 
 A RIFF chunk (described in [this](#terminology-amp-basics) section) whose _chunk
 tag_ is different from any of the chunks described in this document, is
@@ -724,49 +744,73 @@
 Readers SHOULD ignore these chunks. Writers SHOULD preserve them in their
 original order (unless they specifically intend to modify these chunks).
 
-### Assembling the Canvas from frames
+### Assembling the Canvas From Frames
 
-Here we provide an overview of how a reader should assemble a canvas in the
-case of an animated image. The notation _VP8X.field_ means the field in the
-'VP8X' chunk with the same description.
+Here we provide an overview of how a reader MUST assemble a canvas in the case
+of an animated image.
 
-Displaying an _animated image_ canvas MUST be equivalent to the following
-pseudocode:
+The process begins with creating a canvas using the dimensions given in the
+'VP8X' chunk, `Canvas Width Minus One + 1` pixels wide by `Canvas Height Minus
+One + 1` pixels high. The `Loop Count` field from the 'ANIM' chunk controls how
+many times the animation process is repeated. This is `Loop Count - 1` for
+non-zero `Loop Count` values or infinitely if `Loop Count` is zero.
+
+At the beginning of each loop iteration the canvas is filled using the
+background color from the 'ANIM' chunk or an application defined color.
+
+'ANMF' chunks contain individual frames given in display order. Before rendering
+each frame, the previous frame's `Disposal method` is applied.
+
+The rendering of the decoded frame begins at the Cartesian coordinates (`2 *
+Frame X`, `2 * Frame Y`) using the top-left corner of the canvas as the origin.
+`Frame Width Minus One + 1` pixels wide by `Frame Height Minus One + 1` pixels
+high are rendered onto the canvas using the `Blending method`.
+
+The canvas is displayed for `Frame Duration` milliseconds. This continues until
+all frames given by 'ANMF' chunks have been displayed. A new loop iteration is
+then begun or the canvas is left in its final state if all iterations have been
+completed.
+
+The following pseudocode illustrates the rendering process. The notation
+_VP8X.field_ means the field in the 'VP8X' chunk with the same description.
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 assert VP8X.flags.hasAnimation
 canvas ← new image of size VP8X.canvasWidth x VP8X.canvasHeight with
          background color ANIM.background_color.
 loop_count ← ANIM.loopCount
-dispose_method ← ANIM.disposeMethod
+dispose_method ← Dispose to background color
 if loop_count == 0:
-    loop_count = ∞
+  loop_count = ∞
 frame_params ← nil
 assert next chunk in image_data is ANMF
 for loop = 0..loop_count - 1
-    clear canvas to ANIM.background_color or application defined color
-    until eof or non-ANMF chunk
-        frame_params.frameX = Frame X
-        frame_params.frameY = Frame Y
-        frame_params.frameWidth = Frame Width Minus One + 1
-        frame_params.frameHeight = Frame Height Minus One + 1
-        frame_params.frameDuration = Frame Duration
-        frame_right = frame_params.frameX + frame_params.frameWidth
-        frame_bottom = frame_params.frameY + frame_params.frameHeight
-        assert VP8X.canvasWidth >= frame_right
-        assert VP8X.canvasHeight >= frame_bottom
-        for subchunk in 'Frame Data':
-            if subchunk.tag == "ALPH":
-                assert alpha subchunks not found in 'Frame Data' earlier
-                frame_params.alpha = alpha_data
-            else if subchunk.tag == "VP8 " OR subchunk.tag == "VP8L":
-                assert bitstream subchunks not found in 'Frame Data' earlier
-                frame_params.bitstream = bitstream_data
-        render frame with frame_params.alpha and frame_params.bitstream on
-            canvas with top-left corner at (frame_params.frameX,
-            frame_params.frameY), using dispose method dispose_method.
-        canvas contains the decoded image.
-        Show the contents of the canvas for frame_params.frameDuration * 1ms.
+  clear canvas to ANIM.background_color or application defined color
+  until eof or non-ANMF chunk
+    frame_params.frameX = Frame X
+    frame_params.frameY = Frame Y
+    frame_params.frameWidth = Frame Width Minus One + 1
+    frame_params.frameHeight = Frame Height Minus One + 1
+    frame_params.frameDuration = Frame Duration
+    frame_right = frame_params.frameX + frame_params.frameWidth
+    frame_bottom = frame_params.frameY + frame_params.frameHeight
+    assert VP8X.canvasWidth >= frame_right
+    assert VP8X.canvasHeight >= frame_bottom
+    for subchunk in 'Frame Data':
+      if subchunk.tag == "ALPH":
+        assert alpha subchunks not found in 'Frame Data' earlier
+        frame_params.alpha = alpha_data
+      else if subchunk.tag == "VP8 " OR subchunk.tag == "VP8L":
+        assert bitstream subchunks not found in 'Frame Data' earlier
+        frame_params.bitstream = bitstream_data
+    render frame with frame_params.alpha and frame_params.bitstream
+      on canvas with top-left corner at (frame_params.frameX,
+      frame_params.frameY), using blending method
+      frame_params.blendingMethod.
+    canvas contains the decoded image.
+    Show the contents of the canvas for
+    frame_params.frameDuration * 1ms.
+    dispose_method = frame_params.disposeMethod
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 
@@ -802,7 +846,7 @@
 +- XMP  (metadata)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-An animated image with EXIF metadata may look as follows:
+An animated image with Exif metadata may look as follows:
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 RIFF/WEBP
@@ -815,9 +859,10 @@
 +- EXIF (metadata)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-[vp8spec]:  http://tools.ietf.org/html/rfc6386
-[webpllspec]: https://chromium.googlesource.com/webm/libwebp/+/master/doc/webp-lossless-bitstream-spec.txt
-[iccspec]: http://www.color.org/icc_specs2.xalter
-[metadata]: http://www.metadataworkinggroup.org/pdf/mwg_guidance.pdf
-[rfc 1166]: http://tools.ietf.org/html/rfc1166
-[rfc 2119]: http://tools.ietf.org/html/rfc2119
+[vp8spec]:  https://datatracker.ietf.org/doc/html/rfc6386
+[webpllspec]: https://chromium.googlesource.com/webm/libwebp/+/HEAD/doc/webp-lossless-bitstream-spec.txt
+[iccspec]: https://www.color.org/icc_specs2.xalter
+[metadata]: https://web.archive.org/web/20180919181934/http://www.metadataworkinggroup.org/pdf/mwg_guidance.pdf
+[rfc 1166]: https://datatracker.ietf.org/doc/html/rfc1166
+[rfc 2119]: https://datatracker.ietf.org/doc/html/rfc2119
+[rfc 8174]: https://datatracker.ietf.org/doc/html/rfc8174
diff --git a/third_party/libwebp/doc/webp-lossless-bitstream-spec.txt b/third_party/libwebp/doc/webp-lossless-bitstream-spec.txt
index 2d2dde1..ebaf655 100644
--- a/third_party/libwebp/doc/webp-lossless-bitstream-spec.txt
+++ b/third_party/libwebp/doc/webp-lossless-bitstream-spec.txt
@@ -1,113 +1,52 @@
 <!--
 
-Although you may be viewing an alternate representation, this document
-is sourced in Markdown, a light-duty markup scheme, and is optimized for
-the [kramdown](http://kramdown.rubyforge.org/) transformer.
+Although you may be viewing an alternate representation, this document is
+sourced in Markdown, a light-duty markup scheme, and is optimized for the
+[kramdown](https://kramdown.gettalong.org/) transformer.
 
-See the accompanying README. External link targets are referenced at the
-end of this file.
+See the accompanying specs_generation.md. External link targets are referenced
+at the end of this file.
 
 -->
 
 Specification for WebP Lossless Bitstream
 =========================================
 
-_Jyrki Alakuijala, Ph.D., Google, Inc., 2012-06-19_
-
-Paragraphs marked as \[AMENDED\] were amended on 2014-09-16.
+_Jyrki Alakuijala, Ph.D., Google, Inc., 2023-03-09_
 
 Abstract
 --------
 
-WebP lossless is an image format for lossless compression of ARGB
-images. The lossless format stores and restores the pixel values
-exactly, including the color values for zero alpha pixels. The
-format uses subresolution images, recursively embedded into the format
-itself, for storing statistical data about the images, such as the used
-entropy codes, spatial predictors, color space conversion, and color
-table. LZ77, Huffman coding, and a color cache are used for compression
-of the bulk data. Decoding speeds faster than PNG have been
-demonstrated, as well as 25% denser compression than can be achieved
-using today's PNG format.
+WebP lossless is an image format for lossless compression of ARGB images. The
+lossless format stores and restores the pixel values exactly, including the
+color values for pixels whose alpha value is 0. The format uses subresolution
+images, recursively embedded into the format itself, for storing statistical
+data about the images, such as the used entropy codes, spatial predictors, color
+space conversion, and color table. LZ77, prefix coding, and a color cache are
+used for compression of the bulk data. Decoding speeds faster than PNG have been
+demonstrated, as well as 25% denser compression than can be achieved using
+today's PNG format.
 
 
 * TOC placeholder
 {:toc}
 
 
-Nomenclature
-------------
-
-ARGB
-: A pixel value consisting of alpha, red, green, and blue values.
-
-ARGB image
-: A two-dimensional array containing ARGB pixels.
-
-color cache
-: A small hash-addressed array to store recently used colors, to be able
-  to recall them with shorter codes.
-
-color indexing image
-: A one-dimensional image of colors that can be indexed using a small
-  integer (up to 256 within WebP lossless).
-
-color transform image
-: A two-dimensional subresolution image containing data about
-  correlations of color components.
-
-distance mapping
-: Changes LZ77 distances to have the smallest values for pixels in 2D
-  proximity.
-
-entropy image
-: A two-dimensional subresolution image indicating which entropy coding
-  should be used in a respective square in the image, i.e., each pixel
-  is a meta Huffman code.
-
-Huffman code
-: A classic way to do entropy coding where a smaller number of bits are
-  used for more frequent codes.
-
-LZ77
-: Dictionary-based sliding window compression algorithm that either
-  emits symbols or describes them as sequences of past symbols.
-
-meta Huffman code
-: A small integer (up to 16 bits) that indexes an element in the meta
-  Huffman table.
-
-predictor image
-: A two-dimensional subresolution image indicating which spatial
-  predictor is used for a particular square in the image.
-
-prefix coding
-: A way to entropy code larger integers that codes a few bits of the
-  integer using an entropy code and codifies the remaining bits raw.
-  This allows for the descriptions of the entropy codes to remain
-  relatively small even when the range of symbols is large.
-
-scan-line order
-: A processing order of pixels, left-to-right, top-to-bottom, starting
-  from the left-hand-top pixel, proceeding to the right. Once a row is
-  completed, continue from the left-hand column of the next row.
-
-
 1 Introduction
 --------------
 
-This document describes the compressed data representation of a WebP
-lossless image. It is intended as a detailed reference for WebP lossless
-encoder and decoder implementation.
+This document describes the compressed data representation of a WebP lossless
+image. It is intended as a detailed reference for the WebP lossless encoder and
+decoder implementation.
 
-In this document, we extensively use C programming language syntax to
-describe the bitstream, and assume the existence of a function for
-reading bits, `ReadBits(n)`. The bytes are read in the natural order of
-the stream containing them, and bits of each byte are read in
-least-significant-bit-first order. When multiple bits are read at the
-same time, the integer is constructed from the original data in the
-original order. The most significant bits of the returned integer are
-also the most significant bits of the original data. Thus the statement
+In this document, we extensively use C programming language syntax to describe
+the bitstream, and assume the existence of a function for reading bits,
+`ReadBits(n)`. The bytes are read in the natural order of the stream containing
+them, and bits of each byte are read in least-significant-bit-first order. When
+multiple bits are read at the same time, the integer is constructed from the
+original data in the original order. The most significant bits of the returned
+integer are also the most significant bits of the original data. Thus, the
+statement
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 b = ReadBits(2);
@@ -120,24 +59,79 @@
 b |= ReadBits(1) << 1;
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-We assume that each color component (e.g. alpha, red, blue and green) is
-represented using an 8-bit byte. We define the corresponding type as
-uint8. A whole ARGB pixel is represented by a type called uint32, an
-unsigned integer consisting of 32 bits. In the code showing the behavior
-of the transformations, alpha value is codified in bits 31..24, red in
-bits 23..16, green in bits 15..8 and blue in bits 7..0, but
-implementations of the format are free to use another representation
-internally.
+We assume that each color component, that is, alpha, red, blue and green, is
+represented using an 8-bit byte. We define the corresponding type as uint8. A
+whole ARGB pixel is represented by a type called uint32, an unsigned integer
+consisting of 32 bits. In the code showing the behavior of the transformations,
+alpha value is codified in bits 31..24, red in bits 23..16, green in bits 15..8
+and blue in bits 7..0, but implementations of the format are free to use another
+representation internally.
 
-Broadly, a WebP lossless image contains header data, transform
-information and actual image data. Headers contain width and height of
-the image. A WebP lossless image can go through four different types of
-transformation before being entropy encoded. The transform information
-in the bitstream contains the data required to apply the respective
-inverse transforms.
+Broadly, a WebP lossless image contains header data, transform information and
+actual image data. Headers contain width and height of the image. A WebP
+lossless image can go through four different types of transformation before
+being entropy encoded. The transform information in the bitstream contains the
+data required to apply the respective inverse transforms.
 
 
-2 RIFF Header
+2 Nomenclature
+--------------
+
+ARGB
+:   A pixel value consisting of alpha, red, green, and blue values.
+
+ARGB image
+:   A two-dimensional array containing ARGB pixels.
+
+color cache
+:   A small hash-addressed array to store recently used colors, to be able to
+    recall them with shorter codes.
+
+color indexing image
+:   A one-dimensional image of colors that can be indexed using a small integer
+    (up to 256 within WebP lossless).
+
+color transform image
+:   A two-dimensional subresolution image containing data about correlations of
+    color components.
+
+distance mapping
+:   Changes LZ77 distances to have the smallest values for pixels in 2D
+    proximity.
+
+entropy image
+:   A two-dimensional subresolution image indicating which entropy coding should
+    be used in a respective square in the image, that is, each pixel is a meta
+    prefix code.
+
+prefix code
+:   A classic way to do entropy coding where a smaller number of bits are used
+    for more frequent codes.
+
+LZ77
+:   Dictionary-based sliding window compression algorithm that either emits
+    symbols or describes them as sequences of past symbols.
+
+meta prefix code
+:   A small integer (up to 16 bits) that indexes an element in the meta prefix
+    table.
+
+predictor image
+:   A two-dimensional subresolution image indicating which spatial predictor is
+    used for a particular square in the image.
+
+prefix coding
+:   A way to entropy code larger integers that codes a few bits of the integer
+    using an entropy code and codifies the remaining bits raw. This allows for
+    the descriptions of the entropy codes to remain relatively small even when
+    the range of symbols is large.
+
+scan-line order
+:   A processing order of pixels, left-to-right, top-to-bottom, starting from
+    the left-hand-top pixel, proceeding to the right. Once a row is completed,
+    continue from the left-hand column of the next row.
+
+3 RIFF Header
 -------------
 
 The beginning of the header has the RIFF container. This consists of the
@@ -154,49 +148,47 @@
       lossless stream.
    6. One byte signature 0x2f.
 
-The first 28 bits of the bitstream specify the width and height of the
-image. Width and height are decoded as 14-bit integers as follows:
+The first 28 bits of the bitstream specify the width and height of the image.
+Width and height are decoded as 14-bit integers as follows:
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 int image_width = ReadBits(14) + 1;
 int image_height = ReadBits(14) + 1;
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-The 14-bit dynamics for image size limit the maximum size of a WebP
-lossless image to 16384✕16384 pixels.
+The 14-bit precision for image width and height limits the maximum size of a
+WebP lossless image to 16384✕16384 pixels.
 
-The alpha_is_used bit is a hint only, and should not impact decoding.
-It should be set to 0 when all alpha values are 255 in the picture, and
-1 otherwise.
+The alpha_is_used bit is a hint only, and should not impact decoding. It should
+be set to 0 when all alpha values are 255 in the picture, and 1 otherwise.
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 int alpha_is_used = ReadBits(1);
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-The version_number is a 3 bit code that must be set to 0. Any other value
-should be treated as an error. \[AMENDED\]
+The version_number is a 3 bit code that must be set to 0. Any other value should
+be treated as an error.
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 int version_number = ReadBits(3);
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 
-3 Transformations
+4 Transformations
 -----------------
 
-Transformations are reversible manipulations of the image data that can
-reduce the remaining symbolic entropy by modeling spatial and color
-correlations. Transformations can make the final compression more dense.
+Transformations are reversible manipulations of the image data that can reduce
+the remaining symbolic entropy by modeling spatial and color correlations.
+Transformations can make the final compression more dense.
 
-An image can go through four types of transformation. A 1 bit indicates
-the presence of a transform. Each transform is allowed to be used only
-once. The transformations are used only for the main level ARGB image:
-the subresolution images have no transforms, not even the 0 bit
-indicating the end-of-transforms.
+An image can go through four types of transformation. A 1 bit indicates the
+presence of a transform. Each transform is allowed to be used only once. The
+transformations are used only for the main level ARGB image: the subresolution
+images have no transforms, not even the 0 bit indicating the end-of-transforms.
 
-Typically an encoder would use these transforms to reduce the Shannon
-entropy in the residual image. Also, the transform data can be decided
-based on entropy minimization.
+Typically, an encoder would use these transforms to reduce the Shannon entropy
+in the residual image. Also, the transform data can be decided based on entropy
+minimization.
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 while (ReadBits(1)) {  // Transform present.
@@ -209,66 +201,62 @@
 // Decode actual image data (Section 4).
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-If a transform is present then the next two bits specify the transform
-type. There are four types of transforms.
+If a transform is present then the next two bits specify the transform type.
+There are four types of transforms.
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 enum TransformType {
   PREDICTOR_TRANSFORM             = 0,
   COLOR_TRANSFORM                 = 1,
-  SUBTRACT_GREEN                  = 2,
+  SUBTRACT_GREEN_TRANSFORM        = 2,
   COLOR_INDEXING_TRANSFORM        = 3,
 };
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-The transform type is followed by the transform data. Transform data
-contains the information required to apply the inverse transform and
-depends on the transform type. Next we describe the transform data for
-different types.
+The transform type is followed by the transform data. Transform data contains
+the information required to apply the inverse transform and depends on the
+transform type. Next we describe the transform data for different types.
 
 
-### Predictor Transform
+### 4.1 Predictor Transform
 
-The predictor transform can be used to reduce entropy by exploiting the
-fact that neighboring pixels are often correlated. In the predictor
-transform, the current pixel value is predicted from the pixels already
-decoded (in scan-line order) and only the residual value (actual -
-predicted) is encoded. The _prediction mode_ determines the type of
-prediction to use. We divide the image into squares and all the pixels
-in a square use same prediction mode.
+The predictor transform can be used to reduce entropy by exploiting the fact
+that neighboring pixels are often correlated. In the predictor transform, the
+current pixel value is predicted from the pixels already decoded (in scan-line
+order) and only the residual value (actual - predicted) is encoded. The
+_prediction mode_ determines the type of prediction to use. We divide the image
+into squares and all the pixels in a square use the same prediction mode.
 
-The first 3 bits of prediction data define the block width and height in
-number of bits. The number of block columns, `block_xsize`, is used in
-indexing two-dimensionally.
+The first 3 bits of prediction data define the block width and height in number
+of bits. The number of block columns, `block_xsize`, is used in indexing
+two-dimensionally.
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 int size_bits = ReadBits(3) + 2;
 int block_width = (1 << size_bits);
 int block_height = (1 << size_bits);
-#define DIV_ROUND_UP(num, den) ((num) + (den) - 1) / (den))
+#define DIV_ROUND_UP(num, den) (((num) + (den) - 1) / (den))
 int block_xsize = DIV_ROUND_UP(image_width, 1 << size_bits);
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-The transform data contains the prediction mode for each block of the
-image. All the `block_width * block_height` pixels of a block use same
-prediction mode. The prediction modes are treated as pixels of an image
-and encoded using the same techniques described in
-[Chapter 4](#image-data).
+The transform data contains the prediction mode for each block of the image. All
+the `block_width * block_height` pixels of a block use same prediction mode. The
+prediction modes are treated as pixels of an image and encoded using the same
+techniques described in [Chapter 5](#image-data).
 
-For a pixel _x, y_, one can compute the respective filter block address
-by:
+For a pixel _x, y_, one can compute the respective filter block address by:
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 int block_index = (y >> size_bits) * block_xsize +
                   (x >> size_bits);
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-There are 14 different prediction modes. In each prediction mode, the
-current pixel value is predicted from one or more neighboring pixels
-whose values are already known.
+There are 14 different prediction modes. In each prediction mode, the current
+pixel value is predicted from one or more neighboring pixels whose values are
+already known.
 
-We choose the neighboring pixels (TL, T, TR, and L) of the current pixel
-(P) as follows:
+We choose the neighboring pixels (TL, T, TR, and L) of the current pixel (P) as
+follows:
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 O    O    O    O    O    O    O    O    O    O    O
@@ -279,12 +267,12 @@
 X    X    X    X    X    X    X    X    X    X    X
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-where TL means top-left, T top, TR top-right, L left pixel.
-At the time of predicting a value for P, all pixels O, TL, T, TR and L
-have been already processed, and pixel P and all pixels X are unknown.
+where TL means top-left, T top, TR top-right, L left pixel. At the time of
+predicting a value for P, all pixels O, TL, T, TR and L have already been
+processed, and pixel P and all pixels X are unknown.
 
-Given the above neighboring pixels, the different prediction modes are
-defined as follows.
+Given the above neighboring pixels, the different prediction modes are defined
+as follows.
 
 | Mode   | Predicted value of each channel of the current pixel    |
 | ------ | ------------------------------------------------------- |
@@ -331,7 +319,7 @@
            abs(pGreen - GREEN(T)) + abs(pBlue - BLUE(T));
 
   // Return either left or top, the one closer to the prediction.
-  if (pL < pT) {     // \[AMENDED\]
+  if (pL < pT) {
     return L;
   } else {
     return T;
@@ -339,8 +327,8 @@
 }
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-The functions `ClampAddSubtractFull` and `ClampAddSubtractHalf` are
-performed for each ARGB component as follows:
+The functions `ClampAddSubtractFull` and `ClampAddSubtractHalf` are performed
+for each ARGB component as follows:
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Clamp the input value between 0 and 255.
@@ -362,28 +350,26 @@
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 There are special handling rules for some border pixels. If there is a
-prediction transform, regardless of the mode \[0..13\] for these pixels,
-the predicted value for the left-topmost pixel of the image is
-0xff000000, L-pixel for all pixels on the top row, and T-pixel for all
-pixels on the leftmost column.
+prediction transform, regardless of the mode \[0..13\] for these pixels, the
+predicted value for the left-topmost pixel of the image is 0xff000000, L-pixel
+for all pixels on the top row, and T-pixel for all pixels on the leftmost
+column.
 
 Addressing the TR-pixel for pixels on the rightmost column is
-exceptional. The pixels on the rightmost column are predicted by using
-the modes \[0..13\] just like pixels not on border, but by using the
-leftmost pixel on the same row as the current TR-pixel. The TR-pixel
-offset in memory is the same for border and non-border pixels.
+exceptional. The pixels on the rightmost column are predicted by using the modes
+\[0..13\] just like pixels not on the border, but the leftmost pixel on the same
+row as the current pixel is instead used as the TR-pixel.
 
 
-### Color Transform
+### 4.2 Color Transform
 
-The goal of the color transform is to decorrelate the R, G and B values
-of each pixel. Color transform keeps the green (G) value as it is,
-transforms red (R) based on green and transforms blue (B) based on green
-and then based on red.
+The goal of the color transform is to decorrelate the R, G and B values of each
+pixel. The color transform keeps the green (G) value as it is, transforms red
+(R) based on green and transforms blue (B) based on green and then based on red.
 
-As is the case for the predictor transform, first the image is divided
-into blocks and the same transform mode is used for all the pixels in a
-block. For each block there are three types of color transform elements.
+As is the case for the predictor transform, first the image is divided into
+blocks and the same transform mode is used for all the pixels in a block. For
+each block there are three types of color transform elements.
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 typedef struct {
@@ -393,11 +379,10 @@
 } ColorTransformElement;
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-The actual color transformation is done by defining a color transform
-delta. The color transform delta depends on the `ColorTransformElement`,
-which is the same for all the pixels in a particular block. The delta is
-added during color transform. The inverse color transform then is just
-subtracting those deltas.
+The actual color transformation is done by defining a color transform delta. The
+color transform delta depends on the `ColorTransformElement`, which is the same
+for all the pixels in a particular block. The delta is subtracted during the
+color transform. The inverse color transform then is just adding those deltas.
 
 The color transform function is defined as follows:
 
@@ -406,22 +391,22 @@
                     ColorTransformElement *trans,
                     uint8 *new_red, uint8 *new_blue) {
   // Transformed values of red and blue components
-  uint32 tmp_red = red;
-  uint32 tmp_blue = blue;
+  int tmp_red = red;
+  int tmp_blue = blue;
 
-  // Applying transform is just adding the transform deltas
-  tmp_red  += ColorTransformDelta(trans->green_to_red, green);
-  tmp_blue += ColorTransformDelta(trans->green_to_blue, green);
-  tmp_blue += ColorTransformDelta(trans->red_to_blue, red);
+  // Applying the transform is just subtracting the transform deltas
+  tmp_red  -= ColorTransformDelta(trans->green_to_red_,  green);
+  tmp_blue -= ColorTransformDelta(trans->green_to_blue_, green);
+  tmp_blue -= ColorTransformDelta(trans->red_to_blue_, red);
 
   *new_red = tmp_red & 0xff;
   *new_blue = tmp_blue & 0xff;
 }
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-`ColorTransformDelta` is computed using a signed 8-bit integer
-representing a 3.5-fixed-point number, and a signed 8-bit RGB color
-channel (c) \[-128..127\] and is defined as follows:
+`ColorTransformDelta` is computed using a signed 8-bit integer representing a
+3.5-fixed-point number, and a signed 8-bit RGB color channel (c) \[-128..127\]
+and is defined as follows:
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 int8 ColorTransformDelta(int8 t, int8 c) {
@@ -429,22 +414,20 @@
 }
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-A conversion from the 8-bit unsigned representation (uint8) to the 8-bit
-signed one (int8) is required before calling ColorTransformDelta().
-It should be performed using 8-bit two's complement (that is: uint8 range
-\[128-255\] is mapped to the \[-128, -1\] range of its converted int8 value).
+A conversion from the 8-bit unsigned representation (uint8) to the 8-bit signed
+one (int8) is required before calling `ColorTransformDelta()`. It should be
+performed using 8-bit two's complement (that is: uint8 range \[128..255\] is
+mapped to the \[-128..-1\] range of its converted int8 value).
 
-The multiplication is to be done using more precision (with at least
-16-bit dynamics). The sign extension property of the shift operation
-does not matter here: only the lowest 8 bits are used from the result,
-and there the sign extension shifting and unsigned shifting are
-consistent with each other.
+The multiplication is to be done using more precision (with at least 16-bit
+precision). The sign extension property of the shift operation does not matter
+here: only the lowest 8 bits are used from the result, and there the sign
+extension shifting and unsigned shifting are consistent with each other.
 
-Now we describe the contents of color transform data so that decoding
-can apply the inverse color transform and recover the original red and
-blue values. The first 3 bits of the color transform data contain the
-width and height of the image block in number of bits, just like the
-predictor transform:
+Now we describe the contents of color transform data so that decoding can apply
+the inverse color transform and recover the original red and blue values. The
+first 3 bits of the color transform data contain the width and height of the
+image block in number of bits, just like the predictor transform:
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 int size_bits = ReadBits(3) + 2;
@@ -452,41 +435,43 @@
 int block_height = 1 << size_bits;
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-The remaining part of the color transform data contains
-`ColorTransformElement` instances corresponding to each block of the
-image. `ColorTransformElement` instances are treated as pixels of an
-image and encoded using the methods described in
-[Chapter 4](#image-data).
+The remaining part of the color transform data contains `ColorTransformElement`
+instances corresponding to each block of the image. `ColorTransformElement`
+instances are treated as pixels of an image and encoded using the methods
+described in [Chapter 5](#image-data).
 
-During decoding, `ColorTransformElement` instances of the blocks are
-decoded and the inverse color transform is applied on the ARGB values of
-the pixels. As mentioned earlier, that inverse color transform is just
-subtracting `ColorTransformElement` values from the red and blue
-channels.
+During decoding, `ColorTransformElement` instances of the blocks are decoded and
+the inverse color transform is applied on the ARGB values of the pixels. As
+mentioned earlier, that inverse color transform is just adding
+`ColorTransformElement` values to the red and blue channels.
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 void InverseTransform(uint8 red, uint8 green, uint8 blue,
-                      ColorTransformElement *p,
+                      ColorTransformElement *trans,
                       uint8 *new_red, uint8 *new_blue) {
-  // Applying inverse transform is just subtracting the
-  // color transform deltas
-  red  -= ColorTransformDelta(p->green_to_red_,  green);
-  blue -= ColorTransformDelta(p->green_to_blue_, green);
-  blue -= ColorTransformDelta(p->red_to_blue_, red & 0xff);
+  // Transformed values of red and blue components
+  int tmp_red = red;
+  int tmp_blue = blue;
 
-  *new_red = red & 0xff;
-  *new_blue = blue & 0xff;
+  // Applying the inverse transform is just adding the
+  // color transform deltas
+  tmp_red  += ColorTransformDelta(trans->green_to_red, green);
+  tmp_blue += ColorTransformDelta(trans->green_to_blue, green);
+  tmp_blue +=
+      ColorTransformDelta(trans->red_to_blue, tmp_red & 0xff);
+
+  *new_red = tmp_red & 0xff;
+  *new_blue = tmp_blue & 0xff;
 }
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 
-### Subtract Green Transform
+### 4.3 Subtract Green Transform
 
-The subtract green transform subtracts green values from red and blue
-values of each pixel. When this transform is present, the decoder needs
-to add the green value to both red and blue. There is no data associated
-with this transform. The decoder applies the inverse transform as
-follows:
+The subtract green transform subtracts green values from red and blue values of
+each pixel. When this transform is present, the decoder needs to add the green
+value to both red and blue. There is no data associated with this transform. The
+decoder applies the inverse transform as follows:
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 void AddGreenToBlueAndRed(uint8 green, uint8 *red, uint8 *blue) {
@@ -495,71 +480,63 @@
 }
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-This transform is redundant as it can be modeled using the color
-transform, but it is still often useful. Since it can extend the
-dynamics of the color transform and there is no additional data here,
-the subtract green transform can be coded using fewer bits than a
-full-blown color transform.
+This transform is redundant as it can be modeled using the color transform, but
+since there is no additional data here, the subtract green transform can be
+coded using fewer bits than a full-blown color transform.
 
 
-### Color Indexing Transform
+### 4.4 Color Indexing Transform
 
-If there are not many unique pixel values, it may be more efficient to
-create a color index array and replace the pixel values by the array's
-indices. The color indexing transform achieves this. (In the context of
-WebP lossless, we specifically do not call this a palette transform
-because a similar but more dynamic concept exists in WebP lossless
-encoding: color cache.)
+If there are not many unique pixel values, it may be more efficient to create a
+color index array and replace the pixel values by the array's indices. The color
+indexing transform achieves this. (In the context of WebP lossless, we
+specifically do not call this a palette transform because a similar but more
+dynamic concept exists in WebP lossless encoding: color cache).
 
-The color indexing transform checks for the number of unique ARGB values
-in the image. If that number is below a threshold (256), it creates an
-array of those ARGB values, which is then used to replace the pixel
-values with the corresponding index: the green channel of the pixels are
-replaced with the index; all alpha values are set to 255; all red and
-blue values to 0.
+The color indexing transform checks for the number of unique ARGB values in the
+image. If that number is below a threshold (256), it creates an array of those
+ARGB values, which is then used to replace the pixel values with the
+corresponding index: the green channel of the pixels are replaced with the
+index; all alpha values are set to 255; all red and blue values to 0.
 
-The transform data contains color table size and the entries in the
-color table. The decoder reads the color indexing transform data as
-follows:
+The transform data contains color table size and the entries in the color table.
+The decoder reads the color indexing transform data as follows:
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // 8 bit value for color table size
 int color_table_size = ReadBits(8) + 1;
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-The color table is stored using the image storage format itself. The
-color table can be obtained by reading an image, without the RIFF
-header, image size, and transforms, assuming a height of one pixel and
-a width of `color_table_size`. The color table is always
-subtraction-coded to reduce image entropy. The deltas of palette colors
-contain typically much less entropy than the colors themselves, leading
-to significant savings for smaller images. In decoding, every final
-color in the color table can be obtained by adding the previous color
-component values by each ARGB component separately, and storing the
-least significant 8 bits of the result.
+The color table is stored using the image storage format itself. The color table
+can be obtained by reading an image, without the RIFF header, image size, and
+transforms, assuming a height of one pixel and a width of `color_table_size`.
+The color table is always subtraction-coded to reduce image entropy. The deltas
+of palette colors contain typically much less entropy than the colors
+themselves, leading to significant savings for smaller images. In decoding,
+every final color in the color table can be obtained by adding the previous
+color component values by each ARGB component separately, and storing the least
+significant 8 bits of the result.
 
-The inverse transform for the image is simply replacing the pixel values
-(which are indices to the color table) with the actual color table
-values. The indexing is done based on the green component of the ARGB
-color.
+The inverse transform for the image is simply replacing the pixel values (which
+are indices to the color table) with the actual color table values. The indexing
+is done based on the green component of the ARGB color.
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Inverse transform
 argb = color_table[GREEN(argb)];
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-If the index is equal or larger than color_table_size, the argb color value
-should be set to 0x00000000 (transparent black).  \[AMENDED\]
+If the index is equal or larger than `color_table_size`, the argb color value
+should be set to 0x00000000 (transparent black).
 
-When the color table is small (equal to or less than 16 colors), several
-pixels are bundled into a single pixel. The pixel bundling packs several
-(2, 4, or 8) pixels into a single pixel, reducing the image width
-respectively. Pixel bundling allows for a more efficient joint
-distribution entropy coding of neighboring pixels, and gives some
-arithmetic coding-like benefits to the entropy code, but it can only be
-used when there are a small number of unique values.
+When the color table is small (equal to or less than 16 colors), several pixels
+are bundled into a single pixel. The pixel bundling packs several (2, 4, or 8)
+pixels into a single pixel, reducing the image width respectively. Pixel
+bundling allows for a more efficient joint distribution entropy coding of
+neighboring pixels, and gives some arithmetic coding-like benefits to the
+entropy code, but it can only be used when there are 16 or fewer unique values.
 
-`color_table_size` specifies how many pixels are combined together:
+`color_table_size` specifies how many pixels are combined:
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 int width_bits;
@@ -574,13 +551,12 @@
 }
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-`width_bits` has a value of 0, 1, 2 or 3. A value of 0 indicates no
-pixel bundling to be done for the image. A value of 1 indicates that two
-pixels are combined together, and each pixel has a range of \[0..15\]. A
-value of 2 indicates that four pixels are combined together, and each
-pixel has a range of \[0..3\]. A value of 3 indicates that eight pixels
-are combined together and each pixel has a range of \[0..1\], i.e., a
-binary value.
+`width_bits` has a value of 0, 1, 2 or 3. A value of 0 indicates no pixel
+bundling is to be done for the image. A value of 1 indicates that two pixels are
+combined, and each pixel has a range of \[0..15\]. A value of 2 indicates that
+four pixels are combined, and each pixel has a range of \[0..3\]. A value of 3
+indicates that eight pixels are combined and each pixel has a range of \[0..1\],
+that is, a binary value.
 
 The values are packed into the green component as follows:
 
@@ -590,43 +566,43 @@
     4 most-significant bits of the green value at x / 2.
   * `width_bits` = 2: for every x value where x ≡ 0 (mod 4), a green
     value at x is positioned into the 2 least-significant bits of the
-    green value at x / 4, green values at x + 1 to x + 3 in order to the
-    more significant bits of the green value at x / 4.
+    green value at x / 4, green values at x + 1 to x + 3 are positioned in order
+    to the more significant bits of the green value at x / 4.
   * `width_bits` = 3: for every x value where x ≡ 0 (mod 8), a green
     value at x is positioned into the least-significant bit of the green
-    value at x / 8, green values at x + 1 to x + 7 in order to the more
-    significant bits of the green value at x / 8.
+    value at x / 8, green values at x + 1 to x + 7 are positioned in order to
+    the more significant bits of the green value at x / 8.
 
 
-4 Image Data
+5 Image Data
 ------------
 
 Image data is an array of pixel values in scan-line order.
 
-### 4.1 Roles of Image Data
+### 5.1 Roles of Image Data
 
 We use image data in five different roles:
 
   1. ARGB image: Stores the actual pixels of the image.
   1. Entropy image: Stores the
-     [meta Huffman codes](#decoding-of-meta-huffman-codes). The red and green
-     components of a pixel define the meta Huffman code used in a particular
+     [meta prefix codes](#decoding-of-meta-prefix-codes). The red and green
+     components of a pixel define the meta prefix code used in a particular
      block of the ARGB image.
-  1. Predictor image: Stores the metadata for [Predictor
-     Transform](#predictor-transform). The green component of a pixel defines
-     which of the 14 predictors is used within a particular block of the
+  1. Predictor image: Stores the metadata for
+     [Predictor Transform](#predictor-transform). The green component of a pixel
+     defines which of the 14 predictors is used within a particular block of the
      ARGB image.
   1. Color transform image. It is created by `ColorTransformElement` values
      (defined in [Color Transform](#color-transform)) for different blocks of
      the image. Each `ColorTransformElement` `'cte'` is treated as a pixel whose
      alpha component is `255`, red component is `cte.red_to_blue`, green
      component is `cte.green_to_blue` and blue component is `cte.green_to_red`.
-  1. Color indexing image: An array of of size `color_table_size` (up to 256
+  1. Color indexing image: An array of size `color_table_size` (up to 256
      ARGB values) storing the metadata for the
      [Color Indexing Transform](#color-indexing-transform). This is stored as an
      image of width `color_table_size` and height `1`.
 
-### 4.2 Encoding of Image data
+### 5.2 Encoding of Image Data
 
 The encoding of image data is independent of its role.
 
@@ -643,21 +619,22 @@
 
 Each pixel is encoded using one of the three possible methods:
 
-  1. Huffman coded literal: each channel (green, red, blue and alpha) is
+  1. Prefix coded literal: each channel (green, red, blue and alpha) is
      entropy-coded independently;
   2. LZ77 backward reference: a sequence of pixels are copied from elsewhere
      in the image; or
   3. Color cache code: using a short multiplicative hash code (color cache
      index) of a recently seen color.
 
-The following sub-sections describe each of these in detail.
+The following subsections describe each of these in detail.
 
-#### 4.2.1 Huffman Coded Literals
+#### 5.2.1 Prefix Coded Literals
 
-The pixel is stored as Huffman coded values of green, red, blue and alpha (in
-that order). See [this section](#decoding-entropy-coded-image-data) for details.
+The pixel is stored as prefix coded values of green, red, blue and alpha (in
+that order). See [Section 6.2.3](#decoding-entropy-coded-image-data) for
+details.
 
-#### 4.2.2 LZ77 Backward Reference
+#### 5.2.2 LZ77 Backward Reference
 
 Backward references are tuples of _length_ and _distance code_:
 
@@ -674,11 +651,11 @@
 
 **Rationale**: This approach reduces the storage requirement for the entropy
 code. Also, large values are usually rare, and so extra bits would be used for
-very few values in the image. Thus, this approach results in a better
-compression overall.
+very few values in the image. Thus, this approach results in better compression
+overall.
 
 The following table denotes the prefix codes and extra bits used for storing
-different range of values.
+different ranges of values.
 
 Note: The maximum backward reference length is limited to 4096. Hence, only the
 first 24 prefix codes (with the respective extra bits) are meaningful for length
@@ -700,8 +677,8 @@
 | 524289..786432  | 38          | 18         |
 | 786433..1048576 | 39          | 18         |
 
-The pseudocode to obtain a (length or distance) value from the prefix code is
-as follows:
+The pseudocode to obtain a (length or distance) value from the prefix code is as
+follows:
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 if (prefix_code < 4) {
@@ -715,13 +692,13 @@
 **Distance Mapping:**
 {:#distance-mapping}
 
-As noted previously, distance code is a number indicating the position of a
-previously seen pixel, from which the pixels are to be copied. This sub-section
+As noted previously, a distance code is a number indicating the position of a
+previously seen pixel, from which the pixels are to be copied. This subsection
 defines the mapping between a distance code and the position of a previous
 pixel.
 
-The distance codes larger than 120 denote the pixel-distance in scan-line
-order, offset by 120.
+Distance codes larger than 120 denote the pixel-distance in scan-line order,
+offset by 120.
 
 The smallest distance codes \[1..120\] are special, and are reserved for a close
 neighborhood of the current pixel. This neighborhood consists of 120 pixels:
@@ -736,54 +713,58 @@
 `(xi, yi)` is as follows:
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-(0, 1),  (1, 0),  (1, 1),  (-1, 1), (0, 2),  (2, 0),  (1, 2),  (-1, 2),
-(2, 1),  (-2, 1), (2, 2),  (-2, 2), (0, 3),  (3, 0),  (1, 3),  (-1, 3),
-(3, 1),  (-3, 1), (2, 3),  (-2, 3), (3, 2),  (-3, 2), (0, 4),  (4, 0),
-(1, 4),  (-1, 4), (4, 1),  (-4, 1), (3, 3),  (-3, 3), (2, 4),  (-2, 4),
-(4, 2),  (-4, 2), (0, 5),  (3, 4),  (-3, 4), (4, 3),  (-4, 3), (5, 0),
-(1, 5),  (-1, 5), (5, 1),  (-5, 1), (2, 5),  (-2, 5), (5, 2),  (-5, 2),
-(4, 4),  (-4, 4), (3, 5),  (-3, 5), (5, 3),  (-5, 3), (0, 6),  (6, 0),
-(1, 6),  (-1, 6), (6, 1),  (-6, 1), (2, 6),  (-2, 6), (6, 2),  (-6, 2),
-(4, 5),  (-4, 5), (5, 4),  (-5, 4), (3, 6),  (-3, 6), (6, 3),  (-6, 3),
-(0, 7),  (7, 0),  (1, 7),  (-1, 7), (5, 5),  (-5, 5), (7, 1),  (-7, 1),
-(4, 6),  (-4, 6), (6, 4),  (-6, 4), (2, 7),  (-2, 7), (7, 2),  (-7, 2),
-(3, 7),  (-3, 7), (7, 3),  (-7, 3), (5, 6),  (-5, 6), (6, 5),  (-6, 5),
-(8, 0),  (4, 7),  (-4, 7), (7, 4),  (-7, 4), (8, 1),  (8, 2),  (6, 6),
-(-6, 6), (8, 3),  (5, 7),  (-5, 7), (7, 5),  (-7, 5), (8, 4),  (6, 7),
-(-6, 7), (7, 6),  (-7, 6), (8, 5),  (7, 7),  (-7, 7), (8, 6),  (8, 7)
+(0, 1),  (1, 0),  (1, 1),  (-1, 1), (0, 2),  (2, 0),  (1, 2),
+(-1, 2), (2, 1),  (-2, 1), (2, 2),  (-2, 2), (0, 3),  (3, 0),
+(1, 3),  (-1, 3), (3, 1),  (-3, 1), (2, 3),  (-2, 3), (3, 2),
+(-3, 2), (0, 4),  (4, 0),  (1, 4),  (-1, 4), (4, 1),  (-4, 1),
+(3, 3),  (-3, 3), (2, 4),  (-2, 4), (4, 2),  (-4, 2), (0, 5),
+(3, 4),  (-3, 4), (4, 3),  (-4, 3), (5, 0),  (1, 5),  (-1, 5),
+(5, 1),  (-5, 1), (2, 5),  (-2, 5), (5, 2),  (-5, 2), (4, 4),
+(-4, 4), (3, 5),  (-3, 5), (5, 3),  (-5, 3), (0, 6),  (6, 0),
+(1, 6),  (-1, 6), (6, 1),  (-6, 1), (2, 6),  (-2, 6), (6, 2),
+(-6, 2), (4, 5),  (-4, 5), (5, 4),  (-5, 4), (3, 6),  (-3, 6),
+(6, 3),  (-6, 3), (0, 7),  (7, 0),  (1, 7),  (-1, 7), (5, 5),
+(-5, 5), (7, 1),  (-7, 1), (4, 6),  (-4, 6), (6, 4),  (-6, 4),
+(2, 7),  (-2, 7), (7, 2),  (-7, 2), (3, 7),  (-3, 7), (7, 3),
+(-7, 3), (5, 6),  (-5, 6), (6, 5),  (-6, 5), (8, 0),  (4, 7),
+(-4, 7), (7, 4),  (-7, 4), (8, 1),  (8, 2),  (6, 6),  (-6, 6),
+(8, 3),  (5, 7),  (-5, 7), (7, 5),  (-7, 5), (8, 4),  (6, 7),
+(-6, 7), (7, 6),  (-7, 6), (8, 5),  (7, 7),  (-7, 7), (8, 6),
+(8, 7)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-For example, distance code `1` indicates offset of `(0, 1)` for the neighboring
-pixel, that is, the pixel above the current pixel (0-pixel difference in
-X-direction and 1 pixel difference in Y-direction). Similarly, distance code
-`3` indicates left-top pixel.
+For example, the distance code `1` indicates an offset of `(0, 1)` for the
+neighboring pixel, that is, the pixel above the current pixel (0 pixel
+difference in the X-direction and 1 pixel difference in the Y-direction).
+Similarly, the distance code `3` indicates the left-top pixel.
 
-The decoder can convert a distances code 'i' to a scan-line order distance
-'dist' as follows:
+The decoder can convert a distance code `i` to a scan-line order distance `dist`
+as follows:
 
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-(xi, yi) = distance_map[i]
-dist = x + y * xsize
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+(xi, yi) = distance_map[i - 1]
+dist = xi + yi * xsize
 if (dist < 1) {
   dist = 1
 }
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-where 'distance_map' is the mapping noted above and `xsize` is the width of the
+where `distance_map` is the mapping noted above and `xsize` is the width of the
 image in pixels.
 
 
-#### 4.2.3 Color Cache Coding
+#### 5.2.3 Color Cache Coding
+{:#color-cache-code}
 
 Color cache stores a set of colors that have been recently used in the image.
 
 **Rationale:** This way, the recently used colors can sometimes be referred to
-more efficiently than emitting them using other two methods (described in
-[4.2.1](#huffman-coded-literals) and [4.2.2](#lz77-backward-reference)).
+more efficiently than emitting them using the other two methods (described in
+[5.2.1](#prefix-coded-literals) and [5.2.2](#lz77-backward-reference)).
 
 Color cache codes are stored as follows. First, there is a 1-bit value that
 indicates if the color cache is used. If this bit is 0, no color cache codes
-exist, and they are not transmitted in the Huffman code that decodes the green
+exist, and they are not transmitted in the prefix code that decodes the green
 symbols and the length prefix codes. However, if this bit is 1, the color cache
 size is read next:
 
@@ -797,147 +778,270 @@
 `color_cache_code_bits` is \[1..11\]. Compliant decoders must indicate a
 corrupted bitstream for other values.
 
-A color cache is an array of size `color_cache_size`. Each entry
-stores one ARGB color. Colors are looked up by indexing them by
-(0x1e35a7bd * `color`) >> (32 - `color_cache_code_bits`). Only one
-lookup is done in a color cache; there is no conflict resolution.
+A color cache is an array of size `color_cache_size`. Each entry stores one ARGB
+color. Colors are looked up by indexing them by (0x1e35a7bd * `color`) >> (32 -
+`color_cache_code_bits`). Only one lookup is done in a color cache; there is no
+conflict resolution.
 
-In the beginning of decoding or encoding of an image, all entries in all
-color cache values are set to zero. The color cache code is converted to
-this color at decoding time. The state of the color cache is maintained
-by inserting every pixel, be it produced by backward referencing or as
-literals, into the cache in the order they appear in the stream.
+In the beginning of decoding or encoding of an image, all entries in all color
+cache values are set to zero. The color cache code is converted to this color at
+decoding time. The state of the color cache is maintained by inserting every
+pixel, be it produced by backward referencing or as literals, into the cache in
+the order they appear in the stream.
 
 
-5 Entropy Code
+6 Entropy Code
 --------------
 
-### 5.1 Overview
+### 6.1 Overview
 
-Most of the data is coded using [canonical Huffman code][canonical_huff]. Hence,
-the codes are transmitted by sending the _Huffman code lengths_, as opposed to
-the actual _Huffman codes_.
+Most of the data is coded using a [canonical prefix code][canonical_huff].
+Hence, the codes are transmitted by sending the _prefix code lengths_, as
+opposed to the actual _prefix codes_.
 
-In particular, the format uses **spatially-variant Huffman coding**. In other
+In particular, the format uses **spatially-variant prefix coding**. In other
 words, different blocks of the image can potentially use different entropy
 codes.
 
-**Rationale**: Different areas of the image may have different characteristics. So, allowing them to use different entropy codes provides more flexibility and
-potentially a better compression.
+**Rationale**: Different areas of the image may have different characteristics.
+So, allowing them to use different entropy codes provides more flexibility and
+potentially better compression.
 
-### 5.2 Details
+### 6.2 Details
 
-The encoded image data consists of two parts:
+The encoded image data consists of several parts:
 
-  1. Meta Huffman codes
+  1. Decoding and building the prefix codes
+  1. Meta prefix codes
   1. Entropy-coded image data
 
-#### 5.2.1 Decoding of Meta Huffman Codes
+#### 6.2.1 Decoding and Building the Prefix Codes
 
-As noted earlier, the format allows the use of different Huffman codes for
-different blocks of the image. _Meta Huffman codes_ are indexes identifying
-which Huffman codes to use in different parts of the image.
+There are several steps in decoding the prefix codes.
 
-Meta Huffman codes may be used _only_ when the image is being used in the
+**Decoding the Code Lengths:**
+{:#decoding-the-code-lengths}
+
+This section describes how to read the prefix code lengths from the bitstream.
+
+The prefix code lengths can be coded in two ways. The method used is specified
+by a 1-bit value.
+
+  * If this bit is 1, it is a _simple code length code_, and
+  * If this bit is 0, it is a _normal code length code_.
+
+In both cases, there can be unused code lengths that are still part of the
+stream. This may be inefficient, but it is allowed by the format.
+The described tree must be a complete binary tree. A single leaf node is
+considered a complete binary tree and can be encoded using either the simple
+code length code or the normal code length code. When coding a single leaf
+node using the _normal code length code_, all but one code length should be
+zeros, and the single leaf node value is marked with the length of 1 -- even
+when no bits are consumed when that single leaf node tree is used.
+
+**(i) Simple Code Length Code:**
+
+This variant is used in the special case when only 1 or 2 prefix symbols are in
+the range \[0..255\] with code length `1`. All other prefix code lengths are
+implicitly zeros.
+
+The first bit indicates the number of symbols:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int num_symbols = ReadBits(1) + 1;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Following are the symbol values.
+
+This first symbol is coded using 1 or 8 bits depending on the value of
+`is_first_8bits`. The range is \[0..1\] or \[0..255\], respectively. The second
+symbol, if present, is always assumed to be in the range \[0..255\] and coded
+using 8 bits.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int is_first_8bits = ReadBits(1);
+symbol0 = ReadBits(1 + 7 * is_first_8bits);
+code_lengths[symbol0] = 1;
+if (num_symbols == 2) {
+  symbol1 = ReadBits(8);
+  code_lengths[symbol1] = 1;
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+**Note:** Another special case is when _all_ prefix code lengths are _zeros_ (an
+empty prefix code). For example, a prefix code for distance can be empty if
+there are no backward references. Similarly, prefix codes for alpha, red, and
+blue can be empty if all pixels within the same meta prefix code are produced
+using the color cache. However, this case doesn't need special handling, as
+empty prefix codes can be coded as those containing a single symbol `0`.
+
+**(ii) Normal Code Length Code:**
+
+The code lengths of the prefix code fit in 8 bits and are read as follows.
+First, `num_code_lengths` specifies the number of code lengths.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int num_code_lengths = 4 + ReadBits(4);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If `num_code_lengths` is > 19, the bitstream is invalid.
+
+The code lengths are themselves encoded using prefix codes: lower level code
+lengths, `code_length_code_lengths`, first have to be read. The rest of those
+`code_length_code_lengths` (according to the order in `kCodeLengthCodeOrder`)
+are zeros.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int kCodeLengthCodes = 19;
+int kCodeLengthCodeOrder[kCodeLengthCodes] = {
+  17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+};
+int code_length_code_lengths[kCodeLengthCodes] = { 0 };  // All zeros
+for (i = 0; i < num_code_lengths; ++i) {
+  code_length_code_lengths[kCodeLengthCodeOrder[i]] = ReadBits(3);
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Next, if `ReadBits(1) == 0`, the maximum number of different read symbols is
+`num_code_lengths`. Otherwise, it is defined as:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int length_nbits = 2 + 2 * ReadBits(3);
+int max_symbol = 2 + ReadBits(length_nbits);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A prefix table is then built from `code_length_code_lengths` and used to read up
+to `max_symbol` code lengths.
+
+  * Code \[0..15\] indicates literal code lengths.
+    * Value 0 means no symbols have been coded.
+    * Values \[1..15\] indicate the bit length of the respective code.
+  * Code 16 repeats the previous non-zero value \[3..6\] times, that is,
+    `3 + ReadBits(2)` times. If code 16 is used before a non-zero
+    value has been emitted, a value of 8 is repeated.
+  * Code 17 emits a streak of zeros \[3..10\], that is, `3 + ReadBits(3)`
+    times.
+  * Code 18 emits a streak of zeros of length \[11..138\], that is,
+    `11 + ReadBits(7)` times.
+
+Once code lengths are read, a prefix code for each symbol type (A, R, G, B,
+distance) is formed using their respective alphabet sizes:
+
+  * G channel: 256 + 24 + `color_cache_size`
+  * other literals (A,R,B): 256
+  * distance code: 40
+
+The Normal Code Length Code must code a full decision tree, that is, the sum of
+`2 ^ (-length)` for all non-zero codes must be exactly one. There is however
+one exception to this rule, the single leaf node tree, where the leaf node
+value is marked with value 1 and other values are 0s.
+
+#### 6.2.2 Decoding of Meta Prefix Codes
+
+As noted earlier, the format allows the use of different prefix codes for
+different blocks of the image. _Meta prefix codes_ are indexes identifying which
+prefix codes to use in different parts of the image.
+
+Meta prefix codes may be used _only_ when the image is being used in the
 [role](#roles-of-image-data) of an _ARGB image_.
 
-There are two possibilities for the meta Huffman codes, indicated by a 1-bit
+There are two possibilities for the meta prefix codes, indicated by a 1-bit
 value:
 
-  * If this bit is zero, there is only one meta Huffman code used everywhere in
+  * If this bit is zero, there is only one meta prefix code used everywhere in
     the image. No more data is stored.
-  * If this bit is one, the image uses multiple meta Huffman codes. These meta
-    Huffman codes are stored as an _entropy image_ (described below).
+  * If this bit is one, the image uses multiple meta prefix codes. These meta
+    prefix codes are stored as an _entropy image_ (described below).
 
 **Entropy image:**
 
-The entropy image defines which Huffman codes are used in different parts of the
+The entropy image defines which prefix codes are used in different parts of the
 image, as described below.
 
-The first 3-bits contain the `huffman_bits` value. The dimensions of the entropy
-image are derived from 'huffman_bits'.
+The first 3-bits contain the `prefix_bits` value. The dimensions of the entropy
+image are derived from `prefix_bits`.
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-int huffman_bits = ReadBits(3) + 2;
-int huffman_xsize = DIV_ROUND_UP(xsize, 1 << huffman_bits);
-int huffman_ysize = DIV_ROUND_UP(ysize, 1 << huffman_bits);
+int prefix_bits = ReadBits(3) + 2;
+int prefix_xsize = DIV_ROUND_UP(xsize, 1 << prefix_bits);
+int prefix_ysize = DIV_ROUND_UP(ysize, 1 << prefix_bits);
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 where `DIV_ROUND_UP` is as defined [earlier](#predictor-transform).
 
-Next bits contain an entropy image of width `huffman_xsize` and height
-`huffman_ysize`.
+The next bits contain an entropy image of width `prefix_xsize` and height
+`prefix_ysize`.
 
-**Interpretation of Meta Huffman Codes:**
+**Interpretation of Meta Prefix Codes:**
 
-For any given pixel (x, y), there is a set of five Huffman codes associated with
+For any given pixel (x, y), there is a set of five prefix codes associated with
 it. These codes are (in bitstream order):
 
-  * **Huffman code #1**: used for green channel, backward-reference length and
-    color cache
-  * **Huffman code #2, #3 and #4**: used for red, blue and alpha channels
+  * **Prefix code #1**: used for green channel, backward-reference length and
+    color cache.
+  * **Prefix code #2, #3 and #4**: used for red, blue and alpha channels
     respectively.
-  * **Huffman code #5**: used for backward-reference distance.
+  * **Prefix code #5**: used for backward-reference distance.
 
-From here on, we refer to this set as a **Huffman code group**.
+From here on, we refer to this set as a **prefix code group**.
 
-The number of Huffman code groups in the ARGB image can be obtained by finding
-the _largest meta Huffman code_ from the entropy image:
+The number of prefix code groups in the ARGB image can be obtained by finding
+the _largest meta prefix code_ from the entropy image:
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-int num_huff_groups = max(entropy image) + 1;
+int num_prefix_groups = max(entropy image) + 1;
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-where `max(entropy image)` indicates the largest Huffman code stored in the
+w